summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorBenn Pörscke <benn.porscke@stericsson.com>2011-10-07 15:31:57 +0200
committerBenn Pörscke <benn.porscke@stericsson.com>2011-10-07 15:31:57 +0200
commit47a4dbf83a75014d6b3467be18997894f1c617db (patch)
tree7f5d116db48205309fbc4ae0954f20ab8a651e46 /drivers
parentea8a52f9f4bcc3420c38ae07f8378a2f18443970 (diff)
Change-Id: If0ae9fa8067740ab2ede33703c79ec134f204a5e
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig6
-rw-r--r--drivers/Makefile4
-rw-r--r--drivers/amba/bus.c350
-rw-r--r--drivers/base/Kconfig3
-rw-r--r--drivers/base/Makefile1
-rw-r--r--drivers/base/dd.c4
-rw-r--r--drivers/base/platform.c35
-rw-r--r--drivers/base/power/generic_ops.c8
-rw-r--r--drivers/base/power/main.c87
-rw-r--r--drivers/base/power/power.h1
-rw-r--r--drivers/base/power/runtime.c973
-rw-r--r--drivers/base/power/sysfs.c155
-rw-r--r--drivers/base/soc.c79
-rw-r--r--drivers/bluetooth/Kconfig7
-rw-r--r--drivers/bluetooth/Makefile3
-rw-r--r--drivers/bluetooth/btcg2900.c1203
-rw-r--r--drivers/bluetooth/cg2900_uart.c2128
-rw-r--r--drivers/bluetooth/hci_bcsp.c3
-rw-r--r--drivers/bluetooth/hci_h4.c1
-rw-r--r--drivers/bluetooth/hci_ldisc.c107
-rw-r--r--drivers/bluetooth/hci_ll.c1
-rw-r--r--drivers/bluetooth/hci_uart.h20
-rw-r--r--drivers/char/Kconfig17
-rw-r--r--drivers/char/Makefile4
-rw-r--r--drivers/char/dcc_tty.c326
-rw-r--r--drivers/char/mem.c17
-rw-r--r--drivers/char/shrm_char.c864
-rw-r--r--drivers/char/tty_io.c1
-rw-r--r--drivers/cpufreq/cpufreq.c21
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c52
-rw-r--r--drivers/cpuidle/governors/menu.c70
-rw-r--r--drivers/crypto/Kconfig10
-rw-r--r--drivers/crypto/Makefile2
-rw-r--r--drivers/crypto/ux500/Kconfig29
-rw-r--r--drivers/crypto/ux500/Makefile8
-rw-r--r--drivers/crypto/ux500/cryp/Makefile13
-rw-r--r--drivers/crypto/ux500/cryp/cryp.c556
-rw-r--r--drivers/crypto/ux500/cryp/cryp.h338
-rw-r--r--drivers/crypto/ux500/cryp/cryp_core.c2331
-rw-r--r--drivers/crypto/ux500/cryp/cryp_irq.c45
-rw-r--r--drivers/crypto/ux500/cryp/cryp_irq.h31
-rw-r--r--drivers/crypto/ux500/cryp/cryp_irqp.h125
-rw-r--r--drivers/crypto/ux500/cryp/cryp_p.h113
-rw-r--r--drivers/crypto/ux500/hash/Makefile11
-rw-r--r--drivers/crypto/ux500/hash/hash_alg.h371
-rwxr-xr-xdrivers/crypto/ux500/hash/hash_alg_p.h26
-rw-r--r--drivers/crypto/ux500/hash/hash_core.c1235
-rw-r--r--drivers/dma/Kconfig15
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/amba-pl08x.c2167
-rw-r--r--drivers/dma/coh901318.c169
-rw-r--r--drivers/dma/dmaengine.c4
-rw-r--r--drivers/dma/ste_dma40.c2633
-rw-r--r--drivers/dma/ste_dma40_ll.c206
-rw-r--r--drivers/dma/ste_dma40_ll.h120
-rw-r--r--drivers/gpio/Kconfig12
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/ab8500-gpio.c596
-rw-r--r--drivers/gpio/pl061.c4
-rw-r--r--drivers/gpio/stmpe-gpio.c404
-rw-r--r--drivers/gpio/tc35892-gpio.c8
-rw-r--r--drivers/gpu/Makefile3
-rw-r--r--drivers/gpu/mali/Kconfig7
-rw-r--r--drivers/gpu/mali/Makefile3
l---------drivers/gpu/mali/src1
-rw-r--r--drivers/hsi/Kconfig20
-rw-r--r--drivers/hsi/Makefile6
-rw-r--r--drivers/hsi/clients/Kconfig19
-rw-r--r--drivers/hsi/clients/Makefile6
-rw-r--r--drivers/hsi/clients/cfhsi.c318
-rw-r--r--drivers/hsi/clients/hsi_char.c1108
-rw-r--r--drivers/hsi/controllers/Kconfig33
-rw-r--r--drivers/hsi/controllers/Makefile6
-rw-r--r--drivers/hsi/controllers/omap_ssi.c1853
-rw-r--r--drivers/hsi/controllers/ste_hsi.c1536
-rw-r--r--drivers/hsi/hsi.c496
-rw-r--r--drivers/hsi/hsi_boardinfo.c64
-rw-r--r--drivers/hsi/hsi_core.h37
-rw-r--r--drivers/hwmon/Kconfig60
-rw-r--r--drivers/hwmon/Makefile4
-rw-r--r--drivers/hwmon/ab8500.c715
-rwxr-xr-xdrivers/hwmon/db8500.c450
-rw-r--r--drivers/hwmon/l3g4200d.c690
-rw-r--r--drivers/hwmon/lsm303dlh_a.c1310
-rw-r--r--drivers/hwmon/lsm303dlh_m.c878
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c273
-rw-r--r--drivers/i2c/i2c-core.c62
-rw-r--r--drivers/input/Kconfig9
-rw-r--r--drivers/input/Makefile1
-rw-r--r--drivers/input/evdev.c15
-rw-r--r--drivers/input/keyboard/Kconfig70
-rw-r--r--drivers/input/keyboard/Makefile4
-rw-r--r--drivers/input/keyboard/db5500_keypad.c449
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c756
-rw-r--r--drivers/input/keyboard/stmpe-keypad.c473
-rw-r--r--drivers/input/keyboard/tc35893-keypad.c621
-rw-r--r--drivers/input/keyreset.c230
-rw-r--r--drivers/input/misc/Kconfig49
-rw-r--r--drivers/input/misc/Makefile7
-rw-r--r--drivers/input/misc/ab8500-accdet.c1428
-rw-r--r--drivers/input/misc/ab8500-ponkey.c182
-rw-r--r--drivers/input/misc/gpio_axis.c192
-rw-r--r--drivers/input/misc/gpio_event.c260
-rw-r--r--drivers/input/misc/gpio_input.c353
-rw-r--r--drivers/input/misc/gpio_matrix.c432
-rw-r--r--drivers/input/misc/gpio_output.c97
-rw-r--r--drivers/input/misc/keychord.c387
-rw-r--r--drivers/input/misc/lps001wp_prs.c1276
-rw-r--r--drivers/input/misc/ste_ff_vibra.c234
-rw-r--r--drivers/input/touchscreen/Kconfig30
-rw-r--r--drivers/input/touchscreen/Makefile3
-rw-r--r--drivers/input/touchscreen/bu21013_ts.c931
-rw-r--r--drivers/input/touchscreen/synaptics_i2c_rmi.c675
-rw-r--r--drivers/leds/Kconfig33
-rw-r--r--drivers/leds/Makefile4
-rw-r--r--drivers/leds/leds-ab5500.c432
-rw-r--r--drivers/leds/leds-lm3530.c422
-rw-r--r--drivers/leds/leds-lp5521.c751
-rw-r--r--drivers/leds/leds-pwm.c8
-rw-r--r--drivers/leds/ledtrig-sleep.c80
-rw-r--r--drivers/media/IR/Kconfig2
-rw-r--r--drivers/media/radio/CG2900/Makefile8
-rw-r--r--drivers/media/radio/CG2900/cg2900_fm_api.c3106
-rw-r--r--drivers/media/radio/CG2900/cg2900_fm_api.h1074
-rw-r--r--drivers/media/radio/CG2900/cg2900_fm_driver.c4829
-rwxr-xr-xdrivers/media/radio/CG2900/cg2900_fm_driver.h1761
-rw-r--r--drivers/media/radio/CG2900/radio-cg2900.c2855
-rw-r--r--drivers/media/radio/Kconfig16
-rw-r--r--drivers/media/radio/Makefile1
-rw-r--r--drivers/mfd/Kconfig122
-rw-r--r--drivers/mfd/Makefile9
-rw-r--r--drivers/mfd/ab3100-core.c143
-rw-r--r--drivers/mfd/ab3100-otp.c16
-rw-r--r--drivers/mfd/ab3550-core.c23
-rwxr-xr-xdrivers/mfd/ab5500-core.c2495
-rw-r--r--drivers/mfd/ab5500-power.c72
-rw-r--r--drivers/mfd/ab8500-core.c773
-rw-r--r--drivers/mfd/ab8500-debugfs.c1175
-rw-r--r--drivers/mfd/ab8500-denc.c538
-rw-r--r--drivers/mfd/ab8500-gpadc.c642
-rw-r--r--drivers/mfd/ab8500-i2c.c105
-rw-r--r--drivers/mfd/ab8500-spi.c133
-rw-r--r--drivers/mfd/ab8500-sysctrl.c102
-rw-r--r--drivers/mfd/cg2900/Makefile15
-rw-r--r--drivers/mfd/cg2900/cg2900_audio.c3486
-rw-r--r--drivers/mfd/cg2900/cg2900_char_devices.c714
-rw-r--r--drivers/mfd/cg2900/cg2900_chip.c3420
-rw-r--r--drivers/mfd/cg2900/cg2900_chip.h610
-rw-r--r--drivers/mfd/cg2900/cg2900_core.c713
-rw-r--r--drivers/mfd/cg2900/cg2900_core.h51
-rw-r--r--drivers/mfd/cg2900/cg2900_lib.c283
-rw-r--r--drivers/mfd/cg2900/cg2900_lib.h60
-rw-r--r--drivers/mfd/cg2900/cg2900_test.c402
-rw-r--r--drivers/mfd/cg2900/stlc2690_chip.c1653
-rw-r--r--drivers/mfd/cg2900/stlc2690_chip.h47
-rw-r--r--drivers/mfd/stmpe.c1033
-rw-r--r--drivers/mfd/stmpe.h183
-rw-r--r--drivers/mfd/tc35892.c168
-rw-r--r--drivers/misc/Kconfig127
-rw-r--r--drivers/misc/Makefile19
-rw-r--r--drivers/misc/ab8500-pwm.c209
-rw-r--r--drivers/misc/akm8975.c732
-rw-r--r--drivers/misc/apanic.c606
-rw-r--r--drivers/misc/arm-charlcd.c396
-rw-r--r--drivers/misc/audio_io_dev/Kconfig11
-rw-r--r--drivers/misc/audio_io_dev/Makefile9
-rw-r--r--drivers/misc/audio_io_dev/ste_audio_io_ab8500_reg_defs.h349
-rw-r--r--drivers/misc/audio_io_dev/ste_audio_io_core.c1589
-rw-r--r--drivers/misc/audio_io_dev/ste_audio_io_core.h138
-rw-r--r--drivers/misc/audio_io_dev/ste_audio_io_dev.c759
-rw-r--r--drivers/misc/audio_io_dev/ste_audio_io_dev.h32
-rw-r--r--drivers/misc/audio_io_dev/ste_audio_io_func.c4371
-rw-r--r--drivers/misc/audio_io_dev/ste_audio_io_func.h358
-rw-r--r--drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.c189
-rw-r--r--drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.h50
-rw-r--r--drivers/misc/bh1780gli.c306
-rw-r--r--drivers/misc/db8500-modem-trace.c273
-rw-r--r--drivers/misc/dbx500-mloader.c269
-rw-r--r--drivers/misc/dispdev/Makefile1
-rw-r--r--drivers/misc/dispdev/dispdev.c522
-rw-r--r--drivers/misc/hwmem/Makefile3
-rw-r--r--drivers/misc/hwmem/cache_handler.c510
-rw-r--r--drivers/misc/hwmem/cache_handler.h61
-rw-r--r--drivers/misc/hwmem/contig_alloc.c468
-rw-r--r--drivers/misc/hwmem/hwmem-ioctl.c532
-rw-r--r--drivers/misc/hwmem/hwmem-main.c725
-rw-r--r--drivers/misc/i2s/Kconfig19
-rw-r--r--drivers/misc/i2s/Makefile7
-rw-r--r--drivers/misc/i2s/i2s.c631
-rw-r--r--drivers/misc/i2s/msp_i2s.c2046
-rw-r--r--drivers/misc/i2s/msp_i2s.h362
-rw-r--r--drivers/misc/iface_stat.c234
-rw-r--r--drivers/misc/kernel_debugger.c79
-rw-r--r--drivers/misc/mbox.c567
-rw-r--r--drivers/misc/mbox_channels-db5500.c1137
-rw-r--r--drivers/misc/pmem.c1408
-rw-r--r--drivers/misc/shrm/Kconfig49
-rw-r--r--drivers/misc/shrm/Makefile11
-rw-r--r--drivers/misc/shrm/modem_shrm_driver.c666
-rw-r--r--drivers/misc/shrm/shrm_driver.c1439
-rw-r--r--drivers/misc/shrm/shrm_fifo.c827
-rw-r--r--drivers/misc/shrm/shrm_protocol.c1194
-rw-r--r--drivers/misc/stm.c414
-rw-r--r--drivers/misc/uid_stat.c186
-rw-r--r--drivers/misc/wl127x-rfkill.c121
-rw-r--r--drivers/mmc/card/Kconfig26
-rw-r--r--drivers/mmc/card/block.c105
-rw-r--r--drivers/mmc/card/mmc_test.c12
-rw-r--r--drivers/mmc/card/queue.c8
-rw-r--r--drivers/mmc/core/Kconfig17
-rw-r--r--drivers/mmc/core/bus.c6
-rw-r--r--drivers/mmc/core/core.c156
-rw-r--r--drivers/mmc/core/core.h2
-rw-r--r--drivers/mmc/core/host.c15
-rw-r--r--drivers/mmc/core/mmc.c82
-rw-r--r--drivers/mmc/core/mmc_ops.c23
-rw-r--r--drivers/mmc/core/sd.c88
-rw-r--r--drivers/mmc/core/sdio.c175
-rw-r--r--drivers/mmc/core/sdio_bus.c13
-rwxr-xr-x[-rw-r--r--]drivers/mmc/core/sdio_io.c37
-rw-r--r--drivers/mmc/core/sdio_irq.c150
-rw-r--r--drivers/mmc/core/sdio_ops.c9
-rw-r--r--drivers/mmc/host/at91_mci.c3
-rw-r--r--drivers/mmc/host/atmel-mci.c3
-rw-r--r--drivers/mmc/host/au1xmmc.c2
-rw-r--r--drivers/mmc/host/bfin_sdh.c2
-rw-r--r--drivers/mmc/host/davinci_mmc.c8
-rw-r--r--drivers/mmc/host/imxmmc.c3
-rw-r--r--drivers/mmc/host/mmc_spi.c3
-rw-r--r--drivers/mmc/host/mmci.c1505
-rw-r--r--drivers/mmc/host/mmci.h115
-rw-r--r--drivers/mmc/host/msm_sdcc.c3
-rw-r--r--drivers/mmc/host/mvsdio.c3
-rw-r--r--drivers/mmc/host/mxcmmc.c3
-rw-r--r--drivers/mmc/host/omap.c3
-rw-r--r--drivers/mmc/host/omap_hsmmc.c24
-rw-r--r--drivers/mmc/host/pxamci.c43
-rw-r--r--drivers/mmc/host/s3cmci.c3
-rw-r--r--drivers/mmc/host/sdhci.c7
-rw-r--r--drivers/mmc/host/sh_mmcif.c3
-rw-r--r--drivers/mmc/host/tifm_sd.c3
-rw-r--r--drivers/mmc/host/via-sdmmc.c3
-rw-r--r--drivers/mmc/host/wbsd.c3
-rw-r--r--drivers/mtd/nand/Kconfig10
-rw-r--r--drivers/mtd/nand/nand_base.c39
-rw-r--r--drivers/net/Kconfig17
-rw-r--r--drivers/net/Makefile6
-rw-r--r--drivers/net/caif/Kconfig31
-rw-r--r--drivers/net/caif/Makefile17
-rw-r--r--drivers/net/caif/caif_hsi.c623
-rw-r--r--drivers/net/caif/caif_serial.c13
-rw-r--r--drivers/net/caif/caif_spi.c883
-rw-r--r--drivers/net/caif/caif_spi_slave.c257
-rw-r--r--drivers/net/pppolac.c380
-rw-r--r--drivers/net/pppopns.c357
-rw-r--r--drivers/net/smsc911x.c27
-rw-r--r--drivers/net/u8500_shrm.c318
-rw-r--r--drivers/net/wireless/Kconfig1
-rw-r--r--drivers/net/wireless/Makefile2
-rw-r--r--drivers/net/wireless/bcm4329/Kconfig27
-rw-r--r--drivers/net/wireless/bcm4329/Makefile21
-rw-r--r--drivers/net/wireless/bcm4329/aiutils.c686
-rw-r--r--drivers/net/wireless/bcm4329/bcmpcispi.c630
-rw-r--r--drivers/net/wireless/bcm4329/bcmsdh.c652
-rw-r--r--drivers/net/wireless/bcm4329/bcmsdh_linux.c733
-rw-r--r--drivers/net/wireless/bcm4329/bcmsdh_sdmmc.c1304
-rw-r--r--drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c273
-rw-r--r--drivers/net/wireless/bcm4329/bcmsdspi.c1596
-rw-r--r--drivers/net/wireless/bcm4329/bcmsdspi_linux.c252
-rw-r--r--drivers/net/wireless/bcm4329/bcmsdstd.c3127
-rw-r--r--drivers/net/wireless/bcm4329/bcmsdstd_linux.c251
-rw-r--r--drivers/net/wireless/bcm4329/bcmspibrcm.c1726
-rw-r--r--drivers/net/wireless/bcm4329/bcmutils.c1838
-rw-r--r--drivers/net/wireless/bcm4329/bcmwifi.c199
-rw-r--r--drivers/net/wireless/bcm4329/dhd.h437
-rw-r--r--drivers/net/wireless/bcm4329/dhd_bus.h93
-rw-r--r--drivers/net/wireless/bcm4329/dhd_cdc.c522
-rw-r--r--drivers/net/wireless/bcm4329/dhd_common.c2323
-rw-r--r--drivers/net/wireless/bcm4329/dhd_custom_gpio.c179
-rw-r--r--drivers/net/wireless/bcm4329/dhd_dbg.h100
-rw-r--r--drivers/net/wireless/bcm4329/dhd_linux.c3308
-rw-r--r--drivers/net/wireless/bcm4329/dhd_linux_sched.c38
-rw-r--r--drivers/net/wireless/bcm4329/dhd_proto.h102
-rw-r--r--drivers/net/wireless/bcm4329/dhd_sdio.c5883
-rw-r--r--drivers/net/wireless/bcm4329/dngl_stats.h43
-rw-r--r--drivers/net/wireless/bcm4329/hndpmu.c131
-rw-r--r--drivers/net/wireless/bcm4329/include/Makefile21
-rw-r--r--drivers/net/wireless/bcm4329/include/aidmp.h368
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmcdc.h100
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmdefs.h114
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmdevs.h124
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmendian.h205
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmpcispi.h205
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmperf.h36
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmsdbus.h117
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmsdh.h208
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmsdh_sdmmc.h122
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmsdpcm.h263
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmsdspi.h131
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmsdstd.h223
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmspi.h36
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmspibrcm.h134
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmutils.h637
-rw-r--r--drivers/net/wireless/bcm4329/include/bcmwifi.h154
-rw-r--r--drivers/net/wireless/bcm4329/include/dhdioctl.h123
-rw-r--r--drivers/net/wireless/bcm4329/include/epivers.h48
-rw-r--r--drivers/net/wireless/bcm4329/include/hndpmu.h34
-rw-r--r--drivers/net/wireless/bcm4329/include/hndrte_armtrap.h88
-rw-r--r--drivers/net/wireless/bcm4329/include/hndrte_cons.h63
-rw-r--r--drivers/net/wireless/bcm4329/include/hndsoc.h195
-rw-r--r--drivers/net/wireless/bcm4329/include/linux_osl.h322
-rw-r--r--drivers/net/wireless/bcm4329/include/linuxver.h447
-rw-r--r--drivers/net/wireless/bcm4329/include/miniopt.h77
-rw-r--r--drivers/net/wireless/bcm4329/include/msgtrace.h72
-rw-r--r--drivers/net/wireless/bcm4329/include/osl.h55
-rw-r--r--drivers/net/wireless/bcm4329/include/packed_section_end.h54
-rw-r--r--drivers/net/wireless/bcm4329/include/packed_section_start.h61
-rw-r--r--drivers/net/wireless/bcm4329/include/pcicfg.h52
-rw-r--r--drivers/net/wireless/bcm4329/include/proto/802.11.h1433
-rw-r--r--drivers/net/wireless/bcm4329/include/proto/802.11e.h131
-rw-r--r--drivers/net/wireless/bcm4329/include/proto/802.1d.h49
-rw-r--r--drivers/net/wireless/bcm4329/include/proto/bcmeth.h83
-rw-r--r--drivers/net/wireless/bcm4329/include/proto/bcmevent.h212
-rw-r--r--drivers/net/wireless/bcm4329/include/proto/bcmip.h157
-rw-r--r--drivers/net/wireless/bcm4329/include/proto/eapol.h172
-rw-r--r--drivers/net/wireless/bcm4329/include/proto/ethernet.h148
-rw-r--r--drivers/net/wireless/bcm4329/include/proto/sdspi.h71
-rw-r--r--drivers/net/wireless/bcm4329/include/proto/vlan.h63
-rw-r--r--drivers/net/wireless/bcm4329/include/proto/wpa.h159
-rw-r--r--drivers/net/wireless/bcm4329/include/sbchipc.h1026
-rw-r--r--drivers/net/wireless/bcm4329/include/sbconfig.h276
-rw-r--r--drivers/net/wireless/bcm4329/include/sbhnddma.h294
-rw-r--r--drivers/net/wireless/bcm4329/include/sbpcmcia.h109
-rw-r--r--drivers/net/wireless/bcm4329/include/sbsdio.h166
-rw-r--r--drivers/net/wireless/bcm4329/include/sbsdpcmdev.h288
-rw-r--r--drivers/net/wireless/bcm4329/include/sbsocram.h150
-rw-r--r--drivers/net/wireless/bcm4329/include/sdio.h566
-rw-r--r--drivers/net/wireless/bcm4329/include/sdioh.h299
-rw-r--r--drivers/net/wireless/bcm4329/include/sdiovar.h58
-rw-r--r--drivers/net/wireless/bcm4329/include/siutils.h235
-rw-r--r--drivers/net/wireless/bcm4329/include/spid.h153
-rw-r--r--drivers/net/wireless/bcm4329/include/trxhdr.h46
-rw-r--r--drivers/net/wireless/bcm4329/include/typedefs.h303
-rw-r--r--drivers/net/wireless/bcm4329/include/wlioctl.h1664
-rw-r--r--drivers/net/wireless/bcm4329/linux_osl.c622
-rw-r--r--drivers/net/wireless/bcm4329/miniopt.c163
-rw-r--r--drivers/net/wireless/bcm4329/sbutils.c1004
-rw-r--r--drivers/net/wireless/bcm4329/siutils.c1527
-rw-r--r--drivers/net/wireless/bcm4329/siutils_priv.h213
-rw-r--r--drivers/net/wireless/bcm4329/wl_iw.c8319
-rw-r--r--drivers/net/wireless/bcm4329/wl_iw.h284
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c2
-rw-r--r--drivers/power/Kconfig12
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/ab8500_btemp.c1080
-rw-r--r--drivers/power/ab8500_chargalg.c1938
-rw-r--r--drivers/power/ab8500_charger.c2541
-rw-r--r--drivers/power/ab8500_fg.c1938
-rw-r--r--drivers/power/power_supply_core.c29
-rw-r--r--drivers/regulator/Kconfig23
-rw-r--r--drivers/regulator/Makefile3
-rw-r--r--drivers/regulator/ab3100.c5
-rw-r--r--drivers/regulator/ab5500.c508
-rw-r--r--drivers/regulator/ab8500-debug.c1834
-rw-r--r--drivers/regulator/ab8500.c1172
-rw-r--r--drivers/regulator/core.c109
-rw-r--r--drivers/rtc/Kconfig25
-rw-r--r--drivers/rtc/Makefile3
-rw-r--r--drivers/rtc/alarm-dev.c286
-rw-r--r--drivers/rtc/alarm.c590
-rw-r--r--drivers/rtc/class.c13
-rw-r--r--drivers/rtc/rtc-ab.c483
-rw-r--r--drivers/rtc/rtc-ab3100.c2
-rw-r--r--drivers/rtc/rtc-ab8500.c118
-rw-r--r--drivers/rtc/rtc-pl031.c4
-rw-r--r--drivers/serial/8250.c37
-rw-r--r--drivers/serial/Kconfig9
-rw-r--r--drivers/serial/amba-pl010.c2
-rw-r--r--drivers/serial/amba-pl011.c571
-rw-r--r--drivers/serial/serial_core.c17
-rw-r--r--drivers/spi/Kconfig18
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/amba-pl022.c823
-rw-r--r--drivers/spi/stm_msp.c1929
-rw-r--r--drivers/staging/Kconfig27
-rw-r--r--drivers/staging/Makefile11
-rw-r--r--drivers/staging/android/Kconfig103
-rw-r--r--drivers/staging/android/Makefile7
-rw-r--r--drivers/staging/android/binder.c3601
-rw-r--r--drivers/staging/android/binder.h330
-rw-r--r--drivers/staging/android/logger.c616
-rw-r--r--drivers/staging/android/logger.h49
-rw-r--r--drivers/staging/android/lowmemorykiller.c213
-rw-r--r--drivers/staging/android/ram_console.c418
-rw-r--r--drivers/staging/android/ste_timed_vibra.c318
-rw-r--r--drivers/staging/android/timed_gpio.c176
-rw-r--r--drivers/staging/android/timed_gpio.h33
-rw-r--r--drivers/staging/android/timed_output.c123
-rw-r--r--drivers/staging/android/timed_output.h37
-rw-r--r--drivers/staging/camera_flash/Makefile5
-rw-r--r--drivers/staging/camera_flash/flash_chips/adp1653.c594
-rwxr-xr-xdrivers/staging/camera_flash/flash_chips/adp1653.h82
-rw-r--r--drivers/staging/camera_flash/flash_common.c459
-rwxr-xr-xdrivers/staging/camera_flash/flash_common.h56
-rw-r--r--drivers/staging/mmio/Makefile1
-rw-r--r--drivers/staging/mmio/st_mmio.c1246
-rw-r--r--drivers/staging/nmf-cm/Make.config8
-rw-r--r--drivers/staging/nmf-cm/Makefile98
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/cm_engine.h48
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/communication_engine.h73
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/component_engine.h403
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/configuration_engine.h28
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/control/configuration_engine.h193
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/control/control_engine.h26
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/control/irq_engine.h108
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/domain_engine.h108
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/executive_engine_mgt_engine.h28
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/memory_engine.h93
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/migration_engine.h16
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/perfmeter_engine.h41
-rw-r--r--drivers/staging/nmf-cm/cm/engine/api/repository_mgt_engine.h86
-rw-r--r--drivers/staging/nmf-cm/cm/engine/communication/fifo/inc/nmf_fifo_arm.h56
-rw-r--r--drivers/staging/nmf-cm/cm/engine/communication/fifo/src/nmf_fifo_arm.c241
-rw-r--r--drivers/staging/nmf-cm/cm/engine/communication/inc/communication.h44
-rw-r--r--drivers/staging/nmf-cm/cm/engine/communication/inc/communication_type.h61
-rw-r--r--drivers/staging/nmf-cm/cm/engine/communication/src/communication.c326
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/inc/bind.h443
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/inc/component_type.h65
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/inc/description.h109
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/inc/dspevent.h28
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/inc/initializer.h29
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/inc/instance.h220
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/inc/introspection.h109
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/inc/nmfheaderabi.h154
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/inc/template.h89
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/src/binder.c1301
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/src/binder_check.c205
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/src/component_wrapper.c1297
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/src/dspevent.c76
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/src/initializer.c329
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/src/instantiater.c818
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/src/introspection.c325
-rw-r--r--drivers/staging/nmf-cm/cm/engine/component/src/loader.c338
-rw-r--r--drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration.h40
-rw-r--r--drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_status.h45
-rw-r--r--drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_type.h82
-rw-r--r--drivers/staging/nmf-cm/cm/engine/configuration/src/configuration.c213
-rw-r--r--drivers/staging/nmf-cm/cm/engine/configuration/src/configuration_wrapper.c301
-rw-r--r--drivers/staging/nmf-cm/cm/engine/dsp/inc/dsp.h454
-rw-r--r--drivers/staging/nmf-cm/cm/engine/dsp/inc/semaphores_dsp.h22
-rw-r--r--drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h959
-rw-r--r--drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_macros.h86
-rw-r--r--drivers/staging/nmf-cm/cm/engine/dsp/src/dsp.c1074
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/inc/bfd.h123
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/inc/common.h125
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/inc/elfabi.h539
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/inc/elfapi.h123
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/inc/memory.h77
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp-loadmap.h49
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp.h31
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/inc/mpcal.h20
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/inc/reloc.h35
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/src/elf64.c47
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/src/elfload.c742
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/src/elfmmdsp.c575
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/src/elfrelocate.c79
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/src/elfxx.c591
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/src/mmdsp-debug.c287
-rw-r--r--drivers/staging/nmf-cm/cm/engine/elf/src/mpcal.c6
-rw-r--r--drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h42
-rw-r--r--drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/src/executive_engine_mgt.c349
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/inc/chunk_mgr.h41
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/inc/domain.h154
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/inc/domain_type.h60
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/inc/memory.h19
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/inc/memory_type.h152
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/inc/migration.h32
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator.h262
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator_utils.h33
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/src/chunk_mgr.c94
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/src/domain.c590
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/src/domain_wrapper.c95
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/src/memory_wrapper.c221
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/src/migration.c391
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator.c680
-rw-r--r--drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator_utils.c270
-rw-r--r--drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h471
-rw-r--r--drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/src/os_adaptation_layer.c39
-rw-r--r--drivers/staging/nmf-cm/cm/engine/perfmeter/inc/mpcload.h21
-rw-r--r--drivers/staging/nmf-cm/cm/engine/perfmeter/inc/perfmeter_type.h34
-rw-r--r--drivers/staging/nmf-cm/cm/engine/perfmeter/src/mpcload.c116
-rw-r--r--drivers/staging/nmf-cm/cm/engine/power_mgt/inc/power.h59
-rw-r--r--drivers/staging/nmf-cm/cm/engine/power_mgt/src/cmpower.c226
-rw-r--r--drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_mgt.h55
-rw-r--r--drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_type.h28
-rw-r--r--drivers/staging/nmf-cm/cm/engine/repository_mgt/src/repository_mgt.c311
-rw-r--r--drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h32
-rw-r--r--drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/src/hw_semaphores.c171
-rw-r--r--drivers/staging/nmf-cm/cm/engine/semaphores/inc/semaphores.h25
-rw-r--r--drivers/staging/nmf-cm/cm/engine/semaphores/src/semaphores.c94
-rw-r--r--drivers/staging/nmf-cm/cm/engine/trace/inc/trace.h48
-rw-r--r--drivers/staging/nmf-cm/cm/engine/trace/inc/xtitrace.h42
-rw-r--r--drivers/staging/nmf-cm/cm/engine/trace/src/panic.c222
-rw-r--r--drivers/staging/nmf-cm/cm/engine/trace/src/trace.c220
-rw-r--r--drivers/staging/nmf-cm/cm/engine/utils/inc/convert.h21
-rw-r--r--drivers/staging/nmf-cm/cm/engine/utils/inc/mem.h19
-rw-r--r--drivers/staging/nmf-cm/cm/engine/utils/inc/string.h33
-rw-r--r--drivers/staging/nmf-cm/cm/engine/utils/inc/swap.h23
-rw-r--r--drivers/staging/nmf-cm/cm/engine/utils/inc/table.h59
-rw-r--r--drivers/staging/nmf-cm/cm/engine/utils/src/convert.c20
-rw-r--r--drivers/staging/nmf-cm/cm/engine/utils/src/mem.c27
-rw-r--r--drivers/staging/nmf-cm/cm/engine/utils/src/string.c229
-rw-r--r--drivers/staging/nmf-cm/cm/engine/utils/src/swap.c156
-rw-r--r--drivers/staging/nmf-cm/cm/engine/utils/src/table.c155
-rw-r--r--drivers/staging/nmf-cm/cm/inc/cm.h22
-rw-r--r--drivers/staging/nmf-cm/cm/inc/cm_def.h50
-rw-r--r--drivers/staging/nmf-cm/cm/inc/cm_macros.h148
-rw-r--r--drivers/staging/nmf-cm/cm/inc/cm_type.h147
-rw-r--r--drivers/staging/nmf-cm/cm/os/api/channel_os.h103
-rw-r--r--drivers/staging/nmf-cm/cm/os/api/cm_os.h48
-rw-r--r--drivers/staging/nmf-cm/cm/os/api/communication_os.h88
-rw-r--r--drivers/staging/nmf-cm/cm/os/api/component_os.h405
-rw-r--r--drivers/staging/nmf-cm/cm/os/api/configuration_os.h31
-rw-r--r--drivers/staging/nmf-cm/cm/os/api/domain_os.h85
-rw-r--r--drivers/staging/nmf-cm/cm/os/api/executive_engine_mgt_os.h30
-rw-r--r--drivers/staging/nmf-cm/cm/os/api/memory_os.h105
-rw-r--r--drivers/staging/nmf-cm/cm/os/api/migration_os.h18
-rw-r--r--drivers/staging/nmf-cm/cm/os/api/perfmeter_os.h30
-rw-r--r--drivers/staging/nmf-cm/cm/os/api/repository_mgt_os.h89
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/api/cm_proxy.h51
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/api/communication_proxy.h240
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/api/component_proxy.h447
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/api/configuration_proxy.h151
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/api/domain_proxy.h92
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/api/introspection_proxy.h440
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/api/memory_proxy.h151
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/api/migration_proxy.h18
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/api/perfmeter_proxy.h36
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/api/private/stub-requiredapi.h229
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/api/user_proxy.h227
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/common/communication/inc/communication_internal.h169
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/common/communication/inc/hoststubs_mgt.h19
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/common/component/inc/component_info.h47
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/common/component/inc/interface_type.h81
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/common/repository/inc/repository.h34
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/common/utils/inc/list.h66
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/common/wrapper/inc/stub.h44
-rw-r--r--drivers/staging/nmf-cm/cm/proxy/inc/osal.h259
-rw-r--r--drivers/staging/nmf-cm/cm_service.c94
-rw-r--r--drivers/staging/nmf-cm/cm_service.h22
-rw-r--r--drivers/staging/nmf-cm/cm_syscall.c1341
-rw-r--r--drivers/staging/nmf-cm/cmioctl.h571
-rw-r--r--drivers/staging/nmf-cm/cmld.c1328
-rw-r--r--drivers/staging/nmf-cm/cmld.h133
-rw-r--r--drivers/staging/nmf-cm/configuration.c115
-rw-r--r--drivers/staging/nmf-cm/configuration.h74
-rw-r--r--drivers/staging/nmf-cm/cpp.hpp499
-rw-r--r--drivers/staging/nmf-cm/ee/api/ee.h197
-rw-r--r--drivers/staging/nmf-cm/ee/api/ee_type.h32
-rw-r--r--drivers/staging/nmf-cm/ee/api/list.idt18
-rw-r--r--drivers/staging/nmf-cm/ee/api/outnotify.h14
-rw-r--r--drivers/staging/nmf-cm/ee/api/panic.idt73
-rw-r--r--drivers/staging/nmf-cm/ee/api/priority.idt24
-rw-r--r--drivers/staging/nmf-cm/inc/nmf-def.h42
-rw-r--r--drivers/staging/nmf-cm/inc/nmf-limits.h107
-rw-r--r--drivers/staging/nmf-cm/inc/nmf-tracedescription.h324
-rw-r--r--drivers/staging/nmf-cm/inc/nmf_type.idt64
-rw-r--r--drivers/staging/nmf-cm/inc/type.h36
-rw-r--r--drivers/staging/nmf-cm/inc/typedef.h193
-rw-r--r--drivers/staging/nmf-cm/nmf/inc/channel_type.h41
-rw-r--r--drivers/staging/nmf-cm/nmf/inc/component_type.h27
-rw-r--r--drivers/staging/nmf-cm/nmf/inc/service_type.h94
-rw-r--r--drivers/staging/nmf-cm/osal-kernel.c1148
-rw-r--r--drivers/staging/nmf-cm/osal-kernel.h144
-rw-r--r--drivers/staging/nmf-cm/share/communication/inc/communication_fifo.h19
-rw-r--r--drivers/staging/nmf-cm/share/communication/inc/initializer.h37
-rw-r--r--drivers/staging/nmf-cm/share/communication/inc/nmf_fifo_desc.h36
-rw-r--r--drivers/staging/nmf-cm/share/communication/inc/nmf_service.h16
-rw-r--r--drivers/staging/nmf-cm/share/inc/macros.h214
-rw-r--r--drivers/staging/nmf-cm/share/inc/nmf.h47
-rw-r--r--drivers/staging/nmf-cm/share/inc/nomadik_mapping.h98
-rw-r--r--drivers/staging/nmf-cm/share/semaphores/inc/hwsem_hwp.h85
-rw-r--r--drivers/staging/nmf-cm/share/semaphores/inc/semaphores.h43
-rw-r--r--drivers/staging/ste_rmi4/Kconfig9
-rw-r--r--drivers/staging/ste_rmi4/Makefile4
-rw-r--r--drivers/staging/ste_rmi4/TODO7
-rw-r--r--drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c1367
-rw-r--r--drivers/switch/Kconfig15
-rw-r--r--drivers/switch/Makefile4
-rw-r--r--drivers/switch/switch_class.c174
-rw-r--r--drivers/switch/switch_gpio.c172
-rw-r--r--drivers/tee/Kconfig13
-rw-r--r--drivers/tee/Makefile8
-rw-r--r--drivers/tee/tee_driver.c643
-rw-r--r--drivers/tee/tee_service.c17
-rw-r--r--drivers/usb/core/driver.c90
-rw-r--r--drivers/usb/core/hcd.c5
-rw-r--r--drivers/usb/core/hub.c246
-rw-r--r--drivers/usb/core/notify.c6
-rw-r--r--drivers/usb/core/otg_whitelist.h58
-rw-r--r--drivers/usb/core/usb.h14
-rw-r--r--drivers/usb/gadget/Kconfig103
-rw-r--r--drivers/usb/gadget/Makefile7
-rw-r--r--drivers/usb/gadget/android.c478
-rw-r--r--drivers/usb/gadget/composite.c258
-rw-r--r--drivers/usb/gadget/epautoconf.c25
-rw-r--r--drivers/usb/gadget/f_acm.c124
-rw-r--r--drivers/usb/gadget/f_adb.c670
-rw-r--r--drivers/usb/gadget/f_ecm.c182
-rw-r--r--drivers/usb/gadget/f_mass_storage.c157
-rw-r--r--drivers/usb/gadget/f_mtp.c1273
-rw-r--r--drivers/usb/gadget/f_rndis.c219
-rw-r--r--drivers/usb/gadget/file_storage.c39
-rw-r--r--drivers/usb/gadget/multi.c4
-rw-r--r--drivers/usb/gadget/storage_common.c12
-rw-r--r--drivers/usb/gadget/u_ether.c1
-rw-r--r--drivers/usb/gadget/u_ether.h2
-rw-r--r--drivers/usb/gadget/zero.c5
-rw-r--r--drivers/usb/musb/Kconfig12
-rw-r--r--drivers/usb/musb/Makefile5
-rw-r--r--drivers/usb/musb/musb_core.c94
-rw-r--r--drivers/usb/musb/musb_core.h28
-rw-r--r--drivers/usb/musb/musb_dma.h12
-rw-r--r--drivers/usb/musb/musb_gadget.c262
-rw-r--r--drivers/usb/musb/musb_gadget.h7
-rw-r--r--drivers/usb/musb/musb_host.c125
-rw-r--r--drivers/usb/musb/musb_virthub.c5
-rw-r--r--drivers/usb/musb/ste_config.h52
-rw-r--r--drivers/usb/musb/stm_musb.c638
-rw-r--r--drivers/usb/musb/stm_musb_dma.c722
-rw-r--r--drivers/usb/musb/stm_musb_dma.h59
-rw-r--r--drivers/video/Kconfig5
-rw-r--r--drivers/video/Makefile3
-rw-r--r--drivers/video/av8100/Kconfig48
-rw-r--r--drivers/video/av8100/Makefile10
-rw-r--r--drivers/video/av8100/av8100.c3757
-rw-r--r--drivers/video/av8100/av8100_fw.h1165
-rw-r--r--drivers/video/av8100/av8100_regs.h346
-rw-r--r--drivers/video/av8100/hdmi.c2301
-rw-r--r--drivers/video/av8100/hdmi_loc.h67
-rw-r--r--drivers/video/b2r2/Kconfig134
-rw-r--r--drivers/video/b2r2/Makefile15
-rw-r--r--drivers/video/b2r2/b2r2_blt_main.c3283
-rw-r--r--drivers/video/b2r2/b2r2_core.c2666
-rw-r--r--drivers/video/b2r2/b2r2_core.h222
-rw-r--r--drivers/video/b2r2/b2r2_debug.c343
-rw-r--r--drivers/video/b2r2/b2r2_debug.h104
-rw-r--r--drivers/video/b2r2/b2r2_filters.c372
-rw-r--r--drivers/video/b2r2/b2r2_filters.h71
-rw-r--r--drivers/video/b2r2/b2r2_generic.c3165
-rw-r--r--drivers/video/b2r2/b2r2_generic.h51
-rw-r--r--drivers/video/b2r2/b2r2_global.h119
-rw-r--r--drivers/video/b2r2/b2r2_hw.h637
-rw-r--r--drivers/video/b2r2/b2r2_input_validation.c417
-rw-r--r--drivers/video/b2r2/b2r2_input_validation.h28
-rw-r--r--drivers/video/b2r2/b2r2_internal.h360
-rw-r--r--drivers/video/b2r2/b2r2_kernel_if.c37
-rw-r--r--drivers/video/b2r2/b2r2_mem_alloc.c726
-rw-r--r--drivers/video/b2r2/b2r2_mem_alloc.h140
-rw-r--r--drivers/video/b2r2/b2r2_node_gen.c80
-rw-r--r--drivers/video/b2r2/b2r2_node_split.c3357
-rw-r--r--drivers/video/b2r2/b2r2_node_split.h121
-rw-r--r--drivers/video/b2r2/b2r2_profiler/Makefile3
-rw-r--r--drivers/video/b2r2/b2r2_profiler/b2r2_profiler.c258
-rw-r--r--drivers/video/b2r2/b2r2_profiler_api.h66
-rw-r--r--drivers/video/b2r2/b2r2_profiler_socket.c105
-rw-r--r--drivers/video/b2r2/b2r2_profiler_socket.h22
-rw-r--r--drivers/video/b2r2/b2r2_structures.h226
-rw-r--r--drivers/video/b2r2/b2r2_timing.c22
-rw-r--r--drivers/video/b2r2/b2r2_timing.h22
-rw-r--r--drivers/video/b2r2/b2r2_utils.c379
-rw-r--r--drivers/video/b2r2/b2r2_utils.h51
-rw-r--r--drivers/video/backlight/pwm_bl.c7
-rw-r--r--drivers/video/mcde/Kconfig83
-rw-r--r--drivers/video/mcde/Makefile16
-rw-r--r--drivers/video/mcde/display-ab8500.c490
-rw-r--r--drivers/video/mcde/display-av8100.c1492
-rw-r--r--drivers/video/mcde/display-generic_dsi.c300
-rw-r--r--drivers/video/mcde/display-sony_sy35560_dsi.c239
-rw-r--r--drivers/video/mcde/display-vuib500-dpi.c216
-rw-r--r--drivers/video/mcde/dsilink_regs.h2032
-rw-r--r--drivers/video/mcde/mcde_bus.c261
-rw-r--r--drivers/video/mcde/mcde_display.c430
-rw-r--r--drivers/video/mcde/mcde_dss.c385
-rw-r--r--drivers/video/mcde/mcde_fb.c806
-rw-r--r--drivers/video/mcde/mcde_hw.c3389
-rw-r--r--drivers/video/mcde/mcde_mod.c69
-rw-r--r--drivers/video/mcde/mcde_regs.h5315
-rw-r--r--drivers/w1/w1.c8
-rw-r--r--drivers/watchdog/mpcore_wdt.c101
690 files changed, 258007 insertions, 3028 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index a2b902f4d43..88eedd536bd 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -50,6 +50,8 @@ source "drivers/i2c/Kconfig"
source "drivers/spi/Kconfig"
+source "drivers/hsi/Kconfig"
+
source "drivers/pps/Kconfig"
source "drivers/gpio/Kconfig"
@@ -88,6 +90,8 @@ source "drivers/memstick/Kconfig"
source "drivers/leds/Kconfig"
+source "drivers/switch/Kconfig"
+
source "drivers/accessibility/Kconfig"
source "drivers/infiniband/Kconfig"
@@ -111,4 +115,6 @@ source "drivers/xen/Kconfig"
source "drivers/staging/Kconfig"
source "drivers/platform/Kconfig"
+
+source "drivers/tee/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 91874e04855..443bc00957f 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_SCSI) += scsi/
obj-$(CONFIG_ATA) += ata/
obj-$(CONFIG_MTD) += mtd/
obj-$(CONFIG_SPI) += spi/
+obj-y += hsi/
obj-y += net/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
@@ -95,6 +96,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle/
obj-$(CONFIG_MMC) += mmc/
obj-$(CONFIG_MEMSTICK) += memstick/
obj-$(CONFIG_NEW_LEDS) += leds/
+obj-$(CONFIG_SWITCH) += switch/
obj-$(CONFIG_INFINIBAND) += infiniband/
obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/
@@ -113,3 +115,5 @@ obj-$(CONFIG_VLYNQ) += vlynq/
obj-$(CONFIG_STAGING) += staging/
obj-y += platform/
obj-y += ieee802154/
+obj-$(CONFIG_TEE_SUPPORT) += tee/
+
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index f60b2b6a093..e337abc9506 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -13,12 +13,13 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include <linux/amba/bus.h>
#include <asm/irq.h>
#include <asm/sizes.h>
-#define to_amba_device(d) container_of(d, struct amba_device, dev)
#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
static struct amba_id *
@@ -28,6 +29,12 @@ amba_lookup(struct amba_id *table, struct amba_device *dev)
while (table->mask) {
ret = (dev->periphid & table->mask) == table->id;
+ if (ret && (table->name || dev->name)) {
+ if (table->name && dev->name)
+ ret = strcmp(dev->name, table->name) == 0;
+ else
+ ret = 0;
+ }
if (ret)
break;
table++;
@@ -57,26 +64,6 @@ static int amba_uevent(struct device *dev, struct kobj_uevent_env *env)
#define amba_uevent NULL
#endif
-static int amba_suspend(struct device *dev, pm_message_t state)
-{
- struct amba_driver *drv = to_amba_driver(dev->driver);
- int ret = 0;
-
- if (dev->driver && drv->suspend)
- ret = drv->suspend(to_amba_device(dev), state);
- return ret;
-}
-
-static int amba_resume(struct device *dev)
-{
- struct amba_driver *drv = to_amba_driver(dev->driver);
- int ret = 0;
-
- if (dev->driver && drv->resume)
- ret = drv->resume(to_amba_device(dev));
- return ret;
-}
-
#define amba_attr_func(name,fmt,arg...) \
static ssize_t name##_show(struct device *_dev, \
struct device_attribute *attr, char *buf) \
@@ -102,17 +89,330 @@ static struct device_attribute amba_dev_attrs[] = {
__ATTR_NULL,
};
+#ifdef CONFIG_PM_SLEEP
+
+static int amba_legacy_suspend(struct device *dev, pm_message_t mesg)
+{
+ struct amba_driver *adrv = to_amba_driver(dev->driver);
+ struct amba_device *adev = to_amba_device(dev);
+ int ret = 0;
+
+ if (dev->driver && adrv->suspend)
+ ret = adrv->suspend(adev, mesg);
+
+ return ret;
+}
+
+static int amba_legacy_resume(struct device *dev)
+{
+ struct amba_driver *adrv = to_amba_driver(dev->driver);
+ struct amba_device *adev = to_amba_device(dev);
+ int ret = 0;
+
+ if (dev->driver && adrv->resume)
+ ret = adrv->resume(adev);
+
+ return ret;
+}
+
+static int amba_pm_prepare(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm && drv->pm->prepare)
+ ret = drv->pm->prepare(dev);
+
+ return ret;
+}
+
+static void amba_pm_complete(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+
+ if (drv && drv->pm && drv->pm->complete)
+ drv->pm->complete(dev);
+}
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define amba_pm_prepare NULL
+#define amba_pm_complete NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_SUSPEND
+
+static int amba_pm_suspend(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->suspend)
+ ret = drv->pm->suspend(dev);
+ } else {
+ ret = amba_legacy_suspend(dev, PMSG_SUSPEND);
+ }
+
+ return ret;
+}
+
+static int amba_pm_suspend_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->suspend_noirq)
+ ret = drv->pm->suspend_noirq(dev);
+ }
+
+ return ret;
+}
+
+static int amba_pm_resume(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->resume)
+ ret = drv->pm->resume(dev);
+ } else {
+ ret = amba_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int amba_pm_resume_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->resume_noirq)
+ ret = drv->pm->resume_noirq(dev);
+ }
+
+ return ret;
+}
+
+#else /* !CONFIG_SUSPEND */
+
+#define amba_pm_suspend NULL
+#define amba_pm_resume NULL
+#define amba_pm_suspend_noirq NULL
+#define amba_pm_resume_noirq NULL
+
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_HIBERNATION
+
+static int amba_pm_freeze(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->freeze)
+ ret = drv->pm->freeze(dev);
+ } else {
+ ret = amba_legacy_suspend(dev, PMSG_FREEZE);
+ }
+
+ return ret;
+}
+
+static int amba_pm_freeze_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->freeze_noirq)
+ ret = drv->pm->freeze_noirq(dev);
+ }
+
+ return ret;
+}
+
+static int amba_pm_thaw(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->thaw)
+ ret = drv->pm->thaw(dev);
+ } else {
+ ret = amba_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int amba_pm_thaw_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->thaw_noirq)
+ ret = drv->pm->thaw_noirq(dev);
+ }
+
+ return ret;
+}
+
+static int amba_pm_poweroff(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->poweroff)
+ ret = drv->pm->poweroff(dev);
+ } else {
+ ret = amba_legacy_suspend(dev, PMSG_HIBERNATE);
+ }
+
+ return ret;
+}
+
+static int amba_pm_poweroff_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->poweroff_noirq)
+ ret = drv->pm->poweroff_noirq(dev);
+ }
+
+ return ret;
+}
+
+static int amba_pm_restore(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->restore)
+ ret = drv->pm->restore(dev);
+ } else {
+ ret = amba_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int amba_pm_restore_noirq(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->restore_noirq)
+ ret = drv->pm->restore_noirq(dev);
+ }
+
+ return ret;
+}
+
+#else /* !CONFIG_HIBERNATION */
+
+#define amba_pm_freeze NULL
+#define amba_pm_thaw NULL
+#define amba_pm_poweroff NULL
+#define amba_pm_restore NULL
+#define amba_pm_freeze_noirq NULL
+#define amba_pm_thaw_noirq NULL
+#define amba_pm_poweroff_noirq NULL
+#define amba_pm_restore_noirq NULL
+
+#endif /* !CONFIG_HIBERNATION */
+
+#ifdef CONFIG_PM
+
+static const struct dev_pm_ops amba_pm = {
+ .prepare = amba_pm_prepare,
+ .complete = amba_pm_complete,
+ .suspend = amba_pm_suspend,
+ .resume = amba_pm_resume,
+ .freeze = amba_pm_freeze,
+ .thaw = amba_pm_thaw,
+ .poweroff = amba_pm_poweroff,
+ .restore = amba_pm_restore,
+ .suspend_noirq = amba_pm_suspend_noirq,
+ .resume_noirq = amba_pm_resume_noirq,
+ .freeze_noirq = amba_pm_freeze_noirq,
+ .thaw_noirq = amba_pm_thaw_noirq,
+ .poweroff_noirq = amba_pm_poweroff_noirq,
+ .restore_noirq = amba_pm_restore_noirq,
+ SET_RUNTIME_PM_OPS(
+ pm_generic_runtime_suspend,
+ pm_generic_runtime_resume,
+ pm_generic_runtime_idle
+ )
+};
+
+#define AMBA_PM (&amba_pm)
+
+#else /* !CONFIG_PM */
+
+#define AMBA_PM NULL
+
+#endif /* !CONFIG_PM */
+
/*
* Primecells are part of the Advanced Microcontroller Bus Architecture,
* so we call the bus "amba".
*/
-static struct bus_type amba_bustype = {
+struct bus_type amba_bustype = {
.name = "amba",
.dev_attrs = amba_dev_attrs,
.match = amba_match,
.uevent = amba_uevent,
- .suspend = amba_suspend,
- .resume = amba_resume,
+ .pm = AMBA_PM,
};
static int __init amba_init(void)
@@ -252,7 +552,7 @@ int amba_device_register(struct amba_device *dev, struct resource *parent)
iounmap(tmp);
- if (cid == 0xb105f00d)
+ if (cid == AMBA_CID)
dev->periphid = pid;
if (!dev->periphid) {
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index ef38aff737e..63983b5b2d9 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -169,4 +169,7 @@ config SYS_HYPERVISOR
bool
default n
+config SYS_SOC
+ bool
+
endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index c12c7f2f2a6..263f700fec8 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -18,6 +18,7 @@ ifeq ($(CONFIG_SYSFS),y)
obj-$(CONFIG_MODULES) += module.o
endif
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
+obj-$(CONFIG_SYS_SOC) += soc.o
ifeq ($(CONFIG_DEBUG_DRIVER),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 503c2620bbc..da57ee9d63f 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -51,6 +51,10 @@ static int driver_sysfs_add(struct device *dev)
{
int ret;
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
+ BUS_NOTIFY_BIND_DRIVER, dev);
+
ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
kobject_name(&dev->kobj));
if (ret == 0) {
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 4d99c8bdfed..eb1931c2bff 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -1017,6 +1017,41 @@ struct bus_type platform_bus_type = {
};
EXPORT_SYMBOL_GPL(platform_bus_type);
+/**
+ * platform_bus_get_pm_ops() - return pointer to busses dev_pm_ops
+ *
+ * This function can be used by platform code to get the current
+ * set of dev_pm_ops functions used by the platform_bus_type.
+ */
+const struct dev_pm_ops * __init platform_bus_get_pm_ops(void)
+{
+ return platform_bus_type.pm;
+}
+
+/**
+ * platform_bus_set_pm_ops() - update dev_pm_ops for the platform_bus_type
+ *
+ * @pm: pointer to new dev_pm_ops struct to be used for platform_bus_type
+ *
+ * Platform code can override the dev_pm_ops methods of
+ * platform_bus_type by using this function. It is expected that
+ * platform code will first do a platform_bus_get_pm_ops(), then
+ * kmemdup it, then customize selected methods and pass a pointer to
+ * the new struct dev_pm_ops to this function.
+ *
+ * Since platform-specific code is customizing methods for *all*
+ * devices (not just platform-specific devices) it is expected that
+ * any custom overrides of these functions will keep existing behavior
+ * and simply extend it. For example, any customization of the
+ * runtime PM methods should continue to call the pm_generic_*
+ * functions as the default ones do in addition to the
+ * platform-specific behavior.
+ */
+void __init platform_bus_set_pm_ops(const struct dev_pm_ops *pm)
+{
+ platform_bus_type.pm = pm;
+}
+
int __init platform_bus_init(void)
{
int error;
diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c
index 4b29d498125..3d2c3500069 100644
--- a/drivers/base/power/generic_ops.c
+++ b/drivers/base/power/generic_ops.c
@@ -39,14 +39,14 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_idle);
*
* If PM operations are defined for the @dev's driver and they include
* ->runtime_suspend(), execute it and return its error code. Otherwise,
- * return -EINVAL.
+ * return 0.
*/
int pm_generic_runtime_suspend(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int ret;
- ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : -EINVAL;
+ ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0;
return ret;
}
@@ -58,14 +58,14 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend);
*
* If PM operations are defined for the @dev's driver and they include
* ->runtime_resume(), execute it and return its error code. Otherwise,
- * return -EINVAL.
+ * return 0.
*/
int pm_generic_runtime_resume(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int ret;
- ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : -EINVAL;
+ ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0;
return ret;
}
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index cb1cedc69c9..a5a07131ce9 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/async.h>
+#include <linux/timer.h>
#include "../base.h"
#include "power.h"
@@ -45,6 +46,9 @@ LIST_HEAD(dpm_list);
static DEFINE_MUTEX(dpm_list_mtx);
static pm_message_t pm_transition;
+static void dpm_drv_timeout(unsigned long data);
+static DEFINE_TIMER(dpm_drv_wd, dpm_drv_timeout, 0, 0);
+
/*
* Set once the preparation of devices for a PM transition has started, reset
* before starting to resume devices. Protected by dpm_list_mtx.
@@ -437,6 +441,11 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)
TRACE_DEVICE(dev);
TRACE_RESUME(0);
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "EARLY power domain ");
+ pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+ }
+
if (dev->bus && dev->bus->pm) {
pm_dev_dbg(dev, state, "EARLY ");
error = pm_noirq_op(dev, dev->bus->pm, state);
@@ -523,11 +532,17 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
TRACE_DEVICE(dev);
TRACE_RESUME(0);
- dpm_wait(dev->parent, async);
+ if (dev->parent && dev->parent->power.status >= DPM_OFF)
+ dpm_wait(dev->parent, async);
device_lock(dev);
dev->power.status = DPM_RESUMING;
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "power domain ");
+ pm_op(dev, &dev->pwr_domain->ops, state);
+ }
+
if (dev->bus) {
if (dev->bus->pm) {
pm_dev_dbg(dev, state, "");
@@ -584,6 +599,45 @@ static bool is_async(struct device *dev)
}
/**
+ * dpm_drv_timeout - Driver suspend / resume watchdog handler
+ * @data: struct device which timed out
+ *
+ * Called when a driver has timed out suspending or resuming.
+ * There's not much we can do here to recover so
+ * BUG() out for a crash-dump
+ *
+ */
+static void dpm_drv_timeout(unsigned long data)
+{
+ struct device *dev = (struct device *) data;
+
+ printk(KERN_EMERG "**** DPM device timeout: %s (%s)\n", dev_name(dev),
+ (dev->driver ? dev->driver->name : "no driver"));
+ BUG();
+}
+
+/**
+ * dpm_drv_wdset - Sets up driver suspend/resume watchdog timer.
+ * @dev: struct device which we're guarding.
+ *
+ */
+static void dpm_drv_wdset(struct device *dev)
+{
+ dpm_drv_wd.data = (unsigned long) dev;
+ mod_timer(&dpm_drv_wd, jiffies + (HZ * 3));
+}
+
+/**
+ * dpm_drv_wdclr - clears driver suspend/resume watchdog timer.
+ * @dev: struct device which we're no longer guarding.
+ *
+ */
+static void dpm_drv_wdclr(struct device *dev)
+{
+ del_timer_sync(&dpm_drv_wd);
+}
+
+/**
* dpm_resume - Execute "resume" callbacks for non-sysdev devices.
* @state: PM transition of the system being carried out.
*
@@ -619,7 +673,9 @@ static void dpm_resume(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
+ dpm_drv_wdset(dev);
error = device_resume(dev, state, false);
+ dpm_drv_wdclr(dev);
mutex_lock(&dpm_list_mtx);
if (error)
@@ -647,6 +703,11 @@ static void device_complete(struct device *dev, pm_message_t state)
{
device_lock(dev);
+ if (dev->pwr_domain && dev->pwr_domain->ops.complete) {
+ pm_dev_dbg(dev, state, "completing power domain ");
+ dev->pwr_domain->ops.complete(dev);
+ }
+
if (dev->class && dev->class->pm && dev->class->pm->complete) {
pm_dev_dbg(dev, state, "completing class ");
dev->class->pm->complete(dev);
@@ -768,6 +829,13 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
if (dev->bus && dev->bus->pm) {
pm_dev_dbg(dev, state, "LATE ");
error = pm_noirq_op(dev, dev->bus->pm, state);
+ if (error)
+ goto End;
+ }
+
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "LATE power domain ");
+ pm_noirq_op(dev, &dev->pwr_domain->ops, state);
}
End:
@@ -875,6 +943,13 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
pm_dev_dbg(dev, state, "legacy ");
error = legacy_suspend(dev, state, dev->bus->suspend);
}
+ if (error)
+ goto End;
+ }
+
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "power domain ");
+ pm_op(dev, &dev->pwr_domain->ops, state);
}
if (!error)
@@ -934,7 +1009,9 @@ static int dpm_suspend(pm_message_t state)
get_device(dev);
mutex_unlock(&dpm_list_mtx);
+ dpm_drv_wdset(dev);
error = device_suspend(dev);
+ dpm_drv_wdclr(dev);
mutex_lock(&dpm_list_mtx);
if (error) {
@@ -992,7 +1069,15 @@ static int device_prepare(struct device *dev, pm_message_t state)
pm_dev_dbg(dev, state, "preparing class ");
error = dev->class->pm->prepare(dev);
suspend_report_result(dev->class->pm->prepare, error);
+ if (error)
+ goto End;
}
+
+ if (dev->pwr_domain && dev->pwr_domain->ops.prepare) {
+ pm_dev_dbg(dev, state, "preparing power domain ");
+ dev->pwr_domain->ops.prepare(dev);
+ }
+
End:
device_unlock(dev);
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index c0bd03c83b9..0311730f4ee 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -59,6 +59,7 @@ static inline void device_pm_move_last(struct device *dev) {}
extern int dpm_sysfs_add(struct device *);
extern void dpm_sysfs_remove(struct device *);
+extern void rpm_sysfs_remove(struct device *);
#else /* CONFIG_PM */
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index b0ec0e9f27e..a0baafa8ce9 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -2,17 +2,55 @@
* drivers/base/power/runtime.c - Helper functions for device run-time PM
*
* Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ * Copyright (C) 2010 Alan Stern <stern@rowland.harvard.edu>
*
* This file is released under the GPLv2.
*/
#include <linux/sched.h>
#include <linux/pm_runtime.h>
-#include <linux/jiffies.h>
+#include "power.h"
-static int __pm_runtime_resume(struct device *dev, bool from_wq);
-static int __pm_request_idle(struct device *dev);
-static int __pm_request_resume(struct device *dev);
+static int rpm_resume(struct device *dev, int rpmflags);
+static int rpm_suspend(struct device *dev, int rpmflags);
+
+/**
+ * update_pm_runtime_accounting - Update the time accounting of power states
+ * @dev: Device to update the accounting for
+ *
+ * In order to be able to have time accounting of the various power states
+ * (as used by programs such as PowerTOP to show the effectiveness of runtime
+ * PM), we need to track the time spent in each state.
+ * update_pm_runtime_accounting must be called each time before the
+ * runtime_status field is updated, to account the time in the old state
+ * correctly.
+ */
+void update_pm_runtime_accounting(struct device *dev)
+{
+ unsigned long now = jiffies;
+ int delta;
+
+ delta = now - dev->power.accounting_timestamp;
+
+ if (delta < 0)
+ delta = 0;
+
+ dev->power.accounting_timestamp = now;
+
+ if (dev->power.disable_depth > 0)
+ return;
+
+ if (dev->power.runtime_status == RPM_SUSPENDED)
+ dev->power.suspended_jiffies += delta;
+ else
+ dev->power.active_jiffies += delta;
+}
+
+static void __update_runtime_status(struct device *dev, enum rpm_status status)
+{
+ update_pm_runtime_accounting(dev);
+ dev->power.runtime_status = status;
+}
/**
* pm_runtime_deactivate_timer - Deactivate given device's suspend timer.
@@ -40,62 +78,164 @@ static void pm_runtime_cancel_pending(struct device *dev)
dev->power.request = RPM_REQ_NONE;
}
-/**
- * __pm_runtime_idle - Notify device bus type if the device can be suspended.
- * @dev: Device to notify the bus type about.
+/*
+ * pm_runtime_autosuspend_expiration - Get a device's autosuspend-delay expiration time.
+ * @dev: Device to handle.
*
- * This function must be called under dev->power.lock with interrupts disabled.
+ * Compute the autosuspend-delay expiration time based on the device's
+ * power.last_busy time. If the delay has already expired or is disabled
+ * (negative) or the power.use_autosuspend flag isn't set, return 0.
+ * Otherwise return the expiration time in jiffies (adjusted to be nonzero).
+ *
+ * This function may be called either with or without dev->power.lock held.
+ * Either way it can be racy, since power.last_busy may be updated at any time.
*/
-static int __pm_runtime_idle(struct device *dev)
- __releases(&dev->power.lock) __acquires(&dev->power.lock)
+unsigned long pm_runtime_autosuspend_expiration(struct device *dev)
+{
+ int autosuspend_delay;
+ long elapsed;
+ unsigned long last_busy;
+ unsigned long expires = 0;
+
+ if (!dev->power.use_autosuspend)
+ goto out;
+
+ autosuspend_delay = ACCESS_ONCE(dev->power.autosuspend_delay);
+ if (autosuspend_delay < 0)
+ goto out;
+
+ last_busy = ACCESS_ONCE(dev->power.last_busy);
+ elapsed = jiffies - last_busy;
+ if (elapsed < 0)
+ goto out; /* jiffies has wrapped around. */
+
+ /*
+ * If the autosuspend_delay is >= 1 second, align the timer by rounding
+ * up to the nearest second.
+ */
+ expires = last_busy + msecs_to_jiffies(autosuspend_delay);
+ if (autosuspend_delay >= 1000)
+ expires = round_jiffies(expires);
+ expires += !expires;
+ if (elapsed >= expires - last_busy)
+ expires = 0; /* Already expired. */
+
+ out:
+ return expires;
+}
+EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration);
+
+/**
+ * rpm_check_suspend_allowed - Test whether a device may be suspended.
+ * @dev: Device to test.
+ */
+static int rpm_check_suspend_allowed(struct device *dev)
{
int retval = 0;
if (dev->power.runtime_error)
retval = -EINVAL;
- else if (dev->power.idle_notification)
- retval = -EINPROGRESS;
else if (atomic_read(&dev->power.usage_count) > 0
- || dev->power.disable_depth > 0
- || dev->power.runtime_status != RPM_ACTIVE)
+ || dev->power.disable_depth > 0)
retval = -EAGAIN;
else if (!pm_children_suspended(dev))
retval = -EBUSY;
+
+ /* Pending resume requests take precedence over suspends. */
+ else if ((dev->power.deferred_resume
+ && dev->power.runtime_status == RPM_SUSPENDING)
+ || (dev->power.request_pending
+ && dev->power.request == RPM_REQ_RESUME))
+ retval = -EAGAIN;
+ else if (dev->power.runtime_status == RPM_SUSPENDED)
+ retval = 1;
+
+ return retval;
+}
+
+/**
+ * rpm_idle - Notify device bus type if the device can be suspended.
+ * @dev: Device to notify the bus type about.
+ * @rpmflags: Flag bits.
+ *
+ * Check if the device's run-time PM status allows it to be suspended. If
+ * another idle notification has been started earlier, return immediately. If
+ * the RPM_ASYNC flag is set then queue an idle-notification request; otherwise
+ * run the ->runtime_idle() callback directly.
+ *
+ * This function must be called under dev->power.lock with interrupts disabled.
+ */
+static int rpm_idle(struct device *dev, int rpmflags)
+{
+ int (*callback)(struct device *);
+ int (*domain_callback)(struct device *);
+ int retval;
+
+ retval = rpm_check_suspend_allowed(dev);
+ if (retval < 0)
+ ; /* Conditions are wrong. */
+
+ /* Idle notifications are allowed only in the RPM_ACTIVE state. */
+ else if (dev->power.runtime_status != RPM_ACTIVE)
+ retval = -EAGAIN;
+
+ /*
+ * Any pending request other than an idle notification takes
+ * precedence over us, except that the timer may be running.
+ */
+ else if (dev->power.request_pending &&
+ dev->power.request > RPM_REQ_IDLE)
+ retval = -EAGAIN;
+
+ /* Act as though RPM_NOWAIT is always set. */
+ else if (dev->power.idle_notification)
+ retval = -EINPROGRESS;
if (retval)
goto out;
- if (dev->power.request_pending) {
- /*
- * If an idle notification request is pending, cancel it. Any
- * other pending request takes precedence over us.
- */
- if (dev->power.request == RPM_REQ_IDLE) {
- dev->power.request = RPM_REQ_NONE;
- } else if (dev->power.request != RPM_REQ_NONE) {
- retval = -EAGAIN;
- goto out;
+ /* Pending requests need to be canceled. */
+ dev->power.request = RPM_REQ_NONE;
+
+ if (dev->power.no_callbacks) {
+ /* Assume ->runtime_idle() callback would have suspended. */
+ retval = rpm_suspend(dev, rpmflags);
+ goto out;
+ }
+
+ /* Carry out an asynchronous or a synchronous idle notification. */
+ if (rpmflags & RPM_ASYNC) {
+ dev->power.request = RPM_REQ_IDLE;
+ if (!dev->power.request_pending) {
+ dev->power.request_pending = true;
+ queue_work(pm_wq, &dev->power.work);
}
+ goto out;
}
dev->power.idle_notification = true;
- if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) {
- spin_unlock_irq(&dev->power.lock);
+ if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle)
+ callback = dev->bus->pm->runtime_idle;
+ else if (dev->type && dev->type->pm && dev->type->pm->runtime_idle)
+ callback = dev->type->pm->runtime_idle;
+ else if (dev->class && dev->class->pm)
+ callback = dev->class->pm->runtime_idle;
+ else
+ callback = NULL;
- dev->bus->pm->runtime_idle(dev);
+ if (dev->pwr_domain)
+ domain_callback = dev->pwr_domain->ops.runtime_idle;
+ else
+ domain_callback = NULL;
- spin_lock_irq(&dev->power.lock);
- } else if (dev->type && dev->type->pm && dev->type->pm->runtime_idle) {
+ if (callback || domain_callback) {
spin_unlock_irq(&dev->power.lock);
- dev->type->pm->runtime_idle(dev);
+ if (domain_callback)
+ retval = domain_callback(dev);
- spin_lock_irq(&dev->power.lock);
- } else if (dev->class && dev->class->pm
- && dev->class->pm->runtime_idle) {
- spin_unlock_irq(&dev->power.lock);
-
- dev->class->pm->runtime_idle(dev);
+ if (!retval && callback)
+ callback(dev);
spin_lock_irq(&dev->power.lock);
}
@@ -108,74 +248,102 @@ static int __pm_runtime_idle(struct device *dev)
}
/**
- * pm_runtime_idle - Notify device bus type if the device can be suspended.
- * @dev: Device to notify the bus type about.
+ * rpm_callback - Run a given runtime PM callback for a given device.
+ * @cb: Runtime PM callback to run.
+ * @dev: Device to run the callback for.
*/
-int pm_runtime_idle(struct device *dev)
+static int rpm_callback(int (*cb)(struct device *), struct device *dev)
+ __releases(&dev->power.lock) __acquires(&dev->power.lock)
{
int retval;
- spin_lock_irq(&dev->power.lock);
- retval = __pm_runtime_idle(dev);
- spin_unlock_irq(&dev->power.lock);
+ if (!cb)
+ return -ENOSYS;
+
+ if (dev->power.irq_safe) {
+ retval = cb(dev);
+ } else {
+ spin_unlock_irq(&dev->power.lock);
+
+ retval = cb(dev);
+ spin_lock_irq(&dev->power.lock);
+ }
+ dev->power.runtime_error = retval;
return retval;
}
-EXPORT_SYMBOL_GPL(pm_runtime_idle);
/**
- * __pm_runtime_suspend - Carry out run-time suspend of given device.
+ * rpm_suspend - Carry out run-time suspend of given device.
* @dev: Device to suspend.
- * @from_wq: If set, the function has been called via pm_wq.
+ * @rpmflags: Flag bits.
*
- * Check if the device can be suspended and run the ->runtime_suspend() callback
- * provided by its bus type. If another suspend has been started earlier, wait
- * for it to finish. If an idle notification or suspend request is pending or
- * scheduled, cancel it.
+ * Check if the device's run-time PM status allows it to be suspended. If
+ * another suspend has been started earlier, either return immediately or wait
+ * for it to finish, depending on the RPM_NOWAIT and RPM_ASYNC flags. Cancel a
+ * pending idle notification. If the RPM_ASYNC flag is set then queue a
+ * suspend request; otherwise run the ->runtime_suspend() callback directly.
+ * If a deferred resume was requested while the callback was running then carry
+ * it out; otherwise send an idle notification for the device (if the suspend
+ * failed) or for its parent (if the suspend succeeded).
*
* This function must be called under dev->power.lock with interrupts disabled.
*/
-int __pm_runtime_suspend(struct device *dev, bool from_wq)
+static int rpm_suspend(struct device *dev, int rpmflags)
__releases(&dev->power.lock) __acquires(&dev->power.lock)
{
+ int (*callback)(struct device *);
struct device *parent = NULL;
- bool notify = false;
- int retval = 0;
+ int retval;
- dev_dbg(dev, "__pm_runtime_suspend()%s!\n",
- from_wq ? " from workqueue" : "");
+ dev_dbg(dev, "%s flags 0x%x\n", __func__, rpmflags);
repeat:
- if (dev->power.runtime_error) {
- retval = -EINVAL;
- goto out;
- }
+ retval = rpm_check_suspend_allowed(dev);
- /* Pending resume requests take precedence over us. */
- if (dev->power.request_pending
- && dev->power.request == RPM_REQ_RESUME) {
+ if (retval < 0)
+ ; /* Conditions are wrong. */
+
+ /* Synchronous suspends are not allowed in the RPM_RESUMING state. */
+ else if (dev->power.runtime_status == RPM_RESUMING &&
+ !(rpmflags & RPM_ASYNC))
retval = -EAGAIN;
+ if (retval)
goto out;
+
+ /* If the autosuspend_delay time hasn't expired yet, reschedule. */
+ if ((rpmflags & RPM_AUTO)
+ && dev->power.runtime_status != RPM_SUSPENDING) {
+ unsigned long expires = pm_runtime_autosuspend_expiration(dev);
+
+ if (expires != 0) {
+ /* Pending requests need to be canceled. */
+ dev->power.request = RPM_REQ_NONE;
+
+ /*
+ * Optimization: If the timer is already running and is
+ * set to expire at or before the autosuspend delay,
+ * avoid the overhead of resetting it. Just let it
+ * expire; pm_suspend_timer_fn() will take care of the
+ * rest.
+ */
+ if (!(dev->power.timer_expires && time_before_eq(
+ dev->power.timer_expires, expires))) {
+ dev->power.timer_expires = expires;
+ mod_timer(&dev->power.suspend_timer, expires);
+ }
+ dev->power.timer_autosuspends = 1;
+ goto out;
+ }
}
/* Other scheduled or pending requests need to be canceled. */
pm_runtime_cancel_pending(dev);
- if (dev->power.runtime_status == RPM_SUSPENDED)
- retval = 1;
- else if (dev->power.runtime_status == RPM_RESUMING
- || dev->power.disable_depth > 0
- || atomic_read(&dev->power.usage_count) > 0)
- retval = -EAGAIN;
- else if (!pm_children_suspended(dev))
- retval = -EBUSY;
- if (retval)
- goto out;
-
if (dev->power.runtime_status == RPM_SUSPENDING) {
DEFINE_WAIT(wait);
- if (from_wq) {
+ if (rpmflags & (RPM_ASYNC | RPM_NOWAIT)) {
retval = -EINPROGRESS;
goto out;
}
@@ -197,47 +365,45 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq)
goto repeat;
}
- dev->power.runtime_status = RPM_SUSPENDING;
dev->power.deferred_resume = false;
+ if (dev->power.no_callbacks)
+ goto no_callback; /* Assume success. */
+
+ /* Carry out an asynchronous or a synchronous suspend. */
+ if (rpmflags & RPM_ASYNC) {
+ dev->power.request = (rpmflags & RPM_AUTO) ?
+ RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND;
+ if (!dev->power.request_pending) {
+ dev->power.request_pending = true;
+ queue_work(pm_wq, &dev->power.work);
+ }
+ goto out;
+ }
- if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
- spin_unlock_irq(&dev->power.lock);
-
- retval = dev->bus->pm->runtime_suspend(dev);
-
- spin_lock_irq(&dev->power.lock);
- dev->power.runtime_error = retval;
- } else if (dev->type && dev->type->pm
- && dev->type->pm->runtime_suspend) {
- spin_unlock_irq(&dev->power.lock);
-
- retval = dev->type->pm->runtime_suspend(dev);
-
- spin_lock_irq(&dev->power.lock);
- dev->power.runtime_error = retval;
- } else if (dev->class && dev->class->pm
- && dev->class->pm->runtime_suspend) {
- spin_unlock_irq(&dev->power.lock);
-
- retval = dev->class->pm->runtime_suspend(dev);
+ __update_runtime_status(dev, RPM_SUSPENDING);
- spin_lock_irq(&dev->power.lock);
- dev->power.runtime_error = retval;
- } else {
- retval = -ENOSYS;
- }
+ if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend)
+ callback = dev->bus->pm->runtime_suspend;
+ else if (dev->type && dev->type->pm && dev->type->pm->runtime_suspend)
+ callback = dev->type->pm->runtime_suspend;
+ else if (dev->class && dev->class->pm)
+ callback = dev->class->pm->runtime_suspend;
+ else
+ callback = NULL;
+ retval = rpm_callback(callback, dev);
if (retval) {
- dev->power.runtime_status = RPM_ACTIVE;
- if (retval == -EAGAIN || retval == -EBUSY) {
- if (dev->power.timer_expires == 0)
- notify = true;
+ __update_runtime_status(dev, RPM_ACTIVE);
+ dev->power.deferred_resume = 0;
+ if (retval == -EAGAIN || retval == -EBUSY)
dev->power.runtime_error = 0;
- } else {
+ else
pm_runtime_cancel_pending(dev);
- }
} else {
- dev->power.runtime_status = RPM_SUSPENDED;
+ if (dev->pwr_domain)
+ rpm_callback(dev->pwr_domain->ops.runtime_suspend, dev);
+ no_callback:
+ __update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_deactivate_timer(dev);
if (dev->parent) {
@@ -248,89 +414,86 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq)
wake_up_all(&dev->power.wait_queue);
if (dev->power.deferred_resume) {
- __pm_runtime_resume(dev, false);
+ rpm_resume(dev, 0);
retval = -EAGAIN;
goto out;
}
- if (notify)
- __pm_runtime_idle(dev);
-
- if (parent && !parent->power.ignore_children) {
- spin_unlock_irq(&dev->power.lock);
+ /* Maybe the parent is now able to suspend. */
+ if (parent && !parent->power.ignore_children && !dev->power.irq_safe) {
+ spin_unlock(&dev->power.lock);
- pm_request_idle(parent);
+ spin_lock(&parent->power.lock);
+ rpm_idle(parent, RPM_ASYNC);
+ spin_unlock(&parent->power.lock);
- spin_lock_irq(&dev->power.lock);
+ spin_lock(&dev->power.lock);
}
out:
- dev_dbg(dev, "__pm_runtime_suspend() returns %d!\n", retval);
-
- return retval;
-}
-
-/**
- * pm_runtime_suspend - Carry out run-time suspend of given device.
- * @dev: Device to suspend.
- */
-int pm_runtime_suspend(struct device *dev)
-{
- int retval;
-
- spin_lock_irq(&dev->power.lock);
- retval = __pm_runtime_suspend(dev, false);
- spin_unlock_irq(&dev->power.lock);
+ dev_dbg(dev, "%s returns %d\n", __func__, retval);
return retval;
}
-EXPORT_SYMBOL_GPL(pm_runtime_suspend);
/**
- * __pm_runtime_resume - Carry out run-time resume of given device.
+ * rpm_resume - Carry out run-time resume of given device.
* @dev: Device to resume.
- * @from_wq: If set, the function has been called via pm_wq.
+ * @rpmflags: Flag bits.
*
- * Check if the device can be woken up and run the ->runtime_resume() callback
- * provided by its bus type. If another resume has been started earlier, wait
- * for it to finish. If there's a suspend running in parallel with this
- * function, wait for it to finish and resume the device. Cancel any scheduled
- * or pending requests.
+ * Check if the device's run-time PM status allows it to be resumed. Cancel
+ * any scheduled or pending requests. If another resume has been started
+ * earlier, either return imediately or wait for it to finish, depending on the
+ * RPM_NOWAIT and RPM_ASYNC flags. Similarly, if there's a suspend running in
+ * parallel with this function, either tell the other process to resume after
+ * suspending (deferred_resume) or wait for it to finish. If the RPM_ASYNC
+ * flag is set then queue a resume request; otherwise run the
+ * ->runtime_resume() callback directly. Queue an idle notification for the
+ * device if the resume succeeded.
*
* This function must be called under dev->power.lock with interrupts disabled.
*/
-int __pm_runtime_resume(struct device *dev, bool from_wq)
+static int rpm_resume(struct device *dev, int rpmflags)
__releases(&dev->power.lock) __acquires(&dev->power.lock)
{
+ int (*callback)(struct device *);
struct device *parent = NULL;
int retval = 0;
- dev_dbg(dev, "__pm_runtime_resume()%s!\n",
- from_wq ? " from workqueue" : "");
+ dev_dbg(dev, "%s flags 0x%x\n", __func__, rpmflags);
repeat:
- if (dev->power.runtime_error) {
+ if (dev->power.runtime_error)
retval = -EINVAL;
+ else if (dev->power.disable_depth > 0)
+ retval = -EAGAIN;
+ if (retval)
goto out;
- }
- pm_runtime_cancel_pending(dev);
+ /*
+ * Other scheduled or pending requests need to be canceled. Small
+ * optimization: If an autosuspend timer is running, leave it running
+ * rather than cancelling it now only to restart it again in the near
+ * future.
+ */
+ dev->power.request = RPM_REQ_NONE;
+ if (!dev->power.timer_autosuspends)
+ pm_runtime_deactivate_timer(dev);
- if (dev->power.runtime_status == RPM_ACTIVE)
+ if (dev->power.runtime_status == RPM_ACTIVE) {
retval = 1;
- else if (dev->power.disable_depth > 0)
- retval = -EAGAIN;
- if (retval)
goto out;
+ }
if (dev->power.runtime_status == RPM_RESUMING
|| dev->power.runtime_status == RPM_SUSPENDING) {
DEFINE_WAIT(wait);
- if (from_wq) {
+ if (rpmflags & (RPM_ASYNC | RPM_NOWAIT)) {
if (dev->power.runtime_status == RPM_SUSPENDING)
dev->power.deferred_resume = true;
- retval = -EINPROGRESS;
+ else
+ retval = -EINPROGRESS;
goto out;
}
@@ -352,12 +515,43 @@ int __pm_runtime_resume(struct device *dev, bool from_wq)
goto repeat;
}
+ /*
+ * See if we can skip waking up the parent. This is safe only if
+ * power.no_callbacks is set, because otherwise we don't know whether
+ * the resume will actually succeed.
+ */
+ if (dev->power.no_callbacks && !parent && dev->parent) {
+ spin_lock_nested(&dev->parent->power.lock, SINGLE_DEPTH_NESTING);
+ if (dev->parent->power.disable_depth > 0
+ || dev->parent->power.ignore_children
+ || dev->parent->power.runtime_status == RPM_ACTIVE) {
+ atomic_inc(&dev->parent->power.child_count);
+ spin_unlock(&dev->parent->power.lock);
+ goto no_callback; /* Assume success. */
+ }
+ spin_unlock(&dev->parent->power.lock);
+ }
+
+ /* Carry out an asynchronous or a synchronous resume. */
+ if (rpmflags & RPM_ASYNC) {
+ dev->power.request = RPM_REQ_RESUME;
+ if (!dev->power.request_pending) {
+ dev->power.request_pending = true;
+ queue_work(pm_wq, &dev->power.work);
+ }
+ retval = 0;
+ goto out;
+ }
+
if (!parent && dev->parent) {
/*
- * Increment the parent's resume counter and resume it if
- * necessary.
+ * Increment the parent's usage counter and resume it if
+ * necessary. Not needed if dev is irq-safe; then the
+ * parent is permanently resumed.
*/
parent = dev->parent;
+ if (dev->power.irq_safe)
+ goto skip_parent;
spin_unlock(&dev->power.lock);
pm_runtime_get_noresume(parent);
@@ -369,7 +563,7 @@ int __pm_runtime_resume(struct device *dev, bool from_wq)
*/
if (!parent->power.disable_depth
&& !parent->power.ignore_children) {
- __pm_runtime_resume(parent, false);
+ rpm_resume(parent, 0);
if (parent->power.runtime_status != RPM_ACTIVE)
retval = -EBUSY;
}
@@ -380,51 +574,42 @@ int __pm_runtime_resume(struct device *dev, bool from_wq)
goto out;
goto repeat;
}
+ skip_parent:
- dev->power.runtime_status = RPM_RESUMING;
-
- if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) {
- spin_unlock_irq(&dev->power.lock);
+ if (dev->power.no_callbacks)
+ goto no_callback; /* Assume success. */
- retval = dev->bus->pm->runtime_resume(dev);
+ __update_runtime_status(dev, RPM_RESUMING);
- spin_lock_irq(&dev->power.lock);
- dev->power.runtime_error = retval;
- } else if (dev->type && dev->type->pm
- && dev->type->pm->runtime_resume) {
- spin_unlock_irq(&dev->power.lock);
-
- retval = dev->type->pm->runtime_resume(dev);
-
- spin_lock_irq(&dev->power.lock);
- dev->power.runtime_error = retval;
- } else if (dev->class && dev->class->pm
- && dev->class->pm->runtime_resume) {
- spin_unlock_irq(&dev->power.lock);
+ if (dev->pwr_domain)
+ rpm_callback(dev->pwr_domain->ops.runtime_resume, dev);
- retval = dev->class->pm->runtime_resume(dev);
-
- spin_lock_irq(&dev->power.lock);
- dev->power.runtime_error = retval;
- } else {
- retval = -ENOSYS;
- }
+ if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume)
+ callback = dev->bus->pm->runtime_resume;
+ else if (dev->type && dev->type->pm && dev->type->pm->runtime_resume)
+ callback = dev->type->pm->runtime_resume;
+ else if (dev->class && dev->class->pm)
+ callback = dev->class->pm->runtime_resume;
+ else
+ callback = NULL;
+ retval = rpm_callback(callback, dev);
if (retval) {
- dev->power.runtime_status = RPM_SUSPENDED;
+ __update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_cancel_pending(dev);
} else {
- dev->power.runtime_status = RPM_ACTIVE;
+ no_callback:
+ __update_runtime_status(dev, RPM_ACTIVE);
if (parent)
atomic_inc(&parent->power.child_count);
}
wake_up_all(&dev->power.wait_queue);
if (!retval)
- __pm_request_idle(dev);
+ rpm_idle(dev, RPM_ASYNC);
out:
- if (parent) {
+ if (parent && !dev->power.irq_safe) {
spin_unlock_irq(&dev->power.lock);
pm_runtime_put(parent);
@@ -432,26 +617,10 @@ int __pm_runtime_resume(struct device *dev, bool from_wq)
spin_lock_irq(&dev->power.lock);
}
- dev_dbg(dev, "__pm_runtime_resume() returns %d!\n", retval);
-
- return retval;
-}
-
-/**
- * pm_runtime_resume - Carry out run-time resume of given device.
- * @dev: Device to suspend.
- */
-int pm_runtime_resume(struct device *dev)
-{
- int retval;
-
- spin_lock_irq(&dev->power.lock);
- retval = __pm_runtime_resume(dev, false);
- spin_unlock_irq(&dev->power.lock);
+ dev_dbg(dev, "%s returns %d\n", __func__, retval);
return retval;
}
-EXPORT_SYMBOL_GPL(pm_runtime_resume);
/**
* pm_runtime_work - Universal run-time PM work function.
@@ -478,13 +647,16 @@ static void pm_runtime_work(struct work_struct *work)
case RPM_REQ_NONE:
break;
case RPM_REQ_IDLE:
- __pm_runtime_idle(dev);
+ rpm_idle(dev, RPM_NOWAIT);
break;
case RPM_REQ_SUSPEND:
- __pm_runtime_suspend(dev, true);
+ rpm_suspend(dev, RPM_NOWAIT);
+ break;
+ case RPM_REQ_AUTOSUSPEND:
+ rpm_suspend(dev, RPM_NOWAIT | RPM_AUTO);
break;
case RPM_REQ_RESUME:
- __pm_runtime_resume(dev, true);
+ rpm_resume(dev, RPM_NOWAIT);
break;
}
@@ -493,117 +665,10 @@ static void pm_runtime_work(struct work_struct *work)
}
/**
- * __pm_request_idle - Submit an idle notification request for given device.
- * @dev: Device to handle.
- *
- * Check if the device's run-time PM status is correct for suspending the device
- * and queue up a request to run __pm_runtime_idle() for it.
- *
- * This function must be called under dev->power.lock with interrupts disabled.
- */
-static int __pm_request_idle(struct device *dev)
-{
- int retval = 0;
-
- if (dev->power.runtime_error)
- retval = -EINVAL;
- else if (atomic_read(&dev->power.usage_count) > 0
- || dev->power.disable_depth > 0
- || dev->power.runtime_status == RPM_SUSPENDED
- || dev->power.runtime_status == RPM_SUSPENDING)
- retval = -EAGAIN;
- else if (!pm_children_suspended(dev))
- retval = -EBUSY;
- if (retval)
- return retval;
-
- if (dev->power.request_pending) {
- /* Any requests other then RPM_REQ_IDLE take precedence. */
- if (dev->power.request == RPM_REQ_NONE)
- dev->power.request = RPM_REQ_IDLE;
- else if (dev->power.request != RPM_REQ_IDLE)
- retval = -EAGAIN;
- return retval;
- }
-
- dev->power.request = RPM_REQ_IDLE;
- dev->power.request_pending = true;
- queue_work(pm_wq, &dev->power.work);
-
- return retval;
-}
-
-/**
- * pm_request_idle - Submit an idle notification request for given device.
- * @dev: Device to handle.
- */
-int pm_request_idle(struct device *dev)
-{
- unsigned long flags;
- int retval;
-
- spin_lock_irqsave(&dev->power.lock, flags);
- retval = __pm_request_idle(dev);
- spin_unlock_irqrestore(&dev->power.lock, flags);
-
- return retval;
-}
-EXPORT_SYMBOL_GPL(pm_request_idle);
-
-/**
- * __pm_request_suspend - Submit a suspend request for given device.
- * @dev: Device to suspend.
- *
- * This function must be called under dev->power.lock with interrupts disabled.
- */
-static int __pm_request_suspend(struct device *dev)
-{
- int retval = 0;
-
- if (dev->power.runtime_error)
- return -EINVAL;
-
- if (dev->power.runtime_status == RPM_SUSPENDED)
- retval = 1;
- else if (atomic_read(&dev->power.usage_count) > 0
- || dev->power.disable_depth > 0)
- retval = -EAGAIN;
- else if (dev->power.runtime_status == RPM_SUSPENDING)
- retval = -EINPROGRESS;
- else if (!pm_children_suspended(dev))
- retval = -EBUSY;
- if (retval < 0)
- return retval;
-
- pm_runtime_deactivate_timer(dev);
-
- if (dev->power.request_pending) {
- /*
- * Pending resume requests take precedence over us, but we can
- * overtake any other pending request.
- */
- if (dev->power.request == RPM_REQ_RESUME)
- retval = -EAGAIN;
- else if (dev->power.request != RPM_REQ_SUSPEND)
- dev->power.request = retval ?
- RPM_REQ_NONE : RPM_REQ_SUSPEND;
- return retval;
- } else if (retval) {
- return retval;
- }
-
- dev->power.request = RPM_REQ_SUSPEND;
- dev->power.request_pending = true;
- queue_work(pm_wq, &dev->power.work);
-
- return 0;
-}
-
-/**
* pm_suspend_timer_fn - Timer function for pm_schedule_suspend().
* @data: Device pointer passed by pm_schedule_suspend().
*
- * Check if the time is right and execute __pm_request_suspend() in that case.
+ * Check if the time is right and queue a suspend request.
*/
static void pm_suspend_timer_fn(unsigned long data)
{
@@ -617,7 +682,8 @@ static void pm_suspend_timer_fn(unsigned long data)
/* If 'expire' is after 'jiffies' we've been called too early. */
if (expires > 0 && !time_after(expires, jiffies)) {
dev->power.timer_expires = 0;
- __pm_request_suspend(dev);
+ rpm_suspend(dev, dev->power.timer_autosuspends ?
+ (RPM_ASYNC | RPM_AUTO) : RPM_ASYNC);
}
spin_unlock_irqrestore(&dev->power.lock, flags);
@@ -631,47 +697,25 @@ static void pm_suspend_timer_fn(unsigned long data)
int pm_schedule_suspend(struct device *dev, unsigned int delay)
{
unsigned long flags;
- int retval = 0;
+ int retval;
spin_lock_irqsave(&dev->power.lock, flags);
- if (dev->power.runtime_error) {
- retval = -EINVAL;
- goto out;
- }
-
if (!delay) {
- retval = __pm_request_suspend(dev);
+ retval = rpm_suspend(dev, RPM_ASYNC);
goto out;
}
- pm_runtime_deactivate_timer(dev);
-
- if (dev->power.request_pending) {
- /*
- * Pending resume requests take precedence over us, but any
- * other pending requests have to be canceled.
- */
- if (dev->power.request == RPM_REQ_RESUME) {
- retval = -EAGAIN;
- goto out;
- }
- dev->power.request = RPM_REQ_NONE;
- }
-
- if (dev->power.runtime_status == RPM_SUSPENDED)
- retval = 1;
- else if (atomic_read(&dev->power.usage_count) > 0
- || dev->power.disable_depth > 0)
- retval = -EAGAIN;
- else if (!pm_children_suspended(dev))
- retval = -EBUSY;
+ retval = rpm_check_suspend_allowed(dev);
if (retval)
goto out;
+ /* Other scheduled or pending requests need to be canceled. */
+ pm_runtime_cancel_pending(dev);
+
dev->power.timer_expires = jiffies + msecs_to_jiffies(delay);
- if (!dev->power.timer_expires)
- dev->power.timer_expires = 1;
+ dev->power.timer_expires += !dev->power.timer_expires;
+ dev->power.timer_autosuspends = 0;
mod_timer(&dev->power.suspend_timer, dev->power.timer_expires);
out:
@@ -682,103 +726,88 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay)
EXPORT_SYMBOL_GPL(pm_schedule_suspend);
/**
- * pm_request_resume - Submit a resume request for given device.
- * @dev: Device to resume.
+ * __pm_runtime_idle - Entry point for run-time idle operations.
+ * @dev: Device to send idle notification for.
+ * @rpmflags: Flag bits.
*
- * This function must be called under dev->power.lock with interrupts disabled.
+ * If the RPM_GET_PUT flag is set, decrement the device's usage count and
+ * return immediately if it is larger than zero. Then carry out an idle
+ * notification, either synchronous or asynchronous.
+ *
+ * This routine may be called in atomic context if the RPM_ASYNC flag is set.
*/
-static int __pm_request_resume(struct device *dev)
+int __pm_runtime_idle(struct device *dev, int rpmflags)
{
- int retval = 0;
-
- if (dev->power.runtime_error)
- return -EINVAL;
-
- if (dev->power.runtime_status == RPM_ACTIVE)
- retval = 1;
- else if (dev->power.runtime_status == RPM_RESUMING)
- retval = -EINPROGRESS;
- else if (dev->power.disable_depth > 0)
- retval = -EAGAIN;
- if (retval < 0)
- return retval;
-
- pm_runtime_deactivate_timer(dev);
+ unsigned long flags;
+ int retval;
- if (dev->power.runtime_status == RPM_SUSPENDING) {
- dev->power.deferred_resume = true;
- return retval;
- }
- if (dev->power.request_pending) {
- /* If non-resume request is pending, we can overtake it. */
- dev->power.request = retval ? RPM_REQ_NONE : RPM_REQ_RESUME;
- return retval;
+ if (rpmflags & RPM_GET_PUT) {
+ if (!atomic_dec_and_test(&dev->power.usage_count))
+ return 0;
}
- if (retval)
- return retval;
- dev->power.request = RPM_REQ_RESUME;
- dev->power.request_pending = true;
- queue_work(pm_wq, &dev->power.work);
+ spin_lock_irqsave(&dev->power.lock, flags);
+ retval = rpm_idle(dev, rpmflags);
+ spin_unlock_irqrestore(&dev->power.lock, flags);
return retval;
}
+EXPORT_SYMBOL_GPL(__pm_runtime_idle);
/**
- * pm_request_resume - Submit a resume request for given device.
- * @dev: Device to resume.
+ * __pm_runtime_suspend - Entry point for run-time put/suspend operations.
+ * @dev: Device to suspend.
+ * @rpmflags: Flag bits.
+ *
+ * If the RPM_GET_PUT flag is set, decrement the device's usage count and
+ * return immediately if it is larger than zero. Then carry out a suspend,
+ * either synchronous or asynchronous.
+ *
+ * This routine may be called in atomic context if the RPM_ASYNC flag is set.
*/
-int pm_request_resume(struct device *dev)
+int __pm_runtime_suspend(struct device *dev, int rpmflags)
{
unsigned long flags;
int retval;
+ if (rpmflags & RPM_GET_PUT) {
+ if (!atomic_dec_and_test(&dev->power.usage_count))
+ return 0;
+ }
+
spin_lock_irqsave(&dev->power.lock, flags);
- retval = __pm_request_resume(dev);
+ retval = rpm_suspend(dev, rpmflags);
spin_unlock_irqrestore(&dev->power.lock, flags);
return retval;
}
-EXPORT_SYMBOL_GPL(pm_request_resume);
+EXPORT_SYMBOL_GPL(__pm_runtime_suspend);
/**
- * __pm_runtime_get - Reference count a device and wake it up, if necessary.
- * @dev: Device to handle.
- * @sync: If set and the device is suspended, resume it synchronously.
+ * __pm_runtime_resume - Entry point for run-time resume operations.
+ * @dev: Device to resume.
+ * @rpmflags: Flag bits.
+ *
+ * If the RPM_GET_PUT flag is set, increment the device's usage count. Then
+ * carry out a resume, either synchronous or asynchronous.
*
- * Increment the usage count of the device and resume it or submit a resume
- * request for it, depending on the value of @sync.
+ * This routine may be called in atomic context if the RPM_ASYNC flag is set.
*/
-int __pm_runtime_get(struct device *dev, bool sync)
+int __pm_runtime_resume(struct device *dev, int rpmflags)
{
+ unsigned long flags;
int retval;
- atomic_inc(&dev->power.usage_count);
- retval = sync ? pm_runtime_resume(dev) : pm_request_resume(dev);
-
- return retval;
-}
-EXPORT_SYMBOL_GPL(__pm_runtime_get);
+ if (rpmflags & RPM_GET_PUT)
+ atomic_inc(&dev->power.usage_count);
-/**
- * __pm_runtime_put - Decrement the device's usage counter and notify its bus.
- * @dev: Device to handle.
- * @sync: If the device's bus type is to be notified, do that synchronously.
- *
- * Decrement the usage count of the device and if it reaches zero, carry out a
- * synchronous idle notification or submit an idle notification request for it,
- * depending on the value of @sync.
- */
-int __pm_runtime_put(struct device *dev, bool sync)
-{
- int retval = 0;
-
- if (atomic_dec_and_test(&dev->power.usage_count))
- retval = sync ? pm_runtime_idle(dev) : pm_request_idle(dev);
+ spin_lock_irqsave(&dev->power.lock, flags);
+ retval = rpm_resume(dev, rpmflags);
+ spin_unlock_irqrestore(&dev->power.lock, flags);
return retval;
}
-EXPORT_SYMBOL_GPL(__pm_runtime_put);
+EXPORT_SYMBOL_GPL(__pm_runtime_resume);
/**
* __pm_runtime_set_status - Set run-time PM status of a device.
@@ -848,7 +877,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
}
out_set:
- dev->power.runtime_status = status;
+ __update_runtime_status(dev, status);
dev->power.runtime_error = 0;
out:
spin_unlock_irqrestore(&dev->power.lock, flags);
@@ -929,7 +958,7 @@ int pm_runtime_barrier(struct device *dev)
if (dev->power.request_pending
&& dev->power.request == RPM_REQ_RESUME) {
- __pm_runtime_resume(dev, false);
+ rpm_resume(dev, 0);
retval = 1;
}
@@ -978,7 +1007,7 @@ void __pm_runtime_disable(struct device *dev, bool check_resume)
*/
pm_runtime_get_noresume(dev);
- __pm_runtime_resume(dev, false);
+ rpm_resume(dev, 0);
pm_runtime_put_noidle(dev);
}
@@ -1026,7 +1055,7 @@ void pm_runtime_forbid(struct device *dev)
dev->power.runtime_auto = false;
atomic_inc(&dev->power.usage_count);
- __pm_runtime_resume(dev, false);
+ rpm_resume(dev, 0);
out:
spin_unlock_irq(&dev->power.lock);
@@ -1047,7 +1076,7 @@ void pm_runtime_allow(struct device *dev)
dev->power.runtime_auto = true;
if (atomic_dec_and_test(&dev->power.usage_count))
- __pm_runtime_idle(dev);
+ rpm_idle(dev, RPM_AUTO);
out:
spin_unlock_irq(&dev->power.lock);
@@ -1055,6 +1084,125 @@ void pm_runtime_allow(struct device *dev)
EXPORT_SYMBOL_GPL(pm_runtime_allow);
/**
+ * pm_runtime_no_callbacks - Ignore run-time PM callbacks for a device.
+ * @dev: Device to handle.
+ *
+ * Set the power.no_callbacks flag, which tells the PM core that this
+ * device is power-managed through its parent and has no run-time PM
+ * callbacks of its own. The run-time sysfs attributes will be removed.
+ */
+void pm_runtime_no_callbacks(struct device *dev)
+{
+ spin_lock_irq(&dev->power.lock);
+ dev->power.no_callbacks = 1;
+ spin_unlock_irq(&dev->power.lock);
+ if (device_is_registered(dev))
+ rpm_sysfs_remove(dev);
+}
+EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks);
+
+/**
+ * pm_runtime_irq_safe - Leave interrupts disabled during callbacks.
+ * @dev: Device to handle
+ *
+ * Set the power.irq_safe flag, which tells the PM core that the
+ * ->runtime_suspend() and ->runtime_resume() callbacks for this device should
+ * always be invoked with the spinlock held and interrupts disabled. It also
+ * causes the parent's usage counter to be permanently incremented, preventing
+ * the parent from runtime suspending -- otherwise an irq-safe child might have
+ * to wait for a non-irq-safe parent.
+ */
+void pm_runtime_irq_safe(struct device *dev)
+{
+ if (dev->parent)
+ pm_runtime_get_sync(dev->parent);
+ spin_lock_irq(&dev->power.lock);
+ dev->power.irq_safe = 1;
+ spin_unlock_irq(&dev->power.lock);
+}
+EXPORT_SYMBOL_GPL(pm_runtime_irq_safe);
+
+/**
+ * update_autosuspend - Handle a change to a device's autosuspend settings.
+ * @dev: Device to handle.
+ * @old_delay: The former autosuspend_delay value.
+ * @old_use: The former use_autosuspend value.
+ *
+ * Prevent runtime suspend if the new delay is negative and use_autosuspend is
+ * set; otherwise allow it. Send an idle notification if suspends are allowed.
+ *
+ * This function must be called under dev->power.lock with interrupts disabled.
+ */
+static void update_autosuspend(struct device *dev, int old_delay, int old_use)
+{
+ int delay = dev->power.autosuspend_delay;
+
+ /* Should runtime suspend be prevented now? */
+ if (dev->power.use_autosuspend && delay < 0) {
+
+ /* If it used to be allowed then prevent it. */
+ if (!old_use || old_delay >= 0) {
+ atomic_inc(&dev->power.usage_count);
+ rpm_resume(dev, 0);
+ }
+ }
+
+ /* Runtime suspend should be allowed now. */
+ else {
+
+ /* If it used to be prevented then allow it. */
+ if (old_use && old_delay < 0)
+ atomic_dec(&dev->power.usage_count);
+
+ /* Maybe we can autosuspend now. */
+ rpm_idle(dev, RPM_AUTO);
+ }
+}
+
+/**
+ * pm_runtime_set_autosuspend_delay - Set a device's autosuspend_delay value.
+ * @dev: Device to handle.
+ * @delay: Value of the new delay in milliseconds.
+ *
+ * Set the device's power.autosuspend_delay value. If it changes to negative
+ * and the power.use_autosuspend flag is set, prevent run-time suspends. If it
+ * changes the other way, allow run-time suspends.
+ */
+void pm_runtime_set_autosuspend_delay(struct device *dev, int delay)
+{
+ int old_delay, old_use;
+
+ spin_lock_irq(&dev->power.lock);
+ old_delay = dev->power.autosuspend_delay;
+ old_use = dev->power.use_autosuspend;
+ dev->power.autosuspend_delay = delay;
+ update_autosuspend(dev, old_delay, old_use);
+ spin_unlock_irq(&dev->power.lock);
+}
+EXPORT_SYMBOL_GPL(pm_runtime_set_autosuspend_delay);
+
+/**
+ * __pm_runtime_use_autosuspend - Set a device's use_autosuspend flag.
+ * @dev: Device to handle.
+ * @use: New value for use_autosuspend.
+ *
+ * Set the device's power.use_autosuspend flag, and allow or prevent run-time
+ * suspends as needed.
+ */
+void __pm_runtime_use_autosuspend(struct device *dev, bool use)
+{
+ int old_delay, old_use;
+
+ spin_lock_irq(&dev->power.lock);
+ old_delay = dev->power.autosuspend_delay;
+ old_use = dev->power.use_autosuspend;
+ dev->power.use_autosuspend = use;
+ update_autosuspend(dev, old_delay, old_use);
+ spin_unlock_irq(&dev->power.lock);
+}
+EXPORT_SYMBOL_GPL(__pm_runtime_use_autosuspend);
+
+/**
* pm_runtime_init - Initialize run-time PM fields in given device object.
* @dev: Device object to initialize.
*/
@@ -1077,6 +1225,7 @@ void pm_runtime_init(struct device *dev)
dev->power.request_pending = false;
dev->power.request = RPM_REQ_NONE;
dev->power.deferred_resume = false;
+ dev->power.accounting_timestamp = jiffies;
INIT_WORK(&dev->power.work, pm_runtime_work);
dev->power.timer_expires = 0;
@@ -1097,4 +1246,6 @@ void pm_runtime_remove(struct device *dev)
/* Change the status back to 'suspended' to match the initial status. */
if (dev->power.runtime_status == RPM_ACTIVE)
pm_runtime_set_suspended(dev);
+ if (dev->power.irq_safe && dev->parent)
+ pm_runtime_put_sync(dev->parent);
}
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index a4c33bc5125..a1330f710e7 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -6,6 +6,7 @@
#include <linux/string.h>
#include <linux/pm_runtime.h>
#include <asm/atomic.h>
+#include <linux/jiffies.h>
#include "power.h"
/*
@@ -73,11 +74,28 @@
* device are known to the PM core. However, for some devices this
* attribute is set to "enabled" by bus type code or device drivers and in
* that cases it should be safe to leave the default value.
+ *
+ * autosuspend_delay_ms - Report/change a device's autosuspend_delay value
+ *
+ * Some drivers don't want to carry out a runtime suspend as soon as a
+ * device becomes idle; they want it always to remain idle for some period
+ * of time before suspending it. This period is the autosuspend_delay
+ * value (expressed in milliseconds) and it can be controlled by the user.
+ * If the value is negative then the device will never be runtime
+ * suspended.
+ *
+ * NOTE: The autosuspend_delay_ms attribute and the autosuspend_delay
+ * value are used only if the driver calls pm_runtime_use_autosuspend().
+ *
+ * wakeup_count - Report the number of wakeup events related to the device
*/
static const char enabled[] = "enabled";
static const char disabled[] = "disabled";
+const char power_group_name[] = "power";
+EXPORT_SYMBOL_GPL(power_group_name);
+
#ifdef CONFIG_PM_RUNTIME
static const char ctrl_auto[] = "auto";
static const char ctrl_on[] = "on";
@@ -108,6 +126,92 @@ static ssize_t control_store(struct device * dev, struct device_attribute *attr,
}
static DEVICE_ATTR(control, 0644, control_show, control_store);
+
+static ssize_t rtpm_active_time_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ spin_lock_irq(&dev->power.lock);
+ update_pm_runtime_accounting(dev);
+ ret = sprintf(buf, "%i\n", jiffies_to_msecs(dev->power.active_jiffies));
+ spin_unlock_irq(&dev->power.lock);
+ return ret;
+}
+
+static DEVICE_ATTR(runtime_active_time, 0444, rtpm_active_time_show, NULL);
+
+static ssize_t rtpm_suspended_time_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ spin_lock_irq(&dev->power.lock);
+ update_pm_runtime_accounting(dev);
+ ret = sprintf(buf, "%i\n",
+ jiffies_to_msecs(dev->power.suspended_jiffies));
+ spin_unlock_irq(&dev->power.lock);
+ return ret;
+}
+
+static DEVICE_ATTR(runtime_suspended_time, 0444, rtpm_suspended_time_show, NULL);
+
+static ssize_t rtpm_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const char *p;
+
+ if (dev->power.runtime_error) {
+ p = "error\n";
+ } else if (dev->power.disable_depth) {
+ p = "unsupported\n";
+ } else {
+ switch (dev->power.runtime_status) {
+ case RPM_SUSPENDED:
+ p = "suspended\n";
+ break;
+ case RPM_SUSPENDING:
+ p = "suspending\n";
+ break;
+ case RPM_RESUMING:
+ p = "resuming\n";
+ break;
+ case RPM_ACTIVE:
+ p = "active\n";
+ break;
+ default:
+ return -EIO;
+ }
+ }
+ return sprintf(buf, p);
+}
+
+static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL);
+
+static ssize_t autosuspend_delay_ms_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!dev->power.use_autosuspend)
+ return -EIO;
+ return sprintf(buf, "%d\n", dev->power.autosuspend_delay);
+}
+
+static ssize_t autosuspend_delay_ms_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ long delay;
+
+ if (!dev->power.use_autosuspend)
+ return -EIO;
+
+ if (strict_strtol(buf, 10, &delay) != 0 || delay != (int) delay)
+ return -EINVAL;
+
+ pm_runtime_set_autosuspend_delay(dev, delay);
+ return n;
+}
+
+static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show,
+ autosuspend_delay_ms_store);
+
#endif
static ssize_t
@@ -226,13 +330,11 @@ static DEVICE_ATTR(async, 0644, async_show, async_store);
#endif /* CONFIG_PM_ADVANCED_DEBUG */
static struct attribute * power_attrs[] = {
-#ifdef CONFIG_PM_RUNTIME
- &dev_attr_control.attr,
-#endif
&dev_attr_wakeup.attr,
#ifdef CONFIG_PM_ADVANCED_DEBUG
&dev_attr_async.attr,
#ifdef CONFIG_PM_RUNTIME
+ &dev_attr_runtime_status.attr,
&dev_attr_runtime_usage.attr,
&dev_attr_runtime_active_kids.attr,
&dev_attr_runtime_status.attr,
@@ -242,10 +344,53 @@ static struct attribute * power_attrs[] = {
NULL,
};
static struct attribute_group pm_attr_group = {
- .name = "power",
+ .name = power_group_name,
.attrs = power_attrs,
};
+#ifdef CONFIG_PM_RUNTIME
+
+static struct attribute *runtime_attrs[] = {
+#ifndef CONFIG_PM_ADVANCED_DEBUG
+ &dev_attr_runtime_status.attr,
+#endif
+ &dev_attr_control.attr,
+ &dev_attr_runtime_suspended_time.attr,
+ &dev_attr_runtime_active_time.attr,
+ &dev_attr_autosuspend_delay_ms.attr,
+ NULL,
+};
+static struct attribute_group pm_runtime_attr_group = {
+ .name = power_group_name,
+ .attrs = runtime_attrs,
+};
+
+int dpm_sysfs_add(struct device *dev)
+{
+ int rc;
+
+ rc = sysfs_create_group(&dev->kobj, &pm_attr_group);
+ if (rc == 0 && !dev->power.no_callbacks) {
+ rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group);
+ if (rc)
+ sysfs_remove_group(&dev->kobj, &pm_attr_group);
+ }
+ return rc;
+}
+
+void rpm_sysfs_remove(struct device *dev)
+{
+ sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
+}
+
+void dpm_sysfs_remove(struct device *dev)
+{
+ rpm_sysfs_remove(dev);
+ sysfs_remove_group(&dev->kobj, &pm_attr_group);
+}
+
+#else /* CONFIG_PM_RUNTIME */
+
int dpm_sysfs_add(struct device * dev)
{
return sysfs_create_group(&dev->kobj, &pm_attr_group);
@@ -255,3 +400,5 @@ void dpm_sysfs_remove(struct device * dev)
{
sysfs_remove_group(&dev->kobj, &pm_attr_group);
}
+
+#endif
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
new file mode 100644
index 00000000000..046b43bfcdb
--- /dev/null
+++ b/drivers/base/soc.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Maxime Coquelin <maxime.coquelin-nonst@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+struct kobject *soc_object;
+
+ssize_t show_soc_info(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct sysfs_soc_info *si = container_of(attr,
+ struct sysfs_soc_info, attr);
+
+ if (si->info)
+ return sprintf(buf, "%s\n", si->info);
+
+ return si->get_info(buf, si);
+}
+
+int __init register_sysfs_soc_info(struct sysfs_soc_info *info, int nb_info)
+{
+ int i, ret;
+
+ for (i = 0; i < nb_info; i++) {
+ ret = sysfs_create_file(soc_object, &info[i].attr.attr);
+ if (ret) {
+ for (i -= 1; i >= 0; i--)
+ sysfs_remove_file(soc_object, &info[i].attr.attr);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static struct attribute *soc_attrs[] = {
+ NULL,
+};
+
+static struct attribute_group soc_attr_group = {
+ .attrs = soc_attrs,
+};
+
+int __init register_sysfs_soc(struct sysfs_soc_info *info, size_t num)
+{
+ int ret;
+
+ soc_object = kobject_create_and_add("socinfo", NULL);
+ if (!soc_object) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ret = sysfs_create_group(soc_object, &soc_attr_group);
+ if (ret)
+ goto kset_exit;
+
+ ret = register_sysfs_soc_info(info, num);
+ if (ret)
+ goto group_exit;
+
+ return 0;
+
+group_exit:
+ sysfs_remove_group(soc_object, &soc_attr_group);
+kset_exit:
+ kobject_put(soc_object);
+exit:
+ return ret;
+}
+
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 058fbccf2f5..f34ceb744b6 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -207,4 +207,11 @@ config BT_ATH3K
Say Y here to compile support for "Atheros firmware download driver"
into the kernel or say M to compile it as module (ath3k).
+config BT_CG2900
+ tristate "ST-Ericsson CG2900 driver"
+ depends on MFD_CG2900 && BT
+ help
+ Select if ST-Ericsson CG2900 Connectivity controller shall be used as
+ Bluetooth controller for BlueZ.
+
endmenu
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 7e5aed59812..c3a35398521 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -16,8 +16,11 @@ obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
obj-$(CONFIG_BT_ATH3K) += ath3k.o
+obj-$(CONFIG_BT_CG2900) += btcg2900.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
+obj-$(CONFIG_MFD_CG2900_UART) += cg2900_uart.o
+
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
diff --git a/drivers/bluetooth/btcg2900.c b/drivers/bluetooth/btcg2900.c
new file mode 100644
index 00000000000..46a3f9da4bc
--- /dev/null
+++ b/drivers/bluetooth/btcg2900.c
@@ -0,0 +1,1203 @@
+/*
+ * Bluetooth driver for ST-Ericsson CG2900 connectivity controller.
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com)
+ * Henrik Possung (henrik.possung@stericsson.com)
+ * Josef Kindberg (josef.kindberg@stericsson.com)
+ * Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com)
+ * Kjell Andersson (kjell.k.andersson@stericsson.com)
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <asm/byteorder.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/cg2900.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+#include <net/bluetooth/hci_core.h>
+
+#define BT_HEADER_LENGTH 0x03
+
+#define STLC2690_HCI_REV 0x0600
+#define CG2900_PG1_HCI_REV 0x0101
+#define CG2900_PG2_HCI_REV 0x0200
+#define CG2900_PG1_SPECIAL_HCI_REV 0x0700
+
+#define NAME "BTCG2900 "
+
+/* Wait for 5 seconds for a response to our requests */
+#define RESP_TIMEOUT 5000
+
+/* Bluetooth error codes */
+#define HCI_ERR_NO_ERROR 0x00
+#define HCI_ERR_CMD_DISALLOWED 0x0C
+
+/**
+ * enum reset_state - RESET-states of the HCI driver.
+ *
+ * @RESET_IDLE: No reset in progress.
+ * @RESET_ACTIVATED: Reset in progress.
+ * @RESET_UNREGISTERED: hdev is unregistered.
+ */
+
+enum reset_state {
+ RESET_IDLE,
+ RESET_ACTIVATED,
+ RESET_UNREGISTERED
+};
+
+/**
+ * enum enable_state - ENABLE-states of the HCI driver.
+ *
+ * @ENABLE_IDLE: The HCI driver is loaded but not opened.
+ * @ENABLE_WAITING_BT_ENABLED_CC: The HCI driver is waiting for a command
+ * complete event from the BT chip as a
+ * response to a BT Enable (true) command.
+ * @ENABLE_BT_ENABLED: The BT chip is enabled.
+ * @ENABLE_WAITING_BT_DISABLED_CC: The HCI driver is waiting for a command
+ * complete event from the BT chip as a
+ * response to a BT Enable (false) command.
+ * @ENABLE_BT_DISABLED: The BT chip is disabled.
+ * @ENABLE_BT_ERROR: The HCI driver is in a bad state, some
+ * thing has failed and is not expected to
+ * work properly.
+ */
+enum enable_state {
+ ENABLE_IDLE,
+ ENABLE_WAITING_BT_ENABLED_CC,
+ ENABLE_BT_ENABLED,
+ ENABLE_WAITING_BT_DISABLED_CC,
+ ENABLE_BT_DISABLED,
+ ENABLE_BT_ERROR
+};
+
+/* Defines which state the driver has when BT is active */
+#define BTCG2900_ACTIVE_STATE ENABLE_BT_ENABLED
+
+/**
+ * struct btcg2900_info - Specifies HCI driver private data.
+ *
+ * This type specifies CG2900 HCI driver private data.
+ *
+ * @list: list_head struct.
+ * @parent: Parent to this BT device. All BT channels will have
+ * common parent.
+ * @cmd: Device structure for BT command channel.
+ * @evt: Device structure for BT event channel.
+ * @acl: Device structure for BT ACL channel.
+ * @pdev: Device structure for platform device.
+ * @hdev: Device structure for HCI device.
+ * @reset_state: Device enum for HCI driver reset state.
+ * @enable_state: Device enum for HCI driver BT enable state.
+ */
+struct btcg2900_info {
+ struct list_head list;
+ struct device *parent;
+ struct device *cmd;
+ struct device *evt;
+ struct device *acl;
+ struct hci_dev *hdev;
+ enum reset_state reset_state;
+ enum enable_state enable_state;
+};
+
+/**
+ * struct enable_info - Specifies data for sending enable commands.
+ *
+ * @enable: True if command should enable the functionality.
+ * @name: Name of the command, only informative.
+ * @get_cmd: Function for retrieving command.
+ * @success: State to set upon success.
+ * @awaiting_cc: State to set while waiting for response.
+ * @failed: State to set upon failure.
+ */
+struct enable_info {
+ bool enable;
+ char *name;
+ struct sk_buff* (*get_cmd)(struct btcg2900_info *info, bool enable);
+ enum enable_state success;
+ enum enable_state awaiting_cc;
+ enum enable_state failed;
+};
+
+/**
+ * struct dev_info - Specifies private data used when receiving callbacks from CG2900 driver.
+ *
+ * @hci_data_type: Type of data according to BlueZ.
+ */
+struct dev_info {
+ u8 hci_data_type;
+};
+
+/* Defines for vs_bt_enable_cmd */
+#define BT_VS_BT_ENABLE 0xFF10
+#define VS_BT_DISABLE 0x00
+#define VS_BT_ENABLE 0x01
+
+/**
+ * struct vs_bt_enable_cmd - Specifies HCI VS Bluetooth_Enable command.
+ *
+ * @op_code: HCI command op code.
+ * @len: Parameter length of command.
+ * @enable: 0 for disable BT, 1 for enable BT.
+ */
+struct vs_bt_enable_cmd {
+ __le16 op_code;
+ u8 len;
+ u8 enable;
+} __packed;
+
+/*
+ * hci_wait_queue - Main Wait Queue in HCI driver.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(hci_wait_queue);
+
+/*
+ * btcg2900_devices - List of active CG2900 BT devices.
+ */
+static LIST_HEAD(btcg2900_devices);
+
+/* Internal function declarations */
+static int register_bluetooth(struct btcg2900_info *info);
+
+/* Internal functions */
+
+/**
+ * get_bt_enable_cmd() - Get HCI BT enable command.
+ * @info: Device info structure.
+ * @bt_enable: true if Bluetooth IP shall be enabled, false otherwise.
+ *
+ * Returns:
+ * NULL if no command shall be sent,
+ * sk_buffer with command otherwise.
+ */
+static struct sk_buff *get_bt_enable_cmd(struct btcg2900_info *info,
+ bool bt_enable)
+{
+ struct sk_buff *skb;
+ struct vs_bt_enable_cmd *cmd;
+ struct cg2900_rev_data rev_data;
+ struct cg2900_user_data *pf_data;
+
+ pf_data = dev_get_platdata(info->cmd);
+
+ if (!pf_data->get_local_revision(pf_data, &rev_data)) {
+ BT_ERR(NAME "Couldn't get revision");
+ return NULL;
+ }
+
+ /* If connected chip does not support the command return NULL */
+ if (CG2900_PG1_SPECIAL_HCI_REV != rev_data.revision &&
+ CG2900_PG1_HCI_REV != rev_data.revision &&
+ CG2900_PG2_HCI_REV != rev_data.revision)
+ return NULL;
+
+ /* CG2900 used */
+ skb = pf_data->alloc_skb(sizeof(*cmd), GFP_KERNEL);
+ if (!skb) {
+ BT_ERR(NAME "Could not allocate skb");
+ return NULL;
+ }
+
+ cmd = (struct vs_bt_enable_cmd *)skb_put(skb, sizeof(*cmd));
+ cmd->op_code = cpu_to_le16(BT_VS_BT_ENABLE);
+ cmd->len = sizeof(*cmd) - BT_HEADER_LENGTH;
+ if (bt_enable)
+ cmd->enable = VS_BT_ENABLE;
+ else
+ cmd->enable = VS_BT_DISABLE;
+
+ return skb;
+}
+
+/**
+ * close_bt_users() - Close all BT channels.
+ * @info: HCI driver info structure.
+ */
+static void close_bt_users(struct btcg2900_info *info)
+{
+ struct cg2900_user_data *pf_data;
+
+ pf_data = dev_get_platdata(info->cmd);
+ if (pf_data->opened)
+ pf_data->close(pf_data);
+
+ pf_data = dev_get_platdata(info->acl);
+ if (pf_data->opened)
+ pf_data->close(pf_data);
+
+ pf_data = dev_get_platdata(info->evt);
+ if (pf_data->opened)
+ pf_data->close(pf_data);
+}
+
+/**
+ * handle_bt_enable_comp() - Handle received BtEnable Complete event.
+ * @info: Info structure.
+ * @skb: Buffer with data coming from device.
+ *
+ * Returns:
+ * true if data has been handled internally,
+ * false otherwise.
+ */
+static bool handle_bt_enable_comp(struct btcg2900_info *info, u8 status)
+{
+ if (info->enable_state != ENABLE_WAITING_BT_ENABLED_CC &&
+ info->enable_state != ENABLE_WAITING_BT_DISABLED_CC)
+ return false;
+ /*
+ * This is the command complete event for
+ * the HCI_Cmd_VS_Bluetooth_Enable.
+ * Check result and update state.
+ *
+ * The BT chip is enabled/disabled. Either it was enabled/
+ * disabled now (status NO_ERROR) or it was already enabled/
+ * disabled (assuming status CMD_DISALLOWED is already enabled/
+ * disabled).
+ */
+ if (status != HCI_ERR_NO_ERROR && status != HCI_ERR_CMD_DISALLOWED) {
+ BT_ERR(NAME "Could not enable/disable BT core (0x%X)",
+ status);
+ BT_DBG("New enable_state: ENABLE_BT_ERROR");
+ info->enable_state = ENABLE_BT_ERROR;
+ goto finished;
+ }
+
+ if (info->enable_state == ENABLE_WAITING_BT_ENABLED_CC) {
+ BT_DBG("New enable_state: ENABLE_BT_ENABLED");
+ info->enable_state = ENABLE_BT_ENABLED;
+ BT_INFO("CG2900 BT core is enabled");
+ } else {
+ BT_DBG("New enable_state: ENABLE_BT_DISABLED");
+ info->enable_state = ENABLE_BT_DISABLED;
+ BT_INFO("CG2900 BT core is disabled");
+ }
+
+finished:
+ /* Wake up whoever is waiting for this result. */
+ wake_up_all(&hci_wait_queue);
+ return true;
+}
+
+/**
+ * handle_bt_enable_stat() - Handle received BtEnable Status event.
+ * @info: Info structure.
+ * @skb: Buffer with data coming from device.
+ *
+ * Returns:
+ * true if data has been handled internally,
+ * false otherwise.
+ */
+static bool handle_bt_enable_stat(struct btcg2900_info *info, u8 status)
+{
+ if (info->enable_state != ENABLE_WAITING_BT_DISABLED_CC &&
+ info->enable_state != ENABLE_WAITING_BT_ENABLED_CC)
+ return false;
+
+ BT_DBG("HCI Driver received Command Status (BT enable): 0x%X", status);
+ /*
+ * This is the command status event for the HCI_Cmd_VS_Bluetooth_Enable.
+ * Just free the packet.
+ */
+ return true;
+}
+
+/**
+ * handle_rx_evt() - Check if received data is response to internal command.
+ * @info: Info structure.
+ * @skb: Buffer with data coming from device.
+ *
+ * Returns:
+ * true if data has been handled internally,
+ * false otherwise.
+ */
+static bool handle_rx_evt(struct btcg2900_info *info, struct sk_buff *skb)
+{
+ struct hci_event_hdr *evt = (struct hci_event_hdr *)skb->data;
+ struct hci_ev_cmd_complete *cmd_complete;
+ struct hci_ev_cmd_status *cmd_status;
+ u16 op_code;
+ u8 status;
+ bool pkt_handled = false;
+
+ /* If BT is active no internal packets shall be generated */
+ if (info->enable_state == BTCG2900_ACTIVE_STATE)
+ return false;
+
+ if (evt->evt == HCI_EV_CMD_COMPLETE) {
+ cmd_complete = (struct hci_ev_cmd_complete *)(evt + 1);
+ status = *((u8 *)(cmd_complete + 1));
+ op_code = le16_to_cpu(cmd_complete->opcode);
+
+ if (op_code == BT_VS_BT_ENABLE)
+ pkt_handled = handle_bt_enable_comp(info, status);
+ } else if (evt->evt == HCI_EV_CMD_STATUS) {
+ cmd_status = (struct hci_ev_cmd_status *)(evt + 1);
+ op_code = le16_to_cpu(cmd_status->opcode);
+ status = cmd_status->status;
+
+ if (op_code == BT_VS_BT_ENABLE)
+ pkt_handled = handle_bt_enable_stat(info, status);
+ }
+
+ if (pkt_handled)
+ kfree_skb(skb);
+
+ return pkt_handled;
+}
+
+/**
+ * hci_read_cb() - Callback for handling data received from CG2900 driver.
+ * @dev: Device receiving data.
+ * @skb: Buffer with data coming from device.
+ */
+static void hci_read_cb(struct cg2900_user_data *user, struct sk_buff *skb)
+{
+ int err = 0;
+ struct dev_info *dev_info;
+ struct btcg2900_info *info;
+
+ dev_info = cg2900_get_usr(user);
+ info = dev_get_drvdata(user->dev);
+
+ if (user->dev != info->evt || !handle_rx_evt(info, skb)) {
+ bt_cb(skb)->pkt_type = dev_info->hci_data_type;
+ skb->dev = (struct net_device *)info->hdev;
+ /* Update BlueZ stats */
+ info->hdev->stat.byte_rx += skb->len;
+ if (bt_cb(skb)->pkt_type == HCI_ACLDATA_PKT)
+ info->hdev->stat.acl_rx++;
+ else
+ info->hdev->stat.evt_rx++;
+
+ BT_DBG("Data receive %d bytes", skb->len);
+
+ /* Provide BlueZ with received frame*/
+ err = hci_recv_frame(skb);
+ /* If err, skb have been freed in hci_recv_frame() */
+ if (err)
+ BT_ERR(NAME "Failed in supplying packet to Bluetooth"
+ " stack (%d)", err);
+ }
+}
+
+/**
+ * hci_reset_cb() - Callback for handling reset from CG2900 driver.
+ * @dev: CPD device resetting.
+ */
+static void hci_reset_cb(struct cg2900_user_data *dev)
+{
+ int err;
+ struct btcg2900_info *info;
+ struct cg2900_user_data *pf_data;
+
+ BT_INFO(NAME "hci_reset_cb");
+
+ info = dev_get_drvdata(dev->dev);
+
+ BT_DBG("New reset_state: RESET_ACTIVATED");
+ info->reset_state = RESET_ACTIVATED;
+
+ /*
+ * Continue to deregister hdev if all channels has been reset else
+ * return.
+ */
+ pf_data = dev_get_platdata(info->acl);
+ if (pf_data->opened)
+ return;
+ pf_data = dev_get_platdata(info->cmd);
+ if (pf_data->opened)
+ return;
+ pf_data = dev_get_platdata(info->evt);
+ if (pf_data->opened)
+ return;
+
+ /*
+ * Deregister HCI device. Close and Destruct functions should
+ * in turn be called by BlueZ.
+ */
+ BT_DBG("Deregister HCI device");
+ err = hci_unregister_dev(info->hdev);
+ if (err)
+ BT_ERR(NAME "Can not deregister HCI device! (%d)", err);
+ /*
+ * Now we are in trouble. Try to register a new hdev
+ * anyway even though this will cost some memory.
+ */
+
+ wait_event_timeout(hci_wait_queue,
+ (RESET_UNREGISTERED == info->reset_state),
+ msecs_to_jiffies(RESP_TIMEOUT));
+ if (RESET_UNREGISTERED != info->reset_state)
+ /*
+ * Now we are in trouble. Try to register a new hdev
+ * anyway even though this will cost some memory.
+ */
+ BT_ERR(NAME "Timeout expired. Could not deregister HCI device");
+
+ /* Init and register hdev */
+ BT_DBG("Register HCI device");
+ err = register_bluetooth(info);
+ if (err)
+ BT_ERR(NAME "HCI Device registration error (%d)", err);
+}
+
+/**
+ * send_enable_cmd() - Send a command with only enable/disable functionality.
+ * @info: Info structure.
+ * @en_info: Enable info structure.
+ *
+ * Returns:
+ * 0 if successful,
+ * -EACCES if correct response to command is not received,
+ * Error codes from CG2900 write.
+ */
+static int send_enable_cmd(struct btcg2900_info *info,
+ struct enable_info *en_info)
+{
+ struct sk_buff *enable_cmd;
+ int err;
+ struct cg2900_user_data *pf_data;
+
+ /*
+ * Call function that returns the chip dependent enable HCI command.
+ * If NULL is returned, then no bt_enable command should be sent to the
+ * chip.
+ */
+ enable_cmd = en_info->get_cmd(info, en_info->enable);
+ if (!enable_cmd) {
+ BT_DBG("%s New enable_state: %d", en_info->name,
+ en_info->success);
+ info->enable_state = en_info->success;
+ return 0;
+ }
+
+ /* Set the HCI state before sending command to chip. */
+ BT_DBG("%s New enable_state: %d", en_info->name, en_info->awaiting_cc);
+ info->enable_state = en_info->awaiting_cc;
+
+ /* Send command to chip */
+ pf_data = dev_get_platdata(info->cmd);
+ err = pf_data->write(pf_data, enable_cmd);
+ if (err) {
+ BT_ERR("Couldn't send %s command (%d)", en_info->name, err);
+ kfree_skb(enable_cmd);
+ info->enable_state = en_info->failed;
+ return err;
+ }
+
+ /*
+ * Wait for callback to receive command complete and then wake us up
+ * again.
+ */
+ wait_event_timeout(hci_wait_queue,
+ info->enable_state == en_info->success,
+ msecs_to_jiffies(RESP_TIMEOUT));
+ /* Check the current state to see if it worked */
+ if (info->enable_state != en_info->success) {
+ BT_ERR("Could not change %s state (%d)",
+ en_info->name, info->enable_state);
+ BT_DBG("%s New enable_state: %d", en_info->name,
+ en_info->failed);
+ info->enable_state = en_info->failed;
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+/**
+ * btcg2900_open() - Open HCI interface.
+ * @hdev: HCI device being opened.
+ *
+ * BlueZ callback function for opening HCI interface to device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL if NULL pointer is supplied.
+ * -EOPNOTSUPP if supplied packet type is not supported.
+ * -EBUSY if device is already opened.
+ * -EACCES if opening of channels failed.
+ */
+static int btcg2900_open(struct hci_dev *hdev)
+{
+ struct btcg2900_info *info;
+ struct cg2900_user_data *pf_data;
+ int err;
+ struct enable_info en_info;
+
+ BT_INFO("Open ST-Ericsson CG2900 driver");
+
+ if (!hdev) {
+ BT_ERR(NAME "NULL supplied for hdev");
+ return -EINVAL;
+ }
+
+ info = (struct btcg2900_info *)hdev->driver_data;
+ if (!info) {
+ BT_ERR(NAME "NULL supplied for driver_data");
+ return -EINVAL;
+ }
+
+ if (test_and_set_bit(HCI_RUNNING, &(hdev->flags))) {
+ BT_ERR(NAME "Device already opened!");
+ return -EBUSY;
+ }
+
+ pf_data = dev_get_platdata(info->acl);
+ err = pf_data->open(pf_data);
+ if (err) {
+ BT_ERR("Couldn't open BT ACL channel (%d)", err);
+ goto handle_error;
+ }
+
+ pf_data = dev_get_platdata(info->cmd);
+ err = pf_data->open(pf_data);
+ if (err) {
+ BT_ERR("Couldn't open BT CMD channel (%d)", err);
+ goto handle_error;
+ }
+
+ pf_data = dev_get_platdata(info->evt);
+ err = pf_data->open(pf_data);
+ if (err) {
+ BT_ERR("Couldn't open BT EVT channel (%d)", err);
+ goto handle_error;
+ }
+
+ if (info->reset_state == RESET_ACTIVATED) {
+ BT_DBG("New reset_state: RESET_IDLE");
+ info->reset_state = RESET_IDLE;
+ }
+
+ /* First enable the BT core */
+ en_info.enable = true;
+ en_info.get_cmd = get_bt_enable_cmd;
+ en_info.name = "VS BT Enable (true)";
+ en_info.success = ENABLE_BT_ENABLED;
+ en_info.awaiting_cc = ENABLE_WAITING_BT_ENABLED_CC;
+ en_info.failed = ENABLE_BT_DISABLED;
+
+ err = send_enable_cmd(info, &en_info);
+ if (err) {
+ BT_ERR("Couldn't enable BT core (%d)", err);
+ goto handle_error;
+ }
+
+ return 0;
+
+handle_error:
+ close_bt_users(info);
+ clear_bit(HCI_RUNNING, &(hdev->flags));
+ return err;
+
+}
+
+/**
+ * btcg2900_close() - Close HCI interface.
+ * @hdev: HCI device being closed.
+ *
+ * BlueZ callback function for closing HCI interface.
+ * It flushes the interface first.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL if NULL pointer is supplied.
+ * -EOPNOTSUPP if supplied packet type is not supported.
+ * -EBUSY if device is not opened.
+ */
+static int btcg2900_close(struct hci_dev *hdev)
+{
+ struct btcg2900_info *info = NULL;
+ int err;
+ struct enable_info en_info;
+
+ BT_DBG("btcg2900_close");
+
+ if (!hdev) {
+ BT_ERR(NAME "NULL supplied for hdev");
+ return -EINVAL;
+ }
+
+ info = (struct btcg2900_info *)hdev->driver_data;
+ if (!info) {
+ BT_ERR(NAME "NULL supplied for driver_data");
+ return -EINVAL;
+ }
+
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) {
+ BT_ERR(NAME "Device already closed!");
+ return -EBUSY;
+ }
+
+ /* Do not do this if there is an reset ongoing */
+ if (info->reset_state == RESET_ACTIVATED)
+ goto remove_users;
+
+ /* Now disable the BT core */
+ en_info.enable = false;
+ en_info.get_cmd = get_bt_enable_cmd;
+ en_info.name = "VS BT Enable (false)";
+ en_info.success = ENABLE_BT_DISABLED;
+ en_info.awaiting_cc = ENABLE_WAITING_BT_DISABLED_CC;
+ en_info.failed = ENABLE_BT_ENABLED;
+
+ err = send_enable_cmd(info, &en_info);
+ if (err)
+ BT_ERR("Couldn't disable BT core (%d)", err);
+
+remove_users:
+ /* Finally deregister all users and free allocated data */
+ close_bt_users(info);
+ return 0;
+}
+
+/**
+ * btcg2900_send() - Send packet to device.
+ * @skb: sk buffer to be sent.
+ *
+ * BlueZ callback function for sending sk buffer.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL if NULL pointer is supplied.
+ * -EOPNOTSUPP if supplied packet type is not supported.
+ * Error codes from cg2900_write.
+ */
+static int btcg2900_send(struct sk_buff *skb)
+{
+ struct hci_dev *hdev;
+ struct btcg2900_info *info;
+ struct cg2900_user_data *pf_data;
+ int err = 0;
+
+ if (!skb) {
+ BT_ERR(NAME "NULL supplied for skb");
+ return -EINVAL;
+ }
+
+ hdev = (struct hci_dev *)(skb->dev);
+ if (!hdev) {
+ BT_ERR(NAME "NULL supplied for hdev");
+ return -EINVAL;
+ }
+
+ info = (struct btcg2900_info *)hdev->driver_data;
+ if (!info) {
+ BT_ERR(NAME "NULL supplied for info");
+ return -EINVAL;
+ }
+
+ /* Update BlueZ stats */
+ hdev->stat.byte_tx += skb->len;
+
+ BT_DBG("Data transmit %d bytes", skb->len);
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ BT_DBG("Sending HCI_COMMAND_PKT");
+ pf_data = dev_get_platdata(info->cmd);
+ err = pf_data->write(pf_data, skb);
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ BT_DBG("Sending HCI_ACLDATA_PKT");
+ pf_data = dev_get_platdata(info->acl);
+ err = pf_data->write(pf_data, skb);
+ hdev->stat.acl_tx++;
+ break;
+ default:
+ BT_ERR(NAME "Trying to transmit unsupported packet type"
+ " (0x%.2X)", bt_cb(skb)->pkt_type);
+ err = -EOPNOTSUPP;
+ break;
+ };
+
+ return err;
+}
+
+/**
+ * btcg2900_destruct() - Destruct HCI interface.
+ * @hdev: HCI device being destructed.
+ */
+static void btcg2900_destruct(struct hci_dev *hdev)
+{
+ struct btcg2900_info *info;
+
+ BT_DBG("btcg2900_destruct");
+
+ info = hdev->driver_data;
+ if (!info) {
+ BT_ERR(NAME "NULL supplied for info");
+ return;
+ }
+
+ /*
+ * When destruct is called it means that the Bluetooth stack is done
+ * with the HCI device and we can now free it.
+ * Normally we do this only when removing the whole module through
+ * btcg2900_remove(), but when being reset we free the device here and
+ * we then set the reset state so that the reset handler can allocate a
+ * new HCI device and then register it to the Bluetooth stack.
+ */
+ if (info->reset_state == RESET_ACTIVATED) {
+ if (info->hdev) {
+ hci_free_dev(info->hdev);
+ info->hdev = NULL;
+ }
+ BT_DBG("New reset_state: RESET_UNREGISTERED");
+ info->reset_state = RESET_UNREGISTERED;
+ wake_up_all(&hci_wait_queue);
+ }
+}
+
+/**
+ * get_info() - Return info structure for this device.
+ * @dev: Current device.
+ *
+ * Returns:
+ * Pointer to info struct if there is no error.
+ * ERR_PTR(-ENOMEM) if allocation fails.
+ */
+static struct btcg2900_info *get_info(struct device *dev)
+{
+ struct list_head *cursor;
+ struct btcg2900_info *tmp;
+ struct btcg2900_info *info = NULL;
+
+ /* Find the info structure */
+ list_for_each(cursor, &btcg2900_devices) {
+ tmp = list_entry(cursor, struct btcg2900_info, list);
+ if (tmp->parent == dev->parent) {
+ info = tmp;
+ break;
+ }
+ }
+
+ if (info)
+ return info;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ BT_ERR("Could not allocate info struct");
+ return ERR_PTR(-ENOMEM);
+ }
+ info->parent = dev->parent;
+ list_add_tail(&info->list, &btcg2900_devices);
+ BT_DBG("CG2900 device added");
+ return info;
+}
+
+/**
+ * device_removed() - Remove device from list if there are no channels left.
+ * @info: BTCG2900 info structure.
+ */
+static void device_removed(struct btcg2900_info *info)
+{
+ struct list_head *cursor;
+ struct btcg2900_info *tmp;
+
+ if (info->acl || info->cmd || info->evt)
+ /* There are still devices active */
+ return;
+
+ /* Find the info structure and delete it */
+ list_for_each(cursor, &btcg2900_devices) {
+ tmp = list_entry(cursor, struct btcg2900_info, list);
+ if (tmp == info) {
+ list_del(cursor);
+ break;
+ }
+ }
+ kfree(info);
+ BT_DBG("CG2900 device removed");
+}
+
+/**
+ * register_bluetooth() - Initialize module.
+ *
+ * Alloc, init, and register HCI device to BlueZ.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ENOMEM if allocation fails.
+ * Error codes from hci_register_dev.
+ */
+static int register_bluetooth(struct btcg2900_info *info)
+{
+ int err;
+ struct cg2900_user_data *pf_data;
+
+ /* Check if all channels have been probed */
+ if (!info->acl || !info->cmd || !info->evt)
+ return 0;
+
+ pf_data = dev_get_platdata(info->cmd);
+
+ info->hdev = hci_alloc_dev();
+ if (!info->hdev) {
+ BT_ERR("Could not allocate mem for CG2900 BT driver");
+ return -ENOMEM;
+ }
+
+ SET_HCIDEV_DEV(info->hdev, info->parent);
+ info->hdev->bus = pf_data->channel_data.bt_bus;
+ info->hdev->driver_data = info;
+ info->hdev->owner = THIS_MODULE;
+ info->hdev->open = btcg2900_open;
+ info->hdev->close = btcg2900_close;
+ info->hdev->send = btcg2900_send;
+ info->hdev->destruct = btcg2900_destruct;
+
+ err = hci_register_dev(info->hdev);
+ if (err) {
+ BT_ERR("Can not register BTCG2900 HCI device (%d)", err);
+ hci_free_dev(info->hdev);
+ info->hdev = NULL;
+ }
+
+ BT_INFO("CG2900 registered");
+
+ BT_DBG("New enable_state: ENABLE_IDLE");
+ info->enable_state = ENABLE_IDLE;
+ BT_DBG("New reset_state: RESET_IDLE");
+ info->reset_state = RESET_IDLE;
+
+ return err;
+}
+
+/**
+ * probe_common() - Initialize channel and register to BT stack.
+ * @dev: Current device.
+ * @info: BTCG2900 info structure.
+ * @hci_data_type: Data type of this channel, e.g. ACL.
+ *
+ * Allocate and initialize private data. Register to Bluetooth stack.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ENOMEM if allocation fails.
+ * Error codes from register_bluetooth.
+ */
+static int probe_common(struct device *dev,
+ struct btcg2900_info *info,
+ u8 hci_data_type)
+{
+ int err;
+ struct cg2900_user_data *pf_data;
+ struct dev_info *dev_info;
+
+ dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
+ if (!dev_info) {
+ BT_ERR("Could not allocate dev_info");
+ return -ENOMEM;
+ }
+
+ dev_set_drvdata(dev, info);
+
+ pf_data = dev_get_platdata(dev);
+ pf_data->dev = dev;
+ pf_data->read_cb = hci_read_cb;
+ pf_data->reset_cb = hci_reset_cb;
+
+ /* Init and register hdev */
+ err = register_bluetooth(info);
+ if (err) {
+ BT_ERR("HCI Device registration error (%d)", err);
+ kfree(dev_info);
+ return err;
+ }
+ dev_info->hci_data_type = hci_data_type;
+ cg2900_set_usr(pf_data, dev_info);
+
+ return 0;
+}
+
+/**
+ * btcg2900_cmd_probe() - Initialize command channel.
+ * @pdev: Platform device.
+ *
+ * Allocate and initialize private data.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Error codes from get_info and probe_common.
+ */
+static int __devinit btcg2900_cmd_probe(struct platform_device *pdev)
+{
+ int err;
+ struct btcg2900_info *info;
+
+ BT_DBG("Starting CG2900 Command channel");
+
+ info = get_info(&pdev->dev);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ info->cmd = &pdev->dev;
+
+ err = probe_common(&pdev->dev, info, HCI_COMMAND_PKT);
+ if (err) {
+ BT_ERR("Failed to initialize channel");
+ info->cmd = NULL;
+ device_removed(info);
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * btcg2900_acl_probe() - Initialize command channel.
+ * @pdev: Platform device.
+ *
+ * Allocate and initialize private data.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Error codes from get_info and probe_common.
+ */
+static int __devinit btcg2900_acl_probe(struct platform_device *pdev)
+{
+ int err;
+ struct btcg2900_info *info;
+
+ BT_DBG("Starting CG2900 ACL channel");
+
+ info = get_info(&pdev->dev);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ info->acl = &pdev->dev;
+
+ err = probe_common(&pdev->dev, info, HCI_ACLDATA_PKT);
+ if (err) {
+ BT_ERR("Failed to initialize channel");
+ info->acl = NULL;
+ device_removed(info);
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * btcg2900_evt_probe() - Initialize event channel.
+ * @pdev: Platform device.
+ *
+ * Allocate and initialize private data.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Error codes from get_info and probe_common.
+ */
+static int __devinit btcg2900_evt_probe(struct platform_device *pdev)
+{
+ int err;
+ struct btcg2900_info *info;
+
+ BT_DBG("Starting CG2900 Event channel");
+
+ info = get_info(&pdev->dev);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ info->evt = &pdev->dev;
+
+ err = probe_common(&pdev->dev, info, HCI_EVENT_PKT);
+ if (err) {
+ BT_ERR("Failed to initialize channel");
+ info->evt = NULL;
+ device_removed(info);
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * remove_common() - Remove channel.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Error codes from hci_unregister_dev.
+ */
+static int remove_common(struct device *dev,
+ struct btcg2900_info *info)
+{
+ int err = 0;
+ struct cg2900_user_data *pf_data;
+ struct dev_info *dev_info;
+
+ pf_data = dev_get_platdata(dev);
+ dev_info = cg2900_get_usr(pf_data);
+
+ kfree(dev_info);
+ cg2900_set_usr(pf_data, NULL);
+
+ if (!info->hdev)
+ goto finished;
+
+ BT_INFO("Unregistering CG2900");
+ info->hdev->driver_data = NULL;
+ err = hci_unregister_dev(info->hdev);
+ if (err)
+ BT_ERR("Can not unregister HCI device (%d)", err);
+ hci_free_dev(info->hdev);
+ info->hdev = NULL;
+
+finished:
+ device_removed(info);
+ return err;
+}
+
+/**
+ * btcg2900_cmd_remove() - Remove command channel.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Error codes from remove_common.
+ */
+static int __devexit btcg2900_cmd_remove(struct platform_device *pdev)
+{
+ struct btcg2900_info *info;
+
+ BT_DBG("Removing CG2900 Command channel");
+
+ info = dev_get_drvdata(&pdev->dev);
+ info->cmd = NULL;
+ return remove_common(&pdev->dev, info);
+}
+
+/**
+ * btcg2900_acl_remove() - Remove ACL channel.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Error codes from remove_common.
+ */
+static int __devexit btcg2900_acl_remove(struct platform_device *pdev)
+{
+ struct btcg2900_info *info;
+
+ BT_DBG("Removing CG2900 ACL channel");
+
+ info = dev_get_drvdata(&pdev->dev);
+ info->acl = NULL;
+ return remove_common(&pdev->dev, info);
+}
+
+/**
+ * btcg2900_evt_remove() - Remove event channel.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Error codes from remove_common.
+ */
+static int __devexit btcg2900_evt_remove(struct platform_device *pdev)
+{
+ struct btcg2900_info *info;
+
+ BT_DBG("Removing CG2900 Event channel");
+
+ info = dev_get_drvdata(&pdev->dev);
+ info->evt = NULL;
+ return remove_common(&pdev->dev, info);
+}
+
+static struct platform_driver btcg2900_cmd_driver = {
+ .driver = {
+ .name = "cg2900-btcmd",
+ .owner = THIS_MODULE,
+ },
+ .probe = btcg2900_cmd_probe,
+ .remove = __devexit_p(btcg2900_cmd_remove),
+};
+
+static struct platform_driver btcg2900_acl_driver = {
+ .driver = {
+ .name = "cg2900-btacl",
+ .owner = THIS_MODULE,
+ },
+ .probe = btcg2900_acl_probe,
+ .remove = __devexit_p(btcg2900_acl_remove),
+};
+
+static struct platform_driver btcg2900_evt_driver = {
+ .driver = {
+ .name = "cg2900-btevt",
+ .owner = THIS_MODULE,
+ },
+ .probe = btcg2900_evt_probe,
+ .remove = __devexit_p(btcg2900_evt_remove),
+};
+
+/**
+ * btcg2900_init() - Initialize module.
+ *
+ * Registers platform driver.
+ */
+static int __init btcg2900_init(void)
+{
+ int err;
+
+ BT_DBG("btcg2900_init");
+
+ err = platform_driver_register(&btcg2900_cmd_driver);
+ if (err) {
+ BT_ERR("Failed to register cmd (%d)", err);
+ return err;
+ }
+ err = platform_driver_register(&btcg2900_acl_driver);
+ if (err) {
+ BT_ERR("Failed to register acl (%d)", err);
+ return err;
+ }
+ err = platform_driver_register(&btcg2900_evt_driver);
+ if (err) {
+ BT_ERR("Failed to register evt (%d)", err);
+ return err;
+ }
+ return err;
+}
+
+/**
+ * btcg2900_exit() - Remove module.
+ *
+ * Unregisters platform driver.
+ */
+static void __exit btcg2900_exit(void)
+{
+ BT_DBG("btcg2900_exit");
+ platform_driver_unregister(&btcg2900_cmd_driver);
+ platform_driver_unregister(&btcg2900_acl_driver);
+ platform_driver_unregister(&btcg2900_evt_driver);
+}
+
+module_init(btcg2900_init);
+module_exit(btcg2900_exit);
+
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_AUTHOR("Henrik Possung ST-Ericsson");
+MODULE_AUTHOR("Josef Kindberg ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Linux Bluetooth HCI H:4 Driver for ST-Ericsson controller");
diff --git a/drivers/bluetooth/cg2900_uart.c b/drivers/bluetooth/cg2900_uart.c
new file mode 100644
index 00000000000..d227cc83ccf
--- /dev/null
+++ b/drivers/bluetooth/cg2900_uart.c
@@ -0,0 +1,2128 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * Henrik Possung (henrik.possung@stericsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@stericsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * Lukasz Rymanowski (lukasz.rymanowski@tieto.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth UART Driver for ST-Ericsson CG2900 connectivity controller.
+ */
+#define NAME "cg2900_uart"
+#define pr_fmt(fmt) NAME ": " fmt "\n"
+
+#include <asm/atomic.h>
+#include <asm/byteorder.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/tty.h>
+#include <linux/tty_ldisc.h>
+#include <linux/types.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/cg2900.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+
+#include "hci_uart.h"
+
+#define MAIN_DEV (uart_info->dev)
+
+/* Workqueues' names */
+#define UART_WQ_NAME "cg2900_uart_wq"
+#define UART_NAME "cg2900_uart"
+
+/*
+ * A BT command complete event without any parameters is the defined size plus
+ * 1 byte extra for the status field which is always present in a
+ * command complete event.
+ */
+#define HCI_BT_CMD_COMPLETE_LEN (sizeof(struct hci_ev_cmd_complete) + 1)
+
+/* Timers used in milliseconds */
+#define UART_TX_TIMEOUT 100
+#define UART_RX_TIMEOUT 20
+#define UART_RESP_TIMEOUT 1000
+#define UART_RESUME_TIMEOUT 20
+
+/* Number of bytes to reserve at start of sk_buffer when receiving packet */
+#define RX_SKB_RESERVE 8
+/* Max size of received packet (not including reserved bytes) */
+#define RX_SKB_MAX_SIZE 1024
+
+/* Size of the header in the different packets */
+#define HCI_BT_EVT_HDR_SIZE 2
+#define HCI_BT_ACL_HDR_SIZE 4
+#define HCI_FM_RADIO_HDR_SIZE 1
+#define HCI_GNSS_HDR_SIZE 3
+
+/* Position of length field in the different packets */
+#define HCI_EVT_LEN_POS 2
+#define HCI_ACL_LEN_POS 3
+#define FM_RADIO_LEN_POS 1
+#define GNSS_LEN_POS 2
+
+/* Baud rate defines */
+#define ZERO_BAUD_RATE 0
+#define DEFAULT_BAUD_RATE 115200
+#define HIGH_BAUD_RATE 3000000
+
+#define BT_SIZE_OF_HDR (sizeof(__le16) + sizeof(__u8))
+#define BT_PARAM_LEN(__pkt_len) (__pkt_len - BT_SIZE_OF_HDR)
+
+/* Standardized Bluetooth H:4 channels */
+#define HCI_BT_CMD_H4_CHANNEL 0x01
+#define HCI_BT_ACL_H4_CHANNEL 0x02
+#define HCI_BT_SCO_H4_CHANNEL 0x03
+#define HCI_BT_EVT_H4_CHANNEL 0x04
+
+#define BT_BDADDR_SIZE 6
+
+/* Reserve 1 byte for the HCI H:4 header */
+#define HCI_H4_SIZE 1
+#define CG2900_SKB_RESERVE HCI_H4_SIZE
+
+/* Default H4 channels which may change depending on connected controller */
+#define HCI_FM_RADIO_H4_CHANNEL 0x08
+#define HCI_GNSS_H4_CHANNEL 0x09
+
+/* Bluetooth error codes */
+#define HCI_BT_ERROR_NO_ERROR 0x00
+
+/* Bytes in the command Hci_Cmd_ST_Set_Uart_Baud_Rate */
+#define CG2900_BAUD_RATE_57600 0x03
+#define CG2900_BAUD_RATE_115200 0x02
+#define CG2900_BAUD_RATE_230400 0x01
+#define CG2900_BAUD_RATE_460800 0x00
+#define CG2900_BAUD_RATE_921600 0x20
+#define CG2900_BAUD_RATE_2000000 0x25
+#define CG2900_BAUD_RATE_3000000 0x27
+#define CG2900_BAUD_RATE_4000000 0x2B
+
+/* GNSS */
+struct gnss_hci_hdr {
+ __u8 op_code;
+ __le16 plen;
+} __packed;
+
+/* FM legacy command packet */
+struct fm_leg_cmd {
+ __u8 length;
+ __u8 opcode;
+ __u8 read_write;
+ __u8 fm_function;
+ union { /* Payload varies with function */
+ __le16 irqmask;
+ struct fm_leg_fm_cmd {
+ __le16 head;
+ __le16 data[];
+ } fm_cmd;
+ };
+} __packed;
+
+/* FM legacy command complete packet */
+struct fm_leg_cmd_cmpl {
+ __u8 param_length;
+ __u8 status;
+ __u8 opcode;
+ __u8 read_write;
+ __u8 cmd_status;
+ __u8 fm_function;
+ __le16 response_head;
+ __le16 data[];
+} __packed;
+
+/* FM legacy interrupt packet, PG2 style */
+struct fm_leg_irq_v2 {
+ __u8 param_length;
+ __u8 status;
+ __u8 opcode;
+ __u8 event_type;
+ __u8 event_id;
+ __le16 irq;
+} __packed;
+
+/* FM legacy interrupt packet, PG1 style */
+struct fm_leg_irq_v1 {
+ __u8 param_length;
+ __u8 opcode;
+ __u8 event_id;
+ __le16 irq;
+} __packed;
+
+union fm_leg_evt_or_irq {
+ __u8 param_length;
+ struct fm_leg_cmd_cmpl evt;
+ struct fm_leg_irq_v2 irq_v2;
+ struct fm_leg_irq_v1 irq_v1;
+} __packed;
+
+/* BT VS SetBaudRate command */
+#define CG2900_BT_OP_VS_SET_BAUD_RATE 0xFC09
+struct bt_vs_set_baud_rate_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 baud_rate;
+} __packed;
+
+/**
+ * enum uart_rx_state - UART RX-state for UART.
+ * @W4_PACKET_TYPE: Waiting for packet type.
+ * @W4_EVENT_HDR: Waiting for BT event header.
+ * @W4_ACL_HDR: Waiting for BT ACL header.
+ * @W4_FM_RADIO_HDR: Waiting for FM header.
+ * @W4_GNSS_HDR: Waiting for GNSS header.
+ * @W4_DATA: Waiting for data in rest of the packet (after header).
+ */
+enum uart_rx_state {
+ W4_PACKET_TYPE,
+ W4_EVENT_HDR,
+ W4_ACL_HDR,
+ W4_FM_RADIO_HDR,
+ W4_GNSS_HDR,
+ W4_DATA
+};
+
+/**
+ * enum sleep_state - Sleep-state for UART.
+ * @CHIP_AWAKE: Chip is awake.
+ * @CHIP_FALLING_ASLEEP: Chip is falling asleep.
+ * @CHIP_ASLEEP: Chip is asleep.
+ * @CHIP_SUSPENDED: Chip in suspend state.
+ * @CHIP_RESUMING: Chip is going back from suspend state.
+ * @CHIP_POWERED_DOWN: Chip is off.
+ */
+enum sleep_state {
+ CHIP_AWAKE,
+ CHIP_FALLING_ASLEEP,
+ CHIP_ASLEEP,
+ CHIP_SUSPENDED,
+ CHIP_RESUMING,
+ CHIP_POWERED_DOWN
+};
+
+/**
+ * enum baud_rate_change_state - Baud rate-state for UART.
+ * @BAUD_IDLE: No baud rate change is ongoing.
+ * @BAUD_SENDING_RESET: HCI reset has been sent. Waiting for command complete
+ * event.
+ * @BAUD_START: Set baud rate cmd scheduled for sending.
+ * @BAUD_SENDING: Set baud rate cmd sending in progress.
+ * @BAUD_WAITING: Set baud rate cmd sent, waiting for command complete
+ * event.
+ * @BAUD_SUCCESS: Baud rate change has succeeded.
+ * @BAUD_FAIL: Baud rate change has failed.
+ */
+enum baud_rate_change_state {
+ BAUD_IDLE,
+ BAUD_SENDING_RESET,
+ BAUD_START,
+ BAUD_SENDING,
+ BAUD_WAITING,
+ BAUD_SUCCESS,
+ BAUD_FAIL
+};
+
+/**
+ * struct uart_work_struct - Work structure for UART module.
+ * @work: Work structure.
+ * @data: Pointer to private data.
+ *
+ * This structure is used to pack work for work queue.
+ */
+struct uart_work_struct {
+ struct work_struct work;
+ void *data;
+};
+
+/**
+ * struct uart_delayed_work_struct - Work structure for UART module.
+ * @delayed_work: Work structure.
+ * @data: Pointer to private data.
+ *
+ * This structure is used to pack work for work queue.
+ */
+struct uart_delayed_work_struct {
+ struct delayed_work work;
+ void *data;
+};
+
+/**
+ * struct uart_info - Main UART info structure.
+ * @rx_state: Current RX state.
+ * @rx_count: Number of bytes left to receive.
+ * @rx_skb: SK_buffer to store the received data into.
+ * @tx_queue: TX queue for sending data to chip.
+ * @hu: Hci uart structure.
+ * @wq: UART work queue.
+ * @baud_rate_state: UART baud rate change state.
+ * @baud_rate: Current baud rate setting.
+ * @sleep_state: UART sleep state.
+ * @sleep_work: Delayed sleep work struct.
+ * @wakeup_work: Wake-up work struct.
+ * @restart_sleep_work: Reschedule sleep_work and wake-up work struct.
+ * @sleep_state_lock: Used to protect chip state.
+ * @sleep_allowed: Indicates if tty has functions needed for sleep mode.
+ * @tx_in_progress: Indicates data sending in progress.
+ * @rx_in_progress: Indicates data receiving in progress.
+ * @transmission_lock: Spin_lock to protect tx/rx_in_progress.
+ * @regulator: Regulator.
+ * @regulator_enabled: True if regulator is enabled.
+ * @dev: Pointer to CG2900 uart device.
+ * @chip_dev: Chip device for current UART transport.
+ * @cts_irq: CTS interrupt for this UART.
+ * @cts_gpio: CTS GPIO for this UART.
+ * @wake_lock: Wake lock for keeping user space awake (for Android).
+ * @suspend_blocked: True if suspend operation is blocked in the framework.
+ */
+struct uart_info {
+ enum uart_rx_state rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+ struct sk_buff_head tx_queue;
+
+ struct hci_uart *hu;
+
+ struct workqueue_struct *wq;
+ enum baud_rate_change_state baud_rate_state;
+ int baud_rate;
+ enum sleep_state sleep_state;
+ struct uart_delayed_work_struct sleep_work;
+ struct uart_work_struct wakeup_work;
+ struct uart_work_struct restart_sleep_work;
+ struct mutex sleep_state_lock;
+ bool sleep_allowed;
+ bool tx_in_progress;
+ bool rx_in_progress;
+ spinlock_t transmission_lock;
+ struct regulator *regulator;
+ bool regulator_enabled;
+ struct device *dev;
+ struct cg2900_chip_dev chip_dev;
+ int cts_irq;
+ int cts_gpio;
+ struct wake_lock wake_lock;
+ bool suspend_blocked;
+};
+
+/* Module parameters */
+static int uart_default_baud = DEFAULT_BAUD_RATE;
+static int uart_high_baud = HIGH_BAUD_RATE;
+static int uart_debug;
+
+static DECLARE_WAIT_QUEUE_HEAD(uart_wait_queue);
+
+static void wake_up_chip(struct uart_info *uart_info);
+
+/**
+ * is_chip_flow_off() - Check if chip has set flow off.
+ * @tty: Pointer to tty.
+ *
+ * Returns:
+ * true - chip flows off.
+ * false - chip flows on.
+ */
+static bool is_chip_flow_off(struct uart_info *uart_info)
+{
+ int lines = 0;
+
+ if (uart_info->hu)
+ lines = hci_uart_tiocmget(uart_info->hu);
+
+ if (lines & TIOCM_CTS)
+ return false;
+ else
+ return true;
+}
+
+/**
+ * create_work_item() - Create work item and add it to the work queue.
+ * @uart_info: Main Uart structure.
+ * @work_func: Work function.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EBUSY if not possible to queue work.
+ * -ENOMEM if allocation fails.
+ */
+static int create_work_item(struct uart_info *uart_info,
+ work_func_t work_func)
+{
+ struct uart_work_struct *new_work;
+ int res;
+
+ new_work = kmalloc(sizeof(*new_work), GFP_ATOMIC);
+ if (!new_work) {
+ dev_err(MAIN_DEV,
+ "Failed to alloc memory for uart_work_struct\n");
+ return -ENOMEM;
+ }
+
+ new_work->data = uart_info;
+ INIT_WORK(&new_work->work, work_func);
+
+ res = queue_work(uart_info->wq, &new_work->work);
+ if (!res) {
+ dev_err(MAIN_DEV,
+ "Failed to queue work_struct because it's already "
+ "in the queue\n");
+ kfree(new_work);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/**
+ * handle_cts_irq() - Called to handle CTS interrupt in work context.
+ * @work: work which needs to be done.
+ *
+ * The handle_cts_irq() function is a work handler called if interrupt on CTS
+ * occurred. It wakes up the transport.
+ */
+static void handle_cts_irq(struct work_struct *work)
+{
+ struct uart_work_struct *current_work =
+ container_of(work, struct uart_work_struct, work);
+ struct uart_info *uart_info = (struct uart_info *)current_work->data;
+
+ spin_lock_bh(&(uart_info->transmission_lock));
+ /* Mark that there is an ongoing transfer. */
+ uart_info->rx_in_progress = true;
+ spin_unlock_bh(&(uart_info->transmission_lock));
+
+ /* Cancel pending sleep work if there is any. */
+ cancel_delayed_work_sync(&uart_info->sleep_work.work);
+
+ mutex_lock(&(uart_info->sleep_state_lock));
+
+ if (uart_info->sleep_state == CHIP_SUSPENDED) {
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_RESUMING\n");
+ uart_info->sleep_state = CHIP_RESUMING;
+ mutex_unlock(&(uart_info->sleep_state_lock));
+ } else {
+ mutex_unlock(&(uart_info->sleep_state_lock));
+ wake_up_chip(uart_info);
+ }
+
+ kfree(current_work);
+}
+
+/**
+ * cts_interrupt() - Called to handle CTS interrupt.
+ * @irq: Interrupt that occurred.
+ * @dev_id: Device ID where interrupt occurred.
+ *
+ * The cts_interrupt() function is called if interrupt on CTS occurred.
+ * It disables the interrupt and starts a new work thread to handle
+ * the interrupt.
+ */
+static irqreturn_t cts_interrupt(int irq, void *dev_id)
+{
+ struct uart_info *uart_info = dev_get_drvdata(dev_id);
+#ifdef CONFIG_PM
+ disable_irq_wake(irq);
+#endif
+ disable_irq_nosync(irq);
+ if (!uart_info->suspend_blocked) {
+ wake_lock(&uart_info->wake_lock);
+ uart_info->suspend_blocked = true;
+ }
+
+ /* Create work and leave IRQ context. */
+ (void)create_work_item(uart_info, handle_cts_irq);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * set_cts_irq() - Enable interrupt on CTS.
+ * @uart_info: Main Uart structure.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Error codes from request_irq and disable_uart.
+ */
+static int set_cts_irq(struct uart_info *uart_info)
+{
+ int err;
+ int cts_val = 0;
+
+ /* Set IRQ on CTS. */
+ err = request_irq(uart_info->cts_irq,
+ cts_interrupt,
+ IRQF_TRIGGER_FALLING,
+ UART_NAME,
+ uart_info->dev);
+ if (err) {
+ dev_err(MAIN_DEV, "Could not request CTS IRQ (%d)\n", err);
+ return err;
+ }
+
+ /*
+ * It may happen that there was already an interrupt on CTS just before
+ * the enable_irq() call above. If the CTS line is low now it means that
+ * it's happened, so disable the CTS interrupt and return -ECANCELED.
+ */
+ cts_val = gpio_get_value(uart_info->cts_gpio);
+ if (!cts_val) {
+ dev_dbg(MAIN_DEV, "Missed interrupt, going back to "
+ "awake state\n");
+ free_irq(uart_info->cts_irq, uart_info->dev);
+ return -ECANCELED;
+ }
+
+#ifdef CONFIG_PM
+ enable_irq_wake(uart_info->cts_irq);
+#endif
+ return 0;
+}
+
+/**
+ * disable_uart_pins() - Disable the UART pins.
+ * @uart_info: Main Uart structure.
+ */
+static void disable_uart_pins(struct uart_info *uart_info)
+{
+ struct cg2900_platform_data *pf_data;
+
+ pf_data = dev_get_platdata(uart_info->dev);
+
+ if (pf_data->uart.disable_uart) {
+ int err = pf_data->uart.disable_uart(&uart_info->chip_dev);
+ if (err)
+ dev_err(MAIN_DEV,
+ "Unable to disable UART Hardware (%d)\n", err);
+ }
+}
+
+/**
+ * enable_uart_pins() - Enable the UART pins.
+ * @uart_info: Main Uart structure.
+ */
+static void enable_uart_pins(struct uart_info *uart_info)
+{
+ struct cg2900_platform_data *pf_data;
+
+ pf_data = dev_get_platdata(uart_info->dev);
+
+ if (pf_data->uart.enable_uart) {
+ int err = pf_data->uart.enable_uart(&uart_info->chip_dev);
+ if (err)
+ dev_err(MAIN_DEV,
+ "Unable to enable UART Hardware (%d)\n", err);
+ }
+}
+
+/**
+ * unset_cts_irq() - Disable interrupt on CTS.
+ * @uart_info: Main Uart structure.
+ */
+static void unset_cts_irq(struct uart_info *uart_info)
+{
+ /* Free CTS interrupt */
+ free_irq(uart_info->cts_irq, uart_info->dev);
+}
+
+/**
+ * get_sleep_timeout() - Get sleep timeout.
+ * @uart_info: Main Uart structure.
+ *
+ * Check all conditions for sleep and return sleep timeout.
+ * Return:
+ * 0: sleep not allowed.
+ * other: Timeout value in ms.
+ */
+static unsigned long get_sleep_timeout(struct uart_info *uart_info)
+{
+ unsigned long timeout_jiffies = cg2900_get_sleep_timeout();
+
+ if (timeout_jiffies &&
+ uart_info->hu &&
+ uart_info->hu->fd &&
+ uart_info->sleep_allowed)
+ return timeout_jiffies;
+
+ return 0;
+}
+
+/**
+ * work_wake_up_chip() - Called to wake up of the transport in work context.
+ * @work: work which needs to be done.
+ */
+static void work_wake_up_chip(struct work_struct *work)
+{
+ struct uart_work_struct *current_work =
+ container_of(work, struct uart_work_struct, work);
+ struct uart_info *uart_info = (struct uart_info *)current_work->data;
+
+ wake_up_chip(uart_info);
+}
+
+/**
+ * wake_up_chip() - Wakes up the chip and transport.
+ * @work: pointer to a work struct if the function was called that way.
+ *
+ * Depending on the current sleep state it may wake up the transport.
+ */
+static void wake_up_chip(struct uart_info *uart_info)
+{
+ unsigned long timeout_jiffies = get_sleep_timeout(uart_info);
+
+ /* Resuming state is special. Need to get back chip to awake state. */
+ if (!timeout_jiffies && uart_info->sleep_state != CHIP_RESUMING)
+ return;
+
+ if (!uart_info->hu) {
+ dev_err(MAIN_DEV, "wake_up_chip: UART not open\n");
+ return;
+ }
+
+ mutex_lock(&(uart_info->sleep_state_lock));
+
+ /*
+ * If chip is powered down we cannot wake it up here. It has to be woken
+ * up through a call to uart_set_chip_power()
+ */
+ if (CHIP_POWERED_DOWN == uart_info->sleep_state)
+ goto finished;
+
+ if (!uart_info->suspend_blocked) {
+ wake_lock(&uart_info->wake_lock);
+ uart_info->suspend_blocked = true;
+ }
+
+ /*
+ * This function indicates data is transmitted.
+ * Therefore see to that the chip is awake.
+ */
+ if (CHIP_AWAKE == uart_info->sleep_state)
+ goto finished;
+
+ if (CHIP_ASLEEP == uart_info->sleep_state ||
+ CHIP_RESUMING == uart_info->sleep_state) {
+ /* Wait before disabling IRQ */
+ schedule_timeout_killable(
+ msecs_to_jiffies(UART_RESUME_TIMEOUT));
+
+ /* Disable IRQ only when it was enabled. */
+ unset_cts_irq(uart_info);
+ (void)hci_uart_set_baudrate(uart_info->hu,
+ uart_info->baud_rate);
+
+ enable_uart_pins(uart_info);
+
+ /*
+ * Wait before flowing on. Otherwise UART might not be ready in
+ * time
+ */
+ schedule_timeout_killable(
+ msecs_to_jiffies(UART_RESUME_TIMEOUT));
+
+ /* Set FLOW on. */
+ hci_uart_flow_ctrl(uart_info->hu, FLOW_ON);
+ }
+
+ /* Unset BREAK. */
+ dev_dbg(MAIN_DEV, "wake_up_chip: Clear break\n");
+ hci_uart_set_break(uart_info->hu, BREAK_OFF);
+
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_AWAKE\n");
+ uart_info->sleep_state = CHIP_AWAKE;
+
+finished:
+ mutex_unlock(&(uart_info->sleep_state_lock));
+}
+
+/**
+ * set_chip_sleep_mode() - Put the chip and transport to sleep mode.
+ * @work: pointer to work_struct.
+ *
+ * The set_chip_sleep_mode() function is called if there are no ongoing data
+ * transmissions. It tries to put the chip in sleep mode.
+ *
+ */
+static void set_chip_sleep_mode(struct work_struct *work)
+{
+ int err = 0;
+ struct delayed_work *delayed_work =
+ container_of(work, struct delayed_work, work);
+ struct uart_delayed_work_struct *current_work = container_of(
+ delayed_work, struct uart_delayed_work_struct, work);
+ struct uart_info *uart_info = (struct uart_info *)current_work->data;
+ unsigned long timeout_jiffies = get_sleep_timeout(uart_info);
+ int chars_in_buffer;
+
+ if (!timeout_jiffies)
+ return;
+
+ if (!uart_info->hu) {
+ dev_err(MAIN_DEV, "set_chip_sleep_mode: UART not open\n");
+ return;
+ }
+
+ if (uart_info->tx_in_progress || uart_info->rx_in_progress) {
+ dev_dbg(MAIN_DEV, "Not going to sleep, TX/RX in progress\n");
+ return;
+ }
+
+ mutex_lock(&(uart_info->sleep_state_lock));
+
+ switch (uart_info->sleep_state) {
+ case CHIP_FALLING_ASLEEP:
+ if (!is_chip_flow_off(uart_info)) {
+ dev_dbg(MAIN_DEV, "Chip flow is on, it's not ready to"
+ "sleep yet\n");
+ goto schedule_sleep_work;
+ }
+
+ /* Flow OFF. */
+ hci_uart_flow_ctrl(uart_info->hu, FLOW_OFF);
+
+ disable_uart_pins(uart_info);
+
+ /*
+ * Set baud zero.
+ * This cause shut off UART clock as well.
+ */
+ (void)hci_uart_set_baudrate(uart_info->hu, ZERO_BAUD_RATE);
+ err = set_cts_irq(uart_info);
+ if (err < 0) {
+ enable_uart_pins(uart_info);
+ (void)hci_uart_set_baudrate(uart_info->hu,
+ uart_info->baud_rate);
+ hci_uart_flow_ctrl(uart_info->hu, FLOW_ON);
+ hci_uart_set_break(uart_info->hu, BREAK_OFF);
+
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_AWAKE\n");
+ uart_info->sleep_state = CHIP_AWAKE;
+
+ if (err == -ECANCELED)
+ goto finished;
+ else {
+ dev_err(MAIN_DEV, "Can not set interrupt on "
+ "CTS, err:%d\n", err);
+ goto error;
+ }
+ }
+
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_ASLEEP\n");
+ uart_info->sleep_state = CHIP_ASLEEP;
+ if (uart_info->suspend_blocked) {
+ wake_unlock(&uart_info->wake_lock);
+ uart_info->suspend_blocked = false;
+ }
+ break;
+ case CHIP_AWAKE:
+ chars_in_buffer = hci_uart_chars_in_buffer(uart_info->hu);
+ if (chars_in_buffer) {
+ dev_dbg(MAIN_DEV, "sleep_timer_expired: "
+ "tx not finished, stay awake and "
+ "restart the sleep timer\n");
+ goto schedule_sleep_work;
+ }
+
+ dev_dbg(MAIN_DEV, "sleep_timer_expired: Set break\n");
+ hci_uart_set_break(uart_info->hu, BREAK_ON);
+
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_FALLING_ASLEEP\n");
+ uart_info->sleep_state = CHIP_FALLING_ASLEEP;
+ goto schedule_sleep_work;
+
+ case CHIP_POWERED_DOWN:
+ case CHIP_SUSPENDED:
+ case CHIP_ASLEEP: /* Fallthrough. */
+ default:
+ dev_dbg(MAIN_DEV,
+ "Chip sleeps, is suspended or powered down\n");
+ break;
+ }
+
+ mutex_unlock(&(uart_info->sleep_state_lock));
+
+ return;
+
+finished:
+ mutex_unlock(&(uart_info->sleep_state_lock));
+ return;
+schedule_sleep_work:
+ mutex_unlock(&(uart_info->sleep_state_lock));
+ if (timeout_jiffies)
+ queue_delayed_work(uart_info->wq, &uart_info->sleep_work.work,
+ timeout_jiffies);
+ return;
+error:
+ /* Disable sleep mode.*/
+ dev_err(MAIN_DEV, "Disable sleep mode\n");
+ uart_info->sleep_allowed = false;
+ mutex_unlock(&(uart_info->sleep_state_lock));
+}
+
+#ifdef CONFIG_PM
+/**
+ * cg2900_uart_suspend() - Called by Linux PM to put the device in a low power mode.
+ * @pdev: Pointer to platform device.
+ * @state: New state.
+ *
+ * In UART case, CG2900 driver does nothing on suspend.
+ *
+ * Returns:
+ * 0 - Success.
+ */
+static int cg2900_uart_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int err = 0;
+ struct uart_info *uart_info = dev_get_drvdata(&pdev->dev);
+
+ mutex_lock(&(uart_info->sleep_state_lock));
+
+ if (uart_info->sleep_state == CHIP_POWERED_DOWN)
+ goto finished;
+
+ if (uart_info->sleep_state != CHIP_ASLEEP) {
+ err = -EBUSY;
+ goto finished;
+ }
+
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_SUSPENDED\n");
+ uart_info->sleep_state = CHIP_SUSPENDED;
+
+finished:
+ mutex_unlock(&(uart_info->sleep_state_lock));
+ return err;
+}
+
+/**
+ * cg2900_uart_resume() - Called to bring a device back from a low power state.
+ * @pdev: Pointer to platform device.
+ *
+ * In UART case, CG2900 driver does nothing on resume.
+ *
+ * Returns:
+ * 0 - Success.
+ */
+static int cg2900_uart_resume(struct platform_device *pdev)
+{
+ struct uart_info *uart_info = dev_get_drvdata(&pdev->dev);
+
+ mutex_lock(&(uart_info->sleep_state_lock));
+
+ if (uart_info->sleep_state == CHIP_RESUMING)
+ /* System resume because of trafic on UART. Lets wakeup.*/
+ (void)queue_work(uart_info->wq, &uart_info->wakeup_work.work);
+ else if (uart_info->sleep_state != CHIP_POWERED_DOWN) {
+ /* No need to wakeup chip. Go back to Asleep state.*/
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_ASLEEP\n");
+ uart_info->sleep_state = CHIP_ASLEEP;
+ }
+
+ mutex_unlock(&(uart_info->sleep_state_lock));
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+/**
+ * cg2900_enable_regulator() - Enable regulator.
+ * @uart_info: Main Uart structure.
+ *
+ * Returns:
+ * 0 - Success.
+ * Error from regulator_get, regulator_enable.
+ */
+static int cg2900_enable_regulator(struct uart_info *uart_info)
+{
+#ifdef CONFIG_REGULATOR
+ int err;
+
+ /* Get and enable regulator. */
+ uart_info->regulator = regulator_get(uart_info->dev, "gbf_1v8");
+ if (IS_ERR(uart_info->regulator)) {
+ dev_err(MAIN_DEV, "Not able to find regulator\n");
+ err = PTR_ERR(uart_info->regulator);
+ } else {
+ err = regulator_enable(uart_info->regulator);
+ if (err)
+ dev_err(MAIN_DEV, "Not able to enable regulator\n");
+ else
+ uart_info->regulator_enabled = true;
+ }
+ return err;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * cg2900_disable_regulator() - Disable regulator.
+ * @uart_info: Main Uart structure.
+ *
+ */
+static void cg2900_disable_regulator(struct uart_info *uart_info)
+{
+#ifdef CONFIG_REGULATOR
+ /* Disable and put regulator. */
+ if (uart_info->regulator && uart_info->regulator_enabled) {
+ regulator_disable(uart_info->regulator);
+ uart_info->regulator_enabled = false;
+ }
+ regulator_put(uart_info->regulator);
+ uart_info->regulator = NULL;
+#endif
+}
+
+/**
+ * is_set_baud_rate_cmd() - Checks if data contains set baud rate hci cmd.
+ * @data: Pointer to data array to check.
+ *
+ * Returns:
+ * true - if cmd found;
+ * false - otherwise.
+ */
+static bool is_set_baud_rate_cmd(const char *data)
+{
+ struct hci_command_hdr *cmd;
+
+ if (data[0] != HCI_BT_CMD_H4_CHANNEL)
+ return false;
+
+ cmd = (struct hci_command_hdr *)&data[1];
+ if (le16_to_cpu(cmd->opcode) == CG2900_BT_OP_VS_SET_BAUD_RATE &&
+ cmd->plen == BT_PARAM_LEN(sizeof(struct bt_vs_set_baud_rate_cmd)))
+ return true;
+
+ return false;
+}
+
+/**
+ * is_bt_cmd_complete_no_param() - Checks if data contains command complete event for a certain command.
+ * @skb: sk_buffer containing the data including H:4 header.
+ * @opcode: Command op code.
+ * @status: Command status.
+ *
+ * Returns:
+ * true - If this is the command complete we were looking for;
+ * false - otherwise.
+ */
+static bool is_bt_cmd_complete_no_param(struct sk_buff *skb, u16 opcode,
+ u8 *status)
+{
+ struct hci_event_hdr *event;
+ struct hci_ev_cmd_complete *complete;
+ u8 *data = &(skb->data[0]);
+
+ if (HCI_BT_EVT_H4_CHANNEL != *data)
+ return false;
+
+ data += HCI_H4_SIZE;
+ event = (struct hci_event_hdr *)data;
+ if (HCI_EV_CMD_COMPLETE != event->evt ||
+ HCI_BT_CMD_COMPLETE_LEN != event->plen)
+ return false;
+
+ data += sizeof(*event);
+ complete = (struct hci_ev_cmd_complete *)data;
+ if (opcode != le16_to_cpu(complete->opcode))
+ return false;
+
+ if (status) {
+ /*
+ * All command complete have the status field at first byte of
+ * packet data.
+ */
+ data += sizeof(*complete);
+ *status = *data;
+ }
+ return true;
+}
+
+/**
+ * alloc_rx_skb() - Alloc an sk_buff structure for receiving data from controller.
+ * @size: Size in number of octets.
+ * @priority: Allocation priority, e.g. GFP_KERNEL.
+ *
+ * Returns:
+ * Pointer to sk_buff structure.
+ */
+static struct sk_buff *alloc_rx_skb(unsigned int size, gfp_t priority)
+{
+ struct sk_buff *skb;
+
+ /* Allocate the SKB and reserve space for the header */
+ skb = alloc_skb(size + RX_SKB_RESERVE, priority);
+ if (skb)
+ skb_reserve(skb, RX_SKB_RESERVE);
+
+ return skb;
+}
+
+/**
+ * finish_setting_baud_rate() - Handles sending the ste baud rate hci cmd.
+ * @hu: Pointer to associated Hci uart structure.
+ *
+ * finish_setting_baud_rate() makes sure that the set baud rate cmd has
+ * been really sent out on the wire and then switches the tty driver to new
+ * baud rate.
+ */
+static void finish_setting_baud_rate(struct hci_uart *hu)
+{
+ struct uart_info *uart_info =
+ (struct uart_info *)dev_get_drvdata(hu->proto->dev);
+ /*
+ * Give the tty driver time to send data and proceed. If it hasn't
+ * been sent we can't do much about it anyway.
+ */
+ schedule_timeout_killable(msecs_to_jiffies(UART_TX_TIMEOUT));
+
+ /*
+ * Now set the termios struct to the new baudrate. Start by storing
+ * the old termios.
+ */
+ if (hci_uart_set_baudrate(hu, uart_info->baud_rate) < 0) {
+ /* Something went wrong.*/
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_IDLE\n");
+ uart_info->baud_rate_state = BAUD_IDLE;
+ } else {
+ dev_dbg(MAIN_DEV, "Setting termios to new baud rate\n");
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_WAITING\n");
+ uart_info->baud_rate_state = BAUD_WAITING;
+ }
+
+ hci_uart_flow_ctrl(hu, FLOW_ON);
+}
+
+/**
+ * alloc_set_baud_rate_cmd() - Allocates new sk_buff and fills in the change baud rate hci cmd.
+ * @uart_info: Main Uart structure.
+ * @baud: (in/out) Requested new baud rate. Updated to default baud rate
+ * upon invalid value.
+ *
+ * Returns:
+ * Pointer to allocated sk_buff if successful;
+ * NULL otherwise.
+ */
+static struct sk_buff *alloc_set_baud_rate_cmd(struct uart_info *uart_info,
+ int *baud)
+{
+ struct sk_buff *skb;
+ u8 *h4;
+ struct bt_vs_set_baud_rate_cmd *cmd;
+
+ skb = alloc_skb(sizeof(*cmd) + CG2900_SKB_RESERVE, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(MAIN_DEV,
+ "alloc_set_baud_rate_cmd: Failed to alloc skb\n");
+ return NULL;
+ }
+ skb_reserve(skb, CG2900_SKB_RESERVE);
+
+ cmd = (struct bt_vs_set_baud_rate_cmd *)skb_put(skb, sizeof(cmd));
+
+ /* Create the Hci_Cmd_ST_Set_Uart_Baud_Rate packet */
+ cmd->opcode = cpu_to_le16(CG2900_BT_OP_VS_SET_BAUD_RATE);
+ cmd->plen = BT_PARAM_LEN(sizeof(cmd));
+
+ switch (*baud) {
+ case 57600:
+ cmd->baud_rate = CG2900_BAUD_RATE_57600;
+ break;
+ case 115200:
+ cmd->baud_rate = CG2900_BAUD_RATE_115200;
+ break;
+ case 230400:
+ cmd->baud_rate = CG2900_BAUD_RATE_230400;
+ break;
+ case 460800:
+ cmd->baud_rate = CG2900_BAUD_RATE_460800;
+ break;
+ case 921600:
+ cmd->baud_rate = CG2900_BAUD_RATE_921600;
+ break;
+ case 2000000:
+ cmd->baud_rate = CG2900_BAUD_RATE_2000000;
+ break;
+ case 3000000:
+ cmd->baud_rate = CG2900_BAUD_RATE_3000000;
+ break;
+ case 4000000:
+ cmd->baud_rate = CG2900_BAUD_RATE_4000000;
+ break;
+ default:
+ dev_err(MAIN_DEV,
+ "Invalid speed requested (%d), using 115200 bps "
+ "instead\n", *baud);
+ cmd->baud_rate = CG2900_BAUD_RATE_115200;
+ *baud = 115200;
+ break;
+ };
+
+ h4 = skb_push(skb, HCI_H4_SIZE);
+ *h4 = HCI_BT_CMD_H4_CHANNEL;
+
+ return skb;
+}
+
+/**
+ * work_do_transmit() - Transmit data packet to connectivity controller over UART.
+ * @work: Pointer to work info structure. Contains uart_info structure
+ * pointer.
+ */
+static void work_do_transmit(struct work_struct *work)
+{
+ struct uart_work_struct *current_work =
+ container_of(work, struct uart_work_struct, work);
+ struct uart_info *uart_info = (struct uart_info *)current_work->data;
+
+ kfree(current_work);
+
+ if (!uart_info->hu) {
+ dev_err(MAIN_DEV, "work_do_transmit: UART not open\n");
+ return;
+ }
+
+ spin_lock_bh(&(uart_info->transmission_lock));
+ /* Mark that there is an ongoing transfer. */
+ uart_info->tx_in_progress = true;
+ spin_unlock_bh(&(uart_info->transmission_lock));
+
+ /* Cancel pending sleep work if there is any. */
+ cancel_delayed_work_sync(&uart_info->sleep_work.work);
+
+ /* Wake up the chip and transport. */
+ wake_up_chip(uart_info);
+
+ (void)hci_uart_tx_wakeup(uart_info->hu);
+}
+
+/**
+ * work_hw_deregistered() - Handle HW deregistered.
+ * @work: Reference to work data.
+ */
+static void work_hw_deregistered(struct work_struct *work)
+{
+ struct uart_work_struct *current_work;
+ struct uart_info *uart_info;
+ int err;
+ current_work = container_of(work, struct uart_work_struct, work);
+ uart_info = (struct uart_info *)current_work->data;
+
+ err = cg2900_deregister_trans_driver(&uart_info->chip_dev);
+ if (err)
+ dev_err(MAIN_DEV, "Could not deregister UART from Core (%d)\n",
+ err);
+
+ kfree(current_work);
+}
+
+/**
+ * set_baud_rate() - Sets new baud rate for the UART.
+ * @hu: Pointer to hci_uart structure.
+ * @baud: New baud rate.
+ *
+ * This function first sends the HCI command
+ * Hci_Cmd_ST_Set_Uart_Baud_Rate. It then changes the baud rate in HW, and
+ * finally it waits for the Command Complete event for the
+ * Hci_Cmd_ST_Set_Uart_Baud_Rate command.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EALREADY if baud rate change is already in progress.
+ * -EFAULT if one or more of the UART related structs is not allocated.
+ * -ENOMEM if skb allocation has failed.
+ * -EPERM if setting the new baud rate has failed.
+ * Errors from create_work_item.
+ */
+static int set_baud_rate(struct hci_uart *hu, int baud)
+{
+ int err = 0;
+ struct sk_buff *skb;
+ int old_baud_rate;
+ struct uart_info *uart_info =
+ (struct uart_info *)dev_get_drvdata(hu->proto->dev);
+
+ dev_dbg(MAIN_DEV, "set_baud_rate (%d baud)\n", baud);
+
+ if (uart_info->baud_rate_state != BAUD_IDLE) {
+ dev_err(MAIN_DEV,
+ "Trying to set new baud rate before old setting "
+ "is finished\n");
+ return -EALREADY;
+ }
+
+ if (!uart_info->hu) {
+ dev_err(MAIN_DEV, "set_baud_rate: UART not open\n");
+ return -EFAULT;
+ }
+
+ /*
+ * Wait some time to be sure that any RX process has finished (which
+ * flows on RTS in the end) before flowing off the RTS.
+ */
+ schedule_timeout_killable(msecs_to_jiffies(UART_RX_TIMEOUT));
+ hci_uart_flow_ctrl(uart_info->hu, FLOW_OFF);
+
+ /*
+ * Store old baud rate so that we can restore it if something goes
+ * wrong.
+ */
+ old_baud_rate = uart_info->baud_rate;
+
+ skb = alloc_set_baud_rate_cmd(uart_info, &baud);
+ if (!skb) {
+ dev_err(MAIN_DEV, "alloc_set_baud_rate_cmd failed\n");
+ return -ENOMEM;
+ }
+
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_START\n");
+ uart_info->baud_rate_state = BAUD_START;
+ uart_info->baud_rate = baud;
+
+ /* Queue the sk_buffer... */
+ skb_queue_tail(&uart_info->tx_queue, skb);
+
+ /* ... and call the common UART TX function */
+ err = create_work_item(uart_info, work_do_transmit);
+ if (err) {
+ dev_err(MAIN_DEV,
+ "Failed to send change baud rate cmd, freeing skb\n");
+ skb = skb_dequeue_tail(&uart_info->tx_queue);
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_IDLE\n");
+ uart_info->baud_rate_state = BAUD_IDLE;
+ uart_info->baud_rate = old_baud_rate;
+ kfree_skb(skb);
+ return err;
+ }
+
+ dev_dbg(MAIN_DEV, "Set baud rate cmd scheduled for sending\n");
+
+ /*
+ * Now wait for the command complete.
+ * It will come at the new baudrate.
+ */
+ wait_event_timeout(uart_wait_queue,
+ ((BAUD_SUCCESS == uart_info->baud_rate_state) ||
+ (BAUD_FAIL == uart_info->baud_rate_state)),
+ msecs_to_jiffies(UART_RESP_TIMEOUT));
+ if (BAUD_SUCCESS == uart_info->baud_rate_state)
+ dev_info(MAIN_DEV, "Baud rate changed to %d baud\n", baud);
+ else {
+ dev_err(MAIN_DEV, "Failed to set new baud rate (%d)\n",
+ uart_info->baud_rate_state);
+ err = -EPERM;
+ }
+
+ /* Finally flush the TTY so we are sure that is no bad data there */
+ hci_uart_flush_buffer(hu);
+ dev_dbg(MAIN_DEV, "Flushing TTY after baud rate change\n");
+ /* Finished. Set state to IDLE */
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_IDLE\n");
+ uart_info->baud_rate_state = BAUD_IDLE;
+
+ return err;
+}
+
+/**
+ * uart_write() - Transmit data to CG2900 over UART.
+ * @dev: Transport device information.
+ * @skb: SK buffer to transmit.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Errors from create_work_item.
+ */
+static int uart_write(struct cg2900_chip_dev *dev, struct sk_buff *skb)
+{
+ int err;
+ struct uart_info *uart_info = dev_get_drvdata(dev->dev);
+
+ if (uart_debug)
+ dev_dbg(MAIN_DEV, "uart_write: data len = %d\n", skb->len);
+
+ /* Queue the sk_buffer... */
+ skb_queue_tail(&uart_info->tx_queue, skb);
+
+ /* ...and start TX operation */
+
+ err = create_work_item(uart_info, work_do_transmit);
+ if (err)
+ dev_err(MAIN_DEV,
+ "Failed to create work item (%d) uart_tty_wakeup\n",
+ err);
+
+ return err;
+}
+
+/**
+ * uart_open() - Open the CG2900 UART for data transfers.
+ * @dev: Transport device information.
+ *
+ * Returns:
+ * 0 if there is no error,
+ * -EACCES if write to transport failed,
+ * -EIO if chip did not answer to commands.
+ * Errors from set_baud_rate.
+ */
+static int uart_open(struct cg2900_chip_dev *dev)
+{
+ u8 *h4;
+ struct sk_buff *skb;
+ struct hci_command_hdr *cmd;
+ struct uart_info *uart_info = dev_get_drvdata(dev->dev);
+
+ if (!uart_info->hu) {
+ dev_err(MAIN_DEV, "uart_open: UART not open\n");
+ return -EACCES;
+ }
+
+ /*
+ * Chip has just been started up. It has a system to autodetect
+ * exact baud rate and transport to use. There are only a few commands
+ * it will recognize and HCI Reset is one of them.
+ * We therefore start with sending that before actually changing
+ * baud rate.
+ *
+ * Create the Hci_Reset packet
+ */
+
+ skb = alloc_skb(sizeof(*cmd) + HCI_H4_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(MAIN_DEV, "Couldn't allocate sk_buff with length %d\n",
+ sizeof(*cmd));
+ return -EACCES;
+ }
+ skb_reserve(skb, HCI_H4_SIZE);
+ cmd = (struct hci_command_hdr *)skb_put(skb, sizeof(*cmd));
+ cmd->opcode = cpu_to_le16(HCI_OP_RESET);
+ cmd->plen = 0; /* No parameters for HCI reset */
+
+ h4 = skb_push(skb, HCI_H4_SIZE);
+ *h4 = HCI_BT_CMD_H4_CHANNEL;
+
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_SENDING_RESET\n");
+ uart_info->baud_rate_state = BAUD_SENDING_RESET;
+ dev_dbg(MAIN_DEV, "Sending HCI reset before baud rate change\n");
+
+
+ /* Queue the sk_buffer... */
+ skb_queue_tail(&uart_info->tx_queue, skb);
+
+ (void)hci_uart_tx_wakeup(uart_info->hu);
+
+ /*
+ * Wait for command complete. If error, exit without changing
+ * baud rate.
+ */
+ wait_event_timeout(uart_wait_queue,
+ BAUD_IDLE == uart_info->baud_rate_state,
+ msecs_to_jiffies(UART_RESP_TIMEOUT));
+ if (BAUD_IDLE != uart_info->baud_rate_state) {
+ dev_err(MAIN_DEV, "Failed to send HCI Reset\n");
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_IDLE\n");
+ uart_info->baud_rate_state = BAUD_IDLE;
+ return -EIO;
+ }
+
+ /* Just return if there will be no change of baud rate */
+ if (uart_default_baud != uart_high_baud)
+ return set_baud_rate(uart_info->hu, uart_high_baud);
+ else
+ return 0;
+}
+
+/**
+ * uart_set_chip_power() - Enable or disable the CG2900.
+ * @chip_on: true if chip shall be enabled, false otherwise.
+ */
+static void uart_set_chip_power(struct cg2900_chip_dev *dev, bool chip_on)
+{
+ int uart_baudrate = uart_default_baud;
+ struct cg2900_platform_data *pf_data;
+ struct uart_info *uart_info;
+
+ pf_data = dev_get_platdata(dev->dev);
+ uart_info = dev_get_drvdata(dev->dev);
+
+ dev_info(MAIN_DEV, "Set chip power: %s\n",
+ (chip_on ? "ENABLE" : "DISABLE"));
+
+ /* Cancel any ongoing works.*/
+ cancel_work_sync(&uart_info->wakeup_work.work);
+ cancel_delayed_work_sync(&uart_info->sleep_work.work);
+
+ mutex_lock(&uart_info->sleep_state_lock);
+
+ if (!uart_info->hu) {
+ dev_err(MAIN_DEV, "Hci uart struct is not allocated\n");
+ goto unlock;
+ }
+
+ if (chip_on) {
+ if (!uart_info->suspend_blocked) {
+ wake_lock(&uart_info->wake_lock);
+ uart_info->suspend_blocked = true;
+ }
+ if (uart_info->sleep_state != CHIP_POWERED_DOWN) {
+ dev_err(MAIN_DEV, "Chip is already powered up (%d)\n",
+ uart_info->sleep_state);
+ goto unlock;
+ }
+
+ if (cg2900_enable_regulator(uart_info))
+ goto unlock;
+
+ if (pf_data->enable_chip) {
+ pf_data->enable_chip(dev);
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_AWAKE\n");
+ uart_info->sleep_state = CHIP_AWAKE;
+ }
+
+ (void)hci_uart_set_baudrate(uart_info->hu, uart_baudrate);
+
+ hci_uart_flow_ctrl(uart_info->hu, FLOW_ON);
+ hci_uart_set_break(uart_info->hu, BREAK_OFF);
+ } else {
+ /* Turn off the chip.*/
+ switch (uart_info->sleep_state) {
+ case CHIP_AWAKE:
+ break;
+ case CHIP_FALLING_ASLEEP:
+ hci_uart_set_break(uart_info->hu, BREAK_OFF);
+ break;
+ case CHIP_SUSPENDED:
+ case CHIP_ASLEEP:
+ unset_cts_irq(uart_info);
+ enable_uart_pins(uart_info);
+ break;
+ default:
+ break;
+ }
+
+ if (uart_info->suspend_blocked) {
+ wake_unlock(&uart_info->wake_lock);
+ uart_info->suspend_blocked = false;
+ }
+
+ if (pf_data->disable_chip) {
+ pf_data->disable_chip(dev);
+ dev_dbg(MAIN_DEV,
+ "New sleep_state: CHIP_POWERED_DOWN\n");
+ uart_info->sleep_state = CHIP_POWERED_DOWN;
+ }
+ cg2900_disable_regulator(uart_info);
+ /*
+ * Setting baud rate to 0 will tell UART driver to shut off its
+ * clocks.
+ */
+ (void)hci_uart_set_baudrate(uart_info->hu, ZERO_BAUD_RATE);
+ }
+
+unlock:
+ mutex_unlock(&(uart_info->sleep_state_lock));
+}
+
+/**
+ * uart_chip_startup_finished() - CG2900 startup finished.
+ * @dev: Transport device information.
+ */
+static void uart_chip_startup_finished(struct cg2900_chip_dev *dev)
+{
+ struct uart_info *uart_info = dev_get_drvdata(dev->dev);
+ unsigned long timeout_jiffies = get_sleep_timeout(uart_info);
+
+ /* Schedule work to put the chip and transport to sleep. */
+ if (timeout_jiffies)
+ queue_delayed_work(uart_info->wq, &uart_info->sleep_work.work,
+ timeout_jiffies);
+}
+/**
+ * uart_close() - Close the CG2900 UART for data transfers.
+ * @dev: Transport device information.
+ *
+ * Returns:
+ * 0 if there is no error.
+ */
+static int uart_close(struct cg2900_chip_dev *dev)
+{
+ /* The chip is already shut down. Power off the chip. */
+ uart_set_chip_power(dev, false);
+ return 0;
+}
+
+/**
+ * send_skb_to_core() - Sends packet received from UART to CG2900 Core.
+ * @skb: Received data packet.
+ *
+ * This function checks if UART is waiting for Command complete event,
+ * see set_baud_rate.
+ * If it is waiting it checks if it is the expected packet and the status.
+ * If not is passes the packet to CG2900 Core.
+ */
+static void send_skb_to_core(struct uart_info *uart_info, struct sk_buff *skb)
+{
+ u8 status;
+
+ if (!skb) {
+ dev_err(MAIN_DEV, "send_skb_to_core: Received NULL as skb\n");
+ return;
+ }
+
+ if (BAUD_WAITING == uart_info->baud_rate_state) {
+ /*
+ * Should only really be one packet received now:
+ * the CmdComplete for the SetBaudrate command
+ * Let's see if this is the packet we are waiting for.
+ */
+ if (!is_bt_cmd_complete_no_param(skb,
+ CG2900_BT_OP_VS_SET_BAUD_RATE, &status)) {
+ /*
+ * Received other event. Should not really happen,
+ * but pass the data to CG2900 Core anyway.
+ */
+ dev_dbg(MAIN_DEV, "Sending packet to CG2900 Core while "
+ "waiting for BaudRate CmdComplete\n");
+ uart_info->chip_dev.c_cb.data_from_chip
+ (&uart_info->chip_dev, skb);
+ return;
+ }
+
+ /*
+ * We have received complete event for our baud rate
+ * change command
+ */
+ if (HCI_BT_ERROR_NO_ERROR == status) {
+ dev_dbg(MAIN_DEV, "Received baud rate change complete "
+ "event OK\n");
+ dev_dbg(MAIN_DEV,
+ "New baud_rate_state: BAUD_SUCCESS\n");
+ uart_info->baud_rate_state = BAUD_SUCCESS;
+ } else {
+ dev_err(MAIN_DEV,
+ "Received baud rate change complete event "
+ "with status 0x%X\n", status);
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_FAIL\n");
+ uart_info->baud_rate_state = BAUD_FAIL;
+ }
+ wake_up_all(&uart_wait_queue);
+ kfree_skb(skb);
+ } else if (BAUD_SENDING_RESET == uart_info->baud_rate_state) {
+ /*
+ * Should only really be one packet received now:
+ * the CmdComplete for the Reset command
+ * Let's see if this is the packet we are waiting for.
+ */
+ if (!is_bt_cmd_complete_no_param(skb, HCI_OP_RESET, &status)) {
+ /*
+ * Received other event. Should not really happen,
+ * but pass the data to CG2900 Core anyway.
+ */
+ dev_dbg(MAIN_DEV, "Sending packet to CG2900 Core while "
+ "waiting for Reset CmdComplete\n");
+ uart_info->chip_dev.c_cb.data_from_chip
+ (&uart_info->chip_dev, skb);
+ return;
+ }
+
+ /*
+ * We have received complete event for our baud rate
+ * change command
+ */
+ if (HCI_BT_ERROR_NO_ERROR == status) {
+ dev_dbg(MAIN_DEV,
+ "Received HCI reset complete event OK\n");
+ /*
+ * Go back to BAUD_IDLE since this was not really
+ * baud rate change but just a preparation of the chip
+ * to be ready to receive commands.
+ */
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_IDLE\n");
+ uart_info->baud_rate_state = BAUD_IDLE;
+ } else {
+ dev_err(MAIN_DEV,
+ "Received HCI reset complete event with "
+ "status 0x%X", status);
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_FAIL\n");
+ uart_info->baud_rate_state = BAUD_FAIL;
+ }
+ wake_up_all(&uart_wait_queue);
+ kfree_skb(skb);
+ } else {
+ /* Just pass data to CG2900 Core */
+ uart_info->chip_dev.c_cb.data_from_chip
+ (&uart_info->chip_dev, skb);
+ }
+}
+
+/**
+ * check_data_len() - Check number of bytes to receive.
+ * @len: Number of bytes left to receive.
+ */
+static void check_data_len(struct uart_info *uart_info, int len)
+{
+ /* First get number of bytes left in the sk_buffer */
+ register int room = skb_tailroom(uart_info->rx_skb);
+
+ if (!len) {
+ /* No data left to receive. Transmit to CG2900 Core */
+ send_skb_to_core(uart_info, uart_info->rx_skb);
+ } else if (len > room) {
+ dev_err(MAIN_DEV, "Data length is too large (%d > %d)\n",
+ len, room);
+ kfree_skb(uart_info->rx_skb);
+ } else {
+ /*
+ * "Normal" case. Switch to data receiving state and store
+ * data length.
+ */
+ uart_info->rx_state = W4_DATA;
+ uart_info->rx_count = len;
+ return;
+ }
+
+ uart_info->rx_state = W4_PACKET_TYPE;
+ uart_info->rx_skb = NULL;
+ uart_info->rx_count = 0;
+}
+
+/**
+ * work_restart_sleep() - Cancel pending sleep_work, wake-up driver and
+ * schedule new sleep_work in a work context.
+ * @work: work which needs to be done.
+ */
+static void work_restart_sleep(struct work_struct *work)
+{
+ struct uart_work_struct *current_work =
+ container_of(work, struct uart_work_struct, work);
+ struct uart_info *uart_info = (struct uart_info *)current_work->data;
+ unsigned long timeout_jiffies = get_sleep_timeout(uart_info);
+
+ spin_lock_bh(&(uart_info->transmission_lock));
+ uart_info->rx_in_progress = false;
+ spin_unlock_bh(&(uart_info->transmission_lock));
+
+ /* Cancel pending sleep work if there is any. */
+ cancel_delayed_work_sync(&uart_info->sleep_work.work);
+
+ wake_up_chip(uart_info);
+
+ spin_lock_bh(&(uart_info->transmission_lock));
+ /*
+ * If there are no ongoing transfers schedule the sleep work.
+ */
+ if (!(uart_info->tx_in_progress) && timeout_jiffies)
+ queue_delayed_work(uart_info->wq,
+ &uart_info->sleep_work.work,
+ timeout_jiffies);
+ spin_unlock_bh(&(uart_info->transmission_lock));
+}
+
+/**
+ * cg2900_hu_receive() - Handles received UART data.
+ * @data: Data received
+ * @count: Number of bytes received
+ *
+ * The cg2900_hu_receive() function handles received UART data and puts it
+ * together to one complete packet.
+ *
+ * Returns:
+ * Number of bytes not handled, i.e. 0 = no error.
+ */
+static int cg2900_hu_receive(struct hci_uart *hu,
+ void *data, int count)
+{
+ const u8 *r_ptr;
+ u8 *w_ptr;
+ int len;
+ struct hci_event_hdr *evt;
+ struct hci_acl_hdr *acl;
+ union fm_leg_evt_or_irq *fm;
+ struct gnss_hci_hdr *gnss;
+ struct uart_info *uart_info = dev_get_drvdata(hu->proto->dev);
+ u8 *tmp;
+
+ r_ptr = (const u8 *)data;
+
+ spin_lock_bh(&(uart_info->transmission_lock));
+ /* Mark that there is an ongoing transfer. */
+ uart_info->rx_in_progress = true;
+ spin_unlock_bh(&(uart_info->transmission_lock));
+
+ /* Cancel pending sleep work if there is any. */
+ cancel_delayed_work(&uart_info->sleep_work.work);
+
+ if (uart_debug)
+ print_hex_dump_bytes(NAME " RX:\t", DUMP_PREFIX_NONE,
+ data, count);
+
+ /* Continue while there is data left to handle */
+ while (count) {
+ /*
+ * If we have already received a packet we know how many bytes
+ * there are left.
+ */
+ if (!uart_info->rx_count)
+ goto check_h4_header;
+
+ /* First copy received data into the skb_rx */
+ len = min_t(unsigned int, uart_info->rx_count, count);
+ memcpy(skb_put(uart_info->rx_skb, len), r_ptr, len);
+ /* Update counters from the length and step the data pointer */
+ uart_info->rx_count -= len;
+ count -= len;
+ r_ptr += len;
+
+ if (uart_info->rx_count)
+ /*
+ * More data to receive to current packet. Break and
+ * wait for next data on the UART.
+ */
+ break;
+
+ /* Handle the different states */
+ tmp = uart_info->rx_skb->data + CG2900_SKB_RESERVE;
+ switch (uart_info->rx_state) {
+ case W4_DATA:
+ /*
+ * Whole data packet has been received.
+ * Transmit it to CG2900 Core.
+ */
+ send_skb_to_core(uart_info, uart_info->rx_skb);
+
+ uart_info->rx_state = W4_PACKET_TYPE;
+ uart_info->rx_skb = NULL;
+ continue;
+
+ case W4_EVENT_HDR:
+ evt = (struct hci_event_hdr *)tmp;
+ check_data_len(uart_info, evt->plen);
+ /* Header read. Continue with next bytes */
+ continue;
+
+ case W4_ACL_HDR:
+ acl = (struct hci_acl_hdr *)tmp;
+ check_data_len(uart_info, le16_to_cpu(acl->dlen));
+ /* Header read. Continue with next bytes */
+ continue;
+
+ case W4_FM_RADIO_HDR:
+ fm = (union fm_leg_evt_or_irq *)tmp;
+ check_data_len(uart_info, fm->param_length);
+ /* Header read. Continue with next bytes */
+ continue;
+
+ case W4_GNSS_HDR:
+ gnss = (struct gnss_hci_hdr *)tmp;
+ check_data_len(uart_info, le16_to_cpu(gnss->plen));
+ /* Header read. Continue with next bytes */
+ continue;
+
+ default:
+ dev_err(MAIN_DEV,
+ "Bad state indicating memory overwrite "
+ "(0x%X)\n", (u8)(uart_info->rx_state));
+ break;
+ }
+
+check_h4_header:
+ /* Check which H:4 packet this is and update RX states */
+ if (*r_ptr == HCI_BT_EVT_H4_CHANNEL) {
+ uart_info->rx_state = W4_EVENT_HDR;
+ uart_info->rx_count = HCI_BT_EVT_HDR_SIZE;
+ } else if (*r_ptr == HCI_BT_ACL_H4_CHANNEL) {
+ uart_info->rx_state = W4_ACL_HDR;
+ uart_info->rx_count = HCI_BT_ACL_HDR_SIZE;
+ } else if (*r_ptr == HCI_FM_RADIO_H4_CHANNEL) {
+ uart_info->rx_state = W4_FM_RADIO_HDR;
+ uart_info->rx_count = HCI_FM_RADIO_HDR_SIZE;
+ } else if (*r_ptr == HCI_GNSS_H4_CHANNEL) {
+ uart_info->rx_state = W4_GNSS_HDR;
+ uart_info->rx_count = HCI_GNSS_HDR_SIZE;
+ } else {
+ dev_err(MAIN_DEV, "Unknown HCI packet type 0x%X\n",
+ (u8)*r_ptr);
+ r_ptr++;
+ count--;
+ continue;
+ }
+
+ /*
+ * Allocate packet. We do not yet know the size and therefore
+ * allocate max size.
+ */
+ uart_info->rx_skb = alloc_rx_skb(RX_SKB_MAX_SIZE, GFP_ATOMIC);
+ if (!uart_info->rx_skb) {
+ dev_err(MAIN_DEV,
+ "Can't allocate memory for new packet\n");
+ uart_info->rx_state = W4_PACKET_TYPE;
+ uart_info->rx_count = 0;
+
+ spin_lock_bh(&(uart_info->transmission_lock));
+ uart_info->rx_in_progress = false;
+ spin_unlock_bh(&(uart_info->transmission_lock));
+ return 0;
+ }
+
+ /* Write the H:4 header first in the sk_buffer */
+ w_ptr = skb_put(uart_info->rx_skb, 1);
+ *w_ptr = *r_ptr;
+
+ /* First byte (H4 header) read. Goto next byte */
+ r_ptr++;
+ count--;
+ }
+
+ (void)queue_work(uart_info->wq, &uart_info->restart_sleep_work.work);
+
+ return count;
+}
+
+/**
+ * cg2900_hu_open() - Called when UART line discipline changed to N_HCI.
+ * @hu: Pointer to associated Hci uart structure.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Errors from cg2900_register_trans_driver.
+ */
+static int cg2900_hu_open(struct hci_uart *hu)
+{
+ int err;
+ struct uart_info *uart_info = dev_get_drvdata(hu->proto->dev);
+
+ if (!uart_info)
+ return -EACCES;
+
+ dev_info(MAIN_DEV, "UART opened\n");
+
+ skb_queue_head_init(&uart_info->tx_queue);
+
+ uart_info->hu = hu;
+
+ /* Tell CG2900 Core that UART is connected */
+ err = cg2900_register_trans_driver(&uart_info->chip_dev);
+ if (err)
+ dev_err(MAIN_DEV, "Could not register transport driver (%d)\n",
+ err);
+
+ if (hu->tty->ops->tiocmget && hu->tty->ops->break_ctl)
+ uart_info->sleep_allowed = true;
+ else {
+ dev_err(MAIN_DEV, "Sleep mode not available\n");
+ uart_info->sleep_allowed = false;
+ }
+
+ return err;
+
+}
+
+/**
+ * cg2900_hu_close() - Close UART tty.
+ * @hu: Pointer to associated hci_uart structure.
+ *
+ * The uart_tty_close() function is called when the line discipline is changed
+ * to something else, the TTY is closed, or the TTY detects a hangup.
+ */
+static int cg2900_hu_close(struct hci_uart *hu)
+{
+ int err;
+ struct uart_info *uart_info = dev_get_drvdata(hu->proto->dev);
+
+
+ BUG_ON(!uart_info);
+ BUG_ON(!uart_info->wq);
+
+ /* Purge any stored sk_buffers */
+ skb_queue_purge(&uart_info->tx_queue);
+ if (uart_info->rx_skb) {
+ kfree_skb(uart_info->rx_skb);
+ uart_info->rx_skb = NULL;
+ }
+
+ dev_info(MAIN_DEV, "UART closed\n");
+ err = create_work_item(uart_info, work_hw_deregistered);
+ if (err)
+ dev_err(MAIN_DEV, "Failed to create work item (%d) "
+ "work_hw_deregistered\n", err);
+
+ uart_info->hu = NULL;
+
+ return 0;
+}
+
+/**
+ * cg2900_hu_dequeue() - Get new skbuff.
+ * @hu: Pointer to associated hci_uart structure.
+ *
+ * The uart_tty_close() function is called when the line discipline is changed
+ * to something else, the TTY is closed, or the TTY detects a hangup.
+ */
+static struct sk_buff *cg2900_hu_dequeue(struct hci_uart *hu)
+{
+ struct sk_buff *skb;
+ struct uart_info *uart_info = dev_get_drvdata(hu->proto->dev);
+ unsigned long timeout_jiffies = get_sleep_timeout(uart_info);
+
+ spin_lock_bh(&(uart_info->transmission_lock));
+
+ skb = skb_dequeue(&uart_info->tx_queue);
+
+ if (!skb)
+ uart_info->tx_in_progress = false;
+
+ /*
+ * If there are no ongoing transfers schedule the sleep work.
+ */
+ if (!(uart_info->rx_in_progress) && timeout_jiffies && !skb)
+ queue_delayed_work(uart_info->wq,
+ &uart_info->sleep_work.work,
+ timeout_jiffies);
+
+ spin_unlock_bh(&(uart_info->transmission_lock));
+
+ if (BAUD_SENDING == uart_info->baud_rate_state && !skb)
+ finish_setting_baud_rate(hu);
+ /*
+ * If it's set baud rate cmd set correct baud state and after
+ * sending is finished inform the tty driver about the new
+ * baud rate.
+ */
+ if ((BAUD_START == uart_info->baud_rate_state) &&
+ skb && (is_set_baud_rate_cmd(skb->data))) {
+ dev_dbg(MAIN_DEV, "UART set baud rate cmd found\n");
+ uart_info->baud_rate_state = BAUD_SENDING;
+ }
+
+ if (uart_debug && skb)
+ print_hex_dump_bytes(NAME " TX:\t", DUMP_PREFIX_NONE,
+ skb->data, skb->len);
+
+ return skb;
+}
+
+/**
+ * cg2900_hu_flush() - Flush buffers.
+ * @hu: Pointer to associated hci_uart structure.
+ *
+ */
+static int cg2900_hu_flush(struct hci_uart *hu)
+{
+ struct uart_info *uart_info = dev_get_drvdata(hu->proto->dev);
+
+ dev_dbg(MAIN_DEV, "ui %p", uart_info);
+ skb_queue_purge(&uart_info->tx_queue);
+ return 0;
+}
+
+/**
+ * cg2900_uart_probe() - Initialize CG2900 UART resources.
+ * @pdev: Platform device.
+ *
+ * This function initializes the module and registers to the UART framework.
+ *
+ * Returns:
+ * 0 if success.
+ * -ENOMEM for failed alloc or structure creation.
+ * -ECHILD for failed work queue creation.
+ * Error codes generated by tty_register_ldisc.
+ */
+static int __devinit cg2900_uart_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct uart_info *uart_info;
+ struct hci_uart_proto *p;
+ struct resource *resource;
+
+ pr_debug("cg2900_uart_probe");
+
+ uart_info = kzalloc(sizeof(*uart_info), GFP_KERNEL);
+ if (!uart_info) {
+ pr_err("Couldn't allocate uart_info");
+ return -ENOMEM;
+ }
+
+ uart_info->sleep_state = CHIP_POWERED_DOWN;
+ mutex_init(&(uart_info->sleep_state_lock));
+
+ spin_lock_init(&(uart_info->transmission_lock));
+
+ uart_info->chip_dev.t_cb.open = uart_open;
+ uart_info->chip_dev.t_cb.close = uart_close;
+ uart_info->chip_dev.t_cb.write = uart_write;
+ uart_info->chip_dev.t_cb.set_chip_power = uart_set_chip_power;
+ uart_info->chip_dev.t_cb.chip_startup_finished =
+ uart_chip_startup_finished;
+ uart_info->chip_dev.pdev = pdev;
+ uart_info->chip_dev.dev = &pdev->dev;
+ uart_info->chip_dev.t_data = uart_info;
+ wake_lock_init(&uart_info->wake_lock, WAKE_LOCK_SUSPEND, NAME);
+ uart_info->suspend_blocked = false;
+
+ resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ "cts_irq");
+ if (!resource) {
+ dev_err(&pdev->dev, "CTS IRQ does not exist\n");
+ err = -EINVAL;
+ goto error_handling_free;
+ }
+ uart_info->cts_irq = resource->start;
+
+ resource = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "cts_gpio");
+ if (!resource) {
+ dev_err(&pdev->dev, "CTS GPIO does not exist\n");
+ err = -EINVAL;
+ goto error_handling_free;
+ }
+ uart_info->cts_gpio = resource->start;
+
+ /* Init UART TX work queue */
+ uart_info->wq = create_singlethread_workqueue(UART_WQ_NAME);
+ if (!uart_info->wq) {
+ dev_err(MAIN_DEV, "Could not create workqueue\n");
+ err = -ECHILD; /* No child processes */
+ goto error_handling_free;
+ }
+
+ /* Initialize sleep work data */
+ uart_info->sleep_work.data = uart_info;
+ INIT_DELAYED_WORK(&uart_info->sleep_work.work, set_chip_sleep_mode);
+
+ /* Initialize wake-up work data */
+ uart_info->wakeup_work.data = uart_info;
+ INIT_WORK(&uart_info->wakeup_work.work, work_wake_up_chip);
+
+ /* Initialize after_receive work data */
+ uart_info->restart_sleep_work.data = uart_info;
+ INIT_WORK(&uart_info->restart_sleep_work.work, work_restart_sleep);
+
+ uart_info->dev = &pdev->dev;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ dev_err(MAIN_DEV, "cg2900_uart_probe: Could not allocate p\n");
+ goto error_handling_wq;
+ }
+
+ p->dev = uart_info->dev;
+ p->id = HCI_UART_STE;
+ p->open = &cg2900_hu_open;
+ p->close = &cg2900_hu_close;
+ p->recv = &cg2900_hu_receive;
+ p->dequeue = &cg2900_hu_dequeue;
+ p->flush = &cg2900_hu_flush;
+
+ dev_set_drvdata(uart_info->dev, (void *)uart_info);
+
+ err = hci_uart_register_proto(p);
+ if (err) {
+ dev_err(MAIN_DEV, "cg2900_uart_probe: Can not register "
+ "protocol\n");
+ kfree(p);
+ goto error_handling_wq;
+ }
+
+ goto finished;
+
+error_handling_wq:
+ destroy_workqueue(uart_info->wq);
+error_handling_free:
+ kfree(uart_info);
+ uart_info = NULL;
+finished:
+ return err;
+}
+
+/**
+ * cg2900_uart_remove() - Release CG2900 UART resources.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if success.
+ * Error codes generated by tty_unregister_ldisc.
+ */
+static int __devexit cg2900_uart_remove(struct platform_device *pdev)
+{
+ struct uart_info *uart_info = dev_get_drvdata(&pdev->dev);
+
+ pr_debug("cg2900_uart_remove");
+
+ if (!uart_info)
+ return -ECHILD;
+
+ if (uart_info->hu)
+ hci_uart_unregister_proto(uart_info->hu->proto);
+
+ wake_lock_destroy(&uart_info->wake_lock);
+ destroy_workqueue(uart_info->wq);
+
+ dev_info(MAIN_DEV, "CG2900 UART removed\n");
+ kfree(uart_info);
+ uart_info = NULL;
+ return 0;
+}
+
+static struct platform_driver cg2900_uart_driver = {
+ .driver = {
+ .name = "cg2900-uart",
+ .owner = THIS_MODULE,
+ },
+ .probe = cg2900_uart_probe,
+ .remove = __devexit_p(cg2900_uart_remove),
+#ifdef CONFIG_PM
+ .suspend = cg2900_uart_suspend,
+ .resume = cg2900_uart_resume
+#endif
+};
+
+
+/**
+ * cg2900_uart_init() - Initialize module.
+ *
+ * Registers platform driver.
+ */
+static int __init cg2900_uart_init(void)
+{
+ pr_debug("cg2900_uart_init");
+ return platform_driver_register(&cg2900_uart_driver);
+}
+
+/**
+ * cg2900_uart_exit() - Remove module.
+ *
+ * Unregisters platform driver.
+ */
+static void __exit cg2900_uart_exit(void)
+{
+ pr_debug("cg2900_uart_exit");
+ platform_driver_unregister(&cg2900_uart_driver);
+}
+
+module_init(cg2900_uart_init);
+module_exit(cg2900_uart_exit);
+
+module_param(uart_default_baud, int, S_IRUGO);
+MODULE_PARM_DESC(uart_default_baud,
+ "Default UART baud rate, e.g. 115200. If not set 115200 will "
+ "be used.");
+
+module_param(uart_high_baud, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(uart_high_baud,
+ "High speed UART baud rate, e.g. 4000000. If not set 3000000 "
+ "will be used.");
+
+module_param(uart_debug, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(uart_debug, "Enable/Disable debug. 0 means Debug disabled.");
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ST-Ericsson CG2900 UART Driver");
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index 42d69d4de05..dde7c85908e 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -736,7 +736,8 @@ static struct hci_uart_proto bcsp = {
.enqueue = bcsp_enqueue,
.dequeue = bcsp_dequeue,
.recv = bcsp_recv,
- .flush = bcsp_flush
+ .flush = bcsp_flush,
+ .register_hci_dev = true
};
int bcsp_init(void)
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index 3f038f5308a..ab05d0a8f08 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -270,6 +270,7 @@ static struct hci_uart_proto h4p = {
.enqueue = h4_enqueue,
.dequeue = h4_dequeue,
.flush = h4_flush,
+ .register_hci_dev = true
};
int h4_init(void)
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 76a1abb8f21..c73b1dbd3ef 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -46,7 +46,10 @@
#include "hci_uart.h"
-#define VERSION "2.2"
+#define VERSION "2.3"
+
+#define TTY_BREAK_ON (-1)
+#define TTY_BREAK_OFF (0)
static int reset = 0;
@@ -90,6 +93,9 @@ static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
{
struct hci_dev *hdev = hu->hdev;
+ if (!hdev)
+ return;
+
/* Update HCI stat counters */
switch (pkt_type) {
case HCI_COMMAND_PKT:
@@ -139,7 +145,8 @@ restart:
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
len = tty->ops->write(tty, skb->data, skb->len);
- hdev->stat.byte_tx += len;
+ if (hdev)
+ hdev->stat.byte_tx += len;
skb_pull(skb, len);
if (skb->len) {
@@ -158,6 +165,70 @@ restart:
return 0;
}
+int hci_uart_set_break(struct hci_uart *hu, bool break_on)
+{
+ struct tty_struct *tty = hu->tty;
+ int state = TTY_BREAK_OFF;
+
+ if (break_on)
+ state = TTY_BREAK_ON;
+
+ if (tty->ops->break_ctl)
+ return tty->ops->break_ctl(tty, state);
+ else
+ return -EOPNOTSUPP;
+}
+
+void hci_uart_flow_ctrl(struct hci_uart *hu, bool flow_on)
+{
+ if (flow_on)
+ tty_unthrottle(hu->tty);
+ else
+ tty_throttle(hu->tty);
+}
+
+int hci_uart_set_baudrate(struct hci_uart *hu, int baud)
+{
+ struct ktermios old_termios;
+ struct tty_struct *tty = hu->tty;
+
+ if (!tty->ops->set_termios)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&(tty->termios_mutex));
+ /* Start by storing the old termios. */
+ memcpy(&old_termios, tty->termios, sizeof(old_termios));
+
+ tty_encode_baud_rate(tty, baud, baud);
+
+ /* Finally inform the driver */
+ tty->ops->set_termios(tty, &old_termios);
+
+ mutex_unlock(&(tty->termios_mutex));
+
+ return 0;
+}
+
+int hci_uart_tiocmget(struct hci_uart *hu)
+{
+ struct tty_struct *tty = hu->tty;
+
+ if (!tty->ops->tiocmget || !hu->fd)
+ return -EOPNOTSUPP;
+
+ return tty->ops->tiocmget(tty, hu->fd);
+}
+
+void hci_uart_flush_buffer(struct hci_uart *hu)
+{
+ tty_driver_flush_buffer(hu->tty);
+}
+
+int hci_uart_chars_in_buffer(struct hci_uart *hu)
+{
+ return tty_chars_in_buffer(hu->tty);
+}
+
/* ------- Interface to HCI layer ------ */
/* Initialize device */
static int hci_uart_open(struct hci_dev *hdev)
@@ -261,6 +332,9 @@ static int hci_uart_tty_open(struct tty_struct *tty)
if (hu)
return -EEXIST;
+ if (!tty->ops->write)
+ return -ENOTTY;
+
if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
BT_ERR("Can't allocate control structure");
return -ENFILE;
@@ -306,8 +380,11 @@ static void hci_uart_tty_close(struct tty_struct *tty)
if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
hu->proto->close(hu);
- hci_unregister_dev(hdev);
- hci_free_dev(hdev);
+
+ if (hdev) {
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+ }
}
}
}
@@ -362,7 +439,8 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *f
spin_lock(&hu->rx_lock);
hu->proto->recv(hu, (void *) data, count);
- hu->hdev->stat.byte_rx += count;
+ if (hu->hdev)
+ hu->hdev->stat.byte_rx += count;
spin_unlock(&hu->rx_lock);
tty_unthrottle(tty);
@@ -415,11 +493,18 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
if (!p)
return -EPROTONOSUPPORT;
+ hu->proto = p;
+
err = p->open(hu);
if (err)
return err;
- hu->proto = p;
+ /*
+ * Protocol might register hdev by itself.
+ * In that case, there is no need to register it here.
+ */
+ if (!hu->proto->register_hci_dev)
+ return 0;
err = hci_uart_register_dev(hu);
if (err) {
@@ -463,6 +548,8 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
clear_bit(HCI_UART_PROTO_SET, &hu->flags);
return err;
}
+ /* Keep file descriptor.*/
+ hu->fd = file;
} else
return -EBUSY;
break;
@@ -473,8 +560,12 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
return -EUNATCH;
case HCIUARTGETDEVICE:
- if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
- return hu->hdev->id;
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+ if (hu->hdev)
+ return hu->hdev->id;
+ else
+ return -ENOMSG;
+ }
return -EUNATCH;
default:
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index fb8445c7365..b6f7facf81c 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -515,6 +515,7 @@ static struct hci_uart_proto llp = {
.enqueue = ll_enqueue,
.dequeue = ll_dequeue,
.flush = ll_flush,
+ .register_to_bt = true
};
int ll_init(void)
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 50113db06b9..48326f0cf31 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -33,13 +33,20 @@
#define HCIUARTGETDEVICE _IOR('U', 202, int)
/* UART protocols */
-#define HCI_UART_MAX_PROTO 5
+#define HCI_UART_MAX_PROTO 6
#define HCI_UART_H4 0
#define HCI_UART_BCSP 1
#define HCI_UART_3WIRE 2
#define HCI_UART_H4DS 3
#define HCI_UART_LL 4
+#define HCI_UART_STE 5
+
+/* UART break and flow control parameters */
+#define BREAK_ON true
+#define BREAK_OFF false
+#define FLOW_ON true
+#define FLOW_OFF false
struct hci_uart;
@@ -51,6 +58,8 @@ struct hci_uart_proto {
int (*recv)(struct hci_uart *hu, void *data, int len);
int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
struct sk_buff *(*dequeue)(struct hci_uart *hu);
+ bool register_hci_dev;
+ struct device *dev;
};
struct hci_uart {
@@ -64,6 +73,8 @@ struct hci_uart {
struct sk_buff *tx_skb;
unsigned long tx_state;
spinlock_t rx_lock;
+
+ struct file *fd;
};
/* HCI_UART flag bits */
@@ -76,6 +87,12 @@ struct hci_uart {
int hci_uart_register_proto(struct hci_uart_proto *p);
int hci_uart_unregister_proto(struct hci_uart_proto *p);
int hci_uart_tx_wakeup(struct hci_uart *hu);
+int hci_uart_set_baudrate(struct hci_uart *hu, int baud);
+int hci_uart_set_break(struct hci_uart *hu, bool break_on);
+int hci_uart_tiocmget(struct hci_uart *hu);
+void hci_uart_flush_buffer(struct hci_uart *hu);
+void hci_uart_flow_ctrl(struct hci_uart *hu, bool flow_on);
+int hci_uart_chars_in_buffer(struct hci_uart *hu);
#ifdef CONFIG_BT_HCIUART_H4
int h4_init(void);
@@ -91,3 +108,4 @@ int bcsp_deinit(void);
int ll_init(void);
int ll_deinit(void);
#endif
+
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 7cfcc629a7f..632dab9847d 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -88,6 +88,19 @@ config VT_HW_CONSOLE_BINDING
information. For framebuffer console users, please refer to
<file:Documentation/fb/fbcon.txt>.
+config DEVMEM
+ bool "Memory device driver"
+ default y
+ help
+ The memory driver provides two character devices, mem and kmem, which
+ provide access to the system's memory. The mem device is a view of
+ physical memory, and each byte in the device corresponds to the
+ matching physical address. The kmem device is the same as mem, but
+ the addresses correspond to the kernel's virtual address space rather
+ than physical memory. These devices are standard parts of a Linux
+ system and most users should say Y here. You might say N if very
+ security conscience or memory is tight.
+
config DEVKMEM
bool "/dev/kmem virtual device support"
default y
@@ -1119,6 +1132,10 @@ config DEVPORT
depends on ISA || PCI
default y
+config DCC_TTY
+ tristate "DCC tty driver"
+ depends on ARM
+
source "drivers/s390/char/Kconfig"
config RAMOOPS
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 88d6eac6975..41d71912b37 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -107,10 +107,14 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
obj-$(CONFIG_TCG_TPM) += tpm/
+obj-$(CONFIG_DCC_TTY) += dcc_tty.o
obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_RAMOOPS) += ramoops.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
+ifdef CONFIG_PHONET
+obj-$(CONFIG_U8500_SHRM) += shrm_char.o
+endif
js-rtc-y = rtc.o
# Files generated that shall be removed upon make clean
diff --git a/drivers/char/dcc_tty.c b/drivers/char/dcc_tty.c
new file mode 100644
index 00000000000..a787accdcb1
--- /dev/null
+++ b/drivers/char/dcc_tty.c
@@ -0,0 +1,326 @@
+/* drivers/char/dcc_tty.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/hrtimer.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
+MODULE_DESCRIPTION("DCC TTY Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+static spinlock_t g_dcc_tty_lock = SPIN_LOCK_UNLOCKED;
+static struct hrtimer g_dcc_timer;
+static char g_dcc_buffer[16];
+static int g_dcc_buffer_head;
+static int g_dcc_buffer_count;
+static unsigned g_dcc_write_delay_usecs = 1;
+static struct tty_driver *g_dcc_tty_driver;
+static struct tty_struct *g_dcc_tty;
+static int g_dcc_tty_open_count;
+
+static void dcc_poll_locked(void)
+{
+ char ch;
+ int rch;
+ int written;
+
+ while (g_dcc_buffer_count) {
+ ch = g_dcc_buffer[g_dcc_buffer_head];
+ asm(
+ "mrc 14, 0, r15, c0, c1, 0\n"
+ "mcrcc 14, 0, %1, c0, c5, 0\n"
+ "movcc %0, #1\n"
+ "movcs %0, #0\n"
+ : "=r" (written)
+ : "r" (ch)
+ );
+ if (written) {
+ if (ch == '\n')
+ g_dcc_buffer[g_dcc_buffer_head] = '\r';
+ else {
+ g_dcc_buffer_head = (g_dcc_buffer_head + 1) % ARRAY_SIZE(g_dcc_buffer);
+ g_dcc_buffer_count--;
+ if (g_dcc_tty)
+ tty_wakeup(g_dcc_tty);
+ }
+ g_dcc_write_delay_usecs = 1;
+ } else {
+ if (g_dcc_write_delay_usecs > 0x100)
+ break;
+ g_dcc_write_delay_usecs <<= 1;
+ udelay(g_dcc_write_delay_usecs);
+ }
+ }
+
+ if (g_dcc_tty && !test_bit(TTY_THROTTLED, &g_dcc_tty->flags)) {
+ asm(
+ "mrc 14, 0, %0, c0, c1, 0\n"
+ "tst %0, #(1 << 30)\n"
+ "moveq %0, #-1\n"
+ "mrcne 14, 0, %0, c0, c5, 0\n"
+ : "=r" (rch)
+ );
+ if (rch >= 0) {
+ ch = rch;
+ tty_insert_flip_string(g_dcc_tty, &ch, 1);
+ tty_flip_buffer_push(g_dcc_tty);
+ }
+ }
+
+
+ if (g_dcc_buffer_count)
+ hrtimer_start(&g_dcc_timer, ktime_set(0, g_dcc_write_delay_usecs * NSEC_PER_USEC), HRTIMER_MODE_REL);
+ else
+ hrtimer_start(&g_dcc_timer, ktime_set(0, 20 * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+}
+
+static int dcc_tty_open(struct tty_struct * tty, struct file * filp)
+{
+ int ret;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ if (g_dcc_tty == NULL || g_dcc_tty == tty) {
+ g_dcc_tty = tty;
+ g_dcc_tty_open_count++;
+ ret = 0;
+ } else
+ ret = -EBUSY;
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+
+ printk("dcc_tty_open, tty %p, f_flags %x, returned %d\n", tty, filp->f_flags, ret);
+
+ return ret;
+}
+
+static void dcc_tty_close(struct tty_struct * tty, struct file * filp)
+{
+ printk("dcc_tty_close, tty %p, f_flags %x\n", tty, filp->f_flags);
+ if (g_dcc_tty == tty) {
+ if (--g_dcc_tty_open_count == 0)
+ g_dcc_tty = NULL;
+ }
+}
+
+static int dcc_write(const unsigned char *buf_start, int count)
+{
+ const unsigned char *buf = buf_start;
+ unsigned long irq_flags;
+ int copy_len;
+ int space_left;
+ int tail;
+
+ if (count < 1)
+ return 0;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ do {
+ tail = (g_dcc_buffer_head + g_dcc_buffer_count) % ARRAY_SIZE(g_dcc_buffer);
+ copy_len = ARRAY_SIZE(g_dcc_buffer) - tail;
+ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
+ if (copy_len > space_left)
+ copy_len = space_left;
+ if (copy_len > count)
+ copy_len = count;
+ memcpy(&g_dcc_buffer[tail], buf, copy_len);
+ g_dcc_buffer_count += copy_len;
+ buf += copy_len;
+ count -= copy_len;
+ if (copy_len < count && copy_len < space_left) {
+ space_left -= copy_len;
+ copy_len = count;
+ if (copy_len > space_left) {
+ copy_len = space_left;
+ }
+ memcpy(g_dcc_buffer, buf, copy_len);
+ buf += copy_len;
+ count -= copy_len;
+ g_dcc_buffer_count += copy_len;
+ }
+ dcc_poll_locked();
+ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
+ } while(count && space_left);
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+ return buf - buf_start;
+}
+
+static int dcc_tty_write(struct tty_struct * tty, const unsigned char *buf, int count)
+{
+ int ret;
+ /* printk("dcc_tty_write %p, %d\n", buf, count); */
+ ret = dcc_write(buf, count);
+ if (ret != count)
+ printk("dcc_tty_write %p, %d, returned %d\n", buf, count, ret);
+ return ret;
+}
+
+static int dcc_tty_write_room(struct tty_struct *tty)
+{
+ int space_left;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+ return space_left;
+}
+
+static int dcc_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ int ret;
+ asm(
+ "mrc 14, 0, %0, c0, c1, 0\n"
+ "mov %0, %0, LSR #30\n"
+ "and %0, %0, #1\n"
+ : "=r" (ret)
+ );
+ return ret;
+}
+
+static void dcc_tty_unthrottle(struct tty_struct * tty)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ dcc_poll_locked();
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+}
+
+static enum hrtimer_restart dcc_tty_timer_func(struct hrtimer *timer)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ dcc_poll_locked();
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+ return HRTIMER_NORESTART;
+}
+
+void dcc_console_write(struct console *co, const char *b, unsigned count)
+{
+#if 1
+ dcc_write(b, count);
+#else
+ /* blocking printk */
+ while (count > 0) {
+ int written;
+ written = dcc_write(b, count);
+ if (written) {
+ b += written;
+ count -= written;
+ }
+ }
+#endif
+}
+
+static struct tty_driver *dcc_console_device(struct console *c, int *index)
+{
+ *index = 0;
+ return g_dcc_tty_driver;
+}
+
+static int __init dcc_console_setup(struct console *co, char *options)
+{
+ if (co->index != 0)
+ return -ENODEV;
+ return 0;
+}
+
+
+static struct console dcc_console =
+{
+ .name = "ttyDCC",
+ .write = dcc_console_write,
+ .device = dcc_console_device,
+ .setup = dcc_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+static struct tty_operations dcc_tty_ops = {
+ .open = dcc_tty_open,
+ .close = dcc_tty_close,
+ .write = dcc_tty_write,
+ .write_room = dcc_tty_write_room,
+ .chars_in_buffer = dcc_tty_chars_in_buffer,
+ .unthrottle = dcc_tty_unthrottle,
+};
+
+static int __init dcc_tty_init(void)
+{
+ int ret;
+
+ hrtimer_init(&g_dcc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ g_dcc_timer.function = dcc_tty_timer_func;
+
+ g_dcc_tty_driver = alloc_tty_driver(1);
+ if (!g_dcc_tty_driver) {
+ printk(KERN_ERR "dcc_tty_probe: alloc_tty_driver failed\n");
+ ret = -ENOMEM;
+ goto err_alloc_tty_driver_failed;
+ }
+ g_dcc_tty_driver->owner = THIS_MODULE;
+ g_dcc_tty_driver->driver_name = "dcc";
+ g_dcc_tty_driver->name = "ttyDCC";
+ g_dcc_tty_driver->major = 0; // auto assign
+ g_dcc_tty_driver->minor_start = 0;
+ g_dcc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ g_dcc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ g_dcc_tty_driver->init_termios = tty_std_termios;
+ g_dcc_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ tty_set_operations(g_dcc_tty_driver, &dcc_tty_ops);
+ ret = tty_register_driver(g_dcc_tty_driver);
+ if (ret) {
+ printk(KERN_ERR "dcc_tty_probe: tty_register_driver failed, %d\n", ret);
+ goto err_tty_register_driver_failed;
+ }
+ tty_register_device(g_dcc_tty_driver, 0, NULL);
+
+ register_console(&dcc_console);
+ hrtimer_start(&g_dcc_timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+
+ return 0;
+
+err_tty_register_driver_failed:
+ put_tty_driver(g_dcc_tty_driver);
+ g_dcc_tty_driver = NULL;
+err_alloc_tty_driver_failed:
+ return ret;
+}
+
+static void __exit dcc_tty_exit(void)
+{
+ int ret;
+
+ tty_unregister_device(g_dcc_tty_driver, 0);
+ ret = tty_unregister_driver(g_dcc_tty_driver);
+ if (ret < 0) {
+ printk(KERN_ERR "dcc_tty_remove: tty_unregister_driver failed, %d\n", ret);
+ } else {
+ put_tty_driver(g_dcc_tty_driver);
+ }
+ g_dcc_tty_driver = NULL;
+}
+
+module_init(dcc_tty_init);
+module_exit(dcc_tty_exit);
+
+
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 1f528fad351..91091b0f1a3 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -59,6 +59,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
}
#endif
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
#ifdef CONFIG_STRICT_DEVMEM
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
{
@@ -84,7 +85,9 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
return 1;
}
#endif
+#endif
+#ifdef CONFIG_DEVMEM
void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr)
{
}
@@ -211,6 +214,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf,
*ppos += written;
return written;
}
+#endif /* CONFIG_DEVMEM */
+
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
int __weak phys_mem_access_prot_allowed(struct file *file,
unsigned long pfn, unsigned long size, pgprot_t *vma_prot)
@@ -332,6 +338,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma)
}
return 0;
}
+#endif /* CONFIG_DEVMEM */
#ifdef CONFIG_DEVKMEM
static int mmap_kmem(struct file *file, struct vm_area_struct *vma)
@@ -696,6 +703,8 @@ static loff_t null_lseek(struct file *file, loff_t offset, int orig)
return file->f_pos = 0;
}
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT)
+
/*
* The memory devices use the full 32/64 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
@@ -729,10 +738,14 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
return ret;
}
+#endif
+
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT)
static int open_port(struct inode * inode, struct file * filp)
{
return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
}
+#endif
#define zero_lseek null_lseek
#define full_lseek null_lseek
@@ -742,6 +755,7 @@ static int open_port(struct inode * inode, struct file * filp)
#define open_kmem open_mem
#define open_oldmem open_mem
+#ifdef CONFIG_DEVMEM
static const struct file_operations mem_fops = {
.llseek = memory_lseek,
.read = read_mem,
@@ -750,6 +764,7 @@ static const struct file_operations mem_fops = {
.open = open_mem,
.get_unmapped_area = get_unmapped_area_mem,
};
+#endif
#ifdef CONFIG_DEVKMEM
static const struct file_operations kmem_fops = {
@@ -839,7 +854,9 @@ static const struct memdev {
const struct file_operations *fops;
struct backing_dev_info *dev_info;
} devlist[] = {
+#ifdef CONFIG_DEVMEM
[1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi },
+#endif
#ifdef CONFIG_DEVKMEM
[2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi },
#endif
diff --git a/drivers/char/shrm_char.c b/drivers/char/shrm_char.c
new file mode 100644
index 00000000000..a3f99752b41
--- /dev/null
+++ b/drivers/char/shrm_char.c
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Biju Das <biju.das@stericsson.com> for ST-Ericsson
+ * Author: Kumar Sanghavi <kumar.sanghvi@stericsson.com> for ST-Ericsson
+ * Author: Arun Murthy <arun.murthy@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+#include <mach/isa_ioctl.h>
+#include <mach/shrm_driver.h>
+#include <mach/shrm_private.h>
+#include <mach/shrm_config.h>
+#include <mach/shrm.h>
+
+
+#define NAME "IPC_ISA"
+/* L2 header for common loopback device is 0xc0 and hence 0xc0+1 = 193*/
+#define MAX_L2_HEADERS 193
+
+#define SIZE_OF_FIFO (512*1024)
+
+static u8 message_fifo[ISA_DEVICES][SIZE_OF_FIFO];
+
+static u8 wr_rpc_msg[10*1024];
+static u8 wr_sec_msg[10*1024];
+static u8 wr_audio_msg[10*1024];
+
+struct map_device {
+ u8 l2_header;
+ u8 idx;
+ char *name;
+};
+
+static struct map_device map_dev[] = {
+ {ISI_MESSAGING, 0, "isi"},
+ {RPC_MESSAGING, 1, "rpc"},
+ {AUDIO_MESSAGING, 2, "modemaudio"},
+ {SECURITY_MESSAGING, 3, "sec"},
+ {COMMON_LOOPBACK_MESSAGING, 4, "common_loopback"},
+ {AUDIO_LOOPBACK_MESSAGING, 5, "audio_loopback"},
+};
+
+/*
+ * int major:This variable is exported to user as module_param to specify
+ * major number at load time
+ */
+static int major;
+module_param(major, int, 0);
+MODULE_PARM_DESC(major, "Major device number");
+/* global fops mutex */
+static DEFINE_MUTEX(isa_lock);
+
+/**
+ * shrm_get_cdev_index() - return the index mapped to l2 header
+ * @l2_header: L2 header
+ *
+ * struct map_device maps the index(count) with the device L2 header.
+ * This function returns the index for the provided L2 header in case
+ * of success else -ve value.
+ */
+int shrm_get_cdev_index(u8 l2_header)
+{
+ u8 cnt;
+ for (cnt = 0; cnt < ISA_DEVICES; cnt++) {
+ if (map_dev[cnt].l2_header == l2_header)
+ return map_dev[cnt].idx;
+ }
+ return -EINVAL;
+}
+
+/**
+ * shrm_get_cdev_l2header() - return l2_header mapped to the index
+ * @idx: index
+ *
+ * struct map_device maps the index(count) with the device L2 header.
+ * This function returns the L2 header for the given index in case
+ * of success else -ve value.
+ */
+int shrm_get_cdev_l2header(u8 idx)
+{
+ u8 cnt;
+ for (cnt = 0; cnt < ISA_DEVICES; cnt++) {
+ if (map_dev[cnt].idx == idx)
+ return map_dev[cnt].l2_header;
+ }
+ return -EINVAL;
+}
+
+void shrm_char_reset_queues(struct shrm_dev *shrm)
+{
+ struct isadev_context *isadev;
+ struct isa_driver_context *isa_context;
+ struct queue_element *cur_msg = NULL;
+ struct list_head *cur_msg_ptr = NULL;
+ struct list_head *msg_ptr;
+ struct message_queue *q;
+ int no_dev;
+
+ dev_info(shrm->dev, "%s: Resetting char device queues\n", __func__);
+ isa_context = shrm->isa_context;
+ for (no_dev = 0 ; no_dev < ISA_DEVICES ; no_dev++) {
+ isadev = &isa_context->isadev[no_dev];
+ q = &isadev->dl_queue;
+
+ spin_lock_bh(&q->update_lock);
+ /* empty out the msg queue */
+ list_for_each_safe(cur_msg_ptr, msg_ptr, &q->msg_list) {
+ cur_msg = list_entry(cur_msg_ptr,
+ struct queue_element, entry);
+ list_del(cur_msg_ptr);
+ kfree(cur_msg);
+ }
+
+ /* reset the msg queue pointers */
+ q->size = SIZE_OF_FIFO;
+ q->readptr = 0;
+ q->writeptr = 0;
+ q->no = 0;
+
+ /* wake up the blocking read/select */
+ atomic_set(&q->q_rp, 1);
+ wake_up_interruptible(&q->wq_readable);
+
+ spin_unlock_bh(&q->update_lock);
+ }
+}
+
+/**
+ * create_queue() - To create FIFO for Tx and Rx message buffering.
+ * @q: message queue.
+ * @devicetype: device type 0-isi,1-rpc,2-audio,3-security,
+ * 4-common_loopback, 5-audio_loopback.
+ * @shrm: pointer to the shrm device information structure
+ *
+ * This function creates a FIFO buffer of n_bytes size using
+ * dma_alloc_coherent(). It also initializes all queue handling
+ * locks, queue management pointers. It also initializes message list
+ * which occupies this queue.
+ */
+static int create_queue(struct message_queue *q, u32 devicetype,
+ struct shrm_dev *shrm)
+{
+ q->fifo_base = (u8 *)&message_fifo[devicetype];
+ q->size = SIZE_OF_FIFO;
+ q->readptr = 0;
+ q->writeptr = 0;
+ q->no = 0;
+ q->shrm = shrm;
+ spin_lock_init(&q->update_lock);
+ INIT_LIST_HEAD(&q->msg_list);
+ init_waitqueue_head(&q->wq_readable);
+ atomic_set(&q->q_rp, 0);
+
+ return 0;
+}
+
+static void delete_queue(struct message_queue *q)
+{
+ q->size = 0;
+ q->readptr = 0;
+ q->writeptr = 0;
+}
+
+/**
+ * add_msg_to_queue() - Add a message inside queue
+ * @q: message queue
+ * @size: size in bytes
+ *
+ * This function tries to allocate n_bytes of size in FIFO q.
+ * It returns negative number when no memory can be allocated
+ * currently.
+ */
+int add_msg_to_queue(struct message_queue *q, u32 size)
+{
+ struct queue_element *new_msg = NULL;
+ struct shrm_dev *shrm = q->shrm;
+
+ dev_dbg(shrm->dev, "%s IN q->writeptr=%d\n", __func__, q->writeptr);
+ new_msg = kmalloc(sizeof(struct queue_element), GFP_ATOMIC);
+ if (new_msg == NULL) {
+ dev_err(shrm->dev, "unable to allocate memory\n");
+ return -ENOMEM;
+ }
+ new_msg->offset = q->writeptr;
+ new_msg->size = size;
+ new_msg->no = q->no++;
+
+ /* check for overflow condition */
+ if (q->readptr <= q->writeptr) {
+ if (((q->writeptr-q->readptr) + size) >= q->size) {
+ dev_err(shrm->dev, "Buffer overflow !!\n");
+ BUG_ON(((q->writeptr-q->readptr) + size) >= q->size);
+ }
+ } else {
+ if ((q->writeptr + size) >= q->readptr) {
+ dev_err(shrm->dev, "Buffer overflow !!\n");
+ BUG_ON((q->writeptr + size) >= q->readptr);
+ }
+ }
+ q->writeptr = (q->writeptr + size) % q->size;
+ if (list_empty(&q->msg_list)) {
+ list_add_tail(&new_msg->entry, &q->msg_list);
+ /* There can be 2 blocking calls read and another select */
+ atomic_set(&q->q_rp, 1);
+ wake_up_interruptible(&q->wq_readable);
+ } else
+ list_add_tail(&new_msg->entry, &q->msg_list);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return 0;
+}
+
+/**
+ * remove_msg_from_queue() - To remove a message from the msg queue.
+ * @q: message queue
+ *
+ * This function delets a message from the message list associated with message
+ * queue q and also updates read ptr.
+ * If the message list is empty, then, event is set to block the select and
+ * read calls of the paricular queue.
+ *
+ * The message list is FIFO style and message is always added to tail and
+ * removed from head.
+ */
+int remove_msg_from_queue(struct message_queue *q)
+{
+ struct queue_element *old_msg = NULL;
+ struct shrm_dev *shrm = q->shrm;
+ struct list_head *msg_ptr = NULL;
+ struct list_head *old_msg_ptr = NULL;
+
+ dev_dbg(shrm->dev, "%s IN q->readptr %d\n", __func__, q->readptr);
+
+ list_for_each_safe(old_msg_ptr, msg_ptr, &q->msg_list) {
+ old_msg = list_entry(old_msg_ptr, struct queue_element, entry);
+ if (old_msg == NULL) {
+ dev_err(shrm->dev, "no message found\n");
+ return -EFAULT;
+ }
+ list_del(old_msg_ptr);
+ q->readptr = (q->readptr + old_msg->size)%q->size;
+ kfree(old_msg);
+ break;
+ }
+ if (list_empty(&q->msg_list)) {
+ dev_dbg(shrm->dev, "List is empty setting RP= 0\n");
+ atomic_set(&q->q_rp, 0);
+ }
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return 0;
+}
+
+/**
+ * get_size_of_new_msg() - retrieve new message from message list
+ * @q: message queue
+ *
+ * This function will retrieve most recent message from the corresponding
+ * queue list. New message is always retrieved from head side.
+ * It returns new message no, offset if FIFO and size.
+ */
+int get_size_of_new_msg(struct message_queue *q)
+{
+ struct queue_element *new_msg = NULL;
+ struct list_head *msg_list;
+ struct shrm_dev *shrm = q->shrm;
+ int size = 0;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ spin_lock_bh(&q->update_lock);
+ list_for_each(msg_list, &q->msg_list) {
+ new_msg = list_entry(msg_list, struct queue_element, entry);
+ if (new_msg == NULL) {
+ spin_unlock_bh(&q->update_lock);
+ dev_err(shrm->dev, "no message found\n");
+ return -EFAULT;
+ }
+ size = new_msg->size;
+ break;
+ }
+ spin_unlock_bh(&q->update_lock);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return size;
+}
+
+/**
+ * isa_select() - shrm char interface driver select interface
+ * @filp: file descriptor pointer
+ * @wait: poll_table_struct pointer
+ *
+ * This function is used to perform non-blocking read operations. It allows
+ * a process to determine whether it can read from one or more open files
+ * without blocking. These calls can also block a process until any of a
+ * given set of file descriptors becomes available for reading.
+ * If a file is ready to read, POLLIN | POLLRDNORM bitmask is returned.
+ * The driver method is called whenever the user-space program performs a select
+ * system call involving a file descriptor associated with the driver.
+ */
+static u32 isa_select(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct isadev_context *isadev = filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+ struct message_queue *q;
+ u32 mask = 0;
+ u32 m = iminor(filp->f_path.dentry->d_inode);
+ u8 idx = shrm_get_cdev_index(m);
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (shrm->msr_flag)
+ return -ENODEV;
+
+ if (isadev->device_id != idx)
+ return -1;
+
+ q = &isadev->dl_queue;
+ poll_wait(filp, &q->wq_readable, wait);
+ if (atomic_read(&q->q_rp) == 1)
+ mask = POLLIN | POLLRDNORM;
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return mask;
+}
+
+/**
+ * isa_read() - Read from device
+ * @filp: file descriptor
+ * @buf: user buffer pointer
+ * @len: size of requested data transfer
+ * @ppos: not used
+ *
+ * It reads a oldest message from queue and copies it into user buffer and
+ * returns its size.
+ * If there is no message present in queue, then it blocks until new data is
+ * available.
+ */
+ssize_t isa_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
+{
+ u32 size = 0;
+ int ret;
+ char *psrc;
+ struct isadev_context *isadev = (struct isadev_context *)
+ filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+ struct message_queue *q;
+ u32 msgsize;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (len <= 0)
+ return -EFAULT;
+
+ q = &isadev->dl_queue;
+
+ if (shrm->msr_flag) {
+ atomic_set(&q->q_rp, 0);
+ return -ENODEV;
+ }
+
+ spin_lock_bh(&q->update_lock);
+ if (list_empty(&q->msg_list)) {
+ spin_unlock_bh(&q->update_lock);
+ dev_dbg(shrm->dev, "Waiting for Data\n");
+ if (wait_event_interruptible(q->wq_readable,
+ atomic_read(&q->q_rp) == 1))
+ return -ERESTARTSYS;
+ } else
+ spin_unlock_bh(&q->update_lock);
+
+ if (shrm->msr_flag) {
+ atomic_set(&q->q_rp, 0);
+ return -ENODEV;
+ }
+
+ msgsize = get_size_of_new_msg(q);
+
+ if (len < msgsize)
+ return -EINVAL;
+
+ if ((q->readptr+msgsize) >= q->size) {
+ dev_dbg(shrm->dev, "Inside Loop Back\n");
+ psrc = (char *)buf;
+ size = (q->size-q->readptr);
+ /* Copy First Part of msg */
+ if (copy_to_user(psrc,
+ (u8 *)(q->fifo_base+q->readptr),
+ size)) {
+ dev_err(shrm->dev, "copy_to_user failed\n");
+ return -EFAULT;
+ }
+ psrc += size;
+ /* Copy Second Part of msg at the top of fifo */
+ if (copy_to_user(psrc,
+ (u8 *)(q->fifo_base),
+ (msgsize-size))) {
+ dev_err(shrm->dev, "copy_to_user failed\n");
+ return -EFAULT;
+ }
+ } else {
+ if (copy_to_user(buf,
+ (u8 *)(q->fifo_base + q->readptr),
+ msgsize)) {
+ dev_err(shrm->dev, "copy_to_user failed\n");
+ return -EFAULT;
+ }
+ }
+ spin_lock_bh(&q->update_lock);
+ ret = remove_msg_from_queue(q);
+ if (ret < 0) {
+ dev_err(shrm->dev,
+ "Remove msg from message queue failed\n");
+ msgsize = ret;
+ }
+ spin_unlock_bh(&q->update_lock);
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return msgsize;
+}
+
+/**
+ * isa_write() - Write to shrm char device
+ * @filp: file descriptor
+ * @buf: user buffer pointer
+ * @len: size of requested data transfer
+ * @ppos: not used
+ *
+ * It checks if there is space available in queue, and copies the message
+ * inside queue. If there is no space, it blocks until space becomes available.
+ * It also schedules transfer thread to transmit the newly added message.
+ */
+ssize_t isa_write(struct file *filp, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct isadev_context *isadev = filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+ struct message_queue *q;
+ void *addr = 0;
+ int err, l2_header;
+ int ret = 0;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (len <= 0 || buf == NULL)
+ return -EFAULT;
+ q = &isadev->dl_queue;
+ l2_header = shrm_get_cdev_l2header(isadev->device_id);
+ if (l2_header < 0) {
+ dev_err(shrm->dev, "failed to get L2 header\n");
+ return l2_header;
+ }
+
+ switch (l2_header) {
+ case RPC_MESSAGING:
+ dev_dbg(shrm->dev, "RPC\n");
+ addr = (void *)wr_rpc_msg;
+ break;
+ case AUDIO_MESSAGING:
+ dev_dbg(shrm->dev, "Audio\n");
+ addr = (void *)wr_audio_msg;
+ break;
+ case SECURITY_MESSAGING:
+ dev_dbg(shrm->dev, "Security\n");
+ addr = (void *)wr_sec_msg;
+ break;
+ case COMMON_LOOPBACK_MESSAGING:
+ dev_dbg(shrm->dev, "Common loopback\n");
+ addr = isadev->addr;
+ break;
+ case AUDIO_LOOPBACK_MESSAGING:
+ dev_dbg(shrm->dev, "Audio loopback\n");
+ addr = isadev->addr;
+ break;
+ default:
+ dev_dbg(shrm->dev, "Wrong device\n");
+ return -EFAULT;
+ }
+
+ if (copy_from_user(addr, buf, len)) {
+ dev_err(shrm->dev, "copy_from_user failed\n");
+ return -EFAULT;
+ }
+ /* Write msg to Fifo */
+ if ((l2_header == AUDIO_MESSAGING) ||
+ (l2_header == AUDIO_LOOPBACK_MESSAGING)) {
+ mutex_lock(&shrm->isa_context->tx_audio_mutex);
+ err = shm_write_msg(shrm, l2_header, addr, len);
+ if (!err)
+ ret = len;
+ else
+ ret = err;
+ mutex_unlock(&shrm->isa_context->tx_audio_mutex);
+ } else {
+ spin_lock_bh(&shrm->isa_context->common_tx);
+ err = shm_write_msg(shrm, l2_header, addr, len);
+ if (!err)
+ ret = len;
+ else
+ ret = err;
+ spin_unlock_bh(&shrm->isa_context->common_tx);
+ }
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return ret;
+}
+
+/**
+ * isa_ioctl() - To handle different ioctl commands supported by driver.
+ * @inode: structure is used by the kernel internally to represent files
+ * @filp: file descriptor pointer
+ * @cmd: ioctl command
+ * @arg: input param
+ *
+ * Following ioctls are supported by this driver.
+ * DLP_IOCTL_ALLOCATE_BUFFER - To allocate buffer for new uplink message.
+ * This ioctl is called with required message size. It returns offset for
+ * the allocates space in the queue. DLP_IOCTL_PUT_MESSAGE - To indicate
+ * new uplink message available in queuq for transmission. Message is copied
+ * from offset location returned by previous ioctl before calling this ioctl.
+ * DLP_IOCTL_GET_MESSAGE - To check if any downlink message is available in
+ * queue. It returns offset for new message inside queue.
+ * DLP_IOCTL_DEALLOCATE_BUFFER - To deallocate any buffer allocate for
+ * downlink message once the message is copied. Message is copied from offset
+ * location returned by previous ioctl before calling this ioctl.
+ */
+static int isa_ioctl(struct inode *inode, struct file *filp,
+ unsigned cmd, unsigned long arg)
+{
+ int err = 0;
+ struct isadev_context *isadev = filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+ u32 m = iminor(inode);
+
+ isadev = (struct isadev_context *)filp->private_data;
+
+ if (isadev->device_id != m)
+ return -EINVAL;
+
+ switch (cmd) {
+ case DLP_IOC_ALLOCATE_BUFFER:
+ dev_dbg(shrm->dev, "DLP_IOC_ALLOCATE_BUFFER\n");
+ break;
+ case DLP_IOC_PUT_MESSAGE:
+ dev_dbg(shrm->dev, "DLP_IOC_PUT_MESSAGE\n");
+ break;
+ case DLP_IOC_GET_MESSAGE:
+ dev_dbg(shrm->dev, "DLP_IOC_GET_MESSAGE\n");
+ break;
+ case DLP_IOC_DEALLOCATE_BUFFER:
+ dev_dbg(shrm->dev, "DLP_IOC_DEALLOCATE_BUFFER\n");
+ break;
+ default:
+ dev_dbg(shrm->dev, "Unknown IOCTL\n");
+ err = -EFAULT;
+ break;
+ }
+ return err;
+}
+/**
+ * isa_mmap() - Maps kernel queue memory to user space.
+ * @filp: file descriptor pointer
+ * @vma: virtual area memory structure.
+ *
+ * This function maps kernel FIFO into user space. This function
+ * shall be called twice to map both uplink and downlink buffers.
+ */
+static int isa_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct isadev_context *isadev = filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+
+ u32 m = iminor(filp->f_path.dentry->d_inode);
+ dev_dbg(shrm->dev, "%s %d\n", __func__, m);
+
+ return 0;
+}
+
+/**
+ * isa_close() - Close device file
+ * @inode: structure is used by the kernel internally to represent files
+ * @filp: device file descriptor
+ *
+ * This function deletes structues associated with this file, deletes
+ * queues, flushes and destroys workqueus and closes this file.
+ * It also unregisters itself from l2mux driver.
+ */
+static int isa_close(struct inode *inode, struct file *filp)
+{
+ struct isadev_context *isadev = filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+ struct isa_driver_context *isa_context = shrm->isa_context;
+ u8 m;
+ int idx;
+
+ mutex_lock(&isa_lock);
+ m = iminor(filp->f_path.dentry->d_inode);
+ idx = shrm_get_cdev_index(m);
+ if (idx < 0) {
+ dev_err(shrm->dev, "failed to get index\n");
+ return idx;
+ }
+ dev_dbg(shrm->dev, "isa_close %d", m);
+
+ if (atomic_dec_and_test(&isa_context->is_open[idx])) {
+ atomic_inc(&isa_context->is_open[idx]);
+ dev_err(shrm->dev, "Device not opened yet\n");
+ mutex_unlock(&isa_lock);
+ return -ENODEV;
+ }
+ atomic_set(&isa_context->is_open[idx], 1);
+
+ switch (m) {
+ case RPC_MESSAGING:
+ dev_info(shrm->dev, "Close RPC_MESSAGING Device\n");
+ break;
+ case AUDIO_MESSAGING:
+ dev_info(shrm->dev, "Close AUDIO_MESSAGING Device\n");
+ break;
+ case SECURITY_MESSAGING:
+ dev_info(shrm->dev, "CLose SECURITY_MESSAGING Device\n");
+ break;
+ case COMMON_LOOPBACK_MESSAGING:
+ kfree(isadev->addr);
+ dev_info(shrm->dev, "Close COMMON_LOOPBACK_MESSAGING Device\n");
+ break;
+ case AUDIO_LOOPBACK_MESSAGING:
+ kfree(isadev->addr);
+ dev_info(shrm->dev, "Close AUDIO_LOOPBACK_MESSAGING Device\n");
+ break;
+ default:
+ dev_info(shrm->dev, "No such device present\n");
+ mutex_unlock(&isa_lock);
+ return -ENODEV;
+ };
+ mutex_unlock(&isa_lock);
+ return 0;
+}
+/**
+ * isa_open() - Open device file
+ * @inode: structure is used by the kernel internally to represent files
+ * @filp: device file descriptor
+ *
+ * This function performs initialization tasks needed to open SHM channel.
+ * Following tasks are performed.
+ * -return if device is already opened
+ * -create uplink FIFO
+ * -create downlink FIFO
+ * -init delayed workqueue thread
+ * -register to l2mux driver
+ */
+static int isa_open(struct inode *inode, struct file *filp)
+{
+ int err = 0;
+ u8 m;
+ int idx;
+ struct isadev_context *isadev;
+ struct isa_driver_context *isa_context = container_of(
+ inode->i_cdev,
+ struct isa_driver_context,
+ cdev);
+ struct shrm_dev *shrm = isa_context->isadev->dl_queue.shrm;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (get_boot_state() != BOOT_DONE) {
+ dev_err(shrm->dev, "Boot is not done\n");
+ return -EBUSY;
+ }
+ mutex_lock(&isa_lock);
+ m = iminor(inode);
+
+ if ((m != RPC_MESSAGING) &&
+ (m != AUDIO_LOOPBACK_MESSAGING) &&
+ (m != COMMON_LOOPBACK_MESSAGING) &&
+ (m != AUDIO_MESSAGING) &&
+ (m != SECURITY_MESSAGING)) {
+ dev_err(shrm->dev, "No such device present\n");
+ mutex_unlock(&isa_lock);
+ return -ENODEV;
+ }
+ idx = shrm_get_cdev_index(m);
+ if (idx < 0) {
+ dev_err(shrm->dev, "failed to get index\n");
+ return idx;
+ }
+ if (!atomic_dec_and_test(&isa_context->is_open[idx])) {
+ atomic_inc(&isa_context->is_open[idx]);
+ dev_err(shrm->dev, "Device already opened\n");
+ mutex_unlock(&isa_lock);
+ return -EBUSY;
+ }
+ isadev = &isa_context->isadev[idx];
+ if (filp != NULL)
+ filp->private_data = isadev;
+
+ switch (m) {
+ case RPC_MESSAGING:
+ dev_info(shrm->dev, "Open RPC_MESSAGING Device\n");
+ break;
+ case AUDIO_MESSAGING:
+ dev_info(shrm->dev, "Open AUDIO_MESSAGING Device\n");
+ break;
+ case SECURITY_MESSAGING:
+ dev_info(shrm->dev, "Open SECURITY_MESSAGING Device\n");
+ break;
+ case COMMON_LOOPBACK_MESSAGING:
+ isadev->addr = kzalloc(10 * 1024, GFP_KERNEL);
+ if (!isadev->addr) {
+ mutex_unlock(&isa_lock);
+ return -ENOMEM;
+ }
+ dev_info(shrm->dev, "Open COMMON_LOOPBACK_MESSAGING Device\n");
+ break;
+ case AUDIO_LOOPBACK_MESSAGING:
+ isadev->addr = kzalloc(10 * 1024, GFP_KERNEL);
+ if (!isadev->addr) {
+ mutex_unlock(&isa_lock);
+ return -ENOMEM;
+ }
+ dev_info(shrm->dev, "Open AUDIO_LOOPBACK_MESSAGING Device\n");
+ break;
+ };
+
+ mutex_unlock(&isa_lock);
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return err;
+}
+
+const struct file_operations isa_fops = {
+ .owner = THIS_MODULE,
+ .open = isa_open,
+ .release = isa_close,
+ .ioctl = isa_ioctl,
+ .mmap = isa_mmap,
+ .read = isa_read,
+ .write = isa_write,
+ .poll = isa_select,
+};
+
+/**
+ * isa_init() - module insertion function
+ * @shrm: pointer to the shrm device information structure
+ *
+ * This function registers module as a character driver using
+ * register_chrdev_region() or alloc_chrdev_region. It adds this
+ * driver to system using cdev_add() call. Major number is dynamically
+ * allocated using alloc_chrdev_region() by default or left to user to specify
+ * it during load time. For this variable major is used as module_param
+ * Nodes to be created using
+ * mknod /dev/isi c $major 0
+ * mknod /dev/rpc c $major 1
+ * mknod /dev/audio c $major 2
+ * mknod /dev/sec c $major 3
+ */
+int isa_init(struct shrm_dev *shrm)
+{
+ dev_t dev_id;
+ int retval, no_dev;
+ struct isadev_context *isadev;
+ struct isa_driver_context *isa_context;
+
+ isa_context = kzalloc(sizeof(struct isa_driver_context),
+ GFP_KERNEL);
+ if (isa_context == NULL) {
+ dev_err(shrm->dev, "Failed to alloc memory\n");
+ return -ENOMEM;
+ }
+ shrm->isa_context = isa_context;
+ if (major) {
+ dev_id = MKDEV(major, MAX_L2_HEADERS);
+ retval = register_chrdev_region(dev_id, ISA_DEVICES, NAME);
+ } else {
+ /*
+ * L2 header of loopback device is 192(0xc0). As per the shrm
+ * protocol the minor id of the deivce is mapped to the
+ * L2 header.
+ */
+ retval = alloc_chrdev_region(&dev_id, 0, MAX_L2_HEADERS, NAME);
+ major = MAJOR(dev_id);
+ }
+ dev_dbg(shrm->dev, " major %d\n", major);
+
+ cdev_init(&isa_context->cdev, &isa_fops);
+ isa_context->cdev.owner = THIS_MODULE;
+ retval = cdev_add(&isa_context->cdev, dev_id, MAX_L2_HEADERS);
+ if (retval) {
+ dev_err(shrm->dev, "Failed to add char device\n");
+ return retval;
+ }
+ /* create class and device */
+ isa_context->shm_class = class_create(THIS_MODULE, NAME);
+ if (IS_ERR(isa_context->shm_class)) {
+ dev_err(shrm->dev, "Error creating shrm class\n");
+ cdev_del(&isa_context->cdev);
+ retval = PTR_ERR(isa_context->shm_class);
+ kfree(isa_context);
+ return retval;
+ }
+
+ for (no_dev = 0; no_dev < ISA_DEVICES; no_dev++) {
+ atomic_set(&isa_context->is_open[no_dev], 1);
+ device_create(isa_context->shm_class, NULL,
+ MKDEV(MAJOR(dev_id),
+ map_dev[no_dev].l2_header), NULL,
+ map_dev[no_dev].name);
+ }
+
+ isa_context->isadev = kzalloc(sizeof
+ (struct isadev_context)*ISA_DEVICES,
+ GFP_KERNEL);
+ if (isa_context->isadev == NULL) {
+ dev_err(shrm->dev, "Failed to alloc memory\n");
+ return -ENOMEM;
+ }
+ for (no_dev = 0 ; no_dev < ISA_DEVICES ; no_dev++) {
+ isadev = &isa_context->isadev[no_dev];
+ isadev->device_id = no_dev;
+ retval = create_queue(&isadev->dl_queue,
+ isadev->device_id, shrm);
+
+ if (retval < 0) {
+ dev_err(shrm->dev, "create dl_queue failed\n");
+ delete_queue(&isadev->dl_queue);
+ kfree(isadev);
+ return retval;
+ }
+ }
+ mutex_init(&isa_context->tx_audio_mutex);
+ spin_lock_init(&isa_context->common_tx);
+ dev_dbg(shrm->dev, " SHM Char Driver added\n");
+ return retval;
+}
+
+void isa_exit(struct shrm_dev *shrm)
+{
+ int no_dev;
+ struct isadev_context *isadev;
+ struct isa_driver_context *isa_context = shrm->isa_context;
+ dev_t dev_id = MKDEV(major, 0);
+
+ for (no_dev = 0 ; no_dev < ISA_DEVICES ; no_dev++) {
+ device_destroy(isa_context->shm_class,
+ MKDEV(MAJOR(dev_id),
+ map_dev[no_dev].l2_header));
+ isadev = &isa_context->isadev[no_dev];
+ delete_queue(&isadev->dl_queue);
+ kfree(isadev);
+ }
+ class_destroy(isa_context->shm_class);
+ cdev_del(&isa_context->cdev);
+ unregister_chrdev_region(dev_id, ISA_DEVICES);
+ kfree(isa_context);
+ dev_dbg(shrm->dev, " SHM Char Driver removed\n");
+}
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 507441ac6ed..900e3264480 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -3149,4 +3149,3 @@ int __init tty_init(void)
#endif
return 0;
}
-
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 938b74ea9ff..89294ce1067 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -462,6 +462,27 @@ show_one(scaling_cur_freq, cur);
static int __cpufreq_set_policy(struct cpufreq_policy *data,
struct cpufreq_policy *policy);
+int cpufreq_update_freq(int cpu, unsigned int min, unsigned int max)
+{
+ int ret;
+ struct cpufreq_policy new_policy;
+ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+
+ ret = cpufreq_get_policy(&new_policy, cpu);
+ if (ret)
+ return -EINVAL;
+
+ new_policy.min = min;
+ new_policy.max = max;
+
+ ret = __cpufreq_set_policy(policy, &new_policy);
+ policy->user_policy.min = policy->min;
+ policy->user_policy.max = policy->max;
+
+ return 0;
+}
+EXPORT_SYMBOL(cpufreq_update_freq);
+
/**
* cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access
*/
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index e1314212d8d..a8d732c6fa6 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -30,6 +30,8 @@
#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10)
#define DEF_FREQUENCY_UP_THRESHOLD (80)
+#define DEF_SAMPLING_DOWN_FACTOR (10)
+#define MAX_SAMPLING_DOWN_FACTOR (100000)
#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3)
#define MICRO_FREQUENCY_UP_THRESHOLD (95)
#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000)
@@ -82,6 +84,7 @@ struct cpu_dbs_info_s {
unsigned int freq_lo;
unsigned int freq_lo_jiffies;
unsigned int freq_hi_jiffies;
+ unsigned int rate_mult;
int cpu;
unsigned int sample_type:1;
/*
@@ -108,10 +111,12 @@ static struct dbs_tuners {
unsigned int up_threshold;
unsigned int down_differential;
unsigned int ignore_nice;
+ unsigned int sampling_down_factor;
unsigned int powersave_bias;
unsigned int io_is_busy;
} dbs_tuners_ins = {
.up_threshold = DEF_FREQUENCY_UP_THRESHOLD,
+ .sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR,
.down_differential = DEF_FREQUENCY_DOWN_DIFFERENTIAL,
.ignore_nice = 0,
.powersave_bias = 0,
@@ -259,6 +264,7 @@ static ssize_t show_##file_name \
show_one(sampling_rate, sampling_rate);
show_one(io_is_busy, io_is_busy);
show_one(up_threshold, up_threshold);
+show_one(sampling_down_factor, sampling_down_factor);
show_one(ignore_nice_load, ignore_nice);
show_one(powersave_bias, powersave_bias);
@@ -340,6 +346,29 @@ static ssize_t store_up_threshold(struct kobject *a, struct attribute *b,
return count;
}
+static ssize_t store_sampling_down_factor(struct kobject *a,
+ struct attribute *b, const char *buf, size_t count)
+{
+ unsigned int input, j;
+ int ret;
+ ret = sscanf(buf, "%u", &input);
+
+ if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1)
+ return -EINVAL;
+ mutex_lock(&dbs_mutex);
+ dbs_tuners_ins.sampling_down_factor = input;
+
+ /* Reset down sampling multiplier in case it was active */
+ for_each_online_cpu(j) {
+ struct cpu_dbs_info_s *dbs_info;
+ dbs_info = &per_cpu(od_cpu_dbs_info, j);
+ dbs_info->rate_mult = 1;
+ }
+ mutex_unlock(&dbs_mutex);
+
+ return count;
+}
+
static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
{
@@ -401,6 +430,7 @@ static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b,
define_one_global_rw(sampling_rate);
define_one_global_rw(io_is_busy);
define_one_global_rw(up_threshold);
+define_one_global_rw(sampling_down_factor);
define_one_global_rw(ignore_nice_load);
define_one_global_rw(powersave_bias);
@@ -409,6 +439,7 @@ static struct attribute *dbs_attributes[] = {
&sampling_rate_min.attr,
&sampling_rate.attr,
&up_threshold.attr,
+ &sampling_down_factor.attr,
&ignore_nice_load.attr,
&powersave_bias.attr,
&io_is_busy.attr,
@@ -551,6 +582,10 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
/* Check for frequency increase */
if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) {
+ /* If switching to max speed, apply sampling_down_factor */
+ if (policy->cur < policy->max)
+ this_dbs_info->rate_mult =
+ dbs_tuners_ins.sampling_down_factor;
/* if we are already at full speed then break out early */
if (!dbs_tuners_ins.powersave_bias) {
if (policy->cur == policy->max)
@@ -585,6 +620,9 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
(dbs_tuners_ins.up_threshold -
dbs_tuners_ins.down_differential);
+ /* No longer fully busy, reset rate_mult */
+ this_dbs_info->rate_mult = 1;
+
if (freq_next < policy->min)
freq_next = policy->min;
@@ -608,9 +646,12 @@ static void do_dbs_timer(struct work_struct *work)
int sample_type = dbs_info->sample_type;
/* We want all CPUs to do sampling nearly on same jiffy */
- int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+ int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate
+ * dbs_info->rate_mult);
+
+ if (num_online_cpus() > 1)
+ delay -= jiffies % delay;
- delay -= jiffies % delay;
mutex_lock(&dbs_info->timer_mutex);
/* Common NORMAL_SAMPLE setup */
@@ -622,10 +663,16 @@ static void do_dbs_timer(struct work_struct *work)
/* Setup timer for SUB_SAMPLE */
dbs_info->sample_type = DBS_SUB_SAMPLE;
delay = dbs_info->freq_hi_jiffies;
+ } else {
+ /* We want all CPUs to do sampling nearly on same jiffy */
+ delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate
+ * dbs_info->rate_mult);
+ delay -= jiffies % delay;
}
} else {
__cpufreq_driver_target(dbs_info->cur_policy,
dbs_info->freq_lo, CPUFREQ_RELATION_H);
+ delay = dbs_info->freq_lo_jiffies;
}
queue_delayed_work_on(cpu, kondemand_wq, &dbs_info->work, delay);
mutex_unlock(&dbs_info->timer_mutex);
@@ -708,6 +755,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
}
}
this_dbs_info->cpu = cpu;
+ this_dbs_info->rate_mult = 1;
ondemand_powersave_bias_init_cpu(cpu);
/*
* Start the timerschedule work, when this governor
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 1b128702d30..5711a85a942 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -19,6 +19,8 @@
#include <linux/tick.h>
#include <linux/sched.h>
#include <linux/math64.h>
+#include <linux/cpu.h>
+#include <linux/sysfs.h>
#define BUCKETS 12
#define INTERVALS 8
@@ -121,6 +123,7 @@ struct menu_device {
int interval_ptr;
};
+static int tune_multiplier = 1024;
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
@@ -170,6 +173,9 @@ static inline int performance_multiplier(void)
{
int mult = 1;
+ if (tune_multiplier <= 1)
+ return tune_multiplier;
+
/* for higher loadavg, we are more reluctant */
mult += 2 * get_loadavg();
@@ -177,6 +183,9 @@ static inline int performance_multiplier(void)
/* for IO wait tasks (per cpu!) we add 5x each */
mult += 10 * nr_iowait_cpu(smp_processor_id());
+ if (tune_multiplier != 1024)
+ mult = (tune_multiplier * mult) / 1024;
+
return mult;
}
@@ -370,6 +379,54 @@ static void menu_update(struct cpuidle_device *dev)
data->interval_ptr = 0;
}
+int cpuidle_set_multiplier(unsigned int value)
+{
+
+ if (value > 1024)
+ tune_multiplier = 1024;
+ else
+ tune_multiplier = value;
+
+ return 0;
+}
+EXPORT_SYMBOL(cpuidle_set_multiplier);
+
+static ssize_t show_multiplier(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", tune_multiplier);
+}
+
+static ssize_t store_multiplier(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int input;
+ int ret;
+ ret = sscanf(buf, "%u", &input);
+
+ if (ret != 1)
+ return -EINVAL;
+
+ cpuidle_set_multiplier(input);
+
+ return count;
+}
+
+
+static SYSDEV_CLASS_ATTR(multiplier, 0644, show_multiplier, store_multiplier);
+
+static struct attribute *dbs_attributes[] = {
+ &attr_multiplier.attr,
+ NULL
+};
+
+static struct attribute_group dbs_attr_group = {
+ .attrs = dbs_attributes,
+ .name = "cpuidle",
+};
+
/**
* menu_enable_device - scans a CPU's states and does setup
* @dev: the CPU
@@ -397,7 +454,15 @@ static struct cpuidle_governor menu_governor = {
*/
static int __init init_menu(void)
{
- return cpuidle_register_governor(&menu_governor);
+ int ret;
+
+ ret = cpuidle_register_governor(&menu_governor);
+
+ sysfs_merge_group(&(cpu_sysdev_class.kset.kobj),
+ &dbs_attr_group);
+
+ return ret;
+
}
/**
@@ -405,6 +470,9 @@ static int __init init_menu(void)
*/
static void __exit exit_menu(void)
{
+ sysfs_unmerge_group(&(cpu_sysdev_class.kset.kobj),
+ &dbs_attr_group);
+
cpuidle_unregister_governor(&menu_governor);
}
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index fbf94cf496f..efce6c81930 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -243,4 +243,14 @@ config CRYPTO_DEV_OMAP_SHAM
OMAP processors have SHA1/MD5 hw accelerator. Select this if you
want to use the OMAP module for SHA1/MD5 algorithms.
+config CRYPTO_DEV_UX500
+ tristate "Driver for ST-Ericsson UX500 crypto hardware acceleration"
+ depends on ARCH_U8500
+ select CRYPTO_ALGAPI
+ help
+ Driver for ST-Ericsson UX500 crypto engine.
+
+if CRYPTO_DEV_UX500
+ source "drivers/crypto/ux500/Kconfig"
+endif # if CRYPTO_DEV_UX500
endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 6dbbe00c452..7aeb5604454 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -9,4 +9,4 @@ obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/
obj-$(CONFIG_CRYPTO_DEV_OMAP_SHAM) += omap-sham.o
-
+obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/
diff --git a/drivers/crypto/ux500/Kconfig b/drivers/crypto/ux500/Kconfig
new file mode 100644
index 00000000000..165a03d46c0
--- /dev/null
+++ b/drivers/crypto/ux500/Kconfig
@@ -0,0 +1,29 @@
+#
+# Copyright (C) ST-Ericsson SA 2010
+# Author: Shujuan Chen (shujuan.chen@stericsson.com)
+# License terms: GNU General Public License (GPL) version 2
+#
+
+config CRYPTO_DEV_UX500_CRYP
+ tristate "UX500 crypto driver for CRYP block"
+ depends on CRYPTO_DEV_UX500
+ select CRYPTO_DES
+ help
+ This is the driver for the crypto block CRYP.
+
+config CRYPTO_DEV_UX500_HASH
+ tristate "UX500 crypto driver for HASH block"
+ depends on CRYPTO_DEV_UX500
+ select CRYPTO_HASH
+ select CRYPTO_HMAC
+ help
+ This selects the UX500 hash driver for the HASH hardware.
+ Depends on U8500/STM DMA if running in DMA mode.
+
+config CRYPTO_DEV_UX500_DEBUG
+ bool "Activate ux500 platform debug-mode for crypto and hash block"
+ depends on CRYPTO_DEV_UX500_CRYP || CRYPTO_DEV_UX500_HASH
+ default n
+ help
+ Say Y if you want to add debug prints to ux500_hash and
+ ux500_cryp devices.
diff --git a/drivers/crypto/ux500/Makefile b/drivers/crypto/ux500/Makefile
new file mode 100644
index 00000000000..b9a365bade8
--- /dev/null
+++ b/drivers/crypto/ux500/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright (C) ST-Ericsson SA 2010
+# Author: Shujuan Chen (shujuan.chen@stericsson.com)
+# License terms: GNU General Public License (GPL) version 2
+#
+
+obj-$(CONFIG_CRYPTO_DEV_UX500_HASH) += hash/
+obj-$(CONFIG_CRYPTO_DEV_UX500_CRYP) += cryp/
diff --git a/drivers/crypto/ux500/cryp/Makefile b/drivers/crypto/ux500/cryp/Makefile
new file mode 100644
index 00000000000..fd5e6df3861
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/Makefile
@@ -0,0 +1,13 @@
+#/*
+# * Copyright (C) ST-Ericsson SA 2010
+# * Author: shujuan.chen@stericsson.com for ST-Ericsson.
+# * License terms: GNU General Public License (GPL) version 2 */
+
+ifdef CONFIG_CRYPTO_DEV_UX500_DEBUG
+CFLAGS_cryp_core.o := -DDEBUG -O0
+CFLAGS_cryp.o := -DDEBUG -O0
+CFLAGS_cryp_irq.o := -DDEBUG -O0
+endif
+
+obj-$(CONFIG_CRYPTO_DEV_UX500_CRYP) += u8500_cryp.o
+u8500_cryp-objs := cryp.o cryp_irq.o cryp_core.o
diff --git a/drivers/crypto/ux500/cryp/cryp.c b/drivers/crypto/ux500/cryp/cryp.c
new file mode 100644
index 00000000000..94928f7efce
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp.c
@@ -0,0 +1,556 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "cryp_p.h"
+#include "cryp.h"
+
+/**
+ * cryp_wait_until_done - wait until the device logic is not busy
+ */
+void cryp_wait_until_done(struct cryp_device_data *device_data)
+{
+ while (cryp_is_logic_busy(device_data))
+ cpu_relax();
+}
+
+/**
+ * cryp_check - This routine checks Peripheral and PCell Id
+ * @device_data: Pointer to the device data struct for base address.
+ */
+int cryp_check(struct cryp_device_data *device_data)
+{
+ if (NULL == device_data)
+ return -EINVAL;
+
+ /* Check Peripheral and Pcell Id Register for CRYP */
+ if ((CRYP_PERIPHERAL_ID0 == readl(&device_data->base->periphId0))
+ && (CRYP_PERIPHERAL_ID1 == readl(&device_data->base->periphId1))
+ && (CRYP_PERIPHERAL_ID2 == readl(&device_data->base->periphId2))
+ && (CRYP_PERIPHERAL_ID3 == readl(&device_data->base->periphId3))
+ && (CRYP_PCELL_ID0 == readl(&device_data->base->pcellId0))
+ && (CRYP_PCELL_ID1 == readl(&device_data->base->pcellId1))
+ && (CRYP_PCELL_ID2 == readl(&device_data->base->pcellId2))
+ && (CRYP_PCELL_ID3 == readl(&device_data->base->pcellId3))) {
+ return 0;
+ }
+
+ return -EPERM;
+}
+
+/**
+ * cryp_reset - This routine loads the cryp register with the default values
+ * @device_data: Pointer to the device data struct for base address.
+ */
+void cryp_reset(struct cryp_device_data *device_data)
+{
+ writel(CRYP_DMACR_DEFAULT, &device_data->base->dmacr);
+ writel(CRYP_IMSC_DEFAULT, &device_data->base->imsc);
+
+ writel(CRYP_KEY_DEFAULT, &device_data->base->key_1_l);
+ writel(CRYP_KEY_DEFAULT, &device_data->base->key_1_r);
+ writel(CRYP_KEY_DEFAULT, &device_data->base->key_2_l);
+ writel(CRYP_KEY_DEFAULT, &device_data->base->key_2_r);
+ writel(CRYP_KEY_DEFAULT, &device_data->base->key_3_l);
+ writel(CRYP_KEY_DEFAULT, &device_data->base->key_3_r);
+ writel(CRYP_INIT_VECT_DEFAULT, &device_data->base->init_vect_0_l);
+ writel(CRYP_INIT_VECT_DEFAULT, &device_data->base->init_vect_0_r);
+ writel(CRYP_KEY_DEFAULT, &device_data->base->key_4_l);
+ writel(CRYP_KEY_DEFAULT, &device_data->base->key_4_r);
+ writel(CRYP_INIT_VECT_DEFAULT, &device_data->base->init_vect_1_l);
+ writel(CRYP_INIT_VECT_DEFAULT, &device_data->base->init_vect_1_r);
+
+ /* Last step since the protection mode bits need to be modified. */
+ writel(CRYP_CR_DEFAULT | CRYP_CR_FFLUSH, &device_data->base->cr);
+
+ /*
+ * CRYP_INFIFO_READY_MASK is the expected value on the status register
+ * when starting a new calculation, which means Input FIFO is not full
+ * and input FIFO is empty.
+ */
+ while (readl(&device_data->base->status) != CRYP_INFIFO_READY_MASK)
+ cpu_relax();
+}
+
+/**
+ * cryp_activity - This routine enables/disable the cryptography function.
+ * @device_data: Pointer to the device data struct for base address.
+ * @cryp_activity: Enable/Disable functionality
+ */
+void cryp_activity(struct cryp_device_data *device_data,
+ enum cryp_crypen cryp_crypen)
+{
+ CRYP_PUT_BITS(&device_data->base->cr,
+ cryp_crypen,
+ CRYP_CRYPEN_POS,
+ CRYP_CRYPEN_MASK);
+}
+
+/**
+ * cryp_start - starts the computation
+ * @device_data: Pointer to the device data struct for base address.
+ * @cryp_start: Enable/Disable functionality
+ */
+void cryp_start(struct cryp_device_data *device_data)
+{
+ CRYP_PUT_BITS(&device_data->base->cr,
+ CRYP_START_ENABLE,
+ CRYP_START_POS,
+ CRYP_START_MASK);
+}
+
+/**
+ * cryp_init_signal - This routine submit the initialization values.
+ * @device_data: Pointer to the device data struct for base address.
+ * @cryp_init_bit: Enable/Disable init signal
+ */
+void cryp_init_signal(struct cryp_device_data *device_data,
+ enum cryp_init cryp_init_bit)
+{
+ CRYP_PUT_BITS(&device_data->base->cr,
+ cryp_init_bit,
+ CRYP_INIT_POS,
+ CRYP_INIT_MASK);
+}
+
+/**
+ * cryp_key_preparation - This routine prepares key for decryption.
+ * @device_data: Pointer to the device data struct for base address.
+ * @cryp_prepkey: Enable/Disable
+ */
+void cryp_key_preparation(struct cryp_device_data *device_data,
+ enum cryp_key_prep cryp_prepkey)
+{
+ CRYP_PUT_BITS(&device_data->base->cr,
+ cryp_prepkey,
+ CRYP_KSE_POS,
+ CRYP_KSE_MASK);
+}
+
+/**
+ * cryp_flush_inoutfifo - Resets both the input and the output FIFOs
+ * @device_data: Pointer to the device data struct for base address.
+ */
+void cryp_flush_inoutfifo(struct cryp_device_data *device_data)
+{
+ CRYP_SET_BITS(&device_data->base->cr, CRYP_FIFO_FLUSH_MASK);
+}
+
+/**
+ * cryp_set_dir -
+ * @device_data: Pointer to the device data struct for base address.
+ * @dir: Crypto direction, encrypt/decrypt
+ */
+void cryp_set_dir(struct cryp_device_data *device_data, int dir)
+{
+ CRYP_PUT_BITS(&device_data->base->cr,
+ dir,
+ CRYP_ENC_DEC_POS,
+ CRYP_ENC_DEC_MASK);
+
+ CRYP_PUT_BITS(&device_data->base->cr,
+ CRYP_DATA_TYPE_8BIT_SWAP,
+ CRYP_DATA_TYPE_POS,
+ CRYP_DATA_TYPE_MASK);
+}
+
+/**
+ * cryp_cen_flush -
+ * @device_data: Pointer to the device data struct for base address.
+ */
+void cryp_cen_flush(struct cryp_device_data *device_data)
+{
+ CRYP_PUT_BITS(&device_data->base->cr,
+ CRYP_STATE_DISABLE,
+ CRYP_KEY_ACCESS_POS,
+ CRYP_KEY_ACCESS_MASK);
+ CRYP_SET_BITS(&device_data->base->cr,
+ CRYP_FIFO_FLUSH_MASK);
+ CRYP_PUT_BITS(&device_data->base->cr,
+ CRYP_CRYPEN_ENABLE,
+ CRYP_CRYPEN_POS,
+ CRYP_CRYPEN_MASK);
+}
+
+/**
+ * cryp_set_configuration - This routine set the cr CRYP IP
+ * @device_data: Pointer to the device data struct for base address.
+ * @p_cryp_config: Pointer to the configuration parameter
+ */
+int cryp_set_configuration(struct cryp_device_data *device_data,
+ struct cryp_config *p_cryp_config)
+{
+ if (NULL == device_data)
+ return -EINVAL;
+ if (NULL == p_cryp_config)
+ return -EINVAL;
+
+ /* Since more than one bit is written macro put_bits is used*/
+ CRYP_PUT_BITS(&device_data->base->cr,
+ p_cryp_config->key_access,
+ CRYP_KEY_ACCESS_POS,
+ CRYP_KEY_ACCESS_MASK);
+ CRYP_PUT_BITS(&device_data->base->cr,
+ p_cryp_config->key_size,
+ CRYP_KEY_SIZE_POS,
+ CRYP_KEY_SIZE_MASK);
+ CRYP_PUT_BITS(&device_data->base->cr,
+ p_cryp_config->data_type,
+ CRYP_DATA_TYPE_POS,
+ CRYP_DATA_TYPE_MASK);
+
+ /* Prepare key for decryption */
+ if ((CRYP_ALGORITHM_DECRYPT == p_cryp_config->encrypt_or_decrypt) &&
+ ((CRYP_ALGO_AES_ECB == p_cryp_config->algo_mode) ||
+ (CRYP_ALGO_AES_CBC == p_cryp_config->algo_mode))) {
+ CRYP_PUT_BITS(&device_data->base->cr,
+ CRYP_ALGO_AES_ECB,
+ CRYP_ALGOMODE_POS,
+ CRYP_ALGOMODE_MASK);
+ CRYP_PUT_BITS(&device_data->base->cr,
+ CRYP_CRYPEN_ENABLE,
+ CRYP_CRYPEN_POS,
+ CRYP_CRYPEN_MASK);
+ CRYP_PUT_BITS(&device_data->base->cr,
+ KSE_ENABLED,
+ CRYP_KSE_POS,
+ CRYP_KSE_MASK);
+
+ cryp_wait_until_done(device_data);
+
+ CRYP_PUT_BITS(&device_data->base->cr,
+ CRYP_CRYPEN_DISABLE,
+ CRYP_CRYPEN_POS,
+ CRYP_CRYPEN_MASK);
+ }
+
+ CRYP_PUT_BITS(&device_data->base->cr,
+ CRYP_CRYPEN_ENABLE,
+ CRYP_CRYPEN_POS,
+ CRYP_CRYPEN_MASK);
+ CRYP_PUT_BITS(&device_data->base->cr,
+ p_cryp_config->algo_mode,
+ CRYP_ALGOMODE_POS,
+ CRYP_ALGOMODE_MASK);
+ CRYP_PUT_BITS(&device_data->base->cr,
+ p_cryp_config->encrypt_or_decrypt,
+ CRYP_ENC_DEC_POS,
+ CRYP_ENC_DEC_MASK);
+
+ return 0;
+}
+
+/**
+ * cryp_get_configuration - gets the parameter of the control register of IP
+ * @device_data: Pointer to the device data struct for base address.
+ * @p_cryp_config: Gets the configuration parameter from cryp ip.
+ */
+int cryp_get_configuration(struct cryp_device_data *device_data,
+ struct cryp_config *p_cryp_config)
+{
+ if (NULL == p_cryp_config)
+ return -EINVAL;
+
+ p_cryp_config->key_access =
+ ((readl(&device_data->base->cr) & CRYP_KEY_ACCESS_MASK) ?
+ CRYP_STATE_ENABLE :
+ CRYP_STATE_DISABLE);
+ p_cryp_config->key_size =
+ ((readl(&device_data->base->cr) & CRYP_KEY_SIZE_MASK) >>
+ CRYP_KEY_SIZE_POS);
+
+ p_cryp_config->encrypt_or_decrypt =
+ ((readl(&device_data->base->cr) & CRYP_ENC_DEC_MASK) ?
+ CRYP_ALGORITHM_DECRYPT :
+ CRYP_ALGORITHM_ENCRYPT);
+
+ p_cryp_config->data_type =
+ ((readl(&device_data->base->cr) & CRYP_DATA_TYPE_MASK) >>
+ CRYP_DATA_TYPE_POS);
+ p_cryp_config->algo_mode =
+ ((readl(&device_data->base->cr) & CRYP_ALGOMODE_MASK) >>
+ CRYP_ALGOMODE_POS);
+
+ return 0;
+}
+
+/**
+ * cryp_configure_protection - set the protection bits in the CRYP logic.
+ * @device_data: Pointer to the device data struct for base address.
+ * @p_protect_config: Pointer to the protection mode and
+ * secure mode configuration
+ */
+int cryp_configure_protection(struct cryp_device_data *device_data,
+ struct cryp_protection_config *p_protect_config)
+{
+ if (NULL == p_protect_config)
+ return -EINVAL;
+
+ CRYP_WRITE_BIT(&device_data->base->cr,
+ (u32) p_protect_config->secure_access,
+ CRYP_SECURE_MASK);
+ CRYP_PUT_BITS(&device_data->base->cr,
+ p_protect_config->privilege_access,
+ CRYP_PRLG_POS,
+ CRYP_PRLG_MASK);
+
+ return 0;
+}
+
+/**
+ * cryp_is_logic_busy - returns the busy status of the CRYP logic
+ * @device_data: Pointer to the device data struct for base address.
+ */
+int cryp_is_logic_busy(struct cryp_device_data *device_data)
+{
+ return CRYP_TEST_BITS(&device_data->base->status,
+ CRYP_BUSY_STATUS_MASK);
+}
+
+/**
+ * cryp_get_status - This routine returns the complete status of the cryp logic
+ * @device_data: Pointer to the device data struct for base address.
+ */
+/*
+int cryp_get_status(struct cryp_device_data *device_data)
+{
+ return (int) readl(device_data->base->status);
+}
+*/
+
+/**
+ * cryp_configure_for_dma - configures the CRYP IP for DMA operation
+ * @device_data: Pointer to the device data struct for base address.
+ * @dma_req: Specifies the DMA request type value.
+ */
+void cryp_configure_for_dma(struct cryp_device_data *device_data,
+ enum cryp_dma_req_type dma_req)
+{
+ CRYP_SET_BITS(&device_data->base->dmacr,
+ (u32) dma_req);
+}
+
+/**
+ * cryp_configure_key_values - configures the key values for CRYP operations
+ * @device_data: Pointer to the device data struct for base address.
+ * @key_reg_index: Key value index register
+ * @key_value: The key value struct
+ */
+int cryp_configure_key_values(struct cryp_device_data *device_data,
+ enum cryp_key_reg_index key_reg_index,
+ struct cryp_key_value key_value)
+{
+ while (cryp_is_logic_busy(device_data))
+ cpu_relax();
+
+ switch (key_reg_index) {
+ case CRYP_KEY_REG_1:
+ writel(key_value.key_value_left,
+ &device_data->base->key_1_l);
+ writel(key_value.key_value_right,
+ &device_data->base->key_1_r);
+ break;
+ case CRYP_KEY_REG_2:
+ writel(key_value.key_value_left,
+ &device_data->base->key_2_l);
+ writel(key_value.key_value_right,
+ &device_data->base->key_2_r);
+ break;
+ case CRYP_KEY_REG_3:
+ writel(key_value.key_value_left,
+ &device_data->base->key_3_l);
+ writel(key_value.key_value_right,
+ &device_data->base->key_3_r);
+ break;
+ case CRYP_KEY_REG_4:
+ writel(key_value.key_value_left,
+ &device_data->base->key_4_l);
+ writel(key_value.key_value_right,
+ &device_data->base->key_4_r);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+
+/**
+ * cryp_configure_init_vector - configures the initialization vector register
+ * @device_data: Pointer to the device data struct for base address.
+ * @init_vector_index: Specifies the index of the init vector.
+ * @init_vector_value: Specifies the value for the init vector.
+ */
+int cryp_configure_init_vector(struct cryp_device_data *device_data,
+ enum cryp_init_vector_index
+ init_vector_index,
+ struct cryp_init_vector_value
+ init_vector_value)
+{
+ while (cryp_is_logic_busy(device_data))
+ cpu_relax();
+
+ switch (init_vector_index) {
+ case CRYP_INIT_VECTOR_INDEX_0:
+ writel(init_vector_value.init_value_left,
+ &device_data->base->init_vect_0_l);
+ writel(init_vector_value.init_value_right,
+ &device_data->base->init_vect_0_r);
+ break;
+ case CRYP_INIT_VECTOR_INDEX_1:
+ writel(init_vector_value.init_value_left,
+ &device_data->base->init_vect_1_l);
+ writel(init_vector_value.init_value_right,
+ &device_data->base->init_vect_1_r);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * cryp_prep_ctx_mgmt - Prepares for handling the context of the block
+ * @device_data: Pointer to the device data struct for base address.
+ */
+static void cryp_prep_ctx_mgmt(struct cryp_device_data *device_data)
+{
+ cryp_configure_for_dma(device_data, CRYP_DMA_DISABLE_BOTH);
+ cryp_activity(device_data, CRYP_CRYPEN_DISABLE);
+ cryp_wait_until_done(device_data);
+}
+
+/**
+ * cryp_save_device_context - Store hardware registers and
+ * other device context parameter
+ * @device_data: Pointer to the device data struct for base address.
+ * @ctx: Crypto device context
+ */
+void cryp_save_device_context(struct cryp_device_data *device_data,
+ struct cryp_device_context *ctx)
+{
+ struct cryp_register *src_reg = device_data->base;
+
+ cryp_prep_ctx_mgmt(device_data);
+
+ ctx->din = readl(&src_reg->din);
+
+ ctx->dout = readl(&src_reg->dout);
+
+ ctx->cr = readl(&src_reg->cr);
+ ctx->dmacr = readl(&src_reg->dmacr);
+ ctx->imsc = readl(&src_reg->imsc);
+
+ ctx->key_1_l = readl(&src_reg->key_1_l);
+ ctx->key_1_r = readl(&src_reg->key_1_r);
+ ctx->key_2_l = readl(&src_reg->key_2_l);
+ ctx->key_2_r = readl(&src_reg->key_2_r);
+ ctx->key_3_l = readl(&src_reg->key_3_l);
+ ctx->key_3_r = readl(&src_reg->key_3_r);
+ ctx->key_4_l = readl(&src_reg->key_4_l);
+ ctx->key_4_r = readl(&src_reg->key_4_r);
+
+ ctx->init_vect_0_l = readl(&src_reg->init_vect_0_l);
+ ctx->init_vect_0_r = readl(&src_reg->init_vect_0_r);
+ ctx->init_vect_1_l = readl(&src_reg->init_vect_1_l);
+ ctx->init_vect_1_r = readl(&src_reg->init_vect_1_r);
+}
+
+/**
+ * cryp_restore_device_context - Restore hardware registers and
+ * other device context parameter
+ * @device_data: Pointer to the device data struct for base address.
+ * @ctx: Crypto device context
+ */
+void cryp_restore_device_context(struct cryp_device_data *device_data,
+ struct cryp_device_context *ctx)
+{
+ struct cryp_register *reg = device_data->base;
+
+ cryp_prep_ctx_mgmt(device_data);
+
+ writel(ctx->din, &reg->din);
+ writel(ctx->dout, &reg->dout);
+ writel(ctx->cr, &reg->cr);
+ writel(ctx->dmacr, &reg->dmacr);
+ writel(ctx->imsc, &reg->imsc);
+ writel(ctx->key_1_l, &reg->key_1_l);
+ writel(ctx->key_1_r, &reg->key_1_r);
+ writel(ctx->key_2_l, &reg->key_2_l);
+ writel(ctx->key_2_r, &reg->key_2_r);
+ writel(ctx->key_3_l, &reg->key_3_l);
+ writel(ctx->key_3_r, &reg->key_3_r);
+ writel(ctx->key_4_l, &reg->key_4_l);
+ writel(ctx->key_4_r, &reg->key_4_r);
+ writel(ctx->init_vect_0_l, &reg->init_vect_0_l);
+ writel(ctx->init_vect_0_r, &reg->init_vect_0_r);
+ writel(ctx->init_vect_1_l, &reg->init_vect_1_l);
+ writel(ctx->init_vect_1_r, &reg->init_vect_1_r);
+}
+
+/**
+ * cryp_write_indata - This routine writes 32 bit data into the data input
+ * register of the cryptography IP.
+ * @device_data: Pointer to the device data struct for base address.
+ * @write_data: Data word to write
+ */
+int cryp_write_indata(struct cryp_device_data *device_data, u32 write_data)
+{
+ if (NULL == device_data)
+ return -EINVAL;
+ writel(write_data, &device_data->base->din);
+
+ return 0;
+}
+
+/**
+ * cryp_read_indata - This routine reads the 32 bit data from the data input
+ * register into the specified location.
+ * @device_data: Pointer to the device data struct for base address.
+ * @p_read_data: Read the data from the input FIFO.
+ */
+int cryp_read_indata(struct cryp_device_data *device_data, u32 *p_read_data)
+{
+ if (NULL == device_data)
+ return -EINVAL;
+ if (NULL == p_read_data)
+ return -EINVAL;
+
+ *p_read_data = readl(&device_data->base->din);
+
+ return 0;
+}
+
+/**
+ * cryp_read_outdata - This routine reads the data from the data output
+ * register of the CRYP logic
+ * @device_data: Pointer to the device data struct for base address.
+ * @read_data: Read the data from the output FIFO.
+ */
+int cryp_read_outdata(struct cryp_device_data *device_data, u32 *read_data)
+{
+ if (NULL == device_data)
+ return -EINVAL;
+ if (NULL == read_data)
+ return -EINVAL;
+
+ *read_data = readl(&device_data->base->dout);
+
+ return 0;
+}
diff --git a/drivers/crypto/ux500/cryp/cryp.h b/drivers/crypto/ux500/cryp/cryp.h
new file mode 100644
index 00000000000..2d98923071c
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp.h
@@ -0,0 +1,338 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef _CRYP_H_
+#define _CRYP_H_
+
+#include <linux/completion.h>
+#include <linux/dmaengine.h>
+#include <linux/klist.h>
+#include <linux/mutex.h>
+
+/* Module Defines */
+#define CRYP_MODULE_NAME "CRYP HCL Module"
+
+#define DEV_DBG_NAME "crypX crypX:"
+
+/* CRYP enable/disable */
+enum cryp_crypen {
+ CRYP_CRYPEN_DISABLE = 0,
+ CRYP_CRYPEN_ENABLE = 1
+};
+
+/* CRYP Start Computation enable/disable */
+enum cryp_start {
+ CRYP_START_DISABLE = 0,
+ CRYP_START_ENABLE = 1
+};
+
+/* CRYP Init Signal enable/disable */
+enum cryp_init {
+ CRYP_INIT_DISABLE = 0,
+ CRYP_INIT_ENABLE = 1
+};
+
+/* Cryp State enable/disable */
+enum cryp_state {
+ CRYP_STATE_DISABLE = 0,
+ CRYP_STATE_ENABLE = 1
+};
+
+/* Key preparation bit enable */
+enum cryp_key_prep {
+ KSE_DISABLED,
+ KSE_ENABLED
+};
+
+/* Key size for AES*/
+#define CRYP_KEY_SIZE_128 (0)
+#define CRYP_KEY_SIZE_192 (1)
+#define CRYP_KEY_SIZE_256 (2)
+
+/* Data type Swap */
+#define CRYP_DATA_TYPE_32BIT_SWAP (0)
+#define CRYP_DATA_TYPE_16BIT_SWAP (1)
+#define CRYP_DATA_TYPE_8BIT_SWAP (2)
+#define CRYP_DATA_TYPE_BIT_SWAP (3)
+
+/* AES modes */
+enum cryp_algo_mode {
+ CRYP_ALGO_TDES_ECB,
+ CRYP_ALGO_TDES_CBC,
+ CRYP_ALGO_DES_ECB,
+ CRYP_ALGO_DES_CBC,
+ CRYP_ALGO_AES_ECB,
+ CRYP_ALGO_AES_CBC,
+ CRYP_ALGO_AES_CTR,
+ CRYP_ALGO_AES_XTS
+};
+
+/* Cryp Encryption or Decryption */
+enum cryp_algorithm_dir {
+ CRYP_ALGORITHM_ENCRYPT,
+ CRYP_ALGORITHM_DECRYPT
+};
+
+/* Hardware access method */
+enum cryp_mode {
+ CRYP_MODE_POLLING,
+ CRYP_MODE_INTERRUPT,
+ CRYP_MODE_DMA
+};
+
+/**
+ * struct cryp_config -
+ * @key_access: Cryp state enable/disable
+ * @key_size: Key size for AES
+ * @data_type: Data type Swap
+ * @algo_mode: AES modes
+ * @encrypt_or_decrypt: Cryp Encryption or Decryption
+ *
+ * CRYP configuration structure to be passed to set configuration
+ */
+struct cryp_config {
+ enum cryp_state key_access;
+ int key_size;
+ int data_type;
+ enum cryp_algo_mode algo_mode;
+ enum cryp_algorithm_dir encrypt_or_decrypt;
+};
+
+/**
+ * struct cryp_protection_config -
+ * @privilege_access: Privileged cryp state enable/disable
+ * @secure_access: Secure cryp state enable/disable
+ *
+ * Protection configuration structure for setting privilage access
+ */
+struct cryp_protection_config {
+ enum cryp_state privilege_access;
+ enum cryp_state secure_access;
+};
+
+/* Cryp status */
+enum cryp_status_id {
+ CRYP_STATUS_BUSY = 0x10,
+ CRYP_STATUS_OUTPUT_FIFO_FULL = 0x08,
+ CRYP_STATUS_OUTPUT_FIFO_NOT_EMPTY = 0x04,
+ CRYP_STATUS_INPUT_FIFO_NOT_FULL = 0x02,
+ CRYP_STATUS_INPUT_FIFO_EMPTY = 0x01
+};
+
+/* Cryp DMA interface */
+enum cryp_dma_req_type {
+ CRYP_DMA_DISABLE_BOTH,
+ CRYP_DMA_ENABLE_IN_DATA,
+ CRYP_DMA_ENABLE_OUT_DATA,
+ CRYP_DMA_ENABLE_BOTH_DIRECTIONS
+};
+
+enum cryp_dma_channel {
+ CRYP_DMA_RX = 0,
+ CRYP_DMA_TX
+};
+
+/* Key registers */
+enum cryp_key_reg_index {
+ CRYP_KEY_REG_1,
+ CRYP_KEY_REG_2,
+ CRYP_KEY_REG_3,
+ CRYP_KEY_REG_4
+};
+
+/* Key register left and right */
+struct cryp_key_value {
+ u32 key_value_left;
+ u32 key_value_right;
+};
+
+/* Cryp Initialization structure */
+enum cryp_init_vector_index {
+ CRYP_INIT_VECTOR_INDEX_0,
+ CRYP_INIT_VECTOR_INDEX_1
+};
+
+/* struct cryp_init_vector_value -
+ * @init_value_left
+ * @init_value_right
+ * */
+struct cryp_init_vector_value {
+ u32 init_value_left;
+ u32 init_value_right;
+};
+
+/**
+ * struct cryp_device_context - structure for a cryp context.
+ * @cr: control register
+ * @dmacr: DMA control register
+ * @imsc: Interrupt mask set/clear register
+ * @key_1_l: Key 1l register
+ * @key_1_r: Key 1r register
+ * @key_2_l: Key 2l register
+ * @key_2_r: Key 2r register
+ * @key_3_l: Key 3l register
+ * @key_3_r: Key 3r register
+ * @key_4_l: Key 4l register
+ * @key_4_r: Key 4r register
+ * @init_vect_0_l: Initialization vector 0l register
+ * @init_vect_0_r: Initialization vector 0r register
+ * @init_vect_1_l: Initialization vector 1l register
+ * @init_vect_1_r: Initialization vector 0r register
+ * @din: Data in register
+ * @dout: Data out register
+ *
+ * CRYP power management specifc structure.
+ */
+struct cryp_device_context {
+ u32 cr;
+ u32 dmacr;
+ u32 imsc;
+
+ u32 key_1_l;
+ u32 key_1_r;
+ u32 key_2_l;
+ u32 key_2_r;
+ u32 key_3_l;
+ u32 key_3_r;
+ u32 key_4_l;
+ u32 key_4_r;
+
+ u32 init_vect_0_l;
+ u32 init_vect_0_r;
+ u32 init_vect_1_l;
+ u32 init_vect_1_r;
+
+ u32 din;
+ u32 dout;
+};
+
+struct cryp_dma {
+ dma_cap_mask_t mask;
+ struct completion cryp_dma_complete;
+ struct dma_chan *chan_cryp2mem;
+ struct dma_chan *chan_mem2cryp;
+ struct stedma40_chan_cfg *cfg_cryp2mem;
+ struct stedma40_chan_cfg *cfg_mem2cryp;
+ int sg_src_len;
+ int sg_dst_len;
+ struct scatterlist *sg_src;
+ struct scatterlist *sg_dst;
+ int nents_src;
+ int nents_dst;
+};
+
+/**
+ * struct cryp_device_data - structure for a cryp device.
+ * @base: Pointer to the hardware base address.
+ * @dev: Pointer to the devices dev structure.
+ * @cryp_irq_complete: Pointer to an interrupt completion structure.
+ * @clk: Pointer to the device's clock control.
+ * @pwr_regulator: Pointer to the device's power control.
+ * @power_status: Current status of the power.
+ * @ctx_lock: Lock for current_ctx.
+ * @current_ctx: Pointer to the currently allocated context.
+ * @list_node: For inclusion into a klist.
+ * @dma: The dma structure holding channel configuration.
+ * @power_state: TRUE = power state on, FALSE = power state off.
+ * @power_state_mutex: Mutex for power_state.
+ * @restore_dev_ctx: TRUE = saved ctx, FALSE = no saved ctx.
+ */
+struct cryp_device_data {
+ struct cryp_register __iomem *base;
+ struct device *dev;
+ struct completion cryp_irq_complete;
+ struct clk *clk;
+ struct regulator *pwr_regulator;
+ int power_status;
+ struct spinlock ctx_lock;
+ struct cryp_ctx *current_ctx;
+ struct klist_node list_node;
+ struct cryp_dma dma;
+ bool power_state;
+ struct mutex power_state_mutex;
+ bool restore_dev_ctx;
+};
+
+void cryp_wait_until_done(struct cryp_device_data *device_data);
+
+/* Initialization functions */
+
+int cryp_check(struct cryp_device_data *device_data);
+
+void cryp_reset(struct cryp_device_data *device_data);
+
+void cryp_activity(struct cryp_device_data *device_data,
+ enum cryp_crypen cryp_crypen);
+
+void cryp_start(struct cryp_device_data *device_data);
+
+void cryp_init_signal(struct cryp_device_data *device_data,
+ enum cryp_init cryp_init);
+
+void cryp_key_preparation(struct cryp_device_data *device_data,
+ enum cryp_key_prep cryp_key_prep);
+
+void cryp_flush_inoutfifo(struct cryp_device_data *device_data);
+
+void cryp_cen_flush(struct cryp_device_data *device_data);
+
+void cryp_set_dir(struct cryp_device_data *device_data, int dir);
+
+int cryp_set_configuration(struct cryp_device_data *device_data,
+ struct cryp_config *p_cryp_config);
+
+int cryp_get_configuration(struct cryp_device_data *device_data,
+ struct cryp_config *p_cryp_config);
+
+void cryp_configure_for_dma(struct cryp_device_data *device_data,
+ enum cryp_dma_req_type dma_req);
+
+int cryp_configure_key_values(struct cryp_device_data *device_data,
+ enum cryp_key_reg_index key_reg_index,
+ struct cryp_key_value key_value);
+
+int cryp_configure_init_vector(struct cryp_device_data *device_data,
+ enum cryp_init_vector_index
+ init_vector_index,
+ struct cryp_init_vector_value
+ init_vector_value);
+
+int cryp_configure_protection(struct cryp_device_data *device_data,
+ struct cryp_protection_config *p_protect_config);
+
+/* Power management funtions */
+void cryp_save_device_context(struct cryp_device_data *device_data,
+ struct cryp_device_context *ctx);
+
+void cryp_restore_device_context(struct cryp_device_data *device_data,
+ struct cryp_device_context *ctx);
+
+/* Data transfer and status bits. */
+int cryp_is_logic_busy(struct cryp_device_data *device_data);
+
+int cryp_get_status(struct cryp_device_data *device_data);
+
+/**
+ * cryp_write_indata - This routine writes 32 bit data into the data input
+ * register of the cryptography IP.
+ * @device_data: Pointer to the device data struct for base address.
+ * @write_data: Data to write.
+ */
+int cryp_write_indata(struct cryp_device_data *device_data, u32 write_data);
+
+/**
+ * cryp_read_outdata - This routine reads the data from the data output
+ * register of the CRYP logic
+ * @device_data: Pointer to the device data struct for base address.
+ * @read_data: Read the data from the output FIFO.
+ */
+int cryp_read_outdata(struct cryp_device_data *device_data, u32 *read_data);
+
+#endif /* _CRYP_H_ */
diff --git a/drivers/crypto/ux500/cryp/cryp_core.c b/drivers/crypto/ux500/cryp/cryp_core.c
new file mode 100644
index 00000000000..197bb416067
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp_core.c
@@ -0,0 +1,2331 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/crypto.h>
+#include <linux/dmaengine.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irqreturn.h>
+#include <linux/klist.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/semaphore.h>
+
+#include <crypto/aes.h>
+#include <crypto/algapi.h>
+#include <crypto/ctr.h>
+#include <crypto/des.h>
+#include <crypto/scatterwalk.h>
+
+#include <plat/ste_dma40.h>
+
+#include <mach/crypto-ux500.h>
+#include <mach/hardware.h>
+#include <mach/ste-dma40-db8500.h>
+
+#include "cryp_p.h"
+#include "cryp.h"
+
+#define CRYP_MAX_KEY_SIZE 32
+#define BYTES_PER_WORD 4
+
+static int cryp_mode;
+
+static DEFINE_KLIST(cryp_device_list, NULL, NULL);
+
+static struct stedma40_chan_cfg *mem_to_engine;
+static struct stedma40_chan_cfg *engine_to_mem;
+
+/**
+ * struct cryp_driver_data - data specific to the driver.
+ *
+ * @cryp_device_list: A list of registered devices to choose from.
+ * @device_allocation: A semaphore initialized with number of devices.
+ */
+struct cryp_driver_data {
+ struct klist device_list;
+ struct semaphore device_allocation;
+};
+
+/**
+ * struct cryp_ctx - Crypto context
+ * @config: Crypto mode.
+ * @key[CRYP_MAX_KEY_SIZE]: Key.
+ * @keylen: Length of key.
+ * @iv: Pointer to initialization vector.
+ * @indata: Pointer to indata.
+ * @outdata: Pointer to outdata.
+ * @datalen: Length of indata.
+ * @outlen: Length of outdata.
+ * @blocksize: Size of blocks.
+ * @updated: Updated flag.
+ * @dev_ctx: Device dependent context.
+ * @device: Pointer to the device.
+ */
+struct cryp_ctx {
+ struct cryp_config config;
+ u8 key[CRYP_MAX_KEY_SIZE];
+ u32 keylen;
+ u8 *iv;
+ const u8 *indata;
+ u8 *outdata;
+ u32 datalen;
+ u32 outlen;
+ u32 blocksize;
+ u8 updated;
+ struct cryp_device_context dev_ctx;
+ struct cryp_device_data *device;
+};
+
+static struct cryp_driver_data driver_data;
+
+/**
+ * uint8p_to_uint32_be - 4*uint8 to uint32 big endian
+ * @in: Data to convert.
+ */
+static inline u32 uint8p_to_uint32_be(u8 *in)
+{
+ return (u32)in[0]<<24 |
+ ((u32)in[1]<<16) |
+ ((u32)in[2]<<8) |
+ ((u32)in[3]);
+}
+
+/**
+ * uint8p_to_uint32_le - 4*uint8 to uint32 little endian
+ * @in: Data to convert.
+ */
+static inline u32 uint8p_to_uint32_le(u8 *in)
+{
+ return (u32)in[3]<<24 |
+ ((u32)in[2]<<16) |
+ ((u32)in[1]<<8) |
+ ((u32)in[0]);
+}
+
+static inline void uint32_to_uint8p_be(u32 in, u8 *out)
+{
+ out[0] = (u8)(in>>24);
+ out[1] = (u8)(in>>16);
+ out[2] = (u8)(in>>8);
+ out[3] = (u8) in;
+}
+
+static inline void uint32_to_uint8p_le(u32 in, u8 *out)
+{
+ out[3] = (u8)(in>>24);
+ out[2] = (u8)(in>>16);
+ out[1] = (u8)(in>>8);
+ out[0] = (u8) in;
+}
+
+/**
+ * swap_bits_in_byte - mirror the bits in a byte
+ * @b: the byte to be mirrored
+ *
+ * The bits are swapped the following way:
+ * Byte b include bits 0-7, nibble 1 (n1) include bits 0-3 and
+ * nibble 2 (n2) bits 4-7.
+ *
+ * Nibble 1 (n1):
+ * (The "old" (moved) bit is replaced with a zero)
+ * 1. Move bit 6 and 7, 4 positions to the left.
+ * 2. Move bit 3 and 5, 2 positions to the left.
+ * 3. Move bit 1-4, 1 position to the left.
+ *
+ * Nibble 2 (n2):
+ * 1. Move bit 0 and 1, 4 positions to the right.
+ * 2. Move bit 2 and 4, 2 positions to the right.
+ * 3. Move bit 3-6, 1 position to the right.
+ *
+ * Combine the two nibbles to a complete and swapped byte.
+ */
+
+static inline u8 swap_bits_in_byte(u8 b)
+{
+#define R_SHIFT_4_MASK (0xc0) /* Bits 6 and 7, right shift 4 */
+#define R_SHIFT_2_MASK (0x28) /* (After right shift 4) Bits 3 and 5,
+ right shift 2 */
+#define R_SHIFT_1_MASK (0x1e) /* (After right shift 2) Bits 1-4,
+ right shift 1 */
+#define L_SHIFT_4_MASK (0x03) /* Bits 0 and 1, left shift 4 */
+#define L_SHIFT_2_MASK (0x14) /* (After left shift 4) Bits 2 and 4,
+ left shift 2 */
+#define L_SHIFT_1_MASK (0x78) /* (After left shift 1) Bits 3-6,
+ left shift 1 */
+
+ u8 n1;
+ u8 n2;
+
+ /* Swap most significant nibble */
+ /* Right shift 4, bits 6 and 7 */
+ n1 = ((b & R_SHIFT_4_MASK) >> 4) | (b & ~(R_SHIFT_4_MASK >> 4));
+ /* Right shift 2, bits 3 and 5 */
+ n1 = ((n1 & R_SHIFT_2_MASK) >> 2) | (n1 & ~(R_SHIFT_2_MASK >> 2));
+ /* Right shift 1, bits 1-4 */
+ n1 = (n1 & R_SHIFT_1_MASK) >> 1;
+
+ /* Swap least significant nibble */
+ /* Left shift 4, bits 0 and 1 */
+ n2 = ((b & L_SHIFT_4_MASK) << 4) | (b & ~(L_SHIFT_4_MASK << 4));
+ /* Left shift 2, bits 2 and 4 */
+ n2 = ((n2 & L_SHIFT_2_MASK) << 2) | (n2 & ~(L_SHIFT_2_MASK << 2));
+ /* Left shift 1, bits 3-6 */
+ n2 = (n2 & L_SHIFT_1_MASK) << 1;
+
+ return n1 | n2;
+}
+
+static inline void swap_words_in_key_and_bits_in_byte(const u8 *in,
+ u8 *out, u32 len)
+{
+ unsigned int i = 0;
+ int j;
+ int index = 0;
+
+ j = len - BYTES_PER_WORD;
+ while (j >= 0) {
+ for (i = 0; i < BYTES_PER_WORD; i++) {
+ index = len - j - BYTES_PER_WORD + i;
+ out[j + i] =
+ swap_bits_in_byte(in[index]);
+ }
+ j -= BYTES_PER_WORD;
+ }
+}
+
+static inline void swap_4bits_in_bytes(const u8 *in, u8 *out, u32 len)
+{
+ unsigned int i;
+ for (i = 0; i < len; i++)
+ out[i] = swap_bits_in_byte(in[i]);
+}
+
+static irqreturn_t cryp_interrupt_handler(int irq, void *param)
+{
+ struct cryp_ctx *ctx;
+ int i;
+ struct cryp_device_data *device_data;
+
+ if (param == NULL) {
+ BUG_ON(!param);
+ return IRQ_HANDLED;
+ }
+
+ device_data = (struct cryp_device_data *)param;
+
+ ctx = device_data->current_ctx;
+
+ if (ctx == NULL) {
+ BUG_ON(!ctx);
+ return IRQ_HANDLED;
+ }
+
+ if (cryp_pending_irq_src(device_data,
+ CRYP_IRQ_SRC_OUTPUT_FIFO)) {
+ if (ctx->outlen / ctx->blocksize > 0) {
+ for (i = 0; i < ctx->blocksize / 4; i++) {
+ cryp_read_outdata(device_data,
+ (u32 *)ctx->outdata);
+ ctx->outdata += 4;
+ ctx->outlen -= 4;
+ }
+
+ if (ctx->outlen == 0) {
+ cryp_disable_irq_src(device_data,
+ CRYP_IRQ_SRC_OUTPUT_FIFO);
+ complete(&ctx->device->cryp_irq_complete);
+ }
+ }
+ } else if (cryp_pending_irq_src(device_data,
+ CRYP_IRQ_SRC_INPUT_FIFO)) {
+ if (ctx->datalen / ctx->blocksize > 0) {
+ for (i = 0 ; i < ctx->blocksize / 4; i++) {
+ cryp_write_indata(device_data,
+ *((u32 *)ctx->indata));
+ ctx->indata += 4;
+ ctx->datalen -= 4;
+ }
+
+ if (ctx->datalen == 0)
+ cryp_disable_irq_src(device_data,
+ CRYP_IRQ_SRC_INPUT_FIFO);
+
+ if (ctx->config.algo_mode == CRYP_ALGO_AES_XTS) {
+ cryp_start(device_data);
+ cryp_wait_until_done(device_data);
+ }
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mode_is_aes(enum cryp_algo_mode mode)
+{
+ return (CRYP_ALGO_AES_ECB == mode) ||
+ (CRYP_ALGO_AES_CBC == mode) ||
+ (CRYP_ALGO_AES_CTR == mode) ||
+ (CRYP_ALGO_AES_XTS == mode);
+}
+
+static int cfg_iv(struct cryp_device_data *device_data, u32 left, u32 right,
+ enum cryp_init_vector_index index)
+{
+ struct cryp_init_vector_value vector_value;
+
+ dev_dbg(device_data->dev, "[%s]", __func__);
+
+ vector_value.init_value_left = left;
+ vector_value.init_value_right = right;
+
+ return cryp_configure_init_vector(device_data,
+ index,
+ vector_value);
+}
+
+static int cfg_ivs(struct cryp_device_data *device_data, struct cryp_ctx *ctx)
+{
+ int i;
+ int status = 0;
+ int num_of_regs = ctx->blocksize / 8;
+ u32 iv[AES_BLOCK_SIZE / 4];
+
+ dev_dbg(device_data->dev, "[%s]", __func__);
+
+ /*
+ * Since we loop on num_of_regs we need to have a check in case
+ * someone provides an incorrect blocksize which would force calling
+ * cfg_iv with i greater than 2 which is an error.
+ */
+ if (num_of_regs > 2) {
+ dev_err(device_data->dev, "[%s] Incorrect blocksize %d",
+ __func__, ctx->blocksize);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ctx->blocksize / 4; i++)
+ iv[i] = uint8p_to_uint32_be(ctx->iv + i*4);
+
+ for (i = 0; i < num_of_regs; i++) {
+ status = cfg_iv(device_data, iv[i*2], iv[i*2+1],
+ (enum cryp_init_vector_index) i);
+ if (status != 0)
+ return status;
+ }
+ return status;
+}
+
+static int set_key(struct cryp_device_data *device_data,
+ u32 left_key,
+ u32 right_key,
+ enum cryp_key_reg_index index)
+{
+ struct cryp_key_value key_value;
+ int cryp_error;
+
+ dev_dbg(device_data->dev, "[%s]", __func__);
+
+ key_value.key_value_left = left_key;
+ key_value.key_value_right = right_key;
+
+ cryp_error = cryp_configure_key_values(device_data,
+ index,
+ key_value);
+ if (cryp_error != 0)
+ dev_err(device_data->dev, "[%s]: "
+ "cryp_configure_key_values() failed!", __func__);
+
+ return cryp_error;
+}
+
+static int cfg_keys(struct cryp_ctx *ctx)
+{
+ int i;
+ int num_of_regs = ctx->keylen / 8;
+ u32 swapped_key[CRYP_MAX_KEY_SIZE / 4];
+ int cryp_error = 0;
+
+ dev_dbg(ctx->device->dev, "[%s]", __func__);
+
+ if (mode_is_aes(ctx->config.algo_mode)) {
+ swap_words_in_key_and_bits_in_byte((u8 *)ctx->key,
+ (u8 *)swapped_key,
+ ctx->keylen);
+ } else {
+ for (i = 0; i < ctx->keylen / 4; i++)
+ swapped_key[i] = uint8p_to_uint32_be(ctx->key + i*4);
+ }
+
+ for (i = 0; i < num_of_regs; i++) {
+ cryp_error = set_key(ctx->device,
+ *(((u32 *)swapped_key)+i*2),
+ *(((u32 *)swapped_key)+i*2+1),
+ (enum cryp_key_reg_index) i);
+
+ if (cryp_error != 0) {
+ dev_err(ctx->device->dev, "[%s]: set_key() failed!",
+ __func__);
+ return cryp_error;
+ }
+ }
+ return cryp_error;
+}
+
+static int cryp_setup_context(struct cryp_ctx *ctx,
+ struct cryp_device_data *device_data)
+{
+ if (ctx->updated)
+ cryp_restore_device_context(device_data, &ctx->dev_ctx);
+ else {
+ cryp_activity(device_data, CRYP_CRYPEN_DISABLE);
+
+ if (cfg_keys(ctx) != 0) {
+ dev_err(ctx->device->dev, "[%s]: cfg_keys failed!",
+ __func__);
+ return -EPERM;
+ }
+
+ if ((ctx->iv) &&
+ (CRYP_ALGO_AES_ECB != ctx->config.algo_mode) &&
+ (CRYP_ALGO_DES_ECB != ctx->config.algo_mode) &&
+ (CRYP_ALGO_TDES_ECB != ctx->config.algo_mode)) {
+ if (cfg_ivs(device_data, ctx) != 0)
+ return -EPERM;
+ }
+
+ cryp_set_configuration(device_data, &ctx->config);
+ }
+
+ return 0;
+}
+
+
+static int cryp_get_device_data(struct cryp_ctx *ctx,
+ struct cryp_device_data **device_data)
+{
+ int ret;
+ struct klist_iter device_iterator;
+ struct klist_node *device_node;
+ struct cryp_device_data *local_device_data = NULL;
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ /* Wait until a device is available */
+ ret = down_interruptible(&driver_data.device_allocation);
+ if (ret)
+ return ret; /* Interrupted */
+
+ /* Select a device */
+ klist_iter_init(&driver_data.device_list, &device_iterator);
+
+ device_node = klist_next(&device_iterator);
+ while (device_node) {
+ local_device_data = container_of(device_node,
+ struct cryp_device_data, list_node);
+ spin_lock(&local_device_data->ctx_lock);
+ /* current_ctx allocates a device, NULL = unallocated */
+ if (local_device_data->current_ctx) {
+ device_node = klist_next(&device_iterator);
+ } else {
+ local_device_data->current_ctx = ctx;
+ ctx->device = local_device_data;
+ spin_unlock(&local_device_data->ctx_lock);
+ break;
+ }
+ spin_unlock(&local_device_data->ctx_lock);
+ }
+ klist_iter_exit(&device_iterator);
+
+ if (!device_node) {
+ /**
+ * No free device found.
+ * Since we allocated a device with down_interruptible, this
+ * should not be able to happen.
+ * Number of available devices, which are contained in
+ * device_allocation, is therefore decremented by not doing
+ * an up(device_allocation).
+ */
+ return -EBUSY;
+ }
+
+ *device_data = local_device_data;
+
+ return 0;
+}
+
+static void cryp_dma_setup_channel(struct cryp_device_data *device_data,
+ struct device *dev)
+{
+ dma_cap_zero(device_data->dma.mask);
+ dma_cap_set(DMA_SLAVE, device_data->dma.mask);
+
+ device_data->dma.cfg_mem2cryp = mem_to_engine;
+ device_data->dma.chan_mem2cryp =
+ dma_request_channel(device_data->dma.mask,
+ stedma40_filter,
+ device_data->dma.cfg_mem2cryp);
+
+ device_data->dma.cfg_cryp2mem = engine_to_mem;
+ device_data->dma.chan_cryp2mem =
+ dma_request_channel(device_data->dma.mask,
+ stedma40_filter,
+ device_data->dma.cfg_cryp2mem);
+
+ init_completion(&device_data->dma.cryp_dma_complete);
+}
+
+static void cryp_dma_out_callback(void *data)
+{
+ struct cryp_ctx *ctx = (struct cryp_ctx *) data;
+ dev_dbg(ctx->device->dev, "[%s]: ", __func__);
+
+ complete(&ctx->device->dma.cryp_dma_complete);
+}
+
+static int cryp_set_dma_transfer(struct cryp_ctx *ctx,
+ struct scatterlist *sg,
+ int len,
+ enum dma_data_direction direction)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *channel = NULL;
+ dma_cookie_t cookie;
+
+ dev_dbg(ctx->device->dev, "[%s]: ", __func__);
+
+ switch (direction) {
+ case DMA_TO_DEVICE:
+ channel = ctx->device->dma.chan_mem2cryp;
+ ctx->device->dma.sg_src = sg;
+ ctx->device->dma.sg_src_len = dma_map_sg(channel->device->dev,
+ ctx->device->dma.sg_src,
+ ctx->device->dma.nents_src,
+ direction);
+
+ if (!ctx->device->dma.sg_src_len) {
+ dev_dbg(ctx->device->dev,
+ "[%s]: Could not map the sg list (TO_DEVICE)",
+ __func__);
+ return -EFAULT;
+ }
+
+ dev_dbg(ctx->device->dev, "[%s]: Setting up DMA for buffer "
+ "(TO_DEVICE)", __func__);
+
+ desc = channel->device->device_prep_slave_sg(channel,
+ ctx->device->dma.sg_src,
+ ctx->device->dma.sg_src_len,
+ direction,
+ DMA_CTRL_ACK);
+ break;
+
+ case DMA_FROM_DEVICE:
+ channel = ctx->device->dma.chan_cryp2mem;
+ ctx->device->dma.sg_dst = sg;
+
+ ctx->device->dma.sg_dst_len = dma_map_sg(channel->device->dev,
+ ctx->device->dma.sg_dst,
+ ctx->device->dma.nents_dst,
+ direction);
+
+ if (!ctx->device->dma.sg_dst_len) {
+ dev_dbg(ctx->device->dev,
+ "[%s]: Could not map the sg list "
+ "(FROM_DEVICE)", __func__);
+ return -EFAULT;
+ }
+
+ dev_dbg(ctx->device->dev, "[%s]: Setting up DMA for buffer "
+ "(FROM_DEVICE)", __func__);
+
+ desc = channel->device->device_prep_slave_sg(channel,
+ ctx->device->dma.sg_dst,
+ ctx->device->dma.sg_dst_len,
+ direction,
+ DMA_CTRL_ACK |
+ DMA_PREP_INTERRUPT);
+
+ desc->callback = cryp_dma_out_callback;
+ desc->callback_param = ctx;
+ break;
+
+ default:
+ dev_dbg(ctx->device->dev, "[%s]: Invalid DMA direction",
+ __func__);
+ return -EFAULT;
+ }
+
+ cookie = desc->tx_submit(desc);
+ dma_async_issue_pending(channel);
+
+ return 0;
+}
+
+static void cryp_dma_done(struct cryp_ctx *ctx)
+{
+ struct dma_chan *chan;
+
+ dev_dbg(ctx->device->dev, "[%s]: ", __func__);
+
+ chan = ctx->device->dma.chan_mem2cryp;
+ chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+ dma_unmap_sg(chan->device->dev, ctx->device->dma.sg_src,
+ ctx->device->dma.sg_src_len, DMA_TO_DEVICE);
+
+ chan = ctx->device->dma.chan_cryp2mem;
+ chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+ dma_unmap_sg(chan->device->dev, ctx->device->dma.sg_dst,
+ ctx->device->dma.sg_dst_len, DMA_FROM_DEVICE);
+}
+
+static int cryp_dma_write(struct cryp_ctx *ctx, struct scatterlist *sg,
+ int len)
+{
+ int error = cryp_set_dma_transfer(ctx, sg, len, DMA_TO_DEVICE);
+ dev_dbg(ctx->device->dev, "[%s]: ", __func__);
+
+ if (error) {
+ dev_dbg(ctx->device->dev, "[%s]: cryp_set_dma_transfer() "
+ "failed", __func__);
+ return error;
+ }
+
+ return len;
+}
+
+static int cryp_dma_read(struct cryp_ctx *ctx, struct scatterlist *sg, int len)
+{
+ int error = cryp_set_dma_transfer(ctx, sg, len, DMA_FROM_DEVICE);
+ if (error) {
+ dev_dbg(ctx->device->dev, "[%s]: cryp_set_dma_transfer() "
+ "failed", __func__);
+ return error;
+ }
+
+ return len;
+}
+
+static int cryp_polling_mode(struct cryp_ctx *ctx,
+ struct cryp_device_data *device_data)
+{
+ int i;
+ int ret = 0;
+ int remaining_length = ctx->datalen;
+ const u8 *indata = ctx->indata;
+ u8 *outdata = ctx->outdata;
+
+ cryp_activity(device_data, CRYP_CRYPEN_ENABLE);
+ while (remaining_length > 0) {
+ for (i = 0; i < ctx->blocksize / BYTES_PER_WORD; i++) {
+ ret = cryp_write_indata(device_data,
+ *((u32 *)indata));
+ if (ret)
+ goto out;
+ indata += BYTES_PER_WORD;
+ remaining_length -= BYTES_PER_WORD;
+ }
+ cryp_wait_until_done(device_data);
+ for (i = 0; i < ctx->blocksize / BYTES_PER_WORD; i++) {
+ ret = cryp_read_outdata(device_data,
+ (u32 *)outdata);
+ if (ret)
+ goto out;
+ outdata += BYTES_PER_WORD;
+ }
+ cryp_wait_until_done(device_data);
+ }
+out:
+ return ret;
+}
+
+static int cryp_disable_power(
+ struct device *dev,
+ struct cryp_device_data *device_data,
+ bool save_device_context)
+{
+ int ret = 0;
+
+ dev_dbg(dev, "[%s]", __func__);
+
+ mutex_lock(&device_data->power_state_mutex);
+ if (!device_data->power_state)
+ goto out;
+
+ spin_lock(&device_data->ctx_lock);
+ if (save_device_context && device_data->current_ctx) {
+ cryp_save_device_context(device_data,
+ &device_data->current_ctx->dev_ctx);
+ device_data->restore_dev_ctx = true;
+ }
+ spin_unlock(&device_data->ctx_lock);
+
+ clk_disable(device_data->clk);
+ ret = regulator_disable(device_data->pwr_regulator);
+ if (ret)
+ dev_err(dev, "[%s]: "
+ "regulator_disable() failed!",
+ __func__);
+
+ device_data->power_state = false;
+
+out:
+ mutex_unlock(&device_data->power_state_mutex);
+
+ return ret;
+}
+
+static int cryp_enable_power(
+ struct device *dev,
+ struct cryp_device_data *device_data,
+ bool restore_device_context)
+{
+ int ret = 0;
+
+ dev_dbg(dev, "[%s]", __func__);
+
+ mutex_lock(&device_data->power_state_mutex);
+ if (!device_data->power_state) {
+ ret = regulator_enable(device_data->pwr_regulator);
+ if (ret) {
+ dev_err(dev, "[%s]: regulator_enable() failed!",
+ __func__);
+ goto out;
+ }
+
+ ret = clk_enable(device_data->clk);
+ if (ret) {
+ dev_err(dev, "[%s]: clk_enable() failed!",
+ __func__);
+ regulator_disable(device_data->pwr_regulator);
+ goto out;
+ }
+ device_data->power_state = true;
+ }
+
+ if (device_data->restore_dev_ctx) {
+ spin_lock(&device_data->ctx_lock);
+ if (restore_device_context && device_data->current_ctx) {
+ device_data->restore_dev_ctx = false;
+ cryp_restore_device_context(device_data,
+ &device_data->current_ctx->dev_ctx);
+ }
+ spin_unlock(&device_data->ctx_lock);
+ }
+out:
+ mutex_unlock(&device_data->power_state_mutex);
+
+ return ret;
+}
+
+static int hw_crypt_noxts(struct cryp_ctx *ctx,
+ struct cryp_device_data *device_data)
+{
+ int ret;
+
+ const u8 *indata = ctx->indata;
+ u8 *outdata = ctx->outdata;
+ u32 datalen = ctx->datalen;
+ u32 outlen = datalen;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->outlen = ctx->datalen;
+ ctx->config.key_access = CRYP_STATE_ENABLE;
+ ctx->config.data_type = CRYP_DATA_TYPE_8BIT_SWAP;
+
+ cryp_reset(device_data);
+
+ ret = cryp_setup_context(ctx, device_data);
+ if (ret)
+ goto out;
+
+ cryp_flush_inoutfifo(device_data);
+
+ if (cryp_mode == CRYP_MODE_INTERRUPT) {
+ INIT_COMPLETION(ctx->device->cryp_irq_complete);
+
+ cryp_enable_irq_src(device_data, CRYP_IRQ_SRC_INPUT_FIFO);
+ cryp_enable_irq_src(device_data, CRYP_IRQ_SRC_OUTPUT_FIFO);
+
+ cryp_activity(device_data, CRYP_CRYPEN_ENABLE);
+
+ wait_for_completion(&ctx->device->cryp_irq_complete);
+ } else if (cryp_mode == CRYP_MODE_POLLING ||
+ cryp_mode == CRYP_MODE_DMA) {
+ /*
+ * The reason for having DMA in this if case is that if we are
+ * running cryp_mode = 2, then we separate DMA routines for
+ * handling cipher/plaintext > blocksize, except when
+ * running the normal CRYPTO_ALG_TYPE_CIPHER, then we still use
+ * the polling mode. Overhead of doing DMA setup eats up the
+ * benefits using it.
+ */
+ ret = cryp_polling_mode(ctx, device_data);
+ if (ret)
+ goto out;
+ } else {
+ dev_err(ctx->device->dev, "[%s]: Invalid operation mode!",
+ __func__);
+ ret = -EPERM;
+ goto out;
+ }
+
+ ret = 0;
+ cryp_save_device_context(device_data, &ctx->dev_ctx);
+ if (ctx->updated == 0)
+ ctx->updated = 1;
+
+out:
+ ctx->indata = indata;
+ ctx->outdata = outdata;
+ ctx->datalen = datalen;
+ ctx->outlen = outlen;
+
+ return ret;
+}
+
+static int get_nents(struct scatterlist *sg, int nbytes)
+{
+ int nents = 0;
+
+ while (nbytes > 0) {
+ nbytes -= sg->length;
+ sg = scatterwalk_sg_next(sg);
+ nents++;
+ }
+
+ return nents;
+}
+
+static int ablk_dma_crypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ struct cryp_device_data *device_data;
+
+ int bytes_written = 0;
+ int bytes_read = 0;
+ int ret;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.key_access = CRYP_STATE_ENABLE;
+ ctx->config.data_type = CRYP_DATA_TYPE_8BIT_SWAP;
+ ctx->datalen = areq->nbytes;
+ ctx->outlen = areq->nbytes;
+
+ ret = cryp_get_device_data(ctx, &device_data);
+ if (ret)
+ return ret;
+
+ ret = cryp_enable_power(device_data->dev, device_data, false);
+ if (ret) {
+ dev_err(device_data->dev, "[%s]: "
+ "cryp_enable_power() failed!", __func__);
+ goto out;
+ }
+
+ cryp_reset(device_data);
+
+ ret = cryp_setup_context(ctx, device_data);
+ if (ret)
+ goto out_power;
+
+ /* We have the device now, so store the nents in the dma struct. */
+ ctx->device->dma.nents_src = get_nents(areq->src, ctx->datalen);
+ ctx->device->dma.nents_dst = get_nents(areq->dst, ctx->outlen);
+
+ /* Enable DMA in- and output. */
+ cryp_configure_for_dma(device_data, CRYP_DMA_ENABLE_BOTH_DIRECTIONS);
+
+ bytes_written = cryp_dma_write(ctx, areq->src, ctx->datalen);
+ bytes_read = cryp_dma_read(ctx, areq->dst, bytes_written);
+
+ wait_for_completion(&ctx->device->dma.cryp_dma_complete);
+ cryp_dma_done(ctx);
+
+ cryp_save_device_context(device_data, &ctx->dev_ctx);
+ ctx->updated = 1;
+
+out_power:
+ if (cryp_disable_power(device_data->dev, device_data, false))
+ dev_err(device_data->dev, "[%s]: "
+ "cryp_disable_power() failed!", __func__);
+
+out:
+ spin_lock(&device_data->ctx_lock);
+ device_data->current_ctx = NULL;
+ ctx->device = NULL;
+ spin_unlock(&device_data->ctx_lock);
+
+ /*
+ * The down_interruptible part for this semaphore is called in
+ * cryp_get_device_data.
+ */
+ up(&driver_data.device_allocation);
+
+ if (unlikely(bytes_written != bytes_read))
+ return -EPERM;
+
+ return 0;
+}
+
+static int ablk_crypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ struct ablkcipher_walk walk;
+ unsigned long src_paddr;
+ unsigned long dst_paddr;
+ int ret;
+ int nbytes;
+ struct cryp_device_data *device_data;
+
+ ret = cryp_get_device_data(ctx, &device_data);
+ if (ret)
+ goto out;
+
+ ret = cryp_enable_power(device_data->dev, device_data, false);
+ if (ret) {
+ dev_err(device_data->dev, "[%s]: "
+ "cryp_enable_power() failed!", __func__);
+ goto out_power;
+ }
+
+ ablkcipher_walk_init(&walk, areq->dst, areq->src, areq->nbytes);
+ ret = ablkcipher_walk_phys(areq, &walk);
+
+ if (ret) {
+ pr_err(DEV_DBG_NAME "[%s]: ablkcipher_walk_phys() failed!",
+ __func__);
+ goto out_power;
+ }
+
+ while ((nbytes = walk.nbytes) > 0) {
+ ctx->iv = walk.iv;
+ src_paddr = (page_to_phys(walk.src.page) + walk.src.offset);
+ ctx->indata = phys_to_virt(src_paddr);
+
+ dst_paddr = (page_to_phys(walk.dst.page) + walk.dst.offset);
+ ctx->outdata = phys_to_virt(dst_paddr);
+
+ ctx->datalen = nbytes - (nbytes % ctx->blocksize);
+
+ ret = hw_crypt_noxts(ctx, device_data);
+ if (ret)
+ goto out_power;
+
+ nbytes -= ctx->datalen;
+ ret = ablkcipher_walk_done(areq, &walk, nbytes);
+ if (ret)
+ goto out_power;
+ }
+ ablkcipher_walk_complete(&walk);
+
+out_power:
+ if (cryp_disable_power(device_data->dev, device_data, false))
+ dev_err(device_data->dev, "[%s]: "
+ "cryp_disable_power() failed!", __func__);
+out:
+ /* Release the device */
+ spin_lock(&device_data->ctx_lock);
+ device_data->current_ctx = NULL;
+ ctx->device = NULL;
+ spin_unlock(&device_data->ctx_lock);
+
+ /*
+ * The down_interruptible part for this semaphore is called in
+ * cryp_get_device_data.
+ */
+ up(&driver_data.device_allocation);
+
+ return ret;
+}
+
+static int aes_ablkcipher_setkey(struct crypto_ablkcipher *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 *flags = &cipher->base.crt_flags;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ switch (keylen) {
+ case AES_KEYSIZE_128:
+ ctx->config.key_size = CRYP_KEY_SIZE_128;
+ break;
+
+ case AES_KEYSIZE_192:
+ ctx->config.key_size = CRYP_KEY_SIZE_192;
+ break;
+
+ case AES_KEYSIZE_256:
+ ctx->config.key_size = CRYP_KEY_SIZE_256;
+ break;
+
+ default:
+ pr_err(DEV_DBG_NAME "[%s]: Unknown keylen!", __func__);
+ *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+ }
+
+ memcpy(ctx->key, key, keylen);
+ ctx->keylen = keylen;
+
+ ctx->updated = 0;
+
+ return 0;
+}
+
+static int aes_setkey(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct cryp_ctx *ctx = crypto_tfm_ctx(tfm);
+ u32 *flags = &tfm->crt_flags;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ /* For CTR mode */
+ if (keylen != AES_KEYSIZE_128 &&
+ keylen != AES_KEYSIZE_192 &&
+ keylen != AES_KEYSIZE_256) {
+
+ *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ pr_debug(DEV_DBG_NAME " [%s] invalid keylen", __func__);
+ return -EINVAL;
+ }
+
+ if (keylen == AES_KEYSIZE_128)
+ ctx->config.key_size = CRYP_KEY_SIZE_128;
+ else if (keylen == AES_KEYSIZE_192)
+ ctx->config.key_size = CRYP_KEY_SIZE_192;
+ else if (keylen == AES_KEYSIZE_256)
+ ctx->config.key_size = CRYP_KEY_SIZE_256;
+
+ memcpy(ctx->key, key, keylen);
+ ctx->keylen = keylen;
+
+ ctx->updated = 0;
+ return 0;
+}
+
+static int des_ablkcipher_setkey(struct crypto_ablkcipher *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 *flags = &cipher->base.crt_flags;
+ u32 tmp[DES_EXPKEY_WORDS];
+ int ret;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+ if (keylen != DES_KEY_SIZE) {
+ *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_RES_BAD_KEY_LEN",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret = des_ekey(tmp, key);
+ if (unlikely(ret == 0) && (*flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+ *flags |= CRYPTO_TFM_RES_WEAK_KEY;
+ pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_REQ_WEAK_KEY",
+ __func__);
+ return -EINVAL;
+ }
+
+ memcpy(ctx->key, key, keylen);
+ ctx->keylen = keylen;
+
+ ctx->updated = 0;
+ return 0;
+}
+
+static int des_setkey(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct cryp_ctx *ctx = crypto_tfm_ctx(tfm);
+ u32 *flags = &tfm->crt_flags;
+ int ret;
+ u32 tmp[DES_EXPKEY_WORDS];
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ if (keylen != DES_KEY_SIZE) {
+ *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_RES_BAD_KEY_LEN",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret = des_ekey(tmp, key);
+ if (unlikely(ret == 0) && (*flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+ *flags |= CRYPTO_TFM_RES_WEAK_KEY;
+ pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_REQ_WEAK_KEY",
+ __func__);
+ return -EINVAL;
+ }
+
+ memcpy(ctx->key, key, keylen);
+ ctx->keylen = keylen;
+
+ ctx->updated = 0;
+ return 0;
+}
+
+static int des3_ablkcipher_setkey(struct crypto_ablkcipher *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 *flags = &cipher->base.crt_flags;
+ const u32 *K = (const u32 *)key;
+ u32 tmp[DES3_EDE_EXPKEY_WORDS];
+ int i, ret;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+ if (keylen != DES3_EDE_KEY_SIZE) {
+ *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_RES_BAD_KEY_LEN",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Checking key interdependency for weak key detection. */
+ if (unlikely(!((K[0] ^ K[2]) | (K[1] ^ K[3])) ||
+ !((K[2] ^ K[4]) | (K[3] ^ K[5]))) &&
+ (*flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+ *flags |= CRYPTO_TFM_RES_WEAK_KEY;
+ pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_REQ_WEAK_KEY",
+ __func__);
+ return -EINVAL;
+ }
+ for (i = 0; i < 3; i++) {
+ ret = des_ekey(tmp, key + i*DES_KEY_SIZE);
+ if (unlikely(ret == 0) && (*flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+ *flags |= CRYPTO_TFM_RES_WEAK_KEY;
+ pr_debug(DEV_DBG_NAME " [%s]: "
+ "CRYPTO_TFM_REQ_WEAK_KEY", __func__);
+ return -EINVAL;
+ }
+ }
+
+ memcpy(ctx->key, key, keylen);
+ ctx->keylen = keylen;
+
+ ctx->updated = 0;
+ return 0;
+}
+
+static int des3_setkey(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct cryp_ctx *ctx = crypto_tfm_ctx(tfm);
+ u32 *flags = &tfm->crt_flags;
+ const u32 *K = (const u32 *)key;
+ u32 tmp[DES3_EDE_EXPKEY_WORDS];
+ int i, ret;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ if (keylen != DES3_EDE_KEY_SIZE) {
+ *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_RES_BAD_KEY_LEN",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (unlikely(!((K[0] ^ K[2]) | (K[1] ^ K[3])) ||
+ !((K[2] ^ K[4]) | (K[3] ^ K[5]))) &&
+ (*flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+ *flags |= CRYPTO_TFM_RES_WEAK_KEY;
+ pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_REQ_WEAK_KEY",
+ __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = des_ekey(tmp, key + i*DES_KEY_SIZE);
+ if (unlikely(ret == 0) && (*flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+ *flags |= CRYPTO_TFM_RES_WEAK_KEY;
+ pr_debug(DEV_DBG_NAME " [%s]: "
+ "CRYPTO_TFM_REQ_WEAK_KEY", __func__);
+ return -EINVAL;
+ }
+ }
+
+ memcpy(ctx->key, key, keylen);
+ ctx->keylen = keylen;
+
+ ctx->updated = 0;
+ return 0;
+}
+
+static int cryp_hw_calculate(struct cryp_ctx *ctx)
+{
+ struct cryp_device_data *device_data;
+ int ret;
+
+ ret = cryp_get_device_data(ctx, &device_data);
+ if (ret)
+ goto out;
+
+ ret = cryp_enable_power(device_data->dev, device_data, false);
+ if (ret) {
+ dev_err(device_data->dev, "[%s]: "
+ "cryp_enable_power() failed!", __func__);
+ goto out;
+ }
+
+ if (hw_crypt_noxts(ctx, device_data))
+ pr_err("u8500_cryp:crypX: [%s]: hw_crypt_noxts() failed!",
+ __func__);
+
+out:
+ if (cryp_disable_power(device_data->dev, device_data, false))
+ dev_err(device_data->dev, "[%s]: "
+ "cryp_disable_power() failed!", __func__);
+ /* Release the device */
+ spin_lock(&device_data->ctx_lock);
+ device_data->current_ctx = NULL;
+ ctx->device = NULL;
+ spin_unlock(&device_data->ctx_lock);
+
+ /*
+ * The down_interruptible part for this semaphore is called in
+ * cryp_get_device_data.
+ */
+ up(&driver_data.device_allocation);
+
+ return ret;
+}
+
+static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+{
+ struct cryp_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->blocksize = crypto_tfm_alg_blocksize(tfm);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_ENCRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_AES_ECB;
+
+ ctx->indata = in;
+ ctx->outdata = out;
+ ctx->datalen = ctx->blocksize;
+
+ if (cryp_hw_calculate(ctx))
+ pr_err("u8500_cryp:crypX: [%s]: cryp_hw_calculate() failed!",
+ __func__);
+}
+
+static void aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+{
+ struct cryp_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->blocksize = crypto_tfm_alg_blocksize(tfm);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_DECRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_AES_ECB;
+
+ ctx->indata = in;
+ ctx->outdata = out;
+ ctx->datalen = ctx->blocksize;
+
+ if (cryp_hw_calculate(ctx))
+ pr_err("u8500_cryp:crypX: [%s]: cryp_hw_calculate() failed!",
+ __func__);
+}
+
+static void des_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+{
+ struct cryp_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->blocksize = crypto_tfm_alg_blocksize(tfm);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_ENCRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_DES_ECB;
+
+ ctx->indata = in;
+ ctx->outdata = out;
+ ctx->datalen = ctx->blocksize;
+
+ if (cryp_hw_calculate(ctx))
+ pr_err("u8500_cryp:crypX: [%s]: cryp_hw_calculate() failed!",
+ __func__);
+}
+
+static void des_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+{
+ struct cryp_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->blocksize = crypto_tfm_alg_blocksize(tfm);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_DECRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_DES_ECB;
+
+ ctx->indata = in;
+ ctx->outdata = out;
+ ctx->datalen = ctx->blocksize;
+
+ if (cryp_hw_calculate(ctx))
+ pr_err("u8500_cryp:crypX: [%s]: cryp_hw_calculate() failed!",
+ __func__);
+}
+
+static void des3_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+{
+ struct cryp_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->blocksize = crypto_tfm_alg_blocksize(tfm);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_ENCRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_TDES_ECB;
+
+ ctx->indata = in;
+ ctx->outdata = out;
+ ctx->datalen = ctx->blocksize;
+
+ if (cryp_hw_calculate(ctx))
+ pr_err("u8500_cryp:crypX: [%s]: cryp_hw_calculate() failed!",
+ __func__);
+}
+
+static void des3_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+{
+ struct cryp_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->blocksize = crypto_tfm_alg_blocksize(tfm);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_DECRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_TDES_ECB;
+
+ ctx->indata = in;
+ ctx->outdata = out;
+ ctx->datalen = ctx->blocksize;
+
+ if (cryp_hw_calculate(ctx))
+ pr_err("u8500_cryp:crypX: [%s]: cryp_hw_calculate() failed!",
+ __func__);
+}
+
+
+static int aes_ecb_encrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_ENCRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_AES_ECB;
+ ctx->blocksize = AES_BLOCK_SIZE;
+
+ if (cryp_mode == CRYP_MODE_DMA)
+ return ablk_dma_crypt(areq);
+
+ /* For everything except DMA, we run the non DMA version. */
+ return ablk_crypt(areq);
+}
+
+static int aes_ecb_decrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_DECRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_AES_ECB;
+ ctx->blocksize = AES_BLOCK_SIZE;
+
+ if (cryp_mode == CRYP_MODE_DMA)
+ return ablk_dma_crypt(areq);
+
+ /* For everything except DMA, we run the non DMA version. */
+ return ablk_crypt(areq);
+}
+
+static int aes_cbc_encrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 *flags = &cipher->base.crt_flags;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_ENCRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_AES_CBC;
+ ctx->blocksize = AES_BLOCK_SIZE;
+
+ /* Only DMA for ablkcipher, since givcipher not yet supported */
+ if ((cryp_mode == CRYP_MODE_DMA) &&
+ (*flags & CRYPTO_ALG_TYPE_ABLKCIPHER))
+ return ablk_dma_crypt(areq);
+
+ /* For everything except DMA, we run the non DMA version. */
+ return ablk_crypt(areq);
+}
+
+static int aes_cbc_decrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 *flags = &cipher->base.crt_flags;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_DECRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_AES_CBC;
+ ctx->blocksize = AES_BLOCK_SIZE;
+
+ /* Only DMA for ablkcipher, since givcipher not yet supported */
+ if ((cryp_mode == CRYP_MODE_DMA) &&
+ (*flags & CRYPTO_ALG_TYPE_ABLKCIPHER))
+ return ablk_dma_crypt(areq);
+
+ /* For everything except DMA, we run the non DMA version. */
+ return ablk_crypt(areq);
+}
+
+static int aes_ctr_encrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 *flags = &cipher->base.crt_flags;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_ENCRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_AES_CTR;
+ ctx->blocksize = AES_BLOCK_SIZE;
+
+ /* Only DMA for ablkcipher, since givcipher not yet supported */
+ if ((cryp_mode == CRYP_MODE_DMA) &&
+ (*flags & CRYPTO_ALG_TYPE_ABLKCIPHER))
+ return ablk_dma_crypt(areq);
+
+ /* For everything except DMA, we run the non DMA version. */
+ return ablk_crypt(areq);
+}
+
+static int aes_ctr_decrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 *flags = &cipher->base.crt_flags;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_DECRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_AES_CTR;
+ ctx->blocksize = AES_BLOCK_SIZE;
+
+ /* Only DMA for ablkcipher, since givcipher not yet supported */
+ if ((cryp_mode == CRYP_MODE_DMA) &&
+ (*flags & CRYPTO_ALG_TYPE_ABLKCIPHER))
+ return ablk_dma_crypt(areq);
+
+ /* For everything except DMA, we run the non DMA version. */
+ return ablk_crypt(areq);
+}
+
+static int des_ecb_encrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_ENCRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_DES_ECB;
+ ctx->blocksize = DES_BLOCK_SIZE;
+
+ /**
+ * Run the non DMA version also for DMA, since DMA is currently not
+ * working for DES.
+ */
+ return ablk_crypt(areq);
+}
+
+static int des_ecb_decrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_DECRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_DES_ECB;
+ ctx->blocksize = DES_BLOCK_SIZE;
+
+ /**
+ * Run the non DMA version also for DMA, since DMA is currently not
+ * working for DES.
+ */
+ return ablk_crypt(areq);
+}
+
+static int des_cbc_encrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 *flags = &cipher->base.crt_flags;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_ENCRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_DES_CBC;
+ ctx->blocksize = DES_BLOCK_SIZE;
+
+ /* Only DMA for ablkcipher, since givcipher not yet supported */
+ if ((cryp_mode == CRYP_MODE_DMA) &&
+ (*flags & CRYPTO_ALG_TYPE_ABLKCIPHER))
+ return ablk_dma_crypt(areq);
+
+ /**
+ * Run the non DMA version also for DMA, since DMA is currently not
+ * working for DES.
+ */
+ return ablk_crypt(areq);
+}
+
+static int des_cbc_decrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 *flags = &cipher->base.crt_flags;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_DECRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_DES_CBC;
+ ctx->blocksize = DES_BLOCK_SIZE;
+
+ /* Only DMA for ablkcipher, since givcipher not yet supported */
+ if ((cryp_mode == CRYP_MODE_DMA) &&
+ (*flags & CRYPTO_ALG_TYPE_ABLKCIPHER))
+ return ablk_dma_crypt(areq);
+
+ /**
+ * Run the non DMA version also for DMA, since DMA is currently not
+ * working for DES.
+ */
+ return ablk_crypt(areq);
+}
+
+static int des3_ecb_encrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_ENCRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_TDES_ECB;
+ ctx->blocksize = DES3_EDE_BLOCK_SIZE;
+
+ /**
+ * Run the non DMA version also for DMA, since DMA is currently not
+ * working for DES.
+ */
+ return ablk_crypt(areq);
+}
+
+static int des3_ecb_decrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_DECRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_TDES_ECB;
+ ctx->blocksize = DES3_EDE_BLOCK_SIZE;
+
+ /**
+ * Run the non DMA version also for DMA, since DMA is currently not
+ * working for DES.
+ */
+ return ablk_crypt(areq);
+}
+
+static int des3_cbc_encrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 *flags = &cipher->base.crt_flags;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_ENCRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_TDES_CBC;
+ ctx->blocksize = DES3_EDE_BLOCK_SIZE;
+
+ /* Only DMA for ablkcipher, since givcipher not yet supported */
+ if ((cryp_mode == CRYP_MODE_DMA) &&
+ (*flags & CRYPTO_ALG_TYPE_ABLKCIPHER))
+ return ablk_dma_crypt(areq);
+
+ /**
+ * Run the non DMA version also for DMA, since DMA is currently not
+ * working for DES.
+ */
+ return ablk_crypt(areq);
+}
+
+static int des3_cbc_decrypt(struct ablkcipher_request *areq)
+{
+ struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+ struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 *flags = &cipher->base.crt_flags;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ ctx->config.encrypt_or_decrypt = CRYP_ALGORITHM_DECRYPT;
+ ctx->config.algo_mode = CRYP_ALGO_TDES_CBC;
+ ctx->blocksize = DES3_EDE_BLOCK_SIZE;
+
+ /* Only DMA for ablkcipher, since givcipher not yet supported */
+ if ((cryp_mode == CRYP_MODE_DMA) &&
+ (*flags & CRYPTO_ALG_TYPE_ABLKCIPHER))
+ return ablk_dma_crypt(areq);
+
+ /**
+ * Run the non DMA version also for DMA, since DMA is currently not
+ * working for DES.
+ */
+ return ablk_crypt(areq);
+}
+
+/**
+ * struct crypto_alg aes_alg
+ */
+static struct crypto_alg aes_alg = {
+ .cra_name = "aes",
+ .cra_driver_name = "aes-u8500",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct cryp_ctx),
+ .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(aes_alg.cra_list),
+ .cra_u = {
+ .cipher = {
+ .cia_min_keysize = AES_MIN_KEY_SIZE,
+ .cia_max_keysize = AES_MAX_KEY_SIZE,
+ .cia_setkey = aes_setkey,
+ .cia_encrypt = aes_encrypt,
+ .cia_decrypt = aes_decrypt
+ }
+ }
+};
+
+/**
+ * struct crypto_alg des_alg
+ */
+static struct crypto_alg des_alg = {
+ .cra_name = "des",
+ .cra_driver_name = "des-u8500",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct cryp_ctx),
+ .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(des_alg.cra_list),
+ .cra_u = {
+ .cipher = {
+ .cia_min_keysize = DES_KEY_SIZE,
+ .cia_max_keysize = DES_KEY_SIZE,
+ .cia_setkey = des_setkey,
+ .cia_encrypt = des_encrypt,
+ .cia_decrypt = des_decrypt
+ }
+ }
+};
+
+/**
+ * struct crypto_alg des3_alg
+ */
+static struct crypto_alg des3_alg = {
+ .cra_name = "des3_ede",
+ .cra_driver_name = "des3_ede-u8500",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct cryp_ctx),
+ .cra_alignmask = 3,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(des3_alg.cra_list),
+ .cra_u = {
+ .cipher = {
+ .cia_min_keysize = DES3_EDE_KEY_SIZE,
+ .cia_max_keysize = DES3_EDE_KEY_SIZE,
+ .cia_setkey = des3_setkey,
+ .cia_encrypt = des3_encrypt,
+ .cia_decrypt = des3_decrypt
+ }
+ }
+};
+
+/**
+ * struct crypto_alg aes_ecb_alg
+ */
+static struct crypto_alg aes_ecb_alg = {
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = "ecb-aes-u8500",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct cryp_ctx),
+ .cra_alignmask = 3,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(aes_ecb_alg.cra_list),
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = aes_ablkcipher_setkey,
+ .encrypt = aes_ecb_encrypt,
+ .decrypt = aes_ecb_decrypt,
+ }
+ }
+};
+
+/**
+ * struct crypto_alg aes_cbc_alg
+ */
+static struct crypto_alg aes_cbc_alg = {
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "cbc-aes-u8500",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct cryp_ctx),
+ .cra_alignmask = 3,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(aes_cbc_alg.cra_list),
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = aes_ablkcipher_setkey,
+ .encrypt = aes_cbc_encrypt,
+ .decrypt = aes_cbc_decrypt,
+ .ivsize = AES_BLOCK_SIZE,
+ }
+ }
+};
+
+/**
+ * struct crypto_alg aes_ctr_alg
+ */
+static struct crypto_alg aes_ctr_alg = {
+ .cra_name = "ctr(aes)",
+ .cra_driver_name = "ctr-aes-u8500",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct cryp_ctx),
+ .cra_alignmask = 3,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(aes_ctr_alg.cra_list),
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = aes_ablkcipher_setkey,
+ .encrypt = aes_ctr_encrypt,
+ .decrypt = aes_ctr_decrypt,
+ .ivsize = AES_BLOCK_SIZE,
+ }
+ }
+};
+
+/**
+ * struct crypto_alg des_ecb_alg
+ */
+static struct crypto_alg des_ecb_alg = {
+ .cra_name = "ecb(des)",
+ .cra_driver_name = "ecb-des-u8500",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct cryp_ctx),
+ .cra_alignmask = 3,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(des_ecb_alg.cra_list),
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .setkey = des_ablkcipher_setkey,
+ .encrypt = des_ecb_encrypt,
+ .decrypt = des_ecb_decrypt,
+ }
+ }
+};
+
+/**
+ * struct crypto_alg des_cbc_alg
+ */
+static struct crypto_alg des_cbc_alg = {
+ .cra_name = "cbc(des)",
+ .cra_driver_name = "cbc-des-u8500",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct cryp_ctx),
+ .cra_alignmask = 3,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(des_cbc_alg.cra_list),
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .setkey = des_ablkcipher_setkey,
+ .encrypt = des_cbc_encrypt,
+ .decrypt = des_cbc_decrypt,
+ .ivsize = DES_BLOCK_SIZE,
+ }
+ }
+};
+
+/**
+ * struct crypto_alg des3_ecb_alg
+ */
+static struct crypto_alg des3_ecb_alg = {
+ .cra_name = "ecb(des3_ede)",
+ .cra_driver_name = "ecb-des3_ede-u8500",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct cryp_ctx),
+ .cra_alignmask = 3,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(des3_ecb_alg.cra_list),
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .setkey = des3_ablkcipher_setkey,
+ .encrypt = des3_ecb_encrypt,
+ .decrypt = des3_ecb_decrypt,
+ }
+ }
+};
+
+/**
+ * struct crypto_alg des3_cbc_alg
+ */
+static struct crypto_alg des3_cbc_alg = {
+ .cra_name = "cbc(des3_ede)",
+ .cra_driver_name = "cbc-des3_ede-u8500",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct cryp_ctx),
+ .cra_alignmask = 3,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(des3_cbc_alg.cra_list),
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .setkey = des3_ablkcipher_setkey,
+ .encrypt = des3_cbc_encrypt,
+ .decrypt = des3_cbc_decrypt,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ }
+ }
+};
+
+/**
+ * struct crypto_alg *u8500_cryp_algs[] -
+ */
+static struct crypto_alg *u8500_cryp_algs[] = {
+ &aes_alg,
+ &des_alg,
+ &des3_alg,
+ &aes_ecb_alg,
+ &aes_cbc_alg,
+ &aes_ctr_alg,
+ &des_ecb_alg,
+ &des_cbc_alg,
+ &des3_ecb_alg,
+ &des3_cbc_alg
+};
+
+/**
+ * cryp_algs_register_all -
+ */
+static int cryp_algs_register_all(void)
+{
+ int ret;
+ int i;
+ int count;
+
+ pr_debug("[%s]", __func__);
+
+ for (i = 0; i < ARRAY_SIZE(u8500_cryp_algs); i++) {
+ ret = crypto_register_alg(u8500_cryp_algs[i]);
+ if (ret) {
+ count = i;
+ pr_err("[%s] alg registration failed",
+ u8500_cryp_algs[i]->cra_driver_name);
+ goto unreg;
+ }
+ }
+ return 0;
+unreg:
+ for (i = 0; i < count; i++)
+ crypto_unregister_alg(u8500_cryp_algs[i]);
+ return ret;
+}
+
+/**
+ * cryp_algs_unregister_all -
+ */
+static void cryp_algs_unregister_all(void)
+{
+ int i;
+
+ pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+ for (i = 0; i < ARRAY_SIZE(u8500_cryp_algs); i++)
+ crypto_unregister_alg(u8500_cryp_algs[i]);
+}
+
+static int u8500_cryp_probe(struct platform_device *pdev)
+{
+ int ret;
+ int cryp_error = 0;
+ struct resource *res = NULL;
+ struct resource *res_irq = NULL;
+ struct cryp_device_data *device_data;
+ struct cryp_protection_config prot = {
+ .privilege_access = CRYP_STATE_ENABLE
+ };
+ struct device *dev = &pdev->dev;
+
+ dev_dbg(dev, "[%s]", __func__);
+ device_data = kzalloc(sizeof(struct cryp_device_data), GFP_KERNEL);
+ if (!device_data) {
+ dev_err(dev, "[%s]: kzalloc() failed!", __func__);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ device_data->dev = dev;
+ device_data->current_ctx = NULL;
+
+ /* Grab the DMA configuration from platform data. */
+ mem_to_engine = &((struct cryp_platform_data *)
+ dev->platform_data)->mem_to_engine;
+ engine_to_mem = &((struct cryp_platform_data *)
+ dev->platform_data)->engine_to_mem;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "[%s]: platform_get_resource() failed",
+ __func__);
+ ret = -ENODEV;
+ goto out_kfree;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (res == NULL) {
+ dev_err(dev, "[%s]: request_mem_region() failed",
+ __func__);
+ ret = -EBUSY;
+ goto out_kfree;
+ }
+
+ device_data->base = ioremap(res->start, resource_size(res));
+ if (!device_data->base) {
+ dev_err(dev, "[%s]: ioremap failed!", __func__);
+ ret = -ENOMEM;
+ goto out_free_mem;
+ }
+
+ spin_lock_init(&device_data->ctx_lock);
+ mutex_init(&device_data->power_state_mutex);
+
+ /* Enable power for CRYP hardware block */
+ device_data->pwr_regulator = regulator_get(&pdev->dev, "v-ape");
+ if (IS_ERR(device_data->pwr_regulator)) {
+ dev_err(dev, "[%s]: could not get cryp regulator", __func__);
+ ret = PTR_ERR(device_data->pwr_regulator);
+ device_data->pwr_regulator = NULL;
+ goto out_unmap;
+ }
+
+ /* Enable the clk for CRYP hardware block */
+ device_data->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(device_data->clk)) {
+ dev_err(dev, "[%s]: clk_get() failed!", __func__);
+ ret = PTR_ERR(device_data->clk);
+ goto out_regulator;
+ }
+
+ /* Enable device power (and clock) */
+ ret = cryp_enable_power(device_data->dev, device_data, false);
+ if (ret) {
+ dev_err(dev, "[%s]: cryp_enable_power() failed!", __func__);
+ goto out_clk;
+ }
+
+ cryp_error = cryp_check(device_data);
+ if (cryp_error != 0) {
+ dev_err(dev, "[%s]: cryp_init() failed!", __func__);
+ ret = -EINVAL;
+ goto out_power;
+ }
+
+ cryp_error = cryp_configure_protection(device_data, &prot);
+ if (cryp_error != 0) {
+ dev_err(dev, "[%s]: cryp_configure_protection() failed!",
+ __func__);
+ ret = -EINVAL;
+ goto out_power;
+ }
+
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res_irq) {
+ dev_err(dev, "[%s]: IORESOURCE_IRQ unavailable",
+ __func__);
+ goto out_power;
+ }
+
+ ret = request_irq(res_irq->start,
+ cryp_interrupt_handler,
+ 0,
+ "cryp1",
+ device_data);
+ if (ret) {
+ dev_err(dev, "[%s]: Unable to request IRQ", __func__);
+ goto out_power;
+ }
+
+ init_completion(&device_data->cryp_irq_complete);
+
+ if (cryp_mode == CRYP_MODE_DMA)
+ cryp_dma_setup_channel(device_data, dev);
+
+ platform_set_drvdata(pdev, device_data);
+
+ /* Put the new device into the device list... */
+ klist_add_tail(&device_data->list_node, &driver_data.device_list);
+
+ /* ... and signal that a new device is available. */
+ up(&driver_data.device_allocation);
+
+ ret = cryp_algs_register_all();
+ if (ret) {
+ dev_err(dev, "[%s]: cryp_algs_register_all() failed!",
+ __func__);
+ goto out_power;
+ }
+
+ if (cryp_disable_power(&pdev->dev, device_data, false))
+ dev_err(dev, "[%s]: cryp_disable_power() failed!", __func__);
+
+ return 0;
+
+out_power:
+ cryp_disable_power(&pdev->dev, device_data, false);
+
+out_clk:
+ clk_put(device_data->clk);
+
+out_regulator:
+ regulator_put(device_data->pwr_regulator);
+
+out_unmap:
+ iounmap(device_data->base);
+
+out_free_mem:
+ release_mem_region(res->start, resource_size(res));
+
+out_kfree:
+ kfree(device_data);
+out:
+ return ret;
+}
+
+static int u8500_cryp_remove(struct platform_device *pdev)
+{
+ struct resource *res = NULL;
+ struct resource *res_irq = NULL;
+ struct cryp_device_data *device_data;
+
+ dev_dbg(&pdev->dev, "[%s]", __func__);
+ device_data = platform_get_drvdata(pdev);
+ if (!device_data) {
+ dev_err(&pdev->dev, "[%s]: platform_get_drvdata() failed!",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /* Try to decrease the number of available devices. */
+ if (down_trylock(&driver_data.device_allocation))
+ return -EBUSY;
+
+ /* Check that the device is free */
+ spin_lock(&device_data->ctx_lock);
+ /* current_ctx allocates a device, NULL = unallocated */
+ if (device_data->current_ctx) {
+ /* The device is busy */
+ spin_unlock(&device_data->ctx_lock);
+ /* Return the device to the pool. */
+ up(&driver_data.device_allocation);
+ return -EBUSY;
+ }
+
+ spin_unlock(&device_data->ctx_lock);
+
+ /* Remove the device from the list */
+ if (klist_node_attached(&device_data->list_node))
+ klist_remove(&device_data->list_node);
+
+ /* If this was the last device, remove the services */
+ if (list_empty(&driver_data.device_list.k_list))
+ cryp_algs_unregister_all();
+
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res_irq)
+ dev_err(&pdev->dev, "[%s]: IORESOURCE_IRQ, unavailable",
+ __func__);
+ else {
+ disable_irq(res_irq->start);
+ free_irq(res_irq->start, device_data);
+ }
+
+ if (cryp_disable_power(&pdev->dev, device_data, false))
+ dev_err(&pdev->dev, "[%s]: cryp_disable_power() failed",
+ __func__);
+
+ clk_put(device_data->clk);
+ regulator_put(device_data->pwr_regulator);
+
+ iounmap(device_data->base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ kfree(device_data);
+
+ return 0;
+}
+
+static void u8500_cryp_shutdown(struct platform_device *pdev)
+{
+ struct resource *res_irq = NULL;
+ struct cryp_device_data *device_data;
+
+ dev_dbg(&pdev->dev, "[%s]", __func__);
+
+ device_data = platform_get_drvdata(pdev);
+ if (!device_data) {
+ dev_err(&pdev->dev, "[%s]: platform_get_drvdata() failed!",
+ __func__);
+ return;
+ }
+
+ /* Check that the device is free */
+ spin_lock(&device_data->ctx_lock);
+ /* current_ctx allocates a device, NULL = unallocated */
+ if (!device_data->current_ctx) {
+ if (down_trylock(&driver_data.device_allocation))
+ dev_dbg(&pdev->dev, "[%s]: Cryp still in use!"
+ "Shutting down anyway...", __func__);
+ /**
+ * (Allocate the device)
+ * Need to set this to non-null (dummy) value,
+ * to avoid usage if context switching.
+ */
+ device_data->current_ctx++;
+ }
+ spin_unlock(&device_data->ctx_lock);
+
+ /* Remove the device from the list */
+ if (klist_node_attached(&device_data->list_node))
+ klist_remove(&device_data->list_node);
+
+ /* If this was the last device, remove the services */
+ if (list_empty(&driver_data.device_list.k_list))
+ cryp_algs_unregister_all();
+
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res_irq)
+ dev_err(&pdev->dev, "[%s]: IORESOURCE_IRQ, unavailable",
+ __func__);
+ else {
+ disable_irq(res_irq->start);
+ free_irq(res_irq->start, device_data);
+ }
+
+ if (cryp_disable_power(&pdev->dev, device_data, false))
+ dev_err(&pdev->dev, "[%s]: cryp_disable_power() failed",
+ __func__);
+
+}
+
+static int u8500_cryp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int ret;
+ struct cryp_device_data *device_data;
+ struct resource *res_irq;
+ struct cryp_ctx *temp_ctx = NULL;
+
+ dev_dbg(&pdev->dev, "[%s]", __func__);
+
+ /* Handle state? */
+ device_data = platform_get_drvdata(pdev);
+ if (!device_data) {
+ dev_err(&pdev->dev, "[%s]: platform_get_drvdata() failed!",
+ __func__);
+ return -ENOMEM;
+ }
+
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res_irq)
+ dev_err(&pdev->dev, "[%s]: IORESOURCE_IRQ, unavailable",
+ __func__);
+ else
+ disable_irq(res_irq->start);
+
+ spin_lock(&device_data->ctx_lock);
+ if (!device_data->current_ctx)
+ device_data->current_ctx++;
+ spin_unlock(&device_data->ctx_lock);
+
+ if (device_data->current_ctx == ++temp_ctx) {
+ if (down_interruptible(&driver_data.device_allocation))
+ dev_dbg(&pdev->dev, "[%s]: down_interruptible() "
+ "failed", __func__);
+ ret = cryp_disable_power(&pdev->dev, device_data, false);
+
+ } else
+ ret = cryp_disable_power(&pdev->dev, device_data, true);
+
+ if (ret)
+ dev_err(&pdev->dev, "[%s]: cryp_disable_power()", __func__);
+
+ return ret;
+}
+
+static int u8500_cryp_resume(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct cryp_device_data *device_data;
+ struct resource *res_irq;
+ struct cryp_ctx *temp_ctx = NULL;
+
+ dev_dbg(&pdev->dev, "[%s]", __func__);
+
+ device_data = platform_get_drvdata(pdev);
+ if (!device_data) {
+ dev_err(&pdev->dev, "[%s]: platform_get_drvdata() failed!",
+ __func__);
+ return -ENOMEM;
+ }
+
+ spin_lock(&device_data->ctx_lock);
+ if (device_data->current_ctx == ++temp_ctx)
+ device_data->current_ctx = NULL;
+ spin_unlock(&device_data->ctx_lock);
+
+
+ if (!device_data->current_ctx)
+ up(&driver_data.device_allocation);
+ else
+ ret = cryp_enable_power(&pdev->dev, device_data, true);
+
+ if (ret)
+ dev_err(&pdev->dev, "[%s]: cryp_enable_power() failed!",
+ __func__);
+ else {
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res_irq)
+ enable_irq(res_irq->start);
+ }
+
+ return ret;
+}
+
+static struct platform_driver cryp_driver = {
+ .probe = u8500_cryp_probe,
+ .remove = u8500_cryp_remove,
+ .shutdown = u8500_cryp_shutdown,
+ .suspend = u8500_cryp_suspend,
+ .resume = u8500_cryp_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "cryp1"
+ }
+};
+
+static int __init u8500_cryp_mod_init(void)
+{
+ pr_debug("[%s] is called!", __func__);
+
+ klist_init(&driver_data.device_list, NULL, NULL);
+ /* Initialize the semaphore to 0 devices (locked state) */
+ sema_init(&driver_data.device_allocation, 0);
+ return platform_driver_register(&cryp_driver);
+}
+
+static void __exit u8500_cryp_mod_fini(void)
+{
+ pr_debug("[%s] is called!", __func__);
+ platform_driver_unregister(&cryp_driver);
+ return;
+}
+
+module_init(u8500_cryp_mod_init);
+module_exit(u8500_cryp_mod_fini);
+
+module_param(cryp_mode, int, 0);
+
+MODULE_DESCRIPTION("Driver for ST-Ericsson U8500 CRYP crypto engine.");
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/crypto/ux500/cryp/cryp_irq.c b/drivers/crypto/ux500/cryp/cryp_irq.c
new file mode 100644
index 00000000000..eacff226aa8
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp_irq.c
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/device.h>
+
+#include "cryp.h"
+#include "cryp_p.h"
+#include "cryp_irq.h"
+#include "cryp_irqp.h"
+
+void cryp_enable_irq_src(struct cryp_device_data *device_data, u32 irq_src)
+{
+ u32 i;
+
+ dev_dbg(device_data->dev, "[%s]", __func__);
+
+ i = readl(&device_data->base->imsc);
+ set_bit(irq_src, (void *)&i);
+ writel(i, &device_data->base->imsc);
+}
+
+void cryp_disable_irq_src(struct cryp_device_data *device_data, u32 irq_src)
+{
+ u32 i;
+
+ dev_dbg(device_data->dev, "[%s]", __func__);
+
+ i = readl(&device_data->base->imsc);
+ clear_bit(irq_src, (void *)&i);
+ writel(i, &device_data->base->imsc);
+}
+
+bool cryp_pending_irq_src(struct cryp_device_data *device_data, u32 irq_src)
+{
+ return (readl(&device_data->base->mis) & irq_src) > 0;
+}
diff --git a/drivers/crypto/ux500/cryp/cryp_irq.h b/drivers/crypto/ux500/cryp/cryp_irq.h
new file mode 100644
index 00000000000..5a7837f1b8f
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp_irq.h
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef _CRYP_IRQ_H_
+#define _CRYP_IRQ_H_
+
+#include "cryp.h"
+
+enum cryp_irq_src_id {
+ CRYP_IRQ_SRC_INPUT_FIFO = 0x1,
+ CRYP_IRQ_SRC_OUTPUT_FIFO = 0x2,
+ CRYP_IRQ_SRC_ALL = 0x3
+};
+
+/**
+ * M0 Funtions
+ */
+void cryp_enable_irq_src(struct cryp_device_data *device_data, u32 irq_src);
+
+void cryp_disable_irq_src(struct cryp_device_data *device_data, u32 irq_src);
+
+bool cryp_pending_irq_src(struct cryp_device_data *device_data, u32 irq_src);
+
+#endif /* _CRYP_IRQ_H_ */
diff --git a/drivers/crypto/ux500/cryp/cryp_irqp.h b/drivers/crypto/ux500/cryp/cryp_irqp.h
new file mode 100644
index 00000000000..5b60f887d02
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp_irqp.h
@@ -0,0 +1,125 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef __CRYP_IRQP_H_
+#define __CRYP_IRQP_H_
+
+#include "cryp_irq.h"
+
+/**
+ *
+ * CRYP Registers - Offset mapping
+ * +-----------------+
+ * 00h | CRYP_CR | Configuration register
+ * +-----------------+
+ * 04h | CRYP_SR | Status register
+ * +-----------------+
+ * 08h | CRYP_DIN | Data In register
+ * +-----------------+
+ * 0ch | CRYP_DOUT | Data out register
+ * +-----------------+
+ * 10h | CRYP_DMACR | DMA control register
+ * +-----------------+
+ * 14h | CRYP_IMSC | IMSC
+ * +-----------------+
+ * 18h | CRYP_RIS | Raw interrupt status
+ * +-----------------+
+ * 1ch | CRYP_MIS | Masked interrupt status.
+ * +-----------------+
+ * Key registers
+ * IVR registers
+ * Peripheral
+ * Cell IDs
+ *
+ * Refer data structure for other register map
+ */
+
+/**
+ * struct cryp_register
+ * @cr - Configuration register
+ * @status - Status register
+ * @din - Data input register
+ * @din_size - Data input size register
+ * @dout - Data output register
+ * @dout_size - Data output size register
+ * @dmacr - Dma control register
+ * @imsc - Interrupt mask set/clear register
+ * @ris - Raw interrupt status
+ * @mis - Masked interrupt statu register
+ * @key_1_l - Key register 1 L
+ * @key_1_r - Key register 1 R
+ * @key_2_l - Key register 2 L
+ * @key_2_r - Key register 2 R
+ * @key_3_l - Key register 3 L
+ * @key_3_r - Key register 3 R
+ * @key_4_l - Key register 4 L
+ * @key_4_r - Key register 4 R
+ * @init_vect_0_l - init vector 0 L
+ * @init_vect_0_r - init vector 0 R
+ * @init_vect_1_l - init vector 1 L
+ * @init_vect_1_r - init vector 1 R
+ * @cryp_unused1 - unused registers
+ * @itcr - Integration test control register
+ * @itip - Integration test input register
+ * @itop - Integration test output register
+ * @cryp_unused2 - unused registers
+ * @periphId0 - FE0 CRYP Peripheral Identication Register
+ * @periphId1 - FE4
+ * @periphId2 - FE8
+ * @periphId3 - FEC
+ * @pcellId0 - FF0 CRYP PCell Identication Register
+ * @pcellId1 - FF4
+ * @pcellId2 - FF8
+ * @pcellId3 - FFC
+ */
+struct cryp_register {
+ u32 cr; /* Configuration register */
+ u32 status; /* Status register */
+ u32 din; /* Data input register */
+ u32 din_size; /* Data input size register */
+ u32 dout; /* Data output register */
+ u32 dout_size; /* Data output size register */
+ u32 dmacr; /* Dma control register */
+ u32 imsc; /* Interrupt mask set/clear register */
+ u32 ris; /* Raw interrupt status */
+ u32 mis; /* Masked interrupt statu register */
+
+ u32 key_1_l; /*Key register 1 L */
+ u32 key_1_r; /*Key register 1 R */
+ u32 key_2_l; /*Key register 2 L */
+ u32 key_2_r; /*Key register 2 R */
+ u32 key_3_l; /*Key register 3 L */
+ u32 key_3_r; /*Key register 3 R */
+ u32 key_4_l; /*Key register 4 L */
+ u32 key_4_r; /*Key register 4 R */
+
+ u32 init_vect_0_l; /*init vector 0 L */
+ u32 init_vect_0_r; /*init vector 0 R */
+ u32 init_vect_1_l; /*init vector 1 L */
+ u32 init_vect_1_r; /*init vector 1 R */
+
+ u32 cryp_unused1[(0x80 - 0x58) / sizeof(u32)]; /* unused registers */
+ u32 itcr; /*Integration test control register */
+ u32 itip; /*Integration test input register */
+ u32 itop; /*Integration test output register */
+ u32 cryp_unused2[(0xFE0 - 0x8C) / sizeof(u32)]; /* unused registers */
+
+ u32 periphId0; /* FE0 CRYP Peripheral Identication Register */
+ u32 periphId1; /* FE4 */
+ u32 periphId2; /* FE8 */
+ u32 periphId3; /* FEC */
+
+ u32 pcellId0; /* FF0 CRYP PCell Identication Register */
+ u32 pcellId1; /* FF4 */
+ u32 pcellId2; /* FF8 */
+ u32 pcellId3; /* FFC */
+};
+
+#endif
diff --git a/drivers/crypto/ux500/cryp/cryp_p.h b/drivers/crypto/ux500/cryp/cryp_p.h
new file mode 100644
index 00000000000..966de4633cc
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp_p.h
@@ -0,0 +1,113 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef _CRYP_P_H_
+#define _CRYP_P_H_
+
+#include <linux/io.h>
+#include <linux/bitops.h>
+
+#include "cryp.h"
+#include "cryp_irqp.h"
+
+/**
+ * Generic Macros
+ */
+#define CRYP_SET_BITS(reg_name, mask) \
+ writel((readl(reg_name) | mask), reg_name)
+
+#define CRYP_WRITE_BIT(reg_name, val, mask) \
+ writel(((readl(reg_name) & ~(mask)) | ((val) & (mask))), reg_name)
+
+#define CRYP_TEST_BITS(reg_name, val) \
+ (readl(reg_name) & (val))
+
+#define CRYP_PUT_BITS(reg, val, shift, mask) \
+ writel(((readl(reg) & ~(mask)) | \
+ (((u32)val << shift) & (mask))), reg)
+
+/**
+ * CRYP specific Macros
+ */
+#define CRYP_PERIPHERAL_ID0 0xE3
+#define CRYP_PERIPHERAL_ID1 0x05
+#define CRYP_PERIPHERAL_ID2 0x28
+#define CRYP_PERIPHERAL_ID3 0x00
+
+#define CRYP_PCELL_ID0 0x0D
+#define CRYP_PCELL_ID1 0xF0
+#define CRYP_PCELL_ID2 0x05
+#define CRYP_PCELL_ID3 0xB1
+
+/**
+ * CRYP register default values
+ */
+#define MAX_DEVICE_SUPPORT 2
+#define CRYP_CR_DEFAULT 0x0002
+#define CRYP_CR_FFLUSH BIT(14)
+#define CRYP_DMACR_DEFAULT 0x0
+#define CRYP_IMSC_DEFAULT 0x0
+#define CRYP_DIN_DEFAULT 0x0
+#define CRYP_DOUT_DEFAULT 0x0
+#define CRYP_KEY_DEFAULT 0x0
+#define CRYP_INIT_VECT_DEFAULT 0x0
+
+/**
+ * CRYP Control register specific mask
+ */
+#define CRYP_SECURE_MASK BIT(0)
+#define CRYP_PRLG_MASK BIT(1)
+#define CRYP_ENC_DEC_MASK BIT(2)
+#define CRYP_SR_BUSY_MASK BIT(4)
+#define CRYP_KEY_ACCESS_MASK BIT(10)
+#define CRYP_KSE_MASK BIT(11)
+#define CRYP_START_MASK BIT(12)
+#define CRYP_INIT_MASK BIT(13)
+#define CRYP_FIFO_FLUSH_MASK BIT(14)
+#define CRYP_CRYPEN_MASK BIT(15)
+#define CRYP_INFIFO_READY_MASK (BIT(0) | BIT(1))
+#define CRYP_ALGOMODE_MASK (BIT(5) | BIT(4) | BIT(3))
+#define CRYP_DATA_TYPE_MASK (BIT(7) | BIT(6))
+#define CRYP_KEY_SIZE_MASK (BIT(9) | BIT(8))
+
+/**
+ * Bit position used while setting bits in register
+ */
+#define CRYP_PRLG_POS 1
+#define CRYP_ENC_DEC_POS 2
+#define CRYP_ALGOMODE_POS 3
+#define CRYP_SR_BUSY_POS 4
+#define CRYP_DATA_TYPE_POS 6
+#define CRYP_KEY_SIZE_POS 8
+#define CRYP_KEY_ACCESS_POS 10
+#define CRYP_KSE_POS 11
+#define CRYP_START_POS 12
+#define CRYP_INIT_POS 13
+#define CRYP_CRYPEN_POS 15
+
+/**
+ * CRYP Status register
+ */
+#define CRYP_BUSY_STATUS_MASK BIT(4)
+
+/**
+ * CRYP PCRs------PC_NAND control register
+ * BIT_MASK
+ */
+#define CRYP_DMA_REQ_MASK (BIT(1) | BIT(0))
+#define CRYP_DMA_REQ_MASK_POS 0
+
+
+struct cryp_system_context {
+ /* CRYP Register structure */
+ struct cryp_register *p_cryp_reg[MAX_DEVICE_SUPPORT];
+};
+
+#endif
diff --git a/drivers/crypto/ux500/hash/Makefile b/drivers/crypto/ux500/hash/Makefile
new file mode 100644
index 00000000000..6194da8eec8
--- /dev/null
+++ b/drivers/crypto/ux500/hash/Makefile
@@ -0,0 +1,11 @@
+#
+# Copyright (C) ST-Ericsson SA 2010
+# Author: Shujuan Chen (shujuan.chen@stericsson.com)
+# License terms: GNU General Public License (GPL) version 2
+#
+ifdef CONFIG_CRYPTO_DEV_UX500_DEBUG
+CFLAGS_hash_core.o := -DDEBUG
+endif
+
+obj-$(CONFIG_CRYPTO_DEV_UX500_HASH) += u8500_hash.o
+u8500_hash-objs := hash_core.o
diff --git a/drivers/crypto/ux500/hash/hash_alg.h b/drivers/crypto/ux500/hash/hash_alg.h
new file mode 100644
index 00000000000..1c3dd5705fb
--- /dev/null
+++ b/drivers/crypto/ux500/hash/hash_alg.h
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen (shujuan.chen@stericsson.com)
+ * Author: Joakim Bech (joakim.xx.bech@stericsson.com)
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#ifndef _HASH_ALG_H
+#define _HASH_ALG_H
+
+#include <mach/hcl_defs.h>
+
+/* Number of bytes the message digest */
+#define HASH_MSG_DIGEST_SIZE 32
+#define HASH_BLOCK_SIZE 64
+#define HASH_SHA1_DIGEST_SIZE 20
+#define HASH_SHA2_DIGEST_SIZE 32
+
+/* Version defines */
+#define HASH_HCL_VERSION_ID 1
+#define HASH_HCL_MAJOR_ID 2
+#define HASH_HCL_MINOR_ID 1
+
+#define MAX_HASH_DEVICE 2
+
+/* Maximum value of the length's high word */
+#define HASH_HIGH_WORD_MAX_VAL 0xFFFFFFFFUL
+
+/* Power on Reset values HASH registers */
+#define HASH_RESET_CONTROL_REG_VALUE 0x0
+#define HASH_RESET_START_REG_VALUE 0x0
+
+/* Number of context swap registers */
+#define HASH_CSR_COUNT 52
+
+#define HASH_RESET_CSRX_REG_VALUE 0x0
+#define HASH_RESET_CSFULL_REG_VALUE 0x0
+#define HASH_RESET_CSDATAIN_REG_VALUE 0x0
+
+#define HASH_RESET_INDEX_VAL 0x0
+#define HASH_RESET_BIT_INDEX_VAL 0x0
+#define HASH_RESET_BUFFER_VAL 0x0
+#define HASH_RESET_LEN_HIGH_VAL 0x0
+#define HASH_RESET_LEN_LOW_VAL 0x0
+
+/* Control register bitfields */
+#define HASH_CR_RESUME_MASK 0x11FCF
+
+#define HASH_CR_SWITCHON_POS 31
+#define HASH_CR_SWITCHON_MASK MASK_BIT31
+
+#define HASH_CR_EMPTYMSG_POS 20
+#define HASH_CR_EMPTYMSG_MASK MASK_BIT20
+
+#define HASH_CR_DINF_POS 12
+#define HASH_CR_DINF_MASK MASK_BIT12
+
+#define HASH_CR_NBW_POS 8
+#define HASH_CR_NBW_MASK 0x00000F00UL
+
+#define HASH_CR_LKEY_POS 16
+#define HASH_CR_LKEY_MASK MASK_BIT16
+
+#define HASH_CR_ALGO_POS 7
+#define HASH_CR_ALGO_MASK MASK_BIT7
+
+#define HASH_CR_MODE_POS 6
+#define HASH_CR_MODE_MASK MASK_BIT6
+
+#define HASH_CR_DATAFORM_POS 4
+#define HASH_CR_DATAFORM_MASK (MASK_BIT4 | MASK_BIT5)
+
+#define HASH_CR_DMAE_POS 3
+#define HASH_CR_DMAE_MASK MASK_BIT3
+
+#define HASH_CR_INIT_POS 2
+#define HASH_CR_INIT_MASK MASK_BIT2
+
+#define HASH_CR_PRIVN_POS 1
+#define HASH_CR_PRIVN_MASK MASK_BIT1
+
+#define HASH_CR_SECN_POS 0
+#define HASH_CR_SECN_MASK MASK_BIT0
+
+/* Start register bitfields */
+#define HASH_STR_DCAL_POS 8
+#define HASH_STR_DCAL_MASK MASK_BIT8
+
+#define HASH_STR_NBLW_POS 0
+#define HASH_STR_NBLW_MASK 0x0000001FUL
+
+#define HASH_NBLW_MAX_VAL 0x1F
+
+/* PrimeCell IDs */
+#define HASH_P_ID0 0xE0
+#define HASH_P_ID1 0x05
+#define HASH_P_ID2 0x38
+#define HASH_P_ID3 0x00
+#define HASH_CELL_ID0 0x0D
+#define HASH_CELL_ID1 0xF0
+#define HASH_CELL_ID2 0x05
+#define HASH_CELL_ID3 0xB1
+
+#define HASH_SET_DIN(val) HCL_WRITE_REG( \
+ sys_ctx_g.registry[HASH_DEVICE_ID_1]->din, (val))
+
+#define HASH_INITIALIZE \
+ HCL_WRITE_BITS( \
+ sys_ctx_g.registry[HASH_DEVICE_ID_1]->cr, \
+ 0x01 << HASH_CR_INIT_POS, \
+ HASH_CR_INIT_MASK)
+
+#define HASH_SET_DATA_FORMAT(data_format) \
+ HCL_WRITE_BITS( \
+ sys_ctx_g.registry[HASH_DEVICE_ID_1]->cr, \
+ (u32) (data_format) << HASH_CR_DATAFORM_POS, \
+ HASH_CR_DATAFORM_MASK)
+
+#define HASH_GET_HX(pos) \
+ HCL_READ_REG(sys_ctx_g.registry[HASH_DEVICE_ID_1]->hx[pos])
+
+#define HASH_SET_NBLW(val) \
+ HCL_WRITE_BITS( \
+ sys_ctx_g.registry[HASH_DEVICE_ID_1]->str, \
+ (u32) (val) << HASH_STR_NBLW_POS, \
+ HASH_STR_NBLW_MASK)
+
+#define HASH_SET_DCAL \
+ HCL_WRITE_BITS( \
+ sys_ctx_g.registry[HASH_DEVICE_ID_1]->str, \
+ 0x01 << HASH_STR_DCAL_POS, \
+ HASH_STR_DCAL_MASK)
+
+#define HASH_BLOCK_BYTE_SIZE 64
+
+/**
+ * struct uint64 - Structure to handle 64 bits integers.
+ * @high_word: Most significant bits
+ * @low_word: Least significant bits
+ *
+ * Used to handle 64 bits integers.
+ */
+struct uint64 {
+ u32 high_word;
+ u32 low_word;
+};
+
+/**
+ * struct hash_register - Contains all registers in u8500 hash hardware.
+ * @cr: HASH control register (0x000)
+ * @din: HASH data input register (0x004)
+ * @str: HASH start register (0x008)
+ * @hx: HASH digest register 0..7 (0x00c-0x01C)
+ * @padding0: Reserved (0x02C)
+ * @itcr: Integration test control register (0x080)
+ * @itip: Integration test input register (0x084)
+ * @itop: Integration test output register (0x088)
+ * @padding1: Reserved (0x08C)
+ * @csfull: HASH context full register (0x0F8)
+ * @csdatain: HASH context swap data input register (0x0FC)
+ * @csrx: HASH context swap register 0..51 (0x100-0x1CC)
+ * @padding2: Reserved (0x1D0)
+ * @periphid0: HASH peripheral identification register 0 (0xFE0)
+ * @periphid1: HASH peripheral identification register 1 (0xFE4)
+ * @periphid2: HASH peripheral identification register 2 (0xFE8)
+ * @periphid3: HASH peripheral identification register 3 (0xFEC)
+ * @cellid0: HASH PCell identification register 0 (0xFF0)
+ * @cellid1: HASH PCell identification register 1 (0xFF4)
+ * @cellid2: HASH PCell identification register 2 (0xFF8)
+ * @cellid3: HASH PCell identification register 3 (0xFFC)
+ *
+ * The device communicates to the HASH via 32-bit-wide control registers
+ * accessible via the 32-bit width AMBA rev. 2.0 AHB Bus. Below is a structure
+ * with the registers used.
+ */
+struct hash_register {
+ u32 cr;
+ u32 din;
+ u32 str;
+ u32 hx[8];
+
+ u32 padding0[(0x080 - 0x02C) / sizeof(u32)];
+
+ u32 itcr;
+ u32 itip;
+ u32 itop;
+
+ u32 padding1[(0x0F8 - 0x08C) / sizeof(u32)];
+
+ u32 csfull;
+ u32 csdatain;
+ u32 csrx[HASH_CSR_COUNT];
+
+ u32 padding2[(0xFE0 - 0x1D0) / sizeof(u32)];
+
+ u32 periphid0;
+ u32 periphid1;
+ u32 periphid2;
+ u32 periphid3;
+
+ u32 cellid0;
+ u32 cellid1;
+ u32 cellid2;
+ u32 cellid3;
+};
+
+/**
+ * struct hash_state - Hash context state.
+ * @temp_cr: Temporary HASH Control Register
+ * @str_reg: HASH Start Register
+ * @din_reg: HASH Data Input Register
+ * @csr[52]: HASH Context Swap Registers 0-39
+ * @csfull: HASH Context Swap Registers 40 ie Status flags
+ * @csdatain: HASH Context Swap Registers 41 ie Input data
+ * @buffer: Working buffer for messages going to the hardware
+ * @length: Length of the part of the message hashed so far (floor(N/64) * 64)
+ * @index: Valid number of bytes in buffer (N % 64)
+ * @bit_index: Valid number of bits in buffer (N % 8)
+ *
+ * This structure is used between context switches, i.e. when ongoing jobs are
+ * interupted with new jobs. When this happens we need to store intermediate
+ * results in software.
+ *
+ * WARNING: "index" is the member of the structure, to be sure that "buffer"
+ * is aligned on a 4-bytes boundary. This is highly implementation dependent
+ * and MUST be checked whenever this code is ported on new platforms.
+ */
+struct hash_state {
+ u32 temp_cr;
+ u32 str_reg;
+ u32 din_reg;
+ u32 csr[52];
+ u32 csfull;
+ u32 csdatain;
+ u32 buffer[HASH_BLOCK_SIZE / sizeof(u32)];
+ struct uint64 length;
+ u8 index;
+ u8 bit_index;
+};
+
+/**
+ * struct hash_system_context - Structure for the global system context.
+ * @registry: Pointer to the registry of the hash hardware
+ * @state: State of the hash device
+ */
+struct hash_system_context {
+ /*
+ * Pointer to HASH registers structure. We know that this gives a
+ * checkpatch warning and in the current design it needs to be a
+ * volatile. We will change it when we will rewrite the driver similar
+ * to how we have done in cryp-part. We have also read
+ * Documentation/volatile-considered-harmful.txt as checkpatch tell
+ * us to do.
+ */
+ volatile struct hash_register *registry[MAX_HASH_DEVICE];
+
+ /* State of HASH device */
+ struct hash_state state[MAX_HASH_DEVICE];
+};
+
+/**
+ * enum hash_device_id - HASH device ID.
+ * @HASH_DEVICE_ID_0: Hash hardware with ID 0
+ * @HASH_DEVICE_ID_1: Hash hardware with ID 1
+ */
+enum hash_device_id {
+ HASH_DEVICE_ID_0 = 0,
+ HASH_DEVICE_ID_1 = 1
+};
+
+/**
+ * enum hash_data_format - HASH data format.
+ * @HASH_DATA_32_BITS: 32 bits data format
+ * @HASH_DATA_16_BITS: 16 bits data format
+ * @HASH_DATA_8_BITS: 8 bits data format
+ * @HASH_DATA_1_BITS: 1 bit data format
+ */
+enum hash_data_format {
+ HASH_DATA_32_BITS = 0x0,
+ HASH_DATA_16_BITS = 0x1,
+ HASH_DATA_8_BITS = 0x2,
+ HASH_DATA_1_BIT = 0x3
+};
+
+/**
+ * struct hash_protection_config - Device protection configuration.
+ * @privilege_access: FIXME, add comment.
+ * @secure_access: FIXME, add comment.
+ */
+struct hash_protection_config {
+ int privilege_access;
+ int secure_access;
+};
+
+/**
+ * enum hash_algo - Enumeration for selecting between SHA1 or SHA2 algorithm
+ * @HASH_ALGO_SHA1: Indicates that SHA1 is used.
+ * @HASH_ALGO_SHA2: Indicates that SHA2 (SHA256) is used.
+ */
+enum hash_algo {
+ HASH_ALGO_SHA1 = 0x0,
+ HASH_ALGO_SHA2 = 0x1
+};
+
+/**
+ * enum hash_op - Enumeration for selecting between HASH or HMAC mode
+ * @HASH_OPER_MODE_HASH: Indicates usage of normal HASH mode
+ * @HASH_OPER_MODE_HMAC: Indicates usage of HMAC
+ */
+enum hash_op {
+ HASH_OPER_MODE_HASH = 0x0,
+ HASH_OPER_MODE_HMAC = 0x1
+};
+
+/**
+ * struct hash_config - Configuration data for the hardware
+ * @data_format: Format of data entered into the hash data in register
+ * @algorithm: Algorithm selection bit
+ * @oper_mode: Operating mode selection bit
+ */
+struct hash_config {
+ int data_format;
+ int algorithm;
+ int oper_mode;
+};
+
+/**
+ * enum hash_rv - Return values / error codes for hash.
+ */
+enum hash_rv {
+ HASH_OK = 0,
+ HASH_MSG_LENGTH_OVERFLOW,
+ HASH_INVALID_PARAMETER,
+ HASH_UNSUPPORTED_HW
+};
+
+/**
+ * struct hash_ctx - The context used for hash calculations.
+ * @key: The key used in the operation
+ * @keylen: The length of the key
+ * @updated: Indicates if hardware is initialized for new operations
+ * @state: The state of the current calculations
+ * @config: The current configuration
+ */
+struct hash_ctx {
+ u8 key[HASH_BLOCK_BYTE_SIZE];
+ u32 keylen;
+ u8 updated;
+ struct hash_state state;
+ struct hash_config config;
+};
+
+int hash_init_base_address(int hash_device_id, t_logical_address base_address);
+
+int hash_setconfiguration(int hash_device_id, struct hash_config *p_config);
+
+void hash_begin(struct hash_ctx *ctx);
+
+void hash_get_digest(int hid, u8 *digest, int algorithm);
+
+int hash_hw_update(struct shash_desc *desc,
+ int hash_device_id,
+ const u8 *p_data_buffer,
+ u32 msg_length);
+
+int hash_end(struct hash_ctx *ctx, u8 digest[HASH_MSG_DIGEST_SIZE]);
+
+int hash_save_state(int hash_device_id, struct hash_state *state);
+
+int hash_resume_state(int hash_device_id, const struct hash_state *state);
+
+#endif
diff --git a/drivers/crypto/ux500/hash/hash_alg_p.h b/drivers/crypto/ux500/hash/hash_alg_p.h
new file mode 100755
index 00000000000..c85faaeba6f
--- /dev/null
+++ b/drivers/crypto/ux500/hash/hash_alg_p.h
@@ -0,0 +1,26 @@
+/*****************************************************************************/
+/**
+* � ST-Ericsson, 2009 - All rights reserved
+* Reproduction and Communication of this document is strictly prohibited
+* unless specifically authorized in writing by ST-Ericsson
+*
+* static Header file of HASH Processor
+* Specification release related to this implementation: A_V2.2
+* AUTHOR : ST-Ericsson
+*/
+/*****************************************************************************/
+
+#ifndef _HASH_P_H_
+#define _HASH_P_H_
+
+/*--------------------------------------------------------------------------*
+ * Includes *
+ *--------------------------------------------------------------------------*/
+#include "hash_alg.h"
+
+/*--------------------------------------------------------------------------*
+ * Defines *
+ *--------------------------------------------------------------------------*/
+
+#endif /* End _HASH_P_H_ */
+
diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c
new file mode 100644
index 00000000000..a2e4ebd8ac1
--- /dev/null
+++ b/drivers/crypto/ux500/hash/hash_core.c
@@ -0,0 +1,1235 @@
+/*
+ * Cryptographic API.
+ * Support for Nomadik hardware crypto engine.
+
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/crypto.h>
+#include <linux/regulator/consumer.h>
+#include <linux/bitops.h>
+
+#include <crypto/internal/hash.h>
+#include <crypto/sha.h>
+
+#include <mach/hardware.h>
+
+#include "hash_alg.h"
+
+#define DRIVER_NAME "DRIVER HASH"
+/* Enable/Disables debug msgs */
+#define DRIVER_DEBUG 1
+#define DRIVER_DEBUG_PFX DRIVER_NAME
+#define DRIVER_DBG KERN_DEBUG
+
+#define MAX_HASH_DIGEST_BYTE_SIZE 32
+
+static struct mutex hash_hw_acc_mutex;
+
+static int debug;
+static struct hash_system_context sys_ctx_g;
+static struct hash_driver_data *internal_drv_data;
+
+/**
+ * struct hash_driver_data - IO Base and clock.
+ * @base: The IO base for the block.
+ * @clk: The clock.
+ * @regulator: The current regulator.
+ * @power_state: TRUE = power state on, FALSE = power state off.
+ * @power_state_mutex: Mutex for power_state.
+ * @restore_dev_ctx: TRUE = saved ctx, FALSE = no saved ctx.
+ */
+struct hash_driver_data {
+ void __iomem *base;
+ struct device *dev;
+ struct clk *clk;
+ struct regulator *regulator;
+ bool power_state;
+ struct mutex power_state_mutex;
+ bool restore_dev_state;
+};
+
+/* Declaration of functions */
+static void hash_messagepad(int hid, const u32 *message, u8 index_bytes);
+
+/**
+ * clear_reg_str - Clear the registry hash_str.
+ * @hid: Hardware device ID
+ *
+ * This function will clear the dcal bit and the nblw bits.
+ */
+static inline void clear_reg_str(int hid)
+{
+ /*
+ * We will only clear NBLW since writing 0 to DCAL is done by the
+ * hardware
+ */
+ sys_ctx_g.registry[hid]->str &= ~HASH_STR_NBLW_MASK;
+}
+
+static int hash_disable_power(
+ struct device *dev,
+ struct hash_driver_data *device_data,
+ bool save_device_state)
+{
+ int ret = 0;
+
+ dev_dbg(dev, "[%s]", __func__);
+
+ mutex_lock(&device_data->power_state_mutex);
+ if (!device_data->power_state)
+ goto out;
+
+ if (save_device_state) {
+ hash_save_state(HASH_DEVICE_ID_1,
+ &sys_ctx_g.state[HASH_DEVICE_ID_1]);
+ device_data->restore_dev_state = true;
+ }
+
+ clk_disable(device_data->clk);
+ ret = regulator_disable(device_data->regulator);
+ if (ret)
+ dev_err(dev, "[%s]: "
+ "regulator_disable() failed!",
+ __func__);
+
+ device_data->power_state = false;
+
+out:
+ mutex_unlock(&device_data->power_state_mutex);
+
+ return ret;
+}
+
+static int hash_enable_power(
+ struct device *dev,
+ struct hash_driver_data *device_data,
+ bool restore_device_state)
+{
+ int ret = 0;
+
+ dev_dbg(dev, "[%s]", __func__);
+
+ mutex_lock(&device_data->power_state_mutex);
+ if (!device_data->power_state) {
+ ret = regulator_enable(device_data->regulator);
+ if (ret) {
+ dev_err(dev, "[%s]: regulator_enable() failed!",
+ __func__);
+ goto out;
+ }
+
+ ret = clk_enable(device_data->clk);
+ if (ret) {
+ dev_err(dev, "[%s]: clk_enable() failed!",
+ __func__);
+ regulator_disable(device_data->regulator);
+ goto out;
+ }
+ device_data->power_state = true;
+ }
+
+ if (device_data->restore_dev_state) {
+ if (restore_device_state) {
+ device_data->restore_dev_state = false;
+ hash_resume_state(HASH_DEVICE_ID_1,
+ &sys_ctx_g.state[HASH_DEVICE_ID_1]);
+ }
+ }
+out:
+ mutex_unlock(&device_data->power_state_mutex);
+
+ return ret;
+}
+
+/**
+ * init_hash_hw - Initialise the hash hardware for a new calculation.
+ * @desc: The hash descriptor for the job
+ *
+ * This function will enable the bits needed to clear and start a new
+ * calculation.
+ */
+static int init_hash_hw(struct shash_desc *desc)
+{
+ int ret = 0;
+ int hash_rv;
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ pr_debug("[init_hash_hw] (ctx=0x%x)!", (u32)ctx);
+
+ hash_rv = hash_setconfiguration(HASH_DEVICE_ID_1, &ctx->config);
+ if (hash_rv != HASH_OK) {
+ pr_err("hash_setconfiguration() failed!");
+ ret = -EPERM;
+ return ret;
+ }
+
+ hash_begin(ctx);
+
+ return ret;
+}
+
+/**
+ * hash_init - Common hash init function for SHA1/SHA2 (SHA256).
+ * @desc: The hash descriptor for the job
+ *
+ * Initialize structures.
+ */
+static int hash_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ pr_debug("[hash_init]: (ctx=0x%x)!", (u32)ctx);
+
+ memset(&ctx->state, 0, sizeof(struct hash_state));
+ ctx->updated = 0;
+
+ return 0;
+}
+
+/**
+ * hash_update - The hash update function for SHA1/SHA2 (SHA256).
+ * @desc: The hash descriptor for the job
+ * @data: Message that should be hashed
+ * @len: The length of the message that should be hashed
+ */
+static int hash_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
+{
+ int ret = 0;
+ int hash_rv = HASH_OK;
+
+ pr_debug("[hash_update]: (data=0x%x, len=%d)!",
+ (u32)data, len);
+
+ mutex_lock(&hash_hw_acc_mutex);
+
+ /* NOTE: The length of the message is in the form of number of bits */
+ hash_rv = hash_hw_update(desc, HASH_DEVICE_ID_1, data, len * 8);
+ if (hash_rv != HASH_OK) {
+ pr_err("hash_hw_update() failed!");
+ ret = -EPERM;
+ goto out;
+ }
+
+out:
+ mutex_unlock(&hash_hw_acc_mutex);
+ return ret;
+}
+
+/**
+ * hash_final - The hash final function for SHA1/SHA2 (SHA256).
+ * @desc: The hash descriptor for the job
+ * @out: Pointer for the calculated digest
+ */
+static int hash_final(struct shash_desc *desc, u8 *out)
+{
+ int ret = 0;
+ int hash_rv = HASH_OK;
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+ struct hash_driver_data *device_data = internal_drv_data;
+
+ int digestsize = crypto_shash_digestsize(desc->tfm);
+ u8 digest[HASH_MSG_DIGEST_SIZE];
+
+ pr_debug("[hash_final]: (ctx=0x%x)!", (u32) ctx);
+
+ mutex_lock(&hash_hw_acc_mutex);
+
+ /* Enable device power (and clock) */
+ ret = hash_enable_power(device_data->dev, device_data, false);
+ if (ret) {
+ dev_err(device_data->dev, "[%s]: "
+ "hash_enable_power() failed!", __func__);
+ goto out;
+ }
+
+ if (!ctx->updated) {
+ ret = init_hash_hw(desc);
+ if (ret) {
+ pr_err("init_hash_hw() failed!");
+ goto out_power;
+ }
+ } else {
+ hash_rv = hash_resume_state(HASH_DEVICE_ID_1, &ctx->state);
+
+ if (hash_rv != HASH_OK) {
+ pr_err("hash_resume_state() failed!");
+ ret = -EPERM;
+ goto out_power;
+ }
+ }
+
+ hash_messagepad(HASH_DEVICE_ID_1, ctx->state.buffer,
+ ctx->state.index);
+
+ hash_get_digest(HASH_DEVICE_ID_1, digest, ctx->config.algorithm);
+
+ memcpy(out, digest, digestsize);
+
+out_power:
+ /* Disable power (and clock) */
+ if (hash_disable_power(device_data->dev, device_data, false))
+ dev_err(device_data->dev, "[%s]: "
+ "hash_disable_power() failed!", __func__);
+
+out:
+ mutex_unlock(&hash_hw_acc_mutex);
+
+ return ret;
+}
+
+/**
+ * sha1_init - SHA1 init function.
+ * @desc: The hash descriptor for the job
+ */
+static int sha1_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ pr_debug("[sha1_init]: (ctx=0x%x)!", (u32) ctx);
+
+ ctx->config.data_format = HASH_DATA_8_BITS;
+ ctx->config.algorithm = HASH_ALGO_SHA1;
+ ctx->config.oper_mode = HASH_OPER_MODE_HASH;
+
+ return hash_init(desc);
+}
+
+/**
+ * sha256_init - SHA2 (SHA256) init function.
+ * @desc: The hash descriptor for the job
+ */
+static int sha256_init(struct shash_desc *desc)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ pr_debug("[sha256_init]: (ctx=0x%x)!", (u32) ctx);
+
+ ctx->config.data_format = HASH_DATA_8_BITS;
+ ctx->config.algorithm = HASH_ALGO_SHA2;
+ ctx->config.oper_mode = HASH_OPER_MODE_HASH;
+
+ return hash_init(desc);
+}
+
+static int hash_export(struct shash_desc *desc, void *out)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ pr_debug("[hash_export]: (ctx=0x%X) (out=0x%X)",
+ (u32) ctx, (u32) out);
+ memcpy(out, ctx, sizeof(*ctx));
+ return 0;
+}
+
+static int hash_import(struct shash_desc *desc, const void *in)
+{
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+
+ pr_debug("[hash_import]: (ctx=0x%x) (in =0x%X)",
+ (u32) ctx, (u32) in);
+ memcpy(ctx, in, sizeof(*ctx));
+ return 0;
+}
+
+static struct shash_alg sha1_alg = {
+ .digestsize = SHA1_DIGEST_SIZE,
+ .init = sha1_init,
+ .update = hash_update,
+ .final = hash_final,
+ .export = hash_export,
+ .import = hash_import,
+ .descsize = sizeof(struct hash_ctx),
+ .statesize = sizeof(struct hash_ctx),
+ .base = {
+ .cra_name = "sha1",
+ .cra_driver_name = "sha1-u8500",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
+};
+
+static struct shash_alg sha256_alg = {
+ .digestsize = SHA256_DIGEST_SIZE,
+ .init = sha256_init,
+ .update = hash_update,
+ .final = hash_final,
+ .export = hash_export,
+ .import = hash_import,
+ .descsize = sizeof(struct hash_ctx),
+ .statesize = sizeof(struct hash_ctx),
+ .base = {
+ .cra_name = "sha256",
+ .cra_driver_name = "sha256-u8500",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
+};
+
+/**
+ * u8500_hash_probe - Function that probes the hash hardware.
+ * @pdev: The platform device
+ */
+static int u8500_hash_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int hash_rv = HASH_OK;
+ struct resource *res = NULL;
+ struct hash_driver_data *hash_drv_data;
+
+ pr_debug("[u8500_hash_probe]: (pdev=0x%x)", (u32) pdev);
+
+ pr_debug("[u8500_hash_probe]: Calling kzalloc()!");
+ hash_drv_data = kzalloc(sizeof(struct hash_driver_data), GFP_KERNEL);
+ if (!hash_drv_data) {
+ pr_debug("kzalloc() failed!");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ hash_drv_data->dev = &pdev->dev;
+
+ pr_debug("[u8500_hash_probe]: Calling platform_get_resource()!");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ pr_debug("platform_get_resource() failed");
+ ret = -ENODEV;
+ goto out_kfree;
+ }
+
+ pr_debug("[u8500_hash_probe]: Calling request_mem_region()!");
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (res == NULL) {
+ pr_debug("request_mem_region() failed");
+ ret = -EBUSY;
+ goto out_kfree;
+ }
+
+ pr_debug("[u8500_hash_probe]: Calling ioremap()!");
+ hash_drv_data->base = ioremap(res->start, resource_size(res));
+ if (!hash_drv_data->base) {
+ pr_err("[u8500_hash] "
+ "ioremap of hash1 register memory failed!");
+ ret = -ENOMEM;
+ goto out_free_mem;
+ }
+ mutex_init(&hash_drv_data->power_state_mutex);
+
+ /* Enable power for HASH hardware block */
+ hash_drv_data->regulator = regulator_get(&pdev->dev, "v-ape");
+ if (IS_ERR(hash_drv_data->regulator)) {
+ dev_err(&pdev->dev, "[u8500_hash] "
+ "could not get hash regulator\n");
+ ret = PTR_ERR(hash_drv_data->regulator);
+ hash_drv_data->regulator = NULL;
+ goto out_unmap;
+ }
+
+ pr_debug("[u8500_hash_probe]: Calling clk_get()!");
+ /* Enable the clk for HASH1 hardware block */
+ hash_drv_data->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(hash_drv_data->clk)) {
+ pr_err("clk_get() failed!");
+ ret = PTR_ERR(hash_drv_data->clk);
+ goto out_regulator;
+ }
+
+ /* Enable device power (and clock) */
+ ret = hash_enable_power(&pdev->dev, hash_drv_data, false);
+ if (ret) {
+ dev_err(&pdev->dev, "[%s]: hash_enable_power() failed!",
+ __func__);
+ goto out_clk;
+ }
+
+ pr_debug("[u8500_hash_probe]: Calling hash_init_base_address()->"
+ "(base=0x%x,DEVICE_ID=%d)!",
+ (u32) hash_drv_data->base, HASH_DEVICE_ID_1);
+
+ /* Setting base address */
+ hash_rv =
+ hash_init_base_address(HASH_DEVICE_ID_1,
+ (t_logical_address) hash_drv_data->base);
+ if (hash_rv != HASH_OK) {
+ pr_err("hash_init_base_address() failed!");
+ ret = -EPERM;
+ goto out_power;
+ }
+ pr_debug("[u8500_hash_probe]: Calling mutex_init()!");
+ mutex_init(&hash_hw_acc_mutex);
+
+ pr_debug("[u8500_hash_probe]: To register only sha1 and sha256"
+ " algorithms!");
+ internal_drv_data = hash_drv_data;
+
+ ret = crypto_register_shash(&sha1_alg);
+ if (ret) {
+ pr_err("Could not register sha1_alg!");
+ goto out_power;
+ }
+ pr_debug("[u8500_hash_probe]: sha1_alg registered!");
+
+ ret = crypto_register_shash(&sha256_alg);
+ if (ret) {
+ pr_err("Could not register sha256_alg!");
+ goto out_unreg1_tmp;
+ }
+
+ pr_debug("[u8500_hash_probe]: Calling platform_set_drvdata()!");
+ platform_set_drvdata(pdev, hash_drv_data);
+
+ if (hash_disable_power(&pdev->dev, hash_drv_data, false))
+ dev_err(&pdev->dev, "[%s]: hash_disable_power()"
+ " failed!", __func__);
+
+ return 0;
+
+out_unreg1_tmp:
+ crypto_unregister_shash(&sha1_alg);
+
+out_power:
+ hash_disable_power(&pdev->dev, hash_drv_data, false);
+
+out_clk:
+ clk_put(hash_drv_data->clk);
+
+out_regulator:
+ regulator_put(hash_drv_data->regulator);
+
+out_unmap:
+ iounmap(hash_drv_data->base);
+
+out_free_mem:
+ release_mem_region(res->start, res->end - res->start + 1);
+
+out_kfree:
+ kfree(hash_drv_data);
+out:
+ return ret;
+}
+
+/**
+ * u8500_hash_remove - Function that removes the hash device from the platform.
+ * @pdev: The platform device
+ */
+static int u8500_hash_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct hash_driver_data *hash_drv_data;
+
+ pr_debug("[u8500_hash_remove]: (pdev=0x%x)", (u32) pdev);
+
+ pr_debug("[u8500_hash_remove]: Calling platform_get_drvdata()!");
+ hash_drv_data = platform_get_drvdata(pdev);
+
+ pr_debug("[u8500_hash_remove]: To unregister only sha1 and "
+ "sha256 algorithms!");
+ crypto_unregister_shash(&sha1_alg);
+ crypto_unregister_shash(&sha256_alg);
+
+ pr_debug("[u8500_hash_remove]: Calling mutex_destroy()!");
+ mutex_destroy(&hash_hw_acc_mutex);
+
+ pr_debug("[u8500_hash_remove]: Calling clk_disable()!");
+ clk_disable(hash_drv_data->clk);
+
+ pr_debug("[u8500_hash_remove]: Calling clk_put()!");
+ clk_put(hash_drv_data->clk);
+
+ pr_debug("[u8500_hash_remove]: Calling regulator_disable()!");
+ regulator_disable(hash_drv_data->regulator);
+
+ pr_debug("[u8500_hash_remove]: Calling iounmap(): base = 0x%x",
+ (u32) hash_drv_data->base);
+ iounmap(hash_drv_data->base);
+
+ pr_debug("[u8500_hash_remove]: Calling platform_get_resource()!");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ pr_debug("[u8500_hash_remove]: Calling release_mem_region()"
+ "->res->start=0x%x, res->end = 0x%x!",
+ res->start, res->end);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ pr_debug("[u8500_hash_remove]: Calling kfree()!");
+ kfree(hash_drv_data);
+
+ return 0;
+}
+
+static void u8500_hash_shutdown(struct platform_device *pdev)
+{
+ struct resource *res = NULL;
+ struct hash_driver_data *hash_drv_data;
+
+ dev_dbg(&pdev->dev, "[%s]", __func__);
+
+ hash_drv_data = platform_get_drvdata(pdev);
+ if (!hash_drv_data) {
+ dev_err(&pdev->dev, "[%s]: "
+ "platform_get_drvdata() failed!", __func__);
+ return;
+ }
+
+ crypto_unregister_shash(&sha1_alg);
+ crypto_unregister_shash(&sha256_alg);
+
+ mutex_destroy(&hash_hw_acc_mutex);
+
+ iounmap(hash_drv_data->base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+
+ if (hash_disable_power(&pdev->dev, hash_drv_data, false))
+ dev_err(&pdev->dev, "[%s]: "
+ "hash_disable_power() failed", __func__);
+
+ clk_put(hash_drv_data->clk);
+ regulator_put(hash_drv_data->regulator);
+}
+
+static int u8500_hash_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int ret;
+ struct hash_driver_data *hash_drv_data;
+
+ dev_dbg(&pdev->dev, "[%s]", __func__);
+
+ /* Handle state? */
+ hash_drv_data = platform_get_drvdata(pdev);
+ if (!hash_drv_data) {
+ dev_err(&pdev->dev, "[%s]: "
+ "platform_get_drvdata() failed!", __func__);
+ return -ENOMEM;
+ }
+
+ ret = hash_disable_power(&pdev->dev, hash_drv_data, true);
+ if (ret)
+ dev_err(&pdev->dev, "[%s]: "
+ "hash_disable_power()", __func__);
+
+ return ret;
+}
+
+static int u8500_hash_resume(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct hash_driver_data *hash_drv_data;
+
+ dev_dbg(&pdev->dev, "[%s]", __func__);
+
+ hash_drv_data = platform_get_drvdata(pdev);
+ if (!hash_drv_data) {
+ dev_err(&pdev->dev, "[%s]: "
+ "platform_get_drvdata() failed!", __func__);
+ return -ENOMEM;
+ }
+
+ if (hash_drv_data->restore_dev_state) {
+ ret = hash_enable_power(&pdev->dev, hash_drv_data, true);
+ if (ret)
+ dev_err(&pdev->dev, "[%s]: "
+ "hash_enable_power() failed!", __func__);
+ }
+
+ return ret;
+}
+
+
+static struct platform_driver hash_driver = {
+ .probe = u8500_hash_probe,
+ .remove = u8500_hash_remove,
+ .shutdown = u8500_hash_shutdown,
+ .suspend = u8500_hash_suspend,
+ .resume = u8500_hash_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "hash1",
+ },
+};
+
+/**
+ * u8500_hash_mod_init - The kernel module init function.
+ */
+static int __init u8500_hash_mod_init(void)
+{
+ pr_debug("u8500_hash_mod_init() is called!");
+
+ return platform_driver_register(&hash_driver);
+}
+
+/**
+ * u8500_hash_mod_fini - The kernel module exit function.
+ */
+static void __exit u8500_hash_mod_fini(void)
+{
+ pr_debug("u8500_hash_mod_fini() is called!");
+
+ platform_driver_unregister(&hash_driver);
+ return;
+}
+
+/**
+ * hash_processblock - This function processes a single block of 512 bits (64
+ * bytes), word aligned, starting at message.
+ * @hid: Hardware device ID
+ * @message: Block (512 bits) of message to be written to the HASH hardware
+ *
+ * Reentrancy: Non Re-entrant.
+ */
+static void hash_processblock(int hid, const u32 *message)
+{
+ u32 count;
+
+ clear_bit(HASH_STR_NBLW_MASK, (void *)sys_ctx_g.registry[hid]->str);
+
+ /* Partially unrolled loop */
+ for (count = 0; count < (HASH_BLOCK_SIZE / sizeof(u32)); count += 4) {
+ HASH_SET_DIN(message[0]);
+ HASH_SET_DIN(message[1]);
+ HASH_SET_DIN(message[2]);
+ HASH_SET_DIN(message[3]);
+ message += 4;
+ }
+}
+
+/**
+ * hash_messagepad - Pads a message and write the nblw bits.
+ * @hid: Hardware device ID
+ * @message: Last word of a message
+ * @index_bytes: The number of bytes in the last message
+ *
+ * This function manages the final part of the digest calculation, when less
+ * than 512 bits (64 bytes) remain in message. This means index_bytes < 64.
+ *
+ * Reentrancy: Non Re-entrant.
+ */
+static void hash_messagepad(int hid, const u32 *message, u8 index_bytes)
+{
+ pr_debug("[u8500_hash_alg] hash_messagepad"
+ "(bytes in final msg=%d))", index_bytes);
+
+ clear_reg_str(hid);
+
+ /* Main loop */
+ while (index_bytes >= 4) {
+ HASH_SET_DIN(message[0]);
+ index_bytes -= 4;
+ message++;
+ }
+
+ if (index_bytes)
+ HASH_SET_DIN(message[0]);
+
+ while (sys_ctx_g.registry[hid]->str & HASH_STR_DCAL_MASK)
+ cpu_relax();
+
+ /* num_of_bytes == 0 => NBLW <- 0 (32 bits valid in DATAIN) */
+ HASH_SET_NBLW(index_bytes * 8);
+ pr_debug("[u8500_hash_alg] hash_messagepad -> DIN=0x%08x NBLW=%d",
+ sys_ctx_g.registry[hid]->din,
+ sys_ctx_g.registry[hid]->str);
+ HASH_SET_DCAL;
+ pr_debug("[u8500_hash_alg] hash_messagepad after dcal -> "
+ "DIN=0x%08x NBLW=%d",
+ sys_ctx_g.registry[hid]->din,
+ sys_ctx_g.registry[hid]->str);
+
+ while (sys_ctx_g.registry[hid]->str & HASH_STR_DCAL_MASK)
+ cpu_relax();
+}
+
+/**
+ * hash_incrementlength - Increments the length of the current message.
+ * @ctx: Hash context
+ * @incr: Length of message processed already
+ *
+ * Overflow cannot occur, because conditions for overflow are checked in
+ * hash_hw_update.
+ */
+static void hash_incrementlength(struct hash_ctx *ctx, u32 incr)
+{
+ ctx->state.length.low_word += incr;
+
+ /* Check for wrap-around */
+ if (ctx->state.length.low_word < incr)
+ ctx->state.length.high_word++;
+}
+
+/**
+ * hash_setconfiguration - Sets the required configuration for the hash
+ * hardware.
+ * @hid: Hardware device ID
+ * @p_config: Pointer to a configuration structure
+ *
+ * Reentrancy: Non Re-entrant
+ * Reentrancy issues:
+ * 1. Global variable registry(cofiguration register,
+ * parameter register, divider register) is being modified
+ *
+ * Comments 1. : User need to call hash_begin API after calling this
+ * API i.e. the current configuration is set only when
+ * bit INIT is set and we set INIT bit in hash_begin.
+ * Changing the configuration during a computation has
+ * no effect so we first set configuration by calling
+ * this API and then set the INIT bit for the HASH
+ * processor and the curent configuration is taken into
+ * account. As reading INIT bit (with correct protection
+ * rights) will always return 0b so we can't make a check
+ * at software level. So the user has to initialize the
+ * device for new configuration to take in to effect.
+ * 2. The default value of data format is 00b ie the format
+ * of data entered in HASH_DIN register is 32-bit data.
+ * The data written in HASH_DIN is used directly by the
+ * HASH processing, without re ordering.
+ */
+int hash_setconfiguration(int hid, struct hash_config *p_config)
+{
+ int hash_rv = HASH_OK;
+
+ pr_debug("[u8500_hash_alg] hash_setconfiguration())");
+
+ if (p_config->algorithm != HASH_ALGO_SHA1 &&
+ p_config->algorithm != HASH_ALGO_SHA2)
+ return HASH_INVALID_PARAMETER;
+
+ HASH_SET_DATA_FORMAT(p_config->data_format);
+
+ HCL_SET_BITS(sys_ctx_g.registry[hid]->cr, HASH_CR_EMPTYMSG_MASK);
+
+ switch (p_config->algorithm) {
+ case HASH_ALGO_SHA1:
+ HCL_SET_BITS(sys_ctx_g.registry[hid]->cr, HASH_CR_ALGO_MASK);
+ break;
+
+ case HASH_ALGO_SHA2:
+ HCL_CLEAR_BITS(sys_ctx_g.registry[hid]->cr, HASH_CR_ALGO_MASK);
+ break;
+
+ default:
+ pr_debug("[u8500_hash_alg] Incorrect algorithm.");
+ return HASH_INVALID_PARAMETER;
+ }
+
+ /* This bit selects between HASH or HMAC mode for the selected
+ algorithm */
+ if (HASH_OPER_MODE_HASH == p_config->oper_mode) {
+ HCL_CLEAR_BITS(sys_ctx_g.registry
+ [hid]->cr, HASH_CR_MODE_MASK);
+ } else { /* HMAC mode or wrong hash mode */
+ hash_rv = HASH_INVALID_PARAMETER;
+ pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ }
+
+ return hash_rv;
+}
+
+/**
+ * hash_begin - This routine resets some globals and initializes the hash
+ * hardware.
+ * @ctx: Hash context
+ *
+ * Reentrancy: Non Re-entrant
+ *
+ * Comments 1. : User need to call hash_setconfiguration API before
+ * calling this API i.e. the current configuration is set
+ * only when bit INIT is set and we set INIT bit in
+ * hash_begin. Changing the configuration during a
+ * computation has no effect so we first set
+ * configuration by calling this API and then set the
+ * INIT bit for the HASH processor and the current
+ * configuration is taken into account. As reading INIT
+ * bit (with correct protection rights) will always
+ * return 0b so we can't make a check at software level.
+ * So the user has to initialize the device for new
+ * configuration to take in to effect.
+ */
+void hash_begin(struct hash_ctx *ctx)
+{
+ /* HW and SW initializations */
+ /* Note: there is no need to initialize buffer and digest members */
+
+ pr_debug("[u8500_hash_alg] hash_begin())");
+
+ while (sys_ctx_g.registry[HASH_DEVICE_ID_1]->str & HASH_STR_DCAL_MASK)
+ cpu_relax();
+
+ HASH_INITIALIZE;
+
+ HCL_CLEAR_BITS(sys_ctx_g.registry[HASH_DEVICE_ID_1]->str,
+ HASH_STR_NBLW_MASK);
+}
+
+/**
+ * hash_hw_update - Updates current HASH computation hashing another part of
+ * the message.
+ * @hid: Hardware device ID
+ * @p_data_buffer: Byte array containing the message to be hashed (caller
+ * allocated)
+ * @msg_length: Length of message to be hashed (in bits)
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_hw_update(struct shash_desc *desc,
+ int hid,
+ const u8 *p_data_buffer,
+ u32 msg_length)
+{
+ int hash_rv = HASH_OK;
+ u8 index;
+ u8 *p_buffer;
+ u32 count;
+ struct hash_ctx *ctx = shash_desc_ctx(desc);
+ struct hash_driver_data *device_data = internal_drv_data;
+
+ pr_debug("[u8500_hash_alg] hash_hw_update(msg_length=%d / %d), "
+ "in=%d, bin=%d))",
+ msg_length,
+ msg_length / 8,
+ ctx->state.index,
+ ctx->state.bit_index);
+
+ index = ctx->state.index;
+
+ p_buffer = (u8 *)ctx->state.buffer;
+
+ /* Number of bytes in the message */
+ msg_length /= 8;
+
+ /* Check parameters */
+ if (NULL == p_data_buffer) {
+ hash_rv = HASH_INVALID_PARAMETER;
+ pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_rv;
+ }
+
+ /* Check if ctx->state.length + msg_length
+ overflows */
+ if (msg_length >
+ (ctx->state.length.low_word + msg_length)
+ && HASH_HIGH_WORD_MAX_VAL ==
+ (ctx->state.length.high_word)) {
+ hash_rv = HASH_MSG_LENGTH_OVERFLOW;
+ pr_err("[u8500_hash_alg] HASH_MSG_LENGTH_OVERFLOW!");
+ return hash_rv;
+ }
+
+ /* Enable device power (and clock) */
+ hash_rv = hash_enable_power(device_data->dev, device_data, false);
+ if (hash_rv) {
+ dev_err(device_data->dev, "[%s]: "
+ "hash_enable_power() failed!", __func__);
+ goto out;
+ }
+
+ /* Main loop */
+ while (0 != msg_length) {
+ if ((index + msg_length) < HASH_BLOCK_SIZE) {
+ for (count = 0; count < msg_length; count++) {
+ p_buffer[index + count] =
+ *(p_data_buffer + count);
+ }
+
+ index += msg_length;
+ msg_length = 0;
+ } else {
+ if (!ctx->updated) {
+ hash_rv = init_hash_hw(desc);
+ if (hash_rv != HASH_OK) {
+ pr_err("init_hash_hw() failed!");
+ goto out;
+ }
+ ctx->updated = 1;
+ } else {
+ hash_rv =
+ hash_resume_state(HASH_DEVICE_ID_1,
+ &ctx->state);
+ if (hash_rv != HASH_OK) {
+ pr_err("hash_resume_state()"
+ " failed!");
+ goto out_power;
+ }
+ }
+
+ /*
+ * If 'p_data_buffer' is four byte aligned and local
+ * buffer does not have any data, we can write data
+ * directly from 'p_data_buffer' to HW peripheral,
+ * otherwise we first copy data to a local buffer
+ */
+ if ((0 == (((u32) p_data_buffer) % 4))
+ && (0 == index)) {
+ hash_processblock(hid,
+ (const u32 *)p_data_buffer);
+ } else {
+ for (count = 0;
+ count < (u32)(HASH_BLOCK_SIZE - index);
+ count++) {
+ p_buffer[index + count] =
+ *(p_data_buffer + count);
+ }
+
+ hash_processblock(hid, (const u32 *)p_buffer);
+ }
+
+ hash_incrementlength(ctx, HASH_BLOCK_SIZE);
+ p_data_buffer += (HASH_BLOCK_SIZE - index);
+ msg_length -= (HASH_BLOCK_SIZE - index);
+ index = 0;
+
+ hash_rv =
+ hash_save_state(HASH_DEVICE_ID_1, &ctx->state);
+ if (hash_rv != HASH_OK) {
+ pr_err("hash_save_state() failed!");
+ goto out_power;
+ }
+ }
+ }
+
+ ctx->state.index = index;
+
+ pr_debug("[u8500_hash_alg] hash_hw_update END(msg_length=%d in "
+ "bits, in=%d, bin=%d))",
+ msg_length,
+ ctx->state.index,
+ ctx->state.bit_index);
+out_power:
+ /* Disable power (and clock) */
+ if (hash_disable_power(device_data->dev, device_data, false))
+ dev_err(device_data->dev, "[%s]: "
+ "hash_disable_power() failed!", __func__);
+out:
+ return hash_rv;
+}
+
+/**
+ * hash_resume_state - Function that resumes the state of an calculation.
+ * @hid: Hardware device ID
+ * @device_state: The state to be restored in the hash hardware
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_resume_state(int hid, const struct hash_state *device_state)
+{
+ u32 temp_cr;
+ int hash_rv = HASH_OK;
+ s32 count;
+ int hash_mode = HASH_OPER_MODE_HASH;
+
+ pr_debug("[u8500_hash_alg] hash_resume_state(state(0x%x)))",
+ (u32) device_state);
+
+ if (NULL == device_state) {
+ hash_rv = HASH_INVALID_PARAMETER;
+ pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_rv;
+ }
+
+ /* Check correctness of index and length members */
+ if (device_state->index > HASH_BLOCK_SIZE
+ || (device_state->length.low_word % HASH_BLOCK_SIZE) != 0) {
+ hash_rv = HASH_INVALID_PARAMETER;
+ pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_rv;
+ }
+
+ HASH_INITIALIZE;
+
+ temp_cr = device_state->temp_cr;
+ sys_ctx_g.registry[hid]->cr =
+ temp_cr & HASH_CR_RESUME_MASK;
+
+ if (sys_ctx_g.registry[hid]->cr & HASH_CR_MODE_MASK)
+ hash_mode = HASH_OPER_MODE_HMAC;
+ else
+ hash_mode = HASH_OPER_MODE_HASH;
+
+ for (count = 0; count < HASH_CSR_COUNT; count++) {
+ if ((count >= 36) && (hash_mode == HASH_OPER_MODE_HASH))
+ break;
+
+ sys_ctx_g.registry[hid]->csrx[count] =
+ device_state->csr[count];
+ }
+
+ sys_ctx_g.registry[hid]->csfull = device_state->csfull;
+ sys_ctx_g.registry[hid]->csdatain = device_state->csdatain;
+
+ sys_ctx_g.registry[hid]->str = device_state->str_reg;
+ sys_ctx_g.registry[hid]->cr = temp_cr;
+
+ return hash_rv;
+}
+
+/**
+ * hash_save_state - Function that saves the state of hardware.
+ * @hid: Hardware device ID
+ * @device_state: The strucure where the hardware state should be saved
+ *
+ * Reentrancy: Non Re-entrant
+ */
+int hash_save_state(int hid, struct hash_state *device_state)
+{
+ u32 temp_cr;
+ u32 count;
+ int hash_rv = HASH_OK;
+ int hash_mode = HASH_OPER_MODE_HASH;
+
+ pr_debug("[u8500_hash_alg] hash_save_state( state(0x%x)))",
+ (u32) device_state);
+
+ if (NULL == device_state) {
+ hash_rv = HASH_INVALID_PARAMETER;
+ pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_rv;
+ }
+
+ /* Write dummy value to force digest intermediate calculation. This
+ * actually makes sure that there isn't any ongoing calculation in the
+ * hardware.
+ */
+ while (sys_ctx_g.registry[hid]->str & HASH_STR_DCAL_MASK)
+ cpu_relax();
+
+ temp_cr = sys_ctx_g.registry[hid]->cr;
+
+ device_state->str_reg = sys_ctx_g.registry[hid]->str;
+
+ device_state->din_reg = sys_ctx_g.registry[hid]->din;
+
+ if (sys_ctx_g.registry[hid]->cr & HASH_CR_MODE_MASK)
+ hash_mode = HASH_OPER_MODE_HMAC;
+ else
+ hash_mode = HASH_OPER_MODE_HASH;
+
+ for (count = 0; count < HASH_CSR_COUNT; count++) {
+ if ((count >= 36) && (hash_mode == HASH_OPER_MODE_HASH))
+ break;
+
+ device_state->csr[count] =
+ sys_ctx_g.registry[hid]->csrx[count];
+ }
+
+ device_state->csfull = sys_ctx_g.registry[hid]->csfull;
+ device_state->csdatain = sys_ctx_g.registry[hid]->csdatain;
+
+ device_state->temp_cr = temp_cr;
+
+ return hash_rv;
+}
+
+/**
+ * hash_init_base_address - This routine initializes hash register base
+ * address. It also checks for peripheral Ids and PCell Ids.
+ * @hid: Hardware device ID
+ * @base_address: Hash hardware base address
+ *
+ * Reentrancy: Non Re-entrant, global variable registry (register base address)
+ * is being modified.
+ */
+int hash_init_base_address(int hid, t_logical_address base_address)
+{
+ int hash_rv = HASH_OK;
+
+ pr_debug("[u8500_hash_alg] hash_init_base_address())");
+
+ if (0 != base_address) {
+ /* Initializing the registers structure */
+ sys_ctx_g.registry[hid] =
+ (struct hash_register *) base_address;
+
+ /* Checking Peripheral Ids */
+ if ((HASH_P_ID0 == sys_ctx_g.registry[hid]->periphid0)
+ && (HASH_P_ID1 == sys_ctx_g.registry[hid]->periphid1)
+ && (HASH_P_ID2 == sys_ctx_g.registry[hid]->periphid2)
+ && (HASH_P_ID3 == sys_ctx_g.registry[hid]->periphid3)
+ && (HASH_CELL_ID0 == sys_ctx_g.registry[hid]->cellid0)
+ && (HASH_CELL_ID1 == sys_ctx_g.registry[hid]->cellid1)
+ && (HASH_CELL_ID2 == sys_ctx_g.registry[hid]->cellid2)
+ && (HASH_CELL_ID3 == sys_ctx_g.registry[hid]->cellid3)
+ ) {
+ hash_rv = HASH_OK;
+ return hash_rv;
+ } else {
+ hash_rv = HASH_UNSUPPORTED_HW;
+ pr_err("[u8500_hash_alg] HASH_UNSUPPORTED_HW!");
+ return hash_rv;
+ }
+ } /* end if */
+ else {
+ hash_rv = HASH_INVALID_PARAMETER;
+ pr_err("[u8500_hash_alg] HASH_INVALID_PARAMETER!");
+ return hash_rv;
+ }
+}
+
+/**
+ * hash_get_digest - Gets the digest.
+ * @hid: Hardware device ID
+ * @digest: User allocated byte array for the calculated digest
+ * @algorithm: The algorithm in use.
+ *
+ * Reentrancy: Non Re-entrant, global variable registry (hash control register)
+ * is being modified.
+ *
+ * Note that, if this is called before the final message has been handle it
+ * will return the intermediate message digest.
+ */
+void hash_get_digest(int hid, u8 *digest, int algorithm)
+{
+ u32 temp_hx_val, count;
+ int loop_ctr;
+
+ if (algorithm != HASH_ALGO_SHA1 && algorithm != HASH_ALGO_SHA2) {
+ pr_err("[hash_get_digest] Incorrect algorithm %d", algorithm);
+ return;
+ }
+
+ if (algorithm == HASH_ALGO_SHA1)
+ loop_ctr = HASH_SHA1_DIGEST_SIZE / sizeof(u32);
+ else
+ loop_ctr = HASH_SHA2_DIGEST_SIZE / sizeof(u32);
+
+ pr_debug("[u8500_hash_alg] hash_get_digest(digest array:(0x%x))",
+ (u32) digest);
+
+ /* Copy result into digest array */
+ for (count = 0; count < loop_ctr; count++) {
+ temp_hx_val = HASH_GET_HX(count);
+ digest[count * 4] = (u8) ((temp_hx_val >> 24) & 0xFF);
+ digest[count * 4 + 1] = (u8) ((temp_hx_val >> 16) & 0xFF);
+ digest[count * 4 + 2] = (u8) ((temp_hx_val >> 8) & 0xFF);
+ digest[count * 4 + 3] = (u8) ((temp_hx_val >> 0) & 0xFF);
+ }
+}
+
+
+module_init(u8500_hash_mod_init);
+module_exit(u8500_hash_mod_fini);
+
+module_param(debug, int, 0);
+
+MODULE_DESCRIPTION("Driver for ST-Ericsson U8500 HASH engine.");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("sha1-u8500");
+MODULE_ALIAS("sha256-u8500");
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 9e01e96fee9..6f76878f622 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -33,15 +33,22 @@ if DMADEVICES
comment "DMA Devices"
-config ASYNC_TX_DISABLE_CHANNEL_SWITCH
+config ASYNC_TX_ENABLE_CHANNEL_SWITCH
bool
+config AMBA_PL08X
+ bool "ARM PrimeCell PL080 or PL081 support"
+ depends on ARM_AMBA && EXPERIMENTAL
+ select DMA_ENGINE
+ help
+ Platform has a PL08x DMAC device
+ which can provide DMA engine support
+
config INTEL_IOATDMA
tristate "Intel I/OAT DMA support"
depends on PCI && X86
select DMA_ENGINE
select DCA
- select ASYNC_TX_DISABLE_CHANNEL_SWITCH
select ASYNC_TX_DISABLE_PQ_VAL_DMA
select ASYNC_TX_DISABLE_XOR_VAL_DMA
help
@@ -56,6 +63,7 @@ config INTEL_IOP_ADMA
tristate "Intel IOP ADMA support"
depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IOP13XX
select DMA_ENGINE
+ select ASYNC_TX_ENABLE_CHANNEL_SWITCH
help
Enable support for the Intel(R) IOP Series RAID engines.
@@ -80,6 +88,7 @@ config FSL_DMA
tristate "Freescale Elo and Elo Plus DMA support"
depends on FSL_SOC
select DMA_ENGINE
+ select ASYNC_TX_ENABLE_CHANNEL_SWITCH
---help---
Enable support for the Freescale Elo and Elo Plus DMA controllers.
The Elo is the DMA controller on some 82xx and 83xx parts, and the
@@ -96,6 +105,7 @@ config MV_XOR
bool "Marvell XOR engine support"
depends on PLAT_ORION
select DMA_ENGINE
+ select ASYNC_TX_ENABLE_CHANNEL_SWITCH
---help---
Enable support for the Marvell XOR engine.
@@ -153,6 +163,7 @@ config AMCC_PPC440SPE_ADMA
depends on 440SPe || 440SP
select DMA_ENGINE
select ARCH_HAS_ASYNC_TX_FIND_CHANNEL
+ select ASYNC_TX_ENABLE_CHANNEL_SWITCH
help
Enable support for the AMCC PPC440SPe RAID engines.
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 0fe5ebbfda5..c51dbc83ac2 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
obj-$(CONFIG_TIMB_DMA) += timb_dma.o
obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
obj-$(CONFIG_PL330_DMA) += pl330.o
+obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
new file mode 100644
index 00000000000..b605cc9ac3a
--- /dev/null
+++ b/drivers/dma/amba-pl08x.c
@@ -0,0 +1,2167 @@
+/*
+ * Copyright (c) 2006 ARM Ltd.
+ * Copyright (c) 2010 ST-Ericsson SA
+ *
+ * Author: Peter Pearse <peter.pearse@arm.com>
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The full GNU General Public License is iin this distribution in the
+ * file called COPYING.
+ *
+ * Documentation: ARM DDI 0196G == PL080
+ * Documentation: ARM DDI 0218E == PL081
+ *
+ * PL080 & PL081 both have 16 sets of DMA signals that can be routed to
+ * any channel.
+ *
+ * The PL080 has 8 channels available for simultaneous use, and the PL081
+ * has only two channels. So on these DMA controllers the number of channels
+ * and the number of incoming DMA signals are two totally different things.
+ * It is usually not possible to theoretically handle all physical signals,
+ * so a multiplexing scheme with possible denial of use is necessary.
+ *
+ * The PL080 has a dual bus master, PL081 has a single master.
+ *
+ * Memory to peripheral transfer may be visualized as
+ * Get data from memory to DMAC
+ * Until no data left
+ * On burst request from peripheral
+ * Destination burst from DMAC to peripheral
+ * Clear burst request
+ * Raise terminal count interrupt
+ *
+ * For peripherals with a FIFO:
+ * Source burst size == half the depth of the peripheral FIFO
+ * Destination burst size == the depth of the peripheral FIFO
+ *
+ * (Bursts are irrelevant for mem to mem transfers - there are no burst
+ * signals, the DMA controller will simply facilitate its AHB master.)
+ *
+ * ASSUMES default (little) endianness for DMA transfers
+ *
+ * Only DMAC flow control is implemented
+ *
+ * Global TODO:
+ * - Break out common code from arch/arm/mach-s3c64xx and share
+ */
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/dmapool.h>
+#include <linux/amba/bus.h>
+#include <linux/dmaengine.h>
+#include <linux/amba/pl08x.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <asm/hardware/pl080.h>
+#include <asm/dma.h>
+#include <asm/mach/dma.h>
+#include <asm/atomic.h>
+#include <asm/processor.h>
+#include <asm/cacheflush.h>
+
+#define DRIVER_NAME "pl08xdmac"
+
+/**
+ * struct vendor_data - vendor-specific config parameters
+ * for PL08x derivates
+ * @name: the name of this specific variant
+ * @channels: the number of channels available in this variant
+ * @dualmaster: whether this version supports dual AHB masters
+ * or not.
+ */
+struct vendor_data {
+ char *name;
+ u8 channels;
+ bool dualmaster;
+};
+
+/*
+ * PL08X private data structures
+ * An LLI struct - see pl08x TRM
+ * Note that next uses bit[0] as a bus bit,
+ * start & end do not - their bus bit info
+ * is in cctl
+ */
+struct lli {
+ dma_addr_t src;
+ dma_addr_t dst;
+ dma_addr_t next;
+ u32 cctl;
+};
+
+/**
+ * struct pl08x_driver_data - the local state holder for the PL08x
+ * @slave: slave engine for this instance
+ * @memcpy: memcpy engine for this instance
+ * @base: virtual memory base (remapped) for the PL08x
+ * @adev: the corresponding AMBA (PrimeCell) bus entry
+ * @vd: vendor data for this PL08x variant
+ * @pd: platform data passed in from the platform/machine
+ * @phy_chans: array of data for the physical channels
+ * @pool: a pool for the LLI descriptors
+ * @pool_ctr: counter of LLIs in the pool
+ * @lock: a spinlock for this struct
+ */
+struct pl08x_driver_data {
+ struct dma_device slave;
+ struct dma_device memcpy;
+ void __iomem *base;
+ struct amba_device *adev;
+ struct vendor_data *vd;
+ struct pl08x_platform_data *pd;
+ struct pl08x_phy_chan *phy_chans;
+ struct dma_pool *pool;
+ int pool_ctr;
+ spinlock_t lock;
+};
+
+/*
+ * PL08X specific defines
+ */
+
+/*
+ * Memory boundaries: the manual for PL08x says that the controller
+ * cannot read past a 1KiB boundary, so these defines are used to
+ * create transfer LLIs that do not cross such boundaries.
+ */
+#define PL08X_BOUNDARY_SHIFT (10) /* 1KB 0x400 */
+#define PL08X_BOUNDARY_SIZE (1 << PL08X_BOUNDARY_SHIFT)
+
+/* Minimum period between work queue runs */
+#define PL08X_WQ_PERIODMIN 20
+
+/* Size (bytes) of each LLI buffer allocated for one transfer */
+# define PL08X_LLI_TSFR_SIZE 0x2000
+
+/* Maximimum times we call dma_pool_alloc on this pool without freeing */
+#define PL08X_MAX_ALLOCS 0x40
+#define MAX_NUM_TSFR_LLIS (PL08X_LLI_TSFR_SIZE/sizeof(struct lli))
+#define PL08X_ALIGN 8
+
+static inline struct pl08x_dma_chan *to_pl08x_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct pl08x_dma_chan, chan);
+}
+
+/*
+ * Physical channel handling
+ */
+
+/* Whether a certain channel is busy or not */
+static int pl08x_phy_channel_busy(struct pl08x_phy_chan *ch)
+{
+ unsigned int val;
+
+ val = readl(ch->base + PL080_CH_CONFIG);
+ return val & PL080_CONFIG_ACTIVE;
+}
+
+/*
+ * Set the initial DMA register values i.e. those for the first LLI
+ * The next lli pointer and the configuration interrupt bit have
+ * been set when the LLIs were constructed
+ */
+static void pl08x_set_cregs(struct pl08x_driver_data *pl08x,
+ struct pl08x_phy_chan *ch)
+{
+ /* Wait for channel inactive */
+ while (pl08x_phy_channel_busy(ch))
+ ;
+
+ dev_vdbg(&pl08x->adev->dev,
+ "WRITE channel %d: csrc=%08x, cdst=%08x, "
+ "cctl=%08x, clli=%08x, ccfg=%08x\n",
+ ch->id,
+ ch->csrc,
+ ch->cdst,
+ ch->cctl,
+ ch->clli,
+ ch->ccfg);
+
+ writel(ch->csrc, ch->base + PL080_CH_SRC_ADDR);
+ writel(ch->cdst, ch->base + PL080_CH_DST_ADDR);
+ writel(ch->clli, ch->base + PL080_CH_LLI);
+ writel(ch->cctl, ch->base + PL080_CH_CONTROL);
+ writel(ch->ccfg, ch->base + PL080_CH_CONFIG);
+}
+
+static inline void pl08x_config_phychan_for_txd(struct pl08x_dma_chan *plchan)
+{
+ struct pl08x_channel_data *cd = plchan->cd;
+ struct pl08x_phy_chan *phychan = plchan->phychan;
+ struct pl08x_txd *txd = plchan->at;
+
+ /* Copy the basic control register calculated at transfer config */
+ phychan->csrc = txd->csrc;
+ phychan->cdst = txd->cdst;
+ phychan->clli = txd->clli;
+ phychan->cctl = txd->cctl;
+
+ /* Assign the signal to the proper control registers */
+ phychan->ccfg = cd->ccfg;
+ phychan->ccfg &= ~PL080_CONFIG_SRC_SEL_MASK;
+ phychan->ccfg &= ~PL080_CONFIG_DST_SEL_MASK;
+ /* If it wasn't set from AMBA, ignore it */
+ if (txd->direction == DMA_TO_DEVICE)
+ /* Select signal as destination */
+ phychan->ccfg |=
+ (phychan->signal << PL080_CONFIG_DST_SEL_SHIFT);
+ else if (txd->direction == DMA_FROM_DEVICE)
+ /* Select signal as source */
+ phychan->ccfg |=
+ (phychan->signal << PL080_CONFIG_SRC_SEL_SHIFT);
+ /* Always enable error interrupts */
+ phychan->ccfg |= PL080_CONFIG_ERR_IRQ_MASK;
+ /* Always enable terminal interrupts */
+ phychan->ccfg |= PL080_CONFIG_TC_IRQ_MASK;
+}
+
+/*
+ * Enable the DMA channel
+ * Assumes all other configuration bits have been set
+ * as desired before this code is called
+ */
+static void pl08x_enable_phy_chan(struct pl08x_driver_data *pl08x,
+ struct pl08x_phy_chan *ch)
+{
+ u32 val;
+
+ /*
+ * Do not access config register until channel shows as disabled
+ */
+ while (readl(pl08x->base + PL080_EN_CHAN) & (1 << ch->id))
+ ;
+
+ /*
+ * Do not access config register until channel shows as inactive
+ */
+ val = readl(ch->base + PL080_CH_CONFIG);
+ while ((val & PL080_CONFIG_ACTIVE) || (val & PL080_CONFIG_ENABLE))
+ val = readl(ch->base + PL080_CH_CONFIG);
+
+ writel(val | PL080_CONFIG_ENABLE, ch->base + PL080_CH_CONFIG);
+}
+
+/*
+ * Overall DMAC remains enabled always.
+ *
+ * Disabling individual channels could lose data.
+ *
+ * Disable the peripheral DMA after disabling the DMAC
+ * in order to allow the DMAC FIFO to drain, and
+ * hence allow the channel to show inactive
+ *
+ */
+static void pl08x_pause_phy_chan(struct pl08x_phy_chan *ch)
+{
+ u32 val;
+
+ /* Set the HALT bit and wait for the FIFO to drain */
+ val = readl(ch->base + PL080_CH_CONFIG);
+ val |= PL080_CONFIG_HALT;
+ writel(val, ch->base + PL080_CH_CONFIG);
+
+ /* Wait for channel inactive */
+ while (pl08x_phy_channel_busy(ch))
+ ;
+}
+
+static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch)
+{
+ u32 val;
+
+ /* Clear the HALT bit */
+ val = readl(ch->base + PL080_CH_CONFIG);
+ val &= ~PL080_CONFIG_HALT;
+ writel(val, ch->base + PL080_CH_CONFIG);
+}
+
+
+/* Stops the channel */
+static void pl08x_stop_phy_chan(struct pl08x_phy_chan *ch)
+{
+ u32 val;
+
+ pl08x_pause_phy_chan(ch);
+
+ /* Disable channel */
+ val = readl(ch->base + PL080_CH_CONFIG);
+ val &= ~PL080_CONFIG_ENABLE;
+ val &= ~PL080_CONFIG_ERR_IRQ_MASK;
+ val &= ~PL080_CONFIG_TC_IRQ_MASK;
+ writel(val, ch->base + PL080_CH_CONFIG);
+}
+
+static inline u32 get_bytes_in_cctl(u32 cctl)
+{
+ /* The source width defines the number of bytes */
+ u32 bytes = cctl & PL080_CONTROL_TRANSFER_SIZE_MASK;
+
+ switch (cctl >> PL080_CONTROL_SWIDTH_SHIFT) {
+ case PL080_WIDTH_8BIT:
+ break;
+ case PL080_WIDTH_16BIT:
+ bytes *= 2;
+ break;
+ case PL080_WIDTH_32BIT:
+ bytes *= 4;
+ break;
+ }
+ return bytes;
+}
+
+/* The channel should be paused when calling this */
+static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
+{
+ struct pl08x_phy_chan *ch;
+ struct pl08x_txd *txdi = NULL;
+ struct pl08x_txd *txd;
+ unsigned long flags;
+ u32 bytes = 0;
+
+ spin_lock_irqsave(&plchan->lock, flags);
+
+ ch = plchan->phychan;
+ txd = plchan->at;
+
+ /*
+ * Next follow the LLIs to get the number of pending bytes in the
+ * currently active transaction.
+ */
+ if (ch && txd) {
+ struct lli *llis_va = txd->llis_va;
+ struct lli *llis_bus = (struct lli *) txd->llis_bus;
+ u32 clli = readl(ch->base + PL080_CH_LLI);
+
+ /* First get the bytes in the current active LLI */
+ bytes = get_bytes_in_cctl(readl(ch->base + PL080_CH_CONTROL));
+
+ if (clli) {
+ int i = 0;
+
+ /* Forward to the LLI pointed to by clli */
+ while ((clli != (u32) &(llis_bus[i])) &&
+ (i < MAX_NUM_TSFR_LLIS))
+ i++;
+
+ while (clli) {
+ bytes += get_bytes_in_cctl(llis_va[i].cctl);
+ /*
+ * A clli of 0x00000000 will terminate the
+ * LLI list
+ */
+ clli = llis_va[i].next;
+ i++;
+ }
+ }
+ }
+
+ /* Sum up all queued transactions */
+ if (!list_empty(&plchan->desc_list)) {
+ list_for_each_entry(txdi, &plchan->desc_list, node) {
+ bytes += txdi->len;
+ }
+
+ }
+
+ spin_unlock_irqrestore(&plchan->lock, flags);
+
+ return bytes;
+}
+
+/*
+ * Allocate a physical channel for a virtual channel
+ */
+static struct pl08x_phy_chan *
+pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
+ struct pl08x_dma_chan *virt_chan)
+{
+ struct pl08x_phy_chan *ch = NULL;
+ unsigned long flags;
+ int i;
+
+ /*
+ * Try to locate a physical channel to be used for
+ * this transfer. If all are taken return NULL and
+ * the requester will have to cope by using some fallback
+ * PIO mode or retrying later.
+ */
+ for (i = 0; i < pl08x->vd->channels; i++) {
+ ch = &pl08x->phy_chans[i];
+
+ spin_lock_irqsave(&ch->lock, flags);
+
+ if (!ch->serving) {
+ ch->serving = virt_chan;
+ ch->signal = -1;
+ spin_unlock_irqrestore(&ch->lock, flags);
+ break;
+ }
+
+ spin_unlock_irqrestore(&ch->lock, flags);
+ }
+
+ if (i == pl08x->vd->channels) {
+ /* No physical channel available, cope with it */
+ return NULL;
+ }
+
+ return ch;
+}
+
+static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x,
+ struct pl08x_phy_chan *ch)
+{
+ unsigned long flags;
+
+ /* Stop the channel and clear its interrupts */
+ pl08x_stop_phy_chan(ch);
+ writel((1 << ch->id), pl08x->base + PL080_ERR_CLEAR);
+ writel((1 << ch->id), pl08x->base + PL080_TC_CLEAR);
+
+ /* Mark it as free */
+ spin_lock_irqsave(&ch->lock, flags);
+ ch->serving = NULL;
+ spin_unlock_irqrestore(&ch->lock, flags);
+}
+
+/*
+ * LLI handling
+ */
+
+static inline unsigned int pl08x_get_bytes_for_cctl(unsigned int coded)
+{
+ switch (coded) {
+ case PL080_WIDTH_8BIT:
+ return 1;
+ case PL080_WIDTH_16BIT:
+ return 2;
+ case PL080_WIDTH_32BIT:
+ return 4;
+ default:
+ break;
+ }
+ BUG();
+ return 0;
+}
+
+static inline u32 pl08x_cctl_bits(u32 cctl, u8 srcwidth, u8 dstwidth,
+ u32 tsize)
+{
+ u32 retbits = cctl;
+
+ /* Remove all src, dst and transfersize bits */
+ retbits &= ~PL080_CONTROL_DWIDTH_MASK;
+ retbits &= ~PL080_CONTROL_SWIDTH_MASK;
+ retbits &= ~PL080_CONTROL_TRANSFER_SIZE_MASK;
+
+ /* Then set the bits according to the parameters */
+ switch (srcwidth) {
+ case 1:
+ retbits |= PL080_WIDTH_8BIT << PL080_CONTROL_SWIDTH_SHIFT;
+ break;
+ case 2:
+ retbits |= PL080_WIDTH_16BIT << PL080_CONTROL_SWIDTH_SHIFT;
+ break;
+ case 4:
+ retbits |= PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT;
+ break;
+ default:
+ BUG();
+ break;
+ }
+
+ switch (dstwidth) {
+ case 1:
+ retbits |= PL080_WIDTH_8BIT << PL080_CONTROL_DWIDTH_SHIFT;
+ break;
+ case 2:
+ retbits |= PL080_WIDTH_16BIT << PL080_CONTROL_DWIDTH_SHIFT;
+ break;
+ case 4:
+ retbits |= PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT;
+ break;
+ default:
+ BUG();
+ break;
+ }
+
+ retbits |= tsize << PL080_CONTROL_TRANSFER_SIZE_SHIFT;
+ return retbits;
+}
+
+/*
+ * Autoselect a master bus to use for the transfer
+ * this prefers the destination bus if both available
+ * if fixed address on one bus the other will be chosen
+ */
+void pl08x_choose_master_bus(struct pl08x_bus_data *src_bus,
+ struct pl08x_bus_data *dst_bus, struct pl08x_bus_data **mbus,
+ struct pl08x_bus_data **sbus, u32 cctl)
+{
+ if (!(cctl & PL080_CONTROL_DST_INCR)) {
+ *mbus = src_bus;
+ *sbus = dst_bus;
+ } else if (!(cctl & PL080_CONTROL_SRC_INCR)) {
+ *mbus = dst_bus;
+ *sbus = src_bus;
+ } else {
+ if (dst_bus->buswidth == 4) {
+ *mbus = dst_bus;
+ *sbus = src_bus;
+ } else if (src_bus->buswidth == 4) {
+ *mbus = src_bus;
+ *sbus = dst_bus;
+ } else if (dst_bus->buswidth == 2) {
+ *mbus = dst_bus;
+ *sbus = src_bus;
+ } else if (src_bus->buswidth == 2) {
+ *mbus = src_bus;
+ *sbus = dst_bus;
+ } else {
+ /* src_bus->buswidth == 1 */
+ *mbus = dst_bus;
+ *sbus = src_bus;
+ }
+ }
+}
+
+/*
+ * Fills in one LLI for a certain transfer descriptor
+ * and advance the counter
+ */
+int pl08x_fill_lli_for_desc(struct pl08x_driver_data *pl08x,
+ struct pl08x_txd *txd, int num_llis, int len,
+ u32 cctl, u32 *remainder)
+{
+ struct lli *llis_va = txd->llis_va;
+ struct lli *llis_bus = (struct lli *) txd->llis_bus;
+
+ BUG_ON(num_llis >= MAX_NUM_TSFR_LLIS);
+
+ llis_va[num_llis].cctl = cctl;
+ llis_va[num_llis].src = txd->srcbus.addr;
+ llis_va[num_llis].dst = txd->dstbus.addr;
+
+ /*
+ * On versions with dual masters, you can optionally AND on
+ * PL080_LLI_LM_AHB2 to the LLI to tell the hardware to read
+ * in new LLIs with that controller, but we always try to
+ * choose AHB1 to point into memory. The idea is to have AHB2
+ * fixed on the peripheral and AHB1 messing around in the
+ * memory. So we don't manipulate this bit currently.
+ */
+
+ llis_va[num_llis].next =
+ (dma_addr_t)((u32) &(llis_bus[num_llis + 1]));
+
+ if (cctl & PL080_CONTROL_SRC_INCR)
+ txd->srcbus.addr += len;
+ if (cctl & PL080_CONTROL_DST_INCR)
+ txd->dstbus.addr += len;
+
+ *remainder -= len;
+
+ return num_llis + 1;
+}
+
+/*
+ * Return number of bytes to fill to boundary, or len
+ */
+static inline u32 pl08x_pre_boundary(u32 addr, u32 len)
+{
+ u32 boundary;
+
+ boundary = ((addr >> PL08X_BOUNDARY_SHIFT) + 1)
+ << PL08X_BOUNDARY_SHIFT;
+
+ if (boundary < addr + len)
+ return boundary - addr;
+ else
+ return len;
+}
+
+/*
+ * This fills in the table of LLIs for the transfer descriptor
+ * Note that we assume we never have to change the burst sizes
+ * Return 0 for error
+ */
+static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
+ struct pl08x_txd *txd)
+{
+ struct pl08x_channel_data *cd = txd->cd;
+ struct pl08x_bus_data *mbus, *sbus;
+ u32 remainder;
+ int num_llis = 0;
+ u32 cctl;
+ int max_bytes_per_lli;
+ int total_bytes = 0;
+ struct lli *llis_va;
+ struct lli *llis_bus;
+
+ if (!txd) {
+ dev_err(&pl08x->adev->dev, "%s no descriptor\n", __func__);
+ return 0;
+ }
+
+ txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT,
+ &txd->llis_bus);
+ if (!txd->llis_va) {
+ dev_err(&pl08x->adev->dev, "%s no memory for llis\n", __func__);
+ return 0;
+ }
+
+ pl08x->pool_ctr++;
+
+ /*
+ * Initialize bus values for this transfer
+ * from the passed optimal values
+ */
+ if (!cd) {
+ dev_err(&pl08x->adev->dev, "%s no channel data\n", __func__);
+ return 0;
+ }
+
+ /* Get the default CCTL from the platform data */
+ cctl = cd->cctl;
+
+ /*
+ * On the PL080 we have two bus masters and we
+ * should select one for source and one for
+ * destination. We try to use AHB2 for the
+ * bus which does not increment (typically the
+ * peripheral) else we just choose something.
+ */
+ cctl &= ~(PL080_CONTROL_DST_AHB2 | PL080_CONTROL_SRC_AHB2);
+ if (pl08x->vd->dualmaster) {
+ if (cctl & PL080_CONTROL_SRC_INCR)
+ /* Source increments, use AHB2 for destination */
+ cctl |= PL080_CONTROL_DST_AHB2;
+ else if (cctl & PL080_CONTROL_DST_INCR)
+ /* Destination increments, use AHB2 for source */
+ cctl |= PL080_CONTROL_SRC_AHB2;
+ else
+ /* Just pick something, source AHB1 dest AHB2 */
+ cctl |= PL080_CONTROL_DST_AHB2;
+ }
+
+ /* Find maximum width of the source bus */
+ txd->srcbus.maxwidth =
+ pl08x_get_bytes_for_cctl((cctl & PL080_CONTROL_SWIDTH_MASK) >>
+ PL080_CONTROL_SWIDTH_SHIFT);
+
+ /* Find maximum width of the destination bus */
+ txd->dstbus.maxwidth =
+ pl08x_get_bytes_for_cctl((cctl & PL080_CONTROL_DWIDTH_MASK) >>
+ PL080_CONTROL_DWIDTH_SHIFT);
+
+ /* Set up the bus widths to the maximum */
+ txd->srcbus.buswidth = txd->srcbus.maxwidth;
+ txd->dstbus.buswidth = txd->dstbus.maxwidth;
+ dev_vdbg(&pl08x->adev->dev,
+ "%s source bus is %d bytes wide, dest bus is %d bytes wide\n",
+ __func__, txd->srcbus.buswidth, txd->dstbus.buswidth);
+
+
+ /*
+ * Bytes transferred == tsize * MIN(buswidths), not max(buswidths)
+ */
+ max_bytes_per_lli = min(txd->srcbus.buswidth, txd->dstbus.buswidth) *
+ PL080_CONTROL_TRANSFER_SIZE_MASK;
+ dev_vdbg(&pl08x->adev->dev,
+ "%s max bytes per lli = %d\n",
+ __func__, max_bytes_per_lli);
+
+ /* We need to count this down to zero */
+ remainder = txd->len;
+ dev_vdbg(&pl08x->adev->dev,
+ "%s remainder = %d\n",
+ __func__, remainder);
+
+ /*
+ * Choose bus to align to
+ * - prefers destination bus if both available
+ * - if fixed address on one bus chooses other
+ * - modifies cctl to choose an apropriate master
+ */
+ pl08x_choose_master_bus(&txd->srcbus, &txd->dstbus,
+ &mbus, &sbus, cctl);
+
+
+ /*
+ * The lowest bit of the LLI register
+ * is also used to indicate which master to
+ * use for reading the LLIs.
+ */
+
+ if (txd->len < mbus->buswidth) {
+ /*
+ * Less than a bus width available
+ * - send as single bytes
+ */
+ while (remainder) {
+ dev_vdbg(&pl08x->adev->dev,
+ "%s single byte LLIs for a transfer of "
+ "less than a bus width (remain %08x)\n",
+ __func__, remainder);
+ cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
+ num_llis =
+ pl08x_fill_lli_for_desc(pl08x, txd, num_llis, 1,
+ cctl, &remainder);
+ total_bytes++;
+ }
+ } else {
+ /*
+ * Make one byte LLIs until master bus is aligned
+ * - slave will then be aligned also
+ */
+ while ((mbus->addr) % (mbus->buswidth)) {
+ dev_vdbg(&pl08x->adev->dev,
+ "%s adjustment lli for less than bus width "
+ "(remain %08x)\n",
+ __func__, remainder);
+ cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
+ num_llis = pl08x_fill_lli_for_desc
+ (pl08x, txd, num_llis, 1, cctl, &remainder);
+ total_bytes++;
+ }
+
+ /*
+ * Master now aligned
+ * - if slave is not then we must set its width down
+ */
+ if (sbus->addr % sbus->buswidth) {
+ dev_dbg(&pl08x->adev->dev,
+ "%s set down bus width to one byte\n",
+ __func__);
+
+ sbus->buswidth = 1;
+ }
+
+ /*
+ * Make largest possible LLIs until less than one bus
+ * width left
+ */
+ while (remainder > (mbus->buswidth - 1)) {
+ int lli_len, target_len;
+ int tsize;
+ int odd_bytes;
+
+ /*
+ * If enough left try to send max possible,
+ * otherwise try to send the remainder
+ */
+ target_len = remainder;
+ if (remainder > max_bytes_per_lli)
+ target_len = max_bytes_per_lli;
+
+ /*
+ * Set bus lengths for incrementing busses
+ * to number of bytes which fill to next memory
+ * boundary
+ */
+ if (cctl & PL080_CONTROL_SRC_INCR)
+ txd->srcbus.fill_bytes =
+ pl08x_pre_boundary(
+ txd->srcbus.addr,
+ remainder);
+ else
+ txd->srcbus.fill_bytes =
+ max_bytes_per_lli;
+
+ if (cctl & PL080_CONTROL_DST_INCR)
+ txd->dstbus.fill_bytes =
+ pl08x_pre_boundary(
+ txd->dstbus.addr,
+ remainder);
+ else
+ txd->dstbus.fill_bytes =
+ max_bytes_per_lli;
+
+ /*
+ * Find the nearest
+ */
+ lli_len = min(txd->srcbus.fill_bytes,
+ txd->dstbus.fill_bytes);
+
+ BUG_ON(lli_len > remainder);
+
+ if (lli_len <= 0) {
+ dev_err(&pl08x->adev->dev,
+ "%s lli_len is %d, <= 0\n",
+ __func__, lli_len);
+ return 0;
+ }
+
+ if (lli_len == target_len) {
+ /*
+ * Can send what we wanted
+ */
+ /*
+ * Maintain alignment
+ */
+ lli_len = (lli_len/mbus->buswidth) *
+ mbus->buswidth;
+ odd_bytes = 0;
+ } else {
+ /*
+ * So now we know how many bytes to transfer
+ * to get to the nearest boundary
+ * The next lli will past the boundary
+ * - however we may be working to a boundary
+ * on the slave bus
+ * We need to ensure the master stays aligned
+ */
+ odd_bytes = lli_len % mbus->buswidth;
+ /*
+ * - and that we are working in multiples
+ * of the bus widths
+ */
+ lli_len -= odd_bytes;
+
+ }
+
+ if (lli_len) {
+ /*
+ * Check against minimum bus alignment:
+ * Calculate actual transfer size in relation
+ * to bus width an get a maximum remainder of
+ * the smallest bus width - 1
+ */
+ /* FIXME: use round_down()? */
+ tsize = lli_len / min(mbus->buswidth,
+ sbus->buswidth);
+ lli_len = tsize * min(mbus->buswidth,
+ sbus->buswidth);
+
+ if (target_len != lli_len) {
+ dev_vdbg(&pl08x->adev->dev,
+ "%s can't send what we want. Desired %08x, lli of %08x bytes in txd of %08x\n",
+ __func__, target_len, lli_len, txd->len);
+ }
+
+ cctl = pl08x_cctl_bits(cctl,
+ txd->srcbus.buswidth,
+ txd->dstbus.buswidth,
+ tsize);
+
+ dev_vdbg(&pl08x->adev->dev,
+ "%s fill lli with single lli chunk of size %08x (remainder %08x)\n",
+ __func__, lli_len, remainder);
+ num_llis = pl08x_fill_lli_for_desc(pl08x, txd,
+ num_llis, lli_len, cctl,
+ &remainder);
+ total_bytes += lli_len;
+ }
+
+
+ if (odd_bytes) {
+ /*
+ * Creep past the boundary,
+ * maintaining master alignment
+ */
+ int j;
+ for (j = 0; (j < mbus->buswidth)
+ && (remainder); j++) {
+ cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
+ dev_vdbg(&pl08x->adev->dev,
+ "%s align with boundardy, single byte (remain %08x)\n",
+ __func__, remainder);
+ num_llis =
+ pl08x_fill_lli_for_desc(pl08x,
+ txd, num_llis, 1,
+ cctl, &remainder);
+ total_bytes++;
+ }
+ }
+ }
+
+ /*
+ * Send any odd bytes
+ */
+ if (remainder < 0) {
+ dev_err(&pl08x->adev->dev, "%s remainder not fitted 0x%08x bytes\n",
+ __func__, remainder);
+ return 0;
+ }
+
+ while (remainder) {
+ cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
+ dev_vdbg(&pl08x->adev->dev,
+ "%s align with boundardy, single odd byte (remain %d)\n",
+ __func__, remainder);
+ num_llis = pl08x_fill_lli_for_desc(pl08x, txd, num_llis,
+ 1, cctl, &remainder);
+ total_bytes++;
+ }
+ }
+ if (total_bytes != txd->len) {
+ dev_err(&pl08x->adev->dev,
+ "%s size of encoded lli:s don't match total txd, transferred 0x%08x from size 0x%08x\n",
+ __func__, total_bytes, txd->len);
+ return 0;
+ }
+
+ if (num_llis >= MAX_NUM_TSFR_LLIS) {
+ dev_err(&pl08x->adev->dev,
+ "%s need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n",
+ __func__, (u32) MAX_NUM_TSFR_LLIS);
+ return 0;
+ }
+ /*
+ * Decide whether this is a loop or a terminated transfer
+ */
+ llis_va = txd->llis_va;
+ llis_bus = (struct lli *) txd->llis_bus;
+
+ if (cd->circular_buffer) {
+ /*
+ * Loop the circular buffer so that the next element
+ * points back to the beginning of the LLI.
+ */
+ llis_va[num_llis - 1].next =
+ (dma_addr_t)((unsigned int)&(llis_bus[0]));
+ } else {
+ /*
+ * On non-circular buffers, the final LLI terminates
+ * the LLI.
+ */
+ llis_va[num_llis - 1].next = 0;
+ /*
+ * The final LLI element shall also fire an interrupt
+ */
+ llis_va[num_llis - 1].cctl |= PL080_CONTROL_TC_IRQ_EN;
+ }
+
+ /* Now store the channel register values */
+ txd->csrc = llis_va[0].src;
+ txd->cdst = llis_va[0].dst;
+ if (num_llis > 1)
+ txd->clli = llis_va[0].next;
+ else
+ txd->clli = 0;
+
+ txd->cctl = llis_va[0].cctl;
+ /* ccfg will be set at physical channel allocation time */
+
+#ifdef VERBOSE_DEBUG
+ {
+ int i;
+
+ for (i = 0; i < num_llis; i++) {
+ dev_vdbg(&pl08x->adev->dev,
+ "lli %d @%p: csrc=%08x, cdst=%08x, cctl=%08x, clli=%08x\n",
+ i,
+ &llis_va[i],
+ llis_va[i].src,
+ llis_va[i].dst,
+ llis_va[i].cctl,
+ llis_va[i].next
+ );
+ }
+ }
+#endif
+
+ return num_llis;
+}
+
+/* You should call this with the struct pl08x lock held */
+static void pl08x_free_txd(struct pl08x_driver_data *pl08x,
+ struct pl08x_txd *txd)
+{
+ if (!txd)
+ dev_err(&pl08x->adev->dev,
+ "%s no descriptor to free\n",
+ __func__);
+
+ /* Free the LLI */
+ dma_pool_free(pl08x->pool, txd->llis_va,
+ txd->llis_bus);
+
+ pl08x->pool_ctr--;
+
+ kfree(txd);
+}
+
+static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
+ struct pl08x_dma_chan *plchan)
+{
+ struct pl08x_txd *txdi = NULL;
+ struct pl08x_txd *next;
+
+ if (!list_empty(&plchan->desc_list)) {
+ list_for_each_entry_safe(txdi,
+ next, &plchan->desc_list, node) {
+ list_del(&txdi->node);
+ pl08x_free_txd(pl08x, txdi);
+ }
+
+ }
+}
+
+/*
+ * The DMA ENGINE API
+ */
+static int pl08x_alloc_chan_resources(struct dma_chan *chan)
+{
+ return 0;
+}
+
+static void pl08x_free_chan_resources(struct dma_chan *chan)
+{
+}
+
+/*
+ * This should be called with the channel plchan->lock held
+ */
+static int prep_phy_channel(struct pl08x_dma_chan *plchan,
+ struct pl08x_txd *txd)
+{
+ struct pl08x_driver_data *pl08x = plchan->host;
+ struct pl08x_phy_chan *ch;
+ int ret;
+
+ /* Check if we already have a channel */
+ if (plchan->phychan)
+ return 0;
+
+ ch = pl08x_get_phy_channel(pl08x, plchan);
+ if (!ch) {
+ /* No physical channel available, cope with it */
+ dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
+ return -EBUSY;
+ }
+
+ /*
+ * OK we have a physical channel: for memcpy() this is all we
+ * need, but for slaves the physical signals may be muxed!
+ * Can the platform allow us to use this channel?
+ */
+ if (plchan->slave &&
+ ch->signal < 0 &&
+ pl08x->pd->get_signal) {
+ ret = pl08x->pd->get_signal(plchan);
+ if (ret < 0) {
+ dev_dbg(&pl08x->adev->dev,
+ "unable to use physical channel %d for transfer on %s due to platform restrictions\n",
+ ch->id, plchan->name);
+ /* Release physical channel & return */
+ pl08x_put_phy_channel(pl08x, ch);
+ return -EBUSY;
+ }
+ ch->signal = ret;
+ }
+
+ dev_dbg(&pl08x->adev->dev, "allocated physical channel %d and signal %d for xfer on %s\n",
+ ch->id,
+ ch->signal,
+ plchan->name);
+
+ plchan->phychan = ch;
+
+ return 0;
+}
+
+static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(tx->chan);
+
+ atomic_inc(&plchan->last_issued);
+ tx->cookie = atomic_read(&plchan->last_issued);
+ /* This unlock follows the lock in the prep() function */
+ spin_unlock_irqrestore(&plchan->lock, plchan->lockflags);
+
+ return tx->cookie;
+}
+
+static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt(
+ struct dma_chan *chan, unsigned long flags)
+{
+ struct dma_async_tx_descriptor *retval = NULL;
+
+ return retval;
+}
+
+/*
+ * Code accessing dma_async_is_complete() in a tight loop
+ * may give problems - could schedule where indicated.
+ * If slaves are relying on interrupts to signal completion this
+ * function must not be called with interrupts disabled
+ */
+static enum dma_status
+pl08x_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ dma_cookie_t last_used;
+ dma_cookie_t last_complete;
+ enum dma_status ret;
+ u32 bytesleft = 0;
+
+ last_used = atomic_read(&plchan->last_issued);
+ last_complete = plchan->lc;
+
+ ret = dma_async_is_complete(cookie, last_complete, last_used);
+ if (ret == DMA_SUCCESS) {
+ dma_set_tx_state(txstate, last_complete, last_used, 0);
+ return ret;
+ }
+
+ /*
+ * schedule(); could be inserted here
+ */
+
+ /*
+ * This cookie not complete yet
+ */
+ last_used = atomic_read(&plchan->last_issued);
+ last_complete = plchan->lc;
+
+ /* Get number of bytes left in the active transactions and queue */
+ bytesleft = pl08x_getbytes_chan(plchan);
+
+ dma_set_tx_state(txstate, last_complete, last_used,
+ bytesleft);
+
+ if (plchan->state == PL08X_CHAN_PAUSED)
+ return DMA_PAUSED;
+
+ /* Whether waiting or running, we're in progress */
+ return DMA_IN_PROGRESS;
+}
+
+/* PrimeCell DMA extension */
+struct burst_table {
+ int burstwords;
+ u32 reg;
+};
+
+static const struct burst_table burst_sizes[] = {
+ {
+ .burstwords = 256,
+ .reg = (PL080_BSIZE_256 << PL080_CONTROL_SB_SIZE_SHIFT) |
+ (PL080_BSIZE_256 << PL080_CONTROL_DB_SIZE_SHIFT),
+ },
+ {
+ .burstwords = 128,
+ .reg = (PL080_BSIZE_128 << PL080_CONTROL_SB_SIZE_SHIFT) |
+ (PL080_BSIZE_128 << PL080_CONTROL_DB_SIZE_SHIFT),
+ },
+ {
+ .burstwords = 64,
+ .reg = (PL080_BSIZE_64 << PL080_CONTROL_SB_SIZE_SHIFT) |
+ (PL080_BSIZE_64 << PL080_CONTROL_DB_SIZE_SHIFT),
+ },
+ {
+ .burstwords = 32,
+ .reg = (PL080_BSIZE_32 << PL080_CONTROL_SB_SIZE_SHIFT) |
+ (PL080_BSIZE_32 << PL080_CONTROL_DB_SIZE_SHIFT),
+ },
+ {
+ .burstwords = 16,
+ .reg = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT) |
+ (PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT),
+ },
+ {
+ .burstwords = 8,
+ .reg = (PL080_BSIZE_8 << PL080_CONTROL_SB_SIZE_SHIFT) |
+ (PL080_BSIZE_8 << PL080_CONTROL_DB_SIZE_SHIFT),
+ },
+ {
+ .burstwords = 4,
+ .reg = (PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT) |
+ (PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT),
+ },
+ {
+ .burstwords = 1,
+ .reg = (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) |
+ (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT),
+ },
+};
+
+static void dma_set_runtime_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
+{
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ struct pl08x_driver_data *pl08x = plchan->host;
+ struct pl08x_channel_data *cd = plchan->cd;
+ enum dma_slave_buswidth addr_width;
+ u32 maxburst;
+ u32 cctl = 0;
+ /* Mask out all except src and dst channel */
+ u32 ccfg = cd->ccfg & 0x000003DEU;
+ int i = 0;
+
+ /* Transfer direction */
+ plchan->runtime_direction = config->direction;
+ if (config->direction == DMA_TO_DEVICE) {
+ plchan->runtime_addr = config->dst_addr;
+ cctl |= PL080_CONTROL_SRC_INCR;
+ ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+ addr_width = config->dst_addr_width;
+ maxburst = config->dst_maxburst;
+ } else if (config->direction == DMA_FROM_DEVICE) {
+ plchan->runtime_addr = config->src_addr;
+ cctl |= PL080_CONTROL_DST_INCR;
+ ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+ addr_width = config->src_addr_width;
+ maxburst = config->src_maxburst;
+ } else {
+ dev_err(&pl08x->adev->dev,
+ "bad runtime_config: alien transfer direction\n");
+ return;
+ }
+
+ switch (addr_width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ cctl |= (PL080_WIDTH_8BIT << PL080_CONTROL_SWIDTH_SHIFT) |
+ (PL080_WIDTH_8BIT << PL080_CONTROL_DWIDTH_SHIFT);
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ cctl |= (PL080_WIDTH_16BIT << PL080_CONTROL_SWIDTH_SHIFT) |
+ (PL080_WIDTH_16BIT << PL080_CONTROL_DWIDTH_SHIFT);
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ cctl |= (PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT) |
+ (PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT);
+ break;
+ default:
+ dev_err(&pl08x->adev->dev,
+ "bad runtime_config: alien address width\n");
+ return;
+ }
+
+ /*
+ * Now decide on a maxburst:
+ * If this channel will only request single transfers, set
+ * this down to ONE element.
+ */
+ if (plchan->cd->single) {
+ cctl |= (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) |
+ (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT);
+ } else {
+ while (i < ARRAY_SIZE(burst_sizes)) {
+ if (burst_sizes[i].burstwords <= maxburst)
+ break;
+ i++;
+ }
+ cctl |= burst_sizes[i].reg;
+ }
+
+ /* Access the cell in privileged mode, non-bufferable, non-cacheable */
+ cctl &= ~PL080_CONTROL_PROT_MASK;
+ cctl |= PL080_CONTROL_PROT_SYS;
+
+ /* Modify the default channel data to fit PrimeCell request */
+ cd->cctl = cctl;
+ cd->ccfg = ccfg;
+
+ dev_dbg(&pl08x->adev->dev,
+ "configured channel %s (%s) for %s, data width %d, "
+ "maxburst %d words, LE, CCTL=%08x, CCFG=%08x\n",
+ dma_chan_name(chan), plchan->name,
+ (config->direction == DMA_FROM_DEVICE) ? "RX" : "TX",
+ addr_width,
+ maxburst,
+ cctl, ccfg);
+}
+
+/*
+ * Slave transactions callback to the slave device to allow
+ * synchronization of slave DMA signals with the DMAC enable
+ */
+static void pl08x_issue_pending(struct dma_chan *chan)
+{
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ struct pl08x_driver_data *pl08x = plchan->host;
+ unsigned long flags;
+
+ spin_lock_irqsave(&plchan->lock, flags);
+ /* Something is already active */
+ if (plchan->at) {
+ spin_unlock_irqrestore(&plchan->lock, flags);
+ return;
+ }
+
+ /* Didn't get a physical channel so waiting for it ... */
+ if (plchan->state == PL08X_CHAN_WAITING)
+ return;
+
+ /* Take the first element in the queue and execute it */
+ if (!list_empty(&plchan->desc_list)) {
+ struct pl08x_txd *next;
+
+ next = list_first_entry(&plchan->desc_list,
+ struct pl08x_txd,
+ node);
+ list_del(&next->node);
+ plchan->at = next;
+ plchan->state = PL08X_CHAN_RUNNING;
+
+ /* Configure the physical channel for the active txd */
+ pl08x_config_phychan_for_txd(plchan);
+ pl08x_set_cregs(pl08x, plchan->phychan);
+ pl08x_enable_phy_chan(pl08x, plchan->phychan);
+ }
+
+ spin_unlock_irqrestore(&plchan->lock, flags);
+}
+
+static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
+ struct pl08x_txd *txd)
+{
+ int num_llis;
+ struct pl08x_driver_data *pl08x = plchan->host;
+ int ret;
+
+ num_llis = pl08x_fill_llis_for_desc(pl08x, txd);
+
+ if (!num_llis)
+ return -EINVAL;
+
+ spin_lock_irqsave(&plchan->lock, plchan->lockflags);
+
+ /*
+ * If this device is not using a circular buffer then
+ * queue this new descriptor for transfer.
+ * The descriptor for a circular buffer continues
+ * to be used until the channel is freed.
+ */
+ if (txd->cd->circular_buffer)
+ dev_err(&pl08x->adev->dev,
+ "%s attempting to queue a circular buffer\n",
+ __func__);
+ else
+ list_add_tail(&txd->node,
+ &plchan->desc_list);
+
+ /*
+ * See if we already have a physical channel allocated,
+ * else this is the time to try to get one.
+ */
+ ret = prep_phy_channel(plchan, txd);
+ if (ret) {
+ /*
+ * No physical channel available, we will
+ * stack up the memcpy channels until there is a channel
+ * available to handle it whereas slave transfers may
+ * have been denied due to platform channel muxing restrictions
+ * and since there is no guarantee that this will ever be
+ * resolved, and since the signal must be aquired AFTER
+ * aquiring the physical channel, we will let them be NACK:ed
+ * with -EBUSY here. The drivers can alway retry the prep()
+ * call if they are eager on doing this using DMA.
+ */
+ if (plchan->slave) {
+ pl08x_free_txd_list(pl08x, plchan);
+ spin_unlock_irqrestore(&plchan->lock, plchan->lockflags);
+ return -EBUSY;
+ }
+ /* Do this memcpy whenever there is a channel ready */
+ plchan->state = PL08X_CHAN_WAITING;
+ plchan->waiting = txd;
+ } else
+ /*
+ * Else we're all set, paused and ready to roll,
+ * status will switch to PL08X_CHAN_RUNNING when
+ * we call issue_pending(). If there is something
+ * running on the channel already we don't change
+ * its state.
+ */
+ if (plchan->state == PL08X_CHAN_IDLE)
+ plchan->state = PL08X_CHAN_PAUSED;
+
+ /*
+ * Notice that we leave plchan->lock locked on purpose:
+ * it will be unlocked in the subsequent tx_submit()
+ * call. This is a consequence of the current API.
+ */
+
+ return 0;
+}
+
+/*
+ * Initialize a descriptor to be used by memcpy submit
+ */
+static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
+ struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
+ size_t len, unsigned long flags)
+{
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ struct pl08x_driver_data *pl08x = plchan->host;
+ struct pl08x_txd *txd;
+ int ret;
+
+ txd = kzalloc(sizeof(struct pl08x_txd), GFP_NOWAIT);
+ if (!txd) {
+ dev_err(&pl08x->adev->dev,
+ "%s no memory for descriptor\n", __func__);
+ return NULL;
+ }
+
+ dma_async_tx_descriptor_init(&txd->tx, chan);
+ txd->direction = DMA_NONE;
+ txd->srcbus.addr = src;
+ txd->dstbus.addr = dest;
+
+ /* Set platform data for m2m */
+ txd->cd = &pl08x->pd->memcpy_channel;
+ /* Both to be incremented or the code will break */
+ txd->cd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR;
+ txd->tx.tx_submit = pl08x_tx_submit;
+ txd->tx.callback = NULL;
+ txd->tx.callback_param = NULL;
+ txd->len = len;
+
+ INIT_LIST_HEAD(&txd->node);
+ ret = pl08x_prep_channel_resources(plchan, txd);
+ if (ret)
+ return NULL;
+ /*
+ * NB: the channel lock is held at this point so tx_submit()
+ * must be called in direct succession.
+ */
+
+ return &txd->tx;
+}
+
+struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
+ struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_data_direction direction,
+ unsigned long flags)
+{
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ struct pl08x_driver_data *pl08x = plchan->host;
+ struct pl08x_txd *txd;
+ int ret;
+
+ /*
+ * Current implementation ASSUMES only one sg
+ */
+ if (sg_len != 1) {
+ dev_err(&pl08x->adev->dev, "%s prepared too long sglist\n",
+ __func__);
+ BUG();
+ }
+
+ dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n",
+ __func__, sgl->length, plchan->name);
+
+ txd = kzalloc(sizeof(struct pl08x_txd), GFP_NOWAIT);
+ if (!txd) {
+ dev_err(&pl08x->adev->dev, "%s no txd\n", __func__);
+ return NULL;
+ }
+
+ dma_async_tx_descriptor_init(&txd->tx, chan);
+
+ if (direction != plchan->runtime_direction)
+ dev_err(&pl08x->adev->dev, "%s DMA setup does not match "
+ "the direction configured for the PrimeCell\n",
+ __func__);
+
+ /*
+ * Set up addresses, the PrimeCell configured address
+ * will take precedence since this may configure the
+ * channel target address dynamically at runtime.
+ */
+ txd->direction = direction;
+ if (direction == DMA_TO_DEVICE) {
+ txd->srcbus.addr = sgl->dma_address;
+ if (plchan->runtime_addr)
+ txd->dstbus.addr = plchan->runtime_addr;
+ else
+ txd->dstbus.addr = plchan->cd->addr;
+ } else if (direction == DMA_FROM_DEVICE) {
+ if (plchan->runtime_addr)
+ txd->srcbus.addr = plchan->runtime_addr;
+ else
+ txd->srcbus.addr = plchan->cd->addr;
+ txd->dstbus.addr = sgl->dma_address;
+ } else {
+ dev_err(&pl08x->adev->dev,
+ "%s direction unsupported\n", __func__);
+ return NULL;
+ }
+ txd->cd = plchan->cd;
+ txd->tx.tx_submit = pl08x_tx_submit;
+ txd->tx.callback = NULL;
+ txd->tx.callback_param = NULL;
+ txd->len = sgl->length;
+ INIT_LIST_HEAD(&txd->node);
+
+ ret = pl08x_prep_channel_resources(plchan, txd);
+ if (ret)
+ return NULL;
+ /*
+ * NB: the channel lock is held at this point so tx_submit()
+ * must be called in direct succession.
+ */
+
+ return &txd->tx;
+}
+
+static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ struct pl08x_driver_data *pl08x = plchan->host;
+ unsigned long flags;
+ int ret = 0;
+
+ /* Controls applicable to inactive channels */
+ if (cmd == DMA_SLAVE_CONFIG) {
+ dma_set_runtime_config(chan,
+ (struct dma_slave_config *)
+ arg);
+ return 0;
+ }
+
+ /*
+ * Anything succeeds on channels with no physical allocation and
+ * no queued transfers.
+ */
+ spin_lock_irqsave(&plchan->lock, flags);
+ if (!plchan->phychan && !plchan->at) {
+ spin_unlock_irqrestore(&plchan->lock, flags);
+ return 0;
+ }
+
+ switch (cmd) {
+ case DMA_TERMINATE_ALL:
+ plchan->state = PL08X_CHAN_IDLE;
+
+ if (plchan->phychan) {
+ pl08x_stop_phy_chan(plchan->phychan);
+
+ /*
+ * Mark physical channel as free and free any slave
+ * signal
+ */
+ if ((plchan->phychan->signal >= 0) &&
+ pl08x->pd->put_signal) {
+ pl08x->pd->put_signal(plchan);
+ plchan->phychan->signal = -1;
+ }
+ pl08x_put_phy_channel(pl08x, plchan->phychan);
+ plchan->phychan = NULL;
+ }
+ /* Stop any pending tasklet */
+ tasklet_disable(&plchan->tasklet);
+ /* Dequeue jobs and free LLIs */
+ if (plchan->at) {
+ pl08x_free_txd(pl08x, plchan->at);
+ plchan->at = NULL;
+ }
+ /* Dequeue jobs not yet fired as well */
+ pl08x_free_txd_list(pl08x, plchan);
+ break;
+ case DMA_PAUSE:
+ pl08x_pause_phy_chan(plchan->phychan);
+ plchan->state = PL08X_CHAN_PAUSED;
+ break;
+ case DMA_RESUME:
+ pl08x_resume_phy_chan(plchan->phychan);
+ plchan->state = PL08X_CHAN_RUNNING;
+ break;
+ default:
+ /* Unknown command */
+ ret = -ENXIO;
+ break;
+ }
+
+ spin_unlock_irqrestore(&plchan->lock, flags);
+
+ return ret;
+}
+
+bool pl08x_filter_id(struct dma_chan *chan, void *chan_id)
+{
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ char *name = chan_id;
+
+ /* Check that the channel is not taken! */
+ if (!strcmp(plchan->name, name))
+ return true;
+
+ return false;
+}
+
+/*
+ * Just check that the device is there and active
+ * TODO: turn this bit on/off depending on the number of
+ * physical channels actually used, if it is zero... well
+ * shut it off. That will save some power. Cut the clock
+ * at the same time.
+ */
+static void pl08x_ensure_on(struct pl08x_driver_data *pl08x)
+{
+ u32 val;
+
+ val = readl(pl08x->base + PL080_CONFIG);
+ val &= ~(PL080_CONFIG_M2_BE | PL080_CONFIG_M1_BE | PL080_CONFIG_ENABLE);
+ /* We implictly clear bit 1 and that means little-endian mode */
+ val |= PL080_CONFIG_ENABLE;
+ writel(val, pl08x->base + PL080_CONFIG);
+}
+
+static void pl08x_tasklet(unsigned long data)
+{
+ struct pl08x_dma_chan *plchan = (struct pl08x_dma_chan *) data;
+ struct pl08x_phy_chan *phychan = plchan->phychan;
+ struct pl08x_driver_data *pl08x = plchan->host;
+
+ if (!plchan)
+ BUG();
+
+ spin_lock(&plchan->lock);
+
+ if (plchan->at) {
+ dma_async_tx_callback callback =
+ plchan->at->tx.callback;
+ void *callback_param =
+ plchan->at->tx.callback_param;
+
+ /*
+ * Update last completed
+ */
+ plchan->lc =
+ (plchan->at->tx.cookie);
+
+ /*
+ * Callback to signal completion
+ */
+ if (callback)
+ callback(callback_param);
+
+ /*
+ * Device callbacks should NOT clear
+ * the current transaction on the channel
+ * Linus: sometimes they should?
+ */
+ if (!plchan->at)
+ BUG();
+
+ /*
+ * Free the descriptor if it's not for a device
+ * using a circular buffer
+ */
+ if (!plchan->at->cd->circular_buffer) {
+ pl08x_free_txd(pl08x, plchan->at);
+ plchan->at = NULL;
+ }
+ /*
+ * else descriptor for circular
+ * buffers only freed when
+ * client has disabled dma
+ */
+ }
+ /*
+ * If a new descriptor is queued, set it up
+ * plchan->at is NULL here
+ */
+ if (!list_empty(&plchan->desc_list)) {
+ struct pl08x_txd *next;
+
+ next = list_first_entry(&plchan->desc_list,
+ struct pl08x_txd,
+ node);
+ list_del(&next->node);
+ plchan->at = next;
+ /* Configure the physical channel for the next txd */
+ pl08x_config_phychan_for_txd(plchan);
+ pl08x_set_cregs(pl08x, plchan->phychan);
+ pl08x_enable_phy_chan(pl08x, plchan->phychan);
+ } else {
+ struct pl08x_dma_chan *waiting = NULL;
+
+ /*
+ * No more jobs, so free up the physical channel
+ * Free any allocated signal on slave transfers too
+ */
+ if ((phychan->signal >= 0) && pl08x->pd->put_signal) {
+ pl08x->pd->put_signal(plchan);
+ phychan->signal = -1;
+ }
+ pl08x_put_phy_channel(pl08x, phychan);
+ plchan->phychan = NULL;
+ plchan->state = PL08X_CHAN_IDLE;
+
+ /*
+ * And NOW before anyone else can grab that free:d
+ * up physical channel, see if there is some memcpy
+ * pending that seriously needs to start because of
+ * being stacked up while we were choking the
+ * physical channels with data.
+ */
+ list_for_each_entry(waiting, &pl08x->memcpy.channels,
+ chan.device_node) {
+ if (waiting->state == PL08X_CHAN_WAITING &&
+ waiting->waiting != NULL) {
+ int ret;
+
+ /* This should REALLY not fail now */
+ ret = prep_phy_channel(waiting,
+ waiting->waiting);
+ BUG_ON(ret);
+ waiting->state = PL08X_CHAN_RUNNING;
+ waiting->waiting = NULL;
+ pl08x_issue_pending(&waiting->chan);
+ break;
+ }
+ }
+ }
+
+ spin_unlock(&plchan->lock);
+}
+
+static irqreturn_t pl08x_irq(int irq, void *dev)
+{
+ struct pl08x_driver_data *pl08x = dev;
+ u32 mask = 0;
+ u32 val;
+ int i;
+
+ val = readl(pl08x->base + PL080_ERR_STATUS);
+ if (val) {
+ /*
+ * An error interrupt (on one or more channels)
+ */
+ dev_err(&pl08x->adev->dev,
+ "%s error interrupt, register value 0x%08x\n",
+ __func__, val);
+ /*
+ * Simply clear ALL PL08X error interrupts,
+ * regardless of channel and cause
+ * FIXME: should be 0x00000003 on PL081 really.
+ */
+ writel(0x000000FF, pl08x->base + PL080_ERR_CLEAR);
+ }
+ val = readl(pl08x->base + PL080_INT_STATUS);
+ for (i = 0; i < pl08x->vd->channels; i++) {
+ if ((1 << i) & val) {
+ /* Locate physical channel */
+ struct pl08x_phy_chan *phychan = &pl08x->phy_chans[i];
+ struct pl08x_dma_chan *plchan = phychan->serving;
+
+ /* Schedule tasklet on this channel */
+ tasklet_schedule(&plchan->tasklet);
+
+ mask |= (1 << i);
+ }
+ }
+ /*
+ * Clear only the terminal interrupts on channels we processed
+ */
+ writel(mask, pl08x->base + PL080_TC_CLEAR);
+
+ return mask ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/*
+ * Initialise the DMAC memcpy/slave channels.
+ * Make a local wrapper to hold required data
+ */
+static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
+ struct dma_device *dmadev,
+ unsigned int channels,
+ bool slave)
+{
+ struct pl08x_dma_chan *chan;
+ int i;
+
+ INIT_LIST_HEAD(&dmadev->channels);
+ /*
+ * Register as many many memcpy as we have physical channels,
+ * we won't always be able to use all but the code will have
+ * to cope with that situation.
+ */
+ for (i = 0; i < channels; i++) {
+ chan = kzalloc(sizeof(struct pl08x_dma_chan), GFP_KERNEL);
+ if (!chan) {
+ dev_err(&pl08x->adev->dev,
+ "%s no memory for channel\n", __func__);
+ return -ENOMEM;
+ }
+
+ chan->host = pl08x;
+ chan->state = PL08X_CHAN_IDLE;
+
+ if (slave) {
+ chan->slave = true;
+ chan->name = pl08x->pd->slave_channels[i].bus_id;
+ chan->cd = &pl08x->pd->slave_channels[i];
+ } else {
+ chan->cd = &pl08x->pd->memcpy_channel;
+ chan->name = kasprintf(GFP_KERNEL, "memcpy%d", i);
+ if (!chan->name) {
+ kfree(chan);
+ return -ENOMEM;
+ }
+ }
+ dev_info(&pl08x->adev->dev,
+ "initialize virtual channel \"%s\"\n",
+ chan->name);
+
+ chan->chan.device = dmadev;
+ atomic_set(&chan->last_issued, 0);
+ chan->lc = atomic_read(&chan->last_issued);
+
+ spin_lock_init(&chan->lock);
+ INIT_LIST_HEAD(&chan->desc_list);
+ tasklet_init(&chan->tasklet, pl08x_tasklet,
+ (unsigned long) chan);
+
+ list_add_tail(&chan->chan.device_node, &dmadev->channels);
+ }
+ dev_info(&pl08x->adev->dev, "initialized %d virtual %s channels\n",
+ i, slave ? "slave" : "memcpy");
+ return i;
+}
+
+static void pl08x_free_virtual_channels(struct dma_device *dmadev)
+{
+ struct pl08x_dma_chan *chan = NULL;
+ struct pl08x_dma_chan *next;
+
+ list_for_each_entry_safe(chan,
+ next, &dmadev->channels, chan.device_node) {
+ list_del(&chan->chan.device_node);
+ kfree(chan);
+ }
+}
+
+#ifdef CONFIG_DEBUG_FS
+static const char *pl08x_state_str(enum pl08x_dma_chan_state state)
+{
+ switch (state) {
+ case PL08X_CHAN_IDLE:
+ return "idle";
+ case PL08X_CHAN_RUNNING:
+ return "running";
+ case PL08X_CHAN_PAUSED:
+ return "paused";
+ case PL08X_CHAN_WAITING:
+ return "waiting";
+ default:
+ break;
+ }
+ return "UNKNOWN STATE";
+}
+
+static int pl08x_debugfs_show(struct seq_file *s, void *data)
+{
+ struct pl08x_driver_data *pl08x = s->private;
+ struct pl08x_dma_chan *chan;
+ struct pl08x_phy_chan *ch;
+ unsigned long flags;
+ int i;
+
+ seq_printf(s, "PL08x physical channels:\n");
+ seq_printf(s, "CHANNEL:\tUSER:\n");
+ seq_printf(s, "--------\t-----\n");
+ for (i = 0; i < pl08x->vd->channels; i++) {
+ struct pl08x_dma_chan *virt_chan;
+
+ ch = &pl08x->phy_chans[i];
+
+ spin_lock_irqsave(&ch->lock, flags);
+ virt_chan = ch->serving;
+
+ seq_printf(s, "%d\t\t%s\n",
+ ch->id, virt_chan ? virt_chan->name : "(none)");
+
+ spin_unlock_irqrestore(&ch->lock, flags);
+ }
+
+ seq_printf(s, "\nPL08x virtual memcpy channels:\n");
+ seq_printf(s, "CHANNEL:\tSTATE:\n");
+ seq_printf(s, "--------\t------\n");
+ list_for_each_entry(chan, &pl08x->memcpy.channels, chan.device_node) {
+ seq_printf(s, "%s\t\t\%s\n", chan->name,
+ pl08x_state_str(chan->state));
+ }
+
+ seq_printf(s, "\nPL08x virtual slave channels:\n");
+ seq_printf(s, "CHANNEL:\tSTATE:\n");
+ seq_printf(s, "--------\t------\n");
+ list_for_each_entry(chan, &pl08x->slave.channels, chan.device_node) {
+ seq_printf(s, "%s\t\t\%s\n", chan->name,
+ pl08x_state_str(chan->state));
+ }
+
+ return 0;
+}
+
+static int pl08x_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pl08x_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations pl08x_debugfs_operations = {
+ .open = pl08x_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void init_pl08x_debugfs(struct pl08x_driver_data *pl08x)
+{
+ /* Expose a simple debugfs interface to view all clocks */
+ (void) debugfs_create_file(dev_name(&pl08x->adev->dev), S_IFREG | S_IRUGO,
+ NULL, pl08x,
+ &pl08x_debugfs_operations);
+}
+
+#else
+static inline void init_pl08x_debugfs(struct pl08x_driver_data *pl08x)
+{
+}
+#endif
+
+static int pl08x_probe(struct amba_device *adev, struct amba_id *id)
+{
+ struct pl08x_driver_data *pl08x;
+ struct vendor_data *vd = id->data;
+ int ret = 0;
+ int i;
+
+ ret = amba_request_regions(adev, NULL);
+ if (ret)
+ return ret;
+
+ /* Create the driver state holder */
+ pl08x = kzalloc(sizeof(struct pl08x_driver_data), GFP_KERNEL);
+ if (!pl08x) {
+ ret = -ENOMEM;
+ goto out_no_pl08x;
+ }
+
+ /* Initialize memcpy engine */
+ dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask);
+ pl08x->memcpy.dev = &adev->dev;
+ pl08x->memcpy.device_alloc_chan_resources = pl08x_alloc_chan_resources;
+ pl08x->memcpy.device_free_chan_resources = pl08x_free_chan_resources;
+ pl08x->memcpy.device_prep_dma_memcpy = pl08x_prep_dma_memcpy;
+ pl08x->memcpy.device_prep_dma_interrupt = pl08x_prep_dma_interrupt;
+ pl08x->memcpy.device_tx_status = pl08x_dma_tx_status;
+ pl08x->memcpy.device_issue_pending = pl08x_issue_pending;
+ pl08x->memcpy.device_control = pl08x_control;
+
+ /* Initialize slave engine */
+ dma_cap_set(DMA_SLAVE, pl08x->slave.cap_mask);
+ pl08x->slave.dev = &adev->dev;
+ pl08x->slave.device_alloc_chan_resources = pl08x_alloc_chan_resources;
+ pl08x->slave.device_free_chan_resources = pl08x_free_chan_resources;
+ pl08x->slave.device_prep_dma_interrupt = pl08x_prep_dma_interrupt;
+ pl08x->slave.device_tx_status = pl08x_dma_tx_status;
+ pl08x->slave.device_issue_pending = pl08x_issue_pending;
+ pl08x->slave.device_prep_slave_sg = pl08x_prep_slave_sg;
+ pl08x->slave.device_control = pl08x_control;
+
+ /* Get the platform data */
+ pl08x->pd = dev_get_platdata(&adev->dev);
+ if (!pl08x->pd) {
+ dev_err(&adev->dev, "no platform data supplied\n");
+ goto out_no_platdata;
+ }
+
+ /* Assign useful pointers to the driver state */
+ pl08x->adev = adev;
+ pl08x->vd = vd;
+
+ /* A DMA memory pool for LLIs, align on 1-byte boundary */
+ pl08x->pool = dma_pool_create(DRIVER_NAME, &pl08x->adev->dev,
+ PL08X_LLI_TSFR_SIZE, PL08X_ALIGN, 0);
+ if (!pl08x->pool) {
+ ret = -ENOMEM;
+ goto out_no_lli_pool;
+ }
+
+ spin_lock_init(&pl08x->lock);
+
+ pl08x->base = ioremap(adev->res.start, resource_size(&adev->res));
+ if (!pl08x->base) {
+ ret = -ENOMEM;
+ goto out_no_ioremap;
+ }
+
+ /* Turn on the PL08x */
+ pl08x_ensure_on(pl08x);
+
+ /*
+ * Attach the interrupt handler
+ */
+ writel(0x000000FF, pl08x->base + PL080_ERR_CLEAR);
+ writel(0x000000FF, pl08x->base + PL080_TC_CLEAR);
+
+ ret = request_irq(adev->irq[0], pl08x_irq, IRQF_DISABLED,
+ vd->name, pl08x);
+ if (ret) {
+ dev_err(&adev->dev, "%s failed to request interrupt %d\n",
+ __func__, adev->irq[0]);
+ goto out_no_irq;
+ }
+
+ /* Initialize physical channels */
+ pl08x->phy_chans = kmalloc((vd->channels * sizeof(struct pl08x_phy_chan)),
+ GFP_KERNEL);
+ if (!pl08x->phy_chans) {
+ dev_err(&adev->dev, "%s failed to allocate "
+ "physical channel holders\n",
+ __func__);
+ goto out_no_phychans;
+ }
+
+ for (i = 0; i < vd->channels; i++) {
+ struct pl08x_phy_chan *ch = &pl08x->phy_chans[i];
+
+ ch->id = i;
+ ch->base = pl08x->base + PL080_Cx_BASE(i);
+ spin_lock_init(&ch->lock);
+ ch->serving = NULL;
+ ch->signal = -1;
+ dev_info(&adev->dev,
+ "physical channel %d is %s\n", i,
+ pl08x_phy_channel_busy(ch) ? "BUSY" : "FREE");
+ }
+
+ /* Register as many memcpy channels as there are physical channels */
+ ret = pl08x_dma_init_virtual_channels(pl08x, &pl08x->memcpy,
+ pl08x->vd->channels, false);
+ if (ret <= 0) {
+ dev_warn(&pl08x->adev->dev,
+ "%s failed to enumerate memcpy channels - %d\n",
+ __func__, ret);
+ goto out_no_memcpy;
+ }
+ pl08x->memcpy.chancnt = ret;
+
+ /* Register slave channels */
+ ret = pl08x_dma_init_virtual_channels(pl08x, &pl08x->slave,
+ pl08x->pd->num_slave_channels,
+ true);
+ if (ret <= 0) {
+ dev_warn(&pl08x->adev->dev,
+ "%s failed to enumerate slave channels - %d\n",
+ __func__, ret);
+ goto out_no_slave;
+ }
+ pl08x->slave.chancnt = ret;
+
+ ret = dma_async_device_register(&pl08x->memcpy);
+ if (ret) {
+ dev_warn(&pl08x->adev->dev,
+ "%s failed to register memcpy as an async device - %d\n",
+ __func__, ret);
+ goto out_no_memcpy_reg;
+ }
+
+ ret = dma_async_device_register(&pl08x->slave);
+ if (ret) {
+ dev_warn(&pl08x->adev->dev,
+ "%s failed to register slave as an async device - %d\n",
+ __func__, ret);
+ goto out_no_slave_reg;
+ }
+
+ amba_set_drvdata(adev, pl08x);
+ init_pl08x_debugfs(pl08x);
+ dev_info(&pl08x->adev->dev, "ARM(R) %s DMA block initialized @%08x\n",
+ vd->name, adev->res.start);
+ return 0;
+
+out_no_slave_reg:
+ dma_async_device_unregister(&pl08x->memcpy);
+out_no_memcpy_reg:
+ pl08x_free_virtual_channels(&pl08x->slave);
+out_no_slave:
+ pl08x_free_virtual_channels(&pl08x->memcpy);
+out_no_memcpy:
+ kfree(pl08x->phy_chans);
+out_no_phychans:
+ free_irq(adev->irq[0], pl08x);
+out_no_irq:
+ iounmap(pl08x->base);
+out_no_ioremap:
+ dma_pool_destroy(pl08x->pool);
+out_no_lli_pool:
+out_no_platdata:
+ kfree(pl08x);
+out_no_pl08x:
+ amba_release_regions(adev);
+ return ret;
+}
+
+/* PL080 has 8 channels and the PL080 have just 2 */
+static struct vendor_data vendor_pl080 = {
+ .name = "PL080",
+ .channels = 8,
+ .dualmaster = true,
+};
+
+static struct vendor_data vendor_pl081 = {
+ .name = "PL081",
+ .channels = 2,
+ .dualmaster = false,
+};
+
+static struct amba_id pl08x_ids[] = {
+ /* PL080 */
+ {
+ .id = 0x00041080,
+ .mask = 0x000fffff,
+ .data = &vendor_pl080,
+ },
+ /* PL081 */
+ {
+ .id = 0x00041081,
+ .mask = 0x000fffff,
+ .data = &vendor_pl081,
+ },
+ /* Nomadik 8815 PL080 variant */
+ {
+ .id = 0x00280880,
+ .mask = 0x00ffffff,
+ .data = &vendor_pl080,
+ },
+ { 0, 0 },
+};
+
+static struct amba_driver pl08x_amba_driver = {
+ .drv.name = DRIVER_NAME,
+ .id_table = pl08x_ids,
+ .probe = pl08x_probe,
+};
+
+static int __init pl08x_init(void)
+{
+ int retval;
+ retval = amba_driver_register(&pl08x_amba_driver);
+ if (retval)
+ printk(KERN_WARNING DRIVER_NAME
+ "failed to register as an amba device (%d)\n",
+ retval);
+ return retval;
+}
+subsys_initcall(pl08x_init);
diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c
index a724e6be1b4..557e2272e5b 100644
--- a/drivers/dma/coh901318.c
+++ b/drivers/dma/coh901318.c
@@ -72,6 +72,9 @@ struct coh901318_chan {
unsigned long nbr_active_done;
unsigned long busy;
+ u32 runtime_addr;
+ u32 runtime_ctrl;
+
struct coh901318_base *base;
};
@@ -190,6 +193,9 @@ static inline struct coh901318_chan *to_coh901318_chan(struct dma_chan *chan)
static inline dma_addr_t
cohc_dev_addr(struct coh901318_chan *cohc)
{
+ /* Runtime supplied address will take precedence */
+ if (cohc->runtime_addr)
+ return cohc->runtime_addr;
return cohc->base->platform->chan_conf[cohc->id].dev_addr;
}
@@ -1055,6 +1061,14 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
params = cohc_chan_param(cohc);
config = params->config;
+ /*
+ * Add runtime-specific control on top, make
+ * sure the bits you set per peripheral channel are
+ * cleared in the default config from the platform.
+ */
+ ctrl_chained |= cohc->runtime_ctrl;
+ ctrl_last |= cohc->runtime_ctrl;
+ ctrl |= cohc->runtime_ctrl;
if (direction == DMA_TO_DEVICE) {
u32 tx_flags = COH901318_CX_CTRL_PRDD_SOURCE |
@@ -1113,6 +1127,12 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (ret)
goto err_lli_fill;
+ /*
+ * Set the default ctrl for the channel to the one from the lli,
+ * things may have changed due to odd buffer alignment etc.
+ */
+ coh901318_set_ctrl(cohc, lli->control);
+
COH_DBG(coh901318_list_print(cohc, lli));
/* Pick a descriptor to handle this transfer */
@@ -1175,6 +1195,146 @@ coh901318_issue_pending(struct dma_chan *chan)
spin_unlock_irqrestore(&cohc->lock, flags);
}
+/*
+ * Here we wrap in the runtime dma control interface
+ */
+struct burst_table {
+ int burst_8bit;
+ int burst_16bit;
+ int burst_32bit;
+ u32 reg;
+};
+
+static const struct burst_table burst_sizes[] = {
+ {
+ .burst_8bit = 64,
+ .burst_16bit = 32,
+ .burst_32bit = 16,
+ .reg = COH901318_CX_CTRL_BURST_COUNT_64_BYTES,
+ },
+ {
+ .burst_8bit = 48,
+ .burst_16bit = 24,
+ .burst_32bit = 12,
+ .reg = COH901318_CX_CTRL_BURST_COUNT_48_BYTES,
+ },
+ {
+ .burst_8bit = 32,
+ .burst_16bit = 16,
+ .burst_32bit = 8,
+ .reg = COH901318_CX_CTRL_BURST_COUNT_32_BYTES,
+ },
+ {
+ .burst_8bit = 16,
+ .burst_16bit = 8,
+ .burst_32bit = 4,
+ .reg = COH901318_CX_CTRL_BURST_COUNT_16_BYTES,
+ },
+ {
+ .burst_8bit = 8,
+ .burst_16bit = 4,
+ .burst_32bit = 2,
+ .reg = COH901318_CX_CTRL_BURST_COUNT_8_BYTES,
+ },
+ {
+ .burst_8bit = 4,
+ .burst_16bit = 2,
+ .burst_32bit = 1,
+ .reg = COH901318_CX_CTRL_BURST_COUNT_4_BYTES,
+ },
+ {
+ .burst_8bit = 2,
+ .burst_16bit = 1,
+ .burst_32bit = 0,
+ .reg = COH901318_CX_CTRL_BURST_COUNT_2_BYTES,
+ },
+ {
+ .burst_8bit = 1,
+ .burst_16bit = 0,
+ .burst_32bit = 0,
+ .reg = COH901318_CX_CTRL_BURST_COUNT_1_BYTE,
+ },
+};
+
+static void coh901318_dma_set_runtimeconfig(struct dma_chan *chan,
+ struct dma_slave_config *config)
+{
+ struct coh901318_chan *cohc = to_coh901318_chan(chan);
+ dma_addr_t addr;
+ enum dma_slave_buswidth addr_width;
+ u32 maxburst;
+ u32 runtime_ctrl = 0;
+ int i = 0;
+
+ /* We only support mem to per or per to mem transfers */
+ if (config->direction == DMA_FROM_DEVICE) {
+ addr = config->src_addr;
+ addr_width = config->src_addr_width;
+ maxburst = config->src_maxburst;
+ } else if (config->direction == DMA_TO_DEVICE) {
+ addr = config->dst_addr;
+ addr_width = config->dst_addr_width;
+ maxburst = config->dst_maxburst;
+ } else {
+ dev_err(COHC_2_DEV(cohc), "illegal channel mode\n");
+ return;
+ }
+
+ dev_dbg(COHC_2_DEV(cohc), "configure channel for %d byte transfers\n",
+ addr_width);
+ switch (addr_width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ runtime_ctrl |=
+ COH901318_CX_CTRL_SRC_BUS_SIZE_8_BITS |
+ COH901318_CX_CTRL_DST_BUS_SIZE_8_BITS;
+
+ while (i < ARRAY_SIZE(burst_sizes)) {
+ if (burst_sizes[i].burst_8bit <= maxburst)
+ break;
+ i++;
+ }
+
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ runtime_ctrl |=
+ COH901318_CX_CTRL_SRC_BUS_SIZE_16_BITS |
+ COH901318_CX_CTRL_DST_BUS_SIZE_16_BITS;
+
+ while (i < ARRAY_SIZE(burst_sizes)) {
+ if (burst_sizes[i].burst_16bit <= maxburst)
+ break;
+ i++;
+ }
+
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ /* Direction doesn't matter here, it's 32/32 bits */
+ runtime_ctrl |=
+ COH901318_CX_CTRL_SRC_BUS_SIZE_32_BITS |
+ COH901318_CX_CTRL_DST_BUS_SIZE_32_BITS;
+
+ while (i < ARRAY_SIZE(burst_sizes)) {
+ if (burst_sizes[i].burst_32bit <= maxburst)
+ break;
+ i++;
+ }
+
+ break;
+ default:
+ dev_err(COHC_2_DEV(cohc),
+ "bad runtimeconfig: alien address width\n");
+ return;
+ }
+
+ runtime_ctrl |= burst_sizes[i].reg;
+ dev_dbg(COHC_2_DEV(cohc),
+ "selected burst size %d bytes for address width %d bytes, maxburst %d\n",
+ burst_sizes[i].burst_8bit, addr_width, maxburst);
+
+ cohc->runtime_addr = addr;
+ cohc->runtime_ctrl = runtime_ctrl;
+}
+
static int
coh901318_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg)
@@ -1184,6 +1344,14 @@ coh901318_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
struct coh901318_desc *cohd;
void __iomem *virtbase = cohc->base->virtbase;
+ if (cmd == DMA_SLAVE_CONFIG) {
+ struct dma_slave_config *config =
+ (struct dma_slave_config *) arg;
+
+ coh901318_dma_set_runtimeconfig(chan, config);
+ return 0;
+ }
+
if (cmd == DMA_PAUSE) {
coh901318_pause(chan);
return 0;
@@ -1240,6 +1408,7 @@ coh901318_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
return 0;
}
+
void coh901318_base_init(struct dma_device *dma, const int *pick_chans,
struct coh901318_base *base)
{
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 9d31d5eb95c..7eced641ae7 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -702,7 +702,7 @@ int dma_async_device_register(struct dma_device *device)
BUG_ON(!device->dev);
/* note: this only matters in the
- * CONFIG_ASYNC_TX_DISABLE_CHANNEL_SWITCH=y case
+ * CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH=n case
*/
if (device_has_all_tx_types(device))
dma_cap_set(DMA_ASYNC_TX, device->cap_mask);
@@ -976,7 +976,7 @@ void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx,
struct dma_chan *chan)
{
tx->chan = chan;
- #ifndef CONFIG_ASYNC_TX_DISABLE_CHANNEL_SWITCH
+ #ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
spin_lock_init(&tx->lock);
#endif
}
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index c426829f6ab..27a7debc6fa 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -1,24 +1,33 @@
/*
- * driver/dma/ste_dma40.c
- *
- * Copyright (C) ST-Ericsson 2007-2010
+ * Copyright (C) ST-Ericsson SA 2007-2010
+ * Author: Per Friden <per.friden@stericsson.com> for ST-Ericsson
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
* License terms: GNU General Public License (GPL) version 2
- * Author: Per Friden <per.friden@stericsson.com>
- * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
- *
*/
#include <linux/kernel.h>
-#include <linux/slab.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/pm.h>
+#include <linux/err.h>
#include <plat/ste_dma40.h>
+#include <mach/regulator.h>
+
#include "ste_dma40_ll.h"
+#ifdef CONFIG_STE_DMA40_DEBUG
+#include "ste_dma40_debug.h"
+#define MARK sted40_history_text((char *)__func__)
+#else
+#define MARK
+#endif
+
#define D40_NAME "dma40"
#define D40_PHY_CHAN -1
@@ -30,14 +39,21 @@
/* Maximum iterations taken before giving up suspending a channel */
#define D40_SUSPEND_MAX_IT 500
+/* Hardware requirement on LCLA alignment */
+#define LCLA_ALIGNMENT 0x40000
+
+/* Max number of links per event group */
+#define D40_LCLA_LINK_PER_EVENT_GRP 128
+#define D40_LCLA_END D40_LCLA_LINK_PER_EVENT_GRP
+
+/* Attempts before giving up to trying to get pages that are aligned */
+#define MAX_LCLA_ALLOC_ATTEMPTS 256
+
+/* Bit markings for allocation map */
#define D40_ALLOC_FREE (1 << 31)
#define D40_ALLOC_PHY (1 << 30)
#define D40_ALLOC_LOG_FREE 0
-/* The number of free d40_desc to keep in memory before starting
- * to kfree() them */
-#define D40_DESC_CACHE_SIZE 50
-
/* Hardware designer of the block */
#define D40_PERIPHID2_DESIGNER 0x8
@@ -56,6 +72,51 @@ enum d40_command {
D40_DMA_SUSPENDED = 3
};
+/*
+ * These are the registers that has to be saved and later restored
+ * when the DMA hw is powered off.
+ * TODO: Add save/restore of D40_DREG_GCC on dma40 v3 or later, if that works.
+ */
+static u32 d40_backup_regs[] = {
+ D40_DREG_LCPA,
+ D40_DREG_LCLA,
+ D40_DREG_PRMSE,
+ D40_DREG_PRMSO,
+ D40_DREG_PRMOE,
+ D40_DREG_PRMOO,
+};
+
+/* TODO: Check if all these registers have to be saved/restored on dma40 v3 */
+static u32 d40_backup_regs_v3[] = {
+ D40_DREG_PSEG1,
+ D40_DREG_PSEG2,
+ D40_DREG_PSEG3,
+ D40_DREG_PSEG4,
+ D40_DREG_PCEG1,
+ D40_DREG_PCEG2,
+ D40_DREG_PCEG3,
+ D40_DREG_PCEG4,
+ D40_DREG_RSEG1,
+ D40_DREG_RSEG2,
+ D40_DREG_RSEG3,
+ D40_DREG_RSEG4,
+ D40_DREG_RCEG1,
+ D40_DREG_RCEG2,
+ D40_DREG_RCEG3,
+ D40_DREG_RCEG4,
+};
+
+static u32 d40_backup_regs_chan[] = {
+ D40_CHAN_REG_SSCFG,
+ D40_CHAN_REG_SSELT,
+ D40_CHAN_REG_SSPTR,
+ D40_CHAN_REG_SSLNK,
+ D40_CHAN_REG_SDCFG,
+ D40_CHAN_REG_SDELT,
+ D40_CHAN_REG_SDPTR,
+ D40_CHAN_REG_SDLNK,
+};
+
/**
* struct d40_lli_pool - Structure for keeping LLIs in memory
*
@@ -68,9 +129,9 @@ enum d40_command {
*/
struct d40_lli_pool {
void *base;
- int size;
+ int size;
/* Space for dst and src, plus an extra for padding */
- u8 pre_alloc_lli[3 * sizeof(struct d40_phy_lli)];
+ u8 pre_alloc_lli[3 * sizeof(struct d40_phy_lli)];
};
/**
@@ -80,54 +141,58 @@ struct d40_lli_pool {
* points into the lli_pool, to base if lli_len > 1 or to pre_alloc_lli if
* lli_len equals one.
* @lli_log: Same as above but for logical channels.
+ * @last_lcla: lcla used for last link (logical channels)
* @lli_pool: The pool with two entries pre-allocated.
- * @lli_len: Number of LLI's in lli_pool
- * @lli_tcount: Number of LLIs processed in the transfer. When equals lli_len
- * then this transfer job is done.
+ * @lli_len: Number of llis of current descriptor.
+ * @lli_current: Number of transfered llis.
+ * @lcla_alloc: Number of LCLA entries allocated.
* @txd: DMA engine struct. Used for among other things for communication
* during a transfer.
* @node: List entry.
- * @dir: The transfer direction of this job.
* @is_in_client_list: true if the client owns this descriptor.
+ * @is_hw_linked: true if this job will automatically be continued for
+ * the previous one.
+ * @cyclic: true if this is a cyclic job
*
* This descriptor is used for both logical and physical transfers.
*/
-
struct d40_desc {
/* LLI physical */
struct d40_phy_lli_bidir lli_phy;
/* LLI logical */
struct d40_log_lli_bidir lli_log;
+ struct d40_log_lli *last_lcla;
struct d40_lli_pool lli_pool;
- u32 lli_len;
- u32 lli_tcount;
+ int lli_len;
+ int lli_current;
+ int lcla_alloc;
struct dma_async_tx_descriptor txd;
struct list_head node;
- enum dma_data_direction dir;
bool is_in_client_list;
+ bool is_hw_linked;
+ bool cyclic;
};
/**
* struct d40_lcla_pool - LCLA pool settings and data.
*
- * @base: The virtual address of LCLA.
- * @phy: Physical base address of LCLA.
- * @base_size: size of lcla.
+ * @base: The virtual address of LCLA. 18 bit aligned.
+ * @base_unaligned: The orignal kmalloc pointer, if kmalloc is used.
+ * This pointer is only there for clean-up on error.
+ * @pages: The number of pages needed for all physical channels.
+ * Only used later for clean-up on error
* @lock: Lock to protect the content in this struct.
- * @alloc_map: Mapping between physical channel and LCLA entries.
- * @num_blocks: The number of entries of alloc_map. Equals to the
- * number of physical channels.
+ * @alloc_map: big map over which LCLA entry is own by which job.
*/
struct d40_lcla_pool {
void *base;
- dma_addr_t phy;
- resource_size_t base_size;
+ void *base_unaligned;
+ int pages;
spinlock_t lock;
- u32 *alloc_map;
- int num_blocks;
+ struct d40_desc **alloc_map;
};
/**
@@ -135,17 +200,17 @@ struct d40_lcla_pool {
* channels.
*
* @lock: A lock protection this entity.
+ * @reserved: True if used by secure world or otherwise.
* @num: The physical channel number of this entity.
* @allocated_src: Bit mapped to show which src event line's are mapped to
* this physical channel. Can also be free or physically allocated.
* @allocated_dst: Same as for src but is dst.
* allocated_dst and allocated_src uses the D40_ALLOC* defines as well as
- * event line number. Both allocated_src and allocated_dst can not be
- * allocated to a physical channel, since the interrupt handler has then
- * no way of figure out which one the interrupt belongs to.
+ * event line number.
*/
struct d40_phy_res {
spinlock_t lock;
+ bool reserved;
int num;
u32 allocated_src;
u32 allocated_dst;
@@ -163,22 +228,25 @@ struct d40_base;
* @pending_tx: The number of pending transfers. Used between interrupt handler
* and tasklet.
* @busy: Set to true when transfer is ongoing on this channel.
- * @phy_chan: Pointer to physical channel which this instance runs on.
+ * @phy_chan: Pointer to physical channel which this instance runs on. If this
+ * point is NULL, then the channel is not allocated.
* @chan: DMA engine handle.
* @tasklet: Tasklet that gets scheduled from interrupt context to complete a
* transfer and call client callback.
* @client: Cliented owned descriptor list.
* @active: Active descriptor.
+ * @done: Completed jobs
* @queue: Queued jobs.
- * @free: List of free descripts, ready to be reused.
- * @free_len: Number of descriptors in the free list.
* @dma_cfg: The client configuration of this dma channel.
+ * @configured: whether the dma_cfg configuration is valid
* @base: Pointer to the device instance struct.
+ * @cdesc: Cyclic descriptor
* @src_def_cfg: Default cfg register setting for src.
* @dst_def_cfg: Default cfg register setting for dst.
* @log_def: Default logical channel settings.
- * @lcla: Space for one dst src pair for logical channel transfers.
* @lcpa: Pointer to dst and src lcpa settings.
+ * @src_dev_addr: device source address for the channel transfer.
+ * @dst_dev_addr: device destination address for the channel transfer.
*
* This struct can either "be" a logical or a physical channel.
*/
@@ -194,17 +262,22 @@ struct d40_chan {
struct tasklet_struct tasklet;
struct list_head client;
struct list_head active;
+ struct list_head done;
struct list_head queue;
- struct list_head free;
- int free_len;
struct stedma40_chan_cfg dma_cfg;
+ bool configured;
struct d40_base *base;
+ struct stedma40_cyclic_desc *cdesc;
/* Default register configurations */
u32 src_def_cfg;
u32 dst_def_cfg;
struct d40_def_lcsp log_def;
- struct d40_lcla_elem lcla;
struct d40_log_lli_full *lcpa;
+ /* Runtime reconfiguration */
+ dma_addr_t runtime_addr;
+ enum dma_data_direction runtime_direction;
+ dma_addr_t src_dev_addr;
+ dma_addr_t dst_dev_addr;
};
/**
@@ -215,7 +288,9 @@ struct d40_chan {
* the same physical register.
* @dev: The device structure.
* @virtbase: The virtual base address of the DMA's register.
+ * @rev: silicon revision detected.
* @clk: Pointer to the DMA clock structure.
+ * @regulator: Pointer to the regulator struct that powers the DMA block.
* @phy_start: Physical memory start of the DMA registers.
* @phy_size: Size of the DMA register map.
* @irq: The IRQ number.
@@ -240,13 +315,23 @@ struct d40_chan {
* @lcpa_base: The virtual mapped address of LCPA.
* @phy_lcpa: The physical address of the LCPA.
* @lcpa_size: The size of the LCPA area.
+ * @desc_slab: cache for descriptors.
+ * @usage: The number of dma executions. Used by suspend to determite if
+ * the dma can suspend or not.
+ * @reg_val_backup: Here the values of some hardware registers are stored
+ * before the DMA is powered off. They are restored when the power is back on.
+ * @reg_val_backup_v3: Backup of registers that only exits on dma40 v3 and
+ * later.
+ * @reg_val_backup_chan: Backup data for standard channel parameter registers.
*/
struct d40_base {
spinlock_t interrupt_lock;
spinlock_t execmd_lock;
struct device *dev;
void __iomem *virtbase;
+ u8 rev:4;
struct clk *clk;
+ struct ux500_regulator *regulator;
phys_addr_t phy_start;
resource_size_t phy_size;
int irq;
@@ -266,6 +351,14 @@ struct d40_base {
void *lcpa_base;
dma_addr_t phy_lcpa;
resource_size_t lcpa_size;
+ struct kmem_cache *desc_slab;
+ int usage;
+ spinlock_t usage_lock;
+ u32 reg_val_backup
+ [ARRAY_SIZE(d40_backup_regs)];
+ u32 reg_val_backup_v3
+ [ARRAY_SIZE(d40_backup_regs_v3)];
+ u32 *reg_val_backup_chan;
};
/**
@@ -295,6 +388,21 @@ struct d40_reg_val {
unsigned int val;
};
+static struct device *chan2dev(struct d40_chan *d40c)
+{
+ return &d40c->chan.dev->device;
+}
+
+static bool chan_is_physical(struct d40_chan *chan)
+{
+ return chan->log_num == D40_PHY_CHAN;
+}
+
+static bool chan_is_logical(struct d40_chan *chan)
+{
+ return !chan_is_physical(chan);
+}
+
static int d40_pool_lli_alloc(struct d40_desc *d40d,
int lli_len, bool is_log)
{
@@ -330,9 +438,6 @@ static int d40_pool_lli_alloc(struct d40_desc *d40d,
align);
d40d->lli_phy.dst = PTR_ALIGN(d40d->lli_phy.src + lli_len,
align);
-
- d40d->lli_phy.src_addr = virt_to_phys(d40d->lli_phy.src);
- d40d->lli_phy.dst_addr = virt_to_phys(d40d->lli_phy.dst);
}
return 0;
@@ -347,27 +452,67 @@ static void d40_pool_lli_free(struct d40_desc *d40d)
d40d->lli_log.dst = NULL;
d40d->lli_phy.src = NULL;
d40d->lli_phy.dst = NULL;
- d40d->lli_phy.src_addr = 0;
- d40d->lli_phy.dst_addr = 0;
+ d40d->last_lcla = NULL;
}
-static dma_cookie_t d40_assign_cookie(struct d40_chan *d40c,
- struct d40_desc *desc)
+static int d40_lcla_alloc_one(struct d40_chan *d40c,
+ struct d40_desc *d40d)
{
- dma_cookie_t cookie = d40c->chan.cookie;
+ unsigned long flags;
+ int i;
+ int ret = -EINVAL;
- if (++cookie < 0)
- cookie = 1;
+ spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags);
- d40c->chan.cookie = cookie;
- desc->txd.cookie = cookie;
+ /*
+ * Allocate both src and dst at the same time, therefore the half
+ * start on 1 since 0 can't be used since zero is used as end marker.
+ */
+ for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) {
+ int idx = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP + i;
+
+ if (!d40c->base->lcla_pool.alloc_map[idx]) {
+ d40c->base->lcla_pool.alloc_map[idx] = d40d;
+ d40d->lcla_alloc++;
+ ret = i;
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&d40c->base->lcla_pool.lock, flags);
- return cookie;
+ return ret;
}
-static void d40_desc_reset(struct d40_desc *d40d)
+static int d40_lcla_free_all(struct d40_chan *d40c,
+ struct d40_desc *d40d)
{
- d40d->lli_tcount = 0;
+ unsigned long flags;
+ int i;
+ int ret = -EINVAL;
+
+ if (chan_is_physical(d40c))
+ return 0;
+
+ spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags);
+
+ for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) {
+ int idx = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP + i;
+
+ if (d40c->base->lcla_pool.alloc_map[idx] == d40d) {
+ d40c->base->lcla_pool.alloc_map[idx] = NULL;
+ d40d->lcla_alloc--;
+ if (d40d->lcla_alloc == 0) {
+ ret = 0;
+ break;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&d40c->base->lcla_pool.lock, flags);
+
+ return ret;
+
}
static void d40_desc_remove(struct d40_desc *d40d)
@@ -377,45 +522,37 @@ static void d40_desc_remove(struct d40_desc *d40d)
static struct d40_desc *d40_desc_get(struct d40_chan *d40c)
{
- struct d40_desc *desc;
- struct d40_desc *d;
- struct d40_desc *_d;
+ struct d40_desc *desc = NULL;
if (!list_empty(&d40c->client)) {
- list_for_each_entry_safe(d, _d, &d40c->client, node)
+ struct d40_desc *d;
+ struct d40_desc *_d;
+
+ list_for_each_entry_safe(d, _d, &d40c->client, node) {
if (async_tx_test_ack(&d->txd)) {
d40_pool_lli_free(d);
d40_desc_remove(d);
desc = d;
- goto out;
+ memset(desc, 0, sizeof(struct d40_desc));
+ break;
}
+ }
}
- if (list_empty(&d40c->free)) {
- /* Alloc new desc because we're out of used ones */
- desc = kzalloc(sizeof(struct d40_desc), GFP_NOWAIT);
- if (desc == NULL)
- goto out;
+ if (!desc)
+ desc = kmem_cache_zalloc(d40c->base->desc_slab, GFP_NOWAIT);
+
+ if (desc)
INIT_LIST_HEAD(&desc->node);
- } else {
- /* Reuse an old desc. */
- desc = list_first_entry(&d40c->free,
- struct d40_desc,
- node);
- list_del(&desc->node);
- d40c->free_len--;
- }
-out:
+
return desc;
}
static void d40_desc_free(struct d40_chan *d40c, struct d40_desc *d40d)
{
- if (d40c->free_len < D40_DESC_CACHE_SIZE) {
- list_add_tail(&d40d->node, &d40c->free);
- d40c->free_len++;
- } else
- kfree(d40d);
+
+ d40_lcla_free_all(d40c, d40d);
+ kmem_cache_free(d40c->base->desc_slab, d40d);
}
static void d40_desc_submit(struct d40_chan *d40c, struct d40_desc *desc)
@@ -423,6 +560,112 @@ static void d40_desc_submit(struct d40_chan *d40c, struct d40_desc *desc)
list_add_tail(&desc->node, &d40c->active);
}
+static void d40_desc_done(struct d40_chan *d40c, struct d40_desc *desc)
+{
+ list_add_tail(&desc->node, &d40c->done);
+}
+
+static int d40_desc_log_lli_to_lcxa(struct d40_chan *d40c,
+ struct d40_desc *d40d,
+ bool use_lcpa)
+{
+ struct d40_log_lli_bidir *lli = &d40d->lli_log;
+ int curr_lcla = -EINVAL;
+ int first_lcla = 0;
+
+ if ((d40d->lli_len - d40d->lli_current) > 1 ||
+ d40d->cyclic || !use_lcpa) {
+
+ curr_lcla = d40_lcla_alloc_one(d40c, d40d);
+ first_lcla = curr_lcla;
+ }
+
+ if (!d40d->cyclic && use_lcpa) {
+ d40_log_lli_lcpa_write(d40c->lcpa,
+ &lli->dst[d40d->lli_current],
+ &lli->src[d40d->lli_current],
+ curr_lcla,
+ curr_lcla == -EINVAL);
+
+ d40d->lli_current++;
+ }
+
+ /*
+ * Run only in LCPA space for non-cyclic. For cyclic, caller
+ * will handle the error.
+ */
+ if (first_lcla < 0)
+ return first_lcla;
+
+ for (; d40d->lli_current < d40d->lli_len; d40d->lli_current++) {
+ int lli_current = d40d->lli_current;
+ struct d40_log_lli *lcla;
+ int next_lcla;
+ bool interrupt;
+
+ if (d40d->lli_current + 1 < d40d->lli_len)
+ next_lcla = d40_lcla_alloc_one(d40c, d40d);
+ else
+ next_lcla = d40d->cyclic ? first_lcla : -EINVAL;
+
+ interrupt = d40d->cyclic
+ ? d40d->txd.flags & DMA_PREP_INTERRUPT
+ : next_lcla == -EINVAL;
+
+ if (d40d->cyclic && curr_lcla == first_lcla) {
+ /*
+ * For cyclic transactions, the first link is
+ * present in both LCPA and LCLA space because
+ * we can't link back to the one in LCPA space.
+ */
+ d40_log_lli_lcpa_write(d40c->lcpa,
+ &lli->dst[lli_current],
+ &lli->src[lli_current],
+ next_lcla,
+ interrupt);
+ }
+
+ lcla = d40c->base->lcla_pool.base +
+ d40c->phy_chan->num * 1024 +
+ 8 * curr_lcla * 2;
+
+ d40_log_lli_lcla_write(lcla,
+ &lli->dst[lli_current],
+ &lli->src[lli_current],
+ next_lcla,
+ interrupt);
+
+ if (d40d->lli_current == d40d->lli_len - 1)
+ d40d->last_lcla = lcla;
+
+ (void) dma_map_single(d40c->base->dev, lcla,
+ 2 * sizeof(struct d40_log_lli),
+ DMA_TO_DEVICE);
+
+ curr_lcla = next_lcla;
+
+ if (curr_lcla == -EINVAL || curr_lcla == first_lcla) {
+ d40d->lli_current++;
+ break;
+ }
+
+ }
+
+ return first_lcla;
+}
+
+static void d40_desc_load(struct d40_chan *d40c, struct d40_desc *d40d)
+{
+ if (chan_is_physical(d40c)) {
+ d40_phy_lli_write(d40c->base->virtbase,
+ d40c->phy_chan->num,
+ d40d->lli_phy.dst,
+ d40d->lli_phy.src);
+ d40d->lli_current = d40d->lli_len;
+ } else
+ (void) d40_desc_log_lli_to_lcxa(d40c, d40d, true);
+}
+
static struct d40_desc *d40_first_active_get(struct d40_chan *d40c)
{
struct d40_desc *d;
@@ -454,82 +697,194 @@ static struct d40_desc *d40_first_queued(struct d40_chan *d40c)
return d;
}
-/* Support functions for logical channels */
+static struct d40_desc *d40_first_done(struct d40_chan *d40c)
+{
+ if (list_empty(&d40c->done))
+ return NULL;
+
+ return list_first_entry(&d40c->done, struct d40_desc, node);
+}
+
-static int d40_lcla_id_get(struct d40_chan *d40c,
- struct d40_lcla_pool *pool)
+#ifdef CONFIG_PM
+static void d40_save_restore_registers(struct d40_base *base, bool save)
{
- int src_id = 0;
- int dst_id = 0;
- struct d40_log_lli *lcla_lidx_base =
- pool->base + d40c->phy_chan->num * 1024;
int i;
- int lli_per_log = d40c->base->plat_data->llis_per_log;
+ int j;
+ int idx;
+ void __iomem *addr;
- if (d40c->lcla.src_id >= 0 && d40c->lcla.dst_id >= 0)
- return 0;
+ /* Enable all clocks -- revisit after HW bug is fixed */
+ if (!save)
+ writel(D40_DREG_GCC_ENABLE_ALL, base->virtbase + D40_DREG_GCC);
- if (pool->num_blocks > 32)
- return -EINVAL;
+ /* Save/Restore channel specific registers */
+ for (i = 0; i < base->num_phy_chans; i++) {
+ if (base->phy_res[i].reserved)
+ continue;
- spin_lock(&pool->lock);
+ addr = base->virtbase + D40_DREG_PCBASE + i * D40_DREG_PCDELTA;
+ idx = i * ARRAY_SIZE(d40_backup_regs_chan);
- for (i = 0; i < pool->num_blocks; i++) {
- if (!(pool->alloc_map[d40c->phy_chan->num] & (0x1 << i))) {
- pool->alloc_map[d40c->phy_chan->num] |= (0x1 << i);
- break;
+ for (j = 0; j < ARRAY_SIZE(d40_backup_regs_chan); j++) {
+ if (save)
+ base->reg_val_backup_chan[idx + j] =
+ readl(addr + d40_backup_regs_chan[j]);
+ else
+ writel(base->reg_val_backup_chan[idx + j],
+ addr + d40_backup_regs_chan[j]);
}
}
- src_id = i;
- if (src_id >= pool->num_blocks)
- goto err;
- for (; i < pool->num_blocks; i++) {
- if (!(pool->alloc_map[d40c->phy_chan->num] & (0x1 << i))) {
- pool->alloc_map[d40c->phy_chan->num] |= (0x1 << i);
- break;
+ /* Save/Restore global registers */
+ for (i = 0 ; i < ARRAY_SIZE(d40_backup_regs); i++) {
+ addr = base->virtbase + d40_backup_regs[i];
+
+ if (save)
+ base->reg_val_backup[i] = readl(addr);
+ else
+ writel(base->reg_val_backup[i], addr);
+ }
+
+ /* Save/Restore registers only existing on dma40 v3 and later */
+ if (base->rev >= 3) {
+ for (i = 0 ; i < ARRAY_SIZE(d40_backup_regs_v3); i++) {
+ addr = base->virtbase + d40_backup_regs_v3[i];
+
+ if (save)
+ base->reg_val_backup_v3[i] = readl(addr);
+ else
+ writel(base->reg_val_backup_v3[i], addr);
}
}
+}
+#else
+static void d40_save_restore_registers(struct d40_base *base, bool save)
+{
+}
+#endif
- dst_id = i;
- if (dst_id == src_id)
- goto err;
+static void d40_power_off(struct d40_base *base, int phy_num)
+{
+ u32 gcc;
+ int i;
+ int j;
+ int p;
+
+ /*
+ * Disable the rest of the code because of GCC register HW bugs on v1
+ * which are not worth working around. Revisit later.
+ */
+ return;
+
+ /*
+ * Power off event group related to physical channel, if
+ * the other physical channels that belong to the same
+ * event group are not in use
+ */
- d40c->lcla.src_id = src_id;
- d40c->lcla.dst_id = dst_id;
- d40c->lcla.dst = lcla_lidx_base + dst_id * lli_per_log + 1;
- d40c->lcla.src = lcla_lidx_base + src_id * lli_per_log + 1;
+ for (j = 0; j < base->num_phy_chans; j += D40_GROUP_SIZE) {
+ for (i = 0; i < 2; i++) {
+ p = (((phy_num & (base->num_phy_chans - 1)) + i)
+ & (D40_GROUP_SIZE - 1)) + j;
+ if (p == phy_num)
+ continue;
+ /*
+ * If another physical channel in the same group is
+ * allocated, just return.
+ */
+ if (base->phy_res[p].allocated_dst == D40_ALLOC_PHY ||
+ base->phy_res[p].allocated_src == D40_ALLOC_PHY) {
+ return;
+ }
+ }
+ }
- spin_unlock(&pool->lock);
- return 0;
-err:
- spin_unlock(&pool->lock);
- return -EINVAL;
+ /* The GCC register is protected via the usage_lock */
+ gcc = readl(base->virtbase + D40_DREG_GCC);
+
+ gcc &= ~D40_DREG_GCC_EVTGRP_ENA(D40_PHYS_TO_GROUP(phy_num),
+ D40_DREG_GCC_SRC);
+ gcc &= ~D40_DREG_GCC_EVTGRP_ENA(D40_PHYS_TO_GROUP(phy_num),
+ D40_DREG_GCC_DST);
+
+ writel(gcc, base->virtbase + D40_DREG_GCC);
}
-static void d40_lcla_id_put(struct d40_chan *d40c,
- struct d40_lcla_pool *pool,
- int id)
+static void d40_power_on(struct d40_base *base, int phy_num)
{
- if (id < 0)
- return;
+ u32 gcc;
- d40c->lcla.src_id = -1;
- d40c->lcla.dst_id = -1;
+ /*
+ * Disable the rest of the code because of GCC register HW bugs on v1
+ * which are not worth working around. Revisit later.
+ */
+ return;
- spin_lock(&pool->lock);
- pool->alloc_map[d40c->phy_chan->num] &= (~(0x1 << id));
- spin_unlock(&pool->lock);
+ /* The GCC register is protected via the usage_lock */
+ gcc = readl(base->virtbase + D40_DREG_GCC);
+
+ gcc |= D40_DREG_GCC_EVTGRP_ENA(D40_PHYS_TO_GROUP(phy_num),
+ D40_DREG_GCC_SRC);
+ gcc |= D40_DREG_GCC_EVTGRP_ENA(D40_PHYS_TO_GROUP(phy_num),
+ D40_DREG_GCC_DST);
+
+ writel(gcc, base->virtbase + D40_DREG_GCC);
}
-static int d40_channel_execute_command(struct d40_chan *d40c,
- enum d40_command command)
+static void d40_usage_inc(struct d40_chan *d40c)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&d40c->base->usage_lock, flags);
+
+ d40c->base->usage++;
+
+ if (d40c->base->usage == 1) {
+ if (d40c->base->regulator)
+ ux500_regulator_atomic_enable(d40c->base->regulator);
+ d40_save_restore_registers(d40c->base, false);
+ }
+ d40_power_on(d40c->base, d40c->phy_chan->num);
+
+ spin_unlock_irqrestore(&d40c->base->usage_lock, flags);
+}
+
+static void d40_usage_dec(struct d40_chan *d40c)
{
- int status, i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&d40c->base->usage_lock, flags);
+
+ d40_power_off(d40c->base, d40c->phy_chan->num);
+
+ d40c->base->usage--;
+
+ if (d40c->base->usage == 0) {
+ d40_save_restore_registers(d40c->base, true);
+ if (d40c->base->regulator)
+ ux500_regulator_atomic_disable(d40c->base->regulator);
+ }
+
+ spin_unlock_irqrestore(&d40c->base->usage_lock, flags);
+}
+
+static int __d40_execute_command_phy(struct d40_chan *d40c,
+ enum d40_command command)
+{
+ u32 status;
+ int i;
void __iomem *active_reg;
int ret = 0;
unsigned long flags;
+ u32 wmask;
+
+ if (command == D40_DMA_STOP) {
+ ret = __d40_execute_command_phy(d40c, D40_DMA_SUSPEND_REQ);
+ if (ret)
+ return ret;
+ }
spin_lock_irqsave(&d40c->base->execmd_lock, flags);
@@ -547,7 +902,14 @@ static int d40_channel_execute_command(struct d40_chan *d40c,
goto done;
}
- writel(command << D40_CHAN_POS(d40c->phy_chan->num), active_reg);
+#ifdef CONFIG_STE_DMA40_DEBUG
+ if (command == D40_DMA_RUN)
+ sted40_history_snapshot();
+#endif
+
+ wmask = 0xffffffff & ~(D40_CHAN_POS_MASK(d40c->phy_chan->num));
+ writel(wmask | (command << D40_CHAN_POS(d40c->phy_chan->num)),
+ active_reg);
if (command == D40_DMA_SUSPEND_REQ) {
@@ -573,6 +935,9 @@ static int d40_channel_execute_command(struct d40_chan *d40c,
"[%s]: unable to suspend the chl %d (log: %d) status %x\n",
__func__, d40c->phy_chan->num, d40c->log_num,
status);
+#ifdef CONFIG_STE_DMA40_DEBUG
+ sted40_history_dump();
+#endif
dump_stack();
ret = -EBUSY;
}
@@ -586,135 +951,195 @@ done:
static void d40_term_all(struct d40_chan *d40c)
{
struct d40_desc *d40d;
- struct d40_desc *d;
- struct d40_desc *_d;
+
+ /* Release completed descriptors */
+ while ((d40d = d40_first_done(d40c))) {
+ d40_desc_remove(d40d);
+ d40_desc_free(d40c, d40d);
+ }
/* Release active descriptors */
while ((d40d = d40_first_active_get(d40c))) {
d40_desc_remove(d40d);
-
- /* Return desc to free-list */
d40_desc_free(d40c, d40d);
}
/* Release queued descriptors waiting for transfer */
while ((d40d = d40_first_queued(d40c))) {
d40_desc_remove(d40d);
-
- /* Return desc to free-list */
d40_desc_free(d40c, d40d);
}
- /* Release client owned descriptors */
- if (!list_empty(&d40c->client))
- list_for_each_entry_safe(d, _d, &d40c->client, node) {
- d40_pool_lli_free(d);
- d40_desc_remove(d);
- /* Return desc to free-list */
- d40_desc_free(d40c, d40d);
- }
-
- d40_lcla_id_put(d40c, &d40c->base->lcla_pool,
- d40c->lcla.src_id);
- d40_lcla_id_put(d40c, &d40c->base->lcla_pool,
- d40c->lcla.dst_id);
-
d40c->pending_tx = 0;
- d40c->busy = false;
}
-static void d40_config_set_event(struct d40_chan *d40c, bool do_enable)
+static void __d40_config_set_event(struct d40_chan *d40c, bool enable,
+ u32 event, int reg)
{
- u32 val;
- unsigned long flags;
+ void __iomem *addr = d40c->base->virtbase + D40_DREG_PCBASE
+ + d40c->phy_chan->num * D40_DREG_PCDELTA + reg;
+ int tries;
- if (do_enable)
- val = D40_ACTIVATE_EVENTLINE;
- else
- val = D40_DEACTIVATE_EVENTLINE;
+ if (!enable) {
+ writel((D40_DEACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event))
+ | ~D40_EVENTLINE_MASK(event), addr);
+ return;
+ }
- spin_lock_irqsave(&d40c->phy_chan->lock, flags);
+ /*
+ * The hardware sometimes doesn't register the enable when src and dst
+ * event lines are active on the same logical channel. Retry to ensure
+ * it does. Usually only one retry is sufficient.
+ */
+ tries = 100;
+ while (--tries) {
+ writel((D40_ACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event))
+ | ~D40_EVENTLINE_MASK(event), addr);
+ if (readl(addr) & D40_EVENTLINE_MASK(event))
+ break;
+ }
+
+ if (tries != 99)
+ dev_dbg(chan2dev(d40c),
+ "[%s] workaround enable S%cLNK (%d tries)\n",
+ __func__, reg == D40_CHAN_REG_SSLNK ? 'S' : 'D',
+ 100 - tries);
+
+ WARN_ON(!tries);
+}
+
+static void d40_config_set_event(struct d40_chan *d40c, bool do_enable)
+{
/* Enable event line connected to device (or memcpy) */
if ((d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) ||
(d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH)) {
u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type);
- writel((val << D40_EVENTLINE_POS(event)) |
- ~D40_EVENTLINE_MASK(event),
- d40c->base->virtbase + D40_DREG_PCBASE +
- d40c->phy_chan->num * D40_DREG_PCDELTA +
- D40_CHAN_REG_SSLNK);
+ __d40_config_set_event(d40c, do_enable, event,
+ D40_CHAN_REG_SSLNK);
}
+
if (d40c->dma_cfg.dir != STEDMA40_PERIPH_TO_MEM) {
u32 event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type);
- writel((val << D40_EVENTLINE_POS(event)) |
- ~D40_EVENTLINE_MASK(event),
- d40c->base->virtbase + D40_DREG_PCBASE +
- d40c->phy_chan->num * D40_DREG_PCDELTA +
- D40_CHAN_REG_SDLNK);
+ __d40_config_set_event(d40c, do_enable, event,
+ D40_CHAN_REG_SDLNK);
}
-
- spin_unlock_irqrestore(&d40c->phy_chan->lock, flags);
}
static u32 d40_chan_has_events(struct d40_chan *d40c)
{
- u32 val = 0;
+ u32 val;
- /* If SSLNK or SDLNK is zero all events are disabled */
- if ((d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) ||
- (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH))
- val = readl(d40c->base->virtbase + D40_DREG_PCBASE +
- d40c->phy_chan->num * D40_DREG_PCDELTA +
- D40_CHAN_REG_SSLNK);
-
- if (d40c->dma_cfg.dir != STEDMA40_PERIPH_TO_MEM)
- val = readl(d40c->base->virtbase + D40_DREG_PCBASE +
- d40c->phy_chan->num * D40_DREG_PCDELTA +
- D40_CHAN_REG_SDLNK);
+ val = readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SSLNK);
+
+ val |= readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SDLNK);
return val;
}
-static void d40_config_enable_lidx(struct d40_chan *d40c)
+static int
+__d40_execute_command_log(struct d40_chan *d40c, enum d40_command command)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&d40c->phy_chan->lock, flags);
+
+ switch (command) {
+ case D40_DMA_STOP:
+ case D40_DMA_SUSPEND_REQ:
+ ret = __d40_execute_command_phy(d40c, D40_DMA_SUSPEND_REQ);
+ if (ret)
+ goto out;
+
+ d40_config_set_event(d40c, false);
+
+ if (d40_chan_has_events(d40c))
+ ret = __d40_execute_command_phy(d40c, D40_DMA_RUN);
+ else if (command == D40_DMA_STOP)
+ ret = __d40_execute_command_phy(d40c, command);
+ break;
+
+ case D40_DMA_RUN:
+ if (d40c->base->rev == 0) {
+ ret = __d40_execute_command_phy(d40c,
+ D40_DMA_SUSPEND_REQ);
+ if (ret)
+ goto out;
+ }
+
+ d40_config_set_event(d40c, true);
+
+ ret = __d40_execute_command_phy(d40c, command);
+ break;
+
+ case D40_DMA_SUSPENDED:
+ BUG();
+ break;
+ }
+
+out:
+ spin_unlock_irqrestore(&d40c->phy_chan->lock, flags);
+ return ret;
+}
+
+static int d40_channel_execute_command(struct d40_chan *d40c,
+ enum d40_command command)
+{
+ if (chan_is_logical(d40c))
+ return __d40_execute_command_log(d40c, command);
+ else
+ return __d40_execute_command_phy(d40c, command);
+}
+
+static u32 d40_get_prmo(struct d40_chan *d40c)
{
- /* Set LIDX for lcla */
- writel((d40c->phy_chan->num << D40_SREG_ELEM_LOG_LIDX_POS) &
- D40_SREG_ELEM_LOG_LIDX_MASK,
- d40c->base->virtbase + D40_DREG_PCBASE +
- d40c->phy_chan->num * D40_DREG_PCDELTA + D40_CHAN_REG_SDELT);
+ static const unsigned int phy_map[] = {
+ [STEDMA40_PCHAN_BASIC_MODE]
+ = D40_DREG_PRMO_PCHAN_BASIC,
+ [STEDMA40_PCHAN_MODULO_MODE]
+ = D40_DREG_PRMO_PCHAN_MODULO,
+ [STEDMA40_PCHAN_DOUBLE_DST_MODE]
+ = D40_DREG_PRMO_PCHAN_DOUBLE_DST,
+ };
+ static const unsigned int log_map[] = {
+ [STEDMA40_LCHAN_SRC_PHY_DST_LOG]
+ = D40_DREG_PRMO_LCHAN_SRC_PHY_DST_LOG,
+ [STEDMA40_LCHAN_SRC_LOG_DST_PHY]
+ = D40_DREG_PRMO_LCHAN_SRC_LOG_DST_PHY,
+ [STEDMA40_LCHAN_SRC_LOG_DST_LOG]
+ = D40_DREG_PRMO_LCHAN_SRC_LOG_DST_LOG,
+ };
- writel((d40c->phy_chan->num << D40_SREG_ELEM_LOG_LIDX_POS) &
- D40_SREG_ELEM_LOG_LIDX_MASK,
- d40c->base->virtbase + D40_DREG_PCBASE +
- d40c->phy_chan->num * D40_DREG_PCDELTA + D40_CHAN_REG_SSELT);
+ if (chan_is_physical(d40c))
+ return phy_map[d40c->dma_cfg.mode_opt];
+ else
+ return log_map[d40c->dma_cfg.mode_opt];
}
-static int d40_config_write(struct d40_chan *d40c)
+static void d40_config_write(struct d40_chan *d40c)
{
u32 addr_base;
u32 var;
- int res;
-
- res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ);
- if (res)
- return res;
/* Odd addresses are even addresses + 4 */
addr_base = (d40c->phy_chan->num % 2) * 4;
/* Setup channel mode to logical or physical */
- var = ((u32)(d40c->log_num != D40_PHY_CHAN) + 1) <<
+ var = ((u32)(chan_is_logical(d40c)) + 1) <<
D40_CHAN_POS(d40c->phy_chan->num);
writel(var, d40c->base->virtbase + D40_DREG_PRMSE + addr_base);
/* Setup operational mode option register */
- var = ((d40c->dma_cfg.channel_type >> STEDMA40_INFO_CH_MODE_OPT_POS) &
- 0x3) << D40_CHAN_POS(d40c->phy_chan->num);
+ var = d40_get_prmo(d40c) << D40_CHAN_POS(d40c->phy_chan->num);
writel(var, d40c->base->virtbase + D40_DREG_PRMOE + addr_base);
- if (d40c->log_num != D40_PHY_CHAN) {
+ if (chan_is_logical(d40c)) {
/* Set default config for CFG reg */
writel(d40c->src_def_cfg,
d40c->base->virtbase + D40_DREG_PCBASE +
@@ -725,38 +1150,292 @@ static int d40_config_write(struct d40_chan *d40c)
d40c->phy_chan->num * D40_DREG_PCDELTA +
D40_CHAN_REG_SDCFG);
- d40_config_enable_lidx(d40c);
+ /* Set LIDX for lcla */
+ writel((d40c->phy_chan->num << D40_SREG_ELEM_LOG_LIDX_POS) &
+ D40_SREG_ELEM_LOG_LIDX_MASK,
+ d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SDELT);
+
+ writel((d40c->phy_chan->num << D40_SREG_ELEM_LOG_LIDX_POS) &
+ D40_SREG_ELEM_LOG_LIDX_MASK,
+ d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SSELT);
+
+ /* Clear LNK which will be used by d40_chan_has_events() */
+ writel(0,
+ d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SSLNK);
+ writel(0,
+ d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SDLNK);
}
+}
+
+static u32 d40_residue(struct d40_chan *d40c)
+{
+ u32 num_elt;
+
+ if (chan_is_logical(d40c))
+ num_elt = (readl(&d40c->lcpa->lcsp2) &
+ D40_MEM_LCSP2_ECNT_MASK) >> D40_MEM_LCSP2_ECNT_POS;
+ else
+ num_elt = (readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SDELT) &
+ D40_SREG_ELEM_PHY_ECNT_MASK) >>
+ D40_SREG_ELEM_PHY_ECNT_POS;
+ return num_elt * (1 << d40c->dma_cfg.dst_info.data_width);
+}
+
+static bool d40_tx_is_linked(struct d40_chan *d40c)
+{
+ bool is_link;
+
+ if (chan_is_logical(d40c))
+ is_link = readl(&d40c->lcpa->lcsp3) & D40_MEM_LCSP3_DLOS_MASK;
+ else
+ is_link = readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SDLNK) &
+ D40_SREG_LNK_PHYS_LNK_MASK;
+ return is_link;
+}
+
+static int d40_pause(struct dma_chan *chan)
+{
+ struct d40_chan *d40c =
+ container_of(chan, struct d40_chan, chan);
+ int res = 0;
+ unsigned long flags;
+
+ if (!d40c->busy)
+ return 0;
+
+ d40_usage_inc(d40c);
+ spin_lock_irqsave(&d40c->lock, flags);
+
+ res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ);
+
+ d40_usage_dec(d40c);
+
+ spin_unlock_irqrestore(&d40c->lock, flags);
return res;
}
-static void d40_desc_load(struct d40_chan *d40c, struct d40_desc *d40d)
+static int d40_resume(struct dma_chan *chan)
{
+ struct d40_chan *d40c =
+ container_of(chan, struct d40_chan, chan);
+ int res = 0;
+ unsigned long flags;
- if (d40d->lli_phy.dst && d40d->lli_phy.src) {
- d40_phy_lli_write(d40c->base->virtbase,
- d40c->phy_chan->num,
- d40d->lli_phy.dst,
- d40d->lli_phy.src);
- d40d->lli_tcount = d40d->lli_len;
- } else if (d40d->lli_log.dst && d40d->lli_log.src) {
- u32 lli_len;
- struct d40_log_lli *src = d40d->lli_log.src;
- struct d40_log_lli *dst = d40d->lli_log.dst;
+ if (!d40c->busy)
+ return 0;
- src += d40d->lli_tcount;
- dst += d40d->lli_tcount;
+ spin_lock_irqsave(&d40c->lock, flags);
- if (d40d->lli_len <= d40c->base->plat_data->llis_per_log)
- lli_len = d40d->lli_len;
- else
- lli_len = d40c->base->plat_data->llis_per_log;
- d40d->lli_tcount += lli_len;
- d40_log_lli_write(d40c->lcpa, d40c->lcla.src,
- d40c->lcla.dst,
- dst, src,
- d40c->base->plat_data->llis_per_log);
+ d40_usage_inc(d40c);
+
+ /* If bytes left to transfer or linked tx resume job */
+ if (d40_residue(d40c) || d40_tx_is_linked(d40c))
+ res = d40_channel_execute_command(d40c, D40_DMA_RUN);
+
+ d40_usage_dec(d40c);
+ spin_unlock_irqrestore(&d40c->lock, flags);
+ return res;
+}
+
+static bool __d40_tx_submit_link_log(struct d40_chan *d40c,
+ struct d40_desc *d40d,
+ struct d40_desc *prev)
+{
+ struct d40_log_lli *lastlcla = prev->last_lcla;
+ struct d40_log_lli_bidir *lli = &prev->lli_log;
+ int lastidx = prev->lli_len - 1;
+ int los;
+
+ los = d40_desc_log_lli_to_lcxa(d40c, d40d, false);
+ if (los < 0)
+ return false;
+
+ dma_sync_single_for_cpu(d40c->base->dev, virt_to_phys(lastlcla),
+ 2 * sizeof(struct d40_log_lli),
+ DMA_TO_DEVICE);
+
+ d40_log_lli_lcla_write(lastlcla,
+ &lli->dst[lastidx],
+ &lli->src[lastidx],
+ los,
+ true);
+
+ dma_sync_single_for_device(d40c->base->dev, virt_to_phys(lastlcla),
+ 2 * sizeof(struct d40_log_lli),
+ DMA_TO_DEVICE);
+
+ return true;
+}
+
+static bool d40_tx_submit_link_log(struct d40_chan *d40c,
+ struct d40_desc *d40d,
+ struct d40_desc *prev)
+{
+ struct d40_log_lli_bidir *lli = &prev->lli_log;
+ int lastidx = prev->lli_len - 1;
+ unsigned int secnt;
+ int active_slos;
+ int last_slos;
+
+ /*
+ * An only-LCPA transfer will either be transmitting the last link or
+ * be completed.
+ */
+ if (!prev->last_lcla)
+ return false;
+
+ /* We don't support linking to incompletely linked transfers */
+ if (prev->lli_current != prev->lli_len)
+ return false;
+
+ /* A transfer must be ongoing. */
+ secnt = (d40c->lcpa->lcsp0 & D40_MEM_LCSP0_ECNT_MASK)
+ >> D40_MEM_LCSP0_ECNT_POS;
+ if (!secnt) {
+ dev_vdbg(d40c->base->dev, "%s: secnt %u\n", __func__, secnt);
+ return false;
+ }
+
+ /*
+ * We don't support linking while the last link is being transmitted.
+ */
+ active_slos = (d40c->lcpa->lcsp1 & D40_MEM_LCSP1_SLOS_MASK)
+ >> D40_MEM_LCSP1_SLOS_POS;
+ last_slos = (lli->src[lastidx].lcsp13 & D40_MEM_LCSP1_SLOS_MASK)
+ >> D40_MEM_LCSP1_SLOS_POS;
+ if (active_slos == last_slos) {
+ dev_vdbg(d40c->base->dev, "%s: activ %d last %d secnt %u\n",
+ __func__, active_slos, last_slos, secnt);
+ return false;
+ }
+
+ dev_vdbg(d40c->base->dev,
+ "%s: active %#x last %#x, lli_len %d secnt %u\n",
+ __func__, active_slos, last_slos, prev->lli_len, secnt);
+
+ /*
+ * We're either transferring one of this desc's links or one from an
+ * earlier linked one. Either way, update this desc's last link.
+ */
+ return __d40_tx_submit_link_log(d40c, d40d, prev);
+}
+
+static bool __d40_tx_submit_link_phy(struct d40_chan *d40c,
+ struct d40_desc *d40d,
+ struct d40_desc *prev)
+{
+ int lastidx = prev->lli_len - 1;
+ struct d40_phy_lli *lastsrc = &prev->lli_phy.src[lastidx];
+ struct d40_phy_lli *lastdst = &prev->lli_phy.dst[lastidx];
+ struct d40_phy_lli *thissrc = &d40d->lli_phy.src[0];
+ struct d40_phy_lli *thisdst = &d40d->lli_phy.dst[0];
+ unsigned int val;
+
+ dma_sync_single_for_cpu(d40c->base->dev, virt_to_phys(lastsrc),
+ sizeof(*lastsrc), DMA_TO_DEVICE);
+
+ dma_sync_single_for_cpu(d40c->base->dev, virt_to_phys(lastdst),
+ sizeof(*lastdst), DMA_TO_DEVICE);
+
+ /* Keep the settings */
+ val = lastsrc->reg_lnk & ~D40_SREG_LNK_PHYS_LNK_MASK;
+ lastsrc->reg_lnk = val | virt_to_phys(thissrc);
+
+ val = lastdst->reg_lnk & ~D40_SREG_LNK_PHYS_LNK_MASK;
+ lastdst->reg_lnk = val | virt_to_phys(thisdst);
+
+ dma_sync_single_for_device(d40c->base->dev, virt_to_phys(lastsrc),
+ sizeof(*lastsrc), DMA_TO_DEVICE);
+
+ dma_sync_single_for_device(d40c->base->dev, virt_to_phys(lastdst),
+ sizeof(*lastdst), DMA_TO_DEVICE);
+
+ return true;
+}
+
+static bool d40_tx_submit_link_phy(struct d40_chan *d40c,
+ struct d40_desc *d40d,
+ struct d40_desc *prev)
+{
+
+ struct d40_phy_lli *lastsrc = &prev->lli_phy.src[prev->lli_len - 1];
+ unsigned int active_lnk;
+ unsigned int last_lnk;
+ unsigned int secnt;
+
+ /* A transfer must be ongoing. */
+ secnt = readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SSELT) >> D40_SREG_ELEM_LOG_ECNT_POS;
+ if (!secnt) {
+ dev_vdbg(d40c->base->dev, "%s: secnt %u\n", __func__, secnt);
+ return false;
+ }
+
+ active_lnk = readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SSLNK);
+
+ dma_sync_single_for_cpu(d40c->base->dev, virt_to_phys(lastsrc),
+ sizeof(*lastsrc), DMA_TO_DEVICE);
+
+ last_lnk = lastsrc->reg_lnk;
+
+ dma_sync_single_for_device(d40c->base->dev, virt_to_phys(lastsrc),
+ sizeof(*lastsrc), DMA_TO_DEVICE);
+
+ /*
+ * We don't support linking while the last link is being transmitted.
+ */
+ if (active_lnk == last_lnk) {
+ dev_vdbg(d40c->base->dev, "%s: active %d last %d secnt %u\n",
+ __func__, active_lnk, last_lnk, secnt);
+ return false;
}
+
+ dev_vdbg(d40c->base->dev,
+ "%s: activ %#x last %#x, lli_len %d secnt %u\n",
+ __func__, active_lnk, last_lnk, prev->lli_len, secnt);
+
+ /*
+ * We're either transferring one of this desc's links or one from an
+ * earlier linked one. Either way, update this desc's last link.
+ */
+ return __d40_tx_submit_link_phy(d40c, d40d, prev);
+}
+
+static bool d40_tx_submit_link(struct d40_chan *d40c, struct d40_desc *d40d)
+{
+ struct d40_desc *prev;
+ bool hwlinked;
+
+ if (list_empty(&d40c->active))
+ return false;
+
+ prev = list_entry(d40c->active.prev, struct d40_desc, node);
+
+ if (chan_is_physical(d40c))
+ hwlinked = d40_tx_submit_link_phy(d40c, d40d, prev);
+ else
+ hwlinked = d40_tx_submit_link_log(d40c, d40d, prev);
+
+ if (hwlinked)
+ dev_dbg(d40c->base->dev, "%s: hwlinked d40d %p prev %p\n",
+ __func__, d40d, prev);
+
+ return hwlinked;
}
static dma_cookie_t d40_tx_submit(struct dma_async_tx_descriptor *tx)
@@ -767,31 +1446,37 @@ static dma_cookie_t d40_tx_submit(struct dma_async_tx_descriptor *tx)
struct d40_desc *d40d = container_of(tx, struct d40_desc, txd);
unsigned long flags;
+ d40_usage_inc(d40c);
+
+ (void) d40_pause(&d40c->chan);
+
spin_lock_irqsave(&d40c->lock, flags);
- tx->cookie = d40_assign_cookie(d40c, d40d);
+ d40c->chan.cookie++;
+
+ if (d40c->chan.cookie < 0)
+ d40c->chan.cookie = 1;
- d40_desc_queue(d40c, d40d);
+ d40d->txd.cookie = d40c->chan.cookie;
+
+ d40d->is_hw_linked = d40_tx_submit_link(d40c, d40d);
+ if (d40d->is_hw_linked) {
+ /* Linked, therefore already active */
+ d40_desc_submit(d40c, d40d);
+ } else
+ d40_desc_queue(d40c, d40d);
spin_unlock_irqrestore(&d40c->lock, flags);
+ (void) d40_resume(&d40c->chan);
+ d40_usage_dec(d40c);
+
return tx->cookie;
}
static int d40_start(struct d40_chan *d40c)
{
- int err;
-
- if (d40c->log_num != D40_PHY_CHAN) {
- err = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ);
- if (err)
- return err;
- d40_config_set_event(d40c, true);
- }
-
- err = d40_channel_execute_command(d40c, D40_DMA_RUN);
-
- return err;
+ return d40_channel_execute_command(d40c, D40_DMA_RUN);
}
static struct d40_desc *d40_queue_start(struct d40_chan *d40c)
@@ -803,7 +1488,10 @@ static struct d40_desc *d40_queue_start(struct d40_chan *d40c)
d40d = d40_first_queued(d40c);
if (d40d != NULL) {
- d40c->busy = true;
+ if (!d40c->busy) {
+ d40_usage_inc(d40c);
+ d40c->busy = true;
+ }
/* Remove from queue */
d40_desc_remove(d40d);
@@ -811,14 +1499,25 @@ static struct d40_desc *d40_queue_start(struct d40_chan *d40c)
/* Add to active queue */
d40_desc_submit(d40c, d40d);
- /* Initiate DMA job */
- d40_desc_load(d40c, d40d);
+ /*
+ * If this job is already linked in hw,
+ * do not submit it.
+ */
- /* Start dma job */
- err = d40_start(d40c);
+ /*
+ * TODO: make sure is_hw_linked work all the time with logical
+ * jobs
+ */
+ if (!d40d->is_hw_linked) {
+ /* Initiate DMA job */
+ d40_desc_load(d40c, d40d);
- if (err)
- return NULL;
+ /* Start dma job */
+ err = d40_start(d40c);
+
+ if (err)
+ return NULL;
+ }
}
return d40d;
@@ -828,49 +1527,77 @@ static struct d40_desc *d40_queue_start(struct d40_chan *d40c)
static void dma_tc_handle(struct d40_chan *d40c)
{
struct d40_desc *d40d;
+ bool islastactive;
- if (!d40c->phy_chan)
+ if (d40c->cdesc) {
+ d40c->pending_tx++;
+ tasklet_schedule(&d40c->tasklet);
return;
+ }
/* Get first active entry from list */
+redo:
d40d = d40_first_active_get(d40c);
if (d40d == NULL)
return;
- if (d40d->lli_tcount < d40d->lli_len) {
+ d40_lcla_free_all(d40c, d40d);
+ if (d40d->lli_current < d40d->lli_len) {
d40_desc_load(d40c, d40d);
/* Start dma job */
(void) d40_start(d40c);
return;
}
- if (d40_queue_start(d40c) == NULL)
+ /*
+ * More than one active happens when we have
+ * hw linked transfers.
+ */
+ islastactive = list_is_last(&d40d->node, &d40c->active);
+ if (islastactive && d40_queue_start(d40c) == NULL) {
d40c->busy = false;
+ d40_usage_dec(d40c);
+ }
+
+ d40_desc_remove(d40d);
+ d40_desc_done(d40c, d40d);
d40c->pending_tx++;
tasklet_schedule(&d40c->tasklet);
+ /*
+ * When we have multiple active transfers, there is a chance that we
+ * might miss some link interrupts if the time to perform each link is
+ * very small (mostly with mem-to-mem transfers). So, if the hardware
+ * is not transmitting any more links, assume that all the active
+ * transfers are complete.
+ */
+ if (!islastactive && !d40_tx_is_linked(d40c))
+ goto redo;
}
static void dma_tasklet(unsigned long data)
{
struct d40_chan *d40c = (struct d40_chan *) data;
- struct d40_desc *d40d_fin;
+ struct d40_desc *d40d;
unsigned long flags;
dma_async_tx_callback callback;
void *callback_param;
spin_lock_irqsave(&d40c->lock, flags);
- /* Get first active entry from list */
- d40d_fin = d40_first_active_get(d40c);
-
- if (d40d_fin == NULL)
- goto err;
+ if (d40c->cdesc)
+ d40d = d40c->cdesc->d40d;
+ else {
+ /* Get first active entry from list */
+ d40d = d40_first_done(d40c);
+ if (d40d == NULL)
+ goto err;
- d40c->completed = d40d_fin->txd.cookie;
+ d40c->completed = d40d->txd.cookie;
+ }
/*
* If terminating a channel pending_tx is set to zero.
@@ -882,20 +1609,23 @@ static void dma_tasklet(unsigned long data)
}
/* Callback to client */
- callback = d40d_fin->txd.callback;
- callback_param = d40d_fin->txd.callback_param;
-
- if (async_tx_test_ack(&d40d_fin->txd)) {
- d40_pool_lli_free(d40d_fin);
- d40_desc_remove(d40d_fin);
- /* Return desc to free-list */
- d40_desc_free(d40c, d40d_fin);
+
+ if (d40c->cdesc) {
+ callback = d40c->cdesc->period_callback;
+ callback_param = d40c->cdesc->period_callback_param;
} else {
- d40_desc_reset(d40d_fin);
- if (!d40d_fin->is_in_client_list) {
- d40_desc_remove(d40d_fin);
- list_add_tail(&d40d_fin->node, &d40c->client);
- d40d_fin->is_in_client_list = true;
+ callback = d40d->txd.callback;
+ callback_param = d40d->txd.callback_param;
+
+ if (async_tx_test_ack(&d40d->txd)) {
+ d40_pool_lli_free(d40d);
+ d40_desc_remove(d40d);
+ d40_desc_free(d40c, d40d);
+ } else if (!d40d->is_in_client_list) {
+ d40_desc_remove(d40d);
+ d40_lcla_free_all(d40c, d40d);
+ list_add_tail(&d40d->node, &d40c->client);
+ d40d->is_in_client_list = true;
}
}
@@ -906,12 +1636,12 @@ static void dma_tasklet(unsigned long data)
spin_unlock_irqrestore(&d40c->lock, flags);
- if (callback)
+ if (callback && (d40d->txd.flags & DMA_PREP_INTERRUPT))
callback(callback_param);
return;
- err:
+err:
/* Rescue manouver if receiving double interrupts */
if (d40c->pending_tx > 0)
d40c->pending_tx--;
@@ -920,7 +1650,7 @@ static void dma_tasklet(unsigned long data)
static irqreturn_t d40_handle_interrupt(int irq, void *data)
{
- static const struct d40_interrupt_lookup il[] = {
+ static struct d40_interrupt_lookup il[] = {
{D40_DREG_LCTIS0, D40_DREG_LCICR0, false, 0},
{D40_DREG_LCTIS1, D40_DREG_LCICR1, false, 32},
{D40_DREG_LCTIS2, D40_DREG_LCICR2, false, 64},
@@ -935,7 +1665,6 @@ static irqreturn_t d40_handle_interrupt(int irq, void *data)
int i;
u32 regs[ARRAY_SIZE(il)];
- u32 tmp;
u32 idx;
u32 row;
long chan = -1;
@@ -944,7 +1673,9 @@ static irqreturn_t d40_handle_interrupt(int irq, void *data)
struct d40_base *base = data;
spin_lock_irqsave(&base->interrupt_lock, flags);
-
+#ifdef CONFIG_STE_DMA40_DEBUG
+ sted40_history_text("IRQ enter");
+#endif
/* Read interrupt status of both logical and physical channels */
for (i = 0; i < ARRAY_SIZE(il); i++)
regs[i] = readl(base->virtbase + il[i].src);
@@ -961,49 +1692,83 @@ static irqreturn_t d40_handle_interrupt(int irq, void *data)
row = chan / BITS_PER_LONG;
idx = chan & (BITS_PER_LONG - 1);
- /* ACK interrupt */
- tmp = readl(base->virtbase + il[row].clr);
- tmp |= 1 << idx;
- writel(tmp, base->virtbase + il[row].clr);
-
if (il[row].offset == D40_PHY_CHAN)
d40c = base->lookup_phy_chans[idx];
else
d40c = base->lookup_log_chans[il[row].offset + idx];
+
+ if (!d40c) {
+ /*
+ * No error because this can happen if something else
+ * in the system is using the channel.
+ */
+ continue;
+ }
+
+ /* ACK interrupt */
+ writel(1 << idx, base->virtbase + il[row].clr);
+
spin_lock(&d40c->lock);
- if (!il[row].is_error)
+ if (!il[row].is_error) {
+
dma_tc_handle(d40c);
- else
- dev_err(base->dev, "[%s] IRQ chan: %ld offset %d idx %d\n",
+ } else {
+ dev_err(base->dev, "[%s] Error IRQ chan: %ld offset %d idx %d\n",
__func__, chan, il[row].offset, idx);
+#ifdef CONFIG_STE_DMA40_DEBUG
+ sted40_history_dump();
+#endif
+ }
spin_unlock(&d40c->lock);
}
-
+#ifdef CONFIG_STE_DMA40_DEBUG
+ sted40_history_text("IRQ leave");
+#endif
spin_unlock_irqrestore(&base->interrupt_lock, flags);
return IRQ_HANDLED;
}
-
static int d40_validate_conf(struct d40_chan *d40c,
struct stedma40_chan_cfg *conf)
{
int res = 0;
u32 dst_event_group = D40_TYPE_TO_GROUP(conf->dst_dev_type);
u32 src_event_group = D40_TYPE_TO_GROUP(conf->src_dev_type);
- bool is_log = (conf->channel_type & STEDMA40_CHANNEL_IN_OPER_MODE)
- == STEDMA40_CHANNEL_IN_LOG_MODE;
+ bool is_log = conf->mode == STEDMA40_MODE_LOGICAL;
- if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH &&
+ if (!conf->dir) {
+ dev_err(&d40c->chan.dev->device, "[%s] Invalid direction.\n",
+ __func__);
+ res = -EINVAL;
+ }
+
+ if (conf->dst_dev_type != STEDMA40_DEV_DST_MEMORY &&
+ d40c->base->plat_data->dev_tx[conf->dst_dev_type] == 0) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Invalid TX channel address (%d)\n",
+ __func__, conf->dst_dev_type);
+ res = -EINVAL;
+ }
+
+ if (conf->src_dev_type != STEDMA40_DEV_SRC_MEMORY &&
+ d40c->base->plat_data->dev_rx[conf->src_dev_type] == 0) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Invalid RX channel address (%d)\n",
+ __func__, conf->src_dev_type);
+ res = -EINVAL;
+ }
+
+ if (conf->dir == STEDMA40_MEM_TO_PERIPH &&
dst_event_group == STEDMA40_DEV_DST_MEMORY) {
dev_err(&d40c->chan.dev->device, "[%s] Invalid dst\n",
__func__);
res = -EINVAL;
}
- if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM &&
+ if (conf->dir == STEDMA40_PERIPH_TO_MEM &&
src_event_group == STEDMA40_DEV_SRC_MEMORY) {
dev_err(&d40c->chan.dev->device, "[%s] Invalid src\n",
__func__);
@@ -1038,11 +1803,16 @@ static int d40_validate_conf(struct d40_chan *d40c,
return res;
}
-static bool d40_alloc_mask_set(struct d40_phy_res *phy, bool is_src,
- int log_event_line, bool is_log)
+static bool d40_alloc_mask_set(struct d40_phy_res *phy,
+ bool is_src, int log_event_line, bool is_log,
+ bool *first_user)
{
unsigned long flags;
spin_lock_irqsave(&phy->lock, flags);
+
+ *first_user = ((phy->allocated_src | phy->allocated_dst)
+ == D40_ALLOC_FREE);
+
if (!is_log) {
/* Physical interrupts are masked per physical full channel */
if (phy->allocated_src == D40_ALLOC_FREE &&
@@ -1097,7 +1867,6 @@ static bool d40_alloc_mask_free(struct d40_phy_res *phy, bool is_src,
spin_lock_irqsave(&phy->lock, flags);
if (!log_event_line) {
- /* Physical interrupts are masked per physical full channel */
phy->allocated_dst = D40_ALLOC_FREE;
phy->allocated_src = D40_ALLOC_FREE;
is_free = true;
@@ -1124,7 +1893,7 @@ out:
return is_free;
}
-static int d40_allocate_channel(struct d40_chan *d40c)
+static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user)
{
int dev_type;
int event_group;
@@ -1134,9 +1903,7 @@ static int d40_allocate_channel(struct d40_chan *d40c)
int j;
int log_num;
bool is_src;
- bool is_log = (d40c->dma_cfg.channel_type & STEDMA40_CHANNEL_IN_OPER_MODE)
- == STEDMA40_CHANNEL_IN_LOG_MODE;
-
+ bool is_log = d40c->dma_cfg.mode == STEDMA40_MODE_LOGICAL;
phys = d40c->base->phy_res;
@@ -1162,15 +1929,18 @@ static int d40_allocate_channel(struct d40_chan *d40c)
for (i = 0; i < d40c->base->num_phy_chans; i++) {
if (d40_alloc_mask_set(&phys[i], is_src,
- 0, is_log))
+ 0, is_log,
+ first_phy_user))
goto found_phy;
}
} else
for (j = 0; j < d40c->base->num_phy_chans; j += 8) {
int phy_num = j + event_group * 2;
for (i = phy_num; i < phy_num + 2; i++) {
- if (d40_alloc_mask_set(&phys[i], is_src,
- 0, is_log))
+ if (d40_alloc_mask_set(&phys[i],
+ is_src, 0,
+ is_log,
+ first_phy_user))
goto found_phy;
}
}
@@ -1194,13 +1964,15 @@ found_phy:
if (is_src) {
for (i = phy_num; i < phy_num + 2; i++) {
if (d40_alloc_mask_set(&phys[i], is_src,
- event_line, is_log))
+ event_line, is_log,
+ first_phy_user))
goto found_log;
}
} else {
for (i = phy_num + 1; i >= phy_num; i--) {
if (d40_alloc_mask_set(&phys[i], is_src,
- event_line, is_log))
+ event_line, is_log,
+ first_phy_user))
goto found_log;
}
}
@@ -1221,30 +1993,6 @@ out:
}
-static int d40_config_chan(struct d40_chan *d40c,
- struct stedma40_chan_cfg *info)
-{
-
- /* Fill in basic CFG register values */
- d40_phy_cfg(&d40c->dma_cfg, &d40c->src_def_cfg,
- &d40c->dst_def_cfg, d40c->log_num != D40_PHY_CHAN);
-
- if (d40c->log_num != D40_PHY_CHAN) {
- d40_log_cfg(&d40c->dma_cfg,
- &d40c->log_def.lcsp1, &d40c->log_def.lcsp3);
-
- if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM)
- d40c->lcpa = d40c->base->lcpa_base +
- d40c->dma_cfg.src_dev_type * 32;
- else
- d40c->lcpa = d40c->base->lcpa_base +
- d40c->dma_cfg.dst_dev_type * 32 + 16;
- }
-
- /* Write channel configuration to the DMA */
- return d40_config_write(d40c);
-}
-
static int d40_config_memcpy(struct d40_chan *d40c)
{
dma_cap_mask_t cap = d40c->chan.device->cap_mask;
@@ -1267,18 +2015,27 @@ static int d40_config_memcpy(struct d40_chan *d40c)
return 0;
}
-
static int d40_free_dma(struct d40_chan *d40c)
{
int res = 0;
- u32 event, dir;
+ u32 event;
struct d40_phy_res *phy = d40c->phy_chan;
bool is_src;
+ struct d40_desc *d;
+ struct d40_desc *_d;
/* Terminate all queued and active transfers */
d40_term_all(d40c);
+ /* Release client owned descriptors */
+ if (!list_empty(&d40c->client))
+ list_for_each_entry_safe(d, _d, &d40c->client, node) {
+ d40_pool_lli_free(d);
+ d40_desc_remove(d);
+ d40_desc_free(d40c, d);
+ }
+
if (phy == NULL) {
dev_err(&d40c->chan.dev->device, "[%s] phy == null\n",
__func__);
@@ -1292,22 +2049,12 @@ static int d40_free_dma(struct d40_chan *d40c)
return -EINVAL;
}
-
- res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ);
- if (res) {
- dev_err(&d40c->chan.dev->device, "[%s] suspend\n",
- __func__);
- return res;
- }
-
if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH ||
d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) {
event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type);
- dir = D40_CHAN_REG_SDLNK;
is_src = false;
} else if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) {
event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type);
- dir = D40_CHAN_REG_SSLNK;
is_src = true;
} else {
dev_err(&d40c->chan.dev->device,
@@ -1315,79 +2062,32 @@ static int d40_free_dma(struct d40_chan *d40c)
return -EINVAL;
}
- if (d40c->log_num != D40_PHY_CHAN) {
- /*
- * Release logical channel, deactivate the event line during
- * the time physical res is suspended.
- */
- writel((D40_DEACTIVATE_EVENTLINE << D40_EVENTLINE_POS(event)) &
- D40_EVENTLINE_MASK(event),
- d40c->base->virtbase + D40_DREG_PCBASE +
- phy->num * D40_DREG_PCDELTA + dir);
-
- d40c->base->lookup_log_chans[d40c->log_num] = NULL;
-
- /*
- * Check if there are more logical allocation
- * on this phy channel.
- */
- if (!d40_alloc_mask_free(phy, is_src, event)) {
- /* Resume the other logical channels if any */
- if (d40_chan_has_events(d40c)) {
- res = d40_channel_execute_command(d40c,
- D40_DMA_RUN);
- if (res) {
- dev_err(&d40c->chan.dev->device,
- "[%s] Executing RUN command\n",
- __func__);
- return res;
- }
- }
- return 0;
- }
- } else
- d40_alloc_mask_free(phy, is_src, 0);
+ d40_usage_inc(d40c);
- /* Release physical channel */
res = d40_channel_execute_command(d40c, D40_DMA_STOP);
if (res) {
- dev_err(&d40c->chan.dev->device,
- "[%s] Failed to stop channel\n", __func__);
+ dev_err(&d40c->chan.dev->device, "[%s] stop failed\n",
+ __func__);
+ d40_usage_dec(d40c);
return res;
}
- d40c->phy_chan = NULL;
- /* Invalidate channel type */
- d40c->dma_cfg.channel_type = 0;
- d40c->base->lookup_phy_chans[phy->num] = NULL;
-
- return 0;
-
-}
-
-static int d40_pause(struct dma_chan *chan)
-{
- struct d40_chan *d40c =
- container_of(chan, struct d40_chan, chan);
- int res;
+ d40_alloc_mask_free(phy, is_src, chan_is_logical(d40c) ? event : 0);
- unsigned long flags;
+ if (chan_is_logical(d40c))
+ d40c->base->lookup_log_chans[d40c->log_num] = NULL;
+ else
+ d40c->base->lookup_phy_chans[phy->num] = NULL;
- spin_lock_irqsave(&d40c->lock, flags);
+ d40_usage_dec(d40c);
+ if (d40c->busy)
+ d40_usage_dec(d40c);
+ d40c->busy = false;
- res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ);
- if (res == 0) {
- if (d40c->log_num != D40_PHY_CHAN) {
- d40_config_set_event(d40c, false);
- /* Resume the other logical channels if any */
- if (d40_chan_has_events(d40c))
- res = d40_channel_execute_command(d40c,
- D40_DMA_RUN);
- }
- }
+ d40c->phy_chan = NULL;
+ d40c->configured = false;
- spin_unlock_irqrestore(&d40c->lock, flags);
- return res;
+ return 0;
}
static bool d40_is_paused(struct d40_chan *d40c)
@@ -1397,11 +2097,10 @@ static bool d40_is_paused(struct d40_chan *d40c)
void __iomem *active_reg;
u32 status;
u32 event;
- int res;
spin_lock_irqsave(&d40c->lock, flags);
- if (d40c->log_num == D40_PHY_CHAN) {
+ if (chan_is_physical(d40c)) {
if (d40c->phy_chan->num % 2 == 0)
active_reg = d40c->base->virtbase + D40_DREG_ACTIVE;
else
@@ -1416,111 +2115,34 @@ static bool d40_is_paused(struct d40_chan *d40c)
goto _exit;
}
- res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ);
- if (res != 0)
- goto _exit;
-
if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH ||
- d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM)
+ d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) {
event = D40_TYPE_TO_EVENT(d40c->dma_cfg.dst_dev_type);
- else if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM)
+ status = readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SDLNK);
+ } else if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) {
event = D40_TYPE_TO_EVENT(d40c->dma_cfg.src_dev_type);
- else {
+ status = readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SSLNK);
+ } else {
dev_err(&d40c->chan.dev->device,
"[%s] Unknown direction\n", __func__);
goto _exit;
}
- status = d40_chan_has_events(d40c);
+
status = (status & D40_EVENTLINE_MASK(event)) >>
D40_EVENTLINE_POS(event);
if (status != D40_DMA_RUN)
is_paused = true;
-
- /* Resume the other logical channels if any */
- if (d40_chan_has_events(d40c))
- res = d40_channel_execute_command(d40c,
- D40_DMA_RUN);
-
_exit:
spin_unlock_irqrestore(&d40c->lock, flags);
return is_paused;
}
-
-static bool d40_tx_is_linked(struct d40_chan *d40c)
-{
- bool is_link;
-
- if (d40c->log_num != D40_PHY_CHAN)
- is_link = readl(&d40c->lcpa->lcsp3) & D40_MEM_LCSP3_DLOS_MASK;
- else
- is_link = readl(d40c->base->virtbase + D40_DREG_PCBASE +
- d40c->phy_chan->num * D40_DREG_PCDELTA +
- D40_CHAN_REG_SDLNK) &
- D40_SREG_LNK_PHYS_LNK_MASK;
- return is_link;
-}
-
-static u32 d40_residue(struct d40_chan *d40c)
-{
- u32 num_elt;
-
- if (d40c->log_num != D40_PHY_CHAN)
- num_elt = (readl(&d40c->lcpa->lcsp2) & D40_MEM_LCSP2_ECNT_MASK)
- >> D40_MEM_LCSP2_ECNT_POS;
- else
- num_elt = (readl(d40c->base->virtbase + D40_DREG_PCBASE +
- d40c->phy_chan->num * D40_DREG_PCDELTA +
- D40_CHAN_REG_SDELT) &
- D40_SREG_ELEM_PHY_ECNT_MASK) >> D40_SREG_ELEM_PHY_ECNT_POS;
- return num_elt * (1 << d40c->dma_cfg.dst_info.data_width);
-}
-
-static int d40_resume(struct dma_chan *chan)
-{
- struct d40_chan *d40c =
- container_of(chan, struct d40_chan, chan);
- int res = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&d40c->lock, flags);
-
- if (d40c->log_num != D40_PHY_CHAN) {
- res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ);
- if (res)
- goto out;
-
- /* If bytes left to transfer or linked tx resume job */
- if (d40_residue(d40c) || d40_tx_is_linked(d40c)) {
- d40_config_set_event(d40c, true);
- res = d40_channel_execute_command(d40c, D40_DMA_RUN);
- }
- } else if (d40_residue(d40c) || d40_tx_is_linked(d40c))
- res = d40_channel_execute_command(d40c, D40_DMA_RUN);
-
-out:
- spin_unlock_irqrestore(&d40c->lock, flags);
- return res;
-}
-
-static u32 stedma40_residue(struct dma_chan *chan)
-{
- struct d40_chan *d40c =
- container_of(chan, struct d40_chan, chan);
- u32 bytes_left;
- unsigned long flags;
-
- spin_lock_irqsave(&d40c->lock, flags);
- bytes_left = d40_residue(d40c);
- spin_unlock_irqrestore(&d40c->lock, flags);
-
- return bytes_left;
-}
-
-/* Public DMA functions in addition to the DMA engine framework */
-
int stedma40_set_psize(struct dma_chan *chan,
int src_psize,
int dst_psize)
@@ -1531,11 +2153,13 @@ int stedma40_set_psize(struct dma_chan *chan,
spin_lock_irqsave(&d40c->lock, flags);
- if (d40c->log_num != D40_PHY_CHAN) {
+ if (chan_is_logical(d40c)) {
d40c->log_def.lcsp1 &= ~D40_MEM_LCSP1_SCFG_PSIZE_MASK;
d40c->log_def.lcsp3 &= ~D40_MEM_LCSP1_SCFG_PSIZE_MASK;
- d40c->log_def.lcsp1 |= src_psize << D40_MEM_LCSP1_SCFG_PSIZE_POS;
- d40c->log_def.lcsp3 |= dst_psize << D40_MEM_LCSP1_SCFG_PSIZE_POS;
+ d40c->log_def.lcsp1 |= src_psize
+ << D40_MEM_LCSP1_SCFG_PSIZE_POS;
+ d40c->log_def.lcsp3 |= dst_psize
+ << D40_MEM_LCSP1_SCFG_PSIZE_POS;
goto out;
}
@@ -1566,37 +2190,31 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
struct scatterlist *sgl_dst,
struct scatterlist *sgl_src,
unsigned int sgl_len,
- unsigned long flags)
+ unsigned long dma_flags)
{
int res;
struct d40_desc *d40d;
struct d40_chan *d40c = container_of(chan, struct d40_chan,
chan);
- unsigned long flg;
- int lli_max = d40c->base->plat_data->llis_per_log;
+ unsigned long flags;
+ if (d40c->phy_chan == NULL) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Unallocated channel.\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
- spin_lock_irqsave(&d40c->lock, flg);
+ spin_lock_irqsave(&d40c->lock, flags);
d40d = d40_desc_get(d40c);
if (d40d == NULL)
goto err;
- memset(d40d, 0, sizeof(struct d40_desc));
d40d->lli_len = sgl_len;
+ d40d->lli_current = 0;
+ d40d->txd.flags = dma_flags;
- d40d->txd.flags = flags;
-
- if (d40c->log_num != D40_PHY_CHAN) {
- if (sgl_len > 1)
- /*
- * Check if there is space available in lcla. If not,
- * split list into 1-length and run only in lcpa
- * space.
- */
- if (d40_lcla_id_get(d40c,
- &d40c->base->lcla_pool) != 0)
- lli_max = 1;
+ if (chan_is_logical(d40c)) {
if (d40_pool_lli_alloc(d40d, sgl_len, true) < 0) {
dev_err(&d40c->chan.dev->device,
@@ -1604,25 +2222,17 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
goto err;
}
- (void) d40_log_sg_to_lli(d40c->lcla.src_id,
- sgl_src,
+ (void) d40_log_sg_to_lli(sgl_src,
sgl_len,
d40d->lli_log.src,
d40c->log_def.lcsp1,
- d40c->dma_cfg.src_info.data_width,
- flags & DMA_PREP_INTERRUPT, lli_max,
- d40c->base->plat_data->llis_per_log);
+ d40c->dma_cfg.src_info.data_width);
- (void) d40_log_sg_to_lli(d40c->lcla.dst_id,
- sgl_dst,
+ (void) d40_log_sg_to_lli(sgl_dst,
sgl_len,
d40d->lli_log.dst,
d40c->log_def.lcsp3,
- d40c->dma_cfg.dst_info.data_width,
- flags & DMA_PREP_INTERRUPT, lli_max,
- d40c->base->plat_data->llis_per_log);
-
-
+ d40c->dma_cfg.dst_info.data_width);
} else {
if (d40_pool_lli_alloc(d40d, sgl_len, false) < 0) {
dev_err(&d40c->chan.dev->device,
@@ -1634,11 +2244,12 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
sgl_len,
0,
d40d->lli_phy.src,
- d40d->lli_phy.src_addr,
+ virt_to_phys(d40d->lli_phy.src),
d40c->src_def_cfg,
d40c->dma_cfg.src_info.data_width,
d40c->dma_cfg.src_info.psize,
- true);
+ false,
+ false);
if (res < 0)
goto err;
@@ -1647,28 +2258,32 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
sgl_len,
0,
d40d->lli_phy.dst,
- d40d->lli_phy.dst_addr,
+ virt_to_phys(d40d->lli_phy.dst),
d40c->dst_def_cfg,
d40c->dma_cfg.dst_info.data_width,
d40c->dma_cfg.dst_info.psize,
- true);
+ false,
+ false);
if (res < 0)
goto err;
(void) dma_map_single(d40c->base->dev, d40d->lli_phy.src,
d40d->lli_pool.size, DMA_TO_DEVICE);
+
}
dma_async_tx_descriptor_init(&d40d->txd, chan);
d40d->txd.tx_submit = d40_tx_submit;
- spin_unlock_irqrestore(&d40c->lock, flg);
+ spin_unlock_irqrestore(&d40c->lock, flags);
return &d40d->txd;
err:
- spin_unlock_irqrestore(&d40c->lock, flg);
+ if (d40d)
+ d40_desc_free(d40c, d40d);
+ spin_unlock_irqrestore(&d40c->lock, flags);
return NULL;
}
EXPORT_SYMBOL(stedma40_memcpy_sg);
@@ -1687,10 +2302,45 @@ bool stedma40_filter(struct dma_chan *chan, void *data)
} else
err = d40_config_memcpy(d40c);
+ if (!err)
+ d40c->configured = true;
+
return err == 0;
}
EXPORT_SYMBOL(stedma40_filter);
+static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src)
+{
+ bool realtime = d40c->dma_cfg.realtime;
+ bool highprio = d40c->dma_cfg.high_priority;
+ u32 prioreg = highprio ? D40_DREG_PSEG1 : D40_DREG_PCEG1;
+ u32 rtreg = realtime ? D40_DREG_RSEG1 : D40_DREG_RCEG1;
+ u32 event = D40_TYPE_TO_EVENT(dev_type);
+ u32 group = D40_TYPE_TO_GROUP(dev_type);
+ u32 bit = 1 << event;
+
+ /* Destination event lines are stored in the upper halfword */
+ if (!src)
+ bit <<= 16;
+
+ writel(bit, d40c->base->virtbase + prioreg + group * 4);
+ writel(bit, d40c->base->virtbase + rtreg + group * 4);
+}
+
+static void d40_set_prio_realtime(struct d40_chan *d40c)
+{
+ if (d40c->base->rev < 3)
+ return;
+
+ if ((d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) ||
+ (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH))
+ __d40_set_prio_rt(d40c, d40c->dma_cfg.src_dev_type, true);
+
+ if ((d40c->dma_cfg.dir == STEDMA40_MEM_TO_PERIPH) ||
+ (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_PERIPH))
+ __d40_set_prio_rt(d40c, d40c->dma_cfg.dst_dev_type, false);
+}
+
/* DMA ENGINE functions */
static int d40_alloc_chan_resources(struct dma_chan *chan)
{
@@ -1698,46 +2348,63 @@ static int d40_alloc_chan_resources(struct dma_chan *chan)
unsigned long flags;
struct d40_chan *d40c =
container_of(chan, struct d40_chan, chan);
+ bool is_free_phy;
+
+ d40_usage_inc(d40c);
spin_lock_irqsave(&d40c->lock, flags);
d40c->completed = chan->cookie = 1;
- /*
- * If no dma configuration is set (channel_type == 0)
- * use default configuration
- */
- if (d40c->dma_cfg.channel_type == 0) {
+ if (!d40c->configured) {
err = d40_config_memcpy(d40c);
- if (err)
- goto err_alloc;
+ if (err) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Failed to configure memcpy channel\n",
+ __func__);
+ goto fail;
+ }
}
- err = d40_allocate_channel(d40c);
+ err = d40_allocate_channel(d40c, &is_free_phy);
if (err) {
dev_err(&d40c->chan.dev->device,
"[%s] Failed to allocate channel\n", __func__);
- goto err_alloc;
+ goto fail;
}
- err = d40_config_chan(d40c, &d40c->dma_cfg);
- if (err) {
- dev_err(&d40c->chan.dev->device,
- "[%s] Failed to configure channel\n",
- __func__);
- goto err_config;
+ /* Fill in basic CFG register values */
+ d40_phy_cfg(&d40c->dma_cfg, &d40c->src_def_cfg,
+ &d40c->dst_def_cfg, chan_is_logical(d40c));
+
+ d40_set_prio_realtime(d40c);
+
+ if (chan_is_logical(d40c)) {
+ d40_log_cfg(&d40c->dma_cfg,
+ &d40c->log_def.lcsp1, &d40c->log_def.lcsp3);
+
+ if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM)
+ d40c->lcpa = d40c->base->lcpa_base +
+ d40c->dma_cfg.src_dev_type *
+ D40_LCPA_CHAN_SIZE;
+ else
+ d40c->lcpa = d40c->base->lcpa_base +
+ d40c->dma_cfg.dst_dev_type *
+ D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA;
}
- spin_unlock_irqrestore(&d40c->lock, flags);
- return 0;
+ /*
+ * Only write channel configuration to the DMA if the physical
+ * resource is free. In case of multiple logical channels
+ * on the same physical resource, only the first write is necessary.
+ */
- err_config:
- (void) d40_free_dma(d40c);
- err_alloc:
+ if (is_free_phy)
+ d40_config_write(d40c);
+fail:
+ d40_usage_dec(d40c);
spin_unlock_irqrestore(&d40c->lock, flags);
- dev_err(&d40c->chan.dev->device,
- "[%s] Channel allocation failed\n", __func__);
- return -EINVAL;
+ return err;
}
static void d40_free_chan_resources(struct dma_chan *chan)
@@ -1747,6 +2414,12 @@ static void d40_free_chan_resources(struct dma_chan *chan)
int err;
unsigned long flags;
+ if (d40c->phy_chan == NULL) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Cannot free unallocated channel\n", __func__);
+ return;
+ }
+
spin_lock_irqsave(&d40c->lock, flags);
err = d40_free_dma(d40c);
@@ -1761,15 +2434,21 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan,
dma_addr_t dst,
dma_addr_t src,
size_t size,
- unsigned long flags)
+ unsigned long dma_flags)
{
struct d40_desc *d40d;
struct d40_chan *d40c = container_of(chan, struct d40_chan,
chan);
- unsigned long flg;
+ unsigned long flags;
int err = 0;
- spin_lock_irqsave(&d40c->lock, flg);
+ if (d40c->phy_chan == NULL) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Channel is not allocated.\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ spin_lock_irqsave(&d40c->lock, flags);
d40d = d40_desc_get(d40c);
if (d40d == NULL) {
@@ -1778,45 +2457,44 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan,
goto err;
}
- memset(d40d, 0, sizeof(struct d40_desc));
+ d40_usage_inc(d40c);
- d40d->txd.flags = flags;
+ d40d->txd.flags = dma_flags;
dma_async_tx_descriptor_init(&d40d->txd, chan);
d40d->txd.tx_submit = d40_tx_submit;
- if (d40c->log_num != D40_PHY_CHAN) {
+ if (chan_is_logical(d40c)) {
if (d40_pool_lli_alloc(d40d, 1, true) < 0) {
dev_err(&d40c->chan.dev->device,
"[%s] Out of memory\n", __func__);
- goto err;
+ goto err2;
}
d40d->lli_len = 1;
+ d40d->lli_current = 0;
d40_log_fill_lli(d40d->lli_log.src,
src,
size,
- 0,
d40c->log_def.lcsp1,
d40c->dma_cfg.src_info.data_width,
- true, true);
+ true);
d40_log_fill_lli(d40d->lli_log.dst,
dst,
size,
- 0,
d40c->log_def.lcsp3,
d40c->dma_cfg.dst_info.data_width,
- true, true);
+ true);
} else {
if (d40_pool_lli_alloc(d40d, 1, false) < 0) {
dev_err(&d40c->chan.dev->device,
"[%s] Out of memory\n", __func__);
- goto err;
+ goto err2;
}
err = d40_phy_fill_lli(d40d->lli_phy.src,
@@ -1848,28 +2526,79 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan,
d40d->lli_pool.size, DMA_TO_DEVICE);
}
- spin_unlock_irqrestore(&d40c->lock, flg);
+ d40_usage_dec(d40c);
+ spin_unlock_irqrestore(&d40c->lock, flags);
return &d40d->txd;
err_fill_lli:
dev_err(&d40c->chan.dev->device,
"[%s] Failed filling in PHY LLI\n", __func__);
- d40_pool_lli_free(d40d);
+err2:
+ d40_usage_dec(d40c);
err:
- spin_unlock_irqrestore(&d40c->lock, flg);
+ if (d40d)
+ d40_desc_free(d40c, d40d);
+ spin_unlock_irqrestore(&d40c->lock, flags);
return NULL;
}
+static dma_addr_t d40_dev_rx_addr(struct d40_chan *d40c)
+{
+ dma_addr_t dev_addr = 0;
+
+ if (d40c->runtime_addr)
+ dev_addr = d40c->runtime_addr;
+ else if (d40c->src_dev_addr)
+ dev_addr = d40c->src_dev_addr;
+ else
+ dev_addr = d40c->base->plat_data->
+ dev_rx[d40c->dma_cfg.src_dev_type];
+
+ return dev_addr;
+}
+
+static dma_addr_t d40_dev_tx_addr(struct d40_chan *d40c)
+{
+ dma_addr_t dev_addr = 0;
+
+ if (d40c->runtime_addr)
+ dev_addr = d40c->runtime_addr;
+ else if (d40c->dst_dev_addr)
+ dev_addr = d40c->dst_dev_addr;
+ else
+ dev_addr = d40c->base->plat_data->
+ dev_tx[d40c->dma_cfg.dst_dev_type];
+
+ return dev_addr;
+}
+
+int stedma40_set_dev_addr(struct dma_chan *chan,
+ dma_addr_t src_dev_addr,
+ dma_addr_t dst_dev_addr)
+{
+ struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&d40c->lock, flags);
+
+ d40c->src_dev_addr = src_dev_addr;
+ d40c->dst_dev_addr = dst_dev_addr;
+
+ spin_unlock_irqrestore(&d40c->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(stedma40_set_dev_addr);
+
static int d40_prep_slave_sg_log(struct d40_desc *d40d,
struct d40_chan *d40c,
struct scatterlist *sgl,
unsigned int sg_len,
enum dma_data_direction direction,
- unsigned long flags)
+ unsigned long dma_flags)
{
dma_addr_t dev_addr = 0;
int total_size;
- int lli_max = d40c->base->plat_data->llis_per_log;
if (d40_pool_lli_alloc(d40d, sg_len, true) < 0) {
dev_err(&d40c->chan.dev->device,
@@ -1878,43 +2607,22 @@ static int d40_prep_slave_sg_log(struct d40_desc *d40d,
}
d40d->lli_len = sg_len;
- d40d->lli_tcount = 0;
-
- if (sg_len > 1)
- /*
- * Check if there is space available in lcla.
- * If not, split list into 1-length and run only
- * in lcpa space.
- */
- if (d40_lcla_id_get(d40c, &d40c->base->lcla_pool) != 0)
- lli_max = 1;
+ d40d->lli_current = 0;
- if (direction == DMA_FROM_DEVICE) {
- dev_addr = d40c->base->plat_data->dev_rx[d40c->dma_cfg.src_dev_type];
- total_size = d40_log_sg_to_dev(&d40c->lcla,
- sgl, sg_len,
- &d40d->lli_log,
- &d40c->log_def,
- d40c->dma_cfg.src_info.data_width,
- d40c->dma_cfg.dst_info.data_width,
- direction,
- flags & DMA_PREP_INTERRUPT,
- dev_addr, lli_max,
- d40c->base->plat_data->llis_per_log);
- } else if (direction == DMA_TO_DEVICE) {
- dev_addr = d40c->base->plat_data->dev_tx[d40c->dma_cfg.dst_dev_type];
- total_size = d40_log_sg_to_dev(&d40c->lcla,
- sgl, sg_len,
- &d40d->lli_log,
- &d40c->log_def,
- d40c->dma_cfg.src_info.data_width,
- d40c->dma_cfg.dst_info.data_width,
- direction,
- flags & DMA_PREP_INTERRUPT,
- dev_addr, lli_max,
- d40c->base->plat_data->llis_per_log);
- } else
+ if (direction == DMA_FROM_DEVICE)
+ dev_addr = d40_dev_rx_addr(d40c);
+ else if (direction == DMA_TO_DEVICE)
+ dev_addr = d40_dev_tx_addr(d40c);
+ else
return -EINVAL;
+
+ total_size = d40_log_sg_to_dev(sgl, sg_len,
+ &d40d->lli_log,
+ &d40c->log_def,
+ d40c->dma_cfg.src_info.data_width,
+ d40c->dma_cfg.dst_info.data_width,
+ direction,
+ dev_addr);
if (total_size < 0)
return -EINVAL;
@@ -1926,7 +2634,7 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d,
struct scatterlist *sgl,
unsigned int sgl_len,
enum dma_data_direction direction,
- unsigned long flags)
+ unsigned long dma_flags)
{
dma_addr_t src_dev_addr;
dma_addr_t dst_dev_addr;
@@ -1939,13 +2647,13 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d,
}
d40d->lli_len = sgl_len;
- d40d->lli_tcount = 0;
+ d40d->lli_current = 0;
if (direction == DMA_FROM_DEVICE) {
dst_dev_addr = 0;
- src_dev_addr = d40c->base->plat_data->dev_rx[d40c->dma_cfg.src_dev_type];
+ src_dev_addr = d40_dev_rx_addr(d40c);
} else if (direction == DMA_TO_DEVICE) {
- dst_dev_addr = d40c->base->plat_data->dev_tx[d40c->dma_cfg.dst_dev_type];
+ dst_dev_addr = d40_dev_tx_addr(d40c);
src_dev_addr = 0;
} else
return -EINVAL;
@@ -1954,11 +2662,12 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d,
sgl_len,
src_dev_addr,
d40d->lli_phy.src,
- d40d->lli_phy.src_addr,
+ virt_to_phys(d40d->lli_phy.src),
d40c->src_def_cfg,
d40c->dma_cfg.src_info.data_width,
d40c->dma_cfg.src_info.psize,
- true);
+ d40d->cyclic,
+ d40d->txd.flags & DMA_PREP_INTERRUPT);
if (res < 0)
return res;
@@ -1966,11 +2675,12 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d,
sgl_len,
dst_dev_addr,
d40d->lli_phy.dst,
- d40d->lli_phy.dst_addr,
+ virt_to_phys(d40d->lli_phy.dst),
d40c->dst_def_cfg,
d40c->dma_cfg.dst_info.data_width,
d40c->dma_cfg.dst_info.psize,
- true);
+ d40d->cyclic,
+ d40d->txd.flags & DMA_PREP_INTERRUPT);
if (res < 0)
return res;
@@ -1979,53 +2689,66 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d,
return 0;
}
-static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan,
- struct scatterlist *sgl,
- unsigned int sg_len,
- enum dma_data_direction direction,
- unsigned long flags)
+static struct dma_async_tx_descriptor *
+d40_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_data_direction direction,
+ unsigned long dma_flags)
{
struct d40_desc *d40d;
struct d40_chan *d40c = container_of(chan, struct d40_chan,
chan);
- unsigned long flg;
+ unsigned long flags;
int err;
+
+ if (d40c->phy_chan == NULL) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Cannot prepare unallocated channel\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
if (d40c->dma_cfg.pre_transfer)
d40c->dma_cfg.pre_transfer(chan,
d40c->dma_cfg.pre_transfer_data,
sg_dma_len(sgl));
- spin_lock_irqsave(&d40c->lock, flg);
+ spin_lock_irqsave(&d40c->lock, flags);
d40d = d40_desc_get(d40c);
- spin_unlock_irqrestore(&d40c->lock, flg);
if (d40d == NULL)
- return NULL;
-
- memset(d40d, 0, sizeof(struct d40_desc));
+ goto err;
+ d40_usage_inc(d40c);
- if (d40c->log_num != D40_PHY_CHAN)
+ if (chan_is_logical(d40c))
err = d40_prep_slave_sg_log(d40d, d40c, sgl, sg_len,
- direction, flags);
+ direction, dma_flags);
else
err = d40_prep_slave_sg_phy(d40d, d40c, sgl, sg_len,
- direction, flags);
+ direction, dma_flags);
+ d40_usage_dec(d40c);
+
if (err) {
dev_err(&d40c->chan.dev->device,
"[%s] Failed to prepare %s slave sg job: %d\n",
__func__,
- d40c->log_num != D40_PHY_CHAN ? "log" : "phy", err);
- return NULL;
+ chan_is_logical(d40c) ? "log" : "phy", err);
+ goto err;
}
- d40d->txd.flags = flags;
+ d40d->txd.flags = dma_flags;
dma_async_tx_descriptor_init(&d40d->txd, chan);
d40d->txd.tx_submit = d40_tx_submit;
+ spin_unlock_irqrestore(&d40c->lock, flags);
return &d40d->txd;
+
+err:
+ if (d40d)
+ d40_desc_free(d40c, d40d);
+ spin_unlock_irqrestore(&d40c->lock, flags);
+ return NULL;
}
static enum dma_status d40_tx_status(struct dma_chan *chan,
@@ -2033,10 +2756,18 @@ static enum dma_status d40_tx_status(struct dma_chan *chan,
struct dma_tx_state *txstate)
{
struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ unsigned long flags;
dma_cookie_t last_used;
dma_cookie_t last_complete;
int ret;
+ if (d40c->phy_chan == NULL) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Cannot read status of unallocated channel\n",
+ __func__);
+ return -EINVAL;
+ }
+
last_complete = d40c->completed;
last_used = chan->cookie;
@@ -2045,8 +2776,14 @@ static enum dma_status d40_tx_status(struct dma_chan *chan,
else
ret = dma_async_is_complete(cookie, last_complete, last_used);
- dma_set_tx_state(txstate, last_complete, last_used,
- stedma40_residue(chan));
+ if (txstate) {
+ txstate->last = last_complete;
+ txstate->used = last_used;
+
+ spin_lock_irqsave(&d40c->lock, flags);
+ txstate->residue = d40_residue(d40c);
+ spin_unlock_irqrestore(&d40c->lock, flags);
+ }
return ret;
}
@@ -2056,6 +2793,12 @@ static void d40_issue_pending(struct dma_chan *chan)
struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
unsigned long flags;
+ if (d40c->phy_chan == NULL) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Channel is not allocated!\n", __func__);
+ return;
+ }
+
spin_lock_irqsave(&d40c->lock, flags);
/* Busy means that pending jobs are already being processed */
@@ -2065,28 +2808,381 @@ static void d40_issue_pending(struct dma_chan *chan)
spin_unlock_irqrestore(&d40c->lock, flags);
}
+static void d40_terminate_all(struct dma_chan *chan)
+{
+ unsigned long flags;
+ struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ int ret;
+
+ spin_lock_irqsave(&d40c->lock, flags);
+
+ d40_usage_inc(d40c);
+
+ ret = d40_channel_execute_command(d40c, D40_DMA_STOP);
+ if (ret)
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Failed to stop channel\n", __func__);
+
+ d40_term_all(d40c);
+ d40_usage_dec(d40c);
+ if (d40c->busy)
+ d40_usage_dec(d40c);
+ d40c->busy = false;
+
+ spin_unlock_irqrestore(&d40c->lock, flags);
+}
+
+/* Runtime reconfiguration extension */
+static void d40_set_runtime_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
+{
+ struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ struct stedma40_chan_cfg *cfg = &d40c->dma_cfg;
+ enum dma_slave_buswidth config_addr_width;
+ dma_addr_t config_addr;
+ u32 config_maxburst;
+ enum stedma40_periph_data_width addr_width;
+ int psize;
+
+ if (config->direction == DMA_FROM_DEVICE) {
+ dma_addr_t dev_addr_rx =
+ d40c->base->plat_data->dev_rx[cfg->src_dev_type];
+
+ config_addr = config->src_addr;
+ if (dev_addr_rx)
+ dev_dbg(d40c->base->dev,
+ "channel has a pre-wired RX address %08x "
+ "overriding with %08x\n",
+ dev_addr_rx, config_addr);
+ if (cfg->dir != STEDMA40_PERIPH_TO_MEM)
+ dev_dbg(d40c->base->dev,
+ "channel was not configured for peripheral "
+ "to memory transfer (%d) overriding\n",
+ cfg->dir);
+ cfg->dir = STEDMA40_PERIPH_TO_MEM;
+
+ config_addr_width = config->src_addr_width;
+ config_maxburst = config->src_maxburst;
+
+ } else if (config->direction == DMA_TO_DEVICE) {
+ dma_addr_t dev_addr_tx =
+ d40c->base->plat_data->dev_tx[cfg->dst_dev_type];
+
+ config_addr = config->dst_addr;
+ if (dev_addr_tx)
+ dev_dbg(d40c->base->dev,
+ "channel has a pre-wired TX address %08x "
+ "overriding with %08x\n",
+ dev_addr_tx, config_addr);
+ if (cfg->dir != STEDMA40_MEM_TO_PERIPH)
+ dev_dbg(d40c->base->dev,
+ "channel was not configured for memory "
+ "to peripheral transfer (%d) overriding\n",
+ cfg->dir);
+ cfg->dir = STEDMA40_MEM_TO_PERIPH;
+
+ config_addr_width = config->dst_addr_width;
+ config_maxburst = config->dst_maxburst;
+
+ } else {
+ dev_err(d40c->base->dev,
+ "unrecognized channel direction %d\n",
+ config->direction);
+ return;
+ }
+
+ switch (config_addr_width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ addr_width = STEDMA40_BYTE_WIDTH;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ addr_width = STEDMA40_HALFWORD_WIDTH;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ addr_width = STEDMA40_WORD_WIDTH;
+ break;
+ case DMA_SLAVE_BUSWIDTH_8_BYTES:
+ addr_width = STEDMA40_DOUBLEWORD_WIDTH;
+ break;
+ default:
+ dev_err(d40c->base->dev,
+ "illegal peripheral address width "
+ "requested (%d)\n",
+ config->src_addr_width);
+ return;
+ }
+
+ if (chan_is_logical(d40c)) {
+ if (config_maxburst >= 16)
+ psize = STEDMA40_PSIZE_LOG_16;
+ else if (config_maxburst >= 8)
+ psize = STEDMA40_PSIZE_LOG_8;
+ else if (config_maxburst >= 4)
+ psize = STEDMA40_PSIZE_LOG_4;
+ else
+ psize = STEDMA40_PSIZE_LOG_1;
+ } else {
+ if (config_maxburst >= 16)
+ psize = STEDMA40_PSIZE_PHY_16;
+ else if (config_maxburst >= 8)
+ psize = STEDMA40_PSIZE_PHY_8;
+ else if (config_maxburst >= 4)
+ psize = STEDMA40_PSIZE_PHY_4;
+ else
+ psize = STEDMA40_PSIZE_PHY_1;
+ }
+
+ /* Set up all the endpoint configs */
+ cfg->src_info.data_width = addr_width;
+ cfg->src_info.psize = psize;
+ cfg->src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
+ cfg->dst_info.data_width = addr_width;
+ cfg->dst_info.psize = psize;
+ cfg->dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
+
+ /* Fill in register values */
+ if (chan_is_logical(d40c))
+ d40_log_cfg(cfg, &d40c->log_def.lcsp1, &d40c->log_def.lcsp3);
+ else
+ d40_phy_cfg(cfg, &d40c->src_def_cfg,
+ &d40c->dst_def_cfg, false);
+
+ /* These settings will take precedence later */
+ d40c->runtime_addr = config_addr;
+ d40c->runtime_direction = config->direction;
+ dev_dbg(d40c->base->dev,
+ "configured channel %s for %s, data width %d, "
+ "maxburst %d bytes, LE, no flow control\n",
+ dma_chan_name(chan),
+ (config->direction == DMA_FROM_DEVICE) ? "RX" : "TX",
+ config_addr_width,
+ config_maxburst);
+}
+
static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg)
{
- unsigned long flags;
struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ if (d40c->phy_chan == NULL) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Channel is not allocated!\n", __func__);
+ return -EINVAL;
+ }
+
switch (cmd) {
case DMA_TERMINATE_ALL:
- spin_lock_irqsave(&d40c->lock, flags);
- d40_term_all(d40c);
- spin_unlock_irqrestore(&d40c->lock, flags);
+ d40_terminate_all(chan);
return 0;
case DMA_PAUSE:
return d40_pause(chan);
case DMA_RESUME:
return d40_resume(chan);
+ case DMA_SLAVE_CONFIG:
+ d40_set_runtime_config(chan,
+ (struct dma_slave_config *) arg);
+ return 0;
+ default:
+ break;
}
/* Other commands are unimplemented */
return -ENXIO;
}
+dma_addr_t stedma40_get_src_addr(struct dma_chan *chan)
+{
+ struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ dma_addr_t addr;
+
+ if (chan_is_physical(d40c))
+ addr = readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SSPTR);
+ else {
+ unsigned long lower;
+ unsigned long upper;
+
+ /*
+ * There is a potential for overflow between the time the two
+ * halves of the pointer are read.
+ */
+ lower = d40c->lcpa->lcsp0 & D40_MEM_LCSP0_SPTR_MASK;
+ upper = d40c->lcpa->lcsp1 & D40_MEM_LCSP1_SPTR_MASK;
+
+ addr = upper | lower;
+ }
+
+ return addr;
+}
+EXPORT_SYMBOL(stedma40_get_src_addr);
+
+dma_addr_t stedma40_get_dst_addr(struct dma_chan *chan)
+{
+ struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ dma_addr_t addr;
+
+ if (chan_is_physical(d40c))
+ addr = readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SDPTR);
+ else {
+ unsigned long lower;
+ unsigned long upper;
+
+ lower = d40c->lcpa->lcsp2 & D40_MEM_LCSP2_DPTR_MASK;
+ upper = d40c->lcpa->lcsp3 & D40_MEM_LCSP3_DPTR_MASK;
+
+ addr = upper | lower;
+ }
+
+ return addr;
+}
+EXPORT_SYMBOL(stedma40_get_dst_addr);
+
+int stedma40_cyclic_start(struct dma_chan *chan)
+{
+ struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&d40c->lock, flags);
+
+ if (!d40c->cdesc)
+ goto out;
+
+ d40_usage_inc(d40c);
+
+ ret = d40_start(d40c);
+ if (!ret)
+ d40c->busy = true;
+ else
+ d40_usage_dec(d40c);
+
+out:
+ spin_unlock_irqrestore(&d40c->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(stedma40_cyclic_start);
+
+void stedma40_cyclic_stop(struct dma_chan *chan)
+{
+ d40_terminate_all(chan);
+}
+EXPORT_SYMBOL(stedma40_cyclic_stop);
+
+void stedma40_cyclic_free(struct dma_chan *chan)
+{
+ struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ struct stedma40_cyclic_desc *cdesc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&d40c->lock, flags);
+
+ cdesc = d40c->cdesc;
+ if (!cdesc) {
+ spin_unlock_irqrestore(&d40c->lock, flags);
+ return;
+ }
+
+ d40c->cdesc = NULL;
+ d40_lcla_free_all(d40c, cdesc->d40d);
+
+ spin_unlock_irqrestore(&d40c->lock, flags);
+
+ kfree(cdesc);
+}
+EXPORT_SYMBOL(stedma40_cyclic_free);
+
+struct stedma40_cyclic_desc *
+stedma40_cyclic_prep_sg(struct dma_chan *chan,
+ struct scatterlist *sgl,
+ unsigned int sg_len,
+ enum dma_data_direction direction,
+ unsigned long dma_flags)
+{
+ struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ struct stedma40_cyclic_desc *cdesc;
+ struct d40_desc *d40d;
+ unsigned long flags;
+ void *mem;
+ int err;
+
+ mem = kzalloc(sizeof(struct stedma40_cyclic_desc)
+ + sizeof(struct d40_desc), GFP_ATOMIC);
+ if (!mem)
+ return ERR_PTR(-ENOMEM);
+
+ cdesc = mem;
+ d40d = cdesc->d40d = mem + sizeof(struct stedma40_cyclic_desc);
+
+ spin_lock_irqsave(&d40c->lock, flags);
+
+ if (d40c->phy_chan == NULL) {
+ dev_err(&chan->dev->device,
+ "[%s] Cannot prepare unallocated channel\n", __func__);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (d40c->cdesc || d40c->busy) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Cannot prepare cyclic job for busy channel\n",
+ __func__);
+ err = -EBUSY;
+ goto out;
+ }
+
+ d40d->cyclic = true;
+ d40d->txd.flags = dma_flags;
+ INIT_LIST_HEAD(&d40d->node);
+
+ d40_usage_inc(d40c);
+
+ if (chan_is_logical(d40c))
+ err = d40_prep_slave_sg_log(d40d, d40c, sgl, sg_len,
+ direction, dma_flags);
+ else
+ err = d40_prep_slave_sg_phy(d40d, d40c, sgl, sg_len,
+ direction, dma_flags);
+
+ if (err) {
+ dev_err(&d40c->chan.dev->device,
+ "[%s] Failed to prepare %s slave sg job: %d\n",
+ __func__,
+ chan_is_logical(d40c) ? "log" : "phy", err);
+ goto out2;
+ }
+
+ d40_desc_load(d40c, d40d);
+
+ /*
+ * Couldn't get enough LCLA. We don't support splitting of cyclic
+ * jobs.
+ */
+ if (d40d->lli_current != d40d->lli_len) {
+ dev_err(&chan->dev->device,
+ "[%s] Couldn't prepare cyclic job: not enough LCLA",
+ __func__);
+ err = -EBUSY;
+ goto out2;
+ }
+
+ d40c->cdesc = cdesc;
+ d40_usage_dec(d40c);
+ spin_unlock_irqrestore(&d40c->lock, flags);
+ return cdesc;
+out2:
+ d40_usage_dec(d40c);
+out:
+ if (d40c->phy_chan)
+ d40_lcla_free_all(d40c, cdesc->d40d);
+ kfree(cdesc);
+ spin_unlock_irqrestore(&d40c->lock, flags);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL(stedma40_cyclic_prep_sg);
+
/* Initialization functions */
static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma,
@@ -2103,21 +3199,15 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma,
d40c->base = base;
d40c->chan.device = dma;
- /* Invalidate lcla element */
- d40c->lcla.src_id = -1;
- d40c->lcla.dst_id = -1;
-
spin_lock_init(&d40c->lock);
d40c->log_num = D40_PHY_CHAN;
- INIT_LIST_HEAD(&d40c->free);
+ INIT_LIST_HEAD(&d40c->done);
INIT_LIST_HEAD(&d40c->active);
INIT_LIST_HEAD(&d40c->queue);
INIT_LIST_HEAD(&d40c->client);
- d40c->free_len = 0;
-
tasklet_init(&d40c->tasklet, dma_tasklet,
(unsigned long) d40c);
@@ -2142,8 +3232,8 @@ static int __init d40_dmaengine_init(struct d40_base *base,
base->dma_slave.device_prep_dma_memcpy = d40_prep_memcpy;
base->dma_slave.device_prep_slave_sg = d40_prep_slave_sg;
base->dma_slave.device_tx_status = d40_tx_status;
- base->dma_slave.device_issue_pending = d40_issue_pending;
base->dma_slave.device_control = d40_control;
+ base->dma_slave.device_issue_pending = d40_issue_pending;
base->dma_slave.dev = base->dev;
err = dma_async_device_register(&base->dma_slave);
@@ -2166,8 +3256,8 @@ static int __init d40_dmaengine_init(struct d40_base *base,
base->dma_memcpy.device_prep_dma_memcpy = d40_prep_memcpy;
base->dma_memcpy.device_prep_slave_sg = d40_prep_slave_sg;
base->dma_memcpy.device_tx_status = d40_tx_status;
- base->dma_memcpy.device_issue_pending = d40_issue_pending;
base->dma_memcpy.device_control = d40_control;
+ base->dma_memcpy.device_issue_pending = d40_issue_pending;
base->dma_memcpy.dev = base->dev;
/*
* This controller can only access address at even
@@ -2196,8 +3286,9 @@ static int __init d40_dmaengine_init(struct d40_base *base,
base->dma_both.device_prep_dma_memcpy = d40_prep_memcpy;
base->dma_both.device_prep_slave_sg = d40_prep_slave_sg;
base->dma_both.device_tx_status = d40_tx_status;
- base->dma_both.device_issue_pending = d40_issue_pending;
base->dma_both.device_control = d40_control;
+ base->dma_both.device_issue_pending = d40_issue_pending;
+
base->dma_both.dev = base->dev;
base->dma_both.copy_align = 2;
err = dma_async_device_register(&base->dma_both);
@@ -2217,6 +3308,26 @@ failure1:
return err;
}
+/* Suspend resume functionality */
+#ifdef CONFIG_PM
+static int d40_pm_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ unsigned long flags;
+ int ret = 0;
+ struct d40_base *base = platform_get_drvdata(pdev);
+
+ spin_lock_irqsave(&base->usage_lock, flags);
+ if (base->usage)
+ ret = -EINVAL;
+ spin_unlock_irqrestore(&base->usage_lock, flags);
+
+ return ret;
+
+}
+#else
+#define d40_pm_suspend NULL
+#endif
+
/* Initialization functions. */
static int __init d40_phy_res_init(struct d40_base *base)
@@ -2224,6 +3335,7 @@ static int __init d40_phy_res_init(struct d40_base *base)
int i;
int num_phy_chans_avail = 0;
u32 val[2];
+ u32 gcc = D40_DREG_GCC_ENA;
int odd_even_bit = -2;
val[0] = readl(base->virtbase + D40_DREG_PRSME);
@@ -2232,17 +3344,37 @@ static int __init d40_phy_res_init(struct d40_base *base)
for (i = 0; i < base->num_phy_chans; i++) {
base->phy_res[i].num = i;
odd_even_bit += 2 * ((i % 2) == 0);
- if (((val[i % 2] >> odd_even_bit) & 3) == 1) {
+ if (((val[i % 2] >> odd_even_bit) & D40_DREG_PRSM_MODE_MASK)
+ == D40_DREG_PRSM_MODE_SECURE) {
/* Mark security only channels as occupied */
base->phy_res[i].allocated_src = D40_ALLOC_PHY;
base->phy_res[i].allocated_dst = D40_ALLOC_PHY;
+ base->phy_res[i].reserved = true;
+
+ gcc |= D40_DREG_GCC_EVTGRP_ENA(D40_PHYS_TO_GROUP(i),
+ D40_DREG_GCC_DST);
+ gcc |= D40_DREG_GCC_EVTGRP_ENA(D40_PHYS_TO_GROUP(i),
+ D40_DREG_GCC_SRC);
+
} else {
base->phy_res[i].allocated_src = D40_ALLOC_FREE;
base->phy_res[i].allocated_dst = D40_ALLOC_FREE;
+ base->phy_res[i].reserved = false;
num_phy_chans_avail++;
}
spin_lock_init(&base->phy_res[i].lock);
}
+
+ /* Mark disabled channels as occupied */
+ for (i = 0; base->plat_data->disabled_channels[i] != -1; i++) {
+ int chan = base->plat_data->disabled_channels[i];
+
+ base->phy_res[chan].allocated_src = D40_ALLOC_PHY;
+ base->phy_res[chan].allocated_dst = D40_ALLOC_PHY;
+ base->phy_res[chan].reserved = true;
+ num_phy_chans_avail--;
+ }
+
dev_info(base->dev, "%d of %d physical DMA channels available\n",
num_phy_chans_avail, base->num_phy_chans);
@@ -2260,6 +3392,9 @@ static int __init d40_phy_res_init(struct d40_base *base)
val[0] = val[0] >> 2;
}
+ /* Enable all clocks -- revisit after HW bug is fixed */
+ writel(D40_DREG_GCC_ENABLE_ALL, base->virtbase + D40_DREG_GCC);
+
return num_phy_chans_avail;
}
@@ -2271,9 +3406,10 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
{ .reg = D40_DREG_PERIPHID1, .val = 0x0000},
/*
* D40_DREG_PERIPHID2 Depends on HW revision:
- * MOP500/HREF ED has 0x0008,
+ * DB8500ed has 0x0008,
* ? has 0x0018,
- * HREF V1 has 0x0028
+ * DB8500v1 has 0x0028
+ * DB8500v2 has 0x0038
*/
{ .reg = D40_DREG_PERIPHID3, .val = 0x0000},
@@ -2285,12 +3421,14 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
};
struct stedma40_platform_data *plat_data;
struct clk *clk = NULL;
+ struct ux500_regulator *regulator = NULL;
void __iomem *virtbase = NULL;
struct resource *res = NULL;
struct d40_base *base = NULL;
int num_log_chans = 0;
int num_phy_chans;
int i;
+ u32 val;
clk = clk_get(&pdev->dev, NULL);
@@ -2300,6 +3438,20 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
goto failure;
}
+ regulator = ux500_regulator_get(&pdev->dev);
+ if (IS_ERR(regulator)) {
+ dev_warn(&pdev->dev, "[%s] No matching regulator found\n",
+ __func__);
+ regulator = NULL;
+ }
+
+ if (regulator)
+ ux500_regulator_atomic_enable(regulator);
+
+ /*
+ * Since the secure world does not handle clock, we have to
+ * let it run all the time
+ */
clk_enable(clk);
/* Get IO for DMAC base address */
@@ -2329,12 +3481,13 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
}
}
- i = readl(virtbase + D40_DREG_PERIPHID2);
+ /* Get silicon revision */
+ val = readl(virtbase + D40_DREG_PERIPHID2);
- if ((i & 0xf) != D40_PERIPHID2_DESIGNER) {
+ if ((val & 0xf) != D40_PERIPHID2_DESIGNER) {
dev_err(&pdev->dev,
"[%s] Unknown designer! Got %x wanted %x\n",
- __func__, i & 0xf, D40_PERIPHID2_DESIGNER);
+ __func__, val & 0xf, D40_PERIPHID2_DESIGNER);
goto failure;
}
@@ -2342,7 +3495,7 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4;
dev_info(&pdev->dev, "hardware revision: %d @ 0x%x\n",
- (i >> 4) & 0xf, res->start);
+ (val >> 4) & 0xf, res->start);
plat_data = pdev->dev.platform_data;
@@ -2364,7 +3517,9 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
goto failure;
}
+ base->rev = (val >> 4) & 0xf;
base->clk = clk;
+ base->regulator = regulator;
base->num_phy_chans = num_phy_chans;
base->num_log_chans = num_log_chans;
base->phy_start = res->start;
@@ -2374,6 +3529,7 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
base->dev = &pdev->dev;
base->phy_chans = ((void *)base) + ALIGN(sizeof(struct d40_base), 4);
base->log_chans = &base->phy_chans[num_phy_chans];
+ base->usage = 1;
base->phy_res = kzalloc(num_phy_chans * sizeof(struct d40_phy_res),
GFP_KERNEL);
@@ -2397,15 +3553,34 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
if (!base->lookup_log_chans)
goto failure;
}
- base->lcla_pool.alloc_map = kzalloc(num_phy_chans * sizeof(u32),
+
+ base->reg_val_backup_chan = kmalloc(base->num_phy_chans *
+ sizeof(d40_backup_regs_chan),
GFP_KERNEL);
+ if (!base->reg_val_backup_chan)
+ goto failure;
+
+ base->lcla_pool.alloc_map =
+ kzalloc(num_phy_chans * sizeof(struct d40_desc *)
+ * D40_LCLA_LINK_PER_EVENT_GRP, GFP_KERNEL);
if (!base->lcla_pool.alloc_map)
goto failure;
+ base->desc_slab = kmem_cache_create(D40_NAME, sizeof(struct d40_desc),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL);
+ if (base->desc_slab == NULL)
+ goto failure;
+
return base;
failure:
- if (clk) {
+ if (regulator) {
+ ux500_regulator_atomic_disable(regulator);
+ ux500_regulator_put(regulator);
+ }
+
+ if (!IS_ERR(clk)) {
clk_disable(clk);
clk_put(clk);
}
@@ -2419,6 +3594,7 @@ failure:
if (base) {
kfree(base->lcla_pool.alloc_map);
+ kfree(base->reg_val_backup_chan);
kfree(base->lookup_log_chans);
kfree(base->lookup_phy_chans);
kfree(base->phy_res);
@@ -2431,9 +3607,9 @@ failure:
static void __init d40_hw_init(struct d40_base *base)
{
- static const struct d40_reg_val dma_init_reg[] = {
+ static struct d40_reg_val dma_init_reg[] = {
/* Clock every part of the DMA block from start */
- { .reg = D40_DREG_GCC, .val = 0x0000ff01},
+ { .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL},
/* Interrupts on all logical channels */
{ .reg = D40_DREG_LCMIS0, .val = 0xFFFFFFFF},
@@ -2495,6 +3671,81 @@ static void __init d40_hw_init(struct d40_base *base)
}
+static int __init d40_lcla_allocate(struct d40_base *base)
+{
+ unsigned long *page_list;
+ int i;
+ int j;
+ int ret = 0;
+
+ /*
+ * This is somewhat ugly. We need 8192 bytes that are 18 bit aligned,
+ * To full fill this hardware requirement without wasting 256 kb
+ * we allocate pages until we get an aligned one.
+ */
+ page_list = kmalloc(sizeof(unsigned long) * MAX_LCLA_ALLOC_ATTEMPTS,
+ GFP_KERNEL);
+
+ if (!page_list) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ /* Calculating how many pages that are required */
+ base->lcla_pool.pages = SZ_1K * base->num_phy_chans / PAGE_SIZE;
+
+ for (i = 0; i < MAX_LCLA_ALLOC_ATTEMPTS; i++) {
+ page_list[i] = __get_free_pages(GFP_KERNEL,
+ base->lcla_pool.pages);
+ if (!page_list[i]) {
+
+ dev_err(base->dev,
+ "[%s] Failed to allocate %d pages.\n",
+ __func__, base->lcla_pool.pages);
+
+ for (j = 0; j < i; j++)
+ free_pages(page_list[j], base->lcla_pool.pages);
+ goto failure;
+ }
+
+ if ((virt_to_phys((void *)page_list[i]) &
+ (LCLA_ALIGNMENT - 1)) == 0)
+ break;
+ }
+
+ for (j = 0; j < i; j++)
+ free_pages(page_list[j], base->lcla_pool.pages);
+
+ if (i < MAX_LCLA_ALLOC_ATTEMPTS) {
+ base->lcla_pool.base = (void *)page_list[i];
+ } else {
+ /*
+ * After many attempts, no succees with finding the correct
+ * alignment try with allocating a big buffer.
+ */
+ dev_warn(base->dev,
+ "[%s] Failed to get %d pages @ 18 bit align.\n",
+ __func__, base->lcla_pool.pages);
+ base->lcla_pool.base_unaligned = kmalloc(SZ_1K *
+ base->num_phy_chans +
+ LCLA_ALIGNMENT,
+ GFP_KERNEL);
+ if (!base->lcla_pool.base_unaligned) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ base->lcla_pool.base = PTR_ALIGN(base->lcla_pool.base_unaligned,
+ LCLA_ALIGNMENT);
+ }
+
+ writel(virt_to_phys(base->lcla_pool.base),
+ base->virtbase + D40_DREG_LCLA);
+failure:
+ kfree(page_list);
+ return ret;
+}
+
static int __init d40_probe(struct platform_device *pdev)
{
int err;
@@ -2503,6 +3754,7 @@ static int __init d40_probe(struct platform_device *pdev)
struct resource *res = NULL;
int num_reserved_chans;
u32 val;
+ unsigned long flags;
base = d40_hw_detect_init(pdev);
@@ -2515,6 +3767,7 @@ static int __init d40_probe(struct platform_device *pdev)
spin_lock_init(&base->interrupt_lock);
spin_lock_init(&base->execmd_lock);
+ spin_lock_init(&base->usage_lock);
/* Get IO for logical channel parameter address */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lcpa");
@@ -2554,48 +3807,25 @@ static int __init d40_probe(struct platform_device *pdev)
__func__);
goto failure;
}
- /* Get IO for logical channel link address */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lcla");
- if (!res) {
- ret = -ENOENT;
- dev_err(&pdev->dev,
- "[%s] No \"lcla\" resource defined\n",
- __func__);
- goto failure;
- }
- base->lcla_pool.base_size = resource_size(res);
- base->lcla_pool.phy = res->start;
+ ret = d40_lcla_allocate(base);
- if (request_mem_region(res->start, resource_size(res),
- D40_NAME " I/O lcla") == NULL) {
- ret = -EBUSY;
- dev_err(&pdev->dev,
- "[%s] Failed to request LCLA region 0x%x-0x%x\n",
- __func__, res->start, res->end);
+ if (ret) {
+ dev_err(&pdev->dev, "[%s] Failed to allocate LCLA area\n",
+ __func__);
goto failure;
- }
- val = readl(base->virtbase + D40_DREG_LCLA);
- if (res->start != val && val != 0) {
- dev_warn(&pdev->dev,
- "[%s] Mismatch LCLA dma 0x%x, def 0x%x\n",
- __func__, val, res->start);
- } else
- writel(res->start, base->virtbase + D40_DREG_LCLA);
- base->lcla_pool.base = ioremap(res->start, resource_size(res));
- if (!base->lcla_pool.base) {
- ret = -ENOMEM;
- dev_err(&pdev->dev,
- "[%s] Failed to ioremap LCLA 0x%x-0x%x\n",
- __func__, res->start, res->end);
- goto failure;
}
+#ifdef CONFIG_STE_DMA40_DEBUG
+ sted40_history_set_virtbase(base->virtbase,
+ base->lcpa_base,
+ base->lcpa_size,
+ base->lcla_pool.base,
+ SZ_1K * base->num_phy_chans);
+#endif
spin_lock_init(&base->lcla_pool.lock);
- base->lcla_pool.num_blocks = base->num_phy_chans;
-
base->irq = platform_get_irq(pdev, 0);
ret = request_irq(base->irq, d40_handle_interrupt, 0, D40_NAME, base);
@@ -2611,16 +3841,31 @@ static int __init d40_probe(struct platform_device *pdev)
d40_hw_init(base);
+ spin_lock_irqsave(&base->usage_lock, flags);
+
+ base->usage--;
+ d40_save_restore_registers(base, true);
+
+ if (base->regulator)
+ ux500_regulator_atomic_disable(base->regulator);
+
+ spin_unlock_irqrestore(&base->usage_lock, flags);
+
dev_info(base->dev, "initialized\n");
return 0;
failure:
if (base) {
+ if (base->desc_slab)
+ kmem_cache_destroy(base->desc_slab);
if (base->virtbase)
iounmap(base->virtbase);
- if (base->lcla_pool.phy)
- release_mem_region(base->lcla_pool.phy,
- base->lcla_pool.base_size);
+ if (!base->lcla_pool.base_unaligned && base->lcla_pool.base)
+ free_pages((unsigned long)base->lcla_pool.base,
+ base->lcla_pool.pages);
+
+ kfree(base->lcla_pool.base_unaligned);
+
if (base->phy_lcpa)
release_mem_region(base->phy_lcpa,
base->lcpa_size);
@@ -2632,6 +3877,11 @@ failure:
clk_put(base->clk);
}
+ if (base->regulator) {
+ ux500_regulator_atomic_disable(base->regulator);
+ ux500_regulator_put(base->regulator);
+ }
+
kfree(base->lcla_pool.alloc_map);
kfree(base->lookup_log_chans);
kfree(base->lookup_phy_chans);
@@ -2648,6 +3898,7 @@ static struct platform_driver d40_driver = {
.owner = THIS_MODULE,
.name = D40_NAME,
},
+ .suspend = d40_pm_suspend,
};
int __init stedma40_init(void)
diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c
index 561fdd8a80c..f284f9e88c8 100644
--- a/drivers/dma/ste_dma40_ll.c
+++ b/drivers/dma/ste_dma40_ll.c
@@ -1,16 +1,20 @@
/*
- * driver/dma/ste_dma40_ll.c
- *
- * Copyright (C) ST-Ericsson 2007-2010
+ * Copyright (C) ST-Ericsson SA 2007-2010
+ * Author: Per Friden <per.friden@stericsson.com> for ST-Ericsson
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
* License terms: GNU General Public License (GPL) version 2
- * Author: Per Friden <per.friden@stericsson.com>
- * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
*/
#include <linux/kernel.h>
#include <plat/ste_dma40.h>
#include "ste_dma40_ll.h"
+#ifdef CONFIG_STE_DMA40_DEBUG
+#include "ste_dma40_debug.h"
+#define MARK sted40_history_text((char *)__func__)
+#else
+#define MARK
+#endif
/* Sets up proper LCSP1 and LCSP3 register for a logical channel */
void d40_log_cfg(struct stedma40_chan_cfg *cfg,
@@ -39,16 +43,13 @@ void d40_log_cfg(struct stedma40_chan_cfg *cfg,
cfg->dir == STEDMA40_PERIPH_TO_PERIPH)
l3 |= 1 << D40_MEM_LCSP3_DCFG_MST_POS;
- l3 |= 1 << D40_MEM_LCSP3_DCFG_TIM_POS;
l3 |= 1 << D40_MEM_LCSP3_DCFG_EIM_POS;
l3 |= cfg->dst_info.psize << D40_MEM_LCSP3_DCFG_PSIZE_POS;
l3 |= cfg->dst_info.data_width << D40_MEM_LCSP3_DCFG_ESIZE_POS;
- l3 |= 1 << D40_MEM_LCSP3_DTCP_POS;
l1 |= 1 << D40_MEM_LCSP1_SCFG_EIM_POS;
l1 |= cfg->src_info.psize << D40_MEM_LCSP1_SCFG_PSIZE_POS;
l1 |= cfg->src_info.data_width << D40_MEM_LCSP1_SCFG_ESIZE_POS;
- l1 |= 1 << D40_MEM_LCSP1_STCP_POS;
*lcsp1 = l1;
*lcsp3 = l3;
@@ -113,13 +114,15 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
src |= 1 << D40_SREG_CFG_LOG_GIM_POS;
}
- if (cfg->channel_type & STEDMA40_HIGH_PRIORITY_CHANNEL) {
+ if (cfg->high_priority) {
src |= 1 << D40_SREG_CFG_PRI_POS;
dst |= 1 << D40_SREG_CFG_PRI_POS;
}
- src |= cfg->src_info.endianess << D40_SREG_CFG_LBE_POS;
- dst |= cfg->dst_info.endianess << D40_SREG_CFG_LBE_POS;
+ if (cfg->src_info.big_endian)
+ src |= 1 << D40_SREG_CFG_LBE_POS;
+ if (cfg->dst_info.big_endian)
+ dst |= 1 << D40_SREG_CFG_LBE_POS;
*src_cfg = src;
*dst_cfg = dst;
@@ -198,13 +201,15 @@ int d40_phy_sg_to_lli(struct scatterlist *sg,
u32 reg_cfg,
u32 data_width,
int psize,
- bool term_int)
+ bool cyclic,
+ bool cyclic_int)
{
int total_size = 0;
int i;
struct scatterlist *current_sg = sg;
dma_addr_t next_lli_phys;
dma_addr_t dst;
+ bool interrupt;
int err = 0;
for_each_sg(sg, current_sg, sg_len, i) {
@@ -213,16 +218,18 @@ int d40_phy_sg_to_lli(struct scatterlist *sg,
/* If this scatter list entry is the last one, no next link */
if (sg_len - 1 == i)
- next_lli_phys = 0;
+ next_lli_phys = cyclic ? lli_phys : 0;
else
next_lli_phys = ALIGN(lli_phys + (i + 1) *
sizeof(struct d40_phy_lli),
D40_LLI_ALIGN);
+ interrupt = cyclic ? cyclic_int : !next_lli_phys;
+
if (target)
dst = target;
else
- dst = sg_phys(current_sg);
+ dst = sg_dma_address(current_sg);
err = d40_phy_fill_lli(&lli[i],
dst,
@@ -230,7 +237,7 @@ int d40_phy_sg_to_lli(struct scatterlist *sg,
psize,
next_lli_phys,
reg_cfg,
- !next_lli_phys,
+ interrupt,
data_width,
target == dst);
if (err)
@@ -238,7 +245,7 @@ int d40_phy_sg_to_lli(struct scatterlist *sg,
}
return total_size;
- err:
+err:
return err;
}
@@ -271,11 +278,64 @@ void d40_phy_lli_write(void __iomem *virtbase,
/* DMA logical lli operations */
+static void d40_log_lli_link(struct d40_log_lli *lli_dst,
+ struct d40_log_lli *lli_src,
+ int next,
+ bool interrupt)
+{
+ u32 slos = 0;
+ u32 dlos = 0;
+
+ if (next != -EINVAL) {
+ slos = next * 2;
+ dlos = next * 2 + 1;
+ }
+
+ if (interrupt) {
+ lli_dst->lcsp13 |= D40_MEM_LCSP1_SCFG_TIM_MASK;
+ lli_dst->lcsp13 |= D40_MEM_LCSP3_DTCP_MASK;
+ }
+
+ lli_src->lcsp13 = (lli_src->lcsp13 & ~D40_MEM_LCSP1_SLOS_MASK) |
+ (slos << D40_MEM_LCSP1_SLOS_POS);
+
+ lli_dst->lcsp13 = (lli_dst->lcsp13 & ~D40_MEM_LCSP1_SLOS_MASK) |
+ (dlos << D40_MEM_LCSP1_SLOS_POS);
+}
+
+void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa,
+ struct d40_log_lli *lli_dst,
+ struct d40_log_lli *lli_src,
+ int next,
+ bool interrupt)
+{
+ d40_log_lli_link(lli_dst, lli_src, next, interrupt);
+
+ writel(lli_src->lcsp02, &lcpa[0].lcsp0);
+ writel(lli_src->lcsp13, &lcpa[0].lcsp1);
+ writel(lli_dst->lcsp02, &lcpa[0].lcsp2);
+ writel(lli_dst->lcsp13, &lcpa[0].lcsp3);
+}
+
+void d40_log_lli_lcla_write(struct d40_log_lli *lcla,
+ struct d40_log_lli *lli_dst,
+ struct d40_log_lli *lli_src,
+ int next,
+ bool interrupt)
+{
+ d40_log_lli_link(lli_dst, lli_src, next, interrupt);
+
+ writel(lli_src->lcsp02, &lcla[0].lcsp02);
+ writel(lli_src->lcsp13, &lcla[0].lcsp13);
+ writel(lli_dst->lcsp02, &lcla[1].lcsp02);
+ writel(lli_dst->lcsp13, &lcla[1].lcsp13);
+}
+
void d40_log_fill_lli(struct d40_log_lli *lli,
dma_addr_t data, u32 data_size,
- u32 lli_next_off, u32 reg_cfg,
+ u32 reg_cfg,
u32 data_width,
- bool term_int, bool addr_inc)
+ bool addr_inc)
{
lli->lcsp13 = reg_cfg;
@@ -290,165 +350,69 @@ void d40_log_fill_lli(struct d40_log_lli *lli,
if (addr_inc)
lli->lcsp13 |= D40_MEM_LCSP1_SCFG_INCR_MASK;
- lli->lcsp13 |= D40_MEM_LCSP3_DTCP_MASK;
- /* If this scatter list entry is the last one, no next link */
- lli->lcsp13 |= (lli_next_off << D40_MEM_LCSP1_SLOS_POS) &
- D40_MEM_LCSP1_SLOS_MASK;
-
- if (term_int)
- lli->lcsp13 |= D40_MEM_LCSP1_SCFG_TIM_MASK;
- else
- lli->lcsp13 &= ~D40_MEM_LCSP1_SCFG_TIM_MASK;
}
-int d40_log_sg_to_dev(struct d40_lcla_elem *lcla,
- struct scatterlist *sg,
+int d40_log_sg_to_dev(struct scatterlist *sg,
int sg_len,
struct d40_log_lli_bidir *lli,
struct d40_def_lcsp *lcsp,
u32 src_data_width,
u32 dst_data_width,
enum dma_data_direction direction,
- bool term_int, dma_addr_t dev_addr, int max_len,
- int llis_per_log)
+ dma_addr_t dev_addr)
{
int total_size = 0;
struct scatterlist *current_sg = sg;
int i;
- u32 next_lli_off_dst;
- u32 next_lli_off_src;
-
- next_lli_off_src = 0;
- next_lli_off_dst = 0;
for_each_sg(sg, current_sg, sg_len, i) {
total_size += sg_dma_len(current_sg);
- /*
- * If this scatter list entry is the last one or
- * max length, terminate link.
- */
- if (sg_len - 1 == i || ((i+1) % max_len == 0)) {
- next_lli_off_src = 0;
- next_lli_off_dst = 0;
- } else {
- if (next_lli_off_dst == 0 &&
- next_lli_off_src == 0) {
- /* The first lli will be at next_lli_off */
- next_lli_off_dst = (lcla->dst_id *
- llis_per_log + 1);
- next_lli_off_src = (lcla->src_id *
- llis_per_log + 1);
- } else {
- next_lli_off_dst++;
- next_lli_off_src++;
- }
- }
-
if (direction == DMA_TO_DEVICE) {
d40_log_fill_lli(&lli->src[i],
- sg_phys(current_sg),
+ sg_dma_address(current_sg),
sg_dma_len(current_sg),
- next_lli_off_src,
lcsp->lcsp1, src_data_width,
- term_int && !next_lli_off_src,
true);
d40_log_fill_lli(&lli->dst[i],
dev_addr,
sg_dma_len(current_sg),
- next_lli_off_dst,
lcsp->lcsp3, dst_data_width,
- /* No next == terminal interrupt */
- term_int && !next_lli_off_dst,
false);
} else {
d40_log_fill_lli(&lli->dst[i],
- sg_phys(current_sg),
+ sg_dma_address(current_sg),
sg_dma_len(current_sg),
- next_lli_off_dst,
lcsp->lcsp3, dst_data_width,
- /* No next == terminal interrupt */
- term_int && !next_lli_off_dst,
true);
d40_log_fill_lli(&lli->src[i],
dev_addr,
sg_dma_len(current_sg),
- next_lli_off_src,
lcsp->lcsp1, src_data_width,
- term_int && !next_lli_off_src,
false);
}
}
return total_size;
}
-int d40_log_sg_to_lli(int lcla_id,
- struct scatterlist *sg,
+int d40_log_sg_to_lli(struct scatterlist *sg,
int sg_len,
struct d40_log_lli *lli_sg,
u32 lcsp13, /* src or dst*/
- u32 data_width,
- bool term_int, int max_len, int llis_per_log)
+ u32 data_width)
{
int total_size = 0;
struct scatterlist *current_sg = sg;
int i;
- u32 next_lli_off = 0;
for_each_sg(sg, current_sg, sg_len, i) {
total_size += sg_dma_len(current_sg);
- /*
- * If this scatter list entry is the last one or
- * max length, terminate link.
- */
- if (sg_len - 1 == i || ((i+1) % max_len == 0))
- next_lli_off = 0;
- else {
- if (next_lli_off == 0)
- /* The first lli will be at next_lli_off */
- next_lli_off = lcla_id * llis_per_log + 1;
- else
- next_lli_off++;
- }
-
d40_log_fill_lli(&lli_sg[i],
- sg_phys(current_sg),
+ sg_dma_address(current_sg),
sg_dma_len(current_sg),
- next_lli_off,
lcsp13, data_width,
- term_int && !next_lli_off,
true);
}
return total_size;
}
-
-void d40_log_lli_write(struct d40_log_lli_full *lcpa,
- struct d40_log_lli *lcla_src,
- struct d40_log_lli *lcla_dst,
- struct d40_log_lli *lli_dst,
- struct d40_log_lli *lli_src,
- int llis_per_log)
-{
- u32 slos = 0;
- u32 dlos = 0;
- int i;
-
- lcpa->lcsp0 = lli_src->lcsp02;
- lcpa->lcsp1 = lli_src->lcsp13;
- lcpa->lcsp2 = lli_dst->lcsp02;
- lcpa->lcsp3 = lli_dst->lcsp13;
-
- slos = lli_src->lcsp13 & D40_MEM_LCSP1_SLOS_MASK;
- dlos = lli_dst->lcsp13 & D40_MEM_LCSP3_DLOS_MASK;
-
- for (i = 0; (i < llis_per_log) && slos && dlos; i++) {
- writel(lli_src[i+1].lcsp02, &lcla_src[i].lcsp02);
- writel(lli_src[i+1].lcsp13, &lcla_src[i].lcsp13);
- writel(lli_dst[i+1].lcsp02, &lcla_dst[i].lcsp02);
- writel(lli_dst[i+1].lcsp13, &lcla_dst[i].lcsp13);
-
- slos = lli_src[i+1].lcsp13 & D40_MEM_LCSP1_SLOS_MASK;
- dlos = lli_dst[i+1].lcsp13 & D40_MEM_LCSP3_DLOS_MASK;
- }
-}
diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h
index 2029280cb33..22b5fd76f96 100644
--- a/drivers/dma/ste_dma40_ll.h
+++ b/drivers/dma/ste_dma40_ll.h
@@ -1,10 +1,8 @@
/*
- * driver/dma/ste_dma40_ll.h
- *
- * Copyright (C) ST-Ericsson 2007-2010
+ * Copyright (C) ST-Ericsson SA 2007-2010
+ * Author: Per Friden <per.friden@stericsson.com> for ST-Ericsson SA
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson SA
* License terms: GNU General Public License (GPL) version 2
- * Author: Per Friden <per.friden@stericsson.com>
- * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
*/
#ifndef STE_DMA40_LL_H
#define STE_DMA40_LL_H
@@ -13,8 +11,13 @@
#define D40_DREG_PCDELTA (8 * 4)
#define D40_LLI_ALIGN 16 /* LLI alignment must be 16 bytes. */
+#define D40_LCPA_CHAN_SIZE 32
+#define D40_LCPA_CHAN_DST_DELTA 16
+
#define D40_TYPE_TO_GROUP(type) (type / 16)
#define D40_TYPE_TO_EVENT(type) (type % 16)
+#define D40_GROUP_SIZE 8
+#define D40_PHYS_TO_GROUP(phys) ((phys & (D40_GROUP_SIZE - 1)) / 2)
/* Most bits of the CFG register are the same in log as in phy mode */
#define D40_SREG_CFG_MST_POS 15
@@ -93,10 +96,13 @@
/* LCSP2 */
#define D40_MEM_LCSP2_ECNT_POS 16
+#define D40_MEM_LCSP2_DPTR_POS 0
#define D40_MEM_LCSP2_ECNT_MASK (0xFFFF << D40_MEM_LCSP2_ECNT_POS)
+#define D40_MEM_LCSP2_DPTR_MASK (0xFFFF << D40_MEM_LCSP2_DPTR_POS)
/* LCSP3 */
+#define D40_MEM_LCSP3_DPTR_POS 16
#define D40_MEM_LCSP3_DCFG_MST_POS 15
#define D40_MEM_LCSP3_DCFG_TIM_POS 14
#define D40_MEM_LCSP3_DCFG_EIM_POS 13
@@ -106,6 +112,7 @@
#define D40_MEM_LCSP3_DLOS_POS 1
#define D40_MEM_LCSP3_DTCP_POS 0
+#define D40_MEM_LCSP3_DPTR_MASK (0xFFFF << D40_MEM_LCSP3_DPTR_POS)
#define D40_MEM_LCSP3_DLOS_MASK (0x7F << D40_MEM_LCSP3_DLOS_POS)
#define D40_MEM_LCSP3_DTCP_MASK (0x1 << D40_MEM_LCSP3_DTCP_POS)
@@ -122,13 +129,32 @@
/* DMA Register Offsets */
#define D40_DREG_GCC 0x000
+#define D40_DREG_GCC_ENA 0x1
+/* This assumes that there are only 4 event groups */
+#define D40_DREG_GCC_ENABLE_ALL 0xff01
+#define D40_DREG_GCC_EVTGRP_POS 8
+#define D40_DREG_GCC_SRC 0
+#define D40_DREG_GCC_DST 1
+#define D40_DREG_GCC_EVTGRP_ENA(x, y) \
+ (1 << (D40_DREG_GCC_EVTGRP_POS + 2 * x + y))
+
#define D40_DREG_PRTYP 0x004
#define D40_DREG_PRSME 0x008
#define D40_DREG_PRSMO 0x00C
+#define D40_DREG_PRSM_MODE_MASK 0x3
+#define D40_DREG_PRSM_MODE_SECURE 0x1
+
#define D40_DREG_PRMSE 0x010
#define D40_DREG_PRMSO 0x014
#define D40_DREG_PRMOE 0x018
#define D40_DREG_PRMOO 0x01C
+#define D40_DREG_PRMO_PCHAN_BASIC 0x1
+#define D40_DREG_PRMO_PCHAN_MODULO 0x2
+#define D40_DREG_PRMO_PCHAN_DOUBLE_DST 0x3
+#define D40_DREG_PRMO_LCHAN_SRC_PHY_DST_LOG 0x1
+#define D40_DREG_PRMO_LCHAN_SRC_LOG_DST_PHY 0x2
+#define D40_DREG_PRMO_LCHAN_SRC_LOG_DST_LOG 0x3
+
#define D40_DREG_LCPA 0x020
#define D40_DREG_LCLA 0x024
#define D40_DREG_ACTIVE 0x050
@@ -155,6 +181,22 @@
#define D40_DREG_LCEIS1 0x0B4
#define D40_DREG_LCEIS2 0x0B8
#define D40_DREG_LCEIS3 0x0BC
+#define D40_DREG_PSEG1 0x110
+#define D40_DREG_PSEG2 0x114
+#define D40_DREG_PSEG3 0x118
+#define D40_DREG_PSEG4 0x11C
+#define D40_DREG_PCEG1 0x120
+#define D40_DREG_PCEG2 0x124
+#define D40_DREG_PCEG3 0x128
+#define D40_DREG_PCEG4 0x12C
+#define D40_DREG_RSEG1 0x130
+#define D40_DREG_RSEG2 0x134
+#define D40_DREG_RSEG3 0x138
+#define D40_DREG_RSEG4 0x13C
+#define D40_DREG_RCEG1 0x140
+#define D40_DREG_RCEG2 0x144
+#define D40_DREG_RCEG3 0x148
+#define D40_DREG_RCEG4 0x14C
#define D40_DREG_STFU 0xFC8
#define D40_DREG_ICFG 0xFCC
#define D40_DREG_PERIPHID0 0xFE0
@@ -196,8 +238,6 @@ struct d40_phy_lli {
*
* @src: Register settings for src channel.
* @dst: Register settings for dst channel.
- * @dst_addr: Physical destination address.
- * @src_addr: Physical source address.
*
* All DMA transfers have a source and a destination.
*/
@@ -205,8 +245,6 @@ struct d40_phy_lli {
struct d40_phy_lli_bidir {
struct d40_phy_lli *src;
struct d40_phy_lli *dst;
- dma_addr_t dst_addr;
- dma_addr_t src_addr;
};
@@ -268,29 +306,16 @@ struct d40_def_lcsp {
u32 lcsp1;
};
-/**
- * struct d40_lcla_elem - Info for one LCA element.
- *
- * @src_id: logical channel src id
- * @dst_id: logical channel dst id
- * @src: LCPA formated src parameters
- * @dst: LCPA formated dst parameters
- *
- */
-struct d40_lcla_elem {
- int src_id;
- int dst_id;
- struct d40_log_lli *src;
- struct d40_log_lli *dst;
-};
-
/* Physical channels */
void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
- u32 *src_cfg, u32 *dst_cfg, bool is_log);
+ u32 *src_cfg,
+ u32 *dst_cfg,
+ bool is_log);
void d40_log_cfg(struct stedma40_chan_cfg *cfg,
- u32 *lcsp1, u32 *lcsp2);
+ u32 *lcsp1,
+ u32 *lcsp2);
int d40_phy_sg_to_lli(struct scatterlist *sg,
int sg_len,
@@ -300,7 +325,8 @@ int d40_phy_sg_to_lli(struct scatterlist *sg,
u32 reg_cfg,
u32 data_width,
int psize,
- bool term_int);
+ bool cyclic,
+ bool cyclic_int);
int d40_phy_fill_lli(struct d40_phy_lli *lli,
dma_addr_t data,
@@ -320,35 +346,35 @@ void d40_phy_lli_write(void __iomem *virtbase,
/* Logical channels */
void d40_log_fill_lli(struct d40_log_lli *lli,
- dma_addr_t data, u32 data_size,
- u32 lli_next_off, u32 reg_cfg,
+ dma_addr_t data,
+ u32 data_size,
+ u32 reg_cfg,
u32 data_width,
- bool term_int, bool addr_inc);
+ bool addr_inc);
-int d40_log_sg_to_dev(struct d40_lcla_elem *lcla,
- struct scatterlist *sg,
+int d40_log_sg_to_dev(struct scatterlist *sg,
int sg_len,
struct d40_log_lli_bidir *lli,
struct d40_def_lcsp *lcsp,
u32 src_data_width,
u32 dst_data_width,
enum dma_data_direction direction,
- bool term_int, dma_addr_t dev_addr, int max_len,
- int llis_per_log);
-
-void d40_log_lli_write(struct d40_log_lli_full *lcpa,
- struct d40_log_lli *lcla_src,
- struct d40_log_lli *lcla_dst,
- struct d40_log_lli *lli_dst,
- struct d40_log_lli *lli_src,
- int llis_per_log);
-
-int d40_log_sg_to_lli(int lcla_id,
- struct scatterlist *sg,
+ dma_addr_t dev_addr);
+
+int d40_log_sg_to_lli(struct scatterlist *sg,
int sg_len,
struct d40_log_lli *lli_sg,
u32 lcsp13, /* src or dst*/
- u32 data_width,
- bool term_int, int max_len, int llis_per_log);
+ u32 data_width);
+
+void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa,
+ struct d40_log_lli *lli_dst,
+ struct d40_log_lli *lli_src,
+ int next, bool interrupt);
+
+void d40_log_lli_lcla_write(struct d40_log_lli *lcla,
+ struct d40_log_lli *lli_dst,
+ struct d40_log_lli *lli_src,
+ int next, bool interrupt);
#endif /* STE_DMA40_LLI_H */
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7face915b96..c5cb2000098 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -195,6 +195,13 @@ config GPIO_PCF857X
This driver provides an in-kernel interface to those GPIOs using
platform-neutral GPIO calls.
+config GPIO_STMPE
+ bool "STMPE GPIOs"
+ depends on MFD_STMPE
+ help
+ This enables support for the GPIOs found on the STMPE I/O
+ Expanders.
+
config GPIO_TC35892
bool "TC35892 GPIOs"
depends on MFD_TC35892
@@ -343,4 +350,9 @@ config GPIO_JANZ_TTL
This driver provides support for driving the pins in output
mode only. Input mode is not supported.
+config AB8500_GPIO
+ bool "ST-Ericsson AB8500 Mixed Signal Circuit gpio functions"
+ depends on AB8500_CORE
+ help
+ Select this to enable the AB8500 IC GPIO driver
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index e53dcff49b4..88d66867875 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o
obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
obj-$(CONFIG_GPIO_PL061) += pl061.o
+obj-$(CONFIG_GPIO_STMPE) += stmpe-gpio.o
obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o
obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o
obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
@@ -35,3 +36,4 @@ obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o
obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
+obj-$(CONFIG_AB8500_GPIO) += ab8500-gpio.o
diff --git a/drivers/gpio/ab8500-gpio.c b/drivers/gpio/ab8500-gpio.c
new file mode 100644
index 00000000000..502fc0c5687
--- /dev/null
+++ b/drivers/gpio/ab8500-gpio.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) ST-Ericsson 2010
+ *
+ * Author: BIBEK BASU <bibek.basu@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500/ab8500-gpio.h>
+
+/*
+ * GPIO registers offset
+ * Bank: 0x10
+ */
+#define AB8500_GPIO_SEL1_REG 0x00
+#define AB8500_GPIO_SEL2_REG 0x01
+#define AB8500_GPIO_SEL3_REG 0x02
+#define AB8500_GPIO_SEL4_REG 0x03
+#define AB8500_GPIO_SEL5_REG 0x04
+#define AB8500_GPIO_SEL6_REG 0x05
+
+#define AB8500_GPIO_DIR1_REG 0x10
+#define AB8500_GPIO_DIR2_REG 0x11
+#define AB8500_GPIO_DIR3_REG 0x12
+#define AB8500_GPIO_DIR4_REG 0x13
+#define AB8500_GPIO_DIR5_REG 0x14
+#define AB8500_GPIO_DIR6_REG 0x15
+
+#define AB8500_GPIO_OUT1_REG 0x20
+#define AB8500_GPIO_OUT2_REG 0x21
+#define AB8500_GPIO_OUT3_REG 0x22
+#define AB8500_GPIO_OUT4_REG 0x23
+#define AB8500_GPIO_OUT5_REG 0x24
+#define AB8500_GPIO_OUT6_REG 0x25
+
+#define AB8500_GPIO_PUD1_REG 0x30
+#define AB8500_GPIO_PUD2_REG 0x31
+#define AB8500_GPIO_PUD3_REG 0x32
+#define AB8500_GPIO_PUD4_REG 0x33
+#define AB8500_GPIO_PUD5_REG 0x34
+#define AB8500_GPIO_PUD6_REG 0x35
+
+#define AB8500_GPIO_IN1_REG 0x40
+#define AB8500_GPIO_IN2_REG 0x41
+#define AB8500_GPIO_IN3_REG 0x42
+#define AB8500_GPIO_IN4_REG 0x43
+#define AB8500_GPIO_IN5_REG 0x44
+#define AB8500_GPIO_IN6_REG 0x45
+#define AB8500_GPIO_ALTFUN_REG 0x50
+#define ALTFUN_REG_INDEX 6
+#define AB8500_NUM_GPIO 42
+#define AB8500_NUM_VIR_GPIO_IRQ 16
+
+enum {
+ NONE,
+ STARTUP,
+ SHUTDOWN,
+ MASK,
+ UNMASK
+};
+
+struct ab8500_gpio {
+ struct gpio_chip chip;
+ struct ab8500 *parent;
+ struct device *dev;
+ struct mutex lock;
+ u32 irq_base;
+ u32 irq_action;
+ u16 rising;
+ u16 falling;
+};
+/**
+ * to_ab8500_gpio() - get the pointer to ab8500_gpio
+ * @chip: Member of the structure ab8500_gpio
+ */
+static inline struct ab8500_gpio *to_ab8500_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct ab8500_gpio, chip);
+}
+
+static int ab8500_gpio_set_bits(struct gpio_chip *chip, u8 reg,
+ unsigned offset, int val)
+{
+ struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip);
+ u8 pos = offset % 8;
+ int ret;
+ reg = reg + (offset / 8);
+ ret = abx500_mask_and_set_register_interruptible(ab8500_gpio->dev,
+ AB8500_MISC, reg, 1 << pos, val << pos);
+ if (ret < 0)
+ dev_err(ab8500_gpio->dev, "%s write failed\n", __func__);
+ return ret;
+}
+/**
+ * ab8500_gpio_get() - Get the particular GPIO value
+ * @chip: Gpio device
+ * @offset: GPIO number to read
+ */
+static int ab8500_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip);
+ u8 mask = 1 << (offset % 8);
+ u8 reg = AB8500_GPIO_IN1_REG + (offset / 8);
+ int ret;
+ u8 data;
+ ret = abx500_get_register_interruptible(ab8500_gpio->dev, AB8500_MISC,
+ reg, &data);
+ if (ret < 0) {
+ dev_err(ab8500_gpio->dev, "%s read failed\n", __func__);
+ return ret;
+ }
+ return (data & mask) >> (offset % 8);
+}
+
+static void ab8500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip);
+ int ret;
+ /* Write the data */
+ ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val);
+ if (ret < 0)
+ dev_err(ab8500_gpio->dev, "%s write failed\n", __func__);
+}
+
+static int ab8500_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int val)
+{
+ int ret;
+ /* set direction as output */
+ ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 1);
+ if (ret < 0)
+ return ret;
+ /* disable pull down */
+ ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, offset, 1);
+ if (ret < 0)
+ return ret;
+ /* set the output as 1 or 0 */
+ return ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val);
+
+}
+
+static int ab8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ /* set the register as input */
+ return ab8500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 0);
+}
+
+static int ab8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip);
+ int irq_cluster1 = AB8500_PIN_GPIO13 - AB8500_PIN_GPIO6;
+ int irq_cluster2 = irq_cluster1 + 2;
+ offset = offset + ab8500_gpio->chip.base;
+
+ /*
+ * return irq number for only interrupt capable GPIOs
+ * GPIO6 to GPIO13 are IRQ capable
+ * GPIO24 & GPIo25 are IRQ capable
+ * GPIO36 to GPIO41 are IRQ capable
+ */
+ if (offset >= AB8500_PIN_GPIO6 && offset <= AB8500_PIN_GPIO13)
+ return ab8500_gpio->irq_base + offset - AB8500_PIN_GPIO6;
+ else
+ if (offset == AB8500_PIN_GPIO24 || offset == AB8500_PIN_GPIO25)
+ return ab8500_gpio->irq_base + offset
+ - AB8500_PIN_GPIO23 + irq_cluster1;
+ else
+ if (offset >= AB8500_PIN_GPIO36 && offset <= AB8500_PIN_GPIO41)
+ return ab8500_gpio->irq_base + offset
+ - AB8500_PIN_GPIO35 + irq_cluster2;
+ else
+ return -EINVAL;
+}
+
+static struct gpio_chip ab8500gpio_chip = {
+ .label = "ab8500_gpio",
+ .owner = THIS_MODULE,
+ .direction_input = ab8500_gpio_direction_input,
+ .get = ab8500_gpio_get,
+ .direction_output = ab8500_gpio_direction_output,
+ .set = ab8500_gpio_set,
+ .to_irq = ab8500_gpio_to_irq,
+};
+
+static unsigned int irq_to_rising(unsigned int irq)
+{
+ struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ int offset = irq - ab8500_gpio->irq_base;
+ int new_irq = offset + AB8500_INT_GPIO6R
+ + ab8500_gpio->parent->irq_base;
+ return new_irq;
+}
+
+static unsigned int irq_to_falling(unsigned int irq)
+{
+ struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ int offset = irq - ab8500_gpio->irq_base;
+ int new_irq = offset + AB8500_INT_GPIO6F
+ + ab8500_gpio->parent->irq_base;
+ return new_irq;
+
+}
+
+static unsigned int rising_to_irq(unsigned int irq, void *dev)
+{
+ struct ab8500_gpio *ab8500_gpio = dev;
+ int offset = irq - AB8500_INT_GPIO6R
+ - ab8500_gpio->parent->irq_base ;
+ int new_irq = offset + ab8500_gpio->irq_base;
+ return new_irq;
+}
+
+static unsigned int falling_to_irq(unsigned int irq, void *dev)
+{
+ struct ab8500_gpio *ab8500_gpio = dev;
+ int offset = irq - AB8500_INT_GPIO6F
+ - ab8500_gpio->parent->irq_base ;
+ int new_irq = offset + ab8500_gpio->irq_base;
+ return new_irq;
+
+}
+
+/*
+ * IRQ handler
+ */
+
+static irqreturn_t handle_rising(int irq, void *dev)
+{
+
+ handle_nested_irq(rising_to_irq(irq , dev));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t handle_falling(int irq, void *dev)
+{
+
+ handle_nested_irq(falling_to_irq(irq, dev));
+ return IRQ_HANDLED;
+}
+
+static void ab8500_gpio_irq_lock(unsigned int irq)
+{
+ struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ mutex_lock(&ab8500_gpio->lock);
+}
+
+static void ab8500_gpio_irq_sync_unlock(unsigned int irq)
+{
+ struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ int offset = irq - ab8500_gpio->irq_base;
+ bool rising = ab8500_gpio->rising & BIT(offset);
+ bool falling = ab8500_gpio->falling & BIT(offset);
+ int ret;
+
+ switch (ab8500_gpio->irq_action) {
+ case STARTUP:
+ if (rising)
+ ret = request_threaded_irq(irq_to_rising(irq),
+ NULL, handle_rising,
+ IRQF_TRIGGER_RISING,
+ "ab8500-gpio-r", ab8500_gpio);
+ if (falling)
+ ret = request_threaded_irq(irq_to_falling(irq),
+ NULL, handle_falling,
+ IRQF_TRIGGER_FALLING,
+ "ab8500-gpio-f", ab8500_gpio);
+ break;
+ case SHUTDOWN:
+ if (rising)
+ free_irq(irq_to_rising(irq), ab8500_gpio);
+ if (falling)
+ free_irq(irq_to_falling(irq), ab8500_gpio);
+ break;
+ case MASK:
+ if (rising)
+ disable_irq(irq_to_rising(irq));
+ if (falling)
+ disable_irq(irq_to_falling(irq));
+ break;
+ case UNMASK:
+ if (rising)
+ enable_irq(irq_to_rising(irq));
+ if (falling)
+ enable_irq(irq_to_falling(irq));
+ break;
+
+ }
+ ab8500_gpio->irq_action = NONE;
+ ab8500_gpio->rising &= ~(BIT(offset));
+ ab8500_gpio->falling &= ~(BIT(offset));
+ mutex_unlock(&ab8500_gpio->lock);
+}
+
+
+static void ab8500_gpio_irq_mask(unsigned int irq)
+{
+ struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ ab8500_gpio->irq_action = MASK;
+}
+
+static void ab8500_gpio_irq_unmask(unsigned int irq)
+{
+ struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ ab8500_gpio->irq_action = UNMASK;
+}
+
+static int ab8500_gpio_irq_set_type(unsigned int irq, unsigned int type)
+{
+ struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ int offset = irq - ab8500_gpio->irq_base;
+
+ if (type == IRQ_TYPE_EDGE_BOTH) {
+ ab8500_gpio->rising = BIT(offset);
+ ab8500_gpio->falling = BIT(offset);
+ } else if (type == IRQ_TYPE_EDGE_RISING) {
+ ab8500_gpio->rising = BIT(offset);
+ } else {
+ ab8500_gpio->falling = BIT(offset);
+ }
+ return 0;
+}
+
+unsigned int ab8500_gpio_irq_startup(unsigned int irq)
+{
+ struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ ab8500_gpio->irq_action = STARTUP;
+ return 0;
+}
+
+void ab8500_gpio_irq_shutdown(unsigned int irq)
+{
+ struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ ab8500_gpio->irq_action = SHUTDOWN;
+}
+
+static struct irq_chip ab8500_gpio_irq_chip = {
+ .name = "ab8500-gpio",
+ .startup = ab8500_gpio_irq_startup,
+ .shutdown = ab8500_gpio_irq_shutdown,
+ .bus_lock = ab8500_gpio_irq_lock,
+ .bus_sync_unlock = ab8500_gpio_irq_sync_unlock,
+ .mask = ab8500_gpio_irq_mask,
+ .unmask = ab8500_gpio_irq_unmask,
+ .set_type = ab8500_gpio_irq_set_type,
+};
+
+static int ab8500_gpio_irq_init(struct ab8500_gpio *ab8500_gpio)
+{
+ u32 base = ab8500_gpio->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ ; irq++) {
+ set_irq_chip_data(irq, ab8500_gpio);
+ set_irq_chip_and_handler(irq, &ab8500_gpio_irq_chip,
+ handle_simple_irq);
+ set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+
+ return 0;
+}
+
+static void ab8500_gpio_irq_remove(struct ab8500_gpio *ab8500_gpio)
+{
+ int base = ab8500_gpio->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ; irq++) {
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ set_irq_chip_and_handler(irq, NULL, NULL);
+ set_irq_chip_data(irq, NULL);
+ }
+}
+
+static int __devinit ab8500_gpio_probe(struct platform_device *pdev)
+{
+ struct ab8500_platform_data *ab8500_pdata =
+ dev_get_platdata(pdev->dev.parent);
+ struct ab8500_gpio_platform_data *pdata;
+ struct ab8500_gpio *ab8500_gpio;
+ int ret;
+ int i;
+
+ pdata = ab8500_pdata->gpio;
+ if (!pdata) {
+ dev_err(&pdev->dev, "gpio platform data missing\n");
+ return -ENODEV;
+ }
+
+ ab8500_gpio = kzalloc(sizeof(struct ab8500_gpio), GFP_KERNEL);
+ if (ab8500_gpio == NULL) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+ ab8500_gpio->dev = &pdev->dev;
+ ab8500_gpio->parent = dev_get_drvdata(pdev->dev.parent);
+ ab8500_gpio->chip = ab8500gpio_chip;
+ ab8500_gpio->chip.ngpio = AB8500_NUM_GPIO;
+ ab8500_gpio->chip.dev = &pdev->dev;
+ ab8500_gpio->chip.base = pdata->gpio_base;
+ ab8500_gpio->irq_base = pdata->irq_base;
+ /* initialize the lock */
+ mutex_init(&ab8500_gpio->lock);
+ /*
+ * AB8500 core will handle and clear the IRQ
+ * configre GPIO based on config-reg value.
+ * These values are for selecting the PINs as
+ * GPIO or alternate function
+ */
+ for (i = AB8500_GPIO_SEL1_REG; i <= AB8500_GPIO_SEL6_REG; i++) {
+ ret = abx500_set_register_interruptible(ab8500_gpio->dev,
+ AB8500_MISC, i,
+ pdata->config_reg[i]);
+ if (ret < 0)
+ goto out_free;
+ }
+ ret = abx500_set_register_interruptible(ab8500_gpio->dev, AB8500_MISC,
+ AB8500_GPIO_ALTFUN_REG,
+ pdata->config_reg[ALTFUN_REG_INDEX]);
+ if (ret < 0)
+ goto out_free;
+
+ ret = ab8500_gpio_irq_init(ab8500_gpio);
+ if (ret)
+ goto out_free;
+ ret = gpiochip_add(&ab8500_gpio->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to add gpiochip: %d\n",
+ ret);
+ goto out_rem_irq;
+ }
+ platform_set_drvdata(pdev, ab8500_gpio);
+ return 0;
+
+out_rem_irq:
+ ab8500_gpio_irq_remove(ab8500_gpio);
+out_free:
+ mutex_destroy(&ab8500_gpio->lock);
+ kfree(ab8500_gpio);
+ return ret;
+}
+
+/*
+ * ab8500_gpio_remove() - remove Ab8500-gpio driver
+ * @pdev : Platform device registered
+ */
+static int __devexit ab8500_gpio_remove(struct platform_device *pdev)
+{
+ struct ab8500_gpio *ab8500_gpio = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = gpiochip_remove(&ab8500_gpio->chip);
+ if (ret < 0) {
+ dev_err(ab8500_gpio->dev, "unable to remove gpiochip:\
+ %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, NULL);
+ mutex_destroy(&ab8500_gpio->lock);
+ kfree(ab8500_gpio);
+
+ return 0;
+}
+
+/*
+ * ab8500_gpio_configpulldown() - configure pull down
+ * Either the function is used to enable pull down (enable true)
+ * or to leave it dangling (enable false) or the function it used
+ * to enable pull up (enable true) or to leave it dangling (enable false)
+ * @pdev :Platform device registered
+ * @gpio :gpio number
+ * @enable:pull down enabled (True) or disabled (False)
+ */
+int ab8500_config_pull_up_or_down(struct device *dev,
+ unsigned gpio, bool enable)
+{
+ u8 offset = gpio - AB8500_PIN_GPIO1;
+ u8 pos = offset % 8;
+ u8 val = enable ? 0 : 1;
+ u8 reg = AB8500_GPIO_PUD1_REG + (offset / 8);
+ int ret;
+
+ ret = abx500_mask_and_set_register_interruptible(dev,
+ AB8500_MISC, reg, 1 << pos, val << pos);
+ if (ret < 0)
+ dev_err(dev, "%s write failed\n", __func__);
+ return ret;
+}
+EXPORT_SYMBOL(ab8500_config_pull_up_or_down);
+
+/*
+ * ab8500_gpio_config_select()
+ *
+ * Configure functionality of pin, either specific use or GPIO.
+ * @dev: device pointer
+ * @gpio: gpio number
+ * @gpio_select: true if the pin should be used as GPIO
+ */
+int ab8500_gpio_config_select(struct device *dev,
+ unsigned gpio, bool gpio_select)
+{
+ u8 offset = gpio - AB8500_PIN_GPIO1;
+ u8 reg = AB8500_GPIO_SEL1_REG + (offset / 8);
+ u8 pos = offset % 8;
+ u8 val = gpio_select ? 1 : 0;
+ int ret;
+
+ ret = abx500_mask_and_set_register_interruptible(dev,
+ AB8500_MISC, reg, 1 << pos, val << pos);
+ if (ret < 0)
+ dev_err(dev, "%s write failed\n", __func__);
+
+ dev_vdbg(dev, "%s (bank, addr, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
+ __func__, AB8500_MISC, reg, 1 << pos, val << pos);
+
+ return ret;
+}
+
+/*
+ * ab8500_gpio_config_get_select()
+ *
+ * Configure functionality of pin, either specific use or GPIO.
+ * @dev: device pointer
+ * @gpio: gpio number
+ * @gpio_select: pointer to pin selection status
+ */
+int ab8500_gpio_config_get_select(struct device *dev,
+ unsigned gpio, bool *gpio_select)
+{
+ u8 offset = gpio - AB8500_PIN_GPIO1;
+ u8 reg = AB8500_GPIO_SEL1_REG + (offset / 8);
+ u8 pos = offset % 8;
+ u8 val;
+ int ret;
+
+ ret = abx500_get_register_interruptible(dev,
+ AB8500_MISC, reg, &val);
+ if (ret < 0) {
+ dev_err(dev, "%s read failed\n", __func__);
+ return ret;
+ }
+
+ if (val & (1 << pos))
+ *gpio_select = true;
+ else
+ *gpio_select = false;
+
+ dev_vdbg(dev, "%s (bank, addr, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
+ __func__, AB8500_MISC, reg, 1 << pos, val);
+
+ return 0;
+}
+
+static struct platform_driver ab8500_gpio_driver = {
+ .driver = {
+ .name = "ab8500-gpio",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab8500_gpio_probe,
+ .remove = __devexit_p(ab8500_gpio_remove),
+};
+
+static int __init ab8500_gpio_init(void)
+{
+ return platform_driver_register(&ab8500_gpio_driver);
+}
+arch_initcall(ab8500_gpio_init);
+
+static void __exit ab8500_gpio_exit(void)
+{
+ platform_driver_unregister(&ab8500_gpio_driver);
+}
+module_exit(ab8500_gpio_exit);
+MODULE_AUTHOR("BIBEK BASU <bibek.basu@stericsson.com>");
+MODULE_DESCRIPTION("Driver allows to use AB8500 unused pins\
+ to be used as GPIO");
+MODULE_ALIAS("AB8500 GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/pl061.c b/drivers/gpio/pl061.c
index ee568c8fcbd..5005990f751 100644
--- a/drivers/gpio/pl061.c
+++ b/drivers/gpio/pl061.c
@@ -232,7 +232,7 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc)
desc->chip->unmask(irq);
}
-static int __init pl061_probe(struct amba_device *dev, struct amba_id *id)
+static int pl061_probe(struct amba_device *dev, struct amba_id *id)
{
struct pl061_platform_data *pdata;
struct pl061_gpio *chip;
@@ -333,7 +333,7 @@ free_mem:
return ret;
}
-static struct amba_id pl061_ids[] __initdata = {
+static struct amba_id pl061_ids[] = {
{
.id = 0x00041061,
.mask = 0x000fffff,
diff --git a/drivers/gpio/stmpe-gpio.c b/drivers/gpio/stmpe-gpio.c
new file mode 100644
index 00000000000..7c9e6a052c4
--- /dev/null
+++ b/drivers/gpio/stmpe-gpio.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stmpe.h>
+
+/*
+ * These registers are modified under the irq bus lock and cached to avoid
+ * unnecessary writes in bus_sync_unlock.
+ */
+enum { REG_RE, REG_FE, REG_IE };
+
+#define CACHE_NR_REGS 3
+#define CACHE_NR_BANKS (STMPE_NR_GPIOS / 8)
+
+struct stmpe_gpio {
+ struct gpio_chip chip;
+ struct stmpe *stmpe;
+ struct device *dev;
+ struct mutex irq_lock;
+
+ int irq_base;
+ unsigned norequest_mask;
+
+ /* Caches of interrupt control registers for bus_lock */
+ u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
+ u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
+};
+
+static inline struct stmpe_gpio *to_stmpe_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct stmpe_gpio, chip);
+}
+
+static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+ int ret;
+
+ ret = stmpe_reg_read(stmpe, reg);
+ if (ret < 0)
+ return ret;
+
+ return ret & mask;
+}
+
+static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ int which = val ? STMPE_IDX_GPSR_LSB : STMPE_IDX_GPCR_LSB;
+ u8 reg = stmpe->regs[which] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ stmpe_reg_write(stmpe, reg, mask);
+}
+
+static int stmpe_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int val)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ stmpe_gpio_set(chip, offset, val);
+
+ return stmpe_set_bits(stmpe, reg, mask, mask);
+}
+
+static int stmpe_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ return stmpe_set_bits(stmpe, reg, mask, 0);
+}
+
+static int stmpe_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+
+ return stmpe_gpio->irq_base + offset;
+}
+
+static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+
+ if (stmpe_gpio->norequest_mask & (1 << offset))
+ return -EINVAL;
+
+ return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO);
+}
+
+static struct gpio_chip template_chip = {
+ .label = "stmpe",
+ .owner = THIS_MODULE,
+ .direction_input = stmpe_gpio_direction_input,
+ .get = stmpe_gpio_get,
+ .direction_output = stmpe_gpio_direction_output,
+ .set = stmpe_gpio_set,
+ .to_irq = stmpe_gpio_to_irq,
+ .request = stmpe_gpio_request,
+ .can_sleep = 1,
+};
+
+static int stmpe_gpio_irq_set_type(unsigned int irq, unsigned int type)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ int offset = irq - stmpe_gpio->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
+ return -EINVAL;
+
+ if (type == IRQ_TYPE_EDGE_RISING)
+ stmpe_gpio->regs[REG_RE][regoffset] |= mask;
+ else
+ stmpe_gpio->regs[REG_RE][regoffset] &= ~mask;
+
+ if (type == IRQ_TYPE_EDGE_FALLING)
+ stmpe_gpio->regs[REG_FE][regoffset] |= mask;
+ else
+ stmpe_gpio->regs[REG_FE][regoffset] &= ~mask;
+
+ return 0;
+}
+
+static void stmpe_gpio_irq_lock(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+
+ mutex_lock(&stmpe_gpio->irq_lock);
+}
+
+static void stmpe_gpio_irq_sync_unlock(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
+ static const u8 regmap[] = {
+ [REG_RE] = STMPE_IDX_GPRER_LSB,
+ [REG_FE] = STMPE_IDX_GPFER_LSB,
+ [REG_IE] = STMPE_IDX_IEGPIOR_LSB,
+ };
+ int i, j;
+
+ for (i = 0; i < CACHE_NR_REGS; i++) {
+ for (j = 0; j < num_banks; j++) {
+ u8 old = stmpe_gpio->oldregs[i][j];
+ u8 new = stmpe_gpio->regs[i][j];
+
+ if (new == old)
+ continue;
+
+ stmpe_gpio->oldregs[i][j] = new;
+ stmpe_reg_write(stmpe, stmpe->regs[regmap[i]] - j, new);
+ }
+ }
+
+ mutex_unlock(&stmpe_gpio->irq_lock);
+}
+
+static void stmpe_gpio_irq_mask(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ int offset = irq - stmpe_gpio->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe_gpio->regs[REG_IE][regoffset] &= ~mask;
+}
+
+static void stmpe_gpio_irq_unmask(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ int offset = irq - stmpe_gpio->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe_gpio->regs[REG_IE][regoffset] |= mask;
+}
+
+static struct irq_chip stmpe_gpio_irq_chip = {
+ .name = "stmpe-gpio",
+ .bus_lock = stmpe_gpio_irq_lock,
+ .bus_sync_unlock = stmpe_gpio_irq_sync_unlock,
+ .mask = stmpe_gpio_irq_mask,
+ .unmask = stmpe_gpio_irq_unmask,
+ .set_type = stmpe_gpio_irq_set_type,
+};
+
+static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
+{
+ struct stmpe_gpio *stmpe_gpio = dev;
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB];
+ int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
+ u8 status[num_banks];
+ int ret;
+ int i;
+
+ ret = stmpe_block_read(stmpe, statmsbreg, num_banks, status);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = 0; i < num_banks; i++) {
+ int bank = num_banks - i - 1;
+ unsigned int enabled = stmpe_gpio->regs[REG_IE][bank];
+ unsigned int stat = status[i];
+
+ stat &= enabled;
+ if (!stat)
+ continue;
+
+ while (stat) {
+ int bit = __ffs(stat);
+ int line = bank * 8 + bit;
+
+ handle_nested_irq(stmpe_gpio->irq_base + line);
+ stat &= ~(1 << bit);
+ }
+
+ stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
+ stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB] + i,
+ status[i]);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio)
+{
+ int base = stmpe_gpio->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
+ set_irq_chip_data(irq, stmpe_gpio);
+ set_irq_chip_and_handler(irq, &stmpe_gpio_irq_chip,
+ handle_simple_irq);
+ set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+
+ return 0;
+}
+
+static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio)
+{
+ int base = stmpe_gpio->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ set_irq_chip_and_handler(irq, NULL, NULL);
+ set_irq_chip_data(irq, NULL);
+ }
+}
+
+static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct stmpe_gpio_platform_data *pdata;
+ struct stmpe_gpio *stmpe_gpio;
+ int ret;
+ int irq;
+
+ pdata = stmpe->pdata->gpio;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL);
+ if (!stmpe_gpio)
+ return -ENOMEM;
+
+ mutex_init(&stmpe_gpio->irq_lock);
+
+ stmpe_gpio->dev = &pdev->dev;
+ stmpe_gpio->stmpe = stmpe;
+ stmpe_gpio->norequest_mask = pdata ? pdata->norequest_mask : 0;
+
+ stmpe_gpio->chip = template_chip;
+ stmpe_gpio->chip.ngpio = stmpe->num_gpios;
+ stmpe_gpio->chip.dev = &pdev->dev;
+ stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1;
+
+ stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
+ if (ret)
+ goto out_free;
+
+ ret = stmpe_gpio_irq_init(stmpe_gpio);
+ if (ret)
+ goto out_disable;
+
+ ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT,
+ "stmpe-gpio", stmpe_gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
+ goto out_removeirq;
+ }
+
+ ret = gpiochip_add(&stmpe_gpio->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
+ goto out_freeirq;
+ }
+
+ if (pdata && pdata->setup)
+ pdata->setup(stmpe, stmpe_gpio->chip.base);
+
+ platform_set_drvdata(pdev, stmpe_gpio);
+
+ return 0;
+
+out_freeirq:
+ free_irq(irq, stmpe_gpio);
+out_removeirq:
+ stmpe_gpio_irq_remove(stmpe_gpio);
+out_disable:
+ stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
+out_free:
+ kfree(stmpe_gpio);
+ return ret;
+}
+
+static int __devexit stmpe_gpio_remove(struct platform_device *pdev)
+{
+ struct stmpe_gpio *stmpe_gpio = platform_get_drvdata(pdev);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ struct stmpe_gpio_platform_data *pdata = stmpe->pdata->gpio;
+ int irq = platform_get_irq(pdev, 0);
+ int ret;
+
+ if (pdata && pdata->remove)
+ pdata->remove(stmpe, stmpe_gpio->chip.base);
+
+ ret = gpiochip_remove(&stmpe_gpio->chip);
+ if (ret < 0) {
+ dev_err(stmpe_gpio->dev,
+ "unable to remove gpiochip: %d\n", ret);
+ return ret;
+ }
+
+ stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
+
+ free_irq(irq, stmpe_gpio);
+ stmpe_gpio_irq_remove(stmpe_gpio);
+ platform_set_drvdata(pdev, NULL);
+ kfree(stmpe_gpio);
+
+ return 0;
+}
+
+static struct platform_driver stmpe_gpio_driver = {
+ .driver.name = "stmpe-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = stmpe_gpio_probe,
+ .remove = __devexit_p(stmpe_gpio_remove),
+};
+
+static int __init stmpe_gpio_init(void)
+{
+ return platform_driver_register(&stmpe_gpio_driver);
+}
+subsys_initcall(stmpe_gpio_init);
+
+static void __exit stmpe_gpio_exit(void)
+{
+ platform_driver_unregister(&stmpe_gpio_driver);
+}
+module_exit(stmpe_gpio_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPExxxx GPIO driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/gpio/tc35892-gpio.c b/drivers/gpio/tc35892-gpio.c
index 1be6288780d..7e10c935a04 100644
--- a/drivers/gpio/tc35892-gpio.c
+++ b/drivers/gpio/tc35892-gpio.c
@@ -322,6 +322,9 @@ static int __devinit tc35892_gpio_probe(struct platform_device *pdev)
goto out_freeirq;
}
+ if (pdata->setup)
+ pdata->setup(tc35892, tc35892_gpio->chip.base);
+
platform_set_drvdata(pdev, tc35892_gpio);
return 0;
@@ -338,9 +341,14 @@ out_free:
static int __devexit tc35892_gpio_remove(struct platform_device *pdev)
{
struct tc35892_gpio *tc35892_gpio = platform_get_drvdata(pdev);
+ struct tc35892 *tc35892 = tc35892_gpio->tc35892;
+ struct tc35892_gpio_platform_data *pdata = tc35892->pdata->gpio;
int irq = platform_get_irq(pdev, 0);
int ret;
+ if (pdata->remove)
+ pdata->remove(tc35892, tc35892_gpio->chip.base);
+
ret = gpiochip_remove(&tc35892_gpio->chip);
if (ret < 0) {
dev_err(tc35892_gpio->dev,
diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile
index 30879df3dae..22100fc9b47 100644
--- a/drivers/gpu/Makefile
+++ b/drivers/gpu/Makefile
@@ -1 +1,4 @@
obj-y += drm/ vga/
+ifeq ($(CONFIG_GPU_MALI),$(filter $(CONFIG_GPU_MALI),y m))
+ obj-$(CONFIG_GPU_MALI) += mali/
+endif
diff --git a/drivers/gpu/mali/Kconfig b/drivers/gpu/mali/Kconfig
new file mode 100644
index 00000000000..978355d696f
--- /dev/null
+++ b/drivers/gpu/mali/Kconfig
@@ -0,0 +1,7 @@
+config GPU_MALI
+ tristate "ARM Mali 200/300/400 support"
+ depends on ARM
+ ---help---
+ This enables support for the ARM Mali 200/300/400 family of GPUs.
+ Platform specific configuration is made in configuration files in the
+ drivers/gpu/mali/src folder
diff --git a/drivers/gpu/mali/Makefile b/drivers/gpu/mali/Makefile
new file mode 100644
index 00000000000..b246233c7db
--- /dev/null
+++ b/drivers/gpu/mali/Makefile
@@ -0,0 +1,3 @@
+ifeq ($(shell [ -f src/Makefile ] && echo "OK"), OK)
+obj-$(CONFIG_GPU_MALI) += src/
+endif
diff --git a/drivers/gpu/mali/src b/drivers/gpu/mali/src
new file mode 120000
index 00000000000..bd5157f132d
--- /dev/null
+++ b/drivers/gpu/mali/src
@@ -0,0 +1 @@
+../../../../vendor/st-ericsson/multimedia/linux/mali400ko/driver/src/devicedrv/mali/ \ No newline at end of file
diff --git a/drivers/hsi/Kconfig b/drivers/hsi/Kconfig
new file mode 100644
index 00000000000..2c76de438eb
--- /dev/null
+++ b/drivers/hsi/Kconfig
@@ -0,0 +1,20 @@
+#
+# HSI driver configuration
+#
+menuconfig HSI
+ tristate "HSI support"
+ ---help---
+ The "High speed synchronous Serial Interface" is
+ synchronous serial interface used mainly to connect
+ application engines and cellular modems.
+
+if HSI
+
+config HSI_BOARDINFO
+ bool
+ default y
+
+source "drivers/hsi/controllers/Kconfig"
+source "drivers/hsi/clients/Kconfig"
+
+endif # HSI
diff --git a/drivers/hsi/Makefile b/drivers/hsi/Makefile
new file mode 100644
index 00000000000..d47ca5de18c
--- /dev/null
+++ b/drivers/hsi/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for HSI
+#
+obj-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o
+obj-$(CONFIG_HSI) += hsi.o
+obj-y += controllers/ clients/
diff --git a/drivers/hsi/clients/Kconfig b/drivers/hsi/clients/Kconfig
new file mode 100644
index 00000000000..46eef1f77fd
--- /dev/null
+++ b/drivers/hsi/clients/Kconfig
@@ -0,0 +1,19 @@
+#
+# HSI clients configuration
+#
+
+comment "HSI clients"
+
+config HSI_CHAR
+ tristate "HSI/SSI character driver"
+ depends on HSI
+ ---help---
+ If you say Y here, you will enable the HSI/SSI character driver.
+ This driver provides a simple character device interface for
+ serial communication with the cellular modem over HSI/SSI bus.
+config HSI_CAIF
+ tristate "CAIF HSI driver"
+ depends on HSI
+ default n
+ ---help---
+ Provides HSI-CAIF glue layer
diff --git a/drivers/hsi/clients/Makefile b/drivers/hsi/clients/Makefile
new file mode 100644
index 00000000000..dfe33584975
--- /dev/null
+++ b/drivers/hsi/clients/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for HSI clients
+#
+
+obj-$(CONFIG_HSI_CHAR) += hsi_char.o
+obj-$(CONFIG_HSI_CAIF) += cfhsi.o
diff --git a/drivers/hsi/clients/cfhsi.c b/drivers/hsi/clients/cfhsi.c
new file mode 100644
index 00000000000..cf7ce0cb1cb
--- /dev/null
+++ b/drivers/hsi/clients/cfhsi.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Daniel Martensson <Daniel.Martensson@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+
+#include <net/caif/caif_hsi.h>
+
+#include <linux/hsi/hsi.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Daniel Martensson<daniel.martensson@stericsson.com>");
+MODULE_DESCRIPTION("CAIF HSI V3 glue");
+
+#define NR_OF_CAIF_HSI_CHANNELS 2
+
+struct cfhsi_v3 {
+ struct list_head list;
+ struct cfhsi_dev dev;
+ struct platform_device pdev;
+ struct hsi_msg *tx_msg;
+ struct hsi_msg *rx_msg;
+};
+
+/* TODO: Lists are not protected with regards to device removal. */
+static LIST_HEAD(cfhsi_dev_list);
+
+static struct hsi_client *cfhsi_client;
+
+static int cfhsi_tx(u8 *ptr, int len, struct cfhsi_dev *dev)
+{
+ int res;
+ struct cfhsi_v3 *cfhsi = NULL;
+
+ /* Check length and alignment. */
+ BUG_ON(((int)ptr)%4);
+ BUG_ON(len%4);
+
+ cfhsi = container_of(dev, struct cfhsi_v3, dev);
+
+ sg_init_one(cfhsi->tx_msg->sgt.sgl, (const void *)ptr,
+ (unsigned int)len);
+
+ /* Write on HSI device. */
+ res = hsi_async_write(cfhsi_client, cfhsi->tx_msg);
+
+ return res;
+}
+
+static int cfhsi_rx(u8 *ptr, int len, struct cfhsi_dev *dev)
+{
+ int res;
+ struct cfhsi_v3 *cfhsi = NULL;
+
+ /* Check length and alignment. */
+ BUG_ON(((int)ptr)%4);
+ BUG_ON(len%4);
+
+ cfhsi = container_of(dev, struct cfhsi_v3, dev);
+
+ sg_init_one(cfhsi->rx_msg->sgt.sgl, (const void *)ptr,
+ (unsigned int)len);
+
+ /* Read from HSI device. */
+ res = hsi_async_read(cfhsi_client, cfhsi->rx_msg);
+
+ return res;
+}
+
+void cfhsi_v3_release(struct device *dev)
+{
+ pr_warning("%s:%d cfhsi_v3_release called\n", __FILE__, __LINE__);
+}
+
+static inline void cfhsi_v3_destructor(struct hsi_msg *msg)
+{
+ pr_warning("%s:%d cfhsi_v3_destructor called\n", __FILE__, __LINE__);
+}
+
+static inline void cfhsi_v3_read_cb(struct hsi_msg *msg)
+{
+ struct cfhsi_v3 *cfhsi = (struct cfhsi_v3 *)msg->context;
+
+ /* TODO: Error checking. */
+ BUG_ON(!cfhsi->dev.drv);
+ BUG_ON(!cfhsi->dev.drv->rx_done_cb);
+
+ cfhsi->dev.drv->rx_done_cb(cfhsi->dev.drv);
+}
+
+static inline void cfhsi_v3_write_cb(struct hsi_msg *msg)
+{
+ struct cfhsi_v3 *cfhsi = (struct cfhsi_v3 *)msg->context;
+
+ /* TODO: Error checking. */
+ BUG_ON(!cfhsi->dev.drv);
+ BUG_ON(!cfhsi->dev.drv->tx_done_cb);
+
+ cfhsi->dev.drv->tx_done_cb(cfhsi->dev.drv);
+}
+
+static int hsi_proto_probe(struct device *dev)
+{
+ int res;
+ int i;
+ struct cfhsi_v3 *cfhsi = NULL;
+
+ if (cfhsi_client)
+ return -ENODEV; /* TODO: Not correct return. */
+
+ cfhsi_client = to_hsi_client(dev);
+
+ res = hsi_claim_port(cfhsi_client, 0);
+ if (res) {
+ pr_warning("hsi_proto_probe: hsi_claim_port:%d.\n", res);
+ goto err_hsi_claim;
+ }
+
+ /* Right now we don't care about AC_WAKE (No power management). */
+ cfhsi_client->hsi_start_rx = NULL;
+ cfhsi_client->hsi_stop_rx = NULL;
+
+ /* CAIF HSI TX configuration. */
+ cfhsi_client->tx_cfg.mode = HSI_MODE_STREAM;
+ cfhsi_client->tx_cfg.flow = HSI_FLOW_SYNC;
+ cfhsi_client->tx_cfg.channels = NR_OF_CAIF_HSI_CHANNELS;
+ cfhsi_client->tx_cfg.speed = 100000; /* TODO: What speed should be used. */
+ cfhsi_client->tx_cfg.arb_mode = HSI_ARB_RR;
+
+ /* CAIF HSI RX configuration. */
+ cfhsi_client->rx_cfg.mode = HSI_MODE_STREAM;
+ cfhsi_client->rx_cfg.flow = HSI_FLOW_SYNC;
+ cfhsi_client->rx_cfg.channels = NR_OF_CAIF_HSI_CHANNELS;
+ cfhsi_client->rx_cfg.speed = 200000; /* TODO: What speed should be used. */
+ cfhsi_client->rx_cfg.arb_mode = HSI_ARB_RR;
+
+ res = hsi_setup(cfhsi_client);
+ if (res) {
+ pr_warning("hsi_proto_probe: hsi_setup:%d.\n", res);
+ goto err_hsi_setup;
+ }
+
+ /* Make sure that AC_WAKE is high (No power management). */
+ res = hsi_start_tx(cfhsi_client);
+ if (res) {
+ pr_warning("hsi_proto_probe: hsi_start_tx:%d.\n", res);
+ goto err_hsi_start_tx;
+ }
+
+ /* Connect channels to CAIF HSI devices. */
+ for (i = 0; i < NR_OF_CAIF_HSI_CHANNELS; i++) {
+ cfhsi = kzalloc(sizeof(struct cfhsi_v3), GFP_KERNEL);
+ if (!cfhsi) {
+ res = -ENOMEM;
+ /* TODO: Error handling. */
+ }
+
+ /* Assign HSI client to this CAIF HSI device. */
+ cfhsi->dev.cfhsi_tx = cfhsi_tx;
+ cfhsi->dev.cfhsi_rx = cfhsi_rx;
+
+ /* Allocate HSI messages. */
+ cfhsi->tx_msg = hsi_alloc_msg(1, GFP_KERNEL);
+ cfhsi->rx_msg = hsi_alloc_msg(1, GFP_KERNEL);
+ if (!cfhsi->tx_msg || !cfhsi->rx_msg) {
+ res = -ENOMEM;
+ /* TODO: Error handling. */
+ }
+
+ /* Set up TX message. */
+ cfhsi->tx_msg->cl = cfhsi_client;
+ cfhsi->tx_msg->context = (void *)cfhsi;
+ cfhsi->tx_msg->complete = cfhsi_v3_write_cb;
+ cfhsi->tx_msg->destructor = cfhsi_v3_destructor;
+ cfhsi->tx_msg->channel = i;
+ cfhsi->tx_msg->ttype = HSI_MSG_WRITE;
+ cfhsi->tx_msg->break_frame = 0; /* No break frame. */
+
+ /* Set up RX message. */
+ cfhsi->rx_msg->cl = cfhsi_client;
+ cfhsi->rx_msg->context = (void *)cfhsi;
+ cfhsi->rx_msg->complete = cfhsi_v3_read_cb;
+ cfhsi->rx_msg->destructor = cfhsi_v3_destructor;
+ cfhsi->rx_msg->channel = i;
+ cfhsi->rx_msg->ttype = HSI_MSG_READ;
+ cfhsi->rx_msg->break_frame = 0; /* No break frame. */
+
+ /* Initialize CAIF HSI platform device. */
+ cfhsi->pdev.name = "cfhsi";
+ cfhsi->pdev.dev.platform_data = &cfhsi->dev;
+ cfhsi->pdev.dev.release = cfhsi_v3_release;
+ /* Use channel number as id. */
+ cfhsi->pdev.id = i;
+ /* Register platform device. */
+ res = platform_device_register(&cfhsi->pdev);
+ if (res) {
+ pr_warning("hsi_proto_probe: plat_dev_reg:%d.\n", res);
+ res = -ENODEV;
+ /* TODO: Error handling. */
+ }
+
+ /* Add HSI device to device list. */
+ list_add_tail(&cfhsi->list, &cfhsi_dev_list);
+ }
+
+ return res;
+
+ err_hsi_start_tx:
+ err_hsi_setup:
+ hsi_release_port(cfhsi_client);
+ err_hsi_claim:
+ cfhsi_client = NULL;
+
+ return res;
+}
+
+static int hsi_proto_remove(struct device *dev)
+{
+ struct cfhsi_v3 *cfhsi = NULL;
+ struct list_head *list_node;
+ struct list_head *n;
+
+ if (!cfhsi_client)
+ return -ENODEV;
+
+ list_for_each_safe(list_node, n, &cfhsi_dev_list) {
+ cfhsi = list_entry(list_node, struct cfhsi_v3, list);
+ /* Remove from list. */
+ list_del(list_node);
+ /* Our HSI device is gone, unregister CAIF HSI device. */
+ platform_device_del(&cfhsi->pdev);
+ hsi_free_msg(cfhsi->tx_msg);
+ hsi_free_msg(cfhsi->rx_msg);
+ /* Free memory. */
+ kfree(cfhsi);
+ }
+
+ hsi_stop_tx(cfhsi_client);
+ hsi_release_port(cfhsi_client);
+
+ cfhsi_client = NULL;
+
+ return 0;
+}
+
+static int hsi_proto_suspend(struct device *dev, pm_message_t mesg)
+{
+ /* Not handled. */
+ pr_info("hsi_proto_suspend.\n");
+
+ return 0;
+}
+
+static int hsi_proto_resume(struct device *dev)
+{
+ /* Not handled. */
+ pr_info("hsi_proto_resume.\n");
+
+ return 0;
+}
+
+static struct hsi_client_driver cfhsi_v3_driver = {
+ .driver = {
+ .name = "cfhsi_v3_driver",
+ .owner = THIS_MODULE,
+ .probe = hsi_proto_probe,
+ .remove = __devexit_p(hsi_proto_remove),
+ .suspend = hsi_proto_suspend,
+ .resume = hsi_proto_resume,
+ },
+};
+
+static int __init cfhsi_v3_init(void)
+{
+ int res;
+
+ /* Register protocol driver for HSI interface. */
+ res = hsi_register_client_driver(&cfhsi_v3_driver);
+ if (res)
+ pr_warning("Failed to register CAIF HSI V3 driver.\n");
+
+ return res;
+}
+
+static void __exit cfhsi_v3_exit(void)
+{
+ struct cfhsi_v3 *cfhsi = NULL;
+ struct list_head *list_node;
+ struct list_head *n;
+
+ /* Unregister driver. */
+ hsi_unregister_client_driver(&cfhsi_v3_driver);
+
+ if (!cfhsi_client)
+ return;
+
+ list_for_each_safe(list_node, n, &cfhsi_dev_list) {
+ cfhsi = list_entry(list_node, struct cfhsi_v3, list);
+ platform_device_del(&cfhsi->pdev);
+ hsi_free_msg(cfhsi->tx_msg);
+ hsi_free_msg(cfhsi->rx_msg);
+ kfree(cfhsi);
+ }
+
+ hsi_stop_tx(cfhsi_client);
+ hsi_release_port(cfhsi_client);
+
+ cfhsi_client = NULL;
+}
+
+module_init(cfhsi_v3_init);
+module_exit(cfhsi_v3_exit);
diff --git a/drivers/hsi/clients/hsi_char.c b/drivers/hsi/clients/hsi_char.c
new file mode 100644
index 00000000000..4281156657b
--- /dev/null
+++ b/drivers/hsi/clients/hsi_char.c
@@ -0,0 +1,1108 @@
+/*
+ * hsi-char.c
+ *
+ * HSI character device driver, implements the character device
+ * interface.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Andras Domokos <andras.domokos@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <asm/atomic.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/ioctl.h>
+#include <linux/wait.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/scatterlist.h>
+#include <linux/hsi/hsi.h>
+#include <linux/hsi/hsi_char.h>
+
+#define HSI_CHAR_CHANNELS 8
+#define HSI_CHAR_DEVS 8
+#define HSI_CHAR_MSGS 4
+
+#define HSI_CHST_UNAVAIL 0 /* SBZ! */
+#define HSI_CHST_AVAIL 1
+
+#define HSI_CHST_CLOSED (0 << 4)
+#define HSI_CHST_CLOSING (1 << 4)
+#define HSI_CHST_OPENING (2 << 4)
+#define HSI_CHST_OPENED (3 << 4)
+
+#define HSI_CHST_READOFF (0 << 8)
+#define HSI_CHST_READON (1 << 8)
+#define HSI_CHST_READING (2 << 8)
+
+#define HSI_CHST_WRITEOFF (0 << 12)
+#define HSI_CHST_WRITEON (1 << 12)
+#define HSI_CHST_WRITING (2 << 12)
+
+#define HSI_CHST_OC_MASK 0xf0
+#define HSI_CHST_RD_MASK 0xf00
+#define HSI_CHST_WR_MASK 0xf000
+
+#define HSI_CHST_OC(c) ((c)->state & HSI_CHST_OC_MASK)
+#define HSI_CHST_RD(c) ((c)->state & HSI_CHST_RD_MASK)
+#define HSI_CHST_WR(c) ((c)->state & HSI_CHST_WR_MASK)
+
+#define HSI_CHST_OC_SET(c, v) \
+ do { \
+ (c)->state &= ~HSI_CHST_OC_MASK; \
+ (c)->state |= v; \
+ } while (0);
+
+#define HSI_CHST_RD_SET(c, v) \
+ do { \
+ (c)->state &= ~HSI_CHST_RD_MASK; \
+ (c)->state |= v; \
+ } while (0);
+
+#define HSI_CHST_WR_SET(c, v) \
+ do { \
+ (c)->state &= ~HSI_CHST_WR_MASK; \
+ (c)->state |= v; \
+ } while (0);
+
+#define HSI_CHAR_POLL_RST (-1)
+#define HSI_CHAR_POLL_OFF 0
+#define HSI_CHAR_POLL_ON 1
+
+#define HSI_CHAR_RX 0
+#define HSI_CHAR_TX 1
+
+struct hsi_char_channel {
+ unsigned int ch;
+ unsigned int state;
+ int wlrefcnt;
+ int rxpoll;
+ struct hsi_client *cl;
+ struct list_head free_msgs_list;
+ struct list_head rx_msgs_queue;
+ struct list_head tx_msgs_queue;
+ int poll_event;
+ spinlock_t lock;
+ struct fasync_struct *async_queue;
+ wait_queue_head_t rx_wait;
+ wait_queue_head_t tx_wait;
+};
+
+struct hsi_char_client_data {
+ atomic_t refcnt;
+ int attached;
+ atomic_t breq;
+ struct hsi_char_channel channels[HSI_CHAR_DEVS];
+};
+
+static unsigned int max_data_size = 0x1000;
+module_param(max_data_size, uint, 1);
+MODULE_PARM_DESC(max_data_size, "max read/write data size [4,8..65536] (^2)");
+
+static int channels_map[HSI_CHAR_DEVS] = {0, -1, -1 , -1, -1, -1, -1, -1};
+module_param_array(channels_map, int, NULL, 0);
+MODULE_PARM_DESC(channels_map, "Array of HSI channels ([0...7]) to be probed");
+
+static dev_t hsi_char_dev;
+static struct hsi_char_client_data hsi_char_cl_data;
+
+static inline void hsi_char_msg_free(struct hsi_msg *msg)
+{
+ msg->complete = NULL;
+ msg->destructor = NULL;
+ kfree(sg_virt(msg->sgt.sgl));
+ hsi_free_msg(msg);
+}
+
+static inline void hsi_char_msgs_free(struct hsi_char_channel *channel)
+{
+ struct hsi_msg *msg, *tmp;
+
+ list_for_each_entry_safe(msg, tmp, &channel->free_msgs_list, link) {
+ list_del(&msg->link);
+ hsi_char_msg_free(msg);
+ }
+ list_for_each_entry_safe(msg, tmp, &channel->rx_msgs_queue, link) {
+ list_del(&msg->link);
+ hsi_char_msg_free(msg);
+ }
+ list_for_each_entry_safe(msg, tmp, &channel->tx_msgs_queue, link) {
+ list_del(&msg->link);
+ hsi_char_msg_free(msg);
+ }
+}
+
+static inline struct hsi_msg *hsi_char_msg_alloc(unsigned int alloc_size)
+{
+ struct hsi_msg *msg;
+ void *buf;
+
+ msg = hsi_alloc_msg(1, GFP_KERNEL);
+ if (!msg)
+ goto out;
+ buf = kmalloc(alloc_size, GFP_KERNEL);
+ if (!buf) {
+ hsi_free_msg(msg);
+ goto out;
+ }
+ sg_init_one(msg->sgt.sgl, buf, alloc_size);
+ msg->context = buf;
+ return msg;
+out:
+ return NULL;
+}
+
+static inline int hsi_char_msgs_alloc(struct hsi_char_channel *channel)
+{
+ struct hsi_msg *msg;
+ int i;
+
+ for (i = 0; i < HSI_CHAR_MSGS; i++) {
+ msg = hsi_char_msg_alloc(max_data_size);
+ if (!msg)
+ goto out;
+ msg->channel = channel->ch;
+ list_add_tail(&msg->link, &channel->free_msgs_list);
+ }
+ return 0;
+out:
+ hsi_char_msgs_free(channel);
+
+ return -ENOMEM;
+}
+
+static int _hsi_char_release(struct hsi_char_channel *channel, int remove)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(channel->cl);
+ int ret = 0, refcnt;
+
+ spin_lock_bh(&channel->lock);
+ if (HSI_CHST_OC(channel) != HSI_CHST_OPENED)
+ goto out;
+ HSI_CHST_OC_SET(channel, HSI_CHST_CLOSING);
+ spin_unlock_bh(&channel->lock);
+
+ while (channel->wlrefcnt > 0) {
+ hsi_stop_tx(channel->cl);
+ channel->wlrefcnt--;
+ }
+
+ if (channel->rxpoll == HSI_CHAR_POLL_ON)
+ channel->poll_event |= POLLERR;
+
+ wake_up_interruptible(&channel->rx_wait);
+ wake_up_interruptible(&channel->tx_wait);
+
+ refcnt = atomic_dec_return(&cl_data->refcnt);
+ if (!refcnt) {
+ hsi_flush(channel->cl);
+ hsi_release_port(channel->cl);
+ cl_data->attached = 0;
+ }
+ hsi_char_msgs_free(channel);
+
+ spin_lock_bh(&channel->lock);
+ HSI_CHST_OC_SET(channel, HSI_CHST_CLOSED);
+ HSI_CHST_RD_SET(channel, HSI_CHST_READOFF);
+ HSI_CHST_WR_SET(channel, HSI_CHST_WRITEOFF);
+out:
+ if (remove)
+ channel->cl = NULL;
+ spin_unlock_bh(&channel->lock);
+
+ return ret;
+}
+
+static struct hsi_client_driver hsi_char_driver;
+static struct cdev hsi_char_cdev;
+static const struct file_operations hsi_char_fops;
+static struct class *hsi_char_class;
+
+static int __devinit hsi_char_probe(struct device *dev)
+{
+ struct hsi_char_client_data *cl_data = &hsi_char_cl_data;
+ struct hsi_char_channel *channel = cl_data->channels;
+ struct hsi_client *cl = to_hsi_client(dev);
+ char devname[] = "hsi_char";
+ int i;
+ int ret;
+
+ for (i = 0; i < HSI_CHAR_DEVS; i++, channel++) {
+ if (channel->state == HSI_CHST_AVAIL)
+ channel->cl = cl;
+ }
+ cl->hsi_start_rx = NULL;
+ cl->hsi_stop_rx = NULL;
+ atomic_set(&cl_data->refcnt, 0);
+ atomic_set(&cl_data->breq, 1);
+ cl_data->attached = 0;
+ hsi_client_set_drvdata(cl, cl_data);
+
+ ret = alloc_chrdev_region(&hsi_char_dev, 0, HSI_CHAR_DEVS, devname);
+ if (ret < 0) {
+ hsi_unregister_client_driver(&hsi_char_driver);
+ return ret;
+ }
+
+ cdev_init(&hsi_char_cdev, &hsi_char_fops);
+ ret = cdev_add(&hsi_char_cdev, hsi_char_dev, HSI_CHAR_DEVS);
+ if (ret) {
+ unregister_chrdev_region(hsi_char_dev, HSI_CHAR_DEVS);
+ hsi_unregister_client_driver(&hsi_char_driver);
+ return ret;
+ }
+
+ hsi_char_class = class_create(THIS_MODULE, "hsi");
+ if (IS_ERR(hsi_char_class))
+ pr_err("ERROR: hsi class creation failed!\n");
+
+ device_create(hsi_char_class, NULL, hsi_char_cdev.dev, NULL, devname);
+
+ return 0;
+}
+
+static int __devexit hsi_char_remove(struct device *dev)
+{
+ struct hsi_client *cl = to_hsi_client(dev);
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(cl);
+ struct hsi_char_channel *channel = cl_data->channels;
+ int i;
+
+ for (i = 0; i < HSI_CHAR_DEVS; i++, channel++) {
+ if (!(channel->state & HSI_CHST_AVAIL))
+ continue;
+ _hsi_char_release(channel, 1);
+ }
+
+ return 0;
+}
+
+static inline unsigned int hsi_char_msg_len_get(struct hsi_msg *msg)
+{
+ return msg->sgt.sgl->length;
+}
+
+static inline void hsi_char_msg_len_set(struct hsi_msg *msg, unsigned int len)
+{
+ msg->sgt.sgl->length = len;
+}
+
+static void hsi_char_data_available(struct hsi_msg *msg)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(msg->cl);
+ struct hsi_char_channel *channel = cl_data->channels + msg->channel;
+ int ret;
+
+ if (msg->status == HSI_STATUS_ERROR) {
+ ret = hsi_async_read(channel->cl, msg);
+ if (ret < 0) {
+ list_add_tail(&msg->link, &channel->free_msgs_list);
+ spin_lock_bh(&channel->lock);
+ list_add_tail(&msg->link, &channel->free_msgs_list);
+ channel->rxpoll = HSI_CHAR_POLL_OFF;
+ spin_unlock_bh(&channel->lock);
+ }
+ } else {
+ spin_lock_bh(&channel->lock);
+ channel->rxpoll = HSI_CHAR_POLL_OFF;
+ channel->poll_event |= (POLLIN | POLLRDNORM);
+ spin_unlock_bh(&channel->lock);
+ spin_lock_bh(&channel->lock);
+ list_add_tail(&msg->link, &channel->free_msgs_list);
+ spin_unlock_bh(&channel->lock);
+ wake_up_interruptible(&channel->rx_wait);
+ }
+}
+
+static void hsi_char_rx_completed(struct hsi_msg *msg)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(msg->cl);
+ struct hsi_char_channel *channel = cl_data->channels + msg->channel;
+
+ spin_lock_bh(&channel->lock);
+ list_add_tail(&msg->link, &channel->rx_msgs_queue);
+ spin_unlock_bh(&channel->lock);
+ wake_up_interruptible(&channel->rx_wait);
+}
+
+static void hsi_char_rx_msg_destructor(struct hsi_msg *msg)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(msg->cl);
+ struct hsi_char_channel *channel = cl_data->channels + msg->channel;
+
+ spin_lock_bh(&channel->lock);
+ list_add_tail(&msg->link, &channel->free_msgs_list);
+ HSI_CHST_RD_SET(channel, HSI_CHST_READOFF);
+ spin_unlock_bh(&channel->lock);
+}
+
+static void hsi_char_rx_poll_destructor(struct hsi_msg *msg)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(msg->cl);
+ struct hsi_char_channel *channel = cl_data->channels + msg->channel;
+
+ spin_lock_bh(&channel->lock);
+ list_add_tail(&msg->link, &channel->free_msgs_list);
+ channel->rxpoll = HSI_CHAR_POLL_RST;
+ spin_unlock_bh(&channel->lock);
+}
+
+static int hsi_char_rx_poll(struct hsi_char_channel *channel)
+{
+ struct hsi_msg *msg;
+ int ret = 0;
+
+ spin_lock_bh(&channel->lock);
+ if (list_empty(&channel->free_msgs_list)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (channel->rxpoll == HSI_CHAR_POLL_ON)
+ goto out;
+ msg = list_first_entry(&channel->free_msgs_list, struct hsi_msg, link);
+ list_del(&msg->link);
+ channel->rxpoll = HSI_CHAR_POLL_ON;
+ spin_unlock_bh(&channel->lock);
+ hsi_char_msg_len_set(msg, 0);
+ msg->complete = hsi_char_data_available;
+ msg->destructor = hsi_char_rx_poll_destructor;
+ /* don't touch msg->context! */
+ ret = hsi_async_read(channel->cl, msg);
+ spin_lock_bh(&channel->lock);
+ if (ret < 0) {
+ list_add_tail(&msg->link, &channel->free_msgs_list);
+ channel->rxpoll = HSI_CHAR_POLL_OFF;
+ goto out;
+ }
+out:
+ spin_unlock_bh(&channel->lock);
+
+ return ret;
+}
+
+static void hsi_char_tx_completed(struct hsi_msg *msg)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(msg->cl);
+ struct hsi_char_channel *channel = cl_data->channels + msg->channel;
+
+ spin_lock_bh(&channel->lock);
+ list_add_tail(&msg->link, &channel->tx_msgs_queue);
+ channel->poll_event |= (POLLOUT | POLLWRNORM);
+ spin_unlock_bh(&channel->lock);
+ wake_up_interruptible(&channel->tx_wait);
+}
+
+static void hsi_char_tx_msg_destructor(struct hsi_msg *msg)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(msg->cl);
+ struct hsi_char_channel *channel = cl_data->channels + msg->channel;
+
+ spin_lock_bh(&channel->lock);
+ list_add_tail(&msg->link, &channel->free_msgs_list);
+ HSI_CHST_WR_SET(channel, HSI_CHST_WRITEOFF);
+ spin_unlock_bh(&channel->lock);
+}
+
+static void hsi_char_rx_poll_rst(struct hsi_client *cl)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(cl);
+ struct hsi_char_channel *channel = cl_data->channels;
+ int i;
+
+ for (i = 0; i < HSI_CHAR_DEVS; i++, channel++) {
+ if ((HSI_CHST_OC(channel) == HSI_CHST_OPENED) &&
+ (channel->rxpoll == HSI_CHAR_POLL_RST))
+ hsi_char_rx_poll(channel);
+ }
+}
+
+static void hsi_char_reset(struct hsi_client *cl)
+{
+ hsi_flush(cl);
+ hsi_char_rx_poll_rst(cl);
+}
+
+static void hsi_char_rx_cancel(struct hsi_char_channel *channel)
+{
+ hsi_flush(channel->cl);
+ hsi_char_rx_poll_rst(channel->cl);
+}
+
+static void hsi_char_tx_cancel(struct hsi_char_channel *channel)
+{
+ hsi_flush(channel->cl);
+ hsi_char_rx_poll_rst(channel->cl);
+}
+
+static void hsi_char_bcast_break(struct hsi_client *cl)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(cl);
+ struct hsi_char_channel *channel = cl_data->channels;
+ int i;
+
+ for (i = 0; i < HSI_CHAR_DEVS; i++, channel++) {
+ if (HSI_CHST_OC(channel) != HSI_CHST_OPENED)
+ continue;
+ channel->poll_event |= POLLPRI;
+ wake_up_interruptible(&channel->rx_wait);
+ wake_up_interruptible(&channel->tx_wait);
+ }
+}
+
+static void hsi_char_break_received(struct hsi_msg *msg)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(msg->cl);
+ int ret;
+
+ hsi_char_bcast_break(msg->cl);
+ ret = hsi_async_read(msg->cl, msg);
+ if (ret < 0) {
+ hsi_free_msg(msg);
+ atomic_inc(&cl_data->breq);
+ }
+}
+
+static void hsi_char_break_req_destructor(struct hsi_msg *msg)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(msg->cl);
+
+ hsi_free_msg(msg);
+ atomic_inc(&cl_data->breq);
+}
+
+static int hsi_char_break_request(struct hsi_client *cl)
+{
+ struct hsi_char_client_data *cl_data = hsi_client_drvdata(cl);
+ struct hsi_msg *msg;
+ int ret = 0;
+
+ if (!atomic_dec_and_test(&cl_data->breq)) {
+ atomic_inc(&cl_data->breq);
+ return -EBUSY;
+ }
+ msg = hsi_alloc_msg(0, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+ msg->break_frame = 1;
+ msg->complete = hsi_char_break_received;
+ msg->destructor = hsi_char_break_req_destructor;
+ ret = hsi_async_read(cl, msg);
+ if (ret < 0)
+ hsi_free_msg(msg);
+
+ return ret;
+}
+
+static int hsi_char_break_send(struct hsi_client *cl)
+{
+ struct hsi_msg *msg;
+ int ret = 0;
+
+ msg = hsi_alloc_msg(0, GFP_ATOMIC);
+ if (!msg)
+ return -ENOMEM;
+ msg->break_frame = 1;
+ msg->complete = hsi_free_msg;
+ msg->destructor = hsi_free_msg;
+ ret = hsi_async_write(cl, msg);
+ if (ret < 0)
+ hsi_free_msg(msg);
+
+ return ret;
+}
+
+static inline int ssi_check_common_cfg(struct hsi_config *cfg)
+{
+ if ((cfg->mode != HSI_MODE_STREAM) && (cfg->mode != HSI_MODE_FRAME))
+ return -EINVAL;
+ if ((cfg->channels == 0) || (cfg->channels > HSI_CHAR_CHANNELS))
+ return -EINVAL;
+ if (cfg->channels & (cfg->channels - 1))
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline int ssi_check_rx_cfg(struct hsi_config *cfg)
+{
+ int ret;
+
+ ret = ssi_check_common_cfg(cfg);
+ if (ret < 0)
+ return ret;
+ if ((cfg->flow != HSI_FLOW_SYNC) && (cfg->flow != HSI_FLOW_PIPE))
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline int ssi_check_tx_cfg(struct hsi_config *cfg)
+{
+ int ret;
+
+ ret = ssi_check_common_cfg(cfg);
+ if (ret < 0)
+ return ret;
+ if ((cfg->arb_mode != HSI_ARB_RR) && (cfg->arb_mode != HSI_ARB_PRIO))
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline int hsi_char_cfg_set(struct hsi_client *cl,
+ struct hsi_config *cfg, int dir)
+{
+ struct hsi_config *rxtx_cfg;
+ int ret = 0;
+
+ if (dir == HSI_CHAR_RX) {
+ rxtx_cfg = &cl->rx_cfg;
+ ret = ssi_check_rx_cfg(cfg);
+ } else {
+ rxtx_cfg = &cl->tx_cfg;
+ ret = ssi_check_tx_cfg(cfg);
+ }
+ if (ret < 0)
+ return ret;
+
+ *rxtx_cfg = *cfg;
+ ret = hsi_setup(cl);
+ if (ret < 0)
+ return ret;
+
+ if ((dir == HSI_CHAR_RX) && (cfg->mode == HSI_MODE_FRAME))
+ hsi_char_break_request(cl);
+
+ return ret;
+}
+
+static inline void hsi_char_cfg_get(struct hsi_client *cl,
+ struct hsi_config *cfg, int dir)
+{
+ struct hsi_config *rxtx_cfg;
+
+ if (dir == HSI_CHAR_RX)
+ rxtx_cfg = &cl->rx_cfg;
+ else
+ rxtx_cfg = &cl->tx_cfg;
+ *cfg = *rxtx_cfg;
+}
+
+static inline void hsi_char_rx2icfg(struct hsi_config *cfg,
+ struct hsc_rx_config *rx_cfg)
+{
+ cfg->mode = rx_cfg->mode;
+ cfg->flow = rx_cfg->flow;
+ cfg->channels = rx_cfg->channels;
+ cfg->speed = 0;
+}
+
+static inline void hsi_char_tx2icfg(struct hsi_config *cfg,
+ struct hsc_tx_config *tx_cfg)
+{
+ cfg->mode = tx_cfg->mode;
+ cfg->channels = tx_cfg->channels;
+ cfg->speed = tx_cfg->speed;
+ cfg->arb_mode = tx_cfg->arb_mode;
+}
+
+static inline void hsi_char_rx2ecfg(struct hsc_rx_config *rx_cfg,
+ struct hsi_config *cfg)
+{
+ rx_cfg->mode = cfg->mode;
+ rx_cfg->flow = cfg->flow;
+ rx_cfg->channels = cfg->channels;
+}
+
+static inline void hsi_char_tx2ecfg(struct hsc_tx_config *tx_cfg,
+ struct hsi_config *cfg)
+{
+ tx_cfg->mode = cfg->mode;
+ tx_cfg->channels = cfg->channels;
+ tx_cfg->speed = cfg->speed;
+ tx_cfg->arb_mode = cfg->arb_mode;
+}
+
+static ssize_t hsi_char_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct hsi_char_channel *channel = file->private_data;
+ struct hsi_msg *msg = NULL;
+ ssize_t ret;
+
+ if (len == 0) {
+ channel->poll_event &= ~POLLPRI;
+ return 0;
+ }
+ channel->poll_event &= ~POLLPRI;
+
+ if (!IS_ALIGNED(len, sizeof(u32)))
+ return -EINVAL;
+
+ if (len > max_data_size)
+ len = max_data_size;
+
+ spin_lock_bh(&channel->lock);
+ if (HSI_CHST_OC(channel) != HSI_CHST_OPENED) {
+ ret = -ENODEV;
+ goto out;
+ }
+ if (HSI_CHST_RD(channel) != HSI_CHST_READOFF) {
+ ret = -EBUSY;
+ goto out;
+ }
+ if (channel->ch >= channel->cl->rx_cfg.channels) {
+ ret = -ENODEV;
+ goto out;
+ }
+ if (list_empty(&channel->free_msgs_list)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ msg = list_first_entry(&channel->free_msgs_list, struct hsi_msg, link);
+ list_del(&msg->link);
+ spin_unlock_bh(&channel->lock);
+ hsi_char_msg_len_set(msg, len);
+ msg->complete = hsi_char_rx_completed;
+ msg->destructor = hsi_char_rx_msg_destructor;
+ ret = hsi_async_read(channel->cl, msg);
+ spin_lock_bh(&channel->lock);
+ if (ret < 0)
+ goto out;
+ HSI_CHST_RD_SET(channel, HSI_CHST_READING);
+ msg = NULL;
+
+ for ( ; ; ) {
+ DEFINE_WAIT(wait);
+
+ if (!list_empty(&channel->rx_msgs_queue)) {
+ msg = list_first_entry(&channel->rx_msgs_queue,
+ struct hsi_msg, link);
+ HSI_CHST_RD_SET(channel, HSI_CHST_READOFF);
+ channel->poll_event &= ~(POLLIN | POLLRDNORM);
+ list_del(&msg->link);
+ spin_unlock_bh(&channel->lock);
+ if (msg->status == HSI_STATUS_ERROR) {
+ ret = -EIO;
+ } else {
+ ret = copy_to_user((void __user *)buf,
+ msg->context,
+ hsi_char_msg_len_get(msg));
+ if (ret)
+ ret = -EFAULT;
+ else
+ ret = hsi_char_msg_len_get(msg);
+ }
+ spin_lock_bh(&channel->lock);
+ break;
+ } else if (signal_pending(current)) {
+ spin_unlock_bh(&channel->lock);
+ hsi_char_rx_cancel(channel);
+ spin_lock_bh(&channel->lock);
+ HSI_CHST_RD_SET(channel, HSI_CHST_READOFF);
+ ret = -EINTR;
+ break;
+ } else if ((HSI_CHST_OC(channel) == HSI_CHST_CLOSING) ||
+ (HSI_CHST_OC(channel) == HSI_CHST_CLOSING)) {
+ ret = -EIO;
+ break;
+ }
+ prepare_to_wait(&channel->rx_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_bh(&channel->lock);
+
+ schedule();
+
+ spin_lock_bh(&channel->lock);
+ finish_wait(&channel->rx_wait, &wait);
+ }
+out:
+ if (msg)
+ list_add_tail(&msg->link, &channel->free_msgs_list);
+ spin_unlock_bh(&channel->lock);
+
+ return ret;
+}
+
+static ssize_t hsi_char_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct hsi_char_channel *channel = file->private_data;
+ struct hsi_msg *msg = NULL;
+ ssize_t ret;
+
+ if ((len == 0) || !IS_ALIGNED(len, sizeof(u32)))
+ return -EINVAL;
+
+ if (len > max_data_size)
+ len = max_data_size;
+
+ spin_lock_bh(&channel->lock);
+ if (HSI_CHST_OC(channel) != HSI_CHST_OPENED) {
+ ret = -ENODEV;
+ goto out;
+ }
+ if (HSI_CHST_WR(channel) != HSI_CHST_WRITEOFF) {
+ ret = -EBUSY;
+ goto out;
+ }
+ if (channel->ch >= channel->cl->tx_cfg.channels) {
+ ret = -ENODEV;
+ goto out;
+ }
+ if (list_empty(&channel->free_msgs_list)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ msg = list_first_entry(&channel->free_msgs_list, struct hsi_msg, link);
+ list_del(&msg->link);
+ HSI_CHST_WR_SET(channel, HSI_CHST_WRITEON);
+ spin_unlock_bh(&channel->lock);
+
+ if (copy_from_user(msg->context, (void __user *)buf, len)) {
+ spin_lock_bh(&channel->lock);
+ HSI_CHST_WR_SET(channel, HSI_CHST_WRITEOFF);
+ ret = -EFAULT;
+ goto out;
+ }
+
+ hsi_char_msg_len_set(msg, len);
+ msg->complete = hsi_char_tx_completed;
+ msg->destructor = hsi_char_tx_msg_destructor;
+ channel->poll_event &= ~(POLLOUT | POLLWRNORM);
+ ret = hsi_async_write(channel->cl, msg);
+ spin_lock_bh(&channel->lock);
+ if (ret < 0) {
+ channel->poll_event |= (POLLOUT | POLLWRNORM);
+ HSI_CHST_WR_SET(channel, HSI_CHST_WRITEOFF);
+ goto out;
+ }
+ HSI_CHST_WR_SET(channel, HSI_CHST_WRITING);
+ msg = NULL;
+
+ for ( ; ; ) {
+ DEFINE_WAIT(wait);
+
+ if (!list_empty(&channel->tx_msgs_queue)) {
+ msg = list_first_entry(&channel->tx_msgs_queue,
+ struct hsi_msg, link);
+ list_del(&msg->link);
+ HSI_CHST_WR_SET(channel, HSI_CHST_WRITEOFF);
+ if (msg->status == HSI_STATUS_ERROR)
+ ret = -EIO;
+ else
+ ret = hsi_char_msg_len_get(msg);
+ break;
+ } else if (signal_pending(current)) {
+ spin_unlock_bh(&channel->lock);
+ hsi_char_tx_cancel(channel);
+ spin_lock_bh(&channel->lock);
+ HSI_CHST_WR_SET(channel, HSI_CHST_WRITEOFF);
+ ret = -EINTR;
+ break;
+ } else if ((HSI_CHST_OC(channel) == HSI_CHST_CLOSING) ||
+ (HSI_CHST_OC(channel) == HSI_CHST_CLOSING)) {
+ ret = -EIO;
+ break;
+ }
+ prepare_to_wait(&channel->tx_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_bh(&channel->lock);
+
+ schedule();
+
+ spin_lock_bh(&channel->lock);
+ finish_wait(&channel->tx_wait, &wait);
+ }
+out:
+ if (msg)
+ list_add_tail(&msg->link, &channel->free_msgs_list);
+
+ spin_unlock_bh(&channel->lock);
+
+ return ret;
+}
+
+static unsigned int hsi_char_poll(struct file *file, poll_table *wait)
+{
+ struct hsi_char_channel *channel = file->private_data;
+ unsigned int ret;
+
+ spin_lock_bh(&channel->lock);
+ if ((HSI_CHST_OC(channel) != HSI_CHST_OPENED) ||
+ (channel->ch >= channel->cl->rx_cfg.channels)) {
+ spin_unlock_bh(&channel->lock);
+ return -ENODEV;
+ }
+ poll_wait(file, &channel->rx_wait, wait);
+ poll_wait(file, &channel->tx_wait, wait);
+ ret = channel->poll_event;
+ spin_unlock_bh(&channel->lock);
+ hsi_char_rx_poll(channel);
+
+ return ret;
+}
+
+static long hsi_char_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct hsi_char_channel *channel = file->private_data;
+ unsigned int state;
+ struct hsi_config cfg;
+ struct hsc_rx_config rx_cfg;
+ struct hsc_tx_config tx_cfg;
+ long ret = 0;
+
+ if (HSI_CHST_OC(channel) != HSI_CHST_OPENED)
+ return -ENODEV;
+
+ switch (cmd) {
+ case HSC_RESET:
+ hsi_char_reset(channel->cl);
+ break;
+ case HSC_SET_PM:
+ if (copy_from_user(&state, (void __user *)arg, sizeof(state)))
+ return -EFAULT;
+ if (state == HSC_PM_DISABLE) {
+ ret = hsi_start_tx(channel->cl);
+ if (!ret)
+ channel->wlrefcnt++;
+ } else if ((state == HSC_PM_ENABLE)
+ && (channel->wlrefcnt > 0)) {
+ ret = hsi_stop_tx(channel->cl);
+ if (!ret)
+ channel->wlrefcnt--;
+ } else {
+ ret = -EINVAL;
+ }
+ break;
+ case HSC_SEND_BREAK:
+ return hsi_char_break_send(channel->cl);
+ case HSC_SET_RX:
+ if (copy_from_user(&rx_cfg, (void __user *)arg, sizeof(rx_cfg)))
+ return -EFAULT;
+ hsi_char_rx2icfg(&cfg, &rx_cfg);
+ return hsi_char_cfg_set(channel->cl, &cfg, HSI_CHAR_RX);
+ case HSC_GET_RX:
+ hsi_char_cfg_get(channel->cl, &cfg, HSI_CHAR_RX);
+ hsi_char_rx2ecfg(&rx_cfg, &cfg);
+ if (copy_to_user((void __user *)arg, &rx_cfg, sizeof(rx_cfg)))
+ return -EFAULT;
+ break;
+ case HSC_SET_TX:
+ if (copy_from_user(&tx_cfg, (void __user *)arg, sizeof(tx_cfg)))
+ return -EFAULT;
+ hsi_char_tx2icfg(&cfg, &tx_cfg);
+ return hsi_char_cfg_set(channel->cl, &cfg, HSI_CHAR_TX);
+ case HSC_GET_TX:
+ hsi_char_cfg_get(channel->cl, &cfg, HSI_CHAR_TX);
+ hsi_char_tx2ecfg(&tx_cfg, &cfg);
+ if (copy_to_user((void __user *)arg, &tx_cfg, sizeof(tx_cfg)))
+ return -EFAULT;
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return ret;
+}
+
+static int hsi_char_open(struct inode *inode, struct file *file)
+{
+ struct hsi_char_client_data *cl_data = &hsi_char_cl_data;
+ struct hsi_char_channel *channel = cl_data->channels + iminor(inode);
+ int ret = 0, refcnt;
+
+ spin_lock_bh(&channel->lock);
+ if ((channel->state == HSI_CHST_UNAVAIL) || (!channel->cl)) {
+ ret = -ENODEV;
+ goto out;
+ }
+ if (HSI_CHST_OC(channel) != HSI_CHST_CLOSED) {
+ ret = -EBUSY;
+ goto out;
+ }
+ HSI_CHST_OC_SET(channel, HSI_CHST_OPENING);
+ spin_unlock_bh(&channel->lock);
+
+ refcnt = atomic_inc_return(&cl_data->refcnt);
+ if (refcnt == 1) {
+ if (cl_data->attached) {
+ atomic_dec(&cl_data->refcnt);
+ spin_lock_bh(&channel->lock);
+ HSI_CHST_OC_SET(channel, HSI_CHST_CLOSED);
+ ret = -EBUSY;
+ goto out;
+ }
+ ret = hsi_claim_port(channel->cl, 0);
+ if (ret < 0) {
+ atomic_dec(&cl_data->refcnt);
+ spin_lock_bh(&channel->lock);
+ HSI_CHST_OC_SET(channel, HSI_CHST_CLOSED);
+ goto out;
+ }
+ hsi_setup(channel->cl);
+ } else if (!cl_data->attached) {
+ atomic_dec(&cl_data->refcnt);
+ spin_lock_bh(&channel->lock);
+ HSI_CHST_OC_SET(channel, HSI_CHST_CLOSED);
+ ret = -ENODEV;
+ goto out;
+ }
+ ret = hsi_char_msgs_alloc(channel);
+
+ if (ret < 0) {
+ refcnt = atomic_dec_return(&cl_data->refcnt);
+ if (!refcnt)
+ hsi_release_port(channel->cl);
+ spin_lock_bh(&channel->lock);
+ HSI_CHST_OC_SET(channel, HSI_CHST_CLOSED);
+ goto out;
+ }
+ if (refcnt == 1)
+ cl_data->attached = 1;
+ channel->wlrefcnt = 0;
+ channel->rxpoll = HSI_CHAR_POLL_OFF;
+ channel->poll_event = (POLLOUT | POLLWRNORM);
+ file->private_data = channel;
+ spin_lock_bh(&channel->lock);
+ HSI_CHST_OC_SET(channel, HSI_CHST_OPENED);
+out:
+ spin_unlock_bh(&channel->lock);
+
+ return ret;
+}
+
+static int hsi_char_release(struct inode *inode, struct file *file)
+{
+ struct hsi_char_channel *channel = file->private_data;
+ return _hsi_char_release(channel, 0);
+}
+
+static int hsi_char_fasync(int fd, struct file *file, int on)
+{
+ struct hsi_char_channel *channel = file->private_data;
+
+ if (fasync_helper(fd, file, on, &channel->async_queue) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static const struct file_operations hsi_char_fops = {
+ .owner = THIS_MODULE,
+ .read = hsi_char_read,
+ .write = hsi_char_write,
+ .poll = hsi_char_poll,
+ .unlocked_ioctl = hsi_char_ioctl,
+ .open = hsi_char_open,
+ .release = hsi_char_release,
+ .fasync = hsi_char_fasync,
+};
+
+static struct hsi_client_driver hsi_char_driver = {
+ .driver = {
+ .name = "hsi_char",
+ .owner = THIS_MODULE,
+ .probe = hsi_char_probe,
+ .remove = hsi_char_remove,
+ },
+};
+
+static inline void hsi_char_channel_init(struct hsi_char_channel *channel)
+{
+ channel->state = HSI_CHST_AVAIL;
+ INIT_LIST_HEAD(&channel->free_msgs_list);
+ init_waitqueue_head(&channel->rx_wait);
+ init_waitqueue_head(&channel->tx_wait);
+ spin_lock_init(&channel->lock);
+ INIT_LIST_HEAD(&channel->rx_msgs_queue);
+ INIT_LIST_HEAD(&channel->tx_msgs_queue);
+}
+
+static struct cdev hsi_char_cdev;
+
+static int __init hsi_char_init(void)
+{
+ struct hsi_char_client_data *cl_data = &hsi_char_cl_data;
+ struct hsi_char_channel *channel = cl_data->channels;
+ unsigned long ch_mask = 0;
+ unsigned int i;
+ int ret;
+
+ if ((max_data_size < 4) || (max_data_size > 0x10000) ||
+ (max_data_size & (max_data_size - 1))) {
+ pr_err("Invalid max read/write data size");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < HSI_CHAR_DEVS && channels_map[i] >= 0; i++) {
+ if (channels_map[i] >= HSI_CHAR_DEVS) {
+ pr_err("Invalid HSI/SSI channel specified");
+ return -EINVAL;
+ }
+ set_bit(channels_map[i], &ch_mask);
+ }
+
+ if (i == 0) {
+ pr_err("No HSI channels available");
+ return -EINVAL;
+ }
+
+ memset(cl_data->channels, 0, sizeof(cl_data->channels));
+ for (i = 0; i < HSI_CHAR_DEVS; i++, channel++) {
+ channel->ch = i;
+ channel->state = HSI_CHST_UNAVAIL;
+ if (test_bit(i, &ch_mask))
+ hsi_char_channel_init(channel);
+ }
+
+ ret = hsi_register_client_driver(&hsi_char_driver);
+ if (ret) {
+ pr_err("Error while registering HSI/SSI driver %d", ret);
+ return ret;
+ }
+
+ pr_info("HSI/SSI char device loaded\n");
+
+ return 0;
+}
+module_init(hsi_char_init);
+
+static void __exit hsi_char_exit(void)
+{
+ device_destroy(hsi_char_class, hsi_char_cdev.dev);
+ class_destroy(hsi_char_class);
+ cdev_del(&hsi_char_cdev);
+ unregister_chrdev_region(hsi_char_dev, HSI_CHAR_DEVS);
+ hsi_unregister_client_driver(&hsi_char_driver);
+ pr_info("HSI char device removed\n");
+}
+module_exit(hsi_char_exit);
+
+MODULE_AUTHOR("Andras Domokos <andras.domokos@nokia.com>");
+MODULE_ALIAS("hsi:hsi_char");
+MODULE_DESCRIPTION("HSI character device");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hsi/controllers/Kconfig b/drivers/hsi/controllers/Kconfig
new file mode 100644
index 00000000000..76d339eaf32
--- /dev/null
+++ b/drivers/hsi/controllers/Kconfig
@@ -0,0 +1,33 @@
+#
+# HSI controllers configuration
+#
+comment "HSI controllers"
+
+config STE_HSI
+ tristate "STE HSI controller driver"
+ depends on (ARCH_U8500 || ARCH_NOMADIK) && HSI
+ default n
+ help
+ ST-Ericsson HSI controller.
+ If you say Y here, you will enable the U8500 HSI hardware driver.
+
+ If unsure, say N.
+
+config OMAP_SSI
+ tristate "OMAP SSI hardware driver"
+ depends on ARCH_OMAP && HSI
+ default n
+ ---help---
+ SSI is a legacy version of HSI. It is usually used to connect
+ an application engine with a cellular modem.
+ If you say Y here, you will enable the OMAP SSI hardware driver.
+
+ If unsure, say N.
+
+if OMAP_SSI
+
+config OMAP_SSI_CONFIG
+ boolean
+ default y
+
+endif # OMAP_SSI
diff --git a/drivers/hsi/controllers/Makefile b/drivers/hsi/controllers/Makefile
new file mode 100644
index 00000000000..475637a0f23
--- /dev/null
+++ b/drivers/hsi/controllers/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for HSI controllers drivers
+#
+
+obj-$(CONFIG_STE_HSI) += ste_hsi.o
+obj-$(CONFIG_OMAP_SSI) += omap_ssi.o
diff --git a/drivers/hsi/controllers/omap_ssi.c b/drivers/hsi/controllers/omap_ssi.c
new file mode 100644
index 00000000000..a82ea0e13cc
--- /dev/null
+++ b/drivers/hsi/controllers/omap_ssi.c
@@ -0,0 +1,1853 @@
+/*
+ * omap_ssi.c
+ *
+ * Implements the OMAP SSI driver.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/compiler.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/scatterlist.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/hsi/hsi.h>
+#include <linux/debugfs.h>
+#include <plat/omap-pm.h>
+#include <plat/clock.h>
+#include <plat/ssi.h>
+
+#define SSI_MAX_CHANNELS 8
+#define SSI_MAX_GDD_LCH 8
+#define SSI_BYTES_TO_FRAMES(x) ((((x) - 1) >> 2) + 1)
+
+/**
+ * struct ssi_clk_res - Device resource data for the SSI clocks
+ * @clk: Pointer to the clock
+ * @nb: Pointer to the clock notifier for clk, if any
+ */
+struct ssi_clk_res {
+ struct clk *clk;
+ struct notifier_block *nb;
+};
+
+/**
+ * struct gdd_trn - GDD transaction data
+ * @msg: Pointer to the HSI message being served
+ * @sg: Pointer to the current sg entry being served
+ */
+struct gdd_trn {
+ struct hsi_msg *msg;
+ struct scatterlist *sg;
+};
+
+/**
+ * struct omap_ssm_ctx - OMAP synchronous serial module (TX/RX) context
+ * @mode: Bit transmission mode
+ * @channels: Number of channels
+ * @framesize: Frame size in bits
+ * @timeout: RX frame timeout
+ * @divisor: TX divider
+ * @arb_mode: Arbitration mode for TX frame (Round robin, priority)
+ */
+struct omap_ssm_ctx {
+ u32 mode;
+ u32 channels;
+ u32 frame_size;
+ union {
+ u32 timeout; /* Rx Only */
+ struct {
+ u32 arb_mode;
+ u32 divisor;
+ }; /* Tx only */
+ };
+};
+
+/**
+ * struct omap_ssi_port - OMAP SSI port data
+ * @dev: device associated to the port (HSI port)
+ * @sst_dma: SSI transmitter physical base address
+ * @ssr_dma: SSI receiver physical base address
+ * @sst_base: SSI transmitter base address
+ * @ssr_base: SSI receiver base address
+ * @wk_lock: spin lock to serialize access to the wake lines
+ * @lock: Spin lock to serialize access to the SSI port
+ * @channels: Current number of channels configured (1,2,4 or 8)
+ * @txqueue: TX message queues
+ * @rxqueue: RX message queues
+ * @brkqueue: Queue of incoming HWBREAK requests (FRAME mode)
+ * @irq: IRQ number
+ * @wake_irq: IRQ number for incoming wake line (-1 if none)
+ * @pio_tasklet: Bottom half for PIO transfers and events
+ * @wake_tasklet: Bottom half for incoming wake events
+ * @wkin_cken: Keep track of clock references due to the incoming wake line
+ * @wk_refcount: Reference count for output wake line
+ * @sys_mpu_enable: Context for the interrupt enable register for irq 0
+ * @sst: Context for the synchronous serial transmitter
+ * @ssr: Context for the synchronous serial receiver
+ */
+struct omap_ssi_port {
+ struct device *dev;
+ dma_addr_t sst_dma;
+ dma_addr_t ssr_dma;
+ void __iomem *sst_base;
+ void __iomem *ssr_base;
+ spinlock_t wk_lock;
+ spinlock_t lock;
+ unsigned int channels;
+ struct list_head txqueue[SSI_MAX_CHANNELS];
+ struct list_head rxqueue[SSI_MAX_CHANNELS];
+ struct list_head brkqueue;
+ unsigned int irq;
+ int wake_irq;
+ struct tasklet_struct pio_tasklet;
+ struct tasklet_struct wake_tasklet;
+ unsigned int wkin_cken:1; /* Workaround */
+ int wk_refcount;
+ /* OMAP SSI port context */
+ u32 sys_mpu_enable; /* We use only one irq */
+ struct omap_ssm_ctx sst;
+ struct omap_ssm_ctx ssr;
+};
+
+/**
+ * struct omap_ssi_controller - OMAP SSI controller data
+ * @dev: device associated to the controller (HSI controller)
+ * @sys: SSI I/O base address
+ * @gdd: GDD I/O base address
+ * @ick: SSI interconnect clock
+ * @fck: SSI functional clock
+ * @ck_refcount: References count for clocks
+ * @gdd_irq: IRQ line for GDD
+ * @gdd_tasklet: bottom half for DMA transfers
+ * @gdd_trn: Array of GDD transaction data for ongoing GDD transfers
+ * @lock: lock to serialize access to GDD
+ * @ck_lock: lock to serialize access to the clocks
+ * @loss_count: To follow if we need to restore context or not
+ * @max_speed: Maximum TX speed (Kb/s) set by the clients.
+ * @sysconfig: SSI controller saved context
+ * @gdd_gcr: SSI GDD saved context
+ * @get_loss: Pointer to omap_pm_get_dev_context_loss_count, if any
+ * @port: Array of pointers of the ports of the controller
+ * @dir: Debugfs SSI root directory
+ */
+struct omap_ssi_controller {
+ struct device *dev;
+ void __iomem *sys;
+ void __iomem *gdd;
+ struct clk *ick;
+ struct clk *fck;
+ int ck_refcount;
+ unsigned int gdd_irq;
+ struct tasklet_struct gdd_tasklet;
+ struct gdd_trn gdd_trn[SSI_MAX_GDD_LCH];
+ spinlock_t lock;
+ spinlock_t ck_lock;
+ unsigned long fck_rate;
+ int loss_count;
+ u32 max_speed;
+ /* OMAP SSI Controller context */
+ u32 sysconfig;
+ u32 gdd_gcr;
+ int (*get_loss)(struct device *dev);
+ struct omap_ssi_port **port;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dir;
+#endif
+};
+
+static inline unsigned int ssi_wakein(struct hsi_port *port)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+
+ return gpio_get_value(irq_to_gpio(omap_port->wake_irq));
+}
+
+static int ssi_for_each_port(struct hsi_controller *ssi, void *data,
+ int (*fn)(struct omap_ssi_port *p, void *data))
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ unsigned int i = 0;
+ int err = 0;
+
+ for (i = 0; ((i < ssi->num_ports) && !err); i++)
+ err = (*fn)(omap_ssi->port[i], data);
+
+ return err;
+}
+
+static int ssi_set_port_mode(struct omap_ssi_port *omap_port, void *data)
+{
+ u32 *mode = data;
+
+ __raw_writel(*mode, omap_port->sst_base + SSI_SST_MODE_REG);
+ __raw_writel(*mode, omap_port->ssr_base + SSI_SSR_MODE_REG);
+ /* OCP barrier */
+ *mode = __raw_readl(omap_port->ssr_base + SSI_SSR_MODE_REG);
+
+ return 0;
+}
+
+static inline void ssi_set_mode(struct hsi_controller *ssi, u32 mode)
+{
+ ssi_for_each_port(ssi, &mode, ssi_set_port_mode);
+}
+
+static int ssi_restore_port_mode(struct omap_ssi_port *omap_port,
+ void *data __maybe_unused)
+{
+ u32 mode;
+
+ __raw_writel(omap_port->sst.mode,
+ omap_port->sst_base + SSI_SST_MODE_REG);
+ __raw_writel(omap_port->ssr.mode,
+ omap_port->ssr_base + SSI_SSR_MODE_REG);
+ /* OCP barrier */
+ mode = __raw_readl(omap_port->ssr_base + SSI_SSR_MODE_REG);
+
+ return 0;
+}
+
+static int ssi_restore_divisor(struct omap_ssi_port *omap_port,
+ void *data __maybe_unused)
+{
+ __raw_writel(omap_port->sst.divisor,
+ omap_port->sst_base + SSI_SST_DIVISOR_REG);
+
+ return 0;
+}
+
+static int ssi_restore_port_ctx(struct omap_ssi_port *omap_port,
+ void *data __maybe_unused)
+{
+ struct hsi_port *port = to_hsi_port(omap_port->dev);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *base = omap_port->sst_base;
+
+ __raw_writel(omap_port->sys_mpu_enable,
+ omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ /* SST context */
+ __raw_writel(omap_port->sst.frame_size, base + SSI_SST_FRAMESIZE_REG);
+ __raw_writel(omap_port->sst.channels, base + SSI_SST_CHANNELS_REG);
+ __raw_writel(omap_port->sst.arb_mode, base + SSI_SST_ARBMODE_REG);
+ /* SSR context */
+ base = omap_port->ssr_base;
+ __raw_writel(omap_port->ssr.frame_size, base + SSI_SSR_FRAMESIZE_REG);
+ __raw_writel(omap_port->ssr.channels, base + SSI_SSR_CHANNELS_REG);
+ __raw_writel(omap_port->ssr.timeout, base + SSI_SSR_TIMEOUT_REG);
+
+ return 0;
+}
+
+static int ssi_save_port_ctx(struct omap_ssi_port *omap_port,
+ void *data __maybe_unused)
+{
+ struct hsi_port *port = to_hsi_port(omap_port->dev);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ omap_port->sys_mpu_enable = __raw_readl(omap_ssi->sys +
+ SSI_MPU_ENABLE_REG(port->num, 0));
+
+ return 0;
+}
+
+static int ssi_clk_enable(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ int err = 0;
+
+ spin_lock_bh(&omap_ssi->ck_lock);
+ if (omap_ssi->ck_refcount++)
+ goto out;
+ err = clk_enable(omap_ssi->fck);
+ if (unlikely(err < 0))
+ goto out;
+ err = clk_enable(omap_ssi->ick);
+ if (unlikely(err < 0)) {
+ clk_disable(omap_ssi->fck);
+ goto out;
+ }
+ if ((omap_ssi->get_loss) && (omap_ssi->loss_count ==
+ (*omap_ssi->get_loss)(ssi->device.parent)))
+ goto mode; /* We always need to restore the mode & TX divisor */
+
+ __raw_writel(omap_ssi->sysconfig, omap_ssi->sys + SSI_SYSCONFIG_REG);
+ __raw_writel(omap_ssi->gdd_gcr, omap_ssi->gdd + SSI_GDD_GCR_REG);
+
+ ssi_for_each_port(ssi, NULL, ssi_restore_port_ctx);
+mode:
+ ssi_for_each_port(ssi, NULL, ssi_restore_divisor);
+ ssi_for_each_port(ssi, NULL, ssi_restore_port_mode);
+out:
+ spin_unlock_bh(&omap_ssi->ck_lock);
+
+ return err;
+}
+
+static void ssi_clk_disable(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ spin_lock_bh(&omap_ssi->ck_lock);
+ WARN_ON(omap_ssi->ck_refcount <= 0);
+ if (--omap_ssi->ck_refcount)
+ goto out;
+
+ ssi_set_mode(ssi, SSI_MODE_SLEEP);
+
+ if (omap_ssi->get_loss)
+ omap_ssi->loss_count =
+ (*omap_ssi->get_loss)(ssi->device.parent);
+
+ ssi_for_each_port(ssi, NULL, ssi_save_port_ctx);
+ clk_disable(omap_ssi->ick);
+ clk_disable(omap_ssi->fck);
+out:
+ spin_unlock_bh(&omap_ssi->ck_lock);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int ssi_debug_show(struct seq_file *m, void *p __maybe_unused)
+{
+ struct hsi_controller *ssi = m->private;
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *sys = omap_ssi->sys;
+
+ ssi_clk_enable(ssi);
+ seq_printf(m, "REVISION\t: 0x%08x\n",
+ __raw_readl(sys + SSI_REVISION_REG));
+ seq_printf(m, "SYSCONFIG\t: 0x%08x\n",
+ __raw_readl(sys + SSI_SYSCONFIG_REG));
+ seq_printf(m, "SYSSTATUS\t: 0x%08x\n",
+ __raw_readl(sys + SSI_SYSSTATUS_REG));
+ ssi_clk_disable(ssi);
+
+ return 0;
+}
+
+static int ssi_debug_port_show(struct seq_file *m, void *p __maybe_unused)
+{
+ struct hsi_port *port = m->private;
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *base = omap_ssi->sys;
+ unsigned int ch;
+
+ ssi_clk_enable(ssi);
+ if (omap_port->wake_irq > 0)
+ seq_printf(m, "CAWAKE\t\t: %d\n", ssi_wakein(port));
+ seq_printf(m, "WAKE\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_WAKE_REG(port->num)));
+ seq_printf(m, "MPU_ENABLE_IRQ%d\t: 0x%08x\n", 0,
+ __raw_readl(base + SSI_MPU_ENABLE_REG(port->num, 0)));
+ seq_printf(m, "MPU_STATUS_IRQ%d\t: 0x%08x\n", 0,
+ __raw_readl(base + SSI_MPU_STATUS_REG(port->num, 0)));
+ /* SST */
+ base = omap_port->sst_base;
+ seq_printf(m, "\nSST\n===\n");
+ seq_printf(m, "ID SST\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SST_ID_REG));
+ seq_printf(m, "MODE\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SST_MODE_REG));
+ seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
+ __raw_readl(base + SSI_SST_FRAMESIZE_REG));
+ seq_printf(m, "DIVISOR\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SST_DIVISOR_REG));
+ seq_printf(m, "CHANNELS\t: 0x%08x\n",
+ __raw_readl(base + SSI_SST_CHANNELS_REG));
+ seq_printf(m, "ARBMODE\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SST_ARBMODE_REG));
+ seq_printf(m, "TXSTATE\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SST_TXSTATE_REG));
+ seq_printf(m, "BUFSTATE\t: 0x%08x\n",
+ __raw_readl(base + SSI_SST_BUFSTATE_REG));
+ seq_printf(m, "BREAK\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SST_BREAK_REG));
+ for (ch = 0; ch < omap_port->channels; ch++) {
+ seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
+ __raw_readl(base + SSI_SST_BUFFER_CH_REG(ch)));
+ }
+ /* SSR */
+ base = omap_port->ssr_base;
+ seq_printf(m, "\nSSR\n===\n");
+ seq_printf(m, "ID SSR\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SSR_ID_REG));
+ seq_printf(m, "MODE\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SSR_MODE_REG));
+ seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
+ __raw_readl(base + SSI_SSR_FRAMESIZE_REG));
+ seq_printf(m, "CHANNELS\t: 0x%08x\n",
+ __raw_readl(base + SSI_SSR_CHANNELS_REG));
+ seq_printf(m, "TIMEOUT\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SSR_TIMEOUT_REG));
+ seq_printf(m, "RXSTATE\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SSR_RXSTATE_REG));
+ seq_printf(m, "BUFSTATE\t: 0x%08x\n",
+ __raw_readl(base + SSI_SSR_BUFSTATE_REG));
+ seq_printf(m, "BREAK\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SSR_BREAK_REG));
+ seq_printf(m, "ERROR\t\t: 0x%08x\n",
+ __raw_readl(base + SSI_SSR_ERROR_REG));
+ seq_printf(m, "ERRORACK\t: 0x%08x\n",
+ __raw_readl(base + SSI_SSR_ERRORACK_REG));
+ for (ch = 0; ch < omap_port->channels; ch++) {
+ seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
+ __raw_readl(base + SSI_SSR_BUFFER_CH_REG(ch)));
+ }
+ ssi_clk_disable(ssi);
+
+ return 0;
+}
+
+static int ssi_debug_gdd_show(struct seq_file *m, void *p __maybe_unused)
+{
+ struct hsi_controller *ssi = m->private;
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *gdd = omap_ssi->gdd;
+ int lch;
+
+ ssi_clk_enable(ssi);
+ seq_printf(m, "GDD_MPU_STATUS\t: 0x%08x\n",
+ __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG));
+ seq_printf(m, "GDD_MPU_ENABLE\t: 0x%08x\n\n",
+ __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG));
+ seq_printf(m, "HW_ID\t\t: 0x%08x\n",
+ __raw_readl(gdd + SSI_GDD_HW_ID_REG));
+ seq_printf(m, "PPORT_ID\t: 0x%08x\n",
+ __raw_readl(gdd + SSI_GDD_PPORT_ID_REG));
+ seq_printf(m, "MPORT_ID\t: 0x%08x\n",
+ __raw_readl(gdd + SSI_GDD_MPORT_ID_REG));
+ seq_printf(m, "TEST\t\t: 0x%08x\n",
+ __raw_readl(gdd + SSI_GDD_TEST_REG));
+ seq_printf(m, "GCR\t\t: 0x%08x\n",
+ __raw_readl(gdd + SSI_GDD_GCR_REG));
+
+ for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) {
+ seq_printf(m, "\nGDD LCH %d\n=========\n", lch);
+ seq_printf(m, "CSDP\t\t: 0x%04x\n",
+ __raw_readw(gdd + SSI_GDD_CSDP_REG(lch)));
+ seq_printf(m, "CCR\t\t: 0x%04x\n",
+ __raw_readw(gdd + SSI_GDD_CCR_REG(lch)));
+ seq_printf(m, "CICR\t\t: 0x%04x\n",
+ __raw_readw(gdd + SSI_GDD_CICR_REG(lch)));
+ seq_printf(m, "CSR\t\t: 0x%04x\n",
+ __raw_readw(gdd + SSI_GDD_CSR_REG(lch)));
+ seq_printf(m, "CSSA\t\t: 0x%08x\n",
+ __raw_readl(gdd + SSI_GDD_CSSA_REG(lch)));
+ seq_printf(m, "CDSA\t\t: 0x%08x\n",
+ __raw_readl(gdd + SSI_GDD_CDSA_REG(lch)));
+ seq_printf(m, "CEN\t\t: 0x%04x\n",
+ __raw_readw(gdd + SSI_GDD_CEN_REG(lch)));
+ seq_printf(m, "CSAC\t\t: 0x%04x\n",
+ __raw_readw(gdd + SSI_GDD_CSAC_REG(lch)));
+ seq_printf(m, "CDAC\t\t: 0x%04x\n",
+ __raw_readw(gdd + SSI_GDD_CDAC_REG(lch)));
+ seq_printf(m, "CLNK_CTRL\t: 0x%04x\n",
+ __raw_readw(gdd + SSI_GDD_CLNK_CTRL_REG(lch)));
+ }
+ ssi_clk_disable(ssi);
+
+ return 0;
+}
+
+static int ssi_div_get(void *data, u64 *val)
+{
+ struct hsi_port *port = data;
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+
+ ssi_clk_enable(ssi);
+ *val = __raw_readl(omap_port->sst_base + SSI_SST_DIVISOR_REG);
+ ssi_clk_disable(ssi);
+
+ return 0;
+}
+
+static int ssi_div_set(void *data, u64 val)
+{
+ struct hsi_port *port = data;
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+
+ if (val > 127)
+ return -EINVAL;
+
+ ssi_clk_enable(ssi);
+ __raw_writel(val, omap_port->sst_base + SSI_SST_DIVISOR_REG);
+ omap_port->sst.divisor = val;
+ ssi_clk_disable(ssi);
+
+ return 0;
+}
+
+static int ssi_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ssi_debug_show, inode->i_private);
+}
+
+static int ssi_port_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ssi_debug_port_show, inode->i_private);
+}
+
+static int ssi_gdd_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ssi_debug_gdd_show, inode->i_private);
+}
+
+static const struct file_operations ssi_regs_fops = {
+ .open = ssi_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations ssi_port_regs_fops = {
+ .open = ssi_port_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations ssi_gdd_regs_fops = {
+ .open = ssi_gdd_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+DEFINE_SIMPLE_ATTRIBUTE(ssi_sst_div_fops, ssi_div_get, ssi_div_set, "%llu\n");
+
+static int __init ssi_debug_add_port(struct omap_ssi_port *omap_port,
+ void *data)
+{
+ struct hsi_port *port = to_hsi_port(omap_port->dev);
+ struct dentry *dir = data;
+
+ dir = debugfs_create_dir(dev_name(omap_port->dev), dir);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+ debugfs_create_file("regs", S_IRUGO, dir, port, &ssi_port_regs_fops);
+ dir = debugfs_create_dir("sst", dir);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+ debugfs_create_file("divisor", S_IRUGO | S_IWUSR, dir, port,
+ &ssi_sst_div_fops);
+
+ return 0;
+}
+
+static int __init ssi_debug_add_ctrl(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct dentry *dir;
+ int err;
+
+ /* SSI controller */
+ omap_ssi->dir = debugfs_create_dir(dev_name(&ssi->device), NULL);
+ if (IS_ERR(omap_ssi->dir))
+ return PTR_ERR(omap_ssi->dir);
+
+ debugfs_create_file("regs", S_IRUGO, omap_ssi->dir, ssi,
+ &ssi_regs_fops);
+ /* SSI GDD (DMA) */
+ dir = debugfs_create_dir("gdd", omap_ssi->dir);
+ if (IS_ERR(dir))
+ goto rback;
+ debugfs_create_file("regs", S_IRUGO, dir, ssi, &ssi_gdd_regs_fops);
+ /* SSI ports */
+ err = ssi_for_each_port(ssi, omap_ssi->dir, ssi_debug_add_port);
+ if (err < 0)
+ goto rback;
+
+ return 0;
+rback:
+ debugfs_remove_recursive(omap_ssi->dir);
+
+ return PTR_ERR(dir);
+}
+
+static void ssi_debug_remove_ctrl(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ debugfs_remove_recursive(omap_ssi->dir);
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int ssi_claim_lch(struct hsi_msg *msg)
+{
+
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ int lch;
+
+ for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++)
+ if (!omap_ssi->gdd_trn[lch].msg) {
+ omap_ssi->gdd_trn[lch].msg = msg;
+ omap_ssi->gdd_trn[lch].sg = msg->sgt.sgl;
+ return lch;
+ }
+
+ return -EBUSY;
+}
+
+static int ssi_start_pio(struct hsi_msg *msg)
+{
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ u32 val;
+
+ ssi_clk_enable(ssi);
+ if (msg->ttype == HSI_MSG_WRITE) {
+ val = SSI_DATAACCEPT(msg->channel);
+ ssi_clk_enable(ssi); /* Hold clocks for pio writes */
+ } else {
+ val = SSI_DATAAVAILABLE(msg->channel) | SSI_ERROROCCURED;
+ }
+ dev_dbg(&port->device, "Single %s transfer\n",
+ msg->ttype ? "write" : "read");
+ val |= __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ __raw_writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ ssi_clk_disable(ssi);
+ msg->actual_len = 0;
+ msg->status = HSI_STATUS_PROCEEDING;
+
+ return 0;
+}
+
+static int ssi_start_dma(struct hsi_msg *msg, int lch)
+{
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *gdd = omap_ssi->gdd;
+ int err;
+ u16 csdp;
+ u16 ccr;
+ u32 s_addr;
+ u32 d_addr;
+ u32 tmp;
+
+ if (msg->ttype == HSI_MSG_READ) {
+ err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents,
+ DMA_FROM_DEVICE);
+ if (err < 0) {
+ dev_dbg(&ssi->device, "DMA map SG failed !\n");
+ return err;
+ }
+ csdp = SSI_DST_BURST_4x32_BIT | SSI_DST_MEMORY_PORT |
+ SSI_SRC_SINGLE_ACCESS0 | SSI_SRC_PERIPHERAL_PORT |
+ SSI_DATA_TYPE_S32;
+ ccr = msg->channel + 0x10 + (port->num * 8); /* Sync */
+ ccr |= SSI_DST_AMODE_POSTINC | SSI_SRC_AMODE_CONST |
+ SSI_CCR_ENABLE;
+ s_addr = omap_port->ssr_dma +
+ SSI_SSR_BUFFER_CH_REG(msg->channel);
+ d_addr = sg_dma_address(msg->sgt.sgl);
+ } else {
+ err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents,
+ DMA_TO_DEVICE);
+ if (err < 0) {
+ dev_dbg(&ssi->device, "DMA map SG failed !\n");
+ return err;
+ }
+ csdp = SSI_SRC_BURST_4x32_BIT | SSI_SRC_MEMORY_PORT |
+ SSI_DST_SINGLE_ACCESS0 | SSI_DST_PERIPHERAL_PORT |
+ SSI_DATA_TYPE_S32;
+ ccr = (msg->channel + 1 + (port->num * 8)) & 0xf; /* Sync */
+ ccr |= SSI_SRC_AMODE_POSTINC | SSI_DST_AMODE_CONST |
+ SSI_CCR_ENABLE;
+ s_addr = sg_dma_address(msg->sgt.sgl);
+ d_addr = omap_port->sst_dma +
+ SSI_SST_BUFFER_CH_REG(msg->channel);
+ }
+ dev_dbg(&ssi->device, "lch %d cdsp %08x ccr %04x s_addr %08x"
+ " d_addr %08x\n", lch, csdp, ccr, s_addr, d_addr);
+ ssi_clk_enable(ssi); /* Hold clocks during the transfer */
+ __raw_writew(csdp, gdd + SSI_GDD_CSDP_REG(lch));
+ __raw_writew(SSI_BLOCK_IE | SSI_TOUT_IE, gdd + SSI_GDD_CICR_REG(lch));
+ __raw_writel(d_addr, gdd + SSI_GDD_CDSA_REG(lch));
+ __raw_writel(s_addr, gdd + SSI_GDD_CSSA_REG(lch));
+ __raw_writew(SSI_BYTES_TO_FRAMES(msg->sgt.sgl->length),
+ gdd + SSI_GDD_CEN_REG(lch));
+
+ spin_lock_bh(&omap_ssi->lock);
+ tmp = __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ tmp |= SSI_GDD_LCH(lch);
+ __raw_writel(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ spin_unlock_bh(&omap_ssi->lock);
+ __raw_writew(ccr, gdd + SSI_GDD_CCR_REG(lch));
+ msg->status = HSI_STATUS_PROCEEDING;
+
+ return 0;
+}
+
+static int ssi_start_transfer(struct list_head *queue)
+{
+ struct hsi_msg *msg;
+ int lch = -1;
+
+ if (list_empty(queue))
+ return 0;
+ msg = list_first_entry(queue, struct hsi_msg, link);
+ if (msg->status != HSI_STATUS_QUEUED)
+ return 0;
+ if ((msg->sgt.nents) && (msg->sgt.sgl->length > sizeof(u32)))
+ lch = ssi_claim_lch(msg);
+ if (lch >= 0)
+ return ssi_start_dma(msg, lch);
+ else
+ return ssi_start_pio(msg);
+}
+
+static void ssi_transfer(struct omap_ssi_port *omap_port,
+ struct list_head *queue)
+{
+ struct hsi_msg *msg;
+ int err = -1;
+
+ spin_lock_bh(&omap_port->lock);
+ while (err < 0) {
+ err = ssi_start_transfer(queue);
+ if (err < 0) {
+ msg = list_first_entry(queue, struct hsi_msg, link);
+ msg->status = HSI_STATUS_ERROR;
+ msg->actual_len = 0;
+ list_del(&msg->link);
+ spin_unlock_bh(&omap_port->lock);
+ msg->complete(msg);
+ spin_lock_bh(&omap_port->lock);
+ }
+ }
+ spin_unlock_bh(&omap_port->lock);
+}
+
+static u32 ssi_calculate_div(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ u32 tx_fckrate = (u32) omap_ssi->fck_rate;
+
+ /* / 2 : SSI TX clock is always half of the SSI functional clock */
+ tx_fckrate >>= 1;
+ /* Round down when tx_fckrate % omap_ssi->max_speed == 0 */
+ tx_fckrate--;
+ dev_dbg(&ssi->device, "TX div %d for fck_rate %lu Khz speed %d Kb/s\n",
+ tx_fckrate / omap_ssi->max_speed, omap_ssi->fck_rate,
+ omap_ssi->max_speed);
+
+ return tx_fckrate / omap_ssi->max_speed;
+}
+
+static void ssi_error(struct hsi_port *port)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_msg *msg;
+ unsigned int i;
+ u32 err;
+ u32 val;
+ u32 tmp;
+
+ /* ACK error */
+ err = __raw_readl(omap_port->ssr_base + SSI_SSR_ERROR_REG);
+ dev_err(&port->device, "SSI error: 0x%02x\n", err);
+ if (!err) {
+ dev_dbg(&port->device, "spurious SSI error ignored!\n");
+ return;
+ }
+ spin_lock(&omap_ssi->lock);
+ /* Cancel all GDD read transfers */
+ for (i = 0, val = 0; i < SSI_MAX_GDD_LCH; i++) {
+ msg = omap_ssi->gdd_trn[i].msg;
+ if ((msg) && (msg->ttype == HSI_MSG_READ)) {
+ __raw_writew(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i));
+ val |= (1 << i);
+ omap_ssi->gdd_trn[i].msg = NULL;
+ }
+ }
+ tmp = __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ tmp &= ~val;
+ __raw_writel(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ spin_unlock(&omap_ssi->lock);
+ /* Cancel all PIO read transfers */
+ spin_lock(&omap_port->lock);
+ tmp = __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ tmp &= 0xfeff00ff; /* Disable error & all dataavailable interrupts */
+ __raw_writel(tmp, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ /* ACK error */
+ __raw_writel(err, omap_port->ssr_base + SSI_SSR_ERRORACK_REG);
+ __raw_writel(SSI_ERROROCCURED,
+ omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+ /* Signal the error all current pending read requests */
+ for (i = 0; i < omap_port->channels; i++) {
+ if (list_empty(&omap_port->rxqueue[i]))
+ continue;
+ msg = list_first_entry(&omap_port->rxqueue[i], struct hsi_msg,
+ link);
+ list_del(&msg->link);
+ msg->status = HSI_STATUS_ERROR;
+ spin_unlock(&omap_port->lock);
+ msg->complete(msg);
+ /* Now restart queued reads if any */
+ ssi_transfer(omap_port, &omap_port->rxqueue[i]);
+ spin_lock(&omap_port->lock);
+ }
+ spin_unlock(&omap_port->lock);
+}
+
+static void ssi_break_complete(struct hsi_port *port)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_msg *msg;
+ struct hsi_msg *tmp;
+ u32 val;
+
+ dev_dbg(&port->device, "HWBREAK received\n");
+
+ spin_lock(&omap_port->lock);
+ val = __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ val &= ~SSI_BREAKDETECTED;
+ __raw_writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ __raw_writel(0, omap_port->ssr_base + SSI_SSR_BREAK_REG);
+ __raw_writel(SSI_BREAKDETECTED,
+ omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+ spin_unlock(&omap_port->lock);
+
+ list_for_each_entry_safe(msg, tmp, &omap_port->brkqueue, link) {
+ msg->status = HSI_STATUS_COMPLETED;
+ spin_lock(&omap_port->lock);
+ list_del(&msg->link);
+ spin_unlock(&omap_port->lock);
+ msg->complete(msg);
+ }
+
+}
+
+static int ssi_async_break(struct hsi_msg *msg)
+{
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ int err = 0;
+ u32 tmp;
+
+ ssi_clk_enable(ssi);
+ if (msg->ttype == HSI_MSG_WRITE) {
+ if (omap_port->sst.mode != SSI_MODE_FRAME) {
+ err = -EINVAL;
+ goto out;
+ }
+ __raw_writel(1, omap_port->sst_base + SSI_SST_BREAK_REG);
+ msg->status = HSI_STATUS_COMPLETED;
+ msg->complete(msg);
+ } else {
+ if (omap_port->ssr.mode != SSI_MODE_FRAME) {
+ err = -EINVAL;
+ goto out;
+ }
+ spin_lock_bh(&omap_port->lock);
+ tmp = __raw_readl(omap_ssi->sys +
+ SSI_MPU_ENABLE_REG(port->num, 0));
+ __raw_writel(tmp | SSI_BREAKDETECTED,
+ omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ msg->status = HSI_STATUS_PROCEEDING;
+ list_add_tail(&msg->link, &omap_port->brkqueue);
+ spin_unlock_bh(&omap_port->lock);
+ }
+out:
+ ssi_clk_disable(ssi);
+
+ return err;
+}
+
+static int ssi_async(struct hsi_msg *msg)
+{
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct list_head *queue;
+ int err = 0;
+
+ BUG_ON(!msg);
+
+ if (msg->sgt.nents > 1)
+ return -ENOSYS; /* TODO: Add sg support */
+
+ if (msg->break_frame)
+ return ssi_async_break(msg);
+
+ if (msg->ttype) {
+ BUG_ON(msg->channel >= omap_port->sst.channels);
+ queue = &omap_port->txqueue[msg->channel];
+ } else {
+ BUG_ON(msg->channel >= omap_port->ssr.channels);
+ queue = &omap_port->rxqueue[msg->channel];
+ }
+ msg->status = HSI_STATUS_QUEUED;
+ spin_lock_bh(&omap_port->lock);
+ list_add_tail(&msg->link, queue);
+ err = ssi_start_transfer(queue);
+ if (err < 0) {
+ list_del(&msg->link);
+ msg->status = HSI_STATUS_ERROR;
+ }
+ spin_unlock_bh(&omap_port->lock);
+ dev_dbg(&port->device, "msg status %d ttype %d ch %d\n",
+ msg->status, msg->ttype, msg->channel);
+
+ return err;
+}
+
+static void ssi_flush_queue(struct list_head *queue, struct hsi_client *cl)
+{
+ struct list_head *node, *tmp;
+ struct hsi_msg *msg;
+
+ list_for_each_safe(node, tmp, queue) {
+ msg = list_entry(node, struct hsi_msg, link);
+ if ((cl) && (cl != msg->cl))
+ continue;
+ list_del(node);
+ pr_debug("flush queue: ch %d, msg %p len %d type %d ctxt %p\n",
+ msg->channel, msg, msg->sgt.sgl->length,
+ msg->ttype, msg->context);
+ if (msg->destructor)
+ msg->destructor(msg);
+ else
+ hsi_free_msg(msg);
+ }
+}
+
+static int ssi_setup(struct hsi_client *cl)
+{
+ struct hsi_port *port = to_hsi_port(cl->device.parent);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *sst = omap_port->sst_base;
+ void __iomem *ssr = omap_port->ssr_base;
+ u32 div;
+ u32 val;
+ int err = 0;
+
+ ssi_clk_enable(ssi);
+ spin_lock_bh(&omap_port->lock);
+ if (cl->tx_cfg.speed)
+ omap_ssi->max_speed = cl->tx_cfg.speed;
+ div = ssi_calculate_div(ssi);
+ if (div > SSI_MAX_DIVISOR) {
+ dev_err(&cl->device, "Invalid TX speed %d Mb/s (div %d)\n",
+ cl->tx_cfg.speed, div);
+ err = -EINVAL;
+ goto out;
+ }
+ /* Set TX/RX module to sleep to stop TX/RX during cfg update */
+ __raw_writel(SSI_MODE_SLEEP, sst + SSI_SST_MODE_REG);
+ __raw_writel(SSI_MODE_SLEEP, ssr + SSI_SSR_MODE_REG);
+ /* Flush posted write */
+ val = __raw_readl(ssr + SSI_SSR_MODE_REG);
+ /* TX */
+ __raw_writel(31, sst + SSI_SST_FRAMESIZE_REG);
+ __raw_writel(div, sst + SSI_SST_DIVISOR_REG);
+ __raw_writel(cl->tx_cfg.channels, sst + SSI_SST_CHANNELS_REG);
+ __raw_writel(cl->tx_cfg.arb_mode, sst + SSI_SST_ARBMODE_REG);
+ __raw_writel(cl->tx_cfg.mode, sst + SSI_SST_MODE_REG);
+ /* RX */
+ __raw_writel(31, ssr + SSI_SSR_FRAMESIZE_REG);
+ __raw_writel(cl->rx_cfg.channels, ssr + SSI_SSR_CHANNELS_REG);
+ __raw_writel(0, ssr + SSI_SSR_TIMEOUT_REG);
+ /* Cleanup the break queue if we leave FRAME mode */
+ if ((omap_port->ssr.mode == SSI_MODE_FRAME) &&
+ (cl->rx_cfg.mode != SSI_MODE_FRAME))
+ ssi_flush_queue(&omap_port->brkqueue, cl);
+ __raw_writel(cl->rx_cfg.mode, ssr + SSI_SSR_MODE_REG);
+ omap_port->channels = max(cl->rx_cfg.channels, cl->tx_cfg.channels);
+ /* Shadow registering for OFF mode */
+ /* SST */
+ omap_port->sst.divisor = div;
+ omap_port->sst.frame_size = 31;
+ omap_port->sst.channels = cl->tx_cfg.channels;
+ omap_port->sst.arb_mode = cl->tx_cfg.arb_mode;
+ omap_port->sst.mode = cl->tx_cfg.mode;
+ /* SSR */
+ omap_port->ssr.frame_size = 31;
+ omap_port->ssr.timeout = 0;
+ omap_port->ssr.channels = cl->rx_cfg.channels;
+ omap_port->ssr.mode = cl->rx_cfg.mode;
+out:
+ spin_unlock_bh(&omap_port->lock);
+ ssi_clk_disable(ssi);
+
+ return err;
+}
+
+static void ssi_cleanup_queues(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_msg *msg;
+ unsigned int i;
+ u32 rxbufstate = 0;
+ u32 txbufstate = 0;
+ u32 status = SSI_ERROROCCURED;
+ u32 tmp;
+
+ ssi_flush_queue(&omap_port->brkqueue, cl);
+ if (list_empty(&omap_port->brkqueue))
+ status |= SSI_BREAKDETECTED;
+
+ for (i = 0; i < omap_port->channels; i++) {
+ if (list_empty(&omap_port->txqueue[i]))
+ continue;
+ msg = list_first_entry(&omap_port->txqueue[i], struct hsi_msg,
+ link);
+ if ((msg->cl == cl) && (msg->status == HSI_STATUS_PROCEEDING)) {
+ txbufstate |= (1 << i);
+ status |= SSI_DATAACCEPT(i);
+ /* Release the clocks writes, also GDD ones */
+ ssi_clk_disable(ssi);
+ }
+ ssi_flush_queue(&omap_port->txqueue[i], cl);
+ }
+ for (i = 0; i < omap_port->channels; i++) {
+ if (list_empty(&omap_port->rxqueue[i]))
+ continue;
+ msg = list_first_entry(&omap_port->rxqueue[i], struct hsi_msg,
+ link);
+ if ((msg->cl == cl) && (msg->status == HSI_STATUS_PROCEEDING)) {
+ rxbufstate |= (1 << i);
+ status |= SSI_DATAAVAILABLE(i);
+ }
+ ssi_flush_queue(&omap_port->rxqueue[i], cl);
+ /* Check if we keep the error detection interrupt armed */
+ if (!list_empty(&omap_port->rxqueue[i]))
+ status &= ~SSI_ERROROCCURED;
+ }
+ /* Cleanup write buffers */
+ tmp = __raw_readl(omap_port->sst_base + SSI_SST_BUFSTATE_REG);
+ tmp &= ~txbufstate;
+ __raw_writel(tmp, omap_port->sst_base + SSI_SST_BUFSTATE_REG);
+ /* Cleanup read buffers */
+ tmp = __raw_readl(omap_port->ssr_base + SSI_SSR_BUFSTATE_REG);
+ tmp &= ~rxbufstate;
+ __raw_writel(tmp, omap_port->ssr_base + SSI_SSR_BUFSTATE_REG);
+ /* Disarm and ack pending interrupts */
+ tmp = __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ tmp &= ~status;
+ __raw_writel(tmp, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ __raw_writel(status, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+}
+
+static void ssi_cleanup_gdd(struct hsi_controller *ssi, struct hsi_client *cl)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_msg *msg;
+ unsigned int i;
+ u32 val = 0;
+ u32 tmp;
+
+ for (i = 0; i < SSI_MAX_GDD_LCH; i++) {
+ msg = omap_ssi->gdd_trn[i].msg;
+ if ((!msg) || (msg->cl != cl))
+ continue;
+ __raw_writew(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i));
+ val |= (1 << i);
+ /*
+ * Clock references for write will be handled in
+ * ssi_cleanup_queues
+ */
+ if (msg->ttype == HSI_MSG_READ)
+ ssi_clk_disable(ssi);
+ omap_ssi->gdd_trn[i].msg = NULL;
+ }
+ tmp = __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ tmp &= ~val;
+ __raw_writel(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ __raw_writel(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+}
+
+static int ssi_release(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ spin_lock_bh(&omap_port->lock);
+ ssi_clk_enable(ssi);
+ /* Stop all the pending DMA requests for that client */
+ ssi_cleanup_gdd(ssi, cl);
+ /* Now cleanup all the queues */
+ ssi_cleanup_queues(cl);
+ ssi_clk_disable(ssi);
+ /* If it is the last client of the port, do extra checks and cleanup */
+ if (port->claimed <= 1) {
+ /*
+ * Drop the clock reference for the incoming wake line
+ * if it is still kept high by the other side.
+ */
+ if (omap_port->wkin_cken) {
+ ssi_clk_disable(ssi);
+ omap_port->wkin_cken = 0;
+ }
+ ssi_clk_enable(ssi);
+ /* Stop any SSI TX/RX without a client */
+ ssi_set_mode(ssi, SSI_MODE_SLEEP);
+ omap_port->sst.mode = SSI_MODE_SLEEP;
+ omap_port->ssr.mode = SSI_MODE_SLEEP;
+ ssi_clk_disable(ssi);
+ WARN_ON(omap_port->wk_refcount != 0);
+ WARN_ON(omap_ssi->ck_refcount != 0);
+ }
+ spin_unlock_bh(&omap_port->lock);
+
+ return 0;
+}
+
+static int ssi_flush(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_msg *msg;
+ void __iomem *sst = omap_port->sst_base;
+ void __iomem *ssr = omap_port->ssr_base;
+ unsigned int i;
+ u32 err;
+
+ ssi_clk_enable(ssi);
+ spin_lock_bh(&omap_port->lock);
+ /* Stop all DMA transfers */
+ for (i = 0; i < SSI_MAX_GDD_LCH; i++) {
+ msg = omap_ssi->gdd_trn[i].msg;
+ if (!msg || (port != hsi_get_port(msg->cl)))
+ continue;
+ __raw_writew(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i));
+ if (msg->ttype == HSI_MSG_READ)
+ ssi_clk_disable(ssi);
+ omap_ssi->gdd_trn[i].msg = NULL;
+ }
+ /* Flush all SST buffers */
+ __raw_writel(0, sst + SSI_SST_BUFSTATE_REG);
+ __raw_writel(0, sst + SSI_SST_TXSTATE_REG);
+ /* Flush all SSR buffers */
+ __raw_writel(0, ssr + SSI_SSR_RXSTATE_REG);
+ __raw_writel(0, ssr + SSI_SSR_BUFSTATE_REG);
+ /* Flush all errors */
+ err = __raw_readl(ssr + SSI_SSR_ERROR_REG);
+ __raw_writel(err, ssr + SSI_SSR_ERRORACK_REG);
+ /* Flush break */
+ __raw_writel(0, ssr + SSI_SSR_BREAK_REG);
+ /* Clear interrupts */
+ __raw_writel(0, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ __raw_writel(0xffffff00,
+ omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+ __raw_writel(0, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ __raw_writel(0xff, omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+ /* Dequeue all pending requests */
+ for (i = 0; i < omap_port->channels; i++) {
+ /* Release write clocks */
+ if (!list_empty(&omap_port->txqueue[i]))
+ ssi_clk_disable(ssi);
+ ssi_flush_queue(&omap_port->txqueue[i], NULL);
+ ssi_flush_queue(&omap_port->rxqueue[i], NULL);
+ }
+ ssi_flush_queue(&omap_port->brkqueue, NULL);
+ spin_unlock_bh(&omap_port->lock);
+ ssi_clk_disable(ssi);
+
+ return 0;
+}
+
+static int ssi_start_tx(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ dev_dbg(&port->device, "Wake out high %d\n", omap_port->wk_refcount);
+
+ spin_lock_bh(&omap_port->wk_lock);
+ if (omap_port->wk_refcount++) {
+ spin_unlock_bh(&omap_port->wk_lock);
+ return 0;
+ }
+ ssi_clk_enable(ssi); /* Grab clocks */
+ __raw_writel(SSI_WAKE(0), omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
+ spin_unlock_bh(&omap_port->wk_lock);
+
+ return 0;
+}
+
+static int ssi_stop_tx(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ dev_dbg(&port->device, "Wake out low %d\n", omap_port->wk_refcount);
+
+ spin_lock_bh(&omap_port->wk_lock);
+ BUG_ON(!omap_port->wk_refcount);
+ if (--omap_port->wk_refcount) {
+ spin_unlock_bh(&omap_port->wk_lock);
+ return 0;
+ }
+ __raw_writel(SSI_WAKE(0),
+ omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
+ ssi_clk_disable(ssi); /* Release clocks */
+ spin_unlock_bh(&omap_port->wk_lock);
+
+ return 0;
+}
+
+static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue)
+{
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct hsi_msg *msg;
+ u32 *buf;
+ u32 reg;
+ u32 val;
+
+ spin_lock(&omap_port->lock);
+ msg = list_first_entry(queue, struct hsi_msg, link);
+ if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) {
+ msg->actual_len = 0;
+ msg->status = HSI_STATUS_PENDING;
+ }
+ if (msg->ttype == HSI_MSG_WRITE)
+ val = SSI_DATAACCEPT(msg->channel);
+ else
+ val = SSI_DATAAVAILABLE(msg->channel);
+ if (msg->status == HSI_STATUS_PROCEEDING) {
+ buf = sg_virt(msg->sgt.sgl) + msg->actual_len;
+ if (msg->ttype == HSI_MSG_WRITE)
+ __raw_writel(*buf, omap_port->sst_base +
+ SSI_SST_BUFFER_CH_REG(msg->channel));
+ else
+ *buf = __raw_readl(omap_port->ssr_base +
+ SSI_SSR_BUFFER_CH_REG(msg->channel));
+ dev_dbg(&port->device, "ch %d ttype %d 0x%08x\n", msg->channel,
+ msg->ttype, *buf);
+ msg->actual_len += sizeof(*buf);
+ if (msg->actual_len >= msg->sgt.sgl->length)
+ msg->status = HSI_STATUS_COMPLETED;
+ /*
+ * Wait for the last written frame to be really sent before
+ * we call the complete callback
+ */
+ if ((msg->status == HSI_STATUS_PROCEEDING) ||
+ ((msg->status == HSI_STATUS_COMPLETED) &&
+ (msg->ttype == HSI_MSG_WRITE))) {
+ __raw_writel(val, omap_ssi->sys +
+ SSI_MPU_STATUS_REG(port->num, 0));
+ spin_unlock(&omap_port->lock);
+
+ return;
+ }
+
+ }
+ /* Transfer completed at this point */
+ reg = __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ if (msg->ttype == HSI_MSG_WRITE)
+ ssi_clk_disable(ssi); /* Release clocks for write transfer */
+ reg &= ~val;
+ __raw_writel(reg, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ __raw_writel(val, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
+ list_del(&msg->link);
+ spin_unlock(&omap_port->lock);
+ msg->complete(msg);
+ ssi_transfer(omap_port, queue);
+}
+
+static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct hsi_msg *msg = omap_ssi->gdd_trn[lch].msg;
+ struct hsi_port *port = to_hsi_port(msg->cl->device.parent);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ unsigned int dir;
+ u32 csr;
+ u32 val;
+
+ spin_lock(&omap_ssi->lock);
+
+ val = __raw_readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+ val &= ~SSI_GDD_LCH(lch);
+ __raw_writel(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
+
+ if (msg->ttype == HSI_MSG_READ) {
+ dir = DMA_FROM_DEVICE;
+ val = SSI_DATAAVAILABLE(msg->channel);
+ ssi_clk_disable(ssi);
+ } else {
+ dir = DMA_TO_DEVICE;
+ val = SSI_DATAACCEPT(msg->channel);
+ /* Keep clocks reference for write pio event */
+ }
+ dma_unmap_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, dir);
+ csr = __raw_readw(omap_ssi->gdd + SSI_GDD_CSR_REG(lch));
+ omap_ssi->gdd_trn[lch].msg = NULL; /* release GDD lch */
+ dev_dbg(&port->device, "DMA completed ch %d ttype %d\n",
+ msg->channel, msg->ttype);
+ spin_unlock(&omap_ssi->lock);
+ if (csr & SSI_CSR_TOUR) { /* Timeout error */
+ msg->status = HSI_STATUS_ERROR;
+ msg->actual_len = 0;
+ spin_lock(&omap_port->lock);
+ list_del(&msg->link); /* Dequeue msg */
+ spin_unlock(&omap_port->lock);
+ msg->complete(msg);
+ return;
+ }
+ spin_lock(&omap_port->lock);
+ val |= __raw_readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ __raw_writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ spin_unlock(&omap_port->lock);
+
+ msg->status = HSI_STATUS_COMPLETED;
+ msg->actual_len = sg_dma_len(msg->sgt.sgl);
+}
+
+static void ssi_gdd_tasklet(unsigned long dev)
+{
+ struct hsi_controller *ssi = (struct hsi_controller *)dev;
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *sys = omap_ssi->sys;
+ unsigned int lch;
+ u32 status_reg;
+
+ ssi_clk_enable(ssi);
+
+ status_reg = __raw_readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+ for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) {
+ if (status_reg & SSI_GDD_LCH(lch))
+ ssi_gdd_complete(ssi, lch);
+ }
+ __raw_writel(status_reg, sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+ status_reg = __raw_readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG);
+ ssi_clk_disable(ssi);
+ if (status_reg)
+ tasklet_hi_schedule(&omap_ssi->gdd_tasklet);
+ else
+ enable_irq(omap_ssi->gdd_irq);
+
+}
+
+static irqreturn_t ssi_gdd_isr(int irq, void *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ tasklet_hi_schedule(&omap_ssi->gdd_tasklet);
+ disable_irq_nosync(irq);
+
+ return IRQ_HANDLED;
+}
+
+static void ssi_pio_tasklet(unsigned long ssi_port)
+{
+ struct hsi_port *port = (struct hsi_port *)ssi_port;
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ void __iomem *sys = omap_ssi->sys;
+ unsigned int ch;
+ u32 status_reg;
+
+ ssi_clk_enable(ssi);
+ status_reg = __raw_readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+ status_reg &= __raw_readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
+
+ for (ch = 0; ch < omap_port->channels; ch++) {
+ if (status_reg & SSI_DATAACCEPT(ch))
+ ssi_pio_complete(port, &omap_port->txqueue[ch]);
+ if (status_reg & SSI_DATAAVAILABLE(ch))
+ ssi_pio_complete(port, &omap_port->rxqueue[ch]);
+ }
+ if (status_reg & SSI_BREAKDETECTED)
+ ssi_break_complete(port);
+ if (status_reg & SSI_ERROROCCURED)
+ ssi_error(port);
+
+ status_reg = __raw_readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+ status_reg &= __raw_readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
+ ssi_clk_disable(ssi);
+
+ if (status_reg)
+ tasklet_hi_schedule(&omap_port->pio_tasklet);
+ else
+ enable_irq(omap_port->irq);
+}
+
+static irqreturn_t ssi_pio_isr(int irq, void *port)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+
+ tasklet_hi_schedule(&omap_port->pio_tasklet);
+ disable_irq_nosync(irq);
+
+ return IRQ_HANDLED;
+}
+
+static void ssi_wake_tasklet(unsigned long ssi_port)
+{
+ struct hsi_port *port = (struct hsi_port *)ssi_port;
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+
+ if (ssi_wakein(port)) {
+ /**
+ * We can have a quick High-Low-High transition in the line.
+ * In such a case if we have long interrupt latencies,
+ * we can miss the low event or get twice a high event.
+ * This workaround will avoid breaking the clock reference
+ * count when such a situation ocurrs.
+ */
+ spin_lock(&omap_port->lock);
+ if (!omap_port->wkin_cken) {
+ omap_port->wkin_cken = 1;
+ ssi_clk_enable(ssi);
+ }
+ spin_unlock(&omap_port->lock);
+ dev_dbg(&ssi->device, "Wake in high\n");
+ hsi_event(port, HSI_EVENT_START_RX);
+ } else {
+ dev_dbg(&ssi->device, "Wake in low\n");
+ hsi_event(port, HSI_EVENT_STOP_RX);
+ spin_lock(&omap_port->lock);
+ if (omap_port->wkin_cken) {
+ ssi_clk_disable(ssi);
+ omap_port->wkin_cken = 0;
+ }
+ spin_unlock(&omap_port->lock);
+ }
+}
+
+static irqreturn_t ssi_wake_isr(int irq __maybe_unused, void *ssi_port)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(ssi_port);
+
+ tasklet_hi_schedule(&omap_port->wake_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static int __init ssi_port_irq(struct hsi_port *port,
+ struct platform_device *pd)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct resource *irq;
+ int err;
+
+ irq = platform_get_resource(pd, IORESOURCE_IRQ, (port->num * 3) + 1);
+ if (!irq) {
+ dev_err(&port->device, "Port IRQ resource missing\n");
+ return -ENXIO;
+ }
+ omap_port->irq = irq->start;
+ tasklet_init(&omap_port->pio_tasklet, ssi_pio_tasklet,
+ (unsigned long)port);
+ err = devm_request_irq(&pd->dev, omap_port->irq, ssi_pio_isr,
+ IRQF_DISABLED, irq->name, port);
+ if (err < 0)
+ dev_err(&port->device, "Request IRQ %d failed (%d)\n",
+ omap_port->irq, err);
+ return err;
+}
+
+static int __init ssi_wake_irq(struct hsi_port *port,
+ struct platform_device *pd)
+{
+ struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ struct resource *irq;
+ int err;
+
+ irq = platform_get_resource(pd, IORESOURCE_IRQ, (port->num * 3) + 3);
+ if (!irq) {
+ dev_err(&port->device, "Wake in IRQ resource missing");
+ return -ENXIO;
+ }
+ if (irq->flags & IORESOURCE_UNSET) {
+ dev_info(&port->device, "No Wake in support\n");
+ omap_port->wake_irq = -1;
+ return 0;
+ }
+ omap_port->wake_irq = irq->start;
+ tasklet_init(&omap_port->wake_tasklet, ssi_wake_tasklet,
+ (unsigned long)port);
+ err = devm_request_irq(&pd->dev, omap_port->wake_irq, ssi_wake_isr,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ irq->name, port);
+ if (err < 0)
+ dev_err(&port->device, "Request Wake in IRQ %d failed %d\n",
+ omap_port->wake_irq, err);
+ err = enable_irq_wake(omap_port->wake_irq);
+ if (err < 0)
+ dev_err(&port->device, "Enable wake on the wakeline in irq %d"
+ " failed %d\n", omap_port->wake_irq, err);
+
+ return err;
+}
+
+static void __init ssi_queues_init(struct omap_ssi_port *omap_port)
+{
+ unsigned int ch;
+
+ for (ch = 0; ch < SSI_MAX_CHANNELS; ch++) {
+ INIT_LIST_HEAD(&omap_port->txqueue[ch]);
+ INIT_LIST_HEAD(&omap_port->rxqueue[ch]);
+ }
+ INIT_LIST_HEAD(&omap_port->brkqueue);
+}
+
+static int __init ssi_get_iomem(struct platform_device *pd,
+ unsigned int num, void __iomem **pbase, dma_addr_t *phy)
+{
+ struct resource *mem;
+ struct resource *ioarea;
+ void __iomem *base;
+
+ mem = platform_get_resource(pd, IORESOURCE_MEM, num);
+ if (!mem) {
+ dev_err(&pd->dev, "IO memory region missing (%d)\n", num);
+ return -ENXIO;
+ }
+ ioarea = devm_request_mem_region(&pd->dev, mem->start,
+ resource_size(mem), dev_name(&pd->dev));
+ if (!ioarea) {
+ dev_err(&pd->dev, "%s IO memory region request failed\n",
+ mem->name);
+ return -ENXIO;
+ }
+ base = devm_ioremap(&pd->dev, mem->start, resource_size(mem));
+ if (!base) {
+ dev_err(&pd->dev, "%s IO remap failed\n", mem->name);
+ return -ENXIO;
+ }
+ *pbase = base;
+
+ if (phy)
+ *phy = mem->start;
+
+ return 0;
+}
+
+static int __init ssi_ports_init(struct hsi_controller *ssi,
+ struct platform_device *pd)
+{
+ struct hsi_port *port;
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ struct omap_ssi_port *omap_port;
+ unsigned int i;
+ int err;
+
+ omap_ssi->port = devm_kzalloc(&pd->dev,
+ sizeof(omap_port) * ssi->num_ports, GFP_KERNEL);
+ if (!omap_ssi->port)
+ return -ENOMEM;
+
+ for (i = 0; i < ssi->num_ports; i++) {
+ port = &ssi->port[i];
+ omap_port = devm_kzalloc(&pd->dev, sizeof(*omap_port),
+ GFP_KERNEL);
+ if (!omap_port)
+ return -ENOMEM;
+ port->async = ssi_async;
+ port->setup = ssi_setup;
+ port->flush = ssi_flush;
+ port->start_tx = ssi_start_tx;
+ port->stop_tx = ssi_stop_tx;
+ port->release = ssi_release;
+ hsi_port_set_drvdata(port, omap_port);
+ /* Get SST base addresses*/
+ err = ssi_get_iomem(pd, ((i * 2) + 2), &omap_port->sst_base,
+ &omap_port->sst_dma);
+ if (err < 0)
+ return err;
+ /* Get SSR base addresses */
+ err = ssi_get_iomem(pd, ((i * 2) + 3), &omap_port->ssr_base,
+ &omap_port->ssr_dma);
+ if (err < 0)
+ return err;
+ err = ssi_port_irq(port, pd);
+ if (err < 0)
+ return err;
+ err = ssi_wake_irq(port, pd);
+ if (err < 0)
+ return err;
+ ssi_queues_init(omap_port);
+ spin_lock_init(&omap_port->lock);
+ spin_lock_init(&omap_port->wk_lock);
+ omap_port->dev = &port->device;
+ omap_ssi->port[i] = omap_port;
+ }
+
+ return 0;
+}
+
+static void ssi_ports_exit(struct hsi_controller *ssi)
+{
+ struct omap_ssi_port *omap_port;
+ unsigned int i;
+
+ for (i = 0; i < ssi->num_ports; i++) {
+ omap_port = hsi_port_drvdata(&ssi->port[i]);
+ tasklet_kill(&omap_port->wake_tasklet);
+ tasklet_kill(&omap_port->pio_tasklet);
+ }
+}
+
+static void ssi_clk_release(struct device *dev __maybe_unused, void *res)
+{
+ struct ssi_clk_res *r = res;
+
+ clk_put(r->clk);
+}
+
+static struct clk *__init ssi_devm_clk_get(struct device *dev, const char *id)
+{
+ struct ssi_clk_res *pclk;
+ struct clk *clk;
+
+ pclk = devres_alloc(ssi_clk_release, sizeof(*pclk), GFP_KERNEL);
+ if (!pclk) {
+ dev_err(dev, "Could not allocate the device resource entry\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ clk = clk_get(dev, id);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "clock get %s failed %li\n", id, PTR_ERR(clk));
+ devres_free(pclk);
+ } else {
+ pclk->clk = clk;
+ devres_add(dev, pclk);
+ }
+
+ return clk;
+}
+
+static int __init ssi_add_controller(struct hsi_controller *ssi,
+ struct platform_device *pd)
+{
+ struct omap_ssi_platform_data *omap_ssi_pdata = pd->dev.platform_data;
+ struct omap_ssi_controller *omap_ssi;
+ struct resource *irq;
+ int err;
+
+ omap_ssi = devm_kzalloc(&pd->dev, sizeof(*omap_ssi), GFP_KERNEL);
+ if (!omap_ssi) {
+ dev_err(&pd->dev, "not enough memory for omap ssi\n");
+ return -ENOMEM;
+ }
+ ssi->id = pd->id;
+ ssi->owner = THIS_MODULE;
+ ssi->device.parent = &pd->dev;
+ dev_set_name(&ssi->device, "ssi%d", ssi->id);
+ hsi_controller_set_drvdata(ssi, omap_ssi);
+ omap_ssi->dev = &ssi->device;
+ err = ssi_get_iomem(pd, 0, &omap_ssi->sys, NULL);
+ if (err < 0)
+ return err;
+ err = ssi_get_iomem(pd, 1, &omap_ssi->gdd, NULL);
+ if (err < 0)
+ return err;
+ irq = platform_get_resource(pd, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pd->dev, "GDD IRQ resource missing\n");
+ return -ENXIO;
+ }
+ omap_ssi->gdd_irq = irq->start;
+ tasklet_init(&omap_ssi->gdd_tasklet, ssi_gdd_tasklet,
+ (unsigned long)ssi);
+ err = devm_request_irq(&pd->dev, omap_ssi->gdd_irq, ssi_gdd_isr,
+ IRQF_DISABLED, irq->name, ssi);
+ if (err < 0) {
+ dev_err(&ssi->device, "Request GDD IRQ %d failed (%d)",
+ omap_ssi->gdd_irq, err);
+ return err;
+ }
+ err = ssi_ports_init(ssi, pd);
+ if (err < 0)
+ return err;
+ omap_ssi->get_loss = omap_ssi_pdata->get_dev_context_loss_count;
+ omap_ssi->max_speed = UINT_MAX;
+ spin_lock_init(&omap_ssi->lock);
+ spin_lock_init(&omap_ssi->ck_lock);
+ omap_ssi->ick = ssi_devm_clk_get(&pd->dev, "ssi_ick");
+ if (IS_ERR(omap_ssi->ick))
+ return PTR_ERR(omap_ssi->ick);
+ omap_ssi->fck = ssi_devm_clk_get(&pd->dev, "ssi_ssr_fck");
+ if (IS_ERR(omap_ssi->fck))
+ return PTR_ERR(omap_ssi->fck);
+ err = hsi_register_controller(ssi);
+
+ return err;
+}
+
+static int __init ssi_hw_init(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+ unsigned int i;
+ u32 val;
+ int err;
+
+ err = ssi_clk_enable(ssi);
+ if (err < 0) {
+ dev_err(&ssi->device, "Failed to enable the clocks %d\n", err);
+ return err;
+ }
+ /* Reseting SSI controller */
+ __raw_writel(SSI_SOFTRESET, omap_ssi->sys + SSI_SYSCONFIG_REG);
+ val = __raw_readl(omap_ssi->sys + SSI_SYSSTATUS_REG);
+ for (i = 0; ((i < 20) && !(val & SSI_RESETDONE)); i++) {
+ msleep(20);
+ val = __raw_readl(omap_ssi->sys + SSI_SYSSTATUS_REG);
+ }
+ if (!(val & SSI_RESETDONE)) {
+ dev_err(&ssi->device, "SSI HW reset failed\n");
+ ssi_clk_disable(ssi);
+ return -EIO;
+ }
+ /* Reseting GDD */
+ __raw_writel(SSI_SWRESET, omap_ssi->gdd + SSI_GDD_GRST_REG);
+ /* Get FCK rate */
+ omap_ssi->fck_rate = clk_get_rate(omap_ssi->fck) / 1000; /* KHz */
+ dev_dbg(&ssi->device, "SSI fck rate %lu KHz\n", omap_ssi->fck_rate);
+ /* Set default PM settings */
+ val = SSI_AUTOIDLE | SSI_SIDLEMODE_SMART | SSI_MIDLEMODE_SMART;
+ __raw_writel(val, omap_ssi->sys + SSI_SYSCONFIG_REG);
+ omap_ssi->sysconfig = val;
+ __raw_writel(SSI_CLK_AUTOGATING_ON, omap_ssi->sys + SSI_GDD_GCR_REG);
+ omap_ssi->gdd_gcr = SSI_CLK_AUTOGATING_ON;
+ ssi_clk_disable(ssi);
+
+ return 0;
+}
+
+static void ssi_remove_controller(struct hsi_controller *ssi)
+{
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ ssi_ports_exit(ssi);
+ tasklet_kill(&omap_ssi->gdd_tasklet);
+ hsi_unregister_controller(ssi);
+}
+
+static int __init ssi_probe(struct platform_device *pd)
+{
+ struct omap_ssi_platform_data *omap_ssi_pdata = pd->dev.platform_data;
+ struct hsi_controller *ssi;
+ int err;
+
+ if (!omap_ssi_pdata) {
+ dev_err(&pd->dev, "No OMAP SSI platform data\n");
+ return -EINVAL;
+ }
+ ssi = hsi_alloc_controller(omap_ssi_pdata->num_ports, GFP_KERNEL);
+ if (!ssi) {
+ dev_err(&pd->dev, "No memory for controller\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pd, ssi);
+ err = ssi_add_controller(ssi, pd);
+ if (err < 0)
+ goto out1;
+ err = ssi_hw_init(ssi);
+ if (err < 0)
+ goto out2;
+#ifdef CONFIG_DEBUG_FS
+ err = ssi_debug_add_ctrl(ssi);
+ if (err < 0)
+ goto out2;
+#endif
+ return err;
+out2:
+ ssi_remove_controller(ssi);
+out1:
+ platform_set_drvdata(pd, NULL);
+ hsi_free_controller(ssi);
+
+ return err;
+}
+
+static int __exit ssi_remove(struct platform_device *pd)
+{
+ struct hsi_controller *ssi = platform_get_drvdata(pd);
+
+#ifdef CONFIG_DEBUG_FS
+ ssi_debug_remove_ctrl(ssi);
+#endif
+ ssi_remove_controller(ssi);
+ platform_set_drvdata(pd, NULL);
+ hsi_free_controller(ssi);
+
+ return 0;
+}
+
+static struct platform_driver ssi_pdriver = {
+ .remove = __exit_p(ssi_remove),
+ .driver = {
+ .name = "omap_ssi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap_ssi_init(void)
+{
+ pr_info("OMAP SSI hw driver loaded\n");
+ return platform_driver_probe(&ssi_pdriver, ssi_probe);
+}
+module_init(omap_ssi_init);
+
+static void __exit omap_ssi_exit(void)
+{
+ platform_driver_unregister(&ssi_pdriver);
+ pr_info("OMAP SSI driver removed\n");
+}
+module_exit(omap_ssi_exit);
+
+MODULE_ALIAS("platform:omap_ssi");
+MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>");
+MODULE_DESCRIPTION("Synchronous Serial Interface Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hsi/controllers/ste_hsi.c b/drivers/hsi/controllers/ste_hsi.c
new file mode 100644
index 00000000000..db9eb7aeb5c
--- /dev/null
+++ b/drivers/hsi/controllers/ste_hsi.c
@@ -0,0 +1,1536 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com> for ST-Ericsson
+ * Author: Lukasz Baj <lukasz.baj@tieto.com> for ST-Ericsson
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/hsi/hsi.h>
+
+#ifdef CONFIG_STE_DMA40
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#endif
+
+#include <mach/hsi.h>
+
+/**
+ * struct ste_hsi_controller - Nomadik HSI controller data
+ * @dev: device associated to the controller (HSI controller)
+ * @rx_base: HSI receiver registers base address
+ * @tx_base: HSI transmitter registers base address
+ */
+struct ste_hsi_controller {
+ struct device *dev;
+ struct clk *tx_clk;
+ struct clk *rx_clk;
+ struct clk *ssitx_clk;
+ struct clk *ssirx_clk;
+ struct delayed_work clk_work;
+ unsigned char __iomem *rx_base;
+ unsigned char __iomem *tx_base;
+ int overrun_irq[STE_HSI_MAX_CHANNELS];
+ int ck_refcount;
+ spinlock_t ck_lock;
+ spinlock_t lock;
+ unsigned int use_dma:1;
+ unsigned int ck_on:1;
+ /* physical address of rx and tx controller */
+ dma_addr_t rx_dma_base;
+ dma_addr_t tx_dma_base;
+};
+
+#ifdef CONFIG_STE_DMA40
+struct ste_hsi_channel_dma {
+ struct dma_chan *dma_chan;
+ struct dma_async_tx_descriptor *desc;
+ dma_cookie_t cookie;
+};
+#endif
+
+struct ste_hsi_port {
+ struct device *dev;
+ struct list_head txqueue[STE_HSI_MAX_CHANNELS];
+ struct list_head rxqueue[STE_HSI_MAX_CHANNELS];
+ struct list_head brkqueue;
+ int tx_irq;
+ int rx_irq;
+ int excep_irq;
+ struct tasklet_struct rx_tasklet;
+ struct tasklet_struct tx_tasklet;
+ struct tasklet_struct exception_tasklet;
+ struct tasklet_struct overrun_tasklet;
+ unsigned char channels;
+#ifdef CONFIG_STE_DMA40
+ struct ste_hsi_channel_dma tx_dma[STE_HSI_MAX_CHANNELS];
+ struct ste_hsi_channel_dma rx_dma[STE_HSI_MAX_CHANNELS];
+#endif
+};
+
+#define hsi_to_ste_port(port) (hsi_port_drvdata(port))
+#define hsi_to_ste_controller(con) (hsi_controller_drvdata(con))
+#define client_to_ste_port(cl) (hsi_port_drvdata(hsi_get_port(cl)))
+#define client_to_hsi(cl) \
+ (to_hsi_controller(hsi_get_port(cl)->device.parent))
+#define client_to_ste_controller(cl) \
+ (hsi_controller_drvdata(client_to_hsi(cl)))
+#define ste_port_to_ste_controller(port) \
+ ((struct ste_hsi_controller *)hsi_controller_drvdata( \
+ to_hsi_controller(port->dev->parent)))
+
+static u32 ste_hsir_periphid[8] = { 0x2C, 0, 0x8, 0x18, 0xD, 0xF0, 0x5, 0xB1 };
+static u32 ste_hsit_periphid[8] = { 0x2B, 0, 0x8, 0x18, 0xD, 0xF0, 0x5, 0xB1 };
+
+/*
+ * linux/amba/bus.h macros can not be used, because 8 bytes are validated:
+ * PERIPHID0..3 and PCELLID0..3 for HSIR and HSIT.
+ */
+static inline int compare_periphid(u32 *id1, u32 *id2, int count)
+{
+ while (count && *id1++ == *id2++)
+ count--;
+
+ return count;
+}
+
+static void ste_hsi_clk_free(struct clk **pclk)
+{
+ if (IS_ERR(*pclk) && *pclk != NULL)
+ clk_put(*pclk);
+ *pclk = NULL;
+}
+
+static void ste_hsi_clks_free(struct ste_hsi_controller *ste_hsi)
+{
+ ste_hsi_clk_free(&ste_hsi->rx_clk);
+ ste_hsi_clk_free(&ste_hsi->tx_clk);
+ ste_hsi_clk_free(&ste_hsi->ssirx_clk);
+ ste_hsi_clk_free(&ste_hsi->ssitx_clk);
+}
+
+static int ste_hsi_clock_enable(struct hsi_controller *hsi)
+{
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ int err = 0;
+
+ spin_lock_bh(&ste_hsi->ck_lock);
+ if (ste_hsi->ck_refcount++ || ste_hsi->ck_on)
+ goto out;
+
+ err = clk_enable(ste_hsi->ssirx_clk);
+ if (unlikely(err))
+ goto out;
+
+ err = clk_enable(ste_hsi->ssitx_clk);
+ if (unlikely(err))
+ clk_disable(ste_hsi->ssirx_clk);
+
+ err = clk_enable(ste_hsi->rx_clk);
+ if (unlikely(err)) {
+ clk_disable(ste_hsi->ssitx_clk);
+ clk_disable(ste_hsi->ssirx_clk);
+ }
+
+ err = clk_enable(ste_hsi->tx_clk);
+ if (unlikely(err)) {
+ clk_disable(ste_hsi->rx_clk);
+ clk_disable(ste_hsi->ssitx_clk);
+ clk_disable(ste_hsi->ssirx_clk);
+ }
+
+ ste_hsi->ck_on = 1;
+out:
+ if (err)
+ ste_hsi->ck_refcount--;
+
+ spin_unlock_bh(&ste_hsi->ck_lock);
+
+ return err;
+}
+
+static void ste_hsi_delayed_disable_clock(struct work_struct *work)
+{
+ struct ste_hsi_controller *ste_hsi;
+ ste_hsi = container_of(work, struct ste_hsi_controller, clk_work.work);
+
+ spin_lock_bh(&ste_hsi->ck_lock);
+
+ /*
+ * If clock should not be off (enable clock called in meantime)
+ * or clock is already off nothing to do
+ */
+ if (ste_hsi->ck_refcount || !ste_hsi->ck_on)
+ goto out;
+
+ if (readl(ste_hsi->tx_base + STE_HSI_TX_STATE) != STE_HSI_STATE_IDLE ||
+ readl(ste_hsi->rx_base + STE_HSI_RX_STATE)
+ != STE_HSI_STATE_IDLE ||
+ readl(ste_hsi->rx_base + STE_HSI_RX_BUFSTATE) != 0) {
+ /* Try again later */
+ int err = schedule_delayed_work(&ste_hsi->clk_work, HZ);
+ if (err < 0)
+ dev_err(ste_hsi->dev, "Error scheduling work\n");
+ goto out;
+ }
+
+ /* Actual clocks disable */
+ clk_disable(ste_hsi->tx_clk);
+ clk_disable(ste_hsi->rx_clk);
+ clk_disable(ste_hsi->ssitx_clk);
+ clk_disable(ste_hsi->ssirx_clk);
+ ste_hsi->ck_on = 0;
+
+out:
+ spin_unlock_bh(&ste_hsi->ck_lock);
+}
+
+static void ste_hsi_clock_disable(struct hsi_controller *hsi)
+{
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+
+ spin_lock_bh(&ste_hsi->ck_lock);
+
+ /* Sanity check */
+ if (ste_hsi->ck_refcount <= 0)
+ WARN_ON(ste_hsi->ck_refcount <= 0);
+
+ /* Need clock to be disable now? */
+ if (--ste_hsi->ck_refcount)
+ goto out;
+
+ /*
+ * If receiver or transmitter is in the middle something delay clock off
+ */
+ if (readl(ste_hsi->tx_base + STE_HSI_TX_STATE) != STE_HSI_STATE_IDLE ||
+ readl(ste_hsi->rx_base + STE_HSI_RX_STATE)
+ != STE_HSI_STATE_IDLE ||
+ readl(ste_hsi->rx_base + STE_HSI_RX_BUFSTATE) != 0) {
+ int err = schedule_delayed_work(&ste_hsi->clk_work, HZ);
+ if (err < 0)
+ dev_err(&hsi->device, "Error scheduling work\n");
+
+ goto out;
+ }
+
+ /* Actual clocks disabled */
+ clk_disable(ste_hsi->tx_clk);
+ clk_disable(ste_hsi->rx_clk);
+ clk_disable(ste_hsi->ssitx_clk);
+ clk_disable(ste_hsi->ssirx_clk);
+ ste_hsi->ck_on = 0;
+
+out:
+ spin_unlock_bh(&ste_hsi->ck_lock);
+}
+
+static int ste_hsi_start_irq(struct hsi_msg *msg)
+{
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct hsi_controller *hsi = to_hsi_controller(port->device.parent);
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ u32 val;
+ int err;
+
+ err = ste_hsi_clock_enable(hsi);
+ if (unlikely(err))
+ return err;
+
+ msg->actual_len = 0;
+ msg->status = HSI_STATUS_PROCEEDING;
+
+ if (msg->ttype == HSI_MSG_WRITE) {
+ val = readl(ste_hsi->tx_base + STE_HSI_TX_WATERMARKIM) |
+ (1 << msg->channel);
+ writel(val, ste_hsi->tx_base + STE_HSI_TX_WATERMARKIM);
+ } else {
+ val = readl(ste_hsi->rx_base + STE_HSI_RX_WATERMARKIM) |
+ (1 << msg->channel);
+ writel(val, ste_hsi->rx_base + STE_HSI_RX_WATERMARKIM);
+
+ val = readl(ste_hsi->rx_base + STE_HSI_RX_OVERRUNIM) |
+ (1 << msg->channel);
+ writel(val, ste_hsi->rx_base + STE_HSI_RX_OVERRUNIM);
+ }
+
+ return 0;
+}
+
+static int ste_hsi_start_transfer(struct ste_hsi_port *ste_port,
+ struct list_head *queue);
+#ifdef CONFIG_STE_DMA40
+static void ste_hsi_dma_callback(void *dma_async_param)
+{
+ struct hsi_msg *msg = dma_async_param;
+ struct hsi_controller *hsi = client_to_hsi(msg->cl);
+ struct ste_hsi_port *ste_port = client_to_ste_port(msg->cl);
+ struct ste_hsi_controller *ste_hsi = client_to_ste_controller(msg->cl);
+ struct list_head *queue;
+ struct dma_chan *chan;
+ struct ste_hsi_channel_dma *hsi_dma_chan;
+ char *dma_enable_address;
+ enum dma_data_direction direction;
+ u32 dma_mask;
+
+ /* Message finished, remove from list and notify client */
+ spin_lock_bh(&ste_hsi->lock);
+ list_del(&msg->link);
+
+ if (msg->ttype == HSI_MSG_WRITE) {
+ queue = &ste_port->txqueue[msg->channel];
+ direction = DMA_TO_DEVICE;
+ dma_enable_address = ste_hsi->tx_base + STE_HSI_TX_DMAEN;
+ hsi_dma_chan = &ste_port->tx_dma[msg->channel];
+ } else {
+ queue = &ste_port->rxqueue[msg->channel];
+ direction = DMA_FROM_DEVICE;
+ dma_enable_address = ste_hsi->rx_base + STE_HSI_RX_DMAEN;
+ hsi_dma_chan = &ste_port->rx_dma[msg->channel];
+ }
+
+ dma_sync_sg_for_cpu(&hsi->device, msg->sgt.sgl,
+ msg->sgt.nents, direction);
+ chan = hsi_dma_chan->dma_chan;
+
+ /* disable DMA channel on HSI controller */
+ dma_mask = readl(dma_enable_address);
+ writel(dma_mask & ~(1 << msg->channel), dma_enable_address);
+
+ hsi_dma_chan->desc = NULL;
+
+ dma_unmap_sg(&hsi->device, msg->sgt.sgl, msg->sgt.nents, direction);
+
+ msg->status = HSI_STATUS_COMPLETED;
+ msg->actual_len = sg_dma_len(msg->sgt.sgl);
+
+ spin_unlock_bh(&ste_hsi->lock);
+
+ msg->complete(msg);
+
+ ste_hsi_clock_disable(hsi);
+
+ spin_lock_bh(&ste_hsi->lock);
+ ste_hsi_start_transfer(ste_port, queue);
+ spin_unlock_bh(&ste_hsi->lock);
+}
+
+static void dma_device_control(struct ste_hsi_channel_dma *chan,
+ enum dma_ctrl_cmd cmd, unsigned long arg)
+{
+ chan->dma_chan->device->device_control(chan->dma_chan, cmd, arg);
+}
+
+static void ste_hsi_terminate_dma_chan(struct ste_hsi_channel_dma *chan)
+{
+ if (chan->desc) {
+ dma_device_control(chan, DMA_TERMINATE_ALL, 0);
+ chan->desc = NULL;
+ }
+ chan->cookie = 0;
+}
+
+static void ste_hsi_terminate_dma(struct ste_hsi_port *ste_port)
+{
+ int i;
+
+ for (i = 0; i < ste_port->channels; ++i) {
+ ste_hsi_terminate_dma_chan(&ste_port->tx_dma[i]);
+ ste_hsi_terminate_dma_chan(&ste_port->rx_dma[i]);
+ }
+}
+
+static int ste_hsi_start_dma(struct hsi_msg *msg)
+{
+ struct hsi_controller *hsi = client_to_hsi(msg->cl);
+ struct ste_hsi_port *ste_port = client_to_ste_port(msg->cl);
+ struct ste_hsi_controller *ste_hsi = client_to_ste_controller(msg->cl);
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *chan;
+ struct ste_hsi_channel_dma *hsi_dma_chan;
+ char *dma_enable_address;
+ enum dma_data_direction direction;
+ u32 dma_mask;
+ int err;
+
+ err = ste_hsi_clock_enable(hsi);
+ if (unlikely(err))
+ return err;
+
+ if (msg->ttype == HSI_MSG_WRITE) {
+ direction = DMA_TO_DEVICE;
+ dma_enable_address = ste_hsi->tx_base + STE_HSI_TX_DMAEN;
+ hsi_dma_chan = &ste_port->tx_dma[msg->channel];
+ } else {
+ u32 val;
+ direction = DMA_FROM_DEVICE;
+ dma_enable_address = ste_hsi->rx_base + STE_HSI_RX_DMAEN;
+ hsi_dma_chan = &ste_port->rx_dma[msg->channel];
+
+ /* enable overrun for this channel */
+ val = readl(ste_hsi->rx_base + STE_HSI_RX_OVERRUNIM) |
+ (1 << msg->channel);
+ writel(val, ste_hsi->rx_base + STE_HSI_RX_OVERRUNIM);
+ }
+
+ chan = hsi_dma_chan->dma_chan;
+
+ if (0 == dma_map_sg(&hsi->device, msg->sgt.sgl, msg->sgt.nents,
+ direction)) {
+ dev_dbg(&hsi->device, "DMA map SG failed !\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ /* Prepare the scatterlist */
+ desc = chan->device->device_prep_slave_sg(chan,
+ msg->sgt.sgl,
+ msg->sgt.nents,
+ direction,
+ DMA_PREP_INTERRUPT |
+ DMA_CTRL_ACK);
+
+ if (!desc) {
+ dma_unmap_sg(&hsi->device, msg->sgt.sgl, msg->sgt.nents,
+ direction);
+ /* "Complete" DMA (errorpath) */
+ ste_hsi_terminate_dma_chan(hsi_dma_chan);
+ err = -EBUSY;
+ goto out;
+ }
+ desc->callback = ste_hsi_dma_callback;
+ desc->callback_param = msg;
+ hsi_dma_chan->cookie = desc->tx_submit(desc);
+ hsi_dma_chan->desc = desc;
+
+ /* Fire the DMA transaction */
+ chan->device->device_issue_pending(chan);
+
+ /* Enable DMA channel on HSI controller */
+ dma_mask = readl(dma_enable_address);
+ writel(dma_mask | 1 << msg->channel, dma_enable_address);
+
+out:
+ if (unlikely(err))
+ ste_hsi_clock_disable(hsi);
+
+ return err;
+}
+
+static void __init ste_hsi_init_dma(struct ste_hsi_platform_data *data,
+ struct hsi_controller *hsi)
+{
+ struct hsi_port *port;
+ struct ste_hsi_port *ste_port;
+ struct ste_hsi_controller *ste_hsi = hsi_to_ste_controller(hsi);
+ dma_cap_mask_t mask;
+ int i, ch;
+
+ ste_hsi->use_dma = 1;
+ /* Try to acquire a generic DMA engine slave channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ for (i = 0; i < hsi->num_ports; ++i) {
+ port = &hsi->port[i];
+ ste_port = hsi_port_drvdata(port);
+
+ for (ch = 0; ch < STE_HSI_MAX_CHANNELS; ++ch) {
+ ste_port->tx_dma[ch].dma_chan =
+ dma_request_channel(mask,
+ data->port_cfg[i].dma_filter,
+ &data->port_cfg[i].
+ dma_tx_cfg[ch]);
+
+ ste_port->rx_dma[ch].dma_chan =
+ dma_request_channel(mask,
+ data->port_cfg[i].dma_filter,
+ &data->port_cfg[i].
+ dma_rx_cfg[ch]);
+ }
+ }
+}
+
+static int ste_hsi_setup_dma(struct hsi_client *cl)
+{
+ int i;
+ struct hsi_port *port = to_hsi_port(cl->device.parent);
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+ struct hsi_controller *hsi = to_hsi_controller(port->device.parent);
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ struct dma_slave_config rx_conf = {
+ .src_addr = 0, /* dynamic data */
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .direction = DMA_FROM_DEVICE,
+ .src_maxburst = 1,
+ };
+ struct dma_slave_config tx_conf = {
+ .dst_addr = 0, /* dynamic data */
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .direction = DMA_TO_DEVICE,
+ .dst_maxburst = 1,
+ };
+
+ if (!ste_hsi->use_dma)
+ return 0;
+
+ for (i = 0; i < ste_port->channels; ++i) {
+ struct dma_chan *chan;
+
+ chan = ste_port->tx_dma[i].dma_chan;
+ tx_conf.dst_addr = (dma_addr_t) ste_hsi->tx_dma_base +
+ STE_HSI_TX_BUFFERX + 4 * i;
+ chan->device->device_control(chan,
+ DMA_SLAVE_CONFIG,
+ (unsigned long)&tx_conf);
+
+ chan = ste_port->rx_dma[i].dma_chan;
+ rx_conf.src_addr = (dma_addr_t) ste_hsi->rx_dma_base +
+ STE_HSI_RX_BUFFERX + 4 * i;
+ chan->device->device_control(chan,
+ DMA_SLAVE_CONFIG,
+ (unsigned long)&rx_conf);
+ }
+
+ return 0;
+}
+
+#else
+#define ste_hsi_init_dma(data, hsi) do { } while (0)
+#define ste_hsi_start_dma ste_hsi_start_irq
+#define ste_hsi_terminate_dma(ste_port) do { } while (0)
+#define ste_hsi_setup_dma(cl) do { } while (0)
+#endif
+
+static int ste_hsi_start_transfer(struct ste_hsi_port *ste_port,
+ struct list_head *queue)
+{
+ struct hsi_msg *msg;
+ int err;
+
+ if (list_empty(queue))
+ return 0;
+
+ msg = list_first_entry(queue, struct hsi_msg, link);
+ if (msg->status != HSI_STATUS_QUEUED)
+ return 0;
+
+ msg->actual_len = 0;
+ msg->status = HSI_STATUS_PROCEEDING;
+
+ if (ste_port_to_ste_controller(ste_port)->use_dma)
+ err = ste_hsi_start_dma(msg);
+ else
+ err = ste_hsi_start_irq(msg);
+
+ return err;
+}
+
+static void ste_hsi_receive_data(struct hsi_port *port, unsigned int channel)
+{
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+ struct hsi_controller *hsi = to_hsi_controller(port->device.parent);
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ struct list_head *queue = &ste_port->rxqueue[channel];
+ struct hsi_msg *msg;
+ char *bufferx;
+ u8 *buf;
+ int span;
+
+ spin_lock_bh(&ste_hsi->lock);
+
+ if (list_empty(queue))
+ goto out;
+
+ msg = list_first_entry(queue, struct hsi_msg, link);
+ if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) {
+ msg->actual_len = 0;
+ msg->status = HSI_STATUS_PENDING;
+ }
+
+ if (msg->status == HSI_STATUS_PROCEEDING && msg->ttype == HSI_MSG_READ) {
+ unsigned char len;
+ bufferx = ste_hsi->rx_base + STE_HSI_RX_BUFFERX + 4 * channel;
+
+ len = readl(ste_hsi->rx_base + STE_HSI_RX_GAUGEX + 4 * channel);
+ buf = sg_virt(msg->sgt.sgl);
+ buf += msg->actual_len;
+ while (len--) {
+ *(u32 *) buf = readl(bufferx);
+ buf += 4;
+ msg->actual_len += 4;
+ if (msg->actual_len >= msg->sgt.sgl->length) {
+ msg->status = HSI_STATUS_COMPLETED;
+ break;
+ }
+ }
+ }
+
+ /* re-enable interrupt by watermark manipulation */
+ span = readl(ste_hsi->rx_base + STE_HSI_RX_SPANX + 4 * channel);
+ writel(span, ste_hsi->rx_base + STE_HSI_RX_WATERMARKX + 4 * channel);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_WATERMARKX + 4 * channel);
+
+ /*
+ * If message was not transmitted completely enable interrupt for
+ * further work
+ */
+ if (msg->status == HSI_STATUS_PROCEEDING) {
+ u32 val;
+ val = readl(ste_hsi->rx_base + STE_HSI_RX_WATERMARKIM) |
+ (1 << channel);
+ writel(val, ste_hsi->rx_base + STE_HSI_RX_WATERMARKIM);
+ goto out;
+ }
+
+ /* Message finished, remove from list and notify client */
+ list_del(&msg->link);
+ spin_unlock_bh(&ste_hsi->lock);
+ msg->complete(msg);
+
+ ste_hsi_clock_disable(hsi);
+
+ spin_lock_bh(&ste_hsi->lock);
+
+ ste_hsi_start_transfer(ste_port, queue);
+out:
+ spin_unlock_bh(&ste_hsi->lock);
+}
+
+static void ste_hsi_transmit_data(struct hsi_port *port, unsigned int channel)
+{
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+ struct hsi_controller *hsi = to_hsi_controller(port->device.parent);
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ struct list_head *queue = &ste_port->txqueue[channel];
+ struct hsi_msg *msg;
+ u8 *buf;
+ int span;
+
+ if (list_empty(queue))
+ return;
+
+ spin_lock_bh(&ste_hsi->lock);
+ msg = list_first_entry(queue, struct hsi_msg, link);
+ if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) {
+ msg->actual_len = 0;
+ msg->status = HSI_STATUS_PENDING;
+ }
+
+ if (msg->status == HSI_STATUS_PROCEEDING &&
+ msg->ttype == HSI_MSG_WRITE) {
+ unsigned char free_space;
+
+ free_space = readl(ste_hsi->tx_base +
+ STE_HSI_TX_GAUGEX + 4 * channel);
+ buf = sg_virt(msg->sgt.sgl);
+ buf += msg->actual_len;
+ while (free_space--) {
+ writel(*(u32 *) buf, ste_hsi->tx_base +
+ STE_HSI_TX_BUFFERX + 4 * channel);
+ buf += 4;
+ msg->actual_len += 4;
+ if (msg->actual_len >= msg->sgt.sgl->length) {
+ msg->status = HSI_STATUS_COMPLETED;
+ break;
+ }
+ }
+ }
+
+ span = readl(ste_hsi->tx_base + STE_HSI_TX_SPANX + 4 * channel);
+ writel(span, ste_hsi->tx_base + STE_HSI_TX_WATERMARKX + 4 * channel);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_WATERMARKX + 4 * channel);
+
+ if (msg->status == HSI_STATUS_PROCEEDING) {
+ u32 val;
+ val = readl(ste_hsi->tx_base + STE_HSI_TX_WATERMARKIM) |
+ (1 << channel);
+ writel(val, ste_hsi->tx_base + STE_HSI_TX_WATERMARKIM);
+ goto out;
+ }
+
+ list_del(&msg->link);
+ spin_unlock_bh(&ste_hsi->lock);
+ msg->complete(msg);
+
+ ste_hsi_clock_disable(hsi);
+
+ spin_lock_bh(&ste_hsi->lock);
+ ste_hsi_start_transfer(ste_port, queue);
+out:
+ spin_unlock_bh(&ste_hsi->lock);
+}
+
+static void ste_hsi_rx_tasklet(unsigned long data)
+{
+ struct hsi_port *port = (struct hsi_port *)data;
+ struct hsi_controller *hsi = to_hsi_controller(port->device.parent);
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+ u32 irq_status, irq_mask;
+ unsigned int i;
+
+ irq_status = readl(ste_hsi->rx_base + STE_HSI_RX_WATERMARKIS);
+ if (!irq_status)
+ goto out;
+
+ irq_mask = readl(ste_hsi->rx_base + STE_HSI_RX_WATERMARKIM);
+ writel(irq_mask & ~irq_status,
+ ste_hsi->rx_base + STE_HSI_RX_WATERMARKIM);
+ writel(irq_mask, ste_hsi->rx_base + STE_HSI_RX_WATERMARKIC);
+
+ for (i = 0; i < STE_HSI_MAX_CHANNELS; ++i) {
+ if (irq_status & (1 << i))
+ ste_hsi_receive_data(port, i);
+ }
+out:
+ enable_irq(ste_port->rx_irq);
+}
+
+static irqreturn_t ste_hsi_rx_isr(int irq, void *data)
+{
+ struct hsi_port *port = data;
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+
+ disable_irq_nosync(irq);
+ tasklet_hi_schedule(&ste_port->rx_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ste_hsi_tx_isr(int irq, void *data)
+{
+ struct hsi_port *port = data;
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+
+ disable_irq_nosync(irq);
+ tasklet_hi_schedule(&ste_port->tx_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static void ste_hsi_tx_tasklet(unsigned long data)
+{
+ struct hsi_port *port = (struct hsi_port *)data;
+ struct hsi_controller *hsi = to_hsi_controller(port->device.parent);
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+ u32 irq_status, irq_mask;
+ unsigned int i;
+
+ irq_status = readl(ste_hsi->tx_base + STE_HSI_TX_WATERMARKIS);
+ if (!irq_status)
+ goto out;
+
+ irq_mask = readl(ste_hsi->tx_base + STE_HSI_TX_WATERMARKIM);
+ writel(irq_mask & ~irq_status,
+ ste_hsi->tx_base + STE_HSI_TX_WATERMARKIM);
+ writel(irq_mask, ste_hsi->tx_base + STE_HSI_TX_WATERMARKIC);
+
+ for (i = 0; i < STE_HSI_MAX_CHANNELS; ++i) {
+ if (irq_status & (1 << i))
+ ste_hsi_transmit_data(port, i);
+ }
+out:
+ enable_irq(ste_port->tx_irq);
+}
+
+static void ste_hsi_break_complete(struct hsi_port *port,
+ struct ste_hsi_controller *ste_hsi)
+{
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+ struct hsi_msg *msg, *tmp;
+ u32 mask;
+
+ dev_dbg(port->device.parent, "HWBREAK received\n");
+
+ spin_lock_bh(&ste_hsi->lock);
+
+ mask = readl(ste_hsi->rx_base + STE_HSI_RX_EXCEPIM);
+ writel(mask & ~STE_HSI_EXCEP_BREAK,
+ ste_hsi->rx_base + STE_HSI_RX_EXCEPIM);
+
+ spin_unlock_bh(&ste_hsi->lock);
+
+ list_for_each_entry_safe(msg, tmp, &ste_port->brkqueue, link) {
+ msg->status = HSI_STATUS_COMPLETED;
+ list_del(&msg->link);
+ msg->complete(msg);
+ }
+}
+
+static void ste_hsi_error(struct hsi_port *port)
+{
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+ struct hsi_msg *msg;
+ unsigned int i;
+
+ for (i = 0; i < ste_port->channels; i++) {
+ if (list_empty(&ste_port->rxqueue[i]))
+ continue;
+ msg = list_first_entry(&ste_port->rxqueue[i], struct hsi_msg,
+ link);
+ list_del(&msg->link);
+ msg->status = HSI_STATUS_ERROR;
+ msg->complete(msg);
+ /* Now restart queued reads if any */
+ ste_hsi_start_transfer(ste_port, &ste_port->rxqueue[i]);
+ }
+}
+
+static void ste_hsi_exception_tasklet(unsigned long data)
+{
+ struct hsi_port *port = (struct hsi_port *)data;
+ struct hsi_controller *hsi = to_hsi_controller(port->device.parent);
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+ u32 error_status;
+ u32 error_interrupts;
+
+ error_status = readl(ste_hsi->rx_base + STE_HSI_RX_EXCEP);
+ /*
+ * sometimes interrupt that cause running this tasklet is already
+ * inactive so base handling of exception on masked interrupt status
+ * not on exception state register.
+ */
+ error_interrupts = readl(ste_hsi->rx_base + STE_HSI_RX_EXCEPMIS);
+
+ if (error_interrupts & STE_HSI_EXCEP_BREAK)
+ ste_hsi_break_complete(port, ste_hsi);
+
+ if (error_interrupts & STE_HSI_EXCEP_TIMEOUT)
+ dev_err(&hsi->device, "timeout exception occurred\n");
+ if (error_interrupts & STE_HSI_EXCEP_OVERRUN)
+ dev_err(&hsi->device, "overrun exception occurred\n");
+ if (error_interrupts & STE_HSI_EXCEP_PARITY)
+ dev_err(&hsi->device, "parity exception occurred\n");
+
+ if (error_interrupts & ~STE_HSI_EXCEP_BREAK)
+ ste_hsi_error(port);
+
+ /* Acknowledge exception interrupts */
+ writel(error_status, ste_hsi->rx_base + STE_HSI_RX_ACK);
+
+ enable_irq(ste_port->excep_irq);
+}
+
+static irqreturn_t ste_hsi_exception_isr(int irq, void *data)
+{
+ struct hsi_port *port = data;
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+
+ disable_irq_nosync(irq);
+ tasklet_hi_schedule(&ste_port->exception_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static void ste_hsi_overrun_tasklet(unsigned long data)
+{
+ struct hsi_controller *hsi = (struct hsi_controller *)data;
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ struct hsi_port *hsi_port = &hsi->port[0];
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(hsi_port);
+ struct hsi_msg *msg;
+
+ unsigned int channel;
+ u8 rised_overrun;
+ u8 mask;
+ u8 blocked = 0;
+
+ rised_overrun = (u8) readl(ste_hsi->rx_base + STE_HSI_RX_OVERRUNMIS);
+ mask = rised_overrun;
+ for (channel = 0; mask; ++channel, mask >>= 1) {
+ if (!(mask & 1))
+ continue;
+
+ do {
+ /*
+ * No more messages, block interrupt
+ */
+ if (list_empty(&ste_port->rxqueue[channel])) {
+ blocked |= 1 << channel;
+ break;
+ }
+ /*
+ * Complete with error
+ */
+ msg = list_first_entry(&ste_port->rxqueue[channel],
+ struct hsi_msg, link);
+ list_del(&msg->link);
+ msg->status = HSI_STATUS_ERROR;
+ msg->complete(msg);
+
+ /*
+ * Now restart queued reads if any
+ * If start_transfer fails, try with next message
+ */
+ if (ste_hsi_start_transfer(ste_port,
+ &ste_port->rxqueue[channel]))
+ continue;
+ } while (0);
+ }
+
+ /* Overrun acknowledge */
+ writel(rised_overrun, ste_hsi->rx_base + STE_HSI_RX_OVERRUNACK);
+ writel(~blocked & readl(ste_hsi->rx_base + STE_HSI_RX_OVERRUNIM),
+ ste_hsi->rx_base + STE_HSI_RX_OVERRUNIM);
+
+ /*
+ * Enable all that should not be blocked
+ */
+ mask = rised_overrun & ~blocked;
+ for (channel = 0; mask; ++channel, mask >>= 1)
+ enable_irq(ste_hsi->overrun_irq[channel]);
+}
+
+static irqreturn_t ste_hsi_overrun_isr(int irq, void *data)
+{
+ struct hsi_port *port = data;
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+
+ disable_irq_nosync(irq);
+ tasklet_hi_schedule(&ste_port->overrun_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static void __init ste_hsi_queues_init(struct ste_hsi_port *ste_port)
+{
+ unsigned int ch;
+
+ for (ch = 0; ch < STE_HSI_MAX_CHANNELS; ch++) {
+ INIT_LIST_HEAD(&ste_port->txqueue[ch]);
+ INIT_LIST_HEAD(&ste_port->rxqueue[ch]);
+ }
+ INIT_LIST_HEAD(&ste_port->brkqueue);
+}
+
+static int __init ste_hsi_get_iomem(struct platform_device *pdev,
+ const char *res_name,
+ unsigned char __iomem **base,
+ dma_addr_t *phy)
+{
+ struct resource *mem;
+ struct resource *ioarea;
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
+ if (!mem) {
+ dev_err(&pdev->dev, "IO memory region missing!\n");
+ return -ENXIO;
+ }
+
+ ioarea = devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem),
+ dev_name(&pdev->dev));
+ if (!ioarea) {
+ dev_err(&pdev->dev, "Can't request IO memory region!\n");
+ return -ENXIO;
+ }
+
+ *base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!base) {
+ dev_err(&pdev->dev, "%s IO remap failed!\n", mem->name);
+ return -ENXIO;
+ }
+ if (phy)
+ *phy = (dma_addr_t) mem->start;
+
+ return 0;
+}
+
+static int __init ste_hsi_get_irq(struct platform_device *pdev,
+ const char *res_name,
+ irqreturn_t(*isr) (int, void *), void *data,
+ int *irq_number)
+{
+ struct resource *irq;
+ int err;
+
+ irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
+ if (!irq) {
+ dev_err(&pdev->dev, "IO memory region missing!\n");
+ return -ENXIO;
+ }
+
+ err = devm_request_irq(&pdev->dev, irq->start, isr,
+ IRQF_DISABLED, irq->name, data);
+ if (err)
+ dev_err(&pdev->dev, "%s IRQ request failed!\n", irq->name);
+
+ if (irq_number)
+ *irq_number = irq->start;
+
+ return err;
+}
+
+static void ste_hsi_flush_queue(struct list_head *queue, struct hsi_client *cl)
+{
+ struct list_head *node, *tmp;
+ struct hsi_msg *msg;
+
+ list_for_each_safe(node, tmp, queue) {
+ msg = list_entry(node, struct hsi_msg, link);
+ if ((cl) && (cl != msg->cl))
+ continue;
+ list_del(node);
+
+ if (msg->destructor)
+ msg->destructor(msg);
+ else
+ hsi_free_msg(msg);
+ }
+}
+
+static int ste_hsi_async_break(struct hsi_msg *msg)
+{
+ struct hsi_port *port = hsi_get_port(msg->cl);
+ struct ste_hsi_port *ste_port = hsi_to_ste_port(port);
+ struct hsi_controller *hsi = to_hsi_controller(port->device.parent);
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ int err;
+
+ err = ste_hsi_clock_enable(hsi);
+ if (unlikely(err))
+ return err;
+
+ if (msg->ttype == HSI_MSG_WRITE) {
+ if (port->tx_cfg.mode != HSI_MODE_FRAME) {
+ err = -EINVAL;
+ goto out;
+ }
+ writel(1, ste_hsi->tx_base + STE_HSI_TX_BREAK);
+ msg->status = HSI_STATUS_COMPLETED;
+ msg->complete(msg);
+ } else {
+ u32 mask;
+ if (port->rx_cfg.mode != HSI_MODE_FRAME) {
+ err = -EINVAL;
+ goto out;
+ }
+ spin_lock_bh(&ste_hsi->lock);
+ msg->status = HSI_STATUS_PROCEEDING;
+ mask = readl(ste_hsi->rx_base + STE_HSI_RX_EXCEPIM);
+ /* Enable break exception on controller */
+ if (!(mask & STE_HSI_EXCEP_BREAK))
+ writel(mask | STE_HSI_EXCEP_BREAK,
+ ste_hsi->rx_base + STE_HSI_RX_EXCEPIM);
+
+ list_add_tail(&msg->link, &ste_port->brkqueue);
+ spin_unlock_bh(&ste_hsi->lock);
+ }
+
+out:
+ ste_hsi_clock_disable(hsi);
+ return err;
+}
+
+static int ste_hsi_async(struct hsi_msg *msg)
+{
+ struct ste_hsi_controller *ste_hsi;
+ struct ste_hsi_port *ste_port;
+ struct list_head *queue;
+ int err = 0;
+
+ if (unlikely(!msg))
+ return -ENOSYS;
+
+ if (msg->sgt.nents > 1)
+ return -ENOSYS;
+
+ if (unlikely(msg->break_frame))
+ return ste_hsi_async_break(msg);
+
+ ste_port = client_to_ste_port(msg->cl);
+ ste_hsi = client_to_ste_controller(msg->cl);
+
+ if (msg->ttype == HSI_MSG_WRITE) {
+ /* TX transfer */
+ BUG_ON(msg->channel >= ste_port->channels);
+ queue = &ste_port->txqueue[msg->channel];
+ } else {
+ /* RX transfer */
+ queue = &ste_port->rxqueue[msg->channel];
+ }
+
+ spin_lock_bh(&ste_hsi->lock);
+ list_add_tail(&msg->link, queue);
+ msg->status = HSI_STATUS_QUEUED;
+
+ err = ste_hsi_start_transfer(ste_port, queue);
+ if (err)
+ list_del(&msg->link);
+
+ spin_unlock_bh(&ste_hsi->lock);
+
+ return err;
+}
+
+static int ste_hsi_setup(struct hsi_client *cl)
+{
+ struct hsi_port *port = to_hsi_port(cl->device.parent);
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+ struct hsi_controller *hsi = to_hsi_controller(port->device.parent);
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ int err, i, buffers;
+ u32 div = 0;
+
+ err = ste_hsi_clock_enable(hsi);
+ if (unlikely(err))
+ return err;
+
+ if (cl->tx_cfg.speed) {
+ div = clk_get_rate(ste_hsi->tx_clk) / 1000 / cl->tx_cfg.speed;
+ if (div)
+ --div;
+ }
+
+ port->tx_cfg = cl->tx_cfg;
+ port->rx_cfg = cl->rx_cfg;
+ /* Configure TX */
+ writel(cl->tx_cfg.mode, ste_hsi->tx_base + STE_HSI_TX_MODE);
+ writel(div, ste_hsi->tx_base + STE_HSI_TX_DIVISOR);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_PARITY);
+ /* TODO: Wait for idle here */
+ writel(cl->tx_cfg.channels, ste_hsi->tx_base + STE_HSI_TX_CHANNELS);
+ /* Calculate buffers number per channel */
+ buffers = STE_HSI_MAX_BUFFERS / cl->tx_cfg.channels;
+ for (i = 0; i < cl->tx_cfg.channels; i++) {
+ /* Set 32 bit long frames */
+ writel(31, ste_hsi->tx_base + STE_HSI_TX_FRAMELENX + 4 * i);
+ writel(buffers * i,
+ ste_hsi->tx_base + STE_HSI_TX_BASEX + 4 * i);
+ writel(buffers - 1,
+ ste_hsi->tx_base + STE_HSI_TX_SPANX + 4 * i);
+ writel(buffers - 1,
+ ste_hsi->tx_base + STE_HSI_TX_WATERMARKX + 4 * i);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_WATERMARKX + 4 * i);
+ }
+
+ /* Configure RX */
+ writel(cl->rx_cfg.mode, ste_hsi->rx_base + STE_HSI_RX_MODE);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_PARITY);
+ writel(cl->rx_cfg.channels, ste_hsi->rx_base + STE_HSI_RX_CHANNELS);
+ /* Calculate buffers number per channel */
+ buffers = STE_HSI_MAX_BUFFERS / cl->rx_cfg.channels;
+ for (i = 0; i < cl->rx_cfg.channels; i++) {
+ /* Set 32 bit long frames */
+ writel(31, ste_hsi->rx_base + STE_HSI_RX_FRAMELENX + 4 * i);
+ writel(buffers * i,
+ ste_hsi->rx_base + STE_HSI_RX_BASEX + 4 * i);
+ writel(buffers - 1,
+ ste_hsi->rx_base + STE_HSI_RX_SPANX + 4 * i);
+ writel(buffers - 1,
+ ste_hsi->rx_base + STE_HSI_RX_WATERMARKX + 4 * i);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_WATERMARKX + 4 * i);
+ }
+
+ ste_port->channels = max(cl->tx_cfg.channels, cl->rx_cfg.channels);
+
+ ste_hsi_setup_dma(cl);
+
+ ste_hsi_clock_disable(hsi);
+ return err;
+}
+
+static int ste_hsi_flush(struct hsi_client *cl)
+{
+ struct hsi_port *port = to_hsi_port(cl->device.parent);
+ struct ste_hsi_port *ste_port = hsi_port_drvdata(port);
+ struct hsi_controller *hsi = to_hsi_controller(port->device.parent);
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ int i;
+
+ ste_hsi_clock_enable(hsi);
+
+ /* Enter sleep mode */
+ writel(STE_HSI_MODE_SLEEP, ste_hsi->rx_base + STE_HSI_RX_MODE);
+
+ /* Disable DMA, and terminate all outstanding jobs */
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_DMAEN);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_DMAEN);
+ ste_hsi_terminate_dma(ste_port);
+
+ /* Flush all HSIR and HSIT buffers */
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_STATE);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_BUFSTATE);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_STATE);
+ /*
+ * BUFSTATE is cleared twice on purpose:
+ * first time all fifos are cleared
+ * second time to clear data that was in pipline buffer
+ * and was transfered to fifos
+ */
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_BUFSTATE);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_BUFSTATE);
+
+ /* Flush all errors */
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_EXCEP);
+
+ /* Clear interrupts */
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_WATERMARKIM);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_WATERMARKIC);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_WATERMARKIM);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_WATERMARKIC);
+ writel(0xFF, ste_hsi->rx_base + STE_HSI_RX_OVERRUNACK);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_OVERRUNIM);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_EXCEPIM);
+ writel(0x0F, ste_hsi->rx_base + STE_HSI_RX_ACK);
+
+ /* Dequeue all pending requests */
+ for (i = 0; i < ste_port->channels; i++) {
+ /* Release write clocks */
+ if (!list_empty(&ste_port->txqueue[i]))
+ ste_hsi_clock_disable(hsi);
+ if (!list_empty(&ste_port->rxqueue[i]))
+ ste_hsi_clock_disable(hsi);
+ ste_hsi_flush_queue(&ste_port->txqueue[i], NULL);
+ ste_hsi_flush_queue(&ste_port->rxqueue[i], NULL);
+ }
+ ste_hsi_flush_queue(&ste_port->brkqueue, NULL);
+
+ ste_hsi_clock_disable(hsi);
+
+ return 0;
+}
+
+static int ste_hsi_start_tx(struct hsi_client *cl)
+{
+ return 0;
+}
+
+static int ste_hsi_stop_tx(struct hsi_client *cl)
+{
+ return 0;
+}
+
+static int ste_hsi_release(struct hsi_client *cl)
+{
+ int err;
+ struct ste_hsi_controller *ste_hsi = client_to_ste_controller(cl);
+
+ err = ste_hsi_flush(cl);
+ cancel_delayed_work(&ste_hsi->clk_work);
+
+ return 0;
+}
+
+static int ste_hsi_ports_init(struct hsi_controller *hsi,
+ struct platform_device *pdev)
+{
+ struct hsi_port *port;
+ struct ste_hsi_port *ste_port;
+ unsigned int i;
+ char irq_name[20];
+ int err;
+
+ for (i = 0; i < hsi->num_ports; i++) {
+ ste_port = devm_kzalloc(&pdev->dev, sizeof *ste_port,
+ GFP_KERNEL);
+ if (!ste_port)
+ return -ENOMEM;
+
+ port = &hsi->port[i];
+ port->async = ste_hsi_async;
+ port->setup = ste_hsi_setup;
+ port->flush = ste_hsi_flush;
+ port->start_tx = ste_hsi_start_tx;
+ port->stop_tx = ste_hsi_stop_tx;
+ port->release = ste_hsi_release;
+ hsi_port_set_drvdata(port, ste_port);
+ ste_port->dev = &port->device;
+
+ sprintf(irq_name, "hsi_rx_irq%d", i);
+ err = ste_hsi_get_irq(pdev, irq_name, ste_hsi_rx_isr, port,
+ &ste_port->rx_irq);
+ if (err)
+ return err;
+
+ sprintf(irq_name, "hsi_tx_irq%d", i);
+ err = ste_hsi_get_irq(pdev, irq_name, ste_hsi_tx_isr, port,
+ &ste_port->tx_irq);
+ if (err)
+ return err;
+
+ tasklet_init(&ste_port->rx_tasklet, ste_hsi_rx_tasklet,
+ (unsigned long)port);
+
+ tasklet_init(&ste_port->tx_tasklet, ste_hsi_tx_tasklet,
+ (unsigned long)port);
+
+ tasklet_init(&ste_port->exception_tasklet,
+ ste_hsi_exception_tasklet, (unsigned long)port);
+
+ tasklet_init(&ste_port->overrun_tasklet,
+ ste_hsi_overrun_tasklet, (unsigned long)port);
+
+ sprintf(irq_name, "hsi_rx_excep%d", i);
+ err = ste_hsi_get_irq(pdev, irq_name, ste_hsi_exception_isr,
+ port, &ste_port->excep_irq);
+ if (err)
+ return err;
+
+ ste_hsi_queues_init(ste_port);
+ }
+ return 0;
+}
+
+static int __init ste_hsi_hw_init(struct hsi_controller *hsi)
+{
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+ int err;
+
+ err = ste_hsi_clock_enable(hsi);
+ if (unlikely(err))
+ return err;
+
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_BUFSTATE);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_FLUSHBITS);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_PRIORITY);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_BURSTLEN);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_PREAMBLE);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_DATASWAP);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_DMAEN);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_WATERMARKID);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_WATERMARKIC);
+ writel(0, ste_hsi->tx_base + STE_HSI_TX_WATERMARKIM);
+
+ /* 0x23 is reset value per DB8500 Design Spec */
+ writel(0x23, ste_hsi->rx_base + STE_HSI_RX_THRESHOLD);
+
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_BUFSTATE);
+
+ /* Bits 0,1,2 set to 1 to clear exception flags */
+ writel(0x07, ste_hsi->rx_base + STE_HSI_RX_ACK);
+
+ /* Bits 0..7 set to 1 to clear OVERRUN IRQ */
+ writel(0xFF, ste_hsi->rx_base + STE_HSI_RX_OVERRUNACK);
+
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_DMAEN);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_WATERMARKIC);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_WATERMARKIM);
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_OVERRUNIM);
+
+ /* Flush all errors */
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_EXCEP);
+
+ /* 2 is Flush state, no RX exception generated afterwards */
+ writel(2, ste_hsi->rx_base + STE_HSI_RX_STATE);
+
+ writel(0, ste_hsi->rx_base + STE_HSI_RX_EXCEPIM);
+
+ ste_hsi_clock_disable(hsi);
+
+ return err;
+}
+
+static int __init ste_hsi_add_controller(struct hsi_controller *hsi,
+ struct platform_device *pdev)
+{
+ struct ste_hsi_controller *ste_hsi;
+ char overrun_name[] = "hsi_rx_overrun_chxxx";
+ unsigned char i;
+ int err;
+
+ ste_hsi = kzalloc(sizeof(struct ste_hsi_controller), GFP_KERNEL);
+ if (!ste_hsi) {
+ dev_err(&pdev->dev, "Not enough memory for ste_hsi!\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&ste_hsi->lock);
+ spin_lock_init(&ste_hsi->ck_lock);
+ INIT_DELAYED_WORK(&ste_hsi->clk_work, ste_hsi_delayed_disable_clock);
+
+ hsi->id = pdev->id;
+ hsi->device.parent = &pdev->dev;
+ dev_set_name(&hsi->device, "ste-hsi.%d", hsi->id);
+ ste_hsi->dev = &hsi->device;
+ hsi_controller_set_drvdata(hsi, ste_hsi);
+
+ /* Get and reserve resources for receiver */
+ err = ste_hsi_get_iomem(pdev, "hsi_rx_base", &ste_hsi->rx_base,
+ &ste_hsi->rx_dma_base);
+ if (err)
+ goto err_free_mem;
+ dev_info(&pdev->dev, "hsi_rx_base = %p\n", ste_hsi->rx_base);
+
+ /* Get and reserve resources for transmitter */
+ err = ste_hsi_get_iomem(pdev, "hsi_tx_base", &ste_hsi->tx_base,
+ &ste_hsi->tx_dma_base);
+ if (err)
+ goto err_free_mem;
+ dev_info(&pdev->dev, "hsi_tx_base = %p\n", ste_hsi->tx_base);
+
+ /* Get HSIT HSITXCLK clock */
+ ste_hsi->tx_clk = clk_get(&pdev->dev, "hsit_hsitxclk");
+ if (IS_ERR(ste_hsi->tx_clk)) {
+ dev_err(&hsi->device, "Couldn't get HSIT HSITXCLK clock\n");
+ err = PTR_ERR(ste_hsi->tx_clk);
+ goto err_free_mem;
+ }
+
+ /* Get HSIR HSIRXCLK clock */
+ ste_hsi->rx_clk = clk_get(&pdev->dev, "hsir_hsirxclk");
+ if (IS_ERR(ste_hsi->rx_clk)) {
+ dev_err(&hsi->device, "Couldn't get HSIR HSIRXCLK clock\n");
+ err = PTR_ERR(ste_hsi->rx_clk);
+ goto err_clk_free;
+ }
+
+ /* Get HSIT HCLK clock */
+ ste_hsi->ssitx_clk = clk_get(&pdev->dev, "hsit_hclk");
+ if (IS_ERR(ste_hsi->ssitx_clk)) {
+ dev_err(&hsi->device, "Couldn't get HSIT HCLK clock\n");
+ err = PTR_ERR(ste_hsi->ssitx_clk);
+ goto err_clk_free;
+ }
+
+ /* Get HSIR HCLK clock */
+ ste_hsi->ssirx_clk = clk_get(&pdev->dev, "hsir_hclk");
+ if (IS_ERR(ste_hsi->ssirx_clk)) {
+ dev_err(&hsi->device, "Couldn't get HSIR HCLK clock\n");
+ err = PTR_ERR(ste_hsi->ssirx_clk);
+ goto err_clk_free;
+ }
+
+ err = ste_hsi_clock_enable(hsi);
+ if (unlikely(err))
+ goto err_clk_free;
+
+ /* Check if controller is at specified address */
+ if (compare_periphid(ste_hsir_periphid,
+ (u32 *) (ste_hsi->rx_base + 0xFE0), 8)) {
+ dev_err(&pdev->dev, "No hsir controller at = %p\n",
+ ste_hsi->rx_base);
+ err = -ENXIO;
+ goto err_clk_free;
+ }
+
+ /* Check if controller is at specified address */
+ if (compare_periphid(ste_hsit_periphid,
+ (u32 *) (ste_hsi->tx_base + 0xFE0), 8)) {
+ dev_err(&pdev->dev, "No hsit controller at = %p\n",
+ ste_hsi->tx_base);
+ err = -ENXIO;
+ goto err_clk_free;
+ }
+ ste_hsi_clock_disable(hsi);
+
+ err = ste_hsi_hw_init(hsi);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to init HSI controller!\n");
+ goto err_clk_free;
+ }
+
+ for (i = 0; i < STE_HSI_MAX_CHANNELS; i++) {
+ sprintf(overrun_name, "hsi_rx_overrun_ch%d", i);
+ err = ste_hsi_get_irq(pdev, overrun_name, ste_hsi_overrun_isr,
+ hsi, &ste_hsi->overrun_irq[i]);
+ if (err)
+ goto err_clk_free;
+ }
+
+ err = ste_hsi_ports_init(hsi, pdev);
+ if (err)
+ goto err_clk_free;
+
+ err = hsi_register_controller(hsi);
+ if (err)
+ goto err_clk_free;
+
+ return 0;
+
+err_clk_free:
+ ste_hsi_clks_free(ste_hsi);
+err_free_mem:
+ kfree(ste_hsi);
+ return err;
+}
+
+static int ste_hsi_remove_controller(struct hsi_controller *hsi,
+ struct platform_device *pdev)
+{
+ struct ste_hsi_controller *ste_hsi = hsi_controller_drvdata(hsi);
+
+ ste_hsi_clks_free(ste_hsi);
+ hsi_unregister_controller(hsi);
+
+ return 0;
+}
+
+static int __init ste_hsi_probe(struct platform_device *pdev)
+{
+ struct hsi_controller *hsi;
+ struct ste_hsi_platform_data *pdata = pdev->dev.platform_data;
+ int err;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No HSI platform data!\n");
+ return -EINVAL;
+ }
+
+ hsi = hsi_alloc_controller(pdata->num_ports, GFP_KERNEL);
+ if (!hsi) {
+ dev_err(&pdev->dev, "No memory to allocate HSI controller!\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, hsi);
+
+ err = ste_hsi_add_controller(hsi, pdev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Can't add HSI controller!\n");
+ goto err_free_controller;
+ }
+
+ if (pdata->use_dma)
+ ste_hsi_init_dma(pdata, hsi);
+
+ return 0;
+
+err_free_controller:
+ platform_set_drvdata(pdev, NULL);
+ hsi_free_controller(hsi);
+
+ return err;
+}
+
+static int __exit ste_hsi_remove(struct platform_device *pdev)
+{
+ struct hsi_controller *hsi = platform_get_drvdata(pdev);
+
+ ste_hsi_remove_controller(hsi, pdev);
+ hsi_free_controller(hsi);
+
+ return 0;
+}
+
+static struct platform_driver ste_hsi_driver __refdata = {
+ .driver = {
+ .name = "ste_hsi",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(ste_hsi_remove),
+};
+
+static int __init ste_hsi_init(void)
+{
+ return platform_driver_probe(&ste_hsi_driver, ste_hsi_probe);
+}
+
+module_init(ste_hsi_init)
+
+static void __exit ste_hsi_exit(void)
+{
+ platform_driver_unregister(&ste_hsi_driver);
+}
+
+module_exit(ste_hsi_exit)
+
+ MODULE_AUTHOR("Lukasz Baj <lukasz.baj@tieto.com")
+ MODULE_DESCRIPTION("STE HSI driver.")
+ MODULE_LICENSE("GPL")
diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c
new file mode 100644
index 00000000000..06b574394cd
--- /dev/null
+++ b/drivers/hsi/hsi.c
@@ -0,0 +1,496 @@
+/*
+ * hsi.c
+ *
+ * HSI core.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/hsi/hsi.h>
+#include <linux/compiler.h>
+#include <linux/rwsem.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "hsi_core.h"
+
+static struct device_type hsi_ctrl = {
+ .name = "hsi_controller",
+};
+
+static struct device_type hsi_cl = {
+ .name = "hsi_client",
+};
+
+static struct device_type hsi_port = {
+ .name = "hsi_port",
+};
+
+static ssize_t modalias_show(struct device *dev,
+ struct device_attribute *a __maybe_unused, char *buf)
+{
+ return sprintf(buf, "hsi:%s\n", dev_name(dev));
+}
+
+static struct device_attribute hsi_bus_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ if (dev->type == &hsi_cl)
+ add_uevent_var(env, "MODALIAS=hsi:%s", dev_name(dev));
+
+ return 0;
+}
+
+static int hsi_bus_match(struct device *dev, struct device_driver *driver)
+{
+ return strcmp(dev_name(dev), driver->name) == 0;
+}
+
+static struct bus_type hsi_bus_type = {
+ .name = "hsi",
+ .dev_attrs = hsi_bus_dev_attrs,
+ .match = hsi_bus_match,
+ .uevent = hsi_bus_uevent,
+};
+
+static void hsi_client_release(struct device *dev)
+{
+ kfree(to_hsi_client(dev));
+}
+
+static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info)
+{
+ struct hsi_client *cl;
+ unsigned long flags;
+
+ cl = kzalloc(sizeof(*cl), GFP_KERNEL);
+ if (!cl)
+ return;
+ cl->device.type = &hsi_cl;
+ cl->tx_cfg = info->tx_cfg;
+ cl->rx_cfg = info->rx_cfg;
+ cl->device.bus = &hsi_bus_type;
+ cl->device.parent = &port->device;
+ cl->device.release = hsi_client_release;
+ dev_set_name(&cl->device, info->name);
+ cl->device.platform_data = info->platform_data;
+ spin_lock_irqsave(&port->clock, flags);
+ list_add_tail(&cl->link, &port->clients);
+ spin_unlock_irqrestore(&port->clock, flags);
+ if (info->archdata)
+ cl->device.archdata = *info->archdata;
+ if (device_register(&cl->device) < 0) {
+ pr_err("hsi: failed to register client: %s\n", info->name);
+ kfree(cl);
+ }
+}
+
+static void hsi_scan_board_info(struct hsi_controller *hsi)
+{
+ struct hsi_cl_info *cl_info;
+ struct hsi_port *p;
+
+ list_for_each_entry(cl_info, &hsi_board_list, list)
+ if (cl_info->info.hsi_id == hsi->id) {
+ p = hsi_find_port_num(hsi, cl_info->info.port);
+ if (!p)
+ continue;
+ hsi_new_client(p, &cl_info->info);
+ }
+}
+
+static int hsi_remove_client(struct device *dev, void *data __maybe_unused)
+{
+ struct hsi_client *cl = to_hsi_client(dev);
+ struct hsi_port *port = to_hsi_port(dev->parent);
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->clock, flags);
+ list_del(&cl->link);
+ spin_unlock_irqrestore(&port->clock, flags);
+ device_unregister(dev);
+
+ return 0;
+}
+
+static int hsi_remove_port(struct device *dev, void *data __maybe_unused)
+{
+ device_for_each_child(dev, NULL, hsi_remove_client);
+ device_unregister(dev);
+
+ return 0;
+}
+
+static void hsi_controller_release(struct device *dev __maybe_unused)
+{
+}
+
+static void hsi_port_release(struct device *dev __maybe_unused)
+{
+}
+
+/**
+ * hsi_unregister_controller - Unregister an HSI controller
+ * @hsi: The HSI controller to register
+ */
+void hsi_unregister_controller(struct hsi_controller *hsi)
+{
+ device_for_each_child(&hsi->device, NULL, hsi_remove_port);
+ device_unregister(&hsi->device);
+}
+EXPORT_SYMBOL_GPL(hsi_unregister_controller);
+
+/**
+ * hsi_register_controller - Register an HSI controller and its ports
+ * @hsi: The HSI controller to register
+ *
+ * Returns -errno on failure, 0 on success.
+ */
+int hsi_register_controller(struct hsi_controller *hsi)
+{
+ unsigned int i;
+ int err;
+
+ hsi->device.type = &hsi_ctrl;
+ hsi->device.bus = &hsi_bus_type;
+ hsi->device.release = hsi_controller_release;
+ err = device_register(&hsi->device);
+ if (err < 0)
+ return err;
+ for (i = 0; i < hsi->num_ports; i++) {
+ hsi->port[i].device.parent = &hsi->device;
+ hsi->port[i].device.bus = &hsi_bus_type;
+ hsi->port[i].device.release = hsi_port_release;
+ hsi->port[i].device.type = &hsi_port;
+ INIT_LIST_HEAD(&hsi->port[i].clients);
+ spin_lock_init(&hsi->port[i].clock);
+ err = device_register(&hsi->port[i].device);
+ if (err < 0)
+ goto out;
+ }
+ /* Populate HSI bus with HSI clients */
+ hsi_scan_board_info(hsi);
+
+ return 0;
+out:
+ hsi_unregister_controller(hsi);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(hsi_register_controller);
+
+/**
+ * hsi_register_client_driver - Register an HSI client to the HSI bus
+ * @drv: HSI client driver to register
+ *
+ * Returns -errno on failure, 0 on success.
+ */
+int hsi_register_client_driver(struct hsi_client_driver *drv)
+{
+ drv->driver.bus = &hsi_bus_type;
+
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(hsi_register_client_driver);
+
+static inline int hsi_dummy_msg(struct hsi_msg *msg __maybe_unused)
+{
+ return 0;
+}
+
+static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused)
+{
+ return 0;
+}
+
+/**
+ * hsi_alloc_controller - Allocate an HSI controller and its ports
+ * @n_ports: Number of ports on the HSI controller
+ * @flags: Kernel allocation flags
+ *
+ * Return NULL on failure or a pointer to an hsi_controller on success.
+ */
+struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags)
+{
+ struct hsi_controller *hsi;
+ struct hsi_port *port;
+ unsigned int i;
+
+ if (!n_ports)
+ return NULL;
+
+ port = kzalloc(sizeof(*port)*n_ports, flags);
+ if (!port)
+ return NULL;
+ hsi = kzalloc(sizeof(*hsi), flags);
+ if (!hsi)
+ goto out;
+ for (i = 0; i < n_ports; i++) {
+ dev_set_name(&port[i].device, "port%d", i);
+ port[i].num = i;
+ port[i].async = hsi_dummy_msg;
+ port[i].setup = hsi_dummy_cl;
+ port[i].flush = hsi_dummy_cl;
+ port[i].start_tx = hsi_dummy_cl;
+ port[i].stop_tx = hsi_dummy_cl;
+ port[i].release = hsi_dummy_cl;
+ mutex_init(&port[i].lock);
+ }
+ hsi->num_ports = n_ports;
+ hsi->port = port;
+
+ return hsi;
+out:
+ kfree(port);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(hsi_alloc_controller);
+
+/**
+ * hsi_free_controller - Free an HSI controller
+ * @hsi: Pointer to HSI controller
+ */
+void hsi_free_controller(struct hsi_controller *hsi)
+{
+ if (!hsi)
+ return;
+
+ kfree(hsi->port);
+ kfree(hsi);
+}
+EXPORT_SYMBOL_GPL(hsi_free_controller);
+
+/**
+ * hsi_free_msg - Free an HSI message
+ * @msg: Pointer to the HSI message
+ *
+ * Client is responsible to free the buffers pointed by the scatterlists.
+ */
+void hsi_free_msg(struct hsi_msg *msg)
+{
+ if (!msg)
+ return;
+ sg_free_table(&msg->sgt);
+ kfree(msg);
+}
+EXPORT_SYMBOL_GPL(hsi_free_msg);
+
+/**
+ * hsi_alloc_msg - Allocate an HSI message
+ * @nents: Number of memory entries
+ * @flags: Kernel allocation flags
+ *
+ * nents can be 0. This mainly makes sense for read transfer.
+ * In that case, HSI drivers will call the complete callback when
+ * there is data to be read without consuming it.
+ *
+ * Return NULL on failure or a pointer to an hsi_msg on success.
+ */
+struct hsi_msg *hsi_alloc_msg(unsigned int nents, gfp_t flags)
+{
+ struct hsi_msg *msg;
+ int err;
+
+ msg = kzalloc(sizeof(*msg), flags);
+ if (!msg)
+ return NULL;
+
+ if (!nents)
+ return msg;
+
+ err = sg_alloc_table(&msg->sgt, nents, flags);
+ if (unlikely(err)) {
+ kfree(msg);
+ msg = NULL;
+ }
+
+ return msg;
+}
+EXPORT_SYMBOL_GPL(hsi_alloc_msg);
+
+/**
+ * hsi_async - Submit an HSI transfer to the controller
+ * @cl: HSI client sending the transfer
+ * @msg: The HSI transfer passed to controller
+ *
+ * The HSI message must have the channel, ttype, complete and destructor
+ * fields set beforehand. If nents > 0 then the client has to initialize
+ * also the scatterlists to point to the buffers to write to or read from.
+ *
+ * HSI controllers relay on pre-allocated buffers from their clients and they
+ * do not allocate buffers on their own.
+ *
+ * Once the HSI message transfer finishes, the HSI controller calls the
+ * complete callback with the status and actual_len fields of the HSI message
+ * updated. The complete callback can be called before returning from
+ * hsi_async.
+ *
+ * Returns -errno on failure or 0 on success
+ */
+int hsi_async(struct hsi_client *cl, struct hsi_msg *msg)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+
+ if (!hsi_port_claimed(cl))
+ return -EACCES;
+
+ WARN_ON_ONCE(!msg->destructor || !msg->complete);
+ msg->cl = cl;
+
+ return port->async(msg);
+}
+EXPORT_SYMBOL_GPL(hsi_async);
+
+/**
+ * hsi_claim_port - Claim the HSI client's port
+ * @cl: HSI client that wants to claim its port
+ * @share: Flag to indicate if the client wants to share the port or not.
+ *
+ * Returns -errno on failure, 0 on success.
+ */
+int hsi_claim_port(struct hsi_client *cl, unsigned int share)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ int err = 0;
+
+ mutex_lock(&port->lock);
+ if ((port->claimed) && (!port->shared || !share)) {
+ err = -EBUSY;
+ goto out;
+ }
+ if (!try_module_get(to_hsi_controller(port->device.parent)->owner)) {
+ err = -ENODEV;
+ goto out;
+ }
+ port->claimed++;
+ port->shared = !!share;
+ cl->pclaimed = 1;
+out:
+ mutex_unlock(&port->lock);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(hsi_claim_port);
+
+/**
+ * hsi_release_port - Release the HSI client's port
+ * @cl: HSI client which previously claimed its port
+ */
+void hsi_release_port(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+
+ mutex_lock(&port->lock);
+ /* Allow HW driver to do some cleanup */
+ port->release(cl);
+ if (cl->pclaimed)
+ port->claimed--;
+ BUG_ON(port->claimed < 0);
+ cl->pclaimed = 0;
+ if (!port->claimed)
+ port->shared = 0;
+ module_put(to_hsi_controller(port->device.parent)->owner);
+ mutex_unlock(&port->lock);
+}
+EXPORT_SYMBOL_GPL(hsi_release_port);
+
+static int hsi_start_rx(struct hsi_client *cl, void *data __maybe_unused)
+{
+ if (cl->hsi_start_rx)
+ (*cl->hsi_start_rx)(cl);
+
+ return 0;
+}
+
+static int hsi_stop_rx(struct hsi_client *cl, void *data __maybe_unused)
+{
+ if (cl->hsi_stop_rx)
+ (*cl->hsi_stop_rx)(cl);
+
+ return 0;
+}
+
+static int hsi_port_for_each_client(struct hsi_port *port, void *data,
+ int (*fn)(struct hsi_client *cl, void *data))
+{
+ struct hsi_client *cl;
+
+ spin_lock(&port->clock);
+ list_for_each_entry(cl, &port->clients, link) {
+ spin_unlock(&port->clock);
+ (*fn)(cl, data);
+ spin_lock(&port->clock);
+ }
+ spin_unlock(&port->clock);
+
+ return 0;
+}
+
+/**
+ * hsi_event -Notifies clients about port events
+ * @port: Port where the event occurred
+ * @event: The event type
+ *
+ * Clients should not be concerned about wake line behavior. However, due
+ * to a race condition in HSI HW protocol, clients need to be notified
+ * about wake line changes, so they can implement a workaround for it.
+ *
+ * Events:
+ * HSI_EVENT_START_RX - Incoming wake line high
+ * HSI_EVENT_STOP_RX - Incoming wake line down
+ */
+void hsi_event(struct hsi_port *port, unsigned int event)
+{
+ int (*fn)(struct hsi_client *cl, void *data);
+
+ switch (event) {
+ case HSI_EVENT_START_RX:
+ fn = hsi_start_rx;
+ break;
+ case HSI_EVENT_STOP_RX:
+ fn = hsi_stop_rx;
+ break;
+ default:
+ return;
+ }
+ hsi_port_for_each_client(port, NULL, fn);
+}
+EXPORT_SYMBOL_GPL(hsi_event);
+
+static int __init hsi_init(void)
+{
+ return bus_register(&hsi_bus_type);
+}
+postcore_initcall(hsi_init);
+
+static void __exit hsi_exit(void)
+{
+ bus_unregister(&hsi_bus_type);
+}
+module_exit(hsi_exit);
+
+MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>");
+MODULE_DESCRIPTION("High-speed Synchronous Serial Interface (HSI) framework");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hsi/hsi_boardinfo.c b/drivers/hsi/hsi_boardinfo.c
new file mode 100644
index 00000000000..3a9e4e86652
--- /dev/null
+++ b/drivers/hsi/hsi_boardinfo.c
@@ -0,0 +1,64 @@
+/*
+ * hsi_boardinfo.c
+ *
+ * HSI clients registration interface
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/hsi/hsi.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include "hsi_core.h"
+
+/*
+ * hsi_board_list is only used internally by the HSI framework.
+ * No one else is allowed to make use of it.
+ */
+LIST_HEAD(hsi_board_list);
+EXPORT_SYMBOL_GPL(hsi_board_list);
+
+/**
+ * hsi_register_board_info - Register HSI clients information
+ * @info: Array of HSI clients on the board
+ * @len: Length of the array
+ *
+ * HSI clients are statically declared and registered on board files.
+ *
+ * HSI clients will be automatically registered to the HSI bus once the
+ * controller and the port where the clients wishes to attach are registered
+ * to it.
+ *
+ * Return -errno on failure, 0 on success.
+ */
+int __init hsi_register_board_info(struct hsi_board_info const *info,
+ unsigned int len)
+{
+ struct hsi_cl_info *cl_info;
+
+ cl_info = kzalloc(sizeof(*cl_info) * len, GFP_KERNEL);
+ if (!cl_info)
+ return -ENOMEM;
+
+ for (; len; len--, info++, cl_info++) {
+ cl_info->info = *info;
+ list_add_tail(&cl_info->list, &hsi_board_list);
+ }
+
+ return 0;
+}
diff --git a/drivers/hsi/hsi_core.h b/drivers/hsi/hsi_core.h
new file mode 100644
index 00000000000..8005509a28b
--- /dev/null
+++ b/drivers/hsi/hsi_core.h
@@ -0,0 +1,37 @@
+/*
+ * hsi_core.h
+ *
+ * HSI framework internal interfaces,
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __LINUX_HSI_CORE_H__
+#define __LINUX_HSI_CORE_H__
+
+#include <linux/hsi/hsi.h>
+
+struct hsi_cl_info {
+ struct list_head list;
+ struct hsi_board_info info;
+};
+
+extern struct list_head hsi_board_list;
+
+#endif /* __LINUX_HSI_CORE_H__ */
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index e19cf8eb6cc..9902d5bcb61 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -39,6 +39,32 @@ config HWMON_DEBUG_CHIP
comment "Native drivers"
+config SENSORS_AB8500
+ tristate "AB8500 thermal monitoring"
+ depends on AB8500_CORE
+ default n
+ help
+ If you say yes here you get support for the thermal sensor part
+ of the AB8500 chip. The driver includes thermal management for
+ AB8500 die and two GPADC channels. The GPADC channel are preferably
+ used to access sensors outside the AB8500 chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called ab8500-temp.
+
+config SENSORS_DB8500
+ tristate "DB8500 thermal monitoring"
+ depends on UX500_SOC_DB8500
+ default n
+ help
+ If you say yes here you get support for the thermal sensor part
+ of the DB8500 chip. The driver includes thermal management for
+ DB8500 die.
+
+ This driver can also be built as a module. If so, the module
+ will be called db8500_temp.
+
+
config SENSORS_ABITUGURU
tristate "Abit uGuru (rev 1 & 2)"
depends on X86 && EXPERIMENTAL
@@ -603,6 +629,40 @@ config SENSORS_LM93
This driver can also be built as a module. If so, the module
will be called lm93.
+config SENSORS_LSM303DLH
+ tristate "ST LSM303DLH 3-axis accelerometer and 3-axis magnetometer"
+ depends on I2C
+ default n
+ help
+ This driver provides support for the LSM303DLH chip which includes a
+ 3-axis accelerometer and a 3-axis magnetometer.
+
+ This driver can also be built as modules. If so, the module for
+ accelerometer will be called lsm303dlh_a and for magnetometer it will
+ be called lsm303dlh_m.
+
+ Say Y here if you have a device containing lsm303dlh chip.
+
+config SENSORS_LSM303DLH_INPUT_DEVICE
+ bool "ST LSM303DLH INPUT DEVICE"
+ depends on SENSORS_LSM303DLH
+ default n
+ help
+ This driver allows device to be used as an input device with
+ interrupts, need to be enabled only when input device support
+ is required.
+
+config SENSORS_L3G4200D
+ tristate "ST L3G4200D 3-axis gyroscope"
+ depends on I2C
+ default n
+ help
+ If you say yes here you get support for 3-axis gyroscope device
+ L3g4200D.
+
+ This driver can also be built as a module. If so, the module
+ will be called l3g4200d.
+
config SENSORS_LTC4215
tristate "Linear Technology LTC4215"
depends on I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 2138ceb1a71..3e7591740c4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -17,6 +17,8 @@ obj-$(CONFIG_SENSORS_W83793) += w83793.o
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
+obj-$(CONFIG_SENSORS_AB8500) += ab8500.o
+obj-$(CONFIG_SENSORS_DB8500) += db8500.o
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
obj-$(CONFIG_SENSORS_AD7414) += ad7414.o
@@ -74,6 +76,8 @@ obj-$(CONFIG_SENSORS_LM90) += lm90.o
obj-$(CONFIG_SENSORS_LM92) += lm92.o
obj-$(CONFIG_SENSORS_LM93) += lm93.o
obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
+obj-$(CONFIG_SENSORS_LSM303DLH) += lsm303dlh_a.o lsm303dlh_m.o
+obj-$(CONFIG_SENSORS_L3G4200D) += l3g4200d.o
obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c
new file mode 100644
index 00000000000..809e870a840
--- /dev/null
+++ b/drivers/hwmon/ab8500.c
@@ -0,0 +1,715 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Martin Persson <martin.persson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU Gereral Public License (GPL) version 2
+ *
+ * Note:
+ *
+ * AB8500 does not provide auto ADC, so to monitor the required
+ * temperatures, a periodic work is used. It is more important
+ * to not wake up the CPU than to perform this job, hence the use
+ * of a deferred delay.
+ *
+ * A deferred delay for thermal monitor is considered safe because:
+ * If the chip gets too hot during a sleep state it's most likely
+ * due to external factors, such as the surrounding temperature.
+ * I.e. no SW decisions will make any difference.
+ *
+ * If/when the AB8500 thermal warning temperature is reached (threshold
+ * cannot be changed by SW), an interrupt is set and the driver
+ * notifies user space via a sysfs event. If a shut down is not
+ * triggered by user space within a certain time frame,
+ * pm_power off is called.
+ *
+ * If/when AB8500 thermal shutdown temperature is reached a hardware
+ * shutdown of the AB8500 will occur.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/hwmon.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/mfd/ab8500/ab8500-gpadc.h>
+#include <linux/pm.h>
+
+/*
+ * If AB8500 warm interrupt is set, user space will be notified.
+ * If user space doesn't shut down the platform within this time
+ * frame, this driver will. Time unit is ms.
+ */
+#define DEFAULT_POWER_OFF_DELAY 10000
+
+#define NUM_SENSORS 4
+
+/* The driver monitors GPADC - ADC_AUX1 and ADC_AUX2 */
+#define NUM_MONITORED_SENSORS 2
+
+struct ab8500_temp {
+ struct platform_device *pdev;
+ struct device *hwmon_dev;
+ struct ab8500_gpadc *gpadc;
+ u8 gpadc_addr[NUM_SENSORS];
+ unsigned long min[NUM_SENSORS];
+ unsigned long max[NUM_SENSORS];
+ unsigned long max_hyst[NUM_SENSORS];
+ unsigned long crit[NUM_SENSORS];
+ unsigned long min_alarm[NUM_SENSORS];
+ unsigned long max_alarm[NUM_SENSORS];
+ unsigned long max_hyst_alarm[NUM_SENSORS];
+ unsigned long crit_alarm[NUM_SENSORS];
+ struct delayed_work work;
+ struct delayed_work power_off_work;
+ struct mutex lock;
+ /* Delay (ms) between temperature readings */
+ unsigned long gpadc_monitor_delay;
+ /* Delay (ms) before power off */
+ unsigned long power_off_delay;
+};
+
+/*
+ * Thresholds are considered inactive if set to 0.
+ * To avoid confusion for user space applications,
+ * the temp monitor delay is set to 0 if all thresholds
+ * are 0.
+ */
+static bool find_active_thresholds(struct ab8500_temp *data)
+{
+ int i;
+ for (i = 0; i < NUM_MONITORED_SENSORS; i++)
+ if (data->max[i] != 0 || data->max_hyst[i] != 0
+ || data->min[i] != 0)
+ return true;
+
+ dev_dbg(&data->pdev->dev, "No active thresholds,"
+ "cancel deferred job (if it exists)"
+ "and reset temp monitor delay\n");
+ mutex_lock(&data->lock);
+ data->gpadc_monitor_delay = 0;
+ cancel_delayed_work_sync(&data->work);
+ mutex_unlock(&data->lock);
+ return false;
+}
+
+static void thermal_power_off(struct work_struct *work)
+{
+ struct ab8500_temp *data = container_of(work, struct ab8500_temp,
+ power_off_work.work);
+
+ dev_warn(&data->pdev->dev, "Power off due to AB8500 thermal warning\n");
+ pm_power_off();
+}
+
+static void gpadc_monitor(struct work_struct *work)
+{
+ unsigned long delay_in_jiffies;
+ int val, i, ret;
+ /* Container for alarm node name */
+ char alarm_node[30];
+
+ bool updated_min_alarm = false;
+ bool updated_max_alarm = false;
+ bool updated_max_hyst_alarm = false;
+ struct ab8500_temp *data = container_of(work, struct ab8500_temp,
+ work.work);
+
+ for (i = 0; i < NUM_MONITORED_SENSORS; i++) {
+ /* Thresholds are considered inactive if set to 0 */
+ if (data->max[i] == 0 && data->max_hyst[i] == 0
+ && data->min[i] == 0)
+ continue;
+
+ val = ab8500_gpadc_convert(data->gpadc, data->gpadc_addr[i]);
+ if (val < 0) {
+ dev_err(&data->pdev->dev, "GPADC read failed\n");
+ continue;
+ }
+
+ mutex_lock(&data->lock);
+ if (data->min[i] != 0) {
+ if (val < data->min[i]) {
+ if (data->min_alarm[i] == 0) {
+ data->min_alarm[i] = 1;
+ updated_min_alarm = true;
+ }
+ } else {
+ if (data->min_alarm[i] == 1) {
+ data->min_alarm[i] = 0;
+ updated_min_alarm = true;
+ }
+ }
+
+ }
+ if (data->max[i] != 0) {
+ if (val > data->max[i]) {
+ if (data->max_alarm[i] == 0) {
+ data->max_alarm[i] = 1;
+ updated_max_alarm = true;
+ }
+ } else {
+ if (data->max_alarm[i] == 1) {
+ data->max_alarm[i] = 0;
+ updated_max_alarm = true;
+ }
+ }
+
+ }
+ if (data->max_hyst[i] != 0) {
+ if (val > data->max_hyst[i]) {
+ if (data->max_hyst_alarm[i] == 0) {
+ data->max_hyst_alarm[i] = 1;
+ updated_max_hyst_alarm = true;
+ }
+ } else {
+ if (data->max_hyst_alarm[i] == 1) {
+ data->max_hyst_alarm[i] = 0;
+ updated_max_hyst_alarm = true;
+ }
+ }
+ }
+ mutex_unlock(&data->lock);
+
+ /* hwmon attr index starts at 1, thus "i+1" below */
+ if (updated_min_alarm) {
+ ret = snprintf(alarm_node, 16, "temp%d_min_alarm",
+ (i + 1));
+ if (ret < 0) {
+ dev_err(&data->pdev->dev,
+ "Unable to update alarm node (%d)",
+ ret);
+ break;
+ }
+ sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+ }
+ if (updated_max_alarm) {
+ ret = snprintf(alarm_node, 16, "temp%d_max_alarm",
+ (i + 1));
+ if (ret < 0) {
+ dev_err(&data->pdev->dev,
+ "Unable to update alarm node (%d)",
+ ret);
+ break;
+ }
+ sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+ }
+ if (updated_max_hyst_alarm) {
+ ret = snprintf(alarm_node, 21, "temp%d_max_hyst_alarm",
+ (i + 1));
+ if (ret < 0) {
+ dev_err(&data->pdev->dev,
+ "Unable to update alarm node (%d)",
+ ret);
+ break;
+ }
+ sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+ }
+ }
+ delay_in_jiffies = msecs_to_jiffies(data->gpadc_monitor_delay);
+ schedule_delayed_work(&data->work, delay_in_jiffies);
+}
+
+static inline void gpadc_monitor_exit(struct platform_device *pdev)
+{
+ struct ab8500_temp *data = platform_get_drvdata(pdev);
+ cancel_delayed_work_sync(&data->work);
+}
+
+static ssize_t set_temp_monitor_delay(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int res;
+ unsigned long delay_in_jiffies, delay_in_s;
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+
+ if (!find_active_thresholds(data)) {
+ dev_dbg(dev, "No thresholds to monitor, disregarding delay\n");
+ return count;
+ }
+ res = strict_strtoul(buf, 10, &delay_in_s);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ data->gpadc_monitor_delay = delay_in_s * 1000;
+ mutex_unlock(&data->lock);
+ delay_in_jiffies = msecs_to_jiffies(data->gpadc_monitor_delay);
+ schedule_delayed_work(&data->work, delay_in_jiffies);
+
+ return count;
+}
+
+static ssize_t set_temp_power_off_delay(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int res;
+ unsigned long delay_in_jiffies, delay_in_s;
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+
+ res = strict_strtoul(buf, 10, &delay_in_s);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ data->power_off_delay = delay_in_s * 1000;
+ mutex_unlock(&data->lock);
+ delay_in_jiffies = msecs_to_jiffies(data->power_off_delay);
+
+ return count;
+}
+
+static ssize_t show_temp_monitor_delay(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ /* return time in s, not ms */
+ return sprintf(buf, "%lu\n", (data->gpadc_monitor_delay) / 1000);
+}
+
+static ssize_t show_temp_power_off_delay(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ /* return time in s, not ms */
+ return sprintf(buf, "%lu\n", (data->power_off_delay) / 1000);
+}
+
+/* HWMON sysfs interface */
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ /*
+ * To avoid confusion between sensor label and chip name, the function
+ * "show_label" is not used to return the chip name.
+ */
+ return sprintf(buf, "ab8500\n");
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ char *name;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int index = attr->index;
+
+ /*
+ * Make sure these labels correspond to the attribute indexes
+ * used when calling SENSOR_DEVICE_ATRR.
+ * Temperature sensors outside ab8500 (read via GPADC) are marked
+ * with prefix ext_
+ */
+ switch (index) {
+ case 1:
+ name = "ext_rtc_xtal";
+ break;
+ case 2:
+ name = "ext_db8500";
+ break;
+ case 3:
+ name = "battery";
+ break;
+ case 4:
+ name = "ab8500";
+ break;
+ default:
+ return -EINVAL;
+ }
+ return sprintf(buf, "%s\n", name);
+}
+
+static ssize_t show_input(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int val;
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ u8 gpadc_addr = data->gpadc_addr[attr->index - 1];
+
+ val = ab8500_gpadc_convert(data->gpadc, gpadc_addr);
+ if (val < 0)
+ dev_err(&data->pdev->dev, "GPADC read failed\n");
+
+ return sprintf(buf, "%d\n", val);
+}
+
+/* set functions (RW nodes) */
+static ssize_t set_min(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ /*
+ * Threshold is considered inactive if set to 0
+ * hwmon attr index starts at 1, thus "attr->index-1" below
+ */
+ if (val == 0)
+ data->min_alarm[attr->index - 1] = 0;
+
+ data->min[attr->index - 1] = val;
+ mutex_unlock(&data->lock);
+ if (val == 0)
+ (void) find_active_thresholds(data);
+
+ return count;
+}
+
+static ssize_t set_max(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ /*
+ * Threshold is considered inactive if set to 0
+ * hwmon attr index starts at 1, thus "attr->index-1" below
+ */
+ if (val == 0)
+ data->max_alarm[attr->index - 1] = 0;
+
+ data->max[attr->index - 1] = val;
+ mutex_unlock(&data->lock);
+ if (val == 0)
+ (void) find_active_thresholds(data);
+
+ return count;
+}
+
+static ssize_t set_max_hyst(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ /*
+ * Threshold is considered inactive if set to 0
+ * hwmon attr index starts at 1, thus "attr->index-1" below
+ */
+ if (val == 0)
+ data->max_hyst_alarm[attr->index - 1] = 0;
+
+ data->max_hyst[attr->index - 1] = val;
+ mutex_unlock(&data->lock);
+ if (val == 0)
+ (void) find_active_thresholds(data);
+
+ return count;
+}
+
+/*
+ * show functions (RO nodes)
+ * Notice that min/max/max_hyst refer to millivolts and not millidegrees
+ */
+static ssize_t show_min(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->min[attr->index - 1]);
+}
+
+static ssize_t show_max(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max[attr->index - 1]);
+}
+
+static ssize_t show_max_hyst(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max_hyst[attr->index - 1]);
+}
+
+/* Alarms */
+static ssize_t show_min_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->min_alarm[attr->index - 1]);
+}
+
+static ssize_t show_max_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max_alarm[attr->index - 1]);
+}
+
+static ssize_t show_max_hyst_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max_hyst_alarm[attr->index - 1]);
+}
+
+static ssize_t show_crit_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct ab8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->crit_alarm[attr->index - 1]);
+}
+
+/*These node are not included in the kernel hwmon sysfs interface */
+static SENSOR_DEVICE_ATTR(temp_monitor_delay, S_IRUGO | S_IWUSR,
+ show_temp_monitor_delay, set_temp_monitor_delay, 0);
+static SENSOR_DEVICE_ATTR(temp_power_off_delay, S_IRUGO | S_IWUSR,
+ show_temp_power_off_delay,
+ set_temp_power_off_delay, 0);
+
+/* Chip name, required by hwmon*/
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+/* GPADC - ADC_AUX1 */
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_min, set_min, 1);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_max, set_max, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_min_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_max_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst_alarm, S_IRUGO,
+ show_max_hyst_alarm, NULL, 1);
+
+/* GPADC - ADC_AUX2 */
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min, 2);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max, 2);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 2);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_min_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_max_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst_alarm, S_IRUGO,
+ show_max_hyst_alarm, NULL, 2);
+
+/* GPADC - BTEMP_BALL */
+static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 3);
+
+/* AB8500 */
+static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO,
+ show_crit_alarm, NULL, 4);
+
+static struct attribute *ab8500_temp_attributes[] = {
+ &sensor_dev_attr_temp_power_off_delay.dev_attr.attr,
+ &sensor_dev_attr_temp_monitor_delay.dev_attr.attr,
+ &sensor_dev_attr_name.dev_attr.attr,
+ /* GPADC - ADC_AUX1 */
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst_alarm.dev_attr.attr,
+ /* GPADC - ADC_AUX2 */
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst_alarm.dev_attr.attr,
+ /* GPADC - BTEMP_BALL */
+ &sensor_dev_attr_temp3_label.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ /* AB8500 */
+ &sensor_dev_attr_temp4_label.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ab8500_temp_group = {
+ .attrs = ab8500_temp_attributes,
+};
+
+static irqreturn_t ab8500_temp_irq_handler(int irq, void *irq_data)
+{
+ unsigned long delay_in_jiffies;
+ struct platform_device *pdev = irq_data;
+ struct ab8500_temp *data = platform_get_drvdata(pdev);
+
+ /*
+ * Make sure the magic numbers below corresponds to the node
+ * used for AB8500 thermal warning from HW.
+ */
+ mutex_lock(&data->lock);
+ data->crit_alarm[3] = 1;
+ mutex_unlock(&data->lock);
+ sysfs_notify(&pdev->dev.kobj, NULL, "temp4_crit_alarm");
+ dev_info(&pdev->dev, "AB8500 thermal warning, power off in %lu s\n",
+ data->power_off_delay);
+ delay_in_jiffies = msecs_to_jiffies(data->power_off_delay);
+ schedule_delayed_work(&data->power_off_work, delay_in_jiffies);
+ return IRQ_HANDLED;
+}
+
+static int setup_irqs(struct platform_device *pdev)
+{
+ int ret;
+ int irq = platform_get_irq_byname(pdev, "AB8500_TEMP_WARM");
+
+ if (irq < 0)
+ dev_err(&pdev->dev, "Get irq by name failed\n");
+
+ ret = request_threaded_irq(irq, NULL, ab8500_temp_irq_handler,
+ IRQF_NO_SUSPEND, "ab8500-temp", pdev);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
+
+ return ret;
+}
+
+static int __devinit ab8500_temp_probe(struct platform_device *pdev)
+{
+ struct ab8500_temp *data;
+ int err;
+
+ data = kzalloc(sizeof(struct ab8500_temp), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ err = setup_irqs(pdev);
+ if (err < 0)
+ goto exit;
+
+ data->gpadc = ab8500_gpadc_get();
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+ goto exit;
+ }
+
+ INIT_DELAYED_WORK_DEFERRABLE(&data->work, gpadc_monitor);
+ INIT_DELAYED_WORK(&data->power_off_work, thermal_power_off);
+
+ /*
+ * Setup HW defined data.
+ *
+ * Reference hardware (HREF):
+ *
+ * GPADC - ADC_AUX1, connected to NTC R2148 next to RTC_XTAL on HREF
+ * GPADC - ADC_AUX2, connected to NTC R2150 near DB8500 on HREF
+ * Hence, temp#_min/max/max_hyst refer to millivolts and not
+ * millidegrees
+ *
+ * HREF HW does not support reading AB8500 temperature. BUT an
+ * AB8500 IRQ will be launched if die crit temp limit is reached.
+ *
+ * Also:
+ * Battery temperature thresholds will not be exposed via hwmon.
+ *
+ * Make sure indexes correspond to the attribute indexes
+ * used when calling SENSOR_DEVICE_ATRR
+ */
+ data->gpadc_addr[0] = ADC_AUX1;
+ data->gpadc_addr[1] = ADC_AUX2;
+ data->gpadc_addr[2] = BTEMP_BALL;
+
+ mutex_init(&data->lock);
+ data->pdev = pdev;
+ data->power_off_delay = DEFAULT_POWER_OFF_DELAY;
+
+ platform_set_drvdata(pdev, data);
+
+ err = sysfs_create_group(&pdev->dev.kobj, &ab8500_temp_group);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
+ goto exit_platform_data;
+ }
+
+ return 0;
+
+exit_platform_data:
+ platform_set_drvdata(pdev, NULL);
+exit:
+ kfree(data);
+ return err;
+}
+
+static int __devexit ab8500_temp_remove(struct platform_device *pdev)
+{
+ struct ab8500_temp *data = platform_get_drvdata(pdev);
+
+ gpadc_monitor_exit(pdev);
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &ab8500_temp_group);
+ platform_set_drvdata(pdev, NULL);
+ kfree(data);
+ return 0;
+}
+
+/* No action required in suspend/resume, thus the lack of functions */
+static struct platform_driver ab8500_temp_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ab8500-temp",
+ },
+ .probe = ab8500_temp_probe,
+ .remove = __devexit_p(ab8500_temp_remove),
+};
+
+static int __init ab8500_temp_init(void)
+{
+ return platform_driver_register(&ab8500_temp_driver);
+}
+
+static void __exit ab8500_temp_exit(void)
+{
+ platform_driver_unregister(&ab8500_temp_driver);
+}
+
+MODULE_AUTHOR("Martin Persson <martin.persson@stericsson.com>");
+MODULE_DESCRIPTION("AB8500 temperature driver");
+MODULE_LICENSE("GPL");
+
+module_init(ab8500_temp_init)
+module_exit(ab8500_temp_exit)
diff --git a/drivers/hwmon/db8500.c b/drivers/hwmon/db8500.c
new file mode 100755
index 00000000000..b9b4312b8c1
--- /dev/null
+++ b/drivers/hwmon/db8500.c
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ *
+ * Author: WenHai Fang <wenhai.h.fang@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU Gereral Public License (GPL) version 2
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <mach/prcmu-fw-api.h>
+#include <linux/hwmon.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+
+/*
+ * If DB8500 warm interrupt is set, user space will be notified.
+ * If user space doesn't shut down the platform within this time
+ * frame, this driver will. Time unit is ms.
+ */
+#define DEFAULT_POWER_OFF_DELAY 10000
+
+/*
+ * Default measure period to 0xFF x cycle32k
+ */
+#define DEFAULT_MEASURE_TIME 0xFF
+
+/* This driver monitors DB thermal*/
+#define NUM_SENSORS 1
+
+struct db8500_temp {
+ struct platform_device *pdev;
+ struct device *hwmon_dev;
+ unsigned char min[NUM_SENSORS];
+ unsigned char max[NUM_SENSORS];
+ unsigned char crit[NUM_SENSORS];
+ unsigned char min_alarm[NUM_SENSORS];
+ unsigned char max_alarm[NUM_SENSORS];
+ unsigned short measure_time;
+ struct delayed_work power_off_work;
+ struct mutex lock;
+ /* Delay (ms) before power off */
+ unsigned long power_off_delay;
+};
+
+static void thermal_power_off(struct work_struct *work)
+{
+ struct db8500_temp *data = container_of(work, struct db8500_temp,
+ power_off_work.work);
+
+ dev_warn(&data->pdev->dev, "Power off due to DB8500 thermal warning\n");
+ pm_power_off();
+}
+
+static ssize_t set_temp_power_off_delay(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int res;
+ unsigned long delay_in_s;
+ struct db8500_temp *data = dev_get_drvdata(dev);
+
+ res = strict_strtoul(buf, 10, &delay_in_s);
+ if (res < 0) {
+ dev_warn(&data->pdev->dev, "Set power_off_delay wrong\n");
+ return res;
+ }
+
+ mutex_lock(&data->lock);
+ data->power_off_delay = delay_in_s * 1000;
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t show_temp_power_off_delay(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct db8500_temp *data = dev_get_drvdata(dev);
+ /* return time in s, not ms */
+ return sprintf(buf, "%lu\n", (data->power_off_delay) / 1000);
+}
+
+/* HWMON sysfs interface */
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ return sprintf(buf, "db8500\n");
+}
+
+static ssize_t show_label(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ return sprintf(buf, "db8500\n");
+}
+
+/* set functions (RW nodes) */
+static ssize_t set_min(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct db8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ val &= 0xFF;
+ if (val > data->max[attr->index - 1])
+ val = data->max[attr->index - 1];
+
+ data->min[attr->index - 1] = val;
+
+ (void)prcmu_config_hotmon(data->min[attr->index - 1],
+ data->max[attr->index - 1]);
+ mutex_unlock(&data->lock);
+ return count;
+}
+
+static ssize_t set_max(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct db8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ val &= 0xFF;
+ if (val < data->min[attr->index - 1])
+ val = data->min[attr->index - 1];
+
+ data->max[attr->index - 1] = val;
+
+ (void)prcmu_config_hotmon(data->min[attr->index - 1],
+ data->max[attr->index - 1]);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t set_crit(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct db8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ val &= 0xFF;
+ data->crit[attr->index - 1] = val;
+ (void)prcmu_config_hotdog(data->crit[attr->index - 1]);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+/* start/stop temperature measurement */
+static ssize_t start_temp(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct db8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ data->measure_time = val & 0xFFFF;
+ data->min_alarm[attr->index - 1] = 0;
+ data->max_alarm[attr->index - 1] = 0;
+ mutex_unlock(&data->lock);
+
+ (void)prcmu_start_temp_sense(data->measure_time);
+ dev_dbg(&data->pdev->dev, "DB8500 thermal start measurement\n");
+ return count;
+}
+
+static ssize_t stop_temp(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct db8500_temp *data = dev_get_drvdata(dev);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ (void)prcmu_stop_temp_sense();
+ dev_dbg(&data->pdev->dev, "DB8500 thermal stop measurement\n");
+
+ return count;
+}
+
+/*
+ * show functions (RO nodes)
+ * Notice that min/max/crit refer to degrees
+ */
+static ssize_t show_min(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct db8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%d\n", data->min[attr->index - 1]);
+}
+
+static ssize_t show_max(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct db8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%d\n", data->max[attr->index - 1]);
+}
+
+static ssize_t show_crit(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct db8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%d\n", data->crit[attr->index - 1]);
+}
+
+/* Alarms */
+static ssize_t show_min_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct db8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%d\n", data->min_alarm[attr->index - 1]);
+}
+
+static ssize_t show_max_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct db8500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%d\n", data->max_alarm[attr->index - 1]);
+}
+
+/*These node are not included in the kernel hwmon sysfs interface */
+static SENSOR_DEVICE_ATTR(temp_power_off_delay, S_IRUGO | S_IWUSR,
+ show_temp_power_off_delay,
+ set_temp_power_off_delay, 0);
+
+/* Chip name, required by hwmon*/
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_start, S_IWUSR, NULL, start_temp, 1);
+static SENSOR_DEVICE_ATTR(temp1_stop, S_IWUSR, NULL, stop_temp, 1);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_min, set_min, 1);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_max, set_max, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
+ show_crit, set_crit, 1);
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_min_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_max_alarm, NULL, 1);
+
+static struct attribute *db8500_temp_attributes[] = {
+ &sensor_dev_attr_temp_power_off_delay.dev_attr.attr,
+ &sensor_dev_attr_name.dev_attr.attr,
+ &sensor_dev_attr_temp1_start.dev_attr.attr,
+ &sensor_dev_attr_temp1_stop.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group db8500_temp_group = {
+ .attrs = db8500_temp_attributes,
+};
+
+static irqreturn_t prcmu_hotmon_low_irq_handler(int irq, void *irq_data)
+{
+ struct platform_device *pdev = irq_data;
+ struct db8500_temp *data = platform_get_drvdata(pdev);
+
+ mutex_lock(&data->lock);
+ data->min_alarm[0] = 1;
+ mutex_unlock(&data->lock);
+
+ sysfs_notify(&pdev->dev.kobj, NULL, "temp1_min_alarm");
+ dev_dbg(&pdev->dev, "DB8500 thermal low warning\n");
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t prcmu_hotmon_high_irq_handler(int irq, void *irq_data)
+{
+ unsigned long delay_in_jiffies;
+ struct platform_device *pdev = irq_data;
+ struct db8500_temp *data = platform_get_drvdata(pdev);
+
+ mutex_lock(&data->lock);
+ data->max_alarm[0] = 1;
+ mutex_unlock(&data->lock);
+
+ sysfs_notify(&pdev->dev.kobj, NULL, "temp1_max_alarm");
+ dev_dbg(&pdev->dev, "DB8500 thermal warning, power off in %lu s\n",
+ (data->power_off_delay) / 1000);
+ delay_in_jiffies = msecs_to_jiffies(data->power_off_delay);
+ schedule_delayed_work(&data->power_off_work, delay_in_jiffies);
+ return IRQ_HANDLED;
+}
+
+static int __devinit db8500_temp_probe(struct platform_device *pdev)
+{
+ struct db8500_temp *data;
+ int err = 0, i;
+ int irq;
+
+ dev_dbg(&pdev->dev, "db8500_temp: Function db8500_temp_probe.\n");
+
+ data = kzalloc(sizeof(struct db8500_temp), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Get IRQ_HOTMON_LOW failed\n");
+ goto exit;
+ }
+
+ err = request_threaded_irq(irq, NULL, prcmu_hotmon_low_irq_handler,
+ IRQF_NO_SUSPEND, "db8500_temp_low", pdev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "db8500: Failed allocate HOTMON_LOW.\n");
+ goto exit;
+ } else {
+ dev_dbg(&pdev->dev, "db8500: Succeed allocate HOTMON_LOW.\n");
+ }
+
+ irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Get IRQ_HOTMON_HIGH failed\n");
+ goto exit;
+ }
+
+ err = request_threaded_irq(irq, NULL, prcmu_hotmon_high_irq_handler,
+ IRQF_NO_SUSPEND, "db8500_temp_high", pdev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "db8500: Failed allocate HOTMON_HIGH.\n");
+ goto exit;
+ } else {
+ dev_dbg(&pdev->dev, "db8500: Succeed allocate HOTMON_HIGH.\n");
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+ goto exit;
+ }
+
+ for (i = 0; i < NUM_SENSORS; i++) {
+ data->min[i] = 0;
+ data->max[i] = 0xFF;
+ data->crit[i] = 0xFF;
+ data->min_alarm[i] = 0;
+ data->max_alarm[i] = 0;
+ }
+
+ mutex_init(&data->lock);
+ INIT_DELAYED_WORK(&data->power_off_work, thermal_power_off);
+
+ data->pdev = pdev;
+ data->power_off_delay = DEFAULT_POWER_OFF_DELAY;
+ data->measure_time = DEFAULT_MEASURE_TIME;
+
+ platform_set_drvdata(pdev, data);
+
+ err = sysfs_create_group(&pdev->dev.kobj, &db8500_temp_group);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
+ goto exit_platform_data;
+ }
+
+ return 0;
+
+exit_platform_data:
+ platform_set_drvdata(pdev, NULL);
+exit:
+ kfree(data);
+ return err;
+}
+
+static int __devexit db8500_temp_remove(struct platform_device *pdev)
+{
+ struct db8500_temp *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &db8500_temp_group);
+ platform_set_drvdata(pdev, NULL);
+ kfree(data);
+ return 0;
+}
+
+/* No action required in suspend/resume, thus the lack of functions */
+static struct platform_driver db8500_temp_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "db8500_temp",
+ },
+ .probe = db8500_temp_probe,
+ .remove = __devexit_p(db8500_temp_remove),
+};
+
+static int __init db8500_temp_init(void)
+{
+ return platform_driver_register(&db8500_temp_driver);
+}
+
+static void __exit db8500_temp_exit(void)
+{
+ platform_driver_unregister(&db8500_temp_driver);
+}
+
+MODULE_AUTHOR("WenHai Fang <wenhai.h.fang@stericsson.com>");
+MODULE_DESCRIPTION("DB8500 temperature driver");
+MODULE_LICENSE("GPL");
+
+module_init(db8500_temp_init)
+module_exit(db8500_temp_exit)
diff --git a/drivers/hwmon/l3g4200d.c b/drivers/hwmon/l3g4200d.c
new file mode 100644
index 00000000000..03c73b92e7b
--- /dev/null
+++ b/drivers/hwmon/l3g4200d.c
@@ -0,0 +1,690 @@
+/*
+ * ST L3G4200D 3-Axis Gyroscope Driver
+ *
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Chethan Krishna N <chethan.krishna@stericsson.com> for ST-Ericsson
+ * Licence terms: GNU General Public Licence (GPL) version 2
+ */
+
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+
+#include <linux/l3g4200d.h>
+#include <linux/regulator/consumer.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+/* l3g4200d gyroscope registers */
+
+#define WHO_AM_I 0x0F
+
+#define CTRL_REG1 0x20 /* CTRL REG1 */
+#define CTRL_REG2 0x21 /* CTRL REG2 */
+#define CTRL_REG3 0x22 /* CTRL_REG3 */
+#define CTRL_REG4 0x23 /* CTRL_REG4 */
+#define CTRL_REG5 0x24 /* CTRL_REG5 */
+
+#define AXISDATA_REG 0x28
+
+/** Registers Contents */
+
+#define WHOAMI_L3G4200D 0x00D3 /* Expected content for WAI register*/
+
+/* CTRL_REG1 */
+#define PM_OFF 0x00
+#define PM_ON 0x01
+#define ENABLE_ALL_AXES 0x07
+#define BW00 0x00
+#define BW01 0x10
+#define BW10 0x20
+#define BW11 0x30
+#define ODR00 0x00 /* ODR = 100Hz */
+#define ODR01 0x40 /* ODR = 200Hz */
+#define ODR10 0x80 /* ODR = 400Hz */
+#define ODR11 0xC0 /* ODR = 800Hz */
+#define L3G4200D_PM_BIT 3
+#define L3G4200D_PM_MASK (0x01 << L3G4200D_PM_BIT)
+#define L3G4200D_ODR_BIT 4
+#define L3G4200D_ODR_MASK (0x0F << L3G4200D_ODR_BIT)
+#define L3G4200D_ODR_MIN_VAL 0x00
+#define L3G4200D_ODR_MAX_VAL 0x0F
+
+/* CTRL_REG4 */
+#define FS250 0x00
+#define FS500 0x01
+#define FS2000 0x03
+#define BDU_ENABLE 0x80
+#define L3G4200D_FS_BIT 6
+#define L3G4200D_FS_MASK (0x3 << L3G4200D_FS_BIT)
+
+/* multiple byte transfer enable */
+#define MULTIPLE_I2C_TR 0x80
+
+/* device status defines */
+#define DEVICE_OFF 0
+#define DEVICE_ON 1
+#define DEVICE_SUSPENDED 2
+
+/*
+ * L3G4200D gyroscope data
+ * brief structure containing gyroscope values for yaw, pitch and roll in
+ * signed short
+ */
+
+struct l3g4200d_gyro_values {
+ short x; /* x-axis angular rate data. */
+ short y; /* y-axis angluar rate data. */
+ short z; /* z-axis angular rate data. */
+};
+
+struct l3g4200d_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ struct l3g4200d_gyro_values data;
+ struct l3g4200d_gyr_platform_data pdata;
+ struct regulator *regulator;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ unsigned char powermode;
+ unsigned char odr;
+ unsigned char range;
+ int device_status;
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void l3g4200d_early_suspend(struct early_suspend *ddata);
+static void l3g4200d_late_resume(struct early_suspend *ddata);
+#endif
+
+static int l3g4200d_write(struct l3g4200d_data *ddata, u8 reg,
+ u8 val, char *msg)
+{
+ int ret = i2c_smbus_write_byte_data(ddata->client, reg, val);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_write_byte_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
+}
+
+static int l3g4200d_read(struct l3g4200d_data *ddata, u8 reg, char *msg)
+{
+ int ret = i2c_smbus_read_byte_data(ddata->client, reg);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_read_byte_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
+}
+
+static int l3g4200d_readdata(struct l3g4200d_data *ddata)
+{
+ unsigned char gyro_data[6];
+ short data[3];
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(ddata->client,
+ AXISDATA_REG | MULTIPLE_I2C_TR, 6, gyro_data);
+ if (ret < 0) {
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_read_byte_data failed error %d\
+ Register AXISDATA_REG\n", ret);
+ return ret;
+ }
+
+ data[0] = (short) (((gyro_data[1]) << 8) | gyro_data[0]);
+ data[1] = (short) (((gyro_data[3]) << 8) | gyro_data[2]);
+ data[2] = (short) (((gyro_data[5]) << 8) | gyro_data[4]);
+
+ data[ddata->pdata.axis_map_x] = ddata->pdata.negative_x ?
+ -data[ddata->pdata.axis_map_x] : data[ddata->pdata.axis_map_x];
+ data[ddata->pdata.axis_map_y] = ddata->pdata.negative_y ?
+ -data[ddata->pdata.axis_map_y] : data[ddata->pdata.axis_map_y];
+ data[ddata->pdata.axis_map_z] = ddata->pdata.negative_z ?
+ -data[ddata->pdata.axis_map_z] : data[ddata->pdata.axis_map_z];
+
+ ddata->data.x = data[ddata->pdata.axis_map_x];
+ ddata->data.y = data[ddata->pdata.axis_map_y];
+ ddata->data.z = data[ddata->pdata.axis_map_z];
+
+ return ret;
+}
+
+static ssize_t l3g4200d_show_gyrodata(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct l3g4200d_data *ddata = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->powermode == PM_OFF ||
+ ddata->device_status == DEVICE_SUSPENDED) {
+ mutex_unlock(&ddata->lock);
+ return ret;
+ }
+
+ ret = l3g4200d_readdata(ddata);
+
+ if (ret < 0) {
+ mutex_unlock(&ddata->lock);
+ return ret;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return sprintf(buf, "%8x:%8x:%8x\n", ddata->data.x, ddata->data.y,
+ ddata->data.z);
+}
+
+static ssize_t l3g4200d_show_range(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct l3g4200d_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->range);
+}
+
+static ssize_t l3g4200d_store_range(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct l3g4200d_data *ddata = platform_get_drvdata(pdev);
+ long received_value;
+ unsigned char value;
+ int error;
+
+ error = strict_strtol(buf, 0, &received_value);
+ if (error)
+ return error;
+
+ /* check if the received range is in valid range */
+ if (received_value < FS250 || received_value > FS2000)
+ return -EINVAL;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->powermode == PM_OFF) {
+ dev_info(&ddata->client->dev,
+ "The device is switched off, turn it ON using powermode\n");
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ /* enable the BDU bit */
+ value = BDU_ENABLE;
+ value |= ((received_value << L3G4200D_FS_BIT) & L3G4200D_FS_MASK);
+
+ ddata->range = received_value;
+
+ error = l3g4200d_write(ddata, CTRL_REG4, value, "CTRL_REG4");
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+ return count;
+}
+
+static ssize_t l3g4200d_show_datarate(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct l3g4200d_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->odr >> L3G4200D_ODR_BIT);
+}
+
+static ssize_t l3g4200d_store_datarate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct l3g4200d_data *ddata = platform_get_drvdata(pdev);
+ long received_value;
+ unsigned char value;
+ int error;
+
+ error = strict_strtol(buf, 0, &received_value);
+ if (error)
+ return error;
+
+ /* check if the received output datarate value is in valid range */
+ if (received_value < L3G4200D_ODR_MIN_VAL ||
+ received_value > L3G4200D_ODR_MAX_VAL)
+ return -EINVAL;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->powermode == PM_OFF) {
+ dev_info(&ddata->client->dev,
+ "The device is switched off, turn it ON using powermode\n");
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ /*
+ * read the current contents of CTRL_REG1
+ * retain any bits set other than the odr bits
+ */
+ error = l3g4200d_read(ddata, CTRL_REG1, "CTRL_REG1");
+
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ } else
+ value = error;
+
+ value &= ~L3G4200D_ODR_MASK;
+ value |= ((received_value << L3G4200D_ODR_BIT) & L3G4200D_ODR_MASK);
+
+ ddata->odr = received_value << L3G4200D_ODR_BIT;
+
+ error = l3g4200d_write(ddata, CTRL_REG1, value, "CTRL_REG1");
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+ return count;
+}
+
+static ssize_t l3g4200d_show_powermode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct l3g4200d_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->powermode);
+}
+
+static ssize_t l3g4200d_store_powermode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct l3g4200d_data *ddata = platform_get_drvdata(pdev);
+ long received_value;
+ unsigned char value;
+ int error;
+
+ error = strict_strtol(buf, 0, &received_value);
+ if (error)
+ return error;
+
+ /* check if the received power mode is either 0 or 1 */
+ if (received_value < PM_OFF || received_value > PM_ON)
+ return -EINVAL;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->device_status == DEVICE_SUSPENDED &&
+ received_value == PM_OFF) {
+ ddata->powermode = received_value;
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ /* if sent value is same as current value do nothing */
+ if (ddata->powermode == received_value) {
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ /* turn on the power suppliy if it was turned off previously */
+ if (ddata->regulator && ddata->powermode == PM_OFF
+ && (ddata->device_status == DEVICE_OFF
+ || ddata->device_status == DEVICE_SUSPENDED)) {
+ regulator_enable(ddata->regulator);
+ ddata->device_status = DEVICE_ON;
+ }
+
+ /*
+ * read the current contents of CTRL_REG1
+ * retain any bits set other than the power bit
+ */
+ error = l3g4200d_read(ddata, CTRL_REG1, "CTRL_REG1");
+
+ if (error < 0) {
+ if (ddata->regulator && ddata->device_status == DEVICE_ON) {
+ regulator_disable(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ mutex_unlock(&ddata->lock);
+ return error;
+ } else
+ value = error;
+
+ value &= ~L3G4200D_PM_MASK;
+ value |= ((received_value << L3G4200D_PM_BIT) & L3G4200D_PM_MASK);
+
+ ddata->powermode = received_value;
+
+ error = l3g4200d_write(ddata, CTRL_REG1, value, "CTRL_REG1");
+ if (error < 0) {
+ if (ddata->regulator && ddata->device_status == DEVICE_ON) {
+ regulator_disable(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ if (received_value == PM_OFF) {
+ /* set the other configuration values to defaults */
+ ddata->odr = ODR00 | BW00;
+ ddata->range = FS250;
+
+ /* turn off the power supply */
+ if (ddata->regulator && ddata->device_status == DEVICE_ON) {
+ regulator_disable(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ }
+ mutex_unlock(&ddata->lock);
+ return count;
+}
+
+static DEVICE_ATTR(gyrodata, S_IRUGO, l3g4200d_show_gyrodata, NULL);
+
+static DEVICE_ATTR(range, S_IRUGO | S_IWUGO,
+ l3g4200d_show_range, l3g4200d_store_range);
+
+static DEVICE_ATTR(datarate, S_IRUGO | S_IWUGO,
+ l3g4200d_show_datarate, l3g4200d_store_datarate);
+
+static DEVICE_ATTR(powermode, S_IRUGO | S_IWUGO,
+ l3g4200d_show_powermode, l3g4200d_store_powermode);
+
+static struct attribute *l3g4200d_attributes[] = {
+ &dev_attr_gyrodata.attr,
+ &dev_attr_range.attr,
+ &dev_attr_datarate.attr,
+ &dev_attr_powermode.attr,
+ NULL
+};
+
+static const struct attribute_group l3g4200d_attr_group = {
+ .attrs = l3g4200d_attributes,
+};
+
+static int __devinit l3g4200d_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ int ret = -1;
+ struct l3g4200d_data *ddata = NULL;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+ goto exit;
+
+ ddata = kzalloc(sizeof(struct l3g4200d_data), GFP_KERNEL);
+ if (ddata == NULL) {
+ ret = -ENOMEM;
+ goto error_op_failed;
+ }
+
+ ddata->client = client;
+ i2c_set_clientdata(client, ddata);
+
+ memcpy(&ddata->pdata, client->dev.platform_data, sizeof(ddata->pdata));
+ /* store default values in the data structure */
+ ddata->odr = ODR00 | BW00;
+ ddata->range = FS250;
+ ddata->powermode = PM_OFF;
+ ddata->device_status = DEVICE_OFF;
+
+ dev_set_name(&client->dev, ddata->pdata.name_gyr);
+
+ ddata->regulator = regulator_get(&client->dev, "v-gyro");
+ if (IS_ERR(ddata->regulator)) {
+ dev_err(&client->dev, "failed to get regulator\n");
+ ret = PTR_ERR(ddata->regulator);
+ ddata->regulator = NULL;
+ }
+
+ if (ddata->regulator) {
+ regulator_enable(ddata->regulator);
+ ddata->device_status = DEVICE_ON;
+ }
+
+ ret = l3g4200d_read(ddata, WHO_AM_I, "WHO_AM_I");
+ if (ret < 0)
+ goto exit_free_regulator;
+
+ if (ret == WHOAMI_L3G4200D)
+ dev_info(&client->dev, "3-Axis Gyroscope device identification: %d\n", ret);
+ else
+ dev_info(&client->dev, "Gyroscope identification did not match\n");
+
+ mutex_init(&ddata->lock);
+
+ ret = sysfs_create_group(&client->dev.kobj, &l3g4200d_attr_group);
+ if (ret)
+ goto exit_free_regulator;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ddata->early_suspend.level =
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ddata->early_suspend.suspend = l3g4200d_early_suspend;
+ ddata->early_suspend.resume = l3g4200d_late_resume;
+ register_early_suspend(&ddata->early_suspend);
+#endif
+
+ /*
+ * turn off the supplies until somebody turns on the device
+ * using l3g4200d_store_powermode
+ */
+ if (ddata->device_status == DEVICE_ON && ddata->regulator) {
+ regulator_disable(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+
+ return ret;
+
+exit_free_regulator:
+ if (ddata->device_status == DEVICE_ON && ddata->regulator) {
+ regulator_disable(ddata->regulator);
+ regulator_put(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+error_op_failed:
+ kfree(ddata);
+exit:
+ dev_err(&client->dev, "probe function failed %x\n", ret);
+ return ret;
+}
+
+static int __devexit l3g4200d_remove(struct i2c_client *client)
+{
+ struct l3g4200d_data *ddata;
+ ddata = i2c_get_clientdata(client);
+ sysfs_remove_group(&client->dev.kobj, &l3g4200d_attr_group);
+
+ /* safer to turn off the device */
+ if (ddata->powermode != PM_OFF) {
+ l3g4200d_write(ddata, CTRL_REG1, PM_OFF, "CONTROL");
+ if (ddata->regulator && ddata->device_status == DEVICE_ON) {
+ regulator_disable(ddata->regulator);
+ regulator_put(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ }
+
+ i2c_set_clientdata(client, NULL);
+ kfree(ddata);
+
+ return 0;
+}
+
+static int l3g4200d_do_suspend(struct l3g4200d_data *ddata)
+{
+ int ret;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->powermode == PM_OFF) {
+ mutex_unlock(&ddata->lock);
+ return 0;
+ }
+
+ ret = l3g4200d_write(ddata, CTRL_REG1, PM_OFF, "CONTROL");
+
+ /* turn off the power when suspending the device */
+ if (ddata->regulator)
+ regulator_disable(ddata->regulator);
+
+ ddata->device_status = DEVICE_SUSPENDED;
+
+ mutex_unlock(&ddata->lock);
+ return ret;
+}
+
+static int l3g4200d_do_resume(struct l3g4200d_data *ddata)
+{
+ unsigned char range_value;
+ unsigned char shifted_powermode = (ddata->powermode << L3G4200D_PM_BIT);
+ unsigned char shifted_odr = (ddata->odr << L3G4200D_ODR_BIT);
+ unsigned context = ((shifted_powermode | shifted_odr) | ENABLE_ALL_AXES);
+ int ret = 0;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->device_status == DEVICE_ON)
+ goto fail;
+
+ /* in correct mode, no need to change it */
+ if (ddata->powermode == PM_OFF) {
+ ddata->device_status = DEVICE_OFF;
+ goto fail;
+ } else {
+ ddata->device_status = DEVICE_ON;
+ }
+
+ /* turn on the power when resuming the device */
+ if (ddata->regulator)
+ regulator_enable(ddata->regulator);
+
+ ret = l3g4200d_write(ddata, CTRL_REG1, context, "CONTROL");
+ if (ret < 0)
+ goto fail;
+
+ range_value = ddata->range;
+ range_value <<= L3G4200D_FS_BIT;
+ range_value |= BDU_ENABLE;
+
+ ret = l3g4200d_write(ddata, CTRL_REG4, range_value, "RANGE");
+
+fail:
+ mutex_unlock(&ddata->lock);
+ return ret;
+}
+
+#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM))
+static int l3g4200d_suspend(struct device *dev)
+{
+ struct l3g4200d_data *ddata;
+ int ret;
+
+ ddata = dev_get_drvdata(dev);
+
+ ret = l3g4200d_do_suspend(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while suspending the device\n");
+
+ return ret;
+}
+
+static int l3g4200d_resume(struct device *dev)
+{
+ struct l3g4200d_data *ddata;
+ int ret;
+
+ ddata = dev_get_drvdata(dev);
+
+ ret = l3g4200d_do_resume(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while resuming the device\n");
+
+ return ret;
+}
+
+static const struct dev_pm_ops l3g4200d_dev_pm_ops = {
+ .suspend = l3g4200d_suspend,
+ .resume = l3g4200d_resume,
+};
+#else
+static void l3g4200d_early_suspend(struct early_suspend *data)
+{
+ struct l3g4200d_data *ddata =
+ container_of(data, struct l3g4200d_data, early_suspend);
+ int ret;
+
+ ret = l3g4200d_do_suspend(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while suspending the device\n");
+}
+
+static void l3g4200d_late_resume(struct early_suspend *data)
+{
+ struct l3g4200d_data *ddata =
+ container_of(data, struct l3g4200d_data, early_suspend);
+ int ret;
+
+ ret = l3g4200d_do_resume(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while resuming the device\n");
+}
+#endif
+
+static const struct i2c_device_id l3g4200d_id[] = {
+ {"l3g4200d", 0 },
+ { },
+};
+
+static struct i2c_driver l3g4200d_driver = {
+ .driver = {
+ .name = "l3g4200d",
+#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM))
+ .pm = &l3g4200d_dev_pm_ops,
+#endif
+ },
+ .probe = l3g4200d_probe,
+ .remove = l3g4200d_remove,
+ .id_table = l3g4200d_id,
+};
+
+static int __init l3g4200d_init(void)
+{
+ return i2c_add_driver(&l3g4200d_driver);
+}
+
+static void __exit l3g4200d_exit(void)
+{
+ i2c_del_driver(&l3g4200d_driver);
+}
+
+module_init(l3g4200d_init);
+module_exit(l3g4200d_exit);
+
+MODULE_DESCRIPTION("l3g4200d digital gyroscope driver");
+MODULE_AUTHOR("Chethan Krishna N");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lsm303dlh_a.c b/drivers/hwmon/lsm303dlh_a.c
new file mode 100644
index 00000000000..8c2b750ed51
--- /dev/null
+++ b/drivers/hwmon/lsm303dlh_a.c
@@ -0,0 +1,1310 @@
+/*
+ * lsm303dlh_a.c
+ * ST 3-Axis Accelerometer Driver
+ *
+ * Copyright (C) 2010 STMicroelectronics
+ * Author: Carmine Iascone (carmine.iascone@st.com)
+ * Author: Matteo Dameno (matteo.dameno@st.com)
+ *
+ * Copyright (C) 2010 STEricsson
+ * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ * Updated:Preetham Rao Kaskurthi <preetham.rao@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <mach/gpio.h>
+#endif
+
+#include <linux/lsm303dlh.h>
+#include <linux/earlysuspend.h>
+#include <linux/regulator/consumer.h>
+
+ /* lsm303dlh accelerometer registers */
+ #define WHO_AM_I 0x0F
+
+ /* ctrl 1: pm2 pm1 pm0 dr1 dr0 zenable yenable zenable */
+ #define CTRL_REG1 0x20 /* power control reg */
+ #define CTRL_REG2 0x21 /* power control reg */
+ #define CTRL_REG3 0x22 /* power control reg */
+ #define CTRL_REG4 0x23 /* interrupt control reg */
+ #define CTRL_REG5 0x24 /* interrupt control reg */
+
+ #define STATUS_REG 0x27 /* status register */
+
+ #define AXISDATA_REG 0x28 /* axis data */
+
+ #define INT1_CFG 0x30 /* interrupt 1 configuration */
+ #define INT1_SRC 0x31 /* interrupt 1 source reg */
+ #define INT1_THS 0x32 /* interrupt 1 threshold */
+ #define INT1_DURATION 0x33 /* interrupt 1 threshold */
+
+ #define INT2_CFG 0x34 /* interrupt 2 configuration */
+ #define INT2_SRC 0x35 /* interrupt 2 source reg */
+ #define INT2_THS 0x36 /* interrupt 2 threshold */
+ #define INT2_DURATION 0x37 /* interrupt 2 threshold */
+
+ /* Sensitivity adjustment */
+ #define SHIFT_ADJ_2G 4 /* 1/16*/
+ #define SHIFT_ADJ_4G 3 /* 2/16*/
+ #define SHIFT_ADJ_8G 2 /* ~3.9/16*/
+
+ /* Control register 1 */
+ #define LSM303DLH_A_CR1_PM_BIT 5
+ #define LSM303DLH_A_CR1_PM_MASK (0x7 << LSM303DLH_A_CR1_PM_BIT)
+ #define LSM303DLH_A_CR1_DR_BIT 3
+ #define LSM303DLH_A_CR1_DR_MASK (0x3 << LSM303DLH_A_CR1_DR_BIT)
+ #define LSM303DLH_A_CR1_EN_BIT 0
+ #define LSM303DLH_A_CR1_EN_MASK (0x7 << LSM303DLH_A_CR1_EN_BIT)
+ #define LSM303DLH_A_CR1_AXIS_ENABLE 7
+
+ /* Control register 2 */
+ #define LSM303DLH_A_CR4_ST_BIT 1
+ #define LSM303DLH_A_CR4_ST_MASK (0x1 << LSM303DLH_A_CR4_ST_BIT)
+ #define LSM303DLH_A_CR4_STS_BIT 3
+ #define LSM303DLH_A_CR4_STS_MASK (0x1 << LSM303DLH_A_CR4_STS_BIT)
+ #define LSM303DLH_A_CR4_FS_BIT 4
+ #define LSM303DLH_A_CR4_FS_MASK (0x3 << LSM303DLH_A_CR4_FS_BIT)
+ #define LSM303DLH_A_CR4_BLE_BIT 6
+ #define LSM303DLH_A_CR4_BLE_MASK (0x3 << LSM303DLH_A_CR4_BLE_BIT)
+ #define LSM303DLH_A_CR4_BDU_BIT 7
+ #define LSM303DLH_A_CR4_BDU_MASK (0x1 << LSM303DLH_A_CR4_BDU_BIT)
+
+ /* Control register 3 */
+ #define LSM303DLH_A_CR3_I1_BIT 0
+ #define LSM303DLH_A_CR3_I1_MASK (0x3 << LSM303DLH_A_CR3_I1_BIT)
+ #define LSM303DLH_A_CR3_LIR1_BIT 2
+ #define LSM303DLH_A_CR3_LIR1_MASK (0x1 << LSM303DLH_A_CR3_LIR1_BIT)
+ #define LSM303DLH_A_CR3_I2_BIT 3
+ #define LSM303DLH_A_CR3_I2_MASK (0x3 << LSM303DLH_A_CR3_I2_BIT)
+ #define LSM303DLH_A_CR3_LIR2_BIT 5
+ #define LSM303DLH_A_CR3_LIR2_MASK (0x1 << LSM303DLH_A_CR3_LIR2_BIT)
+ #define LSM303DLH_A_CR3_PPOD_BIT 6
+ #define LSM303DLH_A_CR3_PPOD_MASK (0x1 << LSM303DLH_A_CR3_PPOD_BIT)
+ #define LSM303DLH_A_CR3_IHL_BIT 7
+ #define LSM303DLH_A_CR3_IHL_MASK (0x1 << LSM303DLH_A_CR3_IHL_BIT)
+
+ #define LSM303DLH_A_CR3_I_SELF 0x0
+ #define LSM303DLH_A_CR3_I_OR 0x1
+ #define LSM303DLH_A_CR3_I_DATA 0x2
+ #define LSM303DLH_A_CR3_I_BOOT 0x3
+
+ #define LSM303DLH_A_CR3_LIR_LATCH 0x1
+
+ /* Range */
+ #define LSM303DLH_A_RANGE_2G 0x00
+ #define LSM303DLH_A_RANGE_4G 0x01
+ #define LSM303DLH_A_RANGE_8G 0x03
+
+ /* Mode */
+ #define LSM303DLH_A_MODE_OFF 0x00
+ #define LSM303DLH_A_MODE_NORMAL 0x01
+ #define LSM303DLH_A_MODE_LP_HALF 0x02
+ #define LSM303DLH_A_MODE_LP_1 0x03
+ #define LSM303DLH_A_MODE_LP_2 0x02
+ #define LSM303DLH_A_MODE_LP_5 0x05
+ #define LSM303DLH_A_MODE_LP_10 0x06
+
+ /* Rate */
+ #define LSM303DLH_A_RATE_50 0x00
+ #define LSM303DLH_A_RATE_100 0x01
+ #define LSM303DLH_A_RATE_400 0x02
+ #define LSM303DLH_A_RATE_1000 0x03
+
+ /* Sleep & Wake */
+ #define LSM303DLH_A_SLEEPWAKE_DISABLE 0x00
+ #define LSM303DLH_A_SLEEPWAKE_ENABLE 0x3
+
+/* Multiple byte transfer enable */
+#define MULTIPLE_I2C_TR 0x80
+
+/* device status defines */
+#define DEVICE_OFF 0
+#define DEVICE_ON 1
+#define DEVICE_SUSPENDED 2
+
+/* Range -2048 to 2047 */
+struct lsm303dlh_a_t {
+ short x;
+ short y;
+ short z;
+};
+
+/*
+ * accelerometer local data
+ */
+struct lsm303dlh_a_data {
+ struct i2c_client *client;
+ /* lock for sysfs operations */
+ struct mutex lock;
+ struct lsm303dlh_a_t data;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ struct input_dev *input_dev;
+ struct input_dev *input_dev2;
+#endif
+
+ struct lsm303dlh_platform_data pdata;
+ struct regulator *regulator;
+
+ unsigned char range;
+ unsigned char mode;
+ unsigned char rate;
+ unsigned char sleep_wake;
+ int shift_adjust;
+
+ unsigned char interrupt_control;
+ unsigned int interrupt_channel;
+
+ unsigned char interrupt_configure[2];
+ unsigned char interrupt_duration[2];
+ unsigned char interrupt_threshold[2];
+ struct early_suspend early_suspend;
+ int device_status;
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void lsm303dlh_a_early_suspend(struct early_suspend *data);
+static void lsm303dlh_a_late_resume(struct early_suspend *data);
+#endif
+
+static int lsm303dlh_a_write(struct lsm303dlh_a_data *ddata, u8 reg,
+ u8 val, char *msg)
+{
+ int ret = i2c_smbus_write_byte_data(ddata->client, reg, val);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_write_byte_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
+}
+
+static int lsm303dlh_a_read(struct lsm303dlh_a_data *ddata, u8 reg, char *msg)
+{
+ int ret = i2c_smbus_read_byte_data(ddata->client, reg);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_read_byte_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
+}
+
+static int lsm303dlh_a_do_suspend(struct lsm303dlh_a_data *ddata)
+{
+ int ret;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ mutex_unlock(&ddata->lock);
+ return 0;
+ }
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a1));
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a2));
+#endif
+
+ ret = lsm303dlh_a_write(ddata, CTRL_REG1,
+ LSM303DLH_A_MODE_OFF, "CONTROL");
+
+ if (ddata->regulator)
+ regulator_disable(ddata->regulator);
+
+ ddata->device_status = DEVICE_SUSPENDED;
+
+ mutex_unlock(&ddata->lock);
+
+ return ret;
+}
+
+static int lsm303dlh_a_restore(struct lsm303dlh_a_data *ddata)
+{
+ unsigned char reg;
+ unsigned char shifted_mode = (ddata->mode << LSM303DLH_A_CR1_PM_BIT);
+ unsigned char shifted_rate = (ddata->rate << LSM303DLH_A_CR1_DR_BIT);
+ unsigned char context = (shifted_mode | shifted_rate);
+ int ret = 0;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->device_status == DEVICE_ON) {
+ mutex_unlock(&ddata->lock);
+ return 0;
+ }
+
+ /* in correct mode, no need to change it */
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ ddata->device_status = DEVICE_OFF;
+ mutex_unlock(&ddata->lock);
+ return 0;
+ } else
+ ddata->device_status = DEVICE_ON;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ enable_irq(gpio_to_irq(ddata->pdata.irq_a1));
+ enable_irq(gpio_to_irq(ddata->pdata.irq_a2));
+#endif
+
+ if (ddata->regulator)
+ regulator_enable(ddata->regulator);
+
+ /* BDU should be enabled by default/recommened */
+ reg = ddata->range;
+ reg |= LSM303DLH_A_CR4_BDU_MASK;
+ context |= LSM303DLH_A_CR1_AXIS_ENABLE;
+
+ ret = lsm303dlh_a_write(ddata, CTRL_REG1, context,
+ "CTRL_REG1");
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, CTRL_REG4, reg, "CTRL_REG4");
+
+ if (ret < 0)
+ goto fail;
+
+ /* write to the boot bit to reboot memory content */
+ ret = lsm303dlh_a_write(ddata, CTRL_REG2, 0x80, "CTRL_REG2");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, CTRL_REG3, ddata->interrupt_control,
+ "CTRL_REG3");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, INT1_CFG, ddata->interrupt_configure[0],
+ "INT1_CFG");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, INT2_CFG, ddata->interrupt_configure[1],
+ "INT2_CFG");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, INT1_THS, ddata->interrupt_threshold[0],
+ "INT1_THS");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, INT2_THS, ddata->interrupt_threshold[1],
+ "INT2_THS");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, INT1_DURATION,
+ ddata->interrupt_duration[0], "INT1_DURATION");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_a_write(ddata, INT1_DURATION,
+ ddata->interrupt_duration[1], "INT1_DURATION");
+
+ if (ret < 0)
+ goto fail;
+
+fail:
+ if (ret < 0)
+ dev_err(&ddata->client->dev, "could not restore the device %d\n", ret);
+ mutex_unlock(&ddata->lock);
+ return ret;
+}
+
+
+static int lsm303dlh_a_readdata(struct lsm303dlh_a_data *ddata)
+{
+ unsigned char acc_data[6];
+ short data[3];
+
+ int ret = i2c_smbus_read_i2c_block_data(ddata->client,
+ AXISDATA_REG | MULTIPLE_I2C_TR, 6, acc_data);
+ if (ret < 0) {
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_read_byte_data failed error %d\
+ Register AXISDATA_REG \n", ret);
+ return ret;
+ }
+
+ data[0] = (short) (((acc_data[1]) << 8) | acc_data[0]);
+ data[1] = (short) (((acc_data[3]) << 8) | acc_data[2]);
+ data[2] = (short) (((acc_data[5]) << 8) | acc_data[4]);
+
+ data[0] >>= ddata->shift_adjust;
+ data[1] >>= ddata->shift_adjust;
+ data[2] >>= ddata->shift_adjust;
+
+ /* taking position and orientation of x,y,z axis into account*/
+
+ data[ddata->pdata.axis_map_x] = ddata->pdata.negative_x ?
+ -data[ddata->pdata.axis_map_x] : data[ddata->pdata.axis_map_x];
+ data[ddata->pdata.axis_map_y] = ddata->pdata.negative_y ?
+ -data[ddata->pdata.axis_map_y] : data[ddata->pdata.axis_map_y];
+ data[ddata->pdata.axis_map_z] = ddata->pdata.negative_z ?
+ -data[ddata->pdata.axis_map_z] : data[ddata->pdata.axis_map_z];
+
+ ddata->data.x = data[ddata->pdata.axis_map_x];
+ ddata->data.y = data[ddata->pdata.axis_map_y];
+ ddata->data.z = data[ddata->pdata.axis_map_z];
+
+ return ret;
+}
+
+static ssize_t lsm303dlh_a_show_data(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF ||
+ ddata->device_status == DEVICE_SUSPENDED) {
+ mutex_unlock(&ddata->lock);
+ return ret;
+ }
+
+ ret = lsm303dlh_a_readdata(ddata);
+
+ if (ret < 0) {
+ mutex_unlock(&ddata->lock);
+ return ret;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return sprintf(buf, "%8x:%8x:%8x\n", ddata->data.x, ddata->data.y,
+ ddata->data.z);
+}
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+static irqreturn_t lsm303dlh_a_gpio_irq(int irq, void *device_data)
+{
+
+ struct lsm303dlh_a_data *ddata = device_data;
+ int ret;
+ unsigned char reg;
+ struct input_dev *input;
+
+ /* know your interrupt source */
+ if (irq == gpio_to_irq(ddata->pdata.irq_a1)) {
+ reg = INT1_SRC;
+ input = ddata->input_dev;
+ } else if (irq == gpio_to_irq(ddata->pdata.irq_a2)) {
+ reg = INT2_SRC;
+ input = ddata->input_dev2;
+ } else {
+ dev_err(&ddata->client->dev, "spurious interrupt");
+ return IRQ_HANDLED;
+ }
+
+ /* read the axis */
+ ret = lsm303dlh_a_readdata(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "reading data of xyz failed error %d\n", ret);
+
+ input_report_abs(input, ABS_X, ddata->data.x);
+ input_report_abs(input, ABS_Y, ddata->data.y);
+ input_report_abs(input, ABS_Z, ddata->data.z);
+ input_sync(input);
+
+ /* clear the value by reading it */
+ ret = lsm303dlh_a_read(ddata, reg, "INTTERUPT SOURCE");
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "clearing interrupt source failed error %d\n", ret);
+
+ return IRQ_HANDLED;
+
+}
+#endif
+
+static ssize_t lsm303dlh_a_show_interrupt_control(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->interrupt_control);
+}
+
+static ssize_t lsm303dlh_a_store_interrupt_control(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ ddata->interrupt_control = val;
+
+ error = lsm303dlh_a_write(ddata, CTRL_REG3, val, "CTRL_REG3");
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
+}
+
+static ssize_t lsm303dlh_a_show_interrupt_channel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->interrupt_channel);
+}
+
+static ssize_t lsm303dlh_a_store_interrupt_channel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
+
+ ddata->interrupt_channel = val;
+
+ return count;
+}
+
+static ssize_t lsm303dlh_a_show_interrupt_configure(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n",
+ ddata->interrupt_configure[ddata->interrupt_channel]);
+}
+
+static ssize_t lsm303dlh_a_store_interrupt_configure(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ ddata->interrupt_configure[ddata->interrupt_channel] = val;
+
+ if (ddata->interrupt_channel == 0x0)
+ error = lsm303dlh_a_write(ddata, INT1_CFG, val, "INT1_CFG");
+ else
+ error = lsm303dlh_a_write(ddata, INT2_CFG, val, "INT2_CFG");
+
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
+}
+
+static ssize_t lsm303dlh_a_show_interrupt_duration(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n",
+ ddata->interrupt_duration[ddata->interrupt_channel]);
+}
+
+static ssize_t lsm303dlh_a_store_interrupt_duration(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ddata->lock);
+
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ ddata->interrupt_duration[ddata->interrupt_channel] = val;
+
+ if (ddata->interrupt_channel == 0x0)
+ error = lsm303dlh_a_write(ddata, INT1_DURATION, val,
+ "INT1_DURATION");
+ else
+ error = lsm303dlh_a_write(ddata, INT2_DURATION, val,
+ "INT2_DURATION");
+
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
+}
+
+static ssize_t lsm303dlh_a_show_interrupt_threshold(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n",
+ ddata->interrupt_threshold[ddata->interrupt_channel]);
+}
+
+static ssize_t lsm303dlh_a_store_interrupt_threshold(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ ddata->interrupt_threshold[ddata->interrupt_channel] = val;
+
+ if (ddata->interrupt_channel == 0x0)
+ error = lsm303dlh_a_write(ddata, INT1_THS, val, "INT1_THS");
+ else
+ error = lsm303dlh_a_write(ddata, INT2_THS, val, "INT2_THS");
+
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
+}
+
+static ssize_t lsm303dlh_a_show_range(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->range >> LSM303DLH_A_CR4_FS_BIT);
+}
+
+static ssize_t lsm303dlh_a_store_range(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ long val;
+ unsigned long bdu_enabled_val;
+ int error;
+
+
+ error = strict_strtol(buf, 0, &val);
+ if (error)
+ return error;
+
+ if (val < LSM303DLH_A_RANGE_2G || val > LSM303DLH_A_RANGE_8G)
+ return -EINVAL;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ ddata->range = val;
+ ddata->range <<= LSM303DLH_A_CR4_FS_BIT;
+
+ /*
+ * Block mode update is recommended for not
+ * ending up reading different values
+ */
+ bdu_enabled_val = ddata->range;
+ bdu_enabled_val |= LSM303DLH_A_CR4_BDU_MASK;
+
+ error = lsm303dlh_a_write(ddata, CTRL_REG4, bdu_enabled_val,
+ "CTRL_REG4");
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ switch (val) {
+ case LSM303DLH_A_RANGE_2G:
+ ddata->shift_adjust = SHIFT_ADJ_2G;
+ break;
+ case LSM303DLH_A_RANGE_4G:
+ ddata->shift_adjust = SHIFT_ADJ_4G;
+ break;
+ case LSM303DLH_A_RANGE_8G:
+ ddata->shift_adjust = SHIFT_ADJ_8G;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
+}
+
+static ssize_t lsm303dlh_a_show_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->mode);
+}
+
+static ssize_t lsm303dlh_a_store_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ long val;
+ unsigned char data;
+ int error;
+ bool set_boot_bit = false;
+
+ error = strict_strtol(buf, 0, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ddata->lock);
+
+ /* not in correct range */
+
+ if (val < LSM303DLH_A_MODE_OFF || val > LSM303DLH_A_MODE_LP_10) {
+ mutex_unlock(&ddata->lock);
+ return -EINVAL;
+ }
+
+ if (ddata->device_status == DEVICE_SUSPENDED) {
+ if (val == LSM303DLH_A_MODE_OFF) {
+ ddata->mode = val;
+ mutex_unlock(&ddata->lock);
+ return count;
+ } else {
+ /* device is turning on after suspend, reset memory */
+ set_boot_bit = true;
+ }
+ }
+
+ /* if same mode as existing, return */
+ if (ddata->mode == val) {
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ /* turn on the supplies if already off */
+ if (ddata->regulator && ddata->mode == LSM303DLH_A_MODE_OFF
+ && (ddata->device_status == DEVICE_OFF
+ || ddata->device_status == DEVICE_SUSPENDED)) {
+ regulator_enable(ddata->regulator);
+ ddata->device_status = DEVICE_ON;
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ enable_irq(gpio_to_irq(ddata->pdata.irq_a1));
+ enable_irq(gpio_to_irq(ddata->pdata.irq_a2));
+#endif
+ }
+
+ data = lsm303dlh_a_read(ddata, CTRL_REG1, "CTRL_REG1");
+
+ data &= ~LSM303DLH_A_CR1_PM_MASK;
+
+ ddata->mode = val;
+
+ data |= ((val << LSM303DLH_A_CR1_PM_BIT) & LSM303DLH_A_CR1_PM_MASK);
+
+ error = lsm303dlh_a_write(ddata, CTRL_REG1, data, "CTRL_REG1");
+ if (error < 0) {
+ if (ddata->regulator && ddata->device_status == DEVICE_ON) {
+ regulator_disable(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ /*
+ * Power on request when device is in suspended state
+ * write to the boot bit in CTRL_REG2 to reboot memory content
+ * and ensure correct device behavior after it resumes
+ */
+ if (set_boot_bit) {
+ error = lsm303dlh_a_write(ddata, CTRL_REG2, 0x80, "CTRL_REG2");
+ if (error < 0) {
+ if (ddata->regulator && ddata->device_status == DEVICE_ON) {
+ regulator_disable(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+ }
+
+ if (val == LSM303DLH_A_MODE_OFF) {
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a1));
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a2));
+#endif
+ /*
+ * No need to store context here
+ * it is not like suspend/resume
+ * but fall back to default values
+ */
+ ddata->rate = LSM303DLH_A_RATE_50;
+ ddata->range = LSM303DLH_A_RANGE_2G;
+ ddata->shift_adjust = SHIFT_ADJ_2G;
+
+ if (ddata->regulator && ddata->device_status == DEVICE_ON) {
+ regulator_disable(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ }
+ mutex_unlock(&ddata->lock);
+
+ return count;
+}
+
+static ssize_t lsm303dlh_a_show_rate(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->rate);
+}
+
+static ssize_t lsm303dlh_a_store_rate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ long val;
+ unsigned char data;
+ int error;
+
+ error = strict_strtol(buf, 0, &val);
+ if (error)
+ return error;
+
+ if (val < LSM303DLH_A_RATE_50 || val > LSM303DLH_A_RATE_1000)
+ return -EINVAL;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ data = lsm303dlh_a_read(ddata, CTRL_REG1, "CTRL_REG1");
+
+ data &= ~LSM303DLH_A_CR1_DR_MASK;
+
+ ddata->rate = val;
+
+ data |= ((val << LSM303DLH_A_CR1_DR_BIT) & LSM303DLH_A_CR1_DR_MASK);
+
+ error = lsm303dlh_a_write(ddata, CTRL_REG1, data, "CTRL_REG1");
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
+}
+
+static ssize_t lsm303dlh_a_show_sleepwake(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->sleep_wake);
+}
+
+static ssize_t lsm303dlh_a_store_sleepwake(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_a_data *ddata = platform_get_drvdata(pdev);
+ long val;
+ int error;
+
+ if (ddata->mode == LSM303DLH_A_MODE_OFF) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ return count;
+ }
+
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ddata->lock);
+
+ ddata->sleep_wake = val;
+
+ error = lsm303dlh_a_write(ddata, CTRL_REG5, ddata->sleep_wake,
+ "CTRL_REG5");
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
+}
+
+static DEVICE_ATTR(data, S_IRUGO, lsm303dlh_a_show_data, NULL);
+
+static DEVICE_ATTR(range, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_range, lsm303dlh_a_store_range);
+
+static DEVICE_ATTR(mode, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_mode, lsm303dlh_a_store_mode);
+
+static DEVICE_ATTR(rate, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_rate, lsm303dlh_a_store_rate);
+
+static DEVICE_ATTR(sleep_wake, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_sleepwake, lsm303dlh_a_store_sleepwake);
+
+static DEVICE_ATTR(interrupt_control, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_interrupt_control,
+ lsm303dlh_a_store_interrupt_control);
+
+static DEVICE_ATTR(interrupt_channel, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_interrupt_channel,
+ lsm303dlh_a_store_interrupt_channel);
+
+static DEVICE_ATTR(interrupt_configure, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_interrupt_configure,
+ lsm303dlh_a_store_interrupt_configure);
+
+static DEVICE_ATTR(interrupt_duration, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_interrupt_duration,
+ lsm303dlh_a_store_interrupt_duration);
+
+static DEVICE_ATTR(interrupt_threshold, S_IWUGO | S_IRUGO,
+ lsm303dlh_a_show_interrupt_threshold,
+ lsm303dlh_a_store_interrupt_threshold);
+
+static struct attribute *lsm303dlh_a_attributes[] = {
+ &dev_attr_data.attr,
+ &dev_attr_range.attr,
+ &dev_attr_mode.attr,
+ &dev_attr_rate.attr,
+ &dev_attr_sleep_wake.attr,
+ &dev_attr_interrupt_control.attr,
+ &dev_attr_interrupt_channel.attr,
+ &dev_attr_interrupt_configure.attr,
+ &dev_attr_interrupt_duration.attr,
+ &dev_attr_interrupt_threshold.attr,
+ NULL
+};
+
+static const struct attribute_group lsm303dlh_a_attr_group = {
+ .attrs = lsm303dlh_a_attributes,
+};
+
+static int __devinit lsm303dlh_a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct lsm303dlh_a_data *ddata = NULL;
+
+ ddata = kzalloc(sizeof(struct lsm303dlh_a_data), GFP_KERNEL);
+ if (ddata == NULL) {
+ ret = -ENOMEM;
+ goto err_op_failed;
+ }
+
+ ddata->client = client;
+ i2c_set_clientdata(client, ddata);
+
+ /* copy platform specific data */
+ memcpy(&ddata->pdata, client->dev.platform_data, sizeof(ddata->pdata));
+ ddata->mode = LSM303DLH_A_MODE_OFF;
+ ddata->rate = LSM303DLH_A_RATE_50;
+ ddata->range = LSM303DLH_A_RANGE_2G;
+ ddata->sleep_wake = LSM303DLH_A_SLEEPWAKE_DISABLE;
+ ddata->shift_adjust = SHIFT_ADJ_2G;
+ ddata->device_status = DEVICE_OFF;
+ dev_set_name(&client->dev, ddata->pdata.name_a);
+
+ ddata->regulator = regulator_get(&client->dev, "v-accel");
+ if (IS_ERR(ddata->regulator)) {
+ dev_err(&client->dev, "failed to get regulator\n");
+ ret = PTR_ERR(ddata->regulator);
+ ddata->regulator = NULL;
+ }
+
+ if (ddata->regulator) {
+ regulator_enable(ddata->regulator);
+ ddata->device_status = DEVICE_ON;
+ }
+
+ ret = lsm303dlh_a_read(ddata, WHO_AM_I, "WHO_AM_I");
+ if (ret < 0)
+ goto exit_free_regulator;
+
+ dev_info(&client->dev, "3-Axis Accelerometer, ID : %d\n",
+ ret);
+
+ mutex_init(&ddata->lock);
+
+ ret = sysfs_create_group(&client->dev.kobj, &lsm303dlh_a_attr_group);
+ if (ret)
+ goto exit_free_regulator;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+
+ /* accelerometer has two interrupts channels
+ (thresholds,durations and sources)
+ and can support two input devices */
+
+ ddata->input_dev = input_allocate_device();
+ if (!ddata->input_dev) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "Failed to allocate input device\n");
+ goto exit_free_regulator;
+ }
+
+ ddata->input_dev2 = input_allocate_device();
+ if (!ddata->input_dev2) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "Failed to allocate input device\n");
+ goto err_input_alloc_failed;
+ }
+
+ set_bit(EV_ABS, ddata->input_dev->evbit);
+ set_bit(EV_ABS, ddata->input_dev2->evbit);
+
+ /* x-axis acceleration */
+ input_set_abs_params(ddata->input_dev, ABS_X, -32768, 32767, 0, 0);
+ input_set_abs_params(ddata->input_dev2, ABS_X, -32768, 32767, 0, 0);
+ /* y-axis acceleration */
+ input_set_abs_params(ddata->input_dev, ABS_Y, -32768, 32767, 0, 0);
+ input_set_abs_params(ddata->input_dev2, ABS_Y, -32768, 32767, 0, 0);
+ /* z-axis acceleration */
+ input_set_abs_params(ddata->input_dev, ABS_Z, -32768, 32767, 0, 0);
+ input_set_abs_params(ddata->input_dev2, ABS_Z, -32768, 32767, 0, 0);
+
+ ddata->input_dev->name = "accelerometer";
+ ddata->input_dev2->name = "motion";
+
+ ret = input_register_device(ddata->input_dev);
+ if (ret) {
+ dev_err(&client->dev, "Unable to register input device: %s\n",
+ ddata->input_dev->name);
+ goto err_input_register_failed;
+ }
+
+ ret = input_register_device(ddata->input_dev2);
+ if (ret) {
+ dev_err(&client->dev, "Unable to register input device: %s\n",
+ ddata->input_dev->name);
+ goto err_input_register_failed2;
+ }
+
+ /* Register interrupt */
+ ret = request_threaded_irq(gpio_to_irq(ddata->pdata.irq_a1), NULL,
+ lsm303dlh_a_gpio_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "lsm303dlh_a", ddata);
+ if (ret) {
+ dev_err(&client->dev, "request irq1 failed\n");
+ goto err_input_failed;
+ }
+
+ ret = request_threaded_irq(gpio_to_irq(ddata->pdata.irq_a2), NULL,
+ lsm303dlh_a_gpio_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "lsm303dlh_a", ddata);
+ if (ret) {
+ dev_err(&client->dev, "request irq2 failed\n");
+ goto err_input_failed;
+ }
+
+ /* only mode can enable it */
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a1));
+ disable_irq(gpio_to_irq(ddata->pdata.irq_a2));
+
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ddata->early_suspend.level =
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ddata->early_suspend.suspend = lsm303dlh_a_early_suspend;
+ ddata->early_suspend.resume = lsm303dlh_a_late_resume;
+ register_early_suspend(&ddata->early_suspend);
+#endif
+
+ if (ddata->device_status == DEVICE_ON && ddata->regulator) {
+ regulator_disable(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ return ret;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+err_input_failed:
+ input_unregister_device(ddata->input_dev2);
+err_input_register_failed2:
+ input_unregister_device(ddata->input_dev);
+err_input_register_failed:
+ input_free_device(ddata->input_dev2);
+err_input_alloc_failed:
+ input_free_device(ddata->input_dev);
+#endif
+exit_free_regulator:
+ if (ddata->device_status == DEVICE_ON && ddata->regulator) {
+ regulator_disable(ddata->regulator);
+ regulator_put(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+err_op_failed:
+ kfree(ddata);
+ dev_err(&client->dev, "probe function fails %x", ret);
+ return ret;
+}
+
+static int __devexit lsm303dlh_a_remove(struct i2c_client *client)
+{
+ int ret;
+ struct lsm303dlh_a_data *ddata;
+
+ ddata = i2c_get_clientdata(client);
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ input_unregister_device(ddata->input_dev);
+ input_unregister_device(ddata->input_dev2);
+ input_free_device(ddata->input_dev);
+ input_free_device(ddata->input_dev2);
+#endif
+ sysfs_remove_group(&client->dev.kobj, &lsm303dlh_a_attr_group);
+
+ /* safer to make device off */
+ if (ddata->mode != LSM303DLH_A_MODE_OFF) {
+ ret = lsm303dlh_a_write(ddata, CTRL_REG1, 0, "CONTROL");
+
+ if (ret < 0) {
+ dev_err(&client->dev, "could not turn off the device %d", ret);
+ return ret;
+ }
+
+ if (ddata->regulator && ddata->device_status == DEVICE_ON) {
+ regulator_disable(ddata->regulator);
+ regulator_put(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ }
+
+ i2c_set_clientdata(client, NULL);
+ kfree(ddata);
+
+ return 0;
+}
+
+#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM))
+static int lsm303dlh_a_suspend(struct device *dev)
+{
+ struct lsm303dlh_a_data *ddata;
+ int ret;
+
+ ddata = dev_get_drvdata(dev);
+
+ ret = lsm303dlh_a_do_suspend(ddata);
+
+ return ret;
+}
+
+static int lsm303dlh_a_resume(struct device *dev)
+{
+ struct lsm303dlh_a_data *ddata;
+ int ret;
+
+ ddata = dev_get_drvdata(dev);
+
+ ret = lsm303dlh_a_restore(ddata);
+
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while resuming the device");
+
+ return ret;
+}
+static const struct dev_pm_ops lsm303dlh_a_dev_pm_ops = {
+ .suspend = lsm303dlh_a_suspend,
+ .resume = lsm303dlh_a_resume,
+};
+#else
+static void lsm303dlh_a_early_suspend(struct early_suspend *data)
+{
+ struct lsm303dlh_a_data *ddata =
+ container_of(data, struct lsm303dlh_a_data, early_suspend);
+ int ret;
+
+ ret = lsm303dlh_a_do_suspend(ddata);
+}
+
+static void lsm303dlh_a_late_resume(struct early_suspend *data)
+{
+ struct lsm303dlh_a_data *ddata =
+ container_of(data, struct lsm303dlh_a_data, early_suspend);
+ int ret;
+
+ ret = lsm303dlh_a_restore(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "lsm303dlh_a late resume failed\n");
+}
+#endif /* CONFIG_PM */
+
+static const struct i2c_device_id lsm303dlh_a_id[] = {
+ { "lsm303dlh_a", 0 },
+ { },
+};
+
+static struct i2c_driver lsm303dlh_a_driver = {
+ .probe = lsm303dlh_a_probe,
+ .remove = lsm303dlh_a_remove,
+ .id_table = lsm303dlh_a_id,
+ .driver = {
+ .name = "lsm303dlh_a",
+ #if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM))
+ .pm = &lsm303dlh_a_dev_pm_ops,
+ #endif
+ },
+};
+
+static int __init lsm303dlh_a_init(void)
+{
+ return i2c_add_driver(&lsm303dlh_a_driver);
+}
+
+static void __exit lsm303dlh_a_exit(void)
+{
+ i2c_del_driver(&lsm303dlh_a_driver);
+}
+
+module_init(lsm303dlh_a_init)
+module_exit(lsm303dlh_a_exit)
+
+MODULE_DESCRIPTION("lSM303DLH 3-Axis Accelerometer Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("STMicroelectronics");
diff --git a/drivers/hwmon/lsm303dlh_m.c b/drivers/hwmon/lsm303dlh_m.c
new file mode 100644
index 00000000000..1d03d80618e
--- /dev/null
+++ b/drivers/hwmon/lsm303dlh_m.c
@@ -0,0 +1,878 @@
+/*
+ * lsm303dlh_m.c
+ * ST 3-Axis Magnetometer Driver
+ *
+ * Copyright (C) 2010 STMicroelectronics
+ * Author: Carmine Iascone (carmine.iascone@st.com)
+ * Author: Matteo Dameno (matteo.dameno@st.com)
+ *
+ * Copyright (C) 2010 STEricsson
+ * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ * Updated:Preetham Rao Kaskurthi <preetham.rao@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <mach/gpio.h>
+#endif
+
+#include <linux/lsm303dlh.h>
+#include <linux/regulator/consumer.h>
+#include <linux/earlysuspend.h>
+#include <linux/kernel.h>
+
+/* lsm303dlh magnetometer registers */
+#define IRA_REG_M 0x0A
+
+/* Magnetometer registers */
+#define CRA_REG_M 0x00 /* Configuration register A */
+#define CRB_REG_M 0x01 /* Configuration register B */
+#define MR_REG_M 0x02 /* Mode register */
+#define SR_REG_M 0x09 /* Status register */
+
+/* Output register start address*/
+#define OUT_X_M 0x03
+#define OUT_Y_M 0x05
+#define OUT_Z_M 0x07
+
+/* Magnetometer X-Y gain */
+#define XY_GAIN_1_3 1055 /* XY gain at 1.3G */
+#define XY_GAIN_1_9 795 /* XY gain at 1.9G */
+#define XY_GAIN_2_5 635 /* XY gain at 2.5G */
+#define XY_GAIN_4_0 430 /* XY gain at 4.0G */
+#define XY_GAIN_4_7 375 /* XY gain at 4.7G */
+#define XY_GAIN_5_6 320 /* XY gain at 5.6G */
+#define XY_GAIN_8_1 230 /* XY gain at 8.1G */
+
+/* Magnetometer Z gain */
+#define Z_GAIN_1_3 950 /* Z gain at 1.3G */
+#define Z_GAIN_1_9 710 /* Z gain at 1.9G */
+#define Z_GAIN_2_5 570 /* Z gain at 2.5G */
+#define Z_GAIN_4_0 385 /* Z gain at 4.0G */
+#define Z_GAIN_4_7 335 /* Z gain at 4.7G */
+#define Z_GAIN_5_6 285 /* Z gain at 5.6G */
+#define Z_GAIN_8_1 205 /* Z gain at 8.1G */
+
+/* Control A regsiter. */
+#define LSM303DLH_M_CRA_DO_BIT 2
+#define LSM303DLH_M_CRA_DO_MASK (0x7 << LSM303DLH_M_CRA_DO_BIT)
+#define LSM303DLH_M_CRA_MS_BIT 0
+#define LSM303DLH_M_CRA_MS_MASK (0x3 << LSM303DLH_M_CRA_MS_BIT)
+
+/* Control B regsiter. */
+#define LSM303DLH_M_CRB_GN_BIT 5
+#define LSM303DLH_M_CRB_GN_MASK (0x7 << LSM303DLH_M_CRB_GN_BIT)
+
+/* Control Mode regsiter. */
+#define LSM303DLH_M_MR_MD_BIT 0
+#define LSM303DLH_M_MR_MD_MASK (0x3 << LSM303DLH_M_MR_MD_BIT)
+
+/* Control Status regsiter. */
+#define LSM303DLH_M_SR_RDY_BIT 0
+#define LSM303DLH_M_SR_RDY_MASK (0x1 << LSM303DLH_M_SR_RDY_BIT)
+#define LSM303DLH_M_SR_LOC_BIT 1
+#define LSM303DLH_M_SR_LCO_MASK (0x1 << LSM303DLH_M_SR_LOC_BIT)
+#define LSM303DLH_M_SR_REN_BIT 2
+#define LSM303DLH_M_SR_REN_MASK (0x1 << LSM303DLH_M_SR_REN_BIT)
+
+/* Magnetometer gain setting */
+#define LSM303DLH_M_RANGE_1_3G 0x01
+#define LSM303DLH_M_RANGE_1_9G 0x02
+#define LSM303DLH_M_RANGE_2_5G 0x03
+#define LSM303DLH_M_RANGE_4_0G 0x04
+#define LSM303DLH_M_RANGE_4_7G 0x05
+#define LSM303DLH_M_RANGE_5_6G 0x06
+#define LSM303DLH_M_RANGE_8_1G 0x07
+
+/* Magnetometer capturing mode */
+#define LSM303DLH_M_MODE_CONTINUOUS 0
+#define LSM303DLH_M_MODE_SINGLE 1
+#define LSM303DLH_M_MODE_SLEEP 3
+
+/* Magnetometer output data rate */
+#define LSM303DLH_M_RATE_00_75 0x00
+#define LSM303DLH_M_RATE_01_50 0x01
+#define LSM303DLH_M_RATE_03_00 0x02
+#define LSM303DLH_M_RATE_07_50 0x03
+#define LSM303DLH_M_RATE_15_00 0x04
+#define LSM303DLH_M_RATE_30_00 0x05
+#define LSM303DLH_M_RATE_75_00 0x06
+
+/* Multiple byte transfer enable */
+#define MULTIPLE_I2C_TR 0x80
+
+/* device status defines */
+#define DEVICE_OFF 0
+#define DEVICE_ON 1
+#define DEVICE_SUSPENDED 2
+
+/*
+ * magnetometer local data
+ */
+struct lsm303dlh_m_data {
+ struct i2c_client *client;
+ /* lock for sysfs operations */
+ struct mutex lock;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ struct input_dev *input_dev;
+#endif
+ struct regulator *regulator;
+ struct lsm303dlh_platform_data pdata;
+
+ short gain[3];
+ short data[3];
+ unsigned char mode;
+ unsigned char rate;
+ unsigned char range;
+ struct early_suspend early_suspend;
+ int device_status;
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void lsm303dlh_m_early_suspend(struct early_suspend *data);
+static void lsm303dlh_m_late_resume(struct early_suspend *data);
+#endif
+
+static int lsm303dlh_m_set_mode(struct lsm303dlh_m_data *ddata,
+ unsigned char mode);
+static int lsm303dlh_m_write(struct lsm303dlh_m_data *ddata,
+ u8 reg, u8 val, char *msg)
+{
+ int ret = i2c_smbus_write_byte_data(ddata->client, reg, val);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_write_byte_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
+}
+
+static int lsm303dlh_m_do_suspend(struct lsm303dlh_m_data *ddata)
+{
+ int ret;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP) {
+ mutex_unlock(&ddata->lock);
+ return 0;
+ }
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ disable_irq(gpio_to_irq(ddata->pdata.irq_m));
+#endif
+
+ ret = lsm303dlh_m_set_mode(ddata, LSM303DLH_M_MODE_SLEEP);
+
+ if (ddata->regulator)
+ regulator_disable(ddata->regulator);
+
+ ddata->device_status = DEVICE_SUSPENDED;
+
+ mutex_unlock(&ddata->lock);
+
+ return ret;
+}
+
+static int lsm303dlh_m_restore(struct lsm303dlh_m_data *ddata)
+{
+ int ret = 0;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->device_status == DEVICE_ON) {
+ mutex_unlock(&ddata->lock);
+ return 0;
+ }
+
+ /* in correct mode, no need to change it */
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP) {
+ ddata->device_status = DEVICE_OFF;
+ mutex_unlock(&ddata->lock);
+ return 0;
+ } else
+ ddata->device_status = DEVICE_ON;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ enable_irq(gpio_to_irq(ddata->pdata.irq_m));
+#endif
+
+ if (ddata->regulator)
+ regulator_enable(ddata->regulator);
+
+ ret = lsm303dlh_m_write(ddata, CRB_REG_M, ddata->range, "SET RANGE");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_m_write(ddata, CRA_REG_M, ddata->rate, "SET RATE");
+
+ if (ret < 0)
+ goto fail;
+
+ ret = lsm303dlh_m_set_mode(ddata, ddata->mode);
+
+ if (ret < 0)
+ goto fail;
+
+fail:
+ mutex_unlock(&ddata->lock);
+ return ret;
+}
+
+static int lsm303dlh_m_read_multi(struct lsm303dlh_m_data *ddata, u8 reg,
+ u8 count, u8 *val, char *msg)
+{
+ int ret = i2c_smbus_read_i2c_block_data(ddata->client,
+ reg | MULTIPLE_I2C_TR, count, val);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_read_i2c_block_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
+}
+
+static ssize_t lsm303dlh_m_show_rate(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->rate >> LSM303DLH_M_CRA_DO_BIT);
+}
+
+/* set lsm303dlh magnetometer bandwidth */
+static ssize_t lsm303dlh_m_store_rate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+ unsigned long val;
+ unsigned char data;
+ int error;
+
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ddata->lock);
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ data = ((val << LSM303DLH_M_CRA_DO_BIT) & LSM303DLH_M_CRA_DO_MASK);
+ ddata->rate = data;
+
+ error = lsm303dlh_m_write(ddata, CRA_REG_M, data, "SET RATE");
+
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return count;
+}
+
+static int lsm303dlh_m_xyz_read(struct lsm303dlh_m_data *ddata)
+{
+ unsigned char xyz_data[6];
+ int ret = lsm303dlh_m_read_multi(ddata, OUT_X_M,
+ 6, xyz_data, "OUT_X_M");
+ if (ret < 0)
+ return -EINVAL;
+
+ /* MSB is at lower address */
+ ddata->data[0] = (short)
+ (((xyz_data[0]) << 8) | xyz_data[1]);
+ ddata->data[1] = (short)
+ (((xyz_data[2]) << 8) | xyz_data[3]);
+ ddata->data[2] = (short)
+ (((xyz_data[4]) << 8) | xyz_data[5]);
+
+ /* taking orientation of x,y,z axis into account*/
+
+ ddata->data[ddata->pdata.axis_map_x] = ddata->pdata.negative_x ?
+ -ddata->data[ddata->pdata.axis_map_x] :
+ ddata->data[ddata->pdata.axis_map_x];
+ ddata->data[ddata->pdata.axis_map_y] = ddata->pdata.negative_y ?
+ -ddata->data[ddata->pdata.axis_map_y] :
+ ddata->data[ddata->pdata.axis_map_y];
+ ddata->data[ddata->pdata.axis_map_z] = ddata->pdata.negative_z ?
+ -ddata->data[ddata->pdata.axis_map_z] :
+ ddata->data[ddata->pdata.axis_map_z];
+
+ return ret;
+}
+
+static ssize_t lsm303dlh_m_gain(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%8x:%8x:%8x\n",
+ ddata->gain[ddata->pdata.axis_map_x],
+ ddata->gain[ddata->pdata.axis_map_y],
+ ddata->gain[ddata->pdata.axis_map_z]);
+}
+
+static ssize_t lsm303dlh_m_values(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP ||
+ ddata->device_status == DEVICE_SUSPENDED) {
+ mutex_unlock(&ddata->lock);
+ return ret;
+ }
+
+ ret = lsm303dlh_m_xyz_read(ddata);
+
+ if (ret < 0) {
+ mutex_unlock(&ddata->lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ /* taking orientation of x,y,z axis into account*/
+
+ return sprintf(buf, "%8x:%8x:%8x\n",
+ ddata->data[ddata->pdata.axis_map_x],
+ ddata->data[ddata->pdata.axis_map_y],
+ ddata->data[ddata->pdata.axis_map_z]);
+}
+
+static int lsm303dlh_m_set_mode(struct lsm303dlh_m_data *ddata,
+ unsigned char mode)
+{
+ int ret;
+
+ mode = (mode << LSM303DLH_M_MR_MD_BIT);
+
+ ret = i2c_smbus_write_byte_data(ddata->client, MR_REG_M, mode);
+
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_write_byte_data failed error %d\
+ Register (%s)\n", ret, "MODE CONTROL");
+
+ return ret;
+}
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+
+static irqreturn_t lsm303dlh_m_gpio_irq(int irq, void *device_data)
+{
+ struct lsm303dlh_m_data *ddata = device_data;
+ int ret;
+
+ ret = lsm303dlh_m_xyz_read(ddata);
+
+ if (ret < 0) {
+ dev_err(&ddata->client->dev,
+ "reading data of xyz failed error %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ /* taking orientation of x,y,z axis into account*/
+
+ input_report_abs(ddata->input_dev, ABS_X,
+ ddata->data[ddata->pdata.axis_map_x]);
+ input_report_abs(ddata->input_dev, ABS_Y,
+ ddata->data[ddata->pdata.axis_map_y]);
+ input_report_abs(ddata->input_dev, ABS_Z,
+ ddata->data[ddata->pdata.axis_map_z]);
+ input_sync(ddata->input_dev);
+
+ return IRQ_HANDLED;
+
+}
+#endif
+
+static ssize_t lsm303dlh_m_show_range(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->range >> LSM303DLH_M_CRB_GN_BIT);
+}
+
+static ssize_t lsm303dlh_m_store_range(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+ short xy_gain;
+ short z_gain;
+ unsigned long range;
+ int error;
+
+ error = strict_strtoul(buf, 0, &range);
+
+ if (error)
+ return error;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP) {
+ dev_info(&ddata->client->dev,
+ "device is switched off,make it ON using MODE");
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ switch (range) {
+ case LSM303DLH_M_RANGE_1_3G:
+ xy_gain = XY_GAIN_1_3;
+ z_gain = Z_GAIN_1_3;
+ break;
+ case LSM303DLH_M_RANGE_1_9G:
+ xy_gain = XY_GAIN_1_9;
+ z_gain = Z_GAIN_1_9;
+ break;
+ case LSM303DLH_M_RANGE_2_5G:
+ xy_gain = XY_GAIN_2_5;
+ z_gain = Z_GAIN_2_5;
+ break;
+ case LSM303DLH_M_RANGE_4_0G:
+ xy_gain = XY_GAIN_4_0;
+ z_gain = Z_GAIN_4_0;
+ break;
+ case LSM303DLH_M_RANGE_4_7G:
+ xy_gain = XY_GAIN_4_7;
+ z_gain = Z_GAIN_4_7;
+ break;
+ case LSM303DLH_M_RANGE_5_6G:
+ xy_gain = XY_GAIN_5_6;
+ z_gain = Z_GAIN_5_6;
+ break;
+ case LSM303DLH_M_RANGE_8_1G:
+ xy_gain = XY_GAIN_8_1;
+ z_gain = Z_GAIN_8_1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ddata->gain[ddata->pdata.axis_map_x] = xy_gain;
+ ddata->gain[ddata->pdata.axis_map_y] = xy_gain;
+ ddata->gain[ddata->pdata.axis_map_z] = z_gain;
+
+ range <<= LSM303DLH_M_CRB_GN_BIT;
+ range &= LSM303DLH_M_CRB_GN_MASK;
+
+ ddata->range = range;
+
+ error = lsm303dlh_m_write(ddata, CRB_REG_M, range, "SET RANGE");
+ mutex_unlock(&ddata->lock);
+
+ if (error < 0)
+ return error;
+
+ return count;
+}
+
+static ssize_t lsm303dlh_m_show_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ddata->mode);
+}
+
+static ssize_t lsm303dlh_m_store_mode(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lsm303dlh_m_data *ddata = platform_get_drvdata(pdev);
+ unsigned long mode;
+ int error;
+
+ error = strict_strtoul(buf, 0, &mode);
+ if (error)
+ return error;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->device_status == DEVICE_SUSPENDED &&
+ mode == LSM303DLH_M_MODE_SLEEP) {
+ ddata->mode = (mode >> LSM303DLH_M_MR_MD_BIT);
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ /* if same mode as existing, return */
+ if (ddata->mode == mode) {
+ mutex_unlock(&ddata->lock);
+ return count;
+ }
+
+ /* turn on the supplies if already off */
+ if (ddata->mode == LSM303DLH_M_MODE_SLEEP && ddata->regulator
+ && (ddata->device_status == DEVICE_OFF
+ || ddata->device_status == DEVICE_SUSPENDED)) {
+ regulator_enable(ddata->regulator);
+ ddata->device_status = DEVICE_ON;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ enable_irq(gpio_to_irq(ddata->pdata.irq_m));
+#endif
+ }
+
+ error = lsm303dlh_m_set_mode(ddata, mode);
+
+ ddata->mode = (mode >> LSM303DLH_M_MR_MD_BIT);
+ if (error < 0) {
+ if (ddata->regulator && ddata->device_status == DEVICE_ON) {
+ regulator_disable(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ mutex_unlock(&ddata->lock);
+ return error;
+ }
+
+ if (mode == LSM303DLH_M_MODE_SLEEP) {
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ disable_irq(gpio_to_irq(ddata->pdata.irq_m));
+#endif
+
+ /*
+ * No need to store context here, it is not like
+ * suspend/resume but fall back to default values
+ */
+ ddata->rate = LSM303DLH_M_RATE_00_75;
+ ddata->range = LSM303DLH_M_RANGE_1_3G;
+ ddata->range <<= LSM303DLH_M_CRB_GN_BIT;
+ ddata->range &= LSM303DLH_M_CRB_GN_MASK;
+ ddata->gain[ddata->pdata.axis_map_x] = XY_GAIN_1_3;
+ ddata->gain[ddata->pdata.axis_map_y] = XY_GAIN_1_3;
+ ddata->gain[ddata->pdata.axis_map_z] = Z_GAIN_1_3;
+
+ if (ddata->regulator && ddata->device_status == DEVICE_ON) {
+ regulator_disable(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ }
+ mutex_unlock(&ddata->lock);
+
+ return count;
+}
+
+static DEVICE_ATTR(gain, S_IRUGO, lsm303dlh_m_gain, NULL);
+
+static DEVICE_ATTR(data, S_IRUGO, lsm303dlh_m_values, NULL);
+
+static DEVICE_ATTR(mode, S_IWUGO | S_IRUGO,
+ lsm303dlh_m_show_mode, lsm303dlh_m_store_mode);
+
+static DEVICE_ATTR(range, S_IWUGO | S_IRUGO,
+ lsm303dlh_m_show_range, lsm303dlh_m_store_range);
+
+static DEVICE_ATTR(rate, S_IWUGO | S_IRUGO,
+ lsm303dlh_m_show_rate, lsm303dlh_m_store_rate);
+
+static struct attribute *lsm303dlh_m_attributes[] = {
+ &dev_attr_gain.attr,
+ &dev_attr_data.attr,
+ &dev_attr_mode.attr,
+ &dev_attr_range.attr,
+ &dev_attr_rate.attr,
+ NULL
+};
+
+static const struct attribute_group lsm303dlh_m_attr_group = {
+ .attrs = lsm303dlh_m_attributes,
+};
+
+static int __devinit lsm303dlh_m_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct lsm303dlh_m_data *ddata = NULL;
+ unsigned char version[3];
+
+ ddata = kzalloc(sizeof(struct lsm303dlh_m_data), GFP_KERNEL);
+ if (ddata == NULL) {
+ ret = -ENOMEM;
+ goto err_op_failed;
+ }
+
+ ddata->client = client;
+ i2c_set_clientdata(client, ddata);
+
+ /* copy platform specific data */
+ memcpy(&ddata->pdata, client->dev.platform_data, sizeof(ddata->pdata));
+
+ ddata->mode = LSM303DLH_M_MODE_SLEEP;
+ ddata->rate = LSM303DLH_M_RATE_00_75;
+ ddata->range = LSM303DLH_M_RANGE_1_3G;
+ ddata->range <<= LSM303DLH_M_CRB_GN_BIT;
+ ddata->range &= LSM303DLH_M_CRB_GN_MASK;
+ ddata->gain[ddata->pdata.axis_map_x] = XY_GAIN_1_3;
+ ddata->gain[ddata->pdata.axis_map_y] = XY_GAIN_1_3;
+ ddata->gain[ddata->pdata.axis_map_z] = Z_GAIN_1_3;
+ ddata->device_status = DEVICE_OFF;
+ dev_set_name(&client->dev, ddata->pdata.name_m);
+ ddata->regulator = regulator_get(&client->dev, "v-mag");
+
+ if (IS_ERR(ddata->regulator)) {
+ dev_err(&client->dev, "failed to get regulator\n");
+ ret = PTR_ERR(ddata->regulator);
+ ddata->regulator = NULL;
+ }
+
+ if (ddata->regulator) {
+ regulator_enable(ddata->regulator);
+ ddata->device_status = DEVICE_ON;
+ }
+
+ ret = lsm303dlh_m_read_multi(ddata, IRA_REG_M, 3, version, "IRA_REG_M");
+ if (ret < 0)
+ goto exit_free_regulator;
+
+ dev_info(&client->dev, "Magnetometer, ID : %x:%x:%x",
+ version[0], version[1], version[2]);
+
+ mutex_init(&ddata->lock);
+
+ ret = sysfs_create_group(&client->dev.kobj, &lsm303dlh_m_attr_group);
+ if (ret)
+ goto exit_free_regulator;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+
+ ddata->input_dev = input_allocate_device();
+ if (!ddata->input_dev) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "Failed to allocate input device\n");
+ goto exit_free_regulator;
+ }
+
+ set_bit(EV_ABS, ddata->input_dev->evbit);
+
+ /* x-axis acceleration */
+ input_set_abs_params(ddata->input_dev, ABS_X, -32768, 32767, 0, 0);
+ /* y-axis acceleration */
+ input_set_abs_params(ddata->input_dev, ABS_Y, -32768, 32767, 0, 0);
+ /* z-axis acceleration */
+ input_set_abs_params(ddata->input_dev, ABS_Z, -32768, 32767, 0, 0);
+
+ ddata->input_dev->name = "magnetometer";
+
+ ret = input_register_device(ddata->input_dev);
+ if (ret) {
+ dev_err(&client->dev, "Unable to register input device: %s\n",
+ ddata->input_dev->name);
+ goto err_input_register_failed;
+ }
+
+ /* register interrupt */
+ ret = request_threaded_irq(gpio_to_irq(ddata->pdata.irq_m), NULL,
+ lsm303dlh_m_gpio_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "lsm303dlh_m",
+ ddata);
+ if (ret) {
+ dev_err(&client->dev, "request irq EGPIO_PIN_1 failed\n");
+ goto err_input_failed;
+ }
+
+ disable_irq(gpio_to_irq(ddata->pdata.irq_m));
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ddata->early_suspend.level =
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ddata->early_suspend.suspend = lsm303dlh_m_early_suspend;
+ ddata->early_suspend.resume = lsm303dlh_m_late_resume;
+ register_early_suspend(&ddata->early_suspend);
+#endif
+
+ if (ddata->device_status == DEVICE_ON && ddata->regulator) {
+ regulator_disable(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+
+ return ret;
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+err_input_failed:
+ input_unregister_device(ddata->input_dev);
+err_input_register_failed:
+ input_free_device(ddata->input_dev);
+#endif
+exit_free_regulator:
+ if (ddata->device_status == DEVICE_ON && ddata->regulator) {
+ regulator_disable(ddata->regulator);
+ regulator_put(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+err_op_failed:
+ dev_err(&client->dev, "lsm303dlh_m_probe failed %x", ret);
+ kfree(ddata);
+ return ret;
+}
+
+static int __devexit lsm303dlh_m_remove(struct i2c_client *client)
+{
+ struct lsm303dlh_m_data *ddata;
+
+ ddata = i2c_get_clientdata(client);
+
+#ifdef CONFIG_SENSORS_LSM303DLH_INPUT_DEVICE
+ input_unregister_device(ddata->input_dev);
+ input_free_device(ddata->input_dev);
+#endif
+
+ sysfs_remove_group(&client->dev.kobj, &lsm303dlh_m_attr_group);
+
+ /* safer to make device off */
+ if (ddata->mode != LSM303DLH_M_MODE_SLEEP) {
+ lsm303dlh_m_set_mode(ddata, LSM303DLH_M_MODE_SLEEP);
+ if (ddata->regulator && ddata->device_status == DEVICE_ON) {
+ regulator_disable(ddata->regulator);
+ regulator_put(ddata->regulator);
+ ddata->device_status = DEVICE_OFF;
+ }
+ }
+
+ i2c_set_clientdata(client, NULL);
+ kfree(ddata);
+
+ return 0;
+}
+
+#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM))
+static int lsm303dlh_m_suspend(struct device *dev)
+{
+ struct lsm303dlh_m_data *ddata;
+ int ret;
+
+ ddata = dev_get_drvdata(dev);
+
+ ret = lsm303dlh_m_do_suspend(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while suspending the device");
+
+ return ret;
+}
+
+static int lsm303dlh_m_resume(struct device *dev)
+{
+ struct lsm303dlh_m_data *ddata;
+ int ret;
+
+ ddata = dev_get_drvdata(dev);
+
+ ret = lsm303dlh_m_restore(ddata);
+
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while resuming the device");
+
+ return ret;
+}
+static const struct dev_pm_ops lsm303dlh_m_dev_pm_ops = {
+ .suspend = lsm303dlh_m_suspend,
+ .resume = lsm303dlh_m_resume,
+};
+#else
+static void lsm303dlh_m_early_suspend(struct early_suspend *data)
+{
+ struct lsm303dlh_m_data *ddata =
+ container_of(data, struct lsm303dlh_m_data, early_suspend);
+ int ret;
+
+ ret = lsm303dlh_m_do_suspend(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while suspending the device");
+}
+
+static void lsm303dlh_m_late_resume(struct early_suspend *data)
+{
+ struct lsm303dlh_m_data *ddata =
+ container_of(data, struct lsm303dlh_m_data, early_suspend);
+ int ret;
+
+ ret = lsm303dlh_m_restore(ddata);
+
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "lsm303dlh_m late resume failed\n");
+}
+#endif /* CONFIG_PM */
+
+static const struct i2c_device_id lsm303dlh_m_id[] = {
+ { "lsm303dlh_m", 0 },
+ { },
+};
+
+static struct i2c_driver lsm303dlh_m_driver = {
+ .probe = lsm303dlh_m_probe,
+ .remove = lsm303dlh_m_remove,
+ .id_table = lsm303dlh_m_id,
+ .driver = {
+ .name = "lsm303dlh_m",
+ #if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM))
+ .pm = &lsm303dlh_m_dev_pm_ops,
+ #endif
+ },
+};
+
+static int __init lsm303dlh_m_init(void)
+{
+ return i2c_add_driver(&lsm303dlh_m_driver);
+}
+
+static void __exit lsm303dlh_m_exit(void)
+{
+ i2c_del_driver(&lsm303dlh_m_driver);
+}
+
+module_init(lsm303dlh_m_init);
+module_exit(lsm303dlh_m_exit);
+
+MODULE_DESCRIPTION("lSM303DLH 3-Axis Magnetometer Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("STMicroelectronics");
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 73de8ade10b..e9583899ee3 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -15,13 +15,14 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
#include <plat/i2c.h>
@@ -117,9 +118,6 @@ enum i2c_operation {
I2C_READ = 0x01
};
-/* controller response timeout in ms */
-#define I2C_TIMEOUT_MS 500
-
/**
* struct i2c_nmk_client - client specific data
* @slave_adr: 7-bit slave address
@@ -148,6 +146,7 @@ struct i2c_nmk_client {
* @stop: stop condition
* @xfer_complete: acknowledge completion for a I2C message
* @result: controller propogated result
+ * @busy: Busy doing transfer
*/
struct nmk_i2c_dev {
struct platform_device *pdev;
@@ -160,6 +159,8 @@ struct nmk_i2c_dev {
int stop;
struct completion xfer_complete;
int result;
+ struct regulator *regulator;
+ bool busy;
};
/* controller's abort causes */
@@ -206,7 +207,7 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev)
writel((I2C_CR_FTX | I2C_CR_FRX), dev->virtbase + I2C_CR);
for (i = 0; i < LOOP_ATTEMPTS; i++) {
- timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MS);
+ timeout = jiffies + dev->adap.timeout;
while (!time_after(jiffies, timeout)) {
if ((readl(dev->virtbase + I2C_CR) &
@@ -252,7 +253,7 @@ static int init_hw(struct nmk_i2c_dev *dev)
stat = flush_i2c_fifo(dev);
if (stat)
- return stat;
+ goto exit;
/* disable the controller */
i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE);
@@ -263,7 +264,8 @@ static int init_hw(struct nmk_i2c_dev *dev)
dev->cli.operation = I2C_NO_OPERATION;
- return 0;
+exit:
+ return stat;
}
/* enable peripheral, master mode operation */
@@ -416,7 +418,7 @@ static int read_i2c(struct nmk_i2c_dev *dev)
dev->virtbase + I2C_IMSCR);
timeout = wait_for_completion_interruptible_timeout(
- &dev->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS));
+ &dev->xfer_complete, dev->adap.timeout);
if (timeout < 0) {
dev_err(&dev->pdev->dev,
@@ -426,15 +428,33 @@ static int read_i2c(struct nmk_i2c_dev *dev)
}
if (timeout == 0) {
- /* controler has timedout, re-init the h/w */
- dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n");
- (void) init_hw(dev);
+ /* Controller timed out */
+ dev_err(&dev->pdev->dev, "Read from Slave 0x%x timed out\n",
+ dev->cli.slave_adr);
status = -ETIMEDOUT;
}
return status;
}
+static void fill_tx_fifo(struct nmk_i2c_dev *dev, int no_bytes)
+{
+ int count;
+
+ for (count = (no_bytes - 2);
+ (count > 0) &&
+ (dev->cli.count != 0);
+ count--) {
+ /* write to the Tx FIFO */
+ writeb(*dev->cli.buffer,
+ dev->virtbase + I2C_TFR);
+ dev->cli.buffer++;
+ dev->cli.count--;
+ dev->cli.xfer_bytes++;
+ }
+
+}
+
/**
* write_i2c() - Write data to I2C client.
* @dev: private data of I2C Driver
@@ -462,8 +482,13 @@ static int write_i2c(struct nmk_i2c_dev *dev)
init_completion(&dev->xfer_complete);
/* enable interrupts by settings the masks */
- irq_mask = (I2C_IT_TXFNE | I2C_IT_TXFOVR |
- I2C_IT_MAL | I2C_IT_BERR);
+ irq_mask = (I2C_IT_TXFOVR | I2C_IT_MAL | I2C_IT_BERR);
+
+ /* Fill the TX FIFO with transmit data */
+ fill_tx_fifo(dev, MAX_I2C_FIFO_THRESHOLD);
+
+ if (dev->cli.count != 0)
+ irq_mask |= I2C_IT_TXFNE;
/*
* check if we want to transfer a single or multiple bytes, if so
@@ -481,7 +506,7 @@ static int write_i2c(struct nmk_i2c_dev *dev)
dev->virtbase + I2C_IMSCR);
timeout = wait_for_completion_interruptible_timeout(
- &dev->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS));
+ &dev->xfer_complete, dev->adap.timeout);
if (timeout < 0) {
dev_err(&dev->pdev->dev,
@@ -491,9 +516,9 @@ static int write_i2c(struct nmk_i2c_dev *dev)
}
if (timeout == 0) {
- /* controler has timedout, re-init the h/w */
- dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n");
- (void) init_hw(dev);
+ /* Controller timed out */
+ dev_err(&dev->pdev->dev, "Write to slave 0x%x timed out\n",
+ dev->cli.slave_adr);
status = -ETIMEDOUT;
}
@@ -554,47 +579,83 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
int i;
u32 cause;
struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap);
+ u32 i2c_sr;
+ int j;
+
+ dev->busy = true;
+
+ if (dev->regulator)
+ regulator_enable(dev->regulator);
+ pm_runtime_get_sync(&dev->pdev->dev);
+
+ clk_enable(dev->clk);
status = init_hw(dev);
if (status)
- return status;
+ goto out;
- /* setup the i2c controller */
- setup_i2c_controller(dev);
+ for (j = 0; j < 3; j++) {
+ /* setup the i2c controller */
+ setup_i2c_controller(dev);
- for (i = 0; i < num_msgs; i++) {
- if (unlikely(msgs[i].flags & I2C_M_TEN)) {
- dev_err(&dev->pdev->dev, "10 bit addressing"
- "not supported\n");
- return -EINVAL;
- }
- dev->cli.slave_adr = msgs[i].addr;
- dev->cli.buffer = msgs[i].buf;
- dev->cli.count = msgs[i].len;
- dev->stop = (i < (num_msgs - 1)) ? 0 : 1;
- dev->result = 0;
-
- if (msgs[i].flags & I2C_M_RD) {
- /* it is a read operation */
- dev->cli.operation = I2C_READ;
- status = read_i2c(dev);
- } else {
- /* write operation */
- dev->cli.operation = I2C_WRITE;
- status = write_i2c(dev);
- }
- if (status || (dev->result)) {
- /* get the abort cause */
- cause = (readl(dev->virtbase + I2C_SR) >> 4) & 0x7;
- dev_err(&dev->pdev->dev, "error during I2C"
- "message xfer: %d\n", cause);
- dev_err(&dev->pdev->dev, "%s\n",
- cause >= ARRAY_SIZE(abort_causes)
- ? "unknown reason" : abort_causes[cause]);
- return status;
+ for (i = 0; i < num_msgs; i++) {
+ if (unlikely(msgs[i].flags & I2C_M_TEN)) {
+ dev_err(&dev->pdev->dev, "10 bit addressing"
+ "not supported\n");
+
+ status = -EINVAL;
+ goto out;
+ }
+ dev->cli.slave_adr = msgs[i].addr;
+ dev->cli.buffer = msgs[i].buf;
+ dev->cli.count = msgs[i].len;
+ dev->stop = (i < (num_msgs - 1)) ? 0 : 1;
+ dev->result = 0;
+
+ if (msgs[i].flags & I2C_M_RD) {
+ /* it is a read operation */
+ dev->cli.operation = I2C_READ;
+ status = read_i2c(dev);
+ } else {
+ /* write operation */
+ dev->cli.operation = I2C_WRITE;
+ status = write_i2c(dev);
+ }
+ if (status || (dev->result)) {
+ i2c_sr = readl(dev->virtbase + I2C_SR);
+ /*
+ * Check if the controller I2C operation status
+ * is set to ABORT(11b).
+ */
+ if (((i2c_sr >> 2) & 0x3) == 0x3) {
+ /* get the abort cause */
+ cause = (i2c_sr >> 4)
+ & 0x7;
+ dev_err(&dev->pdev->dev, "%s\n", cause
+ >= ARRAY_SIZE(abort_causes) ?
+ "unknown reason" :
+ abort_causes[cause]);
+ }
+
+ (void) init_hw(dev);
+
+ status = status ? status : dev->result;
+
+ break;
+ }
}
- mdelay(1);
+ if (status == 0)
+ break;
}
+
+out:
+ clk_disable(dev->clk);
+ pm_runtime_put_sync(&dev->pdev->dev);
+ if (dev->regulator)
+ regulator_disable(dev->regulator);
+
+ dev->busy = false;
+
/* return the no. messages processed */
if (status)
return status;
@@ -653,17 +714,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
*/
disable_interrupts(dev, I2C_IT_TXFNE);
} else {
- for (count = (MAX_I2C_FIFO_THRESHOLD - tft - 2);
- (count > 0) &&
- (dev->cli.count != 0);
- count--) {
- /* write to the Tx FIFO */
- writeb(*dev->cli.buffer,
- dev->virtbase + I2C_TFR);
- dev->cli.buffer++;
- dev->cli.count--;
- dev->cli.xfer_bytes++;
- }
+ fill_tx_fifo(dev, (MAX_I2C_FIFO_THRESHOLD - tft));
/*
* if done, close the transfer by disabling the
* corresponding TXFNE interrupt
@@ -716,16 +767,11 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
}
}
- i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MTD);
- i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MTDWS);
-
- disable_interrupts(dev,
- (I2C_IT_TXFNE | I2C_IT_TXFE | I2C_IT_TXFF
- | I2C_IT_TXFOVR | I2C_IT_RXFNF
- | I2C_IT_RXFF | I2C_IT_RXFE));
+ disable_all_interrupts(dev);
+ clear_all_interrupts(dev);
if (dev->cli.count) {
- dev->result = -1;
+ dev->result = -EIO;
dev_err(&dev->pdev->dev, "%lu bytes still remain to be"
"xfered\n", dev->cli.count);
(void) init_hw(dev);
@@ -736,7 +782,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
/* Master Arbitration lost interrupt */
case I2C_IT_MAL:
- dev->result = -1;
+ dev->result = -EIO;
(void) init_hw(dev);
i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MAL);
@@ -750,7 +796,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
* during the transaction.
*/
case I2C_IT_BERR:
- dev->result = -1;
+ dev->result = -EIO;
/* get the status */
if (((readl(dev->virtbase + I2C_SR) >> 2) & 0x3) == I2C_ABORT)
(void) init_hw(dev);
@@ -766,7 +812,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
* the Tx FIFO is full.
*/
case I2C_IT_TXFOVR:
- dev->result = -1;
+ dev->result = -EIO;
(void) init_hw(dev);
dev_err(&dev->pdev->dev, "Tx Fifo Over run\n");
@@ -792,12 +838,55 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
return IRQ_HANDLED;
}
+
+#ifdef CONFIG_PM
+static int nmk_i2c_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev);
+
+ if (nmk_i2c->busy)
+ return -EBUSY;
+
+ /*
+ * During system suspend, runtime suspend transitions are disabled.
+ * This check and direct call are here so that even if some driver
+ * performs i2c transactions in its suspend rountine, we would properly
+ * runtime suspend the i2c driver.
+ */
+ if (!pm_runtime_suspended(dev))
+ if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend)
+ dev->bus->pm->runtime_suspend(dev);
+
+ return 0;
+}
+
+static int nmk_i2c_resume(struct device *dev)
+{
+ if (!pm_runtime_suspended(dev))
+ if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume)
+ dev->bus->pm->runtime_resume(dev);
+
+ return 0;
+}
+#else
+#define nmk_i2c_suspend NULL
+#define nmk_i2c_resume NULL
+#endif
+
+/*
+ * We use noirq so that we suspend late and resume before the wakeup interrupt
+ * to ensure that we do the !pm_runtime_suspended() check in resume before
+ * there has been a regular pm runtime resume (via pm_runtime_get_sync()).
+ */
+static const struct dev_pm_ops nmk_i2c_pm = {
+ .suspend_noirq = nmk_i2c_suspend,
+ .resume_noirq = nmk_i2c_resume,
+};
+
static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C
- | I2C_FUNC_SMBUS_BYTE_DATA
- | I2C_FUNC_SMBUS_WORD_DATA
- | I2C_FUNC_SMBUS_I2C_BLOCK;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm nmk_i2c_algo = {
@@ -820,7 +909,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err_no_mem;
}
-
+ dev->busy = false;
dev->pdev = pdev;
platform_set_drvdata(pdev, dev);
@@ -850,6 +939,15 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
goto err_irq;
}
+ dev->regulator = regulator_get(&pdev->dev, "v-i2c");
+ if (IS_ERR(dev->regulator)) {
+ dev_warn(&pdev->dev, "could not get i2c regulator\n");
+ dev->regulator = NULL;
+ }
+
+ pm_suspend_ignore_children(&pdev->dev, true);
+ pm_runtime_enable(&pdev->dev);
+
dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
dev_err(&pdev->dev, "could not get i2c clock\n");
@@ -857,13 +955,12 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
goto err_no_clk;
}
- clk_enable(dev->clk);
-
adap = &dev->adap;
adap->dev.parent = &pdev->dev;
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
adap->algo = &nmk_i2c_algo;
+ adap->timeout = msecs_to_jiffies(pdata->timeout);
/* fetch the controller id */
adap->nr = pdev->id;
@@ -877,12 +974,6 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(adap, dev);
- ret = init_hw(dev);
- if (ret != 0) {
- dev_err(&pdev->dev, "error in initializing i2c hardware\n");
- goto err_init_hw;
- }
-
dev_dbg(&pdev->dev, "initialize I2C%d bus on virtual "
"base %p\n", pdev->id, dev->virtbase);
@@ -894,11 +985,12 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
return 0;
- err_init_hw:
- clk_disable(dev->clk);
err_add_adap:
clk_put(dev->clk);
err_no_clk:
+ if (dev->regulator)
+ regulator_put(dev->regulator);
+ pm_runtime_disable(&pdev->dev);
free_irq(dev->irq, dev);
err_irq:
iounmap(dev->virtbase);
@@ -928,8 +1020,10 @@ static int __devexit nmk_i2c_remove(struct platform_device *pdev)
iounmap(dev->virtbase);
if (res)
release_mem_region(res->start, resource_size(res));
- clk_disable(dev->clk);
clk_put(dev->clk);
+ if (dev->regulator)
+ regulator_put(dev->regulator);
+ pm_runtime_disable(&pdev->dev);
platform_set_drvdata(pdev, NULL);
kfree(dev);
@@ -940,6 +1034,7 @@ static struct platform_driver nmk_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
+ .pm = &nmk_i2c_pm,
},
.probe = nmk_i2c_probe,
.remove = __devexit_p(nmk_i2c_remove),
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 0815e10da7c..4d0a0cd1f40 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -189,90 +189,60 @@ static int i2c_device_pm_suspend(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- if (pm_runtime_suspended(dev))
- return 0;
-
if (pm)
- return pm->suspend ? pm->suspend(dev) : 0;
-
- return i2c_legacy_suspend(dev, PMSG_SUSPEND);
+ return pm_generic_suspend(dev);
+ else
+ return i2c_legacy_suspend(dev, PMSG_SUSPEND);
}
static int i2c_device_pm_resume(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- int ret;
if (pm)
- ret = pm->resume ? pm->resume(dev) : 0;
+ return pm_generic_resume(dev);
else
- ret = i2c_legacy_resume(dev);
-
- if (!ret) {
- pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- }
-
- return ret;
+ return i2c_legacy_resume(dev);
}
static int i2c_device_pm_freeze(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- if (pm_runtime_suspended(dev))
- return 0;
-
if (pm)
- return pm->freeze ? pm->freeze(dev) : 0;
-
- return i2c_legacy_suspend(dev, PMSG_FREEZE);
+ return pm_generic_freeze(dev);
+ else
+ return i2c_legacy_suspend(dev, PMSG_FREEZE);
}
static int i2c_device_pm_thaw(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- if (pm_runtime_suspended(dev))
- return 0;
-
if (pm)
- return pm->thaw ? pm->thaw(dev) : 0;
-
- return i2c_legacy_resume(dev);
+ return pm_generic_thaw(dev);
+ else
+ return i2c_legacy_resume(dev);
}
static int i2c_device_pm_poweroff(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- if (pm_runtime_suspended(dev))
- return 0;
-
if (pm)
- return pm->poweroff ? pm->poweroff(dev) : 0;
-
- return i2c_legacy_suspend(dev, PMSG_HIBERNATE);
+ return pm_generic_poweroff(dev);
+ else
+ return i2c_legacy_suspend(dev, PMSG_HIBERNATE);
}
static int i2c_device_pm_restore(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- int ret;
if (pm)
- ret = pm->restore ? pm->restore(dev) : 0;
+ return pm_generic_restore(dev);
else
- ret = i2c_legacy_resume(dev);
-
- if (!ret) {
- pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- }
-
- return ret;
+ return i2c_legacy_resume(dev);
}
#else /* !CONFIG_PM_SLEEP */
#define i2c_device_pm_suspend NULL
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 07c2cd43109..dd62135e704 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -161,6 +161,15 @@ config INPUT_APMPOWER
To compile this driver as a module, choose M here: the
module will be called apm-power.
+config INPUT_KEYRESET
+ tristate "Reset key"
+ depends on INPUT
+ ---help---
+ Say Y here if you want to reboot when some keys are pressed;
+
+ To compile this driver as a module, choose M here: the
+ module will be called keyreset.
+
config XEN_KBDDEV_FRONTEND
tristate "Xen virtual keyboard and mouse support"
depends on XEN_FBDEV_FRONTEND
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 7ad212d31f9..3c7125758b7 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -24,5 +24,6 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
+obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o
obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 2ee6c7a68bd..1fcd6a2e3dc 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -20,6 +20,7 @@
#include <linux/input.h>
#include <linux/major.h>
#include <linux/device.h>
+#include <linux/wakelock.h>
#include "input-compat.h"
struct evdev {
@@ -43,6 +44,8 @@ struct evdev_client {
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
+ struct wake_lock wake_lock;
+ char name[28];
};
static struct evdev *evdev_table[EVDEV_MINORS];
@@ -55,6 +58,7 @@ static void evdev_pass_event(struct evdev_client *client,
* Interrupts are disabled, just acquire the lock
*/
spin_lock(&client->buffer_lock);
+ wake_lock_timeout(&client->wake_lock, 5 * HZ);
client->buffer[client->head++] = *event;
client->head &= EVDEV_BUFFER_SIZE - 1;
spin_unlock(&client->buffer_lock);
@@ -72,8 +76,11 @@ static void evdev_event(struct input_handle *handle,
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;
+ struct timespec ts;
- do_gettimeofday(&event.time);
+ ktime_get_ts(&ts);
+ event.time.tv_sec = ts.tv_sec;
+ event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
event.type = type;
event.code = code;
event.value = value;
@@ -234,6 +241,7 @@ static int evdev_release(struct inode *inode, struct file *file)
mutex_unlock(&evdev->mutex);
evdev_detach_client(evdev, client);
+ wake_lock_destroy(&client->wake_lock);
kfree(client);
evdev_close_device(evdev);
@@ -270,6 +278,9 @@ static int evdev_open(struct inode *inode, struct file *file)
}
spin_lock_init(&client->buffer_lock);
+ snprintf(client->name, sizeof(client->name), "%s-%d",
+ dev_name(&evdev->dev), task_tgid_vnr(current));
+ wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
client->evdev = evdev;
evdev_attach_client(evdev, client);
@@ -335,6 +346,8 @@ static int evdev_fetch_next_event(struct evdev_client *client,
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= EVDEV_BUFFER_SIZE - 1;
+ if (client->head == client->tail)
+ wake_unlock(&client->wake_lock);
}
spin_unlock_irq(&client->buffer_lock);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 1ba25145b33..3012138fade 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -143,6 +143,16 @@ config KEYBOARD_BFIN
To compile this driver as a module, choose M here: the
module will be called bf54x-keys.
+config KEYBOARD_DB5500
+ tristate "DB5500 keyboard"
+ depends on UX500_SOC_DB5500
+ help
+ Say Y here to enable the on-chip keypad controller on the
+ ST-Ericsson U5500 platform.
+
+ To compile this driver as a module, choose M here: the
+ module will be called db5500_keypad.
+
config KEYBOARD_LKKBD
tristate "DECstation/VAXstation LK201/LK401 keyboard"
select SERIO
@@ -315,6 +325,16 @@ config KEYBOARD_NEWTON
To compile this driver as a module, choose M here: the
module will be called newtonkbd.
+config KEYBOARD_NOMADIK
+ tristate "ST-Ericsson Nomadik SKE keyboard"
+ depends on PLAT_NOMADIK
+ help
+ Say Y here if you want to use a keypad provided on the SKE controller
+ used on the Ux500 and Nomadik platforms
+
+ To compile this driver as a module, choose M here: the
+ module will be called nmk-ske-keypad.
+
config KEYBOARD_OPENCORES
tristate "OpenCores Keyboard Controller"
help
@@ -374,6 +394,16 @@ config KEYBOARD_SH_KEYSC
To compile this driver as a module, choose M here: the
module will be called sh_keysc.
+config KEYBOARD_STMPE
+ tristate "STMPE keypad support"
+ depends on MFD_STMPE
+ help
+ Say Y here if you want to use the keypad controller on STMPE I/O
+ expanders.
+
+ To compile this driver as a module, choose M here: the module will be
+ called stmpe-keypad.
+
config KEYBOARD_DAVINCI
tristate "TI DaVinci Key Scan"
depends on ARCH_DAVINCI_DM365
@@ -404,6 +434,46 @@ config KEYBOARD_TWL4030
To compile this driver as a module, choose M here: the
module will be called twl4030_keypad.
+config KEYBOARD_TOSA
+ tristate "Tosa keyboard (deprecated)"
+ depends on MACH_TOSA
+ help
+ Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa)
+
+ This driver is now deprecated, use generic GPIO based matrix
+ keyboard driver instead.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tosakbd.
+
+config KEYBOARD_TOSA_USE_EXT_KEYCODES
+ bool "Tosa keyboard: use extended keycodes"
+ depends on KEYBOARD_TOSA
+ help
+ Say Y here to enable the tosa keyboard driver to generate extended
+ (>= 127) keycodes. Be aware, that they can't be correctly interpreted
+ by either console keyboard driver or by Kdrive keybd driver.
+
+ Say Y only if you know, what you are doing!
+
+config KEYBOARD_NOMADIK_SKE
+ tristate "Nomadik SKE Keypad support"
+ depends on ARCH_U8500 && !KEYBOARD_STMPE && !KEYBOARD_TC35893
+ default y
+ help
+ Say Y here if you want to use a keypad provided on the SKE controller
+ used on the Ux500 and Nomadik platforms
+
+config KEYBOARD_TC35893
+ tristate "TC35893 Keypad support"
+ depends on I2C
+ help
+ Say Y here if you want to use the keypad controller on
+ TC35893 I/O expander on the U8500 NUIB
+
+ To compile this driver as a module, choose M here: the
+ module will be called tc35893-keypad
+
config KEYBOARD_XTKBD
tristate "XT keyboard"
select SERIO
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 4596d0c6f92..a72a957e136 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
+obj-$(CONFIG_KEYBOARD_DB5500) += db5500_keypad.o
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
@@ -27,14 +28,17 @@ obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
+obj-$(CONFIG_KEYBOARD_NOMADIK_SKE) += nomadik-ske-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
+obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
+obj-$(CONFIG_KEYBOARD_TC35893) += tc35893-keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
diff --git a/drivers/input/keyboard/db5500_keypad.c b/drivers/input/keyboard/db5500_keypad.c
new file mode 100644
index 00000000000..8767b7f81ba
--- /dev/null
+++ b/drivers/input/keyboard/db5500_keypad.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License terms: GNU General Public License, version 2
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <mach/db5500-keypad.h>
+
+#define KEYPAD_CTR 0x0
+#define KEYPAD_IRQ_CLEAR 0x4
+#define KEYPAD_INT_ENABLE 0x8
+#define KEYPAD_INT_STATUS 0xC
+#define KEYPAD_ARRAY_01 0x18
+
+#define KEYPAD_NUM_ARRAY_REGS 5
+
+#define KEYPAD_CTR_WRITE_IRQ_ENABLE (1 << 10)
+#define KEYPAD_CTR_WRITE_CONTROL (1 << 8)
+#define KEYPAD_CTR_SCAN_ENABLE (1 << 7)
+
+#define KEYPAD_ARRAY_CHANGEBIT (1 << 15)
+
+#define KEYPAD_DEBOUNCE_PERIOD_MIN 5 /* ms */
+#define KEYPAD_DEBOUNCE_PERIOD_MAX 80 /* ms */
+
+#define KEYPAD_GND_ROW 8
+
+#define KEYPAD_MAX_ROWS 9
+#define KEYPAD_MAX_COLS 8
+#define KEYPAD_ROW_SHIFT 4
+#define KEYPAD_KEYMAP_SIZE \
+ (KEYPAD_MAX_ROWS * KEYPAD_MAX_COLS)
+
+/**
+ * struct db5500_keypad - data structure used by keypad driver
+ * @irq: irq number
+ * @base: keypad registers base address
+ * @input: pointer to input device object
+ * @board: keypad platform data
+ * @keymap: matrix scan code table for keycodes
+ * @clk: clock structure pointer
+ * @previous_set: previous set of registers
+ */
+struct db5500_keypad {
+ int irq;
+ void __iomem *base;
+ struct input_dev *input;
+ const struct db5500_keypad_platform_data *board;
+ unsigned short keymap[KEYPAD_KEYMAP_SIZE];
+ struct clk *clk;
+ u8 previous_set[KEYPAD_MAX_ROWS];
+};
+
+/*
+ * By default all column reads are 1111 1111b. Any press will pull the column
+ * down, leading to a 0 in any of these locations. We invert these values so
+ * that a 1 means means "column pressed".
+ *
+ * If curr changes from the previous from 0 to 1, we report it as a key press.
+ * If curr changes from the previous from 1 to 0, we report it as a key
+ * release.
+ */
+static void db5500_keypad_report(struct db5500_keypad *keypad, int row,
+ u8 curr, u8 previous)
+{
+ struct input_dev *input = keypad->input;
+ u8 changed = curr ^ previous;
+
+ while (changed) {
+ int col = __ffs(changed);
+ bool press = curr & BIT(col);
+ int code = MATRIX_SCAN_CODE(row, col, KEYPAD_ROW_SHIFT);
+
+ input_event(input, EV_MSC, MSC_SCAN, code);
+ input_report_key(input, keypad->keymap[code], press);
+ input_sync(input);
+
+ changed &= ~BIT(col);
+ }
+}
+
+static irqreturn_t db5500_keypad_irq(int irq, void *dev_id)
+{
+ struct db5500_keypad *keypad = dev_id;
+ u8 current_set[ARRAY_SIZE(keypad->previous_set)];
+ int tries = 100;
+ bool changebit;
+ u32 data_reg;
+ u8 allrows;
+ u8 common;
+ int i;
+
+ writel(0x1, keypad->base + KEYPAD_IRQ_CLEAR);
+
+again:
+ if (!tries--) {
+ dev_warn(&keypad->input->dev, "values failed to stabilize\n");
+ return IRQ_HANDLED;
+ }
+
+ changebit = readl(keypad->base + KEYPAD_ARRAY_01)
+ & KEYPAD_ARRAY_CHANGEBIT;
+
+ for (i = 0; i < KEYPAD_NUM_ARRAY_REGS; i++) {
+ data_reg = readl(keypad->base + KEYPAD_ARRAY_01 + 4 * i);
+
+ /* If the change bit changed, we need to reread the data */
+ if (changebit != !!(data_reg & KEYPAD_ARRAY_CHANGEBIT))
+ goto again;
+
+ current_set[2 * i] = ~(data_reg & 0xff);
+
+ /* Last array reg has only one valid set of columns */
+ if (i != KEYPAD_NUM_ARRAY_REGS - 1)
+ current_set[2 * i + 1] = ~((data_reg & 0xff0000) >> 16);
+ }
+
+ allrows = current_set[KEYPAD_GND_ROW];
+
+ /*
+ * Sometimes during a GND row release, an incorrect report is received
+ * where the ARRAY8 all rows setting does not match the other ARRAY*
+ * rows. Ignore this report; the correct one has been observed to
+ * follow it.
+ */
+ common = 0xff;
+ for (i = 0; i < KEYPAD_GND_ROW; i++)
+ common &= current_set[i];
+
+ if ((allrows & common) != common)
+ return IRQ_HANDLED;
+
+ for (i = 0; i < ARRAY_SIZE(current_set); i++) {
+ /*
+ * If there is an allrows press (GND row), we need to ignore
+ * the allrows values from the reset of the ARRAYs.
+ */
+ if (i < KEYPAD_GND_ROW && allrows)
+ current_set[i] &= ~allrows;
+
+ if (keypad->previous_set[i] == current_set[i])
+ continue;
+
+ db5500_keypad_report(keypad, i, current_set[i],
+ keypad->previous_set[i]);
+ }
+
+ /* update the reference set of array registers */
+ memcpy(keypad->previous_set, current_set, sizeof(keypad->previous_set));
+
+ return IRQ_HANDLED;
+}
+
+static void db5500_keypad_writel(struct db5500_keypad *keypad, u32 val, u32 reg)
+{
+ int timeout = 4;
+ int allowedbit;
+
+ switch (reg) {
+ case KEYPAD_CTR:
+ allowedbit = KEYPAD_CTR_WRITE_CONTROL;
+ break;
+ case KEYPAD_INT_ENABLE:
+ allowedbit = KEYPAD_CTR_WRITE_IRQ_ENABLE;
+ break;
+ default:
+ BUG();
+ }
+
+ do {
+ u32 ctr = readl(keypad->base + KEYPAD_CTR);
+
+ if (ctr & allowedbit)
+ break;
+
+ udelay(50);
+ } while (--timeout);
+
+ /* Five 32k clk cycles (~150us) required, we waited 200us */
+ WARN_ON(!timeout);
+
+ writel(val, keypad->base + reg);
+}
+
+static int db5500_keypad_chip_init(struct db5500_keypad *keypad)
+{
+ int debounce = keypad->board->debounce_ms;
+ int debounce_hits = 0;
+
+ if (debounce < KEYPAD_DEBOUNCE_PERIOD_MIN)
+ debounce = KEYPAD_DEBOUNCE_PERIOD_MIN;
+
+ if (debounce > KEYPAD_DEBOUNCE_PERIOD_MAX) {
+ debounce_hits = DIV_ROUND_UP(debounce,
+ KEYPAD_DEBOUNCE_PERIOD_MAX) - 1;
+ debounce = KEYPAD_DEBOUNCE_PERIOD_MAX;
+ }
+
+ /* Convert the milliseconds to the bit mask */
+ debounce = DIV_ROUND_UP(debounce, KEYPAD_DEBOUNCE_PERIOD_MIN) - 1;
+
+ clk_enable(keypad->clk);
+
+ db5500_keypad_writel(keypad,
+ KEYPAD_CTR_SCAN_ENABLE
+ | ((debounce_hits & 0x7) << 4)
+ | debounce,
+ KEYPAD_CTR);
+
+ db5500_keypad_writel(keypad, 0x1, KEYPAD_INT_ENABLE);
+
+ return 0;
+}
+
+static void db5500_keypad_close(struct db5500_keypad *keypad)
+{
+ db5500_keypad_writel(keypad, 0, KEYPAD_CTR);
+ db5500_keypad_writel(keypad, 0, KEYPAD_INT_ENABLE);
+
+ clk_disable(keypad->clk);
+}
+
+static int __devinit db5500_keypad_probe(struct platform_device *pdev)
+{
+ const struct db5500_keypad_platform_data *plat;
+ struct db5500_keypad *keypad;
+ struct resource *res;
+ struct input_dev *input;
+ void __iomem *base;
+ struct clk *clk;
+ int ret;
+ int irq;
+
+ plat = pdev->dev.platform_data;
+ if (!plat) {
+ dev_err(&pdev->dev, "invalid keypad platform data\n");
+ ret = -EINVAL;
+ goto out_ret;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get keypad irq\n");
+ ret = -EINVAL;
+ goto out_ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "missing platform resources\n");
+ ret = -EINVAL;
+ goto out_ret;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ ret = -EBUSY;
+ goto out_ret;
+ }
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ ret = -ENXIO;
+ goto out_freerequest_memregions;
+ }
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to clk_get\n");
+ ret = PTR_ERR(clk);
+ goto out_iounmap;
+ }
+
+ keypad = kzalloc(sizeof(struct db5500_keypad), GFP_KERNEL);
+ if (!keypad) {
+ dev_err(&pdev->dev, "failed to allocate keypad memory\n");
+ ret = -ENOMEM;
+ goto out_freeclk;
+ }
+
+ input = input_allocate_device();
+ if (!input) {
+ dev_err(&pdev->dev, "failed to input_allocate_device\n");
+ ret = -ENOMEM;
+ goto out_freekeypad;
+ }
+
+ input->id.bustype = BUS_HOST;
+ input->name = "db5500-keypad";
+ input->dev.parent = &pdev->dev;
+
+ input->keycode = keypad->keymap;
+ input->keycodesize = sizeof(keypad->keymap[0]);
+ input->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+
+ __set_bit(EV_KEY, input->evbit);
+ if (!plat->no_autorepeat)
+ __set_bit(EV_REP, input->evbit);
+
+ matrix_keypad_build_keymap(plat->keymap_data, KEYPAD_ROW_SHIFT,
+ input->keycode, input->keybit);
+
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "unable to register input device: %d\n", ret);
+ goto out_freeinput;
+ }
+
+ keypad->irq = irq;
+ keypad->board = plat;
+ keypad->input = input;
+ keypad->base = base;
+ keypad->clk = clk;
+
+ ret = db5500_keypad_chip_init(keypad);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to init keypad hardware\n");
+ goto out_unregisterinput;
+ }
+
+ ret = request_threaded_irq(keypad->irq, NULL, db5500_keypad_irq,
+ IRQF_ONESHOT, "db5500-keypad", keypad);
+ if (ret) {
+ dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
+ goto out_unregisterinput;
+ }
+
+ platform_set_drvdata(pdev, keypad);
+
+ return 0;
+
+out_unregisterinput:
+ input_unregister_device(input);
+ input = NULL;
+ clk_disable(keypad->clk);
+out_freeinput:
+ input_free_device(input);
+out_freekeypad:
+ kfree(keypad);
+out_freeclk:
+ clk_put(clk);
+out_iounmap:
+ iounmap(base);
+out_freerequest_memregions:
+ release_mem_region(res->start, resource_size(res));
+out_ret:
+ return ret;
+}
+
+static int __devexit db5500_keypad_remove(struct platform_device *pdev)
+{
+ struct db5500_keypad *keypad = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ free_irq(keypad->irq, keypad);
+ input_unregister_device(keypad->input);
+
+ clk_disable(keypad->clk);
+ clk_put(keypad->clk);
+
+ iounmap(keypad->base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(keypad);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int db5500_keypad_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct db5500_keypad *keypad = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(irq);
+ else {
+ disable_irq(irq);
+ db5500_keypad_close(keypad);
+ }
+
+ return 0;
+}
+
+static int db5500_keypad_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct db5500_keypad *keypad = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(irq);
+ else {
+ db5500_keypad_chip_init(keypad);
+ enable_irq(irq);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops db5500_keypad_dev_pm_ops = {
+ .suspend = db5500_keypad_suspend,
+ .resume = db5500_keypad_resume,
+};
+#endif
+
+static struct platform_driver db5500_keypad_driver = {
+ .driver = {
+ .name = "db5500-keypad",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &db5500_keypad_dev_pm_ops,
+#endif
+ },
+ .probe = db5500_keypad_probe,
+ .remove = __devexit_p(db5500_keypad_remove),
+};
+
+static int __init db5500_keypad_init(void)
+{
+ return platform_driver_register(&db5500_keypad_driver);
+}
+module_init(db5500_keypad_init);
+
+static void __exit db5500_keypad_exit(void)
+{
+ platform_driver_unregister(&db5500_keypad_driver);
+}
+module_exit(db5500_keypad_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("DB5500 Keypad Driver");
+MODULE_ALIAS("platform:db5500-keypad");
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
new file mode 100644
index 00000000000..49b393b74fb
--- /dev/null
+++ b/drivers/input/keyboard/nomadik-ske-keypad.c
@@ -0,0 +1,756 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * License terms:GNU General Public License (GPL) version 2
+ *
+ * Keypad controller driver for the SKE (Scroll Key Encoder) module used in
+ * the Nomadik 8815 and Ux500 platforms.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+#include <plat/ske.h>
+#include <plat/gpio.h>
+
+/* SKE_CR bits */
+#define SKE_KPMLT (0x1 << 6)
+#define SKE_KPCN (0x7 << 3)
+#define SKE_KPASEN (0x1 << 2)
+#define SKE_KPASON (0x1 << 7)
+
+/* SKE_IMSC bits */
+#define SKE_KPIMA (0x1 << 2)
+
+/* SKE_ICR bits */
+#define SKE_KPICS (0x1 << 3)
+#define SKE_KPICA (0x1 << 2)
+
+/* SKE_RIS bits */
+#define SKE_KPRISA (0x1 << 2)
+
+#define SKE_KEYPAD_ROW_SHIFT 3
+#define SKE_KPD_KEYMAP_SIZE (8 * 8)
+
+/* keypad auto scan registers */
+#define SKE_ASR0 0x20
+#define SKE_ASR1 0x24
+#define SKE_ASR2 0x28
+#define SKE_ASR3 0x2C
+
+#define SKE_NUM_ASRX_REGISTERS (4)
+#define KEY_PRESSED_DELAY 10
+
+/**
+ * struct ske_keypad - data structure used by keypad driver
+ * @irq: irq no
+ * @reg_base: ske regsiters base address
+ * @input: pointer to input device object
+ * @board: keypad platform device
+ * @keymap: matrix scan code table for keycodes
+ * @clk: clock structure pointer
+ * @enable: flag to enable the driver event
+ * @regulator: pointer to the regulator used for ske kyepad
+ * @gpio_input_irq: array for gpio irqs
+ * @key_pressed: hold the key state
+ * @work: delayed work variable for gpio switch
+ * @ske_rows: rows gpio array for ske
+ * @ske_cols: columns gpio array for ske
+ * @gpio_row: gpio row
+ * @gpio_col: gpio column
+ * @gpio_work: delayed work variable for release gpio key
+ */
+struct ske_keypad {
+ int irq;
+ void __iomem *reg_base;
+ struct input_dev *input;
+ const struct ske_keypad_platform_data *board;
+ unsigned short keymap[SKE_KPD_KEYMAP_SIZE];
+ struct clk *clk;
+ spinlock_t ske_keypad_lock;
+ bool enable;
+ struct regulator *regulator;
+ int gpio_input_irq[SKE_KPD_MAX_ROWS];
+ int key_pressed;
+ struct delayed_work work;
+ int ske_rows[SKE_KPD_MAX_ROWS];
+ int ske_cols[SKE_KPD_MAX_COLS];
+ int gpio_row;
+ int gpio_col;
+ struct delayed_work gpio_work;
+};
+
+static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
+ u8 mask, u8 data)
+{
+ u32 ret;
+
+ spin_lock(&keypad->ske_keypad_lock);
+
+ ret = readl(keypad->reg_base + addr);
+ ret &= ~mask;
+ ret |= data;
+ writel(ret, keypad->reg_base + addr);
+
+ spin_unlock(&keypad->ske_keypad_lock);
+}
+
+/*
+ * ske_keypad_chip_init: init keypad controller configuration
+ *
+ * Enable Multi key press detection, auto scan mode
+ */
+static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad)
+{
+ u32 value;
+ int timeout = keypad->board->debounce_ms;
+
+ /* check SKE_RIS to be 0 */
+ while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)
+ cpu_relax();
+
+ if (!timeout)
+ return -EINVAL;
+
+ /*
+ * set debounce value
+ * keypad dbounce is configured in DBCR[15:8]
+ * dbounce value in steps of 32/32.768 ms
+ */
+ spin_lock(&keypad->ske_keypad_lock);
+ value = readl(keypad->reg_base + SKE_DBCR);
+ value = value & 0xff;
+ value |= ((keypad->board->debounce_ms * 32000)/32768) << 8;
+ writel(value, keypad->reg_base + SKE_DBCR);
+ spin_unlock(&keypad->ske_keypad_lock);
+
+ /* enable multi key detection */
+ ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);
+
+ /*
+ * set up the number of columns
+ * KPCN[5:3] defines no. of keypad columns to be auto scanned
+ */
+ value = (keypad->board->kcol - 1) << 3;
+ ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value);
+
+ /* clear keypad interrupt for auto(and pending SW) scans */
+ ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS);
+
+ /* un-mask keypad interrupts */
+ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+ /* enable automatic scan */
+ ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN);
+
+ return 0;
+}
+
+static void ske_mode_enable(struct ske_keypad *keypad, bool enable)
+{
+ int i;
+
+ if (!enable) {
+ writel(0, keypad->reg_base + SKE_CR);
+ if (keypad->board->exit)
+ keypad->board->exit();
+ for (i = 0; i < keypad->board->krow; i++) {
+ enable_irq(keypad->gpio_input_irq[i]);
+ enable_irq_wake(keypad->gpio_input_irq[i]);
+ }
+ clk_disable(keypad->clk);
+ regulator_disable(keypad->regulator);
+ } else {
+ regulator_enable(keypad->regulator);
+ clk_enable(keypad->clk);
+ for (i = 0; i < keypad->board->krow; i++) {
+ disable_irq_nosync(keypad->gpio_input_irq[i]);
+ disable_irq_wake(keypad->gpio_input_irq[i]);
+ }
+ if (keypad->board->init)
+ keypad->board->init();
+ ske_keypad_chip_init(keypad);
+ }
+}
+static void ske_enable(struct ske_keypad *keypad, bool enable)
+{
+ keypad->enable = enable;
+ if (keypad->enable) {
+ enable_irq(keypad->irq);
+ ske_mode_enable(keypad, true);
+ } else {
+ ske_mode_enable(keypad, false);
+ disable_irq(keypad->irq);
+ }
+}
+
+static ssize_t ske_show_attr_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ske_keypad *keypad = platform_get_drvdata(pdev);
+ return sprintf(buf, "%d\n", keypad->enable);
+}
+
+static ssize_t ske_store_attr_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ske_keypad *keypad = platform_get_drvdata(pdev);
+ unsigned long val;
+
+ if (strict_strtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if ((val != 0) && (val != 1))
+ return -EINVAL;
+
+ if (keypad->enable != val) {
+ keypad->enable = val ? true : false;
+ ske_enable(keypad, keypad->enable);
+ }
+ return count;
+}
+
+static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
+ ske_show_attr_enable, ske_store_attr_enable);
+
+static struct attribute *ske_keypad_attrs[] = {
+ &dev_attr_enable.attr,
+ NULL,
+};
+
+static struct attribute_group ske_attr_group = {
+ .attrs = ske_keypad_attrs,
+};
+
+static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col)
+{
+ int row = 0, code, pos;
+ struct input_dev *input = keypad->input;
+ u32 ske_ris;
+ int num_of_rows;
+
+ /* find out the row */
+ num_of_rows = hweight8(status);
+ do {
+ pos = __ffs(status);
+ row = pos;
+ status &= ~(1 << pos);
+
+ code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
+ ske_ris = readl(keypad->reg_base + SKE_RIS);
+ keypad->key_pressed = ske_ris & SKE_KPRISA;
+
+ input_event(input, EV_MSC, MSC_SCAN, code);
+ input_report_key(input, keypad->keymap[code],
+ keypad->key_pressed);
+ input_sync(input);
+ num_of_rows--;
+ } while (num_of_rows);
+}
+
+static void ske_keypad_read_data(struct ske_keypad *keypad)
+{
+ u8 status;
+ int col = 0;
+ int ske_asr, i;
+
+ /*
+ * Read the auto scan registers
+ *
+ * Each SKE_ASRx (x=0 to x=3) contains two row values.
+ * lower byte contains row value for column 2*x,
+ * upper byte contains row value for column 2*x + 1
+ */
+ for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) {
+ ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i));
+ if (!ske_asr)
+ continue;
+
+ /* now that ASRx is zero, find out the coloumn x and row y */
+ status = ske_asr & 0xff;
+ if (status) {
+ col = i * 2;
+ ske_keypad_report(keypad, status, col);
+ }
+ status = (ske_asr & 0xff00) >> 8;
+ if (status) {
+ col = (i * 2) + 1;
+ ske_keypad_report(keypad, status, col);
+ }
+ }
+}
+static void ske_keypad_scan(struct ske_keypad *keypad)
+{
+ int timeout = keypad->board->debounce_ms;
+
+ /* disable auto scan interrupt; mask the interrupt generated */
+ ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
+ ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
+
+ while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --timeout)
+ cpu_relax();
+
+ /* SKEx registers are stable and can be read */
+ ske_keypad_read_data(keypad);
+
+ /* wait until raw interrupt is clear */
+ while ((readl(keypad->reg_base + SKE_RIS)) && --timeout)
+ msleep(KEY_PRESSED_DELAY);
+
+ /* enable auto scan interrupts */
+ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+}
+
+static void ske_gpio_switch_work(struct work_struct *work)
+{
+ struct ske_keypad *keypad = container_of(work,
+ struct ske_keypad, work.work);
+
+ ske_mode_enable(keypad, false);
+ keypad->enable = false;
+}
+
+static void ske_gpio_release_work(struct work_struct *work)
+{
+ int code;
+ struct ske_keypad *keypad = container_of(work,
+ struct ske_keypad, gpio_work.work);
+ struct input_dev *input = keypad->input;
+
+ code = MATRIX_SCAN_CODE(keypad->gpio_row, keypad->gpio_col,
+ SKE_KEYPAD_ROW_SHIFT);
+ input_event(input, EV_MSC, MSC_SCAN, code);
+ input_report_key(input, keypad->keymap[code], 1);
+ input_sync(input);
+ input_report_key(input, keypad->keymap[code], 0);
+ input_sync(input);
+}
+
+static int ske_read_get_gpio_row(struct ske_keypad *keypad)
+{
+ int row;
+ int value = 0;
+ int ret;
+
+ /* read all rows GPIO data register values */
+ for (row = 0; row < SKE_KPD_MAX_ROWS ; row++) {
+ ret = gpio_get_value(keypad->ske_rows[row]);
+ value += (1 << row) * ret;
+ }
+
+ /* get the exact row */
+ for (row = 0; row < keypad->board->krow; row++) {
+ if (((1 << row) & value) == 0)
+ return row;
+ }
+
+ return -1;
+}
+
+static void ske_set_cols(struct ske_keypad *keypad, int col)
+{
+ int i ;
+ int value;
+
+ /*
+ * Set all columns except the requested column
+ * output pin as high
+ */
+ for (i = 0; i < SKE_KPD_MAX_COLS; i++) {
+ if (i == col)
+ value = 0;
+ else
+ value = 1;
+ gpio_request(keypad->ske_cols[i], "ske-kp");
+ gpio_direction_output(keypad->ske_cols[i], value);
+ gpio_free(keypad->ske_cols[i]);
+ }
+}
+
+static void ske_free_cols(struct ske_keypad *keypad)
+{
+ int i ;
+
+ for (i = 0; i < SKE_KPD_MAX_COLS; i++) {
+ gpio_request(keypad->ske_cols[i], "ske-kp");
+ gpio_direction_output(keypad->ske_cols[i], 0);
+ gpio_free(keypad->ske_cols[i]);
+ }
+}
+
+static void ske_manual_scan(struct ske_keypad *keypad)
+{
+ int row;
+ int col;
+
+ for (col = 0; col < keypad->board->kcol; col++) {
+ ske_set_cols(keypad, col);
+ row = ske_read_get_gpio_row(keypad);
+ if (row >= 0) {
+ keypad->key_pressed = 1;
+ keypad->gpio_row = row;
+ keypad->gpio_col = col;
+ break;
+ }
+ }
+ ske_free_cols(keypad);
+}
+
+static irqreturn_t ske_keypad_gpio_irq(int irq, void *dev_id)
+{
+ struct ske_keypad *keypad = dev_id;
+
+ if (!gpio_get_value(IRQ_TO_GPIO(irq))) {
+ ske_manual_scan(keypad);
+ if (!keypad->enable) {
+ keypad->enable = true;
+ ske_mode_enable(keypad, true);
+ }
+ /*
+ * Schedule the work queue to change it to
+ * report the key pressed, if it is not detected in SKE mode.
+ */
+ if (keypad->key_pressed)
+ schedule_delayed_work(&keypad->gpio_work,
+ KEY_PRESSED_DELAY);
+ }
+
+ return IRQ_HANDLED;
+}
+static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
+{
+ struct ske_keypad *keypad = dev_id;
+
+ cancel_delayed_work_sync(&keypad->gpio_work);
+ cancel_delayed_work_sync(&keypad->work);
+ ske_keypad_scan(keypad);
+
+ /*
+ * Schedule the work queue to change it to
+ * GPIO mode, if there is no activity in SKE mode
+ */
+ if (!keypad->key_pressed && keypad->enable)
+ schedule_delayed_work(&keypad->work,
+ keypad->board->switch_delay);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit ske_keypad_probe(struct platform_device *pdev)
+{
+ struct ske_keypad *keypad;
+ struct resource *res = NULL;
+ struct input_dev *input;
+ struct clk *clk;
+ void __iomem *reg_base;
+ int ret = 0;
+ int irq;
+ int i;
+ struct ske_keypad_platform_data *plat = pdev->dev.platform_data;
+
+ if (!plat) {
+ dev_err(&pdev->dev, "invalid keypad platform data\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get keypad irq\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "missing platform resources\n");
+ return -ENXIO;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ return -EBUSY;
+ }
+
+ reg_base = ioremap(res->start, resource_size(res));
+ if (!reg_base) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ ret = -ENXIO;
+ goto out_freerequest_memregions;
+ }
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to clk_get\n");
+ ret = PTR_ERR(clk);
+ goto out_freeioremap;
+ }
+
+ /* resources are sane; we begin allocation */
+ keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL);
+ if (!keypad) {
+ dev_err(&pdev->dev, "failed to allocate keypad memory\n");
+ goto out_freeclk;
+ }
+
+ input = input_allocate_device();
+ if (!input) {
+ dev_err(&pdev->dev, "failed to input_allocate_device\n");
+ ret = -ENOMEM;
+ goto out_freekeypad;
+ }
+ keypad->regulator = regulator_get(&pdev->dev, "v-ape");
+ if (IS_ERR(keypad->regulator)) {
+ dev_err(&pdev->dev, "regulator_get failed\n");
+ keypad->regulator = NULL;
+ goto out_regulator_get;
+ } else {
+ ret = regulator_enable(keypad->regulator);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "regulator_enable failed\n");
+ goto out_regulator_enable;
+ }
+ }
+
+ input->id.bustype = BUS_HOST;
+ input->name = "ux500-ske-keypad";
+ input->dev.parent = &pdev->dev;
+
+ input->keycode = keypad->keymap;
+ input->keycodesize = sizeof(keypad->keymap[0]);
+ input->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+ input_set_drvdata(input, keypad);
+
+ __set_bit(EV_KEY, input->evbit);
+ if (!plat->no_autorepeat)
+ __set_bit(EV_REP, input->evbit);
+
+ matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT,
+ input->keycode, input->keybit);
+
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "unable to register input device: %d\n", ret);
+ goto out_freeinput;
+ }
+
+ keypad->irq = irq;
+ keypad->board = plat;
+ keypad->input = input;
+ keypad->reg_base = reg_base;
+ keypad->clk = clk;
+ INIT_DELAYED_WORK(&keypad->work, ske_gpio_switch_work);
+ INIT_DELAYED_WORK(&keypad->gpio_work, ske_gpio_release_work);
+
+ /* allocations are sane, we begin HW initialization */
+ clk_enable(keypad->clk);
+
+ if (!keypad->board->init) {
+ dev_err(&pdev->dev, "init funtion not defined\n");
+ ret = -EINVAL;
+ goto out_unregisterinput;
+ }
+
+ if (keypad->board->init() < 0) {
+ dev_err(&pdev->dev, "keyboard init config failed\n");
+ ret = -EINVAL;
+ goto out_unregisterinput;
+ }
+
+ if (!keypad->board->exit) {
+ dev_err(&pdev->dev, "exit funtion not defined\n");
+ ret = -EINVAL;
+ goto out_unregisterinput;
+ }
+
+ if (keypad->board->exit() < 0) {
+ dev_err(&pdev->dev, "keyboard exit config failed\n");
+ ret = -EINVAL;
+ goto out_unregisterinput;
+ }
+ for (i = 0; i < SKE_KPD_MAX_ROWS; i++) {
+ keypad->ske_rows[i] = *plat->gpio_input_pins;
+ keypad->ske_cols[i] = *plat->gpio_output_pins;
+ keypad->gpio_input_irq[i] =
+ GPIO_TO_IRQ(keypad->ske_rows[i]);
+ plat->gpio_input_pins++;
+ plat->gpio_output_pins++;
+ }
+
+ for (i = 0; i < keypad->board->krow; i++) {
+ ret = request_threaded_irq(keypad->gpio_input_irq[i],
+ NULL, ske_keypad_gpio_irq,
+ IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND,
+ "ske-keypad-gpio", keypad);
+ if (ret) {
+ dev_err(&pdev->dev, "allocate gpio irq %d failed\n",
+ keypad->gpio_input_irq[i]);
+ goto out_unregisterinput;
+ }
+ enable_irq_wake(keypad->gpio_input_irq[i]);
+ }
+
+ ret = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq,
+ IRQF_ONESHOT, "ske-keypad", keypad);
+ if (ret) {
+ dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
+ goto out_unregisterinput;
+ }
+
+ /* sysfs implementation for dynamic enable/disable the input event */
+ ret = sysfs_create_group(&pdev->dev.kobj, &ske_attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to create sysfs entries\n");
+ goto out_free_irq;
+ }
+
+ if (plat->wakeup_enable)
+ device_init_wakeup(&pdev->dev, true);
+
+ platform_set_drvdata(pdev, keypad);
+
+ clk_disable(keypad->clk);
+ regulator_disable(keypad->regulator);
+
+ return 0;
+
+out_free_irq:
+ free_irq(keypad->irq, keypad);
+out_unregisterinput:
+ input_unregister_device(input);
+ input = NULL;
+ clk_disable(keypad->clk);
+out_freeinput:
+ regulator_disable(keypad->regulator);
+out_regulator_enable:
+ regulator_put(keypad->regulator);
+out_regulator_get:
+ input_free_device(input);
+out_freekeypad:
+ kfree(keypad);
+out_freeclk:
+ clk_put(keypad->clk);
+out_freeioremap:
+ iounmap(reg_base);
+out_freerequest_memregions:
+ release_mem_region(res->start, resource_size(res));
+ return ret;
+}
+
+static int __devexit ske_keypad_remove(struct platform_device *pdev)
+{
+ struct ske_keypad *keypad = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ cancel_delayed_work_sync(&keypad->gpio_work);
+ cancel_delayed_work_sync(&keypad->work);
+ free_irq(keypad->irq, keypad);
+
+ input_unregister_device(keypad->input);
+
+ sysfs_remove_group(&pdev->dev.kobj, &ske_attr_group);
+ clk_disable(keypad->clk);
+ clk_put(keypad->clk);
+
+ if (keypad->board->exit)
+ keypad->board->exit();
+
+ regulator_put(keypad->regulator);
+
+ iounmap(keypad->reg_base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(keypad);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ske_keypad_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ske_keypad *keypad = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(irq);
+ else {
+ cancel_delayed_work_sync(&keypad->gpio_work);
+ cancel_delayed_work_sync(&keypad->work);
+ disable_irq(irq);
+ if (keypad->enable) {
+ ske_mode_enable(keypad, false);
+ keypad->enable = false;
+ }
+ }
+
+ return 0;
+}
+
+static int ske_keypad_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ske_keypad *keypad = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(irq);
+ else {
+ if (!keypad->enable) {
+ keypad->enable = true;
+ ske_mode_enable(keypad, true);
+ }
+ enable_irq(irq);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops ske_keypad_dev_pm_ops = {
+ .suspend = ske_keypad_suspend,
+ .resume = ske_keypad_resume,
+};
+#endif
+
+struct platform_driver ske_keypad_driver = {
+ .driver = {
+ .name = "nmk-ske-keypad",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &ske_keypad_dev_pm_ops,
+#endif
+ },
+ .probe = ske_keypad_probe,
+ .remove = __devexit_p(ske_keypad_remove),
+};
+
+static int __init ske_keypad_init(void)
+{
+ return platform_driver_register(&ske_keypad_driver);
+}
+module_init(ske_keypad_init);
+
+static void __exit ske_keypad_exit(void)
+{
+ platform_driver_unregister(&ske_keypad_driver);
+}
+module_exit(ske_keypad_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com>");
+MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver");
+MODULE_ALIAS("platform:nomadik-ske-keypad");
diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c
new file mode 100644
index 00000000000..9b3d3b408dc
--- /dev/null
+++ b/drivers/input/keyboard/stmpe-keypad.c
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/mfd/stmpe.h>
+
+/* These are at the same addresses in all STMPE variants */
+#define STMPE_KPC_COL 0x60
+#define STMPE_KPC_ROW_MSB 0x61
+#define STMPE_KPC_ROW_LSB 0x62
+#define STMPE_KPC_CTRL_MSB 0x63
+#define STMPE_KPC_CTRL_LSB 0x64
+#define STMPE_KPC_COMBI_KEY_0 0x65
+#define STMPE_KPC_COMBI_KEY_1 0x66
+#define STMPE_KPC_COMBI_KEY_2 0x67
+#define STMPE_KPC_DATA_BYTE0 0x68
+#define STMPE_KPC_DATA_BYTE1 0x69
+#define STMPE_KPC_DATA_BYTE2 0x6a
+#define STMPE_KPC_DATA_BYTE3 0x6b
+#define STMPE_KPC_DATA_BYTE4 0x6c
+
+#define STMPE_KPC_CTRL_LSB_SCAN (0x1 << 0)
+#define STMPE_KPC_CTRL_LSB_DEBOUNCE (0x7f << 1)
+#define STMPE_KPC_CTRL_MSB_SCAN_COUNT (0xf << 4)
+
+#define STMPE_KPC_ROW_MSB_ROWS 0xff
+
+#define STMPE_KPC_DATA_UP (0x1 << 7)
+#define STMPE_KPC_DATA_ROW (0xf << 3)
+#define STMPE_KPC_DATA_COL (0x7 << 0)
+#define STMPE_KPC_DATA_NOKEY_MASK 0x78
+
+#define STMPE_KEYPAD_MAX_DEBOUNCE 127
+#define STMPE_KEYPAD_MAX_SCAN_COUNT 15
+
+#define STMPE_KEYPAD_MAX_ROWS 8
+#define STMPE_KEYPAD_MAX_COLS 8
+#define STMPE_KEYPAD_ROW_SHIFT 3
+#define STMPE_KEYPAD_KEYMAP_SIZE \
+ (STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS)
+
+/**
+ * struct stmpe_keypad_variant - model-specific attributes
+ * @auto_increment: whether the KPC_DATA_BYTE register address
+ * auto-increments on multiple read
+ * @num_data: number of data bytes
+ * @num_normal_data: number of normal keys' data bytes
+ * @max_cols: maximum number of columns supported
+ * @max_rows: maximum number of rows supported
+ * @col_gpios: bitmask of gpios which can be used for columns
+ * @row_gpios: bitmask of gpios which can be used for rows
+ */
+struct stmpe_keypad_variant {
+ bool auto_increment;
+ int num_data;
+ int num_normal_data;
+ int max_cols;
+ int max_rows;
+ unsigned int col_gpios;
+ unsigned int row_gpios;
+};
+
+static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
+ [STMPE1601] = {
+ .auto_increment = true,
+ .num_data = 5,
+ .num_normal_data = 3,
+ .max_cols = 8,
+ .max_rows = 8,
+ .col_gpios = 0x000ff, /* GPIO 0 - 7 */
+ .row_gpios = 0x0ff00, /* GPIO 8 - 15 */
+ },
+ [STMPE2401] = {
+ .auto_increment = false,
+ .num_data = 3,
+ .num_normal_data = 2,
+ .max_cols = 8,
+ .max_rows = 12,
+ .col_gpios = 0x0000ff, /* GPIO 0 - 7*/
+ .row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */
+ },
+ [STMPE2403] = {
+ .auto_increment = true,
+ .num_data = 5,
+ .num_normal_data = 3,
+ .max_cols = 8,
+ .max_rows = 12,
+ .col_gpios = 0x0000ff, /* GPIO 0 - 7*/
+ .row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */
+ },
+};
+
+struct stmpe_keypad {
+ struct stmpe *stmpe;
+ struct input_dev *input;
+ const struct stmpe_keypad_variant *variant;
+ const struct stmpe_keypad_platform_data *plat;
+
+ unsigned int rows;
+ unsigned int cols;
+ bool enable;
+
+ unsigned short keymap[STMPE_KEYPAD_KEYMAP_SIZE];
+};
+
+static ssize_t stmpe_show_attr_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stmpe_keypad *keypad = platform_get_drvdata(pdev);
+ return sprintf(buf, "%d\n", keypad->enable);
+}
+
+static ssize_t stmpe_store_attr_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stmpe_keypad *keypad = platform_get_drvdata(pdev);
+ struct stmpe *stmpe = keypad->stmpe;
+ unsigned long val;
+
+ if (strict_strtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (keypad->enable != val) {
+ keypad->enable = val;
+ if (!val)
+ stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD);
+ else
+ stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD);
+ }
+ return count;
+}
+
+static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
+ stmpe_show_attr_enable, stmpe_store_attr_enable);
+
+static struct attribute *stmpe_keypad_attrs[] = {
+ &dev_attr_enable.attr,
+ NULL,
+};
+
+static struct attribute_group stmpe_attr_group = {
+ .attrs = stmpe_keypad_attrs,
+};
+
+static int stmpe_keypad_read_data(struct stmpe_keypad *keypad, u8 *data)
+{
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ struct stmpe *stmpe = keypad->stmpe;
+ int ret;
+ int i;
+
+ if (variant->auto_increment)
+ return stmpe_block_read(stmpe, STMPE_KPC_DATA_BYTE0,
+ variant->num_data, data);
+
+ for (i = 0; i < variant->num_data; i++) {
+ ret = stmpe_reg_read(stmpe, STMPE_KPC_DATA_BYTE0 + i);
+ if (ret < 0)
+ return ret;
+
+ data[i] = ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t stmpe_keypad_irq(int irq, void *dev)
+{
+ struct stmpe_keypad *keypad = dev;
+ struct input_dev *input = keypad->input;
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ u8 fifo[variant->num_data];
+ int ret;
+ int i;
+
+ ret = stmpe_keypad_read_data(keypad, fifo);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = 0; i < variant->num_normal_data; i++) {
+ u8 data = fifo[i];
+ int row = (data & STMPE_KPC_DATA_ROW) >> 3;
+ int col = data & STMPE_KPC_DATA_COL;
+ int code = MATRIX_SCAN_CODE(row, col, STMPE_KEYPAD_ROW_SHIFT);
+ bool up = data & STMPE_KPC_DATA_UP;
+
+ if ((data & STMPE_KPC_DATA_NOKEY_MASK)
+ == STMPE_KPC_DATA_NOKEY_MASK)
+ continue;
+
+ input_event(input, EV_MSC, MSC_SCAN, code);
+ input_report_key(input, keypad->keymap[code], !up);
+ input_sync(input);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad)
+{
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ unsigned int col_gpios = variant->col_gpios;
+ unsigned int row_gpios = variant->row_gpios;
+ struct stmpe *stmpe = keypad->stmpe;
+ unsigned int pins = 0;
+ int i;
+
+ /*
+ * Figure out which pins need to be set to the keypad alternate
+ * function.
+ *
+ * {cols,rows}_gpios are bitmasks of which pins on the chip can be used
+ * for the keypad.
+ *
+ * keypad->{cols,rows} are a bitmask of which pins (of the ones useable
+ * for the keypad) are used on the board.
+ */
+
+ for (i = 0; i < variant->max_cols; i++) {
+ int num = __ffs(col_gpios);
+
+ if (keypad->cols & (1 << i))
+ pins |= 1 << num;
+
+ col_gpios &= ~(1 << num);
+ }
+
+ for (i = 0; i < variant->max_rows; i++) {
+ int num = __ffs(row_gpios);
+
+ if (keypad->rows & (1 << i))
+ pins |= 1 << num;
+
+ row_gpios &= ~(1 << num);
+ }
+
+ return stmpe_set_altfunc(stmpe, pins, STMPE_BLOCK_KEYPAD);
+}
+
+static int __devinit stmpe_keypad_chip_init(struct stmpe_keypad *keypad)
+{
+ const struct stmpe_keypad_platform_data *plat = keypad->plat;
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ struct stmpe *stmpe = keypad->stmpe;
+ int ret;
+
+ if (plat->debounce_ms > STMPE_KEYPAD_MAX_DEBOUNCE)
+ return -EINVAL;
+
+ if (plat->scan_count > STMPE_KEYPAD_MAX_SCAN_COUNT)
+ return -EINVAL;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD);
+ if (ret < 0)
+ return ret;
+
+ ret = stmpe_keypad_altfunc_init(keypad);
+ if (ret < 0)
+ return ret;
+
+ ret = stmpe_reg_write(stmpe, STMPE_KPC_COL, keypad->cols);
+ if (ret < 0)
+ return ret;
+
+ ret = stmpe_reg_write(stmpe, STMPE_KPC_ROW_LSB, keypad->rows);
+ if (ret < 0)
+ return ret;
+
+ if (variant->max_rows > 8) {
+ ret = stmpe_set_bits(stmpe, STMPE_KPC_ROW_MSB,
+ STMPE_KPC_ROW_MSB_ROWS,
+ keypad->rows >> 8);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_KPC_CTRL_MSB,
+ STMPE_KPC_CTRL_MSB_SCAN_COUNT,
+ plat->scan_count << 4);
+ if (ret < 0)
+ return ret;
+
+ return stmpe_set_bits(stmpe, STMPE_KPC_CTRL_LSB,
+ STMPE_KPC_CTRL_LSB_SCAN |
+ STMPE_KPC_CTRL_LSB_DEBOUNCE,
+ STMPE_KPC_CTRL_LSB_SCAN |
+ (plat->debounce_ms << 1));
+}
+
+static int __devinit stmpe_keypad_probe(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct stmpe_keypad_platform_data *plat;
+ struct stmpe_keypad *keypad;
+ struct input_dev *input;
+ int ret;
+ int irq;
+ int i;
+
+ plat = stmpe->pdata->keypad;
+ if (!plat)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ keypad = kzalloc(sizeof(struct stmpe_keypad), GFP_KERNEL);
+ if (!keypad)
+ return -ENOMEM;
+
+ input = input_allocate_device();
+ if (!input) {
+ ret = -ENOMEM;
+ goto out_freekeypad;
+ }
+
+ input->name = "STMPE-keypad";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &pdev->dev;
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+
+ __set_bit(EV_KEY, input->evbit);
+ if (!plat->no_autorepeat)
+ __set_bit(EV_REP, input->evbit);
+
+ input->keycode = keypad->keymap;
+ input->keycodesize = sizeof(keypad->keymap[0]);
+ input->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+ matrix_keypad_build_keymap(plat->keymap_data, STMPE_KEYPAD_ROW_SHIFT,
+ input->keycode, input->keybit);
+
+ for (i = 0; i < plat->keymap_data->keymap_size; i++) {
+ unsigned int key = plat->keymap_data->keymap[i];
+
+ keypad->cols |= 1 << KEY_COL(key);
+ keypad->rows |= 1 << KEY_ROW(key);
+ }
+
+ keypad->stmpe = stmpe;
+ keypad->plat = plat;
+ keypad->input = input;
+ keypad->variant = &stmpe_keypad_variants[stmpe->partnum];
+
+ ret = stmpe_keypad_chip_init(keypad);
+ if (ret < 0)
+ goto out_freeinput;
+
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "unable to register input device: %d\n", ret);
+ goto out_freeinput;
+ }
+
+ ret = request_threaded_irq(irq, NULL, stmpe_keypad_irq, IRQF_ONESHOT,
+ "stmpe-keypad", keypad);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
+ goto out_unregisterinput;
+ }
+
+ /* sysfs implementation for dynamic enable/disable the input event */
+ ret = sysfs_create_group(&pdev->dev.kobj, &stmpe_attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to create sysfs entries\n");
+ goto out_free_irq;
+ }
+
+ keypad->enable = true;
+ platform_set_drvdata(pdev, keypad);
+
+ return 0;
+
+out_free_irq:
+ free_irq(irq, keypad);
+out_unregisterinput:
+ input_unregister_device(input);
+ input = NULL;
+out_freeinput:
+ input_free_device(input);
+out_freekeypad:
+ kfree(keypad);
+ return ret;
+}
+
+static int __devexit stmpe_keypad_remove(struct platform_device *pdev)
+{
+ struct stmpe_keypad *keypad = platform_get_drvdata(pdev);
+ struct stmpe *stmpe = keypad->stmpe;
+ int irq = platform_get_irq(pdev, 0);
+
+ stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD);
+
+ sysfs_remove_group(&pdev->dev.kobj, &stmpe_attr_group);
+ free_irq(irq, keypad);
+ input_unregister_device(keypad->input);
+ platform_set_drvdata(pdev, NULL);
+ kfree(keypad);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int stmpe_keypad_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stmpe_keypad *keypad = platform_get_drvdata(pdev);
+ struct stmpe *stmpe = keypad->stmpe;
+
+ if (!device_may_wakeup(stmpe->dev))
+ stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD);
+
+ return 0;
+}
+
+static int stmpe_keypad_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stmpe_keypad *keypad = platform_get_drvdata(pdev);
+ struct stmpe *stmpe = keypad->stmpe;
+
+ if (!device_may_wakeup(stmpe->dev))
+ stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD);
+
+ return 0;
+}
+
+static const struct dev_pm_ops stmpe_keypad_dev_pm_ops = {
+ .suspend = stmpe_keypad_suspend,
+ .resume = stmpe_keypad_resume,
+};
+#endif
+
+static struct platform_driver stmpe_keypad_driver = {
+ .driver.name = "stmpe-keypad",
+ .driver.owner = THIS_MODULE,
+#if CONFIG_PM
+ .driver.pm = &stmpe_keypad_dev_pm_ops,
+#endif
+ .probe = stmpe_keypad_probe,
+ .remove = __devexit_p(stmpe_keypad_remove),
+};
+
+static int __init stmpe_keypad_init(void)
+{
+ return platform_driver_register(&stmpe_keypad_driver);
+}
+module_init(stmpe_keypad_init);
+
+static void __exit stmpe_keypad_exit(void)
+{
+ platform_driver_unregister(&stmpe_keypad_driver);
+}
+module_exit(stmpe_keypad_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPExxxx keypad driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/input/keyboard/tc35893-keypad.c b/drivers/input/keyboard/tc35893-keypad.c
new file mode 100644
index 00000000000..5ced8c34151
--- /dev/null
+++ b/drivers/input/keyboard/tc35893-keypad.c
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Jayeeta Banerjee <jayeeta.banerjee@stericsson.com> for ST-Ericsson
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * License Terms: GNU General Public License, version 2
+ *
+ * TC35893 MFD Keypad Controller driver
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <mach/tc35893-keypad.h>
+
+/* Maximum supported keypad matrix row/columns size */
+#define TC_MAX_KPROW 8
+#define TC_MAX_KPCOL 12
+
+/* keypad related Constants */
+#define TC_MAX_DEBOUNCE_SETTLE 0xFF
+#define DEDICATED_KEY_VAL 0xFF
+
+/* Keyboard Configuration Registers Index */
+#define TC_KBDSETTLE_REG 0x01
+#define TC_KBDBOUNCE 0x02
+#define TC_KBDSIZE 0x03
+#define TC_KBCFG_LSB 0x04
+#define TC_KBCFG_MSB 0x05
+#define TC_KBDRIS 0x06
+#define TC_KBDMIS 0x07
+#define TC_KBDIC 0x08
+#define TC_KBDMSK 0x09
+#define TC_KBDCODE0 0x0B
+#define TC_KBDCODE1 0x0C
+#define TC_KBDCODE2 0x0D
+#define TC_KBDCODE3 0x0E
+#define TC_EVTCODE_FIFO 0x10
+
+/* System registers Index */
+#define TC_MANUFACTURE_CODE 0x80
+#define TC_VERSION_ID 0x81
+#define TC_I2CSA 0x82
+#define TC_IOCFG 0xA7
+
+/* clock control registers */
+#define TC_CLKMODE 0x88
+#define TC_CLKCFG 0x89
+#define TC_CLKEN 0x8A
+
+/* Reset Control registers */
+#define TC_RSTCTRL 0x82
+#define TC_RSTINTCLR 0x84
+
+/* special function register and drive config registers */
+#define TC_KBDMFS 0x8F
+#define TC_IRQST 0x91
+#define TC_DRIVE0_LSB 0xA0
+#define TC_DRIVE0_MSB 0xA1
+#define TC_DRIVE1_LSB 0xA2
+#define TC_DRIVE1_MSB 0xA3
+#define TC_DRIVE2_LSB 0xA4
+#define TC_DRIVE2_MSB 0xA5
+#define TC_DRIVE3 0xA6
+
+/* Pull up/down configuration registers */
+#define TC_IOCFG 0xA7
+#define TC_IOPULLCFG0_LSB 0xAA
+#define TC_IOPULLCFG0_MSB 0xAB
+#define TC_IOPULLCFG1_LSB 0xAC
+#define TC_IOPULLCFG1_MSB 0xAD
+#define TC_IOPULLCFG2_LSB 0xAE
+
+/* Pull up/down masks */
+#define TC_NO_PULL_MASK 0x0
+#define TC_PULL_DOWN_MASK 0x1
+#define TC_PULL_UP_MASK 0x2
+#define TC_PULLUP_ALL_MASK 0xAA
+#define TC_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2))
+
+/* Bit masks for IOCFG register */
+#define IOCFG_BALLCFG 0x01
+#define IOCFG_IG 0x08
+
+#define KP_EVCODE_COL_MASK 0x0F
+#define KP_EVCODE_ROW_MASK 0x70
+#define KP_RELEASE_EVT_MASK 0x80
+
+#define KP_ROW_SHIFT 4
+
+#define KP_NO_VALID_KEY_MASK 0x7F
+
+
+#define TC_MAN_CODE_VAL 0x03
+#define TC_SW_VERSION 0x80
+
+/* bit masks for RESTCTRL register */
+#define TC_KBDRST 0x2
+#define TC_IRQRST 0x10
+#define TC_RESET_ALL 0x1B
+
+/* KBDMFS register bit mask */
+#define TC_KBDMFS_EN 0x1
+
+/* CLKEN register bitmask */
+#define KPD_CLK_EN 0x1
+
+/* RSTINTCLR register bit mask */
+#define IRQ_CLEAR 0x1
+
+/* bit masks for keyboard interrupts*/
+#define TC_EVT_LOSS_INT 0x8
+#define TC_EVT_INT 0x4
+#define TC_KBD_LOSS_INT 0x2
+#define TC_KBD_INT 0x1
+
+/* bit masks for keyboard interrupt clear*/
+#define TC_EVT_INT_CLR 0x2
+#define TC_KBD_INT_CLR 0x1
+
+#define TC_KBD_KEYMAP_SIZE 64
+
+/**
+ * struct tc_keypad - data structure used by keypad driver
+ * @input: pointer to input device object
+ * @board: keypad platform device
+ * @client: pointer to i2c client
+ * @lock: lock
+ * @krow: number of rows
+ * @kcol: number of coloumns
+ * @keymap: matrix scan code table for keycodes
+ * @enable: bool to enable/disable keypad operation
+ */
+struct tc_keypad {
+ struct input_dev *input;
+ const struct tc35893_platform_data *board;
+ struct i2c_client *client;
+ struct mutex lock;
+ unsigned int krow;
+ unsigned int kcol;
+ unsigned short keymap[TC_KBD_KEYMAP_SIZE];
+ bool enable;
+};
+
+
+static int tc35893_write_byte(struct i2c_client *client,
+ unsigned char reg, unsigned char data)
+{
+ int ret;
+ struct tc_keypad *keypad = i2c_get_clientdata(client);
+
+ mutex_lock(&keypad->lock);
+ ret = i2c_smbus_write_byte_data(client, reg, data);
+ if (ret < 0)
+ dev_err(&client->dev, "Error in writing reg %x: error = %d\n",
+ reg, ret);
+ mutex_unlock(&keypad->lock);
+
+ return ret;
+}
+
+static int tc35893_read_byte(struct i2c_client *client, unsigned char reg,
+ unsigned char *val)
+{
+ int ret;
+ struct tc_keypad *keypad = i2c_get_clientdata(client);
+
+ mutex_lock(&keypad->lock);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ dev_err(&client->dev, "Error in reading reg %x: error = %d\n",
+ reg, ret);
+ *val = ret;
+ mutex_unlock(&keypad->lock);
+
+ return ret;
+}
+
+static int tc35893_set_bits(struct i2c_client *client, u16 addr,
+ u8 mask, u8 data)
+{
+ int err;
+ u8 val;
+
+ err = tc35893_read_byte(client, addr, &val);
+ if (err < 0)
+ return err;
+
+ val &= ~mask;
+ val |= data;
+
+ err = tc35893_write_byte(client, addr, val);
+
+ return err;
+}
+
+static int __devinit tc35893_keypad_init_key_hardware(struct tc_keypad *keypad)
+{
+ int ret;
+ struct i2c_client *client = keypad->client;
+ u8 settle_time = keypad->board->settle_time;
+ u8 dbounce_period = keypad->board->debounce_period;
+ u8 rows = keypad->board->krow & 0xf; /* mask out the nibble */
+ u8 column = keypad->board->kcol & 0xf; /* mask out the nibble */
+
+ /* validate platform configurations */
+ if ((keypad->board->kcol > TC_MAX_KPCOL) ||
+ (keypad->board->krow > TC_MAX_KPROW) ||
+ (keypad->board->debounce_period > TC_MAX_DEBOUNCE_SETTLE) ||
+ (keypad->board->settle_time > TC_MAX_DEBOUNCE_SETTLE))
+ return -EINVAL;
+
+ /* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */
+ ret = tc35893_set_bits(client, TC_KBDSIZE, 0x0,
+ (rows << KP_ROW_SHIFT) | column);
+ if (ret < 0)
+ return ret;
+
+ /* configure dedicated key config, no dedicated key selected */
+ ret = tc35893_set_bits(client, TC_KBCFG_LSB, 0x0, DEDICATED_KEY_VAL);
+ if (ret < 0)
+ return ret;
+
+ ret = tc35893_set_bits(client, TC_KBCFG_MSB, 0x0, DEDICATED_KEY_VAL);
+ if (ret < 0)
+ return ret;
+
+ /* Configure settle time */
+ ret = tc35893_set_bits(client, TC_KBDSETTLE_REG, 0x0, settle_time);
+ if (ret < 0)
+ return ret;
+
+ /* Configure debounce time */
+ ret = tc35893_set_bits(client, TC_KBDBOUNCE, 0x0, dbounce_period);
+ if (ret < 0)
+ return ret;
+
+ /* Start of initialise keypad GPIOs */
+ ret = tc35893_set_bits(client, TC_IOCFG, 0x0, IOCFG_IG);
+ if (ret < 0)
+ return ret;
+
+ /* Configure pull-up resistors for all row GPIOs */
+ ret = tc35893_set_bits(client, TC_IOPULLCFG0_LSB, 0x0,
+ TC_PULLUP_ALL_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = tc35893_set_bits(client, TC_IOPULLCFG0_MSB, 0x0,
+ TC_PULLUP_ALL_MASK);
+ if (ret < 0)
+ return ret;
+
+ /* Configure pull-up resistors for all column GPIOs */
+ ret = tc35893_set_bits(client, TC_IOPULLCFG1_LSB, 0x0,
+ TC_PULLUP_ALL_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = tc35893_set_bits(client, TC_IOPULLCFG1_MSB, 0x0,
+ TC_PULLUP_ALL_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = tc35893_set_bits(client, TC_IOPULLCFG2_LSB, 0x0,
+ TC_PULLUP_ALL_MASK);
+
+ return ret;
+}
+
+#define TC35893_DATA_REGS 4
+#define TC35893_KEYCODE_FIFO_EMPTY 0x7f
+#define TC35893_KEYCODE_FIFO_CLEAR 0xff
+
+static irqreturn_t tc35893_keypad_irq(int irq, void *dev)
+{
+ struct tc_keypad *keypad = (struct tc_keypad *)dev;
+ u8 i, row_index, col_index, kbd_code, up;
+ u8 code;
+
+ for (i = 0; i < (TC35893_DATA_REGS * 2); i++) {
+ tc35893_read_byte(keypad->client, TC_EVTCODE_FIFO, &kbd_code);
+
+ /* loop till fifo is empty and no more keys are pressed */
+ if ((kbd_code == TC35893_KEYCODE_FIFO_EMPTY) ||
+ (kbd_code == TC35893_KEYCODE_FIFO_CLEAR))
+ continue;
+
+ /* valid key is found */
+ col_index = kbd_code & KP_EVCODE_COL_MASK;
+ row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT;
+ code = MATRIX_SCAN_CODE(row_index, col_index, 0x3);
+ up = kbd_code & KP_RELEASE_EVT_MASK;
+
+ input_event(keypad->input, EV_MSC, MSC_SCAN, code);
+ input_report_key(keypad->input, keypad->keymap[code], !up);
+ input_sync(keypad->input);
+ }
+
+ /* clear IRQ */
+ tc35893_set_bits(keypad->client, TC_KBDIC,
+ 0x0, TC_EVT_INT_CLR | TC_KBD_INT_CLR);
+ /* enable IRQ */
+ tc35893_set_bits(keypad->client, TC_KBDMSK,
+ 0x0, TC_EVT_LOSS_INT | TC_EVT_INT);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit tc35893_keypad_enable(struct i2c_client *client)
+{
+ int ret;
+
+ /* pull the keypad module out of reset */
+ ret = tc35893_set_bits(client, TC_RSTCTRL, TC_KBDRST, 0x0);
+ if (ret < 0)
+ return ret;
+
+ /* configure KBDMFS */
+ ret = tc35893_set_bits(client, TC_KBDMFS, 0x0, TC_KBDMFS_EN);
+ if (ret < 0)
+ return ret;
+
+ /* enable the keypad clock */
+ ret = tc35893_set_bits(client, TC_CLKEN, 0x0, KPD_CLK_EN);
+ if (ret < 0)
+ return ret;
+
+ /* clear pending IRQs */
+ ret = tc35893_set_bits(client, TC_RSTINTCLR, 0x0, 0x1);
+ if (ret < 0)
+ return ret;
+
+ /* enable the IRQs */
+ ret = tc35893_set_bits(client, TC_KBDMSK, 0x0,
+ TC_EVT_LOSS_INT | TC_EVT_INT);
+
+ return ret;
+}
+
+static int __devexit tc35893_keypad_disable(struct i2c_client *client)
+{
+ int ret;
+
+ /* clear IRQ */
+ ret = tc35893_set_bits(client, TC_KBDIC,
+ 0x0, TC_EVT_INT_CLR | TC_KBD_INT_CLR);
+ if (ret < 0)
+ return ret;
+
+ /* disable all interrupts */
+ ret = tc35893_set_bits(client, TC_KBDMSK,
+ ~(TC_EVT_LOSS_INT | TC_EVT_INT), 0x0);
+ if (ret < 0)
+ return ret;
+
+ /* disable the keypad module */
+ ret = tc35893_set_bits(client, TC_CLKEN, 0x1, 0x0);
+ if (ret < 0)
+ return ret;
+
+ /* put the keypad module into reset */
+ ret = tc35893_set_bits(client, TC_RSTCTRL, TC_KBDRST, 0x1);
+
+ return ret;
+}
+
+static ssize_t keypad_show_attr_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tc_keypad *keypad = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%u\n", keypad->enable);
+}
+
+static ssize_t keypad_store_attr_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tc_keypad *keypad = i2c_get_clientdata(client);
+ unsigned long state;
+
+ if (sscanf(buf, "%lu", &state) != 1)
+ return -EINVAL;
+
+ if ((state != 1) && (state != 0))
+ return -EINVAL;
+
+ if (state != keypad->enable) {
+ if (state)
+ tc35893_keypad_enable(keypad->client);
+ else
+ tc35893_keypad_disable(keypad->client);
+ keypad->enable = state;
+ }
+
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
+ keypad_show_attr_enable, keypad_store_attr_enable);
+
+static struct attribute *tc35893_keypad_attrs[] = {
+ &dev_attr_enable.attr,
+ NULL,
+};
+
+static struct attribute_group tc35893_attr_group = {
+ .attrs = tc35893_keypad_attrs,
+};
+
+static int __devinit tc35893_keypad_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct tc35893_platform_data *plat = client->dev.platform_data;
+ struct tc_keypad *keypad;
+ struct input_dev *input;
+ int error;
+
+ if (!plat) {
+ dev_err(&client->dev, "invalid keypad platform data\n");
+ return -EINVAL;
+ }
+
+ if (plat->irq < 0) {
+ dev_err(&client->dev, "failed to get keypad irq\n");
+ return -EINVAL;
+ }
+
+ keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!keypad || !input) {
+ dev_err(&client->dev, "failed to allocate keypad memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ keypad->client = client;
+ keypad->board = plat;
+ keypad->input = input;
+ mutex_init(&keypad->lock);
+
+ i2c_set_clientdata(client, keypad);
+
+ /* issue a soft reset to all modules */
+ error = tc35893_set_bits(client, TC_RSTCTRL, 0x0, TC_RESET_ALL);
+ if (error < 0)
+ return error;
+
+ /* pull the irq module out of reset */
+ error = tc35893_set_bits(client, TC_RSTCTRL, TC_IRQRST, 0x0);
+ if (error < 0)
+ return error;
+
+ error = tc35893_keypad_enable(client);
+ if (error < 0) {
+ dev_err(&client->dev, "failed to enable keypad module\n");
+ goto err_free_mem;
+ }
+
+ error = tc35893_keypad_init_key_hardware(keypad);
+ if (error < 0) {
+ dev_err(&client->dev, "failed to configure keypad module\n");
+ goto err_free_mem;
+ }
+
+ input->id.bustype = BUS_HOST;
+ input->name = client->name;
+ input->dev.parent = &client->dev;
+
+ input->keycode = keypad->keymap;
+ input->keycodesize = sizeof(keypad->keymap[0]);
+ input->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+
+ __set_bit(EV_KEY, input->evbit);
+ if (!plat->no_autorepeat)
+ __set_bit(EV_REP, input->evbit);
+
+ matrix_keypad_build_keymap(plat->keymap_data, 0x3,
+ input->keycode, input->keybit);
+
+ error = request_threaded_irq(plat->irq, NULL,
+ tc35893_keypad_irq, plat->irqtype,
+ input->name, keypad);
+ if (error < 0) {
+ dev_err(&client->dev,
+ "Could not allocate irq %d,error %d\n",
+ plat->irq, error);
+ goto err_free_mem;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&client->dev, "Could not register input device\n");
+ goto err_free_irq;
+ }
+
+ device_init_wakeup(&client->dev, plat->enable_wakeup);
+ device_set_wakeup_capable(&client->dev, plat->enable_wakeup);
+
+ /* sysfs implementation for dynamic enable/disable the input event */
+ error = sysfs_create_group(&client->dev.kobj, &tc35893_attr_group);
+ if (error) {
+ dev_err(&client->dev, "Could not register sysfs entries\n");
+ goto err_free_irq;
+ }
+
+ keypad->enable = true;
+
+ return 0;
+
+err_free_irq:
+ free_irq(plat->irq, keypad);
+err_free_mem:
+ input_free_device(input);
+ kfree(keypad);
+ return error;
+}
+
+static int __devexit tc35893_keypad_remove(struct i2c_client *client)
+{
+ struct tc_keypad *keypad = i2c_get_clientdata(client);
+
+ free_irq(keypad->board->irq, keypad);
+
+ sysfs_remove_group(&client->dev.kobj, &tc35893_attr_group);
+
+ input_unregister_device(keypad->input);
+
+ tc35893_keypad_disable(keypad->client);
+
+ kfree(keypad);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tc35893_keypad_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tc_keypad *keypad = i2c_get_clientdata(client);
+ int irq = keypad->board->irq;
+
+ client = keypad->client;
+
+ /* disable the IRQ */
+ if (!device_may_wakeup(&client->dev))
+ tc35893_keypad_disable(client);
+ else
+ enable_irq_wake(irq);
+
+ return 0;
+}
+
+static int tc35893_keypad_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tc_keypad *keypad = i2c_get_clientdata(client);
+ int irq = keypad->board->irq;
+
+ client = keypad->client;
+
+ /* enable the IRQ */
+ if (!device_may_wakeup(&client->dev))
+ tc35893_keypad_enable(client);
+ else
+ disable_irq_wake(irq);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tc35893_keypad_dev_pm_ops = {
+ .suspend = tc35893_keypad_suspend,
+ .resume = tc35893_keypad_resume,
+};
+#endif
+
+static const struct i2c_device_id tc35893_id[] = {
+ { "tc35893-kp", 23 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tc35893_id);
+
+static struct i2c_driver tc35893kpd_driver = {
+ .driver = {
+ .name = "tc35893-kp",
+#ifdef CONFIG_PM
+ .pm = &tc35893_keypad_dev_pm_ops,
+#endif
+ },
+ .probe = tc35893_keypad_probe,
+ .remove = __devexit_p(tc35893_keypad_remove),
+ .id_table = tc35893_id,
+};
+
+static int __init tc35893_keypad_init(void)
+{
+ return i2c_add_driver(&tc35893kpd_driver);
+}
+
+static void __exit tc35893_keypad_exit(void)
+{
+ return i2c_del_driver(&tc35893kpd_driver);
+}
+
+module_init(tc35893_keypad_init);
+module_exit(tc35893_keypad_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer");
+MODULE_DESCRIPTION("TC35893 Keypad Driver");
+
diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c
new file mode 100644
index 00000000000..b51350602fc
--- /dev/null
+++ b/drivers/input/keyreset.c
@@ -0,0 +1,230 @@
+/* drivers/input/keyreset.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/keyreset.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+
+
+struct keyreset_state {
+ struct input_handler input_handler;
+ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
+ unsigned long upbit[BITS_TO_LONGS(KEY_CNT)];
+ unsigned long key[BITS_TO_LONGS(KEY_CNT)];
+ spinlock_t lock;
+ int key_down_target;
+ int key_down;
+ int key_up;
+ int restart_disabled;
+};
+
+int restart_requested;
+static void deferred_restart(struct work_struct *dummy)
+{
+ restart_requested = 2;
+ sys_sync();
+ restart_requested = 3;
+ kernel_restart(NULL);
+}
+static DECLARE_WORK(restart_work, deferred_restart);
+
+static void keyreset_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+ unsigned long flags;
+ struct keyreset_state *state = handle->private;
+
+ if (type != EV_KEY)
+ return;
+
+ if (code >= KEY_MAX)
+ return;
+
+ if (!test_bit(code, state->keybit))
+ return;
+
+ spin_lock_irqsave(&state->lock, flags);
+ if (!test_bit(code, state->key) == !value)
+ goto done;
+ __change_bit(code, state->key);
+ if (test_bit(code, state->upbit)) {
+ if (value) {
+ state->restart_disabled = 1;
+ state->key_up++;
+ } else
+ state->key_up--;
+ } else {
+ if (value)
+ state->key_down++;
+ else
+ state->key_down--;
+ }
+ if (state->key_down == 0 && state->key_up == 0)
+ state->restart_disabled = 0;
+
+ pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value,
+ state->key_down, state->key_up, state->restart_disabled);
+
+ if (value && !state->restart_disabled &&
+ state->key_down == state->key_down_target) {
+ state->restart_disabled = 1;
+ if (restart_requested)
+ panic("keyboard reset failed, %d", restart_requested);
+ pr_info("keyboard reset\n");
+ schedule_work(&restart_work);
+ restart_requested = 1;
+ }
+done:
+ spin_unlock_irqrestore(&state->lock, flags);
+}
+
+static int keyreset_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ int i;
+ int ret;
+ struct input_handle *handle;
+ struct keyreset_state *state =
+ container_of(handler, struct keyreset_state, input_handler);
+
+ for (i = 0; i < KEY_MAX; i++) {
+ if (test_bit(i, state->keybit) && test_bit(i, dev->keybit))
+ break;
+ }
+ if (i == KEY_MAX)
+ return -ENODEV;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = "keyreset";
+ handle->private = state;
+
+ ret = input_register_handle(handle);
+ if (ret)
+ goto err_input_register_handle;
+
+ ret = input_open_device(handle);
+ if (ret)
+ goto err_input_open_device;
+
+ pr_info("using input dev %s for key reset\n", dev->name);
+
+ return 0;
+
+err_input_open_device:
+ input_unregister_handle(handle);
+err_input_register_handle:
+ kfree(handle);
+ return ret;
+}
+
+static void keyreset_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+static const struct input_device_id keyreset_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(input, keyreset_ids);
+
+static int keyreset_probe(struct platform_device *pdev)
+{
+ int ret;
+ int key, *keyp;
+ struct keyreset_state *state;
+ struct keyreset_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata)
+ return -EINVAL;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ spin_lock_init(&state->lock);
+ keyp = pdata->keys_down;
+ while ((key = *keyp++)) {
+ if (key >= KEY_MAX)
+ continue;
+ state->key_down_target++;
+ __set_bit(key, state->keybit);
+ }
+ if (pdata->keys_up) {
+ keyp = pdata->keys_up;
+ while ((key = *keyp++)) {
+ if (key >= KEY_MAX)
+ continue;
+ __set_bit(key, state->keybit);
+ __set_bit(key, state->upbit);
+ }
+ }
+ state->input_handler.event = keyreset_event;
+ state->input_handler.connect = keyreset_connect;
+ state->input_handler.disconnect = keyreset_disconnect;
+ state->input_handler.name = KEYRESET_NAME;
+ state->input_handler.id_table = keyreset_ids;
+ ret = input_register_handler(&state->input_handler);
+ if (ret) {
+ kfree(state);
+ return ret;
+ }
+ platform_set_drvdata(pdev, state);
+ return 0;
+}
+
+int keyreset_remove(struct platform_device *pdev)
+{
+ struct keyreset_state *state = platform_get_drvdata(pdev);
+ input_unregister_handler(&state->input_handler);
+ kfree(state);
+ return 0;
+}
+
+
+struct platform_driver keyreset_driver = {
+ .driver.name = KEYRESET_NAME,
+ .probe = keyreset_probe,
+ .remove = keyreset_remove,
+};
+
+static int __init keyreset_init(void)
+{
+ return platform_driver_register(&keyreset_driver);
+}
+
+static void __exit keyreset_exit(void)
+{
+ return platform_driver_unregister(&keyreset_driver);
+}
+
+module_init(keyreset_init);
+module_exit(keyreset_exit);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index c44b9eafc55..9151ea98c5f 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -22,6 +22,23 @@ config INPUT_88PM860X_ONKEY
To compile this driver as a module, choose M here: the module
will be called 88pm860x_onkey.
+config INPUT_AB8500_ACCDET
+ bool "AB8500 AV Accessory detection"
+ depends on AB8500_CORE && AB8500_GPADC && AB8500_GPIO
+ help
+ Say Y here to enable AV accessory detection features for ST-Ericsson's
+ AB8500 Mix-Sig PMIC.
+
+config INPUT_AB8500_PONKEY
+ tristate "AB5500/AB8500 Pon (PowerOn) Key"
+ depends on AB5500_CORE || AB8500_CORE
+ help
+ Say Y here to use the PowerOn Key for ST-Ericsson's AB5500/AB8500
+ Mix-Sig PMICs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ab8500-ponkey.
+
config INPUT_AD714X
tristate "Analog Devices AD714x Capacitance Touch Sensor"
help
@@ -183,6 +200,17 @@ config INPUT_ATI_REMOTE2
To compile this driver as a module, choose M here: the module will be
called ati_remote2.
+config INPUT_KEYCHORD
+ tristate "Key chord input driver support"
+ help
+ Say Y here if you want to enable the key chord driver
+ accessible at /dev/keychord. This driver can be used
+ for receiving notifications when client specified key
+ combinations are pressed.
+
+ To compile this driver as a module, choose M here: the
+ module will be called keychord.
+
config INPUT_KEYSPAN_REMOTE
tristate "Keyspan DMR USB remote control (EXPERIMENTAL)"
depends on EXPERIMENTAL
@@ -199,6 +227,12 @@ config INPUT_KEYSPAN_REMOTE
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
+config INPUT_LPS001WP
+ tristate "LPS0001WP pressure sensor from ST Micro"
+ help
+ This is a pressure sensor connected to I2C, mounted on the
+ snowball and other ST-E boards
+
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
@@ -302,6 +336,11 @@ config INPUT_WINBOND_CIR
To compile this driver as a module, choose M here: the module will be
called winbond_cir.
+config INPUT_GPIO
+ tristate "GPIO driver support"
+ help
+ Say Y here if you want to support gpio based keys, wheels etc...
+
config HP_SDC_RTC
tristate "HP SDC Real Time Clock"
depends on (GSC || HP300) && SERIO
@@ -390,4 +429,14 @@ config INPUT_PCAP
To compile this driver as a module, choose M here: the
module will be called pcap_keys.
+config INPUT_STE_FF_VIBRA
+ tristate "ST-Ericsson Force Feedback Vibrator"
+ depends on STE_AUDIO_IO_DEV
+ select INPUT_FF_MEMLESS
+ help
+ This option enables support for ST-Ericsson's Vibrator which
+ registers as an input force feedback driver.
+
+ To compile this driver as a module, choose M here. The module will
+ be called ste_ff_vibra.
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 71fe57d8023..ee8d05c963a 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -5,6 +5,8 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
+obj-$(CONFIG_INPUT_AB8500_ACCDET) += ab8500-accdet.o
+obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o
@@ -16,9 +18,12 @@ obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
+obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
+obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
+obj-$(CONFIG_INPUT_LPS001WP) += lps001wp_prs.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
@@ -37,4 +42,4 @@ obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
-
+obj-$(CONFIG_INPUT_STE_FF_VIBRA) += ste_ff_vibra.o
diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c
new file mode 100644
index 00000000000..4544c0dd5b3
--- /dev/null
+++ b/drivers/input/misc/ab8500-accdet.c
@@ -0,0 +1,1428 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Jarmo K. Kuronen <jarmo.kuronen@symbio.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GPL V2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/module.h> /* Needed by all modules */
+#include <linux/kernel.h> /* Needed for KERN_INFO */
+#include <linux/init.h> /* Needed for the macros */
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500/ab8500-gpadc.h>
+#include <linux/mfd/ab8500/ab8500-gpio.h>
+#include <linux/gpio.h>
+#include <mach/ab8500-accdet.h>
+#ifdef CONFIG_SND_SOC_UX500_AB8500
+#include <sound/ux500_ab8500.h>
+#else
+#define ux500_ab8500_jack_report(i)
+#endif
+
+#define MAX_DET_COUNT 10
+#define MAX_VOLT_DIFF 30
+#define MIN_MIC_POWER -100
+
+/* Unique value used to identify Headset button input device */
+#define BTN_INPUT_UNIQUE_VALUE "AB8500HsBtn"
+#define BTN_INPUT_DEV_NAME "AB8500 Hs Button"
+
+#define DEBOUNCE_PLUG_EVENT_MS 100
+#define DEBOUNCE_PLUG_RETEST_MS 25
+#define DEBOUNCE_UNPLUG_EVENT_MS 0
+
+/*
+ * Register definition for accessory detection.
+ */
+#define AB8500_REGU_CTRL1_SPARE_REG 0x84
+#define AB8500_ACC_DET_DB1_REG 0x80
+#define AB8500_ACC_DET_DB2_REG 0x81
+#define AB8500_ACC_DET_CTRL_REG 0x82
+#define AB8500_IT_SOURCE5_REG 0x04
+
+/* REGISTER: AB8500_ACC_DET_CTRL_REG */
+#define BITS_ACCDETCTRL2_ENA (0x20 | 0x10 | 0x08)
+#define BITS_ACCDETCTRL1_ENA (0x02 | 0x01)
+
+/* REGISTER: AB8500_REGU_CTRL1_SPARE_REG */
+#define BIT_REGUCTRL1SPARE_VAMIC1_GROUND 0x01
+
+/* REGISTER: AB8500_IT_SOURCE5_REG */
+#define BIT_ITSOURCE5_ACCDET1 0x04
+
+/* After being loaded, how fast the first check is to be made */
+#define INIT_DELAY_MS 3000
+
+/* Voltage limits (mV) for various types of AV Accessories */
+#define ACCESSORY_DET_VOL_DONTCARE -1
+#define ACCESSORY_HEADPHONE_DET_VOL_MIN 0
+#define ACCESSORY_HEADPHONE_DET_VOL_MAX 40
+#define ACCESSORY_CVIDEO_DET_VOL_MIN 41
+#define ACCESSORY_CVIDEO_DET_VOL_MAX 105
+#define ACCESSORY_CARKIT_DET_VOL_MIN 1100
+#define ACCESSORY_CARKIT_DET_VOL_MAX 1300
+#define ACCESSORY_HEADSET_DET_VOL_MIN 0
+#define ACCESSORY_HEADSET_DET_VOL_MAX 200
+#define ACCESSORY_OPENCABLE_DET_VOL_MIN 1730
+#define ACCESSORY_OPENCABLE_DET_VOL_MAX 2150
+
+/* Macros */
+
+/*
+ * Conviniency macros to check jack characteristics.
+ */
+#define jack_supports_mic(type) \
+ (type == JACK_TYPE_HEADSET || type == JACK_TYPE_CARKIT)
+#define jack_supports_spkr(type) \
+ ((type != JACK_TYPE_DISCONNECTED) && (type != JACK_TYPE_CONNECTED))
+#define jack_supports_buttons(type) \
+ ((type == JACK_TYPE_HEADSET) ||\
+ (type == JACK_TYPE_CARKIT) ||\
+ (type == JACK_TYPE_OPENCABLE) ||\
+ (type == JACK_TYPE_CONNECTED))
+
+
+/* Enumerations */
+
+/**
+ * @JACK_TYPE_UNSPECIFIED Not known whether any accessories are connected.
+ * @JACK_TYPE_DISCONNECTED No accessories connected.
+ * @JACK_TYPE_CONNECTED Accessory is connected but functionality was unable to
+ * detect the actual type. In this mode, possible button events are reported.
+ * @JACK_TYPE_HEADPHONE Headphone type of accessory (spkrs only) connected
+ * @JACK_TYPE_HEADSET Headset type of accessory (mic+spkrs) connected
+ * @JACK_TYPE_CARKIT Carkit type of accessory connected
+ * @JACK_TYPE_OPENCABLE Open cable connected
+ * @JACK_TYPE_CVIDEO CVideo type of accessory connected.
+ */
+enum accessory_jack_type {
+ JACK_TYPE_UNSPECIFIED,
+ JACK_TYPE_DISCONNECTED,
+ JACK_TYPE_CONNECTED,
+ JACK_TYPE_HEADPHONE,
+ JACK_TYPE_HEADSET,
+ JACK_TYPE_CARKIT,
+ JACK_TYPE_OPENCABLE,
+ JACK_TYPE_CVIDEO
+};
+
+/**
+ * @BUTTON_UNK Button state not known
+ * @BUTTON_PRESSED Button "down"
+ * @BUTTON_RELEASED Button "up"
+ */
+enum accessory_button_state {
+ BUTTON_UNK,
+ BUTTON_PRESSED,
+ BUTTON_RELEASED
+};
+
+/**
+ * @PLUG_IRQ Interrupt gen. when accessory plugged in
+ * @UNPLUG_IRQ Interrupt gen. when accessory plugged out
+ * @BUTTON_PRESS_IRQ Interrupt gen. when accessory button pressed.
+ * @BUTTON_RELEASE_IRQ Interrupt gen. when accessory button released.
+ */
+enum accessory_irq {
+ PLUG_IRQ,
+ UNPLUG_IRQ,
+ BUTTON_PRESS_IRQ,
+ BUTTON_RELEASE_IRQ
+};
+
+/**
+ * Enumerates the op. modes of the avcontrol switch
+ * @AUDIO_IN Audio input is selected
+ * @VIDEO_OUT Video output is selected
+ * @NOT_SET The av-switch control signal is disconnected.
+ */
+enum accessory_avcontrol_dir {
+ AUDIO_IN,
+ VIDEO_OUT,
+ NOT_SET,
+};
+
+/**
+ * @REGULATOR_VAUDIO v-audio regulator
+ * @REGULATOR_VAMIC1 v-amic1 regulator
+ * @REGULATOR_AVSWITCH Audio/Video select switch regulator
+ * @REGULATOR_ALL All regulators combined
+ */
+enum accessory_regulator {
+ REGULATOR_NONE = 0x0,
+ REGULATOR_VAUDIO = 0x1,
+ REGULATOR_VAMIC1 = 0x2,
+ REGULATOR_AVSWITCH = 0x4,
+ REGULATOR_ALL = 0xFF
+};
+
+/* Structures */
+
+/**
+ * Describes an interrupt
+ * @irq interrupt identifier
+ * @name name of the irq in platform data
+ * @isr interrupt service routine
+ * @register are we currently registered to receive interrupts from this source.
+ */
+struct accessory_irq_descriptor {
+ enum accessory_irq irq;
+ const char *name;
+ irq_handler_t isr;
+ int registered;
+};
+
+/**
+ * Encapsulates info of single regulator.
+ * @id regulator identifier
+ * @name name of the regulator
+ * @enabled flag indicating whether regu is currently enabled.
+ * @handle regulator handle
+ */
+struct accessory_regu_descriptor {
+ enum accessory_regulator id;
+ const char *name;
+ int enabled;
+ struct regulator *handle;
+};
+
+/**
+ * Defines attributes for accessory detection operation.
+ * @typename type as string
+ * @type Type of accessory this task tests
+ * @req_det_count How many times this particular type of accessory
+ * needs to be detected in sequence in order to accept. Multidetection
+ * implemented to avoid false detections during plug-in.
+ * @meas_mv Should ACCDETECT2 input voltage be measured just before
+ * making the decision or can cached voltage be used instead.
+ * @minvol minimum voltage (mV) for decision
+ * @maxvol maximum voltage (mV) for decision
+ */
+struct accessory_detect_task {
+ const char *typename;
+ enum accessory_jack_type type;
+ int req_det_count;
+ int meas_mv;
+ int minvol;
+ int maxvol;
+};
+
+/**
+ * Device data, capsulates all relevant device data structures.
+ *
+ * @pdev pointer to platform device
+ * @pdata Platform data
+ * @gpadc interface for ADC data
+ * @irq_work_queue Work queue for deferred interrupt processing
+ *
+ * @detect_work work item to perform detection work
+ * @unplug_irq_work work item to process unplug event
+ * @init_work work item to process initialization work.
+ *
+ * @btn_input_dev button input device used to report btn presses
+ * @btn_state Current state of accessory button
+ *
+ * @jack_type type of currently connected accessory
+ * @reported_jack_type previously reported jack type.
+ * @jack_type_temp temporary storage for currently connected accessory
+ *
+ * @jack_det_count counter how many times in sequence the accessory
+ * type detection has produced same result.
+ * @total_jack_det_count after plug-in irq, how many times detection
+ * has totally been made in order to detect the accessory type
+ *
+ * @detect_jiffies Used to save timestamp when detection was made. Timestamp
+ * used to filter out spurious button presses that might occur during the
+ * plug-in procedure.
+ *
+ * @accdet1_th_set flag to indicate whether accdet1 threshold and debounce
+ * times are configured
+ * @accdet2_th_set flag to indicate whether accdet2 thresholds are configured
+ * @gpio35_dir_set flag to indicate whether GPIO35 (VIDEOCTRL) direction
+ * has been configured.
+ */
+struct ab8500_ad {
+ struct platform_device *pdev;
+ struct ab8500_accdet_platform_data *pdata;
+ struct ab8500_gpadc *gpadc;
+ struct workqueue_struct *irq_work_queue;
+
+ struct delayed_work detect_work;
+ struct delayed_work unplug_irq_work;
+ struct delayed_work init_work;
+
+ struct input_dev *btn_input_dev;
+ enum accessory_button_state btn_state;
+
+ enum accessory_jack_type jack_type;
+ enum accessory_jack_type reported_jack_type;
+ enum accessory_jack_type jack_type_temp;
+
+ int jack_det_count;
+ int total_jack_det_count;
+
+ unsigned long detect_jiffies;
+
+ int accdet1_th_set;
+ int accdet2_th_set;
+ int gpio35_dir_set;
+};
+
+/* Forward declarations */
+
+static void config_accdetect(struct ab8500_ad *dd);
+
+static void release_irq(struct ab8500_ad *dd, enum accessory_irq irq_id);
+static void claim_irq(struct ab8500_ad *dd, enum accessory_irq irq_id);
+
+static irqreturn_t unplug_irq_handler(int irq, void *_userdata);
+static irqreturn_t plug_irq_handler(int irq, void *_userdata);
+static irqreturn_t button_press_irq_handler(int irq, void *_userdata);
+static irqreturn_t button_release_irq_handler(int irq, void *_userdata);
+
+static void unplug_irq_handler_work(struct work_struct *work);
+static void detect_work(struct work_struct *work);
+static void init_work(struct work_struct *work);
+
+static enum accessory_jack_type detect(struct ab8500_ad *dd, int *required_det);
+static void set_av_switch(struct ab8500_ad *dd,
+ enum accessory_avcontrol_dir dir);
+
+/* Static data initialization */
+
+static struct accessory_detect_task detect_ops[] = {
+ {
+ .type = JACK_TYPE_DISCONNECTED,
+ .typename = "DISCONNECTED",
+ .meas_mv = 1,
+ .req_det_count = 1,
+ .minvol = ACCESSORY_DET_VOL_DONTCARE,
+ .maxvol = ACCESSORY_DET_VOL_DONTCARE
+ },
+ {
+ .type = JACK_TYPE_HEADPHONE,
+ .typename = "HEADPHONE",
+ .meas_mv = 1,
+ .req_det_count = 1,
+ .minvol = ACCESSORY_HEADPHONE_DET_VOL_MIN,
+ .maxvol = ACCESSORY_HEADPHONE_DET_VOL_MAX
+ },
+ {
+ .type = JACK_TYPE_CVIDEO,
+ .typename = "CVIDEO",
+ .meas_mv = 0,
+ .req_det_count = 4,
+ .minvol = ACCESSORY_CVIDEO_DET_VOL_MIN,
+ .maxvol = ACCESSORY_CVIDEO_DET_VOL_MAX
+ },
+ {
+ .type = JACK_TYPE_OPENCABLE,
+ .typename = "OPENCABLE",
+ .meas_mv = 0,
+ .req_det_count = 4,
+ .minvol = ACCESSORY_OPENCABLE_DET_VOL_MIN,
+ .maxvol = ACCESSORY_OPENCABLE_DET_VOL_MAX
+ },
+ {
+ .type = JACK_TYPE_CARKIT,
+ .typename = "CARKIT",
+ .meas_mv = 1,
+ .req_det_count = 1,
+ .minvol = ACCESSORY_CARKIT_DET_VOL_MIN,
+ .maxvol = ACCESSORY_CARKIT_DET_VOL_MAX
+ },
+ {
+ .type = JACK_TYPE_HEADSET,
+ .typename = "HEADSET",
+ .meas_mv = 0,
+ .req_det_count = 2,
+ .minvol = ACCESSORY_HEADSET_DET_VOL_MIN,
+ .maxvol = ACCESSORY_HEADSET_DET_VOL_MAX
+ },
+ {
+ .type = JACK_TYPE_CONNECTED,
+ .typename = "CONNECTED",
+ .meas_mv = 0,
+ .req_det_count = 4,
+ .minvol = ACCESSORY_DET_VOL_DONTCARE,
+ .maxvol = ACCESSORY_DET_VOL_DONTCARE
+ }
+};
+
+static struct accessory_regu_descriptor regu_desc[3] = {
+ {
+ .id = REGULATOR_VAUDIO,
+ .name = "v-audio",
+ },
+ {
+ .id = REGULATOR_VAMIC1,
+ .name = "v-amic1",
+ },
+ {
+ .id = REGULATOR_AVSWITCH,
+ .name = "vcc-avswitch",
+ },
+};
+
+static struct accessory_irq_descriptor irq_desc_norm[] = {
+ {
+ .irq = PLUG_IRQ,
+ .name = "ACC_DETECT_1DB_F",
+ .isr = plug_irq_handler,
+ },
+ {
+ .irq = UNPLUG_IRQ,
+ .name = "ACC_DETECT_1DB_R",
+ .isr = unplug_irq_handler,
+ },
+ {
+ .irq = BUTTON_PRESS_IRQ,
+ .name = "ACC_DETECT_22DB_F",
+ .isr = button_press_irq_handler,
+ },
+ {
+ .irq = BUTTON_RELEASE_IRQ,
+ .name = "ACC_DETECT_22DB_R",
+ .isr = button_release_irq_handler,
+ },
+};
+
+static struct accessory_irq_descriptor irq_desc_inverted[] = {
+ {
+ .irq = PLUG_IRQ,
+ .name = "ACC_DETECT_1DB_R",
+ .isr = plug_irq_handler,
+ },
+ {
+ .irq = UNPLUG_IRQ,
+ .name = "ACC_DETECT_1DB_F",
+ .isr = unplug_irq_handler,
+ },
+ {
+ .irq = BUTTON_PRESS_IRQ,
+ .name = "ACC_DETECT_22DB_R",
+ .isr = button_press_irq_handler,
+ },
+ {
+ .irq = BUTTON_RELEASE_IRQ,
+ .name = "ACC_DETECT_22DB_F",
+ .isr = button_release_irq_handler,
+ },
+};
+
+static struct accessory_irq_descriptor *irq_desc;
+
+/*
+ * textual represenation of the accessory type
+ */
+static const char *accessory_str(enum accessory_jack_type type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(detect_ops); i++)
+ if (type == detect_ops[i].type)
+ return detect_ops[i].typename;
+
+ return "UNKNOWN?";
+}
+
+/*
+ * enables regulator but only if it has not been enabled earlier.
+ */
+static void accessory_regulator_enable(enum accessory_regulator reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(regu_desc); i++) {
+ if (reg & regu_desc[i].id) {
+ if (!regu_desc[i].enabled) {
+ if (!regulator_enable(regu_desc[i].handle))
+ regu_desc[i].enabled = 1;
+ }
+ }
+ }
+}
+
+/*
+ * disables regulator but only if it has been previously enabled.
+ */
+static void accessory_regulator_disable(enum accessory_regulator reg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(regu_desc); i++) {
+ if (reg & regu_desc[i].id) {
+ if (regu_desc[i].enabled) {
+ if (!regulator_disable(regu_desc[i].handle))
+ regu_desc[i].enabled = 0;
+ }
+ }
+ }
+}
+
+/*
+ * frees previously retrieved regulators.
+ */
+static void free_regulators(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(regu_desc); i++) {
+ if (regu_desc[i].handle) {
+ regulator_put(regu_desc[i].handle);
+ regu_desc[i].handle = NULL;
+ }
+ }
+}
+
+/*
+ * gets required regulators.
+ */
+static int create_regulators(struct ab8500_ad *dd)
+{
+ int i;
+ int status = 0;
+
+ for (i = 0; i < ARRAY_SIZE(regu_desc); i++) {
+ struct regulator *regu =
+ regulator_get(&dd->pdev->dev, regu_desc[i].name);
+ if (IS_ERR(regu)) {
+ status = PTR_ERR(regu);
+ dev_err(&dd->pdev->dev,
+ "%s: Failed to get supply '%s' (%d).\n",
+ __func__, regu_desc[i].name, status);
+ free_regulators();
+ goto out;
+ } else {
+ regu_desc[i].handle = regu;
+ }
+ }
+
+out:
+ return status;
+}
+
+/*
+ * configures accdet2 input on/off
+ */
+static void config_accdetect2_hw(struct ab8500_ad *dd, int enable)
+{
+ int ret = 0;
+
+ if (!dd->accdet2_th_set) {
+ /* Configure accdetect21+22 thresholds */
+ ret = abx500_set_register_interruptible(&dd->pdev->dev,
+ AB8500_ECI_AV_ACC,
+ AB8500_ACC_DET_DB2_REG,
+ dd->pdata->accdet2122_th);
+ if (ret < 0) {
+ dev_err(&dd->pdev->dev,
+ "%s: Failed to write reg (%d).\n", __func__,
+ ret);
+ goto out;
+ } else {
+ dd->accdet2_th_set = 1;
+ }
+ }
+
+ /* Enable/Disable accdetect21 comparators + pullup */
+ ret = abx500_mask_and_set_register_interruptible(
+ &dd->pdev->dev,
+ AB8500_ECI_AV_ACC,
+ AB8500_ACC_DET_CTRL_REG,
+ BITS_ACCDETCTRL2_ENA,
+ enable ? BITS_ACCDETCTRL2_ENA : 0);
+
+ if (ret < 0)
+ dev_err(&dd->pdev->dev, "%s: Failed to update reg (%d).\n",
+ __func__, ret);
+
+out:
+ return;
+}
+
+/*
+ * configures accdet1 input on/off
+ */
+void config_accdetect1_hw(struct ab8500_ad *dd, int enable)
+{
+ int ret;
+
+ if (!dd->accdet1_th_set) {
+ ret = abx500_set_register_interruptible(&dd->pdev->dev,
+ AB8500_ECI_AV_ACC,
+ AB8500_ACC_DET_DB1_REG,
+ dd->pdata->accdet1_dbth);
+ if (ret < 0)
+ dev_err(&dd->pdev->dev,
+ "%s: Failed to write reg (%d).\n", __func__,
+ ret);
+ else
+ dd->accdet1_th_set = 1;
+ }
+
+ /* enable accdetect1 comparator */
+ ret = abx500_mask_and_set_register_interruptible(
+ &dd->pdev->dev,
+ AB8500_ECI_AV_ACC,
+ AB8500_ACC_DET_CTRL_REG,
+ BITS_ACCDETCTRL1_ENA,
+ enable ? BITS_ACCDETCTRL1_ENA : 0);
+
+ if (ret < 0)
+ dev_err(&dd->pdev->dev,
+ "%s: Failed to update reg (%d).\n", __func__, ret);
+}
+
+/*
+ * create input device for button press reporting
+ */
+static int create_btn_input_dev(struct ab8500_ad *dd)
+{
+ int err;
+
+ dd->btn_input_dev = input_allocate_device();
+ if (!dd->btn_input_dev) {
+ dev_err(&dd->pdev->dev, "%s: Failed to allocate input dev.\n",
+ __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ input_set_capability(dd->btn_input_dev,
+ EV_KEY,
+ dd->pdata->btn_keycode);
+
+ dd->btn_input_dev->name = BTN_INPUT_DEV_NAME;
+ dd->btn_input_dev->uniq = BTN_INPUT_UNIQUE_VALUE;
+ dd->btn_input_dev->dev.parent = &dd->pdev->dev;
+
+ err = input_register_device(dd->btn_input_dev);
+ if (err) {
+ dev_err(&dd->pdev->dev,
+ "%s: register_input_device failed (%d).\n", __func__,
+ err);
+ input_free_device(dd->btn_input_dev);
+ dd->btn_input_dev = NULL;
+ goto out;
+ }
+out:
+ return err;
+}
+
+/*
+ * reports jack status
+ */
+void report_jack_status(struct ab8500_ad *dd)
+{
+ int value = 0;
+
+ /* Never report possible open cable */
+ if (dd->jack_type == JACK_TYPE_OPENCABLE)
+ goto out;
+
+ /* Never report same state twice in a row */
+ if (dd->jack_type == dd->reported_jack_type)
+ goto out;
+ dd->reported_jack_type = dd->jack_type;
+
+ dev_info(&dd->pdev->dev, "Accessory: %s\n",
+ accessory_str(dd->jack_type));
+
+ if (dd->jack_type != JACK_TYPE_DISCONNECTED &&
+ dd->jack_type != JACK_TYPE_UNSPECIFIED)
+ value |= SND_JACK_MECHANICAL;
+ if (jack_supports_mic(dd->jack_type))
+ value |= SND_JACK_MICROPHONE;
+ if (jack_supports_spkr(dd->jack_type))
+ value |= (SND_JACK_HEADPHONE | SND_JACK_LINEOUT);
+ if (dd->jack_type == JACK_TYPE_CVIDEO) {
+ value |= SND_JACK_VIDEOOUT;
+ set_av_switch(dd, VIDEO_OUT);
+ }
+
+ ux500_ab8500_jack_report(value);
+
+out: return;
+}
+
+/*
+ * returns the high level status whether some accessory is connected (1|0).
+ */
+static int detect_plugged_in(struct ab8500_ad *dd)
+{
+ u8 value = 0;
+
+ int status = abx500_get_register_interruptible(
+ &dd->pdev->dev,
+ AB8500_INTERRUPT,
+ AB8500_IT_SOURCE5_REG,
+ &value);
+ if (status < 0) {
+ dev_err(&dd->pdev->dev, "%s: reg read failed (%d).\n",
+ __func__, status);
+ return 0;
+ }
+
+ if (dd->pdata->is_detection_inverted)
+ return value & BIT_ITSOURCE5_ACCDET1 ? 1 : 0;
+ else
+ return value & BIT_ITSOURCE5_ACCDET1 ? 0 : 1;
+}
+
+/*
+ * mic_line_voltage_stable - measures a relative stable voltage from spec. input
+ */
+static int meas_voltage_stable(struct ab8500_ad *dd, u8 input)
+{
+ int iterations = 2;
+ int v1, v2, dv;
+
+ v1 = ab8500_gpadc_convert(dd->gpadc, input);
+ do {
+ msleep(1);
+ --iterations;
+ v2 = ab8500_gpadc_convert(dd->gpadc, input);
+ dv = abs(v2 - v1);
+ v1 = v2;
+ } while (iterations > 0 && dv > MAX_VOLT_DIFF);
+
+ return v1;
+}
+
+/*
+ * worker routine to handle accessory unplug case
+ */
+static void unplug_irq_handler_work(struct work_struct *work)
+{
+ struct ab8500_ad *dd = container_of(work,
+ struct ab8500_ad, unplug_irq_work.work);
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__);
+
+ dd->jack_type = dd->jack_type_temp = JACK_TYPE_DISCONNECTED;
+ dd->jack_det_count = dd->total_jack_det_count = 0;
+ dd->btn_state = BUTTON_UNK;
+ config_accdetect(dd);
+
+ accessory_regulator_disable(REGULATOR_ALL);
+
+ report_jack_status(dd);
+}
+
+/*
+ * interrupt service routine for accessory unplug.
+ */
+static irqreturn_t unplug_irq_handler(int irq, void *_userdata)
+{
+ struct ab8500_ad *dd = _userdata;
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq);
+
+ queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work,
+ msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS));
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * interrupt service routine for accessory plug.
+ */
+static irqreturn_t plug_irq_handler(int irq, void *_userdata)
+{
+ struct ab8500_ad *dd = _userdata;
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n",
+ __func__, irq);
+
+ switch (dd->jack_type) {
+ case JACK_TYPE_DISCONNECTED:
+ case JACK_TYPE_UNSPECIFIED:
+ queue_delayed_work(dd->irq_work_queue, &dd->detect_work,
+ msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS));
+ break;
+
+ default:
+ dev_err(&dd->pdev->dev, "%s: Unexpected plug IRQ\n", __func__);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * worker routine to perform detection.
+ */
+static void detect_work(struct work_struct *work)
+{
+ int req_det_count = 1;
+ enum accessory_jack_type new_type;
+ struct ab8500_ad *dd = container_of(work,
+ struct ab8500_ad, detect_work.work);
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__);
+
+ set_av_switch(dd, AUDIO_IN);
+
+ new_type = detect(dd, &req_det_count);
+
+ dd->total_jack_det_count++;
+ if (dd->jack_type_temp == new_type) {
+ dd->jack_det_count++;
+ } else {
+ dd->jack_det_count = 1;
+ dd->jack_type_temp = new_type;
+ }
+
+ if (dd->total_jack_det_count >= MAX_DET_COUNT) {
+ dev_err(&dd->pdev->dev,
+ "%s: MAX_DET_COUNT(=%d) reached. Bailing out.\n",
+ __func__, MAX_DET_COUNT);
+ queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work,
+ msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS));
+ } else if (dd->jack_det_count >= req_det_count) {
+ dd->total_jack_det_count = dd->jack_det_count = 0;
+ dd->jack_type = new_type;
+ dd->detect_jiffies = jiffies;
+ report_jack_status(dd);
+ config_accdetect(dd);
+ } else {
+ queue_delayed_work(dd->irq_work_queue,
+ &dd->detect_work,
+ msecs_to_jiffies(DEBOUNCE_PLUG_RETEST_MS));
+ }
+}
+
+/*
+ * reports a button event (pressed, released).
+ */
+static void report_btn_event(struct ab8500_ad *dd, int down)
+{
+ input_report_key(dd->btn_input_dev, dd->pdata->btn_keycode, down);
+ input_sync(dd->btn_input_dev);
+
+ dev_dbg(&dd->pdev->dev, "HS-BTN: %s\n", down ? "PRESSED" : "RELEASED");
+}
+
+/*
+ * interrupt service routine invoked when hs button is pressed down.
+ */
+static irqreturn_t button_press_irq_handler(int irq, void *_userdata)
+{
+ struct ab8500_ad *dd = _userdata;
+
+ unsigned long accept_jiffies = dd->detect_jiffies +
+ msecs_to_jiffies(1000);
+ if (time_before(jiffies, accept_jiffies)) {
+ dev_dbg(&dd->pdev->dev, "%s: Skipped spurious btn press.\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq);
+
+ if (dd->jack_type == JACK_TYPE_OPENCABLE) {
+ /* Someting got connected to open cable -> detect.. */
+ config_accdetect2_hw(dd, 0);
+ queue_delayed_work(dd->irq_work_queue, &dd->detect_work,
+ msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS));
+ return IRQ_HANDLED;
+ }
+
+ if (dd->btn_state == BUTTON_PRESSED)
+ return IRQ_HANDLED;
+
+ if (jack_supports_buttons(dd->jack_type)) {
+ dd->btn_state = BUTTON_PRESSED;
+ report_btn_event(dd, 1);
+ } else {
+ dd->btn_state = BUTTON_UNK;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * interrupts service routine invoked when hs button is released.
+ */
+static irqreturn_t button_release_irq_handler(int irq, void *_userdata)
+{
+ struct ab8500_ad *dd = _userdata;
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq);
+
+ if (dd->jack_type == JACK_TYPE_OPENCABLE)
+ return IRQ_HANDLED;
+
+ if (dd->btn_state != BUTTON_PRESSED)
+ return IRQ_HANDLED;
+
+ if (jack_supports_buttons(dd->jack_type)) {
+ report_btn_event(dd, 0);
+ dd->btn_state = BUTTON_RELEASED;
+ } else {
+ dd->btn_state = BUTTON_UNK;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * configures HW so that it is possible to make decision whether
+ * accessory is connected or not.
+ */
+static void config_hw_test_plug_connected(struct ab8500_ad *dd, int enable)
+{
+ int ret;
+
+ dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable);
+
+ ret = ab8500_config_pull_up_or_down(&dd->pdev->dev,
+ dd->pdata->video_ctrl_gpio, !enable);
+ if (ret < 0) {
+ dev_err(&dd->pdev->dev,
+ "%s: Failed to update reg (%d).\n", __func__, ret);
+ return;
+ }
+
+ if (enable)
+ accessory_regulator_enable(REGULATOR_VAMIC1);
+}
+
+/*
+ * configures HW so that carkit/headset detection can be accomplished.
+ */
+static void config_hw_test_basic_carkit(struct ab8500_ad *dd, int enable)
+{
+ int ret;
+
+ dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable);
+
+ if (enable)
+ accessory_regulator_disable(REGULATOR_VAMIC1);
+
+ /* Un-Ground the VAMic1 output when enabled */
+ ret = abx500_mask_and_set_register_interruptible(
+ &dd->pdev->dev,
+ AB8500_REGU_CTRL1,
+ AB8500_REGU_CTRL1_SPARE_REG,
+ BIT_REGUCTRL1SPARE_VAMIC1_GROUND,
+ enable ? BIT_REGUCTRL1SPARE_VAMIC1_GROUND : 0);
+ if (ret < 0)
+ dev_err(&dd->pdev->dev,
+ "%s: Failed to update reg (%d).\n", __func__, ret);
+}
+
+/*
+ * checks whether measured voltage is in given range. depending on arguments,
+ * voltage might be re-measured or previously measured voltage is reused.
+ */
+static int mic_vol_in_range(struct ab8500_ad *dd,
+ int lo, int hi, int force_read)
+{
+ static int mv = MIN_MIC_POWER;
+
+ if (mv == -100 || force_read)
+ mv = meas_voltage_stable(dd, ACC_DETECT2);
+
+ return (mv >= lo && mv <= hi) ? 1 : 0;
+}
+
+/*
+ * checks whether the currently connected HW is of given type.
+ */
+static int detect_hw(struct ab8500_ad *dd,
+ struct accessory_detect_task *task)
+{
+ int status;
+
+ switch (task->type) {
+ case JACK_TYPE_DISCONNECTED:
+ config_hw_test_plug_connected(dd, 1);
+ status = !detect_plugged_in(dd);
+ break;
+ case JACK_TYPE_CONNECTED:
+ config_hw_test_plug_connected(dd, 1);
+ status = detect_plugged_in(dd);
+ break;
+ case JACK_TYPE_CARKIT:
+ config_hw_test_basic_carkit(dd, 1);
+ /* flow through.. */
+ case JACK_TYPE_HEADPHONE:
+ case JACK_TYPE_CVIDEO:
+ case JACK_TYPE_HEADSET:
+ case JACK_TYPE_OPENCABLE:
+ status = mic_vol_in_range(dd,
+ task->minvol,
+ task->maxvol,
+ task->meas_mv);
+ break;
+ default:
+ status = 0;
+ }
+
+ return status;
+}
+
+/*
+ * sets the av switch direction - audio-in vs video-out
+ */
+static void set_av_switch(struct ab8500_ad *dd,
+ enum accessory_avcontrol_dir dir)
+{
+ int ret;
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter (%d)\n", __func__, dir);
+ if (dir == NOT_SET) {
+ ret = gpio_direction_input(dd->pdata->video_ctrl_gpio);
+ dd->gpio35_dir_set = 0;
+ ret = gpio_direction_output(dd->pdata->video_ctrl_gpio, 0);
+ } else if (!dd->gpio35_dir_set) {
+ ret = gpio_direction_output(dd->pdata->video_ctrl_gpio,
+ dir == AUDIO_IN ? 1 : 0);
+ if (ret < 0) {
+ dev_err(&dd->pdev->dev,
+ "%s: Output video ctrl signal failed (%d).\n",
+ __func__, ret);
+ } else {
+ dd->gpio35_dir_set = 1;
+ dev_dbg(&dd->pdev->dev, "AV-SWITCH: %s\n",
+ dir == AUDIO_IN ? "AUDIO_IN" : "VIDEO_OUT");
+ }
+ } else {
+ gpio_set_value(dd->pdata->video_ctrl_gpio,
+ dir == AUDIO_IN ? 1 : 0);
+ }
+}
+
+/*
+ * Tries to detect the currently attached accessory
+ */
+static enum accessory_jack_type detect(struct ab8500_ad *dd,
+ int *req_det_count)
+{
+ enum accessory_jack_type type = JACK_TYPE_DISCONNECTED;
+ int i;
+
+ accessory_regulator_enable(REGULATOR_VAUDIO | REGULATOR_AVSWITCH);
+
+ for (i = 0; i < ARRAY_SIZE(detect_ops); ++i) {
+ if (detect_hw(dd, &detect_ops[i])) {
+ type = detect_ops[i].type;
+ *req_det_count = detect_ops[i].req_det_count;
+ break;
+ }
+ }
+
+ config_hw_test_basic_carkit(dd, 0);
+ config_hw_test_plug_connected(dd, 0);
+
+ if (jack_supports_buttons(type))
+ accessory_regulator_enable(REGULATOR_VAMIC1);
+ else
+ accessory_regulator_disable(REGULATOR_VAMIC1 |
+ REGULATOR_AVSWITCH);
+
+ accessory_regulator_disable(REGULATOR_VAUDIO);
+
+ return type;
+}
+
+/*
+ * registers to specific interrupt
+ */
+static void claim_irq(struct ab8500_ad *dd, enum accessory_irq irq_id)
+{
+ int ret;
+ int irq;
+
+ if (dd->pdata->is_detection_inverted)
+ irq_desc = irq_desc_inverted;
+ else
+ irq_desc = irq_desc_norm;
+
+ if (irq_desc[irq_id].registered)
+ return;
+
+ irq = platform_get_irq_byname(
+ dd->pdev,
+ irq_desc[irq_id].name);
+ if (irq < 0) {
+ dev_err(&dd->pdev->dev,
+ "%s: Failed to get irq %s\n", __func__,
+ irq_desc[irq_id].name);
+ return;
+ }
+
+ ret = request_threaded_irq(irq,
+ NULL,
+ irq_desc[irq_id].isr,
+ IRQF_NO_SUSPEND | IRQF_SHARED,
+ irq_desc[irq_id].name,
+ dd);
+ if (ret != 0) {
+ dev_err(&dd->pdev->dev,
+ "%s: Failed to claim irq %s (%d)\n",
+ __func__,
+ irq_desc[irq_id].name,
+ ret);
+ } else {
+ irq_desc[irq_id].registered = 1;
+ dev_dbg(&dd->pdev->dev, "%s: %s\n",
+ __func__, irq_desc[irq_id].name);
+ }
+}
+
+/*
+ * releases specific interrupt
+ */
+static void release_irq(struct ab8500_ad *dd, enum accessory_irq irq_id)
+{
+ int irq;
+
+ if (dd->pdata->is_detection_inverted)
+ irq_desc = irq_desc_inverted;
+ else
+ irq_desc = irq_desc_norm;
+
+ if (!irq_desc[irq_id].registered)
+ return;
+
+ irq = platform_get_irq_byname(
+ dd->pdev,
+ irq_desc[irq_id].name);
+ if (irq < 0) {
+ dev_err(&dd->pdev->dev,
+ "%s: Failed to get irq %s (%d)\n",
+ __func__,
+ irq_desc[irq_id].name, irq);
+ } else {
+ free_irq(irq, dd);
+ irq_desc[irq_id].registered = 0;
+ dev_dbg(&dd->pdev->dev, "%s: %s\n",
+ __func__, irq_desc[irq_id].name);
+ }
+}
+
+/*
+ * configures interrupts + detection hardware to meet the requirements
+ * set by currently attached accessory type.
+ */
+static void config_accdetect(struct ab8500_ad *dd)
+{
+ switch (dd->jack_type) {
+ case JACK_TYPE_UNSPECIFIED:
+ config_accdetect1_hw(dd, 1);
+ config_accdetect2_hw(dd, 0);
+
+ release_irq(dd, PLUG_IRQ);
+ release_irq(dd, UNPLUG_IRQ);
+ release_irq(dd, BUTTON_PRESS_IRQ);
+ release_irq(dd, BUTTON_RELEASE_IRQ);
+ set_av_switch(dd, NOT_SET);
+ break;
+
+ case JACK_TYPE_DISCONNECTED:
+ set_av_switch(dd, NOT_SET);
+ case JACK_TYPE_HEADPHONE:
+ case JACK_TYPE_CVIDEO:
+ config_accdetect1_hw(dd, 1);
+ config_accdetect2_hw(dd, 0);
+
+ claim_irq(dd, PLUG_IRQ);
+ claim_irq(dd, UNPLUG_IRQ);
+ release_irq(dd, BUTTON_PRESS_IRQ);
+ release_irq(dd, BUTTON_RELEASE_IRQ);
+ break;
+
+ case JACK_TYPE_CONNECTED:
+ case JACK_TYPE_HEADSET:
+ case JACK_TYPE_CARKIT:
+ case JACK_TYPE_OPENCABLE:
+ config_accdetect1_hw(dd, 1);
+ config_accdetect2_hw(dd, 1);
+
+ release_irq(dd, PLUG_IRQ);
+ claim_irq(dd, UNPLUG_IRQ);
+ claim_irq(dd, BUTTON_PRESS_IRQ);
+ claim_irq(dd, BUTTON_RELEASE_IRQ);
+ break;
+
+ default:
+ dev_err(&dd->pdev->dev, "%s: Unknown type: %d\n",
+ __func__, dd->jack_type);
+ }
+}
+
+/*
+ * Deferred initialization of the work.
+ */
+static void init_work(struct work_struct *work)
+{
+ struct ab8500_ad *dd = container_of(work,
+ struct ab8500_ad, init_work.work);
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__);
+
+ dd->jack_type = dd->reported_jack_type = JACK_TYPE_UNSPECIFIED;
+ config_accdetect(dd);
+ queue_delayed_work(dd->irq_work_queue,
+ &dd->detect_work,
+ msecs_to_jiffies(0));
+}
+
+/*
+ * performs platform device initialization
+ */
+int ab8500_accessory_init(struct platform_device *pdev)
+{
+ struct ab8500_ad *dd;
+ struct ab8500_platform_data *plat;
+
+ dev_dbg(&pdev->dev, "Enter: %s\n", __func__);
+
+ dd = kzalloc(sizeof(struct ab8500_ad), GFP_KERNEL);
+ if (!dd) {
+ dev_err(&pdev->dev, "%s: Mem. alloc failed\n", __func__);
+ goto fail_no_mem_for_devdata;
+ }
+
+ dd->pdev = pdev;
+ dd->pdata = pdev->dev.platform_data;
+ plat = dev_get_platdata(pdev->dev.parent);
+
+ if (!plat || !plat->accdet) {
+ dev_err(&pdev->dev, "%s: Failed to get accdet plat data.\n",
+ __func__);
+ goto fail_no_ab8500_dev;
+ }
+ dd->pdata = plat->accdet;
+
+ if (dd->pdata->video_ctrl_gpio) {
+ if (!gpio_is_valid(dd->pdata->video_ctrl_gpio)) {
+ dev_err(&pdev->dev,
+ "%s: Video ctrl GPIO invalid (%d).\n", __func__,
+ dd->pdata->video_ctrl_gpio);
+ goto fail_video_ctrl_gpio;
+ }
+ if (gpio_request(dd->pdata->video_ctrl_gpio, "Video Control")) {
+ dev_err(&pdev->dev, "%s: Get video ctrl GPIO failed.\n",
+ __func__);
+ goto fail_video_ctrl_gpio;
+ }
+ }
+
+ if (create_btn_input_dev(dd) < 0) {
+ dev_err(&pdev->dev, "%s: create_button_input_dev failed.\n",
+ __func__);
+ goto fail_no_btn_input_dev;
+ }
+
+ if (create_regulators(dd) < 0) {
+ dev_err(&pdev->dev, "%s: failed to create regulators\n",
+ __func__);
+ goto fail_no_regulators;
+ }
+ dd->btn_state = BUTTON_UNK;
+
+ dd->irq_work_queue = create_singlethread_workqueue("ab8500_accdet_wq");
+ if (!dd->irq_work_queue) {
+ dev_err(&pdev->dev, "%s: Failed to create wq\n", __func__);
+ goto fail_no_mem_for_wq;
+ }
+ dd->gpadc = ab8500_gpadc_get();
+
+ INIT_DELAYED_WORK(&dd->detect_work, detect_work);
+ INIT_DELAYED_WORK(&dd->unplug_irq_work, unplug_irq_handler_work);
+ INIT_DELAYED_WORK(&dd->init_work, init_work);
+
+ /* Deferred init/detect since no use for the info early in boot */
+ queue_delayed_work(dd->irq_work_queue,
+ &dd->init_work,
+ msecs_to_jiffies(INIT_DELAY_MS));
+
+ platform_set_drvdata(pdev, dd);
+
+ return 0;
+
+fail_no_mem_for_wq:
+ free_regulators();
+fail_no_regulators:
+ input_unregister_device(dd->btn_input_dev);
+fail_no_btn_input_dev:
+ gpio_free(dd->pdata->video_ctrl_gpio);
+fail_video_ctrl_gpio:
+fail_no_ab8500_dev:
+ kfree(dd);
+fail_no_mem_for_devdata:
+
+ return -ENOMEM;
+}
+
+/*
+ * Performs platform device cleanup
+ */
+void ab8500_accessory_cleanup(struct ab8500_ad *dd)
+{
+ dev_dbg(&dd->pdev->dev, "Enter: %s\n", __func__);
+
+ dd->jack_type = JACK_TYPE_UNSPECIFIED;
+ config_accdetect(dd);
+
+ gpio_free(dd->pdata->video_ctrl_gpio);
+ input_unregister_device(dd->btn_input_dev);
+ free_regulators();
+
+ cancel_delayed_work(&dd->detect_work);
+ cancel_delayed_work(&dd->unplug_irq_work);
+ cancel_delayed_work(&dd->init_work);
+ flush_workqueue(dd->irq_work_queue);
+ destroy_workqueue(dd->irq_work_queue);
+
+ kfree(dd);
+}
+
+static int __devinit ab8500_acc_detect_probe(struct platform_device *pdev)
+{
+ return ab8500_accessory_init(pdev);
+}
+
+
+static int __devexit ab8500_acc_detect_remove(struct platform_device *pdev)
+{
+ ab8500_accessory_cleanup(platform_get_drvdata(pdev));
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM)
+static u8 acc_det_ctrl_suspend_val;
+
+static int ab8500_acc_detect_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab8500_ad *dd = platform_get_drvdata(pdev);
+ int irq_id, irq;
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__);
+
+ cancel_delayed_work_sync(&dd->unplug_irq_work);
+ cancel_delayed_work_sync(&dd->detect_work);
+ cancel_delayed_work_sync(&dd->init_work);
+
+ if (dd->pdata->is_detection_inverted)
+ irq_desc = irq_desc_inverted;
+ else
+ irq_desc = irq_desc_norm;
+
+ for (irq_id = 0; irq_id < ARRAY_SIZE(irq_desc_norm); irq_id++) {
+ if (irq_desc[irq_id].registered == 1) {
+ irq = platform_get_irq_byname(
+ dd->pdev,
+ irq_desc[irq_id].name);
+
+ disable_irq(irq);
+ }
+ }
+
+ /* Turn off AccDetect comparators and pull-up */
+ (void) abx500_get_register_interruptible(
+ &dd->pdev->dev,
+ AB8500_ECI_AV_ACC,
+ AB8500_ACC_DET_CTRL_REG,
+ &acc_det_ctrl_suspend_val);
+ /* 0x10 = Keep AccDetect21Ena to avoid headset button irq */
+ (void) abx500_set_register_interruptible(
+ &dd->pdev->dev,
+ AB8500_ECI_AV_ACC,
+ AB8500_ACC_DET_CTRL_REG,
+ 0x10);
+ return 0;
+}
+
+static int ab8500_acc_detect_resume(struct platform_device *pdev)
+{
+ struct ab8500_ad *dd = platform_get_drvdata(pdev);
+ int irq_id, irq;
+
+ dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__);
+
+ /* Turn on AccDetect comparators and pull-up */
+ (void) abx500_set_register_interruptible(
+ &dd->pdev->dev,
+ AB8500_ECI_AV_ACC,
+ AB8500_ACC_DET_CTRL_REG,
+ acc_det_ctrl_suspend_val);
+
+ if (dd->pdata->is_detection_inverted)
+ irq_desc = irq_desc_inverted;
+ else
+ irq_desc = irq_desc_norm;
+
+ for (irq_id = 0; irq_id < ARRAY_SIZE(irq_desc_norm); irq_id++) {
+ if (irq_desc[irq_id].registered == 1) {
+ irq = platform_get_irq_byname(
+ dd->pdev,
+ irq_desc[irq_id].name);
+
+ enable_irq(irq);
+
+ }
+ }
+
+ /* After resume, reinitialize */
+ dd->gpio35_dir_set = dd->accdet1_th_set = dd->accdet2_th_set = 0;
+ queue_delayed_work(dd->irq_work_queue, &dd->init_work, 0);
+
+ return 0;
+}
+#else
+#define ab8500_acc_detect_suspend NULL
+#define ab8500_acc_detect_resume NULL
+#endif
+
+static struct platform_driver ab8500_acc_detect_platform_driver = {
+ .driver = {
+ .name = "ab8500-acc-det",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab8500_acc_detect_probe,
+ .remove = __devexit_p(ab8500_acc_detect_remove),
+ .suspend = ab8500_acc_detect_suspend,
+ .resume = ab8500_acc_detect_resume,
+};
+
+static int __init ab8500_acc_detect_init(void)
+{
+ return platform_driver_register(&ab8500_acc_detect_platform_driver);
+}
+
+static void __exit ab8500_acc_detect_exit(void)
+{
+ platform_driver_unregister(&ab8500_acc_detect_platform_driver);
+}
+
+module_init(ab8500_acc_detect_init);
+module_exit(ab8500_acc_detect_exit);
+
+MODULE_DESCRIPTION("AB8500 AV Accessory detection driver");
+MODULE_ALIAS("platform:ab8500-acc-det");
+MODULE_AUTHOR("ST-Ericsson");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c
new file mode 100644
index 00000000000..39964956de2
--- /dev/null
+++ b/drivers/input/misc/ab8500-ponkey.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * AB8500 Power-On Key handler
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+struct ab8500_ponkey_variant {
+ const char *irq_falling;
+ const char *irq_rising;
+};
+
+static const struct ab8500_ponkey_variant ab5500_onswa = {
+ .irq_falling = "ONSWAn_falling",
+ .irq_rising = "ONSWAn_rising",
+};
+
+static const struct ab8500_ponkey_variant ab8500_ponkey = {
+ .irq_falling = "ONKEY_DBF",
+ .irq_rising = "ONKEY_DBR",
+};
+
+/**
+ * struct ab8500_ponkey_info - ab8500 ponkey information
+ * @input_dev: pointer to input device
+ * @irq_dbf: irq number for falling transition
+ * @irq_dbr: irq number for rising transition
+ */
+struct ab8500_ponkey_info {
+ struct input_dev *idev;
+ int irq_dbf;
+ int irq_dbr;
+};
+
+/* AB8500 gives us an interrupt when ONKEY is held */
+static irqreturn_t ab8500_ponkey_handler(int irq, void *data)
+{
+ struct ab8500_ponkey_info *info = data;
+
+ if (irq == info->irq_dbf)
+ input_report_key(info->idev, KEY_POWER, true);
+ else if (irq == info->irq_dbr)
+ input_report_key(info->idev, KEY_POWER, false);
+
+ input_sync(info->idev);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
+{
+ const struct ab8500_ponkey_variant *variant;
+ struct ab8500_ponkey_info *info;
+ int irq_dbf, irq_dbr, ret;
+
+ variant = (const struct ab8500_ponkey_variant *)
+ pdev->id_entry->driver_data;
+
+ irq_dbf = platform_get_irq_byname(pdev, variant->irq_falling);
+ if (irq_dbf < 0) {
+ dev_err(&pdev->dev, "No IRQ for %s: %d\n",
+ variant->irq_falling, irq_dbf);
+ return irq_dbf;
+ }
+
+ irq_dbr = platform_get_irq_byname(pdev, variant->irq_rising);
+ if (irq_dbr < 0) {
+ dev_err(&pdev->dev, "No IRQ for %s: %d\n",
+ variant->irq_rising, irq_dbr);
+ return irq_dbr;
+ }
+
+ info = kzalloc(sizeof(struct ab8500_ponkey_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->irq_dbf = irq_dbf;
+ info->irq_dbr = irq_dbr;
+
+ info->idev = input_allocate_device();
+ if (!info->idev) {
+ dev_err(&pdev->dev, "Failed to allocate input dev\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ info->idev->name = "AB8500 POn(PowerOn) Key";
+ info->idev->dev.parent = &pdev->dev;
+ info->idev->evbit[0] = BIT_MASK(EV_KEY);
+ info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+
+ ret = input_register_device(info->idev);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register input device: %d\n", ret);
+ goto out_unfreedevice;
+ }
+
+ ret = request_threaded_irq(info->irq_dbf, NULL, ab8500_ponkey_handler,
+ IRQF_NO_SUSPEND, "ab8500-ponkey-dbf",
+ info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n",
+ info->irq_dbf, ret);
+ goto out_unregisterdevice;
+ }
+
+ ret = request_threaded_irq(info->irq_dbr, NULL, ab8500_ponkey_handler,
+ IRQF_NO_SUSPEND, "ab8500-ponkey-dbr",
+ info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n",
+ info->irq_dbr, ret);
+ goto out_irq_dbf;
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ return 0;
+
+out_irq_dbf:
+ free_irq(info->irq_dbf, info);
+out_unregisterdevice:
+ input_unregister_device(info->idev);
+ info->idev = NULL;
+out_unfreedevice:
+ input_free_device(info->idev);
+out:
+ kfree(info);
+ return ret;
+}
+
+static int __devexit ab8500_ponkey_remove(struct platform_device *pdev)
+{
+ struct ab8500_ponkey_info *info = platform_get_drvdata(pdev);
+
+ free_irq(info->irq_dbf, info);
+ free_irq(info->irq_dbr, info);
+ input_unregister_device(info->idev);
+ kfree(info);
+ return 0;
+}
+
+static struct platform_device_id ab8500_ponkey_id_table[] = {
+ { "ab5500-onswa", (kernel_ulong_t)&ab5500_onswa, },
+ { "ab8500-poweron-key", (kernel_ulong_t)&ab8500_ponkey, },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, ab8500_ponkey_id_table);
+
+static struct platform_driver ab8500_ponkey_driver = {
+ .driver = {
+ .name = "ab8500-poweron-key",
+ .owner = THIS_MODULE,
+ },
+ .id_table = ab8500_ponkey_id_table,
+ .probe = ab8500_ponkey_probe,
+ .remove = __devexit_p(ab8500_ponkey_remove),
+};
+
+static int __init ab8500_ponkey_init(void)
+{
+ return platform_driver_register(&ab8500_ponkey_driver);
+}
+module_init(ab8500_ponkey_init);
+
+static void __exit ab8500_ponkey_exit(void)
+{
+ platform_driver_unregister(&ab8500_ponkey_driver);
+}
+module_exit(ab8500_ponkey_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("ST-Ericsson AB8500 Power-ON(Pon) Key driver");
diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c
new file mode 100644
index 00000000000..0acf4a576f5
--- /dev/null
+++ b/drivers/input/misc/gpio_axis.c
@@ -0,0 +1,192 @@
+/* drivers/input/misc/gpio_axis.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+struct gpio_axis_state {
+ struct gpio_event_input_devs *input_devs;
+ struct gpio_event_axis_info *info;
+ uint32_t pos;
+};
+
+uint16_t gpio_axis_4bit_gray_map_table[] = {
+ [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */
+ [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */
+ [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */
+ [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */
+ [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */
+ [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */
+ [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */
+ [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */
+};
+uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in)
+{
+ return gpio_axis_4bit_gray_map_table[in];
+}
+
+uint16_t gpio_axis_5bit_singletrack_map_table[] = {
+ [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */
+ [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */
+ [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */
+ [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */
+ [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */
+ [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */
+ [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */
+ [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */
+ [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */
+ [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */
+};
+uint16_t gpio_axis_5bit_singletrack_map(
+ struct gpio_event_axis_info *info, uint16_t in)
+{
+ return gpio_axis_5bit_singletrack_map_table[in];
+}
+
+static void gpio_event_update_axis(struct gpio_axis_state *as, int report)
+{
+ struct gpio_event_axis_info *ai = as->info;
+ int i;
+ int change;
+ uint16_t state = 0;
+ uint16_t pos;
+ uint16_t old_pos = as->pos;
+ for (i = ai->count - 1; i >= 0; i--)
+ state = (state << 1) | gpio_get_value(ai->gpio[i]);
+ pos = ai->map(ai, state);
+ if (ai->flags & GPIOEAF_PRINT_RAW)
+ pr_info("axis %d-%d raw %x, pos %d -> %d\n",
+ ai->type, ai->code, state, old_pos, pos);
+ if (report && pos != old_pos) {
+ if (ai->type == EV_REL) {
+ change = (ai->decoded_size + pos - old_pos) %
+ ai->decoded_size;
+ if (change > ai->decoded_size / 2)
+ change -= ai->decoded_size;
+ if (change == ai->decoded_size / 2) {
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d unknown direction, "
+ "pos %d -> %d\n", ai->type,
+ ai->code, old_pos, pos);
+ change = 0; /* no closest direction */
+ }
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d change %d\n",
+ ai->type, ai->code, change);
+ input_report_rel(as->input_devs->dev[ai->dev],
+ ai->code, change);
+ } else {
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d now %d\n",
+ ai->type, ai->code, pos);
+ input_event(as->input_devs->dev[ai->dev],
+ ai->type, ai->code, pos);
+ }
+ input_sync(as->input_devs->dev[ai->dev]);
+ }
+ as->pos = pos;
+}
+
+static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_axis_state *as = dev_id;
+ gpio_event_update_axis(as, 1);
+ return IRQ_HANDLED;
+}
+
+int gpio_event_axis_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int ret;
+ int i;
+ int irq;
+ struct gpio_event_axis_info *ai;
+ struct gpio_axis_state *as;
+
+ ai = container_of(info, struct gpio_event_axis_info, info);
+ if (func == GPIO_EVENT_FUNC_SUSPEND) {
+ for (i = 0; i < ai->count; i++)
+ disable_irq(gpio_to_irq(ai->gpio[i]));
+ return 0;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME) {
+ for (i = 0; i < ai->count; i++)
+ enable_irq(gpio_to_irq(ai->gpio[i]));
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ *data = as = kmalloc(sizeof(*as), GFP_KERNEL);
+ if (as == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_axis_state_failed;
+ }
+ as->input_devs = input_devs;
+ as->info = ai;
+ if (ai->dev >= input_devs->count) {
+ pr_err("gpio_event_axis: bad device index %d >= %d "
+ "for %d:%d\n", ai->dev, input_devs->count,
+ ai->type, ai->code);
+ ret = -EINVAL;
+ goto err_bad_device_index;
+ }
+
+ input_set_capability(input_devs->dev[ai->dev],
+ ai->type, ai->code);
+ if (ai->type == EV_ABS) {
+ input_set_abs_params(input_devs->dev[ai->dev], ai->code,
+ 0, ai->decoded_size - 1, 0, 0);
+ }
+ for (i = 0; i < ai->count; i++) {
+ ret = gpio_request(ai->gpio[i], "gpio_event_axis");
+ if (ret < 0)
+ goto err_request_gpio_failed;
+ ret = gpio_direction_input(ai->gpio[i]);
+ if (ret < 0)
+ goto err_gpio_direction_input_failed;
+ ret = irq = gpio_to_irq(ai->gpio[i]);
+ if (ret < 0)
+ goto err_get_irq_num_failed;
+ ret = request_irq(irq, gpio_axis_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "gpio_event_axis", as);
+ if (ret < 0)
+ goto err_request_irq_failed;
+ }
+ gpio_event_update_axis(as, 0);
+ return 0;
+ }
+
+ ret = 0;
+ as = *data;
+ for (i = ai->count - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(ai->gpio[i]), as);
+err_request_irq_failed:
+err_get_irq_num_failed:
+err_gpio_direction_input_failed:
+ gpio_free(ai->gpio[i]);
+err_request_gpio_failed:
+ ;
+ }
+err_bad_device_index:
+ kfree(as);
+ *data = NULL;
+err_alloc_axis_state_failed:
+ return ret;
+}
diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c
new file mode 100644
index 00000000000..a98be67d1ab
--- /dev/null
+++ b/drivers/input/misc/gpio_event.c
@@ -0,0 +1,260 @@
+/* drivers/input/misc/gpio_event.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/earlysuspend.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct gpio_event {
+ struct gpio_event_input_devs *input_devs;
+ const struct gpio_event_platform_data *info;
+ struct early_suspend early_suspend;
+ void *state[0];
+};
+
+static int gpio_input_event(
+ struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ int i;
+ int devnr;
+ int ret = 0;
+ int tmp_ret;
+ struct gpio_event_info **ii;
+ struct gpio_event *ip = input_get_drvdata(dev);
+
+ for (devnr = 0; devnr < ip->input_devs->count; devnr++)
+ if (ip->input_devs->dev[devnr] == dev)
+ break;
+ if (devnr == ip->input_devs->count) {
+ pr_err("gpio_input_event: unknown device %p\n", dev);
+ return -EIO;
+ }
+
+ for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) {
+ if ((*ii)->event) {
+ tmp_ret = (*ii)->event(ip->input_devs, *ii,
+ &ip->state[i],
+ devnr, type, code, value);
+ if (tmp_ret)
+ ret = tmp_ret;
+ }
+ }
+ return ret;
+}
+
+static int gpio_event_call_all_func(struct gpio_event *ip, int func)
+{
+ int i;
+ int ret;
+ struct gpio_event_info **ii;
+
+ if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) {
+ ii = ip->info->info;
+ for (i = 0; i < ip->info->info_count; i++, ii++) {
+ if ((*ii)->func == NULL) {
+ ret = -ENODEV;
+ pr_err("gpio_event_probe: Incomplete pdata, "
+ "no function\n");
+ goto err_no_func;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend)
+ continue;
+ ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i],
+ func);
+ if (ret) {
+ pr_err("gpio_event_probe: function failed\n");
+ goto err_func_failed;
+ }
+ }
+ return 0;
+ }
+
+ ret = 0;
+ i = ip->info->info_count;
+ ii = ip->info->info + i;
+ while (i > 0) {
+ i--;
+ ii--;
+ if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend)
+ continue;
+ (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1);
+err_func_failed:
+err_no_func:
+ ;
+ }
+ return ret;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+void gpio_event_suspend(struct early_suspend *h)
+{
+ struct gpio_event *ip;
+ ip = container_of(h, struct gpio_event, early_suspend);
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND);
+ ip->info->power(ip->info, 0);
+}
+
+void gpio_event_resume(struct early_suspend *h)
+{
+ struct gpio_event *ip;
+ ip = container_of(h, struct gpio_event, early_suspend);
+ ip->info->power(ip->info, 1);
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME);
+}
+#endif
+
+static int gpio_event_probe(struct platform_device *pdev)
+{
+ int err;
+ struct gpio_event *ip;
+ struct gpio_event_platform_data *event_info;
+ int dev_count = 1;
+ int i;
+ int registered = 0;
+
+ event_info = pdev->dev.platform_data;
+ if (event_info == NULL) {
+ pr_err("gpio_event_probe: No pdata\n");
+ return -ENODEV;
+ }
+ if ((!event_info->name && !event_info->names[0]) ||
+ !event_info->info || !event_info->info_count) {
+ pr_err("gpio_event_probe: Incomplete pdata\n");
+ return -ENODEV;
+ }
+ if (!event_info->name)
+ while (event_info->names[dev_count])
+ dev_count++;
+ ip = kzalloc(sizeof(*ip) +
+ sizeof(ip->state[0]) * event_info->info_count +
+ sizeof(*ip->input_devs) +
+ sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL);
+ if (ip == NULL) {
+ err = -ENOMEM;
+ pr_err("gpio_event_probe: Failed to allocate private data\n");
+ goto err_kp_alloc_failed;
+ }
+ ip->input_devs = (void*)&ip->state[event_info->info_count];
+ platform_set_drvdata(pdev, ip);
+
+ for (i = 0; i < dev_count; i++) {
+ struct input_dev *input_dev = input_allocate_device();
+ if (input_dev == NULL) {
+ err = -ENOMEM;
+ pr_err("gpio_event_probe: "
+ "Failed to allocate input device\n");
+ goto err_input_dev_alloc_failed;
+ }
+ input_set_drvdata(input_dev, ip);
+ input_dev->name = event_info->name ?
+ event_info->name : event_info->names[i];
+ input_dev->event = gpio_input_event;
+ ip->input_devs->dev[i] = input_dev;
+ }
+ ip->input_devs->count = dev_count;
+ ip->info = event_info;
+ if (event_info->power) {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ip->early_suspend.suspend = gpio_event_suspend;
+ ip->early_suspend.resume = gpio_event_resume;
+ register_early_suspend(&ip->early_suspend);
+#endif
+ ip->info->power(ip->info, 1);
+ }
+
+ err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT);
+ if (err)
+ goto err_call_all_func_failed;
+
+ for (i = 0; i < dev_count; i++) {
+ err = input_register_device(ip->input_devs->dev[i]);
+ if (err) {
+ pr_err("gpio_event_probe: Unable to register %s "
+ "input device\n", ip->input_devs->dev[i]->name);
+ goto err_input_register_device_failed;
+ }
+ registered++;
+ }
+
+ return 0;
+
+err_input_register_device_failed:
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
+err_call_all_func_failed:
+ if (event_info->power) {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ip->early_suspend);
+#endif
+ ip->info->power(ip->info, 0);
+ }
+ for (i = 0; i < registered; i++)
+ input_unregister_device(ip->input_devs->dev[i]);
+ for (i = dev_count - 1; i >= registered; i--) {
+ input_free_device(ip->input_devs->dev[i]);
+err_input_dev_alloc_failed:
+ ;
+ }
+ kfree(ip);
+err_kp_alloc_failed:
+ return err;
+}
+
+static int gpio_event_remove(struct platform_device *pdev)
+{
+ struct gpio_event *ip = platform_get_drvdata(pdev);
+ int i;
+
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
+ if (ip->info->power) {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ip->early_suspend);
+#endif
+ ip->info->power(ip->info, 0);
+ }
+ for (i = 0; i < ip->input_devs->count; i++)
+ input_unregister_device(ip->input_devs->dev[i]);
+ kfree(ip);
+ return 0;
+}
+
+static struct platform_driver gpio_event_driver = {
+ .probe = gpio_event_probe,
+ .remove = gpio_event_remove,
+ .driver = {
+ .name = GPIO_EVENT_DEV_NAME,
+ },
+};
+
+static int __devinit gpio_event_init(void)
+{
+ return platform_driver_register(&gpio_event_driver);
+}
+
+static void __exit gpio_event_exit(void)
+{
+ platform_driver_unregister(&gpio_event_driver);
+}
+
+module_init(gpio_event_init);
+module_exit(gpio_event_exit);
+
+MODULE_DESCRIPTION("GPIO Event Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c
new file mode 100644
index 00000000000..758df480600
--- /dev/null
+++ b/drivers/input/misc/gpio_input.c
@@ -0,0 +1,353 @@
+/* drivers/input/misc/gpio_input.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/wakelock.h>
+
+enum {
+ DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */
+ DEBOUNCE_PRESSED = BIT(1),
+ DEBOUNCE_NOTPRESSED = BIT(2),
+ DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */
+ DEBOUNCE_POLL = BIT(4), /* Stable polling state */
+
+ DEBOUNCE_UNKNOWN =
+ DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED,
+};
+
+struct gpio_key_state {
+ struct gpio_input_state *ds;
+ uint8_t debounce;
+};
+
+struct gpio_input_state {
+ struct gpio_event_input_devs *input_devs;
+ const struct gpio_event_input_info *info;
+ struct hrtimer timer;
+ int use_irq;
+ int debounce_count;
+ spinlock_t irq_lock;
+ struct wake_lock wake_lock;
+ struct gpio_key_state key_state[0];
+};
+
+static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer)
+{
+ int i;
+ int pressed;
+ struct gpio_input_state *ds =
+ container_of(timer, struct gpio_input_state, timer);
+ unsigned gpio_flags = ds->info->flags;
+ unsigned npolarity;
+ int nkeys = ds->info->keymap_size;
+ const struct gpio_event_direct_entry *key_entry;
+ struct gpio_key_state *key_state;
+ unsigned long irqflags;
+ uint8_t debounce;
+
+#if 0
+ key_entry = kp->keys_info->keymap;
+ key_state = kp->key_state;
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++)
+ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
+ gpio_read_detect_status(key_entry->gpio));
+#endif
+ key_entry = ds->info->keymap;
+ key_state = ds->key_state;
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
+ debounce = key_state->debounce;
+ if (debounce & DEBOUNCE_WAIT_IRQ)
+ continue;
+ if (key_state->debounce & DEBOUNCE_UNSTABLE) {
+ debounce = key_state->debounce = DEBOUNCE_UNKNOWN;
+ enable_irq(gpio_to_irq(key_entry->gpio));
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) continue debounce\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ }
+ npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH);
+ pressed = gpio_get_value(key_entry->gpio) ^ npolarity;
+ if (debounce & DEBOUNCE_POLL) {
+ if (pressed == !(debounce & DEBOUNCE_PRESSED)) {
+ ds->debounce_count++;
+ key_state->debounce = DEBOUNCE_UNKNOWN;
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-"
+ "%x, %d (%d) start debounce\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ }
+ continue;
+ }
+ if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) {
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) debounce pressed 1\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ key_state->debounce = DEBOUNCE_PRESSED;
+ continue;
+ }
+ if (!pressed && (debounce & DEBOUNCE_PRESSED)) {
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) debounce pressed 0\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ key_state->debounce = DEBOUNCE_NOTPRESSED;
+ continue;
+ }
+ /* key is stable */
+ ds->debounce_count--;
+ if (ds->use_irq)
+ key_state->debounce |= DEBOUNCE_WAIT_IRQ;
+ else
+ key_state->debounce |= DEBOUNCE_POLL;
+ if (gpio_flags & GPIOEDF_PRINT_KEYS)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) "
+ "changed to %d\n", ds->info->type,
+ key_entry->code, i, key_entry->gpio, pressed);
+ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
+ key_entry->code, pressed);
+ }
+
+#if 0
+ key_entry = kp->keys_info->keymap;
+ key_state = kp->key_state;
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
+ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
+ gpio_read_detect_status(key_entry->gpio));
+ }
+#endif
+
+ if (ds->debounce_count)
+ hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL);
+ else if (!ds->use_irq)
+ hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL);
+ else
+ wake_unlock(&ds->wake_lock);
+
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_key_state *ks = dev_id;
+ struct gpio_input_state *ds = ks->ds;
+ int keymap_index = ks - ds->key_state;
+ const struct gpio_event_direct_entry *key_entry;
+ unsigned long irqflags;
+ int pressed;
+
+ if (!ds->use_irq)
+ return IRQ_HANDLED;
+
+ key_entry = &ds->info->keymap[keymap_index];
+
+ if (ds->info->debounce_time.tv64) {
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ if (ks->debounce & DEBOUNCE_WAIT_IRQ) {
+ ks->debounce = DEBOUNCE_UNKNOWN;
+ if (ds->debounce_count++ == 0) {
+ wake_lock(&ds->wake_lock);
+ hrtimer_start(
+ &ds->timer, ds->info->debounce_time,
+ HRTIMER_MODE_REL);
+ }
+ if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_event_input_irq_handler: "
+ "key %x-%x, %d (%d) start debounce\n",
+ ds->info->type, key_entry->code,
+ keymap_index, key_entry->gpio);
+ } else {
+ disable_irq_nosync(irq);
+ ks->debounce = DEBOUNCE_UNSTABLE;
+ }
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ } else {
+ pressed = gpio_get_value(key_entry->gpio) ^
+ !(ds->info->flags & GPIOEDF_ACTIVE_HIGH);
+ if (ds->info->flags & GPIOEDF_PRINT_KEYS)
+ pr_info("gpio_event_input_irq_handler: key %x-%x, %d "
+ "(%d) changed to %d\n",
+ ds->info->type, key_entry->code, keymap_index,
+ key_entry->gpio, pressed);
+ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
+ key_entry->code, pressed);
+ }
+ return IRQ_HANDLED;
+}
+
+static int gpio_event_input_request_irqs(struct gpio_input_state *ds)
+{
+ int i;
+ int err;
+ unsigned int irq;
+ unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+ for (i = 0; i < ds->info->keymap_size; i++) {
+ err = irq = gpio_to_irq(ds->info->keymap[i].gpio);
+ if (err < 0)
+ goto err_gpio_get_irq_num_failed;
+ err = request_irq(irq, gpio_event_input_irq_handler,
+ req_flags, "gpio_keys", &ds->key_state[i]);
+ if (err) {
+ pr_err("gpio_event_input_request_irqs: request_irq "
+ "failed for input %d, irq %d\n",
+ ds->info->keymap[i].gpio, irq);
+ goto err_request_irq_failed;
+ }
+ enable_irq_wake(irq);
+ }
+ return 0;
+
+ for (i = ds->info->keymap_size - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(ds->info->keymap[i].gpio),
+ &ds->key_state[i]);
+err_request_irq_failed:
+err_gpio_get_irq_num_failed:
+ ;
+ }
+ return err;
+}
+
+int gpio_event_input_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int ret;
+ int i;
+ unsigned long irqflags;
+ struct gpio_event_input_info *di;
+ struct gpio_input_state *ds = *data;
+
+ di = container_of(info, struct gpio_event_input_info, info);
+
+ if (func == GPIO_EVENT_FUNC_SUSPEND) {
+ if (ds->use_irq)
+ for (i = 0; i < di->keymap_size; i++)
+ disable_irq(gpio_to_irq(di->keymap[i].gpio));
+ hrtimer_cancel(&ds->timer);
+ return 0;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME) {
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ if (ds->use_irq)
+ for (i = 0; i < di->keymap_size; i++)
+ enable_irq(gpio_to_irq(di->keymap[i].gpio));
+ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ if (ktime_to_ns(di->poll_time) <= 0)
+ di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC);
+
+ *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) *
+ di->keymap_size, GFP_KERNEL);
+ if (ds == NULL) {
+ ret = -ENOMEM;
+ pr_err("gpio_event_input_func: "
+ "Failed to allocate private data\n");
+ goto err_ds_alloc_failed;
+ }
+ ds->debounce_count = di->keymap_size;
+ ds->input_devs = input_devs;
+ ds->info = di;
+ wake_lock_init(&ds->wake_lock, WAKE_LOCK_SUSPEND, "gpio_input");
+ spin_lock_init(&ds->irq_lock);
+
+ for (i = 0; i < di->keymap_size; i++) {
+ int dev = di->keymap[i].dev;
+ if (dev >= input_devs->count) {
+ pr_err("gpio_event_input_func: bad device "
+ "index %d >= %d for key code %d\n",
+ dev, input_devs->count,
+ di->keymap[i].code);
+ ret = -EINVAL;
+ goto err_bad_keymap;
+ }
+ input_set_capability(input_devs->dev[dev], di->type,
+ di->keymap[i].code);
+ ds->key_state[i].ds = ds;
+ ds->key_state[i].debounce = DEBOUNCE_UNKNOWN;
+ }
+
+ for (i = 0; i < di->keymap_size; i++) {
+ ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in");
+ if (ret) {
+ pr_err("gpio_event_input_func: gpio_request "
+ "failed for %d\n", di->keymap[i].gpio);
+ goto err_gpio_request_failed;
+ }
+ ret = gpio_direction_input(di->keymap[i].gpio);
+ if (ret) {
+ pr_err("gpio_event_input_func: "
+ "gpio_direction_input failed for %d\n",
+ di->keymap[i].gpio);
+ goto err_gpio_configure_failed;
+ }
+ }
+
+ ret = gpio_event_input_request_irqs(ds);
+
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ ds->use_irq = ret == 0;
+
+ pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s "
+ "mode\n", input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "",
+ ret == 0 ? "interrupt" : "polling");
+
+ hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ds->timer.function = gpio_event_input_timer_func;
+ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ return 0;
+ }
+
+ ret = 0;
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ hrtimer_cancel(&ds->timer);
+ if (ds->use_irq) {
+ for (i = di->keymap_size - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(di->keymap[i].gpio),
+ &ds->key_state[i]);
+ }
+ }
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+
+ for (i = di->keymap_size - 1; i >= 0; i--) {
+err_gpio_configure_failed:
+ gpio_free(di->keymap[i].gpio);
+err_gpio_request_failed:
+ ;
+ }
+err_bad_keymap:
+ wake_lock_destroy(&ds->wake_lock);
+ kfree(ds);
+err_ds_alloc_failed:
+ return ret;
+}
diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c
new file mode 100644
index 00000000000..227eb8fe3c0
--- /dev/null
+++ b/drivers/input/misc/gpio_matrix.c
@@ -0,0 +1,432 @@
+/* drivers/input/misc/gpio_matrix.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/wakelock.h>
+
+struct gpio_kp {
+ struct gpio_event_input_devs *input_devs;
+ struct gpio_event_matrix_info *keypad_info;
+ struct hrtimer timer;
+ struct wake_lock wake_lock;
+ int current_output;
+ unsigned int use_irq:1;
+ unsigned int key_state_changed:1;
+ unsigned int last_key_state_changed:1;
+ unsigned int some_keys_pressed:2;
+ unsigned int disabled_irq:1;
+ unsigned long keys_pressed[0];
+};
+
+static void clear_phantom_key(struct gpio_kp *kp, int out, int in)
+{
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ int key_index = out * mi->ninputs + in;
+ unsigned short keyentry = mi->keymap[key_index];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+
+ if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) {
+ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
+ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
+ "cleared\n", keycode, out, in,
+ mi->output_gpios[out], mi->input_gpios[in]);
+ __clear_bit(key_index, kp->keys_pressed);
+ } else {
+ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
+ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
+ "not cleared\n", keycode, out, in,
+ mi->output_gpios[out], mi->input_gpios[in]);
+ }
+}
+
+static int restore_keys_for_input(struct gpio_kp *kp, int out, int in)
+{
+ int rv = 0;
+ int key_index;
+
+ key_index = out * kp->keypad_info->ninputs + in;
+ while (out < kp->keypad_info->noutputs) {
+ if (test_bit(key_index, kp->keys_pressed)) {
+ rv = 1;
+ clear_phantom_key(kp, out, in);
+ }
+ key_index += kp->keypad_info->ninputs;
+ out++;
+ }
+ return rv;
+}
+
+static void remove_phantom_keys(struct gpio_kp *kp)
+{
+ int out, in, inp;
+ int key_index;
+
+ if (kp->some_keys_pressed < 3)
+ return;
+
+ for (out = 0; out < kp->keypad_info->noutputs; out++) {
+ inp = -1;
+ key_index = out * kp->keypad_info->ninputs;
+ for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) {
+ if (test_bit(key_index, kp->keys_pressed)) {
+ if (inp == -1) {
+ inp = in;
+ continue;
+ }
+ if (inp >= 0) {
+ if (!restore_keys_for_input(kp, out + 1,
+ inp))
+ break;
+ clear_phantom_key(kp, out, inp);
+ inp = -2;
+ }
+ restore_keys_for_input(kp, out, in);
+ }
+ }
+ }
+}
+
+static void report_key(struct gpio_kp *kp, int key_index, int out, int in)
+{
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ int pressed = test_bit(key_index, kp->keys_pressed);
+ unsigned short keyentry = mi->keymap[key_index];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+
+ if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) {
+ if (keycode == KEY_RESERVED) {
+ if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS)
+ pr_info("gpiomatrix: unmapped key, %d-%d "
+ "(%d-%d) changed to %d\n",
+ out, in, mi->output_gpios[out],
+ mi->input_gpios[in], pressed);
+ } else {
+ if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS)
+ pr_info("gpiomatrix: key %x, %d-%d (%d-%d) "
+ "changed to %d\n", keycode,
+ out, in, mi->output_gpios[out],
+ mi->input_gpios[in], pressed);
+ input_report_key(kp->input_devs->dev[dev], keycode, pressed);
+ }
+ }
+}
+
+static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer)
+{
+ int out, in;
+ int key_index;
+ int gpio;
+ struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer);
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ unsigned gpio_keypad_flags = mi->flags;
+ unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH);
+
+ out = kp->current_output;
+ if (out == mi->noutputs) {
+ out = 0;
+ kp->last_key_state_changed = kp->key_state_changed;
+ kp->key_state_changed = 0;
+ kp->some_keys_pressed = 0;
+ } else {
+ key_index = out * mi->ninputs;
+ for (in = 0; in < mi->ninputs; in++, key_index++) {
+ gpio = mi->input_gpios[in];
+ if (gpio_get_value(gpio) ^ !polarity) {
+ if (kp->some_keys_pressed < 3)
+ kp->some_keys_pressed++;
+ kp->key_state_changed |= !__test_and_set_bit(
+ key_index, kp->keys_pressed);
+ } else
+ kp->key_state_changed |= __test_and_clear_bit(
+ key_index, kp->keys_pressed);
+ }
+ gpio = mi->output_gpios[out];
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(gpio, !polarity);
+ else
+ gpio_direction_input(gpio);
+ out++;
+ }
+ kp->current_output = out;
+ if (out < mi->noutputs) {
+ gpio = mi->output_gpios[out];
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(gpio, polarity);
+ else
+ gpio_direction_output(gpio, polarity);
+ hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+ if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) {
+ if (kp->key_state_changed) {
+ hrtimer_start(&kp->timer, mi->debounce_delay,
+ HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+ kp->key_state_changed = kp->last_key_state_changed;
+ }
+ if (kp->key_state_changed) {
+ if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS)
+ remove_phantom_keys(kp);
+ key_index = 0;
+ for (out = 0; out < mi->noutputs; out++)
+ for (in = 0; in < mi->ninputs; in++, key_index++)
+ report_key(kp, key_index, out, in);
+ }
+ if (!kp->use_irq || kp->some_keys_pressed) {
+ hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+
+ /* No keys are pressed, reenable interrupt */
+ for (out = 0; out < mi->noutputs; out++) {
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(mi->output_gpios[out], polarity);
+ else
+ gpio_direction_output(mi->output_gpios[out], polarity);
+ }
+ for (in = 0; in < mi->ninputs; in++)
+ enable_irq(gpio_to_irq(mi->input_gpios[in]));
+ wake_unlock(&kp->wake_lock);
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id)
+{
+ int i;
+ struct gpio_kp *kp = dev_id;
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ unsigned gpio_keypad_flags = mi->flags;
+
+ if (!kp->use_irq) {
+ /* ignore interrupt while registering the handler */
+ kp->disabled_irq = 1;
+ disable_irq_nosync(irq_in);
+ return IRQ_HANDLED;
+ }
+
+ for (i = 0; i < mi->ninputs; i++)
+ disable_irq_nosync(gpio_to_irq(mi->input_gpios[i]));
+ for (i = 0; i < mi->noutputs; i++) {
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(mi->output_gpios[i],
+ !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH));
+ else
+ gpio_direction_input(mi->output_gpios[i]);
+ }
+ wake_lock(&kp->wake_lock);
+ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ return IRQ_HANDLED;
+}
+
+static int gpio_keypad_request_irqs(struct gpio_kp *kp)
+{
+ int i;
+ int err;
+ unsigned int irq;
+ unsigned long request_flags;
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+
+ switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) {
+ default:
+ request_flags = IRQF_TRIGGER_FALLING;
+ break;
+ case GPIOKPF_ACTIVE_HIGH:
+ request_flags = IRQF_TRIGGER_RISING;
+ break;
+ case GPIOKPF_LEVEL_TRIGGERED_IRQ:
+ request_flags = IRQF_TRIGGER_LOW;
+ break;
+ case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH:
+ request_flags = IRQF_TRIGGER_HIGH;
+ break;
+ }
+
+ for (i = 0; i < mi->ninputs; i++) {
+ err = irq = gpio_to_irq(mi->input_gpios[i]);
+ if (err < 0)
+ goto err_gpio_get_irq_num_failed;
+ err = request_irq(irq, gpio_keypad_irq_handler, request_flags,
+ "gpio_kp", kp);
+ if (err) {
+ pr_err("gpiomatrix: request_irq failed for input %d, "
+ "irq %d\n", mi->input_gpios[i], irq);
+ goto err_request_irq_failed;
+ }
+ err = set_irq_wake(irq, 1);
+ if (err) {
+ pr_err("gpiomatrix: set_irq_wake failed for input %d, "
+ "irq %d\n", mi->input_gpios[i], irq);
+ }
+ disable_irq(irq);
+ if (kp->disabled_irq) {
+ kp->disabled_irq = 0;
+ enable_irq(irq);
+ }
+ }
+ return 0;
+
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
+err_request_irq_failed:
+err_gpio_get_irq_num_failed:
+ ;
+ }
+ return err;
+}
+
+int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int i;
+ int err;
+ int key_count;
+ struct gpio_kp *kp;
+ struct gpio_event_matrix_info *mi;
+
+ mi = container_of(info, struct gpio_event_matrix_info, info);
+ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) {
+ /* TODO: disable scanning */
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ if (mi->keymap == NULL ||
+ mi->input_gpios == NULL ||
+ mi->output_gpios == NULL) {
+ err = -ENODEV;
+ pr_err("gpiomatrix: Incomplete pdata\n");
+ goto err_invalid_platform_data;
+ }
+ key_count = mi->ninputs * mi->noutputs;
+
+ *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) *
+ BITS_TO_LONGS(key_count), GFP_KERNEL);
+ if (kp == NULL) {
+ err = -ENOMEM;
+ pr_err("gpiomatrix: Failed to allocate private data\n");
+ goto err_kp_alloc_failed;
+ }
+ kp->input_devs = input_devs;
+ kp->keypad_info = mi;
+ for (i = 0; i < key_count; i++) {
+ unsigned short keyentry = mi->keymap[i];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+ if (dev >= input_devs->count) {
+ pr_err("gpiomatrix: bad device index %d >= "
+ "%d for key code %d\n",
+ dev, input_devs->count, keycode);
+ err = -EINVAL;
+ goto err_bad_keymap;
+ }
+ if (keycode && keycode <= KEY_MAX)
+ input_set_capability(input_devs->dev[dev],
+ EV_KEY, keycode);
+ }
+
+ for (i = 0; i < mi->noutputs; i++) {
+ err = gpio_request(mi->output_gpios[i], "gpio_kp_out");
+ if (err) {
+ pr_err("gpiomatrix: gpio_request failed for "
+ "output %d\n", mi->output_gpios[i]);
+ goto err_request_output_gpio_failed;
+ }
+ if (gpio_cansleep(mi->output_gpios[i])) {
+ pr_err("gpiomatrix: unsupported output gpio %d,"
+ " can sleep\n", mi->output_gpios[i]);
+ err = -EINVAL;
+ goto err_output_gpio_configure_failed;
+ }
+ if (mi->flags & GPIOKPF_DRIVE_INACTIVE)
+ err = gpio_direction_output(mi->output_gpios[i],
+ !(mi->flags & GPIOKPF_ACTIVE_HIGH));
+ else
+ err = gpio_direction_input(mi->output_gpios[i]);
+ if (err) {
+ pr_err("gpiomatrix: gpio_configure failed for "
+ "output %d\n", mi->output_gpios[i]);
+ goto err_output_gpio_configure_failed;
+ }
+ }
+ for (i = 0; i < mi->ninputs; i++) {
+ err = gpio_request(mi->input_gpios[i], "gpio_kp_in");
+ if (err) {
+ pr_err("gpiomatrix: gpio_request failed for "
+ "input %d\n", mi->input_gpios[i]);
+ goto err_request_input_gpio_failed;
+ }
+ err = gpio_direction_input(mi->input_gpios[i]);
+ if (err) {
+ pr_err("gpiomatrix: gpio_direction_input failed"
+ " for input %d\n", mi->input_gpios[i]);
+ goto err_gpio_direction_input_failed;
+ }
+ }
+ kp->current_output = mi->noutputs;
+ kp->key_state_changed = 1;
+
+ hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ kp->timer.function = gpio_keypad_timer_func;
+ wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp");
+ err = gpio_keypad_request_irqs(kp);
+ kp->use_irq = err == 0;
+
+ pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for "
+ "%s%s in %s mode\n", input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "",
+ kp->use_irq ? "interrupt" : "polling");
+
+ if (kp->use_irq)
+ wake_lock(&kp->wake_lock);
+ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+
+ return 0;
+ }
+
+ err = 0;
+ kp = *data;
+
+ if (kp->use_irq)
+ for (i = mi->noutputs - 1; i >= 0; i--)
+ free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
+
+ hrtimer_cancel(&kp->timer);
+ wake_lock_destroy(&kp->wake_lock);
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+err_gpio_direction_input_failed:
+ gpio_free(mi->input_gpios[i]);
+err_request_input_gpio_failed:
+ ;
+ }
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+err_output_gpio_configure_failed:
+ gpio_free(mi->output_gpios[i]);
+err_request_output_gpio_failed:
+ ;
+ }
+err_bad_keymap:
+ kfree(kp);
+err_kp_alloc_failed:
+err_invalid_platform_data:
+ return err;
+}
diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c
new file mode 100644
index 00000000000..2aac2fad0a1
--- /dev/null
+++ b/drivers/input/misc/gpio_output.c
@@ -0,0 +1,97 @@
+/* drivers/input/misc/gpio_output.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+
+int gpio_event_output_event(
+ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
+ void **data, unsigned int dev, unsigned int type,
+ unsigned int code, int value)
+{
+ int i;
+ struct gpio_event_output_info *oi;
+ oi = container_of(info, struct gpio_event_output_info, info);
+ if (type != oi->type)
+ return 0;
+ if (!(oi->flags & GPIOEDF_ACTIVE_HIGH))
+ value = !value;
+ for (i = 0; i < oi->keymap_size; i++)
+ if (dev == oi->keymap[i].dev && code == oi->keymap[i].code)
+ gpio_set_value(oi->keymap[i].gpio, value);
+ return 0;
+}
+
+int gpio_event_output_func(
+ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
+ void **data, int func)
+{
+ int ret;
+ int i;
+ struct gpio_event_output_info *oi;
+ oi = container_of(info, struct gpio_event_output_info, info);
+
+ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME)
+ return 0;
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH);
+
+ for (i = 0; i < oi->keymap_size; i++) {
+ int dev = oi->keymap[i].dev;
+ if (dev >= input_devs->count) {
+ pr_err("gpio_event_output_func: bad device "
+ "index %d >= %d for key code %d\n",
+ dev, input_devs->count,
+ oi->keymap[i].code);
+ ret = -EINVAL;
+ goto err_bad_keymap;
+ }
+ input_set_capability(input_devs->dev[dev], oi->type,
+ oi->keymap[i].code);
+ }
+
+ for (i = 0; i < oi->keymap_size; i++) {
+ ret = gpio_request(oi->keymap[i].gpio,
+ "gpio_event_output");
+ if (ret) {
+ pr_err("gpio_event_output_func: gpio_request "
+ "failed for %d\n", oi->keymap[i].gpio);
+ goto err_gpio_request_failed;
+ }
+ ret = gpio_direction_output(oi->keymap[i].gpio,
+ output_level);
+ if (ret) {
+ pr_err("gpio_event_output_func: "
+ "gpio_direction_output failed for %d\n",
+ oi->keymap[i].gpio);
+ goto err_gpio_direction_output_failed;
+ }
+ }
+ return 0;
+ }
+
+ ret = 0;
+ for (i = oi->keymap_size - 1; i >= 0; i--) {
+err_gpio_direction_output_failed:
+ gpio_free(oi->keymap[i].gpio);
+err_gpio_request_failed:
+ ;
+ }
+err_bad_keymap:
+ return ret;
+}
+
diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c
new file mode 100644
index 00000000000..ca23905f304
--- /dev/null
+++ b/drivers/input/misc/keychord.c
@@ -0,0 +1,387 @@
+/*
+ * drivers/input/misc/keychord.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/keychord.h>
+#include <linux/sched.h>
+
+#define KEYCHORD_NAME "keychord"
+#define BUFFER_SIZE 16
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("Key chord input driver");
+MODULE_SUPPORTED_DEVICE("keychord");
+MODULE_LICENSE("GPL");
+
+#define NEXT_KEYCHORD(kc) ((struct input_keychord *) \
+ ((char *)kc + sizeof(struct input_keychord) + \
+ kc->count * sizeof(kc->keycodes[0])))
+
+struct keychord_device {
+ struct input_handler input_handler;
+ int registered;
+
+ /* list of keychords to monitor */
+ struct input_keychord *keychords;
+ int keychord_count;
+
+ /* bitmask of keys contained in our keychords */
+ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
+ /* current state of the keys */
+ unsigned long keystate[BITS_TO_LONGS(KEY_CNT)];
+ /* number of keys that are currently pressed */
+ int key_down;
+
+ /* second input_device_id is needed for null termination */
+ struct input_device_id device_ids[2];
+
+ spinlock_t lock;
+ wait_queue_head_t waitq;
+ unsigned char head;
+ unsigned char tail;
+ __u16 buff[BUFFER_SIZE];
+};
+
+static int check_keychord(struct keychord_device *kdev,
+ struct input_keychord *keychord)
+{
+ int i;
+
+ if (keychord->count != kdev->key_down)
+ return 0;
+
+ for (i = 0; i < keychord->count; i++) {
+ if (!test_bit(keychord->keycodes[i], kdev->keystate))
+ return 0;
+ }
+
+ /* we have a match */
+ return 1;
+}
+
+static void keychord_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+ struct keychord_device *kdev = handle->private;
+ struct input_keychord *keychord;
+ unsigned long flags;
+ int i, got_chord = 0;
+
+ if (type != EV_KEY || code >= KEY_MAX)
+ return;
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* do nothing if key state did not change */
+ if (!test_bit(code, kdev->keystate) == !value)
+ goto done;
+ __change_bit(code, kdev->keystate);
+ if (value)
+ kdev->key_down++;
+ else
+ kdev->key_down--;
+
+ /* don't notify on key up */
+ if (!value)
+ goto done;
+ /* ignore this event if it is not one of the keys we are monitoring */
+ if (!test_bit(code, kdev->keybit))
+ goto done;
+
+ keychord = kdev->keychords;
+ if (!keychord)
+ goto done;
+
+ /* check to see if the keyboard state matches any keychords */
+ for (i = 0; i < kdev->keychord_count; i++) {
+ if (check_keychord(kdev, keychord)) {
+ kdev->buff[kdev->head] = keychord->id;
+ kdev->head = (kdev->head + 1) % BUFFER_SIZE;
+ got_chord = 1;
+ break;
+ }
+ /* skip to next keychord */
+ keychord = NEXT_KEYCHORD(keychord);
+ }
+
+done:
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ if (got_chord)
+ wake_up_interruptible(&kdev->waitq);
+}
+
+static int keychord_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ int i, ret;
+ struct input_handle *handle;
+ struct keychord_device *kdev =
+ container_of(handler, struct keychord_device, input_handler);
+
+ /*
+ * ignore this input device if it does not contain any keycodes
+ * that we are monitoring
+ */
+ for (i = 0; i < KEY_MAX; i++) {
+ if (test_bit(i, kdev->keybit) && test_bit(i, dev->keybit))
+ break;
+ }
+ if (i == KEY_MAX)
+ return -ENODEV;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = KEYCHORD_NAME;
+ handle->private = kdev;
+
+ ret = input_register_handle(handle);
+ if (ret)
+ goto err_input_register_handle;
+
+ ret = input_open_device(handle);
+ if (ret)
+ goto err_input_open_device;
+
+ pr_info("keychord: using input dev %s for fevent\n", dev->name);
+
+ return 0;
+
+err_input_open_device:
+ input_unregister_handle(handle);
+err_input_register_handle:
+ kfree(handle);
+ return ret;
+}
+
+static void keychord_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+/*
+ * keychord_read is used to read keychord events from the driver
+ */
+static ssize_t keychord_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct keychord_device *kdev = file->private_data;
+ __u16 id;
+ int retval;
+ unsigned long flags;
+
+ if (count < sizeof(id))
+ return -EINVAL;
+ count = sizeof(id);
+
+ if (kdev->head == kdev->tail && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(kdev->waitq,
+ kdev->head != kdev->tail);
+ if (retval)
+ return retval;
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* pop a keychord ID off the queue */
+ id = kdev->buff[kdev->tail];
+ kdev->tail = (kdev->tail + 1) % BUFFER_SIZE;
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ if (copy_to_user(buffer, &id, count))
+ return -EFAULT;
+
+ return count;
+}
+
+/*
+ * keychord_write is used to configure the driver
+ */
+static ssize_t keychord_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct keychord_device *kdev = file->private_data;
+ struct input_keychord *keychords = 0;
+ struct input_keychord *keychord, *next, *end;
+ int ret, i, key;
+ unsigned long flags;
+
+ if (count < sizeof(struct input_keychord))
+ return -EINVAL;
+ keychords = kzalloc(count, GFP_KERNEL);
+ if (!keychords)
+ return -ENOMEM;
+
+ /* read list of keychords from userspace */
+ if (copy_from_user(keychords, buffer, count)) {
+ kfree(keychords);
+ return -EFAULT;
+ }
+
+ /* unregister handler before changing configuration */
+ if (kdev->registered) {
+ input_unregister_handler(&kdev->input_handler);
+ kdev->registered = 0;
+ }
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* clear any existing configuration */
+ kfree(kdev->keychords);
+ kdev->keychords = 0;
+ kdev->keychord_count = 0;
+ kdev->key_down = 0;
+ memset(kdev->keybit, 0, sizeof(kdev->keybit));
+ memset(kdev->keystate, 0, sizeof(kdev->keystate));
+ kdev->head = kdev->tail = 0;
+
+ keychord = keychords;
+ end = (struct input_keychord *)((char *)keychord + count);
+
+ while (keychord < end) {
+ next = NEXT_KEYCHORD(keychord);
+ if (keychord->count <= 0 || next > end) {
+ pr_err("keychord: invalid keycode count %d\n",
+ keychord->count);
+ goto err_unlock_return;
+ }
+ if (keychord->version != KEYCHORD_VERSION) {
+ pr_err("keychord: unsupported version %d\n",
+ keychord->version);
+ goto err_unlock_return;
+ }
+
+ /* keep track of the keys we are monitoring in keybit */
+ for (i = 0; i < keychord->count; i++) {
+ key = keychord->keycodes[i];
+ if (key < 0 || key >= KEY_CNT) {
+ pr_err("keychord: keycode %d out of range\n",
+ key);
+ goto err_unlock_return;
+ }
+ __set_bit(key, kdev->keybit);
+ }
+
+ kdev->keychord_count++;
+ keychord = next;
+ }
+
+ kdev->keychords = keychords;
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ ret = input_register_handler(&kdev->input_handler);
+ if (ret) {
+ kfree(keychords);
+ kdev->keychords = 0;
+ return ret;
+ }
+ kdev->registered = 1;
+
+ return count;
+
+err_unlock_return:
+ spin_unlock_irqrestore(&kdev->lock, flags);
+ kfree(keychords);
+ return -EINVAL;
+}
+
+static unsigned int keychord_poll(struct file *file, poll_table *wait)
+{
+ struct keychord_device *kdev = file->private_data;
+
+ poll_wait(file, &kdev->waitq, wait);
+
+ if (kdev->head != kdev->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int keychord_open(struct inode *inode, struct file *file)
+{
+ struct keychord_device *kdev;
+
+ kdev = kzalloc(sizeof(struct keychord_device), GFP_KERNEL);
+ if (!kdev)
+ return -ENOMEM;
+
+ spin_lock_init(&kdev->lock);
+ init_waitqueue_head(&kdev->waitq);
+
+ kdev->input_handler.event = keychord_event;
+ kdev->input_handler.connect = keychord_connect;
+ kdev->input_handler.disconnect = keychord_disconnect;
+ kdev->input_handler.name = KEYCHORD_NAME;
+ kdev->input_handler.id_table = kdev->device_ids;
+
+ kdev->device_ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT;
+ __set_bit(EV_KEY, kdev->device_ids[0].evbit);
+
+ file->private_data = kdev;
+
+ return 0;
+}
+
+static int keychord_release(struct inode *inode, struct file *file)
+{
+ struct keychord_device *kdev = file->private_data;
+
+ if (kdev->registered)
+ input_unregister_handler(&kdev->input_handler);
+ kfree(kdev);
+
+ return 0;
+}
+
+static const struct file_operations keychord_fops = {
+ .owner = THIS_MODULE,
+ .open = keychord_open,
+ .release = keychord_release,
+ .read = keychord_read,
+ .write = keychord_write,
+ .poll = keychord_poll,
+};
+
+static struct miscdevice keychord_misc = {
+ .fops = &keychord_fops,
+ .name = KEYCHORD_NAME,
+ .minor = MISC_DYNAMIC_MINOR,
+};
+
+static int __init keychord_init(void)
+{
+ return misc_register(&keychord_misc);
+}
+
+static void __exit keychord_exit(void)
+{
+ misc_deregister(&keychord_misc);
+}
+
+module_init(keychord_init);
+module_exit(keychord_exit);
diff --git a/drivers/input/misc/lps001wp_prs.c b/drivers/input/misc/lps001wp_prs.c
new file mode 100644
index 00000000000..9ec96ba3863
--- /dev/null
+++ b/drivers/input/misc/lps001wp_prs.c
@@ -0,0 +1,1276 @@
+
+/******************** (C) COPYRIGHT 2010 STMicroelectronics ********************
+*
+* File Name : lps001wp_prs.c
+* Authors : MSH - Motion Mems BU - Application Team
+* : Matteo Dameno (matteo.dameno@st.com)
+* : Carmine Iascone (carmine.iascone@st.com)
+* : Both authors are willing to be considered the contact
+* : and update points for the driver.
+* Version : V 1.1.1
+* Date : 2010/11/22
+* Description : LPS001WP pressure temperature sensor driver
+*
+********************************************************************************
+*
+* 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.
+*
+* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
+* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
+* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
+* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
+* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
+* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
+* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+*
+******************************************************************************
+
+ Revision 0.9.0 01/10/2010:
+ first beta release
+ Revision 1.1.0 05/11/2010:
+ add sysfs management
+ Revision 1.1.1 22/11/2010:
+ moved to input/misc
+******************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+
+#include <linux/input/lps001wp.h>
+
+
+
+#define DEBUG 1
+
+
+#define PR_ABS_MAX 0xffff
+#define PR_ABS_MIN 0x0000
+#define PR_DLT_MAX 0x7ffff
+#define PR_DLT_MIN -0x80000 /* 16-bit signed value */
+#define TEMP_MAX 0x7fff
+#define TEMP_MIN -0x80000 /* 16-bit signed value */
+
+
+#define SENSITIVITY_T_SHIFT 6 /** = 64 LSB/degrC */
+#define SENSITIVITY_P_SHIFT 4 /** = 16 LSB/mbar */
+
+
+#define OUTDATA_REG 0x28
+#define INDATA_REG 0X30
+
+#define WHOAMI_LPS001WP_PRS 0xBA /* Expctd content for WAI */
+
+/* CONTROL REGISTERS */
+#define WHO_AM_I 0x0F /* WhoAmI register */
+#define CTRL_REG1 0x20 /* power / ODR control reg */
+#define CTRL_REG2 0x21 /* boot reg */
+#define CTRL_REG3 0x22 /* interrupt control reg */
+
+#define STATUS_REG 0X27 /* status reg */
+
+#define PRESS_OUT_L OUTDATA_REG
+
+
+#define REF_P_L INDATA_REG /* pressure reference */
+#define REF_P_H 0x31 /* pressure reference */
+#define THS_P_L 0x32 /* pressure threshold */
+#define THS_P_H 0x33 /* pressure threshold */
+
+#define INT_CFG 0x34 /* interrupt config */
+#define INT_SRC 0x35 /* interrupt source */
+#define INT_ACK 0x36 /* interrupt acknoledge */
+/* end CONTROL REGISTRES */
+
+
+/* Barometer and Termometer output data rate ODR */
+#define LPS001WP_PRS_ODR_MASK 0x30 /* Mask to access odr bits only */
+#define LPS001WP_PRS_ODR_7_1 0x00 /* 7Hz baro and 1Hz term ODR */
+#define LPS001WP_PRS_ODR_7_7 0x01 /* 7Hz baro and 7Hz term ODR */
+#define LPS001WP_PRS_ODR_12_12 0x11 /* 12.5Hz baro and 12.5Hz term ODR */
+
+
+#define LPS001WP_PRS_ENABLE_MASK 0x40 /* */
+#define LPS001WP_PRS_DIFF_MASK 0x08
+#define LPS001WP_PRS_LPOW_MASK 0x80
+
+#define LPS001WP_PRS_DIFF_ON 0x08
+#define LPS001WP_PRS_DIFF_OFF 0x00
+
+#define LPS001WP_PRS_LPOW_ON 0x80
+#define LPS001WP_PRS_LPOW_OFF 0x00
+
+#define FUZZ 0
+#define FLAT 0
+#define I2C_RETRY_DELAY 5
+#define I2C_RETRIES 5
+#define I2C_AUTO_INCREMENT 0x80
+
+/* RESUME STATE INDICES */
+#define RES_CTRL_REG1 0
+#define RES_CTRL_REG2 1
+#define RES_CTRL_REG3 2
+#define RES_REF_P_L 3
+#define RES_REF_P_H 4
+#define RES_THS_P_L 5
+#define RES_THS_P_H 6
+#define RES_INT_CFG 7
+
+#define RESUME_ENTRIES 8
+/* end RESUME STATE INDICES */
+
+/* Pressure Sensor Operating Mode */
+#define LPS001WP_PRS_DIFF_ENABLE 1
+#define LPS001WP_PRS_DIFF_DISABLE 0
+#define LPS001WP_PRS_LPOWER_EN 1
+#define LPS001WP_PRS_LPOWER_DIS 0
+
+static const struct {
+ unsigned int cutoff_ms;
+ unsigned int mask;
+} lps001wp_prs_odr_table[] = {
+ {80, LPS001WP_PRS_ODR_12_12 },
+ {143, LPS001WP_PRS_ODR_7_7 },
+ {1000, LPS001WP_PRS_ODR_7_1 },
+};
+
+struct lps001wp_prs_data {
+ struct i2c_client *client;
+ struct lps001wp_prs_platform_data *pdata;
+
+ struct mutex lock;
+ struct delayed_work input_work;
+
+ struct input_dev *input_dev;
+
+ int hw_initialized;
+ /* hw_working=-1 means not tested yet */
+ int hw_working;
+ u8 diff_enabled;
+ u8 lpowmode_enabled ;
+
+ atomic_t enabled;
+ int on_before_suspend;
+
+ u8 resume_state[RESUME_ENTRIES];
+
+#ifdef DEBUG
+ u8 reg_addr;
+#endif
+};
+
+struct outputdata {
+ u16 abspress;
+ s16 temperature;
+ s16 deltapress;
+};
+
+
+static int lps001wp_prs_i2c_read(struct lps001wp_prs_data *prs,
+ u8 *buf, int len)
+{
+ int err;
+ int tries = 0;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = prs->client->addr,
+ .flags = prs->client->flags & I2C_M_TEN,
+ .len = 1,
+ .buf = buf,
+ },
+ {
+ .addr = prs->client->addr,
+ .flags = (prs->client->flags & I2C_M_TEN) | I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ },
+ };
+
+ do {
+ err = i2c_transfer(prs->client->adapter, msgs, 2);
+ if (err != 2)
+ msleep_interruptible(I2C_RETRY_DELAY);
+ } while ((err != 2) && (++tries < I2C_RETRIES));
+
+ if (err != 2) {
+ dev_err(&prs->client->dev, "read transfer error\n");
+ err = -EIO;
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+static int lps001wp_prs_i2c_write(struct lps001wp_prs_data *prs,
+ u8 *buf, int len)
+{
+ int err;
+ int tries = 0;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = prs->client->addr,
+ .flags = prs->client->flags & I2C_M_TEN,
+ .len = len + 1,
+ .buf = buf,
+ },
+ };
+
+ do {
+ err = i2c_transfer(prs->client->adapter, msgs, 1);
+ if (err != 1)
+ msleep_interruptible(I2C_RETRY_DELAY);
+ } while ((err != 1) && (++tries < I2C_RETRIES));
+
+ if (err != 1) {
+ dev_err(&prs->client->dev, "write transfer error\n");
+ err = -EIO;
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+static int lps001wp_prs_i2c_update(struct lps001wp_prs_data *prs,
+ u8 reg_address, u8 mask, u8 new_bit_values)
+{
+ int err = -1;
+ u8 rdbuf[1] = { reg_address };
+ u8 wrbuf[2] = { reg_address , 0x00 };
+
+ u8 init_val;
+ u8 updated_val;
+ err = lps001wp_prs_i2c_read(prs, rdbuf, 1);
+ if (!(err < 0)) {
+ init_val = rdbuf[0];
+ updated_val = ((mask & new_bit_values) | ((~mask) & init_val));
+ wrbuf[1] = updated_val;
+ err = lps001wp_prs_i2c_write(prs, wrbuf, 2);
+ }
+ return err;
+}
+/* */
+
+static int lps001wp_prs_register_write(struct lps001wp_prs_data *prs, u8 *buf,
+ u8 reg_address, u8 new_value)
+{
+ int err = -1;
+
+ /* Sets configuration register at reg_address
+ * NOTE: this is a straight overwrite */
+ buf[0] = reg_address;
+ buf[1] = new_value;
+ err = lps001wp_prs_i2c_write(prs, buf, 1);
+ if (err < 0)
+ return err;
+ return err;
+}
+
+static int lps001wp_prs_register_read(struct lps001wp_prs_data *prs, u8 *buf,
+ u8 reg_address)
+{
+
+ int err = -1;
+ buf[0] = (reg_address);
+ err = lps001wp_prs_i2c_read(prs, buf, 1);
+
+ return err;
+}
+
+static int lps001wp_prs_register_update(struct lps001wp_prs_data *prs, u8 *buf,
+ u8 reg_address, u8 mask, u8 new_bit_values)
+{
+ int err = -1;
+ u8 init_val;
+ u8 updated_val;
+ err = lps001wp_prs_register_read(prs, buf, reg_address);
+ if (!(err < 0)) {
+ init_val = buf[0];
+ updated_val = ((mask & new_bit_values) | ((~mask) & init_val));
+ err = lps001wp_prs_register_write(prs, buf, reg_address,
+ updated_val);
+ }
+ return err;
+}
+
+/* */
+
+
+static int lps001wp_prs_hw_init(struct lps001wp_prs_data *prs)
+{
+ int err = -1;
+ u8 buf[6];
+
+ printk(KERN_DEBUG "%s: hw init start\n", LPS001WP_PRS_DEV_NAME);
+
+ buf[0] = WHO_AM_I;
+ err = lps001wp_prs_i2c_read(prs, buf, 1);
+ if (err < 0)
+ goto error_firstread;
+ else
+ prs->hw_working = 1;
+ if (buf[0] != WHOAMI_LPS001WP_PRS) {
+ err = -1; /* TODO:choose the right coded error */
+ goto error_unknown_device;
+ }
+
+
+ buf[0] = (I2C_AUTO_INCREMENT | INDATA_REG);
+ buf[1] = prs->resume_state[RES_REF_P_L];
+ buf[2] = prs->resume_state[RES_REF_P_H];
+ buf[3] = prs->resume_state[RES_THS_P_L];
+ buf[4] = prs->resume_state[RES_THS_P_H];
+ err = lps001wp_prs_i2c_write(prs, buf, 4);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | CTRL_REG1);
+ buf[1] = prs->resume_state[RES_CTRL_REG1];
+ buf[2] = prs->resume_state[RES_CTRL_REG2];
+ buf[3] = prs->resume_state[RES_CTRL_REG3];
+ err = lps001wp_prs_i2c_write(prs, buf, 3);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = INT_CFG;
+ buf[1] = prs->resume_state[RES_INT_CFG];
+ err = lps001wp_prs_i2c_write(prs, buf, 1);
+ if (err < 0)
+ goto error1;
+
+
+ prs->hw_initialized = 1;
+ printk(KERN_DEBUG "%s: hw init done\n", LPS001WP_PRS_DEV_NAME);
+ return 0;
+
+error_firstread:
+ prs->hw_working = 0;
+ dev_warn(&prs->client->dev, "Error reading WHO_AM_I: is device "
+ "available/working?\n");
+ goto error1;
+error_unknown_device:
+ dev_err(&prs->client->dev,
+ "device unknown. Expected: 0x%x,"
+ " Replies: 0x%x\n", WHOAMI_LPS001WP_PRS, buf[0]);
+error1:
+ prs->hw_initialized = 0;
+ dev_err(&prs->client->dev, "hw init error 0x%x,0x%x: %d\n", buf[0],
+ buf[1], err);
+ return err;
+}
+
+static void lps001wp_prs_device_power_off(struct lps001wp_prs_data *prs)
+{
+ int err;
+ u8 buf[2] = { CTRL_REG1, LPS001WP_PRS_PM_OFF };
+
+ err = lps001wp_prs_i2c_write(prs, buf, 1);
+ if (err < 0)
+ dev_err(&prs->client->dev, "soft power off failed: %d\n", err);
+
+ if (prs->pdata->power_off) {
+ prs->pdata->power_off();
+ prs->hw_initialized = 0;
+ }
+ if (prs->hw_initialized) {
+ prs->hw_initialized = 0;
+ }
+
+}
+
+static int lps001wp_prs_device_power_on(struct lps001wp_prs_data *prs)
+{
+ int err = -1;
+
+ if (prs->pdata->power_on) {
+ err = prs->pdata->power_on();
+ if (err < 0) {
+ dev_err(&prs->client->dev,
+ "power_on failed: %d\n", err);
+ return err;
+ }
+ }
+
+ if (!prs->hw_initialized) {
+ err = lps001wp_prs_hw_init(prs);
+ if (prs->hw_working == 1 && err < 0) {
+ lps001wp_prs_device_power_off(prs);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+
+
+int lps001wp_prs_update_odr(struct lps001wp_prs_data *prs, int poll_interval_ms)
+{
+ int err = -1;
+ int i;
+
+ u8 buf[2];
+ u8 updated_val;
+ u8 init_val;
+ u8 new_val;
+ u8 mask = LPS001WP_PRS_ODR_MASK;
+
+ /* Following, looks for the longest possible odr interval scrolling the
+ * odr_table vector from the end (shortest interval) backward (longest
+ * interval), to support the poll_interval requested by the system.
+ * It must be the longest interval lower then the poll interval.*/
+ for (i = ARRAY_SIZE(lps001wp_prs_odr_table) - 1; i >= 0; i--) {
+ if (lps001wp_prs_odr_table[i].cutoff_ms <= poll_interval_ms)
+ break;
+ }
+
+ new_val = lps001wp_prs_odr_table[i].mask;
+
+ /* If device is currently enabled, we need to write new
+ * configuration out to it */
+ if (atomic_read(&prs->enabled)) {
+ buf[0] = CTRL_REG1;
+ err = lps001wp_prs_i2c_read(prs, buf, 1);
+ if (err < 0)
+ goto error;
+ init_val = buf[0];
+ prs->resume_state[RES_CTRL_REG1] = init_val;
+
+ buf[0] = CTRL_REG1;
+ updated_val = ((mask & new_val) | ((~mask) & init_val));
+ buf[1] = updated_val;
+ buf[0] = CTRL_REG1;
+ err = lps001wp_prs_i2c_write(prs, buf, 1);
+ if (err < 0)
+ goto error;
+ prs->resume_state[RES_CTRL_REG1] = updated_val;
+ }
+ return err;
+
+error:
+ dev_err(&prs->client->dev, "update odr failed 0x%x,0x%x: %d\n",
+ buf[0], buf[1], err);
+
+ return err;
+}
+
+static int lps001wp_prs_set_press_reference(struct lps001wp_prs_data *prs,
+ u16 new_reference)
+{
+ int err = -1;
+ u8 const reg_addressL = REF_P_L;
+ u8 const reg_addressH = REF_P_H;
+ u8 bit_valuesL, bit_valuesH;
+ u8 buf[2];
+
+ bit_valuesL = (u8) (new_reference & 0x00FF);
+ bit_valuesH = (u8)((new_reference & 0xFF00) >> 8);
+
+ err = lps001wp_prs_register_write(prs, buf, reg_addressL,
+ bit_valuesL);
+ if (err < 0)
+ return err;
+ err = lps001wp_prs_register_write(prs, buf, reg_addressH,
+ bit_valuesH);
+ if (err < 0) {
+ lps001wp_prs_register_write(prs, buf, reg_addressL,
+ prs->resume_state[RES_REF_P_L]);
+ return err;
+ }
+ prs->resume_state[RES_REF_P_L] = bit_valuesL;
+ prs->resume_state[RES_REF_P_H] = bit_valuesH;
+ return err;
+}
+
+static int lps001wp_prs_get_press_reference(struct lps001wp_prs_data *prs,
+ u16 *buf16)
+{
+ int err = -1;
+
+ u8 bit_valuesL, bit_valuesH;
+ u8 buf[2] = {0};
+ u16 temp = 0;
+
+ err = lps001wp_prs_register_read(prs, buf, REF_P_L);
+ if (err < 0)
+ return err;
+ bit_valuesL = buf[0];
+ err = lps001wp_prs_register_read(prs, buf, REF_P_H);
+ if (err < 0)
+ return err;
+ bit_valuesH = buf[0];
+
+ temp = (((u16) bit_valuesH) << 8);
+ *buf16 = (temp | ((u16) bit_valuesL));
+
+ return err;
+}
+
+static int lps001wp_prs_lpow_manage(struct lps001wp_prs_data *prs, u8 control)
+{
+ int err = -1;
+ u8 buf[2] = {0x00, 0x00};
+ u8 const mask = LPS001WP_PRS_LPOW_MASK;
+ u8 bit_values = LPS001WP_PRS_LPOW_OFF;
+
+ if (control >= LPS001WP_PRS_LPOWER_EN) {
+ bit_values = LPS001WP_PRS_LPOW_ON;
+ }
+
+ err = lps001wp_prs_register_update(prs, buf, CTRL_REG1,
+ mask, bit_values);
+
+ if (err < 0)
+ return err;
+ prs->resume_state[RES_CTRL_REG1] = ((mask & bit_values) |
+ (~mask & prs->resume_state[RES_CTRL_REG1]));
+ if (bit_values == LPS001WP_PRS_LPOW_ON)
+ prs->lpowmode_enabled = 1;
+ else
+ prs->lpowmode_enabled = 0;
+ return err;
+}
+
+static int lps001wp_prs_diffen_manage(struct lps001wp_prs_data *prs, u8 control)
+{
+ int err = -1;
+ u8 buf[2] = {0x00, 0x00};
+ u8 const mask = LPS001WP_PRS_DIFF_MASK;
+ u8 bit_values = LPS001WP_PRS_DIFF_OFF;
+
+ if (control >= LPS001WP_PRS_DIFF_ENABLE) {
+ bit_values = LPS001WP_PRS_DIFF_ON;
+ }
+
+ err = lps001wp_prs_register_update(prs, buf, CTRL_REG1,
+ mask, bit_values);
+
+ if (err < 0)
+ return err;
+ prs->resume_state[RES_CTRL_REG1] = ((mask & bit_values) |
+ (~mask & prs->resume_state[RES_CTRL_REG1]));
+ if (bit_values == LPS001WP_PRS_DIFF_ON)
+ prs->diff_enabled = 1;
+ else
+ prs->diff_enabled = 0;
+ return err;
+}
+
+
+static int lps001wp_prs_get_presstemp_data(struct lps001wp_prs_data *prs,
+ struct outputdata *out)
+{
+ int err = -1;
+ /* Data bytes from hardware PRESS_OUT_L, PRESS_OUT_H,
+ * TEMP_OUT_L, TEMP_OUT_H,
+ * DELTA_L, DELTA_H */
+ u8 prs_data[6];
+
+ u16 abspr;
+ s16 temperature, deltapr;
+ int regToRead = 4;
+ prs_data[4] = 0;
+ prs_data[5] = 0;
+
+ if (prs->diff_enabled)
+ regToRead = 6;
+
+ prs_data[0] = (I2C_AUTO_INCREMENT | OUTDATA_REG);
+ err = lps001wp_prs_i2c_read(prs, prs_data, regToRead);
+ if (err < 0)
+ return err;
+
+ abspr = ((((u16) prs_data[1] << 8) | ((u16) prs_data[0])));
+ temperature = ((s16) (((u16) prs_data[3] << 8) | ((u16)prs_data[2])));
+
+ out->abspress = abspr;
+ out->temperature = temperature;
+
+ deltapr = ((s16) (((u16) prs_data[5] << 8) | ((u16)prs_data[4])));
+ out->deltapress = deltapr;
+
+ return err;
+}
+
+static void lps001wp_prs_report_values(struct lps001wp_prs_data *prs,
+ struct outputdata *out)
+{
+ input_report_abs(prs->input_dev, ABS_PR, out->abspress);
+ input_report_abs(prs->input_dev, ABS_TEMP, out->temperature);
+ input_report_abs(prs->input_dev, ABS_DLTPR, out->deltapress);
+ input_sync(prs->input_dev);
+}
+
+static int lps001wp_prs_enable(struct lps001wp_prs_data *prs)
+{
+ int err;
+
+ if (!atomic_cmpxchg(&prs->enabled, 0, 1)) {
+ err = lps001wp_prs_device_power_on(prs);
+ if (err < 0) {
+ atomic_set(&prs->enabled, 0);
+ return err;
+ }
+ schedule_delayed_work(&prs->input_work,
+ msecs_to_jiffies(prs->pdata->poll_interval));
+ }
+
+ return 0;
+}
+
+static int lps001wp_prs_disable(struct lps001wp_prs_data *prs)
+{
+ if (atomic_cmpxchg(&prs->enabled, 1, 0)) {
+ cancel_delayed_work_sync(&prs->input_work);
+ lps001wp_prs_device_power_off(prs);
+ }
+
+ return 0;
+}
+
+static ssize_t read_single_reg(struct device *dev, char *buf, u8 reg)
+{
+ ssize_t ret;
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ int rc = 0;
+
+ u8 data = reg;
+ rc = lps001wp_prs_i2c_read(prs, &data, 1);
+ /*TODO: error need to be managed */
+ ret = sprintf(buf, "0x%02x\n", data);
+ return ret;
+
+}
+
+static int write_reg(struct device *dev, const char *buf, u8 reg)
+{
+ int rc = 0;
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ u8 x[2];
+ unsigned long val;
+
+ if (strict_strtoul(buf, 16, &val))
+ return -EINVAL;
+
+ x[0] = reg;
+ x[1] = val;
+ rc = lps001wp_prs_i2c_write(prs, x, 1);
+ /*TODO: error need to be managed */
+ return rc;
+}
+
+static ssize_t attr_get_polling_rate(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int val;
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ mutex_lock(&prs->lock);
+ val = prs->pdata->poll_interval;
+ mutex_unlock(&prs->lock);
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t attr_set_polling_rate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ unsigned long interval_ms;
+
+ if (strict_strtoul(buf, 10, &interval_ms))
+ return -EINVAL;
+ if (!interval_ms)
+ return -EINVAL;
+ mutex_lock(&prs->lock);
+ prs->pdata->poll_interval = interval_ms;
+ lps001wp_prs_update_odr(prs, interval_ms);
+ mutex_unlock(&prs->lock);
+ return size;
+}
+
+static ssize_t attr_get_diff_enable(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u8 val;
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ mutex_lock(&prs->lock);
+ val = prs->diff_enabled;
+ mutex_unlock(&prs->lock);
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t attr_set_diff_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&prs->lock);
+ lps001wp_prs_diffen_manage(prs, (u8) val);
+ mutex_unlock(&prs->lock);
+ return size;
+}
+
+static ssize_t attr_get_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ int val = atomic_read(&prs->enabled);
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t attr_set_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ if (val)
+ lps001wp_prs_enable(prs);
+ else
+ lps001wp_prs_disable(prs);
+
+ return size;
+}
+
+static ssize_t attr_get_press_ref(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err = -1;
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ u16 val = 0;
+
+ mutex_lock(&prs->lock);
+ err = lps001wp_prs_get_press_reference(prs, &val);
+ mutex_unlock(&prs->lock);
+ if (err < 0)
+ return err;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t attr_set_press_ref(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int err = -1;
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ unsigned long val = 0;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ if (val < PR_ABS_MIN || val > PR_ABS_MAX)
+ return -EINVAL;
+
+ mutex_lock(&prs->lock);
+ err = lps001wp_prs_set_press_reference(prs, val);
+ mutex_unlock(&prs->lock);
+ if (err < 0)
+ return err;
+ return size;
+}
+
+
+static ssize_t attr_get_lowpowmode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u8 val;
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ mutex_lock(&prs->lock);
+ val = prs->lpowmode_enabled;
+ mutex_unlock(&prs->lock);
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t attr_set_lowpowmode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int err = -1;
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&prs->lock);
+ err = lps001wp_prs_lpow_manage(prs, (u8) val);
+ mutex_unlock(&prs->lock);
+ if (err < 0)
+ return err;
+ return size;
+}
+
+
+#ifdef DEBUG
+static ssize_t attr_reg_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int rc;
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ u8 x[2];
+ unsigned long val;
+
+ if (strict_strtoul(buf, 16, &val))
+ return -EINVAL;
+ mutex_lock(&prs->lock);
+ x[0] = prs->reg_addr;
+ mutex_unlock(&prs->lock);
+ x[1] = val;
+ rc = lps001wp_prs_i2c_write(prs, x, 1);
+ /*TODO: error need to be managed */
+ return size;
+}
+
+static ssize_t attr_reg_get(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ ssize_t ret;
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ int rc;
+ u8 data;
+
+ mutex_lock(&prs->lock);
+ data = prs->reg_addr;
+ mutex_unlock(&prs->lock);
+ rc = lps001wp_prs_i2c_read(prs, &data, 1);
+ /*TODO: error need to be managed */
+ ret = sprintf(buf, "0x%02x\n", data);
+ return ret;
+}
+
+static ssize_t attr_addr_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct lps001wp_prs_data *prs = dev_get_drvdata(dev);
+ unsigned long val;
+ if (strict_strtoul(buf, 16, &val))
+ return -EINVAL;
+ mutex_lock(&prs->lock);
+ prs->reg_addr = val;
+ mutex_unlock(&prs->lock);
+ return size;
+}
+#endif
+
+
+
+static struct device_attribute attributes[] = {
+ __ATTR(pollrate_ms, 0664, attr_get_polling_rate, attr_set_polling_rate),
+ __ATTR(enable, 0664, attr_get_enable, attr_set_enable),
+ __ATTR(diff_enable, 0664, attr_get_diff_enable, attr_set_diff_enable),
+ __ATTR(press_reference, 0664, attr_get_press_ref, attr_set_press_ref),
+ __ATTR(lowpow_enable, 0664, attr_get_lowpowmode, attr_set_lowpowmode),
+#ifdef DEBUG
+ __ATTR(reg_value, 0664, attr_reg_get, attr_reg_set),
+ __ATTR(reg_addr, 0220, NULL, attr_addr_set),
+#endif
+};
+
+static int create_sysfs_interfaces(struct device *dev)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(attributes); i++)
+ if (device_create_file(dev, attributes + i))
+ goto error;
+ return 0;
+
+error:
+ for ( ; i >= 0; i--)
+ device_remove_file(dev, attributes + i);
+ dev_err(dev, "%s:Unable to create interface\n", __func__);
+ return -1;
+}
+
+static int remove_sysfs_interfaces(struct device *dev)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(attributes); i++)
+ device_remove_file(dev, attributes + i);
+ return 0;
+}
+
+
+static void lps001wp_prs_input_work_func(struct work_struct *work)
+{
+ struct lps001wp_prs_data *prs;
+
+ struct outputdata output;
+ struct outputdata *out = &output;
+ int err;
+
+ prs = container_of((struct delayed_work *)work,
+ struct lps001wp_prs_data, input_work);
+
+ mutex_lock(&prs->lock);
+ err = lps001wp_prs_get_presstemp_data(prs, out);
+ if (err < 0)
+ dev_err(&prs->client->dev, "get_pressure_data failed\n");
+ else
+ lps001wp_prs_report_values(prs, out);
+
+ schedule_delayed_work(&prs->input_work,
+ msecs_to_jiffies(prs->pdata->poll_interval));
+ mutex_unlock(&prs->lock);
+}
+
+int lps001wp_prs_input_open(struct input_dev *input)
+{
+ struct lps001wp_prs_data *prs = input_get_drvdata(input);
+
+ return lps001wp_prs_enable(prs);
+}
+
+void lps001wp_prs_input_close(struct input_dev *dev)
+{
+ struct lps001wp_prs_data *prs = input_get_drvdata(dev);
+
+ lps001wp_prs_disable(prs);
+}
+
+
+static int lps001wp_prs_validate_pdata(struct lps001wp_prs_data *prs)
+{
+ prs->pdata->poll_interval = max(prs->pdata->poll_interval,
+ prs->pdata->min_interval);
+
+ /* Enforce minimum polling interval */
+ if (prs->pdata->poll_interval < prs->pdata->min_interval) {
+ dev_err(&prs->client->dev, "minimum poll interval violated\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int lps001wp_prs_input_init(struct lps001wp_prs_data *prs)
+{
+ int err;
+
+ INIT_DELAYED_WORK(&prs->input_work, lps001wp_prs_input_work_func);
+ prs->input_dev = input_allocate_device();
+ if (!prs->input_dev) {
+ err = -ENOMEM;
+ dev_err(&prs->client->dev, "input device allocate failed\n");
+ goto err0;
+ }
+
+ prs->input_dev->open = lps001wp_prs_input_open;
+ prs->input_dev->close = lps001wp_prs_input_close;
+ prs->input_dev->name = LPS001WP_PRS_DEV_NAME;
+ prs->input_dev->id.bustype = BUS_I2C;
+ prs->input_dev->dev.parent = &prs->client->dev;
+
+ input_set_drvdata(prs->input_dev, prs);
+
+ set_bit(EV_ABS, prs->input_dev->evbit);
+
+ input_set_abs_params(prs->input_dev, ABS_PR,
+ PR_ABS_MIN, PR_ABS_MAX, FUZZ, FLAT);
+ input_set_abs_params(prs->input_dev, ABS_TEMP,
+ PR_DLT_MIN, PR_DLT_MAX, FUZZ, FLAT);
+ input_set_abs_params(prs->input_dev, ABS_DLTPR,
+ TEMP_MIN, TEMP_MAX, FUZZ, FLAT);
+
+
+ prs->input_dev->name = "LPS001WP barometer";
+
+ err = input_register_device(prs->input_dev);
+ if (err) {
+ dev_err(&prs->client->dev,
+ "unable to register input polled device %s\n",
+ prs->input_dev->name);
+ goto err1;
+ }
+
+ return 0;
+
+err1:
+ input_free_device(prs->input_dev);
+err0:
+ return err;
+}
+
+static void lps001wp_prs_input_cleanup(struct lps001wp_prs_data *prs)
+{
+ input_unregister_device(prs->input_dev);
+ input_free_device(prs->input_dev);
+}
+
+static int lps001wp_prs_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lps001wp_prs_data *prs;
+ int err = -1;
+ int tempvalue;
+
+ pr_info("%s: probe start.\n", LPS001WP_PRS_DEV_NAME);
+
+ if (client->dev.platform_data == NULL) {
+ dev_err(&client->dev, "platform data is NULL. exiting.\n");
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "client not i2c capable\n");
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_err(&client->dev, "client not smb-i2c capable:2\n");
+ err = -EIO;
+ goto exit_check_functionality_failed;
+ }
+
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_I2C_BLOCK)){
+ dev_err(&client->dev, "client not smb-i2c capable:3\n");
+ err = -EIO;
+ goto exit_check_functionality_failed;
+ }
+
+
+ prs = kzalloc(sizeof(struct lps001wp_prs_data), GFP_KERNEL);
+ if (prs == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for module data: "
+ "%d\n", err);
+ goto exit_alloc_data_failed;
+ }
+
+ mutex_init(&prs->lock);
+ mutex_lock(&prs->lock);
+
+ prs->client = client;
+ i2c_set_clientdata(client, prs);
+
+
+ if (i2c_smbus_read_byte(client) < 0) {
+ printk(KERN_ERR "i2c_smbus_read_byte error!!\n");
+ goto err_mutexunlockfreedata;
+ } else {
+ printk(KERN_DEBUG "%s Device detected!\n",
+ LPS001WP_PRS_DEV_NAME);
+ }
+
+ /* read chip id */
+ tempvalue = i2c_smbus_read_word_data(client, WHO_AM_I);
+ if ((tempvalue & 0x00FF) == WHOAMI_LPS001WP_PRS) {
+ printk(KERN_DEBUG "%s I2C driver registered!\n",
+ LPS001WP_PRS_DEV_NAME);
+ } else {
+ prs->client = NULL;
+ printk(KERN_DEBUG "I2C driver not registered!"
+ " Device unknown\n");
+ goto err_mutexunlockfreedata;
+ }
+
+ prs->pdata = kmalloc(sizeof(*prs->pdata), GFP_KERNEL);
+ if (prs->pdata == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for pdata: %d\n",
+ err);
+ goto err_mutexunlockfreedata;
+ }
+
+ memcpy(prs->pdata, client->dev.platform_data, sizeof(*prs->pdata));
+
+ err = lps001wp_prs_validate_pdata(prs);
+ if (err < 0) {
+ dev_err(&client->dev, "failed to validate platform data\n");
+ goto exit_kfree_pdata;
+ }
+
+ i2c_set_clientdata(client, prs);
+
+
+ if (prs->pdata->init) {
+ err = prs->pdata->init();
+ if (err < 0) {
+ dev_err(&client->dev, "init failed: %d\n", err);
+ goto err2;
+ }
+ }
+
+ memset(prs->resume_state, 0, ARRAY_SIZE(prs->resume_state));
+
+ prs->resume_state[RES_CTRL_REG1] = LPS001WP_PRS_PM_NORMAL;
+ prs->resume_state[RES_CTRL_REG2] = 0x00;
+ prs->resume_state[RES_CTRL_REG3] = 0x00;
+ prs->resume_state[RES_REF_P_L] = 0x00;
+ prs->resume_state[RES_REF_P_H] = 0x00;
+ prs->resume_state[RES_THS_P_L] = 0x00;
+ prs->resume_state[RES_THS_P_H] = 0x00;
+ prs->resume_state[RES_INT_CFG] = 0x00;
+
+ err = lps001wp_prs_device_power_on(prs);
+ if (err < 0) {
+ dev_err(&client->dev, "power on failed: %d\n", err);
+ goto err2;
+ }
+
+ prs->diff_enabled = 0;
+ prs->lpowmode_enabled = 0;
+ atomic_set(&prs->enabled, 1);
+
+ err = lps001wp_prs_update_odr(prs, prs->pdata->poll_interval);
+ if (err < 0) {
+ dev_err(&client->dev, "update_odr failed\n");
+ goto err_power_off;
+ }
+
+ err = lps001wp_prs_input_init(prs);
+ if (err < 0) {
+ dev_err(&client->dev, "input init failed\n");
+ goto err_power_off;
+ }
+
+
+ err = create_sysfs_interfaces(&client->dev);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "device LPS001WP_PRS_DEV_NAME sysfs register failed\n");
+ goto err_input_cleanup;
+ }
+
+
+ lps001wp_prs_device_power_off(prs);
+
+ /* As default, do not report information */
+ atomic_set(&prs->enabled, 0);
+
+
+ mutex_unlock(&prs->lock);
+
+ dev_info(&client->dev, "%s: probed\n", LPS001WP_PRS_DEV_NAME);
+
+ return 0;
+
+/*
+remove_sysfs_int:
+ remove_sysfs_interfaces(&client->dev);
+*/
+err_input_cleanup:
+ lps001wp_prs_input_cleanup(prs);
+err_power_off:
+ lps001wp_prs_device_power_off(prs);
+err2:
+ if (prs->pdata->exit)
+ prs->pdata->exit();
+exit_kfree_pdata:
+ kfree(prs->pdata);
+
+err_mutexunlockfreedata:
+ mutex_unlock(&prs->lock);
+ kfree(prs);
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+ printk(KERN_ERR "%s: Driver Init failed\n", LPS001WP_PRS_DEV_NAME);
+ return err;
+}
+
+static int __devexit lps001wp_prs_remove(struct i2c_client *client)
+{
+ struct lps001wp_prs_data *prs = i2c_get_clientdata(client);
+
+ lps001wp_prs_input_cleanup(prs);
+ lps001wp_prs_device_power_off(prs);
+ remove_sysfs_interfaces(&client->dev);
+
+ if (prs->pdata->exit)
+ prs->pdata->exit();
+ kfree(prs->pdata);
+ kfree(prs);
+
+ return 0;
+}
+
+static int lps001wp_prs_resume(struct i2c_client *client)
+{
+ struct lps001wp_prs_data *prs = i2c_get_clientdata(client);
+
+ if (prs->on_before_suspend)
+ return lps001wp_prs_enable(prs);
+ return 0;
+}
+
+static int lps001wp_prs_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct lps001wp_prs_data *prs = i2c_get_clientdata(client);
+
+ prs->on_before_suspend = atomic_read(&prs->enabled);
+ return lps001wp_prs_disable(prs);
+}
+
+static const struct i2c_device_id lps001wp_prs_id[]
+ = { { LPS001WP_PRS_DEV_NAME, 0}, { },};
+
+MODULE_DEVICE_TABLE(i2c, lps001wp_prs_id);
+
+static struct i2c_driver lps001wp_prs_driver = {
+ .driver = {
+ .name = LPS001WP_PRS_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = lps001wp_prs_probe,
+ .remove = __devexit_p(lps001wp_prs_remove),
+ .id_table = lps001wp_prs_id,
+ .resume = lps001wp_prs_resume,
+ .suspend = lps001wp_prs_suspend,
+};
+
+static int __init lps001wp_prs_init(void)
+{
+ printk(KERN_DEBUG "%s barometer driver: init\n",
+ LPS001WP_PRS_DEV_NAME);
+ return i2c_add_driver(&lps001wp_prs_driver);
+}
+
+static void __exit lps001wp_prs_exit(void)
+{
+ #if DEBUG
+ printk(KERN_DEBUG "%s barometer driver exit\n",
+ LPS001WP_PRS_DEV_NAME);
+ #endif
+ i2c_del_driver(&lps001wp_prs_driver);
+ return;
+}
+
+module_init(lps001wp_prs_init);
+module_exit(lps001wp_prs_exit);
+
+MODULE_DESCRIPTION("STMicrolelectronics lps001wp pressure sensor sysfs driver");
+MODULE_AUTHOR("Matteo Dameno, Carmine Iascone, STMicroelectronics");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/misc/ste_ff_vibra.c b/drivers/input/misc/ste_ff_vibra.c
new file mode 100644
index 00000000000..9038e6be046
--- /dev/null
+++ b/drivers/input/misc/ste_ff_vibra.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com>
+ * for ST-Ericsson
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <mach/ste_audio_io_vibrator.h>
+
+#define FF_VIBRA_DOWN 0x0000 /* 0 degrees */
+#define FF_VIBRA_LEFT 0x4000 /* 90 degrees */
+#define FF_VIBRA_UP 0x8000 /* 180 degrees */
+#define FF_VIBRA_RIGHT 0xC000 /* 270 degrees */
+
+/**
+ * struct vibra_info - Vibrator information structure
+ * @idev: Pointer to input device structure
+ * @vibra_workqueue: Pointer to vibrator workqueue structure
+ * @vibra_work: Vibrator work
+ * @direction: Vibration direction
+ * @speed: Vibration speed
+ *
+ * Structure vibra_info holds vibrator informations
+ **/
+struct vibra_info {
+ struct input_dev *idev;
+ struct workqueue_struct *vibra_workqueue;
+ struct work_struct vibra_work;
+ int direction;
+ unsigned char speed;
+};
+
+/**
+ * vibra_play_work() - Vibrator work, sets speed and direction
+ * @work: Pointer to work structure
+ *
+ * This function is called from workqueue, turns on/off vibrator
+ **/
+static void vibra_play_work(struct work_struct *work)
+{
+ struct vibra_info *vinfo = container_of(work,
+ struct vibra_info, vibra_work);
+ struct ste_vibra_speed left_speed = {
+ .positive = 0,
+ .negative = 0,
+ };
+ struct ste_vibra_speed right_speed = {
+ .positive = 0,
+ .negative = 0,
+ };
+
+ /* Divide by 2 because supported range by PWM is 0-100 */
+ vinfo->speed /= 2;
+
+ if ((vinfo->direction > FF_VIBRA_DOWN) &&
+ (vinfo->direction < FF_VIBRA_UP)) {
+ /* 1 - 179 degrees, turn on left vibrator */
+ left_speed.positive = vinfo->speed;
+ } else if (vinfo->direction > FF_VIBRA_UP) {
+ /* more than 180 degrees, turn on right vibrator */
+ right_speed.positive = vinfo->speed;
+ } else {
+ /* 0 (down) or 180 (up) degrees, turn on 2 vibrators */
+ left_speed.positive = vinfo->speed;
+ right_speed.positive = vinfo->speed;
+ }
+
+ ste_audioio_vibrator_pwm_control(STE_AUDIOIO_CLIENT_FF_VIBRA,
+ left_speed, right_speed);
+}
+
+/**
+ * vibra_play() - Memless device control function
+ * @idev: Pointer to input device structure
+ * @data: Pointer to private data (not used)
+ * @effect: Pointer to force feedback effect structure
+ *
+ * This function controls memless device
+ *
+ * Returns:
+ * 0 - success
+ **/
+static int vibra_play(struct input_dev *idev, void *data,
+ struct ff_effect *effect)
+{
+ struct vibra_info *vinfo = input_get_drvdata(idev);
+
+ vinfo->direction = effect->direction;
+ vinfo->speed = effect->u.rumble.strong_magnitude >> 8;
+ if (!vinfo->speed)
+ /* Shift weak magnitude to make it feelable on vibrator */
+ vinfo->speed = effect->u.rumble.weak_magnitude >> 9;
+
+ queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work);
+
+ return 0;
+}
+
+/**
+ * ste_ff_vibra_open() - Input device open function
+ * @idev: Pointer to input device structure
+ *
+ * This function is called on opening input device
+ *
+ * Returns:
+ * -ENOMEM - no memory left
+ * 0 - success
+ **/
+static int ste_ff_vibra_open(struct input_dev *idev)
+{
+ struct vibra_info *vinfo = input_get_drvdata(idev);
+
+ vinfo->vibra_workqueue =
+ create_singlethread_workqueue("ste_ff-ff-vibra");
+ if (!vinfo->vibra_workqueue) {
+ dev_err(&idev->dev, "couldn't create vibra workqueue\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/**
+ * ste_ff_vibra_close() - Input device close function
+ * @idev: Pointer to input device structure
+ *
+ * This function is called on closing input device
+ **/
+static void ste_ff_vibra_close(struct input_dev *idev)
+{
+ struct vibra_info *vinfo = input_get_drvdata(idev);
+
+ cancel_work_sync(&vinfo->vibra_work);
+ INIT_WORK(&vinfo->vibra_work, vibra_play_work);
+ destroy_workqueue(vinfo->vibra_workqueue);
+ vinfo->vibra_workqueue = NULL;
+}
+
+static int __devinit ste_ff_vibra_probe(struct platform_device *pdev)
+{
+ struct vibra_info *vinfo;
+ int ret;
+
+ vinfo = kmalloc(sizeof *vinfo, GFP_KERNEL);
+ if (!vinfo) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ vinfo->idev = input_allocate_device();
+ if (!vinfo->idev) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ ret = -ENOMEM;
+ goto exit_vinfo_free;
+ }
+
+ vinfo->idev->name = "ste-ff-vibra";
+ vinfo->idev->dev.parent = pdev->dev.parent;
+ vinfo->idev->open = ste_ff_vibra_open;
+ vinfo->idev->close = ste_ff_vibra_close;
+ INIT_WORK(&vinfo->vibra_work, vibra_play_work);
+ __set_bit(FF_RUMBLE, vinfo->idev->ffbit);
+
+ ret = input_ff_create_memless(vinfo->idev, NULL, vibra_play);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to create memless device\n");
+ goto exit_idev_free;
+ }
+
+ ret = input_register_device(vinfo->idev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto exit_destroy_memless;
+ }
+
+ input_set_drvdata(vinfo->idev, vinfo);
+ platform_set_drvdata(pdev, vinfo);
+ return 0;
+
+exit_destroy_memless:
+ input_ff_destroy(vinfo->idev);
+exit_idev_free:
+ input_free_device(vinfo->idev);
+exit_vinfo_free:
+ kfree(vinfo);
+ return ret;
+}
+
+static int __devexit ste_ff_vibra_remove(struct platform_device *pdev)
+{
+ struct vibra_info *vinfo = platform_get_drvdata(pdev);
+
+ /*
+ * Function device_release() will call input_dev_release()
+ * which will free ff and input device. No need to call
+ * input_ff_destroy() and input_free_device() explicitly.
+ */
+ input_unregister_device(vinfo->idev);
+ kfree(vinfo);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver ste_ff_vibra_driver = {
+ .driver = {
+ .name = "ste_ff_vibra",
+ .owner = THIS_MODULE,
+ },
+ .probe = ste_ff_vibra_probe,
+ .remove = __devexit_p(ste_ff_vibra_remove)
+};
+
+static int __init ste_ff_vibra_init(void)
+{
+ return platform_driver_register(&ste_ff_vibra_driver);
+}
+module_init(ste_ff_vibra_init);
+
+static void __exit ste_ff_vibra_exit(void)
+{
+ platform_driver_unregister(&ste_ff_vibra_driver);
+}
+module_exit(ste_ff_vibra_exit);
+
+MODULE_AUTHOR("Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com>");
+MODULE_DESCRIPTION("STE Force Feedback Vibrator Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3b9d5e2105d..5a54525b335 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -99,6 +99,30 @@ config TOUCHSCREEN_BITSY
To compile this driver as a module, choose M here: the
module will be called h3600_ts_input.
+config TOUCHSCREEN_BU21013
+ tristate "BU21013 based touch panel controllers"
+ depends on I2C
+ default y
+ help
+ Say Y here if you have a ROHM BU21013 and
+ want to enable support for the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bu21013_ts.
+
+config TOUCHSCREEN_CORGI
+ tristate "SharpSL (Corgi and Spitz series) touchscreen driver (DEPRECATED)"
+ depends on PXA_SHARPSL
+ select CORGI_SSP_DEPRECATED
+ help
+ Say Y here if you have a bu21013 touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bu21013_ts.
+
config TOUCHSCREEN_DA9034
tristate "Touchscreen support for Dialog Semiconductor DA9034"
depends on PMIC_DA903X
@@ -303,6 +327,12 @@ config TOUCHSCREEN_MIGOR
To compile this driver as a module, choose M here: the
module will be called migor_ts.
+config TOUCHSCREEN_SYNAPTICS_I2C_RMI
+ tristate "Synaptics i2c touchscreen"
+ depends on I2C
+ help
+ This enables support for Synaptics RMI over I2C based touchscreens.
+
config TOUCHSCREEN_TOUCHRIGHT
tristate "Touchright serial touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 497964a7a21..b582cec3007 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -12,6 +12,8 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
@@ -31,6 +33,7 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI) += synaptics_i2c_rmi.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c
new file mode 100644
index 00000000000..de460a41d03
--- /dev/null
+++ b/drivers/input/touchscreen/bu21013_ts.c
@@ -0,0 +1,931 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2009
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * License terms:GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/earlysuspend.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/input.h>
+#include <linux/input/bu21013.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+#define PEN_DOWN_INTR 0
+#define RESET_DELAY 30
+#define PENUP_TIMEOUT 2 /* 2msecs */
+#define SCALE_FACTOR 1000
+#define DELTA_MIN 16
+#define MASK_BITS 0x03
+#define SHIFT_8 8
+#define SHIFT_2 2
+#define LENGTH_OF_BUFFER 11
+#define I2C_RETRY_COUNT 5
+
+#define BU21013_SENSORS_BTN_0_7_REG 0x70
+#define BU21013_SENSORS_BTN_8_15_REG 0x71
+#define BU21013_SENSORS_BTN_16_23_REG 0x72
+#define BU21013_X1_POS_MSB_REG 0x73
+#define BU21013_X1_POS_LSB_REG 0x74
+#define BU21013_Y1_POS_MSB_REG 0x75
+#define BU21013_Y1_POS_LSB_REG 0x76
+#define BU21013_X2_POS_MSB_REG 0x77
+#define BU21013_X2_POS_LSB_REG 0x78
+#define BU21013_Y2_POS_MSB_REG 0x79
+#define BU21013_Y2_POS_LSB_REG 0x7A
+#define BU21013_INT_CLR_REG 0xE8
+#define BU21013_INT_MODE_REG 0xE9
+#define BU21013_GAIN_REG 0xEA
+#define BU21013_OFFSET_MODE_REG 0xEB
+#define BU21013_XY_EDGE_REG 0xEC
+#define BU21013_RESET_REG 0xED
+#define BU21013_CALIB_REG 0xEE
+#define BU21013_DONE_REG 0xEF
+#define BU21013_SENSOR_0_7_REG 0xF0
+#define BU21013_SENSOR_8_15_REG 0xF1
+#define BU21013_SENSOR_16_23_REG 0xF2
+#define BU21013_POS_MODE1_REG 0xF3
+#define BU21013_POS_MODE2_REG 0xF4
+#define BU21013_CLK_MODE_REG 0xF5
+#define BU21013_IDLE_REG 0xFA
+#define BU21013_FILTER_REG 0xFB
+#define BU21013_TH_ON_REG 0xFC
+#define BU21013_TH_OFF_REG 0xFD
+
+
+#define BU21013_RESET_ENABLE 0x01
+
+#define BU21013_SENSORS_EN_0_7 0x3F
+#define BU21013_SENSORS_EN_8_15 0xFC
+#define BU21013_SENSORS_EN_16_23 0x1F
+
+#define BU21013_POS_MODE1_0 0x02
+#define BU21013_POS_MODE1_1 0x04
+#define BU21013_POS_MODE1_2 0x08
+
+#define BU21013_POS_MODE2_ZERO 0x01
+#define BU21013_POS_MODE2_AVG1 0x02
+#define BU21013_POS_MODE2_AVG2 0x04
+#define BU21013_POS_MODE2_EN_XY 0x08
+#define BU21013_POS_MODE2_EN_RAW 0x10
+#define BU21013_POS_MODE2_MULTI 0x80
+
+#define BU21013_CLK_MODE_DIV 0x01
+#define BU21013_CLK_MODE_EXT 0x02
+#define BU21013_CLK_MODE_CALIB 0x80
+
+#define BU21013_IDLET_0 0x01
+#define BU21013_IDLET_1 0x02
+#define BU21013_IDLET_2 0x04
+#define BU21013_IDLET_3 0x08
+#define BU21013_IDLE_INTERMIT_EN 0x10
+
+#define BU21013_DELTA_0_6 0x7F
+#define BU21013_FILTER_EN 0x80
+
+#define BU21013_INT_MODE_LEVEL 0x00
+#define BU21013_INT_MODE_EDGE 0x01
+
+#define BU21013_GAIN_0 0x01
+#define BU21013_GAIN_1 0x02
+#define BU21013_GAIN_2 0x04
+
+#define BU21013_OFFSET_MODE_DEFAULT 0x00
+#define BU21013_OFFSET_MODE_MOVE 0x01
+#define BU21013_OFFSET_MODE_DISABLE 0x02
+
+#define BU21013_TH_ON_0 0x01
+#define BU21013_TH_ON_1 0x02
+#define BU21013_TH_ON_2 0x04
+#define BU21013_TH_ON_3 0x08
+#define BU21013_TH_ON_4 0x10
+#define BU21013_TH_ON_5 0x20
+#define BU21013_TH_ON_6 0x40
+#define BU21013_TH_ON_7 0x80
+#define BU21013_TH_ON_MAX 0xFF
+
+#define BU21013_TH_OFF_0 0x01
+#define BU21013_TH_OFF_1 0x02
+#define BU21013_TH_OFF_2 0x04
+#define BU21013_TH_OFF_3 0x08
+#define BU21013_TH_OFF_4 0x10
+#define BU21013_TH_OFF_5 0x20
+#define BU21013_TH_OFF_6 0x40
+#define BU21013_TH_OFF_7 0x80
+#define BU21013_TH_OFF_MAX 0xFF
+
+#define BU21013_X_EDGE_0 0x01
+#define BU21013_X_EDGE_1 0x02
+#define BU21013_X_EDGE_2 0x04
+#define BU21013_X_EDGE_3 0x08
+#define BU21013_Y_EDGE_0 0x10
+#define BU21013_Y_EDGE_1 0x20
+#define BU21013_Y_EDGE_2 0x40
+#define BU21013_Y_EDGE_3 0x80
+
+#define BU21013_DONE 0x01
+#define BU21013_NUMBER_OF_X_SENSORS (6)
+#define BU21013_NUMBER_OF_Y_SENSORS (11)
+
+#define DRIVER_TP "bu21013_ts"
+
+/**
+ * struct bu21013_ts_data - touch panel data structure
+ * @client: pointer to the i2c client
+ * @wait: variable to wait_queue_head_t structure
+ * @touch_stopped: touch stop flag
+ * @chip: pointer to the touch panel controller
+ * @in_dev: pointer to the input device structure
+ * @intr_pin: interrupt pin value
+ * @regulator: pointer to the Regulator used for touch screen
+ * @enable: variable to indicate the enable/disable of touch screen
+ * @ext_clk_enable: true if running on ext clk
+ * @ext_clk_state: Saved state for suspend/resume of ext clk
+ * @factor_x: x scale factor
+ * @factor_y: y scale factor
+ * @tpclk: pointer to clock structure
+ * @early_suspend: early_suspend structure variable
+ *
+ * Touch panel device data structure
+ */
+struct bu21013_ts_data {
+ struct i2c_client *client;
+ wait_queue_head_t wait;
+ bool touch_stopped;
+ struct bu21013_platform_device *chip;
+ struct input_dev *in_dev;
+ unsigned int intr_pin;
+ struct regulator *regulator;
+ bool enable;
+ bool ext_clk_enable;
+ bool ext_clk_state;
+ unsigned int factor_x;
+ unsigned int factor_y;
+ struct clk *tpclk;
+ struct early_suspend early_suspend;
+};
+
+static int bu21013_init_chip(struct bu21013_ts_data *data, bool on_ext_clk);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void bu21013_ts_early_suspend(struct early_suspend *data);
+static void bu21013_ts_late_resume(struct early_suspend *data);
+#endif
+
+/**
+ * bu21013_ext_clk() - enable/disable the external clock
+ * @pdata: touch screen data
+ * @enable: enable external clock
+ * @reconfig: reconfigure chip upon external clock off.
+ *
+ * This function used to enable or disable the external clock and possible
+ * reconfigure hw.
+ */
+static int bu21013_ext_clk(struct bu21013_ts_data *pdata, bool enable,
+ bool reconfig)
+{
+ int retval = 0;
+
+ if (!pdata->tpclk || pdata->ext_clk_enable == enable)
+ return retval;
+
+ if (enable) {
+ pdata->ext_clk_enable = true;
+ clk_enable(pdata->tpclk);
+ retval = bu21013_init_chip(pdata, true);
+ } else {
+ pdata->ext_clk_enable = false;
+ if (reconfig)
+ retval = bu21013_init_chip(pdata, false);
+ clk_disable(pdata->tpclk);
+ }
+ return retval;
+}
+
+/**
+ * bu21013_enable() - enable the touch driver event
+ * @pdata: touch screen data
+ *
+ * This function used to enable the driver and returns integer
+ */
+static int bu21013_enable(struct bu21013_ts_data *pdata)
+{
+ int retval;
+
+ if (pdata->regulator)
+ regulator_enable(pdata->regulator);
+
+ if (pdata->chip->cs_en) {
+ retval = pdata->chip->cs_en(pdata->chip->cs_pin);
+ if (retval < 0) {
+ dev_err(&pdata->client->dev, "enable hw failed\n");
+ return retval;
+ }
+ }
+
+ if (pdata->ext_clk_state)
+ retval = bu21013_ext_clk(pdata, true, true);
+ else
+ retval = bu21013_init_chip(pdata, false);
+
+ if (retval < 0) {
+ dev_err(&pdata->client->dev, "enable hw failed\n");
+ return retval;
+ }
+ pdata->touch_stopped = false;
+ enable_irq(pdata->chip->irq);
+
+ return 0;
+}
+
+/**
+ * bu21013_disable() - disable the touch driver event
+ * @pdata: touch screen data
+ *
+ * This function used to disable the driver and returns integer
+ */
+static void bu21013_disable(struct bu21013_ts_data *pdata)
+{
+ pdata->touch_stopped = true;
+
+ pdata->ext_clk_state = pdata->ext_clk_enable;
+ (void) bu21013_ext_clk(pdata, false, false);
+
+ disable_irq(pdata->chip->irq);
+ if (pdata->chip->cs_dis)
+ pdata->chip->cs_dis(pdata->chip->cs_pin);
+ if (pdata->regulator)
+ regulator_disable(pdata->regulator);
+}
+
+/**
+ * bu21013_show_attr_enable() - show the touch screen controller status
+ * @dev: pointer to device structure
+ * @attr: pointer to device attribute
+ * @buf: parameter buffer
+ *
+ * This funtion is used to show whether the touch screen is enabled or
+ * disabled
+ */
+static ssize_t bu21013_show_attr_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bu21013_ts_data *pdata = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", pdata->enable);
+}
+
+/**
+ * bu21013_store_attr_enable() - Enable/Disable the touchscreen.
+ * @dev: pointer to device structure
+ * @attr: pointer to device attribute
+ * @buf: parameter buffer
+ * @count: number of parameters
+ *
+ * This funtion is used to enable or disable the touch screen controller.
+ */
+static ssize_t bu21013_store_attr_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned long val;
+
+ struct bu21013_ts_data *pdata = dev_get_drvdata(dev);
+
+ if (strict_strtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if ((val != 0) && (val != 1))
+ return -EINVAL;
+
+ if (pdata->enable != val) {
+ pdata->enable = val ? true : false;
+ if (pdata->enable) {
+ ret = bu21013_enable(pdata);
+ if (ret < 0)
+ return ret;
+ } else
+ bu21013_disable(pdata);
+ }
+ return count;
+}
+
+/**
+ * bu21013_show_attr_extclk() - shows the external clock status
+ * @dev: pointer to device structure
+ * @attr: pointer to device attribute
+ * @buf: parameter buffer
+ *
+ * This funtion is used to show whether the external clock for the touch
+ * screen is enabled or disabled.
+ */
+static ssize_t bu21013_show_attr_extclk(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bu21013_ts_data *pdata = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", pdata->ext_clk_enable);
+}
+
+/**
+ * bu21013_store_attr_extclk() - Enable/Disable the external clock
+ * for the tocuh screen controller.
+ * @dev: pointer to device structure
+ * @attr: pointer to device attribute
+ * @buf: parameter buffer
+ * @count: number of parameters
+ *
+ * This funtion is used enabled or disable the external clock for the touch
+ * screen controller.
+ */
+static ssize_t bu21013_store_attr_extclk(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval = 0;
+ struct bu21013_ts_data *pdata = dev_get_drvdata(dev);
+ unsigned long val;
+
+ if (strict_strtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if ((val != 0) && (val != 1))
+ return -EINVAL;
+
+ if (pdata->chip->has_ext_clk) {
+ if (pdata->enable)
+ retval = bu21013_ext_clk(pdata, val, true);
+ else
+ pdata->ext_clk_state = val;
+ if (retval < 0)
+ return retval;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
+ bu21013_show_attr_enable, bu21013_store_attr_enable);
+
+static DEVICE_ATTR(ext_clk, S_IWUSR | S_IRUGO,
+ bu21013_show_attr_extclk, bu21013_store_attr_extclk);
+
+
+static struct attribute *bu21013_attribute[] = {
+ &dev_attr_enable.attr,
+ &dev_attr_ext_clk.attr,
+ NULL,
+};
+
+static struct attribute_group bu21013_attr_group = {
+ .attrs = bu21013_attribute,
+};
+
+
+/**
+ * bu21013_read_block_data(): read the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ * @buf: byte pointer
+ *
+ * Read the touch co-ordinates using i2c read block into buffer
+ * and returns integer.
+ */
+static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf)
+{
+ int ret, i;
+
+ for (i = 0; i < I2C_RETRY_COUNT; i++) {
+ ret = i2c_smbus_read_i2c_block_data
+ (data->client, BU21013_SENSORS_BTN_0_7_REG,
+ LENGTH_OF_BUFFER, buf);
+ if (ret == LENGTH_OF_BUFFER)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * bu21013_do_touch_report(): Get the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ *
+ * Get the touch co-ordinates from touch sensor registers and writes
+ * into device structure and returns integer.
+ */
+static int bu21013_do_touch_report(struct bu21013_ts_data *data)
+{
+ u8 buf[LENGTH_OF_BUFFER];
+ unsigned int pos_x[2], pos_y[2];
+ bool has_x_sensors, has_y_sensors;
+ int finger_down_count = 0;
+ int i;
+
+ if (data == NULL)
+ return -EINVAL;
+
+ if (bu21013_read_block_data(data, buf) < 0)
+ return -EINVAL;
+
+ has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7);
+ has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) |
+ ((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2);
+ if (!has_x_sensors || !has_y_sensors)
+ return 0;
+
+ for (i = 0; i < 2; i++) {
+ const u8 *p = &buf[4 * i + 3];
+ unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS);
+ unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS);
+ if (x == 0 || y == 0)
+ continue;
+ x = x * data->factor_x / SCALE_FACTOR;
+ y = y * data->factor_y / SCALE_FACTOR;
+ pos_x[finger_down_count] = x;
+ pos_y[finger_down_count] = y;
+ finger_down_count++;
+ }
+
+ if (finger_down_count) {
+ if (finger_down_count == 2 &&
+ (abs(pos_x[0] - pos_x[1]) < DELTA_MIN ||
+ abs(pos_y[0] - pos_y[1]) < DELTA_MIN))
+ return 0;
+
+ for (i = 0; i < finger_down_count; i++) {
+ if (data->chip->portrait && data->chip->x_flip)
+ pos_x[i] = data->chip->x_max_res - pos_x[i];
+ if (data->chip->portrait && data->chip->y_flip)
+ pos_y[i] = data->chip->y_max_res - pos_y[i];
+ input_report_abs(data->in_dev, ABS_MT_TOUCH_MAJOR,
+ max(pos_x[i], pos_y[i]));
+ input_report_abs(data->in_dev, ABS_MT_POSITION_X,
+ pos_x[i]);
+ input_report_abs(data->in_dev, ABS_MT_POSITION_Y,
+ pos_y[i]);
+ input_mt_sync(data->in_dev);
+ }
+ } else
+ input_mt_sync(data->in_dev);
+
+ input_sync(data->in_dev);
+
+ return 0;
+}
+/**
+ * bu21013_gpio_irq() - gpio thread function for touch interrupt
+ * @irq: irq value
+ * @device_data: void pointer
+ *
+ * This gpio thread function for touch interrupt
+ * and returns irqreturn_t.
+ */
+static irqreturn_t bu21013_gpio_irq(int irq, void *device_data)
+{
+ struct bu21013_ts_data *data = device_data;
+ struct i2c_client *i2c = data->client;
+ int retval;
+
+ do {
+ retval = bu21013_do_touch_report(data);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "bu21013_do_touch_report failed\n");
+ return IRQ_NONE;
+ }
+ data->intr_pin = data->chip->irq_read_val();
+ if (data->intr_pin == PEN_DOWN_INTR)
+ wait_event_timeout(data->wait, data->touch_stopped,
+ msecs_to_jiffies(PENUP_TIMEOUT));
+ } while (!data->intr_pin && !data->touch_stopped);
+ return IRQ_HANDLED;
+}
+
+/**
+ * bu21013_init_chip() - power on sequence for the bu21013 controller
+ * @data: device structure pointer
+ * @on_ext_clk: Run on external clock
+ *
+ * This function is used to power on
+ * the bu21013 controller and returns integer.
+ */
+static int bu21013_init_chip(struct bu21013_ts_data *data, bool on_ext_clk)
+{
+ int retval;
+ struct i2c_client *i2c = data->client;
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG,
+ BU21013_RESET_ENABLE);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_RESET reg write failed\n");
+ return retval;
+ }
+ msleep(RESET_DELAY);
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG,
+ BU21013_SENSORS_EN_0_7);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n");
+ return retval;
+ }
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG,
+ BU21013_SENSORS_EN_8_15);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n");
+ return retval;
+ }
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG,
+ BU21013_SENSORS_EN_16_23);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n");
+ return retval;
+ }
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG,
+ (BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n");
+ return retval;
+ }
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG,
+ (BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 |
+ BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW |
+ BU21013_POS_MODE2_MULTI));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n");
+ return retval;
+ }
+ if (on_ext_clk)
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+ (BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB));
+ else
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+ (BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n");
+ return retval;
+ }
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG,
+ (BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n");
+ return retval;
+ }
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG,
+ BU21013_INT_MODE_LEVEL);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n");
+ return retval;
+ }
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG,
+ (BU21013_DELTA_0_6 |
+ BU21013_FILTER_EN));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG,
+ BU21013_TH_ON_5);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n");
+ return retval;
+ }
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG,
+ BU21013_TH_OFF_4 | BU21013_TH_OFF_3);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n");
+ return retval;
+ }
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG,
+ (BU21013_GAIN_0 | BU21013_GAIN_1));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n");
+ return retval;
+ }
+
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG,
+ BU21013_OFFSET_MODE_DEFAULT);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n");
+ return retval;
+ }
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG,
+ (BU21013_X_EDGE_0 | BU21013_X_EDGE_2 |
+ BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3));
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n");
+ return retval;
+ }
+ retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG,
+ BU21013_DONE);
+ if (retval < 0) {
+ dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n");
+ return retval;
+ }
+
+ data->factor_x = (data->chip->x_max_res * SCALE_FACTOR /
+ data->chip->touch_x_max);
+ data->factor_y = (data->chip->y_max_res * SCALE_FACTOR /
+ data->chip->touch_y_max);
+ return retval;
+}
+
+/**
+ * bu21013_probe() - initialzes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ * @id: i2c device id pointer
+ *
+ * This function used to initializes the i2c-client touchscreen
+ * driver and returns integer.
+ */
+static int __devinit bu21013_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int retval;
+ struct bu21013_ts_data *bu21013_data;
+ struct input_dev *in_dev;
+ struct bu21013_platform_device *pdata =
+ client->dev.platform_data;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "i2c smbus byte data not supported\n");
+ return -EIO;
+ }
+
+ if (!pdata) {
+ dev_err(&client->dev, "platform data not defined\n");
+ retval = -EINVAL;
+ return retval;
+ }
+
+ bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL);
+ if (!bu21013_data) {
+ dev_err(&client->dev, "device memory alloc failed\n");
+ retval = -ENOMEM;
+ return retval;
+ }
+ /* allocate input device */
+ in_dev = input_allocate_device();
+ if (!in_dev) {
+ dev_err(&client->dev, "input device memory alloc failed\n");
+ retval = -ENOMEM;
+ goto err_alloc;
+ }
+
+ bu21013_data->in_dev = in_dev;
+ bu21013_data->chip = pdata;
+ bu21013_data->client = client;
+
+ bu21013_data->regulator = regulator_get(&client->dev, "avdd");
+ if (IS_ERR(bu21013_data->regulator)) {
+ dev_warn(&client->dev, "regulator_get failed\n");
+ bu21013_data->regulator = NULL;
+ }
+ if (bu21013_data->regulator)
+ regulator_enable(bu21013_data->regulator);
+
+ /* configure the gpio pins */
+ if (pdata->cs_en) {
+ retval = pdata->cs_en(pdata->cs_pin);
+ if (retval < 0) {
+ dev_err(&client->dev, "chip init failed\n");
+ goto err_init_cs;
+ }
+ }
+
+ if (pdata->has_ext_clk) {
+ bu21013_data->tpclk = clk_get(&client->dev, NULL);
+ if (IS_ERR(bu21013_data->tpclk)) {
+ dev_warn(&client->dev, "get extern clock failed\n");
+ bu21013_data->tpclk = NULL;
+ }
+ }
+
+ if (pdata->enable_ext_clk && bu21013_data->tpclk) {
+ retval = clk_enable(bu21013_data->tpclk);
+ if (retval < 0) {
+ dev_err(&client->dev, "clock enable failed\n");
+ goto err_ext_clk;
+ }
+ bu21013_data->ext_clk_enable = true;
+ }
+
+ /* configure the touch panel controller */
+ retval = bu21013_init_chip(bu21013_data, bu21013_data->ext_clk_enable);
+ if (retval < 0) {
+ dev_err(&client->dev, "error in bu21013 config\n");
+ goto err_init_config;
+ }
+
+ init_waitqueue_head(&bu21013_data->wait);
+ bu21013_data->touch_stopped = false;
+
+ /* register the device to input subsystem */
+ in_dev->name = DRIVER_TP;
+ in_dev->id.bustype = BUS_I2C;
+ in_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_SYN, in_dev->evbit);
+ __set_bit(EV_KEY, in_dev->evbit);
+ __set_bit(EV_ABS, in_dev->evbit);
+
+ input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0,
+ pdata->x_max_res, 0, 0);
+ input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0,
+ pdata->y_max_res, 0, 0);
+ input_set_abs_params(in_dev, ABS_MT_TOUCH_MAJOR, 0,
+ max(pdata->x_max_res , pdata->y_max_res), 0, 0);
+ input_set_drvdata(in_dev, bu21013_data);
+ retval = input_register_device(in_dev);
+ if (retval)
+ goto err_input_register;
+
+ retval = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq,
+ (IRQF_TRIGGER_FALLING | IRQF_SHARED),
+ DRIVER_TP, bu21013_data);
+ if (retval) {
+ dev_err(&client->dev, "request irq %d failed\n", pdata->irq);
+ goto err_init_irq;
+ }
+ bu21013_data->enable = true;
+ i2c_set_clientdata(client, bu21013_data);
+
+ /* sysfs implementation for dynamic enable/disable the input event */
+ retval = sysfs_create_group(&client->dev.kobj, &bu21013_attr_group);
+ if (retval) {
+ dev_err(&client->dev, "failed to create sysfs entries\n");
+ goto err_sysfs_create;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ bu21013_data->early_suspend.level =
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ bu21013_data->early_suspend.suspend = bu21013_ts_early_suspend;
+ bu21013_data->early_suspend.resume = bu21013_ts_late_resume;
+ register_early_suspend(&bu21013_data->early_suspend);
+#endif
+ return retval;
+
+err_sysfs_create:
+ free_irq(pdata->irq, bu21013_data);
+ i2c_set_clientdata(client, NULL);
+err_init_irq:
+ input_unregister_device(bu21013_data->in_dev);
+err_input_register:
+ wake_up(&bu21013_data->wait);
+err_init_config:
+ if (bu21013_data->tpclk) {
+ if (bu21013_data->ext_clk_enable)
+ clk_disable(bu21013_data->tpclk);
+ clk_put(bu21013_data->tpclk);
+ }
+err_ext_clk:
+ if (pdata->cs_dis)
+ pdata->cs_dis(pdata->cs_pin);
+err_init_cs:
+ if (bu21013_data->regulator) {
+ regulator_disable(bu21013_data->regulator);
+ regulator_put(bu21013_data->regulator);
+ }
+ input_free_device(bu21013_data->in_dev);
+err_alloc:
+ kfree(bu21013_data);
+
+ return retval;
+}
+
+/**
+ * bu21013_remove() - removes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ *
+ * This function uses to remove the i2c-client
+ * touchscreen driver and returns integer.
+ */
+static int __devexit bu21013_remove(struct i2c_client *client)
+{
+ struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client);
+
+ bu21013_data->touch_stopped = true;
+ sysfs_remove_group(&client->dev.kobj, &bu21013_attr_group);
+ wake_up(&bu21013_data->wait);
+ free_irq(bu21013_data->chip->irq, bu21013_data);
+ bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin);
+ input_unregister_device(bu21013_data->in_dev);
+
+ if (bu21013_data->tpclk) {
+ if (bu21013_data->ext_clk_enable)
+ clk_disable(bu21013_data->tpclk);
+ clk_put(bu21013_data->tpclk);
+ }
+ if (bu21013_data->regulator) {
+ regulator_disable(bu21013_data->regulator);
+ regulator_put(bu21013_data->regulator);
+ }
+ kfree(bu21013_data);
+
+ return 0;
+}
+
+#if !defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)
+/**
+ * bu21013_suspend() - suspend the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to suspend the
+ * touch panel controller and returns integer
+ */
+static int bu21013_suspend(struct device *dev)
+{
+ struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+
+ bu21013_disable(bu21013_data);
+
+ return 0;
+}
+
+/**
+ * bu21013_resume() - resume the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to resume the touch panel
+ * controller and returns integer.
+ */
+static int bu21013_resume(struct device *dev)
+{
+ struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+
+ return bu21013_enable(bu21013_data);
+}
+
+static const struct dev_pm_ops bu21013_dev_pm_ops = {
+ .suspend = bu21013_suspend,
+ .resume = bu21013_resume,
+};
+
+#else
+static void bu21013_ts_early_suspend(struct early_suspend *data)
+{
+ struct bu21013_ts_data *bu21013_data =
+ container_of(data, struct bu21013_ts_data, early_suspend);
+ bu21013_disable(bu21013_data);
+}
+
+static void bu21013_ts_late_resume(struct early_suspend *data)
+{
+ struct bu21013_ts_data *bu21013_data =
+ container_of(data, struct bu21013_ts_data, early_suspend);
+ struct i2c_client *client = bu21013_data->client;
+ int retval;
+
+ retval = bu21013_enable(bu21013_data);
+ if (retval < 0)
+ dev_err(&client->dev, "bu21013 enable failed\n");
+}
+#endif
+
+static const struct i2c_device_id bu21013_id[] = {
+ { DRIVER_TP, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bu21013_id);
+
+static struct i2c_driver bu21013_driver = {
+ .driver = {
+ .name = DRIVER_TP,
+ .owner = THIS_MODULE,
+#if !defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)
+ .pm = &bu21013_dev_pm_ops,
+#endif
+ },
+ .probe = bu21013_probe,
+ .remove = __devexit_p(bu21013_remove),
+ .id_table = bu21013_id,
+};
+
+/**
+ * bu21013_init() - initializes the bu21013 touchscreen driver
+ *
+ * This function used to initializes the bu21013
+ * touchscreen driver and returns integer.
+ */
+static int __init bu21013_init(void)
+{
+ return i2c_add_driver(&bu21013_driver);
+}
+
+/**
+ * bu21013_exit() - de-initializes the bu21013 touchscreen driver
+ *
+ * This function uses to de-initializes the bu21013
+ * touchscreen driver and returns none.
+ */
+static void __exit bu21013_exit(void)
+{
+ i2c_del_driver(&bu21013_driver);
+}
+
+module_init(bu21013_init);
+module_exit(bu21013_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>");
+MODULE_DESCRIPTION("bu21013 touch screen controller driver");
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi.c b/drivers/input/touchscreen/synaptics_i2c_rmi.c
new file mode 100644
index 00000000000..5729602cbb6
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi.c
@@ -0,0 +1,675 @@
+/* drivers/input/keyboard/synaptics_i2c_rmi.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/synaptics_i2c_rmi.h>
+
+static struct workqueue_struct *synaptics_wq;
+
+struct synaptics_ts_data {
+ uint16_t addr;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ int use_irq;
+ bool has_relative_report;
+ struct hrtimer timer;
+ struct work_struct work;
+ uint16_t max[2];
+ int snap_state[2][2];
+ int snap_down_on[2];
+ int snap_down_off[2];
+ int snap_up_on[2];
+ int snap_up_off[2];
+ int snap_down[2];
+ int snap_up[2];
+ uint32_t flags;
+ int reported_finger_count;
+ int8_t sensitivity_adjust;
+ int (*power)(int on);
+ struct early_suspend early_suspend;
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void synaptics_ts_early_suspend(struct early_suspend *h);
+static void synaptics_ts_late_resume(struct early_suspend *h);
+#endif
+
+static int synaptics_init_panel(struct synaptics_ts_data *ts)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n");
+ goto err_page_select_failed;
+ }
+ ret = i2c_smbus_write_byte_data(ts->client, 0x41, 0x04); /* Set "No Clip Z" */
+ if (ret < 0)
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed for No Clip Z\n");
+
+ ret = i2c_smbus_write_byte_data(ts->client, 0x44,
+ ts->sensitivity_adjust);
+ if (ret < 0)
+ pr_err("synaptics_ts: failed to set Sensitivity Adjust\n");
+
+err_page_select_failed:
+ ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x04); /* page select = 0x04 */
+ if (ret < 0)
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n");
+ ret = i2c_smbus_write_byte_data(ts->client, 0xf0, 0x81); /* normal operation, 80 reports per second */
+ if (ret < 0)
+ printk(KERN_ERR "synaptics_ts_resume: i2c_smbus_write_byte_data failed\n");
+ return ret;
+}
+
+static void synaptics_ts_work_func(struct work_struct *work)
+{
+ int i;
+ int ret;
+ int bad_data = 0;
+ struct i2c_msg msg[2];
+ uint8_t start_reg;
+ uint8_t buf[15];
+ struct synaptics_ts_data *ts = container_of(work, struct synaptics_ts_data, work);
+ int buf_len = ts->has_relative_report ? 15 : 13;
+
+ msg[0].addr = ts->client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &start_reg;
+ start_reg = 0x00;
+ msg[1].addr = ts->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = buf_len;
+ msg[1].buf = buf;
+
+ /* printk("synaptics_ts_work_func\n"); */
+ for (i = 0; i < ((ts->use_irq && !bad_data) ? 1 : 10); i++) {
+ ret = i2c_transfer(ts->client->adapter, msg, 2);
+ if (ret < 0) {
+ printk(KERN_ERR "synaptics_ts_work_func: i2c_transfer failed\n");
+ bad_data = 1;
+ } else {
+ /* printk("synaptics_ts_work_func: %x %x %x %x %x %x" */
+ /* " %x %x %x %x %x %x %x %x %x, ret %d\n", */
+ /* buf[0], buf[1], buf[2], buf[3], */
+ /* buf[4], buf[5], buf[6], buf[7], */
+ /* buf[8], buf[9], buf[10], buf[11], */
+ /* buf[12], buf[13], buf[14], ret); */
+ if ((buf[buf_len - 1] & 0xc0) != 0x40) {
+ printk(KERN_WARNING "synaptics_ts_work_func:"
+ " bad read %x %x %x %x %x %x %x %x %x"
+ " %x %x %x %x %x %x, ret %d\n",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], ret);
+ if (bad_data)
+ synaptics_init_panel(ts);
+ bad_data = 1;
+ continue;
+ }
+ bad_data = 0;
+ if ((buf[buf_len - 1] & 1) == 0) {
+ /* printk("read %d coordinates\n", i); */
+ break;
+ } else {
+ int pos[2][2];
+ int f, a;
+ int base;
+ /* int x = buf[3] | (uint16_t)(buf[2] & 0x1f) << 8; */
+ /* int y = buf[5] | (uint16_t)(buf[4] & 0x1f) << 8; */
+ int z = buf[1];
+ int w = buf[0] >> 4;
+ int finger = buf[0] & 7;
+
+ /* int x2 = buf[3+6] | (uint16_t)(buf[2+6] & 0x1f) << 8; */
+ /* int y2 = buf[5+6] | (uint16_t)(buf[4+6] & 0x1f) << 8; */
+ /* int z2 = buf[1+6]; */
+ /* int w2 = buf[0+6] >> 4; */
+ /* int finger2 = buf[0+6] & 7; */
+
+ /* int dx = (int8_t)buf[12]; */
+ /* int dy = (int8_t)buf[13]; */
+ int finger2_pressed;
+
+ /* printk("x %4d, y %4d, z %3d, w %2d, F %d, 2nd: x %4d, y %4d, z %3d, w %2d, F %d, dx %4d, dy %4d\n", */
+ /* x, y, z, w, finger, */
+ /* x2, y2, z2, w2, finger2, */
+ /* dx, dy); */
+
+ base = 2;
+ for (f = 0; f < 2; f++) {
+ uint32_t flip_flag = SYNAPTICS_FLIP_X;
+ for (a = 0; a < 2; a++) {
+ int p = buf[base + 1];
+ p |= (uint16_t)(buf[base] & 0x1f) << 8;
+ if (ts->flags & flip_flag)
+ p = ts->max[a] - p;
+ if (ts->flags & SYNAPTICS_SNAP_TO_INACTIVE_EDGE) {
+ if (ts->snap_state[f][a]) {
+ if (p <= ts->snap_down_off[a])
+ p = ts->snap_down[a];
+ else if (p >= ts->snap_up_off[a])
+ p = ts->snap_up[a];
+ else
+ ts->snap_state[f][a] = 0;
+ } else {
+ if (p <= ts->snap_down_on[a]) {
+ p = ts->snap_down[a];
+ ts->snap_state[f][a] = 1;
+ } else if (p >= ts->snap_up_on[a]) {
+ p = ts->snap_up[a];
+ ts->snap_state[f][a] = 1;
+ }
+ }
+ }
+ pos[f][a] = p;
+ base += 2;
+ flip_flag <<= 1;
+ }
+ base += 2;
+ if (ts->flags & SYNAPTICS_SWAP_XY)
+ swap(pos[f][0], pos[f][1]);
+ }
+ if (z) {
+ input_report_abs(ts->input_dev, ABS_X, pos[0][0]);
+ input_report_abs(ts->input_dev, ABS_Y, pos[0][1]);
+ }
+ input_report_abs(ts->input_dev, ABS_PRESSURE, z);
+ input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, w);
+ input_report_key(ts->input_dev, BTN_TOUCH, finger);
+ finger2_pressed = finger > 1 && finger != 7;
+ input_report_key(ts->input_dev, BTN_2, finger2_pressed);
+ if (finger2_pressed) {
+ input_report_abs(ts->input_dev, ABS_HAT0X, pos[1][0]);
+ input_report_abs(ts->input_dev, ABS_HAT0Y, pos[1][1]);
+ }
+
+ if (!finger)
+ z = 0;
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[0][0]);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[0][1]);
+ input_mt_sync(ts->input_dev);
+ if (finger2_pressed) {
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[1][0]);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[1][1]);
+ input_mt_sync(ts->input_dev);
+ } else if (ts->reported_finger_count > 1) {
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
+ input_mt_sync(ts->input_dev);
+ }
+ ts->reported_finger_count = finger;
+ input_sync(ts->input_dev);
+ }
+ }
+ }
+ if (ts->use_irq)
+ enable_irq(ts->client->irq);
+}
+
+static enum hrtimer_restart synaptics_ts_timer_func(struct hrtimer *timer)
+{
+ struct synaptics_ts_data *ts = container_of(timer, struct synaptics_ts_data, timer);
+ /* printk("synaptics_ts_timer_func\n"); */
+
+ queue_work(synaptics_wq, &ts->work);
+
+ hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t synaptics_ts_irq_handler(int irq, void *dev_id)
+{
+ struct synaptics_ts_data *ts = dev_id;
+
+ /* printk("synaptics_ts_irq_handler\n"); */
+ disable_irq_nosync(ts->client->irq);
+ queue_work(synaptics_wq, &ts->work);
+ return IRQ_HANDLED;
+}
+
+static int synaptics_ts_probe(
+ struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct synaptics_ts_data *ts;
+ uint8_t buf0[4];
+ uint8_t buf1[8];
+ struct i2c_msg msg[2];
+ int ret = 0;
+ uint16_t max_x, max_y;
+ int fuzz_x, fuzz_y, fuzz_p, fuzz_w;
+ struct synaptics_i2c_rmi_platform_data *pdata;
+ unsigned long irqflags;
+ int inactive_area_left;
+ int inactive_area_right;
+ int inactive_area_top;
+ int inactive_area_bottom;
+ int snap_left_on;
+ int snap_left_off;
+ int snap_right_on;
+ int snap_right_off;
+ int snap_top_on;
+ int snap_top_off;
+ int snap_bottom_on;
+ int snap_bottom_off;
+ uint32_t panel_version;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ printk(KERN_ERR "synaptics_ts_probe: need I2C_FUNC_I2C\n");
+ ret = -ENODEV;
+ goto err_check_functionality_failed;
+ }
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (ts == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_data_failed;
+ }
+ INIT_WORK(&ts->work, synaptics_ts_work_func);
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+ pdata = client->dev.platform_data;
+ if (pdata)
+ ts->power = pdata->power;
+ if (ts->power) {
+ ret = ts->power(1);
+ if (ret < 0) {
+ printk(KERN_ERR "synaptics_ts_probe power on failed\n");
+ goto err_power_failed;
+ }
+ }
+
+ ret = i2c_smbus_write_byte_data(ts->client, 0xf4, 0x01); /* device command = reset */
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed\n");
+ /* fail? */
+ }
+ {
+ int retry = 10;
+ while (retry-- > 0) {
+ ret = i2c_smbus_read_byte_data(ts->client, 0xe4);
+ if (ret >= 0)
+ break;
+ msleep(100);
+ }
+ }
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: Product Major Version %x\n", ret);
+ panel_version = ret << 8;
+ ret = i2c_smbus_read_byte_data(ts->client, 0xe5);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: Product Minor Version %x\n", ret);
+ panel_version |= ret;
+
+ ret = i2c_smbus_read_byte_data(ts->client, 0xe3);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: product property %x\n", ret);
+
+ if (pdata) {
+ while (pdata->version > panel_version)
+ pdata++;
+ ts->flags = pdata->flags;
+ ts->sensitivity_adjust = pdata->sensitivity_adjust;
+ irqflags = pdata->irqflags;
+ inactive_area_left = pdata->inactive_left;
+ inactive_area_right = pdata->inactive_right;
+ inactive_area_top = pdata->inactive_top;
+ inactive_area_bottom = pdata->inactive_bottom;
+ snap_left_on = pdata->snap_left_on;
+ snap_left_off = pdata->snap_left_off;
+ snap_right_on = pdata->snap_right_on;
+ snap_right_off = pdata->snap_right_off;
+ snap_top_on = pdata->snap_top_on;
+ snap_top_off = pdata->snap_top_off;
+ snap_bottom_on = pdata->snap_bottom_on;
+ snap_bottom_off = pdata->snap_bottom_off;
+ fuzz_x = pdata->fuzz_x;
+ fuzz_y = pdata->fuzz_y;
+ fuzz_p = pdata->fuzz_p;
+ fuzz_w = pdata->fuzz_w;
+ } else {
+ irqflags = 0;
+ inactive_area_left = 0;
+ inactive_area_right = 0;
+ inactive_area_top = 0;
+ inactive_area_bottom = 0;
+ snap_left_on = 0;
+ snap_left_off = 0;
+ snap_right_on = 0;
+ snap_right_off = 0;
+ snap_top_on = 0;
+ snap_top_off = 0;
+ snap_bottom_on = 0;
+ snap_bottom_off = 0;
+ fuzz_x = 0;
+ fuzz_y = 0;
+ fuzz_p = 0;
+ fuzz_w = 0;
+ }
+
+ ret = i2c_smbus_read_byte_data(ts->client, 0xf0);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: device control %x\n", ret);
+
+ ret = i2c_smbus_read_byte_data(ts->client, 0xf1);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_byte_data failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: interrupt enable %x\n", ret);
+
+ ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed\n");
+ goto err_detect_failed;
+ }
+
+ msg[0].addr = ts->client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = buf0;
+ buf0[0] = 0xe0;
+ msg[1].addr = ts->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 8;
+ msg[1].buf = buf1;
+ ret = i2c_transfer(ts->client->adapter, msg, 2);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_transfer failed\n");
+ goto err_detect_failed;
+ }
+ printk(KERN_INFO "synaptics_ts_probe: 0xe0: %x %x %x %x %x %x %x %x\n",
+ buf1[0], buf1[1], buf1[2], buf1[3],
+ buf1[4], buf1[5], buf1[6], buf1[7]);
+
+ ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n");
+ goto err_detect_failed;
+ }
+ ret = i2c_smbus_read_word_data(ts->client, 0x02);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_word_data failed\n");
+ goto err_detect_failed;
+ }
+ ts->has_relative_report = !(ret & 0x100);
+ printk(KERN_INFO "synaptics_ts_probe: Sensor properties %x\n", ret);
+ ret = i2c_smbus_read_word_data(ts->client, 0x04);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_word_data failed\n");
+ goto err_detect_failed;
+ }
+ ts->max[0] = max_x = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8);
+ ret = i2c_smbus_read_word_data(ts->client, 0x06);
+ if (ret < 0) {
+ printk(KERN_ERR "i2c_smbus_read_word_data failed\n");
+ goto err_detect_failed;
+ }
+ ts->max[1] = max_y = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8);
+ if (ts->flags & SYNAPTICS_SWAP_XY)
+ swap(max_x, max_y);
+
+ ret = synaptics_init_panel(ts); /* will also switch back to page 0x04 */
+ if (ret < 0) {
+ printk(KERN_ERR "synaptics_init_panel failed\n");
+ goto err_detect_failed;
+ }
+
+ ts->input_dev = input_allocate_device();
+ if (ts->input_dev == NULL) {
+ ret = -ENOMEM;
+ printk(KERN_ERR "synaptics_ts_probe: Failed to allocate input device\n");
+ goto err_input_dev_alloc_failed;
+ }
+ ts->input_dev->name = "synaptics-rmi-touchscreen";
+ set_bit(EV_SYN, ts->input_dev->evbit);
+ set_bit(EV_KEY, ts->input_dev->evbit);
+ set_bit(BTN_TOUCH, ts->input_dev->keybit);
+ set_bit(BTN_2, ts->input_dev->keybit);
+ set_bit(EV_ABS, ts->input_dev->evbit);
+ inactive_area_left = inactive_area_left * max_x / 0x10000;
+ inactive_area_right = inactive_area_right * max_x / 0x10000;
+ inactive_area_top = inactive_area_top * max_y / 0x10000;
+ inactive_area_bottom = inactive_area_bottom * max_y / 0x10000;
+ snap_left_on = snap_left_on * max_x / 0x10000;
+ snap_left_off = snap_left_off * max_x / 0x10000;
+ snap_right_on = snap_right_on * max_x / 0x10000;
+ snap_right_off = snap_right_off * max_x / 0x10000;
+ snap_top_on = snap_top_on * max_y / 0x10000;
+ snap_top_off = snap_top_off * max_y / 0x10000;
+ snap_bottom_on = snap_bottom_on * max_y / 0x10000;
+ snap_bottom_off = snap_bottom_off * max_y / 0x10000;
+ fuzz_x = fuzz_x * max_x / 0x10000;
+ fuzz_y = fuzz_y * max_y / 0x10000;
+ ts->snap_down[!!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_left;
+ ts->snap_up[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x + inactive_area_right;
+ ts->snap_down[!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_top;
+ ts->snap_up[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y + inactive_area_bottom;
+ ts->snap_down_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_on;
+ ts->snap_down_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_off;
+ ts->snap_up_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_on;
+ ts->snap_up_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_off;
+ ts->snap_down_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_on;
+ ts->snap_down_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_off;
+ ts->snap_up_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_on;
+ ts->snap_up_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_off;
+ printk(KERN_INFO "synaptics_ts_probe: max_x %d, max_y %d\n", max_x, max_y);
+ printk(KERN_INFO "synaptics_ts_probe: inactive_x %d %d, inactive_y %d %d\n",
+ inactive_area_left, inactive_area_right,
+ inactive_area_top, inactive_area_bottom);
+ printk(KERN_INFO "synaptics_ts_probe: snap_x %d-%d %d-%d, snap_y %d-%d %d-%d\n",
+ snap_left_on, snap_left_off, snap_right_on, snap_right_off,
+ snap_top_on, snap_top_off, snap_bottom_on, snap_bottom_off);
+ input_set_abs_params(ts->input_dev, ABS_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0);
+ input_set_abs_params(ts->input_dev, ABS_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0);
+ input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, fuzz_p, 0);
+ input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, 0, 15, fuzz_w, 0);
+ input_set_abs_params(ts->input_dev, ABS_HAT0X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0);
+ input_set_abs_params(ts->input_dev, ABS_HAT0Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, fuzz_p, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, fuzz_w, 0);
+ /* ts->input_dev->name = ts->keypad_info->name; */
+ ret = input_register_device(ts->input_dev);
+ if (ret) {
+ printk(KERN_ERR "synaptics_ts_probe: Unable to register %s input device\n", ts->input_dev->name);
+ goto err_input_register_device_failed;
+ }
+ if (client->irq) {
+ ret = request_irq(client->irq, synaptics_ts_irq_handler, irqflags, client->name, ts);
+ if (ret == 0) {
+ ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */
+ if (ret)
+ free_irq(client->irq, ts);
+ }
+ if (ret == 0)
+ ts->use_irq = 1;
+ else
+ dev_err(&client->dev, "request_irq failed\n");
+ }
+ if (!ts->use_irq) {
+ hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ts->timer.function = synaptics_ts_timer_func;
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ }
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = synaptics_ts_early_suspend;
+ ts->early_suspend.resume = synaptics_ts_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+
+ printk(KERN_INFO "synaptics_ts_probe: Start touchscreen %s in %s mode\n", ts->input_dev->name, ts->use_irq ? "interrupt" : "polling");
+
+ return 0;
+
+err_input_register_device_failed:
+ input_free_device(ts->input_dev);
+
+err_input_dev_alloc_failed:
+err_detect_failed:
+err_power_failed:
+ kfree(ts);
+err_alloc_data_failed:
+err_check_functionality_failed:
+ return ret;
+}
+
+static int synaptics_ts_remove(struct i2c_client *client)
+{
+ struct synaptics_ts_data *ts = i2c_get_clientdata(client);
+ unregister_early_suspend(&ts->early_suspend);
+ if (ts->use_irq)
+ free_irq(client->irq, ts);
+ else
+ hrtimer_cancel(&ts->timer);
+ input_unregister_device(ts->input_dev);
+ kfree(ts);
+ return 0;
+}
+
+static int synaptics_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ int ret;
+ struct synaptics_ts_data *ts = i2c_get_clientdata(client);
+
+ if (ts->use_irq)
+ disable_irq(client->irq);
+ else
+ hrtimer_cancel(&ts->timer);
+ ret = cancel_work_sync(&ts->work);
+ if (ret && ts->use_irq) /* if work was pending disable-count is now 2 */
+ enable_irq(client->irq);
+ ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */
+ if (ret < 0)
+ printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n");
+
+ ret = i2c_smbus_write_byte_data(client, 0xf0, 0x86); /* deep sleep */
+ if (ret < 0)
+ printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n");
+ if (ts->power) {
+ ret = ts->power(0);
+ if (ret < 0)
+ printk(KERN_ERR "synaptics_ts_resume power off failed\n");
+ }
+ return 0;
+}
+
+static int synaptics_ts_resume(struct i2c_client *client)
+{
+ int ret;
+ struct synaptics_ts_data *ts = i2c_get_clientdata(client);
+
+ if (ts->power) {
+ ret = ts->power(1);
+ if (ret < 0)
+ printk(KERN_ERR "synaptics_ts_resume power on failed\n");
+ }
+
+ synaptics_init_panel(ts);
+
+ if (ts->use_irq)
+ enable_irq(client->irq);
+
+ if (!ts->use_irq)
+ hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+ else
+ i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void synaptics_ts_early_suspend(struct early_suspend *h)
+{
+ struct synaptics_ts_data *ts;
+ ts = container_of(h, struct synaptics_ts_data, early_suspend);
+ synaptics_ts_suspend(ts->client, PMSG_SUSPEND);
+}
+
+static void synaptics_ts_late_resume(struct early_suspend *h)
+{
+ struct synaptics_ts_data *ts;
+ ts = container_of(h, struct synaptics_ts_data, early_suspend);
+ synaptics_ts_resume(ts->client);
+}
+#endif
+
+static const struct i2c_device_id synaptics_ts_id[] = {
+ { SYNAPTICS_I2C_RMI_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver synaptics_ts_driver = {
+ .probe = synaptics_ts_probe,
+ .remove = synaptics_ts_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = synaptics_ts_suspend,
+ .resume = synaptics_ts_resume,
+#endif
+ .id_table = synaptics_ts_id,
+ .driver = {
+ .name = SYNAPTICS_I2C_RMI_NAME,
+ },
+};
+
+static int __devinit synaptics_ts_init(void)
+{
+ synaptics_wq = create_singlethread_workqueue("synaptics_wq");
+ if (!synaptics_wq)
+ return -ENOMEM;
+ return i2c_add_driver(&synaptics_ts_driver);
+}
+
+static void __exit synaptics_ts_exit(void)
+{
+ i2c_del_driver(&synaptics_ts_driver);
+ if (synaptics_wq)
+ destroy_workqueue(synaptics_wq);
+}
+
+module_init(synaptics_ts_init);
+module_exit(synaptics_ts_exit);
+
+MODULE_DESCRIPTION("Synaptics Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 81bf25e67ce..8a7ac599a8d 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -33,6 +33,23 @@ config LEDS_ATMEL_PWM
This option enables support for LEDs driven using outputs
of the dedicated PWM controller found on newer Atmel SOCs.
+config LEDS_LM3530
+ tristate "LCD Backlight driver for LM3530"
+ depends on I2C
+ help
+ This option enables support for the LCD backlight using
+ LM3530 ambient light sensor chip. This ALS chip can be
+ controlled manually or using PWM input or using ambient
+ light automatically.
+
+config LEDS_AB5500
+ tristate "HVLED driver for AB5500"
+ depends on AB5500_CORE
+ help
+ This option enables support for the HVLED in AB5500
+ multi function device. Currently Ab5500 v1.0 chip leds
+ are supported.
+
config LEDS_LOCOMO
tristate "LED Support for Locomo device"
depends on SHARP_LOCOMO
@@ -295,6 +312,16 @@ config LEDS_DELL_NETBOOKS
This adds support for the Latitude 2100 and similar
notebooks that have an external LED.
+config LEDS_LP5521
+ tristate "LED Support for the LP5521 LEDs"
+ depends on LEDS_CLASS && I2C
+ help
+ If you say 'Y' here you get support for the National Semiconductor
+ LP5521 LED driver used in n8x0 boards.
+
+ This driver can be built as a module by choosing 'M'. The module
+ will be called leds-lp5521.
+
config LEDS_MC13783
tristate "LED Support for MC13783 PMIC"
depends on MFD_MC13783
@@ -364,6 +391,12 @@ config LEDS_TRIGGER_DEFAULT_ON
This allows LEDs to be initialised in the ON state.
If unsure, say Y.
+config LEDS_TRIGGER_SLEEP
+ tristate "LED Sleep Mode Trigger"
+ depends on LEDS_TRIGGERS && HAS_EARLYSUSPEND
+ help
+ This turns LEDs on when the screen is off but the cpu still running.
+
comment "iptables trigger is under Netfilter config (LED target)"
depends on LEDS_TRIGGERS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 2493de49937..a10b9b3a8dd 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -9,6 +9,8 @@ obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
+obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
+obj-$(CONFIG_LEDS_AB5500) += leds-ab5500.o
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
@@ -23,6 +25,7 @@ obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
+obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
@@ -48,3 +51,4 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
+obj-$(CONFIG_LEDS_TRIGGER_SLEEP) += ledtrig-sleep.o
diff --git a/drivers/leds/leds-ab5500.c b/drivers/leds/leds-ab5500.c
new file mode 100644
index 00000000000..94db3a6feea
--- /dev/null
+++ b/drivers/leds/leds-ab5500.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA.
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Driver for LED in ST-Ericsson AB5500 v1.0 Analog baseband Controller
+ *
+ * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
+ */
+
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/leds-ab5500.h>
+#include <linux/types.h>
+
+#define AB5500LED_NAME "ab5500-leds"
+
+/* Register offsets */
+#define AB5500_LED_REG_ENABLE 0x03
+#define AB5500_LED_FADE_CTL 0x0D
+
+/* LED-0 */
+#define AB5500_LED0_PWM_DUTY 0x01
+#define AB5500_LED0_PWMFREQ 0x02
+#define AB5500_LED0_SINKCTL 0x0A
+
+/* LED-1 */
+#define AB5500_LED1_PWM_DUTY 0x05
+#define AB5500_LED1_PWMFREQ 0x06
+#define AB5500_LED1_SINKCTL 0x0B
+
+/* LED-2 */
+#define AB5500_LED2_PWM_DUTY 0x08
+#define AB5500_LED2_PWMFREQ 0x09
+#define AB5500_LED2_SINKCTL 0x0C
+
+/* pwm duty cycle */
+#define AB5500_LED_PWMDUTY_OFF 0x0
+#define AB5500_LED_PWMDUTY_MAX 0x3FF
+#define AB5500_LED_PWMDUTY_STEP (AB5500_LED_PWMDUTY_MAX/LED_FULL)
+
+/* pwm frequency */
+#define AB5500_LED_PWMFREQ_MAX 0x0F /* 373.39 @sysclk=26MHz */
+#define AB5500_LED_PWMFREQ_SHIFT 4
+
+/* LED sink current control */
+#define AB5500_LED_SINKCURR_MAX 0x0F /* 40mA */
+#define AB5500_LED_SINKCURR_SHIFT 4
+
+struct ab5500_led {
+ u8 id;
+ u8 max_current;
+ u16 brt_val;
+ enum ab5500_led_status status;
+ struct led_classdev led_cdev;
+ struct work_struct led_work;
+};
+
+struct ab5500_hvleds {
+ struct mutex lock;
+ struct device *dev;
+ struct ab5500_hvleds_platform_data *pdata;
+ struct ab5500_led leds[AB5500_HVLEDS_MAX];
+};
+
+static u8 ab5500_led_pwmduty_reg[] = {
+ AB5500_LED0_PWM_DUTY,
+ AB5500_LED1_PWM_DUTY,
+ AB5500_LED2_PWM_DUTY,
+};
+
+static u8 ab5500_led_pwmfreq_reg[] = {
+ AB5500_LED0_PWMFREQ,
+ AB5500_LED1_PWMFREQ,
+ AB5500_LED2_PWMFREQ,
+};
+
+static u8 ab5500_led_sinkctl_reg[] = {
+ AB5500_LED0_SINKCTL,
+ AB5500_LED1_SINKCTL,
+ AB5500_LED2_SINKCTL
+};
+
+#define to_led(_x) container_of(_x, struct ab5500_led, _x)
+
+static inline struct ab5500_hvleds *led_to_hvleds(struct ab5500_led *led)
+{
+ return container_of(led, struct ab5500_hvleds, leds[led->id]);
+}
+
+static int ab5500_led_pwmduty_write(struct ab5500_hvleds *hvleds,
+ unsigned int led_id, u16 val)
+{
+ int ret;
+ int val_lsb = val & 0xFF;
+ int val_msb = (val & 0x300) >> 8;
+
+ mutex_lock(&hvleds->lock);
+
+ dev_dbg(hvleds->dev, "ab5500-leds: reg[%d] w val = %d\n"
+ "reg[%d] w val = %d\n",
+ ab5500_led_pwmduty_reg[led_id] - 1, val_lsb,
+ ab5500_led_pwmduty_reg[led_id], val_msb);
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmduty_reg[led_id] - 1, val_lsb);
+ ret |= abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmduty_reg[led_id], val_msb);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ ab5500_led_pwmduty_reg[led_id], ret);
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static int ab5500_led_pwmfreq_write(struct ab5500_hvleds *hvleds,
+ unsigned int led_id, u8 val)
+{
+ int ret;
+
+ val = (val & 0x0F) << AB5500_LED_PWMFREQ_SHIFT;
+
+ mutex_lock(&hvleds->lock);
+
+ dev_dbg(hvleds->dev, "ab5500-leds: reg[%d] w val=%d\n",
+ ab5500_led_pwmfreq_reg[led_id], val);
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmfreq_reg[led_id], val);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ ab5500_led_pwmfreq_reg[led_id], ret);
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static int ab5500_led_sinkctl_write(struct ab5500_hvleds *hvleds,
+ unsigned int led_id, u8 val)
+{
+ int ret;
+
+ val = (val & 0x0F) << AB5500_LED_SINKCURR_SHIFT;
+
+ mutex_lock(&hvleds->lock);
+
+ dev_dbg(hvleds->dev, "ab5500-leds: reg[%d] w val=%d\n",
+ ab5500_led_sinkctl_reg[led_id], val);
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_sinkctl_reg[led_id], val);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ ab5500_led_sinkctl_reg[led_id], ret);
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static int ab5500_led_sinkctl_read(struct ab5500_hvleds *hvleds,
+ unsigned int led_id)
+{
+ int ret;
+ u8 val;
+
+ mutex_lock(&hvleds->lock);
+ ret = abx500_get_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_sinkctl_reg[led_id], &val);
+ if (ret < 0) {
+ dev_err(hvleds->dev, "reg[%d] r failed: %d\n",
+ ab5500_led_sinkctl_reg[led_id], ret);
+ mutex_unlock(&hvleds->lock);
+ return ret;
+ }
+ val = (val & 0xF0) >> AB5500_LED_SINKCURR_SHIFT;
+ mutex_unlock(&hvleds->lock);
+
+ return val;
+}
+
+static void ab5500_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brt_val)
+{
+ struct ab5500_led *led = to_led(led_cdev);
+
+ /* adjust LED_FULL to 10bit range */
+ brt_val &= LED_FULL;
+ led->brt_val = brt_val * AB5500_LED_PWMDUTY_STEP;
+ schedule_work(&led->led_work);
+}
+
+static void ab5500_led_work(struct work_struct *led_work)
+{
+ struct ab5500_led *led = to_led(led_work);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ if (led->status == AB5500_LED_ON)
+ ab5500_led_pwmduty_write(hvleds, led->id, led->brt_val);
+}
+
+static ssize_t ab5500_led_show_current(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int led_curr = 0;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ab5500_led *led = to_led(led_cdev);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ led_curr = ab5500_led_sinkctl_read(hvleds, led->id);
+
+ if (led_curr < 0)
+ return led_curr;
+
+ return sprintf(buf, "%d\n", led_curr);
+}
+
+static ssize_t ab5500_led_store_current(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ int ret;
+ unsigned long led_curr;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ab5500_led *led = to_led(led_cdev);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ if (strict_strtoul(buf, 0, &led_curr))
+ return -EINVAL;
+
+ if (led_curr > led->max_current)
+ led_curr = led->max_current;
+
+ ret = ab5500_led_sinkctl_write(hvleds, led->id, led_curr);
+ if (ret < 0)
+ return ret;
+
+ return len;
+}
+
+/* led class device attributes */
+static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO,
+ ab5500_led_show_current, ab5500_led_store_current);
+
+static int ab5500_led_init_registers(struct ab5500_hvleds *hvleds)
+{
+ int ret = 0;
+ unsigned int led_id;
+
+ /* fade - manual : dur mid : pwm duty mid */
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ AB5500_LED_REG_ENABLE, true);
+ if (ret < 0) {
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ AB5500_LED_REG_ENABLE, ret);
+ return ret;
+ }
+
+ for (led_id = 0; led_id < AB5500_HVLEDS_MAX; led_id++) {
+ /* Set pwm freq. and sink current to mid values */
+ ret = ab5500_led_pwmfreq_write(
+ hvleds, led_id, AB5500_LED_PWMFREQ_MAX);
+ if (ret < 0)
+ return ret;
+
+ ret = ab5500_led_sinkctl_write(
+ hvleds, led_id, AB5500_LED_SINKCURR_MAX);
+ if (ret < 0)
+ return ret;
+
+ /* init led off */
+ ret = ab5500_led_pwmduty_write(
+ hvleds, led_id, AB5500_LED_PWMDUTY_OFF);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int ab5500_led_register_leds(struct device *dev,
+ struct ab5500_hvleds_platform_data *pdata,
+ struct ab5500_hvleds *hvleds)
+{
+ int i_led;
+ int err;
+ struct ab5500_led_conf *pled;
+ struct ab5500_led *led;
+
+ hvleds->dev = dev;
+ hvleds->pdata = pdata;
+ for (i_led = 0; i_led < AB5500_HVLEDS_MAX; i_led++) {
+ pled = &pdata->leds[i_led];
+ led = &hvleds->leds[i_led];
+
+ INIT_WORK(&led->led_work, ab5500_led_work);
+
+ led->id = pled->led_id;
+ led->max_current = pled->max_current;
+ led->status = pled->status;
+ led->led_cdev.name = pled->name;
+ led->led_cdev.brightness_set = ab5500_led_brightness_set;
+
+ err = led_classdev_register(dev, &led->led_cdev);
+ if (err < 0) {
+ dev_err(dev, "Register led class failed: %d\n", err);
+ goto bailout1;
+ }
+
+ err = device_create_file(led->led_cdev.dev,
+ &dev_attr_led_current);
+ if (err < 0) {
+ dev_err(dev, "sysfs device creation failed: %d\n", err);
+ goto bailout2;
+ }
+ }
+
+ return err;
+ for (; i_led >= 0; i_led--) {
+ device_remove_file(led->led_cdev.dev, &dev_attr_led_current);
+bailout2:
+ led_classdev_unregister(&hvleds->leds[i_led].led_cdev);
+bailout1:
+ cancel_work_sync(&hvleds->leds[i_led].led_work);
+ }
+ return err;
+}
+
+static int __devinit ab5500_hvleds_probe(struct platform_device *pdev)
+{
+ struct ab5500_hvleds_platform_data *pdata = pdev->dev.platform_data;
+ struct ab5500_hvleds *hvleds = NULL;
+ int err = 0, i;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "platform data required\n");
+ err = -ENODEV;
+ goto err_out;
+ }
+
+ hvleds = kzalloc(sizeof(struct ab5500_hvleds), GFP_KERNEL);
+ if (hvleds == NULL) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ mutex_init(&hvleds->lock);
+
+ /* init leds data and register led_classdev */
+ err = ab5500_led_register_leds(&pdev->dev, pdata, hvleds);
+ if (err < 0) {
+ dev_err(&pdev->dev, "leds registeration failed\n");
+ goto err_out;
+ }
+
+ /* init device registers and set initial led current */
+ err = ab5500_led_init_registers(hvleds);
+ if (err < 0) {
+ dev_err(&pdev->dev, "reg init failed: %d\n", err);
+ goto err_reg_init;
+ }
+
+ dev_info(&pdev->dev, "enabled\n");
+
+ return err;
+
+err_reg_init:
+ for (i = 0; i < AB5500_HVLEDS_MAX; i++) {
+ struct ab5500_led *led = &hvleds->leds[i];
+
+ led_classdev_unregister(&led->led_cdev);
+ device_remove_file(led->led_cdev.dev, &dev_attr_led_current);
+ cancel_work_sync(&led->led_work);
+ }
+err_out:
+ kfree(hvleds);
+ return err;
+}
+
+static int __devexit ab5500_hvleds_remove(struct platform_device *pdev)
+{
+ struct ab5500_hvleds *hvleds = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < AB5500_HVLEDS_MAX; i++) {
+ struct ab5500_led *led = &hvleds->leds[i];
+
+ led_classdev_unregister(&led->led_cdev);
+ device_remove_file(led->led_cdev.dev, &dev_attr_led_current);
+ cancel_work_sync(&led->led_work);
+ }
+ kfree(hvleds);
+ return 0;
+}
+
+static struct platform_driver ab5500_hvleds_driver = {
+ .driver = {
+ .name = AB5500LED_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ab5500_hvleds_probe,
+ .remove = __devexit_p(ab5500_hvleds_remove),
+};
+
+static int __init ab5500_hvleds_module_init(void)
+{
+ return platform_driver_register(&ab5500_hvleds_driver);
+}
+
+static void __exit ab5500_hvleds_module_exit(void)
+{
+ platform_driver_unregister(&ab5500_hvleds_driver);
+}
+
+module_init(ab5500_hvleds_module_init);
+module_exit(ab5500_hvleds_module_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>");
+MODULE_DESCRIPTION("Driver for AB5500 HVLED");
+
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
new file mode 100644
index 00000000000..01ba7395cd1
--- /dev/null
+++ b/drivers/leds/leds-lm3530.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA.
+ * Copyright (C) 2009 Motorola, Inc.
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Simple driver for National Semiconductor LM3530 Backlight driver chip
+ *
+ * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
+ * based on leds-lm3530.c by Dan Murphy <D.Murphy@motorola.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/led-lm3530.h>
+#include <linux/types.h>
+#include <linux/regulator/consumer.h>
+
+#define LM3530_LED_DEV "lcd-backlight"
+#define LM3530_NAME "lm3530-led"
+
+#define LM3530_GEN_CONFIG 0x10
+#define LM3530_ALS_CONFIG 0x20
+#define LM3530_BRT_RAMP_RATE 0x30
+#define LM3530_ALS_ZONE_REG 0x40
+#define LM3530_ALS_IMP_SELECT 0x41
+#define LM3530_BRT_CTRL_REG 0xA0
+#define LM3530_ALS_ZB0_REG 0x60
+#define LM3530_ALS_ZB1_REG 0x61
+#define LM3530_ALS_ZB2_REG 0x62
+#define LM3530_ALS_ZB3_REG 0x63
+#define LM3530_ALS_Z0T_REG 0x70
+#define LM3530_ALS_Z1T_REG 0x71
+#define LM3530_ALS_Z2T_REG 0x72
+#define LM3530_ALS_Z3T_REG 0x73
+#define LM3530_ALS_Z4T_REG 0x74
+#define LM3530_REG_MAX 15
+
+/* General Control Register */
+#define LM3530_EN_I2C_SHIFT (0)
+#define LM3530_RAMP_LAW_SHIFT (1)
+#define LM3530_MAX_CURR_SHIFT (2)
+#define LM3530_EN_PWM_SHIFT (5)
+#define LM3530_PWM_POL_SHIFT (6)
+#define LM3530_EN_PWM_SIMPLE_SHIFT (7)
+
+#define LM3530_ENABLE_I2C (1 << LM3530_EN_I2C_SHIFT)
+#define LM3530_ENABLE_PWM (1 << LM3530_EN_PWM_SHIFT)
+#define LM3530_POL_LOW (1 << LM3530_PWM_POL_SHIFT)
+#define LM3530_ENABLE_PWM_SIMPLE (1 << LM3530_EN_PWM_SIMPLE_SHIFT)
+
+/* ALS Config Register Options */
+#define LM3530_ALS_AVG_TIME_SHIFT (0)
+#define LM3530_EN_ALS_SHIFT (3)
+#define LM3530_ALS_SEL_SHIFT (5)
+
+#define LM3530_ENABLE_ALS (3 << LM3530_EN_ALS_SHIFT)
+
+/* Brightness Ramp Rate Register */
+#define LM3530_BRT_RAMP_FALL_SHIFT (0)
+#define LM3530_BRT_RAMP_RISE_SHIFT (3)
+
+/* ALS Resistor Select */
+#define LM3530_ALS1_IMP_SHIFT (0)
+#define LM3530_ALS2_IMP_SHIFT (4)
+
+/* Zone Boundary Register defaults */
+#define LM3530_DEF_ZB_0 (0x33)
+#define LM3530_DEF_ZB_1 (0x66)
+#define LM3530_DEF_ZB_2 (0x99)
+#define LM3530_DEF_ZB_3 (0xCC)
+
+/* Zone Target Register defaults */
+#define LM3530_DEF_ZT_0 (0x19)
+#define LM3530_DEF_ZT_1 (0x33)
+#define LM3530_DEF_ZT_2 (0x4C)
+#define LM3530_DEF_ZT_3 (0x66)
+#define LM3530_DEF_ZT_4 (0x7F)
+
+struct lm3530_mode_map {
+ const char *mode;
+ enum lm3530_mode mode_val;
+};
+
+static struct lm3530_mode_map mode_map[] = {
+ { "man", LM3530_BL_MODE_MANUAL },
+ { "als", LM3530_BL_MODE_ALS },
+ { "pwm", LM3530_BL_MODE_PWM },
+};
+
+/**
+ * struct lm3530_data
+ * @led_dev: led class device
+ * @client: i2c client
+ * @pdata: LM3530 platform data
+ * @mode: mode of operation - manual, ALS, PWM
+ * @regulator: regulator
+ * @enable: regulator is enabled
+ */
+struct lm3530_data {
+ struct led_classdev led_dev;
+ struct i2c_client *client;
+ struct lm3530_platform_data *pdata;
+ enum lm3530_mode mode;
+ struct regulator *regulator;
+ bool enable;
+};
+
+static const u8 lm3530_reg[LM3530_REG_MAX] = {
+ LM3530_GEN_CONFIG,
+ LM3530_ALS_CONFIG,
+ LM3530_BRT_RAMP_RATE,
+ LM3530_ALS_ZONE_REG,
+ LM3530_ALS_IMP_SELECT,
+ LM3530_BRT_CTRL_REG,
+ LM3530_ALS_ZB0_REG,
+ LM3530_ALS_ZB1_REG,
+ LM3530_ALS_ZB2_REG,
+ LM3530_ALS_ZB3_REG,
+ LM3530_ALS_Z0T_REG,
+ LM3530_ALS_Z1T_REG,
+ LM3530_ALS_Z2T_REG,
+ LM3530_ALS_Z3T_REG,
+ LM3530_ALS_Z4T_REG,
+};
+
+static int lm3530_get_mode_from_str(const char *str)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mode_map); i++)
+ if (sysfs_streq(str, mode_map[i].mode))
+ return mode_map[i].mode_val;
+
+ return -1;
+}
+
+static int lm3530_init_registers(struct lm3530_data *drvdata)
+{
+ int ret = 0;
+ int i;
+ u8 gen_config;
+ u8 als_config = 0;
+ u8 brt_ramp;
+ u8 als_imp_sel = 0;
+ u8 brightness;
+ u8 reg_val[LM3530_REG_MAX];
+ struct lm3530_platform_data *pltfm = drvdata->pdata;
+ struct i2c_client *client = drvdata->client;
+
+ gen_config = (pltfm->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
+ ((pltfm->max_current & 7) << LM3530_MAX_CURR_SHIFT);
+
+ if (drvdata->mode == LM3530_BL_MODE_MANUAL ||
+ drvdata->mode == LM3530_BL_MODE_ALS)
+ gen_config |= (LM3530_ENABLE_I2C);
+
+ if (drvdata->mode == LM3530_BL_MODE_ALS) {
+ als_config =
+ (pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
+ (LM3530_ENABLE_ALS) |
+ (pltfm->als_input_mode << LM3530_ALS_SEL_SHIFT);
+
+ als_imp_sel =
+ (pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
+ (pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
+ }
+
+ if (drvdata->mode == LM3530_BL_MODE_PWM)
+ gen_config |= (LM3530_ENABLE_PWM) |
+ (pltfm->pwm_pol_hi << LM3530_PWM_POL_SHIFT) |
+ (LM3530_ENABLE_PWM_SIMPLE);
+
+ brt_ramp = (pltfm->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
+ (pltfm->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
+
+ brightness = pltfm->brt_val;
+
+ reg_val[0] = gen_config; /* LM3530_GEN_CONFIG */
+ reg_val[1] = als_config; /* LM3530_ALS_CONFIG */
+ reg_val[2] = brt_ramp; /* LM3530_BRT_RAMP_RATE */
+ reg_val[3] = 0x00; /* LM3530_ALS_ZONE_REG */
+ reg_val[4] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */
+ reg_val[5] = brightness; /* LM3530_BRT_CTRL_REG */
+ reg_val[6] = LM3530_DEF_ZB_0; /* LM3530_ALS_ZB0_REG */
+ reg_val[7] = LM3530_DEF_ZB_1; /* LM3530_ALS_ZB1_REG */
+ reg_val[8] = LM3530_DEF_ZB_2; /* LM3530_ALS_ZB2_REG */
+ reg_val[9] = LM3530_DEF_ZB_3; /* LM3530_ALS_ZB3_REG */
+ reg_val[10] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */
+ reg_val[11] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */
+ reg_val[12] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */
+ reg_val[13] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */
+ reg_val[14] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */
+
+ for (i = 0; i < LM3530_REG_MAX; i++) {
+ ret = i2c_smbus_write_byte_data(client,
+ lm3530_reg[i], reg_val[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static void lm3530_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brt_val)
+{
+ int err;
+ struct lm3530_data *drvdata =
+ container_of(led_cdev, struct lm3530_data, led_dev);
+
+ switch (drvdata->mode) {
+ case LM3530_BL_MODE_MANUAL:
+
+ if (!drvdata->enable) {
+ err = regulator_enable(drvdata->regulator);
+ if (err) {
+ dev_err(&drvdata->client->dev,
+ "Unable to enable regulator\n");
+ break;
+ }
+ drvdata->enable = true;
+ lm3530_init_registers(drvdata);
+ if (err) {
+ dev_err(&drvdata->client->dev,
+ "Register Init failed: %d\n", err);
+ break;
+ }
+ }
+
+ /* set the brightness in brightness control register*/
+ err = i2c_smbus_write_byte_data(drvdata->client,
+ LM3530_BRT_CTRL_REG, brt_val / 2);
+ if (err)
+ dev_err(&drvdata->client->dev,
+ "Unable to set brightness: %d\n", err);
+
+ if (brt_val == 0 && drvdata->enable) {
+ err = regulator_disable(drvdata->regulator);
+ if (err)
+ dev_err(&drvdata->client->dev,
+ "Unable to disable regulator\n");
+ drvdata->enable = false;
+ }
+ break;
+ case LM3530_BL_MODE_ALS:
+ break;
+ case LM3530_BL_MODE_PWM:
+ break;
+ default:
+ break;
+ }
+}
+
+
+static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
+ *attr, const char *buf, size_t size)
+{
+ int err;
+ struct i2c_client *client = container_of(
+ dev->parent, struct i2c_client, dev);
+ struct lm3530_data *drvdata = i2c_get_clientdata(client);
+ int mode;
+
+ mode = lm3530_get_mode_from_str(buf);
+ if (mode < 0) {
+ dev_err(dev, "Invalid mode\n");
+ return -EINVAL;
+ }
+
+ if (mode == LM3530_BL_MODE_MANUAL)
+ drvdata->mode = LM3530_BL_MODE_MANUAL;
+ else if (mode == LM3530_BL_MODE_ALS)
+ drvdata->mode = LM3530_BL_MODE_ALS;
+ else if (mode == LM3530_BL_MODE_PWM) {
+ dev_err(dev, "PWM mode not supported\n");
+ return -EINVAL;
+ }
+
+ err = lm3530_init_registers(drvdata);
+ if (err) {
+ dev_err(dev, "Setting %s Mode failed :%d\n", buf, err);
+ return err;
+ }
+
+ return sizeof(drvdata->mode);
+}
+
+static DEVICE_ATTR(mode, 0644, NULL, lm3530_mode_set);
+
+static int __devinit lm3530_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lm3530_platform_data *pdata = client->dev.platform_data;
+ struct lm3530_data *drvdata;
+ int err = 0;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "platform data required\n");
+ err = -ENODEV;
+ goto err_out;
+ }
+
+ /* BL mode */
+ if (pdata->mode > LM3530_BL_MODE_PWM) {
+ dev_err(&client->dev, "Illegal Mode request\n");
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C_FUNC_I2C not supported\n");
+ err = -EIO;
+ goto err_out;
+ }
+
+ drvdata = kzalloc(sizeof(struct lm3530_data), GFP_KERNEL);
+ if (drvdata == NULL) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ drvdata->mode = pdata->mode;
+ drvdata->client = client;
+ drvdata->pdata = pdata;
+ drvdata->enable = false;
+ drvdata->led_dev.name = LM3530_LED_DEV;
+ drvdata->led_dev.brightness_set = lm3530_brightness_set;
+
+ i2c_set_clientdata(client, drvdata);
+
+ drvdata->regulator = regulator_get(&client->dev, "vin");
+ if (IS_ERR(drvdata->regulator)) {
+ dev_err(&client->dev, "regulator get failed\n");
+ err = PTR_ERR(drvdata->regulator);
+ drvdata->regulator = NULL;
+ goto err_regulator_get;
+ }
+
+ err = lm3530_init_registers(drvdata);
+ if (err < 0) {
+ dev_err(&client->dev, "Register Init failed: %d\n", err);
+ err = -ENODEV;
+ goto err_reg_init;
+ }
+
+ err = led_classdev_register((struct device *)
+ &client->dev, &drvdata->led_dev);
+ if (err < 0) {
+ dev_err(&client->dev, "Register led class failed: %d\n", err);
+ err = -ENODEV;
+ goto err_class_register;
+ }
+
+ err = device_create_file(drvdata->led_dev.dev, &dev_attr_mode);
+ if (err < 0) {
+ dev_err(&client->dev, "File device creation failed: %d\n", err);
+ err = -ENODEV;
+ goto err_create_file;
+ }
+
+ return 0;
+
+err_create_file:
+ regulator_put(drvdata->regulator);
+err_regulator_get:
+ led_classdev_unregister(&drvdata->led_dev);
+err_class_register:
+err_reg_init:
+ kfree(drvdata);
+err_out:
+ return err;
+}
+
+static int __devexit lm3530_remove(struct i2c_client *client)
+{
+ struct lm3530_data *drvdata = i2c_get_clientdata(client);
+
+ device_remove_file(drvdata->led_dev.dev, &dev_attr_mode);
+
+ if (drvdata->enable)
+ regulator_disable(drvdata->regulator);
+ regulator_put(drvdata->regulator);
+ led_classdev_unregister(&drvdata->led_dev);
+ kfree(drvdata);
+ return 0;
+}
+
+static const struct i2c_device_id lm3530_id[] = {
+ {LM3530_NAME, 0},
+ {}
+};
+
+static struct i2c_driver lm3530_i2c_driver = {
+ .probe = lm3530_probe,
+ .remove = lm3530_remove,
+ .id_table = lm3530_id,
+ .driver = {
+ .name = LM3530_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init lm3530_init(void)
+{
+ return i2c_add_driver(&lm3530_i2c_driver);
+}
+
+static void __exit lm3530_exit(void)
+{
+ i2c_del_driver(&lm3530_i2c_driver);
+}
+
+module_init(lm3530_init);
+module_exit(lm3530_exit);
+
+MODULE_DESCRIPTION("Back Light driver for LM3530");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>");
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
new file mode 100644
index 00000000000..8f412c6b2f0
--- /dev/null
+++ b/drivers/leds/leds-lp5521.c
@@ -0,0 +1,751 @@
+/*
+ * lp5521.c - LP5521 LED Driver
+ *
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Written by Mathias Nyman <mathias.nyman@xxxxxxxxx>
+ * Updated by Felipe Balbi <felipe.balbi@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/i2c/lp5521.h>
+
+#define LP5521_DRIVER_NAME "lp5521"
+
+#define LP5521_REG_R_PWM 0x02
+#define LP5521_REG_B_PWM 0x04
+#define LP5521_REG_ENABLE 0x00
+#define LP5521_REG_OP_MODE 0x01
+#define LP5521_REG_G_PWM 0x03
+#define LP5521_REG_R_CNTRL 0x05
+#define LP5521_REG_G_CNTRL 0x06
+#define LP5521_REG_B_CNTRL 0x07
+#define LP5521_REG_MISC 0x08
+#define LP5521_REG_R_CHANNEL_PC 0x09
+#define LP5521_REG_G_CHANNEL_PC 0x0a
+#define LP5521_REG_B_CHANNEL_PC 0x0b
+#define LP5521_REG_STATUS 0x0c
+#define LP5521_REG_RESET 0x0d
+#define LP5521_REG_GPO 0x0e
+#define LP5521_REG_R_PROG_MEM 0x10
+#define LP5521_REG_G_PROG_MEM 0x30
+#define LP5521_REG_B_PROG_MEM 0x50
+
+#define LP5521_CURRENT_1m5 0x0f
+#define LP5521_CURRENT_3m1 0x1f
+#define LP5521_CURRENT_4m7 0x2f
+#define LP5521_CURRENT_6m3 0x3f
+#define LP5521_CURRENT_7m9 0x4f
+#define LP5521_CURRENT_9m5 0x5f
+#define LP5521_CURRENT_11m1 0x6f
+#define LP5521_CURRENT_12m7 0x7f
+#define LP5521_CURRENT_14m3 0x8f
+#define LP5521_CURRENT_15m9 0x9f
+#define LP5521_CURRENT_17m5 0xaf
+#define LP5521_CURRENT_19m1 0xbf
+#define LP5521_CURRENT_20m7 0xcf
+#define LP5521_CURRENT_22m3 0xdf
+#define LP5521_CURRENT_23m9 0xef
+#define LP5521_CURRENT_25m5 0xff
+
+#define LP5521_PROGRAM_LENGTH 32 /* in bytes */
+
+struct lp5521_chip {
+ /* device lock */
+ struct mutex lock;
+ struct i2c_client *client;
+
+ struct work_struct red_work;
+ struct work_struct green_work;
+ struct work_struct blue_work;
+
+ struct led_classdev ledr;
+ struct led_classdev ledg;
+ struct led_classdev ledb;
+
+ enum lp5521_mode mode;
+
+ int red;
+ int green;
+ int blue;
+};
+
+static int lp5521_set_mode(struct lp5521_chip *chip,
+ enum lp5521_mode mode);
+
+static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int lp5521_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int lp5521_configure(struct i2c_client *client)
+{
+ int ret = 0;
+
+ /* Enable chip and set light to logarithmic mode */
+ ret |= lp5521_write(client, LP5521_REG_ENABLE, 0xc0);
+ if (ret < 0)
+ return ret;
+
+ /* setting all color pwms to direct control mode */
+ ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ /* setting current to 4.7 mA for all channels */
+ ret |= lp5521_write(client, LP5521_REG_R_CNTRL, LP5521_CURRENT_4m7);
+ if (ret < 0)
+ return ret;
+ ret |= lp5521_write(client, LP5521_REG_G_CNTRL, LP5521_CURRENT_4m7);
+ if (ret < 0)
+ return ret;
+ ret |= lp5521_write(client, LP5521_REG_B_CNTRL, LP5521_CURRENT_4m7);
+ if (ret < 0)
+ return ret;
+
+ /* Enable auto-powersave, set charge pump to auto, red to battery */
+ ret |= lp5521_write(client, LP5521_REG_MISC, 0x3c);
+ if (ret < 0)
+ return ret;
+
+ /* initialize all channels pwm to zero */
+ ret |= lp5521_write(client, LP5521_REG_R_PWM, 0);
+ if (ret < 0)
+ return ret;
+ ret |= lp5521_write(client, LP5521_REG_G_PWM, 0);
+ if (ret < 0)
+ return ret;
+ ret |= lp5521_write(client, LP5521_REG_B_PWM, 0);
+
+ /* Not much can be done about errors at this point */
+ return ret;
+}
+
+static int lp5521_load_program(struct lp5521_chip *chip, u8 * pattern)
+{
+ struct i2c_client *client = chip->client;
+ int ret = 0;
+
+ /* Enter load program mode for all led channels */
+ ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x15); /* 0001 0101 */
+ if (ret)
+ return ret;
+
+ if (chip->red)
+ ret |= i2c_smbus_write_i2c_block_data(client,
+ LP5521_REG_R_PROG_MEM,
+ LP5521_PROGRAM_LENGTH,
+ pattern);
+ if (chip->green)
+ ret |= i2c_smbus_write_i2c_block_data(client,
+ LP5521_REG_G_PROG_MEM,
+ LP5521_PROGRAM_LENGTH,
+ pattern);
+ if (chip->blue)
+ ret |= i2c_smbus_write_i2c_block_data(client,
+ LP5521_REG_B_PROG_MEM,
+ LP5521_PROGRAM_LENGTH,
+ pattern);
+
+ return ret;
+}
+
+static int lp5521_run_program(struct lp5521_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ int reg;
+ u8 mask = 0xc0;
+ u8 exec_state = 0;
+
+ reg = lp5521_read(client, LP5521_REG_ENABLE);
+ if (reg < 0)
+ return reg;
+
+ reg &= mask;
+
+ /* set all active channels exec state to countinous run */
+ exec_state |= (chip->red << 5);
+ exec_state |= (chip->green << 3);
+ exec_state |= (chip->blue << 1);
+
+ reg |= exec_state;
+
+ if (lp5521_write(client, LP5521_REG_ENABLE, reg))
+ dev_dbg(&client->dev, "failed writing to register %02x\n",
+ LP5521_REG_ENABLE);
+
+ /* set op-mode to run for active channels, disabled for others */
+ if (lp5521_write(client, LP5521_REG_OP_MODE, exec_state))
+ dev_dbg(&client->dev, "failed writing to register %02x\n",
+ LP5521_REG_OP_MODE);
+
+ return 0;
+}
+
+/*--------------------------------------------------------------*/
+/* Sysfs interface */
+/*--------------------------------------------------------------*/
+
+static ssize_t show_active_channels(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+ char channels[4];
+ int pos = 0;
+
+ if (chip->red)
+ pos += sprintf(channels + pos, "r");
+ if (chip->green)
+ pos += sprintf(channels + pos, "g");
+ if (chip->blue)
+ pos += sprintf(channels + pos, "b");
+
+ channels[pos] = '\0';
+
+ return sprintf(buf, "%s\n", channels);
+}
+
+static ssize_t store_active_channels(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+
+ chip->red = 0;
+ chip->green = 0;
+ chip->blue = 0;
+
+ if (strchr(buf, 'r') != NULL)
+ chip->red = 1;
+ if (strchr(buf, 'b') != NULL)
+ chip->blue = 1;
+ if (strchr(buf, 'g') != NULL)
+ chip->green = 1;
+
+ return len;
+}
+
+static ssize_t show_color(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int r, g, b;
+
+ r = lp5521_read(client, LP5521_REG_R_PWM);
+ g = lp5521_read(client, LP5521_REG_G_PWM);
+ b = lp5521_read(client, LP5521_REG_B_PWM);
+
+ if (r < 0 || g < 0 || b < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%.2x:%.2x:%.2x\n", r, g, b);
+}
+
+static ssize_t store_color(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+ int ret;
+ unsigned r, g, b;
+
+
+ ret = sscanf(buf, "%2x:%2x:%2x", &r, &g, &b);
+ if (ret != 3)
+ return -EINVAL;
+
+ mutex_lock(&chip->lock);
+
+ ret = lp5521_write(client, LP5521_REG_R_PWM, (u8) r);
+ ret = lp5521_write(client, LP5521_REG_G_PWM, (u8) g);
+ ret = lp5521_write(client, LP5521_REG_B_PWM, (u8) b);
+
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+
+static ssize_t store_load(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+ int ret, nrchars, offset = 0, i = 0;
+ char c[3];
+ unsigned cmd;
+ u8 pattern[LP5521_PROGRAM_LENGTH] = { 0 };
+
+ while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) {
+
+ /* separate sscanfs because length is working only for %s */
+ ret = sscanf(buf + offset, "%2s%n ", c, &nrchars);
+ ret = sscanf(c, "%2x", &cmd);
+ if (ret != 1)
+ goto fail;
+ pattern[i] = (u8) cmd;
+
+ offset += nrchars;
+ i++;
+ }
+
+ /* pattern commands are always two bytes long */
+ if (i % 2)
+ goto fail;
+
+ mutex_lock(&chip->lock);
+
+ ret = lp5521_load_program(chip, pattern);
+ mutex_unlock(&chip->lock);
+
+ if (ret) {
+ dev_err(dev, "lp5521 failed loading pattern\n");
+ return ret;
+ }
+
+ return len;
+fail:
+ dev_err(dev, "lp5521 wrong pattern format\n");
+ return -EINVAL;
+}
+
+static ssize_t show_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+ char *mode;
+
+ mutex_lock(&chip->lock);
+ switch (chip->mode) {
+ case LP5521_MODE_RUN:
+ mode = "run";
+ break;
+ case LP5521_MODE_LOAD:
+ mode = "load";
+ break;
+ case LP5521_MODE_DIRECT_CONTROL:
+ mode = "direct";
+ break;
+ default:
+ mode = "undefined";
+ }
+ mutex_unlock(&chip->lock);
+
+ return sprintf(buf, "%s\n", mode);
+}
+
+static ssize_t store_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+
+ mutex_lock(&chip->lock);
+
+ if (sysfs_streq(buf, "run"))
+ lp5521_set_mode(chip, LP5521_MODE_RUN);
+ else if (sysfs_streq(buf, "load"))
+ lp5521_set_mode(chip, LP5521_MODE_LOAD);
+ else if (sysfs_streq(buf, "direct"))
+ lp5521_set_mode(chip, LP5521_MODE_DIRECT_CONTROL);
+ else
+ len = -EINVAL;
+
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+
+static ssize_t show_current(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int r, g, b;
+
+ r = lp5521_read(client, LP5521_REG_R_CNTRL);
+ g = lp5521_read(client, LP5521_REG_G_CNTRL);
+ b = lp5521_read(client, LP5521_REG_B_CNTRL);
+
+ if (r < 0 || g < 0 || b < 0)
+ return -EINVAL;
+
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+
+ return sprintf(buf, "%x %x %x\n", r, g, b);
+}
+
+static ssize_t store_current(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+ struct i2c_client *client = chip->client;
+ int ret;
+ unsigned curr;
+
+ ret = sscanf(buf, "%1x", &curr);
+ if (ret != 1)
+ return -EINVAL;
+
+ /* current level is determined by the 4 upper bits, rest is ones */
+ curr = (curr << 4) | 0x0f;
+
+ mutex_lock(&chip->lock);
+
+ ret |= lp5521_write(client, LP5521_REG_R_CNTRL, (u8) curr);
+ ret |= lp5521_write(client, LP5521_REG_G_CNTRL, (u8) curr);
+ ret |= lp5521_write(client, LP5521_REG_B_CNTRL, (u8) curr);
+
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+
+static DEVICE_ATTR(color, S_IRUGO | S_IWUGO, show_color, store_color);
+static DEVICE_ATTR(load, S_IWUGO, NULL, store_load);
+static DEVICE_ATTR(mode, S_IRUGO | S_IWUGO, show_mode, store_mode);
+static DEVICE_ATTR(active_channels, S_IRUGO | S_IWUGO,
+ show_active_channels, store_active_channels);
+static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current,
+ store_current);
+
+static int lp5521_register_sysfs(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int ret;
+
+ ret = device_create_file(dev, &dev_attr_color);
+ if (ret)
+ goto fail1;
+ ret = device_create_file(dev, &dev_attr_load);
+ if (ret)
+ goto fail2;
+ ret = device_create_file(dev, &dev_attr_active_channels);
+ if (ret)
+ goto fail3;
+ ret = device_create_file(dev, &dev_attr_mode);
+ if (ret)
+ goto fail4;
+ ret = device_create_file(dev, &dev_attr_led_current);
+ if (ret)
+ goto fail5;
+
+ return 0;
+
+fail5:
+ device_remove_file(dev, &dev_attr_mode);
+fail4:
+ device_remove_file(dev, &dev_attr_active_channels);
+fail3:
+ device_remove_file(dev, &dev_attr_load);
+fail2:
+ device_remove_file(dev, &dev_attr_color);
+fail1:
+ return ret;
+}
+
+static void lp5521_unregister_sysfs(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+
+ device_remove_file(dev, &dev_attr_led_current);
+ device_remove_file(dev, &dev_attr_mode);
+ device_remove_file(dev, &dev_attr_active_channels);
+ device_remove_file(dev, &dev_attr_color);
+ device_remove_file(dev, &dev_attr_load);
+}
+
+/*--------------------------------------------------------------*/
+/* Set chip operating mode */
+/*--------------------------------------------------------------*/
+
+static int lp5521_set_mode(struct lp5521_chip *chip, enum lp5521_mode mode)
+{
+ struct i2c_client *client = chip->client;
+ int ret = 0;
+
+ /* if in that mode already do nothing, except for run */
+ if (chip->mode == mode && mode != LP5521_MODE_RUN)
+ return 0;
+
+ switch (mode) {
+ case LP5521_MODE_RUN:
+ ret = lp5521_run_program(chip);
+ break;
+ case LP5521_MODE_LOAD:
+ ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x15);
+ break;
+ case LP5521_MODE_DIRECT_CONTROL:
+ ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x3F);
+ break;
+ default:
+ dev_dbg(&client->dev, "unsupported mode %d\n", mode);
+ }
+
+ chip->mode = mode;
+
+ return ret;
+}
+
+static void lp5521_red_work(struct work_struct *work)
+{
+ struct lp5521_chip *chip =
+ container_of(work, struct lp5521_chip, red_work);
+ int ret;
+
+ ret = lp5521_configure(chip->client);
+ if (ret) {
+ dev_dbg(&chip->client->dev,
+ "could not configure lp5521, %d\n", ret);
+ return;
+ }
+
+ ret = lp5521_write(chip->client, LP5521_REG_R_PWM, chip->red);
+ if (ret)
+ dev_dbg(&chip->client->dev,
+ "could not set brightness, %d\n", ret);
+}
+
+static void lp5521_red_set(struct led_classdev *led,
+ enum led_brightness value)
+{
+ struct lp5521_chip *chip =
+ container_of(led, struct lp5521_chip, ledr);
+
+ chip->red = value;
+ schedule_work(&chip->red_work);
+}
+
+static void lp5521_green_work(struct work_struct *work)
+{
+ struct lp5521_chip *chip =
+ container_of(work, struct lp5521_chip, green_work);
+ int ret;
+
+ ret = lp5521_configure(chip->client);
+ if (ret) {
+ dev_dbg(&chip->client->dev,
+ "could not configure lp5521, %d\n", ret);
+ return;
+ }
+
+ ret = lp5521_write(chip->client, LP5521_REG_G_PWM, chip->green);
+ if (ret)
+ dev_dbg(&chip->client->dev,
+ "could not set brightness, %d\n", ret);
+}
+
+static void lp5521_green_set(struct led_classdev *led,
+ enum led_brightness value)
+{
+ struct lp5521_chip *chip =
+ container_of(led, struct lp5521_chip, ledg);
+
+ chip->green = value;
+ schedule_work(&chip->green_work);
+}
+
+static void lp5521_blue_work(struct work_struct *work)
+{
+ struct lp5521_chip *chip =
+ container_of(work, struct lp5521_chip, blue_work);
+ int ret;
+
+ ret = lp5521_configure(chip->client);
+ if (ret) {
+ dev_dbg(&chip->client->dev,
+ "could not configure lp5521, %d\n", ret);
+ return;
+ }
+
+ ret = lp5521_write(chip->client, LP5521_REG_B_PWM, chip->blue);
+ if (ret)
+ dev_dbg(&chip->client->dev,
+ "could not set brightness, %d\n", ret);
+}
+
+static void lp5521_blue_set(struct led_classdev *led,
+ enum led_brightness value)
+{
+ struct lp5521_chip *chip =
+ container_of(led, struct lp5521_chip, ledb);
+
+ chip->blue = value;
+ schedule_work(&chip->blue_work);
+}
+
+/*--------------------------------------------------------------*/
+/* Probe, Attach, Remove */
+/*--------------------------------------------------------------*/
+
+static int __devinit lp5521_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lp5521_platform_data *pdata = client->dev.platform_data;
+ struct lp5521_chip *chip;
+ char name[16];
+ int ret = 0;
+
+ if (!pdata) {
+ dev_err(&client->dev, "platform_data is missing\n");
+ return -EINVAL;
+ }
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->client = client;
+ strncpy(client->name, LP5521_DRIVER_NAME, I2C_NAME_SIZE);
+ i2c_set_clientdata(client, chip);
+
+ mutex_init(&chip->lock);
+
+ INIT_WORK(&chip->red_work, lp5521_red_work);
+ INIT_WORK(&chip->green_work, lp5521_green_work);
+ INIT_WORK(&chip->blue_work, lp5521_blue_work);
+
+ ret = lp5521_configure(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "lp5521 error configuring chip \n");
+ goto fail1;
+ }
+
+ /* Set default values */
+ chip->mode = pdata->mode;
+ chip->red = pdata->red_present;
+ chip->green = pdata->green_present;
+ chip->blue = pdata->blue_present;
+
+ chip->ledr.brightness_set = lp5521_red_set;
+ chip->ledr.default_trigger = NULL;
+ snprintf(name, sizeof(name), "%s::red", pdata->label);
+ chip->ledr.name = name;
+ ret = led_classdev_register(&client->dev, &chip->ledr);
+ if (ret < 0) {
+ dev_dbg(&client->dev, "failed to register led %s, %d\n",
+ chip->ledb.name, ret);
+ goto fail1;
+ }
+
+ chip->ledg.brightness_set = lp5521_green_set;
+ chip->ledg.default_trigger = NULL;
+ snprintf(name, sizeof(name), "%s::green", pdata->label);
+ chip->ledg.name = name;
+ ret = led_classdev_register(&client->dev, &chip->ledg);
+ if (ret < 0) {
+ dev_dbg(&client->dev, "failed to register led %s, %d\n",
+ chip->ledb.name, ret);
+ goto fail2;
+ }
+
+ chip->ledb.brightness_set = lp5521_blue_set;
+ chip->ledb.default_trigger = NULL;
+ snprintf(name, sizeof(name), "%s::blue", pdata->label);
+ chip->ledb.name = name;
+ ret = led_classdev_register(&client->dev, &chip->ledb);
+ if (ret < 0) {
+ dev_dbg(&client->dev, "failed to register led %s, %d\n",
+ chip->ledb.name, ret);
+ goto fail3;
+ }
+
+ ret = lp5521_register_sysfs(client);
+ if (ret) {
+ dev_err(&client->dev,
+ "lp5521 registering sysfs failed \n");
+ goto fail4;
+ }
+
+ return 0;
+
+fail4:
+ led_classdev_unregister(&chip->ledb);
+fail3:
+ led_classdev_unregister(&chip->ledg);
+fail2:
+ led_classdev_unregister(&chip->ledr);
+fail1:
+ i2c_set_clientdata(client, NULL);
+ kfree(chip);
+
+ return ret;
+}
+
+static int __exit lp5521_remove(struct i2c_client *client)
+{
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+
+ lp5521_unregister_sysfs(client);
+ i2c_set_clientdata(client, NULL);
+
+ led_classdev_unregister(&chip->ledb);
+ led_classdev_unregister(&chip->ledg);
+ led_classdev_unregister(&chip->ledr);
+
+ kfree(chip);
+
+ return 0;
+}
+
+static const struct i2c_device_id lp5521_id[] = {
+ {LP5521_DRIVER_NAME, 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, lp5521_id);
+
+static struct i2c_driver lp5521_driver = {
+ .driver = {
+ .name = LP5521_DRIVER_NAME,
+ },
+ .probe = lp5521_probe,
+ .remove = __exit_p(lp5521_remove),
+ .id_table = lp5521_id,
+};
+
+static int __init lp5521_init(void)
+{
+ return i2c_add_driver(&lp5521_driver);
+}
+
+module_init(lp5521_init);
+
+static void __exit lp5521_exit(void)
+{
+ i2c_del_driver(&lp5521_driver);
+}
+
+module_exit(lp5521_exit);
+
+MODULE_AUTHOR("Mathias Nyman <mathias.nyman@xxxxxxxxx>");
+MODULE_DESCRIPTION("lp5521 LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index da3fa8dcdf5..a8bf77af0ee 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -27,6 +27,7 @@ struct led_pwm_data {
struct led_classdev cdev;
struct pwm_device *pwm;
unsigned int active_low;
+ unsigned int lth_brightness;
unsigned int period;
};
@@ -42,7 +43,10 @@ static void led_pwm_set(struct led_classdev *led_cdev,
pwm_config(led_dat->pwm, 0, period);
pwm_disable(led_dat->pwm);
} else {
- pwm_config(led_dat->pwm, brightness * period / max, period);
+ brightness = led_dat->lth_brightness + (brightness *
+ (led_dat->period - led_dat->lth_brightness) / max);
+ pwm_config(led_dat->pwm, brightness, led_dat->period);
+
pwm_enable(led_dat->pwm);
}
}
@@ -78,6 +82,8 @@ static int led_pwm_probe(struct platform_device *pdev)
led_dat->cdev.default_trigger = cur_led->default_trigger;
led_dat->active_low = cur_led->active_low;
led_dat->period = cur_led->pwm_period_ns;
+ led_dat->lth_brightness = cur_led->lth_brightness *
+ (cur_led->pwm_period_ns / cur_led->max_brightness);
led_dat->cdev.brightness_set = led_pwm_set;
led_dat->cdev.brightness = LED_OFF;
led_dat->cdev.max_brightness = cur_led->max_brightness;
diff --git a/drivers/leds/ledtrig-sleep.c b/drivers/leds/ledtrig-sleep.c
new file mode 100644
index 00000000000..f1640421215
--- /dev/null
+++ b/drivers/leds/ledtrig-sleep.c
@@ -0,0 +1,80 @@
+/* drivers/leds/ledtrig-sleep.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/earlysuspend.h>
+#include <linux/leds.h>
+#include <linux/suspend.h>
+
+static int ledtrig_sleep_pm_callback(struct notifier_block *nfb,
+ unsigned long action,
+ void *ignored);
+
+DEFINE_LED_TRIGGER(ledtrig_sleep)
+static struct notifier_block ledtrig_sleep_pm_notifier = {
+ .notifier_call = ledtrig_sleep_pm_callback,
+ .priority = 0,
+};
+
+static void ledtrig_sleep_early_suspend(struct early_suspend *h)
+{
+ led_trigger_event(ledtrig_sleep, LED_FULL);
+}
+
+static void ledtrig_sleep_early_resume(struct early_suspend *h)
+{
+ led_trigger_event(ledtrig_sleep, LED_OFF);
+}
+
+static struct early_suspend ledtrig_sleep_early_suspend_handler = {
+ .suspend = ledtrig_sleep_early_suspend,
+ .resume = ledtrig_sleep_early_resume,
+};
+
+static int ledtrig_sleep_pm_callback(struct notifier_block *nfb,
+ unsigned long action,
+ void *ignored)
+{
+ switch (action) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ led_trigger_event(ledtrig_sleep, LED_OFF);
+ return NOTIFY_OK;
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ led_trigger_event(ledtrig_sleep, LED_FULL);
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int __init ledtrig_sleep_init(void)
+{
+ led_trigger_register_simple("sleep", &ledtrig_sleep);
+ register_pm_notifier(&ledtrig_sleep_pm_notifier);
+ register_early_suspend(&ledtrig_sleep_early_suspend_handler);
+ return 0;
+}
+
+static void __exit ledtrig_sleep_exit(void)
+{
+ unregister_early_suspend(&ledtrig_sleep_early_suspend_handler);
+ unregister_pm_notifier(&ledtrig_sleep_pm_notifier);
+ led_trigger_unregister_simple(ledtrig_sleep);
+}
+
+module_init(ledtrig_sleep_init);
+module_exit(ledtrig_sleep_exit);
+
diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig
index d22a8ec523f..8a310fcc994 100644
--- a/drivers/media/IR/Kconfig
+++ b/drivers/media/IR/Kconfig
@@ -1,5 +1,5 @@
config IR_CORE
- tristate
+ tristate "Core IR support"
depends on INPUT
default INPUT
diff --git a/drivers/media/radio/CG2900/Makefile b/drivers/media/radio/CG2900/Makefile
new file mode 100644
index 00000000000..c0cd99372da
--- /dev/null
+++ b/drivers/media/radio/CG2900/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the CG2900 FM Radio Driver
+#
+
+radio_cg2900-objs := radio-cg2900.o cg2900_fm_api.o cg2900_fm_driver.o
+
+obj-$(CONFIG_RADIO_CG2900) += radio_cg2900.o
+
diff --git a/drivers/media/radio/CG2900/cg2900_fm_api.c b/drivers/media/radio/CG2900/cg2900_fm_api.c
new file mode 100644
index 00000000000..b6be4e462e3
--- /dev/null
+++ b/drivers/media/radio/CG2900/cg2900_fm_api.c
@@ -0,0 +1,3106 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Linux FM Host API's for ST-Ericsson FM Chip.
+ *
+ * Author: Hemant Gupta <hemant.gupta@stericsson.com> for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include "cg2900_fm_driver.h"
+
+#define CG2900_FM_BT_SRC_COEFF_INFO_FILE "cg2900_fm_bt_src_coeff_info.fw"
+#define CG2900_FM_EXT_SRC_COEFF_INFO_FILE "cg2900_fm_ext_src_coeff_info.fw"
+#define CG2900_FM_FM_COEFF_INFO_FILE "cg2900_fm_fm_coeff_info.fw"
+#define CG2900_FM_FM_PROG_INFO_FILE "cg2900_fm_fm_prog_info.fw"
+#define CG2900_FM_LINE_BUFFER_LENGTH 128
+#define CG2900_FM_FILENAME_MAX 128
+#define FW_FILE_PARAM_LEN 3
+/* RDS Tx PTY set to Other music */
+#define OTHER_MUSIC 15
+
+static bool fm_rds_status;
+static bool fm_prev_rds_status;
+static u16 program_identification_code;
+static u16 default_program_identification_code = 0x1234;
+static u16 program_type_code;
+static u16 default_program_type_code = OTHER_MUSIC;
+static char program_service[MAX_PSN_SIZE];
+static char default_program_service[MAX_PSN_SIZE] = "FM-Xmit ";
+static char radio_text[MAX_RT_SIZE];
+static char default_radio_text[MAX_RT_SIZE] = "Default Radio Text "
+ "Default Radio Text Default Radio Text Default";
+static bool a_b_flag;
+u8 fm_event;
+static struct mutex rds_mutex;
+struct cg2900_fm_rds_buf fm_rds_buf[MAX_RDS_BUFFER][MAX_RDS_GROUPS];
+struct cg2900_fm_rds_info fm_rds_info;
+static enum cg2900_fm_state fm_state;
+static enum cg2900_fm_mode fm_mode;
+static struct cg2900_version_info version_info;
+
+/**
+ * cg2900_fm_get_one_line_of_text()- Get One line of text from a file.
+ *
+ * Replacement function for stdio function fgets.This function extracts one
+ * line of text from input file.
+ *
+ * @wr_buffer: Buffer to copy text to.
+ * @max_nbr_of_bytes: Max number of bytes to read, i.e. size of rd_buffer.
+ * @rd_buffer: Data to parse.
+ * @bytes_copied: Number of bytes copied to wr_buffer.
+ *
+ * Returns:
+ * Pointer to next data to read.
+ */
+static char *cg2900_fm_get_one_line_of_text(
+ char *wr_buffer,
+ int max_nbr_of_bytes,
+ char *rd_buffer,
+ int *bytes_copied
+ )
+{
+ char *curr_wr = wr_buffer;
+ char *curr_rd = rd_buffer;
+ char in_byte;
+
+ *bytes_copied = 0;
+
+ do {
+ *curr_wr = *curr_rd;
+ in_byte = *curr_wr;
+ curr_wr++;
+ curr_rd++;
+ (*bytes_copied)++;
+ } while ((*bytes_copied <= max_nbr_of_bytes) && (in_byte != '\0')
+ && (in_byte != '\n'));
+ *curr_wr = '\0';
+ return curr_rd;
+}
+
+/**
+ * cg2900_fm_get_file_to_load() - Parse info file and find correct target file.
+ *
+ * @fw: Firmware structure containing file data.
+ * @file_name: (out) Pointer to name of requested file.
+ *
+ * Returns:
+ * True, if target file was found,
+ * False, otherwise.
+ */
+static bool cg2900_fm_get_file_to_load(
+ const struct firmware *fw,
+ char **file_name
+ )
+{
+ char *line_buffer;
+ char *curr_file_buffer;
+ int bytes_left_to_parse = fw->size;
+ int bytes_read = 0;
+ bool file_found = false;
+
+ curr_file_buffer = (char *)&(fw->data[0]);
+
+ line_buffer = kmalloc(CG2900_FM_LINE_BUFFER_LENGTH,
+ GFP_KERNEL);
+
+ if (line_buffer == NULL) {
+ FM_ERR_REPORT("Failed to allocate:"
+ "file_name 0x%X, line_buffer 0x%X",
+ (unsigned int)file_name,
+ (unsigned int)line_buffer);
+ goto error;
+ }
+
+ while (!file_found) {
+ /* Get one line of text from the file to parse */
+ curr_file_buffer =
+ cg2900_fm_get_one_line_of_text(line_buffer,
+ min
+ (CG2900_FM_LINE_BUFFER_LENGTH,
+ (int)(fw->size -
+ bytes_read)),
+ curr_file_buffer,
+ &bytes_read);
+
+ bytes_left_to_parse -= bytes_read;
+ if (bytes_left_to_parse <= 0) {
+ /* End of file => Leave while loop */
+ FM_ERR_REPORT("Reached end of file."
+ "No file found!");
+ break;
+ }
+
+ /*
+ * Check if the line of text is a comment
+ * or not, comments begin with '#'
+ */
+ if (*line_buffer != '#') {
+ u32 hci_rev = 0;
+ u32 lmp_sub = 0;
+
+ FM_DEBUG_REPORT("Found a valid line <%s>",
+ line_buffer);
+
+ /*
+ * Check if we can find the correct
+ * HCI revision and LMP subversion
+ * as well as a file name in the text line
+ * Store the filename if the actual file can
+ * be found in the file system
+ */
+ if (sscanf(line_buffer, "%x%x%s",
+ (unsigned int *)&hci_rev,
+ (unsigned int *)&lmp_sub,
+ *file_name) == FW_FILE_PARAM_LEN
+ && hci_rev == version_info.revision
+ && lmp_sub == version_info.sub_version) {
+ FM_INFO_REPORT("File name = %s "
+ "HCI Revision"
+ "= 0x%04X LMP "
+ "Subversion = 0x%04X",
+ *file_name,
+ (unsigned int)hci_rev,
+ (unsigned int)lmp_sub);
+
+ /*
+ * Name has already been stored above.
+ * Nothing more to do
+ */
+ file_found = true;
+ } else {
+ /*Zero the name buffer so it is clear to next read*/
+ memset(*file_name, 0x00,
+ CG2900_FM_FILENAME_MAX);
+ }
+ }
+ }
+ kfree(line_buffer);
+error:
+ return file_found;
+}
+
+/**
+ * cg2900_fm_load_firmware() - Loads the FM Coeffecients and F/W file(s)
+ *
+ * @device: Pointer to char device requesting the operation.
+ *
+ * Returns:
+ * 0, if firmware download is successful
+ * -ENOENT, file not found.
+ * -ENOMEM, out of memory
+ */
+static int cg2900_fm_load_firmware(
+ struct device *device
+ )
+{
+ int err;
+ bool file_found;
+ int result = 0;
+ const struct firmware *bt_src_coeff_info;
+ const struct firmware *ext_src_coeff_info;
+ const struct firmware *fm_coeff_info;
+ const struct firmware *fm_prog_info;
+ char *bt_src_coeff_file_name = NULL;
+ char *ext_src_coeff_file_name = NULL;
+ char *fm_coeff_file_name = NULL;
+ char *fm_prog_file_name = NULL;
+
+ FM_INFO_REPORT("+cg2900_fm_load_firmware");
+
+ /* Open bt_src_coeff info file. */
+ err = request_firmware(&bt_src_coeff_info,
+ CG2900_FM_BT_SRC_COEFF_INFO_FILE, device);
+ if (err) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't get bt_src_coeff info file");
+ result = -ENOENT;
+ goto error;
+ }
+
+ /*
+ * Now we have the bt_src_coeff info file.
+ * See if we can find the right bt_src_coeff file as well
+ */
+ bt_src_coeff_file_name = kmalloc(CG2900_FM_FILENAME_MAX,
+ GFP_KERNEL);
+ if (bt_src_coeff_file_name == NULL) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't allocate memory for "
+ "bt_src_coeff_file_name");
+ release_firmware(bt_src_coeff_info);
+ result = -ENOMEM;
+ goto error;
+ }
+ file_found = cg2900_fm_get_file_to_load(bt_src_coeff_info,
+ &bt_src_coeff_file_name);
+
+ /* Now we are finished with the bt_src_coeff info file */
+ release_firmware(bt_src_coeff_info);
+
+ if (!file_found) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't find bt_src_coeff file!! "
+ "Major error!!!");
+ result = -ENOENT;
+ goto error;
+ }
+
+ /* Open ext_src_coeff info file. */
+ err = request_firmware(&ext_src_coeff_info,
+ CG2900_FM_EXT_SRC_COEFF_INFO_FILE, device);
+ if (err) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't get ext_src_coeff_info info file");
+ result = -ENOENT;
+ goto error;
+ }
+
+ /*
+ * Now we have the ext_src_coeff info file. See if we can
+ * find the right ext_src_coeff file as well
+ */
+ ext_src_coeff_file_name = kmalloc(CG2900_FM_FILENAME_MAX,
+ GFP_KERNEL);
+ if (ext_src_coeff_file_name == NULL) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't allocate memory for "
+ "ext_src_coeff_file_name");
+ release_firmware(ext_src_coeff_info);
+ result = -ENOMEM;
+ goto error;
+ }
+ file_found = cg2900_fm_get_file_to_load(ext_src_coeff_info,
+ &ext_src_coeff_file_name);
+
+ /* Now we are finished with the ext_src_coeff info file */
+ release_firmware(ext_src_coeff_info);
+
+ if (!file_found) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't find ext_src_coeff_info "
+ "file!!! Major error!");
+ result = -ENOENT;
+ goto error;
+ }
+
+ /* Open fm_coeff info file. */
+ err = request_firmware(&fm_coeff_info,
+ CG2900_FM_FM_COEFF_INFO_FILE, device);
+ if (err) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't get fm_coeff info file");
+ result = -ENOENT;
+ goto error;
+ }
+
+ /*
+ * Now we have the fm_coeff_info info file.
+ * See if we can find the right fm_coeff_info file as well
+ */
+ fm_coeff_file_name = kmalloc(CG2900_FM_FILENAME_MAX,
+ GFP_KERNEL);
+ if (fm_coeff_file_name == NULL) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't allocate memory for "
+ "fm_coeff_file_name");
+ release_firmware(fm_coeff_info);
+ result = -ENOMEM;
+ goto error;
+ }
+ file_found = cg2900_fm_get_file_to_load(fm_coeff_info,
+ &fm_coeff_file_name);
+
+ /* Now we are finished with the fm_coeff info file */
+ release_firmware(fm_coeff_info);
+
+ if (!file_found) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't find fm_coeff file!!! "
+ "Major error!");
+ result = -ENOENT;
+ goto error;
+ }
+
+ /* Open fm_prog info file. */
+ err = request_firmware(&fm_prog_info,
+ CG2900_FM_FM_PROG_INFO_FILE, device);
+ if (err) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't get fm_prog_info info file");
+ result = -ENOENT;
+ goto error;
+ }
+
+ /*
+ * Now we have the fm_prog info file.
+ * See if we can find the right fm_prog file as well
+ */
+ fm_prog_file_name = kmalloc(CG2900_FM_FILENAME_MAX,
+ GFP_KERNEL);
+ if (fm_prog_file_name == NULL) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't allocate memory for "
+ "fm_prog_file_name");
+ release_firmware(fm_prog_info);
+ result = -ENOMEM;
+ goto error;
+ }
+ file_found = cg2900_fm_get_file_to_load(fm_prog_info,
+ &fm_prog_file_name);
+
+ /* Now we are finished with fm_prog patch info file */
+ release_firmware(fm_prog_info);
+
+ if (!file_found) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't find fm_prog_info file!!! "
+ "Major error!");
+ result = -ENOENT;
+ goto error;
+ }
+
+ /* OK. Now it is time to download the firmware */
+ err = request_firmware(&bt_src_coeff_info,
+ bt_src_coeff_file_name, device);
+ if (err < 0) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't get bt_src_coeff file, err = %d", err);
+ result = -ENOENT;
+ goto error;
+ }
+
+ FM_INFO_REPORT("cg2900_fm_load_firmware: Downloading %s of %d bytes",
+ bt_src_coeff_file_name, bt_src_coeff_info->size);
+ if (fmd_send_fm_firmware((u8 *) bt_src_coeff_info->data,
+ bt_src_coeff_info->size)) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: Error in "
+ "downloading %s", bt_src_coeff_file_name);
+ release_firmware(bt_src_coeff_info);
+ result = -ENOENT;
+ goto error;
+ }
+
+ /* Now we are finished with the bt_src_coeff info file */
+ release_firmware(bt_src_coeff_info);
+ err = request_firmware(&ext_src_coeff_info,
+ ext_src_coeff_file_name, device);
+ if (err < 0) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't get ext_src_coeff file, err = %d", err);
+ result = -ENOENT;
+ goto error;
+ }
+
+ FM_INFO_REPORT("cg2900_fm_load_firmware: Downloading %s of %d bytes",
+ ext_src_coeff_file_name, ext_src_coeff_info->size);
+ if (fmd_send_fm_firmware((u8 *) ext_src_coeff_info->data,
+ ext_src_coeff_info->size)) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: Error in "
+ "downloading %s", ext_src_coeff_file_name);
+ release_firmware(ext_src_coeff_info);
+ result = -ENOENT;
+ goto error;
+ }
+
+ /* Now we are finished with the bt_src_coeff info file */
+ release_firmware(ext_src_coeff_info);
+
+ err = request_firmware(&fm_coeff_info, fm_coeff_file_name, device);
+ if (err < 0) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't get fm_coeff file, err = %d", err);
+ result = -ENOENT;
+ goto error;
+ }
+
+ FM_INFO_REPORT("cg2900_fm_load_firmware: Downloading %s of %d bytes",
+ fm_coeff_file_name, fm_coeff_info->size);
+ if (fmd_send_fm_firmware((u8 *) fm_coeff_info->data,
+ fm_coeff_info->size)) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: Error in "
+ "downloading %s", fm_coeff_file_name);
+ release_firmware(fm_coeff_info);
+ result = -ENOENT;
+ goto error;
+ }
+
+ /* Now we are finished with the bt_src_coeff info file */
+ release_firmware(fm_coeff_info);
+
+ err = request_firmware(&fm_prog_info, fm_prog_file_name, device);
+ if (err < 0) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: "
+ "Couldn't get fm_prog file, err = %d", err);
+ result = -ENOENT;
+ goto error;
+ }
+
+ FM_INFO_REPORT("cg2900_fm_load_firmware: Downloading %s of %d bytes",
+ fm_prog_file_name, fm_prog_info->size);
+ if (fmd_send_fm_firmware((u8 *) fm_prog_info->data,
+ fm_prog_info->size)) {
+ FM_ERR_REPORT("cg2900_fm_load_firmware: Error in "
+ "downloading %s", fm_prog_file_name);
+ release_firmware(fm_prog_info);
+ result = -ENOENT;
+ goto error;
+ }
+
+ /* Now we are finished with the bt_src_coeff info file */
+ release_firmware(fm_prog_info);
+
+error:
+ /* Free Allocated memory */
+ if (bt_src_coeff_file_name != NULL)
+ kfree(bt_src_coeff_file_name);
+ if (ext_src_coeff_file_name != NULL)
+ kfree(ext_src_coeff_file_name);
+ if (fm_coeff_file_name != NULL)
+ kfree(fm_coeff_file_name);
+ if (fm_prog_file_name != NULL)
+ kfree(fm_prog_file_name);
+ FM_DEBUG_REPORT("-cg2900_fm_load_firmware: returning %d",
+ result);
+ return result;
+}
+
+/**
+ * cg2900_fm_transmit_rds_groups()- Transmits the RDS Groups.
+ *
+ * Stores the RDS Groups in Chip's buffer and each group is
+ * transmitted every 87.6 ms.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise
+ */
+static int cg2900_fm_transmit_rds_groups(void)
+{
+ int result = 0;
+ u16 group_position = 0;
+ u8 block1[2];
+ u8 block2[2];
+ u8 block3[2];
+ u8 block4[2];
+ int index1 = 0;
+ int index2 = 0;
+ int group_0B_count = 0;
+ int group_2A_count = 0;
+
+ FM_INFO_REPORT("cg2900_fm_transmit_rds_groups");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_transmit_rds_groups: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ while (group_position < 20 && result == 0) {
+ if (group_position < 4) {
+ /* Transmit PSN in Group 0B */
+ block1[0] = program_identification_code;
+ block1[1] = program_identification_code >> 8;
+ /* M/S bit set to Music */
+ if (group_0B_count % 4 == 0) {
+ /* Manipulate DI bit */
+ block2[0] =
+ (0x08 | ((program_type_code & 0x07)
+ << 5))
+ + group_0B_count;
+ } else {
+ block2[0] =
+ (0x0C | ((program_type_code & 0x07)
+ << 5))
+ + group_0B_count;
+ }
+ block2[1] =
+ 0x08 | ((program_type_code & 0x18) >> 3);
+ block3[0] = program_identification_code;
+ block3[1] = program_identification_code >> 8;
+ block4[0] = program_service[index1 + 1];
+ block4[1] = program_service[index1 + 0];
+ index1 += 2;
+ group_0B_count++;
+ } else {
+ /* Transmit RT in Group 2A */
+ block1[0] = program_identification_code;
+ block1[1] = program_identification_code >> 8;
+ if (a_b_flag)
+ block2[0] = (0x10 |
+ ((program_type_code & 0x07)
+ << 5)) + group_2A_count;
+ else
+ block2[0] = (0x00 |
+ ((program_type_code & 0x07)
+ << 5)) + group_2A_count;
+ block2[1] = 0x20 | ((program_type_code & 0x18)
+ >> 3);
+ block3[0] = radio_text[index2 + 1];
+ block3[1] = radio_text[index2 + 0];
+ block4[0] = radio_text[index2 + 3];
+ block4[1] = radio_text[index2 + 2];
+ index2 += 4;
+ group_2A_count++;
+ }
+ FM_DEBUG_REPORT("%02x%02x "
+ "%02x%02x "
+ "%02x%02x "
+ "%02x%02x ",
+ block1[1], block1[0],
+ block2[1], block2[0],
+ block3[1], block3[0],
+ block4[1], block4[0]);
+ result = fmd_tx_set_group(
+ group_position,
+ block1,
+ block2,
+ block3,
+ block4);
+ group_position++;
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_transmit_rds_groups: "
+ "fmd_tx_set_group failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ break;
+ }
+ }
+ a_b_flag = !a_b_flag;
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_transmit_rds_groups: returning %d",
+ result);
+ return result;
+}
+
+/**
+ * cg2900_fm_driver_callback()- Callback function indicating the event.
+ *
+ * This callback function is called on receiving irpt_CommandSucceeded,
+ * irpt_CommandFailed, irpt_bufferFull, etc from FM chip.
+ * @event: event for which the callback function was caled
+ * from FM Driver.
+ * @event_successful: Signifying whether the event is called from FM Driver
+ * on receiving irpt_OperationSucceeded or irpt_OperationFailed.
+ */
+static void cg2900_fm_driver_callback(
+ u8 event,
+ bool event_successful
+ )
+{
+ FM_INFO_REPORT("cg2900_fm_driver_callback: "
+ "event = %02x, event_successful = %x",
+ event, event_successful);
+
+ if (event_successful) {
+ switch (event) {
+ case FMD_EVENT_GEN_POWERUP:
+ FM_DEBUG_REPORT("FMD_EVENT_GEN_POWERUP");
+ break;
+ case FMD_EVENT_ANTENNA_STATUS_CHANGED:
+ FM_DEBUG_REPORT("FMD_EVENT_ANTENNA_STATUS_CHANGED");
+ break;
+ case FMD_EVENT_FREQUENCY_CHANGED:
+ FM_DEBUG_REPORT("FMD_EVENT_FREQUENCY_CHANGED ");
+ break;
+
+ case FMD_EVENT_SEEK_STOPPED:
+ FM_DEBUG_REPORT("FMD_EVENT_SEEK_STOPPED");
+ fm_event = CG2900_EVENT_SCAN_CANCELLED;
+ wake_up_poll_queue();
+ break;
+
+ case FMD_EVENT_SEEK_COMPLETED:
+ FM_DEBUG_REPORT("FMD_EVENT_SEEK_COMPLETED");
+ fm_event = CG2900_EVENT_SEARCH_CHANNEL_FOUND;
+ wake_up_poll_queue();
+ break;
+
+ case FMD_EVENT_SCAN_BAND_COMPLETED:
+ FM_DEBUG_REPORT("FMD_EVENT_SCAN_BAND_COMPLETED");
+ fm_event = CG2900_EVENT_SCAN_CHANNELS_FOUND;
+ wake_up_poll_queue();
+ break;
+
+ case FMD_EVENT_BLOCK_SCAN_COMPLETED:
+ FM_DEBUG_REPORT("FMD_EVENT_BLOCK_SCAN_COMPLETED");
+ fm_event = CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND;
+ wake_up_poll_queue();
+ break;
+
+ case FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE:
+ FM_DEBUG_REPORT("FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE");
+ break;
+
+ case FMD_EVENT_RDSGROUP_RCVD:
+ FM_DEBUG_REPORT("FMD_EVENT_RDSGROUP_RCVD");
+ fmd_set_rds_sem();
+ break;
+
+ default:
+ FM_INFO_REPORT("cg2900_fm_driver_callback: "
+ "Unknown event = %x", event);
+ break;
+ }
+ } else {
+ switch (event) {
+ /*
+ * Seek stop, band scan, seek, block scan could
+ * fail for some reason so wake up poll queue
+ */
+ case FMD_EVENT_SEEK_STOPPED:
+ FM_ERR_REPORT("FMD_EVENT_SEEK_STOPPED");
+ fm_event = CG2900_EVENT_SCAN_CANCELLED;
+ wake_up_poll_queue();
+ break;
+ case FMD_EVENT_SEEK_COMPLETED:
+ FM_ERR_REPORT("FMD_EVENT_SEEK_COMPLETED");
+ fm_event = CG2900_EVENT_SEARCH_CHANNEL_FOUND;
+ wake_up_poll_queue();
+ break;
+ case FMD_EVENT_SCAN_BAND_COMPLETED:
+ FM_ERR_REPORT("FMD_EVENT_SCAN_BAND_COMPLETED");
+ fm_event = CG2900_EVENT_SCAN_CHANNELS_FOUND;
+ wake_up_poll_queue();
+ break;
+ case FMD_EVENT_BLOCK_SCAN_COMPLETED:
+ FM_ERR_REPORT("FMD_EVENT_BLOCK_SCAN_COMPLETED");
+ fm_event = CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND;
+ wake_up_poll_queue();
+ break;
+ default:
+ FM_ERR_REPORT("cg2900_fm_driver_callback: "
+ "event = %x failed!!!!", event);
+ break;
+ }
+ }
+}
+
+/**
+ * cg2900_fm_rds_callback()- Function to retrieve the RDS groups.
+ *
+ * This is called when the chip has received enough RDS groups
+ * so an interrupt irpt_BufferFull is generated to read the groups.
+ */
+static void cg2900_fm_rds_callback(void)
+{
+ u8 index = 0;
+ u16 rds_local_buf_count;
+ int result;
+ FM_INFO_REPORT("cg2900_fm_rds_callback");
+
+ /*
+ * Wait till interrupt is RDS Buffer
+ * full interrupt is received
+ */
+ fmd_get_rds_sem();
+
+ if (!fm_rds_status)
+ return;
+
+ /* RDS Data available, Read the Groups */
+ mutex_lock(&rds_mutex);
+ result = fmd_int_bufferfull(&rds_local_buf_count);
+
+ if (0 != result)
+ goto error;
+
+ while (index < rds_local_buf_count) {
+ /*
+ * Status are in reverse order because of Endianness
+ * of status byte received from chip
+ */
+ result = fmd_rx_get_low_level_rds_groups(
+ index,
+ &fm_rds_buf[fm_rds_info.rds_head][index].block1,
+ &fm_rds_buf[fm_rds_info.rds_head][index].block2,
+ &fm_rds_buf[fm_rds_info.rds_head][index].block3,
+ &fm_rds_buf[fm_rds_info.rds_head][index].block4,
+ &fm_rds_buf[fm_rds_info.rds_head][index].status2,
+ &fm_rds_buf[fm_rds_info.rds_head][index].status1,
+ &fm_rds_buf[fm_rds_info.rds_head][index].status4,
+ &fm_rds_buf[fm_rds_info.rds_head][index].status3);
+ FM_INFO_REPORT("%04x %04x %04x %04x %02x %02x %02x %02x",
+ fm_rds_buf[fm_rds_info.rds_head][index].block1,
+ fm_rds_buf[fm_rds_info.rds_head][index].block2,
+ fm_rds_buf[fm_rds_info.rds_head][index].block3,
+ fm_rds_buf[fm_rds_info.rds_head][index].block4,
+ fm_rds_buf[fm_rds_info.rds_head][index].status1,
+ fm_rds_buf[fm_rds_info.rds_head][index].status2,
+ fm_rds_buf[fm_rds_info.rds_head][index].status3,
+ fm_rds_buf[fm_rds_info.rds_head][index].status4);
+
+ if (0 != result)
+
+ goto error;
+
+ if (!fm_rds_status)
+ return;
+
+ index++;
+ }
+ fm_rds_info.rds_head++;
+ if (fm_rds_info.rds_head == MAX_RDS_BUFFER)
+ fm_rds_info.rds_head = 0;
+ wake_up_read_queue();
+error:
+ mutex_unlock(&rds_mutex);
+}
+
+int cg2900_fm_init(void)
+{
+ int result = 0;
+
+ FM_INFO_REPORT("cg2900_fm_init");
+
+ if (CG2900_FM_STATE_DEINITIALIZED != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_init: Already Initialized");
+ result = -EINVAL;
+ goto error;
+ }
+
+ mutex_init(&rds_mutex);
+
+ memset(&fm_rds_info, 0, sizeof(struct cg2900_fm_rds_info));
+ memset(&version_info, 0, sizeof(struct cg2900_version_info));
+ memset(
+ fm_rds_buf,
+ 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+
+ /* Initalize the Driver */
+ if (fmd_init() != 0) {
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Register the callback */
+ if (fmd_register_callback(
+ (fmd_radio_cb) cg2900_fm_driver_callback) != 0) {
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* initialize global variables */
+ fm_event = CG2900_EVENT_NO_EVENT;
+ fm_state = CG2900_FM_STATE_INITIALIZED;
+ fm_mode = CG2900_FM_IDLE_MODE;
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_init: returning %d",
+ result);
+ return result;
+
+}
+
+int cg2900_fm_deinit(void)
+{
+ int result = 0;
+
+ FM_INFO_REPORT("cg2900_fm_deinit");
+
+ if (CG2900_FM_STATE_INITIALIZED != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_deinit: Already de-Initialized");
+ result = -EINVAL;
+ goto error;
+ }
+ fmd_exit();
+ mutex_destroy(&rds_mutex);
+ fm_state = CG2900_FM_STATE_DEINITIALIZED;
+ fm_mode = CG2900_FM_IDLE_MODE;
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_deinit: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_switch_on(
+ struct device *device
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_switch_on");
+
+ if (CG2900_FM_STATE_INITIALIZED != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_switch_on: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ /* Enable FM IP */
+ FM_DEBUG_REPORT("cg2900_fm_switch_on: " "Sending FM IP Enable");
+
+ if (fmd_send_fm_ip_enable()) {
+ FM_ERR_REPORT("cg2900_fm_switch_on: "
+ "Error in fmd_send_fm_ip_enable");
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Now Download the Coefficient Files and FM Firmware */
+ if (cg2900_fm_load_firmware(device) != 0) {
+ FM_ERR_REPORT("cg2900_fm_switch_on: "
+ "Error in downloading firmware");
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Power up FM */
+ result = fmd_power_up();
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_switch_on: "
+ "fmd_power_up failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Switch Mode To Idle */
+ result = fmd_set_mode(FMD_MODE_IDLE);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_switch_on: "
+ "fmd_set_mode failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ fm_state = CG2900_FM_STATE_SWITCHED_ON;
+ fm_mode = CG2900_FM_IDLE_MODE;
+ memset(&fm_rds_info, 0, sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_switch_on: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_switch_off(void)
+{
+ int result = 0;
+
+ FM_INFO_REPORT("cg2900_fm_switch_off");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state &&
+ CG2900_FM_STATE_STAND_BY != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_switch_off: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ /* Stop the RDS Thread if it is running */
+ if (fm_rds_status) {
+ fm_rds_status = false;
+ fmd_stop_rds_thread();
+ }
+ if (CG2900_FM_STATE_STAND_BY == fm_state) {
+ /* Power up FM */
+ result = fmd_power_up();
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_switch_off: "
+ "fmd_power_up failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ } else
+ fm_state = CG2900_FM_STATE_SWITCHED_ON;
+ }
+ if (fmd_send_fm_ip_disable()) {
+ FM_ERR_REPORT("cg2900_fm_switch_off: "
+ "Problem in fmd_send_fm_ip_"
+ "disable");
+ result = -EINVAL;
+ goto error;
+ }
+ if (0 == result) {
+ fm_state = CG2900_FM_STATE_INITIALIZED;
+ fm_mode = CG2900_FM_IDLE_MODE;
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_switch_off: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_standby(void)
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_standby");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_standby: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_goto_standby();
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_standby: "
+ "FMLGotoStandby failed, "
+ "err = %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ fm_state = CG2900_FM_STATE_STAND_BY;
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_standby: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_power_up_from_standby(void)
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_power_up_from_standby");
+
+ if (CG2900_FM_STATE_STAND_BY != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_power_up_from_standby: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ /* Power up FM */
+ result = fmd_power_up();
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_power_up_from_standby: "
+ "fmd_power_up failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ } else {
+ fm_state = CG2900_FM_STATE_SWITCHED_ON;
+ if (CG2900_FM_TX_MODE == fm_mode) {
+ /* Enable the PA */
+ result = fmd_tx_set_pa(true);
+ if (0 != result) {
+ FM_ERR_REPORT
+ ("cg2900_fm_power_up_from_standby:"
+ " fmd_tx_set_pa " "failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ }
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_power_up_from_standby: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_set_rx_default_settings(
+ u32 freq,
+ u8 band,
+ u8 grid,
+ bool enable_rds,
+ bool enable_stereo
+ )
+{
+ int result;
+ u8 vol_in_percentage;
+
+ FM_INFO_REPORT("cg2900_fm_set_rx_default_settings: freq = %d Hz, "
+ "band = %d, grid = %d, RDS = %d, Stereo Mode = %d",
+ freq, band, grid, enable_rds, enable_stereo);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state &&
+ CG2900_FM_STATE_STAND_BY != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_set_rx_default_settings: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ if (CG2900_FM_STATE_STAND_BY == fm_state) {
+ /* Power up FM */
+ result = fmd_power_up();
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_rx_default_settings: "
+ "fmd_power_up failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ } else
+ fm_state = CG2900_FM_STATE_SWITCHED_ON;
+ }
+ fm_mode = CG2900_FM_RX_MODE;
+
+ FM_DEBUG_REPORT("cg2900_fm_set_rx_default_settings: "
+ "Sending Set mode to Rx");
+ result = fmd_set_mode(FMD_MODE_RX);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_rx_default_settings: "
+ "fmd_set_mode failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Set the Grid */
+ FM_DEBUG_REPORT("cg2900_fm_set_rx_default_settings: "
+ "Sending fmd_rx_set_grid ");
+ result = fmd_rx_set_grid(grid);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_rx_default_settings: "
+ "fmd_rx_set_grid failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Set the Band */
+ FM_DEBUG_REPORT("cg2900_fm_set_rx_default_settings: "
+ "Sending Set fmd_set_freq_range");
+ result = fmd_set_freq_range(band);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_rx_default_settings: "
+ "fmd_set_freq_range failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Set the Frequency */
+ FM_DEBUG_REPORT("cg2900_fm_set_rx_default_settings: "
+ "Sending Set fmd_rx_set_frequency");
+ result = fmd_rx_set_frequency(
+ freq / FREQUENCY_CONVERTOR_KHZ_HZ);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_rx_default_settings: "
+ "fmd_rx_set_frequency failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ FM_DEBUG_REPORT("cg2900_fm_set_rx_default_settings: "
+ "SetFrequency interrupt received, "
+ "Sending Set fmd_rx_set_stereo_mode");
+
+ if (enable_stereo) {
+ /* Set the Stereo Blending mode */
+ result = fmd_rx_set_stereo_mode(
+ FMD_STEREOMODE_BLENDING);
+ } else {
+ /* Set the Mono mode */
+ result = fmd_rx_set_stereo_mode(
+ FMD_STEREOMODE_MONO);
+ }
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_rx_default_settings: "
+ "fmd_rx_set_stereo_mode "
+ "failed %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ FM_DEBUG_REPORT("cg2900_fm_set_rx_default_settings: "
+ "Sending Set rds");
+
+ if (enable_rds) {
+ /* Enable RDS */
+ a_b_flag = false;
+ result = cg2900_fm_rds_on();
+ } else {
+ /* Disable RDS */
+ result = cg2900_fm_rds_off();
+ }
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_rx_default_settings: "
+ "cg2900_fm_rds_on "
+ "failed %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Set the Analog Out Volume to Max */
+ vol_in_percentage = (u8)
+ (((u16) (MAX_ANALOG_VOLUME) * 100)
+ / MAX_ANALOG_VOLUME);
+ result = fmd_set_volume(vol_in_percentage);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_switch_on: "
+ "FMRSetVolume failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_set_rx_default_settings: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_set_tx_default_settings(
+ u32 freq,
+ u8 band,
+ u8 grid,
+ bool enable_rds,
+ bool enable_stereo
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_set_tx_default_settings: freq = %d Hz, "
+ "band = %d, grid = %d, RDS = %d, Stereo Mode = %d",
+ freq, band, grid, enable_rds, enable_stereo);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state &&
+ CG2900_FM_STATE_STAND_BY != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_set_tx_default_settings: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ if (CG2900_FM_STATE_STAND_BY == fm_state) {
+ /* Power up FM */
+ result = fmd_power_up();
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_tx_default_settings: "
+ "fmd_power_up failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ } else
+ fm_state = CG2900_FM_STATE_SWITCHED_ON;
+ }
+ fm_mode = CG2900_FM_TX_MODE;
+ if (fm_rds_status) {
+ fm_rds_status = false;
+ fmd_stop_rds_thread();
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ /* Give 50 ms delay to exit the RDS thread */
+ schedule_timeout_interruptible(msecs_to_jiffies(50));
+ }
+
+ /* Switch To Tx mode */
+ FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: "
+ "Sending Set mode to Tx");
+ result = fmd_set_mode(FMD_MODE_TX);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_tx_default_settings: "
+ "fmd_set_mode failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Set the Grid */
+ FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: "
+ "Sending fmd_tx_set_grid ");
+ result = fmd_tx_set_grid(grid);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_tx_default_settings: "
+ "fmd_tx_set_grid failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Set the Band */
+ FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: "
+ "Sending fmd_tx_set_freq_range");
+ result = fmd_tx_set_freq_range(band);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_tx_default_settings: "
+ "fmd_tx_set_freq_range failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Set the Band */
+ FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: "
+ "Sending fmd_tx_set_preemphasis");
+ result = fmd_tx_set_preemphasis(FMD_EMPHASIS_75US);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_switch_on: "
+ "fmd_tx_set_preemphasis failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Set the Frequency */
+ FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: "
+ "Sending Set fmd_tx_set_frequency");
+ result = fmd_tx_set_frequency(
+ freq / FREQUENCY_CONVERTOR_KHZ_HZ);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_switch_on: "
+ "fmd_tx_set_frequency failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: "
+ "SetFrequency interrupt received, "
+ "Sending Set fmd_tx_enable_stereo_mode");
+
+ /* Set the Stereo mode */
+ result = fmd_tx_enable_stereo_mode(enable_stereo);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_tx_default_settings: "
+ "fmd_tx_enable_stereo_mode "
+ "failed %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: "
+ "Sending Set fmd_tx_set_pa");
+
+ /* Enable the PA */
+ result = fmd_tx_set_pa(true);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_tx_default_settings: "
+ "fmd_tx_set_pa "
+ "failed %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: "
+ "set PA interrupt received, "
+ "Sending Set fmd_tx_set_signal_strength");
+
+ /* Set the Signal Strength to Max */
+ result = fmd_tx_set_signal_strength(
+ MAX_POWER_LEVEL);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_tx_default_settings: "
+ "fmd_tx_set_signal_strength "
+ "failed %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Enable Tx RDS */
+ FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: "
+ "Sending Set cg2900_fm_tx_rds");
+ result = cg2900_fm_tx_rds(enable_rds);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_tx_default_settings: "
+ "cg2900_fm_tx_rds "
+ "failed %x", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_set_grid(
+ u8 grid
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_set_grid: Grid = %d", grid);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_set_grid: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_rx_set_grid(grid);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_grid: "
+ "fmd_rx_set_grid failed");
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_set_grid: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_set_band(
+ u8 band
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_set_band: Band = %d", band);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_set_band: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_set_freq_range(band);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_band: "
+ "fmd_set_freq_range failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_set_band: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_search_up_freq(void)
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_search_up_freq");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_search_up_freq: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ if (fm_rds_status) {
+ /* Stop RDS if it is active */
+ result = cg2900_fm_rds_off();
+ fm_prev_rds_status = true;
+ }
+ result = fmd_rx_seek(CG2900_DIR_UP);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_search_up_freq: "
+ "Error Code %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ result = 0;
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_search_up_freq: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_search_down_freq(void)
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_search_down_freq");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_search_down_freq: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ if (fm_rds_status) {
+ /* Stop RDS if it is active */
+ result = cg2900_fm_rds_off();
+ fm_prev_rds_status = true;
+ }
+ result = fmd_rx_seek(CG2900_DIR_DOWN);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_search_down_freq: "
+ "Error Code %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ result = 0;
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_search_down_freq: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_start_band_scan(void)
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_start_band_scan");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_start_band_scan: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ if (fm_rds_status) {
+ /* Stop RDS if it is active */
+ result = cg2900_fm_rds_off();
+ fm_prev_rds_status = true;
+ }
+ result = fmd_rx_scan_band(DEFAULT_CHANNELS_TO_SCAN);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_start_band_scan: "
+ "Error Code %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ result = 0;
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_start_band_scan: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_stop_scan(void)
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_stop_scan");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_stop_scan: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_rx_stop_seeking();
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_stop_scan: "
+ "Error Code %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ result = 0;
+ if (fm_prev_rds_status) {
+ /* Restart RDS if it was active earlier */
+ cg2900_fm_rds_on();
+ fm_prev_rds_status = false;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_stop_scan: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_get_scan_result(
+ u16 *num_of_scanfreq,
+ u32 *scan_freq,
+ u32 *scan_freq_rssi_level
+ )
+{
+ int result;
+ u32 cnt;
+ u32 index;
+ u32 minfreq;
+ u32 maxfreq;
+ u16 channels[3];
+ u16 rssi[3];
+ u8 freq_range;
+ u8 max_channels = 0;
+
+ FM_INFO_REPORT("cg2900_fm_get_scan_result");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_get_scan_result: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_get_freq_range(&freq_range);
+
+ if (0 != result) {
+ result = -EINVAL;
+ goto error;
+ }
+
+ result = fmd_get_freq_range_properties(
+ freq_range,
+ &minfreq,
+ &maxfreq);
+
+ if (0 != result) {
+ result = -EINVAL;
+ goto error;
+ }
+
+ result = fmd_rx_get_max_channels_to_scan(&max_channels);
+
+ if (0 != result) {
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* In 1 iteration we can retreive max 3 channels */
+ cnt = (max_channels / 3) + 1;
+ while ((cnt--) && (result == 0)) {
+ /*
+ * Get all channels, including empty ones.
+ * In 1 iteration at max 3 channels can be found.
+ */
+ result = fmd_rx_get_scan_band_info(cnt * 3,
+ num_of_scanfreq,
+ channels, rssi);
+ if (0 == result) {
+ index = cnt * 3;
+ /* Convert Freq to Hz from channel number */
+ scan_freq[index] = (minfreq +
+ channels[0] *
+ CHANNEL_FREQ_CONVERTER_MHZ) *
+ FREQUENCY_CONVERTOR_KHZ_HZ;
+ scan_freq_rssi_level[index] = rssi[0];
+ /* Convert Freq to Hz from channel number */
+ scan_freq[index + 1] = (minfreq +
+ channels[1] *
+ CHANNEL_FREQ_CONVERTER_MHZ) *
+ FREQUENCY_CONVERTOR_KHZ_HZ;
+ scan_freq_rssi_level[index + 1] = rssi[1];
+ /* Check if we donot overwrite the array */
+ if (cnt < (max_channels / 3)) {
+ /* Convert Freq to Hz from channel number */
+ scan_freq[index + 2] = (minfreq +
+ channels[2] *
+ CHANNEL_FREQ_CONVERTER_MHZ) *
+ FREQUENCY_CONVERTOR_KHZ_HZ;
+ scan_freq_rssi_level[index + 2]
+ = rssi[2];
+ }
+ }
+ }
+ if (fm_prev_rds_status) {
+ /* Restart RDS if it was active earlier */
+ result = cg2900_fm_rds_on();
+ fm_prev_rds_status = false;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_get_scan_result: returning %d",
+ result);
+ return result;
+
+}
+
+int cg2900_fm_start_block_scan(
+ u32 start_freq,
+ u32 end_freq
+ )
+{
+ int result;
+ u8 antenna;
+
+ FM_INFO_REPORT("cg2900_fm_start_block_scan");
+
+ FM_DEBUG_REPORT("cg2900_fm_start_block_scan: Start Freq = %d, "
+ "End Freq = %d", start_freq, end_freq);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_start_block_scan: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ if (fm_rds_status) {
+ /* Stop RDS if it is active */
+ result = cg2900_fm_rds_off();
+ fm_prev_rds_status = true;
+ }
+ result = fmd_get_antenna(
+ &antenna);
+ result = fmd_rx_block_scan(
+ start_freq/FREQUENCY_CONVERTOR_KHZ_HZ,
+ end_freq/FREQUENCY_CONVERTOR_KHZ_HZ,
+ antenna);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_start_block_scan: "
+ "Error Code %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ result = 0;
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_start_block_scan: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_get_block_scan_result(
+ u16 *num_of_scanchan,
+ u16 *scan_freq_rssi_level
+ )
+{
+ int result = 0;
+ u32 cnt;
+ u32 index;
+ u16 rssi[6];
+
+ FM_INFO_REPORT("cg2900_fm_get_block_scan_result");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_get_block_scan_result: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ cnt = 33;
+ while ((cnt--) && (result == 0)) {
+ /* Get all channels, including empty ones */
+ result = fmd_rx_get_block_scan_result(
+ cnt * 6,
+ num_of_scanchan,
+ rssi);
+ if (0 == result) {
+ index = cnt * 6;
+ scan_freq_rssi_level[index]
+ = rssi[0];
+ scan_freq_rssi_level[index + 1]
+ = rssi[1];
+ scan_freq_rssi_level[index + 2]
+ = rssi[2];
+ scan_freq_rssi_level[index + 3]
+ = rssi[3];
+ scan_freq_rssi_level[index + 4]
+ = rssi[4];
+ scan_freq_rssi_level[index + 5]
+ = rssi[5];
+ }
+ }
+ if (CG2900_FM_RX_MODE == fm_mode) {
+ if (fm_prev_rds_status) {
+ /* Restart RDS if it was active earlier*/
+ result = cg2900_fm_rds_on();
+ fm_prev_rds_status = false;
+ }
+ } else if (CG2900_FM_TX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_get_block_scan_result:"
+ " Sending Set fmd_tx_set_pa");
+
+ /* Enable the PA */
+ result = fmd_tx_set_pa(true);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_get_block_scan_result:"
+ " fmd_tx_set_pa "
+ "failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_get_block_scan_result: returning %d",
+ result);
+ return result;
+
+}
+
+int cg2900_fm_tx_rds(
+ bool enable_rds
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_rds: enable_rds = %d", enable_rds);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_rds: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ if (enable_rds) {
+ /* Set the Tx Buffer Size */
+ result = fmd_tx_buffer_set_size(
+ MAX_RDS_GROUPS - 2);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_rds: "
+ "fmd_tx_buffer_set_size "
+ "failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ } else {
+ result = fmd_tx_set_rds(true);
+ }
+
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_rds: "
+ "fmd_tx_set_rds "
+ "failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ program_identification_code =
+ default_program_identification_code;
+ program_type_code = default_program_type_code;
+ memcpy(program_service,
+ default_program_service,
+ MAX_PSN_SIZE);
+ memcpy(radio_text,
+ default_radio_text, MAX_RT_SIZE);
+ radio_text[strlen(radio_text)] = 0x0D;
+ cg2900_fm_transmit_rds_groups();
+ result = 0;
+ } else {
+ result = fmd_tx_set_rds(false);
+
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_rds: "
+ "fmd_tx_set_rds "
+ "failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_rds: returning %d",
+ result);
+
+ return result;
+}
+
+int cg2900_fm_tx_set_pi_code(
+ u16 pi_code
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_set_pi_code: PI = %04x", pi_code);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_pi_code: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ program_identification_code = pi_code;
+ result = cg2900_fm_transmit_rds_groups();
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_set_pi_code: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_set_pty_code(
+ u16 pty_code
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_set_pty_code: PTY = %04x", pty_code);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_pty_code: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ program_type_code = pty_code;
+ result = cg2900_fm_transmit_rds_groups();
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_set_pty_code: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_set_program_station_name(
+ char *psn,
+ u8 len
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_set_program_station_name: PSN = %s",
+ psn);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_program_station_name: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ if (len < (MAX_PSN_SIZE - 1)) {
+ int count = len;
+ while (count < (MAX_PSN_SIZE - 1))
+ psn[count++] = ' ';
+ }
+ memcpy(program_service, psn, MAX_PSN_SIZE);
+ result = cg2900_fm_transmit_rds_groups();
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_set_program_station_name: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_set_radio_text(
+ char *rt,
+ u8 len
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_set_radio_text: RT = %s", rt);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_radio_text: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ rt[len] = 0x0D;
+ memcpy(radio_text, rt, len + 1);
+
+ result = cg2900_fm_transmit_rds_groups();
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_set_radio_text: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_get_rds_deviation(
+ u16 *deviation
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_get_rds_deviation");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_get_rds_deviation: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_tx_get_rds_deviation(deviation);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_get_rds_deviation: "
+ "fmd_tx_get_rds_deviation failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_get_rds_deviation: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_set_rds_deviation(
+ u16 deviation
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_set_rds_deviation: deviation = %d",
+ deviation);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_rds_deviation: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_tx_set_rds_deviation(deviation);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_rds_deviation: "
+ "fmd_tx_set_rds_deviation failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_set_rds_deviation: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_get_pilot_tone_status(
+ bool *enable
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_get_pilot_tone_status");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_get_pilot_tone_status: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_tx_get_stereo_mode(enable);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_get_pilot_tone_status: "
+ "fmd_tx_get_stereo_mode failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_get_pilot_tone_status: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_set_pilot_tone_status(
+ bool enable
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_set_pilot_tone_status: enable = %d",
+ enable);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_pilot_tone_status: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_tx_enable_stereo_mode(enable);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_pilot_tone_status: "
+ "fmd_tx_enable_stereo_mode failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_set_pilot_tone_status: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_get_pilot_deviation(
+ u16 *deviation
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_get_pilot_deviation");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_get_pilot_deviation: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_tx_get_pilot_deviation(deviation);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_get_pilot_deviation: "
+ "fmd_tx_get_pilot_deviation failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_get_pilot_deviation: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_set_pilot_deviation(
+ u16 deviation
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_set_pilot_deviation: deviation = %d",
+ deviation);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_pilot_deviation: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_tx_set_pilot_deviation(deviation);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_pilot_deviation: "
+ "fmd_tx_set_pilot_deviation failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_set_pilot_deviation: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_get_preemphasis(
+ u8 *preemphasis
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_get_preemphasis");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_get_preemphasis: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_tx_get_preemphasis(preemphasis);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_get_preemphasis: "
+ "fmd_tx_get_preemphasis failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_get_preemphasis: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_set_preemphasis(
+ u8 preemphasis
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_set_preemphasis: preemphasis = %d",
+ preemphasis);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_preemphasis: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_tx_set_preemphasis(preemphasis);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_preemphasis: "
+ "fmd_tx_set_preemphasis failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_set_preemphasis: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_rx_set_deemphasis(
+ u8 deemphasis
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_rx_set_deemphasis: deemphasis = %02x",
+ deemphasis);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_rx_set_deemphasis: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_rx_set_deemphasis(deemphasis);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_rx_set_deemphasis: "
+ "fmd_rx_set_deemphasis failed %d",
+ result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_rx_set_deemphasis: returning %d", result);
+ return result;
+}
+
+int cg2900_fm_tx_get_power_level(
+ u16 *power_level
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_get_power_level");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_get_power_level: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_tx_get_signal_strength(power_level);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_get_power_level: "
+ "fmd_tx_get_signal_strength failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_get_power_level: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_tx_set_power_level(
+ u16 power_level
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_tx_set_power_level: power_level = %d",
+ power_level);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_power_level: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_tx_set_signal_strength(power_level);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_tx_set_power_level: "
+ "fmd_tx_set_preemphasis failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_tx_set_power_level: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_set_audio_balance(
+ s8 balance
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_set_audio_balance, balance = %d", balance);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_set_audio_balance: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_set_balance(balance);
+ if (0 != result) {
+ FM_ERR_REPORT("FMRSetAudioBalance : "
+ "Failed in fmd_set_balance, err = %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_set_audio_balance: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_set_volume(
+ u8 vol_level
+ )
+{
+ int result;
+ u8 vol_in_percentage;
+
+ FM_INFO_REPORT("cg2900_fm_set_volume: Volume Level = %d", vol_level);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_set_volume: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ vol_in_percentage =
+ (u8) (((u16) (vol_level) * 100) / MAX_ANALOG_VOLUME);
+ result = fmd_set_volume(vol_in_percentage);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_increase_volume: "
+ "FMRSetVolume failed, err = %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_set_volume: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_get_volume(
+ u8 *vol_level
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_get_volume");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_get_volume: "
+ "Invalid state of FM Driver = %d", fm_state);
+ *vol_level = 0;
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_get_volume(vol_level);
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_get_volume: returning %d, VolLevel = %d",
+ result, *vol_level);
+ return result;
+}
+
+int cg2900_fm_rds_off(void)
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_rds_off");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_rds_off: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_rx_set_rds(FMD_SWITCH_OFF_RDS);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_rds_off: fmd_rx_set_rds failed, "
+ "err = %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ /* Stop the RDS Thread */
+ fm_rds_status = false;
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ FM_DEBUG_REPORT("cg2900_fm_rds_off: "
+ "Stopping RDS Thread");
+ fmd_stop_rds_thread();
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_rds_off: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_rds_on(void)
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_rds_on");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_rds_on: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ FM_DEBUG_REPORT("cg2900_fm_rds_on:"
+ " Sending fmd_rx_buffer_set_size");
+ result = fmd_rx_buffer_set_size(MAX_RDS_GROUPS);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_rds_on: fmd_rx_buffer_set_size"
+ "failed, err = %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ FM_DEBUG_REPORT("cg2900_fm_rds_on: Sending "
+ "fmd_rx_buffer_set_threshold");
+ result = fmd_rx_buffer_set_threshold(MAX_RDS_GROUPS - 1);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_rds_on: fmd_rx_buffer_set_threshold "
+ "failed, err = %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ FM_DEBUG_REPORT("cg2900_fm_rds_on: Sending fmd_rx_set_rds");
+ result = fmd_rx_set_rds(FMD_SWITCH_ON_RDS_ENHANCED_MODE);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_rds_on: fmd_rx_set_rds failed, "
+ "err = %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ /* Start the RDS Thread to read the RDS Buffers */
+ fm_rds_status = true;
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ fmd_start_rds_thread(cg2900_fm_rds_callback);
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_rds_on: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_get_rds_status(
+ bool *rds_status
+ )
+{
+ int result = 0;
+
+ FM_INFO_REPORT("cg2900_fm_get_rds_status");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_get_rds_status: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ if (CG2900_FM_RX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_get_rds_status: "
+ "fmd_rx_get_rds");
+ result = fmd_rx_get_rds(rds_status);
+ } else if (CG2900_FM_TX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_get_rds_status: "
+ "fmd_tx_get_rds");
+ result = fmd_tx_get_rds(rds_status);
+ }
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_get_rds_status: "
+ "fmd_get_rds failed, Error Code %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_get_rds_status: returning %d, "
+ "rds_status = %d", result,
+ *rds_status);
+ return result;
+}
+
+int cg2900_fm_mute(void)
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_mute");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_mute: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+
+ /* Mute Analog DAC */
+ result = fmd_set_mute(true);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_mute: "
+ "fmd_set_mute failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ /* Mute Ext Src */
+ result = fmd_ext_set_mute(true);
+ if (0 != result) {
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_mute: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_unmute(void)
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_unmute");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_unmute: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ /* Unmute Analog DAC */
+ result = fmd_set_mute(false);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_mute: "
+ "fmd_set_mute failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ /* Unmute Ext Src */
+ result = fmd_ext_set_mute(false);
+ if (0 != result) {
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_unmute: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_get_frequency(
+ u32 *freq
+ )
+{
+ int result = 0;
+ u32 currentFreq = 0;
+
+ FM_INFO_REPORT("cg2900_fm_get_frequency");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_get_frequency: "
+ "Invalid state of FM Driver = %d", fm_state);
+ *freq = 0;
+ result = -EINVAL;
+ goto error;
+ }
+ if (CG2900_FM_RX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_get_frequency: "
+ "fmd_rx_get_frequency");
+ result = fmd_rx_get_frequency(
+ (u32 *) &currentFreq);
+ } else if (CG2900_FM_TX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_get_frequency: "
+ "fmd_tx_get_frequency");
+ result = fmd_tx_get_frequency(
+ (u32 *) &currentFreq);
+ }
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_get_frequency: "
+ "fmd_rx_get_frequency failed %d",
+ (unsigned int)result);
+ *freq = 0;
+ result = -EINVAL;
+ goto error;
+ }
+ /* Convert To Hz */
+ *freq = currentFreq * FREQUENCY_CONVERTOR_KHZ_HZ;
+ FM_DEBUG_REPORT("cg2900_fm_get_frequency: "
+ "Current Frequency = %d Hz", *freq);
+ if (fm_prev_rds_status) {
+ /* Restart RDS if it was active earlier */
+ cg2900_fm_rds_on();
+ fm_prev_rds_status = false;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_get_frequency: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_set_frequency(
+ u32 new_freq
+ )
+{
+ int result = 0;
+
+ FM_INFO_REPORT("cg2900_fm_set_frequency, new_freq = %d",
+ (int)new_freq);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_set_frequency: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ if (CG2900_FM_RX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_set_frequency: "
+ "fmd_rx_set_frequency");
+ result = fmd_rx_set_frequency(
+ new_freq / FREQUENCY_CONVERTOR_KHZ_HZ);
+ } else if (CG2900_FM_TX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_set_frequency: "
+ "fmd_tx_set_frequency");
+ result = fmd_tx_set_frequency(
+ new_freq / FREQUENCY_CONVERTOR_KHZ_HZ);
+ }
+ if (result != 0) {
+ FM_ERR_REPORT("cg2900_fm_set_frequency: "
+ "fmd_rx_set_frequency failed %x",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ if (CG2900_FM_TX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_set_frequency:"
+ " Sending Set" "fmd_tx_set_pa");
+
+ /* Enable the PA */
+ result = fmd_tx_set_pa(true);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_frequency:"
+ " fmd_tx_set_pa "
+ "failed %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ result = 0;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_set_frequency: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_get_signal_strength(
+ u16 *signal_strength
+ )
+{
+ int result = 0;
+
+ FM_INFO_REPORT("cg2900_fm_get_signal_strength");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_get_signal_strength: "
+ "Invalid state of FM Driver = %d", fm_state);
+ *signal_strength = 0;
+ result = -EINVAL;
+ goto error;
+ }
+ if (CG2900_FM_RX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_get_signal_strength: "
+ "fmd_rx_get_signal_strength");
+ result = fmd_rx_get_signal_strength(
+ signal_strength);
+ } else if (CG2900_FM_TX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_get_signal_strength: "
+ "fmd_tx_get_signal_strength");
+ result = fmd_tx_get_signal_strength(
+ signal_strength);
+ }
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_get_signal_strength: "
+ "Error Code %d", (unsigned int)result);
+ *signal_strength = 0;
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_get_signal_strength: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_af_update_get_result(
+ u16 *af_update_rssi
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_af_update_get_result");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_af_update_get_result: "
+ "Invalid state of FM Driver = %d", fm_state);
+ *af_update_rssi = 0;
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_rx_get_af_update_result(af_update_rssi);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_af_update_get_result: "
+ "Error Code %d", (unsigned int)result);
+ *af_update_rssi = 0;
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_af_update_get_result: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_af_update_start(
+ u32 af_freq
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_af_update_start");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_af_update_start: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_rx_af_update_start(
+ af_freq / FREQUENCY_CONVERTOR_KHZ_HZ);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_af_update_start: "
+ "Error Code %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_af_update_start: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_af_switch_get_result(
+ u16 *af_switch_conclusion
+ )
+{
+ int result;
+ u16 af_rssi;
+ u16 af_pi;
+
+ FM_INFO_REPORT("cg2900_fm_af_switch_get_result");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_af_switch_get_result: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_rx_get_af_switch_results(
+ af_switch_conclusion,
+ &af_rssi, &af_pi);
+
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_af_switch_get_result: "
+ "Error Code %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+ FM_DEBUG_REPORT("cg2900_fm_af_switch_get_result: "
+ "AF Switch conclusion = %d "
+ "AF Switch RSSI level = %d "
+ "AF Switch PI code = %d ",
+ *af_switch_conclusion, af_rssi, af_pi);
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_af_switch_get_result: returning %d",
+ result);
+ return result;
+
+}
+
+int cg2900_fm_af_switch_start(
+ u32 af_switch_freq,
+ u16 af_switch_pi
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_af_switch_start");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_af_switch_start: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_rx_af_switch_start(
+ af_switch_freq / FREQUENCY_CONVERTOR_KHZ_HZ,
+ af_switch_pi);
+
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_af_switch_start: "
+ "Error Code %d", (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_af_switch_start: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_get_mode(
+ u8 *cur_mode
+ )
+{
+ int result = 0;
+ bool stereo_mode;
+
+ FM_INFO_REPORT("cg2900_fm_get_mode");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_get_mode: "
+ "Invalid state of FM Driver = %d", fm_state);
+ *cur_mode = CG2900_MODE_MONO;
+ result = -EINVAL;
+ goto error;
+ }
+ if (CG2900_FM_RX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_get_mode: "
+ "fmd_rx_get_stereo_mode");
+ result = fmd_rx_get_stereo_mode(cur_mode);
+ switch (*cur_mode) {
+ case FMD_STEREOMODE_OFF:
+ case FMD_STEREOMODE_BLENDING:
+ *cur_mode = CG2900_MODE_STEREO;
+ break;
+ case FMD_STEREOMODE_MONO:
+ default:
+ *cur_mode = CG2900_MODE_MONO;
+ break;
+ }
+ } else if (CG2900_FM_TX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_get_mode: "
+ "fmd_tx_get_stereo_mode");
+ result = fmd_tx_get_stereo_mode(&stereo_mode);
+ if (stereo_mode)
+ *cur_mode = CG2900_MODE_STEREO;
+ else
+ *cur_mode = CG2900_MODE_MONO;
+ }
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_get_mode: "
+ "fmd_get_stereo_mode failed, "
+ "Error Code %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_get_mode: returning %d, mode = %d",
+ result, *cur_mode);
+ return result;
+}
+
+int cg2900_fm_set_mode(
+ u8 mode
+ )
+{
+ int result = 0;
+ bool enable_stereo_mode = false;
+
+ FM_INFO_REPORT("cg2900_fm_set_mode: mode = %d", mode);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_set_mode: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ if (CG2900_FM_RX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_set_mode: "
+ "fmd_rx_set_stereo_mode");
+ result = fmd_rx_set_stereo_mode(mode);
+ } else if (CG2900_FM_TX_MODE == fm_mode) {
+ FM_DEBUG_REPORT("cg2900_fm_set_mode: "
+ "fmd_tx_set_stereo_mode");
+ if (mode == CG2900_MODE_STEREO)
+ enable_stereo_mode = true;
+ result =
+ fmd_tx_enable_stereo_mode(
+ enable_stereo_mode);
+ }
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_mode: "
+ "fmd_rx_set_stereo_mode failed, "
+ "Error Code %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_set_mode: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_select_antenna(
+ u8 antenna
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_select_antenna: Antenna = %d", antenna);
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_select_antenna: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_set_antenna(antenna);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_select_antenna: "
+ "fmd_set_antenna failed, Error Code %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_select_antenna: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_get_antenna(
+ u8 *antenna
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_get_antenna");
+
+ if (CG2900_FM_STATE_SWITCHED_ON != fm_state) {
+ FM_ERR_REPORT("cg2900_fm_get_antenna: "
+ "Invalid state of FM Driver = %d", fm_state);
+ result = -EINVAL;
+ goto error;
+ }
+ result = fmd_get_antenna(antenna);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_get_antenna: "
+ "fmd_get_antenna failed, Error Code %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_get_antenna: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_get_rssi_threshold(
+ u16 *rssi_thresold
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_get_rssi_threshold");
+
+ result = fmd_rx_get_stop_level(rssi_thresold);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_get_rssi_threshold: "
+ "fmd_rx_get_stop_level failed, Error Code %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_get_rssi_threshold: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_set_rssi_threshold(
+ u16 rssi_thresold
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_set_rssi_threshold: "
+ "RssiThresold = %d", rssi_thresold);
+
+ result = fmd_rx_set_stop_level(rssi_thresold);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_rssi_threshold: "
+ "fmd_rx_set_stop_level failed, Error Code %d",
+ (unsigned int)result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_set_rssi_threshold: returning %d",
+ result);
+ return result;
+}
+
+void cg2900_fm_set_chip_version(
+ u16 revision,
+ u16 sub_version
+ )
+{
+ version_info.revision = revision;
+ version_info.sub_version = sub_version;
+}
+
+int cg2900_fm_set_test_tone_generator(
+ u8 test_tone_status
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_set_test_tone_generator: "
+ "test_tone_status = %02x", test_tone_status);
+
+ result = fmd_set_test_tone_generator_status(test_tone_status);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_set_test_tone_generator: "
+ "fmd_set_test_tone_generator_status failed"
+ ", Error Code %d",
+ result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_set_test_tone_generator: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_test_tone_connect(
+ u8 left_audio_mode,
+ u8 right_audio_mode
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_test_tone_connect: "
+ "left_audio_mode = %02x right_audio_mode = %02x",
+ left_audio_mode, right_audio_mode);
+
+ result = fmd_test_tone_connect(left_audio_mode, right_audio_mode);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_test_tone_connect: "
+ "fmd_set_test_tone_connect failed, Error Code %d",
+ result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_test_tone_connect: returning %d",
+ result);
+ return result;
+}
+
+int cg2900_fm_test_tone_set_params(
+ u8 tone_gen,
+ u16 frequency,
+ u16 volume,
+ u16 phase_offset,
+ u16 dc,
+ u8 waveform
+ )
+{
+ int result;
+
+ FM_INFO_REPORT("cg2900_fm_test_tone_set_params: "
+ "tone_gen = %02x frequency = %04x "
+ "volume = %04x phase_offset = %04x "
+ "dc offset = %04x waveform = %02x",
+ tone_gen, frequency,
+ volume, phase_offset,
+ dc, waveform);
+
+ result = fmd_test_tone_set_params(
+ tone_gen,
+ frequency,
+ volume,
+ phase_offset,
+ dc,
+ waveform);
+ if (0 != result) {
+ FM_ERR_REPORT("cg2900_fm_test_tone_set_params: "
+ "fmd_test_tone_set_params failed, Error Code %d",
+ result);
+ result = -EINVAL;
+ goto error;
+ }
+
+error:
+ FM_DEBUG_REPORT("cg2900_fm_test_tone_set_params: returning %d",
+ result);
+ return result;
+}
+
+MODULE_AUTHOR("Hemant Gupta");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/radio/CG2900/cg2900_fm_api.h b/drivers/media/radio/CG2900/cg2900_fm_api.h
new file mode 100644
index 00000000000..747cdbc1e0d
--- /dev/null
+++ b/drivers/media/radio/CG2900/cg2900_fm_api.h
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Linux FM Host API's for ST-Ericsson FM Chip.
+ *
+ * Author: Hemant Gupta <hemant.gupta@stericsson.com> for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef CG2900_FM_API_H
+#define CG2900_FM_API_H
+
+#include <linux/device.h>
+
+/* Callback function to receive RDS Data. */
+typedef void (*cg2900_fm_rds_cb)(void);
+
+/**
+ * struct cg2900_fm_rds_buf - RDS Group Receiving Structure
+ *
+ * @block1: RDS Block A
+ * @block2: RDS Block B
+ * @block3: RDS Block C
+ * @block4: RDS Block D
+ * @status1: Status of received RDS Block A
+ * @status2: Status of received RDS Block B
+ * @status3: Status of received RDS Block C
+ * @status4: Status of received RDS Block D
+ *
+ * Structure for receiving the RDS Group from FM Chip.
+ */
+struct cg2900_fm_rds_buf {
+ u16 block1;
+ u16 block2;
+ u16 block3;
+ u16 block4;
+ u8 status1;
+ u8 status2;
+ u8 status3;
+ u8 status4;
+};
+
+/**
+ * struct cg2900_fm_rds_info - RDS Information Structure
+ *
+ * @rds_head: RDS Queue Head for storing next valid data.
+ * @rds_tail: RDS Queue Tail for retreiving next valid data.
+ * @rds_group_sent: Number of RDS Groups sent to Application.
+ * @rds_block_sent: Number of RDS Blocks sent to Application.
+ *
+ * Structure for storing the RDS data queue information.
+ */
+struct cg2900_fm_rds_info {
+ u8 rds_head;
+ u8 rds_tail;
+ u8 rds_group_sent;
+ u8 rds_block_sent;
+};
+
+/**
+ * struct cg2900_version_info - Chip HCI Version Info
+ *
+ * @revision: Revision of the controller, e.g. to indicate that it is
+ * a CG2900 controller.
+ * @sub_version: Subversion of the controller, e.g. to indicate a certain
+ * tape-out of the controller.
+ *
+ * Structure for storing the HCI Version Information of the Controller.
+ */
+struct cg2900_version_info {
+ u16 revision;
+ u16 sub_version;
+};
+
+/**
+ * enum cg2900_fm_state - States of FM Driver.
+ *
+ * @CG2900_FM_STATE_DEINITIALIZED: FM driver is not initialized.
+ * @CG2900_FM_STATE_INITIALIZED: FM driver is initialized.
+ * @CG2900_FM_STATE_SWITCHED_ON: FM driver is switched on and in active state.
+ * @CG2900_FM_STATE_STAND_BY: FM Radio is switched on but not in active state.
+ *
+ * Various states of FM Driver.
+ */
+enum cg2900_fm_state {
+ CG2900_FM_STATE_DEINITIALIZED,
+ CG2900_FM_STATE_INITIALIZED,
+ CG2900_FM_STATE_SWITCHED_ON,
+ CG2900_FM_STATE_STAND_BY
+};
+
+/**
+ * enum cg2900_fm_mode - FM Driver Command state .
+ *
+ * @CG2900_FM_IDLE_MODE: FM Radio is in Idle Mode.
+ * @CG2900_FM_RX_MODE: FM Radio is configured in Rx mode.
+ * @CG2900_FM_TX_MODE: FM Radio is configured in Tx mode.
+ *
+ * Various Modes of the FM Radio.
+ */
+enum cg2900_fm_mode {
+ CG2900_FM_IDLE_MODE,
+ CG2900_FM_RX_MODE,
+ CG2900_FM_TX_MODE
+};
+
+/**
+ * enum cg2900_fm_band - Various Frequency band supported.
+ *
+ * @CG2900_FM_BAND_US_EU: European / US Band.
+ * @CG2900_FM_BAND_JAPAN: Japan Band.
+ * @CG2900_FM_BAND_CHINA: China Band.
+ * @CG2900_FM_BAND_CUSTOM: Custom Band.
+ *
+ * Various Frequency band supported.
+ */
+enum cg2900_fm_band {
+ CG2900_FM_BAND_US_EU,
+ CG2900_FM_BAND_JAPAN,
+ CG2900_FM_BAND_CHINA,
+ CG2900_FM_BAND_CUSTOM
+};
+
+/**
+ * enum cg2900_fm_grid - Various Frequency grids supported.
+ *
+ * @CG2900_FM_GRID_50: 50 kHz spacing.
+ * @CG2900_FM_GRID_100: 100 kHz spacing.
+ * @CG2900_FM_GRID_200: 200 kHz spacing.
+ *
+ * Various Frequency grids supported.
+ */
+enum cg2900_fm_grid {
+ CG2900_FM_GRID_50,
+ CG2900_FM_GRID_100,
+ CG2900_FM_GRID_200
+};
+
+/**
+ * enum cg2900_fm_event - Various Events reported by FM API layer.
+ *
+ * @CG2900_EVENT_NO_EVENT: No Event.
+ * @CG2900_EVENT_SEARCH_CHANNEL_FOUND: Seek operation is completed.
+ * @CG2900_EVENT_SCAN_CHANNELS_FOUND: Band Scan is completed.
+ * @CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND: Block Scan is completed.
+ * @CG2900_EVENT_SCAN_CANCELLED: Scan/Seek is cancelled.
+ *
+ * Various Events reported by FM API layer.
+ */
+enum cg2900_fm_event {
+ CG2900_EVENT_NO_EVENT,
+ CG2900_EVENT_SEARCH_CHANNEL_FOUND,
+ CG2900_EVENT_SCAN_CHANNELS_FOUND,
+ CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND,
+ CG2900_EVENT_SCAN_CANCELLED
+};
+
+/**
+ * enum cg2900_fm_direction - Directions used while seek.
+ *
+ * @CG2900_DIR_DOWN: Search in downwards direction.
+ * @CG2900_DIR_UP: Search in upwards direction.
+ *
+ * Directions used while seek.
+ */
+enum cg2900_fm_direction {
+ CG2900_DIR_DOWN,
+ CG2900_DIR_UP
+};
+
+/**
+ * enum cg2900_fm_stereo_mode - Stereo Modes.
+ *
+ * @CG2900_MODE_MONO: Mono Mode.
+ * @CG2900_MODE_STEREO: Stereo Mode.
+ *
+ * Stereo Modes.
+ */
+enum cg2900_fm_stereo_mode {
+ CG2900_MODE_MONO,
+ CG2900_MODE_STEREO
+};
+
+#define CG2900_FM_DEFAULT_RSSI_THRESHOLD 100
+#define MAX_RDS_BUFFER 10
+#define MAX_RDS_GROUPS 22
+#define MIN_ANALOG_VOLUME 0
+#define MAX_ANALOG_VOLUME 20
+#define NUM_OF_RDS_BLOCKS 4
+#define RDS_BLOCK_MASK 0x1C
+#define RDS_ERROR_STATUS_MASK 0x03
+#define RDS_UPTO_TWO_BITS_CORRECTED 0x01
+#define RDS_UPTO_FIVE_BITS_CORRECTED 0x02
+#define MAX_RT_SIZE 65
+#define MAX_PSN_SIZE 9
+#define DEFAULT_CHANNELS_TO_SCAN 32
+#define MAX_CHANNELS_TO_SCAN 99
+#define MAX_CHANNELS_FOR_BLOCK_SCAN 198
+
+extern u8 fm_event;
+extern struct cg2900_fm_rds_buf fm_rds_buf[MAX_RDS_BUFFER][MAX_RDS_GROUPS];
+extern struct cg2900_fm_rds_info fm_rds_info;
+
+/**
+ * cg2900_fm_init()- Initializes FM Radio.
+ *
+ * Initializes the Variables and structures required for FM Driver.
+ * It also registers the callback to receive the events for command
+ * completion, etc
+ *
+ * Returns:
+ * 0, if Initialization successful
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_init(void);
+
+/**
+ * cg2900_fm_deinit()- De-initializes FM Radio.
+ *
+ * De-initializes the Variables and structures required for FM Driver.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_deinit(void);
+
+/**
+ * cg2900_fm_switch_on()- Start up procedure of the FM radio.
+ *
+ * @device: Character device requesting the operation.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_switch_on(
+ struct device *device
+ );
+
+/**
+ * cg2900_fm_switch_off()- Switches off FM radio
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_switch_off(void);
+
+/**
+ * cg2900_fm_standby()- Makes the FM Radio Go in Standby mode.
+ *
+ * The FM Radio memorizes the the last state, i.e. Volume, last
+ * tuned station, etc that helps in resuming quickly to previous state.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_standby(void);
+
+/**
+ * cg2900_fm_power_up_from_standby()- Power Up FM Radio from Standby mode.
+ *
+ * It retruns the FM radio to the same state as it was before
+ * going to Standby.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_power_up_from_standby(void);
+
+/**
+ * cg2900_fm_set_rx_default_settings()- Loads FM Rx Default Settings.
+ *
+ * @freq: Frequency in Hz to be set on the FM Radio.
+ * @band: Band To be Set.
+ * (0: US/EU, 1: Japan, 2: China, 3: Custom)
+ * @grid: Grid specifying Spacing.
+ * (0: 50 KHz, 1: 100 KHz, 2: 200 Khz)
+ * @enable_rds: Flag indicating enable or disable rds transmission.
+ * @enable_stereo: Flag indicating enable or disable stereo mode.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_set_rx_default_settings(
+ u32 freq,
+ u8 band,
+ u8 grid,
+ bool enable_rds,
+ bool enable_stereo
+ );
+
+/**
+ * cg2900_fm_set_tx_default_settings()- Loads FM Tx Default Settings.
+ *
+ * @freq: Frequency in Hz to be set on the FM Radio.
+ * @band: Band To be Set.
+ * (0: US/EU, 1: Japan, 2: China, 3: Custom)
+ * @grid: Grid specifying Spacing.
+ * (0: 50 KHz, 1: 100 KHz, 2: 200 Khz)
+ * @enable_rds: Flag indicating enable or disable rds transmission.
+ * @enable_stereo: Flag indicating enable or disable stereo mode.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_set_tx_default_settings(
+ u32 freq,
+ u8 band,
+ u8 grid,
+ bool enable_rds,
+ bool enable_stereo
+ );
+
+/**
+ * cg2900_fm_set_grid()- Sets the Grid on the FM Radio.
+ *
+ * @grid: Grid specifying Spacing.
+ * (0: 50 KHz,1: 100 KHz,2: 200 Khz)
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_set_grid(
+ u8 grid
+ );
+
+/**
+ * cg2900_fm_set_band()- Sets the Band on the FM Radio.
+ *
+ * @band: Band specifying Region.
+ * (0: US_EU,1: Japan,2: China,3: Custom)
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_set_band(
+ u8 band
+ );
+
+/**
+ * cg2900_fm_search_up_freq()- seek Up.
+ *
+ * Searches the next available station in Upward Direction
+ * starting from the Current freq.
+ *
+ * If the operation is started successfully, the chip will generate the
+ * irpt_OperationSucced. interrupt when the operation is completed
+ * and will tune to the next available frequency.
+ * If no station is found, the chip is still tuned to the original station
+ * before starting the search
+ * Till the interrupt is received, no more API's should be called
+ * except cg2900_fm_stop_scan
+ *
+ * Returns:
+ * 0, if operation started successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_search_up_freq(void);
+
+/**
+ * cg2900_fm_search_down_freq()- seek Down.
+ *
+ * Searches the next available station in Downward Direction
+ * starting from the Current freq.
+ *
+ * If the operation is started successfully, the chip will generate
+ * the irpt_OperationSucced. interrupt when the operation is completed.
+ * and will tune to the next available frequency. If no station is found,
+ * the chip is still tuned to the original station before starting the search.
+ * Till the interrupt is received, no more API's should be called
+ * except cg2900_fm_stop_scan.
+ *
+ * Returns:
+ * 0, if operation started successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_search_down_freq(void);
+
+/**
+ * cg2900_fm_start_band_scan()- Band Scan.
+ *
+ * Searches for available Stations in the entire Band starting from
+ * current freq.
+ * If the operation is started successfully, the chip will generate
+ * the irpt_OperationSucced. interrupt when the operation is completed.
+ * After completion the chip will still be tuned the original station before
+ * starting the Scan. on reception of interrupt, the host should call the AP
+ * cg2900_fm_get_scan_result() to retrieve the Stations and corresponding
+ * RSSI of stations found in the Band.
+ * Till the interrupt is received, no more API's should be called
+ * except cg2900_fm_stop_scan, cg2900_fm_switch_off, cg2900_fm_standby and
+ * cg2900_fm_get_frequency.
+ *
+ * Returns:
+ * 0, if operation started successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_start_band_scan(void);
+
+/**
+ * cg2900_fm_stop_scan()- Stops an active ongoing seek or Band Scan.
+ *
+ * If the operation is started successfully, the chip will generate the
+ * irpt_OperationSucced interrupt when the operation is completed.
+ * Till the interrupt is received, no more API's should be called.
+ *
+ * Returns:
+ * 0, if operation started successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_stop_scan(void);
+
+/**
+ * cg2900_fm_get_scan_result()- Retreives Band Scan Result
+ *
+ * Retrieves the Scan Band Results of the stations found and
+ * the corressponding RSSI values of the stations.
+ * @num_of_scanfreq: (out) Number of Stations found
+ * during Scanning.
+ * @scan_freq: (out) Frequency of Stations in Hz
+ * found during Scanning.
+ * @scan_freq_rssi_level: (out) RSSI level of Stations
+ * found during Scanning.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_get_scan_result(
+ u16 *num_of_scanfreq,
+ u32 *scan_freq,
+ u32 *scan_freq_rssi_level
+ );
+
+/**
+ * cg2900_fm_start_block_scan()- Block Scan.
+ *
+ * Searches for RSSI level of all the channels between the start and stop
+ * channels. If the operation is started successfully, the chip will generate
+ * the irpt_OperationSucced interrupt when the operation is completed.
+ * After completion the chip will still be tuned the original station before
+ * starting the Scan. On reception of interrupt, the host should call the AP
+ * cg2900_fm_get_block_scan_result() to retrieve the RSSI of channels.
+ * Till the interrupt is received, no more API's should be called from Host
+ * except cg2900_fm_stop_scan, cg2900_fm_switch_off, cg2900_fm_standby and
+ * cg2900_fm_get_frequency.
+ * @start_freq: Start channel block scan Frequency.
+ * @end_freq: End channel block scan Frequency
+ *
+ * Returns:
+ * 0, if operation started successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_start_block_scan(
+ u32 start_freq,
+ u32 end_freq
+ );
+
+/**
+ * cg2900_fm_get_scan_result()- Retreives Band Scan Result
+ *
+ * Retrieves the Scan Band Results of the stations found and
+ * the corressponding RSSI values of the stations.
+ * @num_of_scanchan: (out) Number of Stations found
+ * during Scanning.
+ * @scan_freq_rssi_level: (out) RSSI level of Stations
+ * found during Scanning.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_get_block_scan_result(
+ u16 *num_of_scanchan,
+ u16 *scan_freq_rssi_level
+ );
+
+/**
+ * cg2900_fm_tx_get_rds_deviation()- Gets RDS Deviation.
+ *
+ * Retrieves the RDS Deviation level set for FM Tx.
+ * @deviation: (out) Rds Deviation.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_get_rds_deviation(
+ u16 *deviation
+ );
+
+/**
+ * cg2900_fm_tx_set_rds_deviation()- Sets RDS Deviation.
+ *
+ * Sets the RDS Deviation level on FM Tx.
+ * @deviation: Rds Deviation to set on FM Tx.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_set_rds_deviation(
+ u16 deviation
+ );
+
+/**
+ * cg2900_fm_tx_set_pi_code()- Sets PI code for RDS Transmission.
+ *
+ * Sets the Program Identification code to be transmitted.
+ * @pi_code: PI code to be transmitted.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_set_pi_code(
+ u16 pi_code
+ );
+
+/**
+ * cg2900_fm_tx_set_pty_code()- Sets PTY code for RDS Transmission.
+ *
+ * Sets the Program Type code to be transmitted.
+ * @pty_code: PTY code to be transmitted.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_set_pty_code(
+ u16 pty_code
+ );
+
+/**
+ * cg2900_fm_tx_set_program_station_name()- Sets PSN for RDS Transmission.
+ *
+ * Sets the Program Station Name to be transmitted.
+ * @psn: Program Station Name to be transmitted.
+ * @len: Length of Program Station Name to be transmitted.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_set_program_station_name(
+ char *psn,
+ u8 len
+ );
+
+/**
+ * cg2900_fm_tx_set_radio_text()- Sets RT for RDS Transmission.
+ *
+ * Sets the radio text to be transmitted.
+ * @rt: Radio Text to be transmitted.
+ * @len: Length of Radio Text to be transmitted.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_set_radio_text(
+ char *rt,
+ u8 len
+ );
+
+/**
+ * cg2900_fm_tx_get_rds_deviation()- Gets Pilot Tone status
+ *
+ * Gets the current status of pilot tone for FM Tx.
+ * @enable: (out) Flag indicating Pilot Tone is enabled or disabled.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_get_pilot_tone_status(
+ bool *enable
+ );
+
+/**
+ * cg2900_fm_tx_set_pilot_tone_status()- Enables/Disables Pilot Tone.
+ *
+ * Enables or disables the pilot tone for FM Tx.
+ * @enable: Flag indicating enabling or disabling Pilot Tone.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_set_pilot_tone_status(
+ bool enable
+ );
+
+/**
+ * cg2900_fm_tx_get_pilot_deviation()- Gets Pilot Deviation.
+ *
+ * Retrieves the Pilot Tone Deviation level set for FM Tx.
+ * @deviation: (out) Pilot Tone Deviation.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_get_pilot_deviation(
+ u16 *deviation
+ );
+
+/**
+ * cg2900_fm_tx_set_pilot_deviation()- Sets Pilot Deviation.
+ *
+ * Sets the Pilot Tone Deviation level on FM Tx.
+ * @deviation: Pilot Tone Deviation to set.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_set_pilot_deviation(
+ u16 deviation
+ );
+
+/**
+ * cg2900_fm_tx_get_preemphasis()- Gets Pre-emhasis level.
+ *
+ * Retrieves the Preemphasis level set for FM Tx.
+ * @preemphasis: (out) Preemphasis level.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_get_preemphasis(
+ u8 *preemphasis
+ );
+
+/**
+ * cg2900_fm_tx_set_preemphasis()- Sets Pre-emhasis level.
+ *
+ * Sets the Preemphasis level on FM Tx.
+ * @preemphasis: Preemphasis level.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_set_preemphasis(
+ u8 preemphasis
+ );
+
+/**
+ * cg2900_fm_tx_get_power_level()- Gets Power level.
+ *
+ * Retrieves the Power level set for FM Tx.
+ * @power_level: (out) Power level.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_get_power_level(
+ u16 *power_level
+ );
+
+/**
+ * cg2900_fm_tx_set_power_level()- Sets Power level.
+ *
+ * Sets the Power level for FM Tx.
+ * @power_level: Power level.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_set_power_level(
+ u16 power_level
+ );
+
+/**
+ * cg2900_fm_tx_rds()- Enable or disable Tx RDS.
+ *
+ * Enable or disable RDS transmission.
+ * @enable_rds: Flag indicating enabling or disabling RDS.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_tx_rds(
+ bool enable_rds
+ );
+
+/**
+ * cg2900_fm_set_audio_balance()- Sets Audio Balance.
+ *
+ * @balance: Audio Balnce to be Set in Percentage.
+ * (-100: Right Mute.... 0: Both on.... 100: Left Mute)
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_set_audio_balance(
+ s8 balance
+ );
+
+/**
+ * cg2900_fm_set_volume()- Sets the Analog Out Gain of FM Chip.
+ *
+ * @vol_level: Volume Level to be set on Tuner (0-20).
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_set_volume(
+ u8 vol_level
+ );
+
+/**
+ * cg2900_fm_get_volume()- Gets the currently set Analog Out Gain of FM Chip.
+ *
+ * @vol_level: (out)Volume Level set on Tuner (0-20).
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_get_volume(
+ u8 *vol_level
+ );
+
+/**
+ * cg2900_fm_rds_off()- Disables the RDS decoding algorithm in FM chip
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_rds_off(void);
+
+/**
+ * cg2900_fm_rds_on()- Enables the RDS decoding algorithm in FM chip
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_rds_on(void);
+
+/**
+ * cg2900_fm_get_rds_status()- Retrieves the status whether RDS is enabled or not
+ *
+ * @rds_status: (out) Status of RDS
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_get_rds_status(
+ bool *rds_status
+ );
+
+/**
+ * cg2900_fm_mute()- Mutes the Audio output from FM Chip
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_mute(void);
+
+/**
+ * cg2900_fm_unmute()- Unmutes the Audio output from FM Chip
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_unmute(void);
+
+/**
+ * cg2900_fm_get_frequency()- Gets the Curently tuned Frequency on FM Radio
+ *
+ * @freq: (out) Frequency in Hz set on the FM Radio.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_get_frequency(
+ u32 *freq
+ );
+
+/**
+ * cg2900_fm_set_frequency()- Sets the frequency on FM Radio
+ *
+ * @new_freq: Frequency in Hz to be set on the FM Radio.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_set_frequency(
+ u32 new_freq
+ );
+
+/**
+ * cg2900_fm_get_signal_strength()- Gets the RSSI level.
+ *
+ * @signal_strength: (out) RSSI level of the currently
+ * tuned frequency.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_get_signal_strength(
+ u16 *signal_strength
+ );
+
+/**
+ * cg2900_fm_get_af_updat()- Retrives results of AF Update
+ *
+ * @af_update_rssi: (out) RSSI level of the Alternative frequency.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_af_update_get_result(
+ u16 *af_update_rssi
+ );
+
+
+/**
+ * cg2900_fm_af_update_start()- PErforms AF Update.
+ *
+ * @af_freq: AF frequency in Hz whose RSSI is to be retrived.
+ * tuned frequency.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+
+int cg2900_fm_af_update_start(
+ u32 af_freq
+ );
+
+/**
+ * cg2900_fm_af_switch_get_result()- Retrives the AF switch result.
+ *
+ * @af_switch_conclusion: (out) Conclusion of the AF Switch.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_af_switch_get_result(
+ u16 *af_switch_conclusion
+ );
+
+/**
+ * cg2900_fm_af_switch_start()- PErforms AF switch.
+ *
+ * @af_switch_freq: Alternate Frequency in Hz to be switched.
+ * @af_switch_pi: picode of the Alternative frequency.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_af_switch_start(
+ u32 af_switch_freq,
+ u16 af_switch_pi
+ );
+
+/**
+ * cg2900_fm_get_mode()- Gets the mode of the Radio tuner.
+ *
+ * @cur_mode: (out) Current mode set on FM Radio
+ * (0: Stereo, 1: Mono, 2: Blending, 3: Switching).
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_get_mode(
+ u8 *cur_mode
+ );
+
+/**
+ * cg2900_fm_set_mode()- Sets the mode on the Radio tuner.
+ *
+ * @mode: mode to be set on FM Radio
+ * (0: Stereo, 1: Mono, 2: Blending, 3: Switching.)
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_set_mode(
+ u8 mode
+ );
+
+/**
+ * cg2900_fm_select_antenna()- Selects the Antenna of the Radio tuner.
+ *
+ * @antenna: (0: Embedded, 1: Wired.)
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_select_antenna(
+ u8 antenna
+ );
+
+/**
+ * cg2900_fm_get_antenna()- Retreives the currently selected antenna.
+ *
+ * @antenna: out (0: Embedded, 1: Wired.)
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_get_antenna(
+ u8 *antenna
+ );
+
+/**
+ * cg2900_fm_get_rssi_threshold()- Gets the rssi threshold currently
+ *
+ * set on FM radio.
+ * @rssi_thresold: (out) Current rssi threshold set.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_get_rssi_threshold(
+ u16 *rssi_thresold
+ );
+
+/**
+ * cg2900_fm_set_rssi_threshold()- Sets the rssi threshold to be used during
+ *
+ * Band Scan and seek Stations
+ * @rssi_thresold: rssi threshold to be set.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_set_rssi_threshold(
+ u16 rssi_thresold
+ );
+
+/**
+ * cg2900_handle_device_reset()- Handle The reset of Device
+ */
+void cg2900_handle_device_reset(void);
+
+/**
+ * wake_up_poll_queue()- Wakes up the Task waiting on Poll Queue.
+ * This function is called when Scan Band or seek has completed.
+ */
+void wake_up_poll_queue(void);
+
+/**
+ * void wake_up_read_queue()- Wakes up the Task waiting on Read Queue.
+ * This function is called when RDS data is available for reading by
+ * application.
+ */
+void wake_up_read_queue(void);
+
+/**
+ * void cg2900_fm_set_chip_version()- Sets the Version of the Controller.
+ *
+ * This function is used to update the Chip Version information at time
+ * of intitialization of FM driver.
+ * @revision: Revision of the controller, e.g. to indicate that it is
+ * a CG2900 controller.
+ * @sub_version: Subversion of the controller, e.g. to indicate a certain
+ * tape-out of the controller.
+ */
+void cg2900_fm_set_chip_version(
+ u16 revision,
+ u16 sub_version
+ );
+
+/**
+ * cg2900_fm_rx_set_deemphasis()- Sets de-emhasis level.
+ *
+ * Sets the Deemphasis level on FM Rx.
+ * @deemphasis: Deemphasis level.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_rx_set_deemphasis(
+ u8 deemphasis
+ );
+
+/**
+ * cg2900_fm_set_test_tone_generator()- Sets the Test Tone Generator.
+ *
+ * This function is used to enable/disable the Internal Tone Generator of
+ * CG2900.
+ * @test_tone_status: Status of tone generator.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_set_test_tone_generator(
+ u8 test_tone_status
+ );
+
+
+/**
+ * cg2900_fm_test_tone_connect()- Connect Audio outputs/inputs.
+ *
+ * This function connects the audio outputs/inputs of the Internal Tone
+ * Generator of CG2900.
+ * @left_audio_mode: Left Audio Output Mode.
+ * @right_audio_mode: Right Audio Output Mode.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_test_tone_connect(
+ u8 left_audio_mode,
+ u8 right_audio_mode
+ );
+
+/**
+ * cg2900_fm_test_tone_set_params()- Sets the Test Tone Parameters.
+ *
+ * This function is used to set the parameters of the Internal Tone Generator of
+ * CG2900.
+ * @tone_gen: Tone to be configured (Tone 1 or Tone 2)
+ * @frequency: Frequency of the tone.
+ * @volume: Volume of the tone.
+ * @phase_offset: Phase offset of the tone.
+ * @dc: DC to add to tone.
+ * @waveform: Waveform to generate, sine or pulse.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int cg2900_fm_test_tone_set_params(
+ u8 tone_gen,
+ u16 frequency,
+ u16 volume,
+ u16 phase_offset,
+ u16 dc,
+ u8 waveform
+ );
+
+#endif /* CG2900_FM_API_H */
diff --git a/drivers/media/radio/CG2900/cg2900_fm_driver.c b/drivers/media/radio/CG2900/cg2900_fm_driver.c
new file mode 100644
index 00000000000..6ac380409c1
--- /dev/null
+++ b/drivers/media/radio/CG2900/cg2900_fm_driver.c
@@ -0,0 +1,4829 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Linux FM Driver for CG2900 FM Chip
+ *
+ * Author: Hemant Gupta <hemant.gupta@stericsson.com> for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/device.h>
+#include <linux/time.h>
+#include <linux/mfd/cg2900.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <asm-generic/errno-base.h>
+#include "cg2900_fm_driver.h"
+
+/*
+ * Macro for printing the HCI Packet received from Protocol Driver
+ * to FM Driver.
+ */
+#define CG2900_HEX_READ_PACKET_DUMP \
+ if (cg2900_fm_debug_level == FM_HCI_PACKET_LOGS) \
+ fmd_hexdump('<', skb->data, skb->len);
+
+/* Macro for printing the HCI Packet sent to Protocol Driver from FM Driver */
+#define CG2900_HEX_WRITE_PACKET_DUMP \
+ if (cg2900_fm_debug_level == FM_HCI_PACKET_LOGS) \
+ fmd_hexdump('>', send_buffer, num_bytes);
+
+/* Converts the given value to ASCII format*/
+#define ASCVAL(x)(((x) <= 9) ? (x) + '0' : (x) - 10 + 'a')
+
+/* The receive Packet's 1st byte indicates the packet length */
+#define FM_GET_PKT_LEN(__data) __data[0]
+
+/* The receive Packet's following bytes are the actual data. */
+#define FM_GET_RSP_PKT_ADDR(__data) (&__data[1])
+
+/*
+ * LSB consists of shifting the command Id by 3 bits
+ * to left and ORING with the number of parameters of the
+ * command.
+ */
+#define FM_CMD_GET_LSB(__cmd_id, __num_param) \
+ ((u8)(((__cmd_id << 3) & 0x00FF) | __num_param))
+
+/* MSB consists of shifting the command Id by 5 bits to right. */
+#define FM_CMD_GET_MSB(__cmd_id) \
+ ((u8)(__cmd_id >> 5))
+
+/*
+ * Command id is mapped as shifting the MSB 5 bits to left and
+ * ORING with LSB shifted 3 bits to right.
+ */
+#define FM_GET_CMD_ID(__data) \
+ ((u16)((__data[2] << 5) | __data[1] >> 3))
+
+/*
+ * Number of parameters in the response packet are the last 3 bits
+ * of the 1st byte of the received packet.
+ */
+#define FM_GET_NUM_PARAMS(__data) \
+ ((u16)((__data[1] & 0x07)))
+
+/* Function Id is mapped to the 1st byte of the received packet */
+#define FM_GET_FUNCTION_ID(__data) __data[0]
+
+/*
+ * Block Id of the FM Firmware downloaded is mapped to the
+ * 2nd byte of the received packet.
+ */
+#define FM_GET_BLOCK_ID(__data) __data[1]
+
+/* Status of the received packet is mapped to the 4th byte. */
+#define FM_GET_STATUS(__data) __data[3]
+
+/*
+ * For PG1 of CG2900, the FM Interrupt is mapped
+ * to the 3rd and 4th byte of the received packet.
+ */
+#define FM_GET_PGI_INTERRUPT(__data) \
+ ((u16)(__data[3] << 8 | __data[2]))
+
+/*
+ * For PG2 of CG2900, the FM Interrupt is mapped
+ * to the 5th and 6th byte of the received packet.
+ */
+#define FM_GET_PG2_INTERRUPT(__data) \
+ ((u16)(__data[5] << 8 | __data[4]))
+
+#define FM_GET_NUM_RDS_GRPS(__data) __data[0]
+
+/* Response buffer starts from the 4th byte if the response buffer */
+#define FM_GET_RSP_BUFFER_ADDR(__data) (&__data[3])
+
+/* FM Function buffer starts from the 5th byte if the response buffer */
+#define FM_GET_FUNCTION_ADDR(__data) (&__data[4])
+
+/*
+ * Maximum time for chip to respond including the Command
+ * Competion interrupts for some commands. This time has been
+ * adjusted to cater to increased communication time with chip
+ * when debug level is set to 4.
+ */
+#define MAX_RESPONSE_TIME_IN_MS 5000
+
+/*
+ * enum fmd_gocmd_t - FM Driver Command state.
+ *
+ * @FMD_STATE_NONE: FM Driver in Idle state
+ * @FMD_STATE_MODE: FM Driver in setmode state
+ * @FMD_STATE_FREQUENCY: FM Driver in Set frequency state.
+ * @FMD_STATE_PA: FM Driver in SetPA state.
+ * @FMD_STATE_PA_LEVEL: FM Driver in Setpalevl state.
+ * @FMD_STATE_ANTENNA: FM Driver in Setantenna state
+ * @FMD_STATE_MUTE: FM Driver in Setmute state
+ * @FMD_STATE_SEEK: FM Driver in seek mode
+ * @FMD_STATE_SEEK_STOP: FM Driver in seek stop level state.
+ * @FMD_STATE_SCAN_BAND: FM Driver in Scanband mode
+ * @FMD_STATE_TX_SET_CTRL: FM Driver in RDS control state
+ * @FMD_STATE_TX_SET_THRSHLD: FM Driver in RDS threshld state
+ * @FMD_STATE_GEN_POWERUP: FM Driver in Power UP state.
+ * @FMD_STATE_SELECT_REF_CLK: FM Driver in Select Reference clock state.
+ * @FMD_STATE_SET_REF_CLK_PLL: FM Driver in Set Reference Freq state.
+ * @FMD_STATE_BLOCK_SCAN: FM Driver in Block Scan state.
+ * @FMD_STATE_AF_UPDATE: FM Driver in AF Update State.
+ * @FMD_STATE_AF_SWITCH: FM Driver in AF Switch State.
+ * @FMD_STATE_LAST: Last State of FM Driver
+ *
+ * Various states of the FM driver.
+ */
+enum fmd_gocmd {
+ FMD_STATE_NONE,
+ FMD_STATE_MODE,
+ FMD_STATE_FREQUENCY,
+ FMD_STATE_PA,
+ FMD_STATE_PA_LEVEL,
+ FMD_STATE_ANTENNA,
+ FMD_STATE_MUTE,
+ FMD_STATE_SEEK,
+ FMD_STATE_SEEK_STOP,
+ FMD_STATE_SCAN_BAND,
+ FMD_STATE_TX_SET_CTRL,
+ FMD_STATE_TX_SET_THRSHLD,
+ FMD_STATE_GEN_POWERUP,
+ FMD_STATE_SELECT_REF_CLK,
+ FMD_STATE_SET_REF_CLK_PLL,
+ FMD_STATE_BLOCK_SCAN,
+ FMD_STATE_AF_UPDATE,
+ FMD_STATE_AF_SWITCH,
+ FMD_STATE_LAST
+};
+
+/**
+ * struct fmd_rdsgroup_t - Rds group structure.
+ *
+ * @block: Array for RDS Block(s) received.
+ * @status: Array of Status of corresponding RDS block(s).
+ *
+ * It stores the value and status of a particular RDS group
+ * received.
+ */
+struct fmd_rds_group {
+ u16 block[NUM_OF_RDS_BLOCKS];
+ u8 status[NUM_OF_RDS_BLOCKS];
+};
+
+/**
+ * struct fmd_states_info - Main FM state info structure.
+ *
+ * @fmd_initialized: Flag indicating FM Driver is initialized or not
+ * @rx_freq_range: Receiver freq range
+ * @rx_volume: Receiver volume level
+ * @rx_antenna: Receiver Antenna
+ * @rx_seek_stop_level: RDS seek stop Level
+ * @rx_rds_on: Receiver RDS ON
+ * @rx_stereo_mode: Receiver Stereo mode
+ * @max_channels_to_scan: Maximum Number of channels to Scan.
+ * @tx_freq_range: Transmitter freq Range
+ * @tx_preemphasis: Transmitter Pre emphiasis level
+ * @tx_stereo_mode: Transmitter stero mode
+ * @tx_rds_on: Enable RDS
+ * @tx_pilot_dev: PIlot freq deviation
+ * @tx_rds_dev: RDS deviation
+ * @tx_strength: TX Signal Stregnth
+ * @irq_index: Index where last interrupt is added to Interrupt queue
+ * @interrupt_available_for_processing: Flag indicating if interrupt is
+ * available for processing or not.
+ * @interrupt_queue: Circular Queue to store the received interrupt from chip.
+ * @gocmd: Command which is in progress.
+ * @rds_group: Array of RDS group Buffer
+ * @callback: Callback registered by upper layers.
+ */
+struct fmd_states_info {
+ bool fmd_initialized;
+ u8 rx_freq_range;
+ u8 rx_volume;
+ u8 rx_antenna;
+ u16 rx_seek_stop_level;
+ bool rx_rds_on;
+ u8 rx_stereo_mode;
+ u8 tx_freq_range;
+ u8 tx_preemphasis;
+ bool tx_stereo_mode;
+ u8 max_channels_to_scan;
+ bool tx_rds_on;
+ u16 tx_pilot_dev;
+ u16 tx_rds_dev;
+ u16 tx_strength;
+ u8 irq_index;
+ bool interrupt_available_for_processing;
+ u16 interrupt_queue[MAX_COUNT_OF_IRQS];
+ enum fmd_gocmd gocmd;
+ struct fmd_rds_group rds_group[MAX_RDS_GROUPS];
+ fmd_radio_cb callback;
+};
+
+/**
+ * struct fmd_data - Main structure for FM data exchange.
+ *
+ * @cmd_id: Command Id of the command being exchanged.
+ * @num_parameters: Number of parameters
+ * @parameters: FM data parameters.
+ */
+struct fmd_data {
+ u32 cmd_id;
+ u16 num_parameters;
+ u8 *parameters;
+};
+
+static struct fmd_states_info fmd_state_info;
+static struct fmd_data fmd_data;
+static struct semaphore cmd_sem;
+static struct semaphore rds_sem;
+static struct semaphore interrupt_sem;
+static struct task_struct *rds_thread_task;
+static struct task_struct *irq_thread_task;
+static struct device *cg2900_fm_dev;
+static struct mutex write_mutex;
+static struct mutex send_cmd_mutex;
+static spinlock_t fmd_spinlock;
+static spinlock_t fmd_spinlock_read;
+
+/* Debug Level
+ * 1: Only Error Logs
+ * 2: Info Logs
+ * 3: Debug Logs
+ * 4: HCI Logs
+ */
+unsigned short cg2900_fm_debug_level = FM_ERROR_LOGS;
+EXPORT_SYMBOL(cg2900_fm_debug_level);
+
+static cg2900_fm_rds_cb cb_rds_func;
+static bool rds_thread_required;
+static bool irq_thread_required;
+
+static char event_name[FMD_EVENT_LAST_ELEMENT][MAX_NAME_SIZE] = {
+ "FMD_EVENT_OPERATION_COMPLETED",
+ "FMD_EVENT_ANTENNA_STATUS_CHANGED",
+ "FMD_EVENT_FREQUENCY_CHANGED",
+ "FMD_EVENT_SEEK_COMPLETED",
+ "FMD_EVENT_SCAN_BAND_COMPLETED",
+ "FMD_EVENT_BLOCK_SCAN_COMPLETED",
+ "FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE",
+ "FMD_EVENT_MONO_STEREO_TRANSITION_COMPLETE",
+ "FMD_EVENT_SEEK_STOPPED",
+ "FMD_EVENT_GEN_POWERUP",
+ "FMD_EVENT_RDSGROUP_RCVD",
+};
+
+static char interrupt_name[MAX_COUNT_OF_IRQS][MAX_NAME_SIZE] = {
+ "IRPT_OPERATION_SUCCEEDED",
+ "IRPT_OPERATION_FAILED",
+ "IRPT_NOT_DEFINED",
+ "IRPT_RX_BUFFER_FULL_OR_TX_BUFFER_EMPTY",
+ "IRPT_RX_SIGNAL_QUALITY_LOW_OR_TX_MUTE_STATUS_CHANGED",
+ "IRPT_MONO_STEREO_TRANSITION",
+ "IRPT_RX_RDS_SYNC_FOUND_OR_TX_INPUT_OVERDRIVE",
+ "IRPT_RDS_SYNC_LOST",
+ "IRPT_PI_CODE_CHANGED",
+ "IRPT_REQUESTED_BLOCK_AVAILABLE",
+ "IRPT_NOT_DEFINED",
+ "IRPT_NOT_DEFINED",
+ "IRPT_NOT_DEFINED",
+ "IRPT_NOT_DEFINED",
+ "IRPT_WARM_BOOT_READY",
+ "IRPT_COLD_BOOT_READY",
+};
+
+
+static void fmd_hexdump(
+ char prompt,
+ u8 *buffer,
+ int num_bytes
+ );
+static u8 fmd_get_event(
+ enum fmd_gocmd gocmd
+ );
+static void fmd_event_name(
+ u8 event,
+ char *event_name
+ );
+static char *fmd_get_fm_function_name(
+ u8 fm_function
+ );
+static void fmd_interrupt_name(
+ u16 interrupt,
+ char *interrupt_name
+ );
+static void fmd_add_interrupt_to_queue(
+ u16 interrupt
+ );
+static void fmd_process_interrupt(
+ u16 interrupt
+ );
+static void fmd_callback(
+ u8 event,
+ bool event_successful
+ );
+static int fmd_rx_frequency_to_channel(
+ u32 freq,
+ u16 *channel
+ );
+static int fmd_rx_channel_to_frequency(
+ u16 channel_number,
+ u32 *frequency
+ );
+static int fmd_tx_frequency_to_channel(
+ u32 freq,
+ u16 *channel
+ );
+static int fmd_tx_channel_to_frequency(
+ u16 channel_number,
+ u32 *frequency
+ );
+static bool fmd_go_cmd_busy(void);
+static int fmd_send_cmd_and_read_resp(
+ const u16 cmd_id,
+ const u16 num_parameters,
+ const u16 *parameters,
+ u16 *resp_num_parameters,
+ u16 *resp_parameters
+ );
+static int fmd_send_cmd(
+ const u16 cmd_id,
+ const u16 num_parameters,
+ const u16 *parameters
+ );
+static int fmd_read_resp(
+ u16 *cmd_id,
+ u16 *num_parameters,
+ u16 *parameters
+ );
+static void fmd_process_fm_function(
+ u8 *packet_buffer
+ );
+static int fmd_write_file_block(
+ u32 file_block_id,
+ u8 *file_block,
+ u16 file_block_length
+ );
+static void fmd_receive_data(
+ u16 packet_length,
+ u8 *packet_buffer
+ );
+static int fmd_rds_thread(
+ void *data
+ );
+static void fmd_start_irq_thread(void);
+static void fmd_stop_irq_thread(void);
+static int fmd_irq_thread(
+ void *data
+ );
+static int fmd_send_packet(
+ u16 num_bytes,
+ u8 *send_buffer
+ );
+static int fmd_get_cmd_sem(void);
+static void fmd_set_cmd_sem(void);
+static void fmd_get_interrupt_sem(void);
+static void fmd_set_interrupt_sem(void);
+static bool fmd_driver_init(void);
+static void fmd_driver_exit(void);
+
+/* structure declared in time.h */
+struct timespec time_spec;
+
+
+/**
+ * fmd_hexdump() - Displays the HCI Data Bytes exchanged with FM Chip.
+ *
+ * @prompt: Prompt signifying the direction '<' for Rx '>' for Tx
+ * @buffer: Buffer to be displayed.
+ * @num_bytes: Number of bytes of the buffer.
+ */
+ static void fmd_hexdump(
+ char prompt,
+ u8 *buffer,
+ int num_bytes
+ )
+{
+ int i;
+ u8 tmp_val;
+ struct timespec time;
+ static u8 pkt_write[MAX_BUFFER_SIZE], *pkt_ptr;
+
+ getnstimeofday(&time);
+ sprintf(pkt_write, "\n[%08x:%08x] [%04x] %c",
+ (unsigned int)time.tv_sec,
+ (unsigned int)time.tv_nsec,
+ num_bytes, prompt);
+
+ pkt_ptr = pkt_write + strlen(pkt_write);
+ if (buffer == NULL)
+ return;
+
+ /* Copy the buffer only if the input buffer is not NULL */
+ for (i = 0; i < num_bytes; i++) {
+ *pkt_ptr++ = ' ';
+ tmp_val = buffer[i] >> 4;
+ *pkt_ptr++ = ASCVAL(tmp_val);
+ tmp_val = buffer[i] & 0x0F;
+ *pkt_ptr++ = ASCVAL(tmp_val);
+ if (i > 20) {
+ /* Print only 20 bytes at max */
+ break;
+ }
+ }
+ *pkt_ptr++ = '\0';
+ FM_HEX_REPORT("%s", pkt_write);
+}
+
+/**
+ * fmd_get_event() - Returns the Event based on FM Driver State.
+ *
+ * @gocmd: Pending FM Command
+ *
+ * Returns: Corresponding Event
+ */
+static u8 fmd_get_event(
+ enum fmd_gocmd gocmd
+ )
+{
+ u8 event = FMD_EVENT_OPERATION_COMPLETED;
+ switch (gocmd) {
+ case FMD_STATE_ANTENNA:
+ event = FMD_EVENT_ANTENNA_STATUS_CHANGED;
+ break;
+ case FMD_STATE_FREQUENCY:
+ event = FMD_EVENT_FREQUENCY_CHANGED;
+ break;
+ case FMD_STATE_SEEK:
+ event = FMD_EVENT_SEEK_COMPLETED;
+ break;
+ case FMD_STATE_SCAN_BAND:
+ event = FMD_EVENT_SCAN_BAND_COMPLETED;
+ break;
+ case FMD_STATE_BLOCK_SCAN:
+ event = FMD_EVENT_BLOCK_SCAN_COMPLETED;
+ break;
+ case FMD_STATE_AF_UPDATE:
+ /* Drop Down */
+ case FMD_STATE_AF_SWITCH:
+ event = FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE;
+ break;
+ case FMD_STATE_SEEK_STOP:
+ event = FMD_EVENT_SEEK_STOPPED;
+ break;
+ default:
+ event = FMD_EVENT_OPERATION_COMPLETED;
+ break;
+ }
+ return event;
+}
+
+/**
+ * fmd_event_name() - Converts the event to a displayable string.
+ *
+ * @event: Event that has occurred.
+ * @eventname: (out) Buffer to store event name.
+ */
+static void fmd_event_name(
+ u8 event,
+ char *eventname
+ )
+{
+ if (eventname == NULL) {
+ FM_ERR_REPORT("fmd_event_name: Output Buffer is NULL");
+ return;
+ }
+ if (event < FMD_EVENT_LAST_ELEMENT)
+ strcpy(eventname, event_name[event]);
+ else
+ strcpy(eventname, "FMD_EVENT_UNKNOWN");
+}
+
+/**
+ * fmd_get_fm_function_name() - Returns the FM Fucntion name.
+ *
+ * @fm_function: Function whose name is to be retrieved.
+ *
+ * Returns FM Function Name.
+ */
+static char *fmd_get_fm_function_name(
+ u8 fm_function
+ )
+{
+ switch (fm_function) {
+ case FM_FUNCTION_ENABLE:
+ return "FM_FUNCTION_ENABLE";
+ break;
+ case FM_FUNCTION_DISABLE:
+ return "FM_FUNCTION_DISABLE";
+ break;
+ case FM_FUNCTION_RESET:
+ return "FM_FUNCTION_RESET";
+ break;
+ case FM_FUNCTION_WRITE_COMMAND:
+ return "FM_FUNCTION_WRITE_COMMAND";
+ break;
+ case FM_FUNCTION_SET_INT_MASK_ALL:
+ return "FM_FUNCTION_SET_INT_MASK_ALL";
+ break;
+ case FM_FUNCTION_GET_INT_MASK_ALL:
+ return "FM_FUNCTION_GET_INT_MASK_ALL";
+ break;
+ case FM_FUNCTION_SET_INT_MASK:
+ return "FM_FUNCTION_SET_INT_MASK";
+ break;
+ case FM_FUNCTION_GET_INT_MASK:
+ return "FM_FUNCTION_GET_INT_MASK";
+ break;
+ case FM_FUNCTION_FIRMWARE_DOWNLOAD:
+ return "FM_FUNCTION_FIRMWARE_DOWNLOAD";
+ break;
+ default:
+ return "FM_FUNCTION_UNKNOWN";
+ break;
+ }
+}
+
+/**
+ * fmd_interrupt_name() - Converts the interrupt to a displayable string.
+ *
+ * @interrupt: interrupt received from FM Chip
+ * @interruptname: (out) Buffer to store interrupt name.
+ */
+static void fmd_interrupt_name(
+ u16 interrupt,
+ char *interruptname
+ )
+{
+ int index;
+
+ if (interruptname == NULL) {
+ FM_ERR_REPORT("fmd_interrupt_name: Output Buffer is NULL!!!");
+ return;
+ }
+ /* Convert Interrupt to Bit */
+ for (index = 0; index < MAX_COUNT_OF_IRQS; index++) {
+ if (interrupt & (1 << index)) {
+ /* Match found, break the loop */
+ break;
+ }
+ }
+ if (index < MAX_COUNT_OF_IRQS)
+ strcpy(interruptname, interrupt_name[index]);
+ else
+ strcpy(interruptname, "IRPT_UNKNOWN");
+}
+
+/**
+ * fmd_add_interrupt_to_queue() - Add interrupt to IRQ Queue.
+ *
+ * @interrupt: interrupt received from FM Chip
+ */
+static void fmd_add_interrupt_to_queue(
+ u16 interrupt
+ )
+{
+ FM_DEBUG_REPORT("fmd_add_interrupt_to_queue : "
+ "Interrupt Received = %04x", (u16) interrupt);
+
+ /* Reset the index if it reaches the array limit */
+ if (fmd_state_info.irq_index > MAX_COUNT_OF_IRQS - 1) {
+ spin_lock(&fmd_spinlock);
+ fmd_state_info.irq_index = 0;
+ spin_unlock(&fmd_spinlock);
+ }
+
+ spin_lock(&fmd_spinlock);
+ fmd_state_info.interrupt_queue[fmd_state_info.irq_index] = interrupt;
+ fmd_state_info.irq_index++;
+ spin_unlock(&fmd_spinlock);
+ if (!fmd_state_info.interrupt_available_for_processing) {
+ spin_lock(&fmd_spinlock);
+ fmd_state_info.interrupt_available_for_processing = true;
+ spin_unlock(&fmd_spinlock);
+ fmd_set_interrupt_sem();
+ }
+}
+
+/**
+ * fmd_process_interrupt() - Processes the Interrupt.
+ *
+ * This function processes the interrupt received from FM Chip
+ * and calls the corresponding callback registered by upper layers with
+ * proper parameters.
+ * @interrupt: interrupt received from FM Chip
+ */
+static void fmd_process_interrupt(
+ u16 interrupt
+ )
+{
+ char irpt_name[MAX_NAME_SIZE];
+
+ fmd_interrupt_name(interrupt, irpt_name);
+ FM_DEBUG_REPORT("%s", irpt_name);
+ if ((interrupt & IRPT_OPERATION_SUCCEEDED) |
+ (interrupt & IRPT_OPERATION_FAILED)) {
+ bool event_status = (interrupt & IRPT_OPERATION_SUCCEEDED);
+ u8 event = fmd_get_event(fmd_state_info.gocmd);
+
+ switch (fmd_state_info.gocmd) {
+ case FMD_STATE_MODE:
+ /* Mode has been changed. */
+ case FMD_STATE_MUTE:
+ /* FM radio is Muter or Unmuted */
+ case FMD_STATE_PA:
+ /* Power Amplifier has been enabled/disabled */
+ case FMD_STATE_PA_LEVEL:
+ /* Power Amplifier Level has been changed. */
+ case FMD_STATE_SELECT_REF_CLK:
+ /* Reference Clock has been selected. */
+ case FMD_STATE_SET_REF_CLK_PLL:
+ /* Reference Clock frequency has been changed. */
+ case FMD_STATE_TX_SET_CTRL:
+ /* Tx Control has been set. */
+ case FMD_STATE_TX_SET_THRSHLD:
+ /* Tx Threashold has been set. */
+ /* Set State to None and set the waiting semaphore. */
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ fmd_set_cmd_sem();
+ break;
+ case FMD_STATE_ANTENNA:
+ /* Antenna status has been changed. */
+ case FMD_STATE_SEEK_STOP:
+ /* Band scan, seek or block scan has completed. */
+ case FMD_STATE_AF_UPDATE:
+ /* AF Update has completed. */
+ case FMD_STATE_AF_SWITCH:
+ /* AF Switch has completed. */
+ case FMD_STATE_FREQUENCY:
+ /* Frequency has been changed. */
+ /*
+ * Set State to None, set the waiting semaphore,
+ * and inform upper layer.
+ */
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ fmd_set_cmd_sem();
+ fmd_callback(
+ event,
+ event_status);
+ break;
+ case FMD_STATE_SEEK:
+ /* Seek has completed. */
+ case FMD_STATE_SCAN_BAND:
+ /* Band scan has completed. */
+ case FMD_STATE_BLOCK_SCAN:
+ /* Block scan has completed. */
+ /*
+ * Set State to None. No need to set the
+ * semaphore since this is an asyncronous event.
+ */
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ /* Inform Upper layer. */
+ fmd_callback(event, event_status);
+ break;
+ default:
+ /* Do Nothing */
+ FM_ERR_REPORT("Default %s case of "\
+ "interrupt processing", event_status ? \
+ "Success" : "Failed");
+ break;
+ }
+ }
+
+ if (interrupt & IRPT_RX_BUFFERFULL_TX_BUFFEREMPTY) {
+ /*
+ * RDS Buffer Full or RDS Buffer Empty
+ * interrupt received from chip, indicating
+ * that RDS data is available if chip
+ * is in Rx mode or RDS data can be send
+ * to chip in case of Tx mode. Inform the
+ * upper layers about this interrupt.
+ */
+ fmd_callback(
+ FMD_EVENT_RDSGROUP_RCVD,
+ true);
+ }
+
+ if (interrupt & IRPT_RX_MONO_STEREO_TRANSITION) {
+ /*
+ * Mono Stereo Transition interrupt
+ * received from chip, inform the
+ * upper layers about it.
+ */
+ fmd_callback(
+ FMD_EVENT_MONO_STEREO_TRANSITION_COMPLETE,
+ true);
+ }
+
+ if ((interrupt & IRPT_COLD_BOOT_READY) |
+ (interrupt & IRPT_WARM_BOOT_READY)) {
+ switch (fmd_state_info.gocmd) {
+ case FMD_STATE_GEN_POWERUP:
+ /*
+ * Cold Boot/ Warm Boot Interrupt received from
+ * chip, indicating transition from
+ * power off/standby state to active state.
+ * Inform the upper layers about it.
+ */
+ fmd_callback(
+ FMD_EVENT_GEN_POWERUP,
+ true);
+ /* Set State to None and set the waiting semaphore. */
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ fmd_set_cmd_sem();
+ break;
+ default:
+ /* Do Nothing */
+ break;
+ }
+ }
+}
+
+/**
+ * fmd_callback() - Callback function for upper layers.
+ *
+ * Callback function that calls the registered callback of upper
+ * layers with proper parameters.
+ * @event: event for which the callback function was called
+ * from FM Driver.
+ * @event_successful: Signifying whether the event is called from FM
+ * Driver on receiving irpt_Operation_Succeeded or irpt_Operation_Failed.
+ */
+static void fmd_callback(
+ u8 event,
+ bool event_successful
+ )
+{
+ char event_name_string[MAX_NAME_SIZE];
+
+ fmd_event_name(event, event_name_string);
+
+ FM_DEBUG_REPORT("%s %x, %d", event_name_string,
+ (unsigned int)event , (unsigned int)event_successful);
+
+ if (fmd_state_info.callback)
+ fmd_state_info.callback(
+ event,
+ event_successful);
+}
+
+/**
+ * fmd_rx_frequency_to_channel() - Converts Rx frequency to channel number.
+ *
+ * Converts the Frequency in kHz to corresponding Channel number.
+ * This is used for FM Rx.
+ * @freq: Frequency in kHz.
+ * @channel: Channel Number corresponding to the given Frequency.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameters are not valid.
+ *
+ */
+static int fmd_rx_frequency_to_channel(
+ u32 freq,
+ u16 *channel
+ )
+{
+ u8 range;
+ int result;
+ u32 min_freq;
+ u32 max_freq;
+
+ if (channel == NULL) {
+ result = -EINVAL;
+ goto error;
+ }
+
+ result = fmd_get_freq_range(
+ &range);
+
+ if (result != 0)
+ goto error;
+
+ result = fmd_get_freq_range_properties(
+ range,
+ &min_freq,
+ &max_freq);
+
+ if (result != 0)
+ goto error;
+
+ if (freq > max_freq)
+ freq = max_freq;
+ else if (freq < min_freq)
+ freq = min_freq;
+
+ /*
+ * Frequency in kHz needs to be divided with 50 kHz to get
+ * channel number for all FM Bands
+ */
+ *channel = (u16)((freq - min_freq) / CHANNEL_FREQ_CONVERTER_MHZ);
+ result = 0;
+error:
+ return result;
+}
+
+/**
+ * fmd_rx_channel_to_frequency() - Converts Rx Channel number to frequency.
+ *
+ * Converts the Channel Number to corresponding Frequency in kHz.
+ * This is used for FM Rx.
+ * @channel_number: Channel Number to be converted.
+ * @frequency: Frequency corresponding to the corresponding channel in kHz.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameters are not valid.
+ *
+ */
+static int fmd_rx_channel_to_frequency(
+ u16 channel_number,
+ u32 *frequency
+ )
+{
+ u8 range;
+ int result;
+ u32 min_freq;
+ u32 max_freq;
+
+ if (frequency == NULL) {
+ result = -EINVAL;
+ goto error;
+ }
+
+ result = fmd_get_freq_range(
+ &range);
+
+ if (result != 0)
+ goto error;
+
+ result = fmd_get_freq_range_properties(
+ range,
+ &min_freq,
+ &max_freq);
+
+ if (result != 0)
+ goto error;
+
+ /*
+ * Channel Number needs to be multiplied with 50 kHz to get
+ * frequency in kHz for all FM Bands
+ */
+ *frequency = min_freq + (channel_number * CHANNEL_FREQ_CONVERTER_MHZ);
+
+ if (*frequency > max_freq)
+ *frequency = max_freq;
+ else if (*frequency < min_freq)
+ *frequency = min_freq;
+
+error:
+ return result;
+}
+
+/**
+ * fmd_tx_frequency_to_channel() - Converts Tx frequency to channel number.
+ *
+ * Converts the Frequency in kHz to corresponding Channel number.
+ * This is used for FM Tx.
+ * @freq: Frequency in kHz.
+ * @channel: (out)Channel Number corresponding to the given Frequency.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameters are not valid.
+ */
+static int fmd_tx_frequency_to_channel(
+ u32 freq,
+ u16 *channel
+ )
+{
+ u8 range;
+ int result;
+ u32 min_freq;
+ u32 max_freq;
+
+ if (channel == NULL) {
+ result = -EINVAL;
+ goto error;
+ }
+
+ result = fmd_tx_get_freq_range(
+ &range);
+
+ if (result != 0)
+ goto error;
+
+ result = fmd_get_freq_range_properties(
+ range,
+ &min_freq,
+ &max_freq);
+
+ if (result != 0)
+ goto error;
+
+ if (freq > max_freq)
+ freq = max_freq;
+ else if (freq < min_freq)
+ freq = min_freq;
+
+ /*
+ * Frequency in kHz needs to be divided with 50 kHz to get
+ * channel number for all FM Bands
+ */
+ *channel = (u16)((freq - min_freq) / CHANNEL_FREQ_CONVERTER_MHZ);
+ result = 0;
+error:
+ return result;
+}
+
+/**
+ * fmd_tx_channel_to_frequency() - Converts Tx Channel number to frequency.
+ *
+ * Converts the Channel Number to corresponding Frequency in kHz.
+ * This is used for FM Tx.
+ * @channel_number: Channel Number to be converted.
+ * @frequency: Frequency corresponding to the corresponding channel
+ * in kHz.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameters are not valid.
+ */
+static int fmd_tx_channel_to_frequency(
+ u16 channel_number,
+ u32 *frequency
+ )
+{
+ u8 range;
+ int result;
+ u32 min_freq;
+ u32 max_freq;
+
+ if (frequency == NULL) {
+ result = -EINVAL;
+ goto error;
+ }
+
+ result = fmd_tx_get_freq_range(
+ &range);
+
+ if (result != 0)
+ goto error;
+
+ result = fmd_get_freq_range_properties(
+ range,
+ &min_freq,
+ &max_freq);
+
+ if (result != 0)
+ goto error;
+
+ /*
+ * Channel Number needs to be multiplied with 50 kHz to get
+ * frequency in kHz for all FM Bands
+ */
+ *frequency = min_freq + (channel_number * CHANNEL_FREQ_CONVERTER_MHZ);
+
+ if (*frequency > max_freq)
+ *frequency = max_freq;
+ else if (*frequency < min_freq)
+ *frequency = min_freq;
+
+ result = 0;
+error:
+ return result;
+}
+
+/**
+ * fmd_go_cmd_busy() - Function to check if FM Driver is busy or idle
+ *
+ * Returns:
+ * false if FM Driver is Idle
+ * true otherwise
+ */
+static bool fmd_go_cmd_busy(void)
+{
+ return (fmd_state_info.gocmd != FMD_STATE_NONE);
+}
+
+/**
+ * fmd_read_cb() - Handle Received Data
+ *
+ * This function handles data received from connectivity protocol driver.
+ * @dev: Device receiving data.
+ * @skb: Buffer with data coming form device.
+ */
+static void fmd_read_cb(
+ struct cg2900_user_data *dev,
+ struct sk_buff *skb
+ )
+{
+ FM_INFO_REPORT("fmd_read_cb");
+
+ if (skb->data == NULL || skb->len == 0)
+ goto error;
+
+ spin_lock(&fmd_spinlock_read);
+ CG2900_HEX_READ_PACKET_DUMP;
+ /*
+ * The first byte is length of bytes following bytes
+ * Rest of the bytes are the actual data
+ */
+ fmd_receive_data(
+ FM_GET_PKT_LEN(skb->data),
+ FM_GET_RSP_PKT_ADDR(skb->data));
+
+error:
+ kfree_skb(skb);
+ spin_unlock(&fmd_spinlock_read);
+}
+
+/**
+ * fmd_receive_data() - Processes the FM data received from device.
+ *
+ * @packet_length: Length of received Data Packet
+ * @packet_buffer: Received Data buffer.
+ */
+static void fmd_receive_data(
+ u16 packet_length,
+ u8 *packet_buffer
+ )
+{
+ if (packet_buffer == NULL) {
+ FM_ERR_REPORT("fmd_receive_data: Buffer = NULL");
+ return;
+ }
+
+ if (packet_length == FM_PG1_INTERRUPT_EVENT_LEN &&
+ packet_buffer[0] == FM_CATENA_OPCODE &&
+ packet_buffer[1] == FM_EVENT_ID) {
+ /* PG 1.0 interrupt Handling */
+ u16 interrupt = FM_GET_PGI_INTERRUPT(packet_buffer);
+ FM_DEBUG_REPORT("interrupt = %04x",
+ (unsigned int)interrupt);
+ fmd_add_interrupt_to_queue(interrupt);
+ } else if (packet_length == FM_PG2_INTERRUPT_EVENT_LEN &&
+ packet_buffer[0] == FM_SUCCESS_STATUS &&
+ packet_buffer[1] == FM_CATENA_OPCODE &&
+ packet_buffer[2] == FM_EVENT &&
+ packet_buffer[3] == FM_EVENT_ID) {
+ /* PG 2.0 interrupt Handling */
+ u16 interrupt = FM_GET_PG2_INTERRUPT(packet_buffer);
+ FM_DEBUG_REPORT("interrupt = %04x",
+ (unsigned int)interrupt);
+ fmd_add_interrupt_to_queue(interrupt);
+ } else if (packet_buffer[0] == FM_SUCCESS_STATUS &&
+ packet_buffer[1] == FM_CATENA_OPCODE &&
+ packet_buffer[2] == FM_WRITE) {
+ /* Command Complete or RDS Data Handling */
+ u8 fm_status = FM_GET_STATUS(packet_buffer);;
+ switch (fm_status) {
+ case FM_CMD_STATUS_CMD_SUCCESS:
+ fmd_process_fm_function(
+ FM_GET_FUNCTION_ADDR(packet_buffer));
+ break;
+ case FM_CMD_STATUS_HCI_ERR_HW_FAILURE:
+ FM_DEBUG_REPORT(
+ "FM_CMD_STATUS_HCI_ERR_HW_FAILURE");
+ break;
+ case FM_CMD_STATUS_HCI_ERR_INVALID_PARAMETERS:
+ FM_DEBUG_REPORT(
+ "FM_CMD_STATUS_HCI_ERR_INVALID_PARAMETERS");
+ break;
+ case FM_CMD_STATUS_IP_UNINIT:
+ FM_DEBUG_REPORT(
+ "FM_CMD_STATUS_IP_UNINIT");
+ break;
+ case FM_CMD_STATUS_HCI_ERR_UNSPECIFIED_ERROR:
+ FM_DEBUG_REPORT(
+ "FM_CMD_STATUS_HCI_ERR_UNSPECIFIED_ERROR");
+ break;
+ case FM_CMD_STATUS_HCI_ERR_CMD_DISALLOWED:
+ FM_DEBUG_REPORT(
+ "FM_CMD_STATUS_HCI_ERR_CMD_DISALLOWED");
+ break;
+ case FM_CMD_STATUS_WRONG_SEQ_NUM:
+ FM_DEBUG_REPORT(
+ "FM_CMD_STATUS_WRONG_SEQ_NUM");
+ break;
+ case FM_CMD_STATUS_UNKNOWN_FILE_TYPE:
+ FM_DEBUG_REPORT(
+ "FM_CMD_STATUS_UNKNOWN_FILE_TYPE");
+ break;
+ case FM_CMD_STATUS_FILE_VERSION_MISMATCH:
+ FM_DEBUG_REPORT(
+ "FM_CMD_STATUS_FILE_VERSION_MISMATCH");
+ break;
+ default:
+ FM_DEBUG_REPORT(
+ "Unknown Status = %02x", fm_status);
+ break;
+ }
+ }
+}
+
+/**
+ * fmd_reset_cb() - Reset callback fuction.
+ *
+ * @dev: CPD device reseting.
+ */
+static void fmd_reset_cb(struct cg2900_user_data *dev)
+{
+ FM_INFO_REPORT("fmd_reset_cb: Device Reset");
+ spin_lock(&fmd_spinlock_read);
+ cg2900_handle_device_reset();
+ spin_unlock(&fmd_spinlock_read);
+}
+
+/**
+ * fmd_rds_thread() - Thread for receiving RDS data from Chip.
+ *
+ * @data: Data beng passed as parameter on starting the thread.
+ */
+static int fmd_rds_thread(
+ void *data
+ )
+{
+ FM_INFO_REPORT("fmd_rds_thread Created Successfully");
+ while (rds_thread_required) {
+ if (cb_rds_func)
+ cb_rds_func();
+ /* Give 100 ms for context switching */
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+ }
+ FM_DEBUG_REPORT("fmd_rds_thread Exiting!!!");
+ return 0;
+}
+
+/**
+ * fmd_start_irq_thread() - Function for starting Interrupt Thread.
+ */
+static void fmd_start_irq_thread(void)
+{
+ FM_INFO_REPORT("fmd_start_irq_thread");
+ irq_thread_task = kthread_create(fmd_irq_thread, NULL, "irq_thread");
+ if (IS_ERR(irq_thread_task)) {
+ FM_ERR_REPORT("fmd_start_irq_thread: "
+ "Unable to Create irq_thread");
+ irq_thread_task = NULL;
+ return;
+ }
+ wake_up_process(irq_thread_task);
+}
+
+/**
+ * fmd_stop_irq_thread() - Function for stopping Interrupt Thread.
+ */
+static void fmd_stop_irq_thread(void)
+{
+ FM_INFO_REPORT("fmd_stop_irq_thread");
+ kthread_stop(irq_thread_task);
+ irq_thread_task = NULL;
+ FM_DEBUG_REPORT("-fmd_stop_irq_thread");
+}
+
+/**
+ * fmd_irq_thread() - Thread for processing Interrupts received from Chip.
+ *
+ * @data: Data being passed as parameter on starting the thread.
+ */
+
+static int fmd_irq_thread(
+ void *data
+ )
+{
+ int index;
+
+ FM_INFO_REPORT("fmd_irq_thread Created Successfully");
+
+ while (irq_thread_required) {
+ if (!fmd_state_info.interrupt_available_for_processing) {
+ FM_DEBUG_REPORT("fmd_irq_thread: Waiting on irq sem "
+ "interrupt_available_for_processing = %d "
+ "fmd_state_info.fmd_initialized = %d",
+ fmd_state_info.interrupt_available_for_processing,
+ fmd_state_info.fmd_initialized);
+ fmd_get_interrupt_sem();
+ FM_DEBUG_REPORT("fmd_irq_thread: Waiting on irq sem "
+ "interrupt_available_for_processing = %d "
+ "fmd_state_info.fmd_initialized = %d",
+ fmd_state_info.interrupt_available_for_processing,
+ fmd_state_info.fmd_initialized);
+ }
+ index = 0;
+
+ if (fmd_state_info.interrupt_available_for_processing) {
+ while (index < MAX_COUNT_OF_IRQS) {
+ if (fmd_state_info.interrupt_queue[index]
+ != IRPT_INVALID) {
+ FM_DEBUG_REPORT("fmd_process_interrupt "
+ "Interrupt = %04x",
+ fmd_state_info.
+ interrupt_queue[index]);
+ fmd_process_interrupt(
+ fmd_state_info.interrupt_queue[index]);
+ fmd_state_info.interrupt_queue[index]
+ = IRPT_INVALID;
+ }
+ index++;
+ }
+ }
+ fmd_state_info.interrupt_available_for_processing = false;
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+ }
+ FM_DEBUG_REPORT("fmd_irq_thread Exiting!!!");
+ return 0;
+}
+
+/**
+ * fmd_send_packet() - Sends the FM HCI Packet to the CG2900 Protocol Driver.
+ *
+ * @num_bytes: Number of bytes of Data to be sent including
+ * Channel Identifier (08)
+ * @send_buffer: Buffer containing the Data to be sent to Chip.
+ *
+ * Returns:
+ * 0, If packet was sent successfully to
+ * CG2900 Protocol Driver, otherwise the corresponding error.
+ * -EINVAL If parameters are not valid.
+ * -EIO If there is an Input/Output Error.
+ */
+static int fmd_send_packet(
+ u16 num_bytes,
+ u8 *send_buffer
+ )
+{
+ int err;
+ struct sk_buff *skb;
+ struct cg2900_user_data *pf_data;
+
+ FM_INFO_REPORT("fmd_send_packet");
+
+ if (send_buffer == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ if (!cg2900_fm_dev) {
+ FM_ERR_REPORT("fmd_send_packet: No FM device registered");
+ err = -EIO;
+ goto error;
+ }
+
+ pf_data = dev_get_platdata(cg2900_fm_dev);
+ if (!pf_data->opened) {
+ FM_ERR_REPORT("fmd_send_packet: FM channel is not opened");
+ err = -EIO;
+ goto error;
+ }
+
+ mutex_lock(&write_mutex);
+ CG2900_HEX_WRITE_PACKET_DUMP;
+
+ skb = pf_data->alloc_skb(num_bytes, GFP_KERNEL);
+ if (!skb) {
+ FM_ERR_REPORT("fmd_send_packet:Couldn't " \
+ "allocate sk_buff with length %d", num_bytes);
+ err = -EIO;
+ goto error;
+ }
+
+ /*
+ * Copy the buffer removing the FM Header as this
+ * would be done by Protocol Driver
+ */
+ memcpy(skb_put(skb, num_bytes), send_buffer, num_bytes);
+
+ err = pf_data->write(pf_data, skb);
+ if (err) {
+ FM_ERR_REPORT("fmd_send_packet: "
+ "Failed to send(%d) bytes using "
+ "cg2900_write, err = %d",
+ num_bytes, err);
+ kfree(skb);
+ err = -EIO;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ mutex_unlock(&write_mutex);
+ FM_DEBUG_REPORT("fmd_send_packet returning %d", err);
+ return err;
+}
+
+/**
+ * fmd_get_cmd_sem() - Block on Command Semaphore.
+ *
+ * This is required to ensure Flow Control in FM Driver.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ETIME if timeout occurs.
+ */
+static int fmd_get_cmd_sem(void)
+{
+ int ret_val;
+
+ FM_INFO_REPORT("fmd_get_cmd_sem");
+
+ ret_val = down_timeout(&cmd_sem,
+ msecs_to_jiffies(MAX_RESPONSE_TIME_IN_MS));
+
+ if (ret_val)
+ FM_ERR_REPORT("fmd_get_cmd_sem: down_timeout "
+ "returned error = %d", ret_val);
+
+ return ret_val;
+}
+
+/**
+ * fmd_set_cmd_sem() - Unblock on Command Semaphore.
+ *
+ * This is required to ensure Flow Control in FM Driver.
+ */
+static void fmd_set_cmd_sem(void)
+{
+ FM_DEBUG_REPORT("fmd_set_cmd_sem");
+
+ up(&cmd_sem);
+}
+
+/**
+ * fmd_get_interrupt_sem() - Block on Interrupt Semaphore.
+ *
+ * Till Interrupt is received, Interrupt Task is blocked.
+ */
+static void fmd_get_interrupt_sem(void)
+{
+ int ret_val;
+
+ FM_DEBUG_REPORT("fmd_get_interrupt_sem");
+
+ ret_val = down_killable(&interrupt_sem);
+
+ if (ret_val)
+ FM_ERR_REPORT("fmd_get_interrupt_sem: down_killable "
+ "returned error = %d", ret_val);
+}
+
+/**
+ * fmd_set_interrupt_sem() - Unblock on Interrupt Semaphore.
+ *
+ * on receiving Interrupt, Interrupt Task is un-blocked.
+ */
+static void fmd_set_interrupt_sem(void)
+{
+ FM_DEBUG_REPORT("fmd_set_interrupt_sem");
+ up(&interrupt_sem);
+}
+
+/**
+ * fmd_driver_init()- Initializes the Mutex, Semaphore, etc for FM Driver.
+ *
+ * It also registers FM Driver with the Protocol Driver.
+ *
+ * Returns:
+ * true if initialization is successful
+ * false if initiialization fails.
+ */
+static bool fmd_driver_init(void)
+{
+ bool ret_val;
+ struct cg2900_rev_data rev_data;
+ struct cg2900_user_data *pf_data;
+ int err;
+
+ FM_INFO_REPORT("fmd_driver_init");
+
+ if (!cg2900_fm_dev) {
+ FM_ERR_REPORT("No device registered");
+ ret_val = false;
+ goto error;
+ }
+
+ /* Initialize the semaphores */
+ sema_init(&cmd_sem, 0);
+ sema_init(&rds_sem, 0);
+ sema_init(&interrupt_sem, 0);
+ cb_rds_func = NULL;
+ rds_thread_required = false;
+ irq_thread_required = true;
+
+ pf_data = dev_get_platdata(cg2900_fm_dev);
+
+ /* Create Mutex For Reading and Writing */
+ spin_lock_init(&fmd_spinlock_read);
+ mutex_init(&write_mutex);
+ mutex_init(&send_cmd_mutex);
+ spin_lock_init(&fmd_spinlock);
+ fmd_start_irq_thread();
+
+ /* Open the FM channel */
+ err = pf_data->open(pf_data);
+ if (err) {
+ FM_ERR_REPORT("fmd_driver_init: "
+ "Couldn't open FM channel. Either chip is not connected"
+ " or Protocol Driver is not initialized");
+ ret_val = false;
+ goto error;
+ }
+
+ if (!pf_data->get_local_revision(pf_data, &rev_data)) {
+ FM_DEBUG_REPORT("No revision data available");
+ ret_val = false;
+ goto error;
+ }
+
+ FM_DEBUG_REPORT("Read revision data revision %04x "
+ "sub_version %04x",
+ rev_data.revision, rev_data.sub_version);
+ cg2900_fm_set_chip_version(rev_data.revision, rev_data.sub_version);
+ ret_val = true;
+
+error:
+ FM_DEBUG_REPORT("fmd_driver_init: Returning %d", ret_val);
+ return ret_val;
+}
+
+/**
+ * fmd_driver_exit() - Deinitializes the mutex, semaphores, etc.
+ *
+ * It also deregisters FM Driver with the Protocol Driver.
+ *
+ */
+static void fmd_driver_exit(void)
+{
+ struct cg2900_user_data *pf_data;
+
+ FM_INFO_REPORT("fmd_driver_exit");
+ irq_thread_required = false;
+ mutex_destroy(&write_mutex);
+ mutex_destroy(&send_cmd_mutex);
+ fmd_stop_irq_thread();
+ /* Close the FM channel */
+ pf_data = dev_get_platdata(cg2900_fm_dev);
+ if (pf_data->opened)
+ pf_data->close(pf_data);
+}
+
+/**
+ * fmd_send_cmd_and_read_resp() - Send command and read response.
+ *
+ * This function sends the HCI Command to Protocol Driver and
+ * Reads back the Response Packet.
+ * @cmd_id: Command Id to be sent to FM Chip.
+ * @num_parameters: Number of parameters of the command sent.
+ * @parameters: Buffer containing the Buffer to be sent.
+ * @resp_num_parameters: (out) Number of paramters of the response packet.
+ * @resp_parameters: (out) Buffer of the response packet.
+ *
+ * Returns:
+ * 0: If the command is sent successfully and the
+ * response received is also correct.
+ * -EINVAL: If the received response is not correct.
+ * -EIO: If there is an input/output error.
+ * -EINVAL: If parameters are not valid.
+ */
+static int fmd_send_cmd_and_read_resp(
+ const u16 cmd_id,
+ const u16 num_parameters,
+ const u16 *parameters,
+ u16 *resp_num_parameters,
+ u16 *resp_parameters
+ )
+{
+ int result;
+ u16 read_cmd_id = CMD_ID_NONE;
+
+ FM_INFO_REPORT("fmd_send_cmd_and_read_resp");
+
+ mutex_lock(&send_cmd_mutex);
+ result = fmd_send_cmd(
+ cmd_id,
+ num_parameters,
+ parameters);
+
+ if (result != 0)
+ goto error;
+
+ result = fmd_read_resp(
+ &read_cmd_id,
+ resp_num_parameters,
+ resp_parameters);
+
+ if (result != 0)
+ goto error;
+
+ /*
+ * Check that the response belongs to the sent command
+ */
+ if (read_cmd_id != cmd_id)
+ result = -EINVAL;
+
+error:
+ mutex_unlock(&send_cmd_mutex);
+ FM_DEBUG_REPORT("fmd_send_cmd_and_read_resp: "
+ "returning %d", result);
+ return result;
+}
+
+/**
+ * fmd_send_cmd() - This function sends the HCI Command
+ * to Protocol Driver.
+ *
+ * @cmd_id: Command Id to be sent to FM Chip.
+ * @num_parameters: Number of parameters of the command sent.
+ * @parameters: Buffer containing the Buffer to be sent.
+ *
+ * Returns:
+ * 0: If the command is sent successfully to Lower Layers.
+ * -EIO: If there is an input/output error.
+ * -EINVAL: If parameters are not valid.
+ */
+static int fmd_send_cmd(
+ const u16 cmd_id ,
+ const u16 num_parameters,
+ const u16 *parameters
+ )
+{
+ /*
+ * Total Length includes 6 bytes HCI Header
+ * and remaining bytes depending on number of paramters.
+ */
+ u16 total_length = num_parameters * sizeof(u16) + FM_HCI_CMD_HEADER_LEN;
+ /*
+ * Parameter Length includes 5 bytes HCI Header
+ * and remaining bytes depending on number of paramters.
+ */
+ u16 param_length = num_parameters * sizeof(u16) + FM_HCI_CMD_PARAM_LEN;
+ u8 *fm_data = kmalloc(total_length, GFP_KERNEL);
+ int err = -EINVAL;
+
+ FM_INFO_REPORT("fmd_send_cmd");
+
+ if (fm_data == NULL) {
+ err = -EIO;
+ goto error;
+ }
+
+ if (num_parameters && parameters == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ /* HCI encapsulation */
+ fm_data[0] = param_length;
+ fm_data[1] = FM_CATENA_OPCODE;
+ fm_data[2] = FM_WRITE;
+ fm_data[3] = FM_FUNCTION_WRITE_COMMAND;
+ fm_data[4] = FM_CMD_GET_LSB(cmd_id, num_parameters);
+ fm_data[5] = FM_CMD_GET_MSB(cmd_id);
+
+ memcpy(
+ (fm_data + FM_HCI_CMD_HEADER_LEN),
+ (void *)parameters,
+ num_parameters * sizeof(u16));
+
+ /* Send the Packet */
+ err = fmd_send_packet(total_length , fm_data);
+
+error:
+ kfree(fm_data);
+ FM_DEBUG_REPORT("fmd_send_cmd: "
+ "returning %d", err);
+ return err;
+}
+
+/**
+ * fmd_read_resp() - This function reads the response packet of the previous
+ * command sent to FM Chip and copies it to the buffer provided as parameter.
+ *
+ * @cmd_id: (out) Command Id received from FM Chip.
+ * @num_parameters: (out) Number of paramters of the response packet.
+ * @parameters: (out) Buffer of the response packet.
+ *
+ * Returns:
+ * 0: If the response buffer is copied successfully.
+ * -EINVAL: If parameters are not valid.
+ * -ETIME: Otherwise
+ */
+static int fmd_read_resp(
+ u16 *cmd_id,
+ u16 *num_parameters,
+ u16 *parameters
+ )
+{
+ int err;
+ FM_INFO_REPORT("fmd_read_resp");
+
+ /* Wait till response of the command is received */
+ if (fmd_get_cmd_sem()) {
+ err = -ETIME;
+ goto error;
+ }
+
+ /* Check if the parameters are valid */
+ if (cmd_id == NULL || (fmd_data.num_parameters &&
+ (num_parameters == NULL || parameters == NULL))) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ /* Fill the arguments */
+ *cmd_id = fmd_data.cmd_id;
+ if (fmd_data.num_parameters) {
+ *num_parameters = fmd_data.num_parameters;
+ memcpy(
+ parameters,
+ fmd_data.parameters,
+ (*num_parameters * sizeof(u16)));
+ }
+
+ err = 0;
+
+error:
+ FM_DEBUG_REPORT("fmd_read_resp: "
+ "returning %d", err);
+ return err;
+}
+
+/**
+ * fmd_process_fm_function() - Process FM Function.
+ *
+ * This function processes the Response buffer received
+ * from lower layers for the FM function and performs the necessary action to
+ * parse the same.
+ * @packet_buffer: Received Buffer.
+ */
+static void fmd_process_fm_function(
+ u8 *packet_buffer
+ )
+{
+ u8 fm_function_id;
+ u8 block_id;
+
+ if (packet_buffer == NULL)
+ return;
+
+ fm_function_id = FM_GET_FUNCTION_ID(packet_buffer);
+ switch (fm_function_id) {
+ case FM_FUNCTION_ENABLE:
+ case FM_FUNCTION_DISABLE:
+ case FM_FUNCTION_RESET:
+ FM_DEBUG_REPORT(
+ "fmd_process_fm_function: "
+ "command success received for %s",
+ fmd_get_fm_function_name(fm_function_id));
+ /* Release the semaphore since response is received */
+ fmd_set_cmd_sem();
+ break;
+ case FM_FUNCTION_WRITE_COMMAND:
+ FM_DEBUG_REPORT(
+ "fmd_process_fm_function: "
+ "command success received for %s",
+ fmd_get_fm_function_name(fm_function_id));
+
+ fmd_data.cmd_id = FM_GET_CMD_ID(packet_buffer);
+ fmd_data.num_parameters =
+ FM_GET_NUM_PARAMS(packet_buffer);
+
+ FM_DEBUG_REPORT(
+ "fmd_process_fm_function: "
+ "Cmd Id = 0x%04x, Num Of Parms = %02x",
+ fmd_data.cmd_id, fmd_data.num_parameters);
+
+ if (fmd_data.num_parameters) {
+ fmd_data.parameters =
+ FM_GET_RSP_BUFFER_ADDR(packet_buffer);
+ memcpy(fmd_data.parameters,
+ FM_GET_RSP_BUFFER_ADDR(packet_buffer),
+ fmd_data.num_parameters * sizeof(u16));
+ }
+ /* Release the semaphore since response is received */
+ fmd_set_cmd_sem();
+ break;
+ case FM_FUNCTION_FIRMWARE_DOWNLOAD:
+ block_id = FM_GET_BLOCK_ID(packet_buffer);
+ FM_DEBUG_REPORT(
+ "fmd_process_fm_function: "
+ "command success received for %s"
+ "block id = %02x",
+ fmd_get_fm_function_name(fm_function_id),
+ block_id);
+ /* Release the semaphore since response is received */
+ fmd_set_cmd_sem();
+ break;
+ default:
+ FM_ERR_REPORT(
+ "fmd_process_fm_function: "
+ "default case: command success received for %s",
+ fmd_get_fm_function_name(fm_function_id));
+ break;
+ }
+}
+
+/**
+ * fmd_write_file_block() - download firmware.
+ *
+ * This Function adds the header for downloading
+ * the firmware and coeffecient files and sends it to Protocol Driver.
+ * @file_block_id: Block ID of the F/W to be transmitted to FM Chip
+ * @file_block: Buffer containing the bytes to be sent.
+ * @file_block_length: Size of the Firmware buffer.
+ *
+ * Returns:
+ * 0: If there is no error.
+ * -EINVAL: If parameters are not valid.
+ * -ETIME: Otherwise
+ */
+static int fmd_write_file_block(
+ u32 file_block_id,
+ u8 *file_block,
+ u16 file_block_length
+ )
+{
+ int err;
+
+ FM_INFO_REPORT("fmd_write_file_block");
+ if (file_block == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ mutex_lock(&send_cmd_mutex);
+ file_block[0] = file_block_length + FM_HCI_WRITE_FILE_BLK_PARAM_LEN;
+ file_block[1] = FM_CATENA_OPCODE;
+ file_block[2] = FM_WRITE;
+ file_block[3] = FM_FUNCTION_FIRMWARE_DOWNLOAD;
+ file_block[4] = file_block_id;
+ /* Send the Packet */
+ err = fmd_send_packet(
+ file_block_length +
+ FM_HCI_WRITE_FILE_BLK_HEADER_LEN,
+ file_block);
+
+ /* wait till response comes */
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+
+error:
+ mutex_unlock(&send_cmd_mutex);
+ FM_DEBUG_REPORT("fmd_write_file_block: "
+ "returning %d", err);
+ return err;
+}
+
+int fmd_init(void)
+{
+ int err;
+
+ if (!fmd_driver_init()) {
+ err = -EIO;
+ goto error;
+ }
+
+ memset(&fmd_state_info, 0, sizeof(fmd_state_info));
+ fmd_state_info.fmd_initialized = true;
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ fmd_state_info.callback = NULL;
+ fmd_state_info.rx_freq_range = FMD_FREQRANGE_EUROAMERICA;
+ fmd_state_info.rx_stereo_mode = FMD_STEREOMODE_BLENDING;
+ fmd_state_info.rx_volume = MAX_ANALOG_VOLUME;
+ fmd_state_info.rx_antenna = FMD_ANTENNA_EMBEDDED;
+ fmd_state_info.rx_rds_on = false;
+ fmd_state_info.rx_seek_stop_level = DEFAULT_RSSI_THRESHOLD;
+ fmd_state_info.tx_freq_range = FMD_FREQRANGE_EUROAMERICA;
+ fmd_state_info.tx_preemphasis = FMD_EMPHASIS_75US;
+ fmd_state_info.tx_pilot_dev = DEFAULT_PILOT_DEVIATION;
+ fmd_state_info.tx_rds_dev = DEFAULT_RDS_DEVIATION;
+ fmd_state_info.tx_strength = MAX_POWER_LEVEL;
+ fmd_state_info.max_channels_to_scan = DEFAULT_CHANNELS_TO_SCAN;
+ fmd_state_info.tx_stereo_mode = true;
+ fmd_state_info.irq_index = 0;
+ spin_lock_init(&fmd_spinlock);
+ err = 0;
+
+error:
+ FM_DEBUG_REPORT("fmd_init returning = %d", err);
+ return err;
+}
+
+void fmd_exit(void)
+{
+ fmd_set_interrupt_sem();
+ fmd_driver_exit();
+ memset(&fmd_state_info, 0, sizeof(fmd_state_info));
+}
+
+int fmd_register_callback(
+ fmd_radio_cb callback
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ fmd_state_info.callback = callback;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_get_version(
+ u16 *version
+ )
+{
+ int err;
+ int io_result;
+ u16 response_count;
+ u16 response_data[CMD_GET_VERSION_RSP_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (version == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_GEN_GET_VERSION,
+ CMD_GET_VERSION_PARAM_LEN,
+ NULL,
+ &response_count,
+ response_data);
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ memcpy(version,
+ response_data,
+ sizeof(u16) * CMD_GET_VERSION_RSP_PARAM_LEN);
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_set_mode(
+ u8 mode
+ )
+{
+ int err;
+ u16 parameters[CMD_GOTO_MODE_PARAM_LEN];
+ int io_result;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (mode > FMD_MODE_TX) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = mode;
+
+ fmd_state_info.gocmd = FMD_STATE_MODE;
+ FM_ERR_REPORT("Sending Set Mode");
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_GEN_GOTO_MODE,
+ CMD_GOTO_MODE_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_get_freq_range_properties(
+ u8 range,
+ u32 *min_freq,
+ u32 *max_freq
+ )
+{
+ int err;
+
+ if (min_freq == NULL || max_freq == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ switch (range) {
+ case FMD_FREQRANGE_EUROAMERICA:
+ *min_freq = FMD_EU_US_MIN_FREQ_IN_KHZ;
+ *max_freq = FMD_EU_US_MAX_FREQ_IN_KHZ;
+ break;
+ case FMD_FREQRANGE_JAPAN:
+ *min_freq = FMD_JAPAN_MIN_FREQ_IN_KHZ;
+ *max_freq = FMD_JAPAN_MAX_FREQ_IN_KHZ;
+ break;
+ case FMD_FREQRANGE_CHINA:
+ *min_freq = FMD_CHINA_MIN_FREQ_IN_KHZ;
+ *max_freq = FMD_CHINA_MAX_FREQ_IN_KHZ;
+ break;
+ default:
+ *min_freq = FMD_EU_US_MIN_FREQ_IN_KHZ;
+ *max_freq = FMD_EU_US_MAX_FREQ_IN_KHZ;
+ break;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_set_antenna(
+ u8 antenna
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SET_ANTENNA_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (antenna > FMD_ANTENNA_WIRED) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = antenna;
+
+ fmd_state_info.gocmd = FMD_STATE_ANTENNA;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SET_ANTENNA,
+ CMD_SET_ANTENNA_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.rx_antenna = antenna;
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_get_antenna(
+ u8 *antenna
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ *antenna = fmd_state_info.rx_antenna;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_set_freq_range(
+ u8 range
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_TN_SET_BAND_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ parameters[0] = range;
+ parameters[1] = FMD_MIN_CHANNEL_NUMBER;
+ parameters[2] = FMD_MAX_CHANNEL_NUMBER;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_TN_SET_BAND,
+ CMD_TN_SET_BAND_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+ fmd_state_info.rx_freq_range = range;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_get_freq_range(
+ u8 *range
+ )
+{
+ int err;
+
+ if (range == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ *range = fmd_state_info.rx_freq_range;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_set_grid(
+ u8 grid
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_TN_SET_GRID_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (grid > FMD_GRID_200KHZ) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = grid;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_TN_SET_GRID,
+ CMD_TN_SET_GRID_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_set_frequency(
+ u32 freq
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SP_TUNE_SET_CHANNEL_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (freq > FMD_EU_US_MAX_FREQ_IN_KHZ ||
+ freq < FMD_CHINA_MIN_FREQ_IN_KHZ) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ io_result = fmd_rx_frequency_to_channel(
+ freq,
+ &parameters[0]);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.gocmd = FMD_STATE_FREQUENCY;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_TUNE_SET_CHANNEL,
+ CMD_SP_TUNE_SET_CHANNEL_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_get_frequency(
+ u32 *freq
+ )
+{
+ int err;
+ int io_result;
+ u16 response_count;
+ u16 response_data[CMD_SP_TUNE_GET_CHANNEL_RSP_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (freq == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_TUNE_GET_CHANNEL,
+ CMD_SP_TUNE_GET_CHANNEL_PARAM_LEN,
+ NULL,
+ &response_count,
+ response_data);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ io_result = fmd_rx_channel_to_frequency(
+ response_data[0], /* 1st byte is the Frequency */
+ freq);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_set_stereo_mode(
+ u8 mode
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_RP_STEREO_SET_MODE_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (mode > FMD_STEREOMODE_BLENDING) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = mode;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_RP_STEREO_SET_MODE,
+ CMD_RP_STEREO_SET_MODE_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.rx_stereo_mode = mode;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_get_stereo_mode(
+ u8 *mode
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (mode == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *mode = fmd_state_info.rx_stereo_mode;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_get_signal_strength(
+ u16 *strength
+ )
+{
+ int err;
+ int io_result;
+ u16 response_count;
+ u16 response_data[CMD_RP_GET_RSSI_RSP_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (strength == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_RP_GET_RSSI,
+ CMD_RP_GET_RSSI_PARAM_LEN,
+ NULL,
+ &response_count,
+ response_data);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ *strength = response_data[0]; /* 1st byte is the signal strength */
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_set_stop_level(
+ u16 stoplevel
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ fmd_state_info.rx_seek_stop_level = stoplevel;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_get_stop_level(
+ u16 *stop_level
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (stop_level == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *stop_level = fmd_state_info.rx_seek_stop_level;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_seek(
+ bool upwards
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SP_SEARCH_START_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (upwards)
+ parameters[0] = 0x0000;
+ else
+ parameters[0] = 0x0001;
+ parameters[1] = fmd_state_info.rx_seek_stop_level;
+ parameters[2] = DEFAULT_PEAK_NOISE_VALUE;
+ parameters[3] = DEFAULT_AVERAGE_NOISE_MAX_VALUE;
+ fmd_state_info.gocmd = FMD_STATE_SEEK;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_SEARCH_START,
+ CMD_SP_SEARCH_START_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_scan_band(
+ u8 max_channels_to_scan
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SP_SCAN_START_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (max_channels_to_scan > MAX_CHANNELS_TO_SCAN) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = max_channels_to_scan;
+ parameters[1] = fmd_state_info.rx_seek_stop_level;
+ parameters[2] = DEFAULT_PEAK_NOISE_VALUE;
+ parameters[3] = DEFAULT_AVERAGE_NOISE_MAX_VALUE;
+
+ fmd_state_info.gocmd = FMD_STATE_SCAN_BAND;
+ fmd_state_info.max_channels_to_scan = max_channels_to_scan;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_SCAN_START,
+ CMD_SP_SCAN_START_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_get_max_channels_to_scan(
+ u8 *max_channels_to_scan
+ )
+{
+ int err;
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (max_channels_to_scan == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *max_channels_to_scan = fmd_state_info.max_channels_to_scan;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_get_scan_band_info(
+ u32 index,
+ u16 *num_channels,
+ u16 *channels,
+ u16 *rssi
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SP_SCAN_GET_RESULT_PARAM_LEN];
+ u16 response_count;
+ u16 response_data[CMD_SP_SCAN_GET_RESULT_RSP_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (num_channels == NULL || rssi == NULL || channels == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = index;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_SCAN_GET_RESULT,
+ CMD_SP_SCAN_GET_RESULT_PARAM_LEN,
+ parameters,
+ &response_count,
+ response_data);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ /* 1st byte indicates number of channels found */
+ *num_channels = response_data[0];
+ /* 2nd byte indicates 1st channel number */
+ channels[0] = response_data[1];
+ /* 3rd byte indicates RSSI of corresponding channel */
+ rssi[0] = response_data[2];
+ /* 4th byte indicates 2nd channel number */
+ channels[1] = response_data[3];
+ /* 5th byte indicates RSSI of corresponding channel */
+ rssi[1] = response_data[4];
+ /* 6th byte indicates 3rd channel number */
+ channels[2] = response_data[5];
+ /* 7th byte indicates RSSI of corresponding channel */
+ rssi[2] = response_data[6];
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_block_scan(
+ u32 start_freq,
+ u32 stop_freq,
+ u8 antenna
+ )
+{
+ u16 start_channel;
+ u16 stop_channel;
+ int err;
+ int io_result;
+ u16 parameters[CMD_SP_BLOCK_SCAN_START_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (antenna > FMD_ANTENNA_WIRED) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ if (start_freq > FMD_EU_US_MAX_FREQ_IN_KHZ ||
+ start_freq < FMD_CHINA_MIN_FREQ_IN_KHZ) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ if (stop_freq > FMD_EU_US_MAX_FREQ_IN_KHZ ||
+ stop_freq < FMD_CHINA_MIN_FREQ_IN_KHZ) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ /* Convert the start frequency to corresponsing channel */
+ io_result = fmd_rx_frequency_to_channel(
+ start_freq,
+ &start_channel);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ /* Convert the end frequency to corresponsing channel */
+ io_result = fmd_rx_frequency_to_channel(
+ stop_freq,
+ &stop_channel);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ parameters[0] = start_channel;
+ parameters[1] = stop_channel;
+ parameters[2] = antenna;
+
+ fmd_state_info.gocmd = FMD_STATE_BLOCK_SCAN;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_BLOCK_SCAN_START,
+ CMD_SP_BLOCK_SCAN_START_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_get_block_scan_result(
+ u32 index,
+ u16 *num_channels,
+ u16 *rssi
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SP_BLOCK_SCAN_GET_RESULT_PARAM_LEN];
+ u16 response_count;
+ u16 response_data[CMD_SP_BLOCK_SCAN_GET_RESULT_RSP_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (num_channels == NULL || rssi == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = index;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_BLOCK_SCAN_GET_RESULT,
+ CMD_SP_BLOCK_SCAN_GET_RESULT_PARAM_LEN,
+ parameters,
+ &response_count,
+ response_data);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ /*
+ * Response packet has 1st byte as the number
+ * of channels, and the remaining 6 bytes as
+ * rssi values of the channels.
+ */
+ *num_channels = response_data[0];
+ rssi[0] = response_data[1];
+ rssi[1] = response_data[2];
+ rssi[2] = response_data[3];
+ rssi[3] = response_data[4];
+ rssi[4] = response_data[5];
+ rssi[5] = response_data[6];
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_stop_seeking(void)
+{
+ int err;
+ int io_result;
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (!(fmd_state_info.gocmd == FMD_STATE_SEEK ||
+ fmd_state_info.gocmd == FMD_STATE_SCAN_BAND ||
+ fmd_state_info.gocmd == FMD_STATE_BLOCK_SCAN)) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ fmd_state_info.gocmd = FMD_STATE_SEEK_STOP;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_STOP,
+ CMD_SP_STOP_PARAM_LEN,
+ NULL,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_af_update_start(
+ u32 freq
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SP_AF_UPDATE_START_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ io_result = fmd_rx_frequency_to_channel(
+ freq,
+ &parameters[0]);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.gocmd = FMD_STATE_AF_UPDATE;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_AF_UPDATE_START,
+ CMD_SP_AF_UPDATE_START_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ goto error;
+ }
+
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_get_af_update_result(
+ u16 *af_level
+ )
+{
+ int err;
+ int io_result;
+ u16 response_count;
+ u16 response_data[CMD_SP_AF_UPDATE_GET_RESULT_RSP_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (af_level == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_AF_UPDATE_GET_RESULT,
+ CMD_SP_AF_UPDATE_GET_RESULT_PARAM_LEN,
+ NULL,
+ &response_count,
+ response_data);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ /*
+ * 1st byte of response packet is the
+ * RSSI of the AF Frequency.
+ */
+ *af_level = response_data[0];
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_af_switch_start(
+ u32 freq,
+ u16 picode
+ )
+{
+
+ int err;
+ int io_result;
+ u16 parameters[CMD_SP_AF_SWITCH_START_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ io_result = fmd_rx_frequency_to_channel(
+ freq,
+ &parameters[0]);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ parameters[1] = picode;
+ parameters[2] = 0xFFFF; /* PI Mask */
+ parameters[3] = fmd_state_info.rx_seek_stop_level;
+ parameters[4] = 0x0000; /* Unmute when AF's PI matches expected PI */
+
+ fmd_state_info.gocmd = FMD_STATE_AF_SWITCH;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_AF_SWITCH_START,
+ CMD_SP_AF_SWITCH_START_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_get_af_switch_results(
+ u16 *afs_conclusion,
+ u16 *afs_level,
+ u16 *afs_pi
+ )
+{
+ int err;
+ int io_result;
+ u16 response_count;
+ u16 response_data[CMD_SP_AF_SWITCH_GET_RESULT_RWSP_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (afs_conclusion == NULL ||
+ afs_level == NULL ||
+ afs_pi == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_SP_AF_SWITCH_GET_RESULT,
+ CMD_SP_AF_SWITCH_GET_RESULT_PARAM_LEN,
+ NULL,
+ &response_count,
+ response_data);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ *afs_conclusion = response_data[0];
+ *afs_level = response_data[1];
+ *afs_pi = response_data[2];
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_get_rds(
+ bool *on
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (on == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *on = fmd_state_info.rx_rds_on;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_buffer_set_size(
+ u8 size
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_DP_BUFFER_SET_SIZE_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (size > MAX_RDS_GROUPS) {
+ err = -EIO;
+ goto error;
+ }
+
+ parameters[0] = size;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_DP_BUFFER_SET_SIZE,
+ CMD_DP_BUFFER_SET_SIZE_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_buffer_set_threshold(
+ u8 threshold
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_DP_BUFFER_SET_THRESHOLD_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (threshold > MAX_RDS_GROUPS) {
+ err = -EIO;
+ goto error;
+ }
+
+ parameters[0] = threshold;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_DP_BUFFER_SET_THRESHOLD,
+ CMD_DP_BUFFER_SET_THRESHOLD_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_set_rds(
+ u8 on_off_state
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_DP_SET_CONTROL_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ switch (on_off_state) {
+ case FMD_SWITCH_ON_RDS_SIMULATOR:
+ parameters[0] = 0xFFFF;
+ break;
+ case FMD_SWITCH_OFF_RDS:
+ default:
+ parameters[0] = 0x0000;
+ fmd_state_info.rx_rds_on = false;
+ break;
+ case FMD_SWITCH_ON_RDS:
+ parameters[0] = 0x0001;
+ fmd_state_info.rx_rds_on = true;
+ break;
+ case FMD_SWITCH_ON_RDS_ENHANCED_MODE:
+ parameters[0] = 0x0002;
+ fmd_state_info.rx_rds_on = true;
+ break;
+ }
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_DP_SET_CONTROL,
+ CMD_DP_SET_CONTROL_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_get_low_level_rds_groups(
+ u8 index,
+ u16 *block1,
+ u16 *block2,
+ u16 *block3,
+ u16 *block4,
+ u8 *status1,
+ u8 *status2,
+ u8 *status3,
+ u8 *status4
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (block1 == NULL ||
+ block2 == NULL ||
+ block3 == NULL ||
+ block4 == NULL ||
+ status1 == NULL ||
+ status2 == NULL ||
+ status3 == NULL ||
+ status4 == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *block1 = fmd_state_info.rds_group[index].block[0];
+ *block2 = fmd_state_info.rds_group[index].block[1];
+ *block3 = fmd_state_info.rds_group[index].block[2];
+ *block4 = fmd_state_info.rds_group[index].block[3];
+ *status1 = fmd_state_info.rds_group[index].status[0];
+ *status2 = fmd_state_info.rds_group[index].status[1];
+ *status3 = fmd_state_info.rds_group[index].status[2];
+ *status4 = fmd_state_info.rds_group[index].status[3];
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_rx_set_deemphasis(
+ u8 deemphasis
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_RP_SET_DEEMPHASIS_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ switch (deemphasis) {
+ case FMD_EMPHASIS_50US:
+ parameters[0] = FMD_EMPHASIS_50US;
+ break;
+
+ case FMD_EMPHASIS_75US:
+ parameters[0] = FMD_EMPHASIS_75US;
+ break;
+
+ case FMD_EMPHASIS_NONE:
+ default:
+ parameters[0] = FMD_EMPHASIS_NONE;
+ break;
+
+ }
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_RP_SET_DEEMPHASIS,
+ CMD_RP_SET_DEEMPHASIS_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_set_pa(
+ bool on
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_PA_SET_MODE_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (on)
+ parameters[0] = 0x0001;
+ else
+ parameters[0] = 0x0000;
+
+ fmd_state_info.gocmd = FMD_STATE_PA;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_PA_SET_MODE,
+ CMD_PA_SET_MODE_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_set_signal_strength(
+ u16 strength
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_PA_SET_CONTROL_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if ((strength > MAX_POWER_LEVEL)
+ || (strength < MIN_POWER_LEVEL)) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = strength;
+
+ fmd_state_info.gocmd = FMD_STATE_PA_LEVEL;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_PA_SET_CONTROL,
+ CMD_PA_SET_CONTROL_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.tx_strength = strength;
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_get_signal_strength(
+ u16 *strength
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (strength == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *strength = fmd_state_info.tx_strength;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_set_freq_range(
+ u8 range
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_TN_SET_BAND_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (range > FMD_FREQRANGE_CHINA) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = range;
+ parameters[1] = FMD_MIN_CHANNEL_NUMBER;
+ parameters[2] = FMD_MAX_CHANNEL_NUMBER;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_TN_SET_BAND,
+ CMD_TN_SET_BAND_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.tx_freq_range = range;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_get_freq_range(
+ u8 *range
+ )
+{
+ int err;
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (range == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *range = fmd_state_info.tx_freq_range;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_set_grid(
+ u8 grid
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_TN_SET_GRID_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (grid > FMD_GRID_200KHZ) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = grid;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_TN_SET_GRID,
+ CMD_TN_SET_GRID_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_set_preemphasis(
+ u8 preemphasis
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_RP_SET_PREEMPHASIS_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ switch (preemphasis) {
+ case FMD_EMPHASIS_50US:
+ parameters[0] = FMD_EMPHASIS_50US;
+ break;
+ case FMD_EMPHASIS_75US:
+ default:
+ parameters[0] = FMD_EMPHASIS_75US;
+ break;
+ }
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_RP_SET_PREEMPHASIS,
+ CMD_RP_SET_PREEMPHASIS_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.tx_preemphasis = preemphasis;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_get_preemphasis(
+ u8 *preemphasis
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (preemphasis == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *preemphasis = fmd_state_info.tx_preemphasis;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_set_frequency(
+ u32 freq
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SP_TUNE_SET_CHANNEL_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (freq > FMD_EU_US_MAX_FREQ_IN_KHZ ||
+ freq < FMD_CHINA_MIN_FREQ_IN_KHZ) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ io_result = fmd_tx_frequency_to_channel(
+ freq,
+ &parameters[0]);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.gocmd = FMD_STATE_FREQUENCY;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_SP_TUNE_SET_CHANNEL,
+ CMD_SP_TUNE_SET_CHANNEL_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_get_frequency(
+ u32 *freq
+ )
+{
+ int err;
+ int io_result;
+ u16 response_count;
+ u16 response_data[CMD_SP_TUNE_GET_CHANNEL_RSP_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (freq == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_SP_TUNE_GET_CHANNEL,
+ CMD_SP_TUNE_GET_CHANNEL_PARAM_LEN,
+ NULL,
+ &response_count,
+ response_data);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ io_result = fmd_tx_channel_to_frequency(
+ response_data[0], /* 1st byte is the Frequency */
+ freq);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_enable_stereo_mode(
+ bool enable_stereo_mode
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_RP_STEREO_SET_MODE_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ parameters[0] = enable_stereo_mode;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_RP_STEREO_SET_MODE,
+ CMD_RP_STEREO_SET_MODE_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.tx_stereo_mode = enable_stereo_mode;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_get_stereo_mode(
+ bool *stereo_mode
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (stereo_mode == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *stereo_mode = fmd_state_info.tx_stereo_mode;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_set_pilot_deviation(
+ u16 deviation
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_RP_SET_PILOT_DEVIATION_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (deviation > MAX_PILOT_DEVIATION) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = deviation;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_RP_SET_PILOT_DEVIATION,
+ CMD_RP_SET_PILOT_DEVIATION_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.tx_pilot_dev = deviation;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_get_pilot_deviation(
+ u16 *deviation
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (deviation == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *deviation = fmd_state_info.tx_pilot_dev;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_set_rds_deviation(
+ u16 deviation
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_RP_SET_RDS_DEVIATION_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (deviation > MAX_RDS_DEVIATION) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = deviation;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_RP_SET_RDS_DEVIATION,
+ CMD_RP_SET_RDS_DEVIATION_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.tx_rds_dev = deviation;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_get_rds_deviation(
+ u16 *deviation
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (deviation == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *deviation = fmd_state_info.tx_rds_dev;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_set_rds(
+ bool on
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_DP_SET_CONTROL_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (on)
+ parameters[0] = 0x0001;
+ else
+ parameters[0] = 0x0000;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_DP_SET_CONTROL,
+ CMD_DP_SET_CONTROL_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.tx_rds_on = on;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_set_group(
+ u16 position,
+ u8 *block1,
+ u8 *block2,
+ u8 *block3,
+ u8 *block4
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_DP_BUFFER_SET_GROUP_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (block1 == NULL ||
+ block2 == NULL ||
+ block3 == NULL ||
+ block4 == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = position;
+ memcpy(&parameters[1], block1, sizeof(u16));
+ memcpy(&parameters[2], block2, sizeof(u16));
+ memcpy(&parameters[3], block3, sizeof(u16));
+ memcpy(&parameters[4], block4, sizeof(u16));
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_DP_BUFFER_SET_GROUP,
+ CMD_DP_BUFFER_SET_GROUP_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_tx_buffer_set_size(
+ u16 buffer_size
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_DP_BUFFER_SET_SIZE_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ parameters[0] = buffer_size;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMT_DP_BUFFER_SET_SIZE,
+ CMD_DP_BUFFER_SET_SIZE_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+
+}
+
+int fmd_tx_get_rds(
+ bool *on
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (on == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *on = fmd_state_info.tx_rds_on;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_set_balance(
+ s8 balance
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SET_BALANCE_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ /* Convert balance from percentage to chip number */
+ parameters[0] = (((s16)balance) * FMD_MAX_BALANCE) / 100;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_AUP_SET_BALANCE,
+ CMD_SET_BALANCE_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_set_volume(
+ u8 volume
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SET_VOLUME_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ /* Convert volume from percentage to chip number */
+ parameters[0] = (((u16)volume) * FMD_MAX_VOLUME) / 100;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_AUP_SET_VOLUME,
+ CMD_SET_VOLUME_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ fmd_state_info.rx_volume = volume;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_get_volume(
+ u8 *volume
+ )
+{
+ int err;
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (volume == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ *volume = fmd_state_info.rx_volume;
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_set_mute(
+ bool mute_on
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SET_MUTE_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (!mute_on)
+ parameters[0] = 0x0000;
+ else
+ parameters[0] = 0x0001;
+ parameters[1] = 0x0001;
+
+ fmd_state_info.gocmd = FMD_STATE_MUTE;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_AUP_SET_MUTE,
+ CMD_SET_MUTE_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_ext_set_mute(
+ bool mute_on
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_EXT_SET_MUTE_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (!mute_on)
+ parameters[0] = 0x0000;
+ else
+ parameters[0] = 0x0001;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_AUP_EXT_SET_MUTE,
+ CMD_EXT_SET_MUTE_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_power_up(void)
+{
+ int err;
+ int io_result;
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ fmd_state_info.gocmd = FMD_STATE_GEN_POWERUP;
+ FM_ERR_REPORT("Sending Gen Power Up");
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_GEN_POWERUP,
+ CMD_POWERUP_PARAM_LEN,
+ NULL,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_goto_standby(void)
+{
+ int err;
+ int io_result;
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_GEN_GOTO_STANDBY,
+ CMD_GOTO_STANDBY_PARAM_LEN,
+ NULL,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_goto_power_down(void)
+{
+ int err;
+ int io_result;
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_GEN_GOTO_POWERDOWN,
+ CMD_GOTO_POWERDOWN_PARAM_LEN,
+ NULL,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_select_ref_clk(
+ u16 ref_clk
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SELECT_REFERENCE_CLOCK_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ parameters[0] = ref_clk;
+
+ fmd_state_info.gocmd = FMD_STATE_SELECT_REF_CLK;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_GEN_SELECT_REFERENCE_CLOCK,
+ CMD_SELECT_REFERENCE_CLOCK_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_set_ref_clk_pll(
+ u16 freq
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_SET_REFERENCE_CLOCK_PLL_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ parameters[0] = freq;
+
+ fmd_state_info.gocmd = FMD_STATE_SET_REF_CLK_PLL;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_GEN_SET_REFERENCE_CLOCK_PLL,
+ CMD_SET_REFERENCE_CLOCK_PLL_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ fmd_state_info.gocmd = FMD_STATE_NONE;
+ err = io_result;
+ goto error;
+ }
+
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+ else
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_send_fm_ip_enable(void)
+{
+ int err;
+ u8 fm_ip_enable_cmd[CMD_IP_ENABLE_CMD_LEN];
+
+ mutex_lock(&send_cmd_mutex);
+ fm_ip_enable_cmd[0] = CMD_IP_ENABLE_PARAM_LEN;
+ fm_ip_enable_cmd[1] = FM_CATENA_OPCODE;
+ fm_ip_enable_cmd[2] = FM_WRITE ;
+ fm_ip_enable_cmd[3] = FM_FUNCTION_ENABLE;
+
+ /* Send the Packet */
+ err = fmd_send_packet(
+ CMD_IP_ENABLE_CMD_LEN,
+ fm_ip_enable_cmd);
+
+ /* Check the ErrorCode */
+ if (err != 0)
+ goto error;
+
+ /* wait till response comes */
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+
+error:
+ mutex_unlock(&send_cmd_mutex);
+ return err;
+}
+
+int fmd_send_fm_ip_disable(void)
+{
+ int err;
+ u8 fm_ip_disable_cmd[CMD_IP_DISABLE_CMD_LEN];
+
+ mutex_lock(&send_cmd_mutex);
+ fm_ip_disable_cmd[0] = CMD_IP_DISABLE_PARAM_LEN;
+ fm_ip_disable_cmd[1] = FM_CATENA_OPCODE;
+ fm_ip_disable_cmd[2] = FM_WRITE ;
+ fm_ip_disable_cmd[3] = FM_FUNCTION_DISABLE;
+
+ /* Send the Packet */
+ err = fmd_send_packet(
+ CMD_IP_DISABLE_CMD_LEN,
+ fm_ip_disable_cmd);
+
+ /* Check the ErrorCode */
+ if (err != 0)
+ goto error;
+
+ /* wait till response comes */
+ if (fmd_get_cmd_sem())
+ err = -ETIME;
+
+error:
+ mutex_unlock(&send_cmd_mutex);
+ return err;
+}
+
+int fmd_send_fm_firmware(
+ u8 *fw_buffer,
+ u16 fw_size
+ )
+{
+ int err;
+ u16 bytes_to_write = ST_WRITE_FILE_BLK_SIZE -
+ FM_HCI_WRITE_FILE_BLK_PARAM_LEN;
+ u16 bytes_remaining = fw_size;
+ u8 fm_firmware_data[ST_WRITE_FILE_BLK_SIZE + FM_HCI_CMD_HEADER_LEN];
+ u32 block_id = 0;
+
+ if (fw_buffer == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ while (bytes_remaining > 0) {
+ if (bytes_remaining <
+ (ST_WRITE_FILE_BLK_SIZE -
+ FM_HCI_WRITE_FILE_BLK_PARAM_LEN))
+ bytes_to_write = bytes_remaining;
+
+ /*
+ * Five bytes of HCI Header for FM Firmware
+ * so shift the firmware data by 5 bytes
+ */
+ memcpy(
+ fm_firmware_data + FM_HCI_WRITE_FILE_BLK_HEADER_LEN,
+ fw_buffer, bytes_to_write);
+ err = fmd_write_file_block(
+ block_id,
+ fm_firmware_data,
+ bytes_to_write);
+ if (err) {
+ FM_DEBUG_REPORT("fmd_send_fm_firmware: "
+ "Failed to download %d Block "
+ "error = %d", (unsigned int)block_id, err);
+ goto error;
+ }
+ /*
+ * Increment the Block Id by 1, since one
+ * block is successfully transmitted
+ * to the chip.
+ */
+ block_id++;
+ /*
+ * Increment the next firmware buffer equal
+ * to the number of bytes transmitted.
+ */
+ fw_buffer += bytes_to_write;
+ /*
+ * Decrement the number of bytes remaining
+ * equal to number of bytes transmitted successfully.
+ */
+ bytes_remaining -= bytes_to_write;
+
+ if (block_id == ST_MAX_NUMBER_OF_FILE_BLOCKS)
+ block_id = 0;
+ }
+
+error:
+ return err;
+}
+
+int fmd_int_bufferfull(
+ u16 *number_of_rds_groups
+ )
+{
+ u16 response_count;
+ u16 response_data[CMD_DP_BUFFER_GET_GROUP_COUNT_PARAM_LEN];
+ u16 index = 0;
+ u16 rds_group_count;
+ u8 result = -ENOEXEC;
+ struct fmd_rds_group rds_group;
+
+ if (!fmd_state_info.rx_rds_on)
+ goto error;
+
+ /* get group count*/
+ result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_DP_BUFFER_GET_GROUP_COUNT,
+ CMD_DP_BUFFER_GET_GROUP_COUNT_PARAM_LEN,
+ NULL,
+ &response_count,
+ response_data);
+
+ if (result != 0)
+ goto error;
+
+ /* read RDS groups */
+ rds_group_count = FM_GET_NUM_RDS_GRPS(response_data);
+ if (rds_group_count > MAX_RDS_GROUPS)
+ rds_group_count = MAX_RDS_GROUPS;
+
+ *number_of_rds_groups = rds_group_count;
+
+ if (rds_group_count) {
+ FM_DEBUG_REPORT("rds_group_count = %d", rds_group_count);
+ while (rds_group_count-- && fmd_state_info.rx_rds_on) {
+ result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_DP_BUFFER_GET_GROUP,
+ CMD_DP_BUFFER_GET_GROUP_PARAM_LEN,
+ NULL,
+ &response_count,
+ (u16 *)&rds_group);
+
+ if (result != 0)
+ goto error;
+
+ if (fmd_state_info.rx_rds_on)
+ fmd_state_info.rds_group[index++] = rds_group;
+ }
+ }
+error:
+ return result;
+}
+
+void fmd_start_rds_thread(
+ cg2900_fm_rds_cb cb_func
+ )
+{
+ FM_INFO_REPORT("fmd_start_rds_thread");
+ cb_rds_func = cb_func;
+ rds_thread_required = true;
+ rds_thread_task = kthread_create(fmd_rds_thread, NULL, "rds_thread");
+ if (IS_ERR(rds_thread_task)) {
+ FM_ERR_REPORT("fmd_start_rds_thread: "
+ "Unable to Create rds_thread");
+ rds_thread_task = NULL;
+ rds_thread_required = false;
+ return;
+ }
+ wake_up_process(rds_thread_task);
+}
+
+void fmd_stop_rds_thread(void)
+{
+ FM_INFO_REPORT("fmd_stop_rds_thread");
+ /* In case thread is waiting, set the rds sem */
+ fmd_set_rds_sem();
+ /* Re-initialize RDS Semaphore to zero */
+ sema_init(&rds_sem, 0);
+ cb_rds_func = NULL;
+ rds_thread_required = false;
+ if (rds_thread_task) {
+ kthread_stop(rds_thread_task);
+ rds_thread_task = NULL;
+ return;
+ }
+}
+
+void fmd_get_rds_sem(void)
+{
+ int ret_val;
+
+ FM_DEBUG_REPORT("fmd_get_rds_sem");
+ ret_val = down_killable(&rds_sem);
+
+ if (ret_val)
+ FM_ERR_REPORT("fmd_get_rds_sem: down_killable "
+ "returned error = %d", ret_val);
+}
+
+void fmd_set_rds_sem(void)
+{
+ FM_DEBUG_REPORT("fmd_set_rds_sem");
+ up(&rds_sem);
+}
+
+int fmd_set_dev(struct device *dev)
+{
+ struct cg2900_user_data *pf_data;
+
+ FM_DEBUG_REPORT("fmd_set_dev");
+
+ if (dev && cg2900_fm_dev) {
+ FM_ERR_REPORT("Only one FM device supported");
+ return -EACCES;
+ }
+
+ cg2900_fm_dev = dev;
+
+ if (!dev)
+ return 0;
+
+ pf_data = dev_get_platdata(dev);
+ pf_data->dev = dev;
+ pf_data->read_cb = fmd_read_cb;
+ pf_data->reset_cb = fmd_reset_cb;
+
+ return 0;
+}
+
+int fmd_set_test_tone_generator_status(
+ u8 test_tone_status
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_TST_TONE_ENABLE_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (test_tone_status > FMD_TST_TONE_ON_WO_SRC) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = test_tone_status;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_TST_TONE_ENABLE,
+ CMD_TST_TONE_ENABLE_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_test_tone_connect(
+ u8 left_audio_mode,
+ u8 right_audio_mode
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_TST_TONE_CONNECT_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (left_audio_mode > FMD_TST_TONE_AUDIO_TONE_SUM ||
+ right_audio_mode > FMD_TST_TONE_AUDIO_TONE_SUM) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = left_audio_mode;
+ parameters[1] = right_audio_mode;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_TST_TONE_CONNECT,
+ CMD_TST_TONE_CONNECT_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+int fmd_test_tone_set_params(
+ u8 tone_gen,
+ u16 frequency,
+ u16 volume,
+ u16 phase_offset,
+ u16 dc,
+ u8 waveform
+ )
+{
+ int err;
+ int io_result;
+ u16 parameters[CMD_TST_TONE_SET_PARAMS_PARAM_LEN];
+
+ if (fmd_go_cmd_busy()) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ if (!fmd_state_info.fmd_initialized) {
+ err = -ENOEXEC;
+ goto error;
+ }
+
+ if (tone_gen > FMD_TST_TONE_2 ||
+ waveform > FMD_TST_TONE_PULSE ||
+ frequency > 0x7FFF ||
+ volume > 0x7FFF) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ parameters[0] = tone_gen;
+ parameters[1] = frequency;
+ parameters[2] = volume;
+ parameters[3] = phase_offset;
+ parameters[4] = dc;
+ parameters[5] = waveform;
+
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_TST_TONE_SET_PARAMS,
+ CMD_TST_TONE_SET_PARAMS_PARAM_LEN,
+ parameters,
+ NULL,
+ NULL);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ return err;
+}
+
+MODULE_AUTHOR("Hemant Gupta");
+MODULE_LICENSE("GPL v2");
+
+module_param(cg2900_fm_debug_level, ushort, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(cg2900_fm_debug_level, "cg2900_fm_debug_level: "
+ " *1: Only Error Logs* "
+ " 2: Info Logs "
+ " 3: Debug Logs "
+ " 4: HCI Logs");
+
diff --git a/drivers/media/radio/CG2900/cg2900_fm_driver.h b/drivers/media/radio/CG2900/cg2900_fm_driver.h
new file mode 100755
index 00000000000..313824a1e00
--- /dev/null
+++ b/drivers/media/radio/CG2900/cg2900_fm_driver.h
@@ -0,0 +1,1761 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Linux FM Driver for CG2900 FM Chip
+ *
+ * Author: Hemant Gupta <hemant.gupta@stericsson.com> for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _FMDRIVER_H_
+#define _FMDRIVER_H_
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/smp_lock.h>
+#include <linux/semaphore.h>
+#include <linux/version.h>
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mutex.h>
+#include "cg2900_fm_api.h"
+
+/* structure declared in cg2900_fm_driver.c */
+extern struct timespec time_spec;
+
+/* module_param declared in cg2900_fm_driver.c */
+extern unsigned short cg2900_fm_debug_level;
+
+/**
+ * enum fmd_debug_levels - FM Driver Debug Levels.
+ *
+ * @FM_NO_LOGS: No Logs are displayed.
+ * @FM_ERROR_LOGS: Only Error Logs are displayed.
+ * @FM_INFO_LOGS: Function Entry logs are displayed.
+ * @FM_DEBUG_LOGS: Full debugging support.
+ * @FM_HCI_PACKET_LOGS: HCI Packet Sent/received to/by
+ * FM Driver are displayed.
+ *
+ * Various debug levels for FM Driver.
+ */
+enum fmd_debug_levels {
+ FM_NO_LOGS,
+ FM_ERROR_LOGS,
+ FM_INFO_LOGS,
+ FM_DEBUG_LOGS,
+ FM_HCI_PACKET_LOGS
+};
+
+#define FM_HEX_REPORT(fmt, arg...) \
+ if (cg2900_fm_debug_level == FM_HCI_PACKET_LOGS) { \
+ printk(KERN_INFO fmt "\r\n" , ## arg); \
+ }
+
+#define FM_DEBUG_REPORT(fmt, arg...) \
+ if (cg2900_fm_debug_level > FM_INFO_LOGS && \
+ cg2900_fm_debug_level < FM_HCI_PACKET_LOGS) { \
+ getnstimeofday(&time_spec); \
+ printk(KERN_INFO "\n[%08x:%08x] " \
+ "CG2900_FM_Driver: " fmt "\r\n" , \
+ (unsigned int)time_spec.tv_sec, \
+ (unsigned int)time_spec.tv_nsec, ## arg); \
+ }
+
+#define FM_INFO_REPORT(fmt, arg...) \
+ if (cg2900_fm_debug_level > FM_ERROR_LOGS && \
+ cg2900_fm_debug_level < FM_HCI_PACKET_LOGS) { \
+ getnstimeofday(&time_spec); \
+ printk(KERN_INFO "\n[%08x:%08x] " \
+ "CG2900_FM_Driver: " fmt "\r\n" , \
+ (unsigned int)time_spec.tv_sec, \
+ (unsigned int)time_spec.tv_nsec, ## arg); \
+ }
+
+#define FM_ERR_REPORT(fmt, arg...) \
+ if (cg2900_fm_debug_level >= FM_ERROR_LOGS) { \
+ getnstimeofday(&time_spec); \
+ printk(KERN_ERR "\n[%08x:%08x] " \
+ "CG2900_FM_Driver: " fmt "\r\n" , \
+ (unsigned int)time_spec.tv_sec, \
+ (unsigned int)time_spec.tv_nsec, ## arg); \
+ }
+
+#define MAX_COUNT_OF_IRQS 16
+#define MAX_BUFFER_SIZE 512
+#define MAX_NAME_SIZE 100
+/* Minimum Power level for CG2900. The value is in units of dBuV */
+#define MIN_POWER_LEVEL 88
+/* Maximum Power level for CG2900. The value is in units of dBuV */
+#define MAX_POWER_LEVEL 123
+/* Minimum RDS Deviation value for CG2900. The value is in units of 10 Hz */
+#define MIN_RDS_DEVIATION 0
+/* Default RDS Deviation value for CG2900. The value is in units of 10 Hz */
+#define DEFAULT_RDS_DEVIATION 200
+/* Maximum RDS Deviation value for CG2900. The value is in units of 10 Hz */
+#define MAX_RDS_DEVIATION 750
+#define FMD_EU_US_MIN_FREQ_IN_KHZ 87500
+#define FMD_EU_US_MAX_FREQ_IN_KHZ 108000
+#define FMD_JAPAN_MIN_FREQ_IN_KHZ 76000
+#define FMD_JAPAN_MAX_FREQ_IN_KHZ 90000
+#define FMD_CHINA_MIN_FREQ_IN_KHZ 70000
+#define FMD_CHINA_MAX_FREQ_IN_KHZ 108000
+#define FMD_MIN_CHANNEL_NUMBER 0
+#define FMD_MAX_CHANNEL_NUMBER 760
+/*
+ * Maximum supported balance for CG2900. This is just a hexadecimal number
+ * with no units.
+ */
+#define FMD_MAX_BALANCE 0x7FFF
+/*
+ * Maximum supported volume for CG2900. This is just a hexadecimal number
+ * with no units.
+ */
+#define FMD_MAX_VOLUME 0x7FFF
+/* Minimum Program Identification value as per RDS specification */
+#define MIN_PI_VALUE 0x0000
+/* Maximum Program Identification value as per RDS specification */
+#define MAX_PI_VALUE 0xFFFF
+/* Minimum Program Type code value as per RDS specification */
+#define MIN_PTY_VALUE 0
+/* Maximum Program Type code value as per RDS specification */
+#define MAX_PTY_VALUE 31
+/* Minimum Pilot Deviation value for CG2900. The value is in units of 10 Hz */
+#define MIN_PILOT_DEVIATION 0
+/* Default Pilot Deviation value for CG2900. The value is in units of 10 Hz */
+#define DEFAULT_PILOT_DEVIATION 675
+/* Maximum Pilot Deviation value for CG2900. The value is in units of 10 Hz */
+#define MAX_PILOT_DEVIATION 1000
+/*
+ * Default RSSI Threshold for a channel to be considered valid for CG2900.
+ * This is just a hexadecimal number with no units.
+ */
+#define DEFAULT_RSSI_THRESHOLD 0x0100
+/*
+ * Default Peak Noise level for a channel to be considered valid for CG2900.
+ * This is just a hexadecimal number with no units.
+ */
+#define DEFAULT_PEAK_NOISE_VALUE 0x0035
+/*
+ * Default Average Noise level for a channel to be considered valid for CG2900.
+ * This is just a hexadecimal number with no units.
+ */
+#define DEFAULT_AVERAGE_NOISE_MAX_VALUE 0x0030
+#define FREQUENCY_CONVERTOR_KHZ_HZ 1000
+#define CHANNEL_FREQ_CONVERTER_MHZ 50
+/* Interrupt(s) for CG2900 */
+#define IRPT_INVALID 0x0000
+#define IRPT_OPERATION_SUCCEEDED 0x0001
+#define IRPT_OPERATION_FAILED 0x0002
+#define IRPT_RX_BUFFERFULL_TX_BUFFEREMPTY 0x0008
+#define IRPT_RX_SIGNAL_QUALITYLOW_MUTE_STATUS_CHANGED 0x0010
+#define IRPT_RX_MONO_STEREO_TRANSITION 0x0020
+#define IRPT_TX_OVERMODULATION 0x0030
+#define IRPT_RX_RDS_SYNCFOUND_TX_OVERDRIVE 0x0040
+#define IRPT_RDS_SYNC_LOST 0x0080
+#define IRPT_PI_CODE_CHANGED 0x0100
+#define IRPT_REQUESTED_BLOCK_AVAILABLE 0x0200
+#define IRPT_BUFFER_CLEARED 0x2000
+#define IRPT_WARM_BOOT_READY 0x4000
+#define IRPT_COLD_BOOT_READY 0x8000
+/* FM Commands Id */
+#define CMD_ID_NONE 0x0000
+#define CMD_AUP_EXT_SET_MUTE 0x01E2
+#define CMD_AUP_SET_BALANCE 0x0042
+#define CMD_AUP_SET_MUTE 0x0062
+#define CMD_AUP_SET_VOLUME 0x0022
+#define CMD_FMR_DP_BUFFER_GET_GROUP 0x0303
+#define CMD_FMR_DP_BUFFER_GET_GROUP_COUNT 0x0323
+#define CMD_FMR_DP_BUFFER_SET_SIZE 0x0343
+#define CMD_FMR_DP_BUFFER_SET_THRESHOLD 0x06C3
+#define CMD_FMR_DP_SET_CONTROL 0x02A3
+#define CMD_FMR_RP_GET_RSSI 0x0083
+#define CMD_FMR_RP_STEREO_SET_MODE 0x0123
+#define CMD_FMR_SET_ANTENNA 0x0663
+#define CMD_FMR_SP_AF_SWITCH_GET_RESULT 0x0603
+#define CMD_FMR_SP_AF_SWITCH_START 0x04A3
+#define CMD_FMR_SP_AF_UPDATE_GET_RESULT 0x0483
+#define CMD_FMR_SP_AF_UPDATE_START 0x0463
+#define CMD_FMR_SP_BLOCK_SCAN_GET_RESULT 0x06A3
+#define CMD_FMR_SP_BLOCK_SCAN_START 0x0683
+#define CMD_FMR_SP_SCAN_GET_RESULT 0x0423
+#define CMD_FMR_SP_SCAN_START 0x0403
+#define CMD_FMR_SP_SEARCH_START 0x03E3
+#define CMD_FMR_SP_STOP 0x0383
+#define CMD_FMR_SP_TUNE_GET_CHANNEL 0x03A3
+#define CMD_FMR_SP_TUNE_SET_CHANNEL 0x03C3
+#define CMD_FMR_TN_SET_BAND 0x0023
+#define CMD_FMR_TN_SET_GRID 0x0043
+#define CMD_FMR_RP_SET_DEEMPHASIS 0x00C3
+#define CMD_FMT_DP_BUFFER_GET_POSITION 0x0204
+#define CMD_FMT_DP_BUFFER_SET_GROUP 0x0244
+#define CMD_FMT_DP_BUFFER_SET_SIZE 0x0224
+#define CMD_FMT_DP_BUFFER_SET_THRESHOLD 0x0284
+#define CMD_FMT_DP_SET_CONTROL 0x0264
+#define CMD_FMT_PA_SET_CONTROL 0x01A4
+#define CMD_FMT_PA_SET_MODE 0x01E4
+#define CMD_FMT_RP_SET_PILOT_DEVIATION 0x02A4
+#define CMD_FMT_RP_SET_PREEMPHASIS 0x00C4
+#define CMD_FMT_RP_SET_RDS_DEVIATION 0x0344
+#define CMD_FMT_RP_STEREO_SET_MODE 0x0164
+#define CMD_FMT_SP_TUNE_GET_CHANNEL 0x0184
+#define CMD_FMT_SP_TUNE_SET_CHANNEL 0x0064
+#define CMD_FMT_TN_SET_BAND 0x0024
+#define CMD_FMT_TN_SET_GRID 0x0044
+#define CMD_GEN_GET_MODE 0x0021
+#define CMD_GEN_GET_REGISTER_VALUE 0x00E1
+#define CMD_GEN_GET_VERSION 0x00C1
+#define CMD_GEN_GOTO_MODE 0x0041
+#define CMD_GEN_GOTO_POWERDOWN 0x0081
+#define CMD_GEN_GOTO_STANDBY 0x0061
+#define CMD_GEN_POWERUP 0x0141
+#define CMD_GEN_SELECT_REFERENCE_CLOCK 0x0201
+#define CMD_GEN_SET_REFERENCE_CLOCK 0x0161
+#define CMD_GEN_SET_REFERENCE_CLOCK_PLL 0x01A1
+#define CMD_GEN_SET_REGISTER_VALUE 0x0101
+#define CMD_TST_TONE_ENABLE 0x0027
+#define CMD_TST_TONE_CONNECT 0x0047
+#define CMD_TST_TONE_SET_PARAMS 0x0067
+
+/* FM Command Id Parameter Length */
+#define CMD_GET_VERSION_PARAM_LEN 0
+#define CMD_GET_VERSION_RSP_PARAM_LEN 7
+#define CMD_GOTO_MODE_PARAM_LEN 1
+#define CMD_SET_ANTENNA_PARAM_LEN 1
+#define CMD_TN_SET_BAND_PARAM_LEN 3
+#define CMD_TN_SET_GRID_PARAM_LEN 1
+#define CMD_SP_TUNE_SET_CHANNEL_PARAM_LEN 1
+#define CMD_SP_TUNE_GET_CHANNEL_PARAM_LEN 0
+#define CMD_SP_TUNE_GET_CHANNEL_RSP_PARAM_LEN 1
+#define CMD_RP_STEREO_SET_MODE_PARAM_LEN 1
+#define CMD_RP_GET_RSSI_PARAM_LEN 0
+#define CMD_RP_GET_RSSI_RSP_PARAM_LEN 1
+#define CMD_SP_SEARCH_START_PARAM_LEN 4
+#define CMD_SP_SCAN_START_PARAM_LEN 4
+#define CMD_SP_SCAN_GET_RESULT_PARAM_LEN 1
+#define CMD_SP_SCAN_GET_RESULT_RSP_PARAM_LEN 7
+#define CMD_SP_BLOCK_SCAN_START_PARAM_LEN 3
+#define CMD_SP_BLOCK_SCAN_GET_RESULT_PARAM_LEN 1
+#define CMD_SP_BLOCK_SCAN_GET_RESULT_RSP_PARAM_LEN 7
+#define CMD_SP_STOP_PARAM_LEN 0
+#define CMD_SP_AF_UPDATE_START_PARAM_LEN 1
+#define CMD_SP_AF_UPDATE_GET_RESULT_PARAM_LEN 0
+#define CMD_SP_AF_UPDATE_GET_RESULT_RSP_PARAM_LEN 1
+#define CMD_SP_AF_SWITCH_START_PARAM_LEN 5
+#define CMD_SP_AF_SWITCH_GET_RESULT_PARAM_LEN 0
+#define CMD_SP_AF_SWITCH_GET_RESULT_RWSP_PARAM_LEN 3
+#define CMD_DP_BUFFER_SET_SIZE_PARAM_LEN 1
+#define CMD_DP_BUFFER_SET_THRESHOLD_PARAM_LEN 1
+#define CMD_DP_SET_CONTROL_PARAM_LEN 1
+#define CMD_PA_SET_MODE_PARAM_LEN 1
+#define CMD_PA_SET_CONTROL_PARAM_LEN 1
+#define CMD_RP_SET_PREEMPHASIS_PARAM_LEN 1
+#define CMD_RP_SET_DEEMPHASIS_PARAM_LEN 1
+#define CMD_RP_SET_PILOT_DEVIATION_PARAM_LEN 1
+#define CMD_RP_SET_RDS_DEVIATION_PARAM_LEN 1
+#define CMD_DP_BUFFER_SET_GROUP_PARAM_LEN 5
+#define CMD_SET_BALANCE_PARAM_LEN 1
+#define CMD_SET_VOLUME_PARAM_LEN 1
+#define CMD_SET_MUTE_PARAM_LEN 2
+#define CMD_EXT_SET_MUTE_PARAM_LEN 1
+#define CMD_POWERUP_PARAM_LEN 0
+#define CMD_GOTO_STANDBY_PARAM_LEN 0
+#define CMD_GOTO_POWERDOWN_PARAM_LEN 0
+#define CMD_SELECT_REFERENCE_CLOCK_PARAM_LEN 1
+#define CMD_SET_REFERENCE_CLOCK_PLL_PARAM_LEN 1
+#define CMD_DP_BUFFER_GET_GROUP_COUNT_PARAM_LEN 0
+#define CMD_DP_BUFFER_GET_GROUP_PARAM_LEN 0
+#define CMD_IP_ENABLE_CMD_LEN 4
+#define CMD_IP_ENABLE_PARAM_LEN 3
+#define CMD_IP_DISABLE_CMD_LEN 4
+#define CMD_IP_DISABLE_PARAM_LEN 3
+#define CMD_TST_TONE_ENABLE_PARAM_LEN 1
+#define CMD_TST_TONE_CONNECT_PARAM_LEN 2
+#define CMD_TST_TONE_SET_PARAMS_PARAM_LEN 6
+
+/* FM HCI Command and event specific */
+#define FM_WRITE 0x00
+#define FM_READ 0x01
+#define FM_CATENA_OPCODE 0xFE
+#define HCI_CMD_FM 0xFD50
+#define HCI_CMD_VS_WRITE_FILE_BLOCK 0xFC2E
+#define FM_EVENT_ID 0x15
+#define FM_SUCCESS_STATUS 0x00
+#define FM_EVENT 0x01
+#define HCI_COMMAND_COMPLETE_EVENT 0x0E
+#define HCI_VS_DBG_EVENT 0xFF
+#define ST_WRITE_FILE_BLK_SIZE 254
+#define ST_MAX_NUMBER_OF_FILE_BLOCKS 256
+#define FM_PG1_INTERRUPT_EVENT_LEN 0x04
+#define FM_PG2_INTERRUPT_EVENT_LEN 0x06
+#define FM_HCI_CMD_HEADER_LEN 6
+#define FM_HCI_CMD_PARAM_LEN 5
+#define FM_HCI_WRITE_FILE_BLK_HEADER_LEN 5
+#define FM_HCI_WRITE_FILE_BLK_PARAM_LEN 4
+#define HCI_PACKET_INDICATOR_CMD 0x01
+#define HCI_PACKET_INDICATOR_EVENT 0x04
+#define HCI_PACKET_INDICATOR_FM_CMD_EVT 0x08
+/* FM Functions specific to CG2900 */
+#define FM_FUNCTION_ENABLE 0x00
+#define FM_FUNCTION_DISABLE 0x01
+#define FM_FUNCTION_RESET 0x02
+#define FM_FUNCTION_WRITE_COMMAND 0x10
+#define FM_FUNCTION_SET_INT_MASK_ALL 0x20
+#define FM_FUNCTION_GET_INT_MASK_ALL 0x21
+#define FM_FUNCTION_SET_INT_MASK 0x22
+#define FM_FUNCTION_GET_INT_MASK 0x23
+#define FM_FUNCTION_FIRMWARE_DOWNLOAD 0x30
+/* Command succeeded */
+#define FM_CMD_STATUS_CMD_SUCCESS 0x00
+/* HCI_ERR_HW_FAILURE when no response from the IP */
+#define FM_CMD_STATUS_HCI_ERR_HW_FAILURE 0x03
+/* HCI_ERR_INVALID_PARAMETERS. */
+#define FM_CMD_STATUS_HCI_ERR_INVALID_PARAMETERS 0x12
+/* When the host tries to send a command to an IP that hasn't been
+ * initialized.
+ */
+#define FM_CMD_STATUS_IP_UNINIT 0x15
+/* HCI_ERR_UNSPECIFIED_ERROR: any other error */
+#define FM_CMD_STATUS_HCI_ERR_UNSPECIFIED_ERROR 0x1F
+/* HCI_ERR_CMD_DISALLOWED when the host asks for an unauthorized operation
+ * (FM state transition for instance)
+ */
+#define FM_CMD_STATUS_HCI_ERR_CMD_DISALLOWED 0x0C
+/* Wrong sequence number for FM FW download command */
+#define FM_CMD_STATUS_WRONG_SEQ_NUM 0xF1
+/* Unknown file type for FM FW download command */
+#define FM_CMD_STATUS_UNKNOWN_FILE_TYPE 0xF2
+/* File version mismatch for FM FW download command */
+#define FM_CMD_STATUS_FILE_VERSION_MISMATCH 0xF3
+
+
+/**
+ * enum fmd_event - Events received.
+ *
+ * @FMD_EVENT_OPERATION_COMPLETED: Previous operation has been completed.
+ * @FMD_EVENT_ANTENNA_STATUS_CHANGED: Antenna has been changed.
+ * @FMD_EVENT_FREQUENCY_CHANGED: Frequency has been changed.
+ * @FMD_EVENT_SEEK_COMPLETED: Seek operation has completed.
+ * @FMD_EVENT_SCAN_BAND_COMPLETED: Band Scan completed.
+ * @FMD_EVENT_BLOCK_SCAN_COMPLETED: Block Scan completed.
+ * @FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE: Af Update or AF Switch is complete.
+ * @FMD_EVENT_MONO_STEREO_TRANSITION_COMPLETE: Mono stereo transition is
+ * completed.
+ * @FMD_EVENT_SEEK_STOPPED: Previous Seek/Band Scan/ Block Scan operation is
+ * stopped.
+ * @FMD_EVENT_GEN_POWERUP: FM IP Powerup has been powered up.
+ * @FMD_EVENT_RDSGROUP_RCVD: RDS Groups Full interrupt.
+ * @FMD_EVENT_LAST_ELEMENT: Last event, used for keeping count of
+ * number of events.
+ *
+ * Various events received from FM driver for Upper Layer(s) processing.
+ */
+enum fmd_event {
+ FMD_EVENT_OPERATION_COMPLETED,
+ FMD_EVENT_ANTENNA_STATUS_CHANGED,
+ FMD_EVENT_FREQUENCY_CHANGED,
+ FMD_EVENT_SEEK_COMPLETED,
+ FMD_EVENT_SCAN_BAND_COMPLETED,
+ FMD_EVENT_BLOCK_SCAN_COMPLETED,
+ FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE,
+ FMD_EVENT_MONO_STEREO_TRANSITION_COMPLETE,
+ FMD_EVENT_SEEK_STOPPED,
+ FMD_EVENT_GEN_POWERUP,
+ FMD_EVENT_RDSGROUP_RCVD,
+ FMD_EVENT_LAST_ELEMENT
+};
+
+/**
+ * enum fmd_mode - FM Driver Modes.
+ *
+ * @FMD_MODE_IDLE: FM Driver in Idle mode.
+ * @FMD_MODE_RX: FM Driver in Rx mode.
+ * @FMD_MODE_TX: FM Driver in Tx mode.
+ *
+ * Various Modes of FM Radio.
+ */
+enum fmd_mode {
+ FMD_MODE_IDLE,
+ FMD_MODE_RX,
+ FMD_MODE_TX
+};
+
+/**
+ * enum fmd_antenna - Antenna selection.
+ *
+ * @FMD_ANTENNA_EMBEDDED: Embedded Antenna.
+ * @FMD_ANTENNA_WIRED: Wired Antenna.
+ *
+ * Antenna to be used for FM Radio.
+ */
+enum fmd_antenna {
+ FMD_ANTENNA_EMBEDDED,
+ FMD_ANTENNA_WIRED
+};
+
+/**
+ * enum fmd_grid - Grid used on FM Radio.
+ *
+ * @FMD_GRID_50KHZ: 50 kHz grid spacing.
+ * @FMD_GRID_100KHZ: 100 kHz grid spacing.
+ * @FMD_GRID_200KHZ: 200 kHz grid spacing.
+ *
+ * Spacing used on FM Radio.
+ */
+enum fmd_grid {
+ FMD_GRID_50KHZ,
+ FMD_GRID_100KHZ,
+ FMD_GRID_200KHZ
+};
+
+/**
+ * enum fmd_emphasis - De-emphasis/Pre-emphasis level.
+ *
+ * @FMD_EMPHASIS_NONE: De-emphasis Disabled.
+ * @FMD_EMPHASIS_50US: 50 us de-emphasis/pre-emphasis level.
+ * @FMD_EMPHASIS_75US: 75 us de-emphasis/pre-emphasis level.
+ *
+ * De-emphasis/Pre-emphasis level used on FM Radio.
+ */
+enum fmd_emphasis {
+ FMD_EMPHASIS_NONE = 0,
+ FMD_EMPHASIS_50US = 1,
+ FMD_EMPHASIS_75US = 2
+};
+
+/**
+ * enum fmd_freq_range - Frequency range.
+ *
+ * @FMD_FREQRANGE_EUROAMERICA: EU/US Range (87.5 - 108 MHz).
+ * @FMD_FREQRANGE_JAPAN: Japan Range (76 - 90 MHz).
+ * @FMD_FREQRANGE_CHINA: China Range (70 - 108 MHz).
+ *
+ * Various Frequency range(s) supported by FM Radio.
+ */
+enum fmd_freq_range {
+ FMD_FREQRANGE_EUROAMERICA,
+ FMD_FREQRANGE_JAPAN,
+ FMD_FREQRANGE_CHINA
+};
+
+/**
+ * enum fmd_stereo_mode - FM Driver Stereo Modes.
+ *
+ * @FMD_STEREOMODE_OFF: Streo Blending Off.
+ * @FMD_STEREOMODE_MONO: Mono Mode.
+ * @FMD_STEREOMODE_BLENDING: Blending Mode.
+ *
+ * Various Stereo Modes of FM Radio.
+ */
+enum fmd_stereo_mode {
+ FMD_STEREOMODE_OFF,
+ FMD_STEREOMODE_MONO,
+ FMD_STEREOMODE_BLENDING
+};
+
+/**
+ * enum fmd_pilot_tone - Pilot Tone Selection
+ *
+ * @FMD_PILOT_TONE_DISABLED: Pilot Tone to be disabled.
+ * @FMD_PILOT_TONE_ENABLED: Pilot Tone to be enabled.
+ *
+ * Pilot Tone to be enabled or disabled.
+ */
+enum fmd_pilot_tone {
+ FMD_PILOT_TONE_DISABLED,
+ FMD_PILOT_TONE_ENABLED
+};
+
+/**
+ * enum fmd_output - Output of Sample Rate Converter.
+ *
+ * @FMD_OUTPUT_DISABLED: Sample Rate converter in disabled.
+ * @FMD_OUTPUT_I2S: I2S Output from Sample rate converter.
+ * @FMD_OUTPUT_PARALLEL: Parallel output from sample rate converter.
+ *
+ * Sample Rate Converter's output to be set on Connectivity Controller.
+ */
+enum fmd_output {
+ FMD_OUTPUT_DISABLED,
+ FMD_OUTPUT_I2S,
+ FMD_OUTPUT_PARALLEL
+};
+
+/**
+ * enum fmd_input - Audio Input to Sample Rate Converter.
+ *
+ * @FMD_INPUT_ANALOG: Selects the ADC's as audio source
+ * @FMD_INPUT_DIGITAL: Selects Digital Input as audio source.
+ *
+ * Audio Input source for Sample Rate Converter.
+ */
+enum fmd_input {
+ FMD_INPUT_ANALOG,
+ FMD_INPUT_DIGITAL
+};
+
+/**
+ * enum fmd_rds_mode - RDS Mode to be selected for FM Rx.
+ *
+ * @FMD_SWITCH_OFF_RDS: RDS Decoding disabled in FM Chip.
+ * @FMD_SWITCH_ON_RDS: RDS Decoding enabled in FM Chip.
+ * @FMD_SWITCH_ON_RDS_ENHANCED_MODE: Enhanced RDS Mode.
+ * @FMD_SWITCH_ON_RDS_SIMULATOR: RDS Simulator switched on in FM Chip.
+ *
+ * RDS Mode to be selected for FM Rx.
+ */
+enum fmd_rds_mode {
+ FMD_SWITCH_OFF_RDS,
+ FMD_SWITCH_ON_RDS,
+ FMD_SWITCH_ON_RDS_ENHANCED_MODE,
+ FMD_SWITCH_ON_RDS_SIMULATOR
+};
+
+/**
+ * enum fmd_tst_tone_status - Test Tone Generator Status.
+ *
+ * @FMD_TST_TONE_OFF: Test Tone Generator is off.
+ * @FMD_TST_TONE_ON_W_SRC: Test Tone Gen. is on with Sample Rate Conversion.
+ * @FMD_TST_TONE_ON_WO_SRC: Test Tone Gen. is on without Sample Rate Conversion.
+ *
+ * Test Tone Generator status to be set.
+ */
+enum fmd_tst_tone_status {
+ FMD_TST_TONE_OFF,
+ FMD_TST_TONE_ON_W_SRC,
+ FMD_TST_TONE_ON_WO_SRC
+};
+
+/**
+ * enum fmd_tst_tone_audio_mode - Test Tone Generator Audio Output/Input Mode.
+ *
+ * @FMD_TST_TONE_AUDIO_NORMAL: Normal Audio.
+ * @FMD_TST_TONE_AUDIO_ZERO: Zero.
+ * @FMD_TST_TONE_AUDIO_TONE_1: Tone 1.
+ * @FMD_TST_TONE_AUDIO_TONE_2: Tone 2.
+ * @FMD_TST_TONE_AUDIO_TONE_SUM: Sum of Tone 1 and Tone 2.
+ *
+ * Test Tone Generator Audio Output/Input Modes.
+ */
+enum fmd_tst_tone_audio_mode {
+ FMD_TST_TONE_AUDIO_NORMAL,
+ FMD_TST_TONE_AUDIO_ZERO,
+ FMD_TST_TONE_AUDIO_TONE_1,
+ FMD_TST_TONE_AUDIO_TONE_2,
+ FMD_TST_TONE_AUDIO_TONE_SUM
+};
+
+/**
+ * enum fmd_tst_tone - Test Tone of Internal Tone Generator.
+ *
+ * @FMD_TST_TONE_1: Test Tone 1
+ * @FMD_TST_TONE_2: Test Tone 2
+ *
+ * Test Tone.
+ */
+enum fmd_tst_tone {
+ FMD_TST_TONE_1,
+ FMD_TST_TONE_2
+};
+
+/**
+ * enum fmd_tst_tone_waveform - Test Tone Waveform of Internal Tone Generator.
+ *
+ * @FMD_TST_TONE_SINE: Sine wave
+ * @FMD_TST_TONE_PULSE: Pulse wave
+ *
+ * Test Tone waveform.
+ */
+enum fmd_tst_tone_waveform {
+ FMD_TST_TONE_SINE,
+ FMD_TST_TONE_PULSE
+};
+
+/* Callback function to receive radio events. */
+typedef void(*fmd_radio_cb)(
+ u8 event,
+ bool event_successful
+ );
+
+/**
+ * fmd_init() - Initialize the FM Driver internal structures.
+ *
+ * Returns:
+ * 0, if no error.
+ * -EIO, if there is an error.
+ */
+int fmd_init(void);
+
+/**
+ * fmd_exit() - De-initialize the FM Driver.
+ */
+void fmd_exit(void);
+
+/**
+ * fmd_register_callback() - Function to register callback function.
+ *
+ * This function registers the callback function provided by upper layers.
+ * @callback: Fmradio call back Function pointer
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_register_callback(
+ fmd_radio_cb callback
+ );
+
+/**
+ * fmd_get_version() - Retrieves the FM HW and FW version.
+ *
+ * @version: (out) Version Array
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameters are not valid.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_get_version(
+ u16 *version
+ );
+
+/**
+ * fmd_set_mode() - Starts a transition to the given mode.
+ *
+ * @mode: Transition mode
+ *
+ * Returns:
+ * 0, if set mode done successfully.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_set_mode(
+ u8 mode
+ );
+
+/**
+ * fmd_get_freq_range_properties() - Retrieves Freq Range Properties.
+ *
+ * @range: range of freq
+ * @min_freq: (out) Minimum Frequency of the Band in kHz.
+ * @max_freq: (out) Maximum Frequency of the Band in kHz
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ */
+int fmd_get_freq_range_properties(
+ u8 range,
+ u32 *min_freq,
+ u32 *max_freq
+ );
+
+/**
+ * fmd_set_antenna() - Selects the antenna to be used in receive mode.
+ *
+ * embedded - Selects the embedded antenna, wired- Selects the wired antenna.
+ * @antenna: Antenna Type
+ *
+ * Returns:
+ * 0, if set antenna done successfully.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_set_antenna(
+ u8 antenna
+ );
+
+/**
+ * fmd_get_antenna() - Retrieves the currently used antenna type.
+ *
+ * @antenna: (out) Antenna Selected on FM Radio.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_get_antenna(
+ u8 *antenna
+ );
+
+/**
+ * fmd_set_freq_range() - Sets the FM band.
+ *
+ * @range: freq range
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_set_freq_range(
+ u8 range
+ );
+
+/**
+ * fmd_get_freq_range() - Gets the FM band currently in use.
+ *
+ * @range: (out) Frequency Range set on FM Radio.
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ */
+int fmd_get_freq_range(
+ u8 *range
+ );
+
+/**
+ * fmd_rx_set_grid() - Sets the tuning grid.
+ *
+ * @grid: Tuning grid size
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_set_grid(
+ u8 grid
+ );
+
+/**
+ * fmd_rx_set_frequency() - Sets the FM Channel.
+ *
+ * @freq: Frequency to Set in Khz
+ *
+ * Returns:
+ * 0, if set frequency done successfully.
+ * -EINVAL, if parameters are invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_set_frequency(
+ u32 freq
+ );
+
+/**
+ * fmd_rx_get_frequency() - Gets the currently used FM Channel.
+ *
+ * @freq: (out) Current Frequency set on FM Radio.
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameters are invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_get_frequency(
+ u32 *freq
+ );
+
+/**
+ * fmd_rx_set_stereo_mode() - Sets the stereomode functionality.
+ *
+ * @mode: FMD_STEREOMODE_MONO, FMD_STEREOMODE_STEREO and
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_set_stereo_mode(
+ u8 mode
+ );
+
+/**
+ * fmd_rx_get_stereo_mode() - Gets the currently used FM mode.
+ *
+ * FMD_STEREOMODE_MONO, FMD_STEREOMODE_STEREO and
+ * FMD_STEREOMODE_AUTO.
+ * @mode: (out) Mode set on FM Radio, stereo or mono.
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_rx_get_stereo_mode(
+ u8 *mode
+ );
+
+/**
+ * fmd_rx_get_signal_strength() - Gets the RSSI level of current frequency.
+ *
+ * @strength: (out) RSSI level of current channel.
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_get_signal_strength(
+ u16 *strength
+ );
+
+/**
+ * fmd_rx_set_stop_level() - Sets the FM Rx Seek stop level.
+ *
+ * @stoplevel: seek stop level
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_rx_set_stop_level(
+ u16 stoplevel
+ );
+
+/**
+ * fmd_rx_get_stop_level() - Gets the current FM Rx Seek stop level.
+ *
+ * @stoplevel: (out) RSSI Threshold set on FM Radio.
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_rx_get_stop_level(
+ u16 *stoplevel
+ );
+
+/**
+ * fmd_rx_seek() - Perform FM Seek.
+ *
+ * Starts searching relative to the actual channel with
+ * a specific direction, stop.
+ * level and optional noise levels
+ * @upwards: scan up
+ *
+ * Returns:
+ * 0, if seek started successfully.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_seek(
+ bool upwards
+ );
+
+/**
+ * fmd_rx_stop_seeking() - Stops a currently active seek or scan band.
+ *
+ * Returns:
+ * 0, if stop seek done successfully.
+ * -ENOEXEC, if preconditions are violated.
+ * -ENOEXEC, if FM Driver is
+ * not currently in Seek or Scan State..
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_stop_seeking(void);
+
+/**
+ * fmd_rx_af_update_start() - Perform AF update.
+ *
+ * This is used to switch to a shortly tune to a AF freq,
+ * measure its RSSI and tune back to the original frequency.
+ * @freq: Alternative frequncy in KHz to be set for AF updation.
+ *
+ * Returns:
+ * -EBUSY, if FM Driver is not in idle state.
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ */
+int fmd_rx_af_update_start(
+ u32 freq
+ );
+
+/**
+ * fmd_rx_get_af_update_result() - Retrive result of AF update.
+ *
+ * Retrive the RSSI level of the Alternative frequency.
+ * @af_level: RSSI level of the Alternative frequency.
+ *
+ * Returns:
+ * -EBUSY, if FM Driver is not in idle state.
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ */
+int fmd_rx_get_af_update_result(
+ u16 *af_level
+ );
+
+/**
+ * fmd_af_switch_start() -Performs AF switch.
+ *
+ * @freq: Frequency to Set in Khz.
+ * @picode:programable id,unique for each station.
+ *
+ * Returns:
+ * -EBUSY, if FM Driver is not in idle state.
+ * 0, if no error and if AF switch started successfully.
+ * -ENOEXEC, if preconditions are violated.
+ */
+int fmd_rx_af_switch_start(
+ u32 freq,
+ u16 picode
+ );
+
+/**
+ * fmd_rx_get_af_switch_results() -Retrieves the results of AF Switch.
+ *
+ * @afs_conclusion: Conclusion of AF switch.
+ * @afs_level: RSSI level of the Alternative frequnecy.
+ * @afs_pi: PI code of the alternative channel (if found).
+ *
+ * Returns:
+ * -EBUSY, if FM Driver is not in idle state.
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ */
+int fmd_rx_get_af_switch_results(
+ u16 *afs_conclusion,
+ u16 *afs_level,
+ u16 *afs_pi
+ );
+
+/**
+ * fmd_rx_scan_band() - Starts Band Scan.
+ *
+ * Starts scanning the active band for the strongest
+ * channels above a threshold.
+ * @max_channels_to_scan: Maximum number of channels to scan.
+ *
+ * Returns:
+ * 0, if scan band started successfully.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_scan_band(
+ u8 max_channels_to_scan
+ );
+
+/**
+ * fmd_rx_get_max_channels_to_scan() - Retreives the maximum channels.
+ *
+ * Retrieves the maximum number of channels that can be found during
+ * band scann.
+ * @max_channels_to_scan: (out) Maximum number of channels to scan.
+ *
+ * Returns:
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if parameter is invalid.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_get_max_channels_to_scan(
+ u8 *max_channels_to_scan
+ );
+
+/**
+ * fmd_rx_get_scan_band_info() - Retrieves Channels found during scanning.
+ *
+ * Retrieves the scanned active band
+ * for the strongest channels above a threshold.
+ * @index: (out) Index value to retrieve the channels.
+ * @numchannels: (out) Number of channels found during Band Scan.
+ * @channels: (out) Channels found during band scan.
+ * @rssi: (out) Rssi of channels found during Band scan.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_get_scan_band_info(
+ u32 index,
+ u16 *numchannels,
+ u16 *channels,
+ u16 *rssi
+ );
+
+/**
+ * fmd_rx_block_scan() - Starts Block Scan.
+ *
+ * Starts block scan for retriving the RSSI level of channels
+ * in the given block.
+ * @start_freq: Starting frequency of the block from where scanning has
+ * to be started.
+ * @stop_freq: End frequency of the block to be scanned.
+ * @antenna: Antenna to be used during scanning.
+ *
+ * Returns:
+ * 0, if scan band started successfully.
+ * -EINVAL, if parameters are invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_block_scan(
+ u32 start_freq,
+ u32 stop_freq,
+ u8 antenna
+ );
+
+/**
+ * fmd_rx_get_block_scan_result() - Retrieves RSSI Level of channels.
+ *
+ * Retrieves the RSSI level of the channels in the block.
+ * @index: (out) Index value to retrieve the channels.
+ * @numchannels: (out) Number of channels found during Band Scan.
+ * @rssi: (out) Rssi of channels found during Band scan.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_get_block_scan_result(
+ u32 index,
+ u16 *numchannels,
+ u16 *rssi
+ );
+
+/**
+ * fmd_rx_get_rds() - Gets the current status of RDS transmission.
+ *
+ * @on: (out) RDS status
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_rx_get_rds(
+ bool *on
+ );
+
+/**
+ * fmd_rx_buffer_set_size() - Sets the number of groups that the data buffer.
+ * can contain and clears the buffer.
+ *
+ * @size: buffer size
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_buffer_set_size(
+ u8 size
+ );
+
+/**
+ * fmd_rx_buffer_set_threshold() - RDS Buffer Threshold level in FM Chip.
+ *
+ * Sets the group number at which the RDS buffer full interrupt must be
+ * generated. The interrupt will be set after reception of the group.
+ * @threshold: threshold level.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_buffer_set_threshold(
+ u8 threshold
+ );
+
+/**
+ * fmd_rx_set_rds() - Enables or disables demodulation of RDS data.
+ *
+ * @on_off_state : Rx Set ON /OFF control
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_rx_set_rds(
+ u8 on_off_state
+ );
+
+/**
+ * fmd_rx_get_low_level_rds_groups() - Gets Low level RDS group data.
+ *
+ * @index: RDS group index
+ * @block1: (out) RDS Block 1
+ * @block2: (out) RDS Block 2
+ * @block3: (out) RDS Block 3
+ * @block4: (out) RDS Block 4
+ * @status1: (out) RDS data status 1
+ * @status2: (out) RDS data status 2
+ * @status3: (out) RDS data status 3
+ * @status4: (out) RDS data status 4
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_rx_get_low_level_rds_groups(
+ u8 index,
+ u16 *block1,
+ u16 *block2,
+ u16 *block3,
+ u16 *block4,
+ u8 *status1,
+ u8 *status2,
+ u8 *status3,
+ u8 *status4
+ );
+
+/**
+ * fmd_tx_set_pa() - Enables or disables the Power Amplifier.
+ *
+ * @on: Power Amplifier current state to set
+ *
+ * Returns:
+ * 0, if set Power Amplifier done successfully.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_set_pa(
+ bool on
+ );
+
+/**
+ * fmd_tx_set_signal_strength() - Sets the RF-level of the output FM signal.
+ *
+ * @strength: Signal strength to be set for FM Tx in dBuV.
+ *
+ * Returns:
+ * 0, if set RSSI Level done successfully.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_set_signal_strength(
+ u16 strength
+ );
+
+/**
+ * fmd_tx_get_signal_strength() - Retrieves current RSSI of FM Tx.
+ *
+ * @strength: (out) Strength of signal being transmitted in dBuV.
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_tx_get_signal_strength(
+ u16 *strength
+ );
+
+/**
+ * fmd_tx_set_freq_range() - Sets the FM band and specifies the custom band.
+ *
+ * @range: Freq range to set on FM Tx.
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_set_freq_range(
+ u8 range
+ );
+
+/**
+ * fmd_tx_get_freq_range() - Gets the FM band currently in use.
+ *
+ * @range: (out) Frequency Range set on Fm Tx.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ */
+int fmd_tx_get_freq_range(
+ u8 *range
+ );
+
+/**
+ * fmd_tx_set_grid() - Sets the tuning grid size.
+ *
+ * @grid: FM Grid (50 Khz, 100 Khz, 200 Khz) to be set for FM Tx.
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_set_grid(
+ u8 grid
+ );
+
+/**
+ * fmd_tx_get_grid() - Gets the current tuning grid size.
+ *
+ * @grid: (out) FM Grid (50 Khz, 100 Khz, 200 Khz) currently set on FM Tx.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_tx_get_grid(
+ u8 *grid
+ );
+
+/**
+ * fmd_tx_set_preemphasis() - Sets the Preemphasis characteristic of the Tx.
+ *
+ * @preemphasis: Pre-emphasis level to be set for FM Tx.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_set_preemphasis(
+ u8 preemphasis
+ );
+
+/**
+ * fmd_tx_get_preemphasis() - Gets the currently used Preemphasis char of th FM Tx.
+ *
+ * @preemphasis: (out) Preemphasis Level used for FM Tx.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_tx_get_preemphasis(
+ u8 *preemphasis
+ );
+
+/**
+ * fmd_tx_set_frequency() - Sets the FM Channel for Tx.
+ *
+ * @freq: Freq to be set for transmission.
+ *
+ * Returns:
+ * 0, if set frequency done successfully.
+ * -EINVAL, if parameters are invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_set_frequency(
+ u32 freq
+ );
+
+/**
+ * fmd_rx_get_frequency() - Gets the currently used Channel for Tx.
+ *
+ * @freq: (out) Frequency set on FM Tx.
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameters are invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_get_frequency(
+ u32 *freq
+ );
+
+/**
+ * fmd_tx_enable_stereo_mode() - Sets Stereo mode state for TX.
+ *
+ * @enable_stereo_mode: Flag indicating enabling or disabling Stereo mode.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_enable_stereo_mode(
+ bool enable_stereo_mode
+ );
+
+/**
+ * fmd_tx_get_stereo_mode() - Gets the currently used FM Tx stereo mode.
+ *
+ * @stereo_mode: (out) Stereo Mode state set on FM Tx.
+ *
+ * Returns:
+ * 0, if no error.
+ * -EINVAL, if parameter is invalid.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_tx_get_stereo_mode(
+ bool *stereo_mode
+ );
+
+/**
+ * fmd_tx_set_pilot_deviation() - Sets pilot deviation in HZ
+ *
+ * @deviation: Pilot deviation in HZ to set on FM Tx.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_set_pilot_deviation(
+ u16 deviation
+ );
+
+/**
+ * fmd_tx_get_pilot_deviation() - Retrieves the current pilot deviation.
+ *
+ * @deviation: (out) Pilot deviation set on FM Tx.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_tx_get_pilot_deviation(
+ u16 *deviation
+ );
+
+/**
+ * fmd_tx_set_rds_deviation() - Sets Rds deviation in HZ.
+ *
+ * @deviation: RDS deviation in HZ.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_set_rds_deviation(
+ u16 deviation
+ );
+
+/**
+ * fmd_tx_get_rds_deviation() - Retrieves the current Rds deviation.
+ *
+ * @deviation: (out) RDS deviation currently set.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_tx_get_rds_deviation(
+ u16 *deviation
+ );
+
+/**
+ * fmd_tx_set_rds() - Enables or disables RDS transmission for Tx.
+ *
+ * @on: Boolean - RDS ON
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_set_rds(
+ bool on
+ );
+
+/**
+ * fmd_rx_get_rds() - Gets the current status of RDS transmission for FM Tx.
+ *
+ * @on: (out) Rds enabled or disabled.
+ *
+ *Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_tx_get_rds(
+ bool *on
+ );
+
+/**
+ * fmd_tx_set_group() - Programs a grp on a certain position in the RDS buffer.
+ *
+ * @position: RDS group position
+ * @block1: Data to be transmitted in Block 1
+ * @block2: Data to be transmitted in Block 2
+ * @block3: Data to be transmitted in Block 3
+ * @block4: Data to be transmitted in Block 4
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if parameters are invalid.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_set_group(
+ u16 position,
+ u8 *block1,
+ u8 *block2,
+ u8 *block3,
+ u8 *block4
+ );
+
+/**
+ * fmd_tx_buffer_set_size() - Controls the size of the RDS buffer in groups.
+ *
+ * @buffer_size: RDS buffer size.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_tx_buffer_set_size(
+ u16 buffer_size
+ );
+
+/**
+ * fmd_set_volume() - Sets the receive audio volume.
+ *
+ * @volume: Audio volume level
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_set_volume(
+ u8 volume
+ );
+
+/**
+ * fmd_get_volume() - Retrives the current audio volume.
+ *
+ * @volume: Analog Volume level.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if parameter is invalid.
+ * -EBUSY, if FM Driver is not in idle state.
+ */
+int fmd_get_volume(
+ u8 *volume
+ );
+
+/**
+ * fmd_set_balance() - Controls the receiver audio balance.
+ *
+ * @balance: Audio balance level
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_set_balance(
+ s8 balance
+ );
+
+/**
+ * fmd_set_mute() - Enables or disables muting of the analog audio(DAC).
+ *
+ * @mute_on: bool of mute on
+ *
+ * Returns:
+ * 0, if mute done successfully.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_set_mute(
+ bool mute_on
+ );
+
+/**
+ * fmd_ext_set_mute() - Enables or disables muting of the audio channel.
+ *
+ * @mute_on: bool to Mute
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_ext_set_mute(
+ bool mute_on
+ );
+
+/**
+ * fmd_power_up() - Puts the system in Powerup state.
+ *
+ * Returns:
+ * 0, if power up command sent successfully to chip.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_power_up(void);
+
+/**
+ * fmd_goto_standby() - Puts the system in standby mode.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_goto_standby(void);
+
+/**
+ * fmd_goto_power_down() - Puts the system in Powerdown mode.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_goto_power_down(void);
+
+/**
+ * fmd_select_ref_clk() - Selects the FM reference clock.
+ *
+ * @ref_clk: Ref Clock.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_select_ref_clk(
+ u16 ref_clk
+ );
+
+/**
+ * fmd_set_ref_clk_pll() - Sets the freq of Referece Clock.
+ *
+ * Sets frequency and offset correction properties of the external
+ * reference clock of the PLL
+ * @freq: PLL Frequency/ 2 in kHz.
+ *
+ * Returns:
+ * 0, if no error.
+ * -ENOEXEC, if preconditions are violated.
+ * -EBUSY, if FM Driver is not in idle state.
+ * -EINVAL, if wrong response received from chip.
+ */
+int fmd_set_ref_clk_pll(
+ u16 freq
+ );
+
+/**
+ * fmd_send_fm_ip_enable()- Enables the FM IP.
+ *
+ * Returns:
+ * 0: If there is no error.
+ * -ETIME: Otherwise
+ */
+int fmd_send_fm_ip_enable(void);
+
+/**
+ * fmd_send_fm_ip_disable()- Disables the FM IP.
+ *
+ * Returns:
+ * 0, If there is no error.
+ * -ETIME: Otherwise
+ */
+int fmd_send_fm_ip_disable(void);
+
+/**
+ * fmd_send_fm_firmware() - Send the FM Firmware File to Device.
+ *
+ * @fw_buffer: Firmware to be downloaded.
+ * @fw_size: Size of firmware to be downloaded.
+ *
+ * Returns:
+ * 0, If there is no error.
+ * -ETIME: Otherwise
+ */
+int fmd_send_fm_firmware(
+ u8 *fw_buffer,
+ u16 fw_size
+ );
+
+/**
+ * fmd_int_bufferfull() - RDS Groups availabe for reading by Host.
+ *
+ * Gets the number of groups that are available in the
+ * buffer. This function is called in RX mode to read RDS groups.
+ * @number_of_rds_groups: Number of RDS groups ready to
+ * be read from the Host.
+ *
+ * Returns:
+ * 0, If there is no error.
+ * corresponding error Otherwise
+ */
+int fmd_int_bufferfull(
+ u16 *number_of_rds_groups
+ );
+
+/**
+ * fmd_start_rds_thread() - Starts the RDS Thread for receiving RDS Data.
+ *
+ * This is started by Application when it wants to receive RDS Data.
+ * @cb_func: Callback function for receiving RDS Data
+ */
+void fmd_start_rds_thread(
+ cg2900_fm_rds_cb cb_func
+ );
+/**
+ * fmd_stop_rds_thread() - Stops the RDS Thread when Application does not
+ * want to receive RDS.
+ */
+void fmd_stop_rds_thread(void);
+
+/**
+ * fmd_get_rds_sem() - Block on RDS Semaphore.
+ * Till irpt_BufferFull is received, RDS Task is blocked.
+ */
+void fmd_get_rds_sem(void);
+
+/**
+ * fmd_set_rds_sem() - Unblock on RDS Semaphore.
+ * on receiving irpt_BufferFull, RDS Task is un-blocked.
+ */
+void fmd_set_rds_sem(void);
+
+/**
+ * fmd_set_dev() - Set FM device.
+ *
+ * @dev: FM Device
+ *
+ * Returns:
+ * 0, If there is no error.
+ * corresponding error Otherwise
+ */
+int fmd_set_dev(
+ struct device *dev
+ );
+
+/**
+ * fmd_set_test_tone_generator_status()- Sets the Test Tone Generator.
+ *
+ * This function is used to enable/disable the Internal Tone Generator of
+ * CG2900.
+ * @test_tone_status: Status of tone generator.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int fmd_set_test_tone_generator_status(
+ u8 test_tone_status
+ );
+
+/**
+ * fmd_test_tone_connect()- Connect Audio outputs/inputs.
+ *
+ * This function connects the audio outputs/inputs of the
+ * Internal Tone Generator of CG2900.
+ * @left_audio_mode: Left Audio Output Mode.
+ * @right_audio_mode: Right Audio Output Mode.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int fmd_test_tone_connect(
+ u8 left_audio_mode,
+ u8 right_audio_mode
+ );
+
+/**
+ * fmd_test_tone_set_params()- Sets the Test Tone Parameters.
+ *
+ * This function is used to set the parameters of
+ * the Internal Tone Generator of CG2900.
+ * @tone_gen: Tone to be configured (Tone 1 or Tone 2)
+ * @frequency: Frequency of the tone.
+ * @volume: Volume of the tone.
+ * @phase_offset: Phase offset of the tone.
+ * @dc: DC to add to tone.
+ * @waveform: Waveform to generate, sine or pulse.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int fmd_test_tone_set_params(
+ u8 tone_gen,
+ u16 frequency,
+ u16 volume,
+ u16 phase_offset,
+ u16 dc,
+ u8 waveform
+ );
+
+/**
+ * fmd_rx_set_deemphasis()- Connect Audio outputs/inputs.
+ *
+ * This function sets the de-emphasis filter to the
+ * specified de-empahsis level.
+ * @deemphasis: De-emphasis level to set.
+ *
+ * Returns:
+ * 0, if operation completed successfully.
+ * -EINVAL, otherwise.
+ */
+int fmd_rx_set_deemphasis(
+ u8 deemphasis
+ );
+#endif /* _FMDRIVER_H_ */
diff --git a/drivers/media/radio/CG2900/radio-cg2900.c b/drivers/media/radio/CG2900/radio-cg2900.c
new file mode 100644
index 00000000000..4fe3e6ffd5c
--- /dev/null
+++ b/drivers/media/radio/CG2900/radio-cg2900.c
@@ -0,0 +1,2855 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Linux Wrapper for V4l2 FM Driver for CG2900.
+ *
+ * Author: Hemant Gupta <hemant.gupta@stericsson.com> for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include<linux/init.h>
+#include<linux/videodev2.h>
+#include"linux/videodev.h"
+#include<media/v4l2-ioctl.h>
+#include<media/v4l2-common.h>
+#include<linux/module.h>
+#include <linux/platform_device.h>
+#include<linux/string.h>
+#include<linux/wait.h>
+#include"cg2900_fm_driver.h"
+
+#define RADIO_CG2900_VERSION KERNEL_VERSION(1, 1, 0)
+#define BANNER "ST-Ericsson FM Radio Card driver v1.1.0"
+
+#define FMR_HZ_TO_MHZ_CONVERTER 1000000
+#define FMR_EU_US_LOW_FREQ_IN_MHZ 87.5
+#define FMR_EU_US_HIGH_FREQ_IN_MHZ 108
+#define FMR_JAPAN_LOW_FREQ_IN_MHZ 76
+#define FMR_JAPAN_HIGH_FREQ_IN_MHZ 90
+#define FMR_CHINA_LOW_FREQ_IN_MHZ 70
+#define FMR_CHINA_HIGH_FREQ_IN_MHZ 108
+#define FMR_MAX_BLOCK_SCAN_CHANNELS 198
+#define FMR_CHINA_GRID_IN_HZ 50000
+#define FMR_EUROPE_GRID_IN_HZ 100000
+#define FMR_USA_GRID_IN_HZ 200000
+#define FMR_AF_SWITCH_DATA_SIZE 2
+#define FMR_BLOCK_SCAN_DATA_SIZE 2
+#define FMR_TEST_TONE_CONNECT_DATA_SIZE 2
+#define FMR_TEST_TONE_SET_PARAMS_DATA_SIZE 6
+
+/* freq in Hz to V4l2 freq (units of 62.5Hz) */
+#define HZ_TO_V4L2(X) (2*(X)/125)
+/* V4l2 freq (units of 62.5Hz) to freq in Hz */
+#define V4L2_TO_HZ(X) (((X)*125)/(2))
+
+static int cg2900_open(
+ struct file *file
+ );
+static int cg2900_release(
+ struct file *file
+ );
+static ssize_t cg2900_read(
+ struct file *file,
+ char __user *data,
+ size_t count,
+ loff_t *pos
+ );
+static unsigned int cg2900_poll(
+ struct file *file,
+ struct poll_table_struct *wait
+ );
+static int vidioc_querycap(
+ struct file *file,
+ void *priv,
+ struct v4l2_capability *query_caps
+ );
+static int vidioc_get_tuner(
+ struct file *file,
+ void *priv,
+ struct v4l2_tuner *tuner
+ );
+static int vidioc_set_tuner(
+ struct file *file,
+ void *priv,
+ struct v4l2_tuner *tuner
+ );
+static int vidioc_get_modulator(
+ struct file *file,
+ void *priv,
+ struct v4l2_modulator *modulator
+ );
+static int vidioc_set_modulator(
+ struct file *file,
+ void *priv,
+ struct v4l2_modulator *modulator
+ );
+static int vidioc_get_frequency(
+ struct file *file,
+ void *priv,
+ struct v4l2_frequency *freq
+ );
+static int vidioc_set_frequency(
+ struct file *file,
+ void *priv,
+ struct v4l2_frequency *freq
+ );
+static int vidioc_query_ctrl(
+ struct file *file,
+ void *priv,
+ struct v4l2_queryctrl *query_ctrl
+ );
+static int vidioc_get_ctrl(
+ struct file *file,
+ void *priv,
+ struct v4l2_control *ctrl
+ );
+static int vidioc_set_ctrl(
+ struct file *file,
+ void *priv,
+ struct v4l2_control *ctrl
+ );
+static int vidioc_get_ext_ctrls(
+ struct file *file,
+ void *priv,
+ struct v4l2_ext_controls *ext_ctrl
+ );
+static int vidioc_set_ext_ctrls(
+ struct file *file,
+ void *priv,
+ struct v4l2_ext_controls *ext_ctrl
+ );
+static int vidioc_set_hw_freq_seek(
+ struct file *file,
+ void *priv,
+ struct v4l2_hw_freq_seek *freq_seek
+ );
+static int vidioc_get_audio(
+ struct file *file,
+ void *priv,
+ struct v4l2_audio *audio
+ );
+static int vidioc_set_audio(
+ struct file *file,
+ void *priv,
+ struct v4l2_audio *audio
+ );
+static int vidioc_get_input(
+ struct file *filp,
+ void *priv,
+ unsigned int *input
+ );
+static int vidioc_set_input(
+ struct file *filp,
+ void *priv,
+ unsigned int input
+ );
+static void cg2900_convert_err_to_v4l2(
+ char status_byte,
+ char *out_byte
+ );
+
+static u32 freq_low;
+static u32 freq_high;
+
+/* Module Parameters */
+static int radio_nr = -1;
+static int grid;
+static int band;
+
+/* cg2900_poll_queue - Main Wait Queue for polling (Scan/Seek) */
+static wait_queue_head_t cg2900_poll_queue;
+
+/* cg2900_read_queue - Main Wait Queue for receiving RDS Data */
+static DECLARE_WAIT_QUEUE_HEAD(cg2900_read_queue);
+
+/**
+ * enum fm_seek_status - Seek status of FM Radio.
+ *
+ * @FMR_SEEK_NONE: No seek in progress.
+ * @FMR_SEEK_IN_PROGRESS: Seek is in progress.
+ *
+ * Seek status of FM Radio.
+ */
+enum fm_seek_status {
+ FMR_SEEK_NONE,
+ FMR_SEEK_IN_PROGRESS
+};
+
+/**
+ * enum fm_power_state - Power states of FM Radio.
+ *
+ * @FMR_SWITCH_OFF: FM Radio is switched off.
+ * @FMR_SWITCH_ON: FM Radio is switched on.
+ * @FMR_STANDBY: FM Radio in standby state.
+ *
+ * Power states of FM Radio.
+ */
+enum fm_power_state {
+ FMR_SWITCH_OFF,
+ FMR_SWITCH_ON,
+ FMR_STANDBY
+};
+
+/**
+ * struct cg2900_device - Stores FM Device Info.
+ *
+ * @state: state of FM Radio
+ * @muted: FM Radio Mute/Unmute status
+ * @seekstatus: seek status
+ * @rx_rds_enabled: Rds enable/disable status for FM Rx
+ * @tx_rds_enabled: Rds enable/disable status for FM Tx
+ * @rx_stereo_status: Stereo Mode status for FM Rx
+ * @tx_stereo_status: Stereo Mode status for FM Tx
+ * @volume: Analog Volume Gain of FM Radio
+ * @rssi_threshold: rssi Thresold set on FM Radio
+ * @frequency: Frequency tuned on FM Radio in V4L2 Format
+ * @audiopath: Audio Balance
+ * @wait_on_read_queue: Flag for waiting on read queue.
+ * @fm_mode: Enum for storing the current FM Mode.
+ *
+ * FM Driver Information Structure.
+ */
+struct cg2900_device {
+ u8 state;
+ u8 muted;
+ u8 seekstatus;
+ bool rx_rds_enabled;
+ bool tx_rds_enabled;
+ bool rx_stereo_status;
+ bool tx_stereo_status;
+ int volume;
+ u16 rssi_threshold;
+ u32 frequency;
+ u32 audiopath;
+ bool wait_on_read_queue;
+ enum cg2900_fm_mode fm_mode;
+};
+
+/* Global Structure to store the maintain FM Driver device info */
+static struct cg2900_device cg2900_device;
+
+/* V4l2 File Operation Structure */
+static const struct v4l2_file_operations cg2900_fops = {
+ .owner = THIS_MODULE,
+ .open = cg2900_open,
+ .release = cg2900_release,
+ .read = cg2900_read,
+ .poll = cg2900_poll,
+ .ioctl = video_ioctl2,
+};
+
+/* V4L2 IOCTL Operation Structure */
+static const struct v4l2_ioctl_ops cg2900_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_get_tuner,
+ .vidioc_s_tuner = vidioc_set_tuner,
+ .vidioc_g_modulator = vidioc_get_modulator,
+ .vidioc_s_modulator = vidioc_set_modulator,
+ .vidioc_g_frequency = vidioc_get_frequency,
+ .vidioc_s_frequency = vidioc_set_frequency,
+ .vidioc_queryctrl = vidioc_query_ctrl,
+ .vidioc_g_ctrl = vidioc_get_ctrl,
+ .vidioc_s_ctrl = vidioc_set_ctrl,
+ .vidioc_g_ext_ctrls = vidioc_get_ext_ctrls,
+ .vidioc_s_ext_ctrls = vidioc_set_ext_ctrls,
+ .vidioc_s_hw_freq_seek = vidioc_set_hw_freq_seek,
+ .vidioc_g_audio = vidioc_get_audio,
+ .vidioc_s_audio = vidioc_set_audio,
+ .vidioc_g_input = vidioc_get_input,
+ .vidioc_s_input = vidioc_set_input,
+};
+
+/* V4L2 Video Device Structure */
+static struct video_device cg2900_video_device = {
+ .name = "STE CG2900 FM Rx/Tx Radio",
+ .vfl_type = VID_TYPE_TUNER | VID_TYPE_CAPTURE,
+ .fops = &cg2900_fops,
+ .ioctl_ops = &cg2900_ioctl_ops,
+ .release = video_device_release_empty,
+};
+
+static u16 no_of_scan_freq;
+static u16 no_of_block_scan_freq;
+static u32 scanfreq_rssi_level[MAX_CHANNELS_TO_SCAN];
+static u16 block_scan_rssi_level[MAX_CHANNELS_FOR_BLOCK_SCAN];
+static u32 scanfreq[MAX_CHANNELS_TO_SCAN];
+static struct mutex fm_mutex;
+static spinlock_t fm_spinlock;
+static int users;
+
+/**
+ * vidioc_querycap()- Query FM Driver Capabilities.
+ *
+ * This function is used to query the capabilities of the
+ * FM Driver. This function is called when the application issues the IOCTL
+ * VIDIOC_QUERYCAP.
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @query_caps: v4l2_capability structure.
+ *
+ * Returns: 0
+ */
+static int vidioc_querycap(
+ struct file *file,
+ void *priv,
+ struct v4l2_capability *query_caps
+ )
+{
+ FM_INFO_REPORT("vidioc_querycap");
+ memset(
+ query_caps,
+ 0,
+ sizeof(*query_caps)
+ );
+ strlcpy(
+ query_caps->driver,
+ "CG2900 Driver",
+ sizeof(query_caps->driver)
+ );
+ strlcpy(
+ query_caps->card,
+ "CG2900 FM Radio",
+ sizeof(query_caps->card)
+ );
+ strcpy(
+ query_caps->bus_info,
+ "platform"
+ );
+ query_caps->version = RADIO_CG2900_VERSION;
+ query_caps->capabilities =
+ V4L2_CAP_TUNER |
+ V4L2_CAP_MODULATOR |
+ V4L2_CAP_RADIO |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_RDS_CAPTURE |
+ V4L2_CAP_HW_FREQ_SEEK |
+ V4L2_CAP_RDS_OUTPUT;
+ FM_DEBUG_REPORT("vidioc_querycap returning 0");
+ return 0;
+}
+
+/**
+ * vidioc_get_tuner()- Get FM Tuner Features.
+ *
+ * This function is used to get the tuner features.
+ * This function is called when the application issues the IOCTL
+ * VIDIOC_G_TUNER
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @tuner: v4l2_tuner structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_get_tuner(
+ struct file *file,
+ void *priv,
+ struct v4l2_tuner *tuner
+ )
+{
+ int status = 0;
+ u8 mode;
+ bool rds_enabled;
+ u16 rssi;
+ int ret_val = -EINVAL;
+
+ FM_INFO_REPORT("vidioc_get_tuner");
+
+ if (tuner->index > 0) {
+ FM_ERR_REPORT("vidioc_get_tuner: Only 1 tuner supported");
+ goto error;
+ }
+
+ memset(tuner, 0, sizeof(*tuner));
+ strcpy(tuner->name, "CG2900 FM Receiver");
+ tuner->type = V4L2_TUNER_RADIO;
+ tuner->rangelow = HZ_TO_V4L2(freq_low);
+ tuner->rangehigh = HZ_TO_V4L2(freq_high);
+ tuner->capability =
+ V4L2_TUNER_CAP_LOW /* Frequency steps = 1/16 kHz */
+ | V4L2_TUNER_CAP_STEREO /* Can receive stereo */
+ | V4L2_TUNER_CAP_RDS; /* Supports RDS Capture */
+
+ if (cg2900_device.fm_mode == CG2900_FM_RX_MODE) {
+
+ status = cg2900_fm_get_mode(&mode);
+
+ FM_DEBUG_REPORT("vidioc_get_tuner: mode = %d, ", mode);
+
+ if (0 != status) {
+ /* Get mode API failed, set mode to mono */
+ tuner->audmode = V4L2_TUNER_MODE_MONO;
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
+ goto error;
+ }
+
+ switch (mode) {
+ case CG2900_MODE_STEREO:
+ tuner->audmode = V4L2_TUNER_MODE_STEREO;
+ tuner->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ break;
+ case CG2900_MODE_MONO:
+ default:
+ tuner->audmode = V4L2_TUNER_MODE_MONO;
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
+ break;
+ }
+
+ status = cg2900_fm_get_rds_status(&rds_enabled);
+
+ if (0 != status) {
+ tuner->rxsubchans &= ~V4L2_TUNER_SUB_RDS;
+ goto error;
+ }
+
+ if (rds_enabled)
+ tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
+ else
+ tuner->rxsubchans &= ~V4L2_TUNER_SUB_RDS;
+ } else {
+ tuner->audmode = V4L2_TUNER_MODE_MONO;
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
+ }
+
+ if (cg2900_device.fm_mode == CG2900_FM_RX_MODE) {
+ status = cg2900_fm_get_signal_strength(&rssi);
+
+ if (0 != status) {
+ tuner->signal = 0;
+ goto error;
+ }
+ tuner->signal = rssi;
+ } else {
+ tuner->signal = 0;
+ }
+
+ ret_val = 0;
+
+error:
+ FM_DEBUG_REPORT("vidioc_get_tuner: returning %d", ret_val);
+ return ret_val;
+}
+
+/**
+ * vidioc_set_tuner()- Set FM Tuner Features.
+ *
+ * This function is used to set the tuner features.
+ * It also sets the default FM Rx settings.
+ * This function is called when the application issues the IOCTL
+ * VIDIOC_S_TUNER
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @tuner: v4l2_tuner structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_set_tuner(
+ struct file *file,
+ void *priv,
+ struct v4l2_tuner *tuner
+ )
+{
+ bool rds_status = false;
+ bool stereo_status = false;
+ int status = 0;
+ int ret_val = -EINVAL;
+
+ FM_INFO_REPORT("vidioc_set_tuner");
+ if (tuner->index != 0) {
+ FM_ERR_REPORT("vidioc_set_tuner: Only 1 tuner supported");
+ goto error;
+ }
+
+ if (cg2900_device.fm_mode != CG2900_FM_RX_MODE) {
+ /*
+ * FM Rx mode should be configured
+ * as earlier mode was not FM Rx
+ */
+ if (CG2900_FM_BAND_US_EU == band) {
+ freq_low = FMR_EU_US_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ freq_high = FMR_EU_US_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ } else if (CG2900_FM_BAND_JAPAN == band) {
+ freq_low = FMR_JAPAN_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ freq_high = FMR_JAPAN_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ } else if (CG2900_FM_BAND_CHINA == band) {
+ freq_low = FMR_CHINA_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ freq_high = FMR_CHINA_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ }
+ cg2900_device.fm_mode = CG2900_FM_RX_MODE;
+ cg2900_device.rx_rds_enabled =
+ (tuner->rxsubchans & V4L2_TUNER_SUB_RDS) ?
+ true : false;
+ if (tuner->rxsubchans & V4L2_TUNER_SUB_STEREO)
+ stereo_status = true;
+ else if (tuner->rxsubchans & V4L2_TUNER_SUB_MONO)
+ stereo_status = false;
+ cg2900_device.rx_stereo_status = stereo_status;
+ status = cg2900_fm_set_rx_default_settings(freq_low,
+ band,
+ grid,
+ cg2900_device.rx_rds_enabled,
+ cg2900_device.rx_stereo_status);
+
+ if (0 != status) {
+ FM_ERR_REPORT("vidioc_set_tuner: "
+ "cg2900_fm_set_rx_default_settings returned "
+ " %d", status);
+ goto error;
+ }
+ status = cg2900_fm_set_rssi_threshold(
+ cg2900_device.rssi_threshold);
+ if (0 != status) {
+ FM_ERR_REPORT("vidioc_set_tuner: "
+ "cg2900_fm_set_rssi_threshold returned "
+ " %d", status);
+ goto error;
+ }
+ } else {
+ /*
+ * Mode was FM Rx only, change the RDS settings or stereo mode
+ * if they are changed by application
+ */
+ rds_status = (tuner->rxsubchans & V4L2_TUNER_SUB_RDS) ?
+ true : false;
+ if (tuner->rxsubchans & V4L2_TUNER_SUB_STEREO)
+ stereo_status = true;
+ else if (tuner->rxsubchans & V4L2_TUNER_SUB_MONO)
+ stereo_status = false;
+ if (stereo_status != cg2900_device.rx_stereo_status) {
+ cg2900_device.rx_stereo_status = stereo_status;
+ if (stereo_status)
+ status =
+ cg2900_fm_set_mode(
+ FMD_STEREOMODE_BLENDING);
+ else
+ status = cg2900_fm_set_mode(
+ FMD_STEREOMODE_MONO);
+
+ if (0 != status) {
+ FM_ERR_REPORT("vidioc_set_tuner: "
+ "cg2900_fm_set_mode returned "
+ " %d", status);
+ goto error;
+ }
+ }
+ if (rds_status != cg2900_device.rx_rds_enabled) {
+ cg2900_device.rx_rds_enabled = rds_status;
+ if (rds_status)
+ status = cg2900_fm_rds_on();
+ else
+ status = cg2900_fm_rds_off();
+
+ if (0 != status) {
+ FM_ERR_REPORT("vidioc_set_tuner: "
+ "cg2900_fm_rds returned "
+ " %d", status);
+ goto error;
+ }
+ }
+ }
+
+ ret_val = 0;
+
+error:
+ FM_DEBUG_REPORT("vidioc_set_tuner: returning %d", ret_val);
+ return ret_val;
+}
+
+/**
+ * vidioc_get_modulator()- Get FM Modulator Features.
+ *
+ * This function is used to get the modulator features.
+ * This function is called when the application issues the IOCTL
+ * VIDIOC_G_MODULATOR
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @modulator: v4l2_modulator structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_get_modulator(
+ struct file *file,
+ void *priv,
+ struct v4l2_modulator *modulator
+ )
+{
+ int status = 0;
+ bool rds_enabled;
+ u8 mode;
+ int ret_val = -EINVAL;
+
+ FM_INFO_REPORT("vidioc_get_modulator");
+
+ if (modulator->index > 0) {
+ FM_ERR_REPORT("vidioc_get_modulator: Only 1 "
+ "modulator supported");
+ goto error;
+ }
+
+ memset(modulator, 0, sizeof(*modulator));
+ strcpy(modulator->name, "CG2900 FM Transmitter");
+ modulator->rangelow = freq_low;
+ modulator->rangehigh = freq_high;
+ modulator->capability = V4L2_TUNER_CAP_NORM /* Freq steps = 1/16 kHz */
+ | V4L2_TUNER_CAP_STEREO /* Can receive stereo */
+ | V4L2_TUNER_CAP_RDS; /* Supports RDS Capture */
+
+ if (cg2900_device.fm_mode == CG2900_FM_TX_MODE) {
+ status = cg2900_fm_get_mode(&mode);
+ FM_DEBUG_REPORT("vidioc_get_modulator: mode = %d", mode);
+ if (0 != status) {
+ /* Get mode API failed, set mode to mono */
+ modulator->txsubchans = V4L2_TUNER_SUB_MONO;
+ goto error;
+ }
+ switch (mode) {
+ /* Stereo */
+ case CG2900_MODE_STEREO:
+ modulator->txsubchans = V4L2_TUNER_SUB_STEREO;
+ break;
+ /* Mono */
+ case CG2900_MODE_MONO:
+ modulator->txsubchans = V4L2_TUNER_SUB_MONO;
+ break;
+ /* Switching or Blending, set mode as Stereo */
+ default:
+ modulator->txsubchans = V4L2_TUNER_SUB_STEREO;
+ }
+ status = cg2900_fm_get_rds_status(&rds_enabled);
+ if (0 != status) {
+ modulator->txsubchans &= ~V4L2_TUNER_SUB_RDS;
+ goto error;
+ }
+ if (rds_enabled)
+ modulator->txsubchans |= V4L2_TUNER_SUB_RDS;
+ else
+ modulator->txsubchans &= ~V4L2_TUNER_SUB_RDS;
+ } else
+ modulator->txsubchans = V4L2_TUNER_SUB_MONO;
+
+ ret_val = 0;
+
+error:
+ FM_DEBUG_REPORT("vidioc_get_modulator: returning %d",
+ ret_val);
+ return ret_val;
+}
+
+/**
+ * vidioc_set_modulator()- Set FM Modulator Features.
+ *
+ * This function is used to set the Modulaotr features.
+ * It also sets the default FM Tx settings.
+ * This function is called when the application issues the IOCTL
+ * VIDIOC_S_MODULATOR
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @modulator: v4l2_modulator structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_set_modulator(
+ struct file *file,
+ void *priv,
+ struct v4l2_modulator *modulator
+ )
+{
+ bool rds_status = false;
+ bool stereo_status = false;
+ int status = 0;
+ int ret_val = -EINVAL;
+
+ FM_INFO_REPORT("vidioc_set_modulator");
+ if (modulator->index != 0) {
+ FM_ERR_REPORT("vidioc_set_modulator: Only 1 "
+ "modulator supported");
+ goto error;
+ }
+
+ if (cg2900_device.fm_mode != CG2900_FM_TX_MODE) {
+ /*
+ * FM Tx mode should be configured as
+ * earlier mode was not FM Tx
+ */
+ if (band == CG2900_FM_BAND_US_EU) {
+ freq_low = FMR_EU_US_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ freq_high = FMR_EU_US_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ } else if (band == CG2900_FM_BAND_JAPAN) {
+ freq_low = FMR_JAPAN_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ freq_high = FMR_JAPAN_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ } else if (band == CG2900_FM_BAND_CHINA) {
+ freq_low = FMR_CHINA_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ freq_high = FMR_CHINA_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ }
+ cg2900_device.fm_mode = CG2900_FM_TX_MODE;
+ cg2900_device.rx_rds_enabled = false;
+ cg2900_device.tx_rds_enabled =
+ (modulator->txsubchans & V4L2_TUNER_SUB_RDS) ?
+ true : false;
+ if (modulator->txsubchans & V4L2_TUNER_SUB_STEREO)
+ stereo_status = true;
+ else if (modulator->txsubchans & V4L2_TUNER_SUB_MONO)
+ stereo_status = false;
+ cg2900_device.tx_stereo_status = stereo_status;
+
+ status = cg2900_fm_set_tx_default_settings(freq_low,
+ band,
+ grid,
+ cg2900_device.tx_rds_enabled,
+ cg2900_device.
+ tx_stereo_status);
+
+ if (0 != status) {
+ FM_ERR_REPORT("vidioc_set_modulator: "
+ "cg2900_fm_set_tx_default_settings returned "
+ " %d", status);
+ goto error;
+ }
+ } else {
+ /*
+ * Mode was FM Tx only, change the RDS settings or stereo mode
+ * if they are changed by application
+ */
+ rds_status = (modulator->txsubchans & V4L2_TUNER_SUB_RDS) ?
+ true : false;
+ if (modulator->txsubchans & V4L2_TUNER_SUB_STEREO)
+ stereo_status = true;
+ else if (modulator->txsubchans & V4L2_TUNER_SUB_MONO)
+ stereo_status = false;
+ if (stereo_status != cg2900_device.tx_stereo_status) {
+ cg2900_device.tx_stereo_status = stereo_status;
+ status = cg2900_fm_set_mode(stereo_status);
+ if (0 != status) {
+ FM_ERR_REPORT("vidioc_set_modulator: "
+ "cg2900_fm_set_mode returned "
+ " %d", status);
+ goto error;
+ }
+ }
+ if (rds_status != cg2900_device.tx_rds_enabled) {
+ cg2900_device.tx_rds_enabled = rds_status;
+ status = cg2900_fm_tx_rds(rds_status);
+ if (0 != status) {
+ FM_ERR_REPORT("vidioc_set_modulator: "
+ "cg2900_fm_tx_rds returned "
+ " %d", status);
+ goto error;
+ }
+ }
+ }
+
+ ret_val = 0;
+
+error:
+ FM_DEBUG_REPORT("vidioc_set_modulator: returning %d",
+ ret_val);
+ return ret_val;
+}
+
+/**
+ * vidioc_get_frequency()- Get the Current FM Frequnecy.
+ *
+ * This function is used to get the currently tuned
+ * frequency on FM Radio. This function is called when the application
+ * issues the IOCTL VIDIOC_G_FREQUENCY
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @freq: v4l2_frequency structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_get_frequency(
+ struct file *file,
+ void *priv,
+ struct v4l2_frequency *freq
+ )
+{
+ int status;
+ u32 frequency;
+ int ret_val = -EINVAL;
+
+ FM_INFO_REPORT("vidioc_get_frequency: Status = %d",
+ cg2900_device.seekstatus);
+
+ status = cg2900_fm_get_frequency(&frequency);
+
+ if (0 != status) {
+ freq->frequency = cg2900_device.frequency;
+ goto error;
+ }
+
+ if (cg2900_device.seekstatus == FMR_SEEK_IN_PROGRESS) {
+ /* Check if seek is finished or not */
+ if (CG2900_EVENT_SEARCH_CHANNEL_FOUND == fm_event) {
+ /* seek is finished */
+ spin_lock(&fm_spinlock);
+ cg2900_device.frequency = HZ_TO_V4L2(frequency);
+ freq->frequency = cg2900_device.frequency;
+ cg2900_device.seekstatus = FMR_SEEK_NONE;
+ fm_event = CG2900_EVENT_NO_EVENT;
+ spin_unlock(&fm_spinlock);
+ }
+ } else {
+ spin_lock(&fm_spinlock);
+ cg2900_device.frequency = HZ_TO_V4L2(frequency);
+ freq->frequency = cg2900_device.frequency;
+ spin_unlock(&fm_spinlock);
+ }
+ ret_val = 0;
+
+error:
+ FM_DEBUG_REPORT("vidioc_get_frequency: returning = %d",
+ ret_val);
+ return ret_val;
+}
+
+/**
+ * vidioc_set_frequency()- Set the FM Frequnecy.
+ *
+ * This function is used to set the frequency
+ * on FM Radio. This function is called when the application
+ * issues the IOCTL VIDIOC_S_FREQUENCY
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @freq: v4l2_frequency structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_set_frequency(
+ struct file *file,
+ void *priv,
+ struct v4l2_frequency *freq
+ )
+{
+ u32 frequency = freq->frequency;
+ u32 freq_low, freq_high;
+ int status;
+ int ret_val = -EINVAL;
+
+ FM_INFO_REPORT("vidioc_set_frequency: Frequency = "
+ "%d ", V4L2_TO_HZ(frequency));
+
+ /* Check which band is set currently */
+ switch (band) {
+ case CG2900_FM_BAND_US_EU:
+ freq_low = FMR_EU_US_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ freq_high = FMR_EU_US_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ break;
+
+ case CG2900_FM_BAND_CHINA:
+ freq_low = FMR_CHINA_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ freq_high = FMR_CHINA_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ break;
+
+ case CG2900_FM_BAND_JAPAN:
+ freq_low = FMR_JAPAN_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ freq_high = FMR_JAPAN_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ break;
+
+ default:
+ /* Set to US_MAX and CHINA_MIN band */
+ freq_low = FMR_CHINA_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ freq_high = FMR_EU_US_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ }
+
+ /* Check if the frequency set is out of current band */
+ if ((V4L2_TO_HZ(frequency) < freq_low) ||
+ (V4L2_TO_HZ(frequency) > freq_high))
+ goto error;
+
+ spin_lock(&fm_spinlock);
+ fm_event = CG2900_EVENT_NO_EVENT;
+ no_of_scan_freq = 0;
+ spin_unlock(&fm_spinlock);
+
+ cg2900_device.seekstatus = FMR_SEEK_NONE;
+ cg2900_device.frequency = frequency;
+ status = cg2900_fm_set_frequency(V4L2_TO_HZ(frequency));
+
+ if (0 != status)
+ goto error;
+
+ ret_val = 0;
+
+error:
+ FM_DEBUG_REPORT("vidioc_set_frequency: returning = %d",
+ ret_val);
+ return ret_val;
+}
+
+/**
+ * vidioc_query_ctrl()- Query the FM Driver control features.
+ *
+ * This function is used to query the control features on FM Radio.
+ * This function is called when the application
+ * issues the IOCTL VIDIOC_QUERYCTRL
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @query_ctrl: v4l2_queryctrl structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_query_ctrl(
+ struct file *file,
+ void *priv,
+ struct v4l2_queryctrl *query_ctrl
+ )
+{
+ int ret_val = -EINVAL;
+
+ FM_INFO_REPORT("vidioc_query_ctrl");
+ /* Check which control is requested */
+ switch (query_ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ FM_DEBUG_REPORT("vidioc_query_ctrl: V4L2_CID_AUDIO_MUTE");
+ query_ctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
+ query_ctrl->minimum = 0;
+ query_ctrl->maximum = 1;
+ query_ctrl->step = 1;
+ query_ctrl->default_value = 0;
+ query_ctrl->flags = 0;
+ strncpy(query_ctrl->name, "CG2900 Mute", 32);
+ ret_val = 0;
+ break;
+
+ case V4L2_CID_AUDIO_VOLUME:
+ FM_DEBUG_REPORT("vidioc_query_ctrl: V4L2_CID_AUDIO_VOLUME");
+
+ strncpy(query_ctrl->name, "CG2900 Volume", 32);
+ query_ctrl->minimum = MIN_ANALOG_VOLUME;
+ query_ctrl->maximum = MAX_ANALOG_VOLUME;
+ query_ctrl->step = 1;
+ query_ctrl->default_value = MAX_ANALOG_VOLUME;
+ query_ctrl->flags = 0;
+ query_ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ ret_val = 0;
+ break;
+
+ case V4L2_CID_AUDIO_BALANCE:
+ FM_DEBUG_REPORT("vidioc_query_ctrl: V4L2_CID_AUDIO_BALANCE ");
+ strncpy(query_ctrl->name, "CG2900 Audio Balance", 32);
+ query_ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ query_ctrl->minimum = 0x0000;
+ query_ctrl->maximum = 0xFFFF;
+ query_ctrl->step = 0x0001;
+ query_ctrl->default_value = 0x0000;
+ query_ctrl->flags = 0;
+ ret_val = 0;
+ break;
+
+ case V4L2_CID_AUDIO_BASS:
+ FM_DEBUG_REPORT("vidioc_query_ctrl: "
+ "V4L2_CID_AUDIO_BASS (unsupported)");
+ break;
+
+ case V4L2_CID_AUDIO_TREBLE:
+ FM_DEBUG_REPORT("vidioc_query_ctrl: "
+ "V4L2_CID_AUDIO_TREBLE (unsupported)");
+ break;
+
+ default:
+ FM_DEBUG_REPORT("vidioc_query_ctrl: "
+ "--> unsupported id = %d", (int)query_ctrl->id);
+ break;
+ }
+
+ FM_DEBUG_REPORT("vidioc_query_ctrl: returning = %d",
+ ret_val);
+ return ret_val;
+}
+
+/**
+ * vidioc_get_ctrl()- Get the value of a particular Control.
+ *
+ * This function is used to get the value of a
+ * particular control from the FM Driver. This function is called
+ * when the application issues the IOCTL VIDIOC_G_CTRL
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @ctrl: v4l2_control structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_get_ctrl(
+ struct file *file,
+ void *priv,
+ struct v4l2_control *ctrl
+ )
+{
+ int status;
+ u8 value;
+ u16 rssi;
+ u8 antenna;
+ u16 conclusion;
+ int ret_val = -EINVAL;
+
+ FM_INFO_REPORT("vidioc_get_ctrl");
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ status = cg2900_fm_get_volume(&value);
+ if (0 == status) {
+ ctrl->value = value;
+ cg2900_device.volume = value;
+ ret_val = 0;
+ }
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = cg2900_device.muted;
+ ret_val = 0;
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ ctrl->value = cg2900_device.audiopath;
+ ret_val = 0;
+ break;
+ case V4L2_CID_CG2900_RADIO_RSSI_THRESHOLD:
+ ctrl->value = cg2900_device.rssi_threshold;
+ ret_val = 0;
+ break;
+ case V4L2_CID_CG2900_RADIO_SELECT_ANTENNA:
+ status = cg2900_fm_get_antenna(&antenna);
+ FM_DEBUG_REPORT("vidioc_get_ctrl: Antenna = %d", antenna);
+ if (0 == status) {
+ ctrl->value = antenna;
+ ret_val = 0;
+ }
+ break;
+ case V4L2_CID_CG2900_RADIO_RDS_AF_UPDATE_GET_RESULT:
+ status = cg2900_fm_af_update_get_result(&rssi);
+ FM_DEBUG_REPORT("vidioc_get_ctrl: AF RSSI Level = %d", rssi);
+ if (0 == status) {
+ ctrl->value = rssi;
+ ret_val = 0;
+ }
+ break;
+ case V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_GET_RESULT:
+ status = cg2900_fm_af_switch_get_result(&conclusion);
+ FM_DEBUG_REPORT("vidioc_get_ctrl: AF Switch conclusion = %d",
+ conclusion);
+ if (0 != status)
+ break;
+ if (conclusion == 0) {
+ ctrl->value = conclusion;
+ FM_DEBUG_REPORT("vidioc_get_ctrl: "
+ "AF Switch conclusion = %d",
+ ctrl->value);
+ ret_val = 0;
+ } else {
+ /*
+ * Convert positive error code returned by chip
+ * into negative error codes to be in line with linux.
+ */
+ ctrl->value = -conclusion;
+ FM_ERR_REPORT("vidioc_get_ctrl: "
+ "AF-Switch failed with value %d", (__s32)ctrl->value);
+ ret_val = 0;
+ }
+ break;
+ default:
+ FM_DEBUG_REPORT("vidioc_get_ctrl: "
+ "unsupported (id = %d)", (int)ctrl->id);
+ ret_val = -EINVAL;
+ }
+ FM_DEBUG_REPORT("vidioc_get_ctrl: returning = %d",
+ ret_val);
+ return ret_val;
+}
+
+/**
+ * vidioc_set_ctrl()- Set the value of a particular Control.
+ *
+ * This function is used to set the value of a
+ * particular control from the FM Driver. This function is called when the
+ * application issues the IOCTL VIDIOC_S_CTRL
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @ctrl: v4l2_control structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -ERANGE when the parameter is out of range.
+ * -EINVAL: otherwise
+ */
+static int vidioc_set_ctrl(
+ struct file *file,
+ void *priv,
+ struct v4l2_control *ctrl
+ )
+{
+ int status;
+ int ret_val = -EINVAL;
+ FM_INFO_REPORT("vidioc_set_ctrl");
+ /* Check which control is requested */
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "V4L2_CID_AUDIO_MUTE, "
+ "value = %d", ctrl->value);
+ if (ctrl->value > 1 && ctrl->value < 0) {
+ ret_val = -ERANGE;
+ break;
+ }
+
+ if (ctrl->value) {
+ FM_DEBUG_REPORT("vidioc_set_ctrl: Ctrl_Id = "
+ "V4L2_CID_AUDIO_MUTE, "
+ "Muting the Radio");
+ status = cg2900_fm_mute();
+ } else {
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "Ctrl_Id = V4L2_CID_AUDIO_MUTE, "
+ "UnMuting the Radio");
+ status = cg2900_fm_unmute();
+ }
+ if (0 == status) {
+ cg2900_device.muted = ctrl->value;
+ ret_val = 0;
+ }
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "V4L2_CID_AUDIO_VOLUME, "
+ "value = %d", ctrl->value);
+ if (ctrl->value > MAX_ANALOG_VOLUME &&
+ ctrl->value < MIN_ANALOG_VOLUME) {
+ ret_val = -ERANGE;
+ break;
+ }
+ status = cg2900_fm_set_volume(ctrl->value);
+ if (0 == status) {
+ cg2900_device.volume = ctrl->value;
+ ret_val = 0;
+ }
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "V4L2_CID_AUDIO_BALANCE, "
+ "value = %d", ctrl->value);
+ status = cg2900_fm_set_audio_balance(ctrl->value);
+ if (0 == status) {
+ cg2900_device.audiopath = ctrl->value;
+ ret_val = 0;
+ }
+ break;
+ case V4L2_CID_CG2900_RADIO_CHIP_STATE:
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "V4L2_CID_CG2900_RADIO_CHIP_STATE, "
+ "value = %d", ctrl->value);
+ if (V4L2_CG2900_RADIO_STANDBY == ctrl->value)
+ status = cg2900_fm_standby();
+ else if (V4L2_CG2900_RADIO_POWERUP == ctrl->value)
+ status = cg2900_fm_power_up_from_standby();
+ else
+ break;
+ if (0 != status)
+ break;
+ if (V4L2_CG2900_RADIO_STANDBY == ctrl->value)
+ cg2900_device.state = FMR_STANDBY;
+ else if (V4L2_CG2900_RADIO_POWERUP == ctrl->value)
+ cg2900_device.state = FMR_SWITCH_ON;
+ ret_val = 0;
+ break;
+ case V4L2_CID_CG2900_RADIO_SELECT_ANTENNA:
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "V4L2_CID_CG2900_RADIO_SELECT_ANTENNA, "
+ "value = %d", ctrl->value);
+ status = cg2900_fm_select_antenna(ctrl->value);
+ if (0 == status)
+ ret_val = 0;
+ break;
+ case V4L2_CID_CG2900_RADIO_BANDSCAN:
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "V4L2_CID_CG2900_RADIO_BANDSCAN, "
+ "value = %d", ctrl->value);
+ if (V4L2_CG2900_RADIO_BANDSCAN_START == ctrl->value) {
+ cg2900_device.seekstatus = FMR_SEEK_IN_PROGRESS;
+ no_of_scan_freq = 0;
+ status = cg2900_fm_start_band_scan();
+ } else if (V4L2_CG2900_RADIO_BANDSCAN_STOP == ctrl->value) {
+ status = cg2900_fm_stop_scan();
+ cg2900_device.seekstatus = FMR_SEEK_NONE;
+ } else
+ break;
+ if (0 == status)
+ ret_val = 0;
+ break;
+ case V4L2_CID_CG2900_RADIO_RSSI_THRESHOLD:
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "V4L2_CID_CG2900_RADIO_RSSI_THRESHOLD "
+ "= %d", ctrl->value);
+ status = cg2900_fm_set_rssi_threshold(ctrl->value);
+ if (0 == status) {
+ cg2900_device.rssi_threshold = ctrl->value;
+ ret_val = 0;
+ }
+ break;
+ case V4L2_CID_CG2900_RADIO_RDS_AF_UPDATE_START:
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "V4L2_CID_CG2900_RADIO_RDS_AF_UPDATE_START "
+ "freq = %d Hz", ctrl->value);
+ status = cg2900_fm_af_update_start(ctrl->value);
+ if (0 == status)
+ ret_val = 0;
+ break;
+ case V4L2_CID_CG2900_RADIO_TEST_TONE_GENERATOR_SET_STATUS:
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "V4L2_CID_CG2900_RADIO_TEST_TONE_GENERATOR_SET_STATUS "
+ "state = %d ", ctrl->value);
+ if (ctrl->value < V4L2_CG2900_RADIO_TEST_TONE_GEN_OFF ||
+ ctrl->value > V4L2_CG2900_RADIO_TEST_TONE_GENERATOR_ON_WO_SRC) {
+ FM_ERR_REPORT("Invalid parameter = %d", ctrl->value);
+ break;
+ }
+ status = cg2900_fm_set_test_tone_generator(ctrl->value);
+ if (0 == status)
+ ret_val = 0;
+ break;
+ case V4L2_CID_CG2900_RADIO_TUNE_DEEMPHASIS:
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "V4L2_CID_CG2900_RADIO_TUNE_DEEMPHASIS, "
+ "Value = %d",
+ ctrl->value);
+
+ if ((V4L2_CG2900_RADIO_DEEMPHASIS_DISABLED >
+ ctrl->value) ||
+ (V4L2_CG2900_RADIO_DEEMPHASIS_75_uS <
+ ctrl->value)) {
+ FM_ERR_REPORT("Unsupported deemphasis = %d",
+ ctrl->value);
+ break;
+ }
+
+ switch (ctrl->value) {
+ case V4L2_CG2900_RADIO_DEEMPHASIS_50_uS:
+ ctrl->value = FMD_EMPHASIS_50US;
+ break;
+ case V4L2_CG2900_RADIO_DEEMPHASIS_75_uS:
+ ctrl->value = FMD_EMPHASIS_75US;
+ break;
+ case V4L2_CG2900_RADIO_DEEMPHASIS_DISABLED:
+ /* Drop Down */
+ default:
+ ctrl->value = FMD_EMPHASIS_NONE;
+ break;
+ }
+ status = cg2900_fm_rx_set_deemphasis(ctrl->value);
+
+ if (0 == status)
+ ret_val = 0;
+ break;
+ default:
+ FM_DEBUG_REPORT("vidioc_set_ctrl: "
+ "unsupported (id = %d)", (int)ctrl->id);
+ }
+ FM_DEBUG_REPORT("vidioc_set_ctrl: returning = %d",
+ ret_val);
+ return ret_val;
+}
+
+/**
+ * vidioc_get_ext_ctrls()- Get the values of a particular control.
+ *
+ * This function is used to get the value of a
+ * particular control from the FM Driver. This is used when the data to
+ * be received is more than 1 paramter. This function is called when the
+ * application issues the IOCTL VIDIOC_G_EXT_CTRLS
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @ext_ctrl: v4l2_ext_controls structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -ENOSPC: when there is no space to copy the data into the buffer provided
+ * by application.
+ * -EINVAL: otherwise
+ */
+static int vidioc_get_ext_ctrls(
+ struct file *file,
+ void *priv,
+ struct v4l2_ext_controls *ext_ctrl
+ )
+{
+ u32 *dest_buffer;
+ int index = 0;
+ int count = 0;
+ int ret_val = -EINVAL;
+ int status;
+
+ FM_INFO_REPORT("vidioc_get_ext_ctrls: Id = %04x,"
+ "ext_ctrl->ctrl_class = %04x",
+ ext_ctrl->controls->id,
+ ext_ctrl->ctrl_class);
+
+ if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_FM_TX &&
+ ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
+ FM_ERR_REPORT("vidioc_get_ext_ctrls: Unsupported "
+ "ctrl_class = %04x", ext_ctrl->ctrl_class);
+ goto error;
+ }
+
+ switch (ext_ctrl->controls->id) {
+ case V4L2_CID_CG2900_RADIO_BANDSCAN_GET_RESULTS:
+ {
+ if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
+ FM_ERR_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_BANDSCAN_GET_RESULTS "
+ "Unsupported ctrl_class = %04x",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+ if (cg2900_device.seekstatus ==
+ FMR_SEEK_IN_PROGRESS) {
+ if (fm_event ==
+ CG2900_EVENT_SCAN_CHANNELS_FOUND) {
+ /* Check to get Scan Result */
+ status =
+ cg2900_fm_get_scan_result
+ (&no_of_scan_freq, scanfreq,
+ scanfreq_rssi_level);
+ if (0 != status) {
+ FM_ERR_REPORT
+ ("vidioc_get_ext_ctrls: "
+ "cg2900_fm_get_scan_"
+ "result: returned %d",
+ status);
+ break;
+ }
+ }
+ }
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "SeekStatus = %d, GlobalEvent = %d, "
+ "numchannels = %d",
+ cg2900_device.seekstatus,
+ fm_event, no_of_scan_freq);
+
+ if (ext_ctrl->controls->size == 0 &&
+ ext_ctrl->controls->string == NULL) {
+ if (cg2900_device.seekstatus ==
+ FMR_SEEK_IN_PROGRESS &&
+ CG2900_EVENT_SCAN_CHANNELS_FOUND
+ == fm_event) {
+ spin_lock(&fm_spinlock);
+ ext_ctrl->controls->size =
+ no_of_scan_freq;
+ cg2900_device.seekstatus
+ = FMR_SEEK_NONE;
+ fm_event =
+ CG2900_EVENT_NO_EVENT;
+ spin_unlock(&fm_spinlock);
+ return -ENOSPC;
+ }
+ } else if (ext_ctrl->controls->string != NULL) {
+ dest_buffer =
+ (u32 *) ext_ctrl->controls->string;
+ while (index < no_of_scan_freq) {
+ *(dest_buffer + count + 0) =
+ HZ_TO_V4L2(scanfreq[index]);
+ *(dest_buffer + count + 1) =
+ scanfreq_rssi_level[index];
+ count += 2;
+ index++;
+ }
+ ret_val = 0;
+ }
+ break;
+ }
+ case V4L2_CID_CG2900_RADIO_BLOCKSCAN_GET_RESULTS:
+ {
+ if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
+ FM_ERR_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_BLOCKSCAN_GET_RESULTS "
+ "Unsupported ctrl_class = %04x",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+ if (cg2900_device.seekstatus ==
+ FMR_SEEK_IN_PROGRESS) {
+ if (fm_event ==
+ CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND) {
+ /* Check to get BlockScan Result */
+ status =
+ cg2900_fm_get_block_scan_result
+ (&no_of_block_scan_freq,
+ block_scan_rssi_level);
+ if (0 != status) {
+ FM_ERR_REPORT
+ ("vidioc_get_ext_ctrls: "
+ "cg2900_fm_get_block_scan_"
+ "result: " "returned %d",
+ status);
+ return ret_val;
+ }
+ }
+ }
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "SeekStatus = %d, GlobalEvent = %d, "
+ "numchannels = %d",
+ cg2900_device.seekstatus,
+ fm_event, no_of_block_scan_freq);
+ if (ext_ctrl->controls->size == 0 &&
+ ext_ctrl->controls->string == NULL) {
+ if (cg2900_device.seekstatus ==
+ FMR_SEEK_IN_PROGRESS &&
+ CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND
+ == fm_event) {
+ spin_lock(&fm_spinlock);
+ ext_ctrl->controls->size =
+ no_of_block_scan_freq;
+ cg2900_device.seekstatus
+ = FMR_SEEK_NONE;
+ fm_event =
+ CG2900_EVENT_NO_EVENT;
+ spin_unlock(&fm_spinlock);
+ return -ENOSPC;
+ }
+ } else if (ext_ctrl->controls->size >=
+ no_of_block_scan_freq &&
+ ext_ctrl->controls->string != NULL) {
+ dest_buffer =
+ (u32 *) ext_ctrl->controls->string;
+ while (index < no_of_block_scan_freq) {
+ *(dest_buffer + index) =
+ block_scan_rssi_level
+ [index];
+ index++;
+ }
+ ret_val = 0;
+ return ret_val;
+ }
+ break;
+ }
+ case V4L2_CID_RDS_TX_DEVIATION:
+ {
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_RDS_TX_DEVIATION");
+ if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
+ FM_ERR_REPORT("Invalid Ctrl Class = %d",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+ status = cg2900_fm_tx_get_rds_deviation((u16 *) &
+ ext_ctrl->
+ controls->value);
+ if (status == 0)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_PILOT_TONE_ENABLED:
+ {
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_PILOT_TONE_ENABLED");
+ if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
+ FM_ERR_REPORT("Invalid Ctrl Class = %d",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+ status = cg2900_fm_tx_get_pilot_tone_status(
+ (bool *)&ext_ctrl->controls->value);
+ if (status == 0)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_PILOT_TONE_DEVIATION:
+ {
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_PILOT_TONE_DEVIATION");
+ if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
+ FM_ERR_REPORT("Invalid Ctrl Class = %d",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+ status = cg2900_fm_tx_get_pilot_deviation(
+ (u16 *)&ext_ctrl->controls->value);
+ if (status == 0)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ {
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_TUNE_PREEMPHASIS");
+ if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
+ FM_ERR_REPORT("Invalid Ctrl Class = %d",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+ status = cg2900_fm_tx_get_preemphasis(
+ (u8 *)&ext_ctrl->controls->value);
+ if (status == 0)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_TUNE_POWER_LEVEL:
+ {
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_TUNE_POWER_LEVEL");
+ if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
+ FM_ERR_REPORT("Invalid Ctrl Class = %d",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+ status = cg2900_fm_tx_get_power_level(
+ (u16 *)&ext_ctrl->controls->value);
+ if (status == 0)
+ ret_val = 0;
+ break;
+ }
+ default:
+ {
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "unsupported (id = %d)",
+ (int)ext_ctrl->controls->id);
+ }
+ }
+
+error:
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: returning = %d", ret_val);
+ return ret_val;
+}
+
+/**
+ * vidioc_set_ext_ctrls()- Set the values of a particular control.
+ *
+ * This function is used to set the value of a
+ * particular control on the FM Driver. This is used when the data to
+ * be set is more than 1 paramter. This function is called when the
+ * application issues the IOCTL VIDIOC_S_EXT_CTRLS
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @ext_ctrl: v4l2_ext_controls structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -ENOSPC: when there is no space to copy the data into the buffer provided
+ * by application.
+ * -EINVAL: otherwise
+ */
+static int vidioc_set_ext_ctrls(
+ struct file *file,
+ void *priv,
+ struct v4l2_ext_controls *ext_ctrl
+ )
+{
+ int ret_val = -EINVAL;
+ int status;
+
+ FM_INFO_REPORT("vidioc_set_ext_ctrls: Id = %04x, ctrl_class = %04x",
+ ext_ctrl->controls->id, ext_ctrl->ctrl_class);
+
+ if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_FM_TX &&
+ ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
+ FM_ERR_REPORT("vidioc_set_ext_ctrls: Unsupported "
+ "ctrl_class = %04x", ext_ctrl->ctrl_class);
+ goto error;
+ }
+
+ switch (ext_ctrl->controls->id) {
+ case V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_START:
+ {
+ u32 af_switch_freq;
+ u16 af_switch_pi;
+ u32 *af_switch_buf;
+
+ if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
+ FM_ERR_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_START "
+ "Unsupported ctrl_class = %04x",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+
+ if (ext_ctrl->controls->size !=
+ FMR_AF_SWITCH_DATA_SIZE ||
+ ext_ctrl->controls->string == NULL) {
+ FM_ERR_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_START "
+ "Unsupported ctrl_class = %04x",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+
+ af_switch_buf = (u32 *) ext_ctrl->controls->string;
+ af_switch_freq = V4L2_TO_HZ(*af_switch_buf);
+ af_switch_pi = *(af_switch_buf + 1);
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_START: "
+ "AF Switch Freq =%d Hz AF Switch PI = %04x",
+ af_switch_freq,
+ af_switch_pi);
+
+ if (af_switch_freq < (FMR_CHINA_LOW_FREQ_IN_MHZ
+ * FMR_HZ_TO_MHZ_CONVERTER) ||
+ af_switch_freq > (FMR_CHINA_HIGH_FREQ_IN_MHZ
+ * FMR_HZ_TO_MHZ_CONVERTER)) {
+ FM_ERR_REPORT("Invalid Freq = %04x",
+ af_switch_freq);
+ break;
+ }
+
+ status = cg2900_fm_af_switch_start(
+ af_switch_freq,
+ af_switch_pi);
+
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_RDS_TX_DEVIATION:
+ {
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_RDS_TX_DEVIATION, "
+ "Value = %d",
+ ext_ctrl->controls->value);
+
+ if (ext_ctrl->controls->value <= MIN_RDS_DEVIATION &&
+ ext_ctrl->controls->value > MAX_RDS_DEVIATION) {
+ FM_ERR_REPORT("Invalid RDS Deviation = %02x",
+ ext_ctrl->controls->value);
+ break;
+ }
+
+ status = cg2900_fm_tx_set_rds_deviation(
+ ext_ctrl->controls->value);
+
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_RDS_TX_PI:
+ {
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_RDS_TX_PI, PI = %04x",
+ ext_ctrl->controls->value);
+
+ if (ext_ctrl->controls->value <= MIN_PI_VALUE &&
+ ext_ctrl->controls->value > MAX_PI_VALUE) {
+ FM_ERR_REPORT("Invalid PI = %04x",
+ ext_ctrl->controls->value);
+ break;
+ }
+
+ status = cg2900_fm_tx_set_pi_code(
+ ext_ctrl->controls->value);
+
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_RDS_TX_PTY:
+ {
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_RDS_TX_PTY, PTY = %d",
+ ext_ctrl->controls->value);
+
+ if (ext_ctrl->controls->value < MIN_PTY_VALUE &&
+ ext_ctrl->controls->value > MAX_PTY_VALUE) {
+ FM_ERR_REPORT("Invalid PTY = %02x",
+ ext_ctrl->controls->value);
+ break;
+ }
+
+ status = cg2900_fm_tx_set_pty_code(
+ ext_ctrl->controls->value);
+
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_RDS_TX_PS_NAME:
+ {
+ if (ext_ctrl->controls->size > MAX_PSN_SIZE
+ || ext_ctrl->controls->string == NULL) {
+ FM_ERR_REPORT("Invalid PSN");
+ break;
+ }
+
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_RDS_TX_PS_NAME, "
+ "PSN = %s, Len = %d",
+ ext_ctrl->controls->string,
+ ext_ctrl->controls->size);
+
+ status = cg2900_fm_tx_set_program_station_name(
+ ext_ctrl->controls->string,
+ ext_ctrl->controls->size);
+
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_RDS_TX_RADIO_TEXT:
+ {
+ if (ext_ctrl->controls->size >= MAX_RT_SIZE
+ || ext_ctrl->controls->string == NULL) {
+ FM_ERR_REPORT("Invalid RT");
+ break;
+ }
+
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_RDS_TX_RADIO_TEXT, "
+ "RT = %s, Len = %d",
+ ext_ctrl->controls->string,
+ ext_ctrl->controls->size);
+
+ status = cg2900_fm_tx_set_radio_text(
+ ext_ctrl->controls->string,
+ ext_ctrl->controls->size);
+
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_PILOT_TONE_ENABLED:
+ {
+ bool enable;
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_PILOT_TONE_ENABLED, "
+ "Value = %d",
+ ext_ctrl->controls->value);
+
+ if (FMD_PILOT_TONE_ENABLED ==
+ ext_ctrl->controls->value)
+ enable = true;
+ else if (FMD_PILOT_TONE_DISABLED ==
+ ext_ctrl->controls->value)
+ enable = false;
+ else {
+ FM_ERR_REPORT("Unsupported Value = %d",
+ ext_ctrl->controls->value);
+ break;
+ }
+ status = cg2900_fm_tx_set_pilot_tone_status(enable);
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_PILOT_TONE_DEVIATION:
+ {
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_PILOT_TONE_DEVIATION, "
+ "Value = %d",
+ ext_ctrl->controls->value);
+
+ if (ext_ctrl->controls->value <= MIN_PILOT_DEVIATION &&
+ ext_ctrl->controls->value > MAX_PILOT_DEVIATION) {
+ FM_ERR_REPORT("Invalid Pilot Deviation = %02x",
+ ext_ctrl->controls->value);
+ break;
+ }
+
+ status = cg2900_fm_tx_set_pilot_deviation(
+ ext_ctrl->controls->value);
+
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ {
+ u8 preemphasis;
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_TUNE_PREEMPHASIS, "
+ "Value = %d",
+ ext_ctrl->controls->value);
+
+ if ((V4L2_PREEMPHASIS_50_uS >
+ ext_ctrl->controls->value) ||
+ (V4L2_PREEMPHASIS_75_uS <
+ ext_ctrl->controls->value)) {
+ FM_ERR_REPORT("Unsupported Preemphasis = %d",
+ ext_ctrl->controls->value);
+ break;
+ }
+
+ if (V4L2_PREEMPHASIS_50_uS ==
+ ext_ctrl->controls->value) {
+ preemphasis = FMD_EMPHASIS_50US;
+ } else if (V4L2_PREEMPHASIS_75_uS ==
+ ext_ctrl->controls->value) {
+ preemphasis = FMD_EMPHASIS_75US;
+ }
+
+ status = cg2900_fm_tx_set_preemphasis(preemphasis);
+
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_TUNE_POWER_LEVEL:
+ {
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_TUNE_POWER_LEVEL, "
+ "Value = %d",
+ ext_ctrl->controls->value);
+ if (ext_ctrl->controls->value < MIN_POWER_LEVEL &&
+ ext_ctrl->controls->value > MAX_POWER_LEVEL) {
+ FM_ERR_REPORT("Invalid Power Level = %02x",
+ ext_ctrl->controls->value);
+ break;
+ }
+
+ status = cg2900_fm_tx_set_power_level(
+ ext_ctrl->controls->value);
+
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_CG2900_RADIO_BLOCKSCAN_START:
+ {
+ u32 start_freq;
+ u32 end_freq;
+ u32 *block_scan_buf;
+ u32 current_grid;
+ u32 low_freq;
+ u32 high_freq;
+ u32 result_freq;
+ u8 no_of_block_scan_channels;
+
+ /* V4L2 Initial check */
+ if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
+ FM_ERR_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_BLOCKSCAN_START "
+ "Unsupported ctrl_class = %04x",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+
+ if (ext_ctrl->controls->size !=
+ FMR_BLOCK_SCAN_DATA_SIZE ||
+ ext_ctrl->controls->string == NULL) {
+ FM_ERR_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_BLOCKSCAN_START "
+ "Invalid Parameters");
+ break;
+ }
+
+ /* Check for current grid */
+ if (grid == CG2900_FM_GRID_50)
+ current_grid = FMR_CHINA_GRID_IN_HZ;
+ else if (grid == CG2900_FM_GRID_100)
+ current_grid = FMR_EUROPE_GRID_IN_HZ;
+ else
+ current_grid = FMR_USA_GRID_IN_HZ;
+
+ /* Check for current band */
+ if (band == CG2900_FM_BAND_US_EU) {
+ low_freq = FMR_EU_US_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ high_freq = FMR_EU_US_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+
+ } else if (band == CG2900_FM_BAND_JAPAN) {
+ low_freq = FMR_JAPAN_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ high_freq = FMR_JAPAN_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+
+ } else {
+ low_freq = FMR_CHINA_LOW_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ high_freq = FMR_CHINA_HIGH_FREQ_IN_MHZ *
+ FMR_HZ_TO_MHZ_CONVERTER;
+ }
+
+ /* V4L2 Extended control */
+
+ block_scan_buf = (u32 *)ext_ctrl->controls->string;
+ start_freq = V4L2_TO_HZ(*block_scan_buf);
+ end_freq = V4L2_TO_HZ(*(block_scan_buf + 1));
+
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_"
+ "BLOCKSCAN_START: "
+ "Start Freq = %d Hz "
+ "End Freq = %d Hz",
+ start_freq,
+ end_freq);
+
+ result_freq = end_freq - start_freq;
+ no_of_block_scan_channels =
+ (u8)(result_freq / current_grid);
+
+ /* Frequency Check */
+ if (end_freq < start_freq) {
+ FM_ERR_REPORT("Start Freq (%d Hz) "
+ " > End Freq (%d Hz)",
+ start_freq,
+ end_freq);
+ break;
+ }
+
+ if ((start_freq < low_freq) ||
+ (start_freq > high_freq)) {
+ FM_ERR_REPORT("Out of Band Freq: "
+ "Start Freq = %d Hz",
+ start_freq);
+ break;
+ }
+
+ if ((end_freq < low_freq) ||
+ (end_freq > high_freq)) {
+ FM_ERR_REPORT("Out of Band Freq: "
+ "End Freq = %d Hz",
+ end_freq);
+ break;
+ }
+
+ /* Maximum allowed block scan range */
+ if (FMR_MAX_BLOCK_SCAN_CHANNELS <
+ no_of_block_scan_channels) {
+ FM_ERR_REPORT("No of channels (%d)"
+ "exceeds Max Block Scan (%d)",
+ no_of_block_scan_channels,
+ FMR_MAX_BLOCK_SCAN_CHANNELS);
+ break;
+ }
+
+ status = cg2900_fm_start_block_scan(
+ start_freq,
+ end_freq);
+ if (0 == status) {
+ cg2900_device.seekstatus =
+ FMR_SEEK_IN_PROGRESS;
+ ret_val = 0;
+ }
+ break;
+ }
+ case V4L2_CID_CG2900_RADIO_TEST_TONE_CONNECT:
+ {
+ u8 left_audio_mode;
+ u8 right_audio_mode;
+ u8 *test_tone_connect_buf;
+
+ /* V4L2 Initial check */
+ if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
+ FM_ERR_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_"
+ "TEST_TONE_CONNECT "
+ "Unsupported ctrl_class = %04x",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+
+ if (ext_ctrl->controls->size !=
+ FMR_TEST_TONE_CONNECT_DATA_SIZE ||
+ ext_ctrl->controls->string == NULL) {
+ FM_ERR_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_TEST"
+ "_TONE_CONNECT "
+ "Invalid Parameters");
+ break;
+ }
+
+ /* V4L2 Extended control */
+ test_tone_connect_buf =
+ (u8 *)ext_ctrl->controls->string;
+ left_audio_mode = *test_tone_connect_buf;
+ right_audio_mode = *(test_tone_connect_buf + 1);
+
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_TEST_TONE_CONNECT"
+ "left_audio_mode Freq = %02x"
+ "right_audio_modeFreq = %02x",
+ left_audio_mode,
+ right_audio_mode);
+
+ /* Range Check */
+ if (left_audio_mode > \
+ V4L2_CG2900_RADIO_TEST_TONE_TONE_SUM) {
+ FM_ERR_REPORT("Invalid Value of "
+ "left_audio_mode (%02x) ",
+ left_audio_mode);
+ break;
+ }
+
+ if (right_audio_mode > \
+ V4L2_CG2900_RADIO_TEST_TONE_TONE_SUM) {
+ FM_ERR_REPORT("Invalid Value of "
+ "right_audio_mode (%02x) ",
+ left_audio_mode);
+ break;
+ }
+
+ status = cg2900_fm_test_tone_connect(
+ left_audio_mode,
+ right_audio_mode);
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ case V4L2_CID_CG2900_RADIO_TEST_TONE_SET_PARAMS:
+ {
+ u8 tone_gen;
+ u16 frequency;
+ u16 volume;
+ u16 phase_offset;
+ u16 dc;
+ u8 waveform;
+ u16 *test_tone_set_params_buf;
+
+ /* V4L2 Initial check */
+ if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
+ FM_ERR_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_TEST_TONE_SET_PARAMS "
+ "Unsupported ctrl_class = %04x",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+
+ if (ext_ctrl->controls->size !=
+ FMR_TEST_TONE_SET_PARAMS_DATA_SIZE ||
+ ext_ctrl->controls->string == NULL) {
+ FM_ERR_REPORT("vidioc_set_ext_ctrls: "
+ "FMR_TEST_TONE_SET_PARAMS_DATA_SIZE "
+ "Invalid Parameters");
+ break;
+ }
+
+ /* V4L2 Extended control */
+ test_tone_set_params_buf = \
+ (u16 *)ext_ctrl->controls->string;
+
+ tone_gen = (u8)(*test_tone_set_params_buf);
+ frequency = *(test_tone_set_params_buf + 1);
+ volume = *(test_tone_set_params_buf + 2);
+ phase_offset = *(test_tone_set_params_buf + 3);
+ dc = *(test_tone_set_params_buf + 4);
+ waveform = (u8)(*(test_tone_set_params_buf + 5));
+
+ FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_TEST_TONE_SET_PARAMS"
+ "tone_gen = %02x frequency = %04x"
+ "volume = %04x phase_offset = %04x"
+ "dc = %04x waveform = %02x",
+ tone_gen, frequency,
+ volume, phase_offset,
+ dc, waveform);
+
+ /* Range Check */
+ if (tone_gen > FMD_TST_TONE_2) {
+ FM_ERR_REPORT("Invalid Value of "
+ "tone_gen (%02x) ",
+ tone_gen);
+ break;
+ }
+
+ if (waveform > FMD_TST_TONE_PULSE) {
+ FM_ERR_REPORT("Invalid Value of "
+ "waveform (%02x) ",
+ waveform);
+ break;
+ }
+
+ if (frequency > 0x7FFF) {
+ FM_ERR_REPORT("Invalid Value of "
+ "frequency (%04x) ",
+ frequency);
+ break;
+ }
+
+ if (volume > 0x7FFF) {
+ FM_ERR_REPORT("Invalid Value of "
+ "volume (%04x) ",
+ volume);
+ break;
+ }
+
+ status = cg2900_fm_test_tone_set_params(
+ tone_gen,
+ frequency,
+ volume,
+ phase_offset,
+ dc,
+ waveform);
+
+ if (0 == status)
+ ret_val = 0;
+ break;
+ }
+ default:
+ {
+ FM_ERR_REPORT("vidioc_set_ext_ctrls: "
+ "Unsupported Id = %04x",
+ ext_ctrl->controls->id);
+ }
+ }
+error:
+ return ret_val;
+}
+
+/**
+ * vidioc_set_hw_freq_seek()- seek Up/Down Frequency.
+ *
+ * This function is used to start seek
+ * on the FM Radio. Direction if seek is as inicated by the parameter
+ * inside the v4l2_hw_freq_seek structure. This function is called when the
+ * application issues the IOCTL VIDIOC_S_HW_FREQ_SEEK
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @freq_seek: v4l2_hw_freq_seek structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_set_hw_freq_seek(
+ struct file *file,
+ void *priv,
+ struct v4l2_hw_freq_seek *freq_seek
+ )
+{
+ int status;
+ int ret_val = -EINVAL;
+
+ FM_INFO_REPORT("vidioc_set_hw_freq_seek");
+
+ FM_DEBUG_REPORT("vidioc_set_hw_freq_seek: Status = %d, "
+ "Upwards = %d, Wrap Around = %d",
+ cg2900_device.seekstatus,
+ freq_seek->seek_upward, freq_seek->wrap_around);
+
+ if (cg2900_device.seekstatus == FMR_SEEK_IN_PROGRESS) {
+ FM_ERR_REPORT("vidioc_set_hw_freq_seek: "
+ "VIDIOC_S_HW_FREQ_SEEK, "
+ "freq_seek in progress");
+ goto error;
+ }
+
+ spin_lock(&fm_spinlock);
+ fm_event = CG2900_EVENT_NO_EVENT;
+ no_of_scan_freq = 0;
+ spin_unlock(&fm_spinlock);
+
+ if (CG2900_DIR_UP == freq_seek->seek_upward)
+ status = cg2900_fm_search_up_freq();
+ else if (CG2900_DIR_DOWN == freq_seek->seek_upward)
+ status = cg2900_fm_search_down_freq();
+ else
+ goto error;
+
+ if (0 != status)
+ goto error;
+
+ cg2900_device.seekstatus = FMR_SEEK_IN_PROGRESS;
+ ret_val = 0;
+
+error:
+ FM_DEBUG_REPORT("vidioc_set_hw_freq_seek: returning = %d",
+ ret_val);
+ return ret_val;
+}
+
+/**
+ * vidioc_get_audio()- Get Audio features of FM Driver.
+ *
+ * This function is used to get the audio features of FM Driver.
+ * This function is imlemented as a dumy function.
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @audio: (out) v4l2_audio structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_get_audio(
+ struct file *file,
+ void *priv,
+ struct v4l2_audio *audio
+ )
+{
+ FM_INFO_REPORT("vidioc_get_audio");
+ strcpy(audio->name, "");
+ audio->capability = 0;
+ audio->mode = 0;
+ return 0;
+}
+
+/**
+ * vidioc_set_audio()- Set Audio features of FM Driver.
+ *
+ * This function is used to set the audio features of FM Driver.
+ * This function is imlemented as a dumy function.
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @audio: v4l2_audio structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_set_audio(
+ struct file *file,
+ void *priv,
+ struct v4l2_audio *audio
+ )
+{
+ FM_INFO_REPORT("vidioc_set_audio");
+ if (audio->index != 0)
+ return -EINVAL;
+ return 0;
+}
+
+/**
+ * vidioc_get_input()- Get the Input Value
+ *
+ * This function is used to get the Input.
+ * This function is imlemented as a dumy function.
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @input: (out) Value to be stored.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_get_input(
+ struct file *file,
+ void *priv,
+ unsigned int *input
+ )
+{
+ FM_INFO_REPORT("vidioc_get_input");
+ *input = 0;
+ return 0;
+}
+
+/**
+ * vidioc_set_input()- Set the input value.
+ *
+ * This function is used to set input.
+ * This function is imlemented as a dumy function.
+ *
+ * @file: File structure.
+ * @priv: Previous data of file structure.
+ * @input: Value to set
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int vidioc_set_input(
+ struct file *file,
+ void *priv,
+ unsigned int input
+ )
+{
+ FM_INFO_REPORT("vidioc_set_input");
+ if (input != 0)
+ return -EINVAL;
+ return 0;
+}
+
+/**
+ * cg2900_convert_err_to_v4l2()- Convert Error Bits to V4L2 RDS format.
+ *
+ * This function converts the error bits in RDS Block
+ * as received from Chip into V4L2 RDS data specification.
+ *
+ * @status_byte: The status byte as received in RDS Group for
+ * particular RDS Block
+ * @out_byte: byte to store the modified byte with the err bits
+ * alligned as per V4L2 RDS Specifications.
+ */
+static void cg2900_convert_err_to_v4l2(
+ char status_byte,
+ char *out_byte
+ )
+{
+ if ((status_byte & RDS_ERROR_STATUS_MASK) == RDS_ERROR_STATUS_MASK) {
+ /* Uncorrectable Block */
+ *out_byte = (*out_byte | V4L2_RDS_BLOCK_ERROR);
+ } else if (((status_byte & RDS_UPTO_TWO_BITS_CORRECTED)
+ == RDS_UPTO_TWO_BITS_CORRECTED) ||
+ ((status_byte & RDS_UPTO_FIVE_BITS_CORRECTED)
+ == RDS_UPTO_FIVE_BITS_CORRECTED)) {
+ /* Corrected Bits in Block */
+ *out_byte = (*out_byte | V4L2_RDS_BLOCK_CORRECTED);
+ }
+}
+
+/**
+ * cg2900_open()- This function nitializes and switches on FM.
+ *
+ * This is called when the application opens the character device.
+ *
+ * @file: File structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int cg2900_open(
+ struct file *file
+ )
+{
+ int status;
+ int ret_val = -EINVAL;
+ struct video_device *vdev = video_devdata(file);
+
+ lock_kernel();
+ users++;
+ FM_INFO_REPORT("cg2900_open: users = %d", users);
+
+ if (users > 1) {
+ FM_INFO_REPORT("cg2900_open: FM already switched on!!!");
+ ret_val = 0;
+ /*
+ * No need to perform the initialization and switch on FM
+ * since it is already done during the first open call to
+ * this driver.
+ */
+ goto done;
+ }
+
+ status = cg2900_fm_init();
+ if (0 != status)
+ goto init_error;
+
+ FM_DEBUG_REPORT("cg2900_open: Switching on FM");
+ status = cg2900_fm_switch_on(&(vdev->dev));
+ if (0 != status)
+ goto switch_on_error;
+
+ cg2900_device.state = FMR_SWITCH_ON;
+ cg2900_device.frequency = HZ_TO_V4L2(freq_low);
+ cg2900_device.rx_rds_enabled = false;
+ cg2900_device.muted = false;
+ cg2900_device.audiopath = 0;
+ cg2900_device.seekstatus = FMR_SEEK_NONE;
+ cg2900_device.rssi_threshold = CG2900_FM_DEFAULT_RSSI_THRESHOLD;
+ fm_event = CG2900_EVENT_NO_EVENT;
+ no_of_scan_freq = 0;
+ cg2900_device.fm_mode = CG2900_FM_IDLE_MODE;
+ ret_val = 0;
+ goto done;
+
+switch_on_error:
+ cg2900_fm_deinit();
+init_error:
+ users--;
+done:
+ unlock_kernel();
+ FM_DEBUG_REPORT("cg2900_open: returning %d", ret_val);
+ return ret_val;
+}
+
+/**
+ * cg2900_release()- This function switches off FM.
+ *
+ * This function switches off FM and releases the resources.
+ * This is called when the application closes the character
+ * device.
+ *
+ * @file: File structure.
+ *
+ * Returns:
+ * 0 when no error
+ * -EINVAL: otherwise
+ */
+static int cg2900_release(
+ struct file *file
+ )
+{
+ int status;
+ int ret_val = -EINVAL;
+
+ mutex_lock(&fm_mutex);
+
+ FM_INFO_REPORT("cg2900_release");
+ if (users <= 0) {
+ FM_ERR_REPORT("cg2900_release: No users registered "
+ "with FM Driver");
+ goto done;
+ }
+
+ users--;
+ FM_INFO_REPORT("cg2900_release: users = %d", users);
+
+ if (0 == users) {
+ FM_DEBUG_REPORT("cg2900_release: Switching Off FM");
+ status = cg2900_fm_switch_off();
+ status = cg2900_fm_deinit();
+ if (0 != status)
+ goto done;
+
+ cg2900_device.state = FMR_SWITCH_OFF;
+ cg2900_device.frequency = 0;
+ cg2900_device.rx_rds_enabled = false;
+ cg2900_device.muted = false;
+ cg2900_device.seekstatus = FMR_SEEK_NONE;
+ fm_event = CG2900_EVENT_NO_EVENT;
+ no_of_scan_freq = 0;
+ }
+ ret_val = 0;
+
+done:
+ mutex_unlock(&fm_mutex);
+ FM_DEBUG_REPORT("cg2900_release: returning %d", ret_val);
+ return ret_val;
+}
+
+/**
+ * cg2900_read()- This function is invoked when the application
+ * calls read() to receive RDS Data.
+ *
+ * @file: File structure.
+ * @data: buffer provided by application for receving the data.
+ * @count: Number of bytes that application wants to read from driver
+ * @pos: offset
+ *
+ * Returns:
+ * Number of bytes copied to the user buffer
+ * -EFAULT: If there is problem in copying data to buffer supplied
+ * by application
+ * -EIO: If the number of bytes to be read are not a multiple of
+ * struct v4l2_rds_data.
+ * -EAGAIN: More than 22 blocks requested to be read or read
+ * was called in non blocking mode and no data was available for reading.
+ * -EINTR: If read was interrupted by a signal before data was avaialble.
+ * 0 when no data available for reading.
+ */
+static ssize_t cg2900_read(
+ struct file *file,
+ char __user *data,
+ size_t count, loff_t *pos
+ )
+{
+ int current_rds_grp;
+ int index = 0;
+ int blocks_to_read;
+ int ret;
+ struct v4l2_rds_data rdsbuf[MAX_RDS_GROUPS * NUM_OF_RDS_BLOCKS];
+ struct v4l2_rds_data *rdslocalbuf = rdsbuf;
+
+ FM_INFO_REPORT("cg2900_read");
+
+ blocks_to_read = (count / sizeof(struct v4l2_rds_data));
+
+ if (!cg2900_device.rx_rds_enabled) {
+ FM_INFO_REPORT("cg2900_read: returning 0");
+ return 0;
+ }
+
+ if (count % sizeof(struct v4l2_rds_data) != 0) {
+ FM_ERR_REPORT("cg2900_read: Invalid Number of bytes %d "
+ "requested to read", count);
+ return -EIO;
+ }
+
+ if (blocks_to_read > MAX_RDS_GROUPS * NUM_OF_RDS_BLOCKS) {
+ FM_ERR_REPORT("cg2900_read: Too many blocks(%d) "
+ "requested to be read", blocks_to_read);
+ return -EAGAIN;
+ }
+
+ if (file->f_flags & O_NONBLOCK) {
+ /* Non blocking mode selected by application */
+ if (fm_rds_info.rds_head == fm_rds_info.rds_tail) {
+ FM_DEBUG_REPORT("cg2900_read: Non Blocking mode "
+ "selected by application, returning as "
+ "no RDS data is available");
+ return -EAGAIN;
+ }
+ }
+
+ if (fm_rds_info.rds_head == fm_rds_info.rds_tail) {
+ /*
+ * Blocking mode selected by application
+ * if data is not available block on read queue
+ */
+ FM_DEBUG_REPORT("cg2900_read: Blocking mode "
+ "selected by application, waiting till "
+ "rds data is available");
+ cg2900_device.wait_on_read_queue = true;
+ ret = wait_event_interruptible(cg2900_read_queue,
+ (fm_rds_info.rds_head !=
+ fm_rds_info.rds_tail));
+ cg2900_device.wait_on_read_queue = false;
+ FM_DEBUG_REPORT("cg2900_read: "
+ "wait_event_interruptible returned = %d", ret);
+ if (ret == -ERESTARTSYS)
+ return -EINTR;
+ }
+
+ /*
+ * Check again that in the meantime RDS is not disabled
+ * by the user, If yes, return.
+ */
+ if (!cg2900_device.rx_rds_enabled) {
+ FM_INFO_REPORT("cg2900_read: returning 0");
+ return 0;
+ }
+
+ current_rds_grp = fm_rds_info.rds_group_sent;
+
+ if ((fm_rds_info.rds_head == fm_rds_info.rds_tail) ||
+ (fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].block1 == 0x0000)) {
+ FM_INFO_REPORT("cg2900_read: returning 0");
+ return 0;
+ }
+
+ spin_lock(&fm_spinlock);
+ while (index < blocks_to_read) {
+ /* Check which Block needs to be transferred next */
+ switch (fm_rds_info.rds_block_sent % NUM_OF_RDS_BLOCKS) {
+ case 0:
+ (rdslocalbuf + index)->lsb =
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].block1;
+ (rdslocalbuf + index)->msb =
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].block1 >> 8;
+ (rdslocalbuf + index)->block =
+ (fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].status1
+ & RDS_BLOCK_MASK) >> 2;
+ cg2900_convert_err_to_v4l2(
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].status1,
+ &(rdslocalbuf + index)->block);
+ break;
+ case 1:
+ (rdslocalbuf + index)->lsb =
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].block2;
+ (rdslocalbuf + index)->msb =
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].block2 >> 8;
+ (rdslocalbuf + index)->block =
+ (fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].status2
+ & RDS_BLOCK_MASK) >> 2;
+ cg2900_convert_err_to_v4l2(
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].status2,
+ &(rdslocalbuf + index)->block);
+ break;
+ case 2:
+ (rdslocalbuf + index)->lsb =
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].block3;
+ (rdslocalbuf + index)->msb =
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].block3 >> 8;
+ (rdslocalbuf + index)->block =
+ (fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].status3
+ & RDS_BLOCK_MASK) >> 2;
+ cg2900_convert_err_to_v4l2(
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].status3,
+ &(rdslocalbuf + index)->block);
+ break;
+ case 3:
+ (rdslocalbuf + index)->lsb =
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].block4;
+ (rdslocalbuf + index)->msb =
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].block4 >> 8;
+ (rdslocalbuf + index)->block =
+ (fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].status4
+ & RDS_BLOCK_MASK) >> 2;
+ cg2900_convert_err_to_v4l2(
+ fm_rds_buf[fm_rds_info.rds_tail]
+ [current_rds_grp].status4,
+ &(rdslocalbuf + index)->block);
+ current_rds_grp++;
+ if (current_rds_grp == MAX_RDS_GROUPS) {
+ fm_rds_info.rds_tail++;
+ current_rds_grp = 0;
+ }
+ break;
+ default:
+ FM_ERR_REPORT("Invalid RDS Group!!!");
+ spin_unlock(&fm_spinlock);
+ return 0;
+ }
+ index++;
+ fm_rds_info.rds_block_sent++;
+ if (fm_rds_info.rds_block_sent == NUM_OF_RDS_BLOCKS)
+ fm_rds_info.rds_block_sent = 0;
+
+ if (!cg2900_device.rx_rds_enabled) {
+ FM_INFO_REPORT("cg2900_read: returning 0");
+ spin_unlock(&fm_spinlock);
+ return 0;
+ }
+ }
+ /* Update the RDS Group Count Sent to Application */
+ fm_rds_info.rds_group_sent = current_rds_grp;
+ if (fm_rds_info.rds_tail == MAX_RDS_BUFFER)
+ fm_rds_info.rds_tail = 0;
+
+ if (copy_to_user(data, rdslocalbuf, count)) {
+ spin_unlock(&fm_spinlock);
+ FM_ERR_REPORT("cg2900_read: Error "
+ "in copying, returning");
+ return -EFAULT;
+ }
+ spin_unlock(&fm_spinlock);
+ return count;
+}
+
+/**
+ * cg2900_poll()- Check if the operation is complete or not.
+ *
+ * This function is invoked by application on calling poll() and is used to
+ * wait till the desired operation seek/Band Scan are complete.
+ * Driver blocks till the seek/BandScan Operation is complete. The application
+ * decides to read the results of seek/Band Scan based on the returned value of
+ * this function.
+ *
+ * @file: File structure.
+ * @wait: poll table
+ *
+ * Returns:
+ * POLLRDNORM when Scan Band/Block Scan/Seek station is complete.
+ * POLLHUP when seek station/Band Scan is stopped by user using Stop Scan.
+ */
+static unsigned int cg2900_poll(
+ struct file *file,
+ struct poll_table_struct *wait
+ )
+{
+ int ret_val = 0;
+
+ FM_INFO_REPORT("cg2900_poll");
+
+ poll_wait(file, &cg2900_poll_queue, wait);
+
+ if (cg2900_device.seekstatus == FMR_SEEK_IN_PROGRESS &&
+ ((fm_event == CG2900_EVENT_SCAN_CHANNELS_FOUND) ||
+ (fm_event == CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND) ||
+ (fm_event == CG2900_EVENT_SEARCH_CHANNEL_FOUND))) {
+ /* Scan Completed, send event to application */
+ FM_DEBUG_REPORT("poll_wait returning POLLRDNORM");
+ ret_val = POLLRDNORM;
+ goto done;
+ }
+
+ if (fm_event == CG2900_EVENT_SCAN_CANCELLED) {
+ /* Scan/Search cancelled by User */
+ spin_lock(&fm_spinlock);
+ no_of_scan_freq = 0;
+ no_of_block_scan_freq = 0;
+ spin_unlock(&fm_spinlock);
+ cg2900_device.seekstatus = FMR_SEEK_NONE;
+ fm_event = CG2900_EVENT_NO_EVENT;
+ FM_DEBUG_REPORT("cg2900_poll: Cancel operation "
+ "returning POLLHUP");
+ FM_DEBUG_REPORT("poll_wait returning POLLHUP");
+ ret_val = POLLHUP;
+ goto done;
+ }
+
+done:
+ FM_DEBUG_REPORT("poll_wait returning %d", ret_val);
+ return ret_val;
+}
+
+/**
+ * radio_cg2900_probe()- This function registers FM Driver with V4L2 Driver.
+ *
+ * This function is called whenever the driver is probed by the device system,
+ * i.e. when a CG2900 controller has connected. It registers the FM Driver with
+ * Video4Linux as a character device.
+ *
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 on success
+ * -EINVAL on error
+ */
+static int __devinit radio_cg2900_probe(
+ struct platform_device *pdev
+ )
+{
+ int err;
+
+ FM_INFO_REPORT(BANNER);
+
+ err = fmd_set_dev(&pdev->dev);
+ if (err) {
+ FM_ERR_REPORT("Could not set device %s", pdev->name);
+ return err;
+ }
+
+ radio_nr = 0;
+ grid = CG2900_FM_GRID_100;
+ band = CG2900_FM_BAND_US_EU;
+ FM_INFO_REPORT("radio_cg2900_probe: radio_nr= %d.", radio_nr);
+
+ /* Initialize the parameters */
+ if (video_register_device(
+ &cg2900_video_device,
+ VFL_TYPE_RADIO,
+ radio_nr) == -1) {
+ FM_ERR_REPORT("radio_cg2900_probe: video_register_device err");
+ return -EINVAL;
+ }
+ mutex_init(&fm_mutex);
+ spin_lock_init(&fm_spinlock);
+ init_waitqueue_head(&cg2900_poll_queue);
+ users = 0;
+ return 0;
+}
+
+/**
+ * radio_cg2900_remove()- This function removes the FM Driver.
+ *
+ * This function is called whenever the driver is removed by the device system,
+ * i.e. when a CG2900 controller has disconnected. It unregisters the FM Driver
+ * from Video4Linux.
+ *
+ * @pdev: Platform device.
+ *
+ * Returns: 0 on success
+ */
+static int __devexit radio_cg2900_remove(
+ struct platform_device *pdev
+ )
+{
+ FM_INFO_REPORT("radio_cg2900_remove");
+
+ /* Try to Switch Off FM in case it is still switched on */
+ cg2900_fm_switch_off();
+ cg2900_fm_deinit();
+ mutex_destroy(&fm_mutex);
+ video_unregister_device(&cg2900_video_device);
+ fmd_set_dev(NULL);
+ return 0;
+}
+
+static struct platform_driver radio_cg2900_driver = {
+ .driver = {
+ .name = "cg2900-fm",
+ .owner = THIS_MODULE,
+ },
+ .probe = radio_cg2900_probe,
+ .remove = __devexit_p(radio_cg2900_remove),
+};
+
+/**
+ * radio_cg2900_init() - Initialize module.
+ *
+ * Registers platform driver.
+ */
+static int __init radio_cg2900_init(void)
+{
+ FM_INFO_REPORT("radio_cg2900_init");
+ return platform_driver_register(&radio_cg2900_driver);
+}
+
+/**
+ * radio_cg2900_exit() - Remove module.
+ *
+ * Unregisters platform driver.
+ */
+static void __exit radio_cg2900_exit(void)
+{
+ FM_INFO_REPORT("radio_cg2900_exit");
+ platform_driver_unregister(&radio_cg2900_driver);
+}
+
+void wake_up_poll_queue(void)
+{
+ FM_INFO_REPORT("wake_up_poll_queue");
+ wake_up_interruptible(&cg2900_poll_queue);
+}
+
+void wake_up_read_queue(void)
+{
+ FM_INFO_REPORT("wake_up_read_queue");
+ if (cg2900_device.wait_on_read_queue)
+ wake_up_interruptible(&cg2900_read_queue);
+}
+
+void cg2900_handle_device_reset(void)
+{
+ FM_INFO_REPORT("cg2900_handle_device_reset");
+ users = 0;
+ cg2900_device.state = FMR_SWITCH_OFF;
+ cg2900_device.frequency = 0;
+ cg2900_device.rx_rds_enabled = false;
+ cg2900_device.muted = false;
+ cg2900_device.seekstatus = FMR_SEEK_NONE;
+ fm_event = CG2900_EVENT_NO_EVENT;
+ no_of_scan_freq = 0;
+}
+
+module_init(radio_cg2900_init);
+module_exit(radio_cg2900_exit);
+MODULE_AUTHOR("Hemant Gupta");
+MODULE_LICENSE("GPL v2");
+
+module_param(radio_nr, int, S_IRUGO);
+
+module_param(grid, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(grid, "Grid:"
+ "0=50 kHz"
+ "*1=100 kHz*"
+ "2=200 kHz");
+
+module_param(band, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(band, "Band:"
+ "*0=87.5-108 MHz*"
+ "1=76-90 MHz"
+ "2=70-108 MHz");
+
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 83567b898d0..06ad0d3e1dc 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -452,4 +452,20 @@ config RADIO_TIMBERDALE
found behind the Timberdale FPGA on the Russellville board.
Enabling this driver will automatically select the DSP and tuner.
+config RADIO_CG2900
+ tristate "ST-Ericsson CG2900 FM Radio support"
+ depends on MFD_CG2900 && VIDEO_V4L2
+ ---help---
+ Choose Y here if you have one of these FM radio cards. This is a BT,
+ FM and GPS combo chip controlled via HCI.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-CG2900.
+
+
endif # RADIO_ADAPTERS
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index f615583b483..2ce9398ddcf 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -26,5 +26,6 @@ obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o
obj-$(CONFIG_RADIO_TEF6862) += tef6862.o
obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o
+obj-$(CONFIG_RADIO_CG2900) += CG2900/
EXTRA_CFLAGS += -Isound
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 9da0e504bbe..03ff74192c1 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -177,6 +177,29 @@ config TWL4030_CODEC
select MFD_CORE
default n
+config MFD_STMPE
+ bool "Support STMicroelectronics STMPE"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select MFD_CORE
+ help
+ Support for the STMPE family of I/O Expanders from
+ STMicroelectronics.
+
+ Currently supported devices are:
+
+ STMPE811: GPIO, Touchscreen
+ STMPE1601: GPIO, Keypad
+ STMPE2401: GPIO, Keypad
+ STMPE2403: GPIO, Keypad
+
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the functionality
+ of the device. Currently available sub drivers are:
+
+ GPIO: stmpe-gpio
+ Keypad: stmpe-keypad
+ Touchscreen: stmpe-ts
+
config MFD_TC35892
bool "Support Toshiba TC35892"
depends on I2C=y && GENERIC_HARDIRQS
@@ -213,6 +236,48 @@ config MFD_TC6387XB
help
Support for Toshiba Mobile IO Controller TC6387XB
+config MFD_CG2900
+ tristate "Support ST-Ericsson CG2900 main structure"
+ depends on NET
+ help
+ Support for ST-Ericsson CG2900 Connectivity Combo controller main structure.
+ Supports multiple functionalities muxed over a Bluetooth HCI H:4 interface.
+ CG2900 support Bluetooth, FM radio, and GPS.
+
+config MFD_CG2900_CHIP
+ tristate "Support CG2900 Connectivity controller"
+ depends on MFD_CG2900
+ help
+ Support for ST-Ericsson CG2900 Connectivity Controller
+
+config MFD_STLC2690_CHIP
+ tristate "Support STLC2690 Connectivity controller"
+ depends on MFD_CG2900
+ help
+ Support for ST-Ericsson STLC2690 Connectivity Controller
+
+config MFD_CG2900_UART
+ tristate "Support CG2900 UART transport"
+ depends on MFD_CG2900
+ select BT
+ select BT_HCIUART
+ help
+ Support for UART as transport for ST-Ericsson CG2900 Connectivity
+ Controller
+
+config MFD_CG2900_AUDIO
+ tristate "Support CG2900 audio interface"
+ depends on MFD_CG2900
+ help
+ Support for ST-Ericsson CG2900 Connectivity audio interface. Gives a
+ module the ability to setup audio paths within the CG2900 controller.
+
+config MFD_CG2900_TEST
+ tristate "Support CG2900 Test Char Device"
+ depends on MFD_CG2900
+ help
+ Support for ST-Ericsson CG2900 Test Character Device.
+
config MFD_TC6393XB
bool "Support Toshiba TC6393XB"
depends on GPIOLIB && ARM
@@ -221,6 +286,20 @@ config MFD_TC6393XB
help
Support for Toshiba Mobile IO Controller TC6393XB
+config AB5500_CORE
+ bool "ST-Ericsson AB5500 Mixed Signal Circuit core functions"
+ select MFD_CORE
+ depends on GENERIC_HARDIRQS && ABX500_CORE
+ help
+ Select this to enable the AB5500 Mixed Signal IC core
+ functionality. This connects to a AB5500 chip on the I2C bus via
+ the Power and Reset Management Unit (PRCMU). It exposes a number
+ of symbols needed for dependent devices to read and write
+ registers and subscribe to events from this multi-functional IC.
+ This is needed to use other features of the AB5500 such as
+ battery-backed RTC, charging control, Regulators, LEDs, vibrator,
+ system power and temperature, power management and ALSA sound.
+
config PMIC_DA903X
bool "Dialog Semiconductor DA9030/DA9034 PMIC Support"
depends on I2C=y
@@ -382,7 +461,7 @@ config PCF50633_GPIO
config ABX500_CORE
bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
- default y if ARCH_U300
+ default y if ARCH_U300 || ARCH_U8500
help
Say yes here if you have the ABX500 Mixed Signal IC family
chips. This core driver expose register access functions.
@@ -393,6 +472,7 @@ config ABX500_CORE
config AB3100_CORE
bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
depends on I2C=y && ABX500_CORE
+ select MFD_CORE
default y if ARCH_U300
help
Select this to enable the AB3100 Mixed Signal IC core
@@ -422,14 +502,48 @@ config EZX_PCAP
config AB8500_CORE
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
- depends on SPI=y && GENERIC_HARDIRQS
+ depends on GENERIC_HARDIRQS && ABX500_CORE && SPI_MASTER && ARCH_U8500
select MFD_CORE
help
Select this option to enable access to AB8500 power management
- chip. This connects to U8500 on the SSP/SPI bus and exports
- read/write functions for the devices to get access to this chip.
+ chip. This connects to U8500 either on the SSP/SPI bus
+ or the I2C bus via PRCMU. It also adds the irq_chip
+ parts for handling the Mixed Signal chip events.
This chip embeds various other multimedia funtionalities as well.
+config AB8500_I2C_CORE
+ bool "AB8500 register access via PRCMU I2C"
+ depends on AB8500_CORE && UX500_SOC_DB8500
+ default y
+ help
+ This enables register access to the AB8500 chip via PRCMU I2C.
+ The AB8500 chip can be accessed via SPI or I2C. On DB8500 hardware
+ the I2C bus is connected to the Power Reset
+ and Mangagement Unit, PRCMU.
+
+config AB8500_DENC
+ bool "AB8500_DENC driver support(CVBS)"
+ depends on AB8500_CORE
+ help
+ Select this option to add driver support for analog TV out through
+ AB8500.
+
+
+config AB8500_DEBUG
+ bool "Enable debug info via debugfs"
+ depends on AB8500_CORE && DEBUG_FS
+ default y if DEBUG_FS
+ help
+ Select this option if you want debug information using the debug
+ filesystem, debugfs.
+
+config AB8500_GPADC
+ bool "AB8500 GPADC driver"
+ depends on AB8500_CORE
+ default y
+ help
+ AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage.
+
config AB3550_CORE
bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index fb503e77dc6..015a4584e90 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -2,6 +2,7 @@
# Makefile for multifunction miscellaneous devices
#
+obj-$(CONFIG_AB5500_CORE) += ab5500-core.o ab5500-power.o
88pm860x-objs := 88pm860x-core.o 88pm860x-i2c.o
obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o
obj-$(CONFIG_MFD_SM501) += sm501.o
@@ -15,6 +16,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
+obj-$(CONFIG_MFD_STMPE) += stmpe.o
obj-$(CONFIG_MFD_TC35892) += tc35892.o
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
@@ -51,6 +53,7 @@ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o
ifeq ($(CONFIG_SA1100_ASSABET),y)
obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
endif
+obj-y += cg2900/
obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o
obj-$(CONFIG_PMIC_DA903X) += da903x.o
@@ -65,7 +68,11 @@ obj-$(CONFIG_ABX500_CORE) += abx500-core.o
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
obj-$(CONFIG_AB3550_CORE) += ab3550-core.o
-obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-spi.o
+obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
+obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o
+obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o
+obj-$(CONFIG_AB8500_DENC) += ab8500-denc.o
+obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index 66379b41390..8e84e783c9e 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -19,6 +19,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
+#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
/* These are the only registers inside AB3100 used in this main file */
@@ -146,7 +147,7 @@ static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100,
}
static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
- u8 reg, u8 *regval)
+ u8 reg, u8 *regval)
{
int err;
@@ -202,7 +203,7 @@ static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
}
static int get_register_interruptible(struct device *dev, u8 bank, u8 reg,
- u8 *value)
+ u8 *value)
{
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
@@ -665,7 +666,7 @@ struct ab3100_init_setting {
u8 setting;
};
-static const struct ab3100_init_setting __initconst
+static const struct ab3100_init_setting __devinitconst
ab3100_init_settings[] = {
{
.abreg = AB3100_MCA,
@@ -712,7 +713,7 @@ ab3100_init_settings[] = {
},
};
-static int __init ab3100_setup(struct ab3100 *ab3100)
+static int __devinit ab3100_setup(struct ab3100 *ab3100)
{
int err = 0;
int i;
@@ -742,52 +743,64 @@ static int __init ab3100_setup(struct ab3100 *ab3100)
return err;
}
-/*
- * Here we define all the platform devices that appear
- * as children of the AB3100. These are regular platform
- * devices with the IORESOURCE_IO .start and .end set
- * to correspond to the internal AB3100 register range
- * mapping to the corresponding subdevice.
- */
-
-#define AB3100_DEVICE(devname, devid) \
-static struct platform_device ab3100_##devname##_device = { \
- .name = devid, \
- .id = -1, \
-}
-
-/* This lists all the subdevices */
-AB3100_DEVICE(dac, "ab3100-dac");
-AB3100_DEVICE(leds, "ab3100-leds");
-AB3100_DEVICE(power, "ab3100-power");
-AB3100_DEVICE(regulators, "ab3100-regulators");
-AB3100_DEVICE(sim, "ab3100-sim");
-AB3100_DEVICE(uart, "ab3100-uart");
-AB3100_DEVICE(rtc, "ab3100-rtc");
-AB3100_DEVICE(charger, "ab3100-charger");
-AB3100_DEVICE(boost, "ab3100-boost");
-AB3100_DEVICE(adc, "ab3100-adc");
-AB3100_DEVICE(fuelgauge, "ab3100-fuelgauge");
-AB3100_DEVICE(vibrator, "ab3100-vibrator");
-AB3100_DEVICE(otp, "ab3100-otp");
-AB3100_DEVICE(codec, "ab3100-codec");
-
-static struct platform_device *
-ab3100_platform_devs[] = {
- &ab3100_dac_device,
- &ab3100_leds_device,
- &ab3100_power_device,
- &ab3100_regulators_device,
- &ab3100_sim_device,
- &ab3100_uart_device,
- &ab3100_rtc_device,
- &ab3100_charger_device,
- &ab3100_boost_device,
- &ab3100_adc_device,
- &ab3100_fuelgauge_device,
- &ab3100_vibrator_device,
- &ab3100_otp_device,
- &ab3100_codec_device,
+/* The subdevices of the AB3100 */
+static struct mfd_cell ab3100_devs[] = {
+ {
+ .name = "ab3100-dac",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-leds",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-power",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-regulators",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-sim",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-uart",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-rtc",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-charger",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-boost",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-adc",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-fuelgauge",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-vibrator",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-otp",
+ .id = -1,
+ },
+ {
+ .name = "ab3100-codec",
+ .id = -1,
+ },
};
struct ab_family_id {
@@ -795,7 +808,7 @@ struct ab_family_id {
char *name;
};
-static const struct ab_family_id ids[] __initdata = {
+static const struct ab_family_id ids[] __devinitdata = {
/* AB3100 */
{
.id = 0xc0,
@@ -849,8 +862,8 @@ static const struct ab_family_id ids[] __initdata = {
},
};
-static int __init ab3100_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int __devinit ab3100_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
struct ab3100 *ab3100;
struct ab3100_platform_data *ab3100_plf_data =
@@ -934,18 +947,14 @@ static int __init ab3100_probe(struct i2c_client *client,
if (err)
goto exit_no_ops;
- /* Set parent and a pointer back to the container in device data */
- for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++) {
- ab3100_platform_devs[i]->dev.parent =
- &client->dev;
- ab3100_platform_devs[i]->dev.platform_data =
- ab3100_plf_data;
- platform_set_drvdata(ab3100_platform_devs[i], ab3100);
+ /* Set up and register the platform devices. */
+ for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) {
+ ab3100_devs[i].platform_data = ab3100_plf_data;
+ ab3100_devs[i].data_size = sizeof(struct ab3100_platform_data);
}
- /* Register the platform devices */
- platform_add_devices(ab3100_platform_devs,
- ARRAY_SIZE(ab3100_platform_devs));
+ err = mfd_add_devices(&client->dev, 0, ab3100_devs,
+ ARRAY_SIZE(ab3100_devs), NULL, 0);
ab3100_setup_debugfs(ab3100);
@@ -961,14 +970,12 @@ static int __init ab3100_probe(struct i2c_client *client,
return err;
}
-static int __exit ab3100_remove(struct i2c_client *client)
+static int __devexit ab3100_remove(struct i2c_client *client)
{
struct ab3100 *ab3100 = i2c_get_clientdata(client);
- int i;
/* Unregister subdevices */
- for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++)
- platform_device_unregister(ab3100_platform_devs[i]);
+ mfd_remove_devices(&client->dev);
ab3100_remove_debugfs();
i2c_unregister_device(ab3100->testreg_client);
@@ -995,7 +1002,7 @@ static struct i2c_driver ab3100_driver = {
},
.id_table = ab3100_id,
.probe = ab3100_probe,
- .remove = __exit_p(ab3100_remove),
+ .remove = __devexit_p(ab3100_remove),
};
static int __init ab3100_i2c_init(void)
diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c
index 63d2b727ddb..8440010eb2b 100644
--- a/drivers/mfd/ab3100-otp.c
+++ b/drivers/mfd/ab3100-otp.c
@@ -199,7 +199,7 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
err = ab3100_otp_read(otp);
if (err)
- return err;
+ goto err_otp_read;
dev_info(&pdev->dev, "AB3100 OTP readout registered\n");
@@ -208,21 +208,21 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
err = device_create_file(&pdev->dev,
&ab3100_otp_attrs[i]);
if (err)
- goto out_no_sysfs;
+ goto err_create_file;
}
/* debugfs entries */
err = ab3100_otp_init_debugfs(&pdev->dev, otp);
if (err)
- goto out_no_debugfs;
+ goto err_init_debugfs;
return 0;
-out_no_sysfs:
- for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++)
- device_remove_file(&pdev->dev,
- &ab3100_otp_attrs[i]);
-out_no_debugfs:
+err_init_debugfs:
+err_create_file:
+ while (--i >= 0)
+ device_remove_file(&pdev->dev, &ab3100_otp_attrs[i]);
+err_otp_read:
kfree(otp);
return err;
}
diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c
index f54ab62e7bc..8a98739e6d9 100644
--- a/drivers/mfd/ab3550-core.c
+++ b/drivers/mfd/ab3550-core.c
@@ -589,16 +589,16 @@ static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg)
}
/*
- * The exported register access functionality.
+ * The register access functionality.
*/
-int ab3550_get_chip_id(struct device *dev)
+static int ab3550_get_chip_id(struct device *dev)
{
struct ab3550 *ab = dev_get_drvdata(dev->parent);
return (int)ab->chip_id;
}
-int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank,
- u8 reg, u8 bitmask, u8 bitvalues)
+static int ab3550_mask_and_set_register_interruptible(struct device *dev,
+ u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
{
struct ab3550 *ab;
struct platform_device *pdev = to_platform_device(dev);
@@ -612,15 +612,15 @@ int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank,
bitmask, bitvalues);
}
-int ab3550_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
- u8 value)
+static int ab3550_set_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 value)
{
return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
value);
}
-int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
- u8 *value)
+static int ab3550_get_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 *value)
{
struct ab3550 *ab;
struct platform_device *pdev = to_platform_device(dev);
@@ -633,7 +633,7 @@ int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
return get_register_interruptible(ab, bank, reg, value);
}
-int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
+static int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
u8 first_reg, u8 *regvals, u8 numregs)
{
struct ab3550 *ab;
@@ -649,7 +649,8 @@ int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
numregs);
}
-int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event)
+static int ab3550_event_registers_startup_state_get(struct device *dev,
+ u8 *event)
{
struct ab3550 *ab;
@@ -661,7 +662,7 @@ int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event)
return 0;
}
-int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq)
+static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq)
{
struct ab3550 *ab;
struct ab3550_platform_data *plf_data;
diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c
new file mode 100755
index 00000000000..75300857e55
--- /dev/null
+++ b/drivers/mfd/ab5500-core.c
@@ -0,0 +1,2495 @@
+/*
+ * Copyright (C) 2007-2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Low-level core for exclusive access to the AB5500 IC on the I2C bus
+ * and some basic chip-configuration.
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com>
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com>
+ * Author: Karl Komierowski <karl.komierowski@stericsson.com>
+ * Author: Bibek Basu <bibek.basu@stericsson.com>
+ */
+
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/core.h>
+#include <linux/version.h>
+#include <mach/prcmu-db5500.h>
+
+#define AB5500_NAME_STRING "ab5500"
+#define AB5500_ID_FORMAT_STRING "AB5500 %s"
+#define AB5500_NUM_EVENT_REG 23
+#define AB5500_IT_LATCH0_REG 0x40
+#define AB5500_IT_MASK0_REG 0x60
+/* These are the only registers inside AB5500 used in this main file */
+
+/* Read/write operation values. */
+#define AB5500_PERM_RD (0x01)
+#define AB5500_PERM_WR (0x02)
+
+/* Read/write permissions. */
+#define AB5500_PERM_RO (AB5500_PERM_RD)
+#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR)
+
+#define AB5500_MASK_BASE (0x60)
+#define AB5500_MASK_END (0x79)
+#define AB5500_CHIP_ID (0x20)
+
+/**
+ * struct ab5500_bank
+ * @slave_addr: I2C slave_addr found in AB5500 specification
+ * @name: Documentation name of the bank. For reference
+ */
+struct ab5500_bank {
+ u8 slave_addr;
+ const char *name;
+};
+
+static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = {
+ [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = {
+ AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"},
+ [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = {
+ AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"},
+ [AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"},
+ [AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"},
+ [AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"},
+ [AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"},
+ [AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"},
+ [AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"},
+ [AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"},
+ [AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"},
+ [AB5500_BANK_FG_BATTCOM_ACC] = {
+ AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"},
+ [AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"},
+ [AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"},
+ [AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"},
+ [AB5500_BANK_AUDIO_HEADSETUSB] = {
+ AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"},
+};
+
+
+/**
+ * struct ab5500_reg_range
+ * @first: the first address of the range
+ * @last: the last address of the range
+ * @perm: access permissions for the range
+ */
+struct ab5500_reg_range {
+ u8 first;
+ u8 last;
+ u8 perm;
+};
+
+/**
+ * struct ab5500_i2c_ranges
+ * @count: the number of ranges in the list
+ * @range: the list of register ranges
+ */
+struct ab5500_i2c_ranges {
+ u8 nranges;
+ u8 bankid;
+ const struct ab5500_reg_range *range;
+};
+
+/**
+ * struct ab5500_i2c_banks
+ * @count: the number of ranges in the list
+ * @range: the list of register ranges
+ */
+struct ab5500_i2c_banks {
+ u8 nbanks;
+ const struct ab5500_i2c_ranges *bank;
+};
+
+/*
+ * Permissible register ranges for reading and writing per device and bank.
+ *
+ * The ranges must be listed in increasing address order, and no overlaps are
+ * allowed. It is assumed that write permission implies read permission
+ * (i.e. only RO and RW permissions should be used). Ranges with write
+ * permission must not be split up.
+ */
+
+#define NO_RANGE {.count = 0, .range = NULL,}
+static struct ab5500_i2c_banks ab5500_bank_ranges[AB5500_NUM_DEVICES] = {
+ [AB5500_DEVID_USB] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_USB,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x83,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x87,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8B,
+ .last = 0x8B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x91,
+ .last = 0x92,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x93,
+ .last = 0x93,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x94,
+ .last = 0x94,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xA8,
+ .last = 0xB0,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB2,
+ .last = 0xB2,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB4,
+ .last = 0xBC,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xBF,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xC5,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_ADC] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_ADC,
+ .nranges = 6,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x1F,
+ .last = 0x22,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x23,
+ .last = 0x24,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x26,
+ .last = 0x2D,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x34,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x37,
+ .last = 0x57,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x58,
+ .last = 0x58,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_LEDS] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_LED,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_VIDEO] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_VDENC,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x08,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x09,
+ .last = 0x09,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0A,
+ .last = 0x12,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x15,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1B,
+ .last = 0x21,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x27,
+ .last = 0x2C,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x41,
+ .last = 0x41,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x45,
+ .last = 0x5B,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x5D,
+ .last = 0x5D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x69,
+ .last = 0x69,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x6C,
+ .last = 0x6D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x81,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_REGULATORS] = {
+ .nbanks = 2,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_STARTUP,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1F,
+ .last = 0x1F,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x2E,
+ .last = 0x2E,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x30,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x51,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x61,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x66,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8C,
+ .last = 0x96,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xAA,
+ .last = 0xB4,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xB7,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xCA,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xD3,
+ .last = 0xE0,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ {
+ .bankid = AB5500_BANK_SIM_USBSIM,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x13,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_SIM] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_SIM_USBSIM,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x13,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_RTC] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_RTC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x04,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x06,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_CHARGER] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_CHG,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x11,
+ .last = 0x11,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x12,
+ .last = 0x1B,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_FUELGAUGE] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_FG_BATTCOM_ACC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0C,
+ .last = 0x10,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_VIBRATOR] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_VIBRA,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x10,
+ .last = 0x13,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xFE,
+ .last = 0xFE,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_CODEC] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_AUDIO_HEADSETUSB,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x48,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xEB,
+ .last = 0xFB,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ },
+ },
+ [AB5500_DEVID_POWER] = {
+ .nbanks = 1,
+ .bank = (struct ab5500_i2c_ranges []) {
+ {
+ .bankid = AB5500_BANK_STARTUP,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x30,
+ .last = 0x30,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ }
+ },
+ },
+};
+
+#define AB5500_IRQ(bank, bit) ((bank) * 8 + (bit))
+
+/* I appologize for the resource names beeing a mix of upper case
+ * and lower case but I want them to be exact as the documentation */
+static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = {
+ [AB5500_DEVID_LEDS] = {
+ .name = "ab5500-leds",
+ .id = AB5500_DEVID_LEDS,
+ },
+ [AB5500_DEVID_POWER] = {
+ .name = "ab5500-power",
+ .id = AB5500_DEVID_POWER,
+ },
+ [AB5500_DEVID_REGULATORS] = {
+ .name = "ab5500-regulator",
+ .id = AB5500_DEVID_REGULATORS,
+ },
+ [AB5500_DEVID_SIM] = {
+ .name = "ab5500-sim",
+ .id = AB5500_DEVID_SIM,
+ .num_resources = 1,
+ .resources = (struct resource[]) {
+ {
+ .name = "SIMOFF",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(2, 0), /*rising*/
+ .end = AB5500_IRQ(2, 1), /*falling*/
+ },
+ },
+ },
+ [AB5500_DEVID_RTC] = {
+ .name = "ab5500-rtc",
+ .id = AB5500_DEVID_RTC,
+ .num_resources = 1,
+ .resources = (struct resource[]) {
+ {
+ .name = "RTC_Alarm",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(1, 7),
+ .end = AB5500_IRQ(1, 7),
+ }
+ },
+ },
+ [AB5500_DEVID_CHARGER] = {
+ .name = "ab5500-charger",
+ .id = AB5500_DEVID_CHARGER,
+ },
+ [AB5500_DEVID_ADC] = {
+ .name = "ab5500-adc",
+ .id = AB5500_DEVID_ADC,
+ .num_resources = 10,
+ .resources = (struct resource[]) {
+ {
+ .name = "TRIGGER-0",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 0),
+ .end = AB5500_IRQ(0, 0),
+ },
+ {
+ .name = "TRIGGER-1",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 1),
+ .end = AB5500_IRQ(0, 1),
+ },
+ {
+ .name = "TRIGGER-2",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 2),
+ .end = AB5500_IRQ(0, 2),
+ },
+ {
+ .name = "TRIGGER-3",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 3),
+ .end = AB5500_IRQ(0, 3),
+ },
+ {
+ .name = "TRIGGER-4",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 4),
+ .end = AB5500_IRQ(0, 4),
+ },
+ {
+ .name = "TRIGGER-5",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 5),
+ .end = AB5500_IRQ(0, 5),
+ },
+ {
+ .name = "TRIGGER-6",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 6),
+ .end = AB5500_IRQ(0, 6),
+ },
+ {
+ .name = "TRIGGER-7",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 7),
+ .end = AB5500_IRQ(0, 7),
+ },
+ {
+ .name = "TRIGGER-VBAT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 8),
+ .end = AB5500_IRQ(0, 8),
+ },
+ {
+ .name = "TRIGGER-VBAT-TXON",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(0, 9),
+ .end = AB5500_IRQ(0, 9),
+ },
+ },
+ },
+ [AB5500_DEVID_FUELGAUGE] = {
+ .name = "ab5500-fuelgauge",
+ .id = AB5500_DEVID_FUELGAUGE,
+ .num_resources = 6,
+ .resources = (struct resource[]) {
+ {
+ .name = "Batt_attach",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(7, 5),
+ .end = AB5500_IRQ(7, 5),
+ },
+ {
+ .name = "Batt_removal",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(7, 6),
+ .end = AB5500_IRQ(7, 6),
+ },
+ {
+ .name = "UART_framing",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(7, 7),
+ .end = AB5500_IRQ(7, 7),
+ },
+ {
+ .name = "UART_overrun",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 0),
+ .end = AB5500_IRQ(8, 0),
+ },
+ {
+ .name = "UART_Rdy_RX",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 1),
+ .end = AB5500_IRQ(8, 1),
+ },
+ {
+ .name = "UART_Rdy_TX",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 2),
+ .end = AB5500_IRQ(8, 2),
+ },
+ },
+ },
+ [AB5500_DEVID_VIBRATOR] = {
+ .name = "ab5500-vibrator",
+ .id = AB5500_DEVID_VIBRATOR,
+ },
+ [AB5500_DEVID_CODEC] = {
+ .name = "ab5500-codec",
+ .id = AB5500_DEVID_CODEC,
+ .num_resources = 3,
+ .resources = (struct resource[]) {
+ {
+ .name = "audio_spkr1_ovc",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 5),
+ .end = AB5500_IRQ(9, 5),
+ },
+ {
+ .name = "audio_plllocked",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 6),
+ .end = AB5500_IRQ(9, 6),
+ },
+ {
+ .name = "audio_spkr2_ovc",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 4),
+ .end = AB5500_IRQ(17, 4),
+ },
+ },
+ },
+ [AB5500_DEVID_USB] = {
+ .name = "ab5500-usb",
+ .id = AB5500_DEVID_USB,
+ .num_resources = 36,
+ .resources = (struct resource[]) {
+ {
+ .name = "Link_Update",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(22, 1),
+ .end = AB5500_IRQ(22, 1),
+ },
+ {
+ .name = "DCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 3),
+ .end = AB5500_IRQ(8, 4),
+ },
+ {
+ .name = "VBUS_R",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 5),
+ .end = AB5500_IRQ(8, 5),
+ },
+ {
+ .name = "VBUS_F",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 6),
+ .end = AB5500_IRQ(8, 6),
+ },
+ {
+ .name = "CHGstate_10_PCVBUSchg",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(8, 7),
+ .end = AB5500_IRQ(8, 7),
+ },
+ {
+ .name = "DCIOreverse_ovc",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 0),
+ .end = AB5500_IRQ(9, 0),
+ },
+ {
+ .name = "USBCharDetDone",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 1),
+ .end = AB5500_IRQ(9, 1),
+ },
+ {
+ .name = "DCIO_no_limit",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 2),
+ .end = AB5500_IRQ(9, 2),
+ },
+ {
+ .name = "USB_suspend",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 3),
+ .end = AB5500_IRQ(9, 3),
+ },
+ {
+ .name = "DCIOreverse_fwdcurrent",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 4),
+ .end = AB5500_IRQ(9, 4),
+ },
+ {
+ .name = "Vbus_Imeasmax_change",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(9, 5),
+ .end = AB5500_IRQ(9, 6),
+ },
+ {
+ .name = "OVV",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 5),
+ .end = AB5500_IRQ(14, 5),
+ },
+ {
+ .name = "USBcharging_NOTok",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 3),
+ .end = AB5500_IRQ(15, 3),
+ },
+ {
+ .name = "usb_adp_sensoroff",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 6),
+ .end = AB5500_IRQ(15, 6),
+ },
+ {
+ .name = "usb_adp_probeplug",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 7),
+ .end = AB5500_IRQ(15, 7),
+ },
+ {
+ .name = "usb_adp_sinkerror",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 0),
+ .end = AB5500_IRQ(16, 6),
+ },
+ {
+ .name = "usb_adp_sourceerror",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 1),
+ .end = AB5500_IRQ(16, 1),
+ },
+ {
+ .name = "usb_idgnd_r",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 2),
+ .end = AB5500_IRQ(16, 2),
+ },
+ {
+ .name = "usb_idgnd_f",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 3),
+ .end = AB5500_IRQ(16, 3),
+ },
+ {
+ .name = "usb_iddetR1",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 4),
+ .end = AB5500_IRQ(16, 5),
+ },
+ {
+ .name = "usb_iddetR2",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(16, 6),
+ .end = AB5500_IRQ(16, 7),
+ },
+ {
+ .name = "usb_iddetR3",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 0),
+ .end = AB5500_IRQ(17, 1),
+ },
+ {
+ .name = "usb_iddetR4",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 2),
+ .end = AB5500_IRQ(17, 3),
+ },
+ {
+ .name = "CharTempWindowOk",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(17, 7),
+ .end = AB5500_IRQ(18, 0),
+ },
+ {
+ .name = "USB_SprDetect",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 1),
+ .end = AB5500_IRQ(18, 1),
+ },
+ {
+ .name = "usb_adp_probe_unplug",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 2),
+ .end = AB5500_IRQ(18, 2),
+ },
+ {
+ .name = "VBUSChDrop",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 3),
+ .end = AB5500_IRQ(18, 4),
+ },
+ {
+ .name = "dcio_char_rec_done",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 5),
+ .end = AB5500_IRQ(18, 5),
+ },
+ {
+ .name = "Charging_stopped_by_temp",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(18, 6),
+ .end = AB5500_IRQ(18, 6),
+ },
+ {
+ .name = "CHGstate_11_SafeModeVBUS",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 1),
+ .end = AB5500_IRQ(21, 2),
+ },
+ {
+ .name = "CHGstate_12_comletedVBUS",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 2),
+ .end = AB5500_IRQ(21, 2),
+ },
+ {
+ .name = "CHGstate_13_completedVBUS",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 3),
+ .end = AB5500_IRQ(21, 3),
+ },
+ {
+ .name = "CHGstate_14_FullChgDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 4),
+ .end = AB5500_IRQ(21, 4),
+ },
+ {
+ .name = "CHGstate_15_SafeModeDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 5),
+ .end = AB5500_IRQ(21, 5),
+ },
+ {
+ .name = "CHGstate_16_OFFsuspendDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 6),
+ .end = AB5500_IRQ(21, 6),
+ },
+ {
+ .name = "CHGstate_17_completedDCIO",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(21, 7),
+ .end = AB5500_IRQ(21, 7),
+ },
+ },
+ },
+ [AB5500_DEVID_OTP] = {
+ .name = "ab5500-otp",
+ .id = AB5500_DEVID_OTP,
+ },
+ [AB5500_DEVID_VIDEO] = {
+ .name = "ab5500-video",
+ .id = AB5500_DEVID_VIDEO,
+ .num_resources = 1,
+ .resources = (struct resource[]) {
+ {
+ .name = "plugTVdet",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(22, 2),
+ .end = AB5500_IRQ(22, 2),
+ },
+ },
+ },
+ [AB5500_DEVID_DBIECI] = {
+ .name = "ab5500-dbieci",
+ .id = AB5500_DEVID_DBIECI,
+ .num_resources = 10,
+ .resources = (struct resource[]) {
+ {
+ .name = "COLL",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 0),
+ .end = AB5500_IRQ(14, 0),
+ },
+ {
+ .name = "RESERR",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 1),
+ .end = AB5500_IRQ(14, 1),
+ },
+ {
+ .name = "FRAERR",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 2),
+ .end = AB5500_IRQ(14, 2),
+ },
+ {
+ .name = "COMERR",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 3),
+ .end = AB5500_IRQ(14, 3),
+ },
+ {
+ .name = "BSI_indicator",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 4),
+ .end = AB5500_IRQ(14, 4),
+ },
+ {
+ .name = "SPDSET",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 6),
+ .end = AB5500_IRQ(14, 6),
+ },
+ {
+ .name = "DSENT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(14, 7),
+ .end = AB5500_IRQ(14, 7),
+ },
+ {
+ .name = "DREC",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 0),
+ .end = AB5500_IRQ(15, 0),
+ },
+ {
+ .name = "ACCINT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 1),
+ .end = AB5500_IRQ(15, 1),
+ },
+ {
+ .name = "NOPINT",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(15, 2),
+ .end = AB5500_IRQ(15, 2),
+ },
+ },
+ },
+ [AB5500_DEVID_ONSWA] = {
+ .name = "ab5500-onswa",
+ .id = AB5500_DEVID_ONSWA,
+ .num_resources = 2,
+ .resources = (struct resource[]) {
+ {
+ .name = "ONSWAn_rising",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(1, 3),
+ .end = AB5500_IRQ(1, 3),
+ },
+ {
+ .name = "ONSWAn_falling",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(1, 4),
+ .end = AB5500_IRQ(1, 4),
+ },
+ },
+ },
+};
+
+/*
+ * This stubbed prcmu functionality should be removed when the prcmu driver
+ * implements it.
+ */
+static u8 prcmu_event_buf[AB5500_NUM_EVENT_REG];
+
+void prcmu_get_abb_event_buf(u8 **buf)
+{
+ *buf = prcmu_event_buf;
+}
+
+/*
+ * Functionality for getting/setting register values.
+ */
+static int get_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg,
+ u8 *value)
+{
+ int err;
+
+ if (bank >= AB5500_NUM_BANKS)
+ return -EINVAL;
+
+ err = mutex_lock_interruptible(&ab->access_mutex);
+ if (err)
+ return err;
+ err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, reg, value, 1);
+
+ mutex_unlock(&ab->access_mutex);
+ return err;
+}
+
+static int get_register_page_interruptible(struct ab5500 *ab, u8 bank,
+ u8 first_reg, u8 *regvals, u8 numregs)
+{
+ int err;
+
+ if (bank >= AB5500_NUM_BANKS)
+ return -EINVAL;
+
+ err = mutex_lock_interruptible(&ab->access_mutex);
+ if (err)
+ return err;
+
+ while (numregs) {
+ /* The hardware limit for get page is 4 */
+ u8 curnum = min_t(u8, numregs, 4u);
+
+ err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr,
+ first_reg, regvals, curnum);
+ if (err)
+ goto out;
+
+ numregs -= curnum;
+ first_reg += curnum;
+ regvals += curnum;
+ }
+
+out:
+ mutex_unlock(&ab->access_mutex);
+ return err;
+}
+
+static int mask_and_set_register_interruptible(struct ab5500 *ab, u8 bank,
+ u8 reg, u8 bitmask, u8 bitvalues)
+{
+ int err = 0;
+
+ if (bank >= AB5500_NUM_BANKS)
+ return -EINVAL;
+
+ if (bitmask) {
+ u8 buf;
+
+ err = mutex_lock_interruptible(&ab->access_mutex);
+ if (err)
+ return err;
+
+ if (bitmask == 0xFF) /* No need to read in this case. */
+ buf = bitvalues;
+ else { /* Read and modify the register value. */
+ err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr,
+ reg, &buf, 1);
+ if (err)
+ return err;
+
+ buf = ((~bitmask & buf) | (bitmask & bitvalues));
+ }
+ /* Write the new value. */
+ err = db5500_prcmu_abb_write(bankinfo[bank].slave_addr, reg,
+ &buf, 1);
+
+ mutex_unlock(&ab->access_mutex);
+ }
+ return err;
+}
+
+static int
+set_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, u8 value)
+{
+ return mask_and_set_register_interruptible(ab, bank, reg, 0xff, value);
+}
+
+/*
+ * Read/write permission checking functions.
+ */
+static const struct ab5500_i2c_ranges *get_bankref(u8 devid, u8 bank)
+{
+ u8 i;
+
+ if (devid < AB5500_NUM_DEVICES) {
+ for (i = 0; i < ab5500_bank_ranges[devid].nbanks; i++) {
+ if (ab5500_bank_ranges[devid].bank[i].bankid == bank)
+ return &ab5500_bank_ranges[devid].bank[i];
+ }
+ }
+ return NULL;
+}
+
+static bool page_write_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg)
+{
+ u8 i; /* range loop index */
+ const struct ab5500_i2c_ranges *bankref;
+
+ bankref = get_bankref(devid, bank);
+ if (bankref == NULL || last_reg < first_reg)
+ return false;
+
+ for (i = 0; i < bankref->nranges; i++) {
+ if (first_reg < bankref->range[i].first)
+ break;
+ if ((last_reg <= bankref->range[i].last) &&
+ (bankref->range[i].perm & AB5500_PERM_WR))
+ return true;
+ }
+ return false;
+}
+
+static bool reg_write_allowed(u8 devid, u8 bank, u8 reg)
+{
+ return page_write_allowed(devid, bank, reg, reg);
+}
+
+static bool page_read_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg)
+{
+ u8 i;
+ const struct ab5500_i2c_ranges *bankref;
+
+ bankref = get_bankref(devid, bank);
+ if (bankref == NULL || last_reg < first_reg)
+ return false;
+
+
+ /* Find the range (if it exists in the list) that includes first_reg. */
+ for (i = 0; i < bankref->nranges; i++) {
+ if (first_reg < bankref->range[i].first)
+ return false;
+ if (first_reg <= bankref->range[i].last)
+ break;
+ }
+ /* Make sure that the entire range up to and including last_reg is
+ * readable. This may span several of the ranges in the list.
+ */
+ while ((i < bankref->nranges) &&
+ (bankref->range[i].perm & AB5500_PERM_RD)) {
+ if (last_reg <= bankref->range[i].last)
+ return true;
+ if ((++i >= bankref->nranges) ||
+ (bankref->range[i].first !=
+ (bankref->range[i - 1].last + 1))) {
+ break;
+ }
+ }
+ return false;
+}
+
+static bool reg_read_allowed(u8 devid, u8 bank, u8 reg)
+{
+ return page_read_allowed(devid, bank, reg, reg);
+}
+
+
+/*
+ * The exported register access functionality.
+ */
+static int ab5500_get_chip_id(struct device *dev)
+{
+ struct ab5500 *ab = dev_get_drvdata(dev->parent);
+
+ return (int)ab->chip_id;
+}
+
+static int ab5500_mask_and_set_register_interruptible(struct device *dev,
+ u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
+{
+ struct ab5500 *ab;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if ((AB5500_NUM_BANKS <= bank) ||
+ !reg_write_allowed(pdev->id, bank, reg))
+ return -EINVAL;
+
+ ab = dev_get_drvdata(dev->parent);
+ return mask_and_set_register_interruptible(ab, bank, reg,
+ bitmask, bitvalues);
+}
+
+static int ab5500_set_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 value)
+{
+ return ab5500_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
+ value);
+}
+
+static int ab5500_get_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 *value)
+{
+ struct ab5500 *ab;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if ((AB5500_NUM_BANKS <= bank) ||
+ !reg_read_allowed(pdev->id, bank, reg))
+ return -EINVAL;
+
+ ab = dev_get_drvdata(dev->parent);
+ return get_register_interruptible(ab, bank, reg, value);
+}
+
+static int ab5500_get_register_page_interruptible(struct device *dev, u8 bank,
+ u8 first_reg, u8 *regvals, u8 numregs)
+{
+ struct ab5500 *ab;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if ((AB5500_NUM_BANKS <= bank) ||
+ !page_read_allowed(pdev->id, bank,
+ first_reg, (first_reg + numregs - 1)))
+ return -EINVAL;
+
+ ab = dev_get_drvdata(dev->parent);
+ return get_register_page_interruptible(ab, bank, first_reg, regvals,
+ numregs);
+}
+
+static int
+ab5500_event_registers_startup_state_get(struct device *dev, u8 *event)
+{
+ struct ab5500 *ab;
+
+ ab = dev_get_drvdata(dev->parent);
+ if (!ab->startup_events_read)
+ return -EAGAIN; /* Try again later */
+
+ memcpy(event, ab->startup_events, AB5500_NUM_EVENT_REG);
+ return 0;
+}
+
+static int ab5500_startup_irq_enabled(struct device *dev, unsigned int irq)
+{
+ struct ab5500 *ab;
+ bool val;
+
+ ab = get_irq_chip_data(irq);
+ irq -= ab->irq_base;
+ val = ((ab->startup_events[irq / 8] & BIT(irq % 8)) != 0);
+
+ return val;
+}
+
+static struct abx500_ops ab5500_ops = {
+ .get_chip_id = ab5500_get_chip_id,
+ .get_register = ab5500_get_register_interruptible,
+ .set_register = ab5500_set_register_interruptible,
+ .get_register_page = ab5500_get_register_page_interruptible,
+ .set_register_page = NULL,
+ .mask_and_set_register = ab5500_mask_and_set_register_interruptible,
+ .event_registers_startup_state_get =
+ ab5500_event_registers_startup_state_get,
+ .startup_irq_enabled = ab5500_startup_irq_enabled,
+};
+
+static irqreturn_t ab5500_irq(int irq, void *data)
+{
+ struct ab5500 *ab = data;
+ u8 i;
+
+ /*
+ * TODO: use the ITMASTER registers to reduce the number of i2c reads.
+ */
+
+ for (i = 0; i < AB5500_NUM_EVENT_REG; i++) {
+ int status;
+ u8 value;
+
+ status = get_register_interruptible(ab, AB5500_BANK_IT,
+ AB5500_IT_LATCH0_REG + i, &value);
+ if (status < 0 || value == 0)
+ continue;
+
+ do {
+ int bit = __ffs(value);
+ int line = i * 8 + bit;
+
+ handle_nested_irq(ab->irq_base + line);
+ value &= ~(1 << bit);
+ } while (value);
+ }
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = {
+ [AB5500_BANK_LED] = {
+ .bankid = AB5500_BANK_LED,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_ADC] = {
+ .bankid = AB5500_BANK_ADC,
+ .nranges = 6,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x1F,
+ .last = 0x22,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x23,
+ .last = 0x24,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x26,
+ .last = 0x2D,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x34,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x37,
+ .last = 0x57,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x58,
+ .last = 0x58,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ [AB5500_BANK_RTC] = {
+ .bankid = AB5500_BANK_RTC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x04,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x06,
+ .last = 0x0C,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_STARTUP] = {
+ .bankid = AB5500_BANK_STARTUP,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1F,
+ .last = 0x1F,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x2E,
+ .last = 0x2E,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x2F,
+ .last = 0x30,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x51,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x61,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x66,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8C,
+ .last = 0x96,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xAA,
+ .last = 0xB4,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xB7,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xCA,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xD3,
+ .last = 0xE0,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_DBI_ECI] = {
+ .bankid = AB5500_BANK_DBI_ECI,
+ .nranges = 3,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x07,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x10,
+ .last = 0x10,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x13,
+ .last = 0x13,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_CHG] = {
+ .bankid = AB5500_BANK_CHG,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x11,
+ .last = 0x11,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x12,
+ .last = 0x1B,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_FG_BATTCOM_ACC] = {
+ .bankid = AB5500_BANK_FG_BATTCOM_ACC,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0C,
+ .last = 0x10,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_USB] = {
+ .bankid = AB5500_BANK_USB,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x83,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x87,
+ .last = 0x8A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x8B,
+ .last = 0x8B,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x91,
+ .last = 0x92,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x93,
+ .last = 0x93,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x94,
+ .last = 0x94,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xA8,
+ .last = 0xB0,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB2,
+ .last = 0xB2,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xB4,
+ .last = 0xBC,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xBF,
+ .last = 0xBF,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0xC1,
+ .last = 0xC5,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ [AB5500_BANK_IT] = {
+ .bankid = AB5500_BANK_IT,
+ .nranges = 4,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x02,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x20,
+ .last = 0x36,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x40,
+ .last = 0x56,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x60,
+ .last = 0x76,
+ .perm = AB5500_PERM_RO,
+ },
+ },
+ },
+ [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = {
+ .bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST,
+ .nranges = 7,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x02,
+ .last = 0x02,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x12,
+ .last = 0x12,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x30,
+ .last = 0x34,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x40,
+ .last = 0x44,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x54,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x64,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x70,
+ .last = 0x74,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = {
+ .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ .nranges = 13,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x01,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x02,
+ .last = 0x02,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0D,
+ .last = 0x0F,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1C,
+ .last = 0x1C,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1E,
+ .last = 0x1E,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x20,
+ .last = 0x21,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x25,
+ .last = 0x25,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x28,
+ .last = 0x2A,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x30,
+ .last = 0x33,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x40,
+ .last = 0x43,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x50,
+ .last = 0x53,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x60,
+ .last = 0x63,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x70,
+ .last = 0x73,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_VIBRA] = {
+ .bankid = AB5500_BANK_VIBRA,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x10,
+ .last = 0x13,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xFE,
+ .last = 0xFE,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_AUDIO_HEADSETUSB] = {
+ .bankid = AB5500_BANK_AUDIO_HEADSETUSB,
+ .nranges = 2,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x48,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0xEB,
+ .last = 0xFB,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_SIM_USBSIM] = {
+ .bankid = AB5500_BANK_SIM_USBSIM,
+ .nranges = 1,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x13,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+ [AB5500_BANK_VDENC] = {
+ .bankid = AB5500_BANK_VDENC,
+ .nranges = 12,
+ .range = (struct ab5500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x08,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x09,
+ .last = 0x09,
+ .perm = AB5500_PERM_RO,
+ },
+ {
+ .first = 0x0A,
+ .last = 0x12,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x15,
+ .last = 0x19,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x1B,
+ .last = 0x21,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x27,
+ .last = 0x2C,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x41,
+ .last = 0x41,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x45,
+ .last = 0x5B,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x5D,
+ .last = 0x5D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x69,
+ .last = 0x69,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x6C,
+ .last = 0x6D,
+ .perm = AB5500_PERM_RW,
+ },
+ {
+ .first = 0x80,
+ .last = 0x81,
+ .perm = AB5500_PERM_RW,
+ },
+ },
+ },
+};
+static int ab5500_registers_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+ unsigned int i;
+ u8 bank = (u8)ab->debug_bank;
+
+ seq_printf(s, AB5500_NAME_STRING " register values:\n");
+ for (bank = 0; bank < AB5500_NUM_BANKS; bank++) {
+ seq_printf(s, " bank %u, %s (0x%x):\n", bank,
+ bankinfo[bank].name,
+ bankinfo[bank].slave_addr);
+ for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) {
+ u8 reg;
+ int err;
+
+ for (reg = ab5500_reg_ranges[bank].range[i].first;
+ reg <= ab5500_reg_ranges[bank].range[i].last;
+ reg++) {
+ u8 value;
+
+ err = get_register_interruptible(ab, bank, reg,
+ &value);
+ if (err < 0) {
+ dev_err(ab->dev, "get_reg failed %d"
+ "bank 0x%x reg 0x%x\n",
+ err, bank, reg);
+ return err;
+ }
+
+ err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n",
+ bank, reg, value);
+ if (err < 0) {
+ dev_err(ab->dev,
+ "seq_printf overflow\n");
+ /*
+ * Error is not returned here since
+ * the output is wanted in any case
+ */
+ return 0;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int ab5500_registers_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_registers_print, inode->i_private);
+}
+
+static const struct file_operations ab5500_registers_fops = {
+ .open = ab5500_registers_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab5500_bank_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+
+ seq_printf(s, "%d\n", ab->debug_bank);
+ return 0;
+}
+
+static int ab5500_bank_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_bank_print, inode->i_private);
+}
+
+static ssize_t ab5500_bank_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_bank;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_bank);
+ if (err)
+ return -EINVAL;
+
+ if (user_bank >= AB5500_NUM_BANKS) {
+ dev_err(ab->dev,
+ "debugfs error input > number of banks\n");
+ return -EINVAL;
+ }
+
+ ab->debug_bank = user_bank;
+
+ return buf_size;
+}
+
+static int ab5500_address_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+
+ seq_printf(s, "0x%02X\n", ab->debug_address);
+ return 0;
+}
+
+static int ab5500_address_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_address_print, inode->i_private);
+}
+
+static ssize_t ab5500_address_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_address;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_address);
+ if (err)
+ return -EINVAL;
+ if (user_address > 0xff) {
+ dev_err(ab->dev,
+ "debugfs error input > 0xff\n");
+ return -EINVAL;
+ }
+ ab->debug_address = user_address;
+ return buf_size;
+}
+
+static int ab5500_val_print(struct seq_file *s, void *p)
+{
+ struct ab5500 *ab = s->private;
+ int err;
+ u8 regvalue;
+
+ err = get_register_interruptible(ab, (u8)ab->debug_bank,
+ (u8)ab->debug_address, &regvalue);
+ if (err) {
+ dev_err(ab->dev, "get_reg failed %d, bank 0x%x"
+ ", reg 0x%x\n", err, ab->debug_bank,
+ ab->debug_address);
+ return -EINVAL;
+ }
+ seq_printf(s, "0x%02X\n", regvalue);
+
+ return 0;
+}
+
+static int ab5500_val_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab5500_val_print, inode->i_private);
+}
+
+static ssize_t ab5500_val_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+ u8 regvalue;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+ if (user_val > 0xff) {
+ dev_err(ab->dev,
+ "debugfs error input > 0xff\n");
+ return -EINVAL;
+ }
+ err = mask_and_set_register_interruptible(
+ ab, (u8)ab->debug_bank,
+ (u8)ab->debug_address, 0xFF, (u8)user_val);
+ if (err)
+ return -EINVAL;
+
+ get_register_interruptible(ab, (u8)ab->debug_bank,
+ (u8)ab->debug_address, &regvalue);
+ if (err)
+ return -EINVAL;
+
+ return buf_size;
+}
+
+static const struct file_operations ab5500_bank_fops = {
+ .open = ab5500_bank_open,
+ .write = ab5500_bank_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab5500_address_fops = {
+ .open = ab5500_address_open,
+ .write = ab5500_address_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab5500_val_fops = {
+ .open = ab5500_val_open,
+ .write = ab5500_val_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static struct dentry *ab5500_dir;
+static struct dentry *ab5500_reg_file;
+static struct dentry *ab5500_bank_file;
+static struct dentry *ab5500_address_file;
+static struct dentry *ab5500_val_file;
+
+static inline void ab5500_setup_debugfs(struct ab5500 *ab)
+{
+ ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP;
+ ab->debug_address = AB5500_CHIP_ID;
+
+ ab5500_dir = debugfs_create_dir(AB5500_NAME_STRING, NULL);
+ if (!ab5500_dir)
+ goto exit_no_debugfs;
+
+ ab5500_reg_file = debugfs_create_file("all-bank-registers",
+ S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops);
+ if (!ab5500_reg_file)
+ goto exit_destroy_dir;
+
+ ab5500_bank_file = debugfs_create_file("register-bank",
+ (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops);
+ if (!ab5500_bank_file)
+ goto exit_destroy_reg;
+
+ ab5500_address_file = debugfs_create_file("register-address",
+ (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops);
+ if (!ab5500_address_file)
+ goto exit_destroy_bank;
+
+ ab5500_val_file = debugfs_create_file("register-value",
+ (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops);
+ if (!ab5500_val_file)
+ goto exit_destroy_address;
+
+ return;
+
+exit_destroy_address:
+ debugfs_remove(ab5500_address_file);
+exit_destroy_bank:
+ debugfs_remove(ab5500_bank_file);
+exit_destroy_reg:
+ debugfs_remove(ab5500_reg_file);
+exit_destroy_dir:
+ debugfs_remove(ab5500_dir);
+exit_no_debugfs:
+ dev_err(ab->dev, "failed to create debugfs entries.\n");
+ return;
+}
+
+static inline void ab5500_remove_debugfs(void)
+{
+ debugfs_remove(ab5500_val_file);
+ debugfs_remove(ab5500_address_file);
+ debugfs_remove(ab5500_bank_file);
+ debugfs_remove(ab5500_reg_file);
+ debugfs_remove(ab5500_dir);
+}
+
+#else /* !CONFIG_DEBUG_FS */
+static inline void ab5500_setup_debugfs(struct ab5500 *ab)
+{
+}
+static inline void ab5500_remove_debugfs(void)
+{
+}
+#endif
+
+/*
+ * ab5500_setup : Basic set-up, datastructure creation/destruction
+ * and I2C interface.This sets up a default config
+ * in the AB5500 chip so that it will work as expected.
+ * @ab : Pointer to ab5500 structure
+ * @settings : Pointer to struct abx500_init_settings
+ * @size : Size of init data
+ */
+static int __init ab5500_setup(struct ab5500 *ab,
+ struct abx500_init_settings *settings, unsigned int size)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ err = mask_and_set_register_interruptible(ab,
+ settings[i].bank,
+ settings[i].reg,
+ 0xFF, settings[i].setting);
+ if (err)
+ goto exit_no_setup;
+
+ /* If event mask register update the event mask in ab5500 */
+ if ((settings[i].bank == AB5500_BANK_IT) &&
+ (AB5500_MASK_BASE <= settings[i].reg) &&
+ (settings[i].reg <= AB5500_MASK_END)) {
+ ab->mask[settings[i].reg - AB5500_MASK_BASE] =
+ settings[i].setting;
+ }
+ }
+exit_no_setup:
+ return err;
+}
+
+static void ab5500_irq_mask(unsigned int irq)
+{
+ struct ab5500 *ab = get_irq_chip_data(irq);
+ int offset = irq - ab->irq_base;
+ int index = offset / 8;
+ int mask = BIT(offset % 8);
+
+ ab->mask[index] |= mask;
+}
+
+static void ab5500_irq_unmask(unsigned int irq)
+{
+ struct ab5500 *ab = get_irq_chip_data(irq);
+ int offset = irq - ab->irq_base;
+ int index = offset / 8;
+ int mask = BIT(offset % 8);
+
+ ab->mask[index] &= ~mask;
+}
+
+static void ab5500_irq_lock(unsigned int irq)
+{
+ struct ab5500 *ab = get_irq_chip_data(irq);
+
+ mutex_lock(&ab->irq_lock);
+}
+
+static void ab5500_irq_sync_unlock(unsigned int irq)
+{
+ struct ab5500 *ab = get_irq_chip_data(irq);
+ int i;
+
+ for (i = 0; i < AB5500_NUM_IRQ_REGS; i++) {
+ u8 old = ab->oldmask[i];
+ u8 new = ab->mask[i];
+ int reg;
+
+ if (new == old)
+ continue;
+
+ ab->oldmask[i] = new;
+
+ reg = AB5500_IT_MASK0_REG + i;
+ set_register_interruptible(ab, AB5500_BANK_IT, reg, new);
+ }
+
+ mutex_unlock(&ab->irq_lock);
+}
+
+static struct irq_chip ab5500_irq_chip = {
+ .name = "ab5500",
+ .mask = ab5500_irq_mask,
+ .unmask = ab5500_irq_unmask,
+ .bus_lock = ab5500_irq_lock,
+ .bus_sync_unlock = ab5500_irq_sync_unlock,
+};
+
+struct ab_family_id {
+ u8 id;
+ char *name;
+};
+
+static const struct ab_family_id ids[] __initdata = {
+ /* AB5500 */
+ {
+ .id = AB5500_1_0,
+ .name = "1.0"
+ },
+ {
+ .id = AB5500_1_1,
+ .name = "1.1"
+ },
+ /* Terminator */
+ {
+ .id = 0x00,
+ }
+};
+
+static int ab5500_irq_init(struct ab5500 *ab)
+{
+ struct ab5500_platform_data *ab5500_plf_data =
+ dev_get_platdata(ab->dev);
+ int i;
+ unsigned int irq;
+
+ for (i = 0; i < ab5500_plf_data->irq.count; i++) {
+
+ irq = ab5500_plf_data->irq.base + i;
+ set_irq_chip_data(irq, ab);
+ set_irq_chip_and_handler(irq, &ab5500_irq_chip,
+ handle_simple_irq);
+ set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+ return 0;
+}
+
+static void ab5500_irq_remove(struct ab5500 *ab)
+{
+ struct ab5500_platform_data *ab5500_plf_data =
+ dev_get_platdata(ab->dev);
+ int i;
+ unsigned int irq;
+
+ for (i = 0; i < ab5500_plf_data->irq.count; i++) {
+ irq = ab5500_plf_data->irq.base + i;
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ set_irq_chip_and_handler(irq, NULL, NULL);
+ set_irq_chip_data(irq, NULL);
+ }
+}
+
+static int __init ab5500_probe(struct platform_device *pdev)
+{
+ struct ab5500 *ab;
+ struct ab5500_platform_data *ab5500_plf_data =
+ pdev->dev.platform_data;
+ struct resource *res;
+ int err;
+ int i;
+
+ ab = kzalloc(sizeof(struct ab5500), GFP_KERNEL);
+ if (!ab) {
+ dev_err(&pdev->dev,
+ "could not allocate " AB5500_NAME_STRING " device\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize data structure */
+ mutex_init(&ab->access_mutex);
+ mutex_init(&ab->irq_lock);
+ ab->dev = &pdev->dev;
+ ab->irq_base = ab5500_plf_data->irq.base;
+
+ platform_set_drvdata(pdev, ab);
+
+ /* Read chip ID register */
+ err = get_register_interruptible(ab, AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ AB5500_CHIP_ID, &ab->chip_id);
+ if (err) {
+ dev_err(&pdev->dev, "could not communicate with the analog "
+ "baseband chip\n");
+ goto exit_no_detect;
+ }
+
+ for (i = 0; ids[i].id != 0x0; i++) {
+ if (ids[i].id == ab->chip_id) {
+ snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1,
+ AB5500_ID_FORMAT_STRING, ids[i].name);
+ break;
+ }
+ }
+
+ if (ids[i].id == 0x0) {
+ dev_err(&pdev->dev, "unknown analog baseband chip id: 0x%x\n",
+ ab->chip_id);
+ dev_err(&pdev->dev, "driver not started!\n");
+ goto exit_no_detect;
+ }
+
+ dev_info(&pdev->dev, "detected AB chip: %s\n", &ab->chip_name[0]);
+
+ /* Readout ab->starup_events when prcmu driver is in place */
+ ab->startup_events[0] = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "ab5500_platform_get_resource error\n");
+ goto exit_no_detect;
+ }
+ ab->ab5500_irq = res->start;
+
+ /* Clear and mask all interrupts */
+ for (i = 0; i < AB5500_NUM_IRQ_REGS; i++) {
+ u8 latchreg = AB5500_IT_LATCH0_REG + i;
+ u8 maskreg = AB5500_IT_MASK0_REG + i;
+ u8 val;
+
+ get_register_interruptible(ab, AB5500_BANK_IT, latchreg, &val);
+ set_register_interruptible(ab, AB5500_BANK_IT, maskreg, 0xff);
+ ab->mask[i] = ab->oldmask[i] = 0xff;
+ }
+
+ if (ab->irq_base) {
+ err = ab5500_irq_init(ab);
+ if (err)
+ return err;
+
+ err = request_threaded_irq(res->start, NULL, ab5500_irq,
+ IRQF_NO_SUSPEND | IRQF_ONESHOT,
+ "ab5500-core", ab);
+ if (err)
+ goto exit_remove_irq;
+
+ }
+
+ /* This real unpredictable IRQ is of course sampled for entropy */
+ rand_initialize_irq(res->start);
+
+ err = abx500_register_ops(&pdev->dev, &ab5500_ops);
+ if (err) {
+ dev_err(&pdev->dev, "ab5500_register ops error\n");
+ goto exit_no_irq;
+ }
+
+ /* Set up and register the platform devices. */
+ for (i = 0; i < AB5500_NUM_DEVICES; i++) {
+ ab5500_devs[i].platform_data = ab5500_plf_data->dev_data[i];
+ ab5500_devs[i].data_size = ab5500_plf_data->dev_data_sz[i];
+ }
+
+ err = mfd_add_devices(&pdev->dev, 0, ab5500_devs,
+ ARRAY_SIZE(ab5500_devs), NULL,
+ ab5500_plf_data->irq.base);
+ if (err) {
+ dev_err(&pdev->dev, "ab5500_mfd_add_device error\n");
+ goto exit_no_irq;
+ }
+ err = ab5500_setup(ab, ab5500_plf_data->init_settings,
+ ab5500_plf_data->init_settings_sz);
+ if (err) {
+ dev_err(&pdev->dev, "ab5500_setup error\n");
+ goto exit_no_irq;
+ }
+ ab5500_setup_debugfs(ab);
+ return 0;
+
+exit_no_irq:
+ if (ab->irq_base) {
+ free_irq(ab->ab5500_irq, ab);
+exit_remove_irq:
+ ab5500_irq_remove(ab);
+ }
+exit_no_detect:
+ kfree(ab);
+ return err;
+}
+
+static int __exit ab5500_remove(struct platform_device *pdev)
+{
+ struct ab5500 *ab = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ /*
+ * At this point, all subscribers should have unregistered
+ * their notifiers so deactivate IRQ
+ */
+ ab5500_remove_debugfs();
+ mfd_remove_devices(&pdev->dev);
+ if (ab->irq_base) {
+ free_irq(ab->ab5500_irq, ab);
+ ab5500_irq_remove(ab);
+ }
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ kfree(ab);
+ return 0;
+}
+
+static struct platform_driver ab5500_driver = {
+ .driver = {
+ .name = "ab5500-core",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(ab5500_remove),
+};
+
+static int __init ab5500_core_init(void)
+{
+ return platform_driver_probe(&ab5500_driver, ab5500_probe);
+}
+
+static void __exit ab5500_core_exit(void)
+{
+ platform_driver_unregister(&ab5500_driver);
+}
+
+subsys_initcall(ab5500_core_init);
+module_exit(ab5500_core_exit);
+
+MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
+MODULE_DESCRIPTION("AB5500 core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/ab5500-power.c b/drivers/mfd/ab5500-power.c
new file mode 100644
index 00000000000..5bf6a4c5e97
--- /dev/null
+++ b/drivers/mfd/ab5500-power.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+
+static struct device *dev;
+
+#define AB5500_SYSPOR_CONTROL 0x30
+
+static void ab5500_power_off(void)
+{
+ sigset_t old;
+ sigset_t all;
+
+ sigfillset(&all);
+
+ if (!sigprocmask(SIG_BLOCK, &all, &old)) {
+ /* Clear dbb_on */
+ int ret = abx500_set(dev, AB5500_BANK_STARTUP,
+ AB5500_SYSPOR_CONTROL, 0);
+ WARN_ON(ret);
+ }
+}
+
+static int __devinit ab5500_power_probe(struct platform_device *pdev)
+{
+ struct ab5500_platform_data *plat = dev_get_platdata(pdev->dev.parent);
+
+ if (!plat->pm_power_off)
+ return -ENODEV;
+
+ dev = &pdev->dev;
+ pm_power_off = ab5500_power_off;
+
+ return 0;
+}
+
+static int __devexit ab5500_power_remove(struct platform_device *pdev)
+{
+ pm_power_off = NULL;
+ dev = NULL;
+
+ return 0;
+}
+
+static struct platform_driver ab5500_power_driver = {
+ .driver = {
+ .name = "ab5500-power",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab5500_power_probe,
+ .remove = __devexit_p(ab5500_power_remove),
+};
+
+static int __init ab8500_sysctrl_init(void)
+{
+ return platform_driver_register(&ab5500_power_driver);
+}
+
+subsys_initcall(ab8500_sysctrl_init);
+
+MODULE_DESCRIPTION("AB5500 power driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index f3d26fa9c34..e940c2e3f06 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -4,88 +4,83 @@
* License Terms: GNU General Public License v2
* Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
* Author: Rabin Vincent <rabin.vincent@stericsson.com>
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/irq.h>
-#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
+#include <linux/mfd/abx500.h>
#include <linux/mfd/ab8500.h>
+#include <linux/regulator/ab8500.h>
/*
* Interrupt register offsets
* Bank : 0x0E
*/
-#define AB8500_IT_SOURCE1_REG 0x0E00
-#define AB8500_IT_SOURCE2_REG 0x0E01
-#define AB8500_IT_SOURCE3_REG 0x0E02
-#define AB8500_IT_SOURCE4_REG 0x0E03
-#define AB8500_IT_SOURCE5_REG 0x0E04
-#define AB8500_IT_SOURCE6_REG 0x0E05
-#define AB8500_IT_SOURCE7_REG 0x0E06
-#define AB8500_IT_SOURCE8_REG 0x0E07
-#define AB8500_IT_SOURCE19_REG 0x0E12
-#define AB8500_IT_SOURCE20_REG 0x0E13
-#define AB8500_IT_SOURCE21_REG 0x0E14
-#define AB8500_IT_SOURCE22_REG 0x0E15
-#define AB8500_IT_SOURCE23_REG 0x0E16
-#define AB8500_IT_SOURCE24_REG 0x0E17
+#define AB8500_IT_SOURCE1_REG 0x00
+#define AB8500_IT_SOURCE2_REG 0x01
+#define AB8500_IT_SOURCE3_REG 0x02
+#define AB8500_IT_SOURCE4_REG 0x03
+#define AB8500_IT_SOURCE5_REG 0x04
+#define AB8500_IT_SOURCE6_REG 0x05
+#define AB8500_IT_SOURCE7_REG 0x06
+#define AB8500_IT_SOURCE8_REG 0x07
+#define AB8500_IT_SOURCE19_REG 0x12
+#define AB8500_IT_SOURCE20_REG 0x13
+#define AB8500_IT_SOURCE21_REG 0x14
+#define AB8500_IT_SOURCE22_REG 0x15
+#define AB8500_IT_SOURCE23_REG 0x16
+#define AB8500_IT_SOURCE24_REG 0x17
/*
* latch registers
*/
-#define AB8500_IT_LATCH1_REG 0x0E20
-#define AB8500_IT_LATCH2_REG 0x0E21
-#define AB8500_IT_LATCH3_REG 0x0E22
-#define AB8500_IT_LATCH4_REG 0x0E23
-#define AB8500_IT_LATCH5_REG 0x0E24
-#define AB8500_IT_LATCH6_REG 0x0E25
-#define AB8500_IT_LATCH7_REG 0x0E26
-#define AB8500_IT_LATCH8_REG 0x0E27
-#define AB8500_IT_LATCH9_REG 0x0E28
-#define AB8500_IT_LATCH10_REG 0x0E29
-#define AB8500_IT_LATCH19_REG 0x0E32
-#define AB8500_IT_LATCH20_REG 0x0E33
-#define AB8500_IT_LATCH21_REG 0x0E34
-#define AB8500_IT_LATCH22_REG 0x0E35
-#define AB8500_IT_LATCH23_REG 0x0E36
-#define AB8500_IT_LATCH24_REG 0x0E37
+#define AB8500_IT_LATCH1_REG 0x20
+#define AB8500_IT_LATCH2_REG 0x21
+#define AB8500_IT_LATCH3_REG 0x22
+#define AB8500_IT_LATCH4_REG 0x23
+#define AB8500_IT_LATCH5_REG 0x24
+#define AB8500_IT_LATCH6_REG 0x25
+#define AB8500_IT_LATCH7_REG 0x26
+#define AB8500_IT_LATCH8_REG 0x27
+#define AB8500_IT_LATCH9_REG 0x28
+#define AB8500_IT_LATCH10_REG 0x29
+#define AB8500_IT_LATCH12_REG 0x2B
+#define AB8500_IT_LATCH19_REG 0x32
+#define AB8500_IT_LATCH20_REG 0x33
+#define AB8500_IT_LATCH21_REG 0x34
+#define AB8500_IT_LATCH22_REG 0x35
+#define AB8500_IT_LATCH23_REG 0x36
+#define AB8500_IT_LATCH24_REG 0x37
/*
* mask registers
*/
-#define AB8500_IT_MASK1_REG 0x0E40
-#define AB8500_IT_MASK2_REG 0x0E41
-#define AB8500_IT_MASK3_REG 0x0E42
-#define AB8500_IT_MASK4_REG 0x0E43
-#define AB8500_IT_MASK5_REG 0x0E44
-#define AB8500_IT_MASK6_REG 0x0E45
-#define AB8500_IT_MASK7_REG 0x0E46
-#define AB8500_IT_MASK8_REG 0x0E47
-#define AB8500_IT_MASK9_REG 0x0E48
-#define AB8500_IT_MASK10_REG 0x0E49
-#define AB8500_IT_MASK11_REG 0x0E4A
-#define AB8500_IT_MASK12_REG 0x0E4B
-#define AB8500_IT_MASK13_REG 0x0E4C
-#define AB8500_IT_MASK14_REG 0x0E4D
-#define AB8500_IT_MASK15_REG 0x0E4E
-#define AB8500_IT_MASK16_REG 0x0E4F
-#define AB8500_IT_MASK17_REG 0x0E50
-#define AB8500_IT_MASK18_REG 0x0E51
-#define AB8500_IT_MASK19_REG 0x0E52
-#define AB8500_IT_MASK20_REG 0x0E53
-#define AB8500_IT_MASK21_REG 0x0E54
-#define AB8500_IT_MASK22_REG 0x0E55
-#define AB8500_IT_MASK23_REG 0x0E56
-#define AB8500_IT_MASK24_REG 0x0E57
-
-#define AB8500_REV_REG 0x1080
+#define AB8500_IT_MASK1_REG 0x40
+#define AB8500_IT_MASK2_REG 0x41
+#define AB8500_IT_MASK3_REG 0x42
+#define AB8500_IT_MASK4_REG 0x43
+#define AB8500_IT_MASK5_REG 0x44
+#define AB8500_IT_MASK6_REG 0x45
+#define AB8500_IT_MASK7_REG 0x46
+#define AB8500_IT_MASK8_REG 0x47
+#define AB8500_IT_MASK9_REG 0x48
+#define AB8500_IT_MASK10_REG 0x49
+#define AB8500_IT_MASK12_REG 0x4B
+#define AB8500_IT_MASK19_REG 0x52
+#define AB8500_IT_MASK20_REG 0x53
+#define AB8500_IT_MASK21_REG 0x54
+#define AB8500_IT_MASK22_REG 0x55
+
+#define AB8500_REV_REG 0x80
+#define AB8500_SWITCH_OFF_STATUS 0x00
/*
* Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
@@ -95,99 +90,138 @@
* offset 0.
*/
static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
- 0, 1, 2, 3, 4, 6, 7, 8, 9, 18, 19, 20, 21,
+ 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21,
};
-static int __ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data)
+static int ab8500_get_chip_id(struct device *dev)
+{
+ struct ab8500 *ab8500;
+ if (!dev)
+ return -EINVAL;
+ ab8500 = dev_get_drvdata(dev->parent);
+ return ab8500 ? (int)ab8500->chip_id : -EINVAL;
+}
+
+static int set_register_interruptible(struct ab8500 *ab8500, u8 bank,
+ u8 reg, u8 data)
{
int ret;
+ /*
+ * Put the u8 bank and u8 register together into a an u16.
+ * The bank on higher 8 bits and register in lower 8 bits.
+ * */
+ u16 addr = ((u16)bank) << 8 | reg;
dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data);
+ ret = mutex_lock_interruptible(&ab8500->lock);
+ if (ret)
+ return ret;
+
ret = ab8500->write(ab8500, addr, data);
if (ret < 0)
dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
addr, ret);
+ mutex_unlock(&ab8500->lock);
return ret;
}
-/**
- * ab8500_write() - write an AB8500 register
- * @ab8500: device to write to
- * @addr: address of the register
- * @data: value to write
- */
-int ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data)
+static int ab8500_set_register(struct device *dev, u8 bank,
+ u8 reg, u8 value)
{
- int ret;
+ struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
- mutex_lock(&ab8500->lock);
- ret = __ab8500_write(ab8500, addr, data);
- mutex_unlock(&ab8500->lock);
-
- return ret;
+ return set_register_interruptible(ab8500, bank, reg, value);
}
-EXPORT_SYMBOL_GPL(ab8500_write);
-static int __ab8500_read(struct ab8500 *ab8500, u16 addr)
+static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
+ u8 reg, u8 *value)
{
int ret;
+ /* put the u8 bank and u8 reg together into a an u16.
+ * bank on higher 8 bits and reg in lower */
+ u16 addr = ((u16)bank) << 8 | reg;
+
+ ret = mutex_lock_interruptible(&ab8500->lock);
+ if (ret)
+ return ret;
ret = ab8500->read(ab8500, addr);
if (ret < 0)
dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
addr, ret);
+ else
+ *value = ret;
+ mutex_unlock(&ab8500->lock);
dev_vdbg(ab8500->dev, "rd: addr %#x => data %#x\n", addr, ret);
return ret;
}
-/**
- * ab8500_read() - read an AB8500 register
- * @ab8500: device to read from
- * @addr: address of the register
- */
-int ab8500_read(struct ab8500 *ab8500, u16 addr)
+static int ab8500_get_register(struct device *dev, u8 bank,
+ u8 reg, u8 *value)
{
- int ret;
-
- mutex_lock(&ab8500->lock);
- ret = __ab8500_read(ab8500, addr);
- mutex_unlock(&ab8500->lock);
+ struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
- return ret;
+ return get_register_interruptible(ab8500, bank, reg, value);
}
-EXPORT_SYMBOL_GPL(ab8500_read);
-
-/**
- * ab8500_set_bits() - set a bitfield in an AB8500 register
- * @ab8500: device to read from
- * @addr: address of the register
- * @mask: mask of the bitfield to modify
- * @data: value to set to the bitfield
- */
-int ab8500_set_bits(struct ab8500 *ab8500, u16 addr, u8 mask, u8 data)
+
+static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank,
+ u8 reg, u8 bitmask, u8 bitvalues)
{
int ret;
+ u8 data;
+ /* put the u8 bank and u8 reg together into a an u16.
+ * bank on higher 8 bits and reg in lower */
+ u16 addr = ((u16)bank) << 8 | reg;
- mutex_lock(&ab8500->lock);
+ ret = mutex_lock_interruptible(&ab8500->lock);
+ if (ret)
+ return ret;
- ret = __ab8500_read(ab8500, addr);
- if (ret < 0)
+ ret = ab8500->read(ab8500, addr);
+ if (ret < 0) {
+ dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
+ addr, ret);
goto out;
+ }
- ret &= ~mask;
- ret |= data;
+ data = (u8)ret;
+ data = (~bitmask & data) | (bitmask & bitvalues);
- ret = __ab8500_write(ab8500, addr, ret);
+ ret = ab8500->write(ab8500, addr, data);
+ if (ret < 0)
+ dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
+ addr, ret);
+ dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr, data);
out:
mutex_unlock(&ab8500->lock);
return ret;
}
-EXPORT_SYMBOL_GPL(ab8500_set_bits);
+
+static int ab8500_mask_and_set_register(struct device *dev,
+ u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
+
+ return mask_and_set_register_interruptible(ab8500, bank, reg,
+ bitmask, bitvalues);
+
+}
+
+static struct abx500_ops ab8500_ops = {
+ .get_chip_id = ab8500_get_chip_id,
+ .get_register = ab8500_get_register,
+ .set_register = ab8500_set_register,
+ .get_register_page = NULL,
+ .set_register_page = NULL,
+ .mask_and_set_register = ab8500_mask_and_set_register,
+ .event_registers_startup_state_get = NULL,
+ .startup_irq_enabled = NULL,
+};
static void ab8500_irq_lock(unsigned int irq)
{
@@ -209,12 +243,16 @@ static void ab8500_irq_sync_unlock(unsigned int irq)
if (new == old)
continue;
+ /* Interrupt register 12 doesn't exist prior to version 2.0 */
+ if (ab8500_irq_regoffset[i] == 11 &&
+ ab8500->chip_id < AB8500_CUT2P0)
+ continue;
+
ab8500->oldmask[i] = new;
reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i];
- ab8500_write(ab8500, reg, new);
+ set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new);
}
-
mutex_unlock(&ab8500->irq_lock);
}
@@ -243,6 +281,7 @@ static struct irq_chip ab8500_irq_chip = {
.bus_lock = ab8500_irq_lock,
.bus_sync_unlock = ab8500_irq_sync_unlock,
.mask = ab8500_irq_mask,
+ .disable = ab8500_irq_mask,
.unmask = ab8500_irq_unmask,
};
@@ -256,20 +295,25 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
int regoffset = ab8500_irq_regoffset[i];
int status;
+ u8 value;
+
+ /* Interrupt register 12 doesn't exist prior to version 2.0 */
+ if (regoffset == 11 && ab8500->chip_id < AB8500_CUT2P0)
+ continue;
- status = ab8500_read(ab8500, AB8500_IT_LATCH1_REG + regoffset);
- if (status <= 0)
+ status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
+ AB8500_IT_LATCH1_REG + regoffset, &value);
+ if (status < 0 || value == 0)
continue;
do {
- int bit = __ffs(status);
+ int bit = __ffs(value);
int line = i * 8 + bit;
handle_nested_irq(ab8500->irq_base + line);
- status &= ~(1 << bit);
- } while (status);
+ value &= ~(1 << bit);
+ } while (value);
}
-
return IRQ_HANDLED;
}
@@ -307,7 +351,16 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
}
}
-static struct resource ab8500_gpadc_resources[] = {
+static struct resource __devinitdata ab8500_gpio_resources[] = {
+ {
+ .name = "GPIO_INT6",
+ .start = AB8500_INT_GPIO6R,
+ .end = AB8500_INT_GPIO41F,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource __devinitdata ab8500_gpadc_resources[] = {
{
.name = "HW_CONV_END",
.start = AB8500_INT_GP_HW_ADC_CONV_END,
@@ -322,7 +375,7 @@ static struct resource ab8500_gpadc_resources[] = {
},
};
-static struct resource ab8500_rtc_resources[] = {
+static struct resource __devinitdata ab8500_rtc_resources[] = {
{
.name = "60S",
.start = AB8500_INT_RTC_60S,
@@ -337,7 +390,312 @@ static struct resource ab8500_rtc_resources[] = {
},
};
-static struct mfd_cell ab8500_devs[] = {
+static struct resource __devinitdata ab8500_poweronkey_db_resources[] = {
+ {
+ .name = "ONKEY_DBF",
+ .start = AB8500_INT_PON_KEY1DB_F,
+ .end = AB8500_INT_PON_KEY1DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ONKEY_DBR",
+ .start = AB8500_INT_PON_KEY1DB_R,
+ .end = AB8500_INT_PON_KEY1DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_av_acc_detect_resources[] = {
+ {
+ .name = "ACC_DETECT_1DB_F",
+ .start = AB8500_INT_ACC_DETECT_1DB_F,
+ .end = AB8500_INT_ACC_DETECT_1DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_1DB_R",
+ .start = AB8500_INT_ACC_DETECT_1DB_R,
+ .end = AB8500_INT_ACC_DETECT_1DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_21DB_F",
+ .start = AB8500_INT_ACC_DETECT_21DB_F,
+ .end = AB8500_INT_ACC_DETECT_21DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_21DB_R",
+ .start = AB8500_INT_ACC_DETECT_21DB_R,
+ .end = AB8500_INT_ACC_DETECT_21DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_22DB_F",
+ .start = AB8500_INT_ACC_DETECT_22DB_F,
+ .end = AB8500_INT_ACC_DETECT_22DB_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ACC_DETECT_22DB_R",
+ .start = AB8500_INT_ACC_DETECT_22DB_R,
+ .end = AB8500_INT_ACC_DETECT_22DB_R,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_charger_resources[] = {
+ {
+ .name = "MAIN_CH_UNPLUG_DET",
+ .start = AB8500_INT_MAIN_CH_UNPLUG_DET,
+ .end = AB8500_INT_MAIN_CH_UNPLUG_DET,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "MAIN_CHARGE_PLUG_DET",
+ .start = AB8500_INT_MAIN_CH_PLUG_DET,
+ .end = AB8500_INT_MAIN_CH_PLUG_DET,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "VBUS_DET_R",
+ .start = AB8500_INT_VBUS_DET_R,
+ .end = AB8500_INT_VBUS_DET_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "VBUS_DET_F",
+ .start = AB8500_INT_VBUS_DET_F,
+ .end = AB8500_INT_VBUS_DET_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_LINK_STATUS",
+ .start = AB8500_INT_USB_LINK_STATUS,
+ .end = AB8500_INT_USB_LINK_STATUS,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_CHARGE_DET_DONE",
+ .start = AB8500_INT_USB_CHG_DET_DONE,
+ .end = AB8500_INT_USB_CHG_DET_DONE,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "VBUS_OVV",
+ .start = AB8500_INT_VBUS_OVV,
+ .end = AB8500_INT_VBUS_OVV,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_CH_TH_PROT_R",
+ .start = AB8500_INT_USB_CH_TH_PROT_R,
+ .end = AB8500_INT_USB_CH_TH_PROT_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_CH_TH_PROT_F",
+ .start = AB8500_INT_USB_CH_TH_PROT_F,
+ .end = AB8500_INT_USB_CH_TH_PROT_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "MAIN_EXT_CH_NOT_OK",
+ .start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+ .end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "MAIN_CH_TH_PROT_R",
+ .start = AB8500_INT_MAIN_CH_TH_PROT_R,
+ .end = AB8500_INT_MAIN_CH_TH_PROT_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "MAIN_CH_TH_PROT_F",
+ .start = AB8500_INT_MAIN_CH_TH_PROT_F,
+ .end = AB8500_INT_MAIN_CH_TH_PROT_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_CHARGER_NOT_OKR",
+ .start = AB8500_INT_USB_CHARGER_NOT_OK,
+ .end = AB8500_INT_USB_CHARGER_NOT_OK,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_CHARGER_NOT_OKF",
+ .start = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .end = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "CH_WD_EXP",
+ .start = AB8500_INT_CH_WD_EXP,
+ .end = AB8500_INT_CH_WD_EXP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_btemp_resources[] = {
+ {
+ .name = "BAT_CTRL_INDB",
+ .start = AB8500_INT_BAT_CTRL_INDB,
+ .end = AB8500_INT_BAT_CTRL_INDB,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "BTEMP_LOW",
+ .start = AB8500_INT_BTEMP_LOW,
+ .end = AB8500_INT_BTEMP_LOW,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "BTEMP_HIGH",
+ .start = AB8500_INT_BTEMP_HIGH,
+ .end = AB8500_INT_BTEMP_HIGH,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "BTEMP_LOW_MEDIUM",
+ .start = AB8500_INT_BTEMP_LOW_MEDIUM,
+ .end = AB8500_INT_BTEMP_LOW_MEDIUM,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "BTEMP_MEDIUM_HIGH",
+ .start = AB8500_INT_BTEMP_MEDIUM_HIGH,
+ .end = AB8500_INT_BTEMP_MEDIUM_HIGH,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_fg_resources[] = {
+ {
+ .name = "NCONV_ACCU",
+ .start = AB8500_INT_CCN_CONV_ACC,
+ .end = AB8500_INT_CCN_CONV_ACC,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "BATT_OVV",
+ .start = AB8500_INT_BATT_OVV,
+ .end = AB8500_INT_BATT_OVV,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "LOW_BAT_F",
+ .start = AB8500_INT_LOW_BAT_F,
+ .end = AB8500_INT_LOW_BAT_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "LOW_BAT_R",
+ .start = AB8500_INT_LOW_BAT_R,
+ .end = AB8500_INT_LOW_BAT_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "CC_INT_CALIB",
+ .start = AB8500_INT_CC_INT_CALIB,
+ .end = AB8500_INT_CC_INT_CALIB,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_chargalg_resources[] = {};
+
+static struct resource __devinitdata ab8500_debug_resources[] = {
+ {
+ .name = "IRQ_FIRST",
+ .start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+ .end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "IRQ_LAST",
+ .start = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .end = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_usb_resources[] = {
+ {
+ .name = "ID_WAKEUP_R",
+ .start = AB8500_INT_ID_WAKEUP_R,
+ .end = AB8500_INT_ID_WAKEUP_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "ID_WAKEUP_F",
+ .start = AB8500_INT_ID_WAKEUP_F,
+ .end = AB8500_INT_ID_WAKEUP_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "VBUS_DET_F",
+ .start = AB8500_INT_VBUS_DET_F,
+ .end = AB8500_INT_VBUS_DET_F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "VBUS_DET_R",
+ .start = AB8500_INT_VBUS_DET_R,
+ .end = AB8500_INT_VBUS_DET_R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_LINK_STATUS",
+ .start = AB8500_INT_USB_LINK_STATUS,
+ .end = AB8500_INT_USB_LINK_STATUS,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_ADP_PROBE_PLUG",
+ .start = AB8500_INT_ADP_PROBE_PLUG,
+ .end = AB8500_INT_ADP_PROBE_PLUG,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "USB_ADP_PROBE_UNPLUG",
+ .start = AB8500_INT_ADP_PROBE_UNPLUG,
+ .end = AB8500_INT_ADP_PROBE_UNPLUG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource __devinitdata ab8500_temp_resources[] = {
+ {
+ .name = "AB8500_TEMP_WARM",
+ .start = AB8500_INT_TEMP_WARM,
+ .end = AB8500_INT_TEMP_WARM,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell __devinitdata ab8500_devs[] = {
+#ifdef CONFIG_DEBUG_FS
+ {
+ .name = "ab8500-debug",
+ .num_resources = ARRAY_SIZE(ab8500_debug_resources),
+ .resources = ab8500_debug_resources,
+ },
+#endif
+ {
+ .name = "ab8500-sysctrl",
+ },
+ {
+ .name = "ab8500-regulator",
+ },
+ {
+ .name = "ab8500-regulator-debug",
+ },
+ {
+ .name = "ab8500-gpio",
+ .num_resources = ARRAY_SIZE(ab8500_gpio_resources),
+ .resources = ab8500_gpio_resources,
+ },
{
.name = "ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
@@ -348,10 +706,113 @@ static struct mfd_cell ab8500_devs[] = {
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
.resources = ab8500_rtc_resources,
},
- { .name = "ab8500-charger", },
- { .name = "ab8500-audio", },
- { .name = "ab8500-usb", },
- { .name = "ab8500-pwm", },
+ {
+ .name = "ab8500-charger",
+ .num_resources = ARRAY_SIZE(ab8500_charger_resources),
+ .resources = ab8500_charger_resources,
+ },
+ {
+ .name = "ab8500-btemp",
+ .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
+ .resources = ab8500_btemp_resources,
+ },
+ {
+ .name = "ab8500-fg",
+ .num_resources = ARRAY_SIZE(ab8500_fg_resources),
+ .resources = ab8500_fg_resources,
+ },
+ {
+ .name = "ab8500-chargalg",
+ .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
+ .resources = ab8500_chargalg_resources,
+ },
+ {
+ .name = "ab8500-acc-det",
+ .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+ .resources = ab8500_av_acc_detect_resources,
+ },
+ {
+ .name = "ab8500-codec",
+ },
+ {
+ .name = "ab8500-usb",
+ .num_resources = ARRAY_SIZE(ab8500_usb_resources),
+ .resources = ab8500_usb_resources,
+ },
+ {
+ .name = "ab8500-poweron-key",
+ .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
+ .resources = ab8500_poweronkey_db_resources,
+ },
+ {
+ .name = "ab8500-pwm",
+ .id = 1,
+ },
+ {
+ .name = "ab8500-pwm",
+ .id = 2,
+ },
+ {
+ .name = "ab8500-pwm",
+ .id = 3,
+ },
+ { .name = "ab8500-leds", },
+ {
+ .name = "ab8500-denc",
+ },
+ {
+ .name = "ab8500-temp",
+ .num_resources = ARRAY_SIZE(ab8500_temp_resources),
+ .resources = ab8500_temp_resources,
+ },
+};
+
+static ssize_t show_chip_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ab8500 *ab8500;
+
+ ab8500 = dev_get_drvdata(dev);
+ return sprintf(buf, "%#x\n", ab8500 ? ab8500->chip_id : -EINVAL);
+}
+
+/*
+ * ab8500 has switched off due to (SWITCH_OFF_STATUS):
+ * 0x01 Swoff bit programming
+ * 0x02 Thermal protection activation
+ * 0x04 Vbat lower then BattOk falling threshold
+ * 0x08 Watchdog expired
+ * 0x10 Non presence of 32kHz clock
+ * 0x20 Battery level lower than power on reset threshold
+ * 0x40 Power on key 1 pressed longer than 10 seconds
+ * 0x80 DB8500 thermal shutdown
+ */
+static ssize_t show_switch_off_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ u8 value;
+ struct ab8500 *ab8500;
+
+ ab8500 = dev_get_drvdata(dev);
+ ret = get_register_interruptible(ab8500, AB8500_RTC,
+ AB8500_SWITCH_OFF_STATUS, &value);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%#x\n", value);
+}
+
+static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
+static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
+
+static struct attribute *ab8500_sysfs_entries[] = {
+ &dev_attr_chip_id.attr,
+ &dev_attr_switch_off_status.attr,
+ NULL,
+};
+
+static struct attribute_group ab8500_attr_group = {
+ .attrs = ab8500_sysfs_entries,
};
int __devinit ab8500_init(struct ab8500 *ab8500)
@@ -359,6 +820,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
int ret;
int i;
+ u8 value;
if (plat)
ab8500->irq_base = plat->irq_base;
@@ -366,37 +828,64 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
mutex_init(&ab8500->lock);
mutex_init(&ab8500->irq_lock);
- ret = ab8500_read(ab8500, AB8500_REV_REG);
+ ret = get_register_interruptible(ab8500, AB8500_MISC,
+ AB8500_REV_REG, &value);
if (ret < 0)
return ret;
- /*
- * 0x0 - Early Drop
- * 0x10 - Cut 1.0
- * 0x11 - Cut 1.1
- */
- if (ret == 0x0 || ret == 0x10 || ret == 0x11) {
- ab8500->revision = ret;
- dev_info(ab8500->dev, "detected chip, revision: %#x\n", ret);
- } else {
- dev_err(ab8500->dev, "unknown chip, revision: %#x\n", ret);
+ switch (value) {
+ case AB8500_CUTEARLY:
+ case AB8500_CUT1P0:
+ case AB8500_CUT1P1:
+ case AB8500_CUT2P0:
+ case AB8500_CUT3P0:
+ dev_info(ab8500->dev, "detected chip, revision: %#x\n", value);
+ break;
+ default:
+ dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value);
return -EINVAL;
}
+ ab8500->chip_id = value;
+
+ /*
+ * ab8500 has switched off due to (SWITCH_OFF_STATUS):
+ * 0x01 Swoff bit programming
+ * 0x02 Thermal protection activation
+ * 0x04 Vbat lower then BattOk falling threshold
+ * 0x08 Watchdog expired
+ * 0x10 Non presence of 32kHz clock
+ * 0x20 Battery level lower than power on reset threshold
+ * 0x40 Power on key 1 pressed longer than 10 seconds
+ * 0x80 DB8500 thermal shutdown
+ */
+
+ ret = get_register_interruptible(ab8500, AB8500_RTC,
+ AB8500_SWITCH_OFF_STATUS, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(ab8500->dev, "switch off status: %#x", value);
if (plat && plat->init)
plat->init(ab8500);
/* Clear and mask all interrupts */
- for (i = 0; i < 10; i++) {
- ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i);
- ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff);
- }
+ for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
+ /* Interrupt register 12 doesn't exist prior to version 2.0 */
+ if (ab8500_irq_regoffset[i] == 11 &&
+ ab8500->chip_id < AB8500_CUT2P0)
+ continue;
- for (i = 18; i < 24; i++) {
- ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i);
- ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff);
+ get_register_interruptible(ab8500, AB8500_INTERRUPT,
+ AB8500_IT_LATCH1_REG + ab8500_irq_regoffset[i],
+ &value);
+ set_register_interruptible(ab8500, AB8500_INTERRUPT,
+ AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i], 0xff);
}
+ ret = abx500_register_ops(ab8500->dev, &ab8500_ops);
+ if (ret)
+ return ret;
+
for (i = 0; i < AB8500_NUM_IRQ_REGS; i++)
ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
@@ -406,17 +895,22 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
return ret;
ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq,
- IRQF_ONESHOT, "ab8500", ab8500);
+ IRQF_ONESHOT | IRQF_NO_SUSPEND,
+ "ab8500", ab8500);
if (ret)
goto out_removeirq;
}
- ret = mfd_add_devices(ab8500->dev, -1, ab8500_devs,
+ ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
ARRAY_SIZE(ab8500_devs), NULL,
ab8500->irq_base);
if (ret)
goto out_freeirq;
+ ret = sysfs_create_group(&ab8500->dev->kobj, &ab8500_attr_group);
+ if (ret)
+ dev_err(ab8500->dev, "error creating sysfs entries\n");
+
return ret;
out_freeirq:
@@ -430,6 +924,7 @@ out_removeirq:
int __devexit ab8500_exit(struct ab8500 *ab8500)
{
+ sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
mfd_remove_devices(ab8500->dev);
if (ab8500->irq_base) {
free_irq(ab8500->irq, ab8500);
@@ -439,6 +934,6 @@ int __devexit ab8500_exit(struct ab8500 *ab8500)
return 0;
}
-MODULE_AUTHOR("Srinidhi Kasagar, Rabin Vincent");
+MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent");
MODULE_DESCRIPTION("AB8500 MFD core");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
new file mode 100644
index 00000000000..745f4955c8f
--- /dev/null
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -0,0 +1,1175 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
+ * License Terms: GNU General Public License v2
+ */
+/*
+ * AB8500 register access
+ * ======================
+ *
+ * read:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # echo ADDR > <debugfs>/ab8500/register-address
+ * # cat <debugfs>/ab8500/register-value
+ *
+ * write:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # echo ADDR > <debugfs>/ab8500/register-address
+ * # echo VALUE > <debugfs>/ab8500/register-value
+ *
+ * read all registers from a bank:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # cat <debugfs>/ab8500/all-bank-register
+ *
+ * BANK target AB8500 register bank
+ * ADDR target AB8500 register address
+ * VALUE decimal or 0x-prefixed hexadecimal
+ *
+ *
+ * User Space notification on AB8500 IRQ
+ * =====================================
+ *
+ * Allows user space entity to be notified when target AB8500 IRQ occurs.
+ * When subscribed, a sysfs entry is created in ab8500.i2c platform device.
+ * One can pool this file to get target IRQ occurence information.
+ *
+ * subscribe to an AB8500 IRQ:
+ * # echo IRQ > <debugfs>/ab8500/irq-subscribe
+ *
+ * unsubscribe from an AB8500 IRQ:
+ * # echo IRQ > <debugfs>/ab8500/irq-unsubscribe
+ *
+ *
+ * AB8500 register formated read/write access
+ * ==========================================
+ *
+ * Read: read data, data>>SHIFT, data&=MASK, output data
+ * [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE
+ * Write: read data, data &= ~(MASK<<SHIFT), data |= (VALUE<<SHIFT), write data
+ * [0xABCDEF98] shift=12 mask=0xFFF value=0x123 => [0xAB123F98]
+ *
+ * Usage:
+ * # echo "CMD [OPTIONS] BANK ADRESS [VALUE]" > $debugfs/ab8500/hwreg
+ *
+ * CMD read read access
+ * write write access
+ *
+ * BANK target reg bank
+ * ADDRESS target reg address
+ * VALUE (write) value to be updated
+ *
+ * OPTIONS
+ * -d|-dec (read) output in decimal
+ * -h|-hexa (read) output in 0x-hexa (default)
+ * -l|-w|-b 32bit (default), 16bit or 8bit reg access
+ * -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF)
+ * -s|-shift SHIFT bit shift value (read:left, write:right)
+ * -o|-offset OFFSET address offset to add to ADDRESS value
+ *
+ * Warning: bit shift operation is applied to bit-mask.
+ * Warning: bit shift direction depends on read or right command.
+ */
+
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/string.h>
+#include <linux/ctype.h>
+#endif
+
+static u32 debug_bank;
+static u32 debug_address;
+
+static int irq_first;
+static int irq_last;
+static u32 irq_count[AB8500_NR_IRQS];
+
+static struct device_attribute *dev_attr[AB8500_NR_IRQS];
+static char *event_name[AB8500_NR_IRQS];
+
+/**
+ * struct ab8500_reg_range
+ * @first: the first address of the range
+ * @last: the last address of the range
+ * @perm: access permissions for the range
+ */
+struct ab8500_reg_range {
+ u8 first;
+ u8 last;
+ u8 perm;
+};
+
+/**
+ * struct ab8500_i2c_ranges
+ * @num_ranges: the number of ranges in the list
+ * @bankid: bank identifier
+ * @range: the list of register ranges
+ */
+struct ab8500_i2c_ranges {
+ u8 num_ranges;
+ u8 bankid;
+ const struct ab8500_reg_range *range;
+};
+
+/* hwreg- "mask" and "shift" entries ressources */
+struct hwreg_cfg {
+ u32 bank; /* target bank */
+ u32 addr; /* target address */
+ uint fmt; /* format */
+ uint mask; /* read/write mask, applied before any bit shift */
+ int shift; /* bit shift (read:right shift, write:left shift */
+};
+/* fmt bit #0: 0=hexa, 1=dec */
+#define REG_FMT_DEC(c) ((c)->fmt & 0x1)
+#define REG_FMT_HEX(c) (!REG_FMT_DEC(c))
+
+static struct hwreg_cfg hwreg_cfg = {
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+};
+
+#define AB8500_NAME_STRING "ab8500"
+#define AB8500_NUM_BANKS 22
+
+#define AB8500_REV_REG 0x80
+
+static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
+ [0x0] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_SYS_CTRL1_BLOCK] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x02,
+ },
+ {
+ .first = 0x42,
+ .last = 0x42,
+ },
+ {
+ .first = 0x80,
+ .last = 0x81,
+ },
+ },
+ },
+ [AB8500_SYS_CTRL2_BLOCK] = {
+ .num_ranges = 4,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0D,
+ },
+ {
+ .first = 0x0F,
+ .last = 0x17,
+ },
+ {
+ .first = 0x30,
+ .last = 0x30,
+ },
+ {
+ .first = 0x32,
+ .last = 0x33,
+ },
+ },
+ },
+ [AB8500_REGU_CTRL1] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ {
+ .first = 0x03,
+ .last = 0x10,
+ },
+ {
+ .first = 0x80,
+ .last = 0x84,
+ },
+ },
+ },
+ [AB8500_REGU_CTRL2] = {
+ .num_ranges = 5,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x15,
+ },
+ {
+ .first = 0x17,
+ .last = 0x19,
+ },
+ {
+ .first = 0x1B,
+ .last = 0x1D,
+ },
+ {
+ .first = 0x1F,
+ .last = 0x22,
+ },
+ {
+ .first = 0x40,
+ .last = 0x44,
+ },
+ /* 0x80-0x8B is SIM registers and should
+ * not be accessed from here */
+ },
+ },
+ [AB8500_USB] = {
+ .num_ranges = 2,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x80,
+ .last = 0x83,
+ },
+ {
+ .first = 0x87,
+ .last = 0x8A,
+ },
+ },
+ },
+ [AB8500_TVOUT] = {
+ .num_ranges = 9,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x12,
+ },
+ {
+ .first = 0x15,
+ .last = 0x17,
+ },
+ {
+ .first = 0x19,
+ .last = 0x21,
+ },
+ {
+ .first = 0x27,
+ .last = 0x2C,
+ },
+ {
+ .first = 0x41,
+ .last = 0x41,
+ },
+ {
+ .first = 0x45,
+ .last = 0x5B,
+ },
+ {
+ .first = 0x5D,
+ .last = 0x5D,
+ },
+ {
+ .first = 0x69,
+ .last = 0x69,
+ },
+ {
+ .first = 0x80,
+ .last = 0x81,
+ },
+ },
+ },
+ [AB8500_DBI] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_ECI_AV_ACC] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x80,
+ .last = 0x82,
+ },
+ },
+ },
+ [0x9] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_GPADC] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x08,
+ },
+ },
+ },
+ [AB8500_CHARGER] = {
+ .num_ranges = 9,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x03,
+ },
+ {
+ .first = 0x05,
+ .last = 0x05,
+ },
+ {
+ .first = 0x40,
+ .last = 0x40,
+ },
+ {
+ .first = 0x42,
+ .last = 0x42,
+ },
+ {
+ .first = 0x44,
+ .last = 0x44,
+ },
+ {
+ .first = 0x50,
+ .last = 0x55,
+ },
+ {
+ .first = 0x80,
+ .last = 0x82,
+ },
+ {
+ .first = 0xC0,
+ .last = 0xC2,
+ },
+ {
+ .first = 0xf5,
+ .last = 0xf6,
+ },
+ },
+ },
+ [AB8500_GAS_GAUGE] = {
+ .num_ranges = 3,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ {
+ .first = 0x07,
+ .last = 0x0A,
+ },
+ {
+ .first = 0x10,
+ .last = 0x14,
+ },
+ },
+ },
+ [AB8500_AUDIO] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x6F,
+ },
+ },
+ },
+ [AB8500_INTERRUPT] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_RTC] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x0F,
+ },
+ },
+ },
+ [AB8500_MISC] = {
+ .num_ranges = 8,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x05,
+ },
+ {
+ .first = 0x10,
+ .last = 0x15,
+ },
+ {
+ .first = 0x20,
+ .last = 0x25,
+ },
+ {
+ .first = 0x30,
+ .last = 0x35,
+ },
+ {
+ .first = 0x40,
+ .last = 0x45,
+ },
+ {
+ .first = 0x50,
+ .last = 0x50,
+ },
+ {
+ .first = 0x60,
+ .last = 0x67,
+ },
+ {
+ .first = 0x80,
+ .last = 0x80,
+ },
+ },
+ },
+ [AB8500_DEVELOPMENT] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ },
+ },
+ [AB8500_DEBUG] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x05,
+ .last = 0x07,
+ },
+ },
+ },
+ [0x13] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [0x14] = {
+ .num_ranges = 0,
+ .range = NULL,
+ },
+ [AB8500_OTP_EMUL] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x01,
+ .last = 0x0F,
+ },
+ },
+ },
+};
+
+static irqreturn_t ab8500_debug_handler(int irq, void *data)
+{
+ char buf[16];
+ struct kobject *kobj = (struct kobject *)data;
+ unsigned int irq_abb = irq - irq_first;
+
+ if (irq_abb < AB8500_NR_IRQS)
+ irq_count[irq_abb]++;
+ /*
+ * This makes it possible to use poll for events (POLLPRI | POLLERR)
+ * from userspace on sysfs file named <irq-nr>
+ */
+ sprintf(buf, "%d", irq);
+ sysfs_notify(kobj, NULL, buf);
+
+ return IRQ_HANDLED;
+}
+
+static int ab8500_registers_print(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ unsigned int i;
+ u32 bank = debug_bank;
+
+ seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+ seq_printf(s, " bank %u:\n", bank);
+ for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
+ u32 reg;
+
+ for (reg = debug_ranges[bank].range[i].first;
+ reg <= debug_ranges[bank].range[i].last;
+ reg++) {
+ u8 value;
+ int err;
+
+ err = abx500_get_register_interruptible(dev,
+ (u8)bank, (u8)reg, &value);
+ if (err < 0) {
+ dev_err(dev, "ab->read fail %d\n", err);
+ return err;
+ }
+
+ err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n", bank,
+ reg, value);
+ if (err < 0) {
+ dev_err(dev, "seq_printf overflow\n");
+ /* Error is not returned here since
+ * the output is wanted in any case */
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+static int ab8500_registers_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_registers_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_registers_fops = {
+ .open = ab8500_registers_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_bank_print(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "%d\n", debug_bank);
+}
+
+static int ab8500_bank_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_bank_print, inode->i_private);
+}
+
+static ssize_t ab8500_bank_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_bank;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_bank);
+ if (err)
+ return -EINVAL;
+
+ if (user_bank >= AB8500_NUM_BANKS) {
+ dev_err(dev, "debugfs error input > number of banks\n");
+ return -EINVAL;
+ }
+
+ debug_bank = user_bank;
+
+ return buf_size;
+}
+
+static int ab8500_address_print(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "0x%02X\n", debug_address);
+}
+
+static int ab8500_address_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_address_print, inode->i_private);
+}
+
+static ssize_t ab8500_address_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_address;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_address);
+ if (err)
+ return -EINVAL;
+ if (user_address > 0xff) {
+ dev_err(dev, "debugfs error input > 0xff\n");
+ return -EINVAL;
+ }
+ debug_address = user_address;
+ return buf_size;
+}
+
+static int ab8500_val_print(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ int ret;
+ u8 regvalue;
+
+ ret = abx500_get_register_interruptible(dev,
+ (u8)debug_bank, (u8)debug_address, &regvalue);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+ seq_printf(s, "0x%02X\n", regvalue);
+
+ return 0;
+}
+
+static int ab8500_val_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_val_print, inode->i_private);
+}
+
+static ssize_t ab8500_val_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+ if (user_val > 0xff) {
+ dev_err(dev, "debugfs error input > 0xff\n");
+ return -EINVAL;
+ }
+ err = abx500_set_register_interruptible(dev,
+ (u8)debug_bank, debug_address, (u8)user_val);
+ if (err < 0) {
+ printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__);
+ return -EINVAL;
+ }
+ return buf_size;
+}
+
+/*
+ * - HWREG DB8500 formated routines
+ */
+static int ab8500_hwreg_print(struct seq_file *s, void *d)
+{
+ struct device *dev = s->private;
+ int ret;
+ u8 regvalue;
+
+ ret = abx500_get_register_interruptible(dev,
+ (u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, &regvalue);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+
+ if (hwreg_cfg.shift >= 0)
+ regvalue >>= hwreg_cfg.shift;
+ else
+ regvalue <<= -hwreg_cfg.shift;
+ regvalue &= hwreg_cfg.mask;
+
+ if (REG_FMT_DEC(&hwreg_cfg))
+ seq_printf(s, "%d\n", regvalue);
+ else
+ seq_printf(s, "0x%02X\n", regvalue);
+ return 0;
+}
+
+static int ab8500_hwreg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_hwreg_print, inode->i_private);
+}
+
+/*
+ * return length of an ASCII numerical value, 0 is string is not a
+ * numerical value.
+ * string shall start at value 1st char.
+ * string can be tailed with \0 or space or newline chars only.
+ * value can be decimal or hexadecimal (prefixed 0x or 0X).
+ */
+static int strval_len(char *b)
+{
+ char *s = b;
+ if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) {
+ s += 2;
+ for (; *s && (*s != ' ') && (*s != '\n'); s++) {
+ if (!isxdigit(*s))
+ return 0;
+ }
+ } else {
+ if (*s == '-')
+ s++;
+ for (; *s && (*s != ' ') && (*s != '\n'); s++) {
+ if (!isdigit(*s))
+ return 0;
+ }
+ }
+ return (int) (s-b);
+}
+
+/*
+ * parse hwreg input data.
+ * update global hwreg_cfg only if input data syntax is ok.
+ */
+static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg,
+ struct device *dev)
+{
+ uint write, val = 0;
+ struct hwreg_cfg loc = {
+ .bank = 0, /* default: invalid phys addr */
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+ };
+
+ /* read or write ? */
+ if (!strncmp(b, "read ", 5)) {
+ write = 0;
+ b += 5;
+ } else if (!strncmp(b, "write ", 6)) {
+ write = 1;
+ b += 6;
+ } else
+ return -EINVAL;
+
+ /* OPTIONS -l|-w|-b -s -m -o */
+ while ((*b == ' ') || (*b == '-')) {
+ if (*(b-1) != ' ') {
+ b++;
+ continue;
+ }
+ if ((!strncmp(b, "-d ", 3)) ||
+ (!strncmp(b, "-dec ", 5))) {
+ b += (*(b+2) == ' ') ? 3 : 5;
+ loc.fmt |= (1<<0);
+ } else if ((!strncmp(b, "-h ", 3)) ||
+ (!strncmp(b, "-hex ", 5))) {
+ b += (*(b+2) == ' ') ? 3 : 5;
+ loc.fmt &= ~(1<<0);
+ } else if ((!strncmp(b, "-m ", 3)) ||
+ (!strncmp(b, "-mask ", 6))) {
+ b += (*(b+2) == ' ') ? 3 : 6;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.mask = simple_strtoul(b, &b, 0);
+ } else if ((!strncmp(b, "-s ", 3)) ||
+ (!strncmp(b, "-shift ", 7))) {
+ b += (*(b+2) == ' ') ? 3 : 7;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.shift = simple_strtol(b, &b, 0);
+ } else {
+ return -EINVAL;
+ }
+ }
+ /* get arg BANK and ADDRESS */
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.bank = simple_strtoul(b, &b, 0);
+ while (*b == ' ')
+ b++;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.addr = simple_strtoul(b, &b, 0);
+
+ if (write) {
+ while (*b == ' ')
+ b++;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ val = simple_strtoul(b, &b, 0);
+ }
+
+ /* args are ok, update target cfg (mainly for read) */
+ *cfg = loc;
+
+#ifdef ABB_HWREG_DEBUG
+ pr_warn("HWREG request: %s, %s, addr=0x%08X, mask=0x%X, shift=%d"
+ "value=0x%X\n", (write) ? "write" : "read",
+ REG_FMT_DEC(cfg) ? "decimal" : "hexa",
+ cfg->addr, cfg->mask, cfg->shift, val);
+#endif
+
+ if (write) {
+ u8 regvalue;
+ int ret = abx500_get_register_interruptible(dev,
+ (u8)cfg->bank, (u8)cfg->addr, &regvalue);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+
+ if (cfg->shift >= 0) {
+ regvalue &= ~(cfg->mask << (cfg->shift));
+ val = (val & cfg->mask) << (cfg->shift);
+ } else {
+ regvalue &= ~(cfg->mask >> (-cfg->shift));
+ val = (val & cfg->mask) >> (-cfg->shift);
+ }
+ val = val | regvalue;
+
+ ret = abx500_set_register_interruptible(dev,
+ (u8)cfg->bank, (u8)cfg->addr, (u8)val);
+ if (ret < 0) {
+ pr_err("abx500_set_reg failed %d, %d", ret, __LINE__);
+ return -EINVAL;
+ }
+
+ }
+ return 0;
+}
+
+static ssize_t ab8500_hwreg_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[128];
+ int buf_size, ret;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* get args and process */
+ ret = hwreg_common_write(buf, &hwreg_cfg, dev);
+ return (ret) ? ret : buf_size;
+}
+
+/*
+ * - irq subscribe/unsubscribe stuff
+ */
+static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p)
+{
+ seq_printf(s, "%d\n", irq_first);
+
+ return 0;
+}
+
+static int ab8500_subscribe_unsubscribe_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_subscribe_unsubscribe_print,
+ inode->i_private);
+}
+
+/*
+ * Userspace should use poll() on this file. When an event occur
+ * the blocking poll will be released.
+ */
+static ssize_t show_irq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long name;
+ unsigned int irq_index;
+ int err;
+
+ err = strict_strtoul(attr->attr.name, 0, &name);
+ if (err)
+ return err;
+
+ irq_index = name - irq_first;
+ if (irq_index >= AB8500_NR_IRQS)
+ return -EINVAL;
+ else
+ return sprintf(buf, "%u\n", irq_count[irq_index]);
+}
+
+static ssize_t ab8500_subscribe_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+ unsigned int irq_index;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+ if (user_val < irq_first) {
+ dev_err(dev, "debugfs error input < %d\n", irq_first);
+ return -EINVAL;
+ }
+ if (user_val > irq_last) {
+ dev_err(dev, "debugfs error input > %d\n", irq_last);
+ return -EINVAL;
+ }
+
+ irq_index = user_val - irq_first;
+ if (irq_index >= AB8500_NR_IRQS)
+ return -EINVAL;
+
+ /*
+ * This will create a sysfs file named <irq-nr> which userspace can
+ * use to select or poll and get the AB8500 events
+ */
+ dev_attr[irq_index] = kmalloc(sizeof(struct device_attribute),
+ GFP_KERNEL);
+ event_name[irq_index] = kmalloc(buf_size, GFP_KERNEL);
+ sprintf(event_name[irq_index], "%lu", user_val);
+ dev_attr[irq_index]->show = show_irq;
+ dev_attr[irq_index]->store = NULL;
+ dev_attr[irq_index]->attr.name = event_name[irq_index];
+ dev_attr[irq_index]->attr.mode = S_IRUGO;
+ err = sysfs_create_file(&dev->kobj, &dev_attr[irq_index]->attr);
+ if (err < 0) {
+ printk(KERN_ERR "sysfs_create_file failed %d\n", err);
+ return err;
+ }
+
+ err = request_threaded_irq(user_val, NULL, ab8500_debug_handler,
+ IRQF_SHARED | IRQF_NO_SUSPEND, "ab8500-debug", &dev->kobj);
+ if (err < 0) {
+ printk(KERN_ERR "request_threaded_irq failed %d, %lu\n",
+ err, user_val);
+ sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
+ return err;
+ }
+
+ return buf_size;
+}
+
+static ssize_t ab8500_unsubscribe_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+ unsigned int irq_index;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+ if (user_val < irq_first) {
+ dev_err(dev, "debugfs error input < %d\n", irq_first);
+ return -EINVAL;
+ }
+ if (user_val > irq_last) {
+ dev_err(dev, "debugfs error input > %d\n", irq_last);
+ return -EINVAL;
+ }
+
+ irq_index = user_val - irq_first;
+ if (irq_index >= AB8500_NR_IRQS)
+ return -EINVAL;
+
+ /* Set irq count to 0 when unsubscribe */
+ irq_count[irq_index] = 0;
+
+ if (dev_attr[irq_index])
+ sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
+
+
+ free_irq(user_val, &dev->kobj);
+ kfree(event_name[irq_index]);
+ kfree(dev_attr[irq_index]);
+
+ return buf_size;
+}
+
+/*
+ * - several deubgfs nodes fops
+ */
+
+static const struct file_operations ab8500_bank_fops = {
+ .open = ab8500_bank_open,
+ .write = ab8500_bank_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_address_fops = {
+ .open = ab8500_address_open,
+ .write = ab8500_address_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_val_fops = {
+ .open = ab8500_val_open,
+ .write = ab8500_val_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_subscribe_fops = {
+ .open = ab8500_subscribe_unsubscribe_open,
+ .write = ab8500_subscribe_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_unsubscribe_fops = {
+ .open = ab8500_subscribe_unsubscribe_open,
+ .write = ab8500_unsubscribe_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_hwreg_fops = {
+ .open = ab8500_hwreg_open,
+ .write = ab8500_hwreg_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static struct dentry *ab8500_dir;
+
+static int __devinit ab8500_debug_probe(struct platform_device *plf)
+{
+ struct dentry *file;
+ debug_bank = AB8500_MISC;
+ debug_address = AB8500_REV_REG & 0x00FF;
+
+ irq_first = platform_get_irq_byname(plf, "IRQ_FIRST");
+ if (irq_first < 0) {
+ dev_err(&plf->dev, "First irq not found, err %d\n",
+ irq_first);
+ return irq_first;
+ }
+
+ irq_last = platform_get_irq_byname(plf, "IRQ_LAST");
+ if (irq_last < 0) {
+ dev_err(&plf->dev, "Last irq not found, err %d\n",
+ irq_last);
+ return irq_last;
+ }
+
+ ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
+ if (!ab8500_dir)
+ goto err;
+
+ file = debugfs_create_file("all-bank-registers",
+ S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-bank",
+ (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_bank_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-address",
+ (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev,
+ &ab8500_address_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-value",
+ (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_val_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("irq-subscribe",
+ (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev,
+ &ab8500_subscribe_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("irq-unsubscribe",
+ (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev,
+ &ab8500_unsubscribe_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("hwreg",
+ (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev,
+ &ab8500_hwreg_fops);
+ if (!file)
+ goto err;
+
+ return 0;
+
+err:
+ if (ab8500_dir)
+ debugfs_remove_recursive(ab8500_dir);
+ dev_err(&plf->dev, "failed to create debugfs entries.\n");
+ return -ENOMEM;
+}
+
+static int __devexit ab8500_debug_remove(struct platform_device *plf)
+{
+ debugfs_remove_recursive(ab8500_dir);
+ return 0;
+}
+
+static struct platform_driver ab8500_debug_driver = {
+ .driver = {
+ .name = "ab8500-debug",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab8500_debug_probe,
+ .remove = __devexit_p(ab8500_debug_remove)
+};
+
+static int __init ab8500_debug_init(void)
+{
+ return platform_driver_register(&ab8500_debug_driver);
+}
+
+static void __exit ab8500_debug_exit(void)
+{
+ platform_driver_unregister(&ab8500_debug_driver);
+}
+subsys_initcall(ab8500_debug_init);
+module_exit(ab8500_debug_exit);
+
+MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com");
+MODULE_DESCRIPTION("AB8500 DEBUG");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-denc.c b/drivers/mfd/ab8500-denc.c
new file mode 100644
index 00000000000..1bf61e41bcd
--- /dev/null
+++ b/drivers/mfd/ab8500-denc.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * ST-Ericsson AB8500 DENC base driver
+ *
+ * Author: Marcel Tunnissen <marcel.tuennissen@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500/denc-regs.h>
+#include <linux/mfd/ab8500/denc.h>
+
+#define AB8500_NAME "ab8500"
+#define AB8500_DENC_NAME "ab8500_denc"
+
+struct device_usage {
+ struct list_head list;
+ struct platform_device *pdev;
+ bool taken;
+};
+static LIST_HEAD(device_list);
+
+/* To get rid of the extra bank parameter: */
+#define AB8500_REG_BANK_NR(__reg) ((0xff00 & (__reg)) >> 8)
+static inline u8 ab8500_rreg(struct device *dev, u32 reg)
+{
+ u8 val;
+ if (abx500_get_register_interruptible(dev, AB8500_REG_BANK_NR(reg),
+ reg, &val) < 0)
+ return 0;
+ else
+ return val;
+}
+
+static inline int ab8500_wreg(struct device *dev, u32 reg, u8 val)
+{
+ return abx500_set_register_interruptible(dev, AB8500_REG_BANK_NR(reg),
+ reg, val);
+}
+
+/* Only use in the macro below: */
+static inline int _ab8500_wreg_fld(struct device *dev, u32 reg, u8 val,
+ u8 mask, u8 shift)
+{
+ int ret;
+ u8 org_val;
+
+ ret = abx500_get_register_interruptible(dev, AB8500_REG_BANK_NR(reg),
+ reg, &org_val);
+ if (ret < 0)
+ return ret;
+ else
+ ab8500_wreg(dev, reg,
+ (org_val & ~mask) | ((val << shift) & mask));
+ return 0;
+}
+
+#define ab8500_wr_fld(__d, __reg, __fld, __val) \
+ _ab8500_wreg_fld(__d, __reg, __val, __reg##_##__fld##_MASK, \
+ __reg##_##__fld##_SHIFT)
+
+#define ab8500_set_fld(__cur_val, __reg, __fld, __val) \
+ (((__cur_val) & ~__reg##_##__fld##_MASK) | \
+ (((__val) << __reg##_##__fld##_SHIFT) & __reg##_##__fld##_MASK))
+
+#define AB8500_DENC_TRACE(__pd) dev_dbg(&(__pd)->dev, "%s\n", __func__)
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_ab8500_denc_dir;
+static struct dentry *debugfs_ab8500_dump_regs_file;
+static void ab8500_denc_conf_ddr(struct platform_device *pdev);
+static int debugfs_ab8500_open_file(struct inode *inode, struct file *file);
+static ssize_t debugfs_ab8500_dump_regs(struct file *file, char __user *buf,
+ size_t count, loff_t *f_pos);
+
+static const struct file_operations debugfs_ab8500_dump_regs_fops = {
+ .owner = THIS_MODULE,
+ .open = debugfs_ab8500_open_file,
+ .read = debugfs_ab8500_dump_regs,
+};
+#endif /* CONFIG_DEBUG_FS */
+
+static int __devinit ab8500_denc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct ab8500_platform_data *ab8500_pdata =
+ dev_get_platdata(pdev->dev.parent);
+ struct ab8500_denc_platform_data *pdata;
+ struct device_usage *device_data;
+
+ AB8500_DENC_TRACE(pdev);
+
+ if (ab8500_pdata == NULL) {
+ dev_err(&pdev->dev, "AB8500 platform data missing\n");
+ return -EINVAL;
+ }
+
+ pdata = ab8500_pdata->denc;
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "Denc platform data missing\n");
+ return -EINVAL;
+ }
+
+ device_data = kzalloc(sizeof(struct device_usage), GFP_KERNEL);
+ if (!device_data) {
+ dev_err(&pdev->dev, "Failed to allocate device data\n");
+ return -ENOMEM;
+ }
+ device_data->pdev = pdev;
+ list_add_tail(&device_data->list, &device_list);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_ab8500_denc_dir = debugfs_create_dir(pdev->name, NULL);
+ debugfs_ab8500_dump_regs_file = debugfs_create_file(
+ "dumpregs", S_IRUGO,
+ debugfs_ab8500_denc_dir, &pdev->dev,
+ &debugfs_ab8500_dump_regs_fops
+ );
+#endif /* CONFIG_DEBUG_FS */
+ return ret;
+}
+
+static int __devexit ab8500_denc_remove(struct platform_device *pdev)
+{
+ struct list_head *element;
+ struct device_usage *device_data;
+
+ AB8500_DENC_TRACE(pdev);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(debugfs_ab8500_dump_regs_file);
+ debugfs_remove(debugfs_ab8500_denc_dir);
+#endif /* CONFIG_DEBUG_FS */
+
+ list_for_each(element, &device_list) {
+ device_data = list_entry(element, struct device_usage, list);
+ if (device_data->pdev == pdev) {
+ list_del(element);
+ kzfree(device_data);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver ab8500_denc_driver = {
+ .probe = ab8500_denc_probe,
+ .remove = ab8500_denc_remove,
+ .driver = {
+ .name = "ab8500-denc",
+ },
+};
+
+static void setup_27mhz(struct platform_device *pdev, bool enable)
+{
+ u8 data = ab8500_rreg(&pdev->dev, AB8500_SYS_ULP_CLK_CONF);
+
+ AB8500_DENC_TRACE(pdev);
+ /* TODO: check if this field needs to be set */
+ data = ab8500_set_fld(data, AB8500_SYS_ULP_CLK_CONF, CLK_27MHZ_PD_ENA,
+ true);
+ data = ab8500_set_fld(data, AB8500_SYS_ULP_CLK_CONF, CLK_27MHZ_BUF_ENA,
+ enable);
+ data = ab8500_set_fld(data, AB8500_SYS_ULP_CLK_CONF, TVOUT_CLK_INV,
+ false);
+ data = ab8500_set_fld(data, AB8500_SYS_ULP_CLK_CONF, TVOUT_CLK_DE_IN,
+ false);
+ data = ab8500_set_fld(data, AB8500_SYS_ULP_CLK_CONF, CLK_27MHZ_STRE,
+ 1);
+ ab8500_wreg(&pdev->dev, AB8500_SYS_ULP_CLK_CONF, data);
+
+ data = ab8500_rreg(&pdev->dev, AB8500_SYS_CLK_CTRL);
+ data = ab8500_set_fld(data, AB8500_SYS_CLK_CTRL, TVOUT_CLK_VALID,
+ enable);
+ data = ab8500_set_fld(data, AB8500_SYS_CLK_CTRL, TVOUT_PLL_ENA,
+ enable);
+ ab8500_wreg(&pdev->dev, AB8500_SYS_CLK_CTRL, data);
+}
+
+static u32 map_tv_std(enum ab8500_denc_TV_std std)
+{
+ switch (std) {
+ case TV_STD_PAL_BDGHI:
+ return AB8500_DENC_CONF0_STD_PAL_BDGHI;
+ case TV_STD_PAL_N:
+ return AB8500_DENC_CONF0_STD_PAL_N;
+ case TV_STD_PAL_M:
+ return AB8500_DENC_CONF0_STD_PAL_M;
+ case TV_STD_NTSC_M:
+ return AB8500_DENC_CONF0_STD_NTSC_M;
+ default:
+ return 0;
+ }
+}
+
+static u32 map_cr_filter(enum ab8500_denc_cr_filter_bandwidth bw)
+{
+ switch (bw) {
+ case TV_CR_NTSC_LOW_DEF_FILTER:
+ return AB8500_DENC_CONF1_FLT_1_1MHZ;
+ case TV_CR_PAL_LOW_DEF_FILTER:
+ return AB8500_DENC_CONF1_FLT_1_3MHZ;
+ case TV_CR_NTSC_HIGH_DEF_FILTER:
+ return AB8500_DENC_CONF1_FLT_1_6MHZ;
+ case TV_CR_PAL_HIGH_DEF_FILTER:
+ return AB8500_DENC_CONF1_FLT_1_9MHZ;
+ default:
+ return 0;
+ }
+}
+
+static u32 map_phase_rst_mode(enum ab8500_denc_phase_reset_mode mode)
+{
+ switch (mode) {
+ case TV_PHASE_RST_MOD_DISABLE:
+ return AB8500_DENC_CONF8_PH_RST_MODE_DISABLED;
+ case TV_PHASE_RST_MOD_FROM_PHASE_BUF:
+ return AB8500_DENC_CONF8_PH_RST_MODE_UPDATE_FROM_PHASE_BUF;
+ case TV_PHASE_RST_MOD_FROM_INC_DFS:
+ return AB8500_DENC_CONF8_PH_RST_MODE_UPDATE_FROM_INC_DFS;
+ case TV_PHASE_RST_MOD_RST:
+ return AB8500_DENC_CONF8_PH_RST_MODE_RESET;
+ default:
+ return 0;
+ }
+}
+
+static u32 map_plug_time(enum ab8500_denc_plug_time time)
+{
+ switch (time) {
+ case TV_PLUG_TIME_0_5S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_0_5S;
+ case TV_PLUG_TIME_1S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_1S;
+ case TV_PLUG_TIME_1_5S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_1_5S;
+ case TV_PLUG_TIME_2S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_2S;
+ case TV_PLUG_TIME_2_5S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_2_5S;
+ case TV_PLUG_TIME_3S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_3S;
+ default:
+ return 0;
+ }
+}
+
+struct platform_device *ab8500_denc_get_device(void)
+{
+ struct list_head *element;
+ struct device_usage *device_data;
+
+ pr_debug("%s\n", __func__);
+ list_for_each(element, &device_list) {
+ device_data = list_entry(element, struct device_usage, list);
+ if (!device_data->taken) {
+ device_data->taken = true;
+ return device_data->pdev;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(ab8500_denc_get_device);
+
+void ab8500_denc_put_device(struct platform_device *pdev)
+{
+ struct list_head *element;
+ struct device_usage *device_data;
+
+ AB8500_DENC_TRACE(pdev);
+ list_for_each(element, &device_list) {
+ device_data = list_entry(element, struct device_usage, list);
+ if (device_data->pdev == pdev)
+ device_data->taken = false;
+ }
+}
+EXPORT_SYMBOL(ab8500_denc_put_device);
+
+void ab8500_denc_reset(struct platform_device *pdev, bool hard)
+{
+ AB8500_DENC_TRACE(pdev);
+ if (hard) {
+ u8 data = ab8500_rreg(&pdev->dev, AB8500_CTRL3);
+ /* reset start */
+ ab8500_wreg(&pdev->dev, AB8500_CTRL3,
+ ab8500_set_fld(data, AB8500_CTRL3, RESET_DENC_N, 0)
+ );
+ /* reset done */
+ ab8500_wreg(&pdev->dev, AB8500_CTRL3,
+ ab8500_set_fld(data, AB8500_CTRL3, RESET_DENC_N, 1)
+ );
+ } else {
+ ab8500_wr_fld(&pdev->dev, AB8500_DENC_CONF6, SOFT_RESET, 1);
+ mdelay(10);
+ }
+}
+EXPORT_SYMBOL(ab8500_denc_reset);
+
+void ab8500_denc_power_up(struct platform_device *pdev)
+{
+ setup_27mhz(pdev, true);
+}
+EXPORT_SYMBOL(ab8500_denc_power_up);
+
+void ab8500_denc_power_down(struct platform_device *pdev)
+{
+ setup_27mhz(pdev, false);
+}
+EXPORT_SYMBOL(ab8500_denc_power_down);
+
+void ab8500_denc_conf(struct platform_device *pdev,
+ struct ab8500_denc_conf *conf)
+{
+ u8 data;
+
+ AB8500_DENC_TRACE(pdev);
+
+ ab8500_wreg(&pdev->dev, AB8500_DENC_CONF0,
+ AB8500_VAL2REG(AB8500_DENC_CONF0, STD, map_tv_std(conf->TV_std))
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF0, SYNC,
+ conf->test_pattern ? AB8500_DENC_CONF0_SYNC_AUTO_TEST :
+ AB8500_DENC_CONF0_SYNC_F_BASED_SLAVE
+ )
+ );
+ ab8500_wreg(&pdev->dev, AB8500_DENC_CONF1,
+ AB8500_VAL2REG(AB8500_DENC_CONF1, BLK_LI,
+ !conf->partial_blanking)
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF1, FLT,
+ map_cr_filter(conf->cr_filter))
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF1, CO_KI, conf->suppress_col)
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF1, SETUP_MAIN,
+ conf->black_level_setup)
+ /* TODO: handle cc field: set to 0 now */
+ );
+
+ data = ab8500_rreg(&pdev->dev, AB8500_DENC_CONF2);
+ data = ab8500_set_fld(data, AB8500_DENC_CONF2, N_INTRL,
+ conf->progressive);
+ ab8500_wreg(&pdev->dev, AB8500_DENC_CONF2, data);
+
+ ab8500_wreg(&pdev->dev, AB8500_DENC_CONF8,
+ AB8500_VAL2REG(AB8500_DENC_CONF8, PH_RST_MODE,
+ map_phase_rst_mode(conf->phase_reset_mode))
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF8, VAL_422_MUX,
+ conf->act_output)
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF8, BLK_ALL,
+ conf->blank_all)
+ );
+ data = ab8500_rreg(&pdev->dev, AB8500_TVOUT_CTRL);
+ data = ab8500_set_fld(data, AB8500_TVOUT_CTRL, DAC_CTRL0,
+ conf->dac_enable);
+ data = ab8500_set_fld(data, AB8500_TVOUT_CTRL, DAC_CTRL1,
+ conf->act_dc_output);
+ ab8500_wreg(&pdev->dev, AB8500_TVOUT_CTRL, data);
+
+ /* no support for DDR in early versions */
+ if (AB8500_REG2VAL(AB8500_REV, FULL_MASK,
+ ab8500_rreg(&pdev->dev, AB8500_REV)) > 0)
+ ab8500_denc_conf_ddr(pdev);
+}
+EXPORT_SYMBOL(ab8500_denc_conf);
+
+void ab8500_denc_conf_plug_detect(struct platform_device *pdev,
+ bool enable, bool load_RC,
+ enum ab8500_denc_plug_time time)
+{
+ u8 data;
+
+ AB8500_DENC_TRACE(pdev);
+ data = ab8500_rreg(&pdev->dev, AB8500_TVOUT_CTRL);
+ data = ab8500_set_fld(data, AB8500_TVOUT_CTRL, TV_PLUG_ON, enable);
+ data = ab8500_set_fld(data, AB8500_TVOUT_CTRL, TV_LOAD_RC, load_RC);
+ data = ab8500_set_fld(data, AB8500_TVOUT_CTRL, PLUG_TV_TIME,
+ map_plug_time(time));
+ ab8500_wreg(&pdev->dev, AB8500_TVOUT_CTRL, data);
+}
+EXPORT_SYMBOL(ab8500_denc_conf_plug_detect);
+
+void ab8500_denc_mask_int_plug_det(struct platform_device *pdev, bool plug,
+ bool unplug)
+{
+ u8 data = ab8500_rreg(&pdev->dev, AB8500_IT_MASK1);
+
+ AB8500_DENC_TRACE(pdev);
+ data = ab8500_set_fld(data, AB8500_IT_MASK1, PLUG_TV_DET, plug);
+ data = ab8500_set_fld(data, AB8500_IT_MASK1, UNPLUG_TV_DET, unplug);
+ ab8500_wreg(&pdev->dev, AB8500_IT_MASK1, data);
+}
+EXPORT_SYMBOL(ab8500_denc_mask_int_plug_det);
+
+static void ab8500_denc_conf_ddr(struct platform_device *pdev)
+{
+ struct ab8500_platform_data *core_pdata;
+ struct ab8500_denc_platform_data *denc_pdata;
+
+ AB8500_DENC_TRACE(pdev);
+ core_pdata = dev_get_platdata(pdev->dev.parent);
+ denc_pdata = core_pdata->denc;
+ ab8500_wreg(&pdev->dev, AB8500_TVOUT_CTRL2,
+ AB8500_VAL2REG(AB8500_TVOUT_CTRL2,
+ DENC_DDR, denc_pdata->ddr_enable) |
+ AB8500_VAL2REG(AB8500_TVOUT_CTRL2, SWAP_DDR_DATA_IN,
+ denc_pdata->ddr_little_endian));
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int debugfs_ab8500_open_file(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+#define DEBUG_BUF_SIZE 900
+
+#define AB8500_GPIO_DIR5 0x1014
+#define AB8500_GPIO_DIR5_35_SHIFT 2
+#define AB8500_GPIO_DIR5_35_MASK (1 << AB8500_GPIO_DIR5_35_SHIFT)
+#define AB8500_GPIO_OUT5 0x1024
+#define AB8500_GPIO_OUT5_35_SHIFT 2
+#define AB8500_GPIO_OUT5_35_MASK (1 << AB8500_GPIO_OUT5_35_SHIFT)
+#define AB8500_GPIO_OUT5_35_VIDEO 0
+#define AB8500_GPIO_OUT5_35_AUDIO 1
+#define AB8500_GPIO_NPUD5 0x1034
+#define AB8500_GPIO_NPUD5_35_SHIFT 2
+#define AB8500_GPIO_NPUD5_35_MASK (1 << AB8500_GPIO_NPUD5_35_SHIFT)
+#define AB8500_GPIO_NPUD5_35_ACTIVE 0
+#define AB8500_GPIO_NPUD5_35_INACTIVE 1
+
+static ssize_t debugfs_ab8500_dump_regs(struct file *file, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ int ret = 0;
+ size_t data_size = 0;
+ char buffer[DEBUG_BUF_SIZE];
+ struct device *dev = file->private_data;
+
+ data_size += sprintf(buffer + data_size,
+ "AB8500 DENC registers:\n"
+ "------Regulators etc ----------\n"
+ "CTRL3 : 0x%04x = 0x%02x\n"
+ "SYSULPCLK_CONF: 0x%04x = 0x%02x\n"
+ "SYSCLK_CTRL : 0x%04x = 0x%02x\n"
+ "REGU_MISC1 : 0x%04x = 0x%02x\n"
+ "VAUX12_REGU : 0x%04x = 0x%02x\n"
+ "VAUX1_SEL1 : 0x%04x = 0x%02x\n"
+ "------TVout only --------------\n"
+ "DENC_CONF0 : 0x%04x = 0x%02x\n"
+ "DENC_CONF1 : 0x%04x = 0x%02x\n"
+ "DENC_CONF2 : 0x%04x = 0x%02x\n"
+ "DENC_CONF6 : 0x%04x = 0x%02x\n"
+ "DENC_CONF8 : 0x%04x = 0x%02x\n"
+ "TVOUT_CTRL : 0x%04x = 0x%02x\n"
+ "TVOUT_CTRL2 : 0x%04x = 0x%02x\n"
+ "IT_MASK1 : 0x%04x = 0x%02x\n"
+ "------AV connector-------------\n"
+ "GPIO_DIR5 : 0x%04x = 0x%02x\n"
+ "GPIO_OUT5 : 0x%04x = 0x%02x\n"
+ "GPIO_NPUD5 : 0x%04x = 0x%02x\n"
+ ,
+ AB8500_CTRL3, ab8500_rreg(dev, AB8500_CTRL3),
+ AB8500_SYS_ULP_CLK_CONF, ab8500_rreg(dev,
+ AB8500_SYS_ULP_CLK_CONF),
+ AB8500_SYS_CLK_CTRL, ab8500_rreg(dev, AB8500_SYS_CLK_CTRL),
+ AB8500_REGU_MISC1, ab8500_rreg(dev, AB8500_REGU_MISC1),
+ AB8500_VAUX12_REGU, ab8500_rreg(dev, AB8500_VAUX12_REGU),
+ AB8500_VAUX1_SEL, ab8500_rreg(dev, AB8500_VAUX1_SEL),
+ AB8500_DENC_CONF0, ab8500_rreg(dev, AB8500_DENC_CONF0),
+ AB8500_DENC_CONF1, ab8500_rreg(dev, AB8500_DENC_CONF1),
+ AB8500_DENC_CONF2, ab8500_rreg(dev, AB8500_DENC_CONF2),
+ AB8500_DENC_CONF6, ab8500_rreg(dev, AB8500_DENC_CONF6),
+ AB8500_DENC_CONF8, ab8500_rreg(dev, AB8500_DENC_CONF8),
+ AB8500_TVOUT_CTRL, ab8500_rreg(dev, AB8500_TVOUT_CTRL),
+ AB8500_TVOUT_CTRL2, ab8500_rreg(dev, AB8500_TVOUT_CTRL2),
+ AB8500_IT_MASK1, ab8500_rreg(dev, AB8500_IT_MASK1),
+ AB8500_GPIO_DIR5, ab8500_rreg(dev, AB8500_GPIO_DIR5),
+ AB8500_GPIO_OUT5, ab8500_rreg(dev, AB8500_GPIO_OUT5),
+ AB8500_GPIO_NPUD5, ab8500_rreg(dev, AB8500_GPIO_NPUD5)
+ );
+ if (data_size >= DEBUG_BUF_SIZE) {
+ printk(KERN_EMERG "AB8500 DENC: Buffer overrun\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* check if read done */
+ if (*f_pos > data_size)
+ goto out;
+
+ if (*f_pos + count > data_size)
+ count = data_size - *f_pos;
+
+ if (copy_to_user(buf, buffer + *f_pos, count))
+ ret = -EINVAL;
+ *f_pos += count;
+ ret = count;
+out:
+ return ret;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+/* Module init */
+static int __init ab8500_denc_init(void)
+{
+ return platform_driver_register(&ab8500_denc_driver);
+}
+module_init(ab8500_denc_init);
+
+static void __exit ab8500_denc_exit(void)
+{
+ platform_driver_unregister(&ab8500_denc_driver);
+}
+module_exit(ab8500_denc_exit);
+
+MODULE_AUTHOR("Marcel Tunnissen <marcel.tuennissen@stericsson.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ST-Ericsson AB8500 DENC driver");
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
new file mode 100644
index 00000000000..2eb8eba7ca4
--- /dev/null
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Arun R Murthy <arun.murthy@stericsson.com>
+ * Author: Daniel Willerud <daniel.willerud@stericsson.com>
+ * Author: Johan Palsson <johan.palsson@stericsson.com>
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500/ab8500-gpadc.h>
+
+/*
+ * GPADC register offsets
+ * Bank : 0x0A
+ */
+#define AB8500_GPADC_CTRL1_REG 0x00
+#define AB8500_GPADC_CTRL2_REG 0x01
+#define AB8500_GPADC_CTRL3_REG 0x02
+#define AB8500_GPADC_AUTO_TIMER_REG 0x03
+#define AB8500_GPADC_STAT_REG 0x04
+#define AB8500_GPADC_MANDATAL_REG 0x05
+#define AB8500_GPADC_MANDATAH_REG 0x06
+#define AB8500_GPADC_AUTODATAL_REG 0x07
+#define AB8500_GPADC_AUTODATAH_REG 0x08
+#define AB8500_GPADC_MUX_CTRL_REG 0x09
+
+/*
+ * OTP register offsets
+ * Bank : 0x15
+ */
+#define AB8500_GPADC_CAL_1 0x0F
+#define AB8500_GPADC_CAL_2 0x10
+#define AB8500_GPADC_CAL_3 0x11
+#define AB8500_GPADC_CAL_4 0x12
+#define AB8500_GPADC_CAL_5 0x13
+#define AB8500_GPADC_CAL_6 0x14
+#define AB8500_GPADC_CAL_7 0x15
+
+/* gpadc constants */
+#define EN_VINTCORE12 0x04
+#define EN_VTVOUT 0x02
+#define EN_GPADC 0x01
+#define DIS_GPADC 0x00
+#define SW_AVG_16 0x60
+#define ADC_SW_CONV 0x04
+#define EN_ICHAR 0x80
+#define BTEMP_PULL_UP 0x08
+#define EN_BUF 0x40
+#define DIS_ZERO 0x00
+#define GPADC_BUSY 0x01
+
+/* GPADC constants from AB8500 spec, UM0836 */
+#define ADC_RESOLUTION 1024
+#define ADC_CH_BTEMP_MIN 0
+#define ADC_CH_BTEMP_MAX 1350
+#define ADC_CH_DIETEMP_MIN 0
+#define ADC_CH_DIETEMP_MAX 1350
+#define ADC_CH_CHG_V_MIN 0
+#define ADC_CH_CHG_V_MAX 20030
+#define ADC_CH_ACCDET2_MIN 0
+#define ADC_CH_ACCDET2_MAX 2500
+#define ADC_CH_VBAT_MIN 2300
+#define ADC_CH_VBAT_MAX 4800
+#define ADC_CH_CHG_I_MIN 0
+#define ADC_CH_CHG_I_MAX 1500
+#define ADC_CH_BKBAT_MIN 0
+#define ADC_CH_BKBAT_MAX 3200
+
+/* This is used to not lose precision when dividing to get gain and offset */
+#define CALIB_SCALE 1000
+
+enum cal_channels {
+ ADC_INPUT_VMAIN = 0,
+ ADC_INPUT_BTEMP,
+ ADC_INPUT_VBAT,
+ NBR_CAL_INPUTS,
+};
+
+/**
+ * struct adc_cal_data - Table for storing gain and offset for the calibrated
+ * ADC channels
+ * @gain: Gain of the ADC channel
+ * @offset: Offset of the ADC channel
+ */
+struct adc_cal_data {
+ u64 gain;
+ u64 offset;
+};
+
+/**
+ * struct ab8500_gpadc - AB8500 GPADC device information
+ * @chip_id ABB chip id
+ * @dev: pointer to the struct device
+ * @node: a list of AB8500 GPADCs, hence prepared for
+ reentrance
+ * @ab8500_gpadc_complete: pointer to the struct completion, to indicate
+ * the completion of gpadc conversion
+ * @ab8500_gpadc_lock: structure of type mutex
+ * @regu: pointer to the struct regulator
+ * @irq: interrupt number that is used by gpadc
+ * @cal_data array of ADC calibration data structs
+ */
+struct ab8500_gpadc {
+ u8 chip_id;
+ struct device *dev;
+ struct list_head node;
+ struct completion ab8500_gpadc_complete;
+ struct mutex ab8500_gpadc_lock;
+ struct regulator *regu;
+ int irq;
+ struct adc_cal_data cal_data[NBR_CAL_INPUTS];
+};
+
+static LIST_HEAD(ab8500_gpadc_list);
+
+/**
+ * ab8500_gpadc_get() - returns a reference to the primary AB8500 GPADC
+ * (i.e. the first GPADC in the instance list)
+ */
+struct ab8500_gpadc *ab8500_gpadc_get(void)
+{
+ struct ab8500_gpadc *gpadc;
+ gpadc = list_first_entry(&ab8500_gpadc_list, struct ab8500_gpadc, node);
+
+ return gpadc;
+}
+EXPORT_SYMBOL(ab8500_gpadc_get);
+
+static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input,
+ int ad_value)
+{
+ int res;
+
+ switch (input) {
+ case MAIN_CHARGER_V:
+ /* For some reason we don't have calibrated data */
+ if (!gpadc->cal_data[ADC_INPUT_VMAIN].gain) {
+ res = ADC_CH_CHG_V_MIN + (ADC_CH_CHG_V_MAX -
+ ADC_CH_CHG_V_MIN) * ad_value /
+ ADC_RESOLUTION;
+ break;
+ }
+ /* Here we can use the calibrated data */
+ res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VMAIN].gain +
+ gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE;
+ break;
+
+ case BAT_CTRL:
+ case BTEMP_BALL:
+ case ACC_DETECT1:
+ case ADC_AUX1:
+ case ADC_AUX2:
+ /* For some reason we don't have calibrated data */
+ if (!gpadc->cal_data[ADC_INPUT_BTEMP].gain) {
+ res = ADC_CH_BTEMP_MIN + (ADC_CH_BTEMP_MAX -
+ ADC_CH_BTEMP_MIN) * ad_value /
+ ADC_RESOLUTION;
+ break;
+ }
+ /* Here we can use the calibrated data */
+ res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_BTEMP].gain +
+ gpadc->cal_data[ADC_INPUT_BTEMP].offset) / CALIB_SCALE;
+ break;
+
+ case MAIN_BAT_V:
+ /* For some reason we don't have calibrated data */
+ if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) {
+ res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX -
+ ADC_CH_VBAT_MIN) * ad_value /
+ ADC_RESOLUTION;
+ break;
+ }
+ /* Here we can use the calibrated data */
+ res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VBAT].gain +
+ gpadc->cal_data[ADC_INPUT_VBAT].offset) / CALIB_SCALE;
+ break;
+
+ case DIE_TEMP:
+ res = ADC_CH_DIETEMP_MIN +
+ (ADC_CH_DIETEMP_MAX - ADC_CH_DIETEMP_MIN) * ad_value /
+ ADC_RESOLUTION;
+ break;
+
+ case ACC_DETECT2:
+ res = ADC_CH_ACCDET2_MIN +
+ (ADC_CH_ACCDET2_MAX - ADC_CH_ACCDET2_MIN) * ad_value /
+ ADC_RESOLUTION;
+ break;
+
+ case VBUS_V:
+ res = ADC_CH_CHG_V_MIN +
+ (ADC_CH_CHG_V_MAX - ADC_CH_CHG_V_MIN) * ad_value /
+ ADC_RESOLUTION;
+ break;
+
+ case MAIN_CHARGER_C:
+ case USB_CHARGER_C:
+ res = ADC_CH_CHG_I_MIN +
+ (ADC_CH_CHG_I_MAX - ADC_CH_CHG_I_MIN) * ad_value /
+ ADC_RESOLUTION;
+ break;
+
+ case BK_BAT_V:
+ res = ADC_CH_BKBAT_MIN +
+ (ADC_CH_BKBAT_MAX - ADC_CH_BKBAT_MIN) * ad_value /
+ ADC_RESOLUTION;
+ break;
+
+ default:
+ dev_err(gpadc->dev,
+ "unknown channel, not possible to convert\n");
+ res = -EINVAL;
+ break;
+
+ }
+ return res;
+}
+
+/**
+ * ab8500_gpadc_convert() - gpadc conversion
+ * @input: analog input to be converted to digital data
+ *
+ * This function converts the selected analog i/p to digital
+ * data.
+ */
+int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
+{
+ int ret;
+ u16 data = 0;
+ int looplimit = 0;
+ u8 val, low_data, high_data;
+
+ if (!gpadc)
+ return -ENODEV;
+
+ mutex_lock(&gpadc->ab8500_gpadc_lock);
+ /* Enable VTVout LDO this is required for GPADC */
+ regulator_enable(gpadc->regu);
+
+ /* Check if ADC is not busy, lock and proceed */
+ do {
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_STAT_REG, &val);
+ if (ret < 0)
+ goto out;
+ if (!(val & GPADC_BUSY))
+ break;
+ msleep(10);
+ } while (++looplimit < 10);
+ if (looplimit >= 10 && (val & GPADC_BUSY)) {
+ dev_err(gpadc->dev, "gpadc_conversion: GPADC busy");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Enable GPADC */
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n");
+ goto out;
+ }
+
+ /* Select the input source and set average samples to 16 */
+ ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
+ AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16));
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: set avg samples failed\n");
+ goto out;
+ }
+
+ /*
+ * Enable ADC, buffering, select rising edge and enable ADC path
+ * charging current sense if it needed, ABB 3.0 needs some special
+ * treatment too.
+ */
+ switch (input) {
+ case MAIN_CHARGER_C:
+ case USB_CHARGER_C:
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
+ EN_BUF | EN_ICHAR,
+ EN_BUF | EN_ICHAR);
+ break;
+ case BTEMP_BALL:
+ if (gpadc->chip_id >= AB8500_CUT3P0) {
+ /* Turn on btemp pull-up on ABB 3.0 */
+ ret = abx500_mask_and_set_register_interruptible(
+ gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
+ EN_BUF | BTEMP_PULL_UP,
+ EN_BUF | BTEMP_PULL_UP);
+
+ /*
+ * Delay might be needed for ABB8500 cut 3.0, if not, remove
+ * when hardware will be availible
+ */
+ msleep(1);
+ break;
+ }
+ /* Intentional fallthrough */
+ default:
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF);
+ break;
+ }
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: select falling edge failed\n");
+ goto out;
+ }
+
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: start s/w conversion failed\n");
+ goto out;
+ }
+ /* wait for completion of conversion */
+ if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) {
+ dev_err(gpadc->dev,
+ "timeout: didnt recieve GPADC conversion interrupt\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Read the converted RAW data */
+ ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
+ AB8500_GPADC_MANDATAL_REG, &low_data);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n");
+ goto out;
+ }
+
+ ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
+ AB8500_GPADC_MANDATAH_REG, &high_data);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: read high data failed\n");
+ goto out;
+ }
+
+ data = (high_data << 8) | low_data;
+ /* Disable GPADC */
+ ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
+ AB8500_GPADC_CTRL1_REG, DIS_GPADC);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
+ goto out;
+ }
+ /* Disable VTVout LDO this is required for GPADC */
+ regulator_disable(gpadc->regu);
+ mutex_unlock(&gpadc->ab8500_gpadc_lock);
+ ret = ab8500_gpadc_ad_to_voltage(gpadc, input, data);
+ return ret;
+
+out:
+ /*
+ * It has shown to be needed to turn off the GPADC if an error occurs,
+ * otherwise we might have problem when waiting for the busy bit in the
+ * GPADC status register to go low. In V1.1 there wait_for_completion
+ * seems to timeout when waiting for an interrupt.. Not seen in V2.0
+ */
+ (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
+ AB8500_GPADC_CTRL1_REG, DIS_GPADC);
+ regulator_disable(gpadc->regu);
+ mutex_unlock(&gpadc->ab8500_gpadc_lock);
+ dev_err(gpadc->dev,
+ "gpadc_conversion: Failed to AD convert channel %d\n", input);
+ return ret;
+}
+EXPORT_SYMBOL(ab8500_gpadc_convert);
+
+/**
+ * ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion
+ * @irq: irq number
+ * @data: pointer to the data passed during request irq
+ *
+ * This is a interrupt service routine for s/w gpadc conversion completion.
+ * Notifies the gpadc completion is completed and the converted raw value
+ * can be read from the registers.
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc)
+{
+ struct ab8500_gpadc *gpadc = _gpadc;
+
+ complete(&gpadc->ab8500_gpadc_complete);
+
+ return IRQ_HANDLED;
+}
+
+static int otp_cal_regs[] = {
+ AB8500_GPADC_CAL_1,
+ AB8500_GPADC_CAL_2,
+ AB8500_GPADC_CAL_3,
+ AB8500_GPADC_CAL_4,
+ AB8500_GPADC_CAL_5,
+ AB8500_GPADC_CAL_6,
+ AB8500_GPADC_CAL_7,
+};
+
+static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
+{
+ int i;
+ int ret[ARRAY_SIZE(otp_cal_regs)];
+ u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)];
+
+ int vmain_high, vmain_low;
+ int btemp_high, btemp_low;
+ int vbat_high, vbat_low;
+
+ /* First we read all OTP registers and store the error code */
+ for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) {
+ ret[i] = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]);
+ if (ret[i] < 0)
+ dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n",
+ __func__, otp_cal_regs[i]);
+ }
+
+ /*
+ * The ADC calibration data is stored in OTP registers.
+ * The layout of the calibration data is outlined below and a more
+ * detailed description can be found in UM0836
+ *
+ * vm_h/l = vmain_high/low
+ * bt_h/l = btemp_high/low
+ * vb_h/l = vbat_high/low
+ *
+ * Data bits:
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | | vm_h9 | vm_h8
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ *
+ *
+ * Ideal output ADC codes corresponding to injected input voltages
+ * during manufacturing is:
+ *
+ * vmain_high: Vin = 19500mV / ADC ideal code = 997
+ * vmain_low: Vin = 315mV / ADC ideal code = 16
+ * btemp_high: Vin = 1300mV / ADC ideal code = 985
+ * btemp_low: Vin = 21mV / ADC ideal code = 16
+ * vbat_high: Vin = 4700mV / ADC ideal code = 982
+ * vbat_low: Vin = 2380mV / ADC ideal code = 33
+ */
+
+ /* Calculate gain and offset for VMAIN if all reads succeeded */
+ if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
+ vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
+ ((gpadc_cal[1] & 0x3F) << 2) |
+ ((gpadc_cal[2] & 0xC0) >> 6));
+
+ vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
+ (19500 - 315) / (vmain_high - vmain_low);
+
+ gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * 19500 -
+ (CALIB_SCALE * (19500 - 315) /
+ (vmain_high - vmain_low)) * vmain_high;
+ } else {
+ gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
+ }
+
+ /* Calculate gain and offset for BTEMP if all reads succeeded */
+ if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) {
+ btemp_high = (((gpadc_cal[2] & 0x01) << 9) |
+ (gpadc_cal[3] << 1) |
+ ((gpadc_cal[4] & 0x80) >> 7));
+
+ btemp_low = ((gpadc_cal[4] & 0x7C) >> 2);
+
+ gpadc->cal_data[ADC_INPUT_BTEMP].gain =
+ CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low);
+
+ gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 -
+ (CALIB_SCALE * (1300 - 21) /
+ (btemp_high - btemp_low)) * btemp_high;
+ } else {
+ gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0;
+ }
+
+ /* Calculate gain and offset for VBAT if all reads succeeded */
+ if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) {
+ vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]);
+ vbat_low = ((gpadc_cal[6] & 0xFC) >> 2);
+
+ gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE *
+ (4700 - 2380) / (vbat_high - vbat_low);
+
+ gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 -
+ (CALIB_SCALE * (4700 - 2380) /
+ (vbat_high - vbat_low)) * vbat_high;
+ } else {
+ gpadc->cal_data[ADC_INPUT_VBAT].gain = 0;
+ }
+
+ dev_dbg(gpadc->dev, "VMAIN gain %llu offset %llu\n",
+ gpadc->cal_data[ADC_INPUT_VMAIN].gain,
+ gpadc->cal_data[ADC_INPUT_VMAIN].offset);
+
+ dev_dbg(gpadc->dev, "BTEMP gain %llu offset %llu\n",
+ gpadc->cal_data[ADC_INPUT_BTEMP].gain,
+ gpadc->cal_data[ADC_INPUT_BTEMP].offset);
+
+ dev_dbg(gpadc->dev, "VBAT gain %llu offset %llu\n",
+ gpadc->cal_data[ADC_INPUT_VBAT].gain,
+ gpadc->cal_data[ADC_INPUT_VBAT].offset);
+}
+
+static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = kzalloc(sizeof(struct ab8500_gpadc), GFP_KERNEL);
+ if (!gpadc) {
+ dev_err(&pdev->dev, "Error: No memory\n");
+ return -ENOMEM;
+ }
+
+ gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END");
+ if (gpadc->irq < 0) {
+ dev_err(gpadc->dev, "failed to get platform irq-%d\n",
+ gpadc->irq);
+ ret = gpadc->irq;
+ goto fail;
+ }
+
+ gpadc->dev = &pdev->dev;
+ mutex_init(&gpadc->ab8500_gpadc_lock);
+
+ /* Initialize completion used to notify completion of conversion */
+ init_completion(&gpadc->ab8500_gpadc_complete);
+
+ /* Register interrupt - SwAdcComplete */
+ ret = request_threaded_irq(gpadc->irq, NULL,
+ ab8500_bm_gpswadcconvend_handler,
+ IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc", gpadc);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
+ gpadc->irq);
+ goto fail;
+ }
+
+ /* Get Chip ID of the ABB ASIC */
+ ret = abx500_get_chip_id(gpadc->dev);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "failed to get chip ID\n");
+ goto fail_irq;
+ }
+ gpadc->chip_id = (u8) ret;
+
+ /* VTVout LDO used to power up ab8500-GPADC */
+ gpadc->regu = regulator_get(&pdev->dev, "ab8500-gpadc");
+ if (IS_ERR(gpadc->regu)) {
+ ret = PTR_ERR(gpadc->regu);
+ dev_err(gpadc->dev, "failed to get vtvout LDO\n");
+ goto fail_irq;
+ }
+ ab8500_gpadc_read_calibration_data(gpadc);
+ list_add_tail(&gpadc->node, &ab8500_gpadc_list);
+ dev_dbg(gpadc->dev, "probe success\n");
+ return 0;
+fail_irq:
+ free_irq(gpadc->irq, gpadc);
+fail:
+ kfree(gpadc);
+ gpadc = NULL;
+ return ret;
+}
+
+static int __devexit ab8500_gpadc_remove(struct platform_device *pdev)
+{
+ struct ab8500_gpadc *gpadc = platform_get_drvdata(pdev);
+
+ /* remove this gpadc entry from the list */
+ list_del(&gpadc->node);
+ /* remove interrupt - completion of Sw ADC conversion */
+ free_irq(gpadc->irq, gpadc);
+ /* disable VTVout LDO that is being used by GPADC */
+ regulator_put(gpadc->regu);
+ kfree(gpadc);
+ gpadc = NULL;
+ return 0;
+}
+
+static struct platform_driver ab8500_gpadc_driver = {
+ .probe = ab8500_gpadc_probe,
+ .remove = __devexit_p(ab8500_gpadc_remove),
+ .driver = {
+ .name = "ab8500-gpadc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab8500_gpadc_init(void)
+{
+ return platform_driver_register(&ab8500_gpadc_driver);
+}
+
+static void __exit ab8500_gpadc_exit(void)
+{
+ platform_driver_unregister(&ab8500_gpadc_driver);
+}
+
+subsys_initcall_sync(ab8500_gpadc_init);
+module_exit(ab8500_gpadc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson");
+MODULE_ALIAS("platform:ab8500_gpadc");
+MODULE_DESCRIPTION("AB8500 GPADC driver");
diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c
new file mode 100644
index 00000000000..90f9f432786
--- /dev/null
+++ b/drivers/mfd/ab8500-i2c.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
+ * License Terms: GNU General Public License v2
+ * This file was based on drivers/mfd/ab8500-spi.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ab8500.h>
+
+#include <mach/prcmu-fw-api.h>
+
+static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
+{
+ int ret;
+
+ ret = prcmu_abb_write((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
+ if (ret < 0)
+ dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
+ return ret;
+}
+
+static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
+{
+ int ret;
+ u8 data;
+
+ ret = prcmu_abb_read((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
+ if (ret < 0) {
+ dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
+ return ret;
+ }
+ return (int)data;
+}
+
+static int __devinit ab8500_i2c_probe(struct platform_device *plf)
+{
+ struct ab8500 *ab8500;
+ struct resource *resource;
+ int ret;
+
+ ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
+ if (!ab8500)
+ return -ENOMEM;
+
+ ab8500->dev = &plf->dev;
+
+ resource = platform_get_resource(plf, IORESOURCE_IRQ, 0);
+ if (!resource) {
+ kfree(ab8500);
+ return -ENODEV;
+ }
+
+ ab8500->irq = resource->start;
+
+ ab8500->read = ab8500_i2c_read;
+ ab8500->write = ab8500_i2c_write;
+
+ platform_set_drvdata(plf, ab8500);
+
+ ret = ab8500_init(ab8500);
+ if (ret)
+ kfree(ab8500);
+
+ return ret;
+}
+
+static int __devexit ab8500_i2c_remove(struct platform_device *plf)
+{
+ struct ab8500 *ab8500 = platform_get_drvdata(plf);
+
+ ab8500_exit(ab8500);
+ kfree(ab8500);
+
+ return 0;
+}
+
+static struct platform_driver ab8500_i2c_driver = {
+ .driver = {
+ .name = "ab8500-i2c",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab8500_i2c_probe,
+ .remove = __devexit_p(ab8500_i2c_remove)
+};
+
+static int __init ab8500_i2c_init(void)
+{
+ return platform_driver_register(&ab8500_i2c_driver);
+}
+
+static void __exit ab8500_i2c_exit(void)
+{
+ platform_driver_unregister(&ab8500_i2c_driver);
+}
+arch_initcall(ab8500_i2c_init);
+module_exit(ab8500_i2c_exit);
+
+MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com");
+MODULE_DESCRIPTION("AB8500 Core access via PRCMU I2C");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c
deleted file mode 100644
index b81d4f768ef..00000000000
--- a/drivers/mfd/ab8500-spi.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * License Terms: GNU General Public License v2
- * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/spi/spi.h>
-#include <linux/mfd/ab8500.h>
-
-/*
- * This funtion writes to any AB8500 registers using
- * SPI protocol & before it writes it packs the data
- * in the below 24 bit frame format
- *
- * *|------------------------------------|
- * *| 23|22...18|17.......10|9|8|7......0|
- * *| r/w bank adr data |
- * * ------------------------------------
- *
- * This function shouldn't be called from interrupt
- * context
- */
-static int ab8500_spi_write(struct ab8500 *ab8500, u16 addr, u8 data)
-{
- struct spi_device *spi = container_of(ab8500->dev, struct spi_device,
- dev);
- unsigned long spi_data = addr << 10 | data;
- struct spi_transfer xfer;
- struct spi_message msg;
-
- ab8500->tx_buf[0] = spi_data;
- ab8500->rx_buf[0] = 0;
-
- xfer.tx_buf = ab8500->tx_buf;
- xfer.rx_buf = NULL;
- xfer.len = sizeof(unsigned long);
-
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
-
- return spi_sync(spi, &msg);
-}
-
-static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr)
-{
- struct spi_device *spi = container_of(ab8500->dev, struct spi_device,
- dev);
- unsigned long spi_data = 1 << 23 | addr << 10;
- struct spi_transfer xfer;
- struct spi_message msg;
- int ret;
-
- ab8500->tx_buf[0] = spi_data;
- ab8500->rx_buf[0] = 0;
-
- xfer.tx_buf = ab8500->tx_buf;
- xfer.rx_buf = ab8500->rx_buf;
- xfer.len = sizeof(unsigned long);
-
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
-
- ret = spi_sync(spi, &msg);
- if (!ret)
- ret = ab8500->rx_buf[0];
-
- return ret;
-}
-
-static int __devinit ab8500_spi_probe(struct spi_device *spi)
-{
- struct ab8500 *ab8500;
- int ret;
-
- ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
- if (!ab8500)
- return -ENOMEM;
-
- ab8500->dev = &spi->dev;
- ab8500->irq = spi->irq;
-
- ab8500->read = ab8500_spi_read;
- ab8500->write = ab8500_spi_write;
-
- spi_set_drvdata(spi, ab8500);
-
- ret = ab8500_init(ab8500);
- if (ret)
- kfree(ab8500);
-
- return ret;
-}
-
-static int __devexit ab8500_spi_remove(struct spi_device *spi)
-{
- struct ab8500 *ab8500 = spi_get_drvdata(spi);
-
- ab8500_exit(ab8500);
- kfree(ab8500);
-
- return 0;
-}
-
-static struct spi_driver ab8500_spi_driver = {
- .driver = {
- .name = "ab8500",
- .owner = THIS_MODULE,
- },
- .probe = ab8500_spi_probe,
- .remove = __devexit_p(ab8500_spi_remove)
-};
-
-static int __init ab8500_spi_init(void)
-{
- return spi_register_driver(&ab8500_spi_driver);
-}
-subsys_initcall(ab8500_spi_init);
-
-static void __exit ab8500_spi_exit(void)
-{
- spi_unregister_driver(&ab8500_spi_driver);
-}
-module_exit(ab8500_spi_exit);
-
-MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com");
-MODULE_DESCRIPTION("AB8500 SPI");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c
new file mode 100644
index 00000000000..e6de6a279a5
--- /dev/null
+++ b/drivers/mfd/ab8500-sysctrl.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> for ST Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/signal.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500/sysctrl.h>
+
+static struct device *sysctrl_dev;
+
+void ab8500_power_off(void)
+{
+ sigset_t old;
+ sigset_t all;
+
+ sigfillset(&all);
+
+ if (!sigprocmask(SIG_BLOCK, &all, &old)) {
+ (void)ab8500_sysctrl_set(AB8500_STW4500CTRL1,
+ AB8500_STW4500CTRL1_SWOFF |
+ AB8500_STW4500CTRL1_SWRESET4500N);
+ (void)sigprocmask(SIG_SETMASK, &old, NULL);
+ }
+}
+
+static inline bool valid_bank(u8 bank)
+{
+ return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
+ (bank == AB8500_SYS_CTRL2_BLOCK));
+}
+
+int ab8500_sysctrl_read(u16 reg, u8 *value)
+{
+ u8 bank;
+
+ if (sysctrl_dev == NULL)
+ return -EAGAIN;
+
+ bank = (reg >> 8);
+ if (!valid_bank(bank))
+ return -EINVAL;
+
+ return abx500_get_register_interruptible(sysctrl_dev, bank,
+ (u8)(reg & 0xFF), value);
+}
+
+int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
+{
+ u8 bank;
+
+ if (sysctrl_dev == NULL)
+ return -EAGAIN;
+
+ bank = (reg >> 8);
+ if (!valid_bank(bank))
+ return -EINVAL;
+
+ return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
+ (u8)(reg & 0xFF), mask, value);
+}
+
+static int __devinit ab8500_sysctrl_probe(struct platform_device *pdev)
+{
+ struct ab8500_platform_data *plat;
+
+ sysctrl_dev = &pdev->dev;
+ plat = dev_get_platdata(pdev->dev.parent);
+ if (plat->pm_power_off)
+ pm_power_off = ab8500_power_off;
+ return 0;
+}
+
+static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev)
+{
+ sysctrl_dev = NULL;
+ return 0;
+}
+
+static struct platform_driver ab8500_sysctrl_driver = {
+ .driver = {
+ .name = "ab8500-sysctrl",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab8500_sysctrl_probe,
+ .remove = __devexit_p(ab8500_sysctrl_remove),
+};
+
+static int __init ab8500_sysctrl_init(void)
+{
+ return platform_driver_register(&ab8500_sysctrl_driver);
+}
+subsys_initcall(ab8500_sysctrl_init);
+
+MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com");
+MODULE_DESCRIPTION("AB8500 system control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/cg2900/Makefile b/drivers/mfd/cg2900/Makefile
new file mode 100644
index 00000000000..14c29bb6e73
--- /dev/null
+++ b/drivers/mfd/cg2900/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for ST-Ericsson CG2900 connectivity combo controller
+#
+
+obj-$(CONFIG_MFD_CG2900) += cg2900_core.o cg2900_lib.o
+export-objs := cg2900_core.o cg2900_lib.o
+
+obj-$(CONFIG_MFD_CG2900) += cg2900_char_devices.o
+
+obj-$(CONFIG_MFD_CG2900_TEST) += cg2900_test.o
+
+obj-$(CONFIG_MFD_CG2900_CHIP) += cg2900_chip.o
+obj-$(CONFIG_MFD_STLC2690_CHIP) += stlc2690_chip.o
+
+obj-$(CONFIG_MFD_CG2900_AUDIO) += cg2900_audio.o
diff --git a/drivers/mfd/cg2900/cg2900_audio.c b/drivers/mfd/cg2900/cg2900_audio.c
new file mode 100644
index 00000000000..5dcfb1e047d
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_audio.c
@@ -0,0 +1,3486 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth Audio Driver for ST-Ericsson CG2900 controller.
+ */
+#define NAME "cg2900_audio"
+#define pr_fmt(fmt) NAME ": " fmt "\n"
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/mfd/cg2900.h>
+#include <linux/mfd/cg2900_audio.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+
+#include "cg2900_chip.h"
+
+#define MAX_NBR_OF_USERS 10
+#define FIRST_USER 1
+
+/*
+ * This is a default ACL handle. It is necessary to provide to the chip, but
+ * does not actually do anything.
+ */
+#define DEFAULT_ACL_HANDLE 0x0001
+
+/* Use a timeout of 5 seconds when waiting for a command response */
+#define RESP_TIMEOUT 5000
+
+#define BT_DEV (info->dev_bt)
+#define FM_DEV (info->dev_fm)
+
+/* Bluetooth error codes */
+#define HCI_BT_ERROR_NO_ERROR 0x00
+
+/* Used to select proper API, ignoring subrevisions etc */
+enum chip_revision {
+ CHIP_REV_PG1,
+ CHIP_REV_PG2
+};
+
+/**
+ * enum chip_resp_state - State when communicating with the CG2900 controller.
+ * @IDLE: No outstanding packets to the controller.
+ * @WAITING: Packet has been sent to the controller. Waiting for
+ * response.
+ * @RESP_RECEIVED: Response from controller has been received but not yet
+ * handled.
+ */
+enum chip_resp_state {
+ IDLE,
+ WAITING,
+ RESP_RECEIVED
+};
+
+/**
+ * enum main_state - Main state for the CG2900 Audio driver.
+ * @OPENED: Audio driver has registered to CG2900 Core.
+ * @CLOSED: Audio driver is not registered to CG2900 Core.
+ * @RESET: A reset of CG2900 Core has occurred and no user has re-opened
+ * the audio driver.
+ */
+enum main_state {
+ OPENED,
+ CLOSED,
+ RESET
+};
+
+/**
+ * struct endpoint_list - List for storing endpoint configuration nodes.
+ * @ep_list: Pointer to first node in list.
+ * @management_mutex: Mutex for handling access to list.
+ */
+struct endpoint_list {
+ struct list_head ep_list;
+ struct mutex management_mutex;
+};
+
+/**
+ * struct endpoint_config_node - Node for storing endpoint configuration.
+ * @list: list_head struct.
+ * @endpoint_id: Endpoint ID.
+ * @config: Stored configuration for this endpoint.
+ */
+struct endpoint_config_node {
+ struct list_head list;
+ enum cg2900_audio_endpoint_id endpoint_id;
+ union cg2900_endpoint_config_union config;
+};
+
+/**
+ * struct audio_info - Main CG2900 Audio driver info structure.
+ * @list: list_head struct.
+ * @state: Current state of the CG2900 Audio driver.
+ * @revision: Chip revision, used to select API.
+ * @misc_dev: The misc device created by this driver.
+ * @misc_registered: True if misc device is registered.
+ * @parent: Parent device.
+ * @dev_bt: Device registered by this driver for the BT
+ * audio channel.
+ * @dev_fm: Device registered by this driver for the FM
+ * audio channel.
+ * @filp: Current char device file pointer.
+ * @management_mutex: Mutex for handling access to CG2900 Audio driver
+ * management.
+ * @bt_mutex: Mutex for handling access to BT audio channel.
+ * @fm_mutex: Mutex for handling access to FM audio channel.
+ * @nbr_of_users_active: Number of sessions open in the CG2900 Audio
+ * driver.
+ * @i2s_config: DAI I2S configuration.
+ * @i2s_pcm_config: DAI PCM_I2S configuration.
+ * @i2s_config_known: @true if @i2s_config has been set,
+ * @false otherwise.
+ * @i2s_pcm_config_known: @true if @i2s_pcm_config has been set,
+ * @false otherwise.
+ * @endpoints: List containing the endpoint configurations.
+ * @stream_ids: Bitmask for in-use stream ids (only used with
+ * PG2 chip API).
+ */
+struct audio_info {
+ struct list_head list;
+ enum main_state state;
+ enum chip_revision revision;
+ struct miscdevice misc_dev;
+ bool misc_registered;
+ struct device *parent;
+ struct device *dev_bt;
+ struct device *dev_fm;
+ struct file *filp;
+ struct mutex management_mutex;
+ struct mutex bt_mutex;
+ struct mutex fm_mutex;
+ int nbr_of_users_active;
+ struct cg2900_dai_conf_i2s i2s_config;
+ struct cg2900_dai_conf_i2s_pcm i2s_pcm_config;
+ bool i2s_config_known;
+ bool i2s_pcm_config_known;
+ struct endpoint_list endpoints;
+ u8 stream_ids[16];
+};
+
+/**
+ * struct audio_user - CG2900 audio user info structure.
+ * @session: Stored session for the char device.
+ * @resp_state: State for controller communications.
+ * @info: CG2900 audio info structure.
+ */
+struct audio_user {
+ int session;
+ enum chip_resp_state resp_state;
+ struct audio_info *info;
+};
+
+/**
+ * struct audio_cb_info - Callback info structure registered in @user_data.
+ * @user: Audio user currently awaiting data on the channel.
+ * @wq: Wait queue for this channel.
+ * @skb_queue: Sk buffer queue.
+ */
+struct audio_cb_info {
+ struct audio_user *user;
+ wait_queue_head_t wq;
+ struct sk_buff_head skb_queue;
+};
+
+/**
+ * struct char_dev_info - CG2900 character device info structure.
+ * @session: Stored session for the char device.
+ * @stored_data: Data returned when executing last command, if any.
+ * @stored_data_len: Length of @stored_data in bytes.
+ * @management_mutex: Mutex for handling access to char dev management.
+ * @rw_mutex: Mutex for handling access to char dev writes and reads.
+ * @info: CG2900 audio info struct.
+ * @rx_queue: Data queue.
+ */
+struct char_dev_info {
+ int session;
+ u8 *stored_data;
+ int stored_data_len;
+ struct mutex management_mutex;
+ struct mutex rw_mutex;
+ struct audio_info *info;
+ struct sk_buff_head rx_queue;
+};
+
+/*
+ * cg2900_audio_devices - List of active CG2900 audio devices.
+ */
+LIST_HEAD(cg2900_audio_devices);
+
+/*
+ * cg2900_audio_sessions - Pointers to currently opened sessions (maps
+ * session ID to user info).
+ */
+static struct audio_user *cg2900_audio_sessions[MAX_NBR_OF_USERS];
+
+/*
+ * Internal conversion functions
+ *
+ * Since the CG2900 APIs uses several different ways to encode the
+ * same parameter in different cases, we have to use translator
+ * functions.
+ */
+
+/**
+ * session_config_sample_rate() - Convert sample rate to format used in VS_Set_SessionConfiguration.
+ * @rate: Sample rate in API encoding.
+ */
+static u8 session_config_sample_rate(enum cg2900_endpoint_sample_rate rate)
+{
+ static const u8 codes[] = {
+ [ENDPOINT_SAMPLE_RATE_8_KHZ] = CG2900_BT_SESSION_RATE_8K,
+ [ENDPOINT_SAMPLE_RATE_16_KHZ] = CG2900_BT_SESSION_RATE_16K,
+ [ENDPOINT_SAMPLE_RATE_44_1_KHZ] = CG2900_BT_SESSION_RATE_44_1K,
+ [ENDPOINT_SAMPLE_RATE_48_KHZ] = CG2900_BT_SESSION_RATE_48K
+ };
+
+ return codes[rate];
+}
+
+/**
+ * mc_i2s_sample_rate() - Convert sample rate to format used in VS_Port_Config for I2S.
+ * @rate: Sample rate in API encoding.
+ */
+static u8 mc_i2s_sample_rate(enum cg2900_dai_sample_rate rate)
+{
+ static const u8 codes[] = {
+ [SAMPLE_RATE_8] = CG2900_MC_I2S_SAMPLE_RATE_8,
+ [SAMPLE_RATE_16] = CG2900_MC_I2S_SAMPLE_RATE_16,
+ [SAMPLE_RATE_44_1] = CG2900_MC_I2S_SAMPLE_RATE_44_1,
+ [SAMPLE_RATE_48] = CG2900_MC_I2S_SAMPLE_RATE_48
+ };
+
+ return codes[rate];
+}
+
+/**
+ * mc_pcm_sample_rate() - Convert sample rate to format used in VS_Port_Config for PCM/I2S.
+ * @rate: Sample rate in API encoding.
+ */
+static u8 mc_pcm_sample_rate(enum cg2900_dai_sample_rate rate)
+{
+ static const u8 codes[] = {
+ [SAMPLE_RATE_8] = CG2900_MC_PCM_SAMPLE_RATE_8,
+ [SAMPLE_RATE_16] = CG2900_MC_PCM_SAMPLE_RATE_16,
+ [SAMPLE_RATE_44_1] = CG2900_MC_PCM_SAMPLE_RATE_44_1,
+ [SAMPLE_RATE_48] = CG2900_MC_PCM_SAMPLE_RATE_48
+ };
+
+ return codes[rate];
+}
+
+/**
+ * mc_i2s_channel_select() - Convert channel selection to format used in VS_Port_Config.
+ * @sel: Channel selection in API encoding.
+ */
+static u8 mc_i2s_channel_select(enum cg2900_dai_channel_sel sel)
+{
+ static const u8 codes[] = {
+ [CHANNEL_SELECTION_RIGHT] = CG2900_MC_I2S_RIGHT_CHANNEL,
+ [CHANNEL_SELECTION_LEFT] = CG2900_MC_I2S_LEFT_CHANNEL,
+ [CHANNEL_SELECTION_BOTH] = CG2900_MC_I2S_BOTH_CHANNELS
+ };
+ return codes[sel];
+}
+
+/**
+ * get_fs_duration() - Convert framesync-enumeration to real value.
+ * @duration: Framsync duration (API encoding).
+ *
+ * Returns:
+ * Duration in bits.
+ */
+static u16 get_fs_duration(enum cg2900_dai_fs_duration duration)
+{
+ static const u16 values[] = {
+ [SYNC_DURATION_8] = 8,
+ [SYNC_DURATION_16] = 16,
+ [SYNC_DURATION_24] = 24,
+ [SYNC_DURATION_32] = 32,
+ [SYNC_DURATION_48] = 48,
+ [SYNC_DURATION_50] = 50,
+ [SYNC_DURATION_64] = 64,
+ [SYNC_DURATION_75] = 75,
+ [SYNC_DURATION_96] = 96,
+ [SYNC_DURATION_125] = 125,
+ [SYNC_DURATION_128] = 128,
+ [SYNC_DURATION_150] = 150,
+ [SYNC_DURATION_192] = 192,
+ [SYNC_DURATION_250] = 250,
+ [SYNC_DURATION_256] = 256,
+ [SYNC_DURATION_300] = 300,
+ [SYNC_DURATION_384] = 384,
+ [SYNC_DURATION_500] = 500,
+ [SYNC_DURATION_512] = 512,
+ [SYNC_DURATION_600] = 600,
+ [SYNC_DURATION_768] = 768
+ };
+ return values[duration];
+}
+
+/**
+ * mc_i2s_role() - Convert master/slave encoding to format for I2S-ports.
+ * @mode: Master/slave in API encoding.
+ */
+static u8 mc_i2s_role(enum cg2900_dai_mode mode)
+{
+ if (mode == DAI_MODE_SLAVE)
+ return CG2900_I2S_MODE_SLAVE;
+ else
+ return CG2900_I2S_MODE_MASTER;
+}
+
+/**
+ * mc_pcm_role() - Convert master/slave encoding to format for PCM/I2S-port.
+ * @mode: Master/slave in API encoding.
+ */
+static u8 mc_pcm_role(enum cg2900_dai_mode mode)
+{
+ if (mode == DAI_MODE_SLAVE)
+ return CG2900_PCM_MODE_SLAVE;
+ else
+ return CG2900_PCM_MODE_MASTER;
+}
+
+/**
+ * fm_get_conversion() - Convert sample rate to convert up/down used in X_Set_Control FM commands.
+ * @srate: Sample rate.
+ */
+static u16 fm_get_conversion(enum cg2900_endpoint_sample_rate srate)
+{
+ if (srate >= ENDPOINT_SAMPLE_RATE_44_1_KHZ)
+ return CG2900_FM_CMD_SET_CTRL_CONV_UP;
+ else
+ return CG2900_FM_CMD_SET_CTRL_CONV_DOWN;
+}
+
+/**
+ * get_info() - Return info structure for this device.
+ * @dev: Current device.
+ *
+ * This function returns the info structure on the following basis:
+ * * If dev is NULL return first info struct found. If none is found return
+ * NULL.
+ * * If dev is valid we will return corresponding info struct if dev is the
+ * parent of the info struct or if dev's parent is the parent of the info
+ * struct.
+ * * If dev is valid and no info structure is found, a new info struct is
+ * allocated, initialized, and returned.
+ *
+ * Returns:
+ * Pointer to info struct if there is no error.
+ * NULL if NULL was supplied and no info structure exist.
+ * ERR_PTR(-ENOMEM) if allocation fails.
+ */
+static struct audio_info *get_info(struct device *dev)
+{
+ struct list_head *cursor;
+ struct audio_info *tmp;
+ struct audio_info *info = NULL;
+
+ /*
+ * Find the info structure for dev. If NULL is supplied for dev
+ * just return first device found.
+ */
+ list_for_each(cursor, &cg2900_audio_devices) {
+ tmp = list_entry(cursor, struct audio_info, list);
+ if (!dev || tmp->parent == dev->parent || tmp->parent == dev) {
+ info = tmp;
+ break;
+ }
+ }
+
+ if (!dev || info)
+ return info;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(dev, "Could not allocate info struct\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ info->parent = dev->parent;
+
+ /* Initiate the mutexes */
+ mutex_init(&(info->management_mutex));
+ mutex_init(&(info->bt_mutex));
+ mutex_init(&(info->fm_mutex));
+ mutex_init(&(info->endpoints.management_mutex));
+
+ /* Initiate the endpoint list */
+ INIT_LIST_HEAD(&info->endpoints.ep_list);
+
+ list_add_tail(&info->list, &cg2900_audio_devices);
+
+ dev_info(dev, "CG2900 device added\n");
+ return info;
+}
+
+/**
+ * flush_endpoint_list() - Deletes all stored endpoints in @list.
+ * @list: List of endpoints.
+ */
+static void flush_endpoint_list(struct endpoint_list *list)
+{
+ struct list_head *cursor, *next;
+ struct endpoint_config_node *tmp;
+
+ mutex_lock(&list->management_mutex);
+ list_for_each_safe(cursor, next, &(list->ep_list)) {
+ tmp = list_entry(cursor, struct endpoint_config_node, list);
+ list_del(cursor);
+ kfree(tmp);
+ }
+ mutex_unlock(&list->management_mutex);
+}
+
+/**
+ * device_removed() - Remove device from list if there are no channels left.
+ * @info: CG2900 audio info structure.
+ */
+static void device_removed(struct audio_info *info)
+{
+ struct list_head *cursor;
+ struct audio_info *tmp;
+
+ if (info->dev_bt || info->dev_fm)
+ /* There are still devices active */
+ return;
+
+ /* Find the stored info structure */
+ list_for_each(cursor, &cg2900_audio_devices) {
+ tmp = list_entry(cursor, struct audio_info, list);
+ if (tmp == info) {
+ list_del(cursor);
+ break;
+ }
+ }
+
+ flush_endpoint_list(&info->endpoints);
+
+ mutex_destroy(&info->management_mutex);
+ mutex_destroy(&info->bt_mutex);
+ mutex_destroy(&info->fm_mutex);
+ mutex_destroy(&info->endpoints.management_mutex);
+
+ kfree(info);
+ pr_info("CG2900 Audio device removed");
+}
+
+/**
+ * read_cb() - Handle data received from STE connectivity driver.
+ * @dev: Device receiving data.
+ * @skb: Buffer with data coming form device.
+ */
+static void read_cb(struct cg2900_user_data *dev, struct sk_buff *skb)
+{
+ struct audio_cb_info *cb_info;
+
+ cb_info = cg2900_get_usr(dev);
+
+ if (!(cb_info->user)) {
+ dev_err(dev->dev, "NULL supplied as cb_info->user\n");
+ return;
+ }
+
+ /* Mark that packet has been received */
+ dev_dbg(dev->dev, "New resp_state: RESP_RECEIVED");
+ cb_info->user->resp_state = RESP_RECEIVED;
+ skb_queue_tail(&cb_info->skb_queue, skb);
+ wake_up_all(&cb_info->wq);
+}
+
+/**
+ * reset_cb() - Reset callback function.
+ * @dev: CG2900_Core device resetting.
+ */
+static void reset_cb(struct cg2900_user_data *dev)
+{
+ struct audio_info *info;
+
+ dev_dbg(dev->dev, "reset_cb\n");
+
+ info = dev_get_drvdata(dev->dev);
+ mutex_lock(&info->management_mutex);
+ info->nbr_of_users_active = 0;
+ info->state = RESET;
+ mutex_unlock(&info->management_mutex);
+}
+
+/**
+ * get_session_user() - Check that supplied session is within valid range.
+ * @session: Session ID.
+ *
+ * Returns:
+ * Audio_user if there is no error.
+ * NULL for bad session ID.
+ */
+static struct audio_user *get_session_user(int session)
+{
+ struct audio_user *audio_user;
+
+ if (session < FIRST_USER || session >= MAX_NBR_OF_USERS) {
+ pr_err("Calling with invalid session %d", session);
+ return NULL;
+ }
+
+ audio_user = cg2900_audio_sessions[session];
+ if (!audio_user)
+ pr_err("Calling with non-opened session %d", session);
+ return audio_user;
+}
+
+/**
+ * del_endpoint_private() - Deletes an endpoint from @list.
+ * @endpoint_id: Endpoint ID.
+ * @list: List of endpoints.
+ *
+ * Deletes an endpoint from the supplied endpoint list.
+ * This function is not protected by any semaphore.
+ */
+static void del_endpoint_private(enum cg2900_audio_endpoint_id endpoint_id,
+ struct endpoint_list *list)
+{
+ struct list_head *cursor, *next;
+ struct endpoint_config_node *tmp;
+
+ list_for_each_safe(cursor, next, &(list->ep_list)) {
+ tmp = list_entry(cursor, struct endpoint_config_node, list);
+ if (tmp->endpoint_id == endpoint_id) {
+ list_del(cursor);
+ kfree(tmp);
+ }
+ }
+}
+
+/**
+ * add_endpoint() - Add endpoint node to @list.
+ * @ep_config: Endpoint configuration.
+ * @list: List of endpoints.
+ *
+ * Add endpoint node to the supplied list and copies supplied config to node.
+ * If a node already exists for the supplied endpoint, the old node is removed
+ * and replaced by the new node.
+ */
+static void add_endpoint(struct cg2900_endpoint_config *ep_config,
+ struct endpoint_list *list)
+{
+ struct endpoint_config_node *item;
+
+ item = kzalloc(sizeof(*item), GFP_KERNEL);
+ if (!item) {
+ pr_err("add_endpoint: Failed to alloc memory");
+ return;
+ }
+
+ /* Store values */
+ item->endpoint_id = ep_config->endpoint_id;
+ memcpy(&(item->config), &(ep_config->config), sizeof(item->config));
+
+ mutex_lock(&(list->management_mutex));
+
+ /*
+ * Check if endpoint ID already exist in list.
+ * If that is the case, remove it.
+ */
+ if (!list_empty(&(list->ep_list)))
+ del_endpoint_private(ep_config->endpoint_id, list);
+
+ list_add_tail(&(item->list), &(list->ep_list));
+
+ mutex_unlock(&(list->management_mutex));
+}
+
+/**
+ * find_endpoint() - Finds endpoint identified by @endpoint_id in @list.
+ * @endpoint_id: Endpoint ID.
+ * @list: List of endpoints.
+ *
+ * Returns:
+ * Endpoint configuration if there is no error.
+ * NULL if no configuration can be found for @endpoint_id.
+ */
+static union cg2900_endpoint_config_union *
+find_endpoint(enum cg2900_audio_endpoint_id endpoint_id,
+ struct endpoint_list *list)
+{
+ struct list_head *cursor, *next;
+ struct endpoint_config_node *tmp;
+ struct endpoint_config_node *ret_ep = NULL;
+
+ mutex_lock(&list->management_mutex);
+ list_for_each_safe(cursor, next, &(list->ep_list)) {
+ tmp = list_entry(cursor, struct endpoint_config_node, list);
+ if (tmp->endpoint_id == endpoint_id) {
+ ret_ep = tmp;
+ break;
+ }
+ }
+ mutex_unlock(&list->management_mutex);
+
+ if (ret_ep)
+ return &(ret_ep->config);
+ else
+ return NULL;
+}
+
+/**
+ * new_stream_id() - Allocate a new stream id.
+ * @info: Current audio info struct.
+ *
+ * Returns:
+ * 0-127 new valid id.
+ * -ENOMEM if no id is available.
+ */
+static s8 new_stream_id(struct audio_info *info)
+{
+ int r;
+
+ mutex_lock(&info->management_mutex);
+
+ r = find_first_zero_bit(info->stream_ids,
+ 8 * sizeof(info->stream_ids));
+
+ if (r >= 8 * sizeof(info->stream_ids)) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ set_bit(r, (unsigned long int *)info->stream_ids);
+
+out:
+ mutex_unlock(&info->management_mutex);
+ return r;
+}
+
+/**
+ * release_stream_id() - Release a stream id.
+ * @info: Current audio info struct.
+ * @id: Stream to release.
+ */
+static void release_stream_id(struct audio_info *info, u8 id)
+{
+ if (id >= 8 * sizeof(info->stream_ids))
+ return;
+
+ mutex_lock(&info->management_mutex);
+ clear_bit(id, (unsigned long int *)info->stream_ids);
+ mutex_unlock(&info->management_mutex);
+}
+
+/**
+ * receive_fm_write_response() - Wait for and handle the response to an FM Legacy WriteCommand request.
+ * @audio_user: Audio user to check for.
+ * @command: FM command to wait for.
+ *
+ * This function first waits (up to 5 seconds) for a response to an FM
+ * write command and when one arrives, it checks that it is the one we
+ * are waiting for and also that no error has occurred.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ECOMM if no response was received.
+ * -EIO for other errors.
+ */
+static int receive_fm_write_response(struct audio_user *audio_user,
+ u16 command)
+{
+ int err = 0;
+ int res;
+ struct sk_buff *skb;
+ struct fm_leg_cmd_cmpl *pkt;
+ u16 rsp_cmd;
+ struct audio_cb_info *cb_info;
+ struct audio_info *info;
+ struct cg2900_user_data *pf_data;
+
+ info = audio_user->info;
+ pf_data = dev_get_platdata(info->dev_fm);
+ cb_info = cg2900_get_usr(pf_data);
+
+ /*
+ * Wait for callback to receive command complete and then wake us up
+ * again.
+ */
+ res = wait_event_timeout(cb_info->wq,
+ audio_user->resp_state == RESP_RECEIVED,
+ msecs_to_jiffies(RESP_TIMEOUT));
+ if (!res) {
+ dev_err(FM_DEV, "Timeout while waiting for return packet\n");
+ return -ECOMM;
+ } else if (res < 0) {
+ dev_err(FM_DEV,
+ "Error %d occurred while waiting for return packet\n",
+ res);
+ return -ECOMM;
+ }
+
+ /* OK, now we should have received answer. Let's check it. */
+ skb = skb_dequeue_tail(&cb_info->skb_queue);
+ if (!skb) {
+ dev_err(FM_DEV, "No skb in queue when it should be there\n");
+ return -EIO;
+ }
+
+ pkt = (struct fm_leg_cmd_cmpl *)skb->data;
+
+ /* Check if we received the correct event */
+ if (pkt->opcode != CG2900_FM_GEN_ID_LEGACY) {
+ dev_err(FM_DEV,
+ "Received unknown FM packet. 0x%X %X %X %X %X\n",
+ skb->data[0], skb->data[1], skb->data[2],
+ skb->data[3], skb->data[4]);
+ err = -EIO;
+ goto error_handling_free_skb;
+ }
+
+ /* FM Legacy Command complete event */
+ rsp_cmd = cg2900_get_fm_cmd_id(le16_to_cpu(pkt->response_head));
+
+ if (pkt->fm_function != CG2900_FM_CMD_PARAM_WRITECOMMAND ||
+ rsp_cmd != command) {
+ dev_err(FM_DEV,
+ "Received unexpected packet func 0x%X cmd 0x%04X\n",
+ pkt->fm_function, rsp_cmd);
+ err = -EIO;
+ goto error_handling_free_skb;
+ }
+
+ if (pkt->cmd_status != CG2900_FM_CMD_STATUS_COMMAND_SUCCEEDED) {
+ dev_err(FM_DEV, "FM Command failed (%d)\n", pkt->cmd_status);
+ err = -EIO;
+ goto error_handling_free_skb;
+ }
+ /* Operation succeeded. We are now done */
+
+error_handling_free_skb:
+ kfree_skb(skb);
+ return err;
+}
+
+/**
+ * receive_bt_cmd_complete() - Wait for and handle an BT Command Complete event.
+ * @audio_user: Audio user to check for.
+ * @rsp: Opcode of BT command to wait for.
+ * @data: Pointer to buffer if any received data should be stored (except
+ * status).
+ * @data_len: Length of @data in bytes.
+ *
+ * This function first waits for BT Command Complete event (up to 5 seconds)
+ * and when one arrives, it checks that it is the one we are waiting for and
+ * also that no error has occurred.
+ * If @data is supplied it also copies received data into @data.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ECOMM if no response was received.
+ * -EIO for other errors.
+ */
+static int receive_bt_cmd_complete(struct audio_user *audio_user, u16 rsp,
+ void *data, int data_len)
+{
+ int err = 0;
+ int res;
+ struct sk_buff *skb;
+ struct bt_cmd_cmpl_event *evt;
+ u16 opcode;
+ struct audio_cb_info *cb_info;
+ struct audio_info *info;
+ struct cg2900_user_data *pf_data;
+
+ info = audio_user->info;
+ pf_data = dev_get_platdata(info->dev_bt);
+ cb_info = cg2900_get_usr(pf_data);
+
+ /*
+ * Wait for callback to receive command complete and then wake us up
+ * again.
+ */
+ res = wait_event_timeout(cb_info->wq,
+ audio_user->resp_state == RESP_RECEIVED,
+ msecs_to_jiffies(RESP_TIMEOUT));
+ if (!res) {
+ dev_err(BT_DEV, "Timeout while waiting for return packet\n");
+ return -ECOMM;
+ } else if (res < 0) {
+ /* We timed out or an error occurred */
+ dev_err(BT_DEV,
+ "Error %d occurred while waiting for return packet\n",
+ res);
+ return -ECOMM;
+ }
+
+ /* OK, now we should have received answer. Let's check it. */
+ skb = skb_dequeue_tail(&cb_info->skb_queue);
+ if (!skb) {
+ dev_err(BT_DEV, "No skb in queue when it should be there\n");
+ return -EIO;
+ }
+
+ evt = (struct bt_cmd_cmpl_event *)skb->data;
+ if (evt->eventcode != HCI_EV_CMD_COMPLETE) {
+ dev_err(BT_DEV,
+ "We did not receive the event we expected (0x%X)\n",
+ evt->eventcode);
+ err = -EIO;
+ goto error_handling_free_skb;
+ }
+
+ opcode = le16_to_cpu(evt->opcode);
+ if (opcode != rsp) {
+ dev_err(BT_DEV,
+ "Received cmd complete for unexpected command: "
+ "0x%04X\n", opcode);
+ err = -EIO;
+ goto error_handling_free_skb;
+ }
+
+ if (evt->status != HCI_BT_ERROR_NO_ERROR) {
+ dev_err(BT_DEV, "Received command complete with err %d\n",
+ evt->status);
+ err = -EIO;
+ /*
+ * In data there might be more detailed error code.
+ * Let's copy it.
+ */
+ }
+
+ /*
+ * Copy the rest of the parameters if a buffer has been supplied.
+ * The caller must have set the length correctly.
+ */
+ if (data)
+ memcpy(data, evt->data, data_len);
+
+ /* Operation succeeded. We are now done */
+
+error_handling_free_skb:
+ kfree_skb(skb);
+ return err;
+}
+
+/**
+ * send_vs_delete_stream() - Delete an audio stream defined by @stream_handle.
+ * @audio_user: Audio user to check for.
+ * @stream_handle: Handle of the audio stream.
+ *
+ * This function is used to delete an audio stream defined by a stream
+ * handle.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ECOMM if no response was received.
+ * -ENOMEM upon allocation errors.
+ * Errors from @cg2900_write.
+ * -EIO for other errors.
+ */
+static int send_vs_delete_stream(struct audio_user *audio_user,
+ unsigned int stream_handle)
+{
+ int err = 0;
+ struct sk_buff *skb;
+ u16 opcode;
+ struct audio_info *info = audio_user->info;
+ struct cg2900_user_data *pf_data = dev_get_platdata(info->dev_bt);
+ struct audio_cb_info *cb_info = cg2900_get_usr(pf_data);
+
+ /* Now delete the stream - format command... */
+ if (info->revision == CHIP_REV_PG1) {
+ struct bt_vs_reset_session_cfg_cmd *cmd;
+
+ dev_dbg(BT_DEV, "BT: HCI_VS_Reset_Session_Configuration\n");
+
+ skb = pf_data->alloc_skb(sizeof(*cmd), GFP_KERNEL);
+ if (!skb) {
+ dev_err(BT_DEV, "Could not allocate skb\n");
+ err = -ENOMEM;
+ return err;
+ }
+
+ cmd = (struct bt_vs_reset_session_cfg_cmd *)
+ skb_put(skb, sizeof(*cmd));
+
+ opcode = CG2900_BT_VS_RESET_SESSION_CONFIG;
+ cmd->opcode = cpu_to_le16(opcode);
+ cmd->plen = BT_PARAM_LEN(sizeof(*cmd));
+ cmd->id = (u8)stream_handle;
+ } else {
+ struct mc_vs_delete_stream_cmd *cmd;
+
+ dev_dbg(BT_DEV, "BT: HCI_VS_Delete_Stream\n");
+
+ skb = pf_data->alloc_skb(sizeof(*cmd), GFP_KERNEL);
+ if (!skb) {
+ dev_err(BT_DEV, "Could not allocate skb\n");
+ err = -ENOMEM;
+ return err;
+ }
+
+ cmd = (struct mc_vs_delete_stream_cmd *)
+ skb_put(skb, sizeof(*cmd));
+
+ opcode = CG2900_MC_VS_DELETE_STREAM;
+ cmd->opcode = cpu_to_le16(opcode);
+ cmd->plen = BT_PARAM_LEN(sizeof(*cmd));
+ cmd->stream = (u8)stream_handle;
+ }
+
+ /* ...and send it */
+ cb_info->user = audio_user;
+ dev_dbg(BT_DEV, "New resp_state: WAITING\n");
+ audio_user->resp_state = WAITING;
+
+ err = pf_data->write(pf_data, skb);
+ if (err) {
+ dev_err(BT_DEV, "Error %d occurred while transmitting skb\n",
+ err);
+ goto error_handling_free_skb;
+ }
+
+ /* wait for response */
+ if (info->revision == CHIP_REV_PG1) {
+ err = receive_bt_cmd_complete(audio_user, opcode, NULL, 0);
+ } else {
+ u8 vs_err;
+
+ /* All commands in PG2 API returns one byte extra status */
+ err = receive_bt_cmd_complete(audio_user, opcode,
+ &vs_err, sizeof(vs_err));
+
+ if (err)
+ dev_err(BT_DEV,
+ "VS_DELETE_STREAM - failed with error 0x%02X\n",
+ vs_err);
+ else
+ release_stream_id(info, stream_handle);
+ }
+
+ return err;
+
+error_handling_free_skb:
+ kfree_skb(skb);
+ return err;
+}
+
+/**
+ * send_vs_session_ctrl() - Formats an sends a CG2900_BT_VS_SESSION_CTRL command.
+ * @user: Audio user this command belongs to.
+ * @stream_handle: Handle to stream.
+ * @command: Command to execute on stream, should be one of
+ * CG2900_BT_SESSION_START, CG2900_BT_SESSION_STOP,
+ * CG2900_BT_SESSION_PAUSE, CG2900_BT_SESSION_RESUME.
+ *
+ * Packs and sends a command packet and waits for the response. Must
+ * be called with the bt_mutex held.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ENOMEM if not possible to allocate packet.
+ * -ECOMM if no response was received.
+ * -EIO for other errors.
+ */
+static int send_vs_session_ctrl(struct audio_user *user,
+ u8 stream_handle, u8 command)
+{
+ int err = 0;
+ struct bt_vs_session_ctrl_cmd *pkt;
+ struct sk_buff *skb;
+ struct audio_cb_info *cb_info;
+ struct audio_info *info;
+ struct cg2900_user_data *pf_data;
+
+ info = user->info;
+ pf_data = dev_get_platdata(info->dev_bt);
+ cb_info = cg2900_get_usr(pf_data);
+
+ dev_dbg(BT_DEV, "BT: HCI_VS_Session_Control handle: %d cmd: %d\n",
+ stream_handle, command);
+
+ skb = pf_data->alloc_skb(sizeof(*pkt), GFP_KERNEL);
+ if (!skb) {
+ dev_err(BT_DEV,
+ "send_vs_session_ctrl: Could not allocate skb\n");
+ return -ENOMEM;
+ }
+
+ /* Enter data into the skb */
+ pkt = (struct bt_vs_session_ctrl_cmd *) skb_put(skb, sizeof(*pkt));
+
+ pkt->opcode = cpu_to_le16(CG2900_BT_VS_SESSION_CTRL);
+ pkt->plen = BT_PARAM_LEN(sizeof(*pkt));
+ pkt->id = stream_handle;
+ pkt->control = command; /* Start/stop etc */
+
+ cb_info->user = user;
+ dev_dbg(BT_DEV, "New resp_state: WAITING\n");
+ user->resp_state = WAITING;
+
+ /* Send packet to controller */
+ err = pf_data->write(pf_data, skb);
+ if (err) {
+ dev_err(BT_DEV, "Error %d occurred while transmitting skb\n",
+ err);
+ kfree_skb(skb);
+ goto finished;
+ }
+
+ err = receive_bt_cmd_complete(user, CG2900_BT_VS_SESSION_CTRL,
+ NULL, 0);
+finished:
+ dev_dbg(BT_DEV, "New resp_state: IDLE\n");
+ user->resp_state = IDLE;
+ return err;
+}
+
+/**
+ * send_vs_session_config() - Formats an sends a CG2900_BT_VS_SESSION_CONFIG command.
+ * @user: Audio user this command belongs to.
+ * @config_stream: Custom function for configuring the stream.
+ * @priv_data: Private data passed to @config_stream untouched.
+ *
+ * Packs and sends a command packet and waits for the response. Must
+ * be called with the bt_mutex held.
+ *
+ * Space is allocated for one stream and a custom function is used to
+ * fill in the stream configuration.
+ *
+ * Returns:
+ * 0-255 stream handle if no error.
+ * -ENOMEM if not possible to allocate packet.
+ * -ECOMM if no response was received.
+ * -EIO for other errors.
+ */
+static int send_vs_session_config(struct audio_user *user,
+ void(*config_stream)(struct audio_info *, void *,
+ struct session_config_stream *),
+ void *priv_data)
+{
+ int err = 0;
+ struct sk_buff *skb;
+ struct bt_vs_session_config_cmd *pkt;
+ u8 session_id;
+ struct audio_cb_info *cb_info;
+ struct audio_info *info;
+ struct cg2900_user_data *pf_data;
+
+ info = user->info;
+ pf_data = dev_get_platdata(info->dev_bt);
+ cb_info = cg2900_get_usr(pf_data);
+
+ dev_dbg(BT_DEV, "BT: HCI_VS_Set_Session_Configuration\n");
+
+ skb = pf_data->alloc_skb(sizeof(*pkt), GFP_KERNEL);
+ if (!skb) {
+ dev_err(BT_DEV,
+ "send_vs_session_config: Could not allocate skb\n");
+ return -ENOMEM;
+ }
+
+ pkt = (struct bt_vs_session_config_cmd *)skb_put(skb, sizeof(*pkt));
+ /* zero the packet so we don't have to set all reserved fields */
+ memset(pkt, 0, sizeof(*pkt));
+
+ /* Common parameters */
+ pkt->opcode = cpu_to_le16(CG2900_BT_VS_SET_SESSION_CONFIG);
+ pkt->plen = BT_PARAM_LEN(sizeof(*pkt));
+ pkt->n_streams = 1; /* 1 stream configuration supplied */
+
+ /* Let the custom-function fill in the rest */
+ config_stream(info, priv_data, &pkt->stream);
+
+ cb_info->user = user;
+ dev_dbg(BT_DEV, "New resp_state: WAITING\n");
+ user->resp_state = WAITING;
+
+ /* Send packet to controller */
+ err = pf_data->write(pf_data, skb);
+ if (err) {
+ dev_err(BT_DEV, "Error %d occurred while transmitting skb\n",
+ err);
+ kfree_skb(skb);
+ goto finished;
+ }
+
+ err = receive_bt_cmd_complete(user,
+ CG2900_BT_VS_SET_SESSION_CONFIG,
+ &session_id, sizeof(session_id));
+ /* Return session id/stream handle if success */
+ if (!err)
+ err = session_id;
+
+finished:
+ dev_dbg(BT_DEV, "New resp_state: IDLE\n");
+ user->resp_state = IDLE;
+ return err;
+}
+
+/**
+ * send_fm_write_1_param() - Formats and sends an FM legacy write command with one parameter.
+ * @user: Audio user this command belongs to.
+ * @command: Command.
+ * @param: Parameter for command.
+ *
+ * Packs and sends a command packet and waits for the response. Must
+ * be called with the fm_mutex held.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ENOMEM if not possible to allocate packet.
+ * -ECOMM if no response was received.
+ * -EIO for other errors.
+ */
+static int send_fm_write_1_param(struct audio_user *user,
+ u16 command, u16 param)
+{
+ int err = 0;
+ struct sk_buff *skb;
+ struct fm_leg_cmd *cmd;
+ size_t len;
+ struct audio_cb_info *cb_info;
+ struct audio_info *info;
+ struct cg2900_user_data *pf_data;
+
+ info = user->info;
+ pf_data = dev_get_platdata(info->dev_fm);
+ cb_info = cg2900_get_usr(pf_data);
+
+ dev_dbg(FM_DEV, "send_fm_write_1_param cmd 0x%X param 0x%X\n",
+ command, param);
+
+ /* base package + one parameter */
+ len = sizeof(*cmd) + sizeof(cmd->fm_cmd.data[0]);
+
+ skb = pf_data->alloc_skb(len, GFP_KERNEL);
+ if (!skb) {
+ dev_err(FM_DEV,
+ "send_fm_write_1_param: Could not allocate skb\n");
+ return -ENOMEM;
+ }
+
+ cmd = (struct fm_leg_cmd *)skb_put(skb, len);
+
+ cmd->length = CG2900_FM_CMD_PARAM_LEN(len);
+ cmd->opcode = CG2900_FM_GEN_ID_LEGACY;
+ cmd->read_write = CG2900_FM_CMD_LEG_PARAM_WRITE;
+ cmd->fm_function = CG2900_FM_CMD_PARAM_WRITECOMMAND;
+ /* one parameter - builtin assumption for this function */
+ cmd->fm_cmd.head = cpu_to_le16(cg2900_make_fm_cmd_id(command, 1));
+ cmd->fm_cmd.data[0] = cpu_to_le16(param);
+
+ cb_info->user = user;
+ dev_dbg(FM_DEV, "New resp_state: WAITING\n");
+ user->resp_state = WAITING;
+
+ /* Send packet to controller */
+ err = pf_data->write(pf_data, skb);
+ if (err) {
+ dev_err(FM_DEV, "Error %d occurred while transmitting skb\n",
+ err);
+ kfree_skb(skb);
+ goto finished;
+ }
+
+ err = receive_fm_write_response(user, command);
+finished:
+ dev_dbg(FM_DEV, "New resp_state: IDLE\n");
+ user->resp_state = IDLE;
+ return err;
+}
+
+/**
+ * send_vs_stream_ctrl() - Formats an sends a CG2900_MC_VS_STREAM_CONTROL command.
+ * @user: Audio user this command belongs to.
+ * @stream: Stream id.
+ * @command: Start/stop etc.
+ *
+ * Packs and sends a command packet and waits for the response. Must
+ * be called with the bt_mutex held.
+ *
+ * While the HCI command allows for multiple streams in one command,
+ * this function only handles one.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ENOMEM if not possible to allocate packet.
+ * -ECOMM if no response was received.
+ * -EIO for other errors.
+ */
+static int send_vs_stream_ctrl(struct audio_user *user, u8 stream, u8 command)
+{
+ int err = 0;
+ struct sk_buff *skb;
+ struct mc_vs_stream_ctrl_cmd *cmd;
+ size_t len;
+ u8 vs_err;
+ struct audio_cb_info *cb_info;
+ struct audio_info *info;
+ struct cg2900_user_data *pf_data;
+
+ info = user->info;
+ pf_data = dev_get_platdata(info->dev_bt);
+ cb_info = cg2900_get_usr(pf_data);
+
+ dev_dbg(BT_DEV, "send_vs_stream_ctrl stream %d command %d\n", stream,
+ command);
+
+ /* basic length + one stream */
+ len = sizeof(*cmd) + sizeof(cmd->stream[0]);
+
+ skb = pf_data->alloc_skb(len, GFP_KERNEL);
+ if (!skb) {
+ dev_err(BT_DEV, "send_vs_stream_ctrl:Could not allocate skb\n");
+ return -ENOMEM;
+ }
+
+ cmd = (struct mc_vs_stream_ctrl_cmd *)skb_put(skb, len);
+
+ cmd->opcode = cpu_to_le16(CG2900_MC_VS_STREAM_CONTROL);
+ cmd->plen = BT_PARAM_LEN(len);
+ cmd->command = command;
+
+ /* one stream */
+ cmd->n_streams = 1;
+ cmd->stream[0] = stream;
+
+ cb_info->user = user;
+ dev_dbg(BT_DEV, "New resp_state: WAITING\n");
+ user->resp_state = WAITING;
+
+ /* Send packet to controller */
+ err = pf_data->write(pf_data, skb);
+ if (err) {
+ dev_err(BT_DEV, "Error %d occurred while transmitting skb\n",
+ err);
+ kfree_skb(skb);
+ goto finished;
+ }
+
+ /* All commands in PG2 API returns one byte with extra status */
+ err = receive_bt_cmd_complete(user,
+ CG2900_MC_VS_STREAM_CONTROL,
+ &vs_err, sizeof(vs_err));
+ if (err)
+ dev_err(BT_DEV,
+ "VS_STREAM_CONTROL - failed with error 0x%02x\n",
+ vs_err);
+
+finished:
+ dev_dbg(BT_DEV, "New resp_state: IDLE\n");
+ user->resp_state = IDLE;
+ return err;
+}
+
+/**
+ * send_vs_create_stream() - Formats an sends a CG2900_MC_VS_CREATE_STREAM command.
+ * @user: Audio user this command belongs to.
+ * @inport: Stream id.
+ * @outport: Start/stop etc.
+ * @order: Activation order.
+ *
+ * Packs and sends a command packet and waits for the response. Must
+ * be called with the bt_mutex held.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ENOMEM if not possible to allocate packet.
+ * -ECOMM if no response was received.
+ * -EIO for other errors.
+ */
+static int send_vs_create_stream(struct audio_user *user, u8 inport,
+ u8 outport, u8 order)
+{
+ int err = 0;
+ struct sk_buff *skb;
+ struct mc_vs_create_stream_cmd *cmd;
+ s8 id;
+ u8 vs_err;
+ struct audio_cb_info *cb_info;
+ struct audio_info *info;
+ struct cg2900_user_data *pf_data;
+
+ info = user->info;
+ pf_data = dev_get_platdata(info->dev_bt);
+ cb_info = cg2900_get_usr(pf_data);
+
+ dev_dbg(BT_DEV,
+ "send_vs_create_stream inport %d outport %d order %d\n",
+ inport, outport, order);
+
+ id = new_stream_id(info);
+ if (id < 0) {
+ dev_err(BT_DEV, "No free stream id\n");
+ err = -EIO;
+ goto finished;
+ }
+
+ skb = pf_data->alloc_skb(sizeof(*cmd), GFP_KERNEL);
+ if (!skb) {
+ dev_err(BT_DEV,
+ "send_vs_create_stream: Could not allocate skb\n");
+ err = -ENOMEM;
+ goto finished_release_id;
+ }
+
+ cmd = (struct mc_vs_create_stream_cmd *)skb_put(skb, sizeof(*cmd));
+
+ cmd->opcode = cpu_to_le16(CG2900_MC_VS_CREATE_STREAM);
+ cmd->plen = BT_PARAM_LEN(sizeof(*cmd));
+ cmd->id = (u8)id;
+ cmd->inport = inport;
+ cmd->outport = outport;
+ cmd->order = order;
+
+ cb_info->user = user;
+ dev_dbg(BT_DEV, "New resp_state: WAITING\n");
+ user->resp_state = WAITING;
+
+ /* Send packet to controller */
+ err = pf_data->write(pf_data, skb);
+ if (err) {
+ dev_err(BT_DEV, "Error %d occurred while transmitting skb\n",
+ err);
+ kfree_skb(skb);
+ goto finished_release_id;
+ }
+
+ /* All commands in PG2 API returns one byte with extra status */
+ err = receive_bt_cmd_complete(user,
+ CG2900_MC_VS_CREATE_STREAM,
+ &vs_err, sizeof(vs_err));
+ if (err) {
+ dev_err(BT_DEV,
+ "VS_CREATE_STREAM - failed with error 0x%02x\n",
+ vs_err);
+ goto finished_release_id;
+ }
+
+ err = id;
+ goto finished;
+
+finished_release_id:
+ release_stream_id(info, id);
+finished:
+ dev_dbg(BT_DEV, "New resp_state: IDLE\n");
+ user->resp_state = IDLE;
+ return err;
+}
+
+/**
+ * send_vs_port_cfg() - Formats an sends a CG2900_MC_VS_PORT_CONFIG command.
+ * @user: Audio user this command belongs to.
+ * @port: Port id to configure.
+ * @cfg: Pointer to specific configuration.
+ * @cfglen: Length of configuration.
+ *
+ * Packs and sends a command packet and waits for the response. Must
+ * be called with the bt_mutex held.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ENOMEM if not possible to allocate packet.
+ * -ECOMM if no response was received.
+ * -EIO for other errors.
+ */
+static int send_vs_port_cfg(struct audio_user *user, u8 port,
+ const void *cfg, size_t cfglen)
+{
+ int err = 0;
+ struct sk_buff *skb;
+ struct mc_vs_port_cfg_cmd *cmd;
+ void *ptr;
+ u8 vs_err;
+ struct audio_cb_info *cb_info;
+ struct audio_info *info;
+ struct cg2900_user_data *pf_data;
+
+ info = user->info;
+ pf_data = dev_get_platdata(info->dev_bt);
+ cb_info = cg2900_get_usr(pf_data);
+
+ dev_dbg(BT_DEV, "send_vs_port_cfg len %d\n", cfglen);
+
+ skb = pf_data->alloc_skb(sizeof(*cmd) + cfglen, GFP_KERNEL);
+ if (!skb) {
+ dev_err(BT_DEV, "send_vs_port_cfg: Could not allocate skb\n");
+ return -ENOMEM;
+ }
+
+ /* Fill in common part */
+ cmd = (struct mc_vs_port_cfg_cmd *) skb_put(skb, sizeof(*cmd));
+ cmd->opcode = cpu_to_le16(CG2900_MC_VS_PORT_CONFIG);
+ cmd->plen = BT_PARAM_LEN(sizeof(*cmd) + cfglen);
+ cmd->type = port;
+
+ /* Copy specific configuration */
+ ptr = skb_put(skb, cfglen);
+ memcpy(ptr, cfg, cfglen);
+
+ /* Send */
+ cb_info->user = user;
+ dev_dbg(BT_DEV, "New resp_state: WAITING\n");
+ user->resp_state = WAITING;
+
+ err = pf_data->write(pf_data, skb);
+ if (err) {
+ dev_err(BT_DEV, "Error %d occurred while transmitting skb\n",
+ err);
+ kfree_skb(skb);
+ goto finished;
+ }
+
+ /* All commands in PG2 API returns one byte with extra status */
+ err = receive_bt_cmd_complete(user, CG2900_MC_VS_PORT_CONFIG,
+ &vs_err, sizeof(vs_err));
+ if (err)
+ dev_err(BT_DEV, "VS_PORT_CONFIG - failed with error 0x%02x\n",
+ vs_err);
+
+finished:
+ dev_dbg(BT_DEV, "New resp_state: IDLE\n");
+ user->resp_state = IDLE;
+ return err;
+}
+
+/**
+ * set_dai_config_pg1() - Internal implementation of @cg2900_audio_set_dai_config for PG1 hardware.
+ * @audio_user: Pointer to audio user struct.
+ * @config: Pointer to the configuration to set.
+ *
+ * Sets the Digital Audio Interface (DAI) configuration for PG1
+ * hardware. This is and internal function and basic
+ * argument-verification should have been done by the caller.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EACCESS if port is not supported.
+ * -ENOMEM if not possible to allocate packet.
+ * -ECOMM if no response was received.
+ * -EIO for other errors.
+ */
+static int set_dai_config_pg1(struct audio_user *audio_user,
+ struct cg2900_dai_config *config)
+{
+ int err = 0;
+ struct cg2900_dai_conf_i2s_pcm *i2s_pcm;
+ struct sk_buff *skb = NULL;
+ struct bt_vs_set_hw_cfg_cmd_i2s *i2s_cmd;
+ struct bt_vs_set_hw_cfg_cmd_pcm *pcm_cmd;
+ struct audio_info *info = audio_user->info;
+ struct cg2900_user_data *pf_data = dev_get_platdata(info->dev_bt);
+ struct audio_cb_info *cb_info = cg2900_get_usr(pf_data);
+
+ dev_dbg(BT_DEV, "set_dai_config_pg1 port %d\n", config->port);
+
+ /*
+ * Use mutex to assure that only ONE command is sent at any time on
+ * each channel.
+ */
+ mutex_lock(&info->bt_mutex);
+
+ /* Allocate the sk_buffer. The length is actually a max length since
+ * length varies depending on logical transport.
+ */
+ skb = pf_data->alloc_skb(CG2900_BT_LEN_VS_SET_HARDWARE_CONFIG,
+ GFP_KERNEL);
+ if (!skb) {
+ dev_err(BT_DEV, "set_dai_config_pg1: Could not allocate skb\n");
+ err = -ENOMEM;
+ goto finished_unlock_mutex;
+ }
+
+ /* Fill in hci-command according to received configuration */
+ switch (config->port) {
+ case PORT_0_I2S:
+ i2s_cmd = (struct bt_vs_set_hw_cfg_cmd_i2s *)
+ skb_put(skb, sizeof(*i2s_cmd));
+
+ i2s_cmd->opcode = cpu_to_le16(CG2900_BT_VS_SET_HARDWARE_CONFIG);
+ i2s_cmd->plen = BT_PARAM_LEN(sizeof(*i2s_cmd));
+
+ i2s_cmd->vp_type = PORT_PROTOCOL_I2S;
+ i2s_cmd->port_id = 0x00; /* First/only I2S port */
+ i2s_cmd->half_period = config->conf.i2s.half_period;
+
+ i2s_cmd->master_slave = mc_i2s_role(config->conf.i2s.mode);
+
+ /* Store the new configuration */
+ mutex_lock(&info->management_mutex);
+ memcpy(&info->i2s_config, &config->conf.i2s,
+ sizeof(config->conf.i2s));
+ info->i2s_config_known = true;
+ mutex_unlock(&info->management_mutex);
+ break;
+
+ case PORT_1_I2S_PCM:
+ pcm_cmd = (struct bt_vs_set_hw_cfg_cmd_pcm *)
+ skb_put(skb, sizeof(*pcm_cmd));
+
+ pcm_cmd->opcode = cpu_to_le16(CG2900_BT_VS_SET_HARDWARE_CONFIG);
+ pcm_cmd->plen = BT_PARAM_LEN(sizeof(*pcm_cmd));
+
+ i2s_pcm = &config->conf.i2s_pcm;
+
+ /*
+ * PG1 chips don't support I2S over the PCM/I2S bus,
+ * and PG2 chips don't use this command
+ */
+ if (i2s_pcm->protocol != PORT_PROTOCOL_PCM) {
+ dev_err(BT_DEV,
+ "I2S not supported over the PCM/I2S bus\n");
+ err = -EACCES;
+ goto error_handling_free_skb;
+ }
+
+ pcm_cmd->vp_type = PORT_PROTOCOL_PCM;
+ pcm_cmd->port_id = 0x00; /* First/only PCM port */
+
+ HWCONFIG_PCM_SET_MODE(pcm_cmd, mc_pcm_role(i2s_pcm->mode));
+
+ HWCONFIG_PCM_SET_DIR(pcm_cmd, 0, i2s_pcm->slot_0_dir);
+ HWCONFIG_PCM_SET_DIR(pcm_cmd, 1, i2s_pcm->slot_1_dir);
+ HWCONFIG_PCM_SET_DIR(pcm_cmd, 2, i2s_pcm->slot_2_dir);
+ HWCONFIG_PCM_SET_DIR(pcm_cmd, 3, i2s_pcm->slot_3_dir);
+
+ pcm_cmd->bit_clock = i2s_pcm->clk;
+ pcm_cmd->frame_len =
+ cpu_to_le16(get_fs_duration(i2s_pcm->duration));
+
+ /* Store the new configuration */
+ mutex_lock(&info->management_mutex);
+ memcpy(&info->i2s_pcm_config, &config->conf.i2s_pcm,
+ sizeof(config->conf.i2s_pcm));
+ info->i2s_pcm_config_known = true;
+ mutex_unlock(&info->management_mutex);
+ break;
+
+ default:
+ dev_err(BT_DEV, "Unknown port configuration %d\n",
+ config->port);
+ err = -EACCES;
+ goto error_handling_free_skb;
+ };
+
+ cb_info->user = audio_user;
+ dev_dbg(BT_DEV, "New resp_state: WAITING\n");
+ audio_user->resp_state = WAITING;
+
+ /* Send packet to controller */
+ err = pf_data->write(pf_data, skb);
+ if (err) {
+ dev_err(BT_DEV, "Error %d occurred while transmitting skb\n",
+ err);
+ goto error_handling_free_skb;
+ }
+
+ err = receive_bt_cmd_complete(audio_user,
+ CG2900_BT_VS_SET_HARDWARE_CONFIG,
+ NULL, 0);
+
+ goto finished_unlock_mutex;
+
+error_handling_free_skb:
+ kfree_skb(skb);
+finished_unlock_mutex:
+ dev_dbg(BT_DEV, "New resp_state: IDLE\n");
+ audio_user->resp_state = IDLE;
+ mutex_unlock(&info->bt_mutex);
+ return err;
+}
+
+/**
+ * set_dai_config_pg2() - Internal implementation of @cg2900_audio_set_dai_config for PG2 hardware.
+ * @audio_user: Pointer to audio user struct.
+ * @config: Pointer to the configuration to set.
+ *
+ * Sets the Digital Audio Interface (DAI) configuration for PG2
+ * hardware. This is an internal function and basic
+ * argument-verification should have been done by the caller.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EACCESS if port is not supported.
+ * -ENOMEM if not possible to allocate packet.
+ * -ECOMM if no response was received.
+ * -EIO for other errors.
+ */
+static int set_dai_config_pg2(struct audio_user *audio_user,
+ struct cg2900_dai_config *config)
+{
+ int err = 0;
+ struct cg2900_dai_conf_i2s *i2s;
+ struct cg2900_dai_conf_i2s_pcm *i2s_pcm;
+
+ struct mc_vs_port_cfg_i2s i2s_cfg;
+ struct mc_vs_port_cfg_pcm_i2s pcm_cfg;
+ struct audio_info *info = audio_user->info;
+
+ dev_dbg(BT_DEV, "set_dai_config_pg2 port %d\n", config->port);
+
+ /*
+ * Use mutex to assure that only ONE command is sent at any time on
+ * each channel.
+ */
+ mutex_lock(&info->bt_mutex);
+
+ switch (config->port) {
+ case PORT_0_I2S:
+ i2s = &config->conf.i2s;
+
+ memset(&i2s_cfg, 0, sizeof(i2s_cfg)); /* just to be safe */
+
+ /* master/slave */
+ PORTCFG_I2S_SET_ROLE(i2s_cfg, mc_i2s_role(i2s->mode));
+
+ PORTCFG_I2S_SET_HALFPERIOD(i2s_cfg, i2s->half_period);
+ PORTCFG_I2S_SET_CHANNELS(i2s_cfg,
+ mc_i2s_channel_select(i2s->channel_sel));
+ PORTCFG_I2S_SET_SRATE(i2s_cfg,
+ mc_i2s_sample_rate(i2s->sample_rate));
+ switch (i2s->word_width) {
+ case WORD_WIDTH_16:
+ PORTCFG_I2S_SET_WORDLEN(i2s_cfg, CG2900_MC_I2S_WORD_16);
+ break;
+ case WORD_WIDTH_32:
+ PORTCFG_I2S_SET_WORDLEN(i2s_cfg, CG2900_MC_I2S_WORD_32);
+ break;
+ }
+
+ /* Store the new configuration */
+ mutex_lock(&info->management_mutex);
+ memcpy(&(info->i2s_config), &(config->conf.i2s),
+ sizeof(config->conf.i2s));
+ info->i2s_config_known = true;
+ mutex_unlock(&info->management_mutex);
+
+ /* Send */
+ err = send_vs_port_cfg(audio_user, CG2900_MC_PORT_I2S,
+ &i2s_cfg, sizeof(i2s_cfg));
+ break;
+
+ case PORT_1_I2S_PCM:
+ i2s_pcm = &config->conf.i2s_pcm;
+
+ memset(&pcm_cfg, 0, sizeof(pcm_cfg)); /* just to be safe */
+
+ /* master/slave */
+ PORTCFG_PCM_SET_ROLE(pcm_cfg, mc_pcm_role(i2s_pcm->mode));
+
+ /* set direction for all 4 slots */
+ PORTCFG_PCM_SET_DIR(pcm_cfg, 0, i2s_pcm->slot_0_dir);
+ PORTCFG_PCM_SET_DIR(pcm_cfg, 1, i2s_pcm->slot_1_dir);
+ PORTCFG_PCM_SET_DIR(pcm_cfg, 2, i2s_pcm->slot_2_dir);
+ PORTCFG_PCM_SET_DIR(pcm_cfg, 3, i2s_pcm->slot_3_dir);
+
+ /* set used SCO slots, other use cases not supported atm */
+ PORTCFG_PCM_SET_SCO_USED(pcm_cfg, 0, i2s_pcm->slot_0_used);
+ PORTCFG_PCM_SET_SCO_USED(pcm_cfg, 1, i2s_pcm->slot_1_used);
+ PORTCFG_PCM_SET_SCO_USED(pcm_cfg, 2, i2s_pcm->slot_2_used);
+ PORTCFG_PCM_SET_SCO_USED(pcm_cfg, 3, i2s_pcm->slot_3_used);
+
+ /* slot starts */
+ pcm_cfg.slot_start[0] = i2s_pcm->slot_0_start;
+ pcm_cfg.slot_start[1] = i2s_pcm->slot_1_start;
+ pcm_cfg.slot_start[2] = i2s_pcm->slot_2_start;
+ pcm_cfg.slot_start[3] = i2s_pcm->slot_3_start;
+
+ /* audio/voice sample-rate ratio */
+ PORTCFG_PCM_SET_RATIO(pcm_cfg, i2s_pcm->ratio);
+
+ /* PCM or I2S mode */
+ PORTCFG_PCM_SET_MODE(pcm_cfg, i2s_pcm->protocol);
+
+ pcm_cfg.frame_len = i2s_pcm->duration;
+
+ PORTCFG_PCM_SET_BITCLK(pcm_cfg, i2s_pcm->clk);
+ PORTCFG_PCM_SET_SRATE(pcm_cfg,
+ mc_pcm_sample_rate(i2s_pcm->sample_rate));
+
+ /* Store the new configuration */
+ mutex_lock(&info->management_mutex);
+ memcpy(&(info->i2s_pcm_config), &(config->conf.i2s_pcm),
+ sizeof(config->conf.i2s_pcm));
+ info->i2s_pcm_config_known = true;
+ mutex_unlock(&info->management_mutex);
+
+ /* Send */
+ err = send_vs_port_cfg(audio_user, CG2900_MC_PORT_PCM_I2S,
+ &pcm_cfg, sizeof(pcm_cfg));
+ break;
+
+ default:
+ dev_err(BT_DEV, "Unknown port configuration %d\n",
+ config->port);
+ err = -EACCES;
+ };
+
+ mutex_unlock(&info->bt_mutex);
+ return err;
+}
+
+/**
+ * struct i2s_fm_stream_config_priv - Helper struct for stream i2s-fm streams.
+ * @fm_config: FM endpoint configuration.
+ * @rx: true for FM-RX, false for FM-TX.
+ */
+struct i2s_fm_stream_config_priv {
+ struct cg2900_endpoint_config_fm *fm_config;
+ bool rx;
+
+};
+
+/**
+ * config_i2s_fm_stream() - Callback for @send_vs_session_config.
+ * @info: Audio info structure.
+ * @_priv: Pointer to a @i2s_fm_stream_config_priv struct.
+ * @cfg: Pointer to stream config block in command packet.
+ *
+ * Fills in stream configuration for I2S-FM RX/TX.
+ */
+
+static void config_i2s_fm_stream(struct audio_info *info, void *_priv,
+ struct session_config_stream *cfg)
+{
+ struct i2s_fm_stream_config_priv *priv = _priv;
+ struct session_config_vport *fm;
+ struct session_config_vport *i2s;
+
+ cfg->media_type = CG2900_BT_SESSION_MEDIA_TYPE_AUDIO;
+
+ if (info->i2s_config.channel_sel == CHANNEL_SELECTION_BOTH)
+ SESSIONCFG_SET_CHANNELS(cfg, CG2900_BT_MEDIA_CONFIG_STEREO);
+ else
+ SESSIONCFG_SET_CHANNELS(cfg, CG2900_BT_MEDIA_CONFIG_MONO);
+
+ SESSIONCFG_I2S_SET_SRATE(cfg,
+ session_config_sample_rate(priv->fm_config->sample_rate));
+
+ cfg->codec_type = CG2900_CODEC_TYPE_NONE;
+ /* codec mode and parameters not used */
+
+ if (priv->rx) {
+ fm = &cfg->inport; /* FM is input */
+ i2s = &cfg->outport; /* I2S is output */
+ } else {
+ i2s = &cfg->inport; /* I2S is input */
+ fm = &cfg->outport; /* FM is output */
+ }
+
+ fm->type = CG2900_BT_VP_TYPE_FM;
+
+ i2s->type = CG2900_BT_VP_TYPE_I2S;
+ i2s->i2s.index = CG2900_BT_SESSION_I2S_INDEX_I2S;
+ i2s->i2s.channel = info->i2s_config.channel_sel;
+}
+
+/**
+ * conn_start_i2s_to_fm_rx() - Start an audio stream connecting FM RX to I2S.
+ * @audio_user: Audio user to check for.
+ * @stream_handle: [out] Pointer where to store the stream handle.
+ *
+ * This function sets up an FM RX to I2S stream.
+ * It does this by first setting the output mode and then the configuration of
+ * the External Sample Rate Converter.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ECOMM if no response was received.
+ * -ENOMEM upon allocation errors.
+ * -EIO for other errors.
+ */
+static int conn_start_i2s_to_fm_rx(struct audio_user *audio_user,
+ unsigned int *stream_handle)
+{
+ int err = 0;
+ union cg2900_endpoint_config_union *fm_config;
+ struct audio_info *info = audio_user->info;
+
+ dev_dbg(FM_DEV, "conn_start_i2s_to_fm_rx\n");
+
+ fm_config = find_endpoint(ENDPOINT_FM_RX, &info->endpoints);
+ if (!fm_config) {
+ dev_err(FM_DEV, "FM RX not configured before stream start\n");
+ return -EIO;
+ }
+
+ if (!(info->i2s_config_known)) {
+ dev_err(FM_DEV,
+ "I2S DAI not configured before stream start\n");
+ return -EIO;
+ }
+
+ /*
+ * Use mutex to assure that only ONE command is sent at any
+ * time on each channel.
+ */
+ mutex_lock(&info->fm_mutex);
+ mutex_lock(&info->bt_mutex);
+
+ /*
+ * Now set the output mode of the External Sample Rate Converter by
+ * sending HCI_Write command with AUP_EXT_SetMode.
+ */
+ err = send_fm_write_1_param(audio_user,
+ CG2900_FM_CMD_ID_AUP_EXT_SET_MODE,
+ CG2900_FM_CMD_AUP_EXT_SET_MODE_PARALLEL);
+ if (err)
+ goto finished_unlock_mutex;
+
+ /*
+ * Now configure the External Sample Rate Converter by sending
+ * HCI_Write command with AUP_EXT_SetControl.
+ */
+ err = send_fm_write_1_param(
+ audio_user, CG2900_FM_CMD_ID_AUP_EXT_SET_CTRL,
+ fm_get_conversion(fm_config->fm.sample_rate));
+ if (err)
+ goto finished_unlock_mutex;
+
+ /* Set up the stream */
+ if (info->revision == CHIP_REV_PG1) {
+ struct i2s_fm_stream_config_priv stream_priv;
+
+ /* Now send HCI_VS_Set_Session_Configuration command */
+ stream_priv.fm_config = &fm_config->fm;
+ stream_priv.rx = true;
+ err = send_vs_session_config(audio_user, config_i2s_fm_stream,
+ &stream_priv);
+ } else {
+ struct mc_vs_port_cfg_fm fm_cfg;
+
+ memset(&fm_cfg, 0, sizeof(fm_cfg));
+
+ /* Configure port FM RX */
+ /* Expects 0-3 - same as user API - so no conversion needed */
+ PORTCFG_FM_SET_SRATE(fm_cfg, (u8)fm_config->fm.sample_rate);
+
+ err = send_vs_port_cfg(audio_user, CG2900_MC_PORT_FM_RX_1,
+ &fm_cfg, sizeof(fm_cfg));
+ if (err)
+ goto finished_unlock_mutex;
+
+ /* CreateStream */
+ err = send_vs_create_stream(audio_user,
+ CG2900_MC_PORT_FM_RX_1,
+ CG2900_MC_PORT_I2S,
+ 0); /* chip doesn't care */
+ }
+
+ if (err < 0)
+ goto finished_unlock_mutex;
+
+ /* Store the stream handle (used for start and stop stream) */
+ *stream_handle = (u8)err;
+ dev_dbg(FM_DEV, "stream_handle set to %d\n", *stream_handle);
+
+ /* Now start the stream */
+ if (info->revision == CHIP_REV_PG1)
+ err = send_vs_session_ctrl(audio_user, *stream_handle,
+ CG2900_BT_SESSION_START);
+ else
+ err = send_vs_stream_ctrl(audio_user, *stream_handle,
+ CG2900_MC_STREAM_START);
+ /*Let's delete a stream.*/
+ if (err < 0) {
+ dev_dbg(BT_DEV, "Could not start a stream.");
+ (void)send_vs_delete_stream(audio_user, *stream_handle);
+ }
+
+finished_unlock_mutex:
+ dev_dbg(FM_DEV, "New resp_state: IDLE\n");
+ audio_user->resp_state = IDLE;
+ mutex_unlock(&info->bt_mutex);
+ mutex_unlock(&info->fm_mutex);
+ return err;
+}
+
+/**
+ * conn_start_i2s_to_fm_tx() - Start an audio stream connecting FM TX to I2S.
+ * @audio_user: Audio user to check for.
+ * @stream_handle: [out] Pointer where to store the stream handle.
+ *
+ * This function sets up an I2S to FM TX stream.
+ * It does this by first setting the Audio Input source and then setting the
+ * configuration and input source of BT sample rate converter.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ECOMM if no response was received.
+ * -ENOMEM upon allocation errors.
+ * -EIO for other errors.
+ */
+static int conn_start_i2s_to_fm_tx(struct audio_user *audio_user,
+ unsigned int *stream_handle)
+{
+ int err = 0;
+ union cg2900_endpoint_config_union *fm_config;
+ struct audio_info *info = audio_user->info;
+
+ dev_dbg(FM_DEV, "conn_start_i2s_to_fm_tx\n");
+
+ fm_config = find_endpoint(ENDPOINT_FM_TX, &info->endpoints);
+ if (!fm_config) {
+ dev_err(FM_DEV, "FM TX not configured before stream start\n");
+ return -EIO;
+ }
+
+ if (!(info->i2s_config_known)) {
+ dev_err(FM_DEV,
+ "I2S DAI not configured before stream start\n");
+ return -EIO;
+ }
+
+ /*
+ * Use mutex to assure that only ONE command is sent at any time
+ * on each channel.
+ */
+ mutex_lock(&info->fm_mutex);
+ mutex_lock(&info->bt_mutex);
+
+ /*
+ * Select Audio Input Source by sending HCI_Write command with
+ * AIP_SetMode.
+ */
+ dev_dbg(FM_DEV, "FM: AIP_SetMode\n");
+ err = send_fm_write_1_param(audio_user, CG2900_FM_CMD_ID_AIP_SET_MODE,
+ CG2900_FM_CMD_AIP_SET_MODE_INPUT_DIG);
+ if (err)
+ goto finished_unlock_mutex;
+
+ /*
+ * Now configure the BT sample rate converter by sending HCI_Write
+ * command with AIP_BT_SetControl.
+ */
+ dev_dbg(FM_DEV, "FM: AIP_BT_SetControl\n");
+ err = send_fm_write_1_param(
+ audio_user, CG2900_FM_CMD_ID_AIP_BT_SET_CTRL,
+ fm_get_conversion(fm_config->fm.sample_rate));
+ if (err)
+ goto finished_unlock_mutex;
+
+ /*
+ * Now set input of the BT sample rate converter by sending HCI_Write
+ * command with AIP_BT_SetMode.
+ */
+ dev_dbg(FM_DEV, "FM: AIP_BT_SetMode\n");
+ err = send_fm_write_1_param(audio_user,
+ CG2900_FM_CMD_ID_AIP_BT_SET_MODE,
+ CG2900_FM_CMD_AIP_BT_SET_MODE_INPUT_PAR);
+ if (err)
+ goto finished_unlock_mutex;
+
+ /* Set up the stream */
+ if (info->revision == CHIP_REV_PG1) {
+ struct i2s_fm_stream_config_priv stream_priv;
+
+ /* Now send HCI_VS_Set_Session_Configuration command */
+ stream_priv.fm_config = &fm_config->fm;
+ stream_priv.rx = false;
+ err = send_vs_session_config(audio_user, config_i2s_fm_stream,
+ &stream_priv);
+ } else {
+ struct mc_vs_port_cfg_fm fm_cfg;
+
+ memset(&fm_cfg, 0, sizeof(fm_cfg));
+
+ /* Configure port FM TX */
+ /* Expects 0-3 - same as user API - so no conversion needed */
+ PORTCFG_FM_SET_SRATE(fm_cfg, (u8)fm_config->fm.sample_rate);
+
+ err = send_vs_port_cfg(audio_user, CG2900_MC_PORT_FM_TX,
+ &fm_cfg, sizeof(fm_cfg));
+ if (err)
+ goto finished_unlock_mutex;
+
+ /* CreateStream */
+ err = send_vs_create_stream(audio_user,
+ CG2900_MC_PORT_I2S,
+ CG2900_MC_PORT_FM_TX,
+ 0); /* chip doesn't care */
+ }
+
+ if (err < 0)
+ goto finished_unlock_mutex;
+
+ /* Store the stream handle (used for start and stop stream) */
+ *stream_handle = (u8)err;
+ dev_dbg(FM_DEV, "stream_handle set to %d\n", *stream_handle);
+
+ /* Now start the stream */
+ if (info->revision == CHIP_REV_PG1)
+ err = send_vs_session_ctrl(audio_user, *stream_handle,
+ CG2900_BT_SESSION_START);
+ else
+ err = send_vs_stream_ctrl(audio_user, *stream_handle,
+ CG2900_MC_STREAM_START);
+ /* Let's delete and release stream.*/
+ if (err < 0) {
+ dev_dbg(BT_DEV, "Could not start a stream.");
+ (void)send_vs_delete_stream(audio_user, *stream_handle);
+ }
+
+finished_unlock_mutex:
+ dev_dbg(FM_DEV, "New resp_state: IDLE\n");
+ audio_user->resp_state = IDLE;
+ mutex_unlock(&info->bt_mutex);
+ mutex_unlock(&info->fm_mutex);
+ return err;
+}
+
+/**
+ * config_pcm_sco_stream() - Callback for @send_vs_session_config.
+ * @info: Audio info structure.
+ * @_priv: Pointer to a @cg2900_endpoint_config_sco_in_out struct.
+ * @cfg: Pointer to stream config block in command packet.
+ *
+ * Fills in stream configuration for PCM-SCO.
+ */
+static void config_pcm_sco_stream(struct audio_info *info, void *_priv,
+ struct session_config_stream *cfg)
+{
+ struct cg2900_endpoint_config_sco_in_out *sco_ep = _priv;
+
+ cfg->media_type = CG2900_BT_SESSION_MEDIA_TYPE_AUDIO;
+
+ SESSIONCFG_SET_CHANNELS(cfg, CG2900_BT_MEDIA_CONFIG_MONO);
+ SESSIONCFG_I2S_SET_SRATE(cfg,
+ session_config_sample_rate(sco_ep->sample_rate));
+
+ cfg->codec_type = CG2900_CODEC_TYPE_NONE;
+ /* codec mode and parameters not used */
+
+ cfg->inport.type = CG2900_BT_VP_TYPE_BT_SCO;
+ cfg->inport.sco.acl_handle = cpu_to_le16(DEFAULT_ACL_HANDLE);
+
+ cfg->outport.type = CG2900_BT_VP_TYPE_PCM;
+ cfg->outport.pcm.index = CG2900_BT_SESSION_PCM_INDEX_PCM_I2S;
+
+ SESSIONCFG_PCM_SET_USED(cfg->outport, 0,
+ info->i2s_pcm_config.slot_0_used);
+ SESSIONCFG_PCM_SET_USED(cfg->outport, 1,
+ info->i2s_pcm_config.slot_1_used);
+ SESSIONCFG_PCM_SET_USED(cfg->outport, 2,
+ info->i2s_pcm_config.slot_2_used);
+ SESSIONCFG_PCM_SET_USED(cfg->outport, 3,
+ info->i2s_pcm_config.slot_3_used);
+
+ cfg->outport.pcm.slot_start[0] =
+ info->i2s_pcm_config.slot_0_start;
+ cfg->outport.pcm.slot_start[1] =
+ info->i2s_pcm_config.slot_1_start;
+ cfg->outport.pcm.slot_start[2] =
+ info->i2s_pcm_config.slot_2_start;
+ cfg->outport.pcm.slot_start[3] =
+ info->i2s_pcm_config.slot_3_start;
+}
+
+/**
+ * conn_start_pcm_to_sco() - Start an audio stream connecting Bluetooth (e)SCO to PCM_I2S.
+ * @audio_user: Audio user to check for.
+ * @stream_handle: [out] Pointer where to store the stream handle.
+ *
+ * This function sets up a BT to_from PCM_I2S stream. It does this by
+ * first setting the Session configuration and then starting the Audio
+ * Stream.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ECOMM if no response was received.
+ * -ENOMEM upon allocation errors.
+ * Errors from @cg2900_write
+ * -EIO for other errors.
+ */
+static int conn_start_pcm_to_sco(struct audio_user *audio_user,
+ unsigned int *stream_handle)
+{
+ int err = 0;
+ union cg2900_endpoint_config_union *bt_config;
+ struct audio_info *info = audio_user->info;
+
+ dev_dbg(BT_DEV, "conn_start_pcm_to_sco\n");
+
+ bt_config = find_endpoint(ENDPOINT_BT_SCO_INOUT, &info->endpoints);
+ if (!bt_config) {
+ dev_err(BT_DEV, "BT not configured before stream start\n");
+ return -EIO;
+ }
+
+ if (!(info->i2s_pcm_config_known)) {
+ dev_err(BT_DEV,
+ "I2S_PCM DAI not configured before stream start\n");
+ return -EIO;
+ }
+
+ /*
+ * Use mutex to assure that only ONE command is sent at any time on each
+ * channel.
+ */
+ mutex_lock(&info->bt_mutex);
+
+ /* Set up the stream */
+ if (info->revision == CHIP_REV_PG1) {
+ err = send_vs_session_config(audio_user, config_pcm_sco_stream,
+ &bt_config->sco);
+ } else {
+ struct mc_vs_port_cfg_sco sco_cfg;
+
+ /* zero codec params etc */
+ memset(&sco_cfg, 0, sizeof(sco_cfg));
+ sco_cfg.acl_id = DEFAULT_ACL_HANDLE;
+ PORTCFG_SCO_SET_CODEC(sco_cfg, CG2900_CODEC_TYPE_NONE);
+
+ err = send_vs_port_cfg(audio_user, CG2900_MC_PORT_BT_SCO,
+ &sco_cfg, sizeof(sco_cfg));
+ if (err)
+ goto finished_unlock_mutex;
+
+ /* CreateStream */
+ err = send_vs_create_stream(audio_user,
+ CG2900_MC_PORT_PCM_I2S,
+ CG2900_MC_PORT_BT_SCO,
+ 0); /* chip doesn't care */
+ }
+
+ if (err < 0)
+ goto finished_unlock_mutex;
+
+ /* Store the stream handle (used for start and stop stream) */
+ *stream_handle = (u8)err;
+ dev_dbg(BT_DEV, "stream_handle set to %d\n", *stream_handle);
+
+ /* Now start the stream */
+ if (info->revision == CHIP_REV_PG1)
+ err = send_vs_session_ctrl(audio_user, *stream_handle,
+ CG2900_BT_SESSION_START);
+ else
+ err = send_vs_stream_ctrl(audio_user, *stream_handle,
+ CG2900_MC_STREAM_START);
+ /* Let's delete and release stream.*/
+ if (err < 0) {
+ dev_dbg(BT_DEV, "Could not start a stream.");
+ (void)send_vs_delete_stream(audio_user, *stream_handle);
+ }
+
+finished_unlock_mutex:
+ dev_dbg(BT_DEV, "New resp_state: IDLE\n");
+ audio_user->resp_state = IDLE;
+ mutex_unlock(&info->bt_mutex);
+ return err;
+}
+
+/**
+ * conn_stop_stream() - Stops an audio stream defined by @stream_handle.
+ * @audio_user: Audio user to check for.
+ * @stream_handle: Handle of the audio stream.
+ *
+ * This function is used to stop an audio stream defined by a stream
+ * handle. It does this by first stopping the stream and then
+ * resetting the session/stream.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ECOMM if no response was received.
+ * -ENOMEM upon allocation errors.
+ * Errors from @cg2900_write.
+ * -EIO for other errors.
+ */
+static int conn_stop_stream(struct audio_user *audio_user,
+ unsigned int stream_handle)
+{
+ int err = 0;
+ struct audio_info *info = audio_user->info;
+
+ dev_dbg(BT_DEV, "conn_stop_stream handle %d\n", stream_handle);
+
+ /*
+ * Use mutex to assure that only ONE command is sent at any
+ * time on each channel.
+ */
+ mutex_lock(&info->bt_mutex);
+
+ /* Now stop the stream */
+ if (info->revision == CHIP_REV_PG1)
+ err = send_vs_session_ctrl(audio_user, stream_handle,
+ CG2900_BT_SESSION_STOP);
+ else
+ err = send_vs_stream_ctrl(audio_user, stream_handle,
+ CG2900_MC_STREAM_STOP);
+ if (err)
+ goto finished_unlock_mutex;
+
+ err = send_vs_delete_stream(audio_user, stream_handle);
+
+finished_unlock_mutex:
+ dev_dbg(BT_DEV, "New resp_state: IDLE\n");
+ audio_user->resp_state = IDLE;
+ mutex_unlock(&info->bt_mutex);
+ return err;
+}
+
+/**
+ * cg2900_audio_get_devices() - Returns connected CG2900 Audio devices.
+ * @devices: Array of CG2900 Audio devices.
+ * @size: Max number of devices in array.
+ *
+ * Returns:
+ * 0 if no devices exist.
+ * > 0 is the number of devices inserted in the list.
+ * -EINVAL upon bad input parameter.
+ */
+int cg2900_audio_get_devices(struct device *devices[], __u8 size)
+{
+ struct list_head *cursor;
+ struct audio_info *tmp;
+ int i = 0;
+
+ if (!size) {
+ pr_err("No space to insert devices into list\n");
+ return 0;
+ }
+
+ if (!devices) {
+ pr_err("NULL submitted as devices array\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Go through and store the devices. If NULL is supplied for dev
+ * just return first device found.
+ */
+ list_for_each(cursor, &cg2900_audio_devices) {
+ tmp = list_entry(cursor, struct audio_info, list);
+ devices[i] = tmp->parent;
+ i++;
+ if (i == size)
+ break;
+ }
+ return i;
+}
+EXPORT_SYMBOL_GPL(cg2900_audio_get_devices);
+
+/**
+ * cg2900_audio_open() - Opens a session to the ST-Ericsson CG2900 Audio control interface.
+ * @session: [out] Address where to store the session identifier.
+ * Allocated by caller, must not be NULL.
+ * @parent: Parent device representing the CG2900 controller connected.
+ * If NULL is supplied the first available device is used.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EACCES if no info structure can be found.
+ * -EINVAL upon bad input parameter.
+ * -ENOMEM upon allocation failure.
+ * -EMFILE if no more user session could be opened.
+ * -EIO upon failure to register to CG2900.
+ * Error codes from get_info.
+ */
+int cg2900_audio_open(unsigned int *session, struct device *parent)
+{
+ int err = 0;
+ int i;
+ struct audio_info *info;
+ struct cg2900_user_data *pf_data_bt;
+ struct cg2900_user_data *pf_data_fm;
+
+ pr_debug("cg2900_audio_open");
+
+ info = get_info(parent);
+ if (!info) {
+ pr_err("No audio info exist");
+ return -EACCES;
+ } else if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ if (!session) {
+ pr_err("NULL supplied as session");
+ return -EINVAL;
+ }
+
+ mutex_lock(&info->management_mutex);
+
+ *session = 0;
+
+ /*
+ * First find a free session to use and allocate the session structure.
+ */
+ for (i = FIRST_USER;
+ i < MAX_NBR_OF_USERS && cg2900_audio_sessions[i];
+ i++)
+ ; /* Just loop until found or end reached */
+
+ if (i >= MAX_NBR_OF_USERS) {
+ pr_err("Couldn't find free user");
+ err = -EMFILE;
+ goto finished;
+ }
+
+ cg2900_audio_sessions[i] =
+ kzalloc(sizeof(*(cg2900_audio_sessions[0])), GFP_KERNEL);
+ if (!cg2900_audio_sessions[i]) {
+ pr_err("Could not allocate user");
+ err = -ENOMEM;
+ goto finished;
+ }
+ pr_debug("Found free session %d", i);
+ *session = i;
+ info->nbr_of_users_active++;
+
+ cg2900_audio_sessions[*session]->resp_state = IDLE;
+ cg2900_audio_sessions[*session]->session = *session;
+ cg2900_audio_sessions[*session]->info = info;
+
+ pf_data_bt = dev_get_platdata(info->dev_bt);
+ pf_data_fm = dev_get_platdata(info->dev_fm);
+
+ if (info->nbr_of_users_active == 1) {
+ struct cg2900_rev_data rev_data;
+
+ /*
+ * First user so register to CG2900 Core.
+ * First the BT audio device.
+ */
+ err = pf_data_bt->open(pf_data_bt);
+ if (err) {
+ dev_err(BT_DEV, "Failed to open BT audio channel\n");
+ goto error_handling;
+ }
+
+ /* Then the FM audio device */
+ err = pf_data_fm->open(pf_data_fm);
+ if (err) {
+ dev_err(FM_DEV, "Failed to open FM audio channel\n");
+ goto error_handling;
+ }
+
+ /* Read chip revision data */
+ if (!pf_data_bt->get_local_revision(pf_data_bt, &rev_data)) {
+ pr_err("Couldn't retrieve revision data");
+ err = -EIO;
+ goto error_handling;
+ }
+
+ /* Decode revision data */
+ switch (rev_data.revision) {
+ case CG2900_PG1_REV:
+ case CG2900_PG1_SPECIAL_REV:
+ info->revision = CHIP_REV_PG1;
+ break;
+
+ case CG2900_PG2_REV:
+ info->revision = CHIP_REV_PG2;
+ break;
+
+ default:
+ pr_err("Chip rev 0x%04X sub 0x%04X not supported",
+ rev_data.revision, rev_data.sub_version);
+ err = -EIO;
+ goto error_handling;
+ }
+
+ info->state = OPENED;
+ }
+
+ pr_info("Session %d opened", *session);
+
+ goto finished;
+
+error_handling:
+ if (pf_data_fm->opened)
+ pf_data_fm->close(pf_data_fm);
+ if (pf_data_bt->opened)
+ pf_data_bt->close(pf_data_bt);
+ info->nbr_of_users_active--;
+ kfree(cg2900_audio_sessions[*session]);
+ cg2900_audio_sessions[*session] = NULL;
+finished:
+ mutex_unlock(&info->management_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(cg2900_audio_open);
+
+/**
+ * cg2900_audio_close() - Closes an opened session to the ST-Ericsson CG2900 audio control interface.
+ * @session: [in_out] Pointer to session identifier to close.
+ * Will be 0 after this call.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL upon bad input parameter.
+ * -EIO if driver has not been opened.
+ * -EACCES if session has not opened.
+ */
+int cg2900_audio_close(unsigned int *session)
+{
+ int err = 0;
+ struct audio_user *audio_user;
+ struct audio_info *info;
+ struct cg2900_user_data *pf_data_bt;
+ struct cg2900_user_data *pf_data_fm;
+
+ pr_debug("cg2900_audio_close");
+
+ if (!session) {
+ pr_err("NULL pointer supplied");
+ return -EINVAL;
+ }
+
+ audio_user = get_session_user(*session);
+ if (!audio_user) {
+ pr_err("Invalid session ID");
+ return -EINVAL;
+ }
+
+ info = audio_user->info;
+
+ if (info->state != OPENED) {
+ dev_err(BT_DEV, "Audio driver not open\n");
+ return -EIO;
+ }
+
+ mutex_lock(&info->management_mutex);
+
+ pf_data_bt = dev_get_platdata(info->dev_bt);
+ pf_data_fm = dev_get_platdata(info->dev_fm);
+
+ if (!cg2900_audio_sessions[*session]) {
+ dev_err(BT_DEV, "Session %d not opened\n", *session);
+ err = -EACCES;
+ goto err_unlock_mutex;
+ }
+
+ kfree(cg2900_audio_sessions[*session]);
+ cg2900_audio_sessions[*session] = NULL;
+
+ info->nbr_of_users_active--;
+ if (info->nbr_of_users_active == 0) {
+ /* No more sessions open. Close channels */
+ pf_data_fm->close(pf_data_fm);
+ pf_data_bt->close(pf_data_bt);
+ info->state = CLOSED;
+ }
+
+ dev_info(BT_DEV, "Session %d closed\n", *session);
+
+ *session = 0;
+
+err_unlock_mutex:
+ mutex_unlock(&info->management_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(cg2900_audio_close);
+
+/**
+ * cg2900_audio_set_dai_config() - Sets the Digital Audio Interface configuration.
+ * @session: Session identifier this call is related to.
+ * @config: Pointer to the configuration to set.
+ * Allocated by caller, must not be NULL.
+ *
+ * Sets the Digital Audio Interface (DAI) configuration. The DAI is the external
+ * interface between the combo chip and the platform.
+ * For example the PCM or I2S interface.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL upon bad input parameter.
+ * -EIO if driver has not been opened.
+ * -ENOMEM upon allocation failure.
+ * -EACCES if trying to set unsupported configuration.
+ * Errors from @receive_bt_cmd_complete.
+ */
+int cg2900_audio_set_dai_config(unsigned int session,
+ struct cg2900_dai_config *config)
+{
+ int err = 0;
+ struct audio_user *audio_user;
+ struct audio_info *info;
+
+ pr_debug("cg2900_audio_set_dai_config session %d", session);
+
+ audio_user = get_session_user(session);
+ if (!audio_user)
+ return -EINVAL;
+
+ info = audio_user->info;
+
+ if (info->state != OPENED) {
+ dev_err(BT_DEV, "Audio driver not open\n");
+ return -EIO;
+ }
+
+ /* Different commands are used for PG1 and PG2 */
+ if (info->revision == CHIP_REV_PG1)
+ err = set_dai_config_pg1(audio_user, config);
+ else if (info->revision == CHIP_REV_PG2)
+ err = set_dai_config_pg2(audio_user, config);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(cg2900_audio_set_dai_config);
+
+/**
+ * cg2900_audio_get_dai_config() - Gets the current Digital Audio Interface configuration.
+ * @session: Session identifier this call is related to.
+ * @config: [out] Pointer to the configuration to get.
+ * Allocated by caller, must not be NULL.
+ *
+ * Gets the current Digital Audio Interface configuration. Currently this method
+ * can only be called after some one has called
+ * cg2900_audio_set_dai_config(), there is today no way of getting
+ * the static settings file parameters from this method.
+ * Note that the @port parameter within @config must be set when calling this
+ * function so that the ST-Ericsson CG2900 Audio driver will know which
+ * configuration to return.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL upon bad input parameter.
+ * -EIO if driver has not been opened or configuration has not been set.
+ */
+int cg2900_audio_get_dai_config(unsigned int session,
+ struct cg2900_dai_config *config)
+{
+ int err = 0;
+ struct audio_user *audio_user;
+ struct audio_info *info;
+
+ pr_debug("cg2900_audio_get_dai_config session %d", session);
+
+ if (!config) {
+ pr_err("NULL supplied as config structure");
+ return -EINVAL;
+ }
+
+ audio_user = get_session_user(session);
+ if (!audio_user)
+ return -EINVAL;
+
+ info = audio_user->info;
+
+ if (info->state != OPENED) {
+ dev_err(BT_DEV, "Audio driver not open\n");
+ return -EIO;
+ }
+
+ /*
+ * Return DAI configuration based on the received port.
+ * If port has not been configured return error.
+ */
+ switch (config->port) {
+ case PORT_0_I2S:
+ mutex_lock(&info->management_mutex);
+ if (info->i2s_config_known)
+ memcpy(&config->conf.i2s,
+ &info->i2s_config,
+ sizeof(config->conf.i2s));
+ else
+ err = -EIO;
+ mutex_unlock(&info->management_mutex);
+ break;
+
+ case PORT_1_I2S_PCM:
+ mutex_lock(&info->management_mutex);
+ if (info->i2s_pcm_config_known)
+ memcpy(&config->conf.i2s_pcm,
+ &info->i2s_pcm_config,
+ sizeof(config->conf.i2s_pcm));
+ else
+ err = -EIO;
+ mutex_unlock(&info->management_mutex);
+ break;
+
+ default:
+ dev_err(BT_DEV, "Unknown port configuration %d\n",
+ config->port);
+ err = -EIO;
+ break;
+ };
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(cg2900_audio_get_dai_config);
+
+/**
+ * cg2900_audio_config_endpoint() - Configures one endpoint in the combo chip's audio system.
+ * @session: Session identifier this call is related to.
+ * @config: Pointer to the endpoint's configuration structure.
+ *
+ * Configures one endpoint in the combo chip's audio system.
+ * Supported @endpoint_id values are:
+ * * ENDPOINT_BT_SCO_INOUT
+ * * ENDPOINT_BT_A2DP_SRC
+ * * ENDPOINT_FM_RX
+ * * ENDPOINT_FM_TX
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL upon bad input parameter.
+ * -EIO if driver has not been opened.
+ * -EACCES if supplied cg2900_dai_config struct contains not supported
+ * endpoint_id.
+ */
+int cg2900_audio_config_endpoint(unsigned int session,
+ struct cg2900_endpoint_config *config)
+{
+ struct audio_user *audio_user;
+ struct audio_info *info;
+
+ pr_debug("cg2900_audio_config_endpoint\n");
+
+ if (!config) {
+ pr_err("NULL supplied as configuration structure");
+ return -EINVAL;
+ }
+
+ audio_user = get_session_user(session);
+ if (!audio_user)
+ return -EINVAL;
+
+ info = audio_user->info;
+
+ if (info->state != OPENED) {
+ dev_err(BT_DEV, "Audio driver not open\n");
+ return -EIO;
+ }
+
+ switch (config->endpoint_id) {
+ case ENDPOINT_BT_SCO_INOUT:
+ case ENDPOINT_BT_A2DP_SRC:
+ case ENDPOINT_FM_RX:
+ case ENDPOINT_FM_TX:
+ add_endpoint(config, &info->endpoints);
+ break;
+
+ case ENDPOINT_PORT_0_I2S:
+ case ENDPOINT_PORT_1_I2S_PCM:
+ case ENDPOINT_SLIMBUS_VOICE:
+ case ENDPOINT_SLIMBUS_AUDIO:
+ case ENDPOINT_BT_A2DP_SNK:
+ case ENDPOINT_ANALOG_OUT:
+ case ENDPOINT_DSP_AUDIO_IN:
+ case ENDPOINT_DSP_AUDIO_OUT:
+ case ENDPOINT_DSP_VOICE_IN:
+ case ENDPOINT_DSP_VOICE_OUT:
+ case ENDPOINT_DSP_TONE_IN:
+ case ENDPOINT_BURST_BUFFER_IN:
+ case ENDPOINT_BURST_BUFFER_OUT:
+ case ENDPOINT_MUSIC_DECODER:
+ case ENDPOINT_HCI_AUDIO_IN:
+ default:
+ dev_err(BT_DEV, "Unsupported endpoint_id %d\n",
+ config->endpoint_id);
+ return -EACCES;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cg2900_audio_config_endpoint);
+
+static bool is_dai_port(enum cg2900_audio_endpoint_id ep)
+{
+ /* These are the only supported ones */
+ return (ep == ENDPOINT_PORT_0_I2S) || (ep == ENDPOINT_PORT_1_I2S_PCM);
+}
+
+/**
+ * cg2900_audio_start_stream() - Connects two endpoints and starts the audio stream.
+ * @session: Session identifier this call is related to.
+ * @ep_1: One of the endpoints, no relation to direction or role.
+ * @ep_2: The other endpoint, no relation to direction or role.
+ * @stream_handle: Pointer where to store the stream handle.
+ * Allocated by caller, must not be NULL.
+ *
+ * Connects two endpoints and starts the audio stream.
+ * Note that the endpoints need to be configured before the stream is started;
+ * DAI endpoints, such as ENDPOINT_PORT_0_I2S, are
+ * configured through @cg2900_audio_set_dai_config() while other
+ * endpoints are configured through @cg2900_audio_config_endpoint().
+ *
+ * Supported @endpoint_id values are:
+ * * ENDPOINT_PORT_0_I2S
+ * * ENDPOINT_PORT_1_I2S_PCM
+ * * ENDPOINT_BT_SCO_INOUT
+ * * ENDPOINT_FM_RX
+ * * ENDPOINT_FM_TX
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL upon bad input parameter or unsupported configuration.
+ * -EIO if driver has not been opened.
+ * Errors from @conn_start_i2s_to_fm_rx, @conn_start_i2s_to_fm_tx, and
+ * @conn_start_pcm_to_sco.
+ */
+int cg2900_audio_start_stream(unsigned int session,
+ enum cg2900_audio_endpoint_id ep_1,
+ enum cg2900_audio_endpoint_id ep_2,
+ unsigned int *stream_handle)
+{
+ int err;
+ struct audio_user *audio_user;
+ struct audio_info *info;
+
+ pr_debug("cg2900_audio_start_stream session %d ep_1 %d ep_2 %d",
+ session, ep_1, ep_2);
+
+ audio_user = get_session_user(session);
+ if (!audio_user)
+ return -EINVAL;
+
+ info = audio_user->info;
+
+ if (info->state != OPENED) {
+ dev_err(BT_DEV, "Audio driver not open\n");
+ return -EIO;
+ }
+
+ /* Put digital interface in ep_1 to simplify comparison below */
+ if (!is_dai_port(ep_1)) {
+ /* Swap endpoints */
+ enum cg2900_audio_endpoint_id t = ep_1;
+ ep_1 = ep_2;
+ ep_2 = t;
+ }
+
+ if (ep_1 == ENDPOINT_PORT_1_I2S_PCM && ep_2 == ENDPOINT_BT_SCO_INOUT) {
+ err = conn_start_pcm_to_sco(audio_user, stream_handle);
+ } else if (ep_1 == ENDPOINT_PORT_0_I2S && ep_2 == ENDPOINT_FM_RX) {
+ err = conn_start_i2s_to_fm_rx(audio_user, stream_handle);
+ } else if (ep_1 == ENDPOINT_PORT_0_I2S && ep_2 == ENDPOINT_FM_TX) {
+ err = conn_start_i2s_to_fm_tx(audio_user, stream_handle);
+ } else {
+ dev_err(BT_DEV, "Endpoint config not handled: ep1: %d, "
+ "ep2: %d\n", ep_1, ep_2);
+ err = -EINVAL;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(cg2900_audio_start_stream);
+
+/**
+ * cg2900_audio_stop_stream() - Stops a stream and disconnects the endpoints.
+ * @session: Session identifier this call is related to.
+ * @stream_handle: Handle to the stream to stop.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL upon bad input parameter.
+ * -EIO if driver has not been opened.
+ */
+int cg2900_audio_stop_stream(unsigned int session, unsigned int stream_handle)
+{
+ struct audio_user *audio_user;
+ struct audio_info *info;
+
+ pr_debug("cg2900_audio_stop_stream handle %d", stream_handle);
+
+ audio_user = get_session_user(session);
+ if (!audio_user)
+ return -EINVAL;
+
+ info = audio_user->info;
+
+ if (info->state != OPENED) {
+ dev_err(BT_DEV, "Audio driver not open\n");
+ return -EIO;
+ }
+
+ return conn_stop_stream(audio_user, stream_handle);
+}
+EXPORT_SYMBOL_GPL(cg2900_audio_stop_stream);
+
+/**
+ * audio_dev_open() - Open char device.
+ * @inode: Device driver information.
+ * @filp: Pointer to the file struct.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ENOMEM if allocation failed.
+ * Errors from @cg2900_audio_open.
+ */
+static int audio_dev_open(struct inode *inode, struct file *filp)
+{
+ int err;
+ struct char_dev_info *char_dev_info;
+ int minor;
+ struct audio_info *info = NULL;
+ struct audio_info *tmp;
+ struct list_head *cursor;
+
+ pr_debug("audio_dev_open");
+
+ minor = iminor(inode);
+
+ /* Find the info struct for this file */
+ list_for_each(cursor, &cg2900_audio_devices) {
+ tmp = list_entry(cursor, struct audio_info, list);
+ if (tmp->misc_dev.minor == minor) {
+ info = tmp;
+ break;
+ }
+ }
+ if (!info) {
+ pr_err("Could not identify device in inode");
+ return -EINVAL;
+ }
+
+ /*
+ * Allocate the char dev info structure. It will be stored inside
+ * the file pointer and supplied when file_ops are called.
+ * It's free'd in audio_dev_release.
+ */
+ char_dev_info = kzalloc(sizeof(*char_dev_info), GFP_KERNEL);
+ if (!char_dev_info) {
+ dev_err(BT_DEV, "Couldn't allocate char_dev_info\n");
+ return -ENOMEM;
+ }
+ filp->private_data = char_dev_info;
+ char_dev_info->info = info;
+ info->filp = filp;
+
+ mutex_init(&char_dev_info->management_mutex);
+ mutex_init(&char_dev_info->rw_mutex);
+ skb_queue_head_init(&char_dev_info->rx_queue);
+
+ mutex_lock(&char_dev_info->management_mutex);
+ err = cg2900_audio_open(&char_dev_info->session, info->dev_bt->parent);
+ mutex_unlock(&char_dev_info->management_mutex);
+ if (err) {
+ dev_err(BT_DEV, "Failed to open CG2900 Audio driver (%d)\n",
+ err);
+ goto error_handling_free_mem;
+ }
+
+ return 0;
+
+error_handling_free_mem:
+ kfree(char_dev_info);
+ filp->private_data = NULL;
+ return err;
+}
+
+/**
+ * audio_dev_release() - Release char device.
+ * @inode: Device driver information.
+ * @filp: Pointer to the file struct.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EBADF if NULL pointer was supplied in private data.
+ * Errors from @cg2900_audio_close.
+ */
+static int audio_dev_release(struct inode *inode, struct file *filp)
+{
+ int err = 0;
+ struct char_dev_info *dev = filp->private_data;
+ struct audio_info *info;
+
+ if (!dev) {
+ pr_err("audio_dev_release: Transport closed");
+ return -EBADF;
+ }
+
+ info = dev->info;
+
+ dev_dbg(BT_DEV, "audio_dev_release\n");
+
+ mutex_lock(&dev->management_mutex);
+ err = cg2900_audio_close(&dev->session);
+ if (err)
+ /*
+ * Just print the error. Still free the char_dev_info since we
+ * don't know the filp structure is valid after this call
+ */
+ dev_err(BT_DEV, "Error %d when closing CG2900 audio driver\n",
+ err);
+
+ mutex_unlock(&dev->management_mutex);
+
+ kfree(dev);
+ filp->private_data = NULL;
+ info->filp = NULL;
+
+ return err;
+}
+
+/**
+ * audio_dev_read() - Return information to the user from last @write call.
+ * @filp: Pointer to the file struct.
+ * @buf: Received buffer.
+ * @count: Size of buffer.
+ * @f_pos: Position in buffer.
+ *
+ * The audio_dev_read() function returns information from
+ * the last @write call to same char device.
+ * The data is in the following format:
+ * * OpCode of command for this data
+ * * Data content (Length of data is determined by the command OpCode, i.e.
+ * fixed for each command)
+ *
+ * Returns:
+ * Bytes successfully read (could be 0).
+ * -EBADF if NULL pointer was supplied in private data.
+ * -EFAULT if copy_to_user fails.
+ * -ENOMEM upon allocation failure.
+ */
+static ssize_t audio_dev_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *f_pos)
+{
+ struct char_dev_info *dev = filp->private_data;
+ struct audio_info *info;
+ unsigned int bytes_to_copy;
+ int err = 0;
+ struct sk_buff *skb;
+
+ if (!dev) {
+ pr_err("audio_dev_read: Transport closed");
+ return -EBADF;
+ }
+
+ info = dev->info;
+
+ dev_dbg(BT_DEV, "audio_dev_read count %d\n", count);
+
+ mutex_lock(&dev->rw_mutex);
+
+ skb = skb_dequeue(&dev->rx_queue);
+ if (!skb) {
+ /* No data to read */
+ bytes_to_copy = 0;
+ goto finished;
+ }
+
+ bytes_to_copy = min(count, (unsigned int)(skb->len));
+
+ err = copy_to_user(buf, skb->data, bytes_to_copy);
+ if (err) {
+ dev_err(BT_DEV, "copy_to_user error %d\n", err);
+ skb_queue_head(&dev->rx_queue, skb);
+ err = -EFAULT;
+ goto error_handling;
+ }
+
+ skb_pull(skb, bytes_to_copy);
+
+ if (skb->len > 0)
+ skb_queue_head(&dev->rx_queue, skb);
+ else
+ kfree_skb(skb);
+
+ goto finished;
+
+error_handling:
+ mutex_unlock(&dev->rw_mutex);
+ return (ssize_t)err;
+finished:
+ mutex_unlock(&dev->rw_mutex);
+ return bytes_to_copy;
+}
+
+/**
+ * audio_dev_write() - Call CG2900 Audio API function.
+ * @filp: Pointer to the file struct.
+ * @buf: Write buffer.
+ * @count: Size of the buffer write.
+ * @f_pos: Position of buffer.
+ *
+ * audio_dev_write() function executes supplied data and
+ * interprets it as if it was a function call to the CG2900 Audio API.
+ * The data is according to:
+ * * OpCode (4 bytes, see API).
+ * * Data according to OpCode (see API). No padding between parameters.
+ *
+ * Returns:
+ * Bytes successfully written (could be 0). Equals input @count if successful.
+ * -EBADF if NULL pointer was supplied in private data.
+ * -EFAULT if copy_from_user fails.
+ * Error codes from all CG2900 Audio API functions.
+ */
+static ssize_t audio_dev_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ u8 *rec_data;
+ struct char_dev_info *dev = filp->private_data;
+ struct audio_info *info;
+ int err = 0;
+ int op_code = 0;
+ u8 *curr_data;
+ unsigned int stream_handle;
+ struct cg2900_dai_config dai_config;
+ struct cg2900_endpoint_config ep_config;
+ enum cg2900_audio_endpoint_id ep_1;
+ enum cg2900_audio_endpoint_id ep_2;
+ int bytes_left = count;
+
+ pr_debug("audio_dev_write count %d", count);
+
+ if (!dev) {
+ pr_err("audio_dev_write: Transport closed");
+ return -EBADF;
+ }
+ info = dev->info;
+
+ rec_data = kmalloc(count, GFP_KERNEL);
+ if (!rec_data) {
+ dev_err(BT_DEV, "kmalloc failed (%d bytes)\n", count);
+ return -ENOMEM;
+ }
+
+ mutex_lock(&dev->rw_mutex);
+
+ err = copy_from_user(rec_data, buf, count);
+ if (err) {
+ dev_err(BT_DEV, "copy_from_user failed (%d)\n", err);
+ err = -EFAULT;
+ goto finished_mutex_unlock;
+ }
+
+ /* Initialize temporary data pointer used to traverse the packet */
+ curr_data = rec_data;
+
+ op_code = curr_data[0];
+ /* OpCode is int size to keep data int aligned */
+ curr_data += sizeof(unsigned int);
+ bytes_left -= sizeof(unsigned int);
+
+ switch (op_code) {
+ case CG2900_OPCODE_SET_DAI_CONF:
+ if (bytes_left < sizeof(dai_config)) {
+ dev_err(BT_DEV, "Not enough data supplied for "
+ "CG2900_OPCODE_SET_DAI_CONF\n");
+ err = -EINVAL;
+ goto finished_mutex_unlock;
+ }
+ memcpy(&dai_config, curr_data, sizeof(dai_config));
+ dev_dbg(BT_DEV, "CG2900_OPCODE_SET_DAI_CONF port %d\n",
+ dai_config.port);
+ err = cg2900_audio_set_dai_config(dev->session, &dai_config);
+ break;
+
+ case CG2900_OPCODE_GET_DAI_CONF:
+ if (bytes_left < sizeof(dai_config)) {
+ dev_err(BT_DEV, "Not enough data supplied for "
+ "CG2900_OPCODE_GET_DAI_CONF\n");
+ err = -EINVAL;
+ goto finished_mutex_unlock;
+ }
+ /*
+ * Only need to copy the port really, but let's copy
+ * like this for simplicity. It's only test functionality
+ * after all.
+ */
+ memcpy(&dai_config, curr_data, sizeof(dai_config));
+ dev_dbg(BT_DEV, "CG2900_OPCODE_GET_DAI_CONF port %d\n",
+ dai_config.port);
+ err = cg2900_audio_get_dai_config(dev->session, &dai_config);
+ if (!err) {
+ int len;
+ struct sk_buff *skb;
+
+ /*
+ * Command succeeded. Store data so it can be returned
+ * when calling read.
+ */
+ len = sizeof(op_code) + sizeof(dai_config);
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb) {
+ dev_err(BT_DEV, "CG2900_OPCODE_GET_DAI_CONF: "
+ "Could not allocate skb\n");
+ err = -ENOMEM;
+ goto finished_mutex_unlock;
+ }
+ memcpy(skb_put(skb, sizeof(op_code)), &op_code,
+ sizeof(op_code));
+ memcpy(skb_put(skb, sizeof(dai_config)),
+ &dai_config, sizeof(dai_config));
+ skb_queue_tail(&dev->rx_queue, skb);
+ }
+ break;
+
+ case CG2900_OPCODE_CONFIGURE_ENDPOINT:
+ if (bytes_left < sizeof(ep_config)) {
+ dev_err(BT_DEV, "Not enough data supplied for "
+ "CG2900_OPCODE_CONFIGURE_ENDPOINT\n");
+ err = -EINVAL;
+ goto finished_mutex_unlock;
+ }
+ memcpy(&ep_config, curr_data, sizeof(ep_config));
+ dev_dbg(BT_DEV, "CG2900_OPCODE_CONFIGURE_ENDPOINT ep_id %d\n",
+ ep_config.endpoint_id);
+ err = cg2900_audio_config_endpoint(dev->session, &ep_config);
+ break;
+
+ case CG2900_OPCODE_START_STREAM:
+ if (bytes_left < (sizeof(ep_1) + sizeof(ep_2))) {
+ dev_err(BT_DEV, "Not enough data supplied for "
+ "CG2900_OPCODE_START_STREAM\n");
+ err = -EINVAL;
+ goto finished_mutex_unlock;
+ }
+ memcpy(&ep_1, curr_data, sizeof(ep_1));
+ curr_data += sizeof(ep_1);
+ memcpy(&ep_2, curr_data, sizeof(ep_2));
+ dev_dbg(BT_DEV, "CG2900_OPCODE_START_STREAM ep_1 %d ep_2 %d\n",
+ ep_1, ep_2);
+
+ err = cg2900_audio_start_stream(dev->session,
+ ep_1, ep_2, &stream_handle);
+ if (!err) {
+ int len;
+ struct sk_buff *skb;
+
+ /*
+ * Command succeeded. Store data so it can be returned
+ * when calling read.
+ */
+ len = sizeof(op_code) + sizeof(stream_handle);
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb) {
+ dev_err(BT_DEV, "CG2900_OPCODE_START_STREAM: "
+ "Could not allocate skb\n");
+ err = -ENOMEM;
+ goto finished_mutex_unlock;
+ }
+ memcpy(skb_put(skb, sizeof(op_code)), &op_code,
+ sizeof(op_code));
+ memcpy(skb_put(skb, sizeof(stream_handle)),
+ &stream_handle, sizeof(stream_handle));
+ skb_queue_tail(&dev->rx_queue, skb);
+
+ dev_dbg(BT_DEV, "stream_handle %d\n", stream_handle);
+ }
+ break;
+
+ case CG2900_OPCODE_STOP_STREAM:
+ if (bytes_left < sizeof(stream_handle)) {
+ dev_err(BT_DEV, "Not enough data supplied for "
+ "CG2900_OPCODE_STOP_STREAM\n");
+ err = -EINVAL;
+ goto finished_mutex_unlock;
+ }
+ memcpy(&stream_handle, curr_data, sizeof(stream_handle));
+ dev_dbg(BT_DEV, "CG2900_OPCODE_STOP_STREAM stream_handle %d\n",
+ stream_handle);
+ err = cg2900_audio_stop_stream(dev->session, stream_handle);
+ break;
+
+ default:
+ dev_err(BT_DEV, "Received bad op_code %d\n", op_code);
+ break;
+ };
+
+finished_mutex_unlock:
+ kfree(rec_data);
+ mutex_unlock(&dev->rw_mutex);
+
+ if (err)
+ return err;
+ else
+ return count;
+}
+
+/**
+ * audio_dev_poll() - Handle POLL call to the interface.
+ * @filp: Pointer to the file struct.
+ * @wait: Poll table supplied to caller.
+ *
+ * This function is used by the User Space application to see if the device is
+ * still open and if there is any data available for reading.
+ *
+ * Returns:
+ * Mask of current set POLL values.
+ */
+static unsigned int audio_dev_poll(struct file *filp, poll_table *wait)
+{
+ struct char_dev_info *dev = filp->private_data;
+ struct audio_info *info;
+ unsigned int mask = 0;
+
+ if (!dev) {
+ pr_err("audio_dev_poll: Transport closed");
+ return POLLERR | POLLRDHUP;
+ }
+ info = dev->info;
+
+ if (RESET == info->state)
+ mask |= POLLERR | POLLRDHUP | POLLPRI;
+ else
+ /* Unless RESET we can transmit */
+ mask |= POLLOUT;
+
+ if (!skb_queue_empty(&dev->rx_queue))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+static const struct file_operations char_dev_fops = {
+ .open = audio_dev_open,
+ .release = audio_dev_release,
+ .read = audio_dev_read,
+ .write = audio_dev_write,
+ .poll = audio_dev_poll
+};
+
+/**
+ * probe_common() - Register misc device.
+ * @info: Audio info structure.
+ * @dev: Current device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ENOMEM if allocation fails.
+ * Error codes from misc_register.
+ */
+static int probe_common(struct audio_info *info, struct device *dev)
+{
+ struct audio_cb_info *cb_info;
+ struct cg2900_user_data *pf_data;
+ int err;
+
+ cb_info = kzalloc(sizeof(*cb_info), GFP_KERNEL);
+ if (!cb_info) {
+ dev_err(dev, "Failed to allocate cb_info\n");
+ return -ENOMEM;
+ }
+ init_waitqueue_head(&cb_info->wq);
+ skb_queue_head_init(&cb_info->skb_queue);
+
+ pf_data = dev_get_platdata(dev);
+ cg2900_set_usr(pf_data, cb_info);
+ pf_data->dev = dev;
+ pf_data->read_cb = read_cb;
+ pf_data->reset_cb = reset_cb;
+
+ /* Only register misc device when both devices (BT and FM) are probed */
+ if (!info->dev_bt || !info->dev_fm)
+ return 0;
+
+ /* Prepare and register MISC device */
+ info->misc_dev.minor = MISC_DYNAMIC_MINOR;
+ info->misc_dev.name = NAME;
+ info->misc_dev.fops = &char_dev_fops;
+ info->misc_dev.parent = dev;
+ info->misc_dev.mode = S_IRUGO | S_IWUGO;
+
+ err = misc_register(&info->misc_dev);
+ if (err) {
+ dev_err(dev, "Error %d registering misc dev\n", err);
+ return err;
+ }
+ info->misc_registered = true;
+
+ dev_info(dev, "CG2900 Audio driver started\n");
+ return 0;
+}
+
+/**
+ * cg2900_audio_bt_probe() - Initialize CG2900 BT audio resources.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ENOMEM if allocation fails.
+ * -EEXIST if device has already been started.
+ * Error codes from probe_common.
+ */
+static int __devinit cg2900_audio_bt_probe(struct platform_device *pdev)
+{
+ int err;
+ struct audio_info *info;
+
+ dev_dbg(&pdev->dev, "cg2900_audio_bt_probe\n");
+
+ info = get_info(&pdev->dev);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ info->dev_bt = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, info);
+
+ err = probe_common(info, &pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "Could not probe audio BT (%d)\n", err);
+ dev_set_drvdata(&pdev->dev, NULL);
+ device_removed(info);
+ }
+
+ return err;
+}
+
+/**
+ * cg2900_audio_bt_probe() - Initialize CG2900 FM audio resources.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -ENOMEM if allocation fails.
+ * -EEXIST if device has already been started.
+ * Error codes from probe_common.
+ */
+static int __devinit cg2900_audio_fm_probe(struct platform_device *pdev)
+{
+ int err;
+ struct audio_info *info;
+
+ dev_dbg(&pdev->dev, "cg2900_audio_fm_probe\n");
+
+ info = get_info(&pdev->dev);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ info->dev_fm = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, info);
+
+ err = probe_common(info, &pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "Could not probe audio FM (%d)\n", err);
+ dev_set_drvdata(&pdev->dev, NULL);
+ device_removed(info);
+ }
+
+ return err;
+}
+
+/**
+ * common_remove() - Dergister misc device.
+ * @info: Audio info structure.
+ * @dev: Current device.
+ *
+ * Returns:
+ * 0 if success.
+ * Error codes from misc_deregister.
+ */
+static int common_remove(struct audio_info *info, struct device *dev)
+{
+ int err;
+ struct audio_cb_info *cb_info;
+ struct cg2900_user_data *pf_data;
+
+ pf_data = dev_get_platdata(dev);
+ cb_info = cg2900_get_usr(pf_data);
+ skb_queue_purge(&cb_info->skb_queue);
+ wake_up_all(&cb_info->wq);
+ kfree(cb_info);
+
+ if (!info->misc_registered)
+ return 0;
+
+ err = misc_deregister(&info->misc_dev);
+ if (err)
+ dev_err(dev, "Error %d deregistering misc dev\n", err);
+ info->misc_registered = false;
+
+ if (info->filp)
+ info->filp->private_data = NULL;
+
+ dev_info(dev, "CG2900 Audio driver removed\n");
+ return err;
+}
+
+/**
+ * cg2900_audio_bt_remove() - Release CG2900 audio resources.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if success.
+ * Error codes from common_remove.
+ */
+static int __devexit cg2900_audio_bt_remove(struct platform_device *pdev)
+{
+ int err;
+ struct audio_info *info;
+
+ dev_dbg(&pdev->dev, "cg2900_audio_bt_remove\n");
+
+ info = dev_get_drvdata(&pdev->dev);
+
+ info->dev_bt = NULL;
+
+ err = common_remove(info, &pdev->dev);
+ if (err)
+ dev_err(&pdev->dev,
+ "cg2900_audio_bt_remove:common_remove failed\n");
+
+ device_removed(info);
+
+ return 0;
+}
+
+/**
+ * cg2900_audio_fm_remove() - Release CG2900 audio resources.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if success.
+ * Error codes from common_remove.
+ */
+static int __devexit cg2900_audio_fm_remove(struct platform_device *pdev)
+{
+ int err;
+ struct audio_info *info;
+
+ dev_dbg(&pdev->dev, "cg2900_audio_fm_remove\n");
+
+ info = dev_get_drvdata(&pdev->dev);
+
+ info->dev_fm = NULL;
+
+ err = common_remove(info, &pdev->dev);
+ if (err)
+ dev_err(&pdev->dev,
+ "cg2900_audio_fm_remove:common_remove failed\n");
+
+ device_removed(info);
+
+ return 0;
+}
+
+static struct platform_driver cg2900_audio_bt_driver = {
+ .driver = {
+ .name = "cg2900-audiobt",
+ .owner = THIS_MODULE,
+ },
+ .probe = cg2900_audio_bt_probe,
+ .remove = __devexit_p(cg2900_audio_bt_remove),
+};
+
+static struct platform_driver cg2900_audio_fm_driver = {
+ .driver = {
+ .name = "cg2900-audiofm",
+ .owner = THIS_MODULE,
+ },
+ .probe = cg2900_audio_fm_probe,
+ .remove = __devexit_p(cg2900_audio_fm_remove),
+};
+
+/**
+ * cg2900_audio_init() - Initialize module.
+ *
+ * Registers platform driver.
+ */
+static int __init cg2900_audio_init(void)
+{
+ int err;
+
+ pr_debug("cg2900_audio_init");
+
+ err = platform_driver_register(&cg2900_audio_bt_driver);
+ if (err)
+ return err;
+ return platform_driver_register(&cg2900_audio_fm_driver);
+}
+
+/**
+ * cg2900_audio_exit() - Remove module.
+ *
+ * Unregisters platform driver.
+ */
+static void __exit cg2900_audio_exit(void)
+{
+ pr_debug("cg2900_audio_exit");
+ platform_driver_unregister(&cg2900_audio_fm_driver);
+ platform_driver_unregister(&cg2900_audio_bt_driver);
+}
+
+module_init(cg2900_audio_init);
+module_exit(cg2900_audio_exit);
+
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_AUTHOR("Kjell Andersson ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Linux Bluetooth Audio ST-Ericsson controller");
diff --git a/drivers/mfd/cg2900/cg2900_char_devices.c b/drivers/mfd/cg2900/cg2900_char_devices.c
new file mode 100644
index 00000000000..da9d74cc503
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_char_devices.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * Henrik Possung (henrik.possung@stericsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@stericsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson connectivity controller.
+ */
+#define NAME "cg2900_char_dev"
+#define pr_fmt(fmt) NAME ": " fmt "\n"
+
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/mfd/cg2900.h>
+
+#include "cg2900_core.h"
+
+#define MAIN_DEV (dev->dev)
+
+/**
+ * struct char_dev_user - Stores device information.
+ * @dev: Current device.
+ * @miscdev: Registered device struct.
+ * @filp: Current file pointer.
+ * @name: Name of device.
+ * @rx_queue: Data queue.
+ * @rx_wait_queue: Wait queue.
+ * @reset_wait_queue: Reset Wait queue.
+ * @read_mutex: Read mutex.
+ * @write_mutex: Write mutex.
+ * @list: List header for inserting into device list.
+ */
+struct char_dev_user {
+ struct device *dev;
+ struct miscdevice miscdev;
+ struct file *filp;
+ char *name;
+ struct sk_buff_head rx_queue;
+ wait_queue_head_t rx_wait_queue;
+ wait_queue_head_t reset_wait_queue;
+ struct mutex read_mutex;
+ struct mutex write_mutex;
+ struct list_head list;
+};
+
+/**
+ * struct char_info - Stores all current users.
+ * @open_mutex: Open mutex (used for both open and release).
+ * @man_mutex: Management mutex.
+ * @dev_users: List of char dev users.
+ */
+struct char_info {
+ struct mutex open_mutex;
+ struct mutex man_mutex;
+ struct list_head dev_users;
+};
+
+static struct char_info *char_info;
+
+/**
+ * char_dev_read_cb() - Handle data received from controller.
+ * @dev: Device receiving data.
+ * @skb: Buffer with data coming from controller.
+ *
+ * The char_dev_read_cb() function handles data received from the CG2900 driver.
+ */
+static void char_dev_read_cb(struct cg2900_user_data *dev, struct sk_buff *skb)
+{
+ struct char_dev_user *char_dev = dev_get_drvdata(dev->dev);
+
+ dev_dbg(dev->dev, "char_dev_read_cb len %d\n", skb->len);
+
+ skb_queue_tail(&char_dev->rx_queue, skb);
+
+ wake_up_interruptible(&char_dev->rx_wait_queue);
+}
+
+/**
+ * char_dev_reset_cb() - Handle reset from controller.
+ * @dev: Device resetting.
+ *
+ * The char_dev_reset_cb() function handles reset from the CG2900 driver.
+ */
+static void char_dev_reset_cb(struct cg2900_user_data *dev)
+{
+ struct char_dev_user *char_dev = dev_get_drvdata(dev->dev);
+
+ dev_dbg(dev->dev, "char_dev_reset_cb\n");
+
+ wake_up_interruptible(&char_dev->rx_wait_queue);
+ wake_up_interruptible(&char_dev->reset_wait_queue);
+}
+
+/**
+ * char_dev_open() - Open char device.
+ * @inode: Device driver information.
+ * @filp: Pointer to the file struct.
+ *
+ * The char_dev_open() function opens the char device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL if device cannot be found in device list.
+ * Error codes from cg2900->open.
+ */
+static int char_dev_open(struct inode *inode, struct file *filp)
+{
+ int err;
+ int minor;
+ struct char_dev_user *dev = NULL;
+ struct char_dev_user *tmp;
+ struct list_head *cursor;
+ struct cg2900_user_data *user;
+
+ mutex_lock(&char_info->open_mutex);
+
+ minor = iminor(inode);
+
+ /* Find the device for this file */
+ mutex_lock(&char_info->man_mutex);
+ list_for_each(cursor, &char_info->dev_users) {
+ tmp = list_entry(cursor, struct char_dev_user, list);
+ if (tmp->miscdev.minor == minor) {
+ dev = tmp;
+ break;
+ }
+ }
+ mutex_unlock(&char_info->man_mutex);
+ if (!dev) {
+ pr_err("Could not identify device in inode");
+ err = -EINVAL;
+ goto error_handling;
+ }
+
+ filp->private_data = dev;
+ dev->filp = filp;
+ user = dev_get_platdata(dev->dev);
+
+ /* First initiate wait queues for this device. */
+ init_waitqueue_head(&dev->rx_wait_queue);
+ init_waitqueue_head(&dev->reset_wait_queue);
+
+ /* Register to CG2900 Driver */
+ err = user->open(user);
+ if (err) {
+ dev_err(MAIN_DEV,
+ "Couldn't register to CG2900 for H:4 channel %s\n",
+ dev->name);
+ goto error_handling;
+ }
+ dev_info(MAIN_DEV, "char_dev %s opened\n", dev->name);
+
+error_handling:
+ mutex_unlock(&char_info->open_mutex);
+ return err;
+}
+
+/**
+ * char_dev_release() - Release char device.
+ * @inode: Device driver information.
+ * @filp: Pointer to the file struct.
+ *
+ * The char_dev_release() function release the char device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EBADF if NULL pointer was supplied in private data.
+ */
+static int char_dev_release(struct inode *inode, struct file *filp)
+{
+ int err = 0;
+ struct char_dev_user *dev = filp->private_data;
+ struct cg2900_user_data *user;
+
+ pr_debug("char_dev_release");
+
+ if (!dev) {
+ pr_err("char_dev_release: Calling with NULL pointer");
+ return -EBADF;
+ }
+
+ mutex_lock(&char_info->open_mutex);
+ mutex_lock(&dev->read_mutex);
+ mutex_lock(&dev->write_mutex);
+
+ user = dev_get_platdata(dev->dev);
+ if (user->opened)
+ user->close(user);
+
+ dev_info(MAIN_DEV, "char_dev %s closed\n", dev->name);
+
+ filp->private_data = NULL;
+ dev->filp = NULL;
+ wake_up_interruptible(&dev->rx_wait_queue);
+ wake_up_interruptible(&dev->reset_wait_queue);
+
+ mutex_unlock(&dev->write_mutex);
+ mutex_unlock(&dev->read_mutex);
+ mutex_unlock(&char_info->open_mutex);
+
+ return err;
+}
+
+/**
+ * char_dev_read() - Queue and copy buffer to user.
+ * @filp: Pointer to the file struct.
+ * @buf: Received buffer.
+ * @count: Size of buffer.
+ * @f_pos: Position in buffer.
+ *
+ * The char_dev_read() function queues and copy the received buffer to
+ * the user space char device. If no data is available this function will block.
+ *
+ * Returns:
+ * Bytes successfully read (could be 0).
+ * -EBADF if NULL pointer was supplied in private data.
+ * -EFAULT if copy_to_user fails.
+ * Error codes from wait_event_interruptible.
+ */
+static ssize_t char_dev_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *f_pos)
+{
+ struct char_dev_user *dev = filp->private_data;
+ struct cg2900_user_data *user;
+ struct sk_buff *skb;
+ int bytes_to_copy;
+ int err = 0;
+
+ pr_debug("char_dev_read");
+
+ if (!dev) {
+ pr_err("char_dev_read: Calling with NULL pointer");
+ return -EBADF;
+ }
+ mutex_lock(&dev->read_mutex);
+
+ user = dev_get_platdata(dev->dev);
+
+ if (user->opened && skb_queue_empty(&dev->rx_queue)) {
+ err = wait_event_interruptible(dev->rx_wait_queue,
+ (!(skb_queue_empty(&dev->rx_queue))) ||
+ !user->opened);
+ if (err) {
+ dev_err(MAIN_DEV, "Failed to wait for event\n");
+ goto error_handling;
+ }
+ }
+
+ if (!user->opened) {
+ dev_err(MAIN_DEV, "Channel has been closed\n");
+ err = -EBADF;
+ goto error_handling;
+ }
+
+ skb = skb_dequeue(&dev->rx_queue);
+ if (!skb) {
+ dev_dbg(MAIN_DEV,
+ "skb queue is empty - return with zero bytes\n");
+ bytes_to_copy = 0;
+ goto finished;
+ }
+
+ bytes_to_copy = min(count, skb->len);
+
+ err = copy_to_user(buf, skb->data, bytes_to_copy);
+ if (err) {
+ dev_err(MAIN_DEV, "Error %d from copy_to_user\n", err);
+ skb_queue_head(&dev->rx_queue, skb);
+ err = -EFAULT;
+ goto error_handling;
+ }
+
+ skb_pull(skb, bytes_to_copy);
+
+ if (skb->len > 0)
+ skb_queue_head(&dev->rx_queue, skb);
+ else
+ kfree_skb(skb);
+
+ goto finished;
+
+error_handling:
+ mutex_unlock(&dev->read_mutex);
+ return (ssize_t)err;
+finished:
+ mutex_unlock(&dev->read_mutex);
+ return bytes_to_copy;
+}
+
+/**
+ * char_dev_write() - Copy buffer from user and write to CG2900 driver.
+ * @filp: Pointer to the file struct.
+ * @buf: Write buffer.
+ * @count: Size of the buffer write.
+ * @f_pos: Position of buffer.
+ *
+ * Returns:
+ * Bytes successfully written (could be 0).
+ * -EBADF if NULL pointer was supplied in private data.
+ * -EFAULT if copy_from_user fails.
+ */
+static ssize_t char_dev_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct sk_buff *skb;
+ struct char_dev_user *dev = filp->private_data;
+ struct cg2900_user_data *user;
+ int err = 0;
+
+ pr_debug("char_dev_write");
+
+ if (!dev) {
+ pr_err("char_dev_write: Calling with NULL pointer");
+ return -EBADF;
+ }
+
+ user = dev_get_platdata(dev->dev);
+ if (!user->opened) {
+ dev_err(MAIN_DEV, "char_dev_write: Channel not opened\n");
+ return -EACCES;
+ }
+
+ mutex_lock(&dev->write_mutex);
+
+ skb = user->alloc_skb(count, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(MAIN_DEV, "Couldn't allocate sk_buff with length %d\n",
+ count);
+ goto error_handling;
+ }
+
+ err = copy_from_user(skb_put(skb, count), buf, count);
+ if (err) {
+ dev_err(MAIN_DEV, "Error %d from copy_from_user\n", err);
+ kfree_skb(skb);
+ err = -EFAULT;
+ goto error_handling;
+ }
+
+ err = user->write(user, skb);
+ if (err) {
+ dev_err(MAIN_DEV, "cg2900_write failed (%d)\n", err);
+ kfree_skb(skb);
+ goto error_handling;
+ }
+
+ mutex_unlock(&dev->write_mutex);
+ return count;
+
+error_handling:
+ mutex_unlock(&dev->write_mutex);
+ return err;
+}
+
+/**
+ * char_dev_unlocked_ioctl() - Handle IOCTL call to the interface.
+ * @filp: Pointer to the file struct.
+ * @cmd: IOCTL command.
+ * @arg: IOCTL argument.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL if supplied cmd is not supported.
+ * For cmd CG2900_CHAR_DEV_IOCTL_CHECK4RESET 0x01 is returned if device is
+ * reset and 0x02 is returned if device is closed.
+ */
+static long char_dev_unlocked_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct char_dev_user *dev = filp->private_data;
+ struct cg2900_user_data *user;
+ struct cg2900_rev_data rev_data;
+ int err = 0;
+ int ret_val;
+ void __user *user_arg = (void __user *)arg;
+
+ if (!dev) {
+ pr_err("char_dev_unlocked_ioctl: Calling with NULL pointer");
+ return -EBADF;
+ }
+
+ dev_dbg(dev->dev, "char_dev_unlocked_ioctl for %s\n"
+ "\tDIR: %d\n"
+ "\tTYPE: %d\n"
+ "\tNR: %d\n"
+ "\tSIZE: %d",
+ dev->name, _IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd),
+ _IOC_SIZE(cmd));
+
+ user = dev_get_platdata(dev->dev);
+
+ switch (cmd) {
+ case CG2900_CHAR_DEV_IOCTL_RESET:
+ if (!user->opened)
+ return -EACCES;
+ dev_dbg(MAIN_DEV, "ioctl reset command for device %s\n",
+ dev->name);
+ err = user->reset(user);
+ break;
+
+ case CG2900_CHAR_DEV_IOCTL_CHECK4RESET:
+ if (user->opened)
+ ret_val = CG2900_CHAR_DEV_IOCTL_EVENT_IDLE;
+ else
+ ret_val = CG2900_CHAR_DEV_IOCTL_EVENT_RESET;
+
+ dev_dbg(MAIN_DEV, "ioctl check for reset command for device %s",
+ dev->name);
+
+ err = copy_to_user(user_arg, &ret_val, sizeof(ret_val));
+ if (err) {
+ dev_err(MAIN_DEV,
+ "Error %d from copy_to_user for reset\n", err);
+ return -EFAULT;
+ }
+ break;
+
+ case CG2900_CHAR_DEV_IOCTL_GET_REVISION:
+ if (!user->get_local_revision(user, &rev_data)) {
+ dev_err(MAIN_DEV, "No revision data available\n");
+ return -EIO;
+ }
+ dev_dbg(MAIN_DEV, "ioctl check for local revision info\n"
+ "\trevision 0x%04X\n"
+ "\tsub_version 0x%04X\n",
+ rev_data.revision, rev_data.sub_version);
+ err = copy_to_user(user_arg, &rev_data, sizeof(rev_data));
+ if (err) {
+ dev_err(MAIN_DEV,
+ "Error %d from copy_to_user for "
+ "revision\n", err);
+ return -EFAULT;
+ }
+ break;
+
+ default:
+ dev_err(MAIN_DEV, "Unknown ioctl command %08X\n", cmd);
+ err = -EINVAL;
+ break;
+ };
+
+ return err;
+}
+
+/**
+ * char_dev_poll() - Handle POLL call to the interface.
+ * @filp: Pointer to the file struct.
+ * @wait: Poll table supplied to caller.
+ *
+ * Returns:
+ * Mask of current set POLL values
+ */
+static unsigned int char_dev_poll(struct file *filp, poll_table *wait)
+{
+ struct char_dev_user *dev = filp->private_data;
+ struct cg2900_user_data *user;
+ unsigned int mask = 0;
+
+ if (!dev) {
+ pr_debug("char_dev_poll: Device not open");
+ return POLLERR | POLLRDHUP;
+ }
+
+ user = dev_get_platdata(dev->dev);
+
+ poll_wait(filp, &dev->reset_wait_queue, wait);
+ poll_wait(filp, &dev->rx_wait_queue, wait);
+
+ if (!user->opened)
+ mask |= POLLERR | POLLRDHUP | POLLPRI;
+ else
+ mask |= POLLOUT; /* We can TX unless there is an error */
+
+ if (!(skb_queue_empty(&dev->rx_queue)))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+/*
+ * struct char_dev_fops - Char devices file operations.
+ * @read: Function that reads from the char device.
+ * @write: Function that writes to the char device.
+ * @unlocked_ioctl: Function that performs IO operations with
+ * the char device.
+ * @poll: Function that checks if there are possible operations
+ * with the char device.
+ * @open: Function that opens the char device.
+ * @release: Function that release the char device.
+ */
+static const struct file_operations char_dev_fops = {
+ .read = char_dev_read,
+ .write = char_dev_write,
+ .unlocked_ioctl = char_dev_unlocked_ioctl,
+ .poll = char_dev_poll,
+ .open = char_dev_open,
+ .release = char_dev_release
+};
+
+/**
+ * remove_dev() - Remove char device structure for device.
+ * @dev_usr: Char device user.
+ *
+ * The remove_dev() function releases the char_dev structure for this device.
+ */
+static void remove_dev(struct char_dev_user *dev_usr)
+{
+ if (!dev_usr)
+ return;
+
+ dev_dbg(dev_usr->dev,
+ "Removing char device %s with major %d and minor %d\n",
+ dev_usr->name,
+ MAJOR(dev_usr->miscdev.this_device->devt),
+ MINOR(dev_usr->miscdev.this_device->devt));
+
+ skb_queue_purge(&dev_usr->rx_queue);
+
+ mutex_destroy(&dev_usr->read_mutex);
+ mutex_destroy(&dev_usr->write_mutex);
+
+ dev_usr->dev = NULL;
+ if (dev_usr->filp)
+ dev_usr->filp->private_data = NULL;
+
+ /* Remove device node in file system. */
+ misc_deregister(&dev_usr->miscdev);
+ kfree(dev_usr);
+}
+
+/**
+ * cg2900_char_probe() - Initialize char device module.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if success.
+ * -ENOMEM if allocation fails.
+ * -EACCES if device already have been initiated.
+ */
+static int __devinit cg2900_char_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct char_dev_user *dev_usr;
+ struct cg2900_user_data *user;
+
+ dev_dbg(&pdev->dev, "cg2900_char_probe\n");
+
+ user = dev_get_platdata(&pdev->dev);
+ user->dev = &pdev->dev;
+ user->read_cb = char_dev_read_cb;
+ user->reset_cb = char_dev_reset_cb;
+
+ dev_usr = kzalloc(sizeof(*dev_usr), GFP_KERNEL);
+ if (!dev_usr) {
+ dev_err(&pdev->dev, "Couldn't allocate dev_usr\n");
+ return -ENOMEM;
+ }
+
+ dev_set_drvdata(&pdev->dev, dev_usr);
+ dev_usr->dev = &pdev->dev;
+
+ /* Store device name */
+ dev_usr->name = user->channel_data.char_dev_name;
+
+ /* Prepare miscdevice struct before registering the device */
+ dev_usr->miscdev.minor = MISC_DYNAMIC_MINOR;
+ dev_usr->miscdev.name = dev_usr->name;
+ dev_usr->miscdev.nodename = dev_usr->name;
+ dev_usr->miscdev.fops = &char_dev_fops;
+ dev_usr->miscdev.parent = &pdev->dev;
+ dev_usr->miscdev.mode = S_IRUGO | S_IWUGO;
+
+ err = misc_register(&dev_usr->miscdev);
+ if (err) {
+ dev_err(&pdev->dev, "Error %d registering misc dev\n", err);
+ goto err_free_usr;
+ }
+
+ dev_dbg(&pdev->dev, "Added char device %s with major %d and minor %d\n",
+ dev_usr->name, MAJOR(dev_usr->miscdev.this_device->devt),
+ MINOR(dev_usr->miscdev.this_device->devt));
+
+ mutex_init(&dev_usr->read_mutex);
+ mutex_init(&dev_usr->write_mutex);
+
+ skb_queue_head_init(&dev_usr->rx_queue);
+
+ mutex_lock(&char_info->man_mutex);
+ list_add_tail(&dev_usr->list, &char_info->dev_users);
+ mutex_unlock(&char_info->man_mutex);
+
+ return 0;
+
+err_free_usr:
+ kfree(dev_usr);
+ dev_set_drvdata(&pdev->dev, NULL);
+ return err;
+}
+
+/**
+ * cg2900_char_remove() - Release the char device module.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if success (always success).
+ */
+static int __devexit cg2900_char_remove(struct platform_device *pdev)
+{
+ struct list_head *cursor, *next;
+ struct char_dev_user *tmp;
+ struct char_dev_user *user;
+
+ dev_dbg(&pdev->dev, "cg2900_char_remove\n");
+
+ user = dev_get_drvdata(&pdev->dev);
+
+ mutex_lock(&char_info->man_mutex);
+ list_for_each_safe(cursor, next, &char_info->dev_users) {
+ tmp = list_entry(cursor, struct char_dev_user, list);
+ if (tmp == user) {
+ list_del(cursor);
+ remove_dev(tmp);
+ dev_set_drvdata(&pdev->dev, NULL);
+ break;
+ }
+ }
+ mutex_unlock(&char_info->man_mutex);
+ return 0;
+}
+
+static struct platform_driver cg2900_char_driver = {
+ .driver = {
+ .name = "cg2900-chardev",
+ .owner = THIS_MODULE,
+ },
+ .probe = cg2900_char_probe,
+ .remove = __devexit_p(cg2900_char_remove),
+};
+
+/**
+ * cg2900_char_init() - Initialize module.
+ *
+ * Registers platform driver.
+ */
+static int __init cg2900_char_init(void)
+{
+ pr_debug("cg2900_char_init");
+
+ /* Initialize private data. */
+ char_info = kzalloc(sizeof(*char_info), GFP_ATOMIC);
+ if (!char_info) {
+ pr_err("Could not alloc char_info struct");
+ return -ENOMEM;
+ }
+
+ mutex_init(&char_info->open_mutex);
+ mutex_init(&char_info->man_mutex);
+ INIT_LIST_HEAD(&char_info->dev_users);
+
+ return platform_driver_register(&cg2900_char_driver);
+}
+
+/**
+ * cg2900_char_exit() - Remove module.
+ *
+ * Unregisters platform driver.
+ */
+static void __exit cg2900_char_exit(void)
+{
+ struct list_head *cursor, *next;
+ struct char_dev_user *tmp;
+
+ pr_debug("cg2900_char_exit");
+
+ platform_driver_unregister(&cg2900_char_driver);
+
+ if (!char_info)
+ return;
+
+ list_for_each_safe(cursor, next, &char_info->dev_users) {
+ tmp = list_entry(cursor, struct char_dev_user, list);
+ list_del(cursor);
+ remove_dev(tmp);
+ }
+
+ mutex_destroy(&char_info->open_mutex);
+ mutex_destroy(&char_info->man_mutex);
+
+ kfree(char_info);
+ char_info = NULL;
+}
+
+module_init(cg2900_char_init);
+module_exit(cg2900_char_exit);
+
+MODULE_AUTHOR("Henrik Possung ST-Ericsson");
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ST-Ericsson CG2900 Char Devices Driver");
diff --git a/drivers/mfd/cg2900/cg2900_chip.c b/drivers/mfd/cg2900/cg2900_chip.c
new file mode 100644
index 00000000000..4a24c117428
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_chip.c
@@ -0,0 +1,3420 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * Henrik Possung (henrik.possung@stericsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@stericsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson CG2900 GPS/BT/FM controller.
+ */
+#define NAME "cg2900_chip"
+#define pr_fmt(fmt) NAME ": " fmt "\n"
+
+#include <asm/byteorder.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/cg2900.h>
+#include <linux/mfd/core.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+
+#include "cg2900_chip.h"
+#include "cg2900_core.h"
+#include "cg2900_lib.h"
+
+#define MAIN_DEV (main_info->dev)
+#define BOOT_DEV (info->user_in_charge->dev)
+
+#define WQ_NAME "cg2900_chip_wq"
+
+/*
+ * After waiting the first 500 ms we should just try to get the selftest results
+ * for another number of poll attempts
+ */
+#define MAX_NBR_OF_POLLS 50
+
+#define LINE_TOGGLE_DETECT_TIMEOUT 50 /* ms */
+#define CHIP_READY_TIMEOUT 100 /* ms */
+#define CHIP_STARTUP_TIMEOUT 15000 /* ms */
+#define CHIP_SHUTDOWN_TIMEOUT 15000 /* ms */
+#define POWER_SW_OFF_WAIT 500 /* ms */
+#define SELFTEST_INITIAL 500 /* ms */
+#define SELFTEST_POLLING 20 /* ms */
+
+/** CHANNEL_BT_CMD - Bluetooth HCI H:4 channel
+ * for Bluetooth commands in the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_BT_CMD 0x01
+
+/** CHANNEL_BT_ACL - Bluetooth HCI H:4 channel
+ * for Bluetooth ACL data in the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_BT_ACL 0x02
+
+/** CHANNEL_BT_EVT - Bluetooth HCI H:4 channel
+ * for Bluetooth events in the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_BT_EVT 0x04
+
+/** CHANNEL_FM_RADIO - Bluetooth HCI H:4 channel
+ * for FM radio in the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_FM_RADIO 0x08
+
+/** CHANNEL_GNSS - Bluetooth HCI H:4 channel
+ * for GNSS in the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_GNSS 0x09
+
+/** CHANNEL_DEBUG - Bluetooth HCI H:4 channel
+ * for internal debug data in the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_DEBUG 0x0B
+
+/** CHANNEL_STE_TOOLS - Bluetooth HCI H:4 channel
+ * for development tools data in the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_STE_TOOLS 0x0D
+
+/** CHANNEL_HCI_LOGGER - Bluetooth HCI H:4 channel
+ * for logging all transmitted H4 packets (on all channels).
+ */
+#define CHANNEL_HCI_LOGGER 0xFA
+
+/** CHANNEL_CORE - Bluetooth HCI H:4 channel
+ * for user space control of the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_CORE 0xFD
+
+/** CG2900_BT_CMD - Bluetooth HCI H4 channel for Bluetooth commands.
+ */
+#define CG2900_BT_CMD "cg2900_bt_cmd"
+
+/** CG2900_BT_ACL - Bluetooth HCI H4 channel for Bluetooth ACL data.
+ */
+#define CG2900_BT_ACL "cg2900_bt_acl"
+
+/** CG2900_BT_EVT - Bluetooth HCI H4 channel for Bluetooth events.
+ */
+#define CG2900_BT_EVT "cg2900_bt_evt"
+
+/** CG2900_FM_RADIO - Bluetooth HCI H4 channel for FM radio.
+ */
+#define CG2900_FM_RADIO "cg2900_fm_radio"
+
+/** CG2900_GNSS - Bluetooth HCI H4 channel for GNSS.
+ */
+#define CG2900_GNSS "cg2900_gnss"
+
+/** CG2900_DEBUG - Bluetooth HCI H4 channel for internal debug data.
+ */
+#define CG2900_DEBUG "cg2900_debug"
+
+/** CG2900_STE_TOOLS - Bluetooth HCI H4 channel for development tools data.
+ */
+#define CG2900_STE_TOOLS "cg2900_ste_tools"
+
+/** CG2900_HCI_LOGGER - BT channel for logging all transmitted H4 packets.
+ * Data read is copy of all data transferred on the other channels.
+ * Only write allowed is configuration of the HCI Logger.
+ */
+#define CG2900_HCI_LOGGER "cg2900_hci_logger"
+
+/** CG2900_BT_AUDIO - HCI Channel for BT audio configuration commands.
+ * Maps to Bluetooth command and event channels.
+ */
+#define CG2900_BT_AUDIO "cg2900_bt_audio"
+
+/** CG2900_FM_AUDIO - HCI channel for FM audio configuration commands.
+ * Maps to FM Radio channel.
+ */
+#define CG2900_FM_AUDIO "cg2900_fm_audio"
+
+/** CG2900_CORE- Channel for keeping ST-Ericsson CG2900 enabled.
+ * Opening this channel forces the chip to stay powered.
+ * No data can be written to or read from this channel.
+ */
+#define CG2900_CORE "cg2900_core"
+
+/**
+ * enum main_state - Main-state for CG2900 driver.
+ * @CG2900_INIT: CG2900 initializing.
+ * @CG2900_IDLE: No user registered to CG2900 driver.
+ * @CG2900_BOOTING: CG2900 booting after first user is registered.
+ * @CG2900_CLOSING: CG2900 closing after last user has deregistered.
+ * @CG2900_RESETING: CG2900 reset requested.
+ * @CG2900_ACTIVE: CG2900 up and running with at least one user.
+ */
+enum main_state {
+ CG2900_INIT,
+ CG2900_IDLE,
+ CG2900_BOOTING,
+ CG2900_CLOSING,
+ CG2900_RESETING,
+ CG2900_ACTIVE
+};
+
+/**
+ * enum boot_state - BOOT-state for CG2900 chip driver.
+ * @BOOT_NOT_STARTED: Boot has not yet started.
+ * @BOOT_SEND_BD_ADDRESS: VS Store In FS command with BD address
+ * has been sent.
+ * @BOOT_GET_FILES_TO_LOAD: CG2900 chip driver is retrieving file to
+ * load.
+ * @BOOT_DOWNLOAD_PATCH: CG2900 chip driver is downloading
+ * patches.
+ * @BOOT_ACTIVATE_PATCHES_AND_SETTINGS: CG2900 chip driver is activating patches
+ * and settings.
+ * @BOOT_READ_SELFTEST_RESULT: CG2900 is performing selftests that
+ * shall be read out.
+ * @BOOT_DISABLE_BT: Disable BT Core.
+ * @BOOT_READY: CG2900 chip driver boot is ready.
+ * @BOOT_FAILED: CG2900 chip driver boot failed.
+ */
+enum boot_state {
+ BOOT_NOT_STARTED,
+ BOOT_SEND_BD_ADDRESS,
+ BOOT_GET_FILES_TO_LOAD,
+ BOOT_DOWNLOAD_PATCH,
+ BOOT_ACTIVATE_PATCHES_AND_SETTINGS,
+ BOOT_READ_SELFTEST_RESULT,
+ BOOT_DISABLE_BT,
+ BOOT_READY,
+ BOOT_FAILED
+};
+
+/**
+ * enum closing_state - CLOSING-state for CG2900 chip driver.
+ * @CLOSING_RESET: HCI RESET_CMD has been sent.
+ * @CLOSING_POWER_SWITCH_OFF: HCI VS_POWER_SWITCH_OFF command has been sent.
+ * @CLOSING_SHUT_DOWN: We have now shut down the chip.
+ */
+enum closing_state {
+ CLOSING_RESET,
+ CLOSING_POWER_SWITCH_OFF,
+ CLOSING_SHUT_DOWN
+};
+
+/**
+ * enum file_load_state - BOOT_FILE_LOAD-state for CG2900 chip driver.
+ * @FILE_LOAD_GET_PATCH: Loading patches.
+ * @FILE_LOAD_GET_STATIC_SETTINGS: Loading static settings.
+ * @FILE_LOAD_NO_MORE_FILES: No more files to load.
+ * @FILE_LOAD_FAILED: File loading failed.
+ */
+enum file_load_state {
+ FILE_LOAD_GET_PATCH,
+ FILE_LOAD_GET_STATIC_SETTINGS,
+ FILE_LOAD_NO_MORE_FILES,
+ FILE_LOAD_FAILED
+};
+
+/**
+ * enum download_state - BOOT_DOWNLOAD state.
+ * @DOWNLOAD_PENDING: Download in progress.
+ * @DOWNLOAD_SUCCESS: Download successfully finished.
+ * @DOWNLOAD_FAILED: Downloading failed.
+ */
+enum download_state {
+ DOWNLOAD_PENDING,
+ DOWNLOAD_SUCCESS,
+ DOWNLOAD_FAILED
+};
+
+/**
+ * enum fm_radio_mode - FM Radio mode.
+ * It's needed because some FM do-commands generate interrupts only when
+ * the FM driver is in specific mode and we need to know if we should expect
+ * the interrupt.
+ * @FM_RADIO_MODE_IDLE: Radio mode is Idle (default).
+ * @FM_RADIO_MODE_FMT: Radio mode is set to FMT (transmitter).
+ * @FM_RADIO_MODE_FMR: Radio mode is set to FMR (receiver).
+ */
+enum fm_radio_mode {
+ FM_RADIO_MODE_IDLE = 0,
+ FM_RADIO_MODE_FMT = 1,
+ FM_RADIO_MODE_FMR = 2
+};
+
+
+/**
+ * struct cg2900_channel_item - List object for channel.
+ * @list: list_head struct.
+ * @user: User for this channel.
+ */
+struct cg2900_channel_item {
+ struct list_head list;
+ struct cg2900_user_data *user;
+};
+
+/**
+ * struct cg2900_delayed_work_struct - Work structure for CG2900 chip.
+ * @delayed_work: Work structure.
+ * @data: Pointer to private data.
+ */
+struct cg2900_delayed_work_struct {
+ struct delayed_work work;
+ void *data;
+};
+
+/**
+ * struct cg2900_skb_data - Structure for storing private data in an sk_buffer.
+ * @dev: CG2900 device for this sk_buffer.
+ */
+struct cg2900_skb_data {
+ struct cg2900_user_data *user;
+};
+#define cg2900_skb_data(__skb) ((struct cg2900_skb_data *)((__skb)->cb))
+
+/**
+ * struct cg2900_chip_info - Main info structure for CG2900 chip driver.
+ * @dev: Current device. Same as @chip_dev->dev.
+ * @patch_file_name: Stores patch file name.
+ * @settings_file_name: Stores settings file name.
+ * @file_info: Firmware file info (patch or settings).
+ * @boot_state: Current BOOT-state of CG2900 chip driver.
+ * @closing_state: Current CLOSING-state of CG2900 chip driver.
+ * @file_load_state: Current BOOT_FILE_LOAD-state of CG2900 chip
+ * driver.
+ * @download_state: Current BOOT_DOWNLOAD-state of CG2900 chip
+ * driver.
+ * @wq: CG2900 chip driver workqueue.
+ * @chip_dev: Chip handler info.
+ * @tx_bt_lock: Spinlock used to protect some global structures
+ * related to internal BT command flow control.
+ * @tx_fm_lock: Spinlock used to protect some global structures
+ * related to internal FM command flow control.
+ * @tx_fm_audio_awaiting_irpt: Indicates if an FM interrupt event related to
+ * audio driver command is expected.
+ * @fm_radio_mode: Current FM radio mode.
+ * @tx_nr_pkts_allowed_bt: Number of packets allowed to send on BT HCI CMD
+ * H4 channel.
+ * @audio_bt_cmd_op: Stores the OpCode of the last sent audio driver
+ * HCI BT CMD.
+ * @audio_fm_cmd_id: Stores the command id of the last sent
+ * HCI FM RADIO command by the fm audio user.
+ * @hci_fm_cmd_func: Stores the command function of the last sent
+ * HCI FM RADIO command by the fm radio user.
+ * @tx_queue_bt: TX queue for HCI BT commands when nr of commands
+ * allowed is 0 (CG2900 internal flow control).
+ * @tx_queue_fm: TX queue for HCI FM commands when nr of commands
+ * allowed is 0 (CG2900 internal flow control).
+ * @user_in_charge: User currently operating. Normally used at
+ * channel open and close.
+ * @last_user: Last user of this chip. To avoid complications
+ * this will never be set for bt_audio and
+ * fm_audio.
+ * @logger: Logger user of this chip.
+ * @selftest_work: Delayed work for reading selftest results.
+ * @nbr_of_polls: Number of times we should poll for selftest
+ * results.
+ */
+struct cg2900_chip_info {
+ struct device *dev;
+ char *patch_file_name;
+ char *settings_file_name;
+ struct cg2900_file_info file_info;
+ enum main_state main_state;
+ enum boot_state boot_state;
+ enum closing_state closing_state;
+ enum file_load_state file_load_state;
+ enum download_state download_state;
+ struct workqueue_struct *wq;
+ struct cg2900_chip_dev *chip_dev;
+ spinlock_t tx_bt_lock;
+ spinlock_t tx_fm_lock;
+ spinlock_t rw_lock;
+ bool tx_fm_audio_awaiting_irpt;
+ enum fm_radio_mode fm_radio_mode;
+ int tx_nr_pkts_allowed_bt;
+ u16 audio_bt_cmd_op;
+ u16 audio_fm_cmd_id;
+ u16 hci_fm_cmd_func;
+ struct sk_buff_head tx_queue_bt;
+ struct sk_buff_head tx_queue_fm;
+ struct list_head open_channels;
+ struct cg2900_user_data *user_in_charge;
+ struct cg2900_user_data *last_user;
+ struct cg2900_user_data *logger;
+ struct cg2900_user_data *bt_audio;
+ struct cg2900_user_data *fm_audio;
+ struct cg2900_delayed_work_struct selftest_work;
+ int nbr_of_polls;
+};
+
+/**
+ * struct main_info - Main info structure for CG2900 chip driver.
+ * @dev: Device structure.
+ * @cell_base_id: Base ID for MFD cells.
+ * @man_mutex: Management mutex.
+ */
+struct main_info {
+ struct device *dev;
+ int cell_base_id;
+ struct mutex man_mutex;
+};
+
+static struct main_info *main_info;
+
+/*
+ * main_wait_queue - Main Wait Queue in CG2900 driver.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(main_wait_queue);
+
+static void chip_startup_finished(struct cg2900_chip_info *info, int err);
+static void chip_shutdown(struct cg2900_user_data *user);
+
+/**
+ * bt_is_open() - Checks if any BT user is in open state.
+ * @info: CG2900 info.
+ *
+ * Returns:
+ * true if a BT channel is open.
+ * false if no BT channel is open.
+ */
+static bool bt_is_open(struct cg2900_chip_info *info)
+{
+ struct list_head *cursor;
+ struct cg2900_channel_item *tmp;
+
+ list_for_each(cursor, &info->open_channels) {
+ tmp = list_entry(cursor, struct cg2900_channel_item, list);
+ if (tmp->user->h4_channel == CHANNEL_BT_CMD)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * fm_is_open() - Checks if any FM user is in open state.
+ * @info: CG2900 info.
+ *
+ * Returns:
+ * true if a FM channel is open.
+ * false if no FM channel is open.
+ */
+static bool fm_is_open(struct cg2900_chip_info *info)
+{
+ struct list_head *cursor;
+ struct cg2900_channel_item *tmp;
+
+ list_for_each(cursor, &info->open_channels) {
+ tmp = list_entry(cursor, struct cg2900_channel_item, list);
+ if (tmp->user->h4_channel == CHANNEL_FM_RADIO)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * fm_irpt_expected() - check if this FM command will generate an interrupt.
+ * @cmd_id: command identifier.
+ *
+ * Returns:
+ * true if the command will generate an interrupt.
+ * false if it won't.
+ */
+static bool fm_irpt_expected(struct cg2900_chip_info *info, u16 cmd_id)
+{
+ bool retval = false;
+
+ switch (cmd_id) {
+ case CG2900_FM_DO_AIP_FADE_START:
+ if (info->fm_radio_mode == FM_RADIO_MODE_FMT)
+ retval = true;
+ break;
+
+ case CG2900_FM_DO_AUP_BT_FADE_START:
+ case CG2900_FM_DO_AUP_EXT_FADE_START:
+ case CG2900_FM_DO_AUP_FADE_START:
+ if (info->fm_radio_mode == FM_RADIO_MODE_FMR)
+ retval = true;
+ break;
+
+ case CG2900_FM_DO_FMR_SETANTENNA:
+ case CG2900_FM_DO_FMR_SP_AFSWITCH_START:
+ case CG2900_FM_DO_FMR_SP_AFUPDATE_START:
+ case CG2900_FM_DO_FMR_SP_BLOCKSCAN_START:
+ case CG2900_FM_DO_FMR_SP_PRESETPI_START:
+ case CG2900_FM_DO_FMR_SP_SCAN_START:
+ case CG2900_FM_DO_FMR_SP_SEARCH_START:
+ case CG2900_FM_DO_FMR_SP_SEARCHPI_START:
+ case CG2900_FM_DO_FMR_SP_TUNE_SETCHANNEL:
+ case CG2900_FM_DO_FMR_SP_TUNE_STEPCHANNEL:
+ case CG2900_FM_DO_FMT_PA_SETCTRL:
+ case CG2900_FM_DO_FMT_PA_SETMODE:
+ case CG2900_FM_DO_FMT_SP_TUNE_SETCHANNEL:
+ case CG2900_FM_DO_GEN_ANTENNACHECK_START:
+ case CG2900_FM_DO_GEN_GOTOMODE:
+ case CG2900_FM_DO_GEN_POWERSUPPLY_SETMODE:
+ case CG2900_FM_DO_GEN_SELECTREFERENCECLOCK:
+ case CG2900_FM_DO_GEN_SETPROCESSINGCLOCK:
+ case CG2900_FM_DO_GEN_SETREFERENCECLOCKPLL:
+ case CG2900_FM_DO_TST_TX_RAMP_START:
+ retval = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (retval)
+ dev_dbg(info->dev, "Following interrupt event expected for this"
+ " Cmd complete evt: cmd_id = 0x%X\n",
+ cmd_id);
+
+ return retval;
+}
+
+/**
+ * fm_is_do_cmd_irpt() - Check if irpt_val is one of the FM DO command related interrupts.
+ * @irpt_val: interrupt value.
+ *
+ * Returns:
+ * true if it's do-command related interrupt value.
+ * false if it's not.
+ */
+static bool fm_is_do_cmd_irpt(u16 irpt_val)
+{
+ if ((irpt_val & CG2900_FM_IRPT_OPERATION_SUCCEEDED) ||
+ (irpt_val & CG2900_FM_IRPT_OPERATION_FAILED)) {
+ dev_dbg(MAIN_DEV, "Irpt evt for FM do-command found, "
+ "irpt_val = 0x%X\n", irpt_val);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * fm_reset_flow_ctrl - Clears up internal FM flow control.
+ *
+ * Resets outstanding commands and clear FM TX list and set CG2900 FM mode to
+ * idle.
+ */
+static void fm_reset_flow_ctrl(struct cg2900_chip_info *info)
+{
+ dev_dbg(info->dev, "fm_reset_flow_ctrl\n");
+
+ skb_queue_purge(&info->tx_queue_fm);
+
+ /* Reset the fm_cmd_id. */
+ info->audio_fm_cmd_id = CG2900_FM_CMD_NONE;
+ info->hci_fm_cmd_func = CG2900_FM_CMD_PARAM_NONE;
+
+ info->fm_radio_mode = FM_RADIO_MODE_IDLE;
+}
+
+
+/**
+ * fm_parse_cmd - Parses a FM command packet.
+ * @data: FM command packet.
+ * @cmd_func: Out: FM legacy command function.
+ * @cmd_id: Out: FM legacy command ID.
+ */
+static void fm_parse_cmd(u8 *data, u8 *cmd_func, u16 *cmd_id)
+{
+ /* Move past H4-header to start of actual package */
+ struct fm_leg_cmd *pkt = (struct fm_leg_cmd *)(data + HCI_H4_SIZE);
+
+ *cmd_func = CG2900_FM_CMD_PARAM_NONE;
+ *cmd_id = CG2900_FM_CMD_NONE;
+
+ if (pkt->opcode != CG2900_FM_GEN_ID_LEGACY) {
+ dev_err(MAIN_DEV, "fm_parse_cmd: Not an FM legacy command "
+ "0x%02X\n", pkt->opcode);
+ return;
+ }
+
+ *cmd_func = pkt->fm_function;
+ if (*cmd_func == CG2900_FM_CMD_PARAM_WRITECOMMAND)
+ *cmd_id = cg2900_get_fm_cmd_id(le16_to_cpu(pkt->fm_cmd.head));
+}
+
+
+/**
+ * fm_parse_event - Parses a FM event packet
+ * @data: FM event packet.
+ * @event: Out: FM event.
+ * @cmd_func: Out: FM legacy command function.
+ * @cmd_id: Out: FM legacy command ID.
+ * @intr_val: Out: FM interrupt value.
+ */
+static void fm_parse_event(u8 *data, u8 *event, u8 *cmd_func, u16 *cmd_id,
+ u16 *intr_val)
+{
+ /* Move past H4-header to start of actual package */
+ union fm_leg_evt_or_irq *pkt = (union fm_leg_evt_or_irq *)data;
+
+ *cmd_func = CG2900_FM_CMD_PARAM_NONE;
+ *cmd_id = CG2900_FM_CMD_NONE;
+ *intr_val = 0;
+ *event = CG2900_FM_EVENT_UNKNOWN;
+
+ if (pkt->evt.opcode == CG2900_FM_GEN_ID_LEGACY &&
+ pkt->evt.read_write == CG2900_FM_CMD_LEG_PARAM_WRITE) {
+ /* Command complete */
+ *event = CG2900_FM_EVENT_CMD_COMPLETE;
+ *cmd_func = pkt->evt.fm_function;
+ if (*cmd_func == CG2900_FM_CMD_PARAM_WRITECOMMAND)
+ *cmd_id = cg2900_get_fm_cmd_id(
+ le16_to_cpu(pkt->evt.response_head));
+ } else if (pkt->irq_v2.opcode == CG2900_FM_GEN_ID_LEGACY &&
+ pkt->irq_v2.event_type == CG2900_FM_CMD_LEG_PARAM_IRQ) {
+ /* Interrupt, PG2 style */
+ *event = CG2900_FM_EVENT_INTERRUPT;
+ *intr_val = le16_to_cpu(pkt->irq_v2.irq);
+ } else if (pkt->irq_v1.opcode == CG2900_FM_GEN_ID_LEGACY) {
+ /* Interrupt, PG1 style */
+ *event = CG2900_FM_EVENT_INTERRUPT;
+ *intr_val = le16_to_cpu(pkt->irq_v1.irq);
+ } else
+ dev_err(MAIN_DEV, "fm_parse_event: Not an FM legacy command "
+ "0x%X %X %X %X\n", data[0], data[1], data[2], data[3]);
+}
+
+/**
+ * fm_update_mode - Updates the FM mode state machine.
+ * @data: FM command packet.
+ *
+ * Parses a FM command packet and updates the FM mode state machine.
+ */
+static void fm_update_mode(struct cg2900_chip_info *info, u8 *data)
+{
+ u8 cmd_func;
+ u16 cmd_id;
+
+ fm_parse_cmd(data, &cmd_func, &cmd_id);
+
+ if (cmd_func == CG2900_FM_CMD_PARAM_WRITECOMMAND &&
+ cmd_id == CG2900_FM_DO_GEN_GOTOMODE) {
+ /* Move past H4-header to start of actual package */
+ struct fm_leg_cmd *pkt =
+ (struct fm_leg_cmd *)(data + HCI_H4_SIZE);
+
+ info->fm_radio_mode = le16_to_cpu(pkt->fm_cmd.data[0]);
+ dev_dbg(info->dev, "FM Radio mode changed to %d\n",
+ info->fm_radio_mode);
+ }
+}
+
+
+/**
+ * transmit_skb_from_tx_queue_bt() - Check flow control info and transmit skb.
+ *
+ * The transmit_skb_from_tx_queue_bt() function checks if there are tickets
+ * available and commands waiting in the TX queue and if so transmits them
+ * to the controller.
+ * It shall always be called within spinlock_bh.
+ */
+static void transmit_skb_from_tx_queue_bt(struct cg2900_chip_dev *dev)
+{
+ struct cg2900_user_data *user;
+ struct cg2900_chip_info *info = dev->c_data;
+ struct sk_buff *skb;
+
+ dev_dbg(dev->dev, "transmit_skb_from_tx_queue_bt\n");
+
+ /* Dequeue an skb from the head of the list */
+ skb = skb_dequeue(&info->tx_queue_bt);
+ while (skb) {
+ if (info->tx_nr_pkts_allowed_bt <= 0) {
+ /*
+ * If no more packets allowed just return, we'll get
+ * back here after next Command Complete/Status event.
+ * Put skb back at head of queue.
+ */
+ skb_queue_head(&info->tx_queue_bt, skb);
+ return;
+ }
+
+ (info->tx_nr_pkts_allowed_bt)--;
+ dev_dbg(dev->dev, "tx_nr_pkts_allowed_bt = %d\n",
+ info->tx_nr_pkts_allowed_bt);
+
+ user = cg2900_skb_data(skb)->user; /* user is never NULL */
+
+ /*
+ * If it's a command from audio application, store the OpCode,
+ * it'll be used later to decide where to dispatch
+ * the Command Complete event.
+ */
+ if (info->bt_audio == user) {
+ struct hci_command_hdr *hdr = (struct hci_command_hdr *)
+ (skb->data + HCI_H4_SIZE);
+
+ info->audio_bt_cmd_op = le16_to_cpu(hdr->opcode);
+ dev_dbg(user->dev,
+ "Sending cmd from audio driver, saving "
+ "OpCode = 0x%04X\n", info->audio_bt_cmd_op);
+ }
+
+ cg2900_tx_to_chip(user, info->logger, skb);
+
+ /* Dequeue an skb from the head of the list */
+ skb = skb_dequeue(&info->tx_queue_bt);
+ }
+}
+
+/**
+ * transmit_skb_from_tx_queue_fm() - Check flow control info and transmit skb.
+ *
+ * The transmit_skb_from_tx_queue_fm() function checks if it possible to
+ * transmit and commands waiting in the TX queue and if so transmits them
+ * to the controller.
+ * It shall always be called within spinlock_bh.
+ */
+static void transmit_skb_from_tx_queue_fm(struct cg2900_chip_dev *dev)
+{
+ struct cg2900_user_data *user;
+ struct cg2900_chip_info *info = dev->c_data;
+ struct sk_buff *skb;
+
+ dev_dbg(dev->dev, "transmit_skb_from_tx_queue_fm\n");
+
+ /* Dequeue an skb from the head of the list */
+ skb = skb_dequeue(&info->tx_queue_fm);
+ while (skb) {
+ u16 cmd_id;
+ u8 cmd_func;
+
+ if (info->audio_fm_cmd_id != CG2900_FM_CMD_NONE ||
+ info->hci_fm_cmd_func != CG2900_FM_CMD_PARAM_NONE) {
+ /*
+ * There are currently outstanding FM commands.
+ * Wait for them to finish. We will get back here later.
+ * Queue back the skb at head of list.
+ */
+ skb_queue_head(&info->tx_queue_bt, skb);
+ return;
+ }
+
+ user = cg2900_skb_data(skb)->user; /* user is never NULL */
+
+ if (!user->opened) {
+ /*
+ * Channel is not open. That means that the user that
+ * originally sent it has deregistered.
+ * Just throw it away and check the next skb in the
+ * queue.
+ */
+ kfree_skb(skb);
+ /* Dequeue an skb from the head of the list */
+ skb = skb_dequeue(&info->tx_queue_fm);
+ continue;
+ }
+
+ fm_parse_cmd(&(skb->data[0]), &cmd_func, &cmd_id);
+
+ /*
+ * Store the FM command function , it'll be used later to decide
+ * where to dispatch the Command Complete event.
+ */
+ if (info->fm_audio == user) {
+ info->audio_fm_cmd_id = cmd_id;
+ dev_dbg(user->dev, "Sending FM audio cmd 0x%04X\n",
+ info->audio_fm_cmd_id);
+ } else {
+ /* FM radio command */
+ info->hci_fm_cmd_func = cmd_func;
+ fm_update_mode(info, &skb->data[0]);
+ dev_dbg(user->dev, "Sending FM radio cmd 0x%04X\n",
+ info->hci_fm_cmd_func);
+ }
+
+ /*
+ * We have only one ticket on FM. Just return after
+ * sending the skb.
+ */
+ cg2900_tx_to_chip(user, info->logger, skb);
+ return;
+ }
+}
+
+/**
+ * update_flow_ctrl_bt() - Update number of outstanding commands for BT CMD.
+ * @dev: Current chip device.
+ * @skb: skb with received packet.
+ *
+ * The update_flow_ctrl_bt() checks if incoming data packet is
+ * BT Command Complete/Command Status Event and if so updates number of tickets
+ * and number of outstanding commands. It also calls function to send queued
+ * commands (if the list of queued commands is not empty).
+ */
+static void update_flow_ctrl_bt(struct cg2900_chip_dev *dev,
+ const struct sk_buff * const skb)
+{
+ u8 *data = skb->data;
+ struct hci_event_hdr *event;
+ struct cg2900_chip_info *info = dev->c_data;
+
+ event = (struct hci_event_hdr *)data;
+ data += sizeof(*event);
+
+ if (HCI_EV_CMD_COMPLETE == event->evt) {
+ struct hci_ev_cmd_complete *complete;
+ complete = (struct hci_ev_cmd_complete *)data;
+
+ /*
+ * If it's HCI Command Complete Event then we might get some
+ * HCI tickets back. Also we can decrease the number outstanding
+ * HCI commands (if it's not NOP command or one of the commands
+ * that generate both Command Status Event and Command Complete
+ * Event).
+ * Check if we have any HCI commands waiting in the TX list and
+ * send them if there are tickets available.
+ */
+ spin_lock_bh(&info->tx_bt_lock);
+ info->tx_nr_pkts_allowed_bt = complete->ncmd;
+ dev_dbg(dev->dev, "New tx_nr_pkts_allowed_bt = %d\n",
+ info->tx_nr_pkts_allowed_bt);
+
+ if (!skb_queue_empty(&info->tx_queue_bt))
+ transmit_skb_from_tx_queue_bt(dev);
+ spin_unlock_bh(&info->tx_bt_lock);
+ } else if (HCI_EV_CMD_STATUS == event->evt) {
+ struct hci_ev_cmd_status *status;
+ status = (struct hci_ev_cmd_status *)data;
+
+ /*
+ * If it's HCI Command Status Event then we might get some
+ * HCI tickets back. Also we can decrease the number outstanding
+ * HCI commands (if it's not NOP command).
+ * Check if we have any HCI commands waiting in the TX queue and
+ * send them if there are tickets available.
+ */
+ spin_lock_bh(&info->tx_bt_lock);
+ info->tx_nr_pkts_allowed_bt = status->ncmd;
+ dev_dbg(dev->dev, "New tx_nr_pkts_allowed_bt = %d\n",
+ info->tx_nr_pkts_allowed_bt);
+
+ if (!skb_queue_empty(&info->tx_queue_bt))
+ transmit_skb_from_tx_queue_bt(dev);
+ spin_unlock_bh(&info->tx_bt_lock);
+ }
+}
+
+/**
+ * update_flow_ctrl_fm() - Update packets allowed for FM channel.
+ * @dev: Current chip device.
+ * @skb: skb with received packet.
+ *
+ * The update_flow_ctrl_fm() checks if incoming data packet is FM packet
+ * indicating that the previous command has been handled and if so update
+ * packets. It also calls function to send queued commands (if the list of
+ * queued commands is not empty).
+ */
+static void update_flow_ctrl_fm(struct cg2900_chip_dev *dev,
+ const struct sk_buff * const skb)
+{
+ u8 cmd_func = CG2900_FM_CMD_PARAM_NONE;
+ u16 cmd_id = CG2900_FM_CMD_NONE;
+ u16 irpt_val = 0;
+ u8 event = CG2900_FM_EVENT_UNKNOWN;
+ struct cg2900_chip_info *info = dev->c_data;
+
+ fm_parse_event(&(skb->data[0]), &event, &cmd_func, &cmd_id, &irpt_val);
+
+ if (event == CG2900_FM_EVENT_CMD_COMPLETE) {
+ /* FM legacy command complete event */
+ spin_lock_bh(&info->tx_fm_lock);
+ /*
+ * Check if it's not an write command complete event, because
+ * then it cannot be a DO command.
+ * If it's a write command complete event check that is not a
+ * DO command complete event before setting the outstanding
+ * FM packets to none.
+ */
+ if (cmd_func != CG2900_FM_CMD_PARAM_WRITECOMMAND ||
+ !fm_irpt_expected(info, cmd_id)) {
+ info->hci_fm_cmd_func = CG2900_FM_CMD_PARAM_NONE;
+ info->audio_fm_cmd_id = CG2900_FM_CMD_NONE;
+ dev_dbg(dev->dev,
+ "FM_Write: Outstanding FM commands:\n"
+ "\tRadio: 0x%04X\n"
+ "\tAudio: 0x%04X\n",
+ info->hci_fm_cmd_func,
+ info->audio_fm_cmd_id);
+ transmit_skb_from_tx_queue_fm(dev);
+
+ /*
+ * If there was a write do command complete event check if it is
+ * DO command previously sent by the FM audio user. If that's
+ * the case we need remember that in order to be able to
+ * dispatch the interrupt to the correct user.
+ */
+ } else if (cmd_id == info->audio_fm_cmd_id) {
+ info->tx_fm_audio_awaiting_irpt = true;
+ dev_dbg(dev->dev,
+ "FM Audio waiting for interrupt = true\n");
+ }
+ spin_unlock_bh(&info->tx_fm_lock);
+ } else if (event == CG2900_FM_EVENT_INTERRUPT) {
+ /* FM legacy interrupt */
+ if (fm_is_do_cmd_irpt(irpt_val)) {
+ /*
+ * If it is an interrupt related to a DO command update
+ * the outstanding flow control and transmit blocked
+ * FM commands.
+ */
+ spin_lock_bh(&info->tx_fm_lock);
+ info->hci_fm_cmd_func = CG2900_FM_CMD_PARAM_NONE;
+ info->audio_fm_cmd_id = CG2900_FM_CMD_NONE;
+ dev_dbg(dev->dev,
+ "FM_INT: Outstanding FM commands:\n"
+ "\tRadio: 0x%04X\n"
+ "\tAudio: 0x%04X\n",
+ info->hci_fm_cmd_func,
+ info->audio_fm_cmd_id);
+ info->tx_fm_audio_awaiting_irpt = false;
+ dev_dbg(dev->dev,
+ "FM Audio waiting for interrupt = false\n");
+ transmit_skb_from_tx_queue_fm(dev);
+ spin_unlock_bh(&info->tx_fm_lock);
+ }
+ }
+}
+
+/**
+ * send_bt_enable() - Send HCI VS BT Enable command to the chip.
+ * @info: Chip info structure.
+ * @bt_enable: Value for BT Enable parameter (e.g. CG2900_BT_DISABLE).
+ */
+static void send_bt_enable(struct cg2900_chip_info *info, u8 bt_enable)
+{
+ struct bt_vs_bt_enable_cmd cmd;
+
+ cmd.op_code = cpu_to_le16(CG2900_BT_OP_VS_BT_ENABLE);
+ cmd.plen = BT_PARAM_LEN(sizeof(cmd));
+ cmd.enable = bt_enable;
+ cg2900_send_bt_cmd(info->user_in_charge, info->logger,
+ &cmd, sizeof(cmd));
+}
+
+/**
+ * send_bd_address() - Send HCI VS command with BD address to the chip.
+ */
+static void send_bd_address(struct cg2900_chip_info *info)
+{
+ struct bt_vs_store_in_fs_cmd *cmd;
+ u8 plen = sizeof(*cmd) + BT_BDADDR_SIZE;
+
+ cmd = kmalloc(plen, GFP_KERNEL);
+ if (!cmd) {
+ dev_err(info->dev, "send_bd_address could not allocate cmd\n");
+ return;
+ }
+
+ cmd->opcode = cpu_to_le16(CG2900_BT_OP_VS_STORE_IN_FS);
+ cmd->plen = BT_PARAM_LEN(plen);
+ cmd->user_id = CG2900_VS_STORE_IN_FS_USR_ID_BD_ADDR;
+ cmd->len = BT_BDADDR_SIZE;
+ /* Now copy the BD address received from user space control app. */
+ memcpy(cmd->data, bd_address, BT_BDADDR_SIZE);
+
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_SEND_BD_ADDRESS\n");
+ info->boot_state = BOOT_SEND_BD_ADDRESS;
+
+ cg2900_send_bt_cmd(info->user_in_charge, info->logger, cmd, plen);
+
+ kfree(cmd);
+}
+
+/**
+ * send_settings_file() - Transmit settings file.
+ *
+ * The send_settings_file() function transmit settings file.
+ * The file is read in parts to fit in HCI packets. When finished,
+ * close the settings file and send HCI reset to activate settings and patches.
+ */
+static void send_settings_file(struct cg2900_chip_info *info)
+{
+ int bytes_sent;
+
+ bytes_sent = cg2900_read_and_send_file_part(info->user_in_charge,
+ info->logger,
+ &info->file_info);
+ if (bytes_sent > 0) {
+ /* Data sent. Wait for CmdComplete */
+ return;
+ } else if (bytes_sent < 0) {
+ dev_err(BOOT_DEV, "send_settings_file: Error %d occurred\n",
+ bytes_sent);
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ chip_startup_finished(info, bytes_sent);
+ return;
+ }
+
+ /* No data was sent. This file is finished */
+ info->download_state = DOWNLOAD_SUCCESS;
+
+ /* Settings file finished. Release used resources */
+ dev_dbg(BOOT_DEV, "Settings file finished, release used resources\n");
+ release_firmware(info->file_info.fw_file);
+ info->file_info.fw_file = NULL;
+
+ dev_dbg(BOOT_DEV, "New file_load_state: FILE_LOAD_NO_MORE_FILES\n");
+ info->file_load_state = FILE_LOAD_NO_MORE_FILES;
+
+ /* Create and send HCI VS Store In FS command with bd address. */
+ send_bd_address(info);
+}
+
+/**
+ * send_patch_file - Transmit patch file.
+ *
+ * The send_patch_file() function transmit patch file.
+ * The file is read in parts to fit in HCI packets. When the complete file is
+ * transmitted, the file is closed.
+ * When finished, continue with settings file.
+ */
+static void send_patch_file(struct cg2900_chip_dev *dev)
+{
+ int err;
+ int bytes_sent;
+ struct cg2900_chip_info *info = dev->c_data;
+ int file_name_size = strlen("CG2900_XXXX_XXXX_settings.fw");
+
+ bytes_sent = cg2900_read_and_send_file_part(info->user_in_charge,
+ info->logger,
+ &info->file_info);
+ if (bytes_sent > 0) {
+ /* Data sent. Wait for CmdComplete */
+ return;
+ } else if (bytes_sent < 0) {
+ dev_err(BOOT_DEV, "send_patch_file: Error %d occurred\n",
+ bytes_sent);
+ err = bytes_sent;
+ goto error_handling;
+ }
+
+ /* No data was sent. This file is finished */
+ info->download_state = DOWNLOAD_SUCCESS;
+
+ dev_dbg(BOOT_DEV, "Patch file finished, release used resources\n");
+ release_firmware(info->file_info.fw_file);
+ info->file_info.fw_file = NULL;
+
+ /*
+ * Create the settings file name from HCI revision and sub_version.
+ * file_name_size does not include terminating NULL character
+ * so add 1.
+ */
+ err = snprintf(info->settings_file_name, file_name_size + 1,
+ "CG2900_%04X_%04X_settings.fw", dev->chip.hci_revision,
+ dev->chip.hci_sub_version);
+ if (err == file_name_size) {
+ dev_dbg(BOOT_DEV, "Downloading settings file %s\n",
+ info->settings_file_name);
+ } else {
+ dev_err(BOOT_DEV, "Settings file name failed! err=%d\n", err);
+ goto error_handling;
+ }
+
+ /* Retrieve the settings file */
+ err = request_firmware(&info->file_info.fw_file,
+ info->settings_file_name,
+ info->dev);
+ if (err) {
+ dev_err(BOOT_DEV, "Couldn't get settings file (%d)\n", err);
+ goto error_handling;
+ }
+ /* Now send the settings file */
+ dev_dbg(BOOT_DEV,
+ "New file_load_state: FILE_LOAD_GET_STATIC_SETTINGS\n");
+ info->file_load_state = FILE_LOAD_GET_STATIC_SETTINGS;
+ dev_dbg(BOOT_DEV, "New download_state: DOWNLOAD_PENDING\n");
+ info->download_state = DOWNLOAD_PENDING;
+ send_settings_file(info);
+ return;
+
+error_handling:
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ chip_startup_finished(info, err);
+}
+
+/**
+ * work_power_off_chip() - Work item to power off the chip.
+ * @work: Reference to work data.
+ *
+ * The work_power_off_chip() function handles transmission of the HCI command
+ * vs_power_switch_off and then informs the CG2900 Core that this chip driver is
+ * finished and the Core driver can now shut off the chip.
+ */
+static void work_power_off_chip(struct work_struct *work)
+{
+ struct sk_buff *skb = NULL;
+ u8 *h4_header;
+ struct cg2900_platform_data *pf_data;
+ struct cg2900_work *my_work;
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+
+ if (!work) {
+ dev_err(MAIN_DEV, "work_power_off_chip: work == NULL\n");
+ return;
+ }
+
+ my_work = container_of(work, struct cg2900_work, work);
+ dev = my_work->user_data;
+ info = dev->c_data;
+
+ /*
+ * Get the VS Power Switch Off command to use based on connected
+ * connectivity controller
+ */
+ pf_data = dev_get_platdata(dev->dev);
+ if (pf_data->get_power_switch_off_cmd)
+ skb = pf_data->get_power_switch_off_cmd(dev, NULL);
+
+ /*
+ * Transmit the received command.
+ * If no command found for the device, just continue
+ */
+ if (!skb) {
+ dev_err(dev->dev,
+ "Could not retrieve PowerSwitchOff command\n");
+ goto shut_down_chip;
+ }
+
+ dev_dbg(dev->dev,
+ "Got power_switch_off command. Add H4 header and transmit\n");
+
+ /*
+ * Move the data pointer to the H:4 header position and store
+ * the H4 header
+ */
+ h4_header = skb_push(skb, CG2900_SKB_RESERVE);
+ *h4_header = CHANNEL_BT_CMD;
+
+ dev_dbg(dev->dev, "New closing_state: CLOSING_POWER_SWITCH_OFF\n");
+ info->closing_state = CLOSING_POWER_SWITCH_OFF;
+
+ if (info->user_in_charge)
+ cg2900_tx_to_chip(info->user_in_charge, info->logger, skb);
+ else
+ cg2900_tx_no_user(dev, skb);
+
+ /*
+ * Mandatory to wait 500ms after the power_switch_off command has been
+ * transmitted, in order to make sure that the controller is ready.
+ */
+ schedule_timeout_killable(msecs_to_jiffies(POWER_SW_OFF_WAIT));
+
+shut_down_chip:
+ dev_dbg(dev->dev, "New closing_state: CLOSING_SHUT_DOWN\n");
+ info->closing_state = CLOSING_SHUT_DOWN;
+
+ /* Close the transport, which will power off the chip */
+ if (dev->t_cb.close)
+ dev->t_cb.close(dev);
+
+ /* Chip shut-down finished, set correct state and wake up the chip. */
+ dev_dbg(dev->dev, "New main_state: CG2900_IDLE\n");
+ info->main_state = CG2900_IDLE;
+ wake_up_all(&main_wait_queue);
+
+ kfree(my_work);
+}
+
+/**
+ * work_chip_shutdown() - Shut down the chip.
+ * @work: Reference to work data.
+ */
+static void work_chip_shutdown(struct work_struct *work)
+{
+ struct cg2900_work *my_work;
+ struct cg2900_user_data *user;
+
+ if (!work) {
+ dev_err(MAIN_DEV, "work_chip_shutdown: work == NULL\n");
+ return;
+ }
+
+ my_work = container_of(work, struct cg2900_work, work);
+ user = my_work->user_data;
+
+ chip_shutdown(user);
+
+ kfree(my_work);
+}
+
+/**
+ * work_reset_after_error() - Handle reset.
+ * @work: Reference to work data.
+ *
+ * Handle a reset after received Command Complete event.
+ */
+static void work_reset_after_error(struct work_struct *work)
+{
+ struct cg2900_work *my_work;
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+
+ if (!work) {
+ dev_err(MAIN_DEV, "work_reset_after_error: work == NULL\n");
+ return;
+ }
+
+ my_work = container_of(work, struct cg2900_work, work);
+ dev = my_work->user_data;
+ info = dev->c_data;
+
+ chip_startup_finished(info, -EIO);
+
+ kfree(my_work);
+}
+
+/**
+ * work_load_patch_and_settings() - Start loading patches and settings.
+ * @work: Reference to work data.
+ */
+static void work_load_patch_and_settings(struct work_struct *work)
+{
+ int err = 0;
+ struct cg2900_work *my_work;
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+ int file_name_size = strlen("CG2900_XXXX_XXXX_patch.fw");
+
+ if (!work) {
+ dev_err(MAIN_DEV,
+ "work_load_patch_and_settings: work == NULL\n");
+ return;
+ }
+
+ my_work = container_of(work, struct cg2900_work, work);
+ dev = my_work->user_data;
+ info = dev->c_data;
+
+ /* Check that we are in the right state */
+ if (info->boot_state != BOOT_GET_FILES_TO_LOAD)
+ goto finished;
+
+ /*
+ * Create the patch file name from HCI revision and sub_version.
+ * file_name_size does not include terminating NULL character
+ * so add 1.
+ */
+ err = snprintf(info->patch_file_name, file_name_size + 1,
+ "CG2900_%04X_%04X_patch.fw", dev->chip.hci_revision,
+ dev->chip.hci_sub_version);
+ if (err == file_name_size) {
+ dev_dbg(BOOT_DEV, "Downloading patch file %s\n",
+ info->patch_file_name);
+ } else {
+ dev_err(BOOT_DEV, "Patch file name failed! err=%d\n", err);
+ goto error_handling;
+ }
+
+ /* We now all info needed */
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_DOWNLOAD_PATCH\n");
+ info->boot_state = BOOT_DOWNLOAD_PATCH;
+ dev_dbg(BOOT_DEV, "New download_state: DOWNLOAD_PENDING\n");
+ info->download_state = DOWNLOAD_PENDING;
+ dev_dbg(BOOT_DEV, "New file_load_state: FILE_LOAD_GET_PATCH\n");
+ info->file_load_state = FILE_LOAD_GET_PATCH;
+ info->file_info.chunk_id = 0;
+ info->file_info.file_offset = 0;
+ info->file_info.fw_file = NULL;
+
+ /* OK. Now it is time to download the patches */
+ err = request_firmware(&(info->file_info.fw_file),
+ info->patch_file_name,
+ dev->dev);
+ if (err < 0) {
+ dev_err(BOOT_DEV, "Couldn't get patch file (%d)\n", err);
+ goto error_handling;
+ }
+ send_patch_file(dev);
+
+ goto finished;
+
+error_handling:
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ chip_startup_finished(info, -EIO);
+finished:
+ kfree(my_work);
+}
+
+/**
+ * work_cont_file_download() - A file block has been written.
+ * @work: Reference to work data.
+ *
+ * Handle a received HCI VS Write File Block Complete event.
+ * Normally this means continue to send files to the controller.
+ */
+static void work_cont_file_download(struct work_struct *work)
+{
+ struct cg2900_work *my_work;
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+
+ if (!work) {
+ dev_err(MAIN_DEV, "work_cont_file_download: work == NULL\n");
+ return;
+ }
+
+ my_work = container_of(work, struct cg2900_work, work);
+ dev = my_work->user_data;
+ info = dev->c_data;
+
+ /* Continue to send patches or settings to the controller */
+ if (info->file_load_state == FILE_LOAD_GET_PATCH)
+ send_patch_file(dev);
+ else if (info->file_load_state == FILE_LOAD_GET_STATIC_SETTINGS)
+ send_settings_file(info);
+ else
+ dev_dbg(BOOT_DEV, "No more files to load\n");
+
+ kfree(my_work);
+}
+
+/**
+ * work_send_read_selftest_cmd() - HCI VS Read_SelfTests_Result command shall be sent.
+ * @work: Reference to work data.
+ */
+static void work_send_read_selftest_cmd(struct work_struct *work)
+{
+ struct delayed_work *del_work;
+ struct cg2900_delayed_work_struct *current_work;
+ struct cg2900_chip_info *info;
+ struct hci_command_hdr cmd;
+
+ if (!work) {
+ dev_err(MAIN_DEV,
+ "work_send_read_selftest_cmd: work == NULL\n");
+ return;
+ }
+
+ del_work = to_delayed_work(work);
+ current_work = container_of(del_work,
+ struct cg2900_delayed_work_struct, work);
+ info = current_work->data;
+
+ if (info->boot_state != BOOT_READ_SELFTEST_RESULT)
+ return;
+
+ cmd.opcode = cpu_to_le16(CG2900_BT_OP_VS_READ_SELTESTS_RESULT);
+ cmd.plen = 0; /* No parameters for Read Selftests Result */
+ cg2900_send_bt_cmd(info->user_in_charge, info->logger, &cmd,
+ sizeof(cmd));
+}
+
+/**
+ * handle_reset_cmd_complete() - Handles HCI Reset Command Complete event.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_reset_cmd_complete(struct cg2900_chip_dev *dev, u8 *data)
+{
+ u8 status = data[0];
+ struct cg2900_chip_info *info = dev->c_data;
+
+ dev_dbg(BOOT_DEV, "Received Reset complete event with status 0x%X\n",
+ status);
+
+ if (CG2900_CLOSING != info->main_state &&
+ CLOSING_RESET != info->closing_state)
+ return false;
+
+ if (HCI_BT_ERROR_NO_ERROR != status) {
+ /*
+ * Continue in case of error, the chip is going to be shut down
+ * anyway.
+ */
+ dev_err(BOOT_DEV, "Command complete for HciReset received with "
+ "error 0x%X\n", status);
+ }
+
+ cg2900_create_work_item(info->wq, work_power_off_chip, dev);
+
+ return true;
+}
+
+/**
+ * handle_vs_store_in_fs_cmd_complete() - Handles HCI VS StoreInFS Command Complete event.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_vs_store_in_fs_cmd_complete(struct cg2900_chip_dev *dev,
+ u8 *data)
+{
+ u8 status = data[0];
+ struct cg2900_chip_info *info = dev->c_data;
+
+ dev_dbg(BOOT_DEV,
+ "Received Store_in_FS complete event with status 0x%X\n",
+ status);
+
+ if (info->boot_state != BOOT_SEND_BD_ADDRESS)
+ return false;
+
+ if (HCI_BT_ERROR_NO_ERROR == status) {
+ struct hci_command_hdr cmd;
+
+ /* Send HCI SystemReset command to activate patches */
+ dev_dbg(BOOT_DEV,
+ "New boot_state: BOOT_ACTIVATE_PATCHES_AND_SETTINGS\n");
+ info->boot_state = BOOT_ACTIVATE_PATCHES_AND_SETTINGS;
+
+ cmd.opcode = cpu_to_le16(CG2900_BT_OP_VS_SYSTEM_RESET);
+ cmd.plen = 0; /* No parameters for System Reset */
+ cg2900_send_bt_cmd(info->user_in_charge, info->logger, &cmd,
+ sizeof(cmd));
+ } else {
+ dev_err(BOOT_DEV,
+ "Command complete for StoreInFS received with error "
+ "0x%X\n", status);
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ cg2900_create_work_item(info->wq, work_reset_after_error, dev);
+ }
+
+ return true;
+}
+
+/**
+ * handle_vs_write_file_block_cmd_complete() - Handles HCI VS WriteFileBlock Command Complete event.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_vs_write_file_block_cmd_complete(struct cg2900_chip_dev *dev,
+ u8 *data)
+{
+ u8 status = data[0];
+ struct cg2900_chip_info *info = dev->c_data;
+
+ if (info->boot_state != BOOT_DOWNLOAD_PATCH ||
+ info->download_state != DOWNLOAD_PENDING)
+ return false;
+
+ if (HCI_BT_ERROR_NO_ERROR == status)
+ cg2900_create_work_item(info->wq, work_cont_file_download, dev);
+ else {
+ dev_err(BOOT_DEV,
+ "Command complete for WriteFileBlock received with"
+ " error 0x%X\n", status);
+ dev_dbg(BOOT_DEV, "New download_state: DOWNLOAD_FAILED\n");
+ info->download_state = DOWNLOAD_FAILED;
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ if (info->file_info.fw_file) {
+ release_firmware(info->file_info.fw_file);
+ info->file_info.fw_file = NULL;
+ }
+ cg2900_create_work_item(info->wq, work_reset_after_error, dev);
+ }
+
+ return true;
+}
+
+/**
+ * handle_vs_write_file_block_cmd_status() - Handles HCI VS WriteFileBlock Command Status event.
+ * @status: Returned status of WriteFileBlock command.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_vs_write_file_block_cmd_status(struct cg2900_chip_dev *dev,
+ u8 status)
+{
+ struct cg2900_chip_info *info = dev->c_data;
+
+ if (info->boot_state != BOOT_DOWNLOAD_PATCH ||
+ info->download_state != DOWNLOAD_PENDING)
+ return false;
+
+ /*
+ * Only do something if there is an error. Otherwise we will wait for
+ * CmdComplete.
+ */
+ if (HCI_BT_ERROR_NO_ERROR != status) {
+ dev_err(BOOT_DEV,
+ "Command status for WriteFileBlock received with"
+ " error 0x%X\n", status);
+ dev_dbg(BOOT_DEV, "New download_state: DOWNLOAD_FAILED\n");
+ info->download_state = DOWNLOAD_FAILED;
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ if (info->file_info.fw_file) {
+ release_firmware(info->file_info.fw_file);
+ info->file_info.fw_file = NULL;
+ }
+ cg2900_create_work_item(info->wq, work_reset_after_error, dev);
+ }
+
+ return true;
+}
+
+/**
+ * handle_vs_power_switch_off_cmd_complete() - Handles HCI VS PowerSwitchOff Command Complete event.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_vs_power_switch_off_cmd_complete(struct cg2900_chip_dev *dev,
+ u8 *data)
+{
+ u8 status = data[0];
+ struct cg2900_chip_info *info = dev->c_data;
+
+ if (CLOSING_POWER_SWITCH_OFF != info->closing_state)
+ return false;
+
+ dev_dbg(BOOT_DEV,
+ "handle_vs_power_switch_off_cmd_complete status %d\n", status);
+
+ /*
+ * We were waiting for this but we don't need to do anything upon
+ * reception except warn for error status
+ */
+ if (HCI_BT_ERROR_NO_ERROR != status)
+ dev_err(BOOT_DEV,
+ "Command Complete for PowerSwitchOff received with "
+ "error 0x%X", status);
+
+ return true;
+}
+
+/**
+ * handle_vs_system_reset_cmd_complete() - Handle HCI VS SystemReset Command Complete event.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_vs_system_reset_cmd_complete(struct cg2900_chip_dev *dev,
+ u8 *data)
+{
+ u8 status = data[0];
+ struct cg2900_chip_info *info = dev->c_data;
+
+ if (info->boot_state != BOOT_ACTIVATE_PATCHES_AND_SETTINGS)
+ return false;
+
+ dev_dbg(BOOT_DEV, "handle_vs_system_reset_cmd_complete status %d\n",
+ status);
+
+ if (HCI_BT_ERROR_NO_ERROR == status) {
+ if (dev->chip.hci_revision == CG2900_PG2_REV) {
+ /*
+ * We must now wait for the selftest results. They will
+ * take a certain amount of time to finish so start a
+ * delayed work that will then send the command.
+ */
+ dev_dbg(BOOT_DEV,
+ "New boot_state: BOOT_READ_SELFTEST_RESULT\n");
+ info->boot_state = BOOT_READ_SELFTEST_RESULT;
+ queue_delayed_work(info->wq, &info->selftest_work.work,
+ msecs_to_jiffies(SELFTEST_INITIAL));
+ info->nbr_of_polls = 0;
+ } else {
+ /*
+ * We are now almost finished. Shut off BT Core. It will
+ * be re-enabled by the Bluetooth driver when needed.
+ */
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_DISABLE_BT\n");
+ info->boot_state = BOOT_DISABLE_BT;
+ send_bt_enable(info, CG2900_BT_DISABLE);
+ }
+ } else {
+ dev_err(BOOT_DEV,
+ "Received Reset complete event with status 0x%X\n",
+ status);
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ chip_startup_finished(info, -EIO);
+ }
+
+ return true;
+}
+
+/**
+ * handle_vs_read_selftests_cmd_complete() - Handle HCI VS ReadSelfTestsResult Command Complete event.
+ * @dev: Current chip.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_vs_read_selftests_cmd_complete(struct cg2900_chip_dev *dev,
+ u8 *data)
+{
+ struct bt_vs_read_selftests_result_evt *evt =
+ (struct bt_vs_read_selftests_result_evt *)data;
+ struct cg2900_chip_info *info = dev->c_data;
+
+ if (info->boot_state != BOOT_READ_SELFTEST_RESULT)
+ return false;
+
+ dev_dbg(BOOT_DEV,
+ "handle_vs_read_selftests_cmd_complete status %d result %d\n",
+ evt->status, evt->result);
+
+ if (HCI_BT_ERROR_NO_ERROR != evt->status)
+ goto err_handling;
+
+ if (CG2900_BT_SELFTEST_SUCCESSFUL == evt->result ||
+ CG2900_BT_SELFTEST_FAILED == evt->result) {
+ if (CG2900_BT_SELFTEST_FAILED == evt->result)
+ dev_err(BOOT_DEV, "CG2900 self test failed\n");
+
+ /*
+ * We are now almost finished. Shut off BT Core. It will
+ * be re-enabled by the Bluetooth driver when needed.
+ */
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_DISABLE_BT\n");
+ info->boot_state = BOOT_DISABLE_BT;
+ send_bt_enable(info, CG2900_BT_DISABLE);
+ return true;
+ } else if (CG2900_BT_SELFTEST_NOT_COMPLETED == evt->result) {
+ /*
+ * Self tests are not yet finished. Wait some more time
+ * before resending the command
+ */
+ if (info->nbr_of_polls > MAX_NBR_OF_POLLS) {
+ dev_err(BOOT_DEV, "Selftest results reached max"
+ " number of polls\n");
+ goto err_handling;
+ }
+ queue_delayed_work(info->wq, &info->selftest_work.work,
+ msecs_to_jiffies(SELFTEST_POLLING));
+ info->nbr_of_polls++;
+ return true;
+ }
+
+err_handling:
+ dev_err(BOOT_DEV,
+ "Received Read SelfTests Result complete event with "
+ "status 0x%X and result 0x%X\n",
+ evt->status, evt->result);
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ chip_startup_finished(info, -EIO);
+ return true;
+}
+
+/**
+ * handle_vs_bt_enable_cmd_status() - Handles HCI VS BtEnable Command Status event.
+ * @status: Returned status of BtEnable command.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_vs_bt_enable_cmd_status(struct cg2900_chip_dev *dev,
+ u8 status)
+{
+ struct cg2900_chip_info *info = dev->c_data;
+
+ if (info->boot_state != BOOT_DISABLE_BT)
+ return false;
+
+ dev_dbg(BOOT_DEV, "handle_vs_bt_enable_cmd_status status %d\n", status);
+
+ /*
+ * Only do something if there is an error. Otherwise we will wait for
+ * CmdComplete.
+ */
+ if (HCI_BT_ERROR_NO_ERROR != status) {
+ dev_err(BOOT_DEV,
+ "Received BtEnable status event with status 0x%X\n",
+ status);
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ chip_startup_finished(info, -EIO);
+ }
+
+ return true;
+}
+
+/**
+ * handle_vs_bt_enable_cmd_complete() - Handle HCI VS BtEnable Command Complete event.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_vs_bt_enable_cmd_complete(struct cg2900_chip_dev *dev,
+ u8 *data)
+{
+ u8 status = data[0];
+ struct cg2900_chip_info *info = dev->c_data;
+
+ if (info->boot_state != BOOT_DISABLE_BT)
+ return false;
+
+ dev_dbg(BOOT_DEV, "handle_vs_bt_enable_cmd_complete status %d\n",
+ status);
+
+ if (HCI_BT_ERROR_NO_ERROR == status) {
+ /*
+ * The boot sequence is now finished successfully.
+ * Set states and signal to waiting thread.
+ */
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_READY\n");
+ info->boot_state = BOOT_READY;
+ chip_startup_finished(info, 0);
+ } else {
+ dev_err(BOOT_DEV,
+ "Received BtEnable complete event with status 0x%X\n",
+ status);
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ chip_startup_finished(info, -EIO);
+ }
+
+ return true;
+}
+
+/**
+ * handle_rx_data_bt_evt() - Check if received data should be handled in CG2900 chip driver.
+ * @skb: Data packet
+ *
+ * The handle_rx_data_bt_evt() function checks if received data should be
+ * handled in CG2900 chip driver. If so handle it correctly.
+ * Received data is always HCI BT Event.
+ *
+ * Returns:
+ * True, if packet was handled internally,
+ * False, otherwise.
+ */
+static bool handle_rx_data_bt_evt(struct cg2900_chip_dev *dev,
+ struct sk_buff *skb)
+{
+ bool pkt_handled = false;
+ /* skb cannot be NULL here so it is safe to de-reference */
+ u8 *data = skb->data;
+ struct hci_event_hdr *evt;
+ u16 op_code;
+
+ evt = (struct hci_event_hdr *)data;
+ data += sizeof(*evt);
+
+ /* First check the event code. */
+ if (HCI_EV_CMD_COMPLETE == evt->evt) {
+ struct hci_ev_cmd_complete *cmd_complete;
+
+ cmd_complete = (struct hci_ev_cmd_complete *)data;
+ op_code = le16_to_cpu(cmd_complete->opcode);
+ dev_dbg(dev->dev,
+ "Received Command Complete: op_code = 0x%04X\n",
+ op_code);
+ /* Move to first byte after OCF */
+ data += sizeof(*cmd_complete);
+
+ if (op_code == HCI_OP_RESET)
+ pkt_handled = handle_reset_cmd_complete(dev, data);
+ else if (op_code == CG2900_BT_OP_VS_STORE_IN_FS)
+ pkt_handled = handle_vs_store_in_fs_cmd_complete(dev,
+ data);
+ else if (op_code == CG2900_BT_OP_VS_WRITE_FILE_BLOCK)
+ pkt_handled =
+ handle_vs_write_file_block_cmd_complete(dev,
+ data);
+ else if (op_code == CG2900_BT_OP_VS_POWER_SWITCH_OFF)
+ pkt_handled =
+ handle_vs_power_switch_off_cmd_complete(dev,
+ data);
+ else if (op_code == CG2900_BT_OP_VS_SYSTEM_RESET)
+ pkt_handled = handle_vs_system_reset_cmd_complete(dev,
+ data);
+ else if (op_code == CG2900_BT_OP_VS_BT_ENABLE)
+ pkt_handled = handle_vs_bt_enable_cmd_complete(dev,
+ data);
+ else if (op_code == CG2900_BT_OP_VS_READ_SELTESTS_RESULT)
+ pkt_handled = handle_vs_read_selftests_cmd_complete(dev,
+ data);
+ } else if (HCI_EV_CMD_STATUS == evt->evt) {
+ struct hci_ev_cmd_status *cmd_status;
+
+ cmd_status = (struct hci_ev_cmd_status *)data;
+
+ op_code = le16_to_cpu(cmd_status->opcode);
+
+ dev_dbg(dev->dev, "Received Command Status: op_code = 0x%04X\n",
+ op_code);
+
+ if (op_code == CG2900_BT_OP_VS_WRITE_FILE_BLOCK)
+ pkt_handled = handle_vs_write_file_block_cmd_status
+ (dev, cmd_status->status);
+ else if (op_code == CG2900_BT_OP_VS_BT_ENABLE)
+ pkt_handled = handle_vs_bt_enable_cmd_status
+ (dev, cmd_status->status);
+ } else if (HCI_EV_HW_ERROR == evt->evt) {
+ struct hci_ev_hw_error *hw_error;
+
+ hw_error = (struct hci_ev_hw_error *)data;
+ /*
+ * Only do a printout. There might be a receiving stack that can
+ * handle this event
+ */
+ dev_err(dev->dev, "HW Error event received with error 0x%02X\n",
+ hw_error->hw_code);
+ return false;
+ } else
+ return false;
+
+ if (pkt_handled)
+ kfree_skb(skb);
+
+ return pkt_handled;
+}
+
+/**
+ * transmit_skb_with_flow_ctrl_bt() - Send the BT skb to the controller if it is allowed or queue it.
+ * @user: Current user.
+ * @skb: Data packet.
+ *
+ * The transmit_skb_with_flow_ctrl_bt() function checks if there are
+ * tickets available and if so transmits buffer to controller. Otherwise the skb
+ * and user name is stored in a list for later sending.
+ * If enabled, copy the transmitted data to the HCI logger as well.
+ */
+static void transmit_skb_with_flow_ctrl_bt(struct cg2900_user_data *user,
+ struct sk_buff *skb)
+{
+ struct cg2900_chip_dev *dev = cg2900_get_prv(user);
+ struct cg2900_chip_info *info = dev->c_data;
+
+ /*
+ * Because there are more users of some H4 channels (currently audio
+ * application for BT command and FM channel) we need to have an
+ * internal HCI command flow control in CG2900 driver.
+ * So check here how many tickets we have and store skb in a queue if
+ * there are no tickets left. The skb will be sent later when we get
+ * more ticket(s).
+ */
+ spin_lock_bh(&info->tx_bt_lock);
+
+ if (info->tx_nr_pkts_allowed_bt > 0) {
+ info->tx_nr_pkts_allowed_bt--;
+ dev_dbg(user->dev, "New tx_nr_pkts_allowed_bt = %d\n",
+ info->tx_nr_pkts_allowed_bt);
+
+ /*
+ * If it's command from audio app store the OpCode,
+ * it'll be used later to decide where to dispatch Command
+ * Complete event.
+ */
+ if (info->bt_audio == user) {
+ struct hci_command_hdr *hdr = (struct hci_command_hdr *)
+ (skb->data + HCI_H4_SIZE);
+
+ info->audio_bt_cmd_op = le16_to_cpu(hdr->opcode);
+ dev_dbg(user->dev,
+ "Sending cmd from audio driver, saving "
+ "OpCode = 0x%X\n",
+ info->audio_bt_cmd_op);
+ }
+
+ cg2900_tx_to_chip(user, info->logger, skb);
+ } else {
+ dev_dbg(user->dev, "Not allowed to send cmd to controller, "
+ "storing in TX queue\n");
+
+ cg2900_skb_data(skb)->user = user;
+ skb_queue_tail(&info->tx_queue_bt, skb);
+ }
+ spin_unlock_bh(&info->tx_bt_lock);
+}
+
+/**
+ * transmit_skb_with_flow_ctrl_fm() - Send the FM skb to the controller if it is allowed or queue it.
+ * @user: Current user.
+ * @skb: Data packet.
+ *
+ * The transmit_skb_with_flow_ctrl_fm() function checks if chip is available and
+ * if so transmits buffer to controller. Otherwise the skb and user name is
+ * stored in a list for later sending.
+ * Also it updates the FM radio mode if it's FM GOTOMODE command, this is needed
+ * to know how to handle some FM DO commands complete events.
+ * If enabled, copy the transmitted data to the HCI logger as well.
+ */
+static void transmit_skb_with_flow_ctrl_fm(struct cg2900_user_data *user,
+ struct sk_buff *skb)
+{
+ u8 cmd_func = CG2900_FM_CMD_PARAM_NONE;
+ u16 cmd_id = CG2900_FM_CMD_NONE;
+ struct cg2900_chip_dev *dev = cg2900_get_prv(user);
+ struct cg2900_chip_info *info = dev->c_data;
+
+ fm_parse_cmd(&(skb->data[0]), &cmd_func, &cmd_id);
+
+ /*
+ * If this is an FM IP disable or reset send command and also reset
+ * the flow control and audio user.
+ */
+ if (cmd_func == CG2900_FM_CMD_PARAM_DISABLE ||
+ cmd_func == CG2900_FM_CMD_PARAM_RESET) {
+ spin_lock_bh(&info->tx_fm_lock);
+ fm_reset_flow_ctrl(info);
+ spin_unlock_bh(&info->tx_fm_lock);
+ cg2900_tx_to_chip(user, info->logger, skb);
+ return;
+ }
+
+ /*
+ * If this is a FM user and no FM audio user command pending just send
+ * FM command. It is up to the user of the FM channel to handle its own
+ * flow control.
+ */
+ spin_lock_bh(&info->tx_fm_lock);
+ if (info->fm_audio != user &&
+ info->audio_fm_cmd_id == CG2900_FM_CMD_NONE) {
+ info->hci_fm_cmd_func = cmd_func;
+ dev_dbg(user->dev, "Sending FM radio command 0x%04X\n",
+ info->hci_fm_cmd_func);
+ /* If a GotoMode command update FM mode */
+ fm_update_mode(info, &skb->data[0]);
+ cg2900_tx_to_chip(user, info->logger, skb);
+ } else if (info->fm_audio == user &&
+ info->hci_fm_cmd_func == CG2900_FM_CMD_PARAM_NONE &&
+ info->audio_fm_cmd_id == CG2900_FM_CMD_NONE) {
+ /*
+ * If it's command from fm audio user store the command id.
+ * It'll be used later to decide where to dispatch
+ * command complete event.
+ */
+ info->audio_fm_cmd_id = cmd_id;
+ dev_dbg(user->dev, "Sending FM audio command 0x%04X\n",
+ info->audio_fm_cmd_id);
+ cg2900_tx_to_chip(user, info->logger, skb);
+ } else {
+ dev_dbg(user->dev,
+ "Not allowed to send FM cmd to controller, storing in "
+ "TX queue\n");
+
+ cg2900_skb_data(skb)->user = user;
+ skb_queue_tail(&info->tx_queue_fm, skb);
+ }
+ spin_unlock_bh(&info->tx_fm_lock);
+}
+
+/**
+ * is_bt_audio_user() - Checks if this packet is for the BT audio user.
+ * @info: CG2900 info.
+ * @h4_channel: H:4 channel for this packet.
+ * @skb: Packet to check.
+ *
+ * Returns:
+ * true if packet is for BT audio user.
+ * false otherwise.
+ */
+static bool is_bt_audio_user(struct cg2900_chip_info *info, int h4_channel,
+ const struct sk_buff * const skb)
+{
+ struct hci_event_hdr *hdr;
+ u8 *payload;
+ u16 opcode;
+
+ if (h4_channel != CHANNEL_BT_EVT)
+ return false;
+
+ hdr = (struct hci_event_hdr *)skb->data;
+ payload = (u8 *)(hdr + 1); /* follows header */
+
+ if (HCI_EV_CMD_COMPLETE == hdr->evt)
+ opcode = le16_to_cpu(
+ ((struct hci_ev_cmd_complete *)payload)->opcode);
+ else if (HCI_EV_CMD_STATUS == hdr->evt)
+ opcode = le16_to_cpu(
+ ((struct hci_ev_cmd_status *)payload)->opcode);
+ else
+ return false;
+
+ if (opcode != info->audio_bt_cmd_op)
+ return false;
+
+ dev_dbg(info->bt_audio->dev, "Audio BT OpCode match = 0x%04X\n",
+ opcode);
+ info->audio_bt_cmd_op = CG2900_BT_OPCODE_NONE;
+ return true;
+}
+
+/**
+ * is_fm_audio_user() - Checks if this packet is for the FM audio user.
+ * @info: CG2900 info.
+ * @h4_channel: H:4 channel for this packet.
+ * @skb: Packet to check.
+ *
+ * Returns:
+ * true if packet is for BT audio user.
+ * false otherwise.
+ */
+static bool is_fm_audio_user(struct cg2900_chip_info *info, int h4_channel,
+ const struct sk_buff * const skb)
+{
+ u8 cmd_func;
+ u16 cmd_id;
+ u16 irpt_val;
+ u8 event;
+
+ if (h4_channel != CHANNEL_FM_RADIO)
+ return false;
+
+ cmd_func = CG2900_FM_CMD_PARAM_NONE;
+ cmd_id = CG2900_FM_CMD_NONE;
+ irpt_val = 0;
+ event = CG2900_FM_EVENT_UNKNOWN;
+
+ fm_parse_event(&skb->data[0], &event, &cmd_func, &cmd_id,
+ &irpt_val);
+ /* Check if command complete event FM legacy interface. */
+ if ((event == CG2900_FM_EVENT_CMD_COMPLETE) &&
+ (cmd_func == CG2900_FM_CMD_PARAM_WRITECOMMAND) &&
+ (cmd_id == info->audio_fm_cmd_id)) {
+ dev_dbg(info->fm_audio->dev,
+ "FM Audio Function Code match = 0x%04X\n",
+ cmd_id);
+ return true;
+ }
+
+ /* Check if Interrupt legacy interface. */
+ if ((event == CG2900_FM_EVENT_INTERRUPT) &&
+ (fm_is_do_cmd_irpt(irpt_val)) &&
+ (info->tx_fm_audio_awaiting_irpt))
+ return true;
+
+ return false;
+}
+
+/**
+ * data_from_chip() - Called when data is received from the chip.
+ * @dev: Chip info.
+ * @cg2900_dev: CG2900 user for this packet.
+ * @skb: Packet received.
+ *
+ * The data_from_chip() function updates flow control and checks
+ * if packet is a response for a packet it itself has transmitted. If not it
+ * finds the correct user and sends the packet* to the user.
+ */
+static void data_from_chip(struct cg2900_chip_dev *dev,
+ struct sk_buff *skb)
+{
+ int h4_channel;
+ struct list_head *cursor;
+ struct cg2900_channel_item *tmp;
+ struct cg2900_chip_info *info = dev->c_data;
+ struct cg2900_user_data *user = NULL;
+
+ spin_lock_bh(&info->rw_lock);
+ /* Copy RX Data into logger.*/
+ if (info->logger)
+ cg2900_send_to_hci_logger(info->logger, skb,
+ LOGGER_DIRECTION_RX);
+
+ h4_channel = skb->data[0];
+ skb_pull(skb, HCI_H4_SIZE);
+
+ /* First check if it is a BT or FM audio event */
+ if (is_bt_audio_user(info, h4_channel, skb))
+ user = info->bt_audio;
+ else if (is_fm_audio_user(info, h4_channel, skb))
+ user = info->fm_audio;
+ spin_unlock_bh(&info->rw_lock);
+
+ /* Now check if we should update flow control */
+ if (h4_channel == CHANNEL_BT_EVT)
+ update_flow_ctrl_bt(dev, skb);
+ else if (h4_channel == CHANNEL_FM_RADIO)
+ update_flow_ctrl_fm(dev, skb);
+
+ /* Then check if this is a response to data we have sent */
+ if (h4_channel == CHANNEL_BT_EVT && handle_rx_data_bt_evt(dev, skb))
+ return;
+
+ spin_lock_bh(&info->rw_lock);
+
+ if (user)
+ goto user_found;
+
+ /* Let's see if it is the last user */
+ if (info->last_user && info->last_user->h4_channel == h4_channel) {
+ user = info->last_user;
+ goto user_found;
+ }
+
+ /*
+ * Search through the list of all open channels to find the user.
+ * We skip the audio channels since they have already been checked
+ * earlier in this function.
+ */
+ list_for_each(cursor, &info->open_channels) {
+ tmp = list_entry(cursor, struct cg2900_channel_item, list);
+ if (tmp->user->h4_channel == h4_channel &&
+ !tmp->user->is_audio) {
+ user = tmp->user;
+ goto user_found;
+ }
+ }
+
+user_found:
+ if (user != info->bt_audio && user != info->fm_audio)
+ info->last_user = user;
+
+ spin_unlock_bh(&info->rw_lock);
+
+ if (user)
+ user->read_cb(user, skb);
+ else {
+ dev_err(dev->dev,
+ "Could not find corresponding user to h4_channel %d\n",
+ h4_channel);
+ kfree_skb(skb);
+ }
+}
+
+/**
+ * chip_removed() - Called when transport has been removed.
+ * @dev: Chip device.
+ *
+ * Removes registered MFD devices and frees internal resources.
+ */
+static void chip_removed(struct cg2900_chip_dev *dev)
+{
+ struct cg2900_chip_info *info = dev->c_data;
+
+ cancel_delayed_work(&info->selftest_work.work);
+ mfd_remove_devices(dev->dev);
+ kfree(info->settings_file_name);
+ kfree(info->patch_file_name);
+ destroy_workqueue(info->wq);
+ kfree(info);
+ dev->c_data = NULL;
+ dev->c_cb.chip_removed = NULL;
+ dev->c_cb.data_from_chip = NULL;
+}
+
+/**
+ * last_bt_user_removed() - Called when last BT user is removed.
+ * @info: Chip handler info.
+ *
+ * Clears out TX queue for BT.
+ */
+static void last_bt_user_removed(struct cg2900_chip_info *info)
+{
+ spin_lock_bh(&info->tx_bt_lock);
+ skb_queue_purge(&info->tx_queue_bt);
+
+ /*
+ * Reset number of packets allowed and number of outstanding
+ * BT commands.
+ */
+ info->tx_nr_pkts_allowed_bt = 1;
+ /* Reset the audio_bt_cmd_op. */
+ info->audio_bt_cmd_op = CG2900_BT_OPCODE_NONE;
+ spin_unlock_bh(&info->tx_bt_lock);
+}
+
+/**
+ * last_fm_user_removed() - Called when last FM user is removed.
+ * @info: Chip handler info.
+ *
+ * Clears out TX queue for BT.
+ */
+static void last_fm_user_removed(struct cg2900_chip_info *info)
+{
+ spin_lock_bh(&info->tx_fm_lock);
+ fm_reset_flow_ctrl(info);
+ spin_unlock_bh(&info->tx_fm_lock);
+}
+
+/**
+ * chip_shutdown() - Reset and power the chip off.
+ * @user: MFD device.
+ */
+static void chip_shutdown(struct cg2900_user_data *user)
+{
+ struct hci_command_hdr cmd;
+ struct cg2900_chip_dev *dev = cg2900_get_prv(user);
+ struct cg2900_chip_info *info = dev->c_data;
+
+ dev_dbg(user->dev, "chip_shutdown\n");
+
+ /* First do a quick power switch of the chip to assure a good state */
+ if (dev->t_cb.set_chip_power)
+ dev->t_cb.set_chip_power(dev, false);
+
+ /*
+ * Wait 50ms before continuing to be sure that the chip detects
+ * chip power off.
+ */
+ schedule_timeout_killable(
+ msecs_to_jiffies(LINE_TOGGLE_DETECT_TIMEOUT));
+
+ if (dev->t_cb.set_chip_power)
+ dev->t_cb.set_chip_power(dev, true);
+
+ /* Wait 100ms before continuing to be sure that the chip is ready */
+ schedule_timeout_killable(msecs_to_jiffies(CHIP_READY_TIMEOUT));
+
+ if (user != info->bt_audio && user != info->fm_audio)
+ info->last_user = user;
+ info->user_in_charge = user;
+
+ /*
+ * Transmit HCI reset command to ensure the chip is using
+ * the correct transport and to put BT part in reset.
+ */
+ dev_dbg(user->dev, "New closing_state: CLOSING_RESET\n");
+ info->closing_state = CLOSING_RESET;
+ cmd.opcode = cpu_to_le16(HCI_OP_RESET);
+ cmd.plen = 0; /* No parameters for HCI reset */
+ cg2900_send_bt_cmd(info->user_in_charge, info->logger, &cmd,
+ sizeof(cmd));
+}
+
+/**
+ * chip_startup_finished() - Called when chip startup has finished.
+ * @info: Chip handler info.
+ * @err: Result of chip startup, 0 for no error.
+ *
+ * Shuts down the chip upon error, sets state to active, wakes waiting threads,
+ * and informs transport that startup has finished.
+ */
+static void chip_startup_finished(struct cg2900_chip_info *info, int err)
+{
+ dev_dbg(BOOT_DEV, "chip_startup_finished (%d)\n", err);
+
+ if (err)
+ /* Shutdown the chip */
+ cg2900_create_work_item(info->wq, work_chip_shutdown,
+ info->user_in_charge);
+ else {
+ dev_dbg(BOOT_DEV, "New main_state: CG2900_ACTIVE\n");
+ info->main_state = CG2900_ACTIVE;
+ }
+
+ wake_up_all(&main_wait_queue);
+
+ if (err)
+ return;
+
+ if (!info->chip_dev->t_cb.chip_startup_finished)
+ dev_dbg(BOOT_DEV, "chip_startup_finished callback not found\n");
+ else
+ info->chip_dev->t_cb.chip_startup_finished(info->chip_dev);
+}
+
+/**
+ * cg2900_open() - Called when user wants to open an H4 channel.
+ * @user: MFD device to open.
+ *
+ * Checks that H4 channel is not already opened. If chip is not started, starts
+ * up the chip. Sets channel as opened and adds user to active users.
+ *
+ * Returns:
+ * 0 if success.
+ * -EINVAL if user is NULL or read_cb is NULL.
+ * -EBUSY if chip is in transit state (being started or shutdown).
+ * -EACCES if H4 channel is already opened.
+ * -ENOMEM if allocation fails.
+ * -EIO if chip startup fails.
+ * Error codes generated by t_cb.open.
+ */
+static int cg2900_open(struct cg2900_user_data *user)
+{
+ int err;
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+ struct list_head *cursor;
+ struct cg2900_channel_item *tmp;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV, "cg2900_open: Calling with NULL pointer\n");
+ return -EINVAL;
+ }
+
+ if (!user->read_cb) {
+ dev_err(user->dev, "cg2900_open: read_cb missing\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(user->dev, "cg2900_open\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ mutex_lock(&main_info->man_mutex);
+
+ /*
+ * Add a minor wait in order to avoid CPU blocking, looping openings.
+ * Note there will of course be no wait if we are already in the right
+ * state.
+ */
+ err = wait_event_timeout(main_wait_queue,
+ (CG2900_IDLE == info->main_state ||
+ CG2900_ACTIVE == info->main_state),
+ msecs_to_jiffies(LINE_TOGGLE_DETECT_TIMEOUT));
+ if (err <= 0) {
+ if (CG2900_INIT == info->main_state)
+ dev_err(user->dev, "Transport not opened\n");
+ else
+ dev_err(user->dev, "cg2900_open currently busy (0x%X). "
+ "Try again\n", info->main_state);
+ err = -EBUSY;
+ goto err_free_mutex;
+ }
+
+ err = 0;
+
+ list_for_each(cursor, &info->open_channels) {
+ tmp = list_entry(cursor, struct cg2900_channel_item, list);
+ if (tmp->user->h4_channel == user->h4_channel &&
+ tmp->user->is_audio == user->is_audio) {
+ dev_err(user->dev, "Channel %d is already opened\n",
+ user->h4_channel);
+ err = -EACCES;
+ goto err_free_mutex;
+ }
+ }
+
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp) {
+ dev_err(user->dev, "Could not allocate tmp\n");
+ err = -ENOMEM;
+ goto err_free_mutex;
+ }
+ tmp->user = user;
+
+ if (CG2900_ACTIVE != info->main_state &&
+ !user->chip_independent) {
+ /* Open transport and start-up the chip */
+ if (dev->t_cb.set_chip_power)
+ dev->t_cb.set_chip_power(dev, true);
+
+ /* Wait to be sure that the chip is ready */
+ schedule_timeout_killable(
+ msecs_to_jiffies(CHIP_READY_TIMEOUT));
+
+ if (dev->t_cb.open) {
+ err = dev->t_cb.open(dev);
+ if (err) {
+ if (dev->t_cb.set_chip_power)
+ dev->t_cb.set_chip_power(dev, false);
+ goto err_free_list_item;
+ }
+ }
+
+ /* Start the boot sequence */
+ info->user_in_charge = user;
+ if (user != info->bt_audio && user != info->fm_audio)
+ info->last_user = user;
+ dev_dbg(user->dev, "New boot_state: BOOT_GET_FILES_TO_LOAD\n");
+ info->boot_state = BOOT_GET_FILES_TO_LOAD;
+ dev_dbg(user->dev, "New main_state: CG2900_BOOTING\n");
+ info->main_state = CG2900_BOOTING;
+ cg2900_create_work_item(info->wq, work_load_patch_and_settings,
+ dev);
+
+ dev_dbg(user->dev, "Wait up to 15 seconds for chip to start\n");
+ wait_event_timeout(main_wait_queue,
+ (CG2900_ACTIVE == info->main_state ||
+ CG2900_IDLE == info->main_state),
+ msecs_to_jiffies(CHIP_STARTUP_TIMEOUT));
+ if (CG2900_ACTIVE != info->main_state) {
+ dev_err(user->dev, "CG2900 driver failed to start\n");
+
+ if (dev->t_cb.close)
+ dev->t_cb.close(dev);
+
+ dev_dbg(user->dev, "New main_state: CG2900_IDLE\n");
+ info->main_state = CG2900_IDLE;
+ err = -EIO;
+ goto err_free_list_item;
+ }
+ }
+
+ list_add_tail(&tmp->list, &info->open_channels);
+
+ user->opened = true;
+
+ dev_dbg(user->dev, "H:4 channel opened\n");
+
+ mutex_unlock(&main_info->man_mutex);
+ return 0;
+err_free_list_item:
+ kfree(tmp);
+err_free_mutex:
+ mutex_unlock(&main_info->man_mutex);
+ return err;
+}
+
+/**
+ * cg2900_hci_log_open() - Called when user wants to open HCI logger channel.
+ * @user: MFD device to open.
+ *
+ * Registers user as hci_logger and calls @cg2900_open to open the channel.
+ *
+ * Returns:
+ * 0 if success.
+ * -EINVAL if user is NULL.
+ * -EACCES if H4 channel is already opened.
+ * Error codes generated by cg2900_open.
+ */
+static int cg2900_hci_log_open(struct cg2900_user_data *user)
+{
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+ int err;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV,
+ "cg2900_hci_log_open: Calling with NULL pointer\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(user->dev, "cg2900_hci_log_open\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ if (info->logger) {
+ dev_err(user->dev, "HCI Logger already stored\n");
+ return -EACCES;
+ }
+
+ info->logger = user;
+ err = cg2900_open(user);
+ if (err)
+ info->logger = NULL;
+ return err;
+}
+
+/**
+ * cg2900_bt_audio_open() - Called when user wants to open BT audio channel.
+ * @user: MFD device to open.
+ *
+ * Registers user as bt_audio and calls @cg2900_open to open the channel.
+ *
+ * Returns:
+ * 0 if success.
+ * -EINVAL if user is NULL.
+ * -EACCES if H4 channel is already opened.
+ * Error codes generated by cg2900_open.
+ */
+static int cg2900_bt_audio_open(struct cg2900_user_data *user)
+{
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+ int err;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV,
+ "cg2900_bt_audio_open: Calling with NULL pointer\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(user->dev, "cg2900_bt_audio_open\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ if (info->bt_audio) {
+ dev_err(user->dev, "BT Audio already stored\n");
+ return -EACCES;
+ }
+
+ info->bt_audio = user;
+ err = cg2900_open(user);
+ if (err)
+ info->bt_audio = NULL;
+ return err;
+}
+
+/**
+ * cg2900_fm_audio_open() - Called when user wants to open FM audio channel.
+ * @user: MFD device to open.
+ *
+ * Registers user as fm_audio and calls @cg2900_open to open the channel.
+ *
+ * Returns:
+ * 0 if success.
+ * -EINVAL if user is NULL.
+ * -EACCES if H4 channel is already opened.
+ * Error codes generated by cg2900_open.
+ */
+static int cg2900_fm_audio_open(struct cg2900_user_data *user)
+{
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+ int err;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV,
+ "cg2900_fm_audio_open: Calling with NULL pointer\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(user->dev, "cg2900_fm_audio_open\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ if (info->fm_audio) {
+ dev_err(user->dev, "FM Audio already stored\n");
+ return -EACCES;
+ }
+
+ info->fm_audio = user;
+ err = cg2900_open(user);
+ if (err)
+ info->fm_audio = NULL;
+ return err;
+}
+
+/**
+ * cg2900_close() - Called when user wants to close an H4 channel.
+ * @user: MFD device to close.
+ *
+ * Clears up internal resources, sets channel as closed, and shuts down chip if
+ * this was the last user.
+ */
+static void cg2900_close(struct cg2900_user_data *user)
+{
+ bool keep_powered = false;
+ struct list_head *cursor, *next;
+ struct cg2900_channel_item *tmp;
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV, "cg2900_close: Calling with NULL pointer\n");
+ return;
+ }
+
+ dev_dbg(user->dev, "cg2900_close\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ mutex_lock(&main_info->man_mutex);
+
+ /*
+ * Go through each open channel. Remove our channel and check if there
+ * is any other channel that want to keep the chip running
+ */
+ list_for_each_safe(cursor, next, &info->open_channels) {
+ tmp = list_entry(cursor, struct cg2900_channel_item, list);
+ if (tmp->user == user) {
+ list_del(cursor);
+ kfree(tmp);
+ } else if (!tmp->user->chip_independent)
+ keep_powered = true;
+ }
+
+ if (user->h4_channel == CHANNEL_BT_CMD && !bt_is_open(info))
+ last_bt_user_removed(info);
+ else if (user->h4_channel == CHANNEL_FM_RADIO && !fm_is_open(info))
+ last_fm_user_removed(info);
+
+ if (keep_powered)
+ /* This was not the last user, we're done. */
+ goto finished;
+
+ if (CG2900_IDLE == info->main_state)
+ /* Chip has already been shut down. */
+ goto finished;
+
+ dev_dbg(user->dev, "New main_state: CG2900_CLOSING\n");
+ info->main_state = CG2900_CLOSING;
+ chip_shutdown(user);
+
+ dev_dbg(user->dev, "Wait up to 15 seconds for chip to shut-down\n");
+ wait_event_timeout(main_wait_queue,
+ (CG2900_IDLE == info->main_state),
+ msecs_to_jiffies(CHIP_SHUTDOWN_TIMEOUT));
+
+ /* Force shutdown if we timed out */
+ if (CG2900_IDLE != info->main_state) {
+ dev_err(user->dev,
+ "ST-Ericsson CG2900 Core Driver was shut-down with "
+ "problems\n");
+
+ if (dev->t_cb.close)
+ dev->t_cb.close(dev);
+
+ dev_dbg(user->dev, "New main_state: CG2900_IDLE\n");
+ info->main_state = CG2900_IDLE;
+ }
+
+finished:
+ mutex_unlock(&main_info->man_mutex);
+ user->opened = false;
+ dev_dbg(user->dev, "H:4 channel closed\n");
+}
+
+/**
+ * cg2900_hci_log_close() - Called when user wants to close HCI logger channel.
+ * @user: MFD device to close.
+ *
+ * Clears hci_logger user and calls @cg2900_close to close the channel.
+ */
+static void cg2900_hci_log_close(struct cg2900_user_data *user)
+{
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV,
+ "cg2900_hci_log_close: Calling with NULL pointer\n");
+ return;
+ }
+
+ dev_dbg(user->dev, "cg2900_hci_log_close\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ if (user != info->logger) {
+ dev_err(user->dev, "cg2900_hci_log_close: Trying to remove "
+ "another user\n");
+ return;
+ }
+
+ info->logger = NULL;
+ cg2900_close(user);
+}
+
+/**
+ * cg2900_bt_audio_close() - Called when user wants to close BT audio channel.
+ * @user: MFD device to close.
+ *
+ * Clears bt_audio user and calls @cg2900_close to close the channel.
+ */
+static void cg2900_bt_audio_close(struct cg2900_user_data *user)
+{
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV,
+ "cg2900_bt_audio_close: Calling with NULL pointer\n");
+ return;
+ }
+
+ dev_dbg(user->dev, "cg2900_bt_audio_close\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ if (user != info->bt_audio) {
+ dev_err(user->dev, "cg2900_bt_audio_close: Trying to remove "
+ "another user\n");
+ return;
+ }
+
+ info->bt_audio = NULL;
+ cg2900_close(user);
+}
+
+/**
+ * cg2900_fm_audio_close() - Called when user wants to close FM audio channel.
+ * @user: MFD device to close.
+ *
+ * Clears fm_audio user and calls @cg2900_close to close the channel.
+ */
+static void cg2900_fm_audio_close(struct cg2900_user_data *user)
+{
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV,
+ "cg2900_fm_audio_close: Calling with NULL pointer\n");
+ return;
+ }
+
+ dev_dbg(user->dev, "cg2900_fm_audio_close\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ if (user != info->fm_audio) {
+ dev_err(user->dev, "cg2900_fm_audio_close: Trying to remove "
+ "another user\n");
+ return;
+ }
+
+ info->fm_audio = NULL;
+ cg2900_close(user);
+}
+
+/**
+ * cg2900_reset() - Called when user wants to reset the chip.
+ * @user: MFD device to reset.
+ *
+ * Closes down the chip and calls reset_cb for all open users.
+ *
+ * Returns:
+ * 0 if success.
+ * -EINVAL if user is NULL.
+ */
+static int cg2900_reset(struct cg2900_user_data *user)
+{
+ struct list_head *cursor, *next;
+ struct cg2900_channel_item *tmp;
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+
+ if (!user) {
+ dev_err(MAIN_DEV, "cg2900_reset: Calling with NULL pointer\n");
+ return -EINVAL;
+ }
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ dev_info(user->dev, "cg2900_reset\n");
+
+ BUG_ON(!main_info);
+
+ mutex_lock(&main_info->man_mutex);
+
+ dev_dbg(user->dev, "New main_state: CG2900_RESETING\n");
+ info->main_state = CG2900_RESETING;
+
+ chip_shutdown(user);
+
+ /*
+ * Inform all opened channels about the reset and free the user devices
+ */
+ list_for_each_safe(cursor, next, &info->open_channels) {
+ tmp = list_entry(cursor, struct cg2900_channel_item, list);
+ list_del(cursor);
+ tmp->user->opened = false;
+ tmp->user->reset_cb(tmp->user);
+ kfree(tmp);
+ }
+
+ /* Reset finished. We are now idle until first channel is opened */
+ dev_dbg(user->dev, "New main_state: CG2900_IDLE\n");
+ info->main_state = CG2900_IDLE;
+
+ mutex_unlock(&main_info->man_mutex);
+
+ /*
+ * Send wake-up since this might have been called from a failed boot.
+ * No harm done if it is a CG2900 chip user who called.
+ */
+ wake_up_all(&main_wait_queue);
+
+ return 0;
+}
+
+/**
+ * cg2900_alloc_skb() - Allocates socket buffer.
+ * @size: Sk_buffer size in bytes.
+ * @priority: GFP priorit for allocation.
+ *
+ * Allocates a sk_buffer and reserves space for H4 header.
+ *
+ * Returns:
+ * sk_buffer if success.
+ * NULL if allocation fails.
+ */
+static struct sk_buff *cg2900_alloc_skb(unsigned int size, gfp_t priority)
+{
+ struct sk_buff *skb;
+
+ dev_dbg(MAIN_DEV, "cg2900_alloc_skb size %d bytes\n", size);
+
+ /* Allocate the SKB and reserve space for the header */
+ skb = alloc_skb(size + CG2900_SKB_RESERVE, priority);
+ if (skb)
+ skb_reserve(skb, CG2900_SKB_RESERVE);
+
+ return skb;
+}
+
+/**
+ * cg2900_write() - Called when user wants to write to the chip.
+ * @user: MFD device representing H4 channel to write to.
+ * @skb: Sk_buffer to transmit.
+ *
+ * Transmits the sk_buffer to the chip. If it is a BT cmd or FM audio packet it
+ * is checked that it is allowed to transmit the chip.
+ * Note that if error is returned it is up to the user to free the skb.
+ *
+ * Returns:
+ * 0 if success.
+ * -EINVAL if user or skb is NULL.
+ * -EACCES if channel is closed.
+ */
+static int cg2900_write(struct cg2900_user_data *user, struct sk_buff *skb)
+{
+ u8 *h4_header;
+ struct cg2900_chip_dev *dev;
+ struct cg2900_chip_info *info;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV, "cg2900_write: Calling with NULL pointer\n");
+ return -EINVAL;
+ }
+
+ if (!skb) {
+ dev_err(user->dev, "cg2900_write with no sk_buffer\n");
+ return -EINVAL;
+ }
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ dev_dbg(user->dev, "cg2900_write length %d bytes\n", skb->len);
+
+ if (!user->opened) {
+ dev_err(user->dev,
+ "Trying to transmit data on a closed channel\n");
+ return -EACCES;
+ }
+
+ /*
+ * Move the data pointer to the H:4 header position and
+ * store the H4 header.
+ */
+ h4_header = skb_push(skb, CG2900_SKB_RESERVE);
+ *h4_header = (u8)user->h4_channel;
+
+ if (user->h4_channel == CHANNEL_BT_CMD)
+ transmit_skb_with_flow_ctrl_bt(user, skb);
+ else if (user->h4_channel == CHANNEL_FM_RADIO)
+ transmit_skb_with_flow_ctrl_fm(user, skb);
+ else
+ cg2900_tx_to_chip(user, info->logger, skb);
+
+ return 0;
+}
+
+/**
+ * cg2900_no_write() - Used for channels where it is not allowed to write.
+ * @user: MFD device representing H4 channel to write to.
+ * @skb: Sk_buffer to transmit.
+ *
+ * Returns:
+ * -EPERM.
+ */
+static int cg2900_no_write(struct cg2900_user_data *user,
+ __attribute__((unused)) struct sk_buff *skb)
+{
+ dev_err(user->dev, "Not allowed to send on this channel\n");
+ return -EPERM;
+}
+
+/**
+ * cg2900_get_local_revision() - Called to retrieve revision data for the chip.
+ * @user: MFD device to check.
+ * @rev_data: Revision data to fill in.
+ *
+ * Returns:
+ * true if success.
+ * false upon failure.
+ */
+static bool cg2900_get_local_revision(struct cg2900_user_data *user,
+ struct cg2900_rev_data *rev_data)
+{
+ struct cg2900_chip_dev *dev;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV, "cg2900_get_local_revision: Calling with "
+ "NULL pointer\n");
+ return false;
+ }
+
+ if (!rev_data) {
+ dev_err(user->dev, "Calling with rev_data NULL\n");
+ return false;
+ }
+
+ dev = cg2900_get_prv(user);
+
+ rev_data->revision = dev->chip.hci_revision;
+ rev_data->sub_version = dev->chip.hci_sub_version;
+
+ return true;
+}
+
+static struct cg2900_user_data btcmd_data = {
+ .h4_channel = CHANNEL_BT_CMD,
+};
+static struct cg2900_user_data btacl_data = {
+ .h4_channel = CHANNEL_BT_ACL,
+};
+static struct cg2900_user_data btevt_data = {
+ .h4_channel = CHANNEL_BT_EVT,
+};
+static struct cg2900_user_data fm_data = {
+ .h4_channel = CHANNEL_FM_RADIO,
+};
+static struct cg2900_user_data gnss_data = {
+ .h4_channel = CHANNEL_GNSS,
+};
+static struct cg2900_user_data debug_data = {
+ .h4_channel = CHANNEL_DEBUG,
+};
+static struct cg2900_user_data ste_tools_data = {
+ .h4_channel = CHANNEL_STE_TOOLS,
+};
+static struct cg2900_user_data hci_logger_data = {
+ .h4_channel = CHANNEL_HCI_LOGGER,
+ .chip_independent = true,
+ .write = cg2900_no_write,
+ .open = cg2900_hci_log_open,
+ .close = cg2900_hci_log_close,
+};
+static struct cg2900_user_data core_data = {
+ .h4_channel = CHANNEL_CORE,
+ .write = cg2900_no_write,
+};
+static struct cg2900_user_data audio_bt_data = {
+ .h4_channel = CHANNEL_BT_CMD,
+ .is_audio = true,
+ .open = cg2900_bt_audio_open,
+ .close = cg2900_bt_audio_close,
+};
+static struct cg2900_user_data audio_fm_data = {
+ .h4_channel = CHANNEL_FM_RADIO,
+ .is_audio = true,
+ .open = cg2900_fm_audio_open,
+ .close = cg2900_fm_audio_close,
+};
+
+static struct mfd_cell cg2900_devs[] = {
+ {
+ .name = "cg2900-btcmd",
+ .platform_data = &btcmd_data,
+ .data_size = sizeof(btcmd_data),
+ },
+ {
+ .name = "cg2900-btacl",
+ .platform_data = &btacl_data,
+ .data_size = sizeof(btacl_data),
+ },
+ {
+ .name = "cg2900-btevt",
+ .platform_data = &btevt_data,
+ .data_size = sizeof(btevt_data),
+ },
+ {
+ .name = "cg2900-fm",
+ .platform_data = &fm_data,
+ .data_size = sizeof(fm_data),
+ },
+ {
+ .name = "cg2900-gnss",
+ .platform_data = &gnss_data,
+ .data_size = sizeof(gnss_data),
+ },
+ {
+ .name = "cg2900-debug",
+ .platform_data = &debug_data,
+ .data_size = sizeof(debug_data),
+ },
+ {
+ .name = "cg2900-stetools",
+ .platform_data = &ste_tools_data,
+ .data_size = sizeof(ste_tools_data),
+ },
+ {
+ .name = "cg2900-hcilogger",
+ .platform_data = &hci_logger_data,
+ .data_size = sizeof(hci_logger_data),
+ },
+ {
+ .name = "cg2900-core",
+ .platform_data = &core_data,
+ .data_size = sizeof(core_data),
+ },
+ {
+ .name = "cg2900-audiobt",
+ .platform_data = &audio_bt_data,
+ .data_size = sizeof(audio_bt_data),
+ },
+ {
+ .name = "cg2900-audiofm",
+ .platform_data = &audio_fm_data,
+ .data_size = sizeof(audio_fm_data),
+ },
+};
+
+static struct cg2900_user_data char_btcmd_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_BT_CMD,
+ },
+ .h4_channel = CHANNEL_BT_CMD,
+};
+static struct cg2900_user_data char_btacl_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_BT_ACL,
+ },
+ .h4_channel = CHANNEL_BT_ACL,
+};
+static struct cg2900_user_data char_btevt_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_BT_EVT,
+ },
+ .h4_channel = CHANNEL_BT_EVT,
+};
+static struct cg2900_user_data char_fm_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_FM_RADIO,
+ },
+ .h4_channel = CHANNEL_FM_RADIO,
+};
+static struct cg2900_user_data char_gnss_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_GNSS,
+ },
+ .h4_channel = CHANNEL_GNSS,
+};
+static struct cg2900_user_data char_debug_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_DEBUG,
+ },
+ .h4_channel = CHANNEL_DEBUG,
+};
+static struct cg2900_user_data char_ste_tools_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_STE_TOOLS,
+ },
+ .h4_channel = CHANNEL_STE_TOOLS,
+};
+static struct cg2900_user_data char_hci_logger_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_HCI_LOGGER,
+ },
+ .h4_channel = CHANNEL_HCI_LOGGER,
+ .chip_independent = true,
+ .write = cg2900_no_write,
+ .open = cg2900_hci_log_open,
+ .close = cg2900_hci_log_close,
+};
+static struct cg2900_user_data char_core_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_CORE,
+ },
+ .h4_channel = CHANNEL_CORE,
+ .write = cg2900_no_write,
+};
+static struct cg2900_user_data char_audio_bt_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_BT_AUDIO,
+ },
+ .h4_channel = CHANNEL_BT_CMD,
+ .is_audio = true,
+};
+static struct cg2900_user_data char_audio_fm_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_FM_AUDIO,
+ },
+ .h4_channel = CHANNEL_FM_RADIO,
+ .is_audio = true,
+};
+
+static struct mfd_cell cg2900_char_devs[] = {
+ {
+ .name = "cg2900-chardev",
+ .id = 0,
+ .platform_data = &char_btcmd_data,
+ .data_size = sizeof(char_btcmd_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 1,
+ .platform_data = &char_btacl_data,
+ .data_size = sizeof(char_btacl_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 2,
+ .platform_data = &char_btevt_data,
+ .data_size = sizeof(char_btevt_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 3,
+ .platform_data = &char_fm_data,
+ .data_size = sizeof(char_fm_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 4,
+ .platform_data = &char_gnss_data,
+ .data_size = sizeof(char_gnss_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 5,
+ .platform_data = &char_debug_data,
+ .data_size = sizeof(char_debug_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 6,
+ .platform_data = &char_ste_tools_data,
+ .data_size = sizeof(char_ste_tools_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 7,
+ .platform_data = &char_hci_logger_data,
+ .data_size = sizeof(char_hci_logger_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 8,
+ .platform_data = &char_core_data,
+ .data_size = sizeof(char_core_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 9,
+ .platform_data = &char_audio_bt_data,
+ .data_size = sizeof(char_audio_bt_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 10,
+ .platform_data = &char_audio_fm_data,
+ .data_size = sizeof(char_audio_fm_data),
+ },
+};
+
+/**
+ * set_plat_data() - Initializes data for an MFD cell.
+ * @cell: MFD cell.
+ * @dev: Current chip.
+ *
+ * Sets each callback to default function unless already set.
+ */
+static void set_plat_data(struct mfd_cell *cell, struct cg2900_chip_dev *dev)
+{
+ struct cg2900_user_data *pf_data = cell->platform_data;
+
+ if (!pf_data->open)
+ pf_data->open = cg2900_open;
+ if (!pf_data->close)
+ pf_data->close = cg2900_close;
+ if (!pf_data->reset)
+ pf_data->reset = cg2900_reset;
+ if (!pf_data->alloc_skb)
+ pf_data->alloc_skb = cg2900_alloc_skb;
+ if (!pf_data->write)
+ pf_data->write = cg2900_write;
+ if (!pf_data->get_local_revision)
+ pf_data->get_local_revision = cg2900_get_local_revision;
+
+ cg2900_set_prv(pf_data, dev);
+}
+
+/**
+ * check_chip_support() - Checks if connected chip is handled by this driver.
+ * @dev: Chip info structure.
+ *
+ * First check if chip is supported by this driver. If that is the case fill in
+ * the callbacks in @dev and initiate internal variables. Finally create MFD
+ * devices for all supported H4 channels. When finished power off the chip.
+ *
+ * Returns:
+ * true if chip is handled by this driver.
+ * false otherwise.
+ */
+static bool check_chip_support(struct cg2900_chip_dev *dev)
+{
+ struct cg2900_platform_data *pf_data;
+ struct cg2900_chip_info *info;
+ int i;
+ int err;
+
+ dev_dbg(dev->dev, "check_chip_support\n");
+
+ /*
+ * Check if this is a CG2900 revision.
+ * We do not care about the sub-version at the moment. Change this if
+ * necessary.
+ */
+ if ((dev->chip.manufacturer != CG2900_SUPP_MANUFACTURER) ||
+ (dev->chip.hci_revision != CG2900_PG1_SPECIAL_REV &&
+ (dev->chip.hci_revision < CG2900_SUPP_REVISION_MIN ||
+ dev->chip.hci_revision > CG2900_SUPP_REVISION_MAX))) {
+ dev_dbg(dev->dev, "Chip not supported by CG2900 driver\n"
+ "\tMan: 0x%02X\n"
+ "\tRev: 0x%04X\n"
+ "\tSub: 0x%04X\n",
+ dev->chip.manufacturer, dev->chip.hci_revision,
+ dev->chip.hci_sub_version);
+ return false;
+ }
+
+ /* Store needed data */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(dev->dev, "Couldn't allocate info struct\n");
+ return false;
+ }
+
+ /* Initialize all variables */
+ skb_queue_head_init(&info->tx_queue_bt);
+ skb_queue_head_init(&info->tx_queue_fm);
+
+ INIT_LIST_HEAD(&info->open_channels);
+
+ spin_lock_init(&info->tx_bt_lock);
+ spin_lock_init(&info->tx_fm_lock);
+ spin_lock_init(&info->rw_lock);
+
+ info->tx_nr_pkts_allowed_bt = 1;
+ info->audio_bt_cmd_op = CG2900_BT_OPCODE_NONE;
+ info->audio_fm_cmd_id = CG2900_FM_CMD_NONE;
+ info->hci_fm_cmd_func = CG2900_FM_CMD_PARAM_NONE;
+ info->fm_radio_mode = FM_RADIO_MODE_IDLE;
+ info->chip_dev = dev;
+ info->dev = dev->dev;
+
+ info->wq = create_singlethread_workqueue(WQ_NAME);
+ if (!info->wq) {
+ dev_err(dev->dev, "Could not create workqueue\n");
+ goto err_handling_free_info;
+ }
+
+ info->patch_file_name = kzalloc(NAME_MAX + 1, GFP_ATOMIC);
+ if (!info->patch_file_name) {
+ dev_err(dev->dev,
+ "Couldn't allocate name buffer for patch file\n");
+ goto err_handling_destroy_wq;
+ }
+
+ info->settings_file_name = kzalloc(NAME_MAX + 1, GFP_ATOMIC);
+ if (!info->settings_file_name) {
+ dev_err(dev->dev,
+ "Couldn't allocate name buffers settings file\n");
+ goto err_handling_free_patch_name;
+ }
+
+ info->selftest_work.data = info;
+ INIT_DELAYED_WORK(&info->selftest_work.work,
+ work_send_read_selftest_cmd);
+
+ dev->c_data = info;
+ /* Set the callbacks */
+ dev->c_cb.data_from_chip = data_from_chip;
+ dev->c_cb.chip_removed = chip_removed;
+
+ mutex_lock(&main_info->man_mutex);
+
+ pf_data = dev_get_platdata(dev->dev);
+ btcmd_data.channel_data.bt_bus = pf_data->bus;
+ btacl_data.channel_data.bt_bus = pf_data->bus;
+ btevt_data.channel_data.bt_bus = pf_data->bus;
+
+ for (i = 0; i < ARRAY_SIZE(cg2900_devs); i++)
+ set_plat_data(&cg2900_devs[i], dev);
+ for (i = 0; i < ARRAY_SIZE(cg2900_char_devs); i++)
+ set_plat_data(&cg2900_char_devs[i], dev);
+
+ err = mfd_add_devices(dev->dev, main_info->cell_base_id, cg2900_devs,
+ ARRAY_SIZE(cg2900_devs), NULL, 0);
+ if (err) {
+ dev_err(dev->dev, "Failed to add cg2900_devs (%d)\n", err);
+ goto err_handling_free_settings_name;
+ }
+
+ err = mfd_add_devices(dev->dev, main_info->cell_base_id,
+ cg2900_char_devs, ARRAY_SIZE(cg2900_char_devs),
+ NULL, 0);
+ if (err) {
+ dev_err(dev->dev, "Failed to add cg2900_char_devs (%d)\n", err);
+ goto err_handling_remove_devs;
+ }
+
+ main_info->cell_base_id += 30;
+ mutex_unlock(&main_info->man_mutex);
+
+ dev_info(dev->dev, "Chip supported by the CG2900 chip driver\n");
+
+ /* Finish by turning off the chip */
+ cg2900_create_work_item(info->wq, work_power_off_chip, dev);
+
+ return true;
+
+err_handling_remove_devs:
+ mfd_remove_devices(dev->dev);
+err_handling_free_settings_name:
+ kfree(info->settings_file_name);
+ mutex_unlock(&main_info->man_mutex);
+err_handling_free_patch_name:
+ kfree(info->patch_file_name);
+err_handling_destroy_wq:
+ destroy_workqueue(info->wq);
+err_handling_free_info:
+ kfree(info);
+ return false;
+}
+
+static struct cg2900_id_callbacks chip_support_callbacks = {
+ .check_chip_support = check_chip_support,
+};
+
+/**
+ * cg2900_chip_probe() - Initialize CG2900 chip handler resources.
+ * @pdev: Platform device.
+ *
+ * This function initializes the CG2900 driver, then registers to
+ * the CG2900 Core.
+ *
+ * Returns:
+ * 0 if success.
+ * -ENOMEM for failed alloc or structure creation.
+ * Error codes generated by cg2900_register_chip_driver.
+ */
+static int __devinit cg2900_chip_probe(struct platform_device *pdev)
+{
+ int err;
+
+ dev_dbg(&pdev->dev, "cg2900_chip_probe\n");
+
+ main_info = kzalloc(sizeof(*main_info), GFP_ATOMIC);
+ if (!main_info) {
+ dev_err(&pdev->dev, "Couldn't allocate main_info\n");
+ return -ENOMEM;
+ }
+
+ main_info->dev = &pdev->dev;
+ mutex_init(&main_info->man_mutex);
+
+ err = cg2900_register_chip_driver(&chip_support_callbacks);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Couldn't register chip driver (%d)\n", err);
+ goto error_handling;
+ }
+
+ dev_info(&pdev->dev, "CG2900 chip driver started\n");
+
+ return 0;
+
+error_handling:
+ mutex_destroy(&main_info->man_mutex);
+ kfree(main_info);
+ main_info = NULL;
+ return err;
+}
+
+/**
+ * cg2900_chip_remove() - Release CG2900 chip handler resources.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if success (always success).
+ */
+static int __devexit cg2900_chip_remove(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "CG2900 chip driver removed\n");
+
+ cg2900_deregister_chip_driver(&chip_support_callbacks);
+
+ if (!main_info)
+ return 0;
+ mutex_destroy(&main_info->man_mutex);
+ kfree(main_info);
+ main_info = NULL;
+ return 0;
+}
+
+static struct platform_driver cg2900_chip_driver = {
+ .driver = {
+ .name = "cg2900-chip",
+ .owner = THIS_MODULE,
+ },
+ .probe = cg2900_chip_probe,
+ .remove = __devexit_p(cg2900_chip_remove),
+};
+
+/**
+ * cg2900_chip_init() - Initialize module.
+ *
+ * Registers platform driver.
+ */
+static int __init cg2900_chip_init(void)
+{
+ pr_debug("cg2900_chip_init");
+ return platform_driver_register(&cg2900_chip_driver);
+}
+
+/**
+ * cg2900_chip_exit() - Remove module.
+ *
+ * Unregisters platform driver.
+ */
+static void __exit cg2900_chip_exit(void)
+{
+ pr_debug("cg2900_chip_exit");
+ platform_driver_unregister(&cg2900_chip_driver);
+}
+
+module_init(cg2900_chip_init);
+module_exit(cg2900_chip_exit);
+
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Linux CG2900 Connectivity Device Driver");
diff --git a/drivers/mfd/cg2900/cg2900_chip.h b/drivers/mfd/cg2900/cg2900_chip.h
new file mode 100644
index 00000000000..a53009f99d1
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_chip.h
@@ -0,0 +1,610 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * Henrik Possung (henrik.possung@stericsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@stericsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson CG2900 GPS/BT/FM controller.
+ */
+
+#ifndef _CG2900_CHIP_H_
+#define _CG2900_CHIP_H_
+
+/*
+ * Utility
+ */
+
+static inline void set_low_nibble(__u8 *var, __u8 value)
+{
+ *var = (*var & 0xf0) | (value & 0x0f);
+}
+
+static inline void set_high_nibble(__u8 *var, __u8 value)
+{
+ *var = (*var & 0x0f) | (value << 4);
+}
+
+static inline void store_bit(__u8 *var, size_t bit, __u8 value)
+{
+ *var = (*var & ~(1u << bit)) | (value << bit);
+}
+
+/*
+ * General chip defines
+ */
+
+/* Supported chips */
+#define CG2900_SUPP_MANUFACTURER 0x30
+#define CG2900_SUPP_REVISION_MIN 0x0100
+#define CG2900_SUPP_REVISION_MAX 0x0200
+
+/* Specific chip version data */
+#define CG2900_PG1_REV 0x0101
+#define CG2900_PG2_REV 0x0200
+#define CG2900_PG1_SPECIAL_REV 0x0700
+
+/*
+ * Bluetooth
+ */
+
+#define BT_SIZE_OF_HDR (sizeof(__le16) + sizeof(__u8))
+#define BT_PARAM_LEN(__pkt_len) (__pkt_len - BT_SIZE_OF_HDR)
+
+struct bt_cmd_cmpl_event {
+ __u8 eventcode;
+ __u8 plen;
+ __u8 n_commands;
+ __le16 opcode;
+ /*
+ * According to BT-specification what follows is "parameters"
+ * and unique to every command, but all commands start the
+ * parameters with the status field so include it here for
+ * convenience
+ */
+ __u8 status;
+ __u8 data[];
+} __packed;
+
+/* BT VS Store In FS command */
+#define CG2900_BT_OP_VS_STORE_IN_FS 0xFC22
+struct bt_vs_store_in_fs_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 user_id;
+ __u8 len;
+ __u8 data[];
+} __packed;
+
+#define CG2900_VS_STORE_IN_FS_USR_ID_BD_ADDR 0xFE
+
+/* BT VS Write File Block command */
+#define CG2900_BT_OP_VS_WRITE_FILE_BLOCK 0xFC2E
+struct bt_vs_write_file_block_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 id;
+ __u8 data[];
+} __packed;
+
+#define CG2900_BT_DISABLE 0x00
+#define CG2900_BT_ENABLE 0x01
+
+/* BT VS BT Enable command */
+#define CG2900_BT_OP_VS_BT_ENABLE 0xFF10
+struct bt_vs_bt_enable_cmd {
+ __le16 op_code;
+ u8 plen;
+ u8 enable;
+} __packed;
+
+/* Bytes in the command Hci_Cmd_ST_Set_Uart_Baud_Rate */
+#define CG2900_BAUD_RATE_57600 0x03
+#define CG2900_BAUD_RATE_115200 0x02
+#define CG2900_BAUD_RATE_230400 0x01
+#define CG2900_BAUD_RATE_460800 0x00
+#define CG2900_BAUD_RATE_921600 0x20
+#define CG2900_BAUD_RATE_2000000 0x25
+#define CG2900_BAUD_RATE_3000000 0x27
+#define CG2900_BAUD_RATE_4000000 0x2B
+
+/* BT VS SetBaudRate command */
+#define CG2900_BT_OP_VS_SET_BAUD_RATE 0xFC09
+struct bt_vs_set_baud_rate_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 baud_rate;
+} __packed;
+
+#define CG2900_BT_SELFTEST_SUCCESSFUL 0x00
+#define CG2900_BT_SELFTEST_FAILED 0x01
+#define CG2900_BT_SELFTEST_NOT_COMPLETED 0x02
+
+/* BT VS ReadSelfTestsResult command & event */
+#define CG2900_BT_OP_VS_READ_SELTESTS_RESULT 0xFC10
+struct bt_vs_read_selftests_result_evt {
+ __u8 status;
+ __u8 result;
+} __packed;
+
+/* Bluetooth Vendor Specific Opcodes */
+#define CG2900_BT_OP_VS_POWER_SWITCH_OFF 0xFD40
+#define CG2900_BT_OP_VS_SYSTEM_RESET 0xFF12
+
+#define CG2900_BT_OPCODE_NONE 0xFFFF
+
+/*
+ * Common multimedia
+ */
+
+#define CG2900_CODEC_TYPE_NONE 0x00
+#define CG2900_CODEC_TYPE_SBC 0x01
+
+#define CG2900_PCM_MODE_SLAVE 0x00
+#define CG2900_PCM_MODE_MASTER 0x01
+
+#define CG2900_I2S_MODE_MASTER 0x00
+#define CG2900_I2S_MODE_SLAVE 0x01
+
+/*
+ * CG2900 PG1 multimedia API
+ */
+
+#define CG2900_BT_VP_TYPE_PCM 0x00
+#define CG2900_BT_VP_TYPE_I2S 0x01
+#define CG2900_BT_VP_TYPE_SLIMBUS 0x02
+#define CG2900_BT_VP_TYPE_FM 0x03
+#define CG2900_BT_VP_TYPE_BT_SCO 0x04
+#define CG2900_BT_VP_TYPE_BT_A2DP 0x05
+#define CG2900_BT_VP_TYPE_ANALOG 0x07
+
+#define CG2900_BT_VS_SET_HARDWARE_CONFIG 0xFD54
+/* These don't have the same length, so a union won't work */
+struct bt_vs_set_hw_cfg_cmd_pcm {
+ __le16 opcode;
+ __u8 plen;
+ __u8 vp_type;
+ __u8 port_id;
+ __u8 mode_dir; /* NB: mode is in bit 1 (not 0) */
+ __u8 bit_clock;
+ __le16 frame_len;
+} __packed;
+#define HWCONFIG_PCM_SET_MODE(pcfg, mode) \
+ set_low_nibble(&(pcfg)->mode_dir, (mode) << 1)
+#define HWCONFIG_PCM_SET_DIR(pcfg, idx, dir) \
+ store_bit(&(pcfg)->mode_dir, (idx) + 4, (dir))
+
+struct bt_vs_set_hw_cfg_cmd_i2s {
+ __le16 opcode;
+ __u8 plen;
+ __u8 vp_type;
+ __u8 port_id;
+ __u8 half_period;
+ __u8 master_slave;
+} __packed;
+
+/* Max length for allocating */
+#define CG2900_BT_LEN_VS_SET_HARDWARE_CONFIG \
+ (sizeof(struct bt_vs_set_hw_cfg_cmd_pcm))
+
+#define CG2900_BT_VS_SET_SESSION_CONFIG 0xFD55
+struct session_config_vport {
+ __u8 type;
+ union {
+ struct {
+ __le16 acl_handle;
+ __u8 reserved[10];
+ } sco;
+ struct {
+ __u8 reserved[12];
+ } fm;
+ struct {
+ __u8 index;
+ __u8 slots_used;
+ __u8 slot_start[4];
+ __u8 reserved[6];
+ } pcm;
+ struct {
+ __u8 index;
+ __u8 channel;
+ __u8 reserved[10];
+ } i2s;
+ };
+} __packed;
+#define SESSIONCFG_PCM_SET_USED(port, idx, use) \
+ store_bit(&(port).pcm.slots_used, (idx), (use))
+
+struct session_config_stream {
+ __u8 media_type;
+ __u8 csel_srate;
+ __u8 codec_type;
+ __u8 codec_mode;
+ __u8 codec_params[3];
+ struct session_config_vport inport;
+ struct session_config_vport outport;
+} __packed;
+#define SESSIONCFG_SET_CHANNELS(pcfg, chnl) \
+ set_low_nibble(&(pcfg)->csel_srate, (chnl))
+#define SESSIONCFG_I2S_SET_SRATE(pcfg, rate) \
+ set_high_nibble(&(pcfg)->csel_srate, (rate))
+
+struct bt_vs_session_config_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 n_streams; /* we only support one here */
+ struct session_config_stream stream;
+} __packed;
+
+#define CG2900_BT_SESSION_MEDIA_TYPE_AUDIO 0x00
+
+#define CG2900_BT_SESSION_RATE_8K 0x01
+#define CG2900_BT_SESSION_RATE_16K 0x02
+#define CG2900_BT_SESSION_RATE_44_1K 0x04
+#define CG2900_BT_SESSION_RATE_48K 0x05
+
+#define CG2900_BT_MEDIA_CONFIG_MONO 0x00
+#define CG2900_BT_MEDIA_CONFIG_STEREO 0x01
+#define CG2900_BT_MEDIA_CONFIG_JOINT_STEREO 0x02
+#define CG2900_BT_MEDIA_CONFIG_DUAL_CHANNEL 0x03
+
+#define CG2900_BT_SESSION_I2S_INDEX_I2S 0x00
+#define CG2900_BT_SESSION_PCM_INDEX_PCM_I2S 0x00
+
+
+#define CG2900_BT_VS_SESSION_CTRL 0xFD57
+struct bt_vs_session_ctrl_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 id;
+ __u8 control;
+} __packed;
+
+#define CG2900_BT_SESSION_START 0x00
+#define CG2900_BT_SESSION_STOP 0x01
+#define CG2900_BT_SESSION_PAUSE 0x02
+#define CG2900_BT_SESSION_RESUME 0x03
+
+#define CG2900_BT_VS_RESET_SESSION_CONFIG 0xFD56
+struct bt_vs_reset_session_cfg_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 id;
+} __packed;
+
+/*
+ * CG2900 PG2 multimedia API
+ */
+
+#define CG2900_MC_PORT_PCM_I2S 0x00
+#define CG2900_MC_PORT_I2S 0x01
+#define CG2900_MC_PORT_BT_SCO 0x04
+#define CG2900_MC_PORT_FM_RX_0 0x07
+#define CG2900_MC_PORT_FM_RX_1 0x08
+#define CG2900_MC_PORT_FM_TX 0x09
+
+#define CG2900_MC_VS_PORT_CONFIG 0xFD64
+struct mc_vs_port_cfg_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 type;
+ /*
+ * one of the following configuration structs should follow, but they
+ * have different lengths so a union will not work
+ */
+} __packed;
+
+struct mc_vs_port_cfg_pcm_i2s {
+ __u8 role_dir;
+ __u8 sco_a2dp_slots_used;
+ __u8 fm_slots_used;
+ __u8 ring_slots_used;
+ __u8 slot_start[4];
+ __u8 ratio_mode;
+ __u8 frame_len;
+ __u8 bitclk_srate;
+} __packed;
+#define PORTCFG_PCM_SET_ROLE(cfg, role) \
+ set_low_nibble(&(cfg).role_dir, (role))
+#define PORTCFG_PCM_SET_DIR(cfg, idx, dir) \
+ store_bit(&(cfg).role_dir, (idx) + 4, (dir))
+static inline void portcfg_pcm_set_sco_used(struct mc_vs_port_cfg_pcm_i2s *cfg,
+ size_t index, __u8 use)
+{
+ if (use) {
+ /* clear corresponding slot in all cases */
+ cfg->sco_a2dp_slots_used &= ~(0x11 << index);
+ cfg->fm_slots_used &= ~(0x11 << index);
+ cfg->ring_slots_used &= ~(0x11 << index);
+ /* set for sco */
+ cfg->sco_a2dp_slots_used |= (1u << index);
+ } else {
+ /* only clear for sco */
+ cfg->sco_a2dp_slots_used &= ~(1u << index);
+ }
+}
+#define PORTCFG_PCM_SET_SCO_USED(cfg, idx, use) \
+ portcfg_pcm_set_sco_used(&cfg, idx, use)
+#define PORTCFG_PCM_SET_RATIO(cfg, r) \
+ set_low_nibble(&(cfg).ratio_mode, (r))
+#define PORTCFG_PCM_SET_MODE(cfg, mode) \
+ set_high_nibble(&(cfg).ratio_mode, (mode))
+#define PORTCFG_PCM_SET_BITCLK(cfg, clk) \
+ set_low_nibble(&(cfg).bitclk_srate, (clk))
+#define PORTCFG_PCM_SET_SRATE(cfg, rate) \
+ set_high_nibble(&(cfg).bitclk_srate, (rate))
+
+#define CG2900_MC_PCM_SAMPLE_RATE_8 1
+#define CG2900_MC_PCM_SAMPLE_RATE_16 2
+#define CG2900_MC_PCM_SAMPLE_RATE_44_1 4
+#define CG2900_MC_PCM_SAMPLE_RATE_48 6
+
+struct mc_vs_port_cfg_i2s {
+ __u8 role_hper;
+ __u8 csel_srate;
+ __u8 wordlen;
+};
+#define PORTCFG_I2S_SET_ROLE(cfg, role) \
+ set_low_nibble(&(cfg).role_hper, (role))
+#define PORTCFG_I2S_SET_HALFPERIOD(cfg, hper) \
+ set_high_nibble(&(cfg).role_hper, (hper))
+#define PORTCFG_I2S_SET_CHANNELS(cfg, chnl) \
+ set_low_nibble(&(cfg).csel_srate, (chnl))
+#define PORTCFG_I2S_SET_SRATE(cfg, rate) \
+ set_high_nibble(&(cfg).csel_srate, (rate))
+#define PORTCFG_I2S_SET_WORDLEN(cfg, len) \
+ set_low_nibble(&(cfg).wordlen, len)
+
+#define CG2900_MC_I2S_RIGHT_CHANNEL 1
+#define CG2900_MC_I2S_LEFT_CHANNEL 2
+#define CG2900_MC_I2S_BOTH_CHANNELS 3
+
+#define CG2900_MC_I2S_SAMPLE_RATE_8 0
+#define CG2900_MC_I2S_SAMPLE_RATE_16 1
+#define CG2900_MC_I2S_SAMPLE_RATE_44_1 2
+#define CG2900_MC_I2S_SAMPLE_RATE_48 4
+
+#define CG2900_MC_I2S_WORD_16 1
+#define CG2900_MC_I2S_WORD_32 3
+
+struct mc_vs_port_cfg_fm {
+ __u8 srate; /* NB: value goes in _upper_ nibble! */
+};
+#define PORTCFG_FM_SET_SRATE(cfg, rate) \
+ set_high_nibble(&(cfg).srate, (rate))
+
+struct mc_vs_port_cfg_sco {
+ __le16 acl_id;
+ __u8 wbs_codec;
+} __packed;
+#define PORTCFG_SCO_SET_CODEC(cfg, codec) \
+ set_high_nibble(&(cfg).wbs_codec, (codec))
+
+#define CG2900_MC_VS_CREATE_STREAM 0xFD66
+struct mc_vs_create_stream_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 id;
+ __u8 inport;
+ __u8 outport;
+ __u8 order; /* NB: not used by chip */
+} __packed;
+
+#define CG2900_MC_VS_DELETE_STREAM 0xFD67
+struct mc_vs_delete_stream_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 stream;
+} __packed;
+
+#define CG2900_MC_VS_STREAM_CONTROL 0xFD68
+struct mc_vs_stream_ctrl_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 command;
+ __u8 n_streams;
+ __u8 stream[];
+} __packed;
+
+#define CG2900_MC_STREAM_START 0x00
+#define CG2900_MC_STREAM_STOP 0x01
+#define CG2900_MC_STREAM_STOP_FLUSH 0x02
+
+#define CG2900_MC_VS_SET_FM_START_MODE 0xFD69
+
+/*
+ * FM
+ */
+
+/* FM legacy command packet */
+struct fm_leg_cmd {
+ __u8 length;
+ __u8 opcode;
+ __u8 read_write;
+ __u8 fm_function;
+ union { /* Payload varies with function */
+ __le16 irqmask;
+ struct fm_leg_fm_cmd {
+ __le16 head;
+ __le16 data[];
+ } fm_cmd;
+ };
+} __packed;
+
+/* FM legacy command complete packet */
+struct fm_leg_cmd_cmpl {
+ __u8 param_length;
+ __u8 status;
+ __u8 opcode;
+ __u8 read_write;
+ __u8 cmd_status;
+ __u8 fm_function;
+ __le16 response_head;
+ __le16 data[];
+} __packed;
+
+/* FM legacy interrupt packet, PG2 style */
+struct fm_leg_irq_v2 {
+ __u8 param_length;
+ __u8 status;
+ __u8 opcode;
+ __u8 event_type;
+ __u8 event_id;
+ __le16 irq;
+} __packed;
+
+/* FM legacy interrupt packet, PG1 style */
+struct fm_leg_irq_v1 {
+ __u8 param_length;
+ __u8 opcode;
+ __u8 event_id;
+ __le16 irq;
+} __packed;
+
+union fm_leg_evt_or_irq {
+ __u8 param_length;
+ struct fm_leg_cmd_cmpl evt;
+ struct fm_leg_irq_v2 irq_v2;
+ struct fm_leg_irq_v1 irq_v1;
+} __packed;
+
+/* FM Opcode generic*/
+#define CG2900_FM_GEN_ID_LEGACY 0xFE
+
+/* FM event*/
+#define CG2900_FM_EVENT_UNKNOWN 0
+#define CG2900_FM_EVENT_CMD_COMPLETE 1
+#define CG2900_FM_EVENT_INTERRUPT 2
+
+/* FM do-command identifiers. */
+#define CG2900_FM_DO_AIP_FADE_START 0x0046
+#define CG2900_FM_DO_AUP_BT_FADE_START 0x01C2
+#define CG2900_FM_DO_AUP_EXT_FADE_START 0x0102
+#define CG2900_FM_DO_AUP_FADE_START 0x00A2
+#define CG2900_FM_DO_FMR_SETANTENNA 0x0663
+#define CG2900_FM_DO_FMR_SP_AFSWITCH_START 0x04A3
+#define CG2900_FM_DO_FMR_SP_AFUPDATE_START 0x0463
+#define CG2900_FM_DO_FMR_SP_BLOCKSCAN_START 0x0683
+#define CG2900_FM_DO_FMR_SP_PRESETPI_START 0x0443
+#define CG2900_FM_DO_FMR_SP_SCAN_START 0x0403
+#define CG2900_FM_DO_FMR_SP_SEARCH_START 0x03E3
+#define CG2900_FM_DO_FMR_SP_SEARCHPI_START 0x0703
+#define CG2900_FM_DO_FMR_SP_TUNE_SETCHANNEL 0x03C3
+#define CG2900_FM_DO_FMR_SP_TUNE_STEPCHANNEL 0x04C3
+#define CG2900_FM_DO_FMT_PA_SETCTRL 0x01A4
+#define CG2900_FM_DO_FMT_PA_SETMODE 0x01E4
+#define CG2900_FM_DO_FMT_SP_TUNE_SETCHANNEL 0x0064
+#define CG2900_FM_DO_GEN_ANTENNACHECK_START 0x02A1
+#define CG2900_FM_DO_GEN_GOTOMODE 0x0041
+#define CG2900_FM_DO_GEN_POWERSUPPLY_SETMODE 0x0221
+#define CG2900_FM_DO_GEN_SELECTREFERENCECLOCK 0x0201
+#define CG2900_FM_DO_GEN_SETPROCESSINGCLOCK 0x0241
+#define CG2900_FM_DO_GEN_SETREFERENCECLOCKPLL 0x01A1
+#define CG2900_FM_DO_TST_TX_RAMP_START 0x0147
+#define CG2900_FM_CMD_NONE 0xFFFF
+#define CG2900_FM_CMD_ID_GEN_GOTO_POWER_DOWN 0x0081
+#define CG2900_FM_CMD_ID_GEN_GOTO_STANDBY 0x0061
+
+/* FM Command IDs */
+#define CG2900_FM_CMD_ID_AUP_EXT_SET_MODE 0x0162
+#define CG2900_FM_CMD_ID_AUP_EXT_SET_CTRL 0x0182
+#define CG2900_FM_CMD_ID_AIP_SET_MODE 0x01C6
+#define CG2900_FM_CMD_ID_AIP_BT_SET_CTRL 0x01A6
+#define CG2900_FM_CMD_ID_AIP_BT_SET_MODE 0x01E6
+
+/* FM Command Parameters. */
+#define CG2900_FM_CMD_PARAM_ENABLE 0x00
+#define CG2900_FM_CMD_PARAM_DISABLE 0x01
+#define CG2900_FM_CMD_PARAM_RESET 0x02
+#define CG2900_FM_CMD_PARAM_WRITECOMMAND 0x10
+#define CG2900_FM_CMD_PARAM_SET_INT_MASK_ALL 0x20
+#define CG2900_FM_CMD_PARAM_GET_INT_MASK_ALL 0x21
+#define CG2900_FM_CMD_PARAM_SET_INT_MASK 0x22
+#define CG2900_FM_CMD_PARAM_GET_INT_MASK 0x23
+#define CG2900_FM_CMD_PARAM_FM_FW_DOWNLOAD 0x30
+#define CG2900_FM_CMD_PARAM_NONE 0xFF
+
+/* FM Legacy Command Parameters */
+#define CG2900_FM_CMD_LEG_PARAM_WRITE 0x00
+#define CG2900_FM_CMD_LEG_PARAM_IRQ 0x01
+
+/* FM Command Status. */
+#define CG2900_FM_CMD_STATUS_COMMAND_SUCCEEDED 0x00
+#define CG2900_FM_CMD_STATUS_HW_FAILURE 0x03
+#define CG2900_FM_CMD_STATUS_INVALID_PARAMS 0x12
+#define CG2900_FM_CMD_STATUS_UNINITILIZED 0x15
+#define CG2900_FM_CMD_STATUS_UNSPECIFIED_ERROR 0x1F
+#define CG2900_FM_CMD_STATUS_COMMAND_DISALLOWED 0x0C
+#define CG2900_FM_CMD_STATUS_FW_WRONG_SEQUENCE_NR 0xF1
+#define CG2900_FM_CMD_STATUS_FW_UNKNOWN_FILE 0xF2
+#define CG2900_FM_CMD_STATUS_FW_FILE_VER_MISMATCH 0xF3
+
+/* FM Interrupts. */
+#define CG2900_FM_IRPT_FIQ 0x0000
+#define CG2900_FM_IRPT_OPERATION_SUCCEEDED 0x0001
+#define CG2900_FM_IRPT_OPERATION_FAILED 0x0002
+#define CG2900_FM_IRPT_BUFFER_FULL 0x0008
+#define CG2900_FM_IRPT_BUFFER_EMPTY 0x0008
+#define CG2900_FM_IRPT_SIGNAL_QUALITY_LOW 0x0010
+#define CG2900_FM_IRPT_MUTE_STATUS_CHANGED 0x0010
+#define CG2900_FM_IRPT_MONO_STEREO_TRANSITION 0x0020
+#define CG2900_FM_IRPT_OVER_MODULATION 0x0020
+#define CG2900_FM_IRPT_RDS_SYNC_FOUND 0x0040
+#define CG2900_FM_IRPT_INPUT_OVERDRIVE 0x0040
+#define CG2900_FM_IRPT_RDS_SYNC_LOST 0x0080
+#define CG2900_FM_IRPT_PI_CODE_CHANGED 0x0100
+#define CG2900_FM_IRPT_REQUEST_BLOCK_AVALIBLE 0x0200
+#define CG2900_FM_IRPT_BUFFER_CLEARED 0x2000
+#define CG2900_FM_IRPT_WARM_BOOT_READY 0x4000
+#define CG2900_FM_IRPT_COLD_BOOT_READY 0x8000
+
+/* FM Legacy Function Command Parameters */
+
+/* AUP_EXT_SetMode Output enum */
+#define CG2900_FM_CMD_AUP_EXT_SET_MODE_DISABLED 0x0000
+#define CG2900_FM_CMD_AUP_EXT_SET_MODE_I2S 0x0001
+#define CG2900_FM_CMD_AUP_EXT_SET_MODE_PARALLEL 0x0002
+
+/* SetControl Conversion enum */
+#define CG2900_FM_CMD_SET_CTRL_CONV_UP 0x0000
+#define CG2900_FM_CMD_SET_CTRL_CONV_DOWN 0x0001
+
+/* AIP_SetMode Input enum */
+#define CG2900_FM_CMD_AIP_SET_MODE_INPUT_ANA 0x0000
+#define CG2900_FM_CMD_AIP_SET_MODE_INPUT_DIG 0x0001
+
+/* AIP_BT_SetMode Input enum */
+#define CG2900_FM_CMD_AIP_BT_SET_MODE_INPUT_RESERVED 0x0000
+#define CG2900_FM_CMD_AIP_BT_SET_MODE_INPUT_I2S 0x0001
+#define CG2900_FM_CMD_AIP_BT_SET_MODE_INPUT_PAR 0x0002
+#define CG2900_FM_CMD_AIP_BT_SET_MODE_INPUT_FIFO 0x0003
+
+/* FM Parameter Lengths = FM command length - length field (1 byte) */
+#define CG2900_FM_CMD_PARAM_LEN(len) (len - 1)
+
+/*
+ * FM Command ID mapped per byte and shifted 3 bits left
+ * Also adds number of parameters at first 3 bits of LSB.
+ */
+static inline __u16 cg2900_get_fm_cmd_id(__u16 opcode)
+{
+ return opcode >> 3;
+}
+
+static inline __u16 cg2900_make_fm_cmd_id(__u16 id, __u8 num_params)
+{
+ return (id << 3) | num_params;
+}
+
+/*
+ * GNSS
+ */
+
+struct gnss_hci_hdr {
+ __u8 op_code;
+ __le16 plen;
+} __packed;
+
+#endif /* _CG2900_CHIP_H_ */
diff --git a/drivers/mfd/cg2900/cg2900_core.c b/drivers/mfd/cg2900/cg2900_core.c
new file mode 100644
index 00000000000..52eac4d3db6
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_core.c
@@ -0,0 +1,713 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * Henrik Possung (henrik.possung@stericsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@stericsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson CG2900 GPS/BT/FM controller.
+ */
+#define NAME "cg2900_core"
+#define pr_fmt(fmt) NAME ": " fmt "\n"
+
+#include <asm/byteorder.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/stat.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/cg2900.h>
+#include <linux/mfd/core.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+
+#include "cg2900_core.h"
+
+/* Device names */
+#define CG2900_CDEV_NAME "cg2900_core_test"
+#define CG2900_CLASS_NAME "cg2900_class"
+#define CG2900_DEVICE_NAME "cg2900_driver"
+#define CORE_WQ_NAME "cg2900_core_wq"
+
+#define LOGGER_DIRECTION_TX 0
+#define LOGGER_DIRECTION_RX 1
+
+/*
+ * Timeout values
+ */
+#define CHIP_READY_TIMEOUT (100) /* ms */
+#define REVISION_READOUT_TIMEOUT (500) /* ms */
+#define SLEEP_TIMEOUT_MS (10000) /* ms */
+
+/**
+ * enum boot_state - BOOT-state for CG2900 Core.
+ * @BOOT_RESET: HCI Reset has been sent.
+ * @BOOT_READ_LOCAL_VERSION_INFORMATION: ReadLocalVersionInformation
+ * command has been sent.
+ * @BOOT_READY: CG2900 Core boot is ready.
+ * @BOOT_FAILED: CG2900 Core boot failed.
+ */
+enum boot_state {
+ BOOT_RESET,
+ BOOT_READ_LOCAL_VERSION_INFORMATION,
+ BOOT_READY,
+ BOOT_FAILED
+};
+
+/**
+ * struct chip_handler_item - Structure to store chip handler cb.
+ * @list: list_head struct.
+ * @cb: Chip handler callback struct.
+ */
+struct chip_handler_item {
+ struct list_head list;
+ struct cg2900_id_callbacks cb;
+};
+
+/**
+ * struct core_info - Main info structure for CG2900 Core.
+ * @boot_state: Current BOOT-state of CG2900 Core.
+ * @wq: CG2900 Core workqueue.
+ * @chip_dev: Device structure for chip driver.
+ * @work: Work structure.
+ */
+struct core_info {
+ enum boot_state boot_state;
+ struct workqueue_struct *wq;
+ struct cg2900_chip_dev *chip_dev;
+ struct work_struct work;
+};
+
+/**
+ * struct main_info - Main info structure for CG2900 Core.
+ * @dev: Device structure for STE Connectivity driver.
+ * @man_mutex: Management mutex.
+ * @chip_handlers: List of the register handlers for different chips.
+ * @wq: Wait queue.
+ */
+struct main_info {
+ struct device *dev;
+ struct mutex man_mutex;
+ struct list_head chip_handlers;
+ wait_queue_head_t wq;
+};
+
+/* core_info - Main information object for CG2900 Core. */
+static struct main_info *main_info;
+
+/* Module parameters */
+u8 bd_address[] = {0x00, 0xBE, 0xAD, 0xDE, 0x80, 0x00};
+EXPORT_SYMBOL_GPL(bd_address);
+int bd_addr_count = BT_BDADDR_SIZE;
+
+static int sleep_timeout_ms = SLEEP_TIMEOUT_MS;
+
+/**
+ * send_bt_cmd() - Copy and send sk_buffer with no assigned user.
+ * @dev: Current chip to transmit to.
+ * @data: Data to send.
+ * @length: Length in bytes of data.
+ *
+ * The send_bt_cmd() function allocate sk_buffer, copy supplied
+ * data to it, and send the sk_buffer to controller.
+ */
+void send_bt_cmd(struct cg2900_chip_dev *dev, void *data, int length)
+{
+ struct sk_buff *skb;
+ int err;
+
+ skb = alloc_skb(length + HCI_H4_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(dev->dev, "send_bt_cmd: Couldn't alloc sk_buff with "
+ "length %d\n", length);
+ return;
+ }
+
+ skb_reserve(skb, HCI_H4_SIZE);
+ memcpy(skb_put(skb, length), data, length);
+ skb_push(skb, HCI_H4_SIZE);
+ skb->data[0] = HCI_BT_CMD_H4_CHANNEL;
+
+ err = dev->t_cb.write(dev, skb);
+ if (err) {
+ dev_err(dev->dev, "send_bt_cmd: Transport write failed (%d)\n",
+ err);
+ kfree_skb(skb);
+ }
+}
+
+/**
+ * handle_reset_cmd_complete_evt() - Handle a received HCI Command Complete event for a Reset command.
+ * @dev: Current device.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * True, if packet was handled internally,
+ * False, otherwise.
+ */
+static bool handle_reset_cmd_complete_evt(struct cg2900_chip_dev *dev, u8 *data)
+{
+ bool pkt_handled = false;
+ u8 status = data[0];
+ struct hci_command_hdr cmd;
+ struct core_info *info = dev->prv_data;
+
+ dev_dbg(dev->dev, "Received Reset complete event with status 0x%X\n",
+ status);
+
+ if (info->boot_state == BOOT_RESET) {
+ /* Transmit HCI Read Local Version Information command */
+ dev_dbg(dev->dev, "New boot_state: "
+ "BOOT_READ_LOCAL_VERSION_INFORMATION\n");
+ info->boot_state = BOOT_READ_LOCAL_VERSION_INFORMATION;
+ cmd.opcode = cpu_to_le16(HCI_OP_READ_LOCAL_VERSION);
+ cmd.plen = 0; /* No parameters for HCI reset */
+ send_bt_cmd(dev, &cmd, sizeof(cmd));
+
+ pkt_handled = true;
+ }
+
+ return pkt_handled;
+}
+
+/**
+ * handle_read_local_version_info_cmd_complete_evt() - Handle a received HCI Command Complete event for a ReadLocalVersionInformation command.
+ * @dev: Current device.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * True, if packet was handled internally,
+ * False, otherwise.
+ */
+static bool
+handle_read_local_version_info_cmd_complete_evt(struct cg2900_chip_dev *dev,
+ u8 *data)
+{
+ struct hci_rp_read_local_version *evt;
+ struct core_info *info = dev->prv_data;
+
+ /* Check we're in the right state */
+ if (info->boot_state != BOOT_READ_LOCAL_VERSION_INFORMATION)
+ return false;
+
+ /* We got an answer for our HCI command. Extract data */
+ evt = (struct hci_rp_read_local_version *)data;
+
+ /* We will handle the packet */
+ if (HCI_BT_ERROR_NO_ERROR != evt->status) {
+ dev_err(dev->dev, "Received Read Local Version Information "
+ "with status 0x%X\n", evt->status);
+ dev_dbg(dev->dev, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ wake_up_all(&main_info->wq);
+ return true;
+ }
+
+ /* The command worked. Store the data */
+ dev->chip.hci_version = evt->hci_ver;
+ dev->chip.hci_revision = le16_to_cpu(evt->hci_rev);
+ dev->chip.lmp_pal_version = evt->lmp_ver;
+ dev->chip.manufacturer = le16_to_cpu(evt->manufacturer);
+ dev->chip.hci_sub_version = le16_to_cpu(evt->lmp_subver);
+ dev_info(dev->dev, "Received Read Local Version Information with:\n"
+ "\thci_version: 0x%02X\n"
+ "\thci_revision: 0x%04X\n"
+ "\tlmp_pal_version: 0x%02X\n"
+ "\tmanufacturer: 0x%04X\n"
+ "\thci_sub_version: 0x%04X\n",
+ dev->chip.hci_version, dev->chip.hci_revision,
+ dev->chip.lmp_pal_version, dev->chip.manufacturer,
+ dev->chip.hci_sub_version);
+
+ dev_dbg(dev->dev, "New boot_state: BOOT_READY\n");
+ info->boot_state = BOOT_READY;
+ wake_up_all(&main_info->wq);
+
+ return true;
+}
+
+/**
+ * handle_rx_data_bt_evt() - Check if data should be handled in CG2900 Core.
+ * @dev: Current chip
+ * @skb: Data packet
+ *
+ * The handle_rx_data_bt_evt() function checks if received data should be
+ * handled in CG2900 Core. If so handle it correctly.
+ * Received data is always HCI BT Event.
+ *
+ * Returns:
+ * True, if packet was handled internally,
+ * False, otherwise.
+ */
+static bool handle_rx_data_bt_evt(struct cg2900_chip_dev *dev,
+ struct sk_buff *skb)
+{
+ bool pkt_handled = false;
+ u8 *data = &skb->data[CG2900_SKB_RESERVE];
+ struct hci_event_hdr *evt;
+ struct hci_ev_cmd_complete *cmd_complete;
+ u16 op_code;
+
+ evt = (struct hci_event_hdr *)data;
+
+ /* First check the event code */
+ if (HCI_EV_CMD_COMPLETE != evt->evt)
+ return false;
+
+ data += sizeof(*evt);
+ cmd_complete = (struct hci_ev_cmd_complete *)data;
+
+ op_code = le16_to_cpu(cmd_complete->opcode);
+
+ dev_dbg(dev->dev, "Received Command Complete: op_code = 0x%04X\n",
+ op_code);
+ data += sizeof(*cmd_complete); /* Move to first byte after OCF */
+
+ if (op_code == HCI_OP_RESET)
+ pkt_handled = handle_reset_cmd_complete_evt(dev, data);
+ else if (op_code == HCI_OP_READ_LOCAL_VERSION)
+ pkt_handled = handle_read_local_version_info_cmd_complete_evt
+ (dev, data);
+
+ if (pkt_handled)
+ kfree_skb(skb);
+
+ return pkt_handled;
+}
+
+static void cg2900_data_from_chip(struct cg2900_chip_dev *dev,
+ struct sk_buff *skb)
+{
+ u8 h4_channel;
+
+ dev_dbg(dev->dev, "cg2900_data_from_chip\n");
+
+ if (!skb) {
+ dev_err(dev->dev, "No data supplied\n");
+ return;
+ }
+
+ h4_channel = skb->data[0];
+
+ /*
+ * First check if this is the response for something
+ * we have sent internally.
+ */
+ if (HCI_BT_EVT_H4_CHANNEL == h4_channel &&
+ handle_rx_data_bt_evt(dev, skb)) {
+ dev_dbg(dev->dev, "Received packet handled internally\n");
+ } else {
+ dev_err(dev->dev,
+ "cg2900_data_from_chip: Received unexpected packet\n");
+ kfree_skb(skb);
+ }
+}
+
+/**
+ * work_hw_registered() - Called when the interface to HW has been established.
+ * @work: Reference to work data.
+ *
+ * Since there now is a transport identify the connected chip and decide which
+ * chip handler to use.
+ */
+static void work_hw_registered(struct work_struct *work)
+{
+ struct hci_command_hdr cmd;
+ struct cg2900_chip_dev *dev;
+ struct core_info *info;
+ bool chip_handled = false;
+ struct list_head *cursor;
+ struct chip_handler_item *tmp;
+
+ dev_dbg(main_info->dev, "work_hw_registered\n");
+
+ if (!work) {
+ dev_err(main_info->dev, "work_hw_registered: work == NULL\n");
+ return;
+ }
+
+ info = container_of(work, struct core_info, work);
+ dev = info->chip_dev;
+
+ /*
+ * This might look strange, but we need to read out
+ * the revision info in order to be able to shutdown the chip properly.
+ */
+ if (dev->t_cb.set_chip_power)
+ dev->t_cb.set_chip_power(dev, true);
+
+ /* Wait 100ms before continuing to be sure that the chip is ready */
+ schedule_timeout_killable(msecs_to_jiffies(CHIP_READY_TIMEOUT));
+
+ /* Set our function to receive data from chip */
+ dev->c_cb.data_from_chip = cg2900_data_from_chip;
+
+ /*
+ * Transmit HCI reset command to ensure the chip is using
+ * the correct transport
+ */
+ dev_dbg(dev->dev, "New boot_state: BOOT_RESET\n");
+ info->boot_state = BOOT_RESET;
+ cmd.opcode = cpu_to_le16(HCI_OP_RESET);
+ cmd.plen = 0; /* No parameters for HCI reset */
+ send_bt_cmd(dev, &cmd, sizeof(cmd));
+
+ dev_dbg(dev->dev,
+ "Wait up to 500 milliseconds for revision to be read\n");
+ wait_event_timeout(main_info->wq,
+ (BOOT_READY == info->boot_state ||
+ BOOT_FAILED == info->boot_state),
+ msecs_to_jiffies(REVISION_READOUT_TIMEOUT));
+
+ if (BOOT_READY != info->boot_state) {
+ dev_err(dev->dev,
+ "Could not read out revision from the chip\n");
+ return;
+ }
+
+ dev->c_cb.data_from_chip = NULL;
+
+ mutex_lock(&main_info->man_mutex);
+ list_for_each(cursor, &main_info->chip_handlers) {
+ tmp = list_entry(cursor, struct chip_handler_item, list);
+ chip_handled = tmp->cb.check_chip_support(dev);
+ if (chip_handled) {
+ dev_info(dev->dev, "Chip handler found\n");
+ break;
+ }
+ }
+ mutex_unlock(&main_info->man_mutex);
+
+ if (!chip_handled)
+ dev_info(dev->dev, "No chip handler found\n");
+}
+
+/**
+ * cg2900_register_chip_driver() - Register a chip handler.
+ * @cb: Callbacks to call when chip is connected.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL if NULL is supplied as @cb.
+ * -ENOMEM if allocation fails or work queue can't be created.
+ */
+int cg2900_register_chip_driver(struct cg2900_id_callbacks *cb)
+{
+ struct chip_handler_item *item;
+
+ dev_dbg(main_info->dev, "cg2900_register_chip_driver\n");
+
+ if (!cb) {
+ dev_err(main_info->dev, "NULL supplied as cb\n");
+ return -EINVAL;
+ }
+
+ item = kzalloc(sizeof(*item), GFP_KERNEL);
+ if (!item) {
+ dev_err(main_info->dev,
+ "cg2900_register_chip_driver: "
+ "Failed to alloc memory\n");
+ return -ENOMEM;
+ }
+
+ memcpy(&item->cb, cb, sizeof(cb));
+ mutex_lock(&main_info->man_mutex);
+ list_add_tail(&item->list, &main_info->chip_handlers);
+ mutex_unlock(&main_info->man_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cg2900_register_chip_driver);
+
+/**
+ * cg2900_deregister_chip_driver() - Deregister a chip handler.
+ * @cb: Callbacks to call when chip is connected.
+ */
+void cg2900_deregister_chip_driver(struct cg2900_id_callbacks *cb)
+{
+ struct chip_handler_item *tmp;
+ struct list_head *cursor, *next;
+
+ dev_dbg(main_info->dev, "cg2900_deregister_chip_driver\n");
+
+ if (!cb) {
+ dev_err(main_info->dev, "NULL supplied as cb\n");
+ return;
+ }
+ mutex_lock(&main_info->man_mutex);
+ list_for_each_safe(cursor, next, &main_info->chip_handlers) {
+ tmp = list_entry(cursor, struct chip_handler_item, list);
+ if (tmp->cb.check_chip_support == cb->check_chip_support) {
+ list_del(cursor);
+ kfree(tmp);
+ break;
+ }
+ }
+ mutex_unlock(&main_info->man_mutex);
+}
+EXPORT_SYMBOL_GPL(cg2900_deregister_chip_driver);
+
+/**
+ * cg2900_register_trans_driver() - Register a transport driver.
+ * @dev: Transport device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL if NULL is supplied as @cb.
+ * -ENOMEM if allocation fails or work queue can't be created.
+ * -EACCES if work can't be queued.
+ */
+int cg2900_register_trans_driver(struct cg2900_chip_dev *dev)
+{
+ int err;
+ struct cg2900_platform_data *pf_data;
+ struct core_info *info;
+
+ BUG_ON(!main_info);
+
+ if (!dev || !dev->dev) {
+ dev_err(main_info->dev, "cg2900_register_trans_driver: "
+ "Received NULL pointer\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev->dev, "cg2900_register_trans_driver\n");
+
+ if (!dev->t_cb.write) {
+ dev_err(dev->dev, "cg2900_register_trans_driver: Write function"
+ " missing\n");
+ return -EINVAL;
+ }
+
+ pf_data = dev_get_platdata(dev->dev);
+ if (!pf_data) {
+ dev_err(dev->dev, "cg2900_register_trans_driver: Missing "
+ "platform data\n");
+ return -EINVAL;
+ }
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(dev->dev, "Couldn't allocate info\n");
+ return -ENOMEM;
+ }
+
+ if (pf_data->init) {
+ err = pf_data->init(dev);
+ if (err) {
+ dev_err(dev->dev, "Platform init failed (%d)\n", err);
+ goto error_handling;
+ }
+ }
+
+ info->chip_dev = dev;
+ dev->prv_data = info;
+
+ info->wq = create_singlethread_workqueue(CORE_WQ_NAME);
+ if (!info->wq) {
+ dev_err(dev->dev, "Could not create workqueue\n");
+ err = -ENOMEM;
+ goto error_handling_exit;
+ }
+
+ dev_info(dev->dev, "Transport connected\n");
+
+ INIT_WORK(&info->work, work_hw_registered);
+ if (!queue_work(info->wq, &info->work)) {
+ dev_err(dev->dev, "Failed to queue work_hw_registered because "
+ "it's already in the queue\n");
+ err = -EACCES;
+ goto error_handling_wq;
+ }
+
+ return 0;
+
+error_handling_wq:
+ destroy_workqueue(info->wq);
+error_handling_exit:
+ if (pf_data->exit)
+ pf_data->exit(dev);
+error_handling:
+ kfree(info);
+ return err;
+}
+EXPORT_SYMBOL_GPL(cg2900_register_trans_driver);
+
+/**
+ * cg2900_deregister_trans_driver() - Deregister a transport driver.
+ * @dev: Transport device.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EINVAL if NULL is supplied as @cb.
+ * -ENOMEM if allocation fails or work queue can't be created.
+ */
+int cg2900_deregister_trans_driver(struct cg2900_chip_dev *dev)
+{
+ struct cg2900_platform_data *pf_data;
+ struct core_info *info = dev->prv_data;
+
+ BUG_ON(!main_info);
+
+ dev_dbg(dev->dev, "cg2900_deregister_trans_driver\n");
+
+ if (dev->c_cb.chip_removed)
+ dev->c_cb.chip_removed(dev);
+
+ destroy_workqueue(info->wq);
+
+ dev->prv_data = NULL;
+ kfree(info);
+
+ dev_info(dev->dev, "Transport disconnected\n");
+
+ pf_data = dev_get_platdata(dev->dev);
+ if (!pf_data) {
+ dev_err(dev->dev, "Missing platform data\n");
+ return -EINVAL;
+ }
+
+ if (pf_data->exit)
+ pf_data->exit(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cg2900_deregister_trans_driver);
+
+/**
+ * cg2900_get_sleep_timeout() - Return sleep timeout in jiffies.
+ *
+ * Returns:
+ * Sleep timeout in jiffies. 0 means that sleep timeout shall not be used.
+ */
+unsigned long cg2900_get_sleep_timeout(void)
+{
+ if (!sleep_timeout_ms)
+ return 0;
+
+ return msecs_to_jiffies(sleep_timeout_ms);
+}
+EXPORT_SYMBOL_GPL(cg2900_get_sleep_timeout);
+
+/**
+ * cg2900_probe() - Initialize module.
+ *
+ * @pdev: Platform device.
+ *
+ * This function initialize the transport and CG2900 Core, then
+ * register to the transport framework.
+ *
+ * Returns:
+ * 0 if success.
+ * -ENOMEM for failed alloc or structure creation.
+ */
+static int __devinit cg2900_probe(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "cg2900_probe\n");
+
+ main_info = kzalloc(sizeof(*main_info), GFP_KERNEL);
+ if (!main_info) {
+ dev_err(&pdev->dev, "Couldn't allocate main_info\n");
+ return -ENOMEM;
+ }
+
+ main_info->dev = &pdev->dev;
+ mutex_init(&main_info->man_mutex);
+ INIT_LIST_HEAD(&main_info->chip_handlers);
+ init_waitqueue_head(&main_info->wq);
+
+ dev_info(&pdev->dev, "CG2900 Core driver started\n");
+
+ return 0;
+}
+
+/**
+ * cg2900_remove() - Remove module.
+ *
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if success.
+ * -ENOMEM if core_info does not exist.
+ * -EINVAL if platform data does not exist in the device.
+ */
+static int __devexit cg2900_remove(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "cg2900_remove\n");
+
+ kfree(main_info);
+ main_info = NULL;
+
+ dev_info(&pdev->dev, "CG2900 Core driver removed\n");
+
+ return 0;
+}
+
+static struct platform_driver cg2900_driver = {
+ .driver = {
+ .name = "cg2900",
+ .owner = THIS_MODULE,
+ },
+ .probe = cg2900_probe,
+ .remove = __devexit_p(cg2900_remove),
+};
+
+/**
+ * cg2900_init() - Initialize module.
+ *
+ * Registers platform driver.
+ */
+static int __init cg2900_init(void)
+{
+ pr_debug("cg2900_init");
+ return platform_driver_register(&cg2900_driver);
+}
+
+/**
+ * cg2900_exit() - Remove module.
+ *
+ * Unregisters platform driver.
+ */
+static void __exit cg2900_exit(void)
+{
+ pr_debug("cg2900_exit");
+ platform_driver_unregister(&cg2900_driver);
+}
+
+module_init(cg2900_init);
+module_exit(cg2900_exit);
+
+module_param(sleep_timeout_ms, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(sleep_timeout_ms,
+ "Sleep timeout for data transmissions:\n"
+ "\tDefault 10000 ms\n"
+ "\t0 = disable\n"
+ "\t>0 = sleep timeout in milliseconds");
+
+module_param_array(bd_address, byte, &bd_addr_count,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(bd_address,
+ "Bluetooth Device address. "
+ "Default 0x00 0x80 0xDE 0xAD 0xBE 0xEF. "
+ "Enter as comma separated value.");
+
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Linux Bluetooth HCI H:4 CG2900 Connectivity Device Driver");
diff --git a/drivers/mfd/cg2900/cg2900_core.h b/drivers/mfd/cg2900/cg2900_core.h
new file mode 100644
index 00000000000..bdd951a501d
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_core.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * Henrik Possung (henrik.possung@stericsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@stericsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson CG2900 GPS/BT/FM controller.
+ */
+
+#ifndef _CG2900_CORE_H_
+#define _CG2900_CORE_H_
+
+#include <linux/device.h>
+#include <linux/skbuff.h>
+
+/* Reserve 1 byte for the HCI H:4 header */
+#define HCI_H4_SIZE 1
+#define CG2900_SKB_RESERVE HCI_H4_SIZE
+
+/* Number of bytes to reserve at start of sk_buffer when receiving packet */
+#define RX_SKB_RESERVE 8
+
+#define BT_BDADDR_SIZE 6
+
+/* Standardized Bluetooth H:4 channels */
+#define HCI_BT_CMD_H4_CHANNEL 0x01
+#define HCI_BT_ACL_H4_CHANNEL 0x02
+#define HCI_BT_SCO_H4_CHANNEL 0x03
+#define HCI_BT_EVT_H4_CHANNEL 0x04
+
+/* Default H4 channels which may change depending on connected controller */
+#define HCI_FM_RADIO_H4_CHANNEL 0x08
+#define HCI_GNSS_H4_CHANNEL 0x09
+
+/* Bluetooth error codes */
+#define HCI_BT_ERROR_NO_ERROR 0x00
+
+/* Bluetooth lengths */
+#define HCI_BT_SEND_FILE_MAX_CHUNK_SIZE 254
+
+#define LOGGER_DIRECTION_TX 0
+#define LOGGER_DIRECTION_RX 1
+
+/* module_param declared in cg2900_core.c */
+extern u8 bd_address[BT_BDADDR_SIZE];
+
+#endif /* _CG2900_CORE_H_ */
diff --git a/drivers/mfd/cg2900/cg2900_lib.c b/drivers/mfd/cg2900/cg2900_lib.c
new file mode 100644
index 00000000000..3b739235dd3
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_lib.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson CG2900 GPS/BT/FM controller.
+ */
+#define NAME "cg2900_lib"
+#define pr_fmt(fmt) NAME ": " fmt "\n"
+
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/mfd/cg2900.h>
+
+#include "cg2900_chip.h"
+#include "cg2900_core.h"
+#include "cg2900_lib.h"
+
+/*
+ * Max length in bytes for line buffer used to parse settings and patch file.
+ * Must be max length of name plus characters used to define chip version.
+ */
+#define LINE_BUFFER_LENGTH (NAME_MAX + 30)
+#define LOGGER_HEADER_SIZE 1
+/**
+ * cg2900_tx_to_chip() - Transmit buffer to the transport.
+ * @user: User data for BT command channel.
+ * @logger: User data for logger channel.
+ * @skb: Data packet.
+ *
+ * The transmit_skb_to_chip() function transmit buffer to the transport.
+ * If enabled, copy the transmitted data to the HCI logger as well.
+ */
+void cg2900_tx_to_chip(struct cg2900_user_data *user,
+ struct cg2900_user_data *logger, struct sk_buff *skb)
+{
+ int err;
+ struct cg2900_chip_dev *chip_dev;
+
+ dev_dbg(user->dev, "cg2900_tx_to_chip %d bytes.\n", skb->len);
+
+ if (logger)
+ cg2900_send_to_hci_logger(logger, skb, LOGGER_DIRECTION_TX);
+
+ chip_dev = cg2900_get_prv(user);
+ err = chip_dev->t_cb.write(chip_dev, skb);
+ if (err) {
+ dev_err(user->dev, "cg2900_tx_to_chip: Transport write failed "
+ "(%d)\n", err);
+ kfree_skb(skb);
+ }
+}
+EXPORT_SYMBOL_GPL(cg2900_tx_to_chip);
+
+/**
+ * cg2900_tx_no_user() - Transmit buffer to the transport.
+ * @dev: Current chip to transmit to.
+ * @skb: Data packet.
+ *
+ * This function transmits buffer to the transport when no user exist (system
+ * startup for example).
+ */
+void cg2900_tx_no_user(struct cg2900_chip_dev *dev, struct sk_buff *skb)
+{
+ int err;
+
+ dev_dbg(dev->dev, "cg2900_tx_no_user %d bytes.\n", skb->len);
+
+ err = dev->t_cb.write(dev, skb);
+ if (err) {
+ dev_err(dev->dev, "cg2900_tx_no_user: Transport write failed "
+ "(%d)\n", err);
+ kfree_skb(skb);
+ }
+}
+EXPORT_SYMBOL_GPL(cg2900_tx_no_user);
+
+/**
+ * create_and_send_bt_cmd() - Copy and send sk_buffer.
+ * @user: User data for current channel.
+ * @logger: User data for logger channel.
+ * @data: Data to send.
+ * @length: Length in bytes of data.
+ *
+ * The create_and_send_bt_cmd() function allocate sk_buffer, copy supplied data
+ * to it, and send the sk_buffer to controller.
+ */
+void cg2900_send_bt_cmd(struct cg2900_user_data *user,
+ struct cg2900_user_data *logger,
+ void *data, int length)
+{
+ struct sk_buff *skb;
+
+ skb = user->alloc_skb(length, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(user->dev, "cg2900_send_bt_cmd: Couldn't alloc "
+ "sk_buff with length %d\n", length);
+ return;
+ }
+
+ memcpy(skb_put(skb, length), data, length);
+ skb_push(skb, HCI_H4_SIZE);
+ skb->data[0] = HCI_BT_CMD_H4_CHANNEL;
+
+ cg2900_tx_to_chip(user, logger, skb);
+}
+EXPORT_SYMBOL_GPL(cg2900_send_bt_cmd);
+
+/**
+ * cg2900_send_bt_cmd_no_user() - Copy and send sk_buffer with no assigned user.
+ * @dev: Current chip to transmit to.
+ * @data: Data to send.
+ * @length: Length in bytes of data.
+ *
+ * The cg2900_send_bt_cmd_no_user() function allocate sk_buffer, copy supplied
+ * data to it, and send the sk_buffer to controller.
+ */
+void cg2900_send_bt_cmd_no_user(struct cg2900_chip_dev *dev, void *data,
+ int length)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(length + HCI_H4_SIZE, GFP_KERNEL);
+ if (!skb) {
+ dev_err(dev->dev, "cg2900_send_bt_cmd_no_user: Couldn't alloc "
+ "sk_buff with length %d\n", length);
+ return;
+ }
+
+ skb_reserve(skb, HCI_H4_SIZE);
+ memcpy(skb_put(skb, length), data, length);
+ skb_push(skb, HCI_H4_SIZE);
+ skb->data[0] = HCI_BT_CMD_H4_CHANNEL;
+
+ cg2900_tx_no_user(dev, skb);
+}
+EXPORT_SYMBOL_GPL(cg2900_send_bt_cmd_no_user);
+
+/**
+ * create_work_item() - Create work item and add it to the work queue.
+ * @wq: Work queue.
+ * @work_func: Work function.
+ * @user_data: Arbitrary data set by user.
+ *
+ * The create_work_item() function creates work item and add it to
+ * the work queue.
+ * Note that work is allocated by kmalloc and work must be freed when work
+ * function is started.
+ */
+void cg2900_create_work_item(struct workqueue_struct *wq, work_func_t work_func,
+ void *user_data)
+{
+ struct cg2900_work *new_work;
+ int err;
+
+ new_work = kmalloc(sizeof(*new_work), GFP_ATOMIC);
+ if (!new_work) {
+ pr_err("Failed to alloc memory for new_work");
+ return;
+ }
+
+ INIT_WORK(&new_work->work, work_func);
+ new_work->user_data = user_data;
+
+ err = queue_work(wq, &new_work->work);
+ if (!err) {
+ pr_err("Failed to queue work_struct because it's already "
+ "in the queue");
+ kfree(new_work);
+ }
+}
+EXPORT_SYMBOL_GPL(cg2900_create_work_item);
+
+/**
+ * read_and_send_file_part() - Transmit a part of the supplied file.
+ * @user: User data for current channel.
+ * @logger: User data for logger channel.
+ * @info: File information.
+ *
+ * The cg2900_read_and_send_file_part() function transmit a part of the supplied
+ * file to the controller.
+ *
+ * Returns:
+ * 0 if there is no more data in the file.
+ * >0 for number of bytes sent.
+ * -ENOMEM if skb allocation failed.
+ */
+int cg2900_read_and_send_file_part(struct cg2900_user_data *user,
+ struct cg2900_user_data *logger,
+ struct cg2900_file_info *info)
+{
+ int bytes_to_copy;
+ struct sk_buff *skb;
+ struct bt_vs_write_file_block_cmd *cmd;
+ int plen;
+
+ /*
+ * Calculate number of bytes to copy;
+ * either max bytes for HCI packet or number of bytes left in file
+ */
+ bytes_to_copy = min((int)HCI_BT_SEND_FILE_MAX_CHUNK_SIZE,
+ (int)(info->fw_file->size - info->file_offset));
+
+ if (bytes_to_copy <= 0) {
+ /* Nothing more to read in file. */
+ dev_dbg(user->dev, "File download finished\n");
+ info->chunk_id = 0;
+ info->file_offset = 0;
+ return 0;
+ }
+
+ /* There is more data to send */
+ plen = sizeof(*cmd) + bytes_to_copy;
+ skb = user->alloc_skb(plen, GFP_KERNEL);
+ if (!skb) {
+ dev_err(user->dev, "Couldn't allocate sk_buffer\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, plen);
+
+ cmd = (struct bt_vs_write_file_block_cmd *)skb->data;
+ cmd->opcode = cpu_to_le16(CG2900_BT_OP_VS_WRITE_FILE_BLOCK);
+ cmd->plen = BT_PARAM_LEN(plen);
+ cmd->id = info->chunk_id;
+ info->chunk_id++;
+
+ /* Copy the data from offset position */
+ memcpy(cmd->data,
+ &(info->fw_file->data[info->file_offset]),
+ bytes_to_copy);
+
+ /* Increase offset with number of bytes copied */
+ info->file_offset += bytes_to_copy;
+
+ skb_push(skb, CG2900_SKB_RESERVE);
+ skb->data[0] = HCI_BT_CMD_H4_CHANNEL;
+
+ cg2900_tx_to_chip(user, logger, skb);
+
+ return bytes_to_copy;
+}
+EXPORT_SYMBOL_GPL(cg2900_read_and_send_file_part);
+
+void cg2900_send_to_hci_logger(struct cg2900_user_data *logger,
+ struct sk_buff *skb,
+ u8 direction)
+{
+ struct sk_buff *skb_log;
+ u8 *p;
+
+ /*
+ * Alloc a new sk_buff and copy the data into it. Then send it to
+ * the HCI logger.
+ */
+ skb_log = alloc_skb(skb->len + LOGGER_HEADER_SIZE, GFP_NOWAIT);
+ if (!skb_log) {
+ pr_err("cg2900_send_to_hci_logger:\
+ Couldn't allocate skb_log\n");
+ return;
+ }
+ /* Reserve 1 byte for direction.*/
+ skb_reserve(skb_log, LOGGER_HEADER_SIZE);
+
+ memcpy(skb_put(skb_log, skb->len), skb->data, skb->len);
+ p = skb_push(skb_log, LOGGER_HEADER_SIZE);
+ *p = (u8) direction;
+
+ if (logger->read_cb)
+ logger->read_cb(logger, skb_log);
+ else
+ kfree_skb(skb_log);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(cg2900_send_to_hci_logger);
+
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Linux CG2900 Library functions");
diff --git a/drivers/mfd/cg2900/cg2900_lib.h b/drivers/mfd/cg2900/cg2900_lib.h
new file mode 100644
index 00000000000..37b08556221
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_lib.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson CG2900 GPS/BT/FM controller.
+ */
+
+#ifndef _CG2900_LIB_H_
+#define _CG2900_LIB_H_
+
+#include <linux/firmware.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/cg2900.h>
+
+/**
+ * struct cg2900_work - Generic work structure.
+ * @work: Work structure.
+ * @user_data: Arbitrary data set by user.
+ */
+struct cg2900_work {
+ struct work_struct work;
+ void *user_data;
+};
+
+/**
+ * struct cg2900_file_info - Info structure for file to download.
+ * @fw_file: Stores firmware file.
+ * @file_offset: Current read offset in firmware file.
+ * @chunk_id: Stores current chunk ID of write file
+ * operations.
+ */
+struct cg2900_file_info {
+ const struct firmware *fw_file;
+ int file_offset;
+ u8 chunk_id;
+};
+
+extern void cg2900_tx_to_chip(struct cg2900_user_data *user,
+ struct cg2900_user_data *logger,
+ struct sk_buff *skb);
+extern void cg2900_tx_no_user(struct cg2900_chip_dev *dev, struct sk_buff *skb);
+extern void cg2900_send_bt_cmd(struct cg2900_user_data *user,
+ struct cg2900_user_data *logger,
+ void *data, int length);
+extern void cg2900_send_bt_cmd_no_user(struct cg2900_chip_dev *dev, void *data,
+ int length);
+extern void cg2900_create_work_item(struct workqueue_struct *wq,
+ work_func_t work_func,
+ void *user_data);
+extern int cg2900_read_and_send_file_part(struct cg2900_user_data *user,
+ struct cg2900_user_data *logger,
+ struct cg2900_file_info *info);
+extern void cg2900_send_to_hci_logger(struct cg2900_user_data *logger,
+ struct sk_buff *skb,
+ u8 direction);
+
+#endif /* _CG2900_LIB_H_ */
diff --git a/drivers/mfd/cg2900/cg2900_test.c b/drivers/mfd/cg2900/cg2900_test.c
new file mode 100644
index 00000000000..3678a29b249
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_test.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Driver for ST-Ericsson CG2900 test character device.
+ */
+#define NAME "cg2900_test"
+#define pr_fmt(fmt) NAME ": " fmt "\n"
+
+#include <asm/byteorder.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+#include <linux/mfd/cg2900.h>
+
+#include "cg2900_core.h"
+
+#define MISC_DEV (info->misc_dev.this_device)
+
+/* Device names */
+#define CG2900_CDEV_NAME "cg2900_core_test"
+
+/**
+ * struct test_info - Main info structure for CG2900 test char device.
+ * @misc_dev: Registered Misc Device.
+ * @rx_queue: RX data queue.
+ * @dev: Device structure for STE Connectivity driver.
+ * @pdev: Platform device structure for STE Connectivity driver.
+ */
+struct test_info {
+ struct miscdevice misc_dev;
+ struct sk_buff_head rx_queue;
+ struct device *dev;
+ struct platform_device *pdev;
+};
+
+static struct test_info *test_info;
+
+/*
+ * main_wait_queue - Char device Wait Queue in CG2900 Core.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(char_wait_queue);
+
+/**
+ * tx_to_char_dev() - Handle data received from CG2900 Core.
+ * @dev: Current chip device information.
+ * @skb: Buffer with data coming form device.
+ */
+static int tx_to_char_dev(struct cg2900_chip_dev *dev, struct sk_buff *skb)
+{
+ struct test_info *info = dev->t_data;
+ skb_queue_tail(&info->rx_queue, skb);
+ wake_up_interruptible_all(&char_wait_queue);
+ return 0;
+}
+
+/**
+ * cg2900_test_open() - User space char device has been opened.
+ * @inode: Device driver information.
+ * @filp: Pointer to the file struct.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EACCES if transport already exists.
+ * -ENOMEM if allocation fails.
+ * Errors from create_work_item.
+ */
+static int cg2900_test_open(struct inode *inode, struct file *filp)
+{
+ struct test_info *info = test_info;
+ struct cg2900_chip_dev *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ dev_err(MISC_DEV, "Cannot allocate test_dev\n");
+ return -ENOMEM;
+ }
+ dev->dev = info->dev;
+ dev->pdev = info->pdev;
+ dev->t_data = info;
+ dev->t_cb.write = tx_to_char_dev;
+ filp->private_data = dev;
+
+ dev_info(MISC_DEV, "CG2900 test char dev opened\n");
+ return cg2900_register_trans_driver(dev);
+}
+
+/**
+ * cg2900_test_release() - User space char device has been closed.
+ * @inode: Device driver information.
+ * @filp: Pointer to the file struct.
+ *
+ * Returns:
+ * 0 if there is no error.
+ */
+static int cg2900_test_release(struct inode *inode, struct file *filp)
+{
+ struct cg2900_chip_dev *dev = filp->private_data;
+ struct test_info *info = dev->t_data;
+
+ dev_info(MISC_DEV, "CG2900 test char dev closed\n");
+ skb_queue_purge(&info->rx_queue);
+ cg2900_deregister_trans_driver(dev);
+ kfree(dev);
+
+ return 0;
+}
+
+/**
+ * cg2900_test_read() - Queue and copy buffer to user space char device.
+ * @filp: Pointer to the file struct.
+ * @buf: Received buffer.
+ * @count: Count of received data in bytes.
+ * @f_pos: Position in buffer.
+ *
+ * Returns:
+ * >= 0 is number of bytes read.
+ * -EFAULT if copy_to_user fails.
+ */
+static ssize_t cg2900_test_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct sk_buff *skb;
+ int bytes_to_copy;
+ int err;
+ struct cg2900_chip_dev *dev = filp->private_data;
+ struct test_info *info = dev->t_data;
+ struct sk_buff_head *rx_queue = &info->rx_queue;
+
+ dev_dbg(MISC_DEV, "cg2900_test_read count %d\n", count);
+
+ if (skb_queue_empty(rx_queue))
+ wait_event_interruptible(char_wait_queue,
+ !(skb_queue_empty(rx_queue)));
+
+ skb = skb_dequeue(rx_queue);
+ if (!skb) {
+ dev_dbg(MISC_DEV,
+ "skb queue is empty - return with zero bytes\n");
+ bytes_to_copy = 0;
+ goto finished;
+ }
+
+ bytes_to_copy = min(count, skb->len);
+ err = copy_to_user(buf, skb->data, bytes_to_copy);
+ if (err) {
+ skb_queue_head(rx_queue, skb);
+ return -EFAULT;
+ }
+
+ skb_pull(skb, bytes_to_copy);
+
+ if (skb->len > 0)
+ skb_queue_head(rx_queue, skb);
+ else
+ kfree_skb(skb);
+
+finished:
+ return bytes_to_copy;
+}
+
+/**
+ * cg2900_test_write() - Copy buffer from user and write to CG2900 Core.
+ * @filp: Pointer to the file struct.
+ * @buf: Read buffer.
+ * @count: Size of the buffer write.
+ * @f_pos: Position in buffer.
+ *
+ * Returns:
+ * >= 0 is number of bytes written.
+ * -EFAULT if copy_from_user fails.
+ */
+static ssize_t cg2900_test_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct sk_buff *skb;
+ struct cg2900_chip_dev *dev = filp->private_data;
+ struct test_info *info = dev->t_data;
+
+ dev_dbg(MISC_DEV, "cg2900_test_write count %d\n", count);
+
+ /* Allocate the SKB and reserve space for the header */
+ skb = alloc_skb(count + RX_SKB_RESERVE, GFP_KERNEL);
+ if (!skb) {
+ dev_err(MISC_DEV, "cg2900_test_write: Failed to alloc skb\n");
+ return -ENOMEM;
+ }
+ skb_reserve(skb, RX_SKB_RESERVE);
+
+ if (copy_from_user(skb_put(skb, count), buf, count)) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+
+ dev->c_cb.data_from_chip(dev, skb);
+
+ return count;
+}
+
+/**
+ * cg2900_test_poll() - Handle POLL call to the interface.
+ * @filp: Pointer to the file struct.
+ * @wait: Poll table supplied to caller.
+ *
+ * Returns:
+ * Mask of current set POLL values (0 or (POLLIN | POLLRDNORM))
+ */
+static unsigned int cg2900_test_poll(struct file *filp, poll_table *wait)
+{
+ struct cg2900_chip_dev *dev = filp->private_data;
+ struct test_info *info = dev->t_data;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &char_wait_queue, wait);
+
+ if (!(skb_queue_empty(&info->rx_queue)))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+static const struct file_operations test_char_dev_fops = {
+ .open = cg2900_test_open,
+ .release = cg2900_test_release,
+ .read = cg2900_test_read,
+ .write = cg2900_test_write,
+ .poll = cg2900_test_poll
+};
+
+/**
+ * test_char_dev_create() - Create a char device for testing.
+ * @info: Test device info.
+ *
+ * Creates a separate char device that will interact directly with userspace
+ * test application.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Error codes from misc_register.
+ */
+static int test_char_dev_create(struct test_info *info)
+{
+ int err;
+
+ /* Initialize the RX queue */
+ skb_queue_head_init(&info->rx_queue);
+
+ /* Prepare miscdevice struct before registering the device */
+ info->misc_dev.minor = MISC_DYNAMIC_MINOR;
+ info->misc_dev.name = CG2900_CDEV_NAME;
+ info->misc_dev.fops = &test_char_dev_fops;
+ info->misc_dev.parent = info->dev;
+ info->misc_dev.mode = S_IRUGO | S_IWUGO;
+
+ err = misc_register(&info->misc_dev);
+ if (err) {
+ dev_err(info->dev, "Error %d registering misc dev", err);
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * test_char_dev_destroy() - Clean up after test_char_dev_create().
+ * @info: Test device info.
+ */
+static void test_char_dev_destroy(struct test_info *info)
+{
+ int err;
+
+ err = misc_deregister(&info->misc_dev);
+ if (err)
+ dev_err(info->dev, "Error %d deregistering misc dev\n", err);
+
+ /* Clean the message queue */
+ skb_queue_purge(&info->rx_queue);
+}
+
+/**
+ * cg2900_test_probe() - Initialize module.
+ *
+ * @pdev: Platform device.
+ *
+ * This function initializes and registers the test misc char device.
+ *
+ * Returns:
+ * 0 if success.
+ * -ENOMEM for failed alloc or structure creation.
+ * -EEXIST if device already exists.
+ * Error codes generated by test_char_dev_create.
+ */
+static int __devinit cg2900_test_probe(struct platform_device *pdev)
+{
+ int err;
+
+ dev_dbg(&pdev->dev, "cg2900_test_probe\n");
+
+ if (test_info) {
+ dev_err(&pdev->dev, "test_info exists\n");
+ return -EEXIST;
+ }
+
+ test_info = kzalloc(sizeof(*test_info), GFP_KERNEL);
+ if (!test_info) {
+ dev_err(&pdev->dev, "Couldn't allocate test_info\n");
+ return -ENOMEM;
+ }
+
+ test_info->dev = &pdev->dev;
+ test_info->pdev = pdev;
+
+ /* Create and add test char device. */
+ err = test_char_dev_create(test_info);
+ if (err) {
+ kfree(test_info);
+ test_info = NULL;
+ return err;
+ }
+
+ dev_set_drvdata(&pdev->dev, test_info);
+
+ dev_info(&pdev->dev, "CG2900 test char device driver started\n");
+
+ return 0;
+}
+
+/**
+ * cg2900_test_remove() - Remove module.
+ *
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if success.
+ * -ENOMEM if core_info does not exist.
+ * -EINVAL if platform data does not exist in the device.
+ */
+static int __devexit cg2900_test_remove(struct platform_device *pdev)
+{
+ struct test_info *test_info;
+
+ dev_dbg(&pdev->dev, "cg2900_test_remove\n");
+ test_info = dev_get_drvdata(&pdev->dev);
+ test_char_dev_destroy(test_info);
+ dev_set_drvdata(&pdev->dev, NULL);
+ kfree(test_info);
+ test_info = NULL;
+ dev_info(&pdev->dev, "CG2900 Test char device driver removed\n");
+ return 0;
+}
+
+static struct platform_driver cg2900_test_driver = {
+ .driver = {
+ .name = "cg2900-test",
+ .owner = THIS_MODULE,
+ },
+ .probe = cg2900_test_probe,
+ .remove = __devexit_p(cg2900_test_remove),
+};
+
+/**
+ * cg2900_test_init() - Initialize module.
+ *
+ * Registers platform driver.
+ */
+static int __init cg2900_test_init(void)
+{
+ pr_debug("cg2900_test_init");
+ return platform_driver_register(&cg2900_test_driver);
+}
+
+/**
+ * cg2900_test_exit() - Remove module.
+ *
+ * Unregisters platform driver.
+ */
+static void __exit cg2900_test_exit(void)
+{
+ pr_debug("cg2900_test_exit");
+ platform_driver_unregister(&cg2900_test_driver);
+}
+
+module_init(cg2900_test_init);
+module_exit(cg2900_test_exit);
+
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Linux CG2900 Test Char Device Driver");
diff --git a/drivers/mfd/cg2900/stlc2690_chip.c b/drivers/mfd/cg2900/stlc2690_chip.c
new file mode 100644
index 00000000000..4acf92c53a8
--- /dev/null
+++ b/drivers/mfd/cg2900/stlc2690_chip.c
@@ -0,0 +1,1653 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * Henrik Possung (henrik.possung@stericsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@stericsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson STLC2690 BT/FM controller.
+ */
+#define NAME "stlc2690_chip"
+#define pr_fmt(fmt) NAME ": " fmt "\n"
+
+#include <asm/byteorder.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/stat.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/cg2900.h>
+#include <linux/mfd/core.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+
+#include "cg2900_core.h"
+#include "cg2900_lib.h"
+#include "stlc2690_chip.h"
+
+#define MAIN_DEV (main_info->dev)
+#define BOOT_DEV (info->user_in_charge->dev)
+
+#define WQ_NAME "stlc2690_chip_wq"
+
+#define LINE_TOGGLE_DETECT_TIMEOUT 50 /* ms */
+#define CHIP_READY_TIMEOUT 100 /* ms */
+#define CHIP_STARTUP_TIMEOUT 15000 /* ms */
+#define CHIP_SHUTDOWN_TIMEOUT 15000 /* ms */
+
+/** CHANNEL_BT_CMD - Bluetooth HCI H:4 channel
+ * for Bluetooth commands in the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_BT_CMD 0x01
+
+/** CHANNEL_BT_ACL - Bluetooth HCI H:4 channel
+ * for Bluetooth ACL data in the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_BT_ACL 0x02
+
+/** CHANNEL_BT_EVT - Bluetooth HCI H:4 channel
+ * for Bluetooth events in the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_BT_EVT 0x04
+
+/** CHANNEL_HCI_LOGGER - Bluetooth HCI H:4 channel
+ * for logging all transmitted H4 packets (on all channels).
+ */
+#define CHANNEL_HCI_LOGGER 0xFA
+
+/** CHANNEL_CORE - Bluetooth HCI H:4 channel
+ * for user space control of the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_CORE 0xFD
+
+/*
+ * For the char dev names we keep the same names in order to be able to reuse
+ * the users and to keep a consistent interface.
+ */
+
+/** STLC2690_BT_CMD - Bluetooth HCI H4 channel for Bluetooth commands.
+ */
+#define STLC2690_BT_CMD "cg2900_bt_cmd"
+
+/** STLC2690_BT_ACL - Bluetooth HCI H4 channel for Bluetooth ACL data.
+ */
+#define STLC2690_BT_ACL "cg2900_bt_acl"
+
+/** STLC2690_BT_EVT - Bluetooth HCI H4 channel for Bluetooth events.
+ */
+#define STLC2690_BT_EVT "cg2900_bt_evt"
+
+/** STLC2690_HCI_LOGGER - BT channel for logging all transmitted H4 packets.
+ * Data read is copy of all data transferred on the other channels.
+ * Only write allowed is configuration of the HCI Logger.
+ */
+#define STLC2690_HCI_LOGGER "cg2900_hci_logger"
+
+/** STLC2690_CORE- Channel for keeping ST-Ericsson STLC2690 enabled.
+ * Opening this channel forces the chip to stay powered.
+ * No data can be written to or read from this channel.
+ */
+#define STLC2690_CORE "cg2900_core"
+
+/**
+ * enum main_state - Main-state for STLC2690 driver.
+ * @STLC2690_INIT: STLC2690 initializing.
+ * @STLC2690_IDLE: No user registered to STLC2690 driver.
+ * @STLC2690_BOOTING: STLC2690 booting after first user is registered.
+ * @STLC2690_CLOSING: STLC2690 closing after last user has deregistered.
+ * @STLC2690_RESETING: STLC2690 reset requested.
+ * @STLC2690_ACTIVE: STLC2690 up and running with at least one user.
+ */
+enum main_state {
+ STLC2690_INIT,
+ STLC2690_IDLE,
+ STLC2690_BOOTING,
+ STLC2690_CLOSING,
+ STLC2690_RESETING,
+ STLC2690_ACTIVE
+};
+
+/**
+ * enum boot_state - BOOT-state for STLC2690 chip driver.
+ * @BOOT_RESET: HCI Reset has been sent.
+ * @BOOT_SEND_BD_ADDRESS: VS Store In FS command with BD address
+ * has been sent.
+ * @BOOT_GET_FILES_TO_LOAD: STLC2690 chip driver is retrieving file
+ * to load.
+ * @BOOT_DOWNLOAD_PATCH: STLC2690 chip driver is downloading
+ * patches.
+ * @BOOT_ACTIVATE_PATCHES_AND_SETTINGS: STLC2690 chip driver is activating
+ * patches and settings.
+ * @BOOT_READY: STLC2690 chip driver boot is ready.
+ * @BOOT_FAILED: STLC2690 chip driver boot failed.
+ */
+enum boot_state {
+ BOOT_RESET,
+ BOOT_SEND_BD_ADDRESS,
+ BOOT_GET_FILES_TO_LOAD,
+ BOOT_DOWNLOAD_PATCH,
+ BOOT_ACTIVATE_PATCHES_AND_SETTINGS,
+ BOOT_READY,
+ BOOT_FAILED
+};
+
+/**
+ * enum file_load_state - BOOT_FILE_LOAD-state for STLC2690 chip driver.
+ * @FILE_LOAD_GET_PATCH: Loading patches.
+ * @FILE_LOAD_GET_STATIC_SETTINGS: Loading static settings.
+ * @FILE_LOAD_NO_MORE_FILES: No more files to load.
+ * @FILE_LOAD_FAILED: File loading failed.
+ */
+enum file_load_state {
+ FILE_LOAD_GET_PATCH,
+ FILE_LOAD_GET_STATIC_SETTINGS,
+ FILE_LOAD_NO_MORE_FILES,
+ FILE_LOAD_FAILED
+};
+
+/**
+ * enum download_state - BOOT_DOWNLOAD state.
+ * @DOWNLOAD_PENDING: Download in progress.
+ * @DOWNLOAD_SUCCESS: Download successfully finished.
+ * @DOWNLOAD_FAILED: Downloading failed.
+ */
+enum download_state {
+ DOWNLOAD_PENDING,
+ DOWNLOAD_SUCCESS,
+ DOWNLOAD_FAILED
+};
+
+
+/**
+ * struct stlc2690_channel_item - List object for channel.
+ * @list: list_head struct.
+ * @user: User for this channel.
+ */
+struct stlc2690_channel_item {
+ struct list_head list;
+ struct cg2900_user_data *user;
+};
+
+/**
+ * struct stlc2690_skb_data - Structure for storing private data in an sk_buffer.
+ * @dev: STLC2690 device for this sk_buffer.
+ */
+struct stlc2690_skb_data {
+ struct cg2900_user_data *user;
+};
+#define stlc2690_skb_data(__skb) ((struct stlc2690_skb_data *)((__skb)->cb))
+
+/**
+ * struct stlc2690_chip_info - Main info structure for STLC2690 chip driver.
+ * @patch_file_name: Stores patch file name.
+ * @settings_file_name: Stores settings file name.
+ * @file_info: Firmware file info (patch or settings).
+ * @main_state: Current MAIN-state of STLC2690 chip driver.
+ * @boot_state: Current BOOT-state of STLC2690 chip driver.
+ * @file_load_state: Current BOOT_FILE_LOAD-state of STLC2690 chip
+ * driver.
+ * @download_state: Current BOOT_DOWNLOAD-state of STLC2690 chip
+ * driver.
+ * @wq: STLC2690 chip driver workqueue.
+ * @chip_dev: Chip handler info.
+ * @user_in_charge: User currently operating. Normally used at
+ * channel open and close.
+ * @last_user: Last user of this chip.
+ * @logger: Logger user of this chip.
+ */
+struct stlc2690_chip_info {
+ char *patch_file_name;
+ char *settings_file_name;
+ struct cg2900_file_info file_info;
+ enum main_state main_state;
+ enum boot_state boot_state;
+ enum file_load_state file_load_state;
+ enum download_state download_state;
+ struct workqueue_struct *wq;
+ struct cg2900_chip_dev *chip_dev;
+ spinlock_t rw_lock;
+ struct list_head open_channels;
+ struct cg2900_user_data *user_in_charge;
+ struct cg2900_user_data *last_user;
+ struct cg2900_user_data *logger;
+};
+
+/**
+ * struct main_info - Main info structure for STLC2690 chip driver.
+ * @dev: Device structure.
+ * @cell_base_id: Base ID for MFD cells.
+ * @man_mutex: Management mutex.
+ */
+struct main_info {
+ struct device *dev;
+ int cell_base_id;
+ struct mutex man_mutex;
+};
+
+static struct main_info *main_info;
+
+/*
+ * main_wait_queue - Main Wait Queue in STLC2690 driver.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(main_wait_queue);
+
+static void chip_startup_finished(struct stlc2690_chip_info *info, int err);
+
+/**
+ * send_bd_address() - Send HCI VS command with BD address to the chip.
+ */
+static void send_bd_address(struct stlc2690_chip_info *info)
+{
+ struct bt_vs_store_in_fs_cmd *cmd;
+ u8 plen = sizeof(*cmd) + BT_BDADDR_SIZE;
+
+ cmd = kmalloc(plen, GFP_KERNEL);
+ if (!cmd)
+ return;
+
+ cmd->opcode = cpu_to_le16(STLC2690_BT_OP_VS_STORE_IN_FS);
+ cmd->plen = BT_PARAM_LEN(plen);
+ cmd->user_id = STLC2690_VS_STORE_IN_FS_USR_ID_BD_ADDR;
+ cmd->len = BT_BDADDR_SIZE;
+ /* Now copy the BD address received from user space control app. */
+ memcpy(cmd->data, bd_address, BT_BDADDR_SIZE);
+
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_SEND_BD_ADDRESS\n");
+ info->boot_state = BOOT_SEND_BD_ADDRESS;
+
+ cg2900_send_bt_cmd(info->user_in_charge, info->logger, cmd, plen);
+
+ kfree(cmd);
+}
+
+/**
+ * send_settings_file() - Transmit settings file.
+ *
+ * The send_settings_file() function transmit settings file.
+ * The file is read in parts to fit in HCI packets. When finished,
+ * close the settings file and send HCI reset to activate settings and patches.
+ */
+static void send_settings_file(struct stlc2690_chip_info *info)
+{
+ int bytes_sent;
+
+ bytes_sent = cg2900_read_and_send_file_part(info->user_in_charge,
+ info->logger,
+ &info->file_info);
+ if (bytes_sent > 0) {
+ /* Data sent. Wait for CmdComplete */
+ return;
+ } else if (bytes_sent < 0) {
+ dev_err(BOOT_DEV, "send_settings_file: Error %d occurred\n",
+ bytes_sent);
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ chip_startup_finished(info, bytes_sent);
+ return;
+ }
+
+ /* No data was sent. This file is finished */
+ info->download_state = DOWNLOAD_SUCCESS;
+
+ /* Settings file finished. Release used resources */
+ dev_dbg(BOOT_DEV, "Settings file finished, release used resources\n");
+ release_firmware(info->file_info.fw_file);
+ info->file_info.fw_file = NULL;
+
+ dev_dbg(BOOT_DEV, "New file_load_state: FILE_LOAD_NO_MORE_FILES\n");
+ info->file_load_state = FILE_LOAD_NO_MORE_FILES;
+
+ /* Create and send HCI VS Store In FS command with bd address. */
+ send_bd_address(info);
+}
+
+/**
+ * send_patch_file - Transmit patch file.
+ *
+ * The send_patch_file() function transmit patch file.
+ * The file is read in parts to fit in HCI packets. When the complete file is
+ * transmitted, the file is closed.
+ * When finished, continue with settings file.
+ */
+static void send_patch_file(struct cg2900_chip_dev *dev)
+{
+ int err;
+ int bytes_sent;
+ struct stlc2690_chip_info *info = dev->c_data;
+ int file_name_size = strlen("STLC2690_XXXX_XXXX_settings.fw");
+
+ bytes_sent = cg2900_read_and_send_file_part(info->user_in_charge,
+ info->logger,
+ &info->file_info);
+ if (bytes_sent > 0) {
+ /* Data sent. Wait for CmdComplete */
+ return;
+ } else if (bytes_sent < 0) {
+ dev_err(BOOT_DEV, "send_patch_file: Error %d occurred\n",
+ bytes_sent);
+ err = bytes_sent;
+ goto error_handling;
+ }
+
+ /* No data was sent. This file is finished */
+ info->download_state = DOWNLOAD_SUCCESS;
+
+ dev_dbg(BOOT_DEV, "Patch file finished, release used resources\n");
+ release_firmware(info->file_info.fw_file);
+ info->file_info.fw_file = NULL;
+
+ /*
+ * Create the settings file name from HCI revision and sub_version.
+ * file_name_size does not include terminating NULL character
+ * so add 1.
+ */
+ err = snprintf(info->settings_file_name, file_name_size + 1,
+ "STLC2690_%04X_%04X_settings.fw",
+ dev->chip.hci_revision, dev->chip.hci_sub_version);
+ if (err == file_name_size) {
+ dev_dbg(BOOT_DEV, "Downloading settings file %s\n",
+ info->settings_file_name);
+ } else {
+ dev_err(BOOT_DEV, "Settings file name failed! err=%d\n", err);
+ goto error_handling;
+ }
+
+ /* Retrieve the settings file */
+ err = request_firmware(&info->file_info.fw_file,
+ info->settings_file_name,
+ info->chip_dev->dev);
+ if (err) {
+ dev_err(BOOT_DEV, "Couldn't get settings file (%d)\n", err);
+ goto error_handling;
+ }
+ /* Now send the settings file */
+ dev_dbg(BOOT_DEV,
+ "New file_load_state: FILE_LOAD_GET_STATIC_SETTINGS\n");
+ info->file_load_state = FILE_LOAD_GET_STATIC_SETTINGS;
+ dev_dbg(BOOT_DEV, "New download_state: DOWNLOAD_PENDING\n");
+ info->download_state = DOWNLOAD_PENDING;
+ send_settings_file(info);
+ return;
+
+error_handling:
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ chip_startup_finished(info, err);
+}
+
+/**
+ * work_reset_after_error() - Handle reset.
+ * @work: Reference to work data.
+ *
+ * Handle a reset after received Command Complete event.
+ */
+static void work_reset_after_error(struct work_struct *work)
+{
+ struct cg2900_work *my_work;
+ struct cg2900_chip_dev *dev;
+ struct stlc2690_chip_info *info;
+
+ if (!work) {
+ dev_err(MAIN_DEV, "work_reset_after_error: work == NULL\n");
+ return;
+ }
+
+ my_work = container_of(work, struct cg2900_work, work);
+ dev = my_work->user_data;
+ info = dev->c_data;
+
+ chip_startup_finished(info, -EIO);
+
+ kfree(my_work);
+}
+
+/**
+ * work_load_patch_and_settings() - Start loading patches and settings.
+ * @work: Reference to work data.
+ */
+static void work_load_patch_and_settings(struct work_struct *work)
+{
+ int err = 0;
+ struct cg2900_work *my_work;
+ struct cg2900_chip_dev *dev;
+ struct stlc2690_chip_info *info;
+ int file_name_size = strlen("STLC2690_XXXX_XXXX_patch.fw");
+
+ if (!work) {
+ dev_err(MAIN_DEV,
+ "work_load_patch_and_settings: work == NULL\n");
+ return;
+ }
+
+ my_work = container_of(work, struct cg2900_work, work);
+ dev = my_work->user_data;
+ info = dev->c_data;
+
+ /* Check that we are in the right state */
+ if (info->boot_state != BOOT_GET_FILES_TO_LOAD)
+ goto finished;
+
+ /*
+ * Create the patch file name from HCI revision and sub_version.
+ * file_name_size does not include terminating NULL character
+ * so add 1.
+ */
+ err = snprintf(info->patch_file_name, file_name_size + 1,
+ "STLC2690_%04X_%04X_patch.fw", dev->chip.hci_revision,
+ dev->chip.hci_sub_version);
+ if (err == file_name_size) {
+ dev_dbg(BOOT_DEV, "Downloading patch file %s\n",
+ info->patch_file_name);
+ } else {
+ dev_err(BOOT_DEV, "Patch file name failed! err=%d\n", err);
+ goto error_handling;
+ }
+
+ /* We now all info needed */
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_DOWNLOAD_PATCH\n");
+ info->boot_state = BOOT_DOWNLOAD_PATCH;
+ dev_dbg(BOOT_DEV, "New download_state: DOWNLOAD_PENDING\n");
+ info->download_state = DOWNLOAD_PENDING;
+ dev_dbg(BOOT_DEV, "New file_load_state: FILE_LOAD_GET_PATCH\n");
+ info->file_load_state = FILE_LOAD_GET_PATCH;
+ info->file_info.chunk_id = 0;
+ info->file_info.file_offset = 0;
+ info->file_info.fw_file = NULL;
+
+ /* OK. Now it is time to download the patches */
+ err = request_firmware(&(info->file_info.fw_file),
+ info->patch_file_name,
+ dev->dev);
+ if (err < 0) {
+ dev_err(BOOT_DEV, "Couldn't get patch file (%d)\n", err);
+ goto error_handling;
+ }
+ send_patch_file(dev);
+
+ goto finished;
+
+error_handling:
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ chip_startup_finished(info, -EIO);
+finished:
+ kfree(my_work);
+}
+
+/**
+ * work_cont_file_download() - A file block has been written.
+ * @work: Reference to work data.
+ *
+ * Handle a received HCI VS Write File Block Complete event.
+ * Normally this means continue to send files to the controller.
+ */
+static void work_cont_file_download(struct work_struct *work)
+{
+ struct cg2900_work *my_work;
+ struct cg2900_chip_dev *dev;
+ struct stlc2690_chip_info *info;
+
+ if (!work) {
+ dev_err(MAIN_DEV, "work_cont_file_download: work == NULL\n");
+ return;
+ }
+
+ my_work = container_of(work, struct cg2900_work, work);
+ dev = my_work->user_data;
+ info = dev->c_data;
+
+ /* Continue to send patches or settings to the controller */
+ if (info->file_load_state == FILE_LOAD_GET_PATCH)
+ send_patch_file(dev);
+ else if (info->file_load_state == FILE_LOAD_GET_STATIC_SETTINGS)
+ send_settings_file(info);
+ else
+ dev_dbg(BOOT_DEV, "No more files to load\n");
+
+ kfree(my_work);
+}
+
+/**
+ * handle_reset_cmd_complete() - Handles HCI Reset Command Complete event.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_reset_cmd_complete(struct cg2900_chip_dev *dev, u8 *data)
+{
+ u8 status = data[0];
+ struct stlc2690_chip_info *info = dev->c_data;
+
+ dev_dbg(BOOT_DEV, "Received Reset complete event with status 0x%X\n",
+ status);
+
+ if (BOOT_RESET != info->boot_state &&
+ BOOT_ACTIVATE_PATCHES_AND_SETTINGS != info->boot_state)
+ return false;
+
+ if (HCI_BT_ERROR_NO_ERROR != status) {
+ dev_err(BOOT_DEV, "Command complete for HciReset received with "
+ "error 0x%X\n", status);
+ cg2900_create_work_item(info->wq, work_reset_after_error, dev);
+ return true;
+ }
+
+ if (BOOT_RESET == info->boot_state) {
+ info->boot_state = BOOT_GET_FILES_TO_LOAD;
+ cg2900_create_work_item(info->wq, work_load_patch_and_settings,
+ dev);
+ } else {
+ /*
+ * The boot sequence is now finished successfully.
+ * Set states and signal to waiting thread.
+ */
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_READY\n");
+ info->boot_state = BOOT_READY;
+ chip_startup_finished(info, 0);
+ }
+
+ return true;
+}
+
+
+/**
+ * handle_vs_store_in_fs_cmd_complete() - Handles HCI VS StoreInFS Command Complete event.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_vs_store_in_fs_cmd_complete(struct cg2900_chip_dev *dev,
+ u8 *data)
+{
+ u8 status = data[0];
+ struct stlc2690_chip_info *info = dev->c_data;
+
+ dev_dbg(BOOT_DEV,
+ "Received Store_in_FS complete event with status 0x%X\n",
+ status);
+
+ if (info->boot_state != BOOT_SEND_BD_ADDRESS)
+ return false;
+
+ if (HCI_BT_ERROR_NO_ERROR == status) {
+ struct hci_command_hdr cmd;
+
+ /* Send HCI Reset command to activate patches */
+ dev_dbg(BOOT_DEV,
+ "New boot_state: BOOT_ACTIVATE_PATCHES_AND_SETTINGS\n");
+ info->boot_state = BOOT_ACTIVATE_PATCHES_AND_SETTINGS;
+
+ cmd.opcode = cpu_to_le16(HCI_OP_RESET);
+ cmd.plen = 0; /* No parameters for Reset */
+ cg2900_send_bt_cmd(info->user_in_charge, info->logger, &cmd,
+ sizeof(cmd));
+ } else {
+ dev_err(BOOT_DEV,
+ "Command complete for StoreInFS received with error "
+ "0x%X\n", status);
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ cg2900_create_work_item(info->wq, work_reset_after_error, dev);
+ }
+
+ return true;
+}
+
+/**
+ * handle_vs_write_file_block_cmd_complete() - Handles HCI VS WriteFileBlock Command Complete event.
+ * @data: Pointer to received HCI data packet.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_vs_write_file_block_cmd_complete(struct cg2900_chip_dev *dev,
+ u8 *data)
+{
+ u8 status = data[0];
+ struct stlc2690_chip_info *info = dev->c_data;
+
+ if (info->boot_state != BOOT_DOWNLOAD_PATCH ||
+ info->download_state != DOWNLOAD_PENDING)
+ return false;
+
+ if (HCI_BT_ERROR_NO_ERROR == status)
+ cg2900_create_work_item(info->wq, work_cont_file_download, dev);
+ else {
+ dev_err(BOOT_DEV,
+ "Command complete for WriteFileBlock received with"
+ " error 0x%X\n", status);
+ dev_dbg(BOOT_DEV, "New download_state: DOWNLOAD_FAILED\n");
+ info->download_state = DOWNLOAD_FAILED;
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ if (info->file_info.fw_file) {
+ release_firmware(info->file_info.fw_file);
+ info->file_info.fw_file = NULL;
+ }
+ cg2900_create_work_item(info->wq, work_reset_after_error, dev);
+ }
+
+ return true;
+}
+
+/**
+ * handle_vs_write_file_block_cmd_status() - Handles HCI VS WriteFileBlock Command Status event.
+ * @status: Returned status of WriteFileBlock command.
+ *
+ * Returns:
+ * true, if packet was handled internally,
+ * false, otherwise.
+ */
+static bool handle_vs_write_file_block_cmd_status(struct cg2900_chip_dev *dev,
+ u8 status)
+{
+ struct stlc2690_chip_info *info = dev->c_data;
+
+ if (info->boot_state != BOOT_DOWNLOAD_PATCH ||
+ info->download_state != DOWNLOAD_PENDING)
+ return false;
+
+ /*
+ * Only do something if there is an error. Otherwise we will wait for
+ * CmdComplete.
+ */
+ if (HCI_BT_ERROR_NO_ERROR != status) {
+ dev_err(BOOT_DEV,
+ "Command status for WriteFileBlock received with"
+ " error 0x%X\n", status);
+ dev_dbg(BOOT_DEV, "New download_state: DOWNLOAD_FAILED\n");
+ info->download_state = DOWNLOAD_FAILED;
+ dev_dbg(BOOT_DEV, "New boot_state: BOOT_FAILED\n");
+ info->boot_state = BOOT_FAILED;
+ if (info->file_info.fw_file) {
+ release_firmware(info->file_info.fw_file);
+ info->file_info.fw_file = NULL;
+ }
+ cg2900_create_work_item(info->wq, work_reset_after_error, dev);
+ }
+
+ return true;
+}
+
+/**
+ * handle_rx_data_bt_evt() - Check if received data should be handled in STLC2690 chip driver.
+ * @skb: Data packet
+ *
+ * The handle_rx_data_bt_evt() function checks if received data should be
+ * handled in STLC2690 chip driver. If so handle it correctly.
+ * Received data is always HCI BT Event.
+ *
+ * Returns:
+ * True, if packet was handled internally,
+ * False, otherwise.
+ */
+static bool handle_rx_data_bt_evt(struct cg2900_chip_dev *dev,
+ struct sk_buff *skb)
+{
+ bool pkt_handled = false;
+ /* skb cannot be NULL here so it is safe to de-reference */
+ u8 *data = skb->data;
+ struct hci_event_hdr *evt;
+ u16 op_code;
+
+ evt = (struct hci_event_hdr *)data;
+ data += sizeof(*evt);
+
+ /* First check the event code. */
+ if (HCI_EV_CMD_COMPLETE == evt->evt) {
+ struct hci_ev_cmd_complete *cmd_complete;
+
+ cmd_complete = (struct hci_ev_cmd_complete *)data;
+ op_code = le16_to_cpu(cmd_complete->opcode);
+ dev_dbg(dev->dev,
+ "Received Command Complete: op_code = 0x%04X\n",
+ op_code);
+ /* Move to first byte after OCF */
+ data += sizeof(*cmd_complete);
+
+ if (op_code == HCI_OP_RESET)
+ pkt_handled = handle_reset_cmd_complete(dev, data);
+ else if (op_code == STLC2690_BT_OP_VS_STORE_IN_FS)
+ pkt_handled = handle_vs_store_in_fs_cmd_complete(dev,
+ data);
+ else if (op_code == STLC2690_BT_OP_VS_WRITE_FILE_BLOCK)
+ pkt_handled =
+ handle_vs_write_file_block_cmd_complete(dev,
+ data);
+ } else if (HCI_EV_CMD_STATUS == evt->evt) {
+ struct hci_ev_cmd_status *cmd_status;
+
+ cmd_status = (struct hci_ev_cmd_status *)data;
+
+ op_code = le16_to_cpu(cmd_status->opcode);
+
+ dev_dbg(dev->dev, "Received Command Status: op_code = 0x%04X\n",
+ op_code);
+
+ if (op_code == STLC2690_BT_OP_VS_WRITE_FILE_BLOCK)
+ pkt_handled = handle_vs_write_file_block_cmd_status
+ (dev, cmd_status->status);
+ } else if (HCI_EV_HW_ERROR == evt->evt) {
+ struct hci_ev_hw_error *hw_error;
+
+ hw_error = (struct hci_ev_hw_error *)data;
+ /*
+ * Only do a printout. There might be a receiving stack that can
+ * handle this event
+ */
+ dev_err(dev->dev, "HW Error event received with error 0x%02X\n",
+ hw_error->hw_code);
+ return false;
+ } else
+ return false;
+
+ if (pkt_handled)
+ kfree_skb(skb);
+
+ return pkt_handled;
+}
+
+/**
+ * data_from_chip() - Called when data is received from the chip.
+ * @dev: Chip info.
+ * @skb: Packet received.
+ *
+ * The data_from_chip() function checks if packet is a response for a packet it
+ * itself has transmitted. If not it finds the correct user and sends the packet
+ * to the user.
+ */
+static void data_from_chip(struct cg2900_chip_dev *dev,
+ struct sk_buff *skb)
+{
+ int h4_channel;
+ struct list_head *cursor;
+ struct stlc2690_channel_item *tmp;
+ struct stlc2690_chip_info *info = dev->c_data;
+ struct cg2900_user_data *user = NULL;
+
+ h4_channel = skb->data[0];
+ skb_pull(skb, HCI_H4_SIZE);
+
+ /* Then check if this is a response to data we have sent */
+ if (h4_channel == CHANNEL_BT_EVT && handle_rx_data_bt_evt(dev, skb))
+ return;
+
+ spin_lock_bh(&info->rw_lock);
+
+ /* Let's see if this packet has the same user as the last one */
+ if (info->last_user && info->last_user->h4_channel == h4_channel) {
+ user = info->last_user;
+ goto user_found;
+ }
+
+ /* Search through the list of all open channels to find the user */
+ list_for_each(cursor, &info->open_channels) {
+ tmp = list_entry(cursor, struct stlc2690_channel_item, list);
+ if (tmp->user->h4_channel == h4_channel) {
+ user = tmp->user;
+ goto user_found;
+ }
+ }
+
+user_found:
+ info->last_user = user;
+ spin_unlock_bh(&info->rw_lock);
+
+ if (user)
+ user->read_cb(user, skb);
+ else {
+ dev_err(dev->dev,
+ "Could not find corresponding user to h4_channel %d\n",
+ h4_channel);
+ kfree_skb(skb);
+ }
+}
+
+static void chip_removed(struct cg2900_chip_dev *dev)
+{
+ struct stlc2690_chip_info *info = dev->c_data;
+
+ mfd_remove_devices(dev->dev);
+ kfree(info->settings_file_name);
+ kfree(info->patch_file_name);
+ destroy_workqueue(info->wq);
+ kfree(info);
+ dev->c_data = NULL;
+ dev->c_cb.chip_removed = NULL;
+ dev->c_cb.data_from_chip = NULL;
+}
+
+/**
+ * chip_shutdown() - Reset and power the chip off.
+ */
+static void chip_shutdown(struct cg2900_user_data *user)
+{
+ struct cg2900_chip_dev *dev = cg2900_get_prv(user);
+ struct stlc2690_chip_info *info = dev->c_data;
+
+ dev_dbg(user->dev, "chip_shutdown\n");
+
+ /* Close the transport, which will power off the chip */
+ if (dev->t_cb.close)
+ dev->t_cb.close(dev);
+
+ /* Chip shut-down finished, set correct state and wake up the chip. */
+ dev_dbg(dev->dev, "New main_state: STLC2690_IDLE\n");
+ info->main_state = STLC2690_IDLE;
+ wake_up_all(&main_wait_queue);
+}
+
+static void chip_startup_finished(struct stlc2690_chip_info *info, int err)
+{
+ dev_dbg(BOOT_DEV, "chip_startup_finished (%d)\n", err);
+
+ if (err)
+ /* Shutdown the chip */
+ chip_shutdown(info->user_in_charge);
+ else {
+ dev_dbg(BOOT_DEV, "New main_state: CORE_ACTIVE\n");
+ info->main_state = STLC2690_ACTIVE;
+ }
+
+ wake_up_all(&main_wait_queue);
+
+ if (err)
+ return;
+
+ if (!info->chip_dev->t_cb.chip_startup_finished)
+ dev_err(BOOT_DEV, "chip_startup_finished callback not found\n");
+ else
+ info->chip_dev->t_cb.chip_startup_finished(info->chip_dev);
+}
+
+static int stlc2690_open(struct cg2900_user_data *user)
+{
+ int err;
+ struct cg2900_chip_dev *dev;
+ struct stlc2690_chip_info *info;
+ struct list_head *cursor;
+ struct stlc2690_channel_item *tmp;
+ struct hci_command_hdr cmd;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV, "stlc2690_open: Calling with NULL pointer\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(user->dev, "stlc2690_open\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ mutex_lock(&main_info->man_mutex);
+
+ /* Add a minor wait in order to avoid CPU blocking, looping openings */
+ err = wait_event_timeout(main_wait_queue,
+ (STLC2690_IDLE == info->main_state ||
+ STLC2690_ACTIVE == info->main_state),
+ msecs_to_jiffies(LINE_TOGGLE_DETECT_TIMEOUT));
+ if (err <= 0) {
+ if (STLC2690_INIT == info->main_state)
+ dev_err(user->dev, "Transport not opened\n");
+ else
+ dev_err(user->dev, "stlc2690_open currently busy "
+ "(0x%X). Try again\n", info->main_state);
+ err = -EBUSY;
+ goto err_free_mutex;
+ }
+
+ err = 0;
+
+ list_for_each(cursor, &info->open_channels) {
+ tmp = list_entry(cursor, struct stlc2690_channel_item, list);
+ if (tmp->user->h4_channel == user->h4_channel) {
+ dev_err(user->dev, "Channel %d is already opened\n",
+ user->h4_channel);
+ err = -EACCES;
+ goto err_free_mutex;
+ }
+ }
+
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp) {
+ dev_err(user->dev, "Could not allocate tmp\n");
+ err = -ENOMEM;
+ goto err_free_mutex;
+ }
+ tmp->user = user;
+
+ if (STLC2690_ACTIVE != info->main_state &&
+ !user->chip_independent) {
+ /* Open transport and start-up the chip */
+ if (dev->t_cb.set_chip_power)
+ dev->t_cb.set_chip_power(dev, true);
+
+ /* Wait to be sure that the chip is ready */
+ schedule_timeout_killable(
+ msecs_to_jiffies(CHIP_READY_TIMEOUT));
+
+ if (dev->t_cb.open)
+ err = dev->t_cb.open(dev);
+ if (err) {
+ if (dev->t_cb.set_chip_power)
+ dev->t_cb.set_chip_power(dev, false);
+ goto err_free_list_item;
+ }
+
+ /* Start the boot sequence */
+ info->user_in_charge = user;
+ info->last_user = user;
+ dev_dbg(user->dev, "New boot_state: BOOT_RESET\n");
+ info->boot_state = BOOT_RESET;
+ dev_dbg(user->dev, "New main_state: STLC2690_BOOTING\n");
+ info->main_state = STLC2690_BOOTING;
+ cmd.opcode = cpu_to_le16(HCI_OP_RESET);
+ cmd.plen = 0; /* No parameters for HCI reset */
+ cg2900_send_bt_cmd(user, info->logger, &cmd, sizeof(cmd));
+
+ dev_dbg(user->dev, "Wait up to 15 seconds for chip to start\n");
+ wait_event_timeout(main_wait_queue,
+ (STLC2690_ACTIVE == info->main_state ||
+ STLC2690_IDLE == info->main_state),
+ msecs_to_jiffies(CHIP_STARTUP_TIMEOUT));
+ if (STLC2690_ACTIVE != info->main_state) {
+ dev_err(user->dev, "STLC2690 driver failed to start\n");
+
+ if (dev->t_cb.close)
+ dev->t_cb.close(dev);
+
+ dev_dbg(user->dev, "New main_state: CORE_IDLE\n");
+ info->main_state = STLC2690_IDLE;
+ err = -EIO;
+ goto err_free_list_item;
+ }
+ }
+
+ list_add_tail(&tmp->list, &info->open_channels);
+
+ user->opened = true;
+
+ dev_dbg(user->dev, "H:4 channel opened\n");
+
+ mutex_unlock(&main_info->man_mutex);
+ return 0;
+err_free_list_item:
+ kfree(tmp);
+err_free_mutex:
+ mutex_unlock(&main_info->man_mutex);
+ return err;
+}
+
+static int stlc2690_hci_log_open(struct cg2900_user_data *user)
+{
+ struct cg2900_chip_dev *dev;
+ struct stlc2690_chip_info *info;
+ int err;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV,
+ "stlc2690_hci_log_open: Calling with NULL pointer\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(user->dev, "stlc2690_hci_log_open\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ if (info->logger) {
+ dev_err(user->dev, "HCI Logger already stored\n");
+ return -EACCES;
+ }
+
+ info->logger = user;
+ err = stlc2690_open(user);
+ if (err)
+ info->logger = NULL;
+ return err;
+}
+
+static void stlc2690_close(struct cg2900_user_data *user)
+{
+ bool keep_powered = false;
+ struct list_head *cursor, *next;
+ struct stlc2690_channel_item *tmp;
+ struct cg2900_chip_dev *dev;
+ struct stlc2690_chip_info *info;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV,
+ "stlc2690_close: Calling with NULL pointer\n");
+ return;
+ }
+
+ dev_dbg(user->dev, "stlc2690_close\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ mutex_lock(&main_info->man_mutex);
+
+ /*
+ * Go through each open channel. Remove our channel and check if there
+ * is any other channel that want to keep the chip running
+ */
+ list_for_each_safe(cursor, next, &info->open_channels) {
+ tmp = list_entry(cursor, struct stlc2690_channel_item, list);
+ if (tmp->user == user) {
+ list_del(cursor);
+ kfree(tmp);
+ } else if (!tmp->user->chip_independent)
+ keep_powered = true;
+ }
+
+ if (keep_powered)
+ /* This was not the last user, we're done. */
+ goto finished;
+
+ if (STLC2690_IDLE == info->main_state)
+ /* Chip has already been shut down. */
+ goto finished;
+
+ dev_dbg(user->dev, "New main_state: CORE_CLOSING\n");
+ info->main_state = STLC2690_CLOSING;
+ chip_shutdown(user);
+
+ dev_dbg(user->dev, "Wait up to 15 seconds for chip to shut-down\n");
+ wait_event_timeout(main_wait_queue,
+ STLC2690_IDLE == info->main_state,
+ msecs_to_jiffies(CHIP_SHUTDOWN_TIMEOUT));
+
+ /* Force shutdown if we timed out */
+ if (STLC2690_IDLE != info->main_state) {
+ dev_err(user->dev,
+ "ST-Ericsson STLC2690 Core Driver was shut-down with "
+ "problems\n");
+
+ if (dev->t_cb.close)
+ dev->t_cb.close(dev);
+
+ dev_dbg(user->dev, "New main_state: CORE_IDLE\n");
+ info->main_state = STLC2690_IDLE;
+ }
+
+finished:
+ mutex_unlock(&main_info->man_mutex);
+ user->opened = false;
+ dev_dbg(user->dev, "H:4 channel closed\n");
+}
+
+static void stlc2690_hci_log_close(struct cg2900_user_data *user)
+{
+ struct cg2900_chip_dev *dev;
+ struct stlc2690_chip_info *info;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV,
+ "stlc2690_hci_log_close: Calling with NULL pointer\n");
+ return;
+ }
+
+ dev_dbg(user->dev, "stlc2690_hci_log_close\n");
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ info->logger = NULL;
+ stlc2690_close(user);
+}
+
+static int stlc2690_reset(struct cg2900_user_data *user)
+{
+ struct list_head *cursor, *next;
+ struct stlc2690_channel_item *tmp;
+ struct cg2900_chip_dev *dev;
+ struct stlc2690_chip_info *info;
+
+ if (!user) {
+ dev_err(MAIN_DEV,
+ "stlc2690_reset: Calling with NULL pointer\n");
+ return -EINVAL;
+ }
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ dev_info(user->dev, "stlc2690_reset\n");
+
+ BUG_ON(!main_info);
+
+ mutex_lock(&main_info->man_mutex);
+
+ dev_dbg(user->dev, "New main_state: CORE_RESETING\n");
+ info->main_state = STLC2690_RESETING;
+
+ chip_shutdown(user);
+
+ /*
+ * Inform all opened channels about the reset and free the user devices
+ */
+ list_for_each_safe(cursor, next, &info->open_channels) {
+ tmp = list_entry(cursor, struct stlc2690_channel_item, list);
+ list_del(cursor);
+ tmp->user->opened = false;
+ tmp->user->reset_cb(tmp->user);
+ kfree(tmp);
+ }
+
+ /* Reset finished. We are now idle until first channel is opened */
+ dev_dbg(user->dev, "New main_state: STLC2690_IDLE\n");
+ info->main_state = STLC2690_IDLE;
+
+ mutex_unlock(&main_info->man_mutex);
+
+ /*
+ * Send wake-up since this might have been called from a failed boot.
+ * No harm done if it is a STLC2690 chip user who called.
+ */
+ wake_up_all(&main_wait_queue);
+
+ return 0;
+}
+
+static struct sk_buff *stlc2690_alloc_skb(unsigned int size, gfp_t priority)
+{
+ struct sk_buff *skb;
+
+ dev_dbg(MAIN_DEV, "stlc2690_alloc_skb size %d bytes\n", size);
+
+ /* Allocate the SKB and reserve space for the header */
+ skb = alloc_skb(size + CG2900_SKB_RESERVE, priority);
+ if (skb)
+ skb_reserve(skb, CG2900_SKB_RESERVE);
+
+ return skb;
+}
+
+static int stlc2690_write(struct cg2900_user_data *user, struct sk_buff *skb)
+{
+ int err = 0;
+ u8 *h4_header;
+ struct cg2900_chip_dev *dev;
+ struct stlc2690_chip_info *info;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV,
+ "stlc2690_write: Calling with NULL pointer\n");
+ return -EINVAL;
+ }
+
+ if (!skb) {
+ dev_err(user->dev, "stlc2690_write with no sk_buffer\n");
+ return -EINVAL;
+ }
+
+ dev = cg2900_get_prv(user);
+ info = dev->c_data;
+
+ dev_dbg(user->dev, "stlc2690_write length %d bytes\n", skb->len);
+
+ if (!user->opened) {
+ dev_err(user->dev,
+ "Trying to transmit data on a closed channel\n");
+ return -EACCES;
+ }
+
+ /*
+ * Move the data pointer to the H:4 header position and
+ * store the H4 header.
+ */
+ h4_header = skb_push(skb, CG2900_SKB_RESERVE);
+ *h4_header = (u8)user->h4_channel;
+ cg2900_tx_to_chip(user, info->logger, skb);
+
+ return err;
+}
+
+static int stlc2690_no_write(struct cg2900_user_data *user,
+ struct sk_buff *skb)
+{
+ dev_err(user->dev, "Not allowed to send on this channel\n");
+ return -EPERM;
+}
+
+static bool stlc2690_get_local_revision(struct cg2900_user_data *user,
+ struct cg2900_rev_data *rev_data)
+{
+ struct cg2900_chip_dev *dev;
+
+ BUG_ON(!main_info);
+
+ if (!user) {
+ dev_err(MAIN_DEV, "stlc2690_get_local_revision: Calling with "
+ "NULL pointer\n");
+ return false;
+ }
+
+ if (!rev_data) {
+ dev_err(user->dev, "Calling with rev_data NULL\n");
+ return false;
+ }
+
+ dev = cg2900_get_prv(user);
+
+ rev_data->revision = dev->chip.hci_revision;
+ rev_data->sub_version = dev->chip.hci_sub_version;
+
+ return true;
+}
+
+static struct cg2900_user_data btcmd_data = {
+ .h4_channel = CHANNEL_BT_CMD,
+};
+static struct cg2900_user_data btacl_data = {
+ .h4_channel = CHANNEL_BT_ACL,
+};
+static struct cg2900_user_data btevt_data = {
+ .h4_channel = CHANNEL_BT_EVT,
+};
+static struct cg2900_user_data hci_logger_data = {
+ .h4_channel = CHANNEL_HCI_LOGGER,
+ .chip_independent = true,
+ .write = stlc2690_no_write,
+ .open = stlc2690_hci_log_open,
+ .close = stlc2690_hci_log_close,
+};
+static struct cg2900_user_data core_data = {
+ .h4_channel = CHANNEL_CORE,
+ .write = stlc2690_no_write,
+};
+
+static struct mfd_cell stlc2690_devs[] = {
+ {
+ .name = "cg2900-btcmd",
+ .platform_data = &btcmd_data,
+ .data_size = sizeof(btcmd_data),
+ },
+ {
+ .name = "cg2900-btacl",
+ .platform_data = &btacl_data,
+ .data_size = sizeof(btacl_data),
+ },
+ {
+ .name = "cg2900-btevt",
+ .platform_data = &btevt_data,
+ .data_size = sizeof(btevt_data),
+ },
+ {
+ .name = "cg2900-hcilogger",
+ .platform_data = &hci_logger_data,
+ .data_size = sizeof(hci_logger_data),
+ },
+ {
+ .name = "cg2900-core",
+ .platform_data = &core_data,
+ .data_size = sizeof(core_data),
+ },
+};
+
+static struct cg2900_user_data char_btcmd_data = {
+ .channel_data = {
+ .char_dev_name = STLC2690_BT_CMD,
+ },
+ .h4_channel = CHANNEL_BT_CMD,
+};
+static struct cg2900_user_data char_btacl_data = {
+ .channel_data = {
+ .char_dev_name = STLC2690_BT_ACL,
+ },
+ .h4_channel = CHANNEL_BT_ACL,
+};
+static struct cg2900_user_data char_btevt_data = {
+ .channel_data = {
+ .char_dev_name = STLC2690_BT_EVT,
+ },
+ .h4_channel = CHANNEL_BT_EVT,
+};
+static struct cg2900_user_data char_hci_logger_data = {
+ .channel_data = {
+ .char_dev_name = STLC2690_HCI_LOGGER,
+ },
+ .h4_channel = CHANNEL_HCI_LOGGER,
+ .chip_independent = true,
+ .write = stlc2690_no_write,
+ .open = stlc2690_hci_log_open,
+ .close = stlc2690_hci_log_close,
+};
+static struct cg2900_user_data char_core_data = {
+ .channel_data = {
+ .char_dev_name = STLC2690_CORE,
+ },
+ .h4_channel = CHANNEL_CORE,
+ .write = stlc2690_no_write,
+};
+
+static struct mfd_cell stlc2690_char_devs[] = {
+ {
+ .name = "cg2900-chardev",
+ .id = 0,
+ .platform_data = &char_btcmd_data,
+ .data_size = sizeof(char_btcmd_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 1,
+ .platform_data = &char_btacl_data,
+ .data_size = sizeof(char_btacl_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 2,
+ .platform_data = &char_btevt_data,
+ .data_size = sizeof(char_btevt_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 7,
+ .platform_data = &char_hci_logger_data,
+ .data_size = sizeof(char_hci_logger_data),
+ },
+ {
+ .name = "cg2900-chardev",
+ .id = 8,
+ .platform_data = &char_core_data,
+ .data_size = sizeof(char_core_data),
+ },
+};
+
+/**
+ * set_plat_data() - Initializes data for an MFD cell.
+ * @cell: MFD cell.
+ * @dev: Current chip.
+ *
+ * Sets each callback to default function unless already set.
+ */
+static void set_plat_data(struct mfd_cell *cell, struct cg2900_chip_dev *dev)
+{
+ struct cg2900_user_data *user = cell->platform_data;
+
+ if (!user->open)
+ user->open = stlc2690_open;
+ if (!user->close)
+ user->close = stlc2690_close;
+ if (!user->reset)
+ user->reset = stlc2690_reset;
+ if (!user->alloc_skb)
+ user->alloc_skb = stlc2690_alloc_skb;
+ if (!user->write)
+ user->write = stlc2690_write;
+ if (!user->get_local_revision)
+ user->get_local_revision = stlc2690_get_local_revision;
+
+ cg2900_set_prv(user, dev);
+}
+
+/**
+ * check_chip_support() - Checks if connected chip is handled by this driver.
+ * @dev: Chip info structure.
+ *
+ * If supported return true and fill in @callbacks.
+ *
+ * Returns:
+ * true if chip is handled by this driver.
+ * false otherwise.
+ */
+static bool check_chip_support(struct cg2900_chip_dev *dev)
+{
+ struct cg2900_platform_data *pf_data;
+ struct stlc2690_chip_info *info;
+ int i;
+ int err;
+
+ dev_dbg(dev->dev, "check_chip_support\n");
+
+ /*
+ * Check if this is a STLC2690 revision.
+ * We do not care about the sub-version at the moment. Change this if
+ * necessary.
+ */
+ if (dev->chip.manufacturer != STLC2690_SUPP_MANUFACTURER ||
+ dev->chip.hci_revision < STLC2690_SUPP_REVISION_MIN ||
+ dev->chip.hci_revision > STLC2690_SUPP_REVISION_MAX) {
+ dev_dbg(dev->dev, "Chip not supported by STLC2690 driver\n"
+ "\tMan: 0x%02X\n"
+ "\tRev: 0x%04X\n"
+ "\tSub: 0x%04X\n",
+ dev->chip.manufacturer, dev->chip.hci_revision,
+ dev->chip.hci_sub_version);
+ return false;
+ }
+
+ /* Store needed data */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(dev->dev, "Couldn't allocate info struct\n");
+ return false;
+ }
+
+ /* Initialize all variables */
+ INIT_LIST_HEAD(&info->open_channels);
+ spin_lock_init(&info->rw_lock);
+ info->chip_dev = dev;
+
+ info->wq = create_singlethread_workqueue(WQ_NAME);
+ if (!info->wq) {
+ dev_err(dev->dev, "Could not create workqueue\n");
+ goto err_handling_free_info;
+ }
+
+ info->patch_file_name = kzalloc(NAME_MAX + 1, GFP_ATOMIC);
+ if (!info->patch_file_name) {
+ dev_err(dev->dev,
+ "Couldn't allocate name buffer for patch file\n");
+ goto err_handling_destroy_wq;
+ }
+
+ info->settings_file_name = kzalloc(NAME_MAX + 1, GFP_ATOMIC);
+ if (!info->settings_file_name) {
+ dev_err(dev->dev,
+ "Couldn't allocate name buffers settings file\n");
+ goto err_handling_free_patch_name;
+ }
+
+ dev->c_data = info;
+ /* Set the callbacks */
+ dev->c_cb.data_from_chip = data_from_chip;
+ dev->c_cb.chip_removed = chip_removed,
+ info->chip_dev = dev;
+
+ mutex_lock(&main_info->man_mutex);
+
+ pf_data = dev_get_platdata(dev->dev);
+ btcmd_data.channel_data.bt_bus = pf_data->bus;
+ btacl_data.channel_data.bt_bus = pf_data->bus;
+ btevt_data.channel_data.bt_bus = pf_data->bus;
+
+ for (i = 0; i < ARRAY_SIZE(stlc2690_devs); i++)
+ set_plat_data(&stlc2690_devs[i], dev);
+ for (i = 0; i < ARRAY_SIZE(stlc2690_char_devs); i++)
+ set_plat_data(&stlc2690_char_devs[i], dev);
+
+ err = mfd_add_devices(dev->dev, main_info->cell_base_id, stlc2690_devs,
+ ARRAY_SIZE(stlc2690_devs), NULL, 0);
+ if (err) {
+ dev_err(dev->dev, "Failed to add stlc2690_devs (%d)\n", err);
+ goto err_handling_free_settings_name;
+ }
+
+ err = mfd_add_devices(dev->dev, main_info->cell_base_id,
+ stlc2690_char_devs,
+ ARRAY_SIZE(stlc2690_char_devs), NULL, 0);
+ if (err) {
+ dev_err(dev->dev, "Failed to add stlc2690_char_devs (%d)\n",
+ err);
+ goto err_handling_remove_devs;
+ }
+
+ main_info->cell_base_id += 30;
+ mutex_unlock(&main_info->man_mutex);
+
+ dev_info(dev->dev, "Chip supported by the STLC2690 chip driver\n");
+
+ /* Close the transport, which will power off the chip */
+ if (dev->t_cb.close)
+ dev->t_cb.close(dev);
+
+ dev_dbg(dev->dev, "New main_state: STLC2690_IDLE\n");
+ info->main_state = STLC2690_IDLE;
+
+ return true;
+
+err_handling_remove_devs:
+ mfd_remove_devices(dev->dev);
+err_handling_free_settings_name:
+ kfree(info->settings_file_name);
+err_handling_free_patch_name:
+ kfree(info->patch_file_name);
+err_handling_destroy_wq:
+ destroy_workqueue(info->wq);
+err_handling_free_info:
+ kfree(info);
+ return false;
+}
+
+static struct cg2900_id_callbacks chip_support_callbacks = {
+ .check_chip_support = check_chip_support,
+};
+
+/**
+ * stlc2690_chip_probe() - Initialize STLC2690 chip handler resources.
+ * @pdev: Platform device.
+ *
+ * This function initializes the STLC2690 driver, then registers to
+ * the CG2900 Core.
+ *
+ * Returns:
+ * 0 if success.
+ * -ENOMEM for failed alloc or structure creation.
+ * Error codes generated by cg2900_register_chip_driver.
+ */
+static int __devinit stlc2690_chip_probe(struct platform_device *pdev)
+{
+ int err;
+
+ dev_dbg(&pdev->dev, "stlc2690_chip_probe\n");
+
+ main_info = kzalloc(sizeof(*main_info), GFP_ATOMIC);
+ if (!main_info) {
+ dev_err(&pdev->dev, "Couldn't allocate main_info\n");
+ return -ENOMEM;
+ }
+
+ main_info->dev = &pdev->dev;
+ mutex_init(&main_info->man_mutex);
+
+ err = cg2900_register_chip_driver(&chip_support_callbacks);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Couldn't register chip driver (%d)\n", err);
+ goto error_handling;
+ }
+
+ dev_info(&pdev->dev, "STLC2690 chip driver started\n");
+
+ return 0;
+
+error_handling:
+ mutex_destroy(&main_info->man_mutex);
+ kfree(main_info);
+ main_info = NULL;
+ return err;
+}
+
+/**
+ * stlc2690_chip_remove() - Release STLC2690 chip handler resources.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if success (always success).
+ */
+static int __devexit stlc2690_chip_remove(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "STLC2690 chip driver removed\n");
+
+ cg2900_deregister_chip_driver(&chip_support_callbacks);
+
+ if (!main_info)
+ return 0;
+
+ mutex_destroy(&main_info->man_mutex);
+ kfree(main_info);
+ main_info = NULL;
+ return 0;
+}
+
+static struct platform_driver stlc2690_chip_driver = {
+ .driver = {
+ .name = "stlc2690-chip",
+ .owner = THIS_MODULE,
+ },
+ .probe = stlc2690_chip_probe,
+ .remove = __devexit_p(stlc2690_chip_remove),
+};
+
+/**
+ * stlc2690_chip_init() - Initialize module.
+ *
+ * Registers platform driver.
+ */
+static int __init stlc2690_chip_init(void)
+{
+ pr_debug("stlc2690_chip_init");
+ return platform_driver_register(&stlc2690_chip_driver);
+}
+
+/**
+ * stlc2690_chip_exit() - Remove module.
+ *
+ * Unregisters platform driver.
+ */
+static void __exit stlc2690_chip_exit(void)
+{
+ pr_debug("stlc2690_chip_exit");
+ platform_driver_unregister(&stlc2690_chip_driver);
+}
+
+module_init(stlc2690_chip_init);
+module_exit(stlc2690_chip_exit);
+
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Linux STLC2690 Connectivity Device Driver");
diff --git a/drivers/mfd/cg2900/stlc2690_chip.h b/drivers/mfd/cg2900/stlc2690_chip.h
new file mode 100644
index 00000000000..d14e7737636
--- /dev/null
+++ b/drivers/mfd/cg2900/stlc2690_chip.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
+ * Henrik Possung (henrik.possung@stericsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@stericsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson STLC2690 BT/FM controller.
+ */
+
+#ifndef _STLC2690_CHIP_H_
+#define _STLC2690_CHIP_H_
+
+/* Supported chips */
+#define STLC2690_SUPP_MANUFACTURER 0x30
+#define STLC2690_SUPP_REVISION_MIN 0x0500
+#define STLC2690_SUPP_REVISION_MAX 0x06FF
+
+#define BT_SIZE_OF_HDR (sizeof(__le16) + sizeof(__u8))
+#define BT_PARAM_LEN(__pkt_len) (__pkt_len - BT_SIZE_OF_HDR)
+
+/* BT VS Store In FS command */
+#define STLC2690_BT_OP_VS_STORE_IN_FS 0xFC22
+struct bt_vs_store_in_fs_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 user_id;
+ __u8 len;
+ __u8 data[];
+} __packed;
+
+/* BT VS Write File Block command */
+#define STLC2690_BT_OP_VS_WRITE_FILE_BLOCK 0xFC2E
+struct bt_vs_write_file_block_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 id;
+ __u8 data[];
+} __packed;
+
+/* User ID for storing BD address in chip using Store_In_FS command */
+#define STLC2690_VS_STORE_IN_FS_USR_ID_BD_ADDR 0xFE
+
+#endif /* _STLC2690_CHIP_H_ */
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
new file mode 100644
index 00000000000..e507ec2f4b9
--- /dev/null
+++ b/drivers/mfd/stmpe.c
@@ -0,0 +1,1033 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stmpe.h>
+#include "stmpe.h"
+
+static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
+{
+ return stmpe->variant->enable(stmpe, blocks, true);
+}
+
+static int __stmpe_disable(struct stmpe *stmpe, unsigned int blocks)
+{
+ return stmpe->variant->enable(stmpe, blocks, false);
+}
+
+static int __stmpe_reg_read(struct stmpe *stmpe, u8 reg)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(stmpe->i2c, reg);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to read reg %#x: %d\n",
+ reg, ret);
+
+ dev_vdbg(stmpe->dev, "rd: reg %#x => data %#x\n", reg, ret);
+
+ return ret;
+}
+
+static int __stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+ int ret;
+
+ dev_vdbg(stmpe->dev, "wr: reg %#x <= %#x\n", reg, val);
+
+ ret = i2c_smbus_write_byte_data(stmpe->i2c, reg, val);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to write reg %#x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+
+static int __stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val)
+{
+ int ret;
+
+ ret = __stmpe_reg_read(stmpe, reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~mask;
+ ret |= val;
+
+ return __stmpe_reg_write(stmpe, reg, ret);
+}
+
+static int __stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length,
+ u8 *values)
+{
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(stmpe->i2c, reg, length, values);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to read regs %#x: %d\n",
+ reg, ret);
+
+ dev_vdbg(stmpe->dev, "rd: reg %#x (%d) => ret %#x\n", reg, length, ret);
+ stmpe_dump_bytes("stmpe rd: ", values, length);
+
+ return ret;
+}
+
+static int __stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+ const u8 *values)
+{
+ int ret;
+
+ dev_vdbg(stmpe->dev, "wr: regs %#x (%d)\n", reg, length);
+ stmpe_dump_bytes("stmpe wr: ", values, length);
+
+ ret = i2c_smbus_write_i2c_block_data(stmpe->i2c, reg, length,
+ values);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to write regs %#x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+
+/**
+ * stmpe_enable - enable blocks on an STMPE device
+ * @stmpe: Device to work on
+ * @blocks: Mask of blocks (enum stmpe_block values) to enable
+ */
+int stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_enable(stmpe, blocks);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_enable);
+
+/**
+ * stmpe_disable - disable blocks on an STMPE device
+ * @stmpe: Device to work on
+ * @blocks: Mask of blocks (enum stmpe_block values) to enable
+ */
+int stmpe_disable(struct stmpe *stmpe, unsigned int blocks)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_disable(stmpe, blocks);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_disable);
+
+/**
+ * stmpe_reg_read() - read a single STMPE register
+ * @stmpe: Device to read from
+ * @reg: Register to read
+ */
+int stmpe_reg_read(struct stmpe *stmpe, u8 reg)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_reg_read(stmpe, reg);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_reg_read);
+
+/**
+ * stmpe_reg_write() - write a single STMPE register
+ * @stmpe: Device to write to
+ * @reg: Register to write
+ * @val: Value to write
+ */
+int stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_reg_write(stmpe, reg, val);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_reg_write);
+
+/**
+ * stmpe_set_bits() - set the value of a bitfield in a STMPE register
+ * @stmpe: Device to write to
+ * @reg: Register to write
+ * @mask: Mask of bits to set
+ * @val: Value to set
+ */
+int stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_set_bits(stmpe, reg, mask, val);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_set_bits);
+
+/**
+ * stmpe_block_read() - read multiple STMPE registers
+ * @stmpe: Device to read from
+ * @reg: First register
+ * @length: Number of registers
+ * @values: Buffer to write to
+ */
+int stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length, u8 *values)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_block_read(stmpe, reg, length, values);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_block_read);
+
+/**
+ * stmpe_block_write() - write multiple STMPE registers
+ * @stmpe: Device to write to
+ * @reg: First register
+ * @length: Number of registers
+ * @values: Values to write
+ */
+int stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+ const u8 *values)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_block_write(stmpe, reg, length, values);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_block_write);
+
+/**
+ * stmpe_set_altfunc: set the alternate function for STMPE pins
+ * @stmpe: Device to configure
+ * @pins: Bitmask of pins to affect
+ * @block: block to enable alternate functions for
+ *
+ * @pins is assumed to have a bit set for each of the bits whose alternate
+ * function is to be changed, numbered according to the GPIOXY numbers.
+ *
+ * If the GPIO module is not enabled, this function automatically enables it in
+ * order to perform the change.
+ */
+int stmpe_set_altfunc(struct stmpe *stmpe, u32 pins, enum stmpe_block block)
+{
+ struct stmpe_variant_info *variant = stmpe->variant;
+ u8 regaddr = stmpe->regs[STMPE_IDX_GPAFR_U_MSB];
+ int af_bits = variant->af_bits;
+ int numregs = DIV_ROUND_UP(stmpe->num_gpios * af_bits, 8);
+ int afperreg = 8 / af_bits;
+ int mask = (1 << af_bits) - 1;
+ u8 regs[numregs];
+ int af;
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+
+ ret = __stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
+ if (ret < 0)
+ goto out;
+
+ ret = __stmpe_block_read(stmpe, regaddr, numregs, regs);
+ if (ret < 0)
+ goto out;
+
+ af = variant->get_altfunc(stmpe, block);
+
+ while (pins) {
+ int pin = __ffs(pins);
+ int regoffset = numregs - (pin / afperreg) - 1;
+ int pos = (pin % afperreg) * (8 / afperreg);
+
+ regs[regoffset] &= ~(mask << pos);
+ regs[regoffset] |= af << pos;
+
+ pins &= ~(1 << pin);
+ }
+
+ ret = __stmpe_block_write(stmpe, regaddr, numregs, regs);
+
+out:
+ mutex_unlock(&stmpe->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_set_altfunc);
+
+/*
+ * GPIO (all variants)
+ */
+
+static struct resource stmpe_gpio_resources[] = {
+ /* Start and end filled dynamically */
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell stmpe_gpio_cell = {
+ .name = "stmpe-gpio",
+ .resources = stmpe_gpio_resources,
+ .num_resources = ARRAY_SIZE(stmpe_gpio_resources),
+};
+
+/*
+ * Keypad (1601, 2401, 2403)
+ */
+
+static struct resource stmpe_keypad_resources[] = {
+ {
+ .name = "KEYPAD",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "KEYPAD_OVER",
+ .start = 1,
+ .end = 1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell stmpe_keypad_cell = {
+ .name = "stmpe-keypad",
+ .resources = stmpe_keypad_resources,
+ .num_resources = ARRAY_SIZE(stmpe_keypad_resources),
+};
+
+/*
+ * Touchscreen (STMPE811)
+ */
+
+static struct resource stmpe_ts_resources[] = {
+ {
+ .name = "TOUCH_DET",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "FIFO_TH",
+ .start = 1,
+ .end = 1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell stmpe_ts_cell = {
+ .name = "stmpe-ts",
+ .resources = stmpe_ts_resources,
+ .num_resources = ARRAY_SIZE(stmpe_ts_resources),
+};
+
+/*
+ * STMPE811
+ */
+
+static const u8 stmpe811_regs[] = {
+ [STMPE_IDX_CHIP_ID] = STMPE811_REG_CHIP_ID,
+ [STMPE_IDX_ICR_LSB] = STMPE811_REG_INT_CTRL,
+ [STMPE_IDX_IER_LSB] = STMPE811_REG_INT_EN,
+ [STMPE_IDX_ISR_MSB] = STMPE811_REG_INT_STA,
+ [STMPE_IDX_GPMR_LSB] = STMPE811_REG_GPIO_MP_STA,
+ [STMPE_IDX_GPSR_LSB] = STMPE811_REG_GPIO_SET_PIN,
+ [STMPE_IDX_GPCR_LSB] = STMPE811_REG_GPIO_CLR_PIN,
+ [STMPE_IDX_GPDR_LSB] = STMPE811_REG_GPIO_DIR,
+ [STMPE_IDX_GPRER_LSB] = STMPE811_REG_GPIO_RE,
+ [STMPE_IDX_GPFER_LSB] = STMPE811_REG_GPIO_FE,
+ [STMPE_IDX_GPAFR_U_MSB] = STMPE811_REG_GPIO_AF,
+ [STMPE_IDX_IEGPIOR_LSB] = STMPE811_REG_GPIO_INT_EN,
+ [STMPE_IDX_ISGPIOR_MSB] = STMPE811_REG_GPIO_INT_STA,
+ [STMPE_IDX_GPEDR_MSB] = STMPE811_REG_GPIO_ED,
+};
+
+static struct stmpe_variant_block stmpe811_blocks[] = {
+ {
+ .cell = &stmpe_gpio_cell,
+ .irq = STMPE811_IRQ_GPIOC,
+ .block = STMPE_BLOCK_GPIO,
+ },
+ {
+ .cell = &stmpe_ts_cell,
+ .irq = STMPE811_IRQ_TOUCH_DET,
+ .block = STMPE_BLOCK_TOUCHSCREEN,
+ },
+};
+
+static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks,
+ bool enable)
+{
+ unsigned int mask = 0;
+
+ if (blocks & STMPE_BLOCK_GPIO)
+ mask |= STMPE811_SYS_CTRL2_GPIO_OFF;
+
+ if (blocks & STMPE_BLOCK_ADC)
+ mask |= STMPE811_SYS_CTRL2_ADC_OFF;
+
+ if (blocks & STMPE_BLOCK_TOUCHSCREEN)
+ mask |= STMPE811_SYS_CTRL2_TSC_OFF;
+
+ return __stmpe_set_bits(stmpe, STMPE811_REG_SYS_CTRL2, mask,
+ enable ? 0 : mask);
+}
+
+static int stmpe811_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+ /* 0 for touchscreen, 1 for GPIO */
+ return block != STMPE_BLOCK_TOUCHSCREEN;
+}
+
+static struct stmpe_variant_info stmpe811 = {
+ .name = "stmpe811",
+ .id_val = 0x0811,
+ .id_mask = 0xffff,
+ .num_gpios = 8,
+ .af_bits = 1,
+ .regs = stmpe811_regs,
+ .blocks = stmpe811_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe811_blocks),
+ .num_irqs = STMPE811_NR_INTERNAL_IRQS,
+ .enable = stmpe811_enable,
+ .get_altfunc = stmpe811_get_altfunc,
+};
+
+/*
+ * STMPE1601
+ */
+
+static const u8 stmpe1601_regs[] = {
+ [STMPE_IDX_CHIP_ID] = STMPE1601_REG_CHIP_ID,
+ [STMPE_IDX_ICR_LSB] = STMPE1601_REG_ICR_LSB,
+ [STMPE_IDX_IER_LSB] = STMPE1601_REG_IER_LSB,
+ [STMPE_IDX_ISR_MSB] = STMPE1601_REG_ISR_MSB,
+ [STMPE_IDX_GPMR_LSB] = STMPE1601_REG_GPIO_MP_LSB,
+ [STMPE_IDX_GPSR_LSB] = STMPE1601_REG_GPIO_SET_LSB,
+ [STMPE_IDX_GPCR_LSB] = STMPE1601_REG_GPIO_CLR_LSB,
+ [STMPE_IDX_GPDR_LSB] = STMPE1601_REG_GPIO_SET_DIR_LSB,
+ [STMPE_IDX_GPRER_LSB] = STMPE1601_REG_GPIO_RE_LSB,
+ [STMPE_IDX_GPFER_LSB] = STMPE1601_REG_GPIO_FE_LSB,
+ [STMPE_IDX_GPAFR_U_MSB] = STMPE1601_REG_GPIO_AF_U_MSB,
+ [STMPE_IDX_IEGPIOR_LSB] = STMPE1601_REG_INT_EN_GPIO_MASK_LSB,
+ [STMPE_IDX_ISGPIOR_MSB] = STMPE1601_REG_INT_STA_GPIO_MSB,
+ [STMPE_IDX_GPEDR_MSB] = STMPE1601_REG_GPIO_ED_MSB,
+};
+
+static struct stmpe_variant_block stmpe1601_blocks[] = {
+ {
+ .cell = &stmpe_gpio_cell,
+ .irq = STMPE24XX_IRQ_GPIOC,
+ .block = STMPE_BLOCK_GPIO,
+ },
+ {
+ .cell = &stmpe_keypad_cell,
+ .irq = STMPE24XX_IRQ_KEYPAD,
+ .block = STMPE_BLOCK_KEYPAD,
+ },
+};
+
+/* supported autosleep timeout delay (in msecs) */
+static const int stmpe_autosleep_delay[] = {
+ 4, 16, 32, 64, 128, 256, 512, 1024,
+};
+
+static int stmpe_round_timeout(int timeout)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(stmpe_autosleep_delay); i++) {
+ if (stmpe_autosleep_delay[i] >= timeout)
+ return i;
+ }
+
+ /*
+ * requests for delays longer than supported should not return the
+ * longest supported delay
+ */
+ return -EINVAL;
+}
+
+static int stmpe_autosleep(struct stmpe *stmpe, int autosleep_timeout)
+{
+ int ret;
+
+ if (!stmpe->variant->enable_autosleep)
+ return -ENOSYS;
+
+ mutex_lock(&stmpe->lock);
+ ret = stmpe->variant->enable_autosleep(stmpe, autosleep_timeout);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+
+/*
+ * Both stmpe 1601/2403 support same layout for autosleep
+ */
+static int stmpe1601_autosleep(struct stmpe *stmpe,
+ int autosleep_timeout)
+{
+ int ret, timeout;
+
+ /* choose the best available timeout */
+ timeout = stmpe_round_timeout(autosleep_timeout);
+ if (timeout < 0) {
+ dev_err(stmpe->dev, "invalid timeout\n");
+ return timeout;
+ }
+
+ ret = __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
+ STMPE1601_AUTOSLEEP_TIMEOUT_MASK,
+ timeout);
+ if (ret < 0)
+ return ret;
+
+ return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
+ STPME1601_AUTOSLEEP_ENABLE,
+ STPME1601_AUTOSLEEP_ENABLE);
+}
+
+static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks,
+ bool enable)
+{
+ unsigned int mask = 0;
+
+ if (blocks & STMPE_BLOCK_GPIO)
+ mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO;
+
+ if (blocks & STMPE_BLOCK_KEYPAD)
+ mask |= STMPE1601_SYS_CTRL_ENABLE_KPC;
+
+ return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask,
+ enable ? mask : 0);
+}
+
+static int stmpe1601_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+ switch (block) {
+ case STMPE_BLOCK_PWM:
+ return 2;
+
+ case STMPE_BLOCK_KEYPAD:
+ return 1;
+
+ case STMPE_BLOCK_GPIO:
+ default:
+ return 0;
+ }
+}
+
+static struct stmpe_variant_info stmpe1601 = {
+ .name = "stmpe1601",
+ .id_val = 0x0210,
+ .id_mask = 0xfff0, /* at least 0x0210 and 0x0212 */
+ .num_gpios = 16,
+ .af_bits = 2,
+ .regs = stmpe1601_regs,
+ .blocks = stmpe1601_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe1601_blocks),
+ .num_irqs = STMPE1601_NR_INTERNAL_IRQS,
+ .enable = stmpe1601_enable,
+ .get_altfunc = stmpe1601_get_altfunc,
+ .enable_autosleep = stmpe1601_autosleep,
+};
+
+/*
+ * STMPE24XX
+ */
+
+static const u8 stmpe24xx_regs[] = {
+ [STMPE_IDX_CHIP_ID] = STMPE24XX_REG_CHIP_ID,
+ [STMPE_IDX_ICR_LSB] = STMPE24XX_REG_ICR_LSB,
+ [STMPE_IDX_IER_LSB] = STMPE24XX_REG_IER_LSB,
+ [STMPE_IDX_ISR_MSB] = STMPE24XX_REG_ISR_MSB,
+ [STMPE_IDX_GPMR_LSB] = STMPE24XX_REG_GPMR_LSB,
+ [STMPE_IDX_GPSR_LSB] = STMPE24XX_REG_GPSR_LSB,
+ [STMPE_IDX_GPCR_LSB] = STMPE24XX_REG_GPCR_LSB,
+ [STMPE_IDX_GPDR_LSB] = STMPE24XX_REG_GPDR_LSB,
+ [STMPE_IDX_GPRER_LSB] = STMPE24XX_REG_GPRER_LSB,
+ [STMPE_IDX_GPFER_LSB] = STMPE24XX_REG_GPFER_LSB,
+ [STMPE_IDX_GPAFR_U_MSB] = STMPE24XX_REG_GPAFR_U_MSB,
+ [STMPE_IDX_IEGPIOR_LSB] = STMPE24XX_REG_IEGPIOR_LSB,
+ [STMPE_IDX_ISGPIOR_MSB] = STMPE24XX_REG_ISGPIOR_MSB,
+ [STMPE_IDX_GPEDR_MSB] = STMPE24XX_REG_GPEDR_MSB,
+};
+
+static struct stmpe_variant_block stmpe24xx_blocks[] = {
+ {
+ .cell = &stmpe_gpio_cell,
+ .irq = STMPE24XX_IRQ_GPIOC,
+ .block = STMPE_BLOCK_GPIO,
+ },
+ {
+ .cell = &stmpe_keypad_cell,
+ .irq = STMPE24XX_IRQ_KEYPAD,
+ .block = STMPE_BLOCK_KEYPAD,
+ },
+};
+
+static int stmpe24xx_enable(struct stmpe *stmpe, unsigned int blocks,
+ bool enable)
+{
+ unsigned int mask = 0;
+
+ if (blocks & STMPE_BLOCK_GPIO)
+ mask |= STMPE24XX_SYS_CTRL_ENABLE_GPIO;
+
+ if (blocks & STMPE_BLOCK_KEYPAD)
+ mask |= STMPE24XX_SYS_CTRL_ENABLE_KPC;
+
+ return __stmpe_set_bits(stmpe, STMPE24XX_REG_SYS_CTRL, mask,
+ enable ? mask : 0);
+}
+
+static int stmpe24xx_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+ switch (block) {
+ case STMPE_BLOCK_ROTATOR:
+ return 2;
+
+ case STMPE_BLOCK_KEYPAD:
+ return 1;
+
+ case STMPE_BLOCK_GPIO:
+ default:
+ return 0;
+ }
+}
+
+static struct stmpe_variant_info stmpe2401 = {
+ .name = "stmpe2401",
+ .id_val = 0x0101,
+ .id_mask = 0xffff,
+ .num_gpios = 24,
+ .af_bits = 2,
+ .regs = stmpe24xx_regs,
+ .blocks = stmpe24xx_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe24xx_blocks),
+ .num_irqs = STMPE24XX_NR_INTERNAL_IRQS,
+ .enable = stmpe24xx_enable,
+ .get_altfunc = stmpe24xx_get_altfunc,
+};
+
+static struct stmpe_variant_info stmpe2403 = {
+ .name = "stmpe2403",
+ .id_val = 0x0120,
+ .id_mask = 0xffff,
+ .num_gpios = 24,
+ .af_bits = 2,
+ .regs = stmpe24xx_regs,
+ .blocks = stmpe24xx_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe24xx_blocks),
+ .num_irqs = STMPE24XX_NR_INTERNAL_IRQS,
+ .enable = stmpe24xx_enable,
+ .get_altfunc = stmpe24xx_get_altfunc,
+ .enable_autosleep = stmpe1601_autosleep, /* same as stmpe1601 */
+};
+
+static struct stmpe_variant_info *stmpe_variant_info[] = {
+ [STMPE811] = &stmpe811,
+ [STMPE1601] = &stmpe1601,
+ [STMPE2401] = &stmpe2401,
+ [STMPE2403] = &stmpe2403,
+};
+
+static irqreturn_t stmpe_irq(int irq, void *data)
+{
+ struct stmpe *stmpe = data;
+ struct stmpe_variant_info *variant = stmpe->variant;
+ int num = DIV_ROUND_UP(variant->num_irqs, 8);
+ u8 israddr = stmpe->regs[STMPE_IDX_ISR_MSB];
+ u8 isr[num];
+ int ret;
+ int i;
+
+ ret = stmpe_block_read(stmpe, israddr, num, isr);
+ if (ret < 0)
+ return IRQ_NONE;
+back:
+ for (i = 0; i < num; i++) {
+ int bank = num - i - 1;
+ u8 status = isr[i];
+ u8 clear;
+
+ status &= stmpe->ier[bank];
+ if (!status)
+ continue;
+
+ clear = status;
+ while (status) {
+ int bit = __ffs(status);
+ int line = bank * 8 + bit;
+
+ handle_nested_irq(stmpe->irq_base + line);
+ status &= ~(1 << bit);
+ }
+
+ stmpe_reg_write(stmpe, israddr + i, clear);
+ }
+
+ /*
+ It may happen that on the first status read interrupt
+ sources may not showup, so read one more time.
+ */
+ ret = stmpe_block_read(stmpe, israddr, num, isr);
+ if (ret >= 0) {
+ for (i = 0; i < num; i++) {
+ int bank = num - i - 1;
+ u8 status = isr[i];
+
+ status &= stmpe->ier[bank];
+ if (status)
+ goto back;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void stmpe_irq_lock(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+
+ mutex_lock(&stmpe->irq_lock);
+}
+
+static void stmpe_irq_sync_unlock(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+ struct stmpe_variant_info *variant = stmpe->variant;
+ int num = DIV_ROUND_UP(variant->num_irqs, 8);
+ int i;
+
+ for (i = 0; i < num; i++) {
+ u8 new = stmpe->ier[i];
+ u8 old = stmpe->oldier[i];
+
+ if (new == old)
+ continue;
+
+ stmpe->oldier[i] = new;
+ stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_IER_LSB] - i, new);
+ }
+
+ mutex_unlock(&stmpe->irq_lock);
+}
+
+static void stmpe_irq_mask(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+ int offset = irq - stmpe->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe->ier[regoffset] &= ~mask;
+}
+
+static void stmpe_irq_unmask(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+ int offset = irq - stmpe->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe->ier[regoffset] |= mask;
+}
+
+static struct irq_chip stmpe_irq_chip = {
+ .name = "stmpe",
+ .bus_lock = stmpe_irq_lock,
+ .bus_sync_unlock = stmpe_irq_sync_unlock,
+ .mask = stmpe_irq_mask,
+ .unmask = stmpe_irq_unmask,
+};
+
+static int __devinit stmpe_irq_init(struct stmpe *stmpe)
+{
+ int num_irqs = stmpe->variant->num_irqs;
+ int base = stmpe->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + num_irqs; irq++) {
+ set_irq_chip_data(irq, stmpe);
+ set_irq_chip_and_handler(irq, &stmpe_irq_chip,
+ handle_edge_irq);
+ set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+
+ return 0;
+}
+
+static void stmpe_irq_remove(struct stmpe *stmpe)
+{
+ int num_irqs = stmpe->variant->num_irqs;
+ int base = stmpe->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + num_irqs; irq++) {
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ set_irq_chip_and_handler(irq, NULL, NULL);
+ set_irq_chip_data(irq, NULL);
+ }
+}
+
+static int __devinit stmpe_chip_init(struct stmpe *stmpe)
+{
+ unsigned int irq_trigger = stmpe->pdata->irq_trigger;
+ int autosleep_timeout = stmpe->pdata->autosleep_timeout;
+ struct stmpe_variant_info *variant = stmpe->variant;
+ u8 icr = STMPE_ICR_LSB_GIM;
+ unsigned int id;
+ u8 data[2];
+ int ret;
+
+ ret = stmpe_block_read(stmpe, stmpe->regs[STMPE_IDX_CHIP_ID],
+ ARRAY_SIZE(data), data);
+ if (ret < 0)
+ return ret;
+
+ id = (data[0] << 8) | data[1];
+ if ((id & variant->id_mask) != variant->id_val) {
+ dev_err(stmpe->dev, "unknown chip id: %#x\n", id);
+ return -EINVAL;
+ }
+
+ dev_info(stmpe->dev, "%s detected, chip id: %#x\n", variant->name, id);
+
+ /* Disable all modules -- subdrivers should enable what they need. */
+ ret = stmpe_disable(stmpe, ~0);
+ if (ret)
+ return ret;
+
+ if (irq_trigger == IRQF_TRIGGER_FALLING ||
+ irq_trigger == IRQF_TRIGGER_RISING)
+ icr |= STMPE_ICR_LSB_EDGE;
+
+ if (irq_trigger == IRQF_TRIGGER_RISING ||
+ irq_trigger == IRQF_TRIGGER_HIGH)
+ icr |= STMPE_ICR_LSB_HIGH;
+
+ if (stmpe->pdata->irq_invert_polarity)
+ icr ^= STMPE_ICR_LSB_HIGH;
+
+ if (stmpe->pdata->autosleep) {
+ ret = stmpe_autosleep(stmpe, autosleep_timeout);
+ if (ret)
+ return ret;
+ }
+
+ return stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_ICR_LSB], icr);
+}
+
+static int __devinit stmpe_add_device(struct stmpe *stmpe,
+ struct mfd_cell *cell, int irq)
+{
+ return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1,
+ NULL, stmpe->irq_base + irq);
+}
+
+static int __devinit stmpe_devices_init(struct stmpe *stmpe)
+{
+ struct stmpe_variant_info *variant = stmpe->variant;
+ unsigned int platform_blocks = stmpe->pdata->blocks;
+ int ret = -EINVAL;
+ int i;
+
+ for (i = 0; i < variant->num_blocks; i++) {
+ struct stmpe_variant_block *block = &variant->blocks[i];
+
+ if (!(platform_blocks & block->block))
+ continue;
+
+ platform_blocks &= ~block->block;
+ ret = stmpe_add_device(stmpe, block->cell, block->irq);
+ if (ret)
+ return ret;
+ }
+
+ if (platform_blocks)
+ dev_warn(stmpe->dev,
+ "platform wants blocks (%#x) not present on variant",
+ platform_blocks);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int stmpe_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+
+ if (device_may_wakeup(&i2c->dev))
+ enable_irq_wake(i2c->irq);
+
+ return 0;
+}
+
+static int stmpe_resume(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+
+ if (device_may_wakeup(&i2c->dev))
+ disable_irq_wake(i2c->irq);
+
+ return 0;
+}
+#endif
+
+static int __devinit stmpe_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct stmpe_platform_data *pdata = i2c->dev.platform_data;
+ struct stmpe *stmpe;
+ int ret;
+
+ if (!pdata)
+ return -EINVAL;
+
+ stmpe = kzalloc(sizeof(struct stmpe), GFP_KERNEL);
+ if (!stmpe)
+ return -ENOMEM;
+
+ mutex_init(&stmpe->irq_lock);
+ mutex_init(&stmpe->lock);
+
+ stmpe->dev = &i2c->dev;
+ stmpe->i2c = i2c;
+
+ stmpe->pdata = pdata;
+ stmpe->irq_base = pdata->irq_base;
+
+ stmpe->partnum = id->driver_data;
+ stmpe->variant = stmpe_variant_info[stmpe->partnum];
+ stmpe->regs = stmpe->variant->regs;
+ stmpe->num_gpios = stmpe->variant->num_gpios;
+
+ i2c_set_clientdata(i2c, stmpe);
+
+ ret = stmpe_chip_init(stmpe);
+ if (ret)
+ goto out_free;
+
+ ret = stmpe_irq_init(stmpe);
+ if (ret)
+ goto out_free;
+
+ ret = request_threaded_irq(stmpe->i2c->irq, NULL, stmpe_irq,
+ pdata->irq_trigger | IRQF_ONESHOT,
+ "stmpe", stmpe);
+ if (ret) {
+ dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret);
+ goto out_removeirq;
+ }
+
+ ret = stmpe_devices_init(stmpe);
+ if (ret) {
+ dev_err(stmpe->dev, "failed to add children\n");
+ goto out_removedevs;
+ }
+
+ return 0;
+
+out_removedevs:
+ mfd_remove_devices(stmpe->dev);
+ free_irq(stmpe->i2c->irq, stmpe);
+out_removeirq:
+ stmpe_irq_remove(stmpe);
+out_free:
+ kfree(stmpe);
+ return ret;
+}
+
+static int __devexit stmpe_remove(struct i2c_client *client)
+{
+ struct stmpe *stmpe = i2c_get_clientdata(client);
+
+ mfd_remove_devices(stmpe->dev);
+
+ free_irq(stmpe->i2c->irq, stmpe);
+ stmpe_irq_remove(stmpe);
+
+ kfree(stmpe);
+
+ return 0;
+}
+
+static const struct i2c_device_id stmpe_id[] = {
+ { "stmpe811", STMPE811 },
+ { "stmpe1601", STMPE1601 },
+ { "stmpe2401", STMPE2401 },
+ { "stmpe2403", STMPE2403 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, stmpe_id);
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops stmpe_dev_pm_ops = {
+ .suspend = stmpe_suspend,
+ .resume = stmpe_resume,
+};
+#endif
+
+static struct i2c_driver stmpe_driver = {
+ .driver.name = "stmpe",
+ .driver.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .driver.pm = &stmpe_dev_pm_ops,
+#endif
+ .probe = stmpe_probe,
+ .remove = __devexit_p(stmpe_remove),
+ .id_table = stmpe_id,
+};
+
+static int __init stmpe_init(void)
+{
+ return i2c_add_driver(&stmpe_driver);
+}
+subsys_initcall(stmpe_init);
+
+static void __exit stmpe_exit(void)
+{
+ i2c_del_driver(&stmpe_driver);
+}
+module_exit(stmpe_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPE MFD core driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h
new file mode 100644
index 00000000000..0dbdc4e8cd7
--- /dev/null
+++ b/drivers/mfd/stmpe.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#ifndef __STMPE_H
+#define __STMPE_H
+
+#ifdef STMPE_DUMP_BYTES
+static inline void stmpe_dump_bytes(const char *str, const void *buf,
+ size_t len)
+{
+ print_hex_dump_bytes(str, DUMP_PREFIX_OFFSET, buf, len);
+}
+#else
+static inline void stmpe_dump_bytes(const char *str, const void *buf,
+ size_t len)
+{
+}
+#endif
+
+/**
+ * struct stmpe_variant_block - information about block
+ * @cell: base mfd cell
+ * @irq: interrupt number to be added to each IORESOURCE_IRQ
+ * in the cell
+ * @block: block id; used for identification with platform data and for
+ * enable and altfunc callbacks
+ */
+struct stmpe_variant_block {
+ struct mfd_cell *cell;
+ int irq;
+ enum stmpe_block block;
+};
+
+/**
+ * struct stmpe_variant_info - variant-specific information
+ * @name: part name
+ * @id_val: content of CHIPID register
+ * @id_mask: bits valid in CHIPID register for comparison with id_val
+ * @num_gpios: number of GPIOS
+ * @af_bits: number of bits used to specify the alternate function
+ * @blocks: list of blocks present on this device
+ * @num_blocks: number of blocks present on this device
+ * @num_irqs: number of internal IRQs available on this device
+ * @enable: callback to enable the specified blocks.
+ * Called with the I/O lock held.
+ * @get_altfunc: callback to get the alternate function number for the
+ * specific block
+ * @enable_autosleep: callback to configure autosleep with specified timeout
+ */
+struct stmpe_variant_info {
+ const char *name;
+ u16 id_val;
+ u16 id_mask;
+ int num_gpios;
+ int af_bits;
+ const u8 *regs;
+ struct stmpe_variant_block *blocks;
+ int num_blocks;
+ int num_irqs;
+ int (*enable)(struct stmpe *stmpe, unsigned int blocks, bool enable);
+ int (*get_altfunc)(struct stmpe *stmpe, enum stmpe_block block);
+ int (*enable_autosleep)(struct stmpe *stmpe, int autosleep_timeout);
+};
+
+#define STMPE_ICR_LSB_HIGH (1 << 2)
+#define STMPE_ICR_LSB_EDGE (1 << 1)
+#define STMPE_ICR_LSB_GIM (1 << 0)
+
+/*
+ * STMPE811
+ */
+
+#define STMPE811_IRQ_TOUCH_DET 0
+#define STMPE811_IRQ_FIFO_TH 1
+#define STMPE811_IRQ_FIFO_OFLOW 2
+#define STMPE811_IRQ_FIFO_FULL 3
+#define STMPE811_IRQ_FIFO_EMPTY 4
+#define STMPE811_IRQ_TEMP_SENS 5
+#define STMPE811_IRQ_ADC 6
+#define STMPE811_IRQ_GPIOC 7
+#define STMPE811_NR_INTERNAL_IRQS 8
+
+#define STMPE811_REG_CHIP_ID 0x00
+#define STMPE811_REG_SYS_CTRL2 0x04
+#define STMPE811_REG_INT_CTRL 0x09
+#define STMPE811_REG_INT_EN 0x0A
+#define STMPE811_REG_INT_STA 0x0B
+#define STMPE811_REG_GPIO_INT_EN 0x0C
+#define STMPE811_REG_GPIO_INT_STA 0x0D
+#define STMPE811_REG_GPIO_SET_PIN 0x10
+#define STMPE811_REG_GPIO_CLR_PIN 0x11
+#define STMPE811_REG_GPIO_MP_STA 0x12
+#define STMPE811_REG_GPIO_DIR 0x13
+#define STMPE811_REG_GPIO_ED 0x14
+#define STMPE811_REG_GPIO_RE 0x15
+#define STMPE811_REG_GPIO_FE 0x16
+#define STMPE811_REG_GPIO_AF 0x17
+
+#define STMPE811_SYS_CTRL2_ADC_OFF (1 << 0)
+#define STMPE811_SYS_CTRL2_TSC_OFF (1 << 1)
+#define STMPE811_SYS_CTRL2_GPIO_OFF (1 << 2)
+#define STMPE811_SYS_CTRL2_TS_OFF (1 << 3)
+
+/*
+ * STMPE1601
+ */
+
+#define STMPE1601_IRQ_GPIOC 8
+#define STMPE1601_IRQ_PWM3 7
+#define STMPE1601_IRQ_PWM2 6
+#define STMPE1601_IRQ_PWM1 5
+#define STMPE1601_IRQ_PWM0 4
+#define STMPE1601_IRQ_KEYPAD_OVER 2
+#define STMPE1601_IRQ_KEYPAD 1
+#define STMPE1601_IRQ_WAKEUP 0
+#define STMPE1601_NR_INTERNAL_IRQS 9
+
+#define STMPE1601_REG_SYS_CTRL 0x02
+#define STMPE1601_REG_SYS_CTRL2 0x03
+#define STMPE1601_REG_ICR_LSB 0x11
+#define STMPE1601_REG_IER_LSB 0x13
+#define STMPE1601_REG_ISR_MSB 0x14
+#define STMPE1601_REG_CHIP_ID 0x80
+#define STMPE1601_REG_INT_EN_GPIO_MASK_LSB 0x17
+#define STMPE1601_REG_INT_STA_GPIO_MSB 0x18
+#define STMPE1601_REG_GPIO_MP_LSB 0x87
+#define STMPE1601_REG_GPIO_SET_LSB 0x83
+#define STMPE1601_REG_GPIO_CLR_LSB 0x85
+#define STMPE1601_REG_GPIO_SET_DIR_LSB 0x89
+#define STMPE1601_REG_GPIO_ED_MSB 0x8A
+#define STMPE1601_REG_GPIO_RE_LSB 0x8D
+#define STMPE1601_REG_GPIO_FE_LSB 0x8F
+#define STMPE1601_REG_GPIO_AF_U_MSB 0x92
+
+#define STMPE1601_SYS_CTRL_ENABLE_GPIO (1 << 3)
+#define STMPE1601_SYS_CTRL_ENABLE_KPC (1 << 1)
+#define STMPE1601_SYSCON_ENABLE_SPWM (1 << 0)
+
+/* The 1601/2403 share the same masks */
+#define STMPE1601_AUTOSLEEP_TIMEOUT_MASK (0x7)
+#define STPME1601_AUTOSLEEP_ENABLE (1 << 3)
+
+/*
+ * STMPE24xx
+ */
+
+#define STMPE24XX_IRQ_GPIOC 8
+#define STMPE24XX_IRQ_PWM2 7
+#define STMPE24XX_IRQ_PWM1 6
+#define STMPE24XX_IRQ_PWM0 5
+#define STMPE24XX_IRQ_ROT_OVER 4
+#define STMPE24XX_IRQ_ROT 3
+#define STMPE24XX_IRQ_KEYPAD_OVER 2
+#define STMPE24XX_IRQ_KEYPAD 1
+#define STMPE24XX_IRQ_WAKEUP 0
+#define STMPE24XX_NR_INTERNAL_IRQS 9
+
+#define STMPE24XX_REG_SYS_CTRL 0x02
+#define STMPE24XX_REG_ICR_LSB 0x11
+#define STMPE24XX_REG_IER_LSB 0x13
+#define STMPE24XX_REG_ISR_MSB 0x14
+#define STMPE24XX_REG_CHIP_ID 0x80
+#define STMPE24XX_REG_IEGPIOR_LSB 0x18
+#define STMPE24XX_REG_ISGPIOR_MSB 0x19
+#define STMPE24XX_REG_GPMR_LSB 0xA5
+#define STMPE24XX_REG_GPSR_LSB 0x85
+#define STMPE24XX_REG_GPCR_LSB 0x88
+#define STMPE24XX_REG_GPDR_LSB 0x8B
+#define STMPE24XX_REG_GPEDR_MSB 0x8C
+#define STMPE24XX_REG_GPRER_LSB 0x91
+#define STMPE24XX_REG_GPFER_LSB 0x94
+#define STMPE24XX_REG_GPAFR_U_MSB 0x9B
+
+#define STMPE24XX_SYS_CTRL_ENABLE_GPIO (1 << 3)
+#define STMPE24XX_SYSCON_ENABLE_PWM (1 << 2)
+#define STMPE24XX_SYS_CTRL_ENABLE_KPC (1 << 1)
+#define STMPE24XX_SYSCON_ENABLE_ROT (1 << 0)
+
+#endif
diff --git a/drivers/mfd/tc35892.c b/drivers/mfd/tc35892.c
index e619e2a5599..ddea55210b7 100644
--- a/drivers/mfd/tc35892.c
+++ b/drivers/mfd/tc35892.c
@@ -14,6 +14,9 @@
#include <linux/mfd/core.h>
#include <linux/mfd/tc35892.h>
+#define TC35892_CLKMODE_MODCTL_SLEEP 0x0
+#define TC35892_CLKMODE_MODCTL_OPERATION (1 << 0)
+
/**
* tc35892_reg_read() - read a single TC35892 register
* @tc35892: Device to read from
@@ -142,6 +145,7 @@ static irqreturn_t tc35892_irq(int irq, void *data)
struct tc35892 *tc35892 = data;
int status;
+again:
status = tc35892_reg_read(tc35892, TC35892_IRQST);
if (status < 0)
return IRQ_NONE;
@@ -156,9 +160,12 @@ static irqreturn_t tc35892_irq(int irq, void *data)
/*
* A dummy read or write (to any register) appears to be necessary to
* have the last interrupt clear (for example, GPIO IC write) take
- * effect.
+ * effect. In such a case, recheck for any interrupt which is still
+ * pending.
*/
- tc35892_reg_read(tc35892, TC35892_IRQST);
+ status = tc35892_reg_read(tc35892, TC35892_IRQST);
+ if (status)
+ goto again;
return IRQ_HANDLED;
}
@@ -227,12 +234,15 @@ static int tc35892_chip_init(struct tc35892 *tc35892)
dev_info(tc35892->dev, "manufacturer: %#x, version: %#x\n", manf, ver);
- /* Put everything except the IRQ module into reset */
+ /*
+ * Put everything except the IRQ module into reset;
+ * also spare the GPIO module for any pin initialization
+ * done during pre-kernel boot
+ */
ret = tc35892_reg_write(tc35892, TC35892_RSTCTRL,
TC35892_RSTCTRL_TIMRST
| TC35892_RSTCTRL_ROTRST
- | TC35892_RSTCTRL_KBDRST
- | TC35892_RSTCTRL_GPIRST);
+ | TC35892_RSTCTRL_KBDRST);
if (ret < 0)
return ret;
@@ -314,6 +324,151 @@ static int __devexit tc35892_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
+
+static u32 sleep_regs[] = {
+ TC35892_IOPC0_L,
+ TC35892_IOPC0_H,
+ TC35892_IOPC1_L,
+ TC35892_IOPC1_H,
+ TC35892_IOPC2_L,
+ TC35892_IOPC2_H,
+ TC35892_DRIVE0_L,
+ TC35892_DRIVE0_H,
+ TC35892_DRIVE1_L,
+ TC35892_DRIVE1_H,
+ TC35892_DRIVE2_L,
+ TC35892_DRIVE2_H,
+ TC35892_DRIVE3,
+ TC35892_GPIODATA0,
+ TC35892_GPIOMASK0,
+ TC35892_GPIODATA1,
+ TC35892_GPIOMASK1,
+ TC35892_GPIODATA2,
+ TC35892_GPIOMASK2,
+ TC35892_GPIODIR0,
+ TC35892_GPIODIR1,
+ TC35892_GPIODIR2,
+ TC35892_GPIOIE0,
+ TC35892_GPIOIE1,
+ TC35892_GPIOIE2,
+ TC35892_RSTCTRL,
+ TC35892_CLKCFG,
+};
+
+static u8 sleep_regs_val[] = {
+ 0x00, /* TC35892_IOPC0_L */
+ 0x00, /* TC35892_IOPC0_H */
+ 0x00, /* TC35892_IOPC1_L */
+ 0x00, /* TC35892_IOPC1_H */
+ 0x00, /* TC35892_IOPC2_L */
+ 0x00, /* TC35892_IOPC2_H */
+ 0xff, /* TC35892_DRIVE0_L */
+ 0xff, /* TC35892_DRIVE0_H */
+ 0xff, /* TC35892_DRIVE1_L */
+ 0xff, /* TC35892_DRIVE1_H */
+ 0xff, /* TC35892_DRIVE2_L */
+ 0xff, /* TC35892_DRIVE2_H */
+ 0x0f, /* TC35892_DRIVE3 */
+ 0x80, /* TC35892_GPIODATA0 */
+ 0x80, /* TC35892_GPIOMASK0 */
+ 0x80, /* TC35892_GPIODATA1 */
+ 0x80, /* TC35892_GPIOMASK1 */
+ 0x06, /* TC35892_GPIODATA2 */
+ 0x06, /* TC35892_GPIOMASK2 */
+ 0xf0, /* TC35892_GPIODIR0 */
+ 0xe0, /* TC35892_GPIODIR1 */
+ 0xee, /* TC35892_GPIODIR2 */
+ 0x0f, /* TC35892_GPIOIE0 */
+ 0x1f, /* TC35892_GPIOIE1 */
+ 0x11, /* TC35892_GPIOIE2 */
+ 0x0f, /* TC35892_RSTCTRL */
+ 0xb0 /* TC35892_CLKCFG */
+
+};
+
+static u8 sleep_regs_backup[ARRAY_SIZE(sleep_regs)];
+
+static int tc35892_suspend(struct device *dev)
+{
+ struct tc35892 *tc35892 = dev_get_drvdata(dev);
+ struct i2c_client *client = tc35892->i2c;
+ int ret = 0;
+ int i, j;
+ int val;
+
+ /* Put the system to sleep mode */
+ if (!device_may_wakeup(&client->dev)) {
+ for (i = 0; i < ARRAY_SIZE(sleep_regs); i++) {
+ val = tc35892_reg_read(tc35892,
+ sleep_regs[i]);
+ if (val < 0)
+ goto out;
+
+ sleep_regs_backup[i] = (u8) (val & 0xff);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sleep_regs); i++) {
+ ret = tc35892_reg_write(tc35892,
+ sleep_regs[i],
+ sleep_regs_val[i]);
+ if (ret < 0)
+ goto fail;
+
+ }
+
+ ret = tc35892_reg_write(tc35892,
+ TC35892_CLKMODE,
+ TC35892_CLKMODE_MODCTL_SLEEP);
+ }
+out:
+ return ret;
+fail:
+ for (j = 0; j <= i; j++) {
+ ret = tc35892_reg_write(tc35892,
+ sleep_regs[i],
+ sleep_regs_backup[i]);
+ if (ret < 0)
+ break;
+ }
+ return ret;
+}
+
+static int tc35892_resume(struct device *dev)
+{
+ struct tc35892 *tc35892 = dev_get_drvdata(dev);
+ struct i2c_client *client = tc35892->i2c;
+ int ret = 0;
+ int i;
+
+ /* Enable the system into operation */
+ if (!device_may_wakeup(&client->dev))
+ {
+ ret = tc35892_reg_write(tc35892,
+ TC35892_CLKMODE,
+ TC35892_CLKMODE_MODCTL_OPERATION);
+ if (ret < 0)
+ goto out;
+
+ for (i = ARRAY_SIZE(sleep_regs) - 1; i >= 0; i--) {
+ ret = tc35892_reg_write(tc35892,
+ sleep_regs[i],
+ sleep_regs_backup[i]);
+ /* Not much to do here if we fail */
+ if (ret < 0)
+ break;
+ }
+ }
+out:
+ return ret;
+}
+
+static const struct dev_pm_ops tc35892_dev_pm_ops = {
+ .suspend = tc35892_suspend,
+ .resume = tc35892_resume,
+};
+#endif
+
static const struct i2c_device_id tc35892_id[] = {
{ "tc35892", 24 },
{ }
@@ -323,6 +478,9 @@ MODULE_DEVICE_TABLE(i2c, tc35892_id);
static struct i2c_driver tc35892_driver = {
.driver.name = "tc35892",
.driver.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .driver.pm = &tc35892_dev_pm_ops,
+#endif
.probe = tc35892_probe,
.remove = __devexit_p(tc35892_remove),
.id_table = tc35892_id,
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 26386a92f5a..942a7a280cd 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -53,6 +53,10 @@ config AD525X_DPOT_SPI
To compile this driver as a module, choose M here: the
module will be called ad525x_dpot-spi.
+config ANDROID_PMEM
+ bool "Android pmem allocator"
+ default y
+
config ATMEL_PWM
tristate "Atmel AT32/AT91 PWM support"
depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9
@@ -62,6 +66,15 @@ config ATMEL_PWM
purposes including software controlled power-efficient backlights
on LCD displays, motor control, and waveform generation.
+config AB8500_PWM
+ bool "AB8500 PWM support"
+ depends on AB8500_CORE
+ select HAVE_PWM
+ help
+ This driver exports functions to enable/disble/config/free Pulse
+ Width Modulation in the Analog Baseband Chip AB8500.
+ It is used by led and backlight driver to control the intensity.
+
config ATMEL_TCLIB
bool "Atmel AT32/AT91 Timer/Counter Library"
depends on (AVR32 || ARCH_AT91)
@@ -199,6 +212,13 @@ config ENCLOSURE_SERVICES
driver (SCSI/ATA) which supports enclosures
or a SCSI enclosure device (SES) to use these services.
+config KERNEL_DEBUGGER_CORE
+ bool "Kernel Debugger Core"
+ default n
+ ---help---
+ Generic kernel debugging command processor used by low level
+ (interrupt context) platform-specific debuggers.
+
config SGI_XP
tristate "Support communication between SGI SSIs"
depends on NET
@@ -304,6 +324,24 @@ config SENSORS_TSL2550
This driver can also be built as a module. If so, the module
will be called tsl2550.
+config SENSORS_AK8975
+ tristate "AK8975 compass support"
+ default n
+ depends on I2C
+ help
+ If you say yes here you get support for Asahi Kasei's
+ orientation sensor AK8975.
+
+config SENSORS_BH1780
+ tristate "ROHM BH1780GLI ambient light sensor"
+ depends on I2C && SYSFS
+ help
+ If you say yes here you get support for the ROHM BH1780GLI
+ ambient light sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called bh1780gli.
+
config EP93XX_PWM
tristate "EP93xx PWM support"
depends on ARCH_EP93XX
@@ -337,6 +375,14 @@ config TI_DAC7512
This driver can also be built as a module. If so, the module
will be calles ti_dac7512.
+config UID_STAT
+ bool "UID based statistics tracking exported to /proc/uid_stat"
+ default n
+
+config IFACE_STAT
+ bool "Persistent interface statistics tracking exported to /proc/iface_stat"
+ default n
+
config VMWARE_BALLOON
tristate "VMware Balloon Driver"
depends on X86
@@ -353,9 +399,90 @@ config VMWARE_BALLOON
To compile this driver as a module, choose M here: the
module will be called vmware_balloon.
+config WL127X_RFKILL
+ tristate "Bluetooth power control driver for TI wl127x"
+ depends on RFKILL
+ default n
+ ---help---
+ Creates an rfkill entry in sysfs for power control of Bluetooth
+ TI wl127x chips.
+
+config APANIC
+ bool "Android kernel panic diagnostics driver"
+ default n
+ ---help---
+ Driver which handles kernel panics and attempts to write
+ critical debugging data to flash.
+
+config APANIC_PLABEL
+ string "Android panic dump flash partition label"
+ depends on APANIC
+ default "kpanic"
+ ---help---
+ If your platform uses a different flash partition label for storing
+ crashdumps, enter it here.
+
+config ARM_CHARLCD
+ bool "ARM Ltd. Character LCD Driver"
+ depends on PLAT_VERSATILE
+ help
+ This is a driver for the character LCD found on the ARM Ltd.
+ Versatile and RealView Platform Baseboards. It doesn't do
+ very much more than display the text "ARM Linux" on the first
+ line and the Linux version on the second line, but that's
+ still useful.
+
+config STE_TRACE_MODEM
+ tristate "DB8500 trace Modem"
+ depends on ARCH_U8500
+ default n
+ help
+ Select this option to enable modem tracing by APE
+
+config HWMEM
+ bool "Hardware memory driver"
+ default n
+ help
+ This driver provides a way to allocate contiguous system memory which
+ can be used by hardware. It also enables accessing hwmem allocated
+ memory buffers through a secure id which can be shared across processes.
+
+config DBX500_MLOADER
+ tristate "Modem firmware loader for db8500"
+ default n
+ depends on UX500_SOC_DB8500 || UX500_SOC_DB5500
+ help
+ Provides a user interface to load modem firmware on dbx500 SOCs
+
+config STM_TRACE
+ bool "Ux500 STM Trace driver"
+ depends on ARCH_U8500
+ default n
+ help
+ Simple System Trace Module driver. It allows to use and configure the
+ STM, either from kernel space, or from user space.
+
+config DISPDEV
+ bool "Display overlay device"
+ default n
+ help
+ This driver provides a way to use a second overlay for a display (in
+ addition to the framebuffer). The device allows for registration of
+ userspace buffers to be used with the overlay.
+
+config U5500_MBOX
+ bool "Mailbox support"
+ depends on (UX500_SOC_DB5500 && U5500_MODEM_IRQ)
+ default y
+ help
+ Add support for U5500 mailbox communication with modem side
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
source "drivers/misc/iwmc3200top/Kconfig"
+source "drivers/misc/i2s/Kconfig"
+source "drivers/misc/shrm/Kconfig"
+source "drivers/misc/audio_io_dev/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 6ed06a19474..ef21612c261 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -14,8 +14,11 @@ obj-$(CONFIG_LKDTM) += lkdtm.o
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
obj-$(CONFIG_PHANTOM) += phantom.o
+obj-$(CONFIG_ANDROID_PMEM) += pmem.o
+obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o
obj-$(CONFIG_SGI_IOC4) += ioc4.o
obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
+obj-$(CONFIG_KERNEL_DEBUGGER_CORE) += kernel_debugger.o
obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
obj-$(CONFIG_SGI_XP) += sgi-xp/
obj-$(CONFIG_SGI_GRU) += sgi-gru/
@@ -26,8 +29,24 @@ obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
obj-$(CONFIG_DS1682) += ds1682.o
obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o
+obj-$(CONFIG_UID_STAT) += uid_stat.o
+obj-$(CONFIG_IFACE_STAT) += iface_stat.o
obj-$(CONFIG_C2PORT) += c2port/
obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
obj-y += eeprom/
obj-y += cb710/
obj-$(CONFIG_VMWARE_BALLOON) += vmware_balloon.o
+obj-$(CONFIG_WL127X_RFKILL) += wl127x-rfkill.o
+obj-$(CONFIG_APANIC) += apanic.o
+obj-$(CONFIG_SENSORS_AK8975) += akm8975.o
+obj-$(CONFIG_STM_I2S) += i2s/
+obj-$(CONFIG_STE_TRACE_MODEM) += db8500-modem-trace.o
+obj-$(CONFIG_U8500_SHRM) += shrm/
+obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
+obj-$(CONFIG_STE_AUDIO_IO_DEV) += audio_io_dev/
+obj-$(CONFIG_HWMEM) += hwmem/
+obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
+obj-$(CONFIG_DBX500_MLOADER) += dbx500-mloader.o
+obj-$(CONFIG_STM_TRACE) += stm.o
+obj-$(CONFIG_DISPDEV) += dispdev/
+obj-$(CONFIG_U5500_MBOX) += mbox.o mbox_channels-db5500.o
diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c
new file mode 100644
index 00000000000..e56d9c993da
--- /dev/null
+++ b/drivers/misc/ab8500-pwm.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Arun R Murthy <arun.murthy@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/pwm.h>
+#include <linux/clk.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+
+/*
+ * PWM Out generators
+ * Bank: 0x10
+ */
+#define AB8500_PWM_OUT_CTRL1_REG 0x60
+#define AB8500_PWM_OUT_CTRL2_REG 0x61
+#define AB8500_PWM_OUT_CTRL7_REG 0x66
+
+/* backlight driver constants */
+#define ENABLE_PWM 1
+#define DISABLE_PWM 0
+
+struct pwm_device {
+ struct device *dev;
+ struct list_head node;
+ struct clk *clk;
+ const char *label;
+ unsigned int pwm_id;
+ bool clk_enabled;
+};
+
+static LIST_HEAD(pwm_list);
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ int ret = 0;
+ unsigned int higher_val, lower_val;
+ u8 reg;
+
+ /*
+ * get the first 8 bits that are be written to
+ * AB8500_PWM_OUT_CTRL1_REG[0:7]
+ */
+ lower_val = duty_ns & 0x00FF;
+ /*
+ * get bits [9:10] that are to be written to
+ * AB8500_PWM_OUT_CTRL2_REG[0:1]
+ */
+ higher_val = ((duty_ns & 0x0300) >> 8);
+
+ reg = AB8500_PWM_OUT_CTRL1_REG + ((pwm->pwm_id - 1) * 2);
+
+ ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
+ reg, (u8)lower_val);
+ if (ret < 0)
+ return ret;
+ ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
+ (reg + 1), (u8)higher_val);
+
+ return ret;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+ int ret;
+
+ if (!pwm->clk_enabled) {
+ ret = clk_enable(pwm->clk);
+ if (ret < 0) {
+ dev_err(pwm->dev, "failed to enable clock\n");
+ return ret;
+ }
+ pwm->clk_enabled = true;
+ }
+ ret = abx500_mask_and_set_register_interruptible(pwm->dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+ 1 << (pwm->pwm_id-1), 1 << (pwm->pwm_id-1));
+ if (ret < 0)
+ dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n",
+ pwm->label, ret);
+ return ret;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+ int ret;
+
+ ret = abx500_mask_and_set_register_interruptible(pwm->dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+ 1 << (pwm->pwm_id-1), DISABLE_PWM);
+ /*
+ * Workaround to set PWM in disable.
+ * If enable bit is not toggled the PWM might output 50/50 duty cycle
+ * even though it should be disabled
+ */
+ ret &= abx500_mask_and_set_register_interruptible(pwm->dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+ 1 << (pwm->pwm_id-1),
+ ENABLE_PWM << (pwm->pwm_id-1));
+ ret &= abx500_mask_and_set_register_interruptible(pwm->dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+ 1 << (pwm->pwm_id-1), DISABLE_PWM);
+
+ if (ret < 0)
+ dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n",
+ pwm->label, ret);
+ if (pwm->clk_enabled) {
+ clk_disable(pwm->clk);
+ pwm->clk_enabled = false;
+ }
+
+ return;
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+ struct pwm_device *pwm;
+
+ list_for_each_entry(pwm, &pwm_list, node) {
+ if (pwm->pwm_id == pwm_id) {
+ pwm->label = label;
+ pwm->pwm_id = pwm_id;
+ return pwm;
+ }
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+ pwm_disable(pwm);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static int __devinit ab8500_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_device *pwm;
+ int ret = 0;
+
+ /*
+ * Nothing to be done in probe, this is required to get the
+ * device which is required for ab8500 read and write
+ */
+ pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+ if (pwm == NULL) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+ pwm->dev = &pdev->dev;
+ pwm->pwm_id = pdev->id;
+ list_add_tail(&pwm->node, &pwm_list);
+ platform_set_drvdata(pdev, pwm);
+
+ pwm->clk = clk_get(pwm->dev, NULL);
+ if (IS_ERR(pwm->clk)) {
+ dev_err(pwm->dev, "clock request failed\n");
+ ret = PTR_ERR(pwm->clk);
+ kfree(pwm);
+ return ret;
+ }
+ pwm->clk_enabled = false;
+ dev_dbg(pwm->dev, "pwm probe successful\n");
+ return ret;
+}
+
+static int __devexit ab8500_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_device *pwm = platform_get_drvdata(pdev);
+ list_del(&pwm->node);
+ clk_put(pwm->clk);
+ dev_dbg(&pdev->dev, "pwm driver removed\n");
+ kfree(pwm);
+ return 0;
+}
+
+static struct platform_driver ab8500_pwm_driver = {
+ .driver = {
+ .name = "ab8500-pwm",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab8500_pwm_probe,
+ .remove = __devexit_p(ab8500_pwm_remove),
+};
+
+static int __init ab8500_pwm_init(void)
+{
+ return platform_driver_register(&ab8500_pwm_driver);
+}
+
+static void __exit ab8500_pwm_exit(void)
+{
+ platform_driver_unregister(&ab8500_pwm_driver);
+}
+
+subsys_initcall(ab8500_pwm_init);
+module_exit(ab8500_pwm_exit);
+MODULE_AUTHOR("Arun MURTHY <arun.murthy@stericsson.com>");
+MODULE_DESCRIPTION("AB8500 Pulse Width Modulation Driver");
+MODULE_ALIAS("AB8500 PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/akm8975.c b/drivers/misc/akm8975.c
new file mode 100644
index 00000000000..830d2897afd
--- /dev/null
+++ b/drivers/misc/akm8975.c
@@ -0,0 +1,732 @@
+/* drivers/misc/akm8975.c - akm8975 compass driver
+ *
+ * Copyright (C) 2007-2008 HTC Corporation.
+ * Author: Hou-Kun Chen <houkun.chen@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Revised by AKM 2009/04/02
+ * Revised by Motorola 2010/05/27
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <linux/gpio.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/freezer.h>
+#include <linux/akm8975.h>
+#include <linux/earlysuspend.h>
+
+#define AK8975DRV_CALL_DBG 0
+#if AK8975DRV_CALL_DBG
+#define FUNCDBG(msg) pr_err("%s:%s\n", __func__, msg);
+#else
+#define FUNCDBG(msg)
+#endif
+
+#define AK8975DRV_DATA_DBG 0
+#define MAX_FAILURE_COUNT 10
+
+struct akm8975_data {
+ struct i2c_client *this_client;
+ struct akm8975_platform_data *pdata;
+ struct input_dev *input_dev;
+ struct work_struct work;
+ struct mutex flags_lock;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+};
+
+/*
+* Because misc devices can not carry a pointer from driver register to
+* open, we keep this global. This limits the driver to a single instance.
+*/
+struct akm8975_data *akmd_data;
+
+static DECLARE_WAIT_QUEUE_HEAD(open_wq);
+
+static atomic_t open_flag;
+
+static short m_flag;
+static short a_flag;
+static short t_flag;
+static short mv_flag;
+
+static short akmd_delay;
+
+static ssize_t akm8975_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ return sprintf(buf, "%u\n", i2c_smbus_read_byte_data(client,
+ AK8975_REG_CNTL));
+}
+static ssize_t akm8975_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned long val;
+ strict_strtoul(buf, 10, &val);
+ if (val > 0xff)
+ return -EINVAL;
+ i2c_smbus_write_byte_data(client, AK8975_REG_CNTL, val);
+ return count;
+}
+static DEVICE_ATTR(akm_ms1, S_IWUSR | S_IRUGO, akm8975_show, akm8975_store);
+
+static int akm8975_i2c_rxdata(struct akm8975_data *akm, char *buf, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = akm->this_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = buf,
+ },
+ {
+ .addr = akm->this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = buf,
+ },
+ };
+
+ FUNCDBG("called");
+
+ if (i2c_transfer(akm->this_client->adapter, msgs, 2) < 0) {
+ pr_err("akm8975_i2c_rxdata: transfer error\n");
+ return EIO;
+ } else
+ return 0;
+}
+
+static int akm8975_i2c_txdata(struct akm8975_data *akm, char *buf, int length)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = akm->this_client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = buf,
+ },
+ };
+
+ FUNCDBG("called");
+
+ if (i2c_transfer(akm->this_client->adapter, msgs, 1) < 0) {
+ pr_err("akm8975_i2c_txdata: transfer error\n");
+ return -EIO;
+ } else
+ return 0;
+}
+
+static void akm8975_ecs_report_value(struct akm8975_data *akm, short *rbuf)
+{
+ struct akm8975_data *data = i2c_get_clientdata(akm->this_client);
+
+ FUNCDBG("called");
+
+#if AK8975DRV_DATA_DBG
+ pr_info("akm8975_ecs_report_value: yaw = %d, pitch = %d, roll = %d\n",
+ rbuf[0], rbuf[1], rbuf[2]);
+ pr_info("tmp = %d, m_stat= %d, g_stat=%d\n", rbuf[3], rbuf[4], rbuf[5]);
+ pr_info("Acceleration: x = %d LSB, y = %d LSB, z = %d LSB\n",
+ rbuf[6], rbuf[7], rbuf[8]);
+ pr_info("Magnetic: x = %d LSB, y = %d LSB, z = %d LSB\n\n",
+ rbuf[9], rbuf[10], rbuf[11]);
+#endif
+ mutex_lock(&akm->flags_lock);
+ /* Report magnetic sensor information */
+ if (m_flag) {
+ input_report_abs(data->input_dev, ABS_RX, rbuf[0]);
+ input_report_abs(data->input_dev, ABS_RY, rbuf[1]);
+ input_report_abs(data->input_dev, ABS_RZ, rbuf[2]);
+ input_report_abs(data->input_dev, ABS_RUDDER, rbuf[4]);
+ }
+
+ /* Report acceleration sensor information */
+ if (a_flag) {
+ input_report_abs(data->input_dev, ABS_X, rbuf[6]);
+ input_report_abs(data->input_dev, ABS_Y, rbuf[7]);
+ input_report_abs(data->input_dev, ABS_Z, rbuf[8]);
+ input_report_abs(data->input_dev, ABS_WHEEL, rbuf[5]);
+ }
+
+ /* Report temperature information */
+ if (t_flag)
+ input_report_abs(data->input_dev, ABS_THROTTLE, rbuf[3]);
+
+ if (mv_flag) {
+ input_report_abs(data->input_dev, ABS_HAT0X, rbuf[9]);
+ input_report_abs(data->input_dev, ABS_HAT0Y, rbuf[10]);
+ input_report_abs(data->input_dev, ABS_BRAKE, rbuf[11]);
+ }
+ mutex_unlock(&akm->flags_lock);
+
+ input_sync(data->input_dev);
+}
+
+static void akm8975_ecs_close_done(struct akm8975_data *akm)
+{
+ FUNCDBG("called");
+ mutex_lock(&akm->flags_lock);
+ m_flag = 1;
+ a_flag = 1;
+ t_flag = 1;
+ mv_flag = 1;
+ mutex_unlock(&akm->flags_lock);
+}
+
+static int akm_aot_open(struct inode *inode, struct file *file)
+{
+ int ret = -1;
+
+ FUNCDBG("called");
+ if (atomic_cmpxchg(&open_flag, 0, 1) == 0) {
+ wake_up(&open_wq);
+ ret = 0;
+ }
+
+ ret = nonseekable_open(inode, file);
+ if (ret)
+ return ret;
+
+ file->private_data = akmd_data;
+
+ return ret;
+}
+
+static int akm_aot_release(struct inode *inode, struct file *file)
+{
+ FUNCDBG("called");
+ atomic_set(&open_flag, 0);
+ wake_up(&open_wq);
+ return 0;
+}
+
+static int akm_aot_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *) arg;
+ short flag;
+ struct akm8975_data *akm = file->private_data;
+
+ FUNCDBG("called");
+
+ switch (cmd) {
+ case ECS_IOCTL_APP_SET_MFLAG:
+ case ECS_IOCTL_APP_SET_AFLAG:
+ case ECS_IOCTL_APP_SET_MVFLAG:
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ if (flag < 0 || flag > 1)
+ return -EINVAL;
+ break;
+ case ECS_IOCTL_APP_SET_DELAY:
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ mutex_lock(&akm->flags_lock);
+ switch (cmd) {
+ case ECS_IOCTL_APP_SET_MFLAG:
+ m_flag = flag;
+ break;
+ case ECS_IOCTL_APP_GET_MFLAG:
+ flag = m_flag;
+ break;
+ case ECS_IOCTL_APP_SET_AFLAG:
+ a_flag = flag;
+ break;
+ case ECS_IOCTL_APP_GET_AFLAG:
+ flag = a_flag;
+ break;
+ case ECS_IOCTL_APP_SET_MVFLAG:
+ mv_flag = flag;
+ break;
+ case ECS_IOCTL_APP_GET_MVFLAG:
+ flag = mv_flag;
+ break;
+ case ECS_IOCTL_APP_SET_DELAY:
+ akmd_delay = flag;
+ break;
+ case ECS_IOCTL_APP_GET_DELAY:
+ flag = akmd_delay;
+ break;
+ default:
+ return -ENOTTY;
+ }
+ mutex_unlock(&akm->flags_lock);
+
+ switch (cmd) {
+ case ECS_IOCTL_APP_GET_MFLAG:
+ case ECS_IOCTL_APP_GET_AFLAG:
+ case ECS_IOCTL_APP_GET_MVFLAG:
+ case ECS_IOCTL_APP_GET_DELAY:
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int akmd_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+
+ FUNCDBG("called");
+ err = nonseekable_open(inode, file);
+ if (err)
+ return err;
+
+ file->private_data = akmd_data;
+ return 0;
+}
+
+static int akmd_release(struct inode *inode, struct file *file)
+{
+ struct akm8975_data *akm = file->private_data;
+
+ FUNCDBG("called");
+ akm8975_ecs_close_done(akm);
+ return 0;
+}
+
+static int akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *) arg;
+
+ char rwbuf[16];
+ int ret = -1;
+ int status;
+ short value[12];
+ short delay;
+ struct akm8975_data *akm = file->private_data;
+
+ FUNCDBG("called");
+
+ switch (cmd) {
+ case ECS_IOCTL_READ:
+ case ECS_IOCTL_WRITE:
+ if (copy_from_user(&rwbuf, argp, sizeof(rwbuf)))
+ return -EFAULT;
+ break;
+
+ case ECS_IOCTL_SET_YPR:
+ if (copy_from_user(&value, argp, sizeof(value)))
+ return -EFAULT;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_READ:
+ if (rwbuf[0] < 1)
+ return -EINVAL;
+
+ ret = akm8975_i2c_rxdata(akm, &rwbuf[1], rwbuf[0]);
+ if (ret < 0)
+ return ret;
+ break;
+
+ case ECS_IOCTL_WRITE:
+ if (rwbuf[0] < 2)
+ return -EINVAL;
+
+ ret = akm8975_i2c_txdata(akm, &rwbuf[1], rwbuf[0]);
+ if (ret < 0)
+ return ret;
+ break;
+ case ECS_IOCTL_SET_YPR:
+ akm8975_ecs_report_value(akm, value);
+ break;
+
+ case ECS_IOCTL_GET_OPEN_STATUS:
+ wait_event_interruptible(open_wq,
+ (atomic_read(&open_flag) != 0));
+ status = atomic_read(&open_flag);
+ break;
+ case ECS_IOCTL_GET_CLOSE_STATUS:
+ wait_event_interruptible(open_wq,
+ (atomic_read(&open_flag) == 0));
+ status = atomic_read(&open_flag);
+ break;
+
+ case ECS_IOCTL_GET_DELAY:
+ delay = akmd_delay;
+ break;
+
+ default:
+ FUNCDBG("Unknown cmd\n");
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+ case ECS_IOCTL_READ:
+ if (copy_to_user(argp, &rwbuf, sizeof(rwbuf)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GET_OPEN_STATUS:
+ case ECS_IOCTL_GET_CLOSE_STATUS:
+ if (copy_to_user(argp, &status, sizeof(status)))
+ return -EFAULT;
+ break;
+ case ECS_IOCTL_GET_DELAY:
+ if (copy_to_user(argp, &delay, sizeof(delay)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* needed to clear the int. pin */
+static void akm_work_func(struct work_struct *work)
+{
+ struct akm8975_data *akm =
+ container_of(work, struct akm8975_data, work);
+
+ FUNCDBG("called");
+ enable_irq(akm->this_client->irq);
+}
+
+static irqreturn_t akm8975_interrupt(int irq, void *dev_id)
+{
+ struct akm8975_data *akm = dev_id;
+ FUNCDBG("called");
+
+ disable_irq_nosync(akm->this_client->irq);
+ schedule_work(&akm->work);
+ return IRQ_HANDLED;
+}
+
+static int akm8975_power_off(struct akm8975_data *akm)
+{
+#if AK8975DRV_CALL_DBG
+ pr_info("%s\n", __func__);
+#endif
+ if (akm->pdata->power_off)
+ akm->pdata->power_off();
+
+ return 0;
+}
+
+static int akm8975_power_on(struct akm8975_data *akm)
+{
+ int err;
+
+#if AK8975DRV_CALL_DBG
+ pr_info("%s\n", __func__);
+#endif
+ if (akm->pdata->power_on) {
+ err = akm->pdata->power_on();
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int akm8975_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct akm8975_data *akm = i2c_get_clientdata(client);
+
+#if AK8975DRV_CALL_DBG
+ pr_info("%s\n", __func__);
+#endif
+ /* TO DO: might need more work after power mgmt
+ is enabled */
+ return akm8975_power_off(akm);
+}
+
+static int akm8975_resume(struct i2c_client *client)
+{
+ struct akm8975_data *akm = i2c_get_clientdata(client);
+
+#if AK8975DRV_CALL_DBG
+ pr_info("%s\n", __func__);
+#endif
+ /* TO DO: might need more work after power mgmt
+ is enabled */
+ return akm8975_power_on(akm);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void akm8975_early_suspend(struct early_suspend *handler)
+{
+ struct akm8975_data *akm;
+ akm = container_of(handler, struct akm8975_data, early_suspend);
+
+#if AK8975DRV_CALL_DBG
+ pr_info("%s\n", __func__);
+#endif
+ akm8975_suspend(akm->this_client, PMSG_SUSPEND);
+}
+
+static void akm8975_early_resume(struct early_suspend *handler)
+{
+ struct akm8975_data *akm;
+ akm = container_of(handler, struct akm8975_data, early_suspend);
+
+#if AK8975DRV_CALL_DBG
+ pr_info("%s\n", __func__);
+#endif
+ akm8975_resume(akm->this_client);
+}
+#endif
+
+
+static int akm8975_init_client(struct i2c_client *client)
+{
+ struct akm8975_data *data;
+ int ret;
+
+ data = i2c_get_clientdata(client);
+
+ ret = request_irq(client->irq, akm8975_interrupt, IRQF_TRIGGER_RISING,
+ "akm8975", data);
+
+ if (ret < 0) {
+ pr_err("akm8975_init_client: request irq failed\n");
+ goto err;
+ }
+
+ init_waitqueue_head(&open_wq);
+
+ mutex_lock(&data->flags_lock);
+ m_flag = 1;
+ a_flag = 1;
+ t_flag = 1;
+ mv_flag = 1;
+ mutex_unlock(&data->flags_lock);
+
+ return 0;
+err:
+ return ret;
+}
+
+static const struct file_operations akmd_fops = {
+ .owner = THIS_MODULE,
+ .open = akmd_open,
+ .release = akmd_release,
+ .ioctl = akmd_ioctl,
+};
+
+static const struct file_operations akm_aot_fops = {
+ .owner = THIS_MODULE,
+ .open = akm_aot_open,
+ .release = akm_aot_release,
+ .ioctl = akm_aot_ioctl,
+};
+
+static struct miscdevice akm_aot_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "akm8975_aot",
+ .fops = &akm_aot_fops,
+};
+
+static struct miscdevice akmd_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "akm8975_dev",
+ .fops = &akmd_fops,
+};
+
+int akm8975_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct akm8975_data *akm;
+ int err;
+ FUNCDBG("called");
+
+ if (client->dev.platform_data == NULL) {
+ dev_err(&client->dev, "platform data is NULL. exiting.\n");
+ err = -ENODEV;
+ goto exit_platform_data_null;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "platform data is NULL. exiting.\n");
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ akm = kzalloc(sizeof(struct akm8975_data), GFP_KERNEL);
+ if (!akm) {
+ dev_err(&client->dev,
+ "failed to allocate memory for module data\n");
+ err = -ENOMEM;
+ goto exit_alloc_data_failed;
+ }
+
+ akm->pdata = client->dev.platform_data;
+
+ mutex_init(&akm->flags_lock);
+ INIT_WORK(&akm->work, akm_work_func);
+ i2c_set_clientdata(client, akm);
+
+ err = akm8975_power_on(akm);
+ if (err < 0)
+ goto exit_power_on_failed;
+
+ akm8975_init_client(client);
+ akm->this_client = client;
+ akmd_data = akm;
+
+ akm->input_dev = input_allocate_device();
+ if (!akm->input_dev) {
+ err = -ENOMEM;
+ dev_err(&akm->this_client->dev,
+ "input device allocate failed\n");
+ goto exit_input_dev_alloc_failed;
+ }
+
+ set_bit(EV_ABS, akm->input_dev->evbit);
+
+ /* yaw */
+ input_set_abs_params(akm->input_dev, ABS_RX, 0, 23040, 0, 0);
+ /* pitch */
+ input_set_abs_params(akm->input_dev, ABS_RY, -11520, 11520, 0, 0);
+ /* roll */
+ input_set_abs_params(akm->input_dev, ABS_RZ, -5760, 5760, 0, 0);
+ /* x-axis acceleration */
+ input_set_abs_params(akm->input_dev, ABS_X, -5760, 5760, 0, 0);
+ /* y-axis acceleration */
+ input_set_abs_params(akm->input_dev, ABS_Y, -5760, 5760, 0, 0);
+ /* z-axis acceleration */
+ input_set_abs_params(akm->input_dev, ABS_Z, -5760, 5760, 0, 0);
+ /* temparature */
+ input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0);
+ /* status of magnetic sensor */
+ input_set_abs_params(akm->input_dev, ABS_RUDDER, 0, 3, 0, 0);
+ /* status of acceleration sensor */
+ input_set_abs_params(akm->input_dev, ABS_WHEEL, 0, 3, 0, 0);
+ /* x-axis of raw magnetic vector */
+ input_set_abs_params(akm->input_dev, ABS_HAT0X, -20480, 20479, 0, 0);
+ /* y-axis of raw magnetic vector */
+ input_set_abs_params(akm->input_dev, ABS_HAT0Y, -20480, 20479, 0, 0);
+ /* z-axis of raw magnetic vector */
+ input_set_abs_params(akm->input_dev, ABS_BRAKE, -20480, 20479, 0, 0);
+
+ akm->input_dev->name = "compass";
+
+ err = input_register_device(akm->input_dev);
+ if (err) {
+ pr_err("akm8975_probe: Unable to register input device: %s\n",
+ akm->input_dev->name);
+ goto exit_input_register_device_failed;
+ }
+
+ err = misc_register(&akmd_device);
+ if (err) {
+ pr_err("akm8975_probe: akmd_device register failed\n");
+ goto exit_misc_device_register_failed;
+ }
+
+ err = misc_register(&akm_aot_device);
+ if (err) {
+ pr_err("akm8975_probe: akm_aot_device register failed\n");
+ goto exit_misc_device_register_failed;
+ }
+
+ err = device_create_file(&client->dev, &dev_attr_akm_ms1);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ akm->early_suspend.suspend = akm8975_early_suspend;
+ akm->early_suspend.resume = akm8975_early_resume;
+ register_early_suspend(&akm->early_suspend);
+#endif
+ return 0;
+
+exit_misc_device_register_failed:
+exit_input_register_device_failed:
+ input_free_device(akm->input_dev);
+exit_input_dev_alloc_failed:
+ akm8975_power_off(akm);
+exit_power_on_failed:
+ kfree(akm);
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+exit_platform_data_null:
+ return err;
+}
+
+static int __devexit akm8975_remove(struct i2c_client *client)
+{
+ struct akm8975_data *akm = i2c_get_clientdata(client);
+ FUNCDBG("called");
+ free_irq(client->irq, NULL);
+ input_unregister_device(akm->input_dev);
+ misc_deregister(&akmd_device);
+ misc_deregister(&akm_aot_device);
+ akm8975_power_off(akm);
+ kfree(akm);
+ return 0;
+}
+
+static const struct i2c_device_id akm8975_id[] = {
+ { "akm8975", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, akm8975_id);
+
+static struct i2c_driver akm8975_driver = {
+ .probe = akm8975_probe,
+ .remove = akm8975_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .resume = akm8975_resume,
+ .suspend = akm8975_suspend,
+#endif
+ .id_table = akm8975_id,
+ .driver = {
+ .name = "akm8975",
+ },
+};
+
+static int __init akm8975_init(void)
+{
+ pr_info("AK8975 compass driver: init\n");
+ FUNCDBG("AK8975 compass driver: init\n");
+ return i2c_add_driver(&akm8975_driver);
+}
+
+static void __exit akm8975_exit(void)
+{
+ FUNCDBG("AK8975 compass driver: exit\n");
+ i2c_del_driver(&akm8975_driver);
+}
+
+module_init(akm8975_init);
+module_exit(akm8975_exit);
+
+MODULE_AUTHOR("Hou-Kun Chen <hk_chen@htc.com>");
+MODULE_DESCRIPTION("AK8975 compass driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/apanic.c b/drivers/misc/apanic.c
new file mode 100644
index 00000000000..ca875f89da7
--- /dev/null
+++ b/drivers/misc/apanic.c
@@ -0,0 +1,606 @@
+/* drivers/misc/apanic.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: San Mehat <san@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/mtd/mtd.h>
+#include <linux/notifier.h>
+#include <linux/mtd/mtd.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/preempt.h>
+
+extern void ram_console_enable_console(int);
+
+struct panic_header {
+ u32 magic;
+#define PANIC_MAGIC 0xdeadf00d
+
+ u32 version;
+#define PHDR_VERSION 0x01
+
+ u32 console_offset;
+ u32 console_length;
+
+ u32 threads_offset;
+ u32 threads_length;
+};
+
+struct apanic_data {
+ struct mtd_info *mtd;
+ struct panic_header curr;
+ void *bounce;
+ struct proc_dir_entry *apanic_console;
+ struct proc_dir_entry *apanic_threads;
+};
+
+static struct apanic_data drv_ctx;
+static struct work_struct proc_removal_work;
+static DEFINE_MUTEX(drv_mutex);
+
+static unsigned int *apanic_bbt;
+static unsigned int apanic_erase_blocks;
+static unsigned int apanic_good_blocks;
+
+static void set_bb(unsigned int block, unsigned int *bbt)
+{
+ unsigned int flag = 1;
+
+ BUG_ON(block >= apanic_erase_blocks);
+
+ flag = flag << (block%32);
+ apanic_bbt[block/32] |= flag;
+ apanic_good_blocks--;
+}
+
+static unsigned int get_bb(unsigned int block, unsigned int *bbt)
+{
+ unsigned int flag;
+
+ BUG_ON(block >= apanic_erase_blocks);
+
+ flag = 1 << (block%32);
+ return apanic_bbt[block/32] & flag;
+}
+
+static void alloc_bbt(struct mtd_info *mtd, unsigned int *bbt)
+{
+ int bbt_size;
+ apanic_erase_blocks = (mtd->size)>>(mtd->erasesize_shift);
+ bbt_size = (apanic_erase_blocks+32)/32;
+
+ apanic_bbt = kmalloc(bbt_size*4, GFP_KERNEL);
+ memset(apanic_bbt, 0, bbt_size*4);
+ apanic_good_blocks = apanic_erase_blocks;
+}
+static void scan_bbt(struct mtd_info *mtd, unsigned int *bbt)
+{
+ int i;
+
+ for (i = 0; i < apanic_erase_blocks; i++) {
+ if (mtd->block_isbad(mtd, i*mtd->erasesize))
+ set_bb(i, apanic_bbt);
+ }
+}
+
+#define APANIC_INVALID_OFFSET 0xFFFFFFFF
+
+static unsigned int phy_offset(struct mtd_info *mtd, unsigned int offset)
+{
+ unsigned int logic_block = offset>>(mtd->erasesize_shift);
+ unsigned int phy_block;
+ unsigned good_block = 0;
+
+ for (phy_block = 0; phy_block < apanic_erase_blocks; phy_block++) {
+ if (!get_bb(phy_block, apanic_bbt))
+ good_block++;
+ if (good_block == (logic_block + 1))
+ break;
+ }
+
+ if (good_block != (logic_block + 1))
+ return APANIC_INVALID_OFFSET;
+
+ return offset + ((phy_block-logic_block)<<mtd->erasesize_shift);
+}
+
+static void apanic_erase_callback(struct erase_info *done)
+{
+ wait_queue_head_t *wait_q = (wait_queue_head_t *) done->priv;
+ wake_up(wait_q);
+}
+
+static int apanic_proc_read(char *buffer, char **start, off_t offset,
+ int count, int *peof, void *dat)
+{
+ struct apanic_data *ctx = &drv_ctx;
+ size_t file_length;
+ off_t file_offset;
+ unsigned int page_no;
+ off_t page_offset;
+ int rc;
+ size_t len;
+
+ if (!count)
+ return 0;
+
+ mutex_lock(&drv_mutex);
+
+ switch ((int) dat) {
+ case 1: /* apanic_console */
+ file_length = ctx->curr.console_length;
+ file_offset = ctx->curr.console_offset;
+ break;
+ case 2: /* apanic_threads */
+ file_length = ctx->curr.threads_length;
+ file_offset = ctx->curr.threads_offset;
+ break;
+ default:
+ pr_err("Bad dat (%d)\n", (int) dat);
+ mutex_unlock(&drv_mutex);
+ return -EINVAL;
+ }
+
+ if ((offset + count) > file_length) {
+ mutex_unlock(&drv_mutex);
+ return 0;
+ }
+
+ /* We only support reading a maximum of a flash page */
+ if (count > ctx->mtd->writesize)
+ count = ctx->mtd->writesize;
+
+ page_no = (file_offset + offset) / ctx->mtd->writesize;
+ page_offset = (file_offset + offset) % ctx->mtd->writesize;
+
+
+ if (phy_offset(ctx->mtd, (page_no * ctx->mtd->writesize))
+ == APANIC_INVALID_OFFSET) {
+ pr_err("apanic: reading an invalid address\n");
+ mutex_unlock(&drv_mutex);
+ return -EINVAL;
+ }
+ rc = ctx->mtd->read(ctx->mtd,
+ phy_offset(ctx->mtd, (page_no * ctx->mtd->writesize)),
+ ctx->mtd->writesize,
+ &len, ctx->bounce);
+
+ if (page_offset)
+ count -= page_offset;
+ memcpy(buffer, ctx->bounce + page_offset, count);
+
+ *start = count;
+
+ if ((offset + count) == file_length)
+ *peof = 1;
+
+ mutex_unlock(&drv_mutex);
+ return count;
+}
+
+static void mtd_panic_erase(void)
+{
+ struct apanic_data *ctx = &drv_ctx;
+ struct erase_info erase;
+ DECLARE_WAITQUEUE(wait, current);
+ wait_queue_head_t wait_q;
+ int rc, i;
+
+ init_waitqueue_head(&wait_q);
+ erase.mtd = ctx->mtd;
+ erase.callback = apanic_erase_callback;
+ erase.len = ctx->mtd->erasesize;
+ erase.priv = (u_long)&wait_q;
+ for (i = 0; i < ctx->mtd->size; i += ctx->mtd->erasesize) {
+ erase.addr = i;
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&wait_q, &wait);
+
+ if (get_bb(erase.addr>>ctx->mtd->erasesize_shift, apanic_bbt)) {
+ printk(KERN_WARNING
+ "apanic: Skipping erase of bad "
+ "block @%llx\n", erase.addr);
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&wait_q, &wait);
+ continue;
+ }
+
+ rc = ctx->mtd->erase(ctx->mtd, &erase);
+ if (rc) {
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&wait_q, &wait);
+ printk(KERN_ERR
+ "apanic: Erase of 0x%llx, 0x%llx failed\n",
+ (unsigned long long) erase.addr,
+ (unsigned long long) erase.len);
+ if (rc == -EIO) {
+ if (ctx->mtd->block_markbad(ctx->mtd,
+ erase.addr)) {
+ printk(KERN_ERR
+ "apanic: Err marking blk bad\n");
+ goto out;
+ }
+ printk(KERN_INFO
+ "apanic: Marked a bad block"
+ " @%llx\n", erase.addr);
+ set_bb(erase.addr>>ctx->mtd->erasesize_shift,
+ apanic_bbt);
+ continue;
+ }
+ goto out;
+ }
+ schedule();
+ remove_wait_queue(&wait_q, &wait);
+ }
+ printk(KERN_DEBUG "apanic: %s partition erased\n",
+ CONFIG_APANIC_PLABEL);
+out:
+ return;
+}
+
+static void apanic_remove_proc_work(struct work_struct *work)
+{
+ struct apanic_data *ctx = &drv_ctx;
+
+ mutex_lock(&drv_mutex);
+ mtd_panic_erase();
+ memset(&ctx->curr, 0, sizeof(struct panic_header));
+ if (ctx->apanic_console) {
+ remove_proc_entry("apanic_console", NULL);
+ ctx->apanic_console = NULL;
+ }
+ if (ctx->apanic_threads) {
+ remove_proc_entry("apanic_threads", NULL);
+ ctx->apanic_threads = NULL;
+ }
+ mutex_unlock(&drv_mutex);
+}
+
+static int apanic_proc_write(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ schedule_work(&proc_removal_work);
+ return count;
+}
+
+static void mtd_panic_notify_add(struct mtd_info *mtd)
+{
+ struct apanic_data *ctx = &drv_ctx;
+ struct panic_header *hdr = ctx->bounce;
+ size_t len;
+ int rc;
+ int proc_entry_created = 0;
+
+ if (strcmp(mtd->name, CONFIG_APANIC_PLABEL))
+ return;
+
+ ctx->mtd = mtd;
+
+ alloc_bbt(mtd, apanic_bbt);
+ scan_bbt(mtd, apanic_bbt);
+
+ if (apanic_good_blocks == 0) {
+ printk(KERN_ERR "apanic: no any good blocks?!\n");
+ goto out_err;
+ }
+
+ rc = mtd->read(mtd, phy_offset(mtd, 0), mtd->writesize,
+ &len, ctx->bounce);
+ if (rc && rc == -EBADMSG) {
+ printk(KERN_WARNING
+ "apanic: Bad ECC on block 0 (ignored)\n");
+ } else if (rc && rc != -EUCLEAN) {
+ printk(KERN_ERR "apanic: Error reading block 0 (%d)\n", rc);
+ goto out_err;
+ }
+
+ if (len != mtd->writesize) {
+ printk(KERN_ERR "apanic: Bad read size (%d)\n", rc);
+ goto out_err;
+ }
+
+ printk(KERN_INFO "apanic: Bound to mtd partition '%s'\n", mtd->name);
+
+ if (hdr->magic != PANIC_MAGIC) {
+ printk(KERN_INFO "apanic: No panic data available\n");
+ mtd_panic_erase();
+ return;
+ }
+
+ if (hdr->version != PHDR_VERSION) {
+ printk(KERN_INFO "apanic: Version mismatch (%d != %d)\n",
+ hdr->version, PHDR_VERSION);
+ mtd_panic_erase();
+ return;
+ }
+
+ memcpy(&ctx->curr, hdr, sizeof(struct panic_header));
+
+ printk(KERN_INFO "apanic: c(%u, %u) t(%u, %u)\n",
+ hdr->console_offset, hdr->console_length,
+ hdr->threads_offset, hdr->threads_length);
+
+ if (hdr->console_length) {
+ ctx->apanic_console = create_proc_entry("apanic_console",
+ S_IFREG | S_IRUGO, NULL);
+ if (!ctx->apanic_console)
+ printk(KERN_ERR "%s: failed creating procfile\n",
+ __func__);
+ else {
+ ctx->apanic_console->read_proc = apanic_proc_read;
+ ctx->apanic_console->write_proc = apanic_proc_write;
+ ctx->apanic_console->size = hdr->console_length;
+ ctx->apanic_console->data = (void *) 1;
+ proc_entry_created = 1;
+ }
+ }
+
+ if (hdr->threads_length) {
+ ctx->apanic_threads = create_proc_entry("apanic_threads",
+ S_IFREG | S_IRUGO, NULL);
+ if (!ctx->apanic_threads)
+ printk(KERN_ERR "%s: failed creating procfile\n",
+ __func__);
+ else {
+ ctx->apanic_threads->read_proc = apanic_proc_read;
+ ctx->apanic_threads->write_proc = apanic_proc_write;
+ ctx->apanic_threads->size = hdr->threads_length;
+ ctx->apanic_threads->data = (void *) 2;
+ proc_entry_created = 1;
+ }
+ }
+
+ if (!proc_entry_created)
+ mtd_panic_erase();
+
+ return;
+out_err:
+ ctx->mtd = NULL;
+}
+
+static void mtd_panic_notify_remove(struct mtd_info *mtd)
+{
+ struct apanic_data *ctx = &drv_ctx;
+ if (mtd == ctx->mtd) {
+ ctx->mtd = NULL;
+ printk(KERN_INFO "apanic: Unbound from %s\n", mtd->name);
+ }
+}
+
+static struct mtd_notifier mtd_panic_notifier = {
+ .add = mtd_panic_notify_add,
+ .remove = mtd_panic_notify_remove,
+};
+
+static int in_panic = 0;
+
+static int apanic_writeflashpage(struct mtd_info *mtd, loff_t to,
+ const u_char *buf)
+{
+ int rc;
+ size_t wlen;
+ int panic = in_interrupt() | in_atomic();
+
+ if (panic && !mtd->panic_write) {
+ printk(KERN_EMERG "%s: No panic_write available\n", __func__);
+ return 0;
+ } else if (!panic && !mtd->write) {
+ printk(KERN_EMERG "%s: No write available\n", __func__);
+ return 0;
+ }
+
+ to = phy_offset(mtd, to);
+ if (to == APANIC_INVALID_OFFSET) {
+ printk(KERN_EMERG "apanic: write to invalid address\n");
+ return 0;
+ }
+
+ if (panic)
+ rc = mtd->panic_write(mtd, to, mtd->writesize, &wlen, buf);
+ else
+ rc = mtd->write(mtd, to, mtd->writesize, &wlen, buf);
+
+ if (rc) {
+ printk(KERN_EMERG
+ "%s: Error writing data to flash (%d)\n",
+ __func__, rc);
+ return rc;
+ }
+
+ return wlen;
+}
+
+extern int log_buf_copy(char *dest, int idx, int len);
+extern void log_buf_clear(void);
+
+/*
+ * Writes the contents of the console to the specified offset in flash.
+ * Returns number of bytes written
+ */
+static int apanic_write_console(struct mtd_info *mtd, unsigned int off)
+{
+ struct apanic_data *ctx = &drv_ctx;
+ int saved_oip;
+ int idx = 0;
+ int rc, rc2;
+ unsigned int last_chunk = 0;
+
+ while (!last_chunk) {
+ saved_oip = oops_in_progress;
+ oops_in_progress = 1;
+ rc = log_buf_copy(ctx->bounce, idx, mtd->writesize);
+ if (rc < 0)
+ break;
+
+ if (rc != mtd->writesize)
+ last_chunk = rc;
+
+ oops_in_progress = saved_oip;
+ if (rc <= 0)
+ break;
+ if (rc != mtd->writesize)
+ memset(ctx->bounce + rc, 0, mtd->writesize - rc);
+
+ rc2 = apanic_writeflashpage(mtd, off, ctx->bounce);
+ if (rc2 <= 0) {
+ printk(KERN_EMERG
+ "apanic: Flash write failed (%d)\n", rc2);
+ return idx;
+ }
+ if (!last_chunk)
+ idx += rc2;
+ else
+ idx += last_chunk;
+ off += rc2;
+ }
+ return idx;
+}
+
+static int apanic(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ struct apanic_data *ctx = &drv_ctx;
+ struct panic_header *hdr = (struct panic_header *) ctx->bounce;
+ int console_offset = 0;
+ int console_len = 0;
+ int threads_offset = 0;
+ int threads_len = 0;
+ int rc;
+
+ if (in_panic)
+ return NOTIFY_DONE;
+ in_panic = 1;
+#ifdef CONFIG_PREEMPT
+ /* Ensure that cond_resched() won't try to preempt anybody */
+ add_preempt_count(PREEMPT_ACTIVE);
+#endif
+ touch_softlockup_watchdog();
+
+ if (!ctx->mtd)
+ goto out;
+
+ if (ctx->curr.magic) {
+ printk(KERN_EMERG "Crash partition in use!\n");
+ goto out;
+ }
+ console_offset = ctx->mtd->writesize;
+
+ /*
+ * Write out the console
+ */
+ console_len = apanic_write_console(ctx->mtd, console_offset);
+ if (console_len < 0) {
+ printk(KERN_EMERG "Error writing console to panic log! (%d)\n",
+ console_len);
+ console_len = 0;
+ }
+
+ /*
+ * Write out all threads
+ */
+ threads_offset = ALIGN(console_offset + console_len,
+ ctx->mtd->writesize);
+ if (!threads_offset)
+ threads_offset = ctx->mtd->writesize;
+
+ ram_console_enable_console(0);
+
+ log_buf_clear();
+ show_state_filter(0);
+ threads_len = apanic_write_console(ctx->mtd, threads_offset);
+ if (threads_len < 0) {
+ printk(KERN_EMERG "Error writing threads to panic log! (%d)\n",
+ threads_len);
+ threads_len = 0;
+ }
+
+ /*
+ * Finally write the panic header
+ */
+ memset(ctx->bounce, 0, PAGE_SIZE);
+ hdr->magic = PANIC_MAGIC;
+ hdr->version = PHDR_VERSION;
+
+ hdr->console_offset = console_offset;
+ hdr->console_length = console_len;
+
+ hdr->threads_offset = threads_offset;
+ hdr->threads_length = threads_len;
+
+ rc = apanic_writeflashpage(ctx->mtd, 0, ctx->bounce);
+ if (rc <= 0) {
+ printk(KERN_EMERG "apanic: Header write failed (%d)\n",
+ rc);
+ goto out;
+ }
+
+ printk(KERN_EMERG "apanic: Panic dump sucessfully written to flash\n");
+
+ out:
+#ifdef CONFIG_PREEMPT
+ sub_preempt_count(PREEMPT_ACTIVE);
+#endif
+ in_panic = 0;
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_blk = {
+ .notifier_call = apanic,
+};
+
+static int panic_dbg_get(void *data, u64 *val)
+{
+ apanic(NULL, 0, NULL);
+ return 0;
+}
+
+static int panic_dbg_set(void *data, u64 val)
+{
+ BUG();
+ return -1;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(panic_dbg_fops, panic_dbg_get, panic_dbg_set, "%llu\n");
+
+int __init apanic_init(void)
+{
+ register_mtd_user(&mtd_panic_notifier);
+ atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
+ debugfs_create_file("apanic", 0644, NULL, NULL, &panic_dbg_fops);
+ memset(&drv_ctx, 0, sizeof(drv_ctx));
+ drv_ctx.bounce = (void *) __get_free_page(GFP_KERNEL);
+ INIT_WORK(&proc_removal_work, apanic_remove_proc_work);
+ printk(KERN_INFO "Android kernel panic handler initialized (bind=%s)\n",
+ CONFIG_APANIC_PLABEL);
+ return 0;
+}
+
+module_init(apanic_init);
diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c
new file mode 100644
index 00000000000..9e3879ef58f
--- /dev/null
+++ b/drivers/misc/arm-charlcd.c
@@ -0,0 +1,396 @@
+/*
+ * Driver for the on-board character LCD found on some ARM reference boards
+ * This is basically an Hitachi HD44780 LCD with a custom IP block to drive it
+ * http://en.wikipedia.org/wiki/HD44780_Character_LCD
+ * Currently it will just display the text "ARM Linux" and the linux version
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Linus Walleij <triad@df.lth.se>
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <generated/utsrelease.h>
+
+#define DRIVERNAME "arm-charlcd"
+#define CHARLCD_TIMEOUT (msecs_to_jiffies(1000))
+
+/* Offsets to registers */
+#define CHAR_COM 0x00U
+#define CHAR_DAT 0x04U
+#define CHAR_RD 0x08U
+#define CHAR_RAW 0x0CU
+#define CHAR_MASK 0x10U
+#define CHAR_STAT 0x14U
+
+#define CHAR_RAW_CLEAR 0x00000000U
+#define CHAR_RAW_VALID 0x00000100U
+
+/* Hitachi HD44780 display commands */
+#define HD_CLEAR 0x01U
+#define HD_HOME 0x02U
+#define HD_ENTRYMODE 0x04U
+#define HD_ENTRYMODE_INCREMENT 0x02U
+#define HD_ENTRYMODE_SHIFT 0x01U
+#define HD_DISPCTRL 0x08U
+#define HD_DISPCTRL_ON 0x04U
+#define HD_DISPCTRL_CURSOR_ON 0x02U
+#define HD_DISPCTRL_CURSOR_BLINK 0x01U
+#define HD_CRSR_SHIFT 0x10U
+#define HD_CRSR_SHIFT_DISPLAY 0x08U
+#define HD_CRSR_SHIFT_DISPLAY_RIGHT 0x04U
+#define HD_FUNCSET 0x20U
+#define HD_FUNCSET_8BIT 0x10U
+#define HD_FUNCSET_2_LINES 0x08U
+#define HD_FUNCSET_FONT_5X10 0x04U
+#define HD_SET_CGRAM 0x40U
+#define HD_SET_DDRAM 0x80U
+#define HD_BUSY_FLAG 0x80U
+
+/**
+ * @dev: a pointer back to containing device
+ * @phybase: the offset to the controller in physical memory
+ * @physize: the size of the physical page
+ * @virtbase: the offset to the controller in virtual memory
+ * @irq: reserved interrupt number
+ * @complete: completion structure for the last LCD command
+ */
+struct charlcd {
+ struct device *dev;
+ u32 phybase;
+ u32 physize;
+ void __iomem *virtbase;
+ int irq;
+ struct completion complete;
+ struct delayed_work init_work;
+};
+
+static irqreturn_t charlcd_interrupt(int irq, void *data)
+{
+ struct charlcd *lcd = data;
+ u8 status;
+
+ status = readl(lcd->virtbase + CHAR_STAT) & 0x01;
+ /* Clear IRQ */
+ writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
+ if (status)
+ complete(&lcd->complete);
+ else
+ dev_info(lcd->dev, "Spurious IRQ (%02x)\n", status);
+ return IRQ_HANDLED;
+}
+
+
+static void charlcd_wait_complete_irq(struct charlcd *lcd)
+{
+ int ret;
+
+ ret = wait_for_completion_interruptible_timeout(&lcd->complete,
+ CHARLCD_TIMEOUT);
+ /* Disable IRQ after completion */
+ writel(0x00, lcd->virtbase + CHAR_MASK);
+
+ if (ret < 0) {
+ dev_err(lcd->dev,
+ "wait_for_completion_interruptible_timeout() "
+ "returned %d waiting for ready\n", ret);
+ return;
+ }
+
+ if (ret == 0) {
+ dev_err(lcd->dev, "charlcd controller timed out "
+ "waiting for ready\n");
+ return;
+ }
+}
+
+static u8 charlcd_4bit_read_char(struct charlcd *lcd)
+{
+ u8 data;
+ u32 val;
+ int i;
+
+ /* If we can, use an IRQ to wait for the data, else poll */
+ if (lcd->irq >= 0)
+ charlcd_wait_complete_irq(lcd);
+ else {
+ i = 0;
+ val = 0;
+ while (!(val & CHAR_RAW_VALID) && i < 10) {
+ udelay(100);
+ val = readl(lcd->virtbase + CHAR_RAW);
+ i++;
+ }
+
+ writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
+ }
+ msleep(1);
+
+ /* Read the 4 high bits of the data */
+ data = readl(lcd->virtbase + CHAR_RD) & 0xf0;
+
+ /*
+ * The second read for the low bits does not trigger an IRQ
+ * so in this case we have to poll for the 4 lower bits
+ */
+ i = 0;
+ val = 0;
+ while (!(val & CHAR_RAW_VALID) && i < 10) {
+ udelay(100);
+ val = readl(lcd->virtbase + CHAR_RAW);
+ i++;
+ }
+ writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
+ msleep(1);
+
+ /* Read the 4 low bits of the data */
+ data |= (readl(lcd->virtbase + CHAR_RD) >> 4) & 0x0f;
+
+ return data;
+}
+
+static bool charlcd_4bit_read_bf(struct charlcd *lcd)
+{
+ if (lcd->irq >= 0) {
+ /*
+ * If we'll use IRQs to wait for the busyflag, clear any
+ * pending flag and enable IRQ
+ */
+ writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
+ init_completion(&lcd->complete);
+ writel(0x01, lcd->virtbase + CHAR_MASK);
+ }
+ readl(lcd->virtbase + CHAR_COM);
+ return charlcd_4bit_read_char(lcd) & HD_BUSY_FLAG ? true : false;
+}
+
+static void charlcd_4bit_wait_busy(struct charlcd *lcd)
+{
+ int retries = 50;
+
+ udelay(100);
+ while (charlcd_4bit_read_bf(lcd) && retries)
+ retries--;
+ if (!retries)
+ dev_err(lcd->dev, "timeout waiting for busyflag\n");
+}
+
+static void charlcd_4bit_command(struct charlcd *lcd, u8 cmd)
+{
+ u32 cmdlo = (cmd << 4) & 0xf0;
+ u32 cmdhi = (cmd & 0xf0);
+
+ writel(cmdhi, lcd->virtbase + CHAR_COM);
+ udelay(10);
+ writel(cmdlo, lcd->virtbase + CHAR_COM);
+ charlcd_4bit_wait_busy(lcd);
+}
+
+static void charlcd_4bit_char(struct charlcd *lcd, u8 ch)
+{
+ u32 chlo = (ch << 4) & 0xf0;
+ u32 chhi = (ch & 0xf0);
+
+ writel(chhi, lcd->virtbase + CHAR_DAT);
+ udelay(10);
+ writel(chlo, lcd->virtbase + CHAR_DAT);
+ charlcd_4bit_wait_busy(lcd);
+}
+
+static void charlcd_4bit_print(struct charlcd *lcd, int line, const char *str)
+{
+ u8 offset;
+ int i;
+
+ /*
+ * We support line 0, 1
+ * Line 1 runs from 0x00..0x27
+ * Line 2 runs from 0x28..0x4f
+ */
+ if (line == 0)
+ offset = 0;
+ else if (line == 1)
+ offset = 0x28;
+ else
+ return;
+
+ /* Set offset */
+ charlcd_4bit_command(lcd, HD_SET_DDRAM | offset);
+
+ /* Send string */
+ for (i = 0; i < strlen(str) && i < 0x28; i++)
+ charlcd_4bit_char(lcd, str[i]);
+}
+
+static void charlcd_4bit_init(struct charlcd *lcd)
+{
+ /* These commands cannot be checked with the busy flag */
+ writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
+ msleep(5);
+ writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
+ udelay(100);
+ writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
+ udelay(100);
+ /* Go to 4bit mode */
+ writel(HD_FUNCSET, lcd->virtbase + CHAR_COM);
+ udelay(100);
+ /*
+ * 4bit mode, 2 lines, 5x8 font, after this the number of lines
+ * and the font cannot be changed until the next initialization sequence
+ */
+ charlcd_4bit_command(lcd, HD_FUNCSET | HD_FUNCSET_2_LINES);
+ charlcd_4bit_command(lcd, HD_DISPCTRL | HD_DISPCTRL_ON);
+ charlcd_4bit_command(lcd, HD_ENTRYMODE | HD_ENTRYMODE_INCREMENT);
+ charlcd_4bit_command(lcd, HD_CLEAR);
+ charlcd_4bit_command(lcd, HD_HOME);
+ /* Put something useful in the display */
+ charlcd_4bit_print(lcd, 0, "ARM Linux");
+ charlcd_4bit_print(lcd, 1, UTS_RELEASE);
+}
+
+static void charlcd_init_work(struct work_struct *work)
+{
+ struct charlcd *lcd =
+ container_of(work, struct charlcd, init_work.work);
+
+ charlcd_4bit_init(lcd);
+}
+
+static int __init charlcd_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct charlcd *lcd;
+ struct resource *res;
+
+ lcd = kzalloc(sizeof(struct charlcd), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ lcd->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENOENT;
+ goto out_no_resource;
+ }
+ lcd->phybase = res->start;
+ lcd->physize = resource_size(res);
+
+ if (request_mem_region(lcd->phybase, lcd->physize,
+ DRIVERNAME) == NULL) {
+ ret = -EBUSY;
+ goto out_no_memregion;
+ }
+
+ lcd->virtbase = ioremap(lcd->phybase, lcd->physize);
+ if (!lcd->virtbase) {
+ ret = -ENOMEM;
+ goto out_no_remap;
+ }
+
+ lcd->irq = platform_get_irq(pdev, 0);
+ /* If no IRQ is supplied, we'll survive without it */
+ if (lcd->irq >= 0) {
+ if (request_irq(lcd->irq, charlcd_interrupt, IRQF_DISABLED,
+ DRIVERNAME, lcd)) {
+ ret = -EIO;
+ goto out_no_irq;
+ }
+ }
+
+ platform_set_drvdata(pdev, lcd);
+
+ /*
+ * Initialize the display in a delayed work, because
+ * it is VERY slow and would slow down the boot of the system.
+ */
+ INIT_DELAYED_WORK(&lcd->init_work, charlcd_init_work);
+ schedule_delayed_work(&lcd->init_work, 0);
+
+ dev_info(&pdev->dev, "initalized ARM character LCD at %08x\n",
+ lcd->phybase);
+
+ return 0;
+
+out_no_irq:
+ iounmap(lcd->virtbase);
+out_no_remap:
+ platform_set_drvdata(pdev, NULL);
+out_no_memregion:
+ release_mem_region(lcd->phybase, SZ_4K);
+out_no_resource:
+ kfree(lcd);
+ return ret;
+}
+
+static int __exit charlcd_remove(struct platform_device *pdev)
+{
+ struct charlcd *lcd = platform_get_drvdata(pdev);
+
+ if (lcd) {
+ free_irq(lcd->irq, lcd);
+ iounmap(lcd->virtbase);
+ release_mem_region(lcd->phybase, lcd->physize);
+ platform_set_drvdata(pdev, NULL);
+ kfree(lcd);
+ }
+
+ return 0;
+}
+
+static int charlcd_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct charlcd *lcd = platform_get_drvdata(pdev);
+
+ /* Power the display off */
+ charlcd_4bit_command(lcd, HD_DISPCTRL);
+ return 0;
+}
+
+static int charlcd_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct charlcd *lcd = platform_get_drvdata(pdev);
+
+ /* Turn the display back on */
+ charlcd_4bit_command(lcd, HD_DISPCTRL | HD_DISPCTRL_ON);
+ return 0;
+}
+
+static const struct dev_pm_ops charlcd_pm_ops = {
+ .suspend = charlcd_suspend,
+ .resume = charlcd_resume,
+};
+
+static struct platform_driver charlcd_driver = {
+ .driver = {
+ .name = DRIVERNAME,
+ .owner = THIS_MODULE,
+ .pm = &charlcd_pm_ops,
+ },
+ .remove = __exit_p(charlcd_remove),
+};
+
+static int __init charlcd_init(void)
+{
+ return platform_driver_probe(&charlcd_driver, charlcd_probe);
+}
+
+static void __exit charlcd_exit(void)
+{
+ platform_driver_unregister(&charlcd_driver);
+}
+
+module_init(charlcd_init);
+module_exit(charlcd_exit);
+
+MODULE_AUTHOR("Linus Walleij <triad@df.lth.se>");
+MODULE_DESCRIPTION("ARM Character LCD Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/audio_io_dev/Kconfig b/drivers/misc/audio_io_dev/Kconfig
new file mode 100644
index 00000000000..57bb77172f7
--- /dev/null
+++ b/drivers/misc/audio_io_dev/Kconfig
@@ -0,0 +1,11 @@
+#
+# AB8500 Audio IO Device Driver configuration
+#
+config STE_AUDIO_IO_DEV
+ bool "AB8500 Audio IO device driver"
+ depends on ARCH_U8500 && AB8500_CORE && STM_MSP_I2S
+ default y
+ ---help---
+ If you say Y here, you will enable the AB8500 Audio IO device driver.
+
+ If unsure, say N.
diff --git a/drivers/misc/audio_io_dev/Makefile b/drivers/misc/audio_io_dev/Makefile
new file mode 100644
index 00000000000..44b21fcc573
--- /dev/null
+++ b/drivers/misc/audio_io_dev/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for AB8500 device drivers
+#
+obj-$(CONFIG_STE_AUDIO_IO_DEV) += ste_audio_io.o
+ste_audio_io-objs := ste_audio_io_dev.o\
+ ste_audio_io_core.o\
+ ste_audio_io_func.o\
+ ste_audio_io_hwctrl_common.o
+
diff --git a/drivers/misc/audio_io_dev/ste_audio_io_ab8500_reg_defs.h b/drivers/misc/audio_io_dev/ste_audio_io_ab8500_reg_defs.h
new file mode 100644
index 00000000000..1436430f7de
--- /dev/null
+++ b/drivers/misc/audio_io_dev/ste_audio_io_ab8500_reg_defs.h
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+
+#ifndef _AUDIOIO_REG_DEFS_H_
+#define _AUDIOIO_REG_DEFS_H_
+
+
+ /* Registers */
+#define POWER_UP_CONTROL_REG 0x0D00
+#define SOFTWARE_RESET_REG 0x0D01
+#define DIGITAL_AD_CHANNELS_ENABLE_REG 0x0D02
+#define DIGITAL_DA_CHANNELS_ENABLE_REG 0x0D03
+#define LOW_POWER_HS_EAR_CONF_REG 0x0D04
+#define LINE_IN_MIC_CONF_REG 0x0D05
+#define DMIC_ENABLE_REG 0x0D06
+#define ADC_DAC_ENABLE_REG 0x0D07
+#define ANALOG_OUTPUT_ENABLE_REG 0x0D08
+#define DIGITAL_OUTPUT_ENABLE_REG 0x0D09
+#define MUTE_HS_EAR_REG 0x0D0A
+#define SHORT_CIRCUIT_DISABLE_REG 0x0D0B
+#define NCP_ENABLE_HS_AUTOSTART_REG 0x0D0C
+#define ENVELOPE_THRESHOLD_REG 0x0D0D
+#define ENVELOPE_DECAY_TIME_REG 0x0D0E
+#define VIB_DRIVER_CONF_REG 0x0D0F
+#define PWM_VIBNL_CONF_REG 0x0D10
+#define PWM_VIBPL_CONF_REG 0x0D11
+#define PWM_VIBNR_CONF_REG 0x0D12
+#define PWM_VIBPR_CONF_REG 0x0D13
+#define ANALOG_MIC1_GAIN_REG 0x0D14
+#define ANALOG_MIC2_GAIN_REG 0x0D15
+#define ANALOG_HS_GAIN_REG 0x0D16
+#define ANALOG_LINE_IN_GAIN_REG 0x0D17
+#define LINE_IN_TO_HSL_GAIN_REG 0x0D18
+#define LINE_IN_TO_HSR_GAIN_REG 0x0D19
+#define AD_FILTER_CONF_REG 0x0D1A
+#define IF0_IF1_MASTER_CONF_REG 0x0D1B
+#define IF0_CONF_REG 0x0D1C
+#define TDM_IF_BYPASS_B_FIFO_REG 0x0D1D
+#define IF1_CONF_REG 0x0D1E
+#define AD_ALLOCATION_TO_SLOT0_1_REG 0x0D1F
+#define AD_ALLOCATION_TO_SLOT2_3_REG 0x0D20
+#define AD_ALLOCATION_TO_SLOT4_5_REG 0x0D21
+#define AD_ALLOCATION_TO_SLOT6_7_REG 0x0D22
+#define AD_ALLOCATION_TO_SLOT8_9_REG 0x0D23
+#define AD_ALLOCATION_TO_SLOT10_11_REG 0x0D24
+#define AD_ALLOCATION_TO_SLOT12_13_REG 0x0D25
+#define AD_ALLOCATION_TO_SLOT14_15_REG 0x0D26
+#define AD_ALLOCATION_TO_SLOT16_17_REG 0x0D27
+#define AD_ALLOCATION_TO_SLOT18_19_REG 0x0D28
+#define AD_ALLOCATION_TO_SLOT20_21_REG 0x0D29
+#define AD_ALLOCATION_TO_SLOT22_23_REG 0x0D2A
+#define AD_ALLOCATION_TO_SLOT24_25_REG 0x0D2B
+#define AD_ALLOCATION_TO_SLOT26_27_REG 0x0D2C
+#define AD_ALLOCATION_TO_SLOT28_29_REG 0x0D2D
+#define AD_ALLOCATION_TO_SLOT30_31_REG 0x0D2E
+#define AD_SLOT_0_TO_7_TRISTATE_REG 0x0D2F
+#define AD_SLOT_8_TO_15_TRISTATE_REG 0x0D30
+#define AD_SLOT_16_TO_23_TRISTATE_REG 0x0D31
+#define AD_SLOT_24_TO_31_TRISTATE_REG 0x0D32
+#define SLOT_SELECTION_TO_DA1_REG 0x0D33
+#define SLOT_SELECTION_TO_DA2_REG 0x0D34
+#define SLOT_SELECTION_TO_DA3_REG 0x0D35
+#define SLOT_SELECTION_TO_DA4_REG 0x0D36
+#define SLOT_SELECTION_TO_DA5_REG 0x0D37
+#define SLOT_SELECTION_TO_DA6_REG 0x0D38
+#define SLOT_SELECTION_TO_DA7_REG 0x0D39
+#define SLOT_SELECTION_TO_DA8_REG 0x0D3A
+#define CLASS_D_EMI_PARALLEL_CONF_REG 0x0D3B
+#define CLASS_D_PATH_CONTROL_REG 0x0D3C
+#define CLASS_D_DITHER_CONTROL_REG 0x0D3D
+#define DMIC_DECIMATOR_FILTER_REG 0x0D3E
+#define DIGITAL_MUXES_REG1 0x0D3F
+#define DIGITAL_MUXES_REG2 0x0D40
+#define AD1_DIGITAL_GAIN_REG 0x0D41
+#define AD2_DIGITAL_GAIN_REG 0x0D42
+#define AD3_DIGITAL_GAIN_REG 0x0D43
+#define AD4_DIGITAL_GAIN_REG 0x0D44
+#define AD5_DIGITAL_GAIN_REG 0x0D45
+#define AD6_DIGITAL_GAIN_REG 0x0D46
+#define DA1_DIGITAL_GAIN_REG 0x0D47
+#define DA2_DIGITAL_GAIN_REG 0x0D48
+#define DA3_DIGITAL_GAIN_REG 0x0D49
+#define DA4_DIGITAL_GAIN_REG 0x0D4A
+#define DA5_DIGITAL_GAIN_REG 0x0D4B
+#define DA6_DIGITAL_GAIN_REG 0x0D4C
+#define AD1_TO_HFL_DIGITAL_GAIN_REG 0x0D4D
+#define AD2_TO_HFR_DIGITAL_GAIN_REG 0x0D4E
+#define HSL_EAR_DIGITAL_GAIN_REG 0x0D4F
+#define HSR_DIGITAL_GAIN_REG 0x0D50
+#define SIDETONE_FIR1_GAIN_REG 0x0D51
+#define SIDETONE_FIR2_GAIN_REG 0x0D52
+#define ANC_FILTER_CONTROL_REG 0x0D53
+#define ANC_WARPED_GAIN_REG 0x0D54
+#define ANC_FIR_OUTPUT_GAIN_REG 0x0D55
+#define ANC_IIR_OUTPUT_GAIN_REG 0x0D56
+#define ANC_FIR_COEFF_MSB_REG 0x0D57
+#define ANC_FIR_COEFF_LSB_REG 0x0D58
+#define ANC_IIR_COEFF_MSB_REG 0x0D59
+#define ANC_IIR_COEFF_LSB_REG 0x0D5A
+#define ANC_WARP_DELAY_MSB_REG 0x0D5B
+#define ANC_WARP_DELAY_LSB_REG 0x0D5C
+#define ANC_FIR_PEAK_MSB_REG 0x0D5D
+#define ANC_FIR_PEAK_LSB_REG 0x0D5E
+#define ANC_IIR_PEAK_MSB_REG 0x0D5F
+#define ANC_IIR_PEAK_LSB_REG 0x0D60
+#define SIDETONE_FIR_ADDR_REG 0x0D61
+#define SIDETONE_FIR_COEFF_MSB_REG 0x0D62
+#define SIDETONE_FIR_COEFF_LSB_REG 0x0D63
+#define FILTERS_CONTROL_REG 0x0D64
+#define IRQ_MASK_LSB_REG 0x0D65
+#define IRQ_STATUS_LSB_REG 0x0D66
+#define IRQ_MASK_MSB_REG 0x0D67
+#define IRQ_STATUS_MSB_REG 0x0D68
+#define BURST_FIFO_INT_CONTROL_REG 0x0D69
+#define BURST_FIFO_LENGTH_REG 0x0D6A
+#define BURST_FIFO_CONTROL_REG 0x0D6B
+#define BURST_FIFO_SWITCH_FRAME_REG 0x0D6C
+#define BURST_FIFO_WAKE_UP_DELAY_REG 0x0D6D
+#define BURST_FIFO_SAMPLES_REG 0x0D6E
+#define REVISION_REG 0x0D6F
+
+/* POWER_UP_CONTROL_REG Masks */
+#define DEVICE_POWER_UP 0x80
+#define ANALOG_PARTS_POWER_UP 0x08
+
+/* SOFTWARE_RESET_REG Masks */
+#define SW_RESET 0x80
+
+/* DIGITAL_AD_CHANNELS_ENABLE_REG Masks */
+#define EN_AD1 0x80
+#define EN_AD2 0x80
+#define EN_AD3 0x20
+#define EN_AD4 0x20
+#define EN_AD5 0x08
+#define EN_AD6 0x04
+
+/* DIGITAL_DA_CHANNELS_ENABLE_REG Masks */
+#define EN_DA1 0x80
+#define EN_DA2 0x40
+#define EN_DA3 0x20
+#define EN_DA4 0x10
+#define EN_DA5 0x08
+#define EN_DA6 0x04
+
+/* LOW_POWER_HS_EAR_CONF_REG Masks */
+#define LOW_POWER_HS 0x80
+#define HS_DAC_DRIVER_LP 0x40
+#define HS_DAC_LP 0x20
+#define EAR_DAC_LP 0x10
+
+/* LINE_IN_MIC_CONF_REG Masks */
+#define EN_MIC1 0x80
+#define EN_MIC2 0x40
+#define EN_LIN_IN_L 0x20
+#define EN_LIN_IN_R 0x10
+#define MUT_MIC1 0x08
+#define MUT_MIC2 0x04
+#define MUT_LIN_IN_L 0x02
+#define MUT_LIN_IN_R 0x01
+
+/* DMIC_ENABLE_REG Masks */
+#define EN_DMIC1 0x80
+#define EN_DMIC2 0x40
+#define EN_DMIC3 0x20
+#define EN_DMIC4 0x10
+#define EN_DMIC5 0x08
+#define EN_DMIC6 0x04
+
+/* ADC_DAC_ENABLE_REG Masks */
+#define SEL_MIC1B_CLR_MIC1A 0x80
+#define SEL_LINR_CLR_MIC2 0x40
+#define POWER_UP_HSL_DAC 0x20
+#define POWER_UP_HSR_DAC 0x10
+#define POWER_UP_ADC1 0x04
+#define POWER_UP_ADC3 0x02
+#define POWER_UP_ADC2 0x01
+
+/* ANALOG_OUTPUT_ENABLE_REG and DIGITAL_OUTPUT_ENABLE_REG and
+ MUTE_HS_EAR_REG Masks */
+#define EN_EAR_DAC_MASK 0x04
+#define EN_HSL_DAC_MASK 0x02
+#define EN_HSR_DAC_MASK 0x01
+#define EN_EAR_MASK 0x40
+#define EN_HSL_MASK 0x20
+#define EN_HSR_MASK 0x10
+#define EN_HFL_MASK 0x08
+#define EN_HFR_MASK 0x04
+#define EN_VIBL_MASK 0x02
+#define EN_VIBR_MASK 0x01
+
+/* SHORT_CIRCUIT_DISABLE_REG Masks */
+#define HS_SHORT_DIS 0x20
+#define HS_PULL_DOWN_EN 0x10
+#define HS_OSC_EN 0x04
+#define DIS_HS_FAD 0x02
+#define HS_ZCD_DIS 0x01
+
+/* NCP_ENABLE_HS_AUTOSTART_REG Masks */
+#define EN_NEG_CP 0x80
+#define HS_AUTO_EN 0x01
+
+/* ANALOG_MIC1_GAIN_REG and ANALOG_MIC1_GAIN_REG Masks */
+#define MIC_ANALOG_GAIN_MASK 0x1F
+
+/*ANALOG_HS_GAIN_REG and ANALOG_LINE_IN_GAIN_REG Masks*/
+#define L_ANALOG_GAIN_MASK 0xF0
+#define R_ANALOG_GAIN_MASK 0x0F
+
+/* IF0_IF1_MASTER_CONF_REG Masks */
+#define EN_MASTGEN 0x80
+#define BITCLK_OSR_N_64 0x02
+#define BITCLK_OSR_N_128 0x04
+#define BITCLK_OSR_N_256 0x06
+#define EN_FSYNC_BITCLK 0x01
+#define EN_FSYNC_BITCLK1 0x10
+
+/* IF0_CONF_REG and IF1_CONF_REG Masks */
+#define FSYNC_FALLING_EDGE 0x40
+#define BITCLK_FALLING_EDGE 0x20
+#define IF_DELAYED 0x10
+#define I2S_LEFT_ALIGNED_FORMAT 0x08
+#define TDM_FORMAT 0x04
+#define WORD_LENGTH_32 0x03
+#define WORD_LENGTH_24 0x02
+#define WORD_LENGTH_20 0x01
+#define WORD_LENGTH_16 0x00
+
+/* TDM_IF_BYPASS_B_FIFO_REG Masks */
+#define IF0_BFifoEn 0x01
+#define IF0_MASTER 0x02
+
+#define IF1_MASTER 0x20
+/*
+ * AD_ALLOCATION_TO_SLOT0_1_REG and AD_ALLOCATION_TO_SLOT2_3_REG and
+ * AD_ALLOCATION_TO_SLOT4_5_REG and AD_ALLOCATION_TO_SLOT6_7_REG Masks
+ */
+#define DATA_FROM_AD_OUT1 0x00
+#define DATA_FROM_AD_OUT2 0x01
+#define DATA_FROM_AD_OUT3 0x02
+#define DATA_FROM_AD_OUT4 0x03
+#define DATA_FROM_AD_OUT5 0x04
+#define DATA_FROM_AD_OUT6 0x05
+#define DATA_FROM_AD_OUT7 0x06
+#define DATA_FROM_AD_OUT8 0x07
+#define TRISTATE 0x0C
+
+/*
+ * SLOT_SELECTION_TO_DA1_REG and SLOT_SELECTION_TO_DA2_REG and
+ * SLOT_SELECTION_TO_DA3_REG and SLOT_SELECTION_TO_DA4_REG Masks
+ * SLOT_SELECTION_TO_DA5_REG and SLOT_SELECTION_TO_DA6_REG Masks
+ */
+#define SLOT08_FOR_DA_PATH 0x08
+#define SLOT09_FOR_DA_PATH 0x09
+#define SLOT10_FOR_DA_PATH 0x0A
+#define SLOT11_FOR_DA_PATH 0x0B
+#define SLOT12_FOR_DA_PATH 0x0C
+#define SLOT13_FOR_DA_PATH 0x0D
+#define SLOT14_FOR_DA_PATH 0x0E
+#define SLOT15_FOR_DA_PATH 0x0F
+
+/* DIGITAL_MUXES_REG1 Masks */
+#define DA1_TO_HSL 0x80
+#define DA2_TO_HSR 0x40
+#define SEL_DMIC1_FOR_AD_OUT1 0x20
+#define SEL_DMIC2_FOR_AD_OUT2 0x10
+#define SEL_DMIC3_FOR_AD_OUT3 0x08
+/*#define SEL_DMIC5_FOR_AD_OUT5 0x04*/
+/*#define SEL_DMIC6_FOR_AD_OUT6 0x02*/
+/*#define SEL_DMIC1_FOR_AD_OUT1 0x01*/
+
+/*
+ * AD1_DIGITAL_GAIN_REG and AD2_DIGITAL_GAIN_REG & AD3_DIGITAL_GAIN_REG Masks
+ * AD4_DIGITAL_GAIN_REG and AD5_DIGITAL_GAIN_REG & AD6_DIGITAL_GAIN_REG Masks
+ * DA1_DIGITAL_GAIN_REG and DA2_DIGITAL_GAIN_REG & DA3_DIGITAL_GAIN_REG Masks
+ * DA4_DIGITAL_GAIN_REG and DA5_DIGITAL_GAIN_REG & DA6_DIGITAL_GAIN_REG Masks
+ */
+#define DIS_FADING 0x40
+#define DIGITAL_GAIN_MASK 0x3F
+
+/*
+ * HSL_EAR_DIGITAL_GAIN_REG and HSR_DIGITAL_GAIN_REG Masks
+ */
+#define FADE_SPEED_MASK 0xC0
+#define DIS_DIG_GAIN_FADING 0x10
+#define HS_DIGITAL_GAIN_MASK 0x0F
+
+/* FMRx/FMTx Masks */
+#define SLOT24_FOR_DA_PATH 0x18
+#define SEL_AD_OUT8_FROM_DAIN7 0x20
+#define SLOT25_FOR_DA_PATH 0x19
+#define SEL_AD_OUT6_FROM_DAIN8 0x20
+#define SEL_IF8_FROM_AD_OUT7 0x60
+#define SEL_IF17_FROM_AD_OUT7 0x60
+#define SEL_IF16_FROM_AD_OUT8 0x07
+
+#define SEL_IF6_FROM_AD_OUT5 0x04
+#define SEL_IF7_FROM_AD_OUT6 0x50
+#define SEL_IF17_FROM_AD_OUT6 0x50
+#define SEL_AD_OUT5_FROM_DAIN7 0x20
+
+/* Burst FIFO Control Masks */
+#define WAKEUP_SIGNAL_SAMPLE_COUNT 0x1B
+#define BURST_FIFO_TRANSFER_LENGTH 0xC0
+#define BURST_FIFO_INF_RUNNING 0x01
+#define BURST_FIFO_INF_IN_MASTER_MODE 0x02
+#define PRE_BIT_CLK0_COUNT 0x1C
+#define BURST_FIFO_WAKUP_DEALAY 0x70
+
+/* Filter Control Masks */
+/* SideTone Masks */
+#define SIDETONE_DIGITAL_GAIN_MASK 0x1F
+#define FIR1_FROMAD1 0x0C
+#define FIR1_FROMAD2 0x03
+#define FIR1_FROMAD3 0x08
+#define FIR1_DAIN1 0x0C
+
+#define FIR2_FROMAD2 0x00
+#define FIR2_FROMAD3 0x01
+#define FIR2_FROMAD4 0x02
+#define FIR2_DAIN2 0x03
+
+#define FIR2_ANDFIR1AD3 0x09
+#define FIR_FILTERCONTROL 0x04
+#define APPLY_FIR_COEFFS_MASK 0x80
+
+/* IRQ status masks */
+#define NCP_READY_MASK 0x80
+
+/* AB8500 power control Masks */
+#define AB8500_VER_1_0 0x10
+#define AB8500_VER_1_1 0x11
+#define CLK_32K_OUT2_DISABLE 0x01
+#define INACTIVE_RESET_AUDIO 0x02
+#define AB8500_REQ_SYS_CLK 0x08
+#define ENABLE_AUDIO_CLK_TO_AUDIO_BLK 0x10
+#define ENABLE_VINTCORE12_SUPPLY 0x04
+#define VAMIC2_ENABLE 0x10
+#define VAMIC1_ENABLE 0x08
+#define VDMIC_ENABLE 0x04
+#define VAUDIO_ENABLE 0x02
+#define GPIO27_DIR_OUTPUT 0x04
+#define GPIO29_DIR_OUTPUT 0x10
+#define GPIO31_DIR_OUTPUT 0x40
+#define GPIO35_DIR_OUTPUT 0X04
+#endif
diff --git a/drivers/misc/audio_io_dev/ste_audio_io_core.c b/drivers/misc/audio_io_dev/ste_audio_io_core.c
new file mode 100644
index 00000000000..bf93c6a9053
--- /dev/null
+++ b/drivers/misc/audio_io_dev/ste_audio_io_core.c
@@ -0,0 +1,1589 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <mach/ste_audio_io_vibrator.h>
+#include <mach/ste_audio_io.h>
+
+#include "ste_audio_io_core.h"
+#include "ste_audio_io_hwctrl_common.h"
+#include "ste_audio_io_ab8500_reg_defs.h"
+
+static struct audiocodec_context_t *ptr_audio_codec_cnxt;
+
+static struct clk *clk_ptr_msp1;
+static struct clk *clk_ptr_msp3;
+static struct clk *clk_ptr_audioclk;
+static struct clk *clk_ptr_sysclk;
+static struct clk *clk_ptr_ulpclk;
+
+static struct regulator *regulator_vdmic;
+static struct regulator *regulator_vaudio;
+static struct regulator *regulator_vamic1;
+static struct regulator *regulator_vamic2;
+struct regulator *regulator_avsource;
+
+static void ste_audio_io_init_transducer_cnxt(void);
+static int ste_audio_io_core_init_regulators(void);
+static int ste_audio_io_core_init_clocks(void);
+static int ste_audio_io_core_free_regulators(void);
+static int ste_audio_io_core_free_clocks(void);
+static int ste_audio_io_enable_audio_clock(void);
+static int ste_audio_io_disable_audio_clock(void);
+
+static struct transducer_context_t transducer_headset = {
+ .pwr_up_func = ste_audio_io_power_up_headset,
+ .pwr_down_func = ste_audio_io_power_down_headset,
+ .set_gain_func = ste_audio_io_set_headset_gain,
+ .get_gain_func = ste_audio_io_get_headset_gain,
+ .mute_func = ste_audio_io_mute_headset,
+ .unmute_func = ste_audio_io_unmute_headset,
+ .enable_fade_func = ste_audio_io_enable_fade_headset,
+ .disable_fade_func = ste_audio_io_disable_fade_headset,
+ .switch_to_burst_func = ste_audio_io_switch_to_burst_mode_headset,
+ .switch_to_normal_func = ste_audio_io_switch_to_normal_mode_headset
+};
+
+static struct transducer_context_t transducer_earpiece = {
+ .pwr_up_func = ste_audio_io_power_up_earpiece,
+ .pwr_down_func = ste_audio_io_power_down_earpiece,
+ .set_gain_func = ste_audio_io_set_earpiece_gain,
+ .get_gain_func = ste_audio_io_get_earpiece_gain,
+ .mute_func = ste_audio_io_mute_earpiece,
+ .unmute_func = ste_audio_io_unmute_earpiece,
+ .enable_fade_func = ste_audio_io_enable_fade_earpiece,
+ .disable_fade_func = ste_audio_io_disable_fade_earpiece
+};
+
+static struct transducer_context_t transducer_ihf = {
+ .pwr_up_func = ste_audio_io_power_up_ihf,
+ .pwr_down_func = ste_audio_io_power_down_ihf,
+ .set_gain_func = ste_audio_io_set_ihf_gain,
+ .get_gain_func = ste_audio_io_get_ihf_gain,
+ .mute_func = ste_audio_io_mute_ihf,
+ .unmute_func = ste_audio_io_unmute_ihf,
+ .enable_fade_func = ste_audio_io_enable_fade_ihf,
+ .disable_fade_func = ste_audio_io_disable_fade_ihf
+
+};
+
+static struct transducer_context_t transducer_vibl = {
+ .pwr_up_func = ste_audio_io_power_up_vibl,
+ .pwr_down_func = ste_audio_io_power_down_vibl,
+ .set_gain_func = ste_audio_io_set_vibl_gain,
+ .get_gain_func = ste_audio_io_get_vibl_gain,
+ .mute_func = ste_audio_io_mute_vibl,
+ .unmute_func = ste_audio_io_unmute_vibl,
+ .enable_fade_func = ste_audio_io_enable_fade_vibl,
+ .disable_fade_func = ste_audio_io_disable_fade_vibl
+};
+
+static struct transducer_context_t transducer_vibr = {
+ .pwr_up_func = ste_audio_io_power_up_vibr,
+ .pwr_down_func = ste_audio_io_power_down_vibr,
+ .set_gain_func = ste_audio_io_set_vibr_gain,
+ .get_gain_func = ste_audio_io_get_vibr_gain,
+ .mute_func = ste_audio_io_mute_vibr,
+ .unmute_func = ste_audio_io_unmute_vibr,
+ .enable_fade_func = ste_audio_io_enable_fade_vibr,
+ .disable_fade_func = ste_audio_io_disable_fade_vibr
+};
+
+static struct transducer_context_t transducer_mic1a = {
+ .pwr_up_func = ste_audio_io_power_up_mic1a,
+ .pwr_down_func = ste_audio_io_power_down_mic1a,
+ .set_gain_func = ste_audio_io_set_mic1a_gain,
+ .get_gain_func = ste_audio_io_get_mic1a_gain,
+ .mute_func = ste_audio_io_mute_mic1a,
+ .unmute_func = ste_audio_io_unmute_mic1a,
+ .enable_fade_func = ste_audio_io_enable_fade_mic1a,
+ .disable_fade_func = ste_audio_io_disable_fade_mic1a
+};
+
+static struct transducer_context_t transducer_mic1b = {
+ .pwr_up_func = ste_audio_io_power_up_mic1b,
+ .pwr_down_func = ste_audio_io_power_down_mic1b,
+ .set_gain_func = ste_audio_io_set_mic1a_gain,
+ .get_gain_func = ste_audio_io_get_mic1a_gain,
+ .mute_func = ste_audio_io_mute_mic1a,
+ .unmute_func = ste_audio_io_unmute_mic1a,
+ .enable_fade_func = ste_audio_io_enable_fade_mic1a,
+ .disable_fade_func = ste_audio_io_disable_fade_mic1a,
+ .enable_loop = ste_audio_io_enable_loop_mic1b,
+ .disable_loop = ste_audio_io_disable_loop_mic1b
+};
+
+static struct transducer_context_t transducer_mic2 = {
+ .pwr_up_func = ste_audio_io_power_up_mic2,
+ .pwr_down_func = ste_audio_io_power_down_mic2,
+ .set_gain_func = ste_audio_io_set_mic2_gain,
+ .get_gain_func = ste_audio_io_get_mic2_gain,
+ .mute_func = ste_audio_io_mute_mic2,
+ .unmute_func = ste_audio_io_unmute_mic2,
+ .enable_fade_func = ste_audio_io_enable_fade_mic2,
+ .disable_fade_func = ste_audio_io_disable_fade_mic2
+};
+
+static struct transducer_context_t transducer_lin = {
+ .pwr_up_func = ste_audio_io_power_up_lin,
+ .pwr_down_func = ste_audio_io_power_down_lin,
+ .set_gain_func = ste_audio_io_set_lin_gain,
+ .get_gain_func = ste_audio_io_get_lin_gain,
+ .mute_func = ste_audio_io_mute_lin,
+ .unmute_func = ste_audio_io_unmute_lin,
+ .enable_fade_func = ste_audio_io_enable_fade_lin,
+ .disable_fade_func = ste_audio_io_disable_fade_lin
+};
+
+static struct transducer_context_t transducer_dmic12 = {
+ .pwr_up_func = ste_audio_io_power_up_dmic12,
+ .pwr_down_func = ste_audio_io_power_down_dmic12,
+ .set_gain_func = ste_audio_io_set_dmic12_gain,
+ .get_gain_func = ste_audio_io_get_dmic12_gain,
+ .mute_func = ste_audio_io_mute_dmic12,
+ .unmute_func = ste_audio_io_unmute_dmic12,
+ .enable_fade_func = ste_audio_io_enable_fade_dmic12,
+ .disable_fade_func = ste_audio_io_disable_fade_dmic12,
+ .enable_loop = ste_audio_io_enable_loop_dmic12,
+ .disable_loop = ste_audio_io_disable_loop_dmic12
+};
+
+static struct transducer_context_t transducer_dmic34 = {
+ .pwr_up_func = ste_audio_io_power_up_dmic34,
+ .pwr_down_func = ste_audio_io_power_down_dmic34,
+ .set_gain_func = ste_audio_io_set_dmic34_gain,
+ .get_gain_func = ste_audio_io_get_dmic34_gain,
+ .mute_func = ste_audio_io_mute_dmic34,
+ .unmute_func = ste_audio_io_unmute_dmic34,
+ .enable_fade_func = ste_audio_io_enable_fade_dmic34,
+ .disable_fade_func = ste_audio_io_disable_fade_dmic34
+};
+
+static struct transducer_context_t transducer_dmic56 = {
+ .pwr_up_func = ste_audio_io_power_up_dmic56,
+ .pwr_down_func = ste_audio_io_power_down_dmic56,
+ .set_gain_func = ste_audio_io_set_dmic56_gain,
+ .get_gain_func = ste_audio_io_get_dmic56_gain,
+ .mute_func = ste_audio_io_mute_dmic56,
+ .unmute_func = ste_audio_io_unmute_dmic56,
+ .enable_fade_func = ste_audio_io_enable_fade_dmic56,
+ .disable_fade_func = ste_audio_io_disable_fade_dmic56,
+};
+
+static struct transducer_context_t transducer_fmrx = {
+ .pwr_up_func = ste_audio_io_power_up_fmrx,
+ .pwr_down_func = ste_audio_io_power_down_fmrx,
+};
+
+static struct transducer_context_t transducer_fmtx = {
+ .pwr_up_func = ste_audio_io_power_up_fmtx,
+ .pwr_down_func = ste_audio_io_power_down_fmtx,
+};
+
+static struct transducer_context_t transducer_bluetooth = {
+ .pwr_up_func = ste_audio_io_power_up_bluetooth,
+ .pwr_down_func = ste_audio_io_power_down_bluetooth,
+};
+
+bool ste_audio_io_core_is_ready_for_suspend()
+{
+ bool err = false;
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+ if ((!ptr_audio_codec_cnxt->power_client) &&
+ (!ptr_audio_codec_cnxt->audio_codec_powerup))
+ err = true;
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+ return err;
+}
+
+static int ste_audio_io_core_init_regulators()
+{
+ int error = 0;
+ regulator_vdmic = regulator_get(NULL, "v-dmic");
+ if (IS_ERR(regulator_vdmic)) {
+ error = PTR_ERR(regulator_vdmic);
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Register error for v-dmic=%d", error);
+ return error;
+ }
+ regulator_vamic1 = regulator_get(NULL, "v-amic1");
+ if (IS_ERR(regulator_vamic1)) {
+ error = PTR_ERR(regulator_vamic1);
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Register error for v-amic1=%d", error);
+ goto free_regulator_vdmic;
+ }
+ regulator_vamic2 = regulator_get(NULL, "v-amic2");
+ if (IS_ERR(regulator_vamic2)) {
+ error = PTR_ERR(regulator_vamic2);
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Register error for v-amic2=%d", error);
+ goto free_regulator_vdmic_vamic1;
+ }
+ regulator_vaudio = regulator_get(NULL, "v-audio");
+ if (IS_ERR(regulator_vaudio)) {
+ error = PTR_ERR(regulator_vaudio);
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Register error for v-audio=%d", error);
+ goto free_regulator_vdmic_vamic1_vamic2;
+ }
+ regulator_avsource = regulator_get(ptr_audio_codec_cnxt->dev,
+ "vcc-avswitch");
+ if (IS_ERR(regulator_avsource)) {
+ error = PTR_ERR(regulator_avsource);
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Register error for vcc-avswitch =%d", error);
+ goto free_regulator_vdmic_vamic1_vamic2_vaudio;
+ }
+ return error;
+free_regulator_vdmic_vamic1_vamic2_vaudio:
+ regulator_put(regulator_vaudio);
+free_regulator_vdmic_vamic1_vamic2:
+ regulator_put(regulator_vamic2);
+free_regulator_vdmic_vamic1:
+ regulator_put(regulator_vamic1);
+free_regulator_vdmic:
+ regulator_put(regulator_vdmic);
+ return error;
+}
+
+static int ste_audio_io_core_free_regulators()
+{
+ regulator_put(regulator_vdmic);
+ regulator_put(regulator_vamic1);
+ regulator_put(regulator_vamic2);
+ regulator_put(regulator_vaudio);
+ regulator_put(regulator_avsource);
+ return 0;
+}
+
+static int ste_audio_io_core_init_clocks()
+{
+ int error = 0;
+ clk_ptr_sysclk = clk_get(ptr_audio_codec_cnxt->dev, "sysclk");
+ if (IS_ERR(clk_ptr_sysclk)) {
+ error = -EFAULT;
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Sysclk get failed error = %d", error);
+ return error;
+ }
+ clk_ptr_ulpclk = clk_get(ptr_audio_codec_cnxt->dev, "ulpclk");
+ if (IS_ERR(clk_ptr_ulpclk)) {
+ error = -EFAULT;
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Ulpclk get failed error = %d", error);
+ goto free_sysclk;
+ }
+ clk_ptr_audioclk = clk_get(ptr_audio_codec_cnxt->dev, "audioclk");
+ if (IS_ERR(clk_ptr_audioclk)) {
+ error = -EFAULT;
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Audioclk get failed error = %d", error);
+ goto free_ulpclk;
+ }
+ return error;
+free_ulpclk:
+ clk_put(clk_ptr_ulpclk);
+free_sysclk:
+ clk_put(clk_ptr_sysclk);
+ return error;
+}
+
+static int ste_audio_io_core_free_clocks()
+{
+ clk_put(clk_ptr_audioclk);
+ clk_put(clk_ptr_ulpclk);
+ clk_put(clk_ptr_sysclk);
+ return 0;
+}
+
+int ste_audio_io_core_api_init_data(struct platform_device *pdev)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *pdata = dev_get_platdata(ab8500->dev);
+ int error = 0;
+ ptr_audio_codec_cnxt = kmalloc(sizeof(struct audiocodec_context_t),
+ GFP_KERNEL);
+ if (!ptr_audio_codec_cnxt)
+ return -ENOMEM;
+
+ memset(ptr_audio_codec_cnxt, 0, sizeof(*ptr_audio_codec_cnxt));
+ ptr_audio_codec_cnxt->dev = &pdev->dev;
+ ptr_audio_codec_cnxt->clk_type = AUDIOIO_ULP_CLK;
+ mutex_init(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ if (pdata) {
+ if (pdata->audio) {
+ ptr_audio_codec_cnxt->gpio_altf_init =
+ pdata->audio->ste_gpio_altf_init;
+ ptr_audio_codec_cnxt->gpio_altf_exit =
+ pdata->audio->ste_gpio_altf_exit;
+ }
+ }
+
+ error = ste_audio_io_core_init_regulators();
+ if (error)
+ goto free_audio_codec_cnxt;
+ error = ste_audio_io_core_init_clocks();
+ if (error)
+ goto free_audio_codec_cnxt_regulators;
+ ste_audio_io_init_transducer_cnxt();
+ return error;
+
+free_audio_codec_cnxt_regulators:
+ ste_audio_io_core_free_regulators();
+free_audio_codec_cnxt:
+ kfree(ptr_audio_codec_cnxt);
+ return error;
+}
+
+static int ste_audio_io_enable_audio_clock()
+{
+ int error = 0;
+ if (ptr_audio_codec_cnxt->is_audio_clk_enabled)
+ return 0;
+
+ if (AUDIOIO_ULP_CLK == ptr_audio_codec_cnxt->clk_type) {
+ error = clk_set_parent(clk_ptr_audioclk, clk_ptr_ulpclk);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Setting Ulpclk as parent failed error = %d", error);
+ return error;
+ }
+ } else {
+ error = clk_set_parent(clk_ptr_audioclk, clk_ptr_sysclk);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Setting Sysclk as parent failed error = %d", error);
+ return error;
+ }
+ }
+ error = clk_enable(clk_ptr_audioclk);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Audioclk enable failed error = %d", error);
+ return error;
+ }
+ ptr_audio_codec_cnxt->is_audio_clk_enabled = 1;
+ return error;
+}
+
+static int ste_audio_io_disable_audio_clock()
+{
+ if (!ptr_audio_codec_cnxt->is_audio_clk_enabled)
+ return 0;
+ clk_disable(clk_ptr_audioclk);
+ ptr_audio_codec_cnxt->is_audio_clk_enabled = 0;
+ return 0;
+}
+
+static void ste_audio_io_init_transducer_cnxt(void)
+{
+ ptr_audio_codec_cnxt->transducer[HS_CH] = &transducer_headset;
+ ptr_audio_codec_cnxt->transducer[EAR_CH] = &transducer_earpiece;
+ ptr_audio_codec_cnxt->transducer[IHF_CH] = &transducer_ihf;
+ ptr_audio_codec_cnxt->transducer[VIBL_CH] = &transducer_vibl;
+ ptr_audio_codec_cnxt->transducer[VIBR_CH] = &transducer_vibr;
+ ptr_audio_codec_cnxt->transducer[MIC1A_CH] = &transducer_mic1a;
+ ptr_audio_codec_cnxt->transducer[MIC1B_CH] = &transducer_mic1b;
+ ptr_audio_codec_cnxt->transducer[MIC2_CH] = &transducer_mic2;
+ ptr_audio_codec_cnxt->transducer[LIN_CH] = &transducer_lin;
+ ptr_audio_codec_cnxt->transducer[DMIC12_CH] = &transducer_dmic12;
+ ptr_audio_codec_cnxt->transducer[DMIC34_CH] = &transducer_dmic34;
+ ptr_audio_codec_cnxt->transducer[DMIC56_CH] = &transducer_dmic56;
+ ptr_audio_codec_cnxt->transducer[FMRX_CH] = &transducer_fmrx;
+ ptr_audio_codec_cnxt->transducer[FMTX_CH] = &transducer_fmtx;
+ ptr_audio_codec_cnxt->transducer[BLUETOOTH_CH] = &transducer_bluetooth;
+}
+
+void ste_audio_io_core_api_free_data(void)
+{
+ ste_audio_io_core_free_regulators();
+ ste_audio_io_core_free_clocks();
+ kfree(ptr_audio_codec_cnxt);
+}
+
+static int ste_audio_io_core_api_enable_regulators(int channel_type)
+{
+ int error = 0;
+
+ switch (channel_type) {
+ case EAR_CH:
+ case HS_CH:
+ case IHF_CH:
+ case VIBL_CH:
+ case VIBR_CH:
+ case LIN_CH:
+ case FMRX_CH:
+ case FMTX_CH:
+ case BLUETOOTH_CH:
+ /* vaduio already enabled
+ no additional regualtor required */
+ break;
+
+ case MIC1A_CH:
+ case MIC1B_CH:
+ error = regulator_enable(regulator_vamic1);
+ if (error)
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "unable to enable regulator vamic1 error = %d", error);
+ break;
+
+ case MIC2_CH:
+ error = regulator_enable(regulator_vamic2);
+ if (error)
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "unable to enable regulator vamic2 error = %d", error);
+ break;
+
+ case DMIC12_CH:
+ case DMIC34_CH:
+ case DMIC56_CH:
+ case MULTI_MIC_CH:
+ error = regulator_enable(regulator_vdmic);
+ if (error)
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "unable to enable regulator vdmic error = %d", error);
+ }
+ return error;
+}
+
+static int ste_audio_io_core_api_disable_regulators(int channel_type)
+{
+ int error = 0;
+
+ switch (channel_type) {
+ case EAR_CH:
+ case HS_CH:
+ case IHF_CH:
+ case VIBL_CH:
+ case VIBR_CH:
+ case LIN_CH:
+ case FMRX_CH:
+ case FMTX_CH:
+ case BLUETOOTH_CH:
+ /* no need to disable separately*/
+ break;
+
+ case MIC1A_CH:
+ case MIC1B_CH:
+ error = regulator_disable(regulator_vamic1);
+ if (error)
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "unable to disable regulator vamic1 error = %d", error);
+ break;
+
+ case MIC2_CH:
+ error = regulator_disable(regulator_vamic2);
+ if (error)
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "unable to disable regulator vamic2 error = %d", error);
+ break;
+
+ case DMIC12_CH:
+ case DMIC34_CH:
+ case DMIC56_CH:
+ case MULTI_MIC_CH:
+ error = regulator_disable(regulator_vdmic);
+ if (error)
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "unable to disable regulator vdmic error = %d", error);
+ }
+ return error;
+}
+
+int ste_audio_io_core_api_powerup_audiocodec(int power_client)
+{
+ int error = 0;
+ int acodec_device_id;
+ __u8 data, old_data;
+ /* aquire mutex */
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ acodec_device_id = abx500_get_chip_id(&ste_audio_io_device->dev);
+
+ /*
+ * If there is no power client registered, power up
+ * common audio blocks for audio and vibrator
+ */
+ if (!ptr_audio_codec_cnxt->power_client) {
+ error = ste_audio_io_enable_audio_clock();
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Unable to enable audio clock = %d", error);
+ goto err_cleanup;
+ }
+ old_data = HW_REG_READ(AB8500_CTRL3_REG);
+
+ /* Enable 32 Khz clock signal on Clk32KOut2 ball */
+ data = (~CLK_32K_OUT2_DISABLE) & old_data;
+ error = HW_REG_WRITE(AB8500_CTRL3_REG, data);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "enabling 32KHz clock error = %d", error);
+ goto err_cleanup;
+ }
+ data = INACTIVE_RESET_AUDIO | old_data;
+ error = HW_REG_WRITE(AB8500_CTRL3_REG, data);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "deactivate audio codec reset error = %d", error);
+ goto err_cleanup;
+ }
+ regulator_enable(regulator_vaudio);
+
+ old_data = HW_REG_READ(AB8500_GPIO_DIR4_REG);
+ data = (GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT |
+ GPIO31_DIR_OUTPUT) | old_data;
+ error = HW_REG_WRITE(AB8500_GPIO_DIR4_REG, data);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "setting gpio dir4 error = %d", error);
+ goto err_cleanup;
+ }
+ error = HW_REG_WRITE(SOFTWARE_RESET_REG, SW_RESET);
+ if (error != 0) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Software reset error=%d", error);
+ goto err_cleanup;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(POWER_UP_CONTROL_REG,
+ (DEVICE_POWER_UP|ANALOG_PARTS_POWER_UP), 0);
+ if (error != 0) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Device Power Up, error=%d", error);
+ goto err_cleanup;
+ }
+ }
+ /* Save information that given client already powered up audio block */
+ ptr_audio_codec_cnxt->power_client |= power_client;
+
+ /* If audio block requested power up, turn on additional audio blocks */
+ if (power_client == STE_AUDIOIO_POWER_AUDIO) {
+ if (!ptr_audio_codec_cnxt->audio_codec_powerup) {
+ clk_ptr_msp1 = clk_get_sys("msp1", NULL);
+ if (!IS_ERR(clk_ptr_msp1)) {
+ error = clk_enable(clk_ptr_msp1);
+ if (error)
+ goto err_cleanup;
+ } else {
+ error = -EFAULT;
+ goto err_cleanup;
+ }
+
+ if (AB8500_REV_20 <= acodec_device_id) {
+ clk_ptr_msp3 = clk_get_sys("msp3", NULL);
+ if (!IS_ERR(clk_ptr_msp3)) {
+ error = clk_enable(clk_ptr_msp3);
+ if (error)
+ goto err_cleanup;
+ } else {
+ error = -EFAULT;
+ goto err_cleanup;
+ }
+ }
+
+ if (ptr_audio_codec_cnxt->gpio_altf_init) {
+ error = ptr_audio_codec_cnxt->gpio_altf_init();
+ if (error)
+ goto err_cleanup;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(IF0_IF1_MASTER_CONF_REG,
+ EN_MASTGEN, 0);
+ if (error != 0) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Enable Master Generator, error=%d", error);
+ goto err_cleanup;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(TDM_IF_BYPASS_B_FIFO_REG,
+ IF0_MASTER, 0);
+ if (error != 0) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "IF0: Master Mode, error=%d", error);
+ goto err_cleanup;
+ }
+
+ /* Configuring IF0 */
+
+ error = HW_ACODEC_MODIFY_WRITE(IF0_IF1_MASTER_CONF_REG,
+ BITCLK_OSR_N_256, 0);
+ if (error != 0) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "IF0: Enable FsBitClk & FSync error=%d", error);
+ goto err_cleanup;
+ }
+
+ error = HW_REG_WRITE(IF0_CONF_REG, IF_DELAYED
+ | TDM_FORMAT | WORD_LENGTH_20);
+ if (error != 0) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "IF0: TDM Format 16 Bits word length, error=%d",
+ error);
+ goto err_cleanup;
+ }
+ }
+ ptr_audio_codec_cnxt->audio_codec_powerup++;
+ }
+err_cleanup:
+ /* release mutex */
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+ return error;
+}
+
+int ste_audio_io_core_api_powerdown_audiocodec(int power_client)
+{
+ int error = 0;
+ /* aquire mutex */
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ /* Update power client status */
+ if (power_client == STE_AUDIOIO_POWER_AUDIO) {
+ ptr_audio_codec_cnxt->audio_codec_powerup--;
+ if (!ptr_audio_codec_cnxt->audio_codec_powerup) {
+ ptr_audio_codec_cnxt->power_client &= ~power_client;
+ ste_audio_io_disable_audio_clock();
+ clk_disable(clk_ptr_msp1);
+ clk_put(clk_ptr_msp1);
+ if (AB8500_REV_20 <=
+ abx500_get_chip_id(&ste_audio_io_device->dev)) {
+ clk_disable(clk_ptr_msp3);
+ clk_put(clk_ptr_msp3);
+ }
+
+ if (ptr_audio_codec_cnxt->gpio_altf_exit) {
+ error = ptr_audio_codec_cnxt->gpio_altf_exit();
+ if (error)
+ goto err_cleanup;
+ }
+ }
+ } else
+ ptr_audio_codec_cnxt->power_client &= ~power_client;
+
+ /* If no power client registered, power down audio block */
+ if (!ptr_audio_codec_cnxt->power_client) {
+ regulator_disable(regulator_vaudio);
+ ste_audio_io_disable_audio_clock();
+ if (error != 0) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Device Power Down and Analog Parts Power Down error = %d ",
+ error);
+ goto err_cleanup;
+ }
+ }
+
+err_cleanup:
+ /* release mutex */
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+ return error;
+}
+/**
+ * @brief Read from AB8500 device
+ * @dev_data Pointer to the structure __audioio_data
+ * @return 0
+ */
+
+int ste_audio_io_core_api_access_read(struct audioio_data_t *dev_data)
+{
+ int reg;
+ if (NULL == dev_data)
+ return -EFAULT;
+ reg = (dev_data->block<<8)|(dev_data->addr&0xff);
+ dev_data->data = HW_REG_READ(reg);
+ return 0;
+}
+/**
+ * @brief Write on AB8500 device
+ * @dev_data Pointer to the structure __audioio_data
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_core_api_access_write(struct audioio_data_t *dev_data)
+{
+ int retval, reg;
+ if (NULL == dev_data)
+ return -EFAULT;
+
+ reg = (dev_data->block<<8)|(dev_data->addr&0xff);
+ retval = HW_REG_WRITE(reg, dev_data->data);
+
+ return retval;
+}
+/**
+ * @brief Store the power and mute status of transducer
+ * @channel_index Channel-index of transducer
+ * @ptr Array storing the status
+ * @value status being stored
+ * @return 0 on success otherwise negative error code
+ */
+
+void ste_audio_io_core_api_store_data(enum AUDIOIO_CH_INDEX channel_index,
+ int *ptr, int value)
+{
+ if (channel_index & e_CHANNEL_1)
+ ptr[0] = value;
+
+ if (channel_index & e_CHANNEL_2)
+ ptr[1] = value;
+
+ if (channel_index & e_CHANNEL_3)
+ ptr[2] = value;
+
+ if (channel_index & e_CHANNEL_4)
+ ptr[3] = value;
+}
+/**
+ * @brief Get power or mute status on a specific channel
+ * @channel_index Channel-index of the transducer
+ * @ptr Pointer to is_power_up array or is_muted array
+ * @return status of control switch
+ */
+enum AUDIOIO_COMMON_SWITCH ste_audio_io_core_api_get_status(
+ enum AUDIOIO_CH_INDEX channel_index, int *ptr)
+{
+ if (channel_index & e_CHANNEL_1) {
+ if (AUDIOIO_TRUE == ptr[0])
+ return AUDIOIO_COMMON_ON;
+ else
+ return AUDIOIO_COMMON_OFF;
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ if (AUDIOIO_TRUE == ptr[1])
+ return AUDIOIO_COMMON_ON;
+ else
+ return AUDIOIO_COMMON_OFF;
+ }
+
+ if (channel_index & e_CHANNEL_3) {
+ if (AUDIOIO_TRUE == ptr[2])
+ return AUDIOIO_COMMON_ON;
+ else
+ return AUDIOIO_COMMON_OFF;
+ }
+
+ if (channel_index & e_CHANNEL_4) {
+ if (AUDIOIO_TRUE == ptr[3])
+ return AUDIOIO_COMMON_ON;
+ else
+ return AUDIOIO_COMMON_OFF;
+ }
+ return 0;
+}
+
+int ste_audio_io_core_api_acodec_power_control(struct audioio_acodec_pwr_ctrl_t
+ *audio_acodec_pwr_ctrl)
+{
+ int error = 0;
+ if (audio_acodec_pwr_ctrl->ctrl_switch == AUDIOIO_COMMON_ON)
+ error = ste_audio_io_core_api_powerup_audiocodec(
+ STE_AUDIOIO_POWER_AUDIO);
+ else
+ error = ste_audio_io_core_api_powerdown_audiocodec(
+ STE_AUDIOIO_POWER_AUDIO);
+
+ return error;
+}
+/**
+ * @brief Control for powering on/off HW components on a specific channel
+ * @pwr_ctrl Pointer to the structure __audioio_pwr_ctrl
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_core_api_power_control_transducer(
+ struct audioio_pwr_ctrl_t *pwr_ctrl)
+{
+ int error = 0;
+ struct transducer_context_t *ptr = NULL;
+ enum AUDIOIO_CH_INDEX channel_index;
+
+ channel_index = pwr_ctrl->channel_index;
+
+ if ((pwr_ctrl->channel_type < FIRST_CH)
+ || (pwr_ctrl->channel_type > LAST_CH))
+ return -EINVAL;
+
+ ptr = ptr_audio_codec_cnxt->transducer[pwr_ctrl->channel_type];
+
+ /* aquire mutex */
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ if (AUDIOIO_COMMON_ON == pwr_ctrl->ctrl_switch) {
+ if (ptr->pwr_up_func) {
+ error = ste_audio_io_core_api_enable_regulators(
+ pwr_ctrl->channel_type);
+ if (error)
+ goto free_mutex;
+
+ error = ptr->pwr_up_func(pwr_ctrl->channel_index,
+ ptr_audio_codec_cnxt->dev);
+ if (0 == error) {
+ ste_audio_io_core_api_store_data(channel_index,
+ ptr->is_power_up, AUDIOIO_TRUE);
+ }
+ }
+ } else {
+ if (ptr->pwr_down_func) {
+ error = ptr->pwr_down_func(pwr_ctrl->channel_index,
+ ptr_audio_codec_cnxt->dev);
+ if (0 == error) {
+ ste_audio_io_core_api_store_data(channel_index,
+ ptr->is_power_up, AUDIOIO_FALSE);
+ }
+ error = ste_audio_io_core_api_disable_regulators(
+ pwr_ctrl->channel_type);
+ }
+ }
+
+free_mutex:
+ /* release mutex */
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ return error;
+}
+/**
+ * @brief Query power state of HW path on specified channel
+ * @pwr_ctrl Pointer to the structure __audioio_pwr_ctrl
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_core_api_power_status_transducer(
+ struct audioio_pwr_ctrl_t *pwr_ctrl)
+{
+
+ struct transducer_context_t *ptr = NULL;
+ enum AUDIOIO_CH_INDEX channel_index;
+
+ channel_index = pwr_ctrl->channel_index;
+
+ if ((pwr_ctrl->channel_type < FIRST_CH)
+ || (pwr_ctrl->channel_type > LAST_CH))
+ return -EINVAL;
+
+ ptr = ptr_audio_codec_cnxt->transducer[pwr_ctrl->channel_type];
+
+
+ /* aquire mutex */
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+
+ pwr_ctrl->ctrl_switch = ste_audio_io_core_api_get_status(channel_index,
+ ptr->is_power_up);
+
+ /* release mutex */
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ return 0;
+
+}
+
+int ste_audio_io_core_api_loop_control(struct audioio_loop_ctrl_t *loop_ctrl)
+{
+ int error = 0;
+ struct transducer_context_t *ptr = NULL;
+
+ if ((loop_ctrl->channel_type < FIRST_CH)
+ || (loop_ctrl->channel_type > LAST_CH))
+ return -EINVAL;
+
+ ptr = ptr_audio_codec_cnxt->transducer[loop_ctrl->channel_type];
+
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ if (AUDIOIO_COMMON_ON == loop_ctrl->ctrl_switch) {
+ if (ptr->enable_loop) {
+ error = ptr->enable_loop(loop_ctrl->channel_index,
+ loop_ctrl->hw_loop,
+ loop_ctrl->loop_gain,
+ ptr_audio_codec_cnxt->dev,
+ ptr_audio_codec_cnxt->transducer);
+ if (error)
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Loop enable failed for hw loop = %d, error = %d ",
+ (int)loop_ctrl->hw_loop, error);
+ } else {
+ error = -EFAULT;
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Hw Loop enable does not exist for channel= %d, error = %d ",
+ (int)loop_ctrl->channel_type, error);
+ }
+ } else {
+ if (ptr->disable_loop) {
+ error = ptr->disable_loop(loop_ctrl->channel_index,
+ loop_ctrl->hw_loop,
+ ptr_audio_codec_cnxt->dev,
+ ptr_audio_codec_cnxt->transducer);
+ if (error)
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Loop disable failed for hw loop = %d, error = %d ",
+ (int)loop_ctrl->hw_loop, error);
+ } else {
+ error = -EFAULT;
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Hw Loop disable does not exist for channel= %d, error = %d ",
+ (int)loop_ctrl->channel_type, error);
+ }
+ }
+
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ return error;
+}
+
+int ste_audio_io_core_api_loop_status(struct audioio_loop_ctrl_t *loop_ctrl)
+{
+ return 0;
+}
+
+int ste_audio_io_core_api_get_transducer_gain_capability(
+ struct audioio_get_gain_t *get_gain)
+{
+ return 0;
+}
+
+int ste_audio_io_core_api_gain_capabilities_loop(
+ struct audioio_gain_loop_t *gain_loop)
+{
+ if ((gain_loop->channel_type < FIRST_CH)
+ || (gain_loop->channel_type > LAST_CH))
+ return -EINVAL;
+
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ gain_loop->num_loop =
+ transducer_max_no_Of_supported_loops[gain_loop->channel_type];
+ gain_loop->max_gains = max_no_of_loop_gains[gain_loop->channel_type];
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+ return 0;
+}
+
+int ste_audio_io_core_api_supported_loops(
+ struct audioio_support_loop_t *support_loop)
+{
+ if ((support_loop->channel_type < FIRST_CH)
+ || (support_loop->channel_type > LAST_CH))
+ return -EINVAL;
+
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+ support_loop->spprtd_loop_index =
+ transducer_no_Of_supported_loop_indexes[support_loop->channel_type];
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+ return 0;
+}
+
+int ste_audio_io_core_api_gain_descriptor_transducer(
+ struct audioio_gain_desc_trnsdr_t *gdesc_trnsdr)
+{
+ return 0;
+}
+/**
+ * @brief Control for muting a specific channel in HW
+ * @mute_trnsdr Pointer to the structure __audioio_mute_trnsdr
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_core_api_mute_control_transducer(
+ struct audioio_mute_trnsdr_t *mute_trnsdr)
+{
+ int error = 0;
+ struct transducer_context_t *ptr = NULL;
+ enum AUDIOIO_CH_INDEX channel_index;
+
+ channel_index = mute_trnsdr->channel_index;
+
+ if ((mute_trnsdr->channel_type < FIRST_CH)
+ || (mute_trnsdr->channel_type > LAST_CH))
+ return -EINVAL;
+
+ ptr = ptr_audio_codec_cnxt->transducer[mute_trnsdr->channel_type];
+
+ /* aquire mutex */
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ if (AUDIOIO_COMMON_ON == mute_trnsdr->ctrl_switch) {
+ if (ptr->mute_func) {
+ error = ptr->mute_func(mute_trnsdr->channel_index,
+ ptr_audio_codec_cnxt->dev);
+ if (0 == error) {
+ ste_audio_io_core_api_store_data(channel_index ,
+ ptr->is_muted, AUDIOIO_TRUE);
+ }
+ }
+ } else {
+ if (ptr->unmute_func) {
+ if (0 == ptr->unmute_func(channel_index, ptr->gain,
+ ptr_audio_codec_cnxt->dev)) {
+ ste_audio_io_core_api_store_data(channel_index,
+ ptr->is_muted, AUDIOIO_FALSE);
+ }
+ }
+ }
+
+ /* release mutex */
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ return error;
+}
+/**
+ * @brief Query state of mute on specified channel
+ * @mute_trnsdr Pointer to the structure __audioio_mute_trnsdr
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_core_api_mute_status_transducer(
+ struct audioio_mute_trnsdr_t *mute_trnsdr)
+{
+ struct transducer_context_t *ptr = NULL;
+ enum AUDIOIO_CH_INDEX channel_index;
+
+ channel_index = mute_trnsdr->channel_index;
+
+ if ((mute_trnsdr->channel_type < FIRST_CH)
+ || (mute_trnsdr->channel_type > LAST_CH))
+ return -EINVAL;
+
+ ptr = ptr_audio_codec_cnxt->transducer[mute_trnsdr->channel_type];
+
+ /* aquire mutex */
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ mute_trnsdr->ctrl_switch = ste_audio_io_core_api_get_status(
+ channel_index, ptr->is_muted);
+ /* release mutex */
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ return 0;
+}
+/**
+ * @brief control the fading on the transducer called on.
+ * @fade_ctrl Pointer to the structure __audioio_fade_ctrl
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_core_api_fading_control(struct audioio_fade_ctrl_t *fade_ctrl)
+{
+ int error = 0;
+ struct transducer_context_t *ptr = NULL;
+
+ if ((fade_ctrl->channel_type < FIRST_CH)
+ || (fade_ctrl->channel_type > LAST_CH))
+ return -EINVAL;
+ ptr = ptr_audio_codec_cnxt->transducer[fade_ctrl->channel_type];
+
+ /* aquire mutex */
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ if (AUDIOIO_COMMON_ON == fade_ctrl->ctrl_switch)
+ error = ptr->enable_fade_func(ptr_audio_codec_cnxt->dev);
+
+ else
+ error = ptr->disable_fade_func(ptr_audio_codec_cnxt->dev);
+
+
+ /* release mutex */
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ return error;
+}
+/**
+ * @brief control the low power mode of headset.
+ * @burst_ctrl Pointer to the structure __audioio_burst_ctrl
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_core_api_burstmode_control(
+ struct audioio_burst_ctrl_t *burst_ctrl)
+{
+ int error = 0;
+ struct transducer_context_t *ptr = NULL;
+ int burst_fifo_switch_frame;
+
+ burst_fifo_switch_frame = burst_ctrl->burst_fifo_switch_frame;
+
+ if ((burst_ctrl->channel_type < FIRST_CH)
+ || (burst_ctrl->channel_type > LAST_CH))
+ return -EINVAL;
+ ptr = ptr_audio_codec_cnxt->transducer[burst_ctrl->channel_type];
+
+ /* aquire mutex */
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ if (AUDIOIO_COMMON_ON == burst_ctrl->ctrl_switch) {
+ if (ptr->switch_to_burst_func)
+ error = ptr->switch_to_burst_func(
+ burst_fifo_switch_frame,
+ ptr_audio_codec_cnxt->dev);
+ } else
+ if (ptr->switch_to_normal_func)
+ error = ptr->switch_to_normal_func(
+ ptr_audio_codec_cnxt->dev);
+ /* release mutex */
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ return error;
+}
+/**
+ * @brief Convert channel index to array index
+ * @channel_index Channel Index of transducer
+ * @return Array index corresponding to the specified channel index
+ */
+
+int convert_channel_index_to_array_index(enum AUDIOIO_CH_INDEX channel_index)
+{
+ if (channel_index & e_CHANNEL_1)
+ return 0;
+ else if (channel_index & e_CHANNEL_2)
+ return 1;
+ else if (channel_index & e_CHANNEL_3)
+ return 2;
+ else
+ return 3;
+}
+
+/**
+ * @brief Set individual gain along the HW path of a specified channel
+ * @gctrl_trnsdr Pointer to the structure __audioio_gain_ctrl_trnsdr
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_core_api_gain_control_transducer(
+ struct audioio_gain_ctrl_trnsdr_t *gctrl_trnsdr)
+{
+ struct transducer_context_t *ptr = NULL;
+ enum AUDIOIO_CH_INDEX channel_index;
+ int ch_array_index;
+ u16 gain_index;
+ int gain_value;
+ u32 linear;
+ int channel_type;
+ int error;
+ int min_gain, max_gain, gain;
+
+ if ((gctrl_trnsdr->channel_type < FIRST_CH)
+ || (gctrl_trnsdr->channel_type > LAST_CH))
+ return -EINVAL;
+
+ if (gctrl_trnsdr->gain_index >= MAX_NO_GAINS)
+ return -EINVAL;
+
+ ptr = ptr_audio_codec_cnxt->transducer[gctrl_trnsdr->channel_type];
+ channel_index = gctrl_trnsdr->channel_index;
+ gain_index = gctrl_trnsdr->gain_index;
+ gain_value = gctrl_trnsdr->gain_value;
+ linear = gctrl_trnsdr->linear;
+ channel_type = gctrl_trnsdr->channel_type;
+
+ ch_array_index = convert_channel_index_to_array_index(channel_index);
+ if (linear) { /* Gain is in the range 0 to 100 */
+ min_gain = gain_descriptor[channel_type]\
+ [ch_array_index][gain_index].min_gain;
+ max_gain = gain_descriptor[channel_type]\
+ [ch_array_index][gain_index].max_gain;
+
+ gain = ((gain_value * (max_gain - min_gain))/100) + min_gain;
+ } else
+ /* Convert to db */
+ gain = gain_value/100;
+
+ gain_value = gain;
+
+#if 1
+ if (gain_index >= transducer_no_of_gains[channel_type])
+ return -EINVAL;
+
+ if (gain_value < gain_descriptor[channel_type]\
+ [ch_array_index][gain_index].min_gain)
+ return -EINVAL;
+
+ if (gain_value > gain_descriptor[channel_type]\
+ [ch_array_index][gain_index].max_gain)
+ return -EINVAL;
+
+#endif
+
+ /* aquire mutex */
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ error = ptr->set_gain_func(channel_index,
+ gain_index, gain_value, linear,
+ ptr_audio_codec_cnxt->dev);
+ if (0 == error)
+ ste_audio_io_core_api_store_data(channel_index ,
+ ptr->gain, gain_value);
+
+
+ /* release mutex */
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ return error;
+}
+/**
+ * @brief Get individual gain along the HW path of a specified channel
+ * @gctrl_trnsdr Pointer to the structure __audioio_gain_ctrl_trnsdr
+ * @return 0 on success otherwise negative error code
+ */
+
+
+int ste_audio_io_core_api_gain_query_transducer(
+ struct audioio_gain_ctrl_trnsdr_t *gctrl_trnsdr)
+{
+ struct transducer_context_t *ptr = NULL;
+ enum AUDIOIO_CH_INDEX channel_index;
+ u16 gain_index;
+ u32 linear;
+ int left_volume, right_volume;
+ int max_gain, min_gain;
+ int ch_array_index;
+
+ if ((gctrl_trnsdr->channel_type < FIRST_CH)
+ || (gctrl_trnsdr->channel_type > LAST_CH))
+ return -EINVAL;
+
+ if (gctrl_trnsdr->gain_index >= MAX_NO_GAINS)
+ return -EINVAL;
+
+ ptr = ptr_audio_codec_cnxt->transducer[gctrl_trnsdr->channel_type];
+
+ channel_index = gctrl_trnsdr->channel_index;
+ gain_index = gctrl_trnsdr->gain_index;
+ linear = gctrl_trnsdr->linear;
+
+ ptr->get_gain_func(&left_volume, &right_volume, gain_index,
+ ptr_audio_codec_cnxt->dev);
+
+ ch_array_index = convert_channel_index_to_array_index(channel_index);
+ max_gain = gain_descriptor[gctrl_trnsdr->channel_type]\
+ [ch_array_index][gain_index].max_gain;
+ min_gain = gain_descriptor[gctrl_trnsdr->channel_type]\
+ [ch_array_index][gain_index].min_gain;
+
+ switch (channel_index) {
+ case e_CHANNEL_1:
+ gctrl_trnsdr->gain_value = linear ? \
+ min_gain+left_volume*(max_gain-min_gain)/100 : left_volume;
+ break;
+ case e_CHANNEL_2:
+ gctrl_trnsdr->gain_value = linear ? \
+ min_gain+right_volume*(max_gain-min_gain)/100 : right_volume;
+ break;
+ case e_CHANNEL_3:
+ break;
+ case e_CHANNEL_4:
+ break;
+ case e_CHANNEL_ALL:
+ if (left_volume == right_volume) {
+ if (linear)
+ gctrl_trnsdr->gain_value =
+ min_gain+right_volume*(max_gain-min_gain)/100;
+ else
+ gctrl_trnsdr->gain_value = right_volume;
+ }
+ }
+
+ return 0;
+}
+
+
+int ste_audio_io_core_api_fsbitclk_control(
+ struct audioio_fsbitclk_ctrl_t *fsbitclk_ctrl)
+{
+ int error = 0;
+
+ if (AUDIOIO_COMMON_ON == fsbitclk_ctrl->ctrl_switch)
+ error = HW_ACODEC_MODIFY_WRITE(IF0_IF1_MASTER_CONF_REG,
+ EN_FSYNC_BITCLK, 0);
+ else
+ error = HW_ACODEC_MODIFY_WRITE(IF0_IF1_MASTER_CONF_REG, 0,
+ EN_FSYNC_BITCLK);
+
+ return error;
+}
+int ste_audio_io_core_api_pseudoburst_control(
+ struct audioio_pseudoburst_ctrl_t *pseudoburst_ctrl)
+{
+ int error = 0;
+
+ return error;
+}
+int ste_audio_io_core_debug(int x)
+{
+ debug_audioio(x);
+
+return 0;
+}
+
+/**
+ * ste_audioio_vibrator_alloc()
+ * @client: Client id which allocates vibrator
+ * @mask: Mask against which vibrator usage is checked
+ *
+ * This function allocates vibrator.
+ * Mask is added here as audioio driver controls left and right vibrator
+ * separately (can work independently). In case when audioio has allocated
+ * one of its channels (left or right) it should be still able to allocate
+ * the other channel.
+ *
+ * Returns:
+ * 0 - Success
+ * -EBUSY - other client already registered
+ **/
+int ste_audioio_vibrator_alloc(int client, int mask)
+{
+ int error = 0;
+
+ /* Check if other client is already using vibrator */
+ if (ptr_audio_codec_cnxt->vibra_client & ~mask)
+ error = -EBUSY;
+ else
+ ptr_audio_codec_cnxt->vibra_client |= client;
+
+ return error;
+}
+
+/**
+ * ste_audioio_vibrator_release()
+ * @client: Client id which releases vibrator
+ *
+ * This function releases vibrator
+ **/
+void ste_audioio_vibrator_release(int client)
+{
+ ptr_audio_codec_cnxt->vibra_client &= ~client;
+}
+
+/**
+ * ste_audioio_vibrator_pwm_control()
+ * @client: Client id which will use vibrator
+ * @left_speed: Left vibrator speed
+ * @right_speed: Right vibrator speed
+ *
+ * This function controls vibrator using PWM source
+ *
+ * Returns:
+ * 0 - success
+ * -EBUSY - Vibrator already used
+ **/
+int ste_audioio_vibrator_pwm_control(
+ int client,
+ struct ste_vibra_speed left_speed,
+ struct ste_vibra_speed right_speed)
+{
+ int error = 0;
+
+ mutex_lock(&ptr_audio_codec_cnxt->audio_io_mutex);
+
+ /* Try to allocate vibrator for given client */
+ error = ste_audioio_vibrator_alloc(client, client);
+
+ mutex_unlock(&ptr_audio_codec_cnxt->audio_io_mutex);
+
+ if (error)
+ return error;
+
+ /* Duty cycle supported by vibrator's PWM is 0-100 */
+ if (left_speed.positive > STE_AUDIOIO_VIBRATOR_MAX_SPEED)
+ left_speed.positive = STE_AUDIOIO_VIBRATOR_MAX_SPEED;
+
+ if (right_speed.positive > STE_AUDIOIO_VIBRATOR_MAX_SPEED)
+ right_speed.positive = STE_AUDIOIO_VIBRATOR_MAX_SPEED;
+
+ if (left_speed.negative > STE_AUDIOIO_VIBRATOR_MAX_SPEED)
+ left_speed.negative = STE_AUDIOIO_VIBRATOR_MAX_SPEED;
+
+ if (right_speed.negative > STE_AUDIOIO_VIBRATOR_MAX_SPEED)
+ right_speed.negative = STE_AUDIOIO_VIBRATOR_MAX_SPEED;
+
+ if (left_speed.negative || right_speed.negative ||
+ left_speed.positive || right_speed.positive) {
+ /* Power up audio block for vibrator */
+ error = ste_audio_io_core_api_powerup_audiocodec(
+ STE_AUDIOIO_POWER_VIBRA);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Audio power up failed %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG,
+ (EN_VIBL_MASK|EN_VIBR_MASK), 0);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Powerup Vibrator Class-D driver %d",
+ error);
+ return error;
+ }
+
+ error = HW_REG_WRITE(VIB_DRIVER_CONF_REG, 0xff);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Enable Vibrator PWM generator %d",
+ error);
+ return error;
+ }
+ }
+
+ error = HW_REG_WRITE(PWM_VIBNL_CONF_REG, left_speed.negative);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Write Left Vibrator negative PWM %d", error);
+ goto err_cleanup;
+ }
+
+ error = HW_REG_WRITE(PWM_VIBPL_CONF_REG, left_speed.positive);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Write Left Vibrator positive PWM %d", error);
+ goto err_cleanup;
+ }
+
+ error = HW_REG_WRITE(PWM_VIBNR_CONF_REG, right_speed.negative);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Write Right Vibrator negative PWM %d", error);
+ goto err_cleanup;
+ }
+
+ error = HW_REG_WRITE(PWM_VIBPR_CONF_REG, right_speed.positive);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Write Right Vibrator positive PWM %d", error);
+ goto err_cleanup;
+ }
+
+ if (!left_speed.negative && !right_speed.negative &&
+ !left_speed.positive && !right_speed.positive) {
+ error = HW_REG_WRITE(VIB_DRIVER_CONF_REG, 0);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Disable PWM Vibrator generator %d",
+ error);
+ goto err_cleanup;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG,
+ 0, (EN_VIBL_MASK|EN_VIBR_MASK));
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Power down Vibrator Class-D driver %d",
+ error);
+ goto err_cleanup;
+ }
+
+ /* Power down audio block */
+ error = ste_audio_io_core_api_powerdown_audiocodec(
+ STE_AUDIOIO_POWER_VIBRA);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Audio power down failed %d", error);
+ goto err_cleanup;
+ }
+ }
+
+err_cleanup:
+ /* Release client */
+ if (!left_speed.negative && !right_speed.negative &&
+ !left_speed.positive && !right_speed.positive) {
+ mutex_lock(&ptr_audio_codec_cnxt->audio_io_mutex);
+ ste_audioio_vibrator_release(client);
+ mutex_unlock(&ptr_audio_codec_cnxt->audio_io_mutex);
+ }
+ return error;
+}
+EXPORT_SYMBOL(ste_audioio_vibrator_pwm_control);
+
+/**
+ * @brief This function sets FIR coefficients
+ * @fir_coeffs: pointer to structure audioio_fir_coefficients_t
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_core_api_fir_coeffs_control(struct audioio_fir_coefficients_t
+ *fir_coeffs)
+{
+ unsigned char coefficient;
+ int i, error;
+
+ if (fir_coeffs->start_addr >= STE_AUDIOIO_MAX_COEFFICIENTS)
+ return -EINVAL;
+
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+
+ error = HW_REG_WRITE(SIDETONE_FIR_ADDR_REG, fir_coeffs->start_addr);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "FIR start address write failed %d", error);
+ goto err_cleanup;
+ }
+
+ for (i = fir_coeffs->start_addr;
+ i < STE_AUDIOIO_MAX_COEFFICIENTS; i++) {
+
+ coefficient = (fir_coeffs->coefficients[i]>>8) & 0xff;
+ error = HW_REG_WRITE(SIDETONE_FIR_COEFF_MSB_REG, coefficient);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "FIR coefficient [%d] msb write failed %d", i, error);
+ goto err_cleanup;
+ }
+
+ coefficient = fir_coeffs->coefficients[i] & 0xff;
+ error = HW_REG_WRITE(SIDETONE_FIR_COEFF_LSB_REG, coefficient);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "FIR coefficient [%d] lsb write failed %d", i, error);
+ goto err_cleanup;
+ }
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(SIDETONE_FIR_ADDR_REG,
+ APPLY_FIR_COEFFS_MASK, 0);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "FIR coefficients activation failed %d", error);
+ goto err_cleanup;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(FILTERS_CONTROL_REG,
+ FIR_FILTERCONTROL, 0);
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "ST FIR Filters enable failed %d", error);
+ goto err_cleanup;
+ }
+
+err_cleanup:
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+ return error;
+}
+
+/**
+ * @brief This function sets and enable clock
+ * @clk_type: pointer to structure audioio_clk_select_t
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_core_clk_select_control(struct audioio_clk_select_t
+ *clk_type)
+{
+ int error = 0;
+ mutex_lock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+ if (ptr_audio_codec_cnxt->clk_type != clk_type->required_clk) {
+ /* disable running clk*/
+ ste_audio_io_disable_audio_clock();
+ /* assign required clk*/
+ ptr_audio_codec_cnxt->clk_type = clk_type->required_clk;
+ /* enable required clk*/
+ error = ste_audio_io_enable_audio_clock();
+ if (error) {
+ dev_err(ptr_audio_codec_cnxt->dev,
+ "Clock enabled failed = %d", error);
+ goto err_cleanup;
+ }
+ }
+err_cleanup:
+ mutex_unlock(&(ptr_audio_codec_cnxt->audio_io_mutex));
+ return error;
+}
+
diff --git a/drivers/misc/audio_io_dev/ste_audio_io_core.h b/drivers/misc/audio_io_dev/ste_audio_io_core.h
new file mode 100644
index 00000000000..44849f1d5e8
--- /dev/null
+++ b/drivers/misc/audio_io_dev/ste_audio_io_core.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#ifndef _AUDIOIO_CORE_H_
+#define _AUDIOIO_CORE_H_
+
+#include <mach/ste_audio_io_ioctl.h>
+#include "ste_audio_io_func.h"
+#include "ste_audio_io_hwctrl_common.h"
+
+#define MAX_NO_CHANNELS 4
+
+#define STE_AUDIOIO_POWER_AUDIO 1
+#define STE_AUDIOIO_POWER_VIBRA 2
+
+struct transducer_context_t {
+ /* public variables */
+ int gain[MAX_NO_CHANNELS];
+ int is_muted[MAX_NO_CHANNELS];
+ int is_power_up[MAX_NO_CHANNELS];
+ /* public funcs */
+ int (*pwr_up_func)(enum AUDIOIO_CH_INDEX, struct device *);
+ int (*pwr_down_func)(enum AUDIOIO_CH_INDEX, struct device *);
+ int (*pwr_state_func)(struct device *);
+ int (*set_gain_func)(enum AUDIOIO_CH_INDEX, u16, int, u32,
+ struct device *);
+ int (*get_gain_func)(int *, int *, u16, struct device *);
+ int (*mute_func)(enum AUDIOIO_CH_INDEX, struct device *);
+ int (*unmute_func)(enum AUDIOIO_CH_INDEX, int *, struct device *);
+ int (*mute_state_func)(struct device *);
+ int (*enable_fade_func)(struct device *);
+ int (*disable_fade_func)(struct device *);
+ int (*switch_to_burst_func)(int, struct device *);
+ int (*switch_to_normal_func)(struct device *);
+ int (*enable_loop)(enum AUDIOIO_CH_INDEX, enum AUDIOIO_HAL_HW_LOOPS,
+ int, struct device *, void *);
+ int (*disable_loop)(enum AUDIOIO_CH_INDEX, enum AUDIOIO_HAL_HW_LOOPS,
+ struct device *, void *);
+};
+
+struct audiocodec_context_t {
+ int audio_codec_powerup;
+ int is_audio_clk_enabled;
+ enum AUDIOIO_CLK_TYPE clk_type;
+ int power_client;
+ int vibra_client;
+ struct mutex audio_io_mutex;
+ struct mutex vibrator_mutex;
+ struct transducer_context_t *transducer[MAX_NO_TRANSDUCERS];
+ struct device *dev;
+ int (*gpio_altf_init) (void);
+ int (*gpio_altf_exit) (void);
+};
+
+
+int ste_audio_io_core_api_access_read(struct audioio_data_t *dev_data);
+
+int ste_audio_io_core_api_access_write(struct audioio_data_t *dev_data);
+
+int ste_audio_io_core_api_power_control_transducer(
+ struct audioio_pwr_ctrl_t *pwr_ctrl);
+
+int ste_audio_io_core_api_power_status_transducer(
+ struct audioio_pwr_ctrl_t *pwr_ctrl);
+
+int ste_audio_io_core_api_loop_control(struct audioio_loop_ctrl_t *loop_ctrl);
+
+int ste_audio_io_core_api_loop_status(struct audioio_loop_ctrl_t *loop_ctrl);
+
+int ste_audio_io_core_api_get_transducer_gain_capability(
+ struct audioio_get_gain_t *get_gain);
+
+int ste_audio_io_core_api_gain_capabilities_loop(
+ struct audioio_gain_loop_t *gain_loop);
+
+int ste_audio_io_core_api_supported_loops(
+ struct audioio_support_loop_t *support_loop);
+
+int ste_audio_io_core_api_gain_descriptor_transducer(
+ struct audioio_gain_desc_trnsdr_t *gdesc_trnsdr);
+
+int ste_audio_io_core_api_gain_control_transducer(
+ struct audioio_gain_ctrl_trnsdr_t *gctrl_trnsdr);
+
+int ste_audio_io_core_api_gain_query_transducer(
+ struct audioio_gain_ctrl_trnsdr_t *gctrl_trnsdr);
+
+int ste_audio_io_core_api_mute_control_transducer(
+ struct audioio_mute_trnsdr_t *mute_trnsdr);
+
+int ste_audio_io_core_api_mute_status_transducer(
+ struct audioio_mute_trnsdr_t *mute_trnsdr);
+
+int ste_audio_io_core_api_fading_control(struct audioio_fade_ctrl_t *fade_ctrl);
+
+int ste_audio_io_core_api_burstmode_control(
+ struct audioio_burst_ctrl_t *burst_ctrl);
+
+int ste_audio_io_core_api_powerup_audiocodec(int power_client);
+
+int ste_audio_io_core_api_powerdown_audiocodec(int power_client);
+
+int ste_audio_io_core_api_init_data(struct platform_device *pdev);
+
+bool ste_audio_io_core_is_ready_for_suspend(void);
+void ste_audio_io_core_api_free_data(void);
+
+int ste_audio_io_core_api_fsbitclk_control(
+ struct audioio_fsbitclk_ctrl_t *fsbitclk_ctrl);
+int ste_audio_io_core_api_pseudoburst_control(
+ struct audioio_pseudoburst_ctrl_t *pseudoburst_ctrl);
+
+void ste_audio_io_core_api_store_data(enum AUDIOIO_CH_INDEX channel_index,
+ int *ptr, int value);
+
+int ste_audioio_vibrator_alloc(int client, int mask);
+
+void ste_audioio_vibrator_release(int client);
+
+enum AUDIOIO_COMMON_SWITCH ste_audio_io_core_api_get_status(
+ enum AUDIOIO_CH_INDEX channel_index, int *ptr);
+
+int ste_audio_io_core_api_acodec_power_control(struct audioio_acodec_pwr_ctrl_t
+ *audio_acodec_pwr_ctrl);
+
+int ste_audio_io_core_api_fir_coeffs_control(struct audioio_fir_coefficients_t
+ *fir_coeffs);
+
+int ste_audio_io_core_clk_select_control(struct audioio_clk_select_t
+ *clk_type);
+
+int ste_audio_io_core_debug(int x);
+
+#endif /* _AUDIOIO_CORE_H_ */
+
diff --git a/drivers/misc/audio_io_dev/ste_audio_io_dev.c b/drivers/misc/audio_io_dev/ste_audio_io_dev.c
new file mode 100644
index 00000000000..edea31e4315
--- /dev/null
+++ b/drivers/misc/audio_io_dev/ste_audio_io_dev.c
@@ -0,0 +1,759 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include "ste_audio_io_dev.h"
+
+#define STR_DEBUG_ON "debug on"
+#define AUDIOIO_DEVNAME "ab8500-codec"
+
+static int ste_audio_io_open(struct inode *inode, struct file *filp);
+static int ste_audio_io_release(struct inode *inode, struct file *filp);
+static long ste_audio_io_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+static int ste_audio_io_cmd_parser(unsigned int cmd, unsigned long arg);
+static ssize_t ste_audio_io_write(struct file *filp,
+ const char __user *buf, size_t count, loff_t *f_pos);
+
+
+/**
+ * @brief Check IOCTL type, command no and access direction
+ * @ inode value corresponding to the file descriptor
+ * @file value corresponding to the file descriptor
+ * @cmd IOCTL command code
+ * @arg Command argument
+ * @return 0 on success otherwise negative error code
+ */
+static long ste_audio_io_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int retval = 0;
+ int err = 0;
+
+ /* Check type and command number */
+ if (_IOC_TYPE(cmd) != AUDIOIO_IOC_MAGIC)
+ return -ENOTTY;
+
+ /* IOC_DIR is from the user perspective, while access_ok is
+ * from the kernel perspective; so they look reversed.
+ */
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ err = !access_ok(VERIFY_WRITE, (void __user *)arg,
+ _IOC_SIZE(cmd));
+ if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
+ err = !access_ok(VERIFY_READ, (void __user *)arg,
+ _IOC_SIZE(cmd));
+ if (err)
+ return -EFAULT;
+
+ retval = ste_audio_io_cmd_parser(cmd, arg);
+
+ return retval;
+}
+/**
+ * @brief IOCTL call to read the value from AB8500 device
+ * @cmd IOCTL command code
+ * @arg Command argument
+ * @return 0 on success otherwise negative error code
+ */
+
+static int process_read_register_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_data_t *audio_dev_data;
+
+ audio_dev_data = (struct audioio_data_t *)&cmd_data;
+
+ if (copy_from_user(audio_dev_data, (void __user *)arg,
+ sizeof(struct audioio_data_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_access_read(audio_dev_data);
+ if (0 != retval)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, audio_dev_data,
+ sizeof(struct audioio_data_t)))
+ return -EFAULT;
+ return 0;
+}
+/**
+ * @brief IOCTL call to write the given value to the AB8500 device
+ * @cmd IOCTL command code
+ * @arg Command argument
+ * @return 0 on success otherwise negative error code
+ */
+
+static int process_write_register_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_data_t *audio_dev_data;
+
+ audio_dev_data = (struct audioio_data_t *)&cmd_data;
+
+ if (copy_from_user(audio_dev_data, (void __user *)arg,
+ sizeof(struct audioio_data_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_access_write(audio_dev_data);
+
+ return retval;
+}
+/**
+ * @brief IOCTL call to control the power on/off of hardware components
+ * @cmd IOCTL command code
+ * @arg Command argument
+ * @return 0 on success otherwise negative error code
+ */
+
+static int process_pwr_ctrl_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_pwr_ctrl_t *audio_pwr_ctrl;
+
+ audio_pwr_ctrl = (struct audioio_pwr_ctrl_t *)&cmd_data;
+
+ if (copy_from_user(audio_pwr_ctrl, (void __user *)arg,
+ sizeof(struct audioio_pwr_ctrl_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_power_control_transducer(audio_pwr_ctrl);
+
+ return retval;
+}
+
+static int process_pwr_sts_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_pwr_ctrl_t *audio_pwr_sts;
+
+ audio_pwr_sts = (struct audioio_pwr_ctrl_t *)&cmd_data;
+
+ if (copy_from_user(audio_pwr_sts, (void __user *)arg,
+ sizeof(struct audioio_pwr_ctrl_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_power_status_transducer(audio_pwr_sts);
+ if (0 != retval)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, audio_pwr_sts,
+ sizeof(struct audioio_pwr_ctrl_t)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int process_lp_ctrl_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_loop_ctrl_t *audio_lp_ctrl;
+
+ audio_lp_ctrl = (struct audioio_loop_ctrl_t *)&cmd_data;
+
+ if (copy_from_user(audio_lp_ctrl, (void __user *)arg,
+ sizeof(struct audioio_loop_ctrl_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_loop_control(audio_lp_ctrl);
+
+ return retval;
+}
+
+static int process_lp_sts_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_loop_ctrl_t *audio_lp_sts;
+
+ audio_lp_sts = (struct audioio_loop_ctrl_t *)&cmd_data;
+
+
+ if (copy_from_user(audio_lp_sts, (void __user *)arg,
+ sizeof(struct audioio_loop_ctrl_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_loop_status(audio_lp_sts);
+ if (0 != retval)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, audio_lp_sts,
+ sizeof(struct audioio_loop_ctrl_t)))
+ return -EFAULT;
+ return 0;
+}
+
+static int process_get_trnsdr_gain_capability_cmd(unsigned int cmd,
+ unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_get_gain_t *audio_trnsdr_gain;
+
+ audio_trnsdr_gain = (struct audioio_get_gain_t *)&cmd_data;
+
+ if (copy_from_user(audio_trnsdr_gain, (void __user *)arg,
+ sizeof(struct audioio_get_gain_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_get_transducer_gain_capability(
+ audio_trnsdr_gain);
+ if (0 != retval)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, audio_trnsdr_gain,
+ sizeof(struct audioio_get_gain_t)))
+ return -EFAULT;
+ return 0;
+}
+
+static int process_gain_cap_loop_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_gain_loop_t *audio_gain_loop;
+
+ audio_gain_loop = (struct audioio_gain_loop_t *)&cmd_data;
+
+ if (copy_from_user(audio_gain_loop, (void __user *)arg,
+ sizeof(struct audioio_gain_loop_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_gain_capabilities_loop(audio_gain_loop);
+ if (0 != retval)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, audio_gain_loop,
+ sizeof(struct audioio_gain_loop_t)))
+ return -EFAULT;
+ return 0;
+}
+
+
+static int process_support_loop_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_support_loop_t *audio_spprt_loop;
+
+ audio_spprt_loop = (struct audioio_support_loop_t *)&cmd_data;
+
+ if (copy_from_user(audio_spprt_loop, (void __user *)arg,
+ sizeof(struct audioio_support_loop_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_supported_loops(audio_spprt_loop);
+ if (0 != retval)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, audio_spprt_loop,
+ sizeof(struct audioio_support_loop_t)))
+ return -EFAULT;
+ return 0;
+}
+
+
+static int process_gain_desc_trnsdr_cmd(unsigned int cmd, unsigned long arg)
+
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_gain_desc_trnsdr_t *audio_gain_desc;
+
+ audio_gain_desc = (struct audioio_gain_desc_trnsdr_t *)&cmd_data;
+
+ if (copy_from_user(audio_gain_desc, (void __user *)arg,
+ sizeof(struct audioio_gain_desc_trnsdr_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_gain_descriptor_transducer(
+ audio_gain_desc);
+ if (0 != retval)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, audio_gain_desc,
+ sizeof(struct audioio_gain_desc_trnsdr_t)))
+ return -EFAULT;
+ return 0;
+}
+
+
+static int process_gain_ctrl_trnsdr_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_gain_ctrl_trnsdr_t *audio_gain_ctrl;
+
+ audio_gain_ctrl = (struct audioio_gain_ctrl_trnsdr_t *)&cmd_data;
+
+ if (copy_from_user(audio_gain_ctrl, (void __user *)arg,
+ sizeof(struct audioio_gain_ctrl_trnsdr_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_gain_control_transducer(
+ audio_gain_ctrl);
+
+ return retval;
+}
+
+static int process_gain_query_trnsdr_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_gain_ctrl_trnsdr_t *audio_gain_query;
+
+ audio_gain_query = (struct audioio_gain_ctrl_trnsdr_t *)&cmd_data;
+
+ if (copy_from_user(audio_gain_query, (void __user *)arg,
+ sizeof(struct audioio_gain_ctrl_trnsdr_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_gain_query_transducer(audio_gain_query);
+ if (0 != retval)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, audio_gain_query,
+ sizeof(struct audioio_gain_ctrl_trnsdr_t)))
+ return -EFAULT;
+ return 0;
+}
+
+static int process_mute_ctrl_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_mute_trnsdr_t *audio_mute_ctrl;
+
+ audio_mute_ctrl = (struct audioio_mute_trnsdr_t *)&cmd_data;
+ if (copy_from_user(audio_mute_ctrl , (void __user *)arg,
+ sizeof(struct audioio_mute_trnsdr_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_mute_control_transducer(
+ audio_mute_ctrl);
+
+ return retval;
+}
+
+static int process_mute_sts_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_mute_trnsdr_t *audio_mute_sts;
+
+ audio_mute_sts = (struct audioio_mute_trnsdr_t *)&cmd_data;
+
+ if (copy_from_user(audio_mute_sts, (void __user *)arg,
+ sizeof(struct audioio_mute_trnsdr_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_mute_status_transducer(audio_mute_sts);
+ if (0 != retval)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, audio_mute_sts,
+ sizeof(struct audioio_mute_trnsdr_t)))
+ return -EFAULT;
+ return 0;
+}
+
+static int process_fade_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_fade_ctrl_t *audio_fade;
+ audio_fade = (struct audioio_fade_ctrl_t *)&cmd_data;
+
+ if (copy_from_user(audio_fade , (void __user *)arg,
+ sizeof(struct audioio_fade_ctrl_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_fading_control(audio_fade);
+
+ return retval;
+}
+
+static int process_burst_ctrl_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_burst_ctrl_t *audio_burst;
+
+ audio_burst = (struct audioio_burst_ctrl_t *)&cmd_data;
+ if (copy_from_user(audio_burst , (void __user *)arg,
+ sizeof(struct audioio_burst_ctrl_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_burstmode_control(audio_burst);
+
+ return retval;
+
+ return 0;
+}
+
+static int process_fsbitclk_ctrl_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_fsbitclk_ctrl_t *audio_fsbitclk;
+
+ audio_fsbitclk = (struct audioio_fsbitclk_ctrl_t *)&cmd_data;
+
+ if (copy_from_user(audio_fsbitclk , (void __user *)arg,
+ sizeof(struct audioio_fsbitclk_ctrl_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_fsbitclk_control(audio_fsbitclk);
+
+ return retval;
+
+ return 0;
+
+}
+
+static int process_pseudoburst_ctrl_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_pseudoburst_ctrl_t *audio_pseudoburst;
+
+ audio_pseudoburst = (struct audioio_pseudoburst_ctrl_t *)&cmd_data;
+
+ if (copy_from_user(audio_pseudoburst , (void __user *)arg,
+ sizeof(struct audioio_pseudoburst_ctrl_t)))
+ return -EFAULT;
+
+ retval = ste_audio_io_core_api_pseudoburst_control(audio_pseudoburst);
+
+ return retval;
+
+ return 0;
+
+}
+static int process_audiocodec_pwr_ctrl_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ union audioio_cmd_data_t cmd_data;
+ struct audioio_acodec_pwr_ctrl_t *audio_acodec_pwr_ctrl;
+ audio_acodec_pwr_ctrl = (struct audioio_acodec_pwr_ctrl_t *)&cmd_data;
+ if (copy_from_user(audio_acodec_pwr_ctrl, (void __user *)arg,
+ sizeof(struct audioio_acodec_pwr_ctrl_t)))
+ return -EFAULT;
+ retval = ste_audio_io_core_api_acodec_power_control(
+ audio_acodec_pwr_ctrl);
+ return retval;
+}
+
+static int process_fir_coeffs_ctrl_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval;
+ struct audioio_fir_coefficients_t *cmd_data;
+ cmd_data = kmalloc(sizeof(struct audioio_fir_coefficients_t),
+ GFP_KERNEL);
+ if (!cmd_data)
+ return -ENOMEM;
+ if (copy_from_user(cmd_data, (void __user *)arg,
+ sizeof(struct audioio_fir_coefficients_t))) {
+ kfree(cmd_data);
+ return -EFAULT;
+ }
+ retval = ste_audio_io_core_api_fir_coeffs_control(cmd_data);
+ kfree(cmd_data);
+ return retval;
+}
+
+static int process_clk_select_cmd(unsigned int cmd, unsigned long arg)
+{
+ int retval;
+ struct audioio_clk_select_t *cmd_data;
+ cmd_data = kmalloc(sizeof(struct audioio_clk_select_t),
+ GFP_KERNEL);
+ if (!cmd_data)
+ return -ENOMEM;
+ if (copy_from_user(cmd_data, (void __user *)arg,
+ sizeof(struct audioio_clk_select_t))) {
+ kfree(cmd_data);
+ return -EFAULT;
+ }
+ retval = ste_audio_io_core_clk_select_control(cmd_data);
+ kfree(cmd_data);
+ return retval;
+}
+
+static int ste_audio_io_cmd_parser(unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+
+ switch (cmd) {
+ case AUDIOIO_READ_REGISTER:
+ retval = process_read_register_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_WRITE_REGISTER:
+ retval = process_write_register_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_PWR_CTRL_TRNSDR:
+ retval = process_pwr_ctrl_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_PWR_STS_TRNSDR:
+ retval = process_pwr_sts_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_LOOP_CTRL:
+ retval = process_lp_ctrl_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_LOOP_STS:
+ retval = process_lp_sts_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_GET_TRNSDR_GAIN_CAPABILITY:
+ retval = process_get_trnsdr_gain_capability_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_GAIN_CAP_LOOP:
+ retval = process_gain_cap_loop_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_SUPPORT_LOOP:
+ retval = process_support_loop_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_GAIN_DESC_TRNSDR:
+ retval = process_gain_desc_trnsdr_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_GAIN_CTRL_TRNSDR:
+ retval = process_gain_ctrl_trnsdr_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_GAIN_QUERY_TRNSDR:
+ retval = process_gain_query_trnsdr_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_MUTE_CTRL_TRNSDR:
+ retval = process_mute_ctrl_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_MUTE_STS_TRNSDR:
+ retval = process_mute_sts_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_FADE_CTRL:
+ retval = process_fade_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_BURST_CTRL:
+ retval = process_burst_ctrl_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_FSBITCLK_CTRL:
+ retval = process_fsbitclk_ctrl_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_PSEUDOBURST_CTRL:
+ retval = process_pseudoburst_ctrl_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_AUDIOCODEC_PWR_CTRL:
+ retval = process_audiocodec_pwr_ctrl_cmd(cmd, arg);
+ break;
+
+ case AUDIOIO_FIR_COEFFS_CTRL:
+ retval = process_fir_coeffs_ctrl_cmd(cmd, arg);
+ break;
+ case AUDIOIO_CLK_SELECT_CTRL:
+ retval = process_clk_select_cmd(cmd, arg);
+ break;
+ }
+ return retval;
+}
+
+static int ste_audio_io_open(struct inode *inode, struct file *filp)
+{
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+ return 0;
+}
+
+static int ste_audio_io_release(struct inode *inode, struct file *filp)
+{
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+static ssize_t ste_audio_io_write(struct file *filp,
+ const char __user *buf, size_t count, loff_t *f_pos)
+{
+ char *x = kmalloc(count, GFP_KERNEL);
+ int debug_flag = 0;
+
+ if (copy_from_user(x, buf, count))
+ return -EFAULT;
+
+ if (count >= strlen(STR_DEBUG_ON)) {
+
+ if (!strncmp(STR_DEBUG_ON, x, strlen(STR_DEBUG_ON)))
+ debug_flag = 1;
+ else
+ debug_flag = 0;
+ }
+
+ ste_audio_io_core_debug(debug_flag);
+
+ kfree(x);
+
+ return count;
+}
+
+static const struct file_operations ste_audio_io_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ste_audio_io_ioctl,
+ .open = ste_audio_io_open,
+ .release = ste_audio_io_release,
+ .write = ste_audio_io_write,
+};
+
+/**
+ * audio_io_misc_dev - Misc device config for audio_io
+ */
+static struct miscdevice audio_io_misc_dev = {
+ MISC_DYNAMIC_MINOR,
+ "audioio",
+ &ste_audio_io_fops
+};
+
+/**
+ * ste_audio_io_probe() - probe the device
+ * @pdev: pointer to the platform device structure
+ *
+ * This funtion is called after the driver is registered to platform
+ * device framework. It does allocate the memory for the internal
+ * data structure and intialized core APIs.
+ */
+static int ste_audio_io_drv_probe(struct platform_device *pdev)
+{
+ int error;
+
+ ste_audio_io_device = pdev;
+
+ dev_dbg(&ste_audio_io_device->dev, "ste_audio_io device probe\n");
+
+ error = misc_register(&audio_io_misc_dev);
+ if (error) {
+ printk(KERN_WARNING "%s: registering misc device failed\n",
+ __func__);
+ return error;
+ }
+
+ error = ste_audio_io_core_api_init_data(ste_audio_io_device);
+ if (error < 0) {
+ dev_err(&ste_audio_io_device->dev,
+ "ste_audioio_core_api_init_data failed err = %d",
+ error);
+ goto ste_audio_io_misc_deregister;
+ }
+ return 0;
+
+ste_audio_io_misc_deregister:
+ misc_deregister(&audio_io_misc_dev);
+ return error;
+}
+
+/**
+ * ste_audio_io_remove() - Removes the device
+ * @pdev: pointer to the platform_device structure
+ *
+ * This function is called when this mnodule is removed using rmmod
+ */
+static int ste_audio_io_drv_remove(struct platform_device *pdev)
+{
+ ste_audio_io_core_api_free_data();
+ misc_deregister(&audio_io_misc_dev);
+ return 0;
+}
+
+/**
+ * ste_audio_io_drv_suspend - suspend audio_io
+ * @pdev: platform data
+ * @state: power down level
+ */
+static int ste_audio_io_drv_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ if (ste_audio_io_core_is_ready_for_suspend())
+ return 0;
+ else
+ return -EINVAL;
+}
+
+/**
+ * ste_audio_io_drv_resume - put back audio_io in the normal state
+ * @pdev: platform data
+ */
+static int ste_audio_io_drv_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+/**
+ * struct audio_io_driver: audio_io platform structure
+ * @probe: The probe funtion to be called
+ * @remove: The remove funtion to be called
+ * @resume: The resume function to be called
+ * @suspend: The suspend function to be called
+ * @driver: The driver data
+ */
+static struct platform_driver ste_audio_io_driver = {
+ .probe = ste_audio_io_drv_probe,
+ .remove = ste_audio_io_drv_remove,
+ .driver = {
+ .name = AUDIOIO_DEVNAME,
+ .owner = THIS_MODULE,
+ },
+ .suspend = ste_audio_io_drv_suspend,
+ .resume = ste_audio_io_drv_resume,
+};
+
+/** Pointer to platform device needed to access abx500 core functions */
+struct platform_device *ste_audio_io_device;
+
+static int __init ste_audio_io_init(void)
+{
+ return platform_driver_register(&ste_audio_io_driver);
+}
+module_init(ste_audio_io_init);
+
+static void __exit ste_audio_io_exit(void)
+{
+ platform_driver_unregister(&ste_audio_io_driver);
+}
+module_exit(ste_audio_io_exit);
+
+MODULE_AUTHOR("Deepak KARDA <deepak.karda@stericsson.com>");
+MODULE_DESCRIPTION("STE_AUDIO_IO");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/audio_io_dev/ste_audio_io_dev.h b/drivers/misc/audio_io_dev/ste_audio_io_dev.h
new file mode 100644
index 00000000000..bcb9dce3ad2
--- /dev/null
+++ b/drivers/misc/audio_io_dev/ste_audio_io_dev.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#ifndef _AUDIOIO_DEV_H_
+#define _AUDIOIO_DEV_H_
+
+#include <mach/ste_audio_io_ioctl.h>
+#include "ste_audio_io_core.h"
+
+union audioio_cmd_data_t {
+ struct audioio_burst_ctrl_t audioio_burst_ctrl;
+ struct audioio_fade_ctrl_t audioio_fade_ctrl;
+ struct audioio_mute_trnsdr_t audioio_mute_trnsdr;
+ struct audioio_gain_ctrl_trnsdr_t audioio_gain_ctrl_trnsdr;
+ struct audioio_gain_desc_trnsdr_t audioio_gain_desc_trnsdr;
+ struct audioio_support_loop_t audioio_support_loop;
+ struct audioio_gain_loop_t audioio_gain_loop;
+ struct audioio_get_gain_t audioio_get_gain;
+ struct audioio_loop_ctrl_t audioio_loop_ctrl;
+ struct audioio_pwr_ctrl_t audioio_pwr_ctrl;
+ struct audioio_data_t audioio_data;
+ struct audioio_fsbitclk_ctrl_t audioio_fsbitclk_ctrl;
+ struct audioio_acodec_pwr_ctrl_t audioio_acodec_pwr_ctrl;
+ struct audioio_pseudoburst_ctrl_t audioio_pseudoburst_ctrl;
+};
+
+
+#endif /* _AUDIOIO_DEV_H_ */
+
diff --git a/drivers/misc/audio_io_dev/ste_audio_io_func.c b/drivers/misc/audio_io_dev/ste_audio_io_func.c
new file mode 100644
index 00000000000..73c9b269685
--- /dev/null
+++ b/drivers/misc/audio_io_dev/ste_audio_io_func.c
@@ -0,0 +1,4371 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <mach/ste_audio_io_vibrator.h>
+#include <mach/ste_audio_io.h>
+#include "ste_audio_io_func.h"
+#include "ste_audio_io_core.h"
+#include "ste_audio_io_ab8500_reg_defs.h"
+#include "ste_audio_io_hwctrl_common.h"
+
+static struct clk *clk_ptr_msp0;
+static int bluetooth_power_up_count;
+static int acodec_reg_dump;
+
+#define NCP_TIMEOUT 200 /* 200 ms */
+/*
+ * TODO: Use proper register defines instead of home-made generic ones.
+ */
+#define SHIFT_QUARTET0 0
+#define SHIFT_QUARTET1 4
+#define MASK_QUARTET (0xFUL)
+#define MASK_QUARTET1 (MASK_QUARTET << SHIFT_QUARTET1)
+#define MASK_QUARTET0 (MASK_QUARTET << SHIFT_QUARTET0)
+
+/**
+ * @brief Modify the specified register
+ * @reg Register
+ * @mask_set Bit to be set
+ * @mask_clear Bit to be cleared
+ * @return 0 on success otherwise negative error code
+ */
+
+unsigned int ab8500_acodec_modify_write(unsigned int reg, u8 mask_set,
+ u8 mask_clear)
+{
+ u8 value8, retval = 0;
+ value8 = HW_REG_READ(reg);
+ /* clear the specified bit */
+ value8 &= ~mask_clear;
+ /* set the asked bit */
+ value8 |= mask_set;
+ retval = HW_REG_WRITE(reg, value8);
+ return retval;
+}
+
+/**
+ * @brief Power up headset on a specific channel
+ * @channel_index Channel-index of headset
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_power_up_headset(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_DA = 0;
+ unsigned long end_time;
+
+ /* Check if HS PowerUp request is mono or Stereo channel */
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "HS should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ ste_audio_io_mute_headset(channel_index, dev);
+
+ error = HW_ACODEC_MODIFY_WRITE(NCP_ENABLE_HS_AUTOSTART_REG,
+ HS_AUTO_EN, 0);
+ if (0 != error) {
+ dev_err(dev, "NCP fully controlled with EnCpHs bit %d", error);
+ return error;
+ }
+ error = HW_ACODEC_MODIFY_WRITE(NCP_ENABLE_HS_AUTOSTART_REG,
+ (EN_NEG_CP|HS_AUTO_EN), 0);
+ if (0 != error) {
+ dev_err(dev, "Enable Negative Charge Pump %d", error);
+ return error;
+ }
+
+ /* Wait for negative charge pump to start */
+ end_time = jiffies + msecs_to_jiffies(NCP_TIMEOUT);
+ while (!(HW_REG_READ(IRQ_STATUS_MSB_REG) & NCP_READY_MASK)
+ && time_after_eq(end_time, jiffies)) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+
+ if (!(HW_REG_READ(IRQ_STATUS_MSB_REG) & NCP_READY_MASK)) {
+ error = -EFAULT;
+ dev_err(dev, "Negative Charge Pump start error % d", error);
+ return error;
+ }
+
+ /* Enable DA1 for HSL */
+ if (channel_index & e_CHANNEL_1) {
+
+ /* Power Up HSL driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG,
+ EN_HSL_MASK, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up HSL Driver %d", error);
+ return error;
+ }
+
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+
+ if (EN_DA1 & initialVal_DA)
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA1_REG,
+ SLOT08_FOR_DA_PATH, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to DA1 from Slot 08 %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1,
+ DA1_TO_HSL, 0);
+ if (0 != error) {
+ dev_err(dev,
+ "DA_IN1 path mixed with sidetone FIR %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ EN_DA1, 0);
+ if (0 != error) {
+ dev_err(dev, "Power up HSL %d ", error);
+ return error;
+ }
+
+ /* Power Up HSL DAC driver */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG,
+ POWER_UP_HSL_DAC, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up HSL DAC driver %d", error);
+ return error;
+ }
+
+ /* Power up HSL DAC and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG,
+ EN_HSL_MASK, 0);
+ if (0 != error) {
+ dev_err(dev,
+ "Power up HSL DAC and digital path %d",
+ error);
+ return error;
+ }
+
+ /*
+ * Disable short detection. Pull Down output to ground,
+ * Use local oscillator, Gain change without zero cross control
+ */
+ error = HW_ACODEC_MODIFY_WRITE(SHORT_CIRCUIT_DISABLE_REG,
+ HS_SHORT_DIS|HS_PULL_DOWN_EN|HS_OSC_EN|HS_ZCD_DIS, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable short detection."
+ "Pull Down output to ground,Use local oscillator,Gain"
+ "change without zero cross control %d", error);
+ return error;
+ }
+ }
+
+ /* Enable DA2 for HSR */
+ if (channel_index & e_CHANNEL_2) {
+
+ /* Power Up HSR driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG,
+ EN_HSR_MASK, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up HSR Driver %d", error);
+ return error;
+ }
+
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ if (EN_DA2 & initialVal_DA)
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA2_REG,
+ SLOT09_FOR_DA_PATH, 0);
+ if (0 != error) {
+ dev_err(dev,
+ "Data sent to DA2 from Slot 09 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, DA2_TO_HSR,
+ 0);
+ if (0 != error) {
+ dev_err(dev,
+ "DA_IN2 path mixed with sidetone FIR %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ EN_DA2, 0);
+ if (0 != error) {
+ dev_err(dev, "Power up HSR %d ", error);
+ return error;
+ }
+
+ /* Power Up HSR DAC driver */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG,
+ POWER_UP_HSR_DAC, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up HSR DAC driver %d", error);
+ return error;
+ }
+
+ /* Power up HSR DAC and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG,
+ EN_HSR_MASK, 0);
+ if (0 != error) {
+ dev_err(dev,
+ "Power up HSR DAC and digital path %d",
+ error);
+ return error;
+ }
+
+ /*
+ * TEST START .havent cleared the bits in power down.Disable short
+ * detection. Pull Down output to ground, Use local oscillator,
+ * Gain change without zero cross control
+ */
+
+ error = HW_ACODEC_MODIFY_WRITE(SHORT_CIRCUIT_DISABLE_REG,
+ HS_SHORT_DIS|HS_PULL_DOWN_EN|HS_OSC_EN|HS_ZCD_DIS, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable short detection."
+ "Pull Down output to ground, Use local oscillator,"
+ "Gain change without zero cross control %d", error);
+ return error;
+ }
+ /* TEST END */
+ }
+ ste_audio_io_unmute_headset(channel_index, 0, dev);
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+
+/**
+ * @brief Power down headset on a specific channel
+ * @channel_index Channel-index of headset
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_power_down_headset(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_DA = 0;
+ unsigned long end_time;
+
+ /* Check if HS Power Down request is mono or Stereo channel */
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "HS should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ /* Disable Negative Charge Pump */
+ error = HW_ACODEC_MODIFY_WRITE(NCP_ENABLE_HS_AUTOSTART_REG,
+ (EN_NEG_CP|HS_AUTO_EN), 0);
+ if (0 != error) {
+ dev_err(dev, "NCP not fully controlled with EnCpHs bit %d",
+ error);
+ return error;
+ }
+ error = HW_ACODEC_MODIFY_WRITE(NCP_ENABLE_HS_AUTOSTART_REG, 0,
+ EN_NEG_CP);
+ if (0 != error) {
+ dev_err(dev, "Disable Negative Charge Pump %d", error);
+ return error;
+ }
+
+ /* Wait for negative charge pump to stop */
+ end_time = jiffies + msecs_to_jiffies(NCP_TIMEOUT);
+ while ((HW_REG_READ(IRQ_STATUS_MSB_REG) & NCP_READY_MASK)
+ && time_after_eq(end_time, jiffies)) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+
+ if (HW_REG_READ(IRQ_STATUS_MSB_REG) & NCP_READY_MASK) {
+ error = -EFAULT;
+ dev_err(dev, "Negative Charge Pump stop error % d", error);
+ return error;
+ }
+
+ if (channel_index & e_CHANNEL_1) {
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ if (!(initialVal_DA & EN_DA1))
+ return 0;
+
+ /* Power Down HSL driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, 0,
+ EN_HSL_MASK);
+ if (0 != error) {
+ dev_err(dev, "Power down HSL Driver %d", error);
+ return error;
+ }
+
+ /* Power Down HSL DAC driver */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0,
+ POWER_UP_HSL_DAC);
+ if (0 != error) {
+ dev_err(dev, "Power Up HSL DAC Driver %d", error);
+ return error;
+ }
+
+ /* Power Down HSL DAC and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0,
+ EN_HSL_MASK);
+ if (0 != error) {
+ dev_err(dev,
+ "Power down HSL DAC and digital path %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ 0, EN_DA1);
+ if (0 != error) {
+ dev_err(dev, "Disable DA1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1,
+ 0, DA1_TO_HSL);
+ if (0 != error) {
+ dev_err(dev,
+ "Clear DA_IN1 path mixed with sidetone FIR %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA1_REG, 0,
+ SLOT08_FOR_DA_PATH);
+ if (0 != error) {
+ dev_err(dev,
+ "Data sent to DA1 cleared from Slot 08 %d",
+ error);
+ return error;
+ }
+
+
+ }
+ /* Enable DA2 for HSR */
+
+ if (channel_index & e_CHANNEL_2) {
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ if (!(initialVal_DA & EN_DA2))
+ return 0;
+
+ /* Power Down HSR driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, 0,
+ EN_HSR_MASK);
+ if (0 != error) {
+ dev_err(dev, "Power down HSR Driver %d", error);
+ return error;
+ }
+
+ /* Power Down HSR DAC driver */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0,
+ POWER_UP_HSR_DAC);
+ if (0 != error) {
+ dev_err(dev, "Power down HSR DAC Driver %d", error);
+ return error;
+ }
+
+ /* Power Down HSR DAC and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0,
+ EN_HSR_MASK);
+ if (0 != error) {
+ dev_err(dev,
+ "Power down HSR DAC and digital path %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ 0, EN_DA2);
+ if (0 != error) {
+ dev_err(dev, "Disable DA2 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0,
+ DA2_TO_HSR);
+ if (0 != error) {
+ dev_err(dev,
+ "Clear DA_IN2 path mixed with sidetone FIR %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA2_REG, 0,
+ SLOT09_FOR_DA_PATH);
+ if (0 != error) {
+ dev_err(dev,
+ "Data sent to DA2 cleared from Slot 09 %d",
+ error);
+ return error;
+ }
+
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+
+/**
+ * @brief Mute headset on a specific channel
+ * @channel_index Headeset channel-index
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_mute_headset(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ /* Check if HS Mute request is mono or Stereo channel */
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "HS should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ if (channel_index & e_CHANNEL_1) {
+ /* Mute HSL */
+ error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG,
+ EN_HSL_MASK | EN_HSL_DAC_MASK,
+ 0);
+ if (0 != error) {
+ dev_err(dev, "Mute HSL %d", error);
+ return error;
+ }
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ /* Mute HSR */
+ error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG,
+ EN_HSR_MASK | EN_HSR_DAC_MASK,
+ 0);
+ if (0 != error) {
+ dev_err(dev, "Mute HSR %d", error);
+ return error;
+ }
+ }
+
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+
+/**
+ * @brief Unmute headset on a specific channel
+ * @channel_index Headeset channel-index
+ * @gain Gain index of headset
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_unmute_headset(enum AUDIOIO_CH_INDEX channel_index, int *gain,
+ struct device *dev)
+{
+ int error = 0;
+
+ /* Check if HS UnMute request is mono or Stereo channel */
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "HS should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ if (channel_index & e_CHANNEL_1) {
+ /* UnMute HSL */
+ error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG, 0,
+ EN_HSL_MASK | EN_HSL_DAC_MASK);
+ if (0 != error) {
+ dev_err(dev, "UnMute HSL %d", error);
+ return error;
+ }
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ /* UnMute HSR */
+ error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG, 0,
+ EN_HSR_MASK | EN_HSR_DAC_MASK);
+ if (0 != error) {
+ dev_err(dev, "UnMute HSR %d", error);
+ return error;
+ }
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+
+/**
+ * @brief Enables fading of headset on a specific channel
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_enable_fade_headset(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(SHORT_CIRCUIT_DISABLE_REG,
+ 0, DIS_HS_FAD);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for HS %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DA1_DIGITAL_GAIN_REG, 0, DIS_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for HSL %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(HSL_EAR_DIGITAL_GAIN_REG, 0,
+ DIS_DIG_GAIN_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for Digital Gain of HSL %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DA2_DIGITAL_GAIN_REG, 0, DIS_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for HSR %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(HSR_DIGITAL_GAIN_REG, 0,
+ DIS_DIG_GAIN_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for Digital Gain of HSR %d",
+ error);
+ return error;
+ }
+
+ return error;
+}
+/**
+ * @brief Disables fading of headset on a specific channel
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_disable_fade_headset(struct device *dev)
+{
+ int error = 0;
+ error = HW_ACODEC_MODIFY_WRITE(SHORT_CIRCUIT_DISABLE_REG,
+ DIS_HS_FAD, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for HS %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DA1_DIGITAL_GAIN_REG, DIS_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for HSL %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(HSL_EAR_DIGITAL_GAIN_REG,
+ DIS_DIG_GAIN_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for Digital Gain of HSL %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DA2_DIGITAL_GAIN_REG, DIS_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for HSR %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(HSR_DIGITAL_GAIN_REG,
+ DIS_DIG_GAIN_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for Digital Gain of HSR %d",
+ error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Power up earpiece
+ * @channel_index Channel-index
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_power_up_earpiece(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_DA = 0;
+
+ /* Check if Earpiece PowerUp request is mono channel */
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "EARPIECE should have mono channel");
+ return -EINVAL;
+ }
+
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+
+ /* Check if Earpiece is already powered up or DA1 being used by HS */
+ if (EN_DA1 & initialVal_DA)
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1,
+ DA1_TO_HSL, 0);
+ if (0 != error) {
+ dev_err(dev,
+ "DA_IN1 path mixed with sidetone FIR %d", error);
+ return error;
+ }
+
+ /* Enable DA1 */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA1_REG,
+ SLOT08_FOR_DA_PATH, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to DA1 from Slot 08 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ EN_DA1, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable DA1 %d", error);
+ return error;
+ }
+
+ /* Power Up EAR class-AB driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG,
+ EN_EAR_MASK, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up EAR class-AB driver %d", error);
+ return error;
+ }
+
+ /* Power up EAR DAC and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(
+ DIGITAL_OUTPUT_ENABLE_REG, EN_EAR_MASK, 0);
+ if (0 != error) {
+ dev_err(dev, "Power up EAR DAC and digital path %d", error);
+ return error;
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+/**
+ * @brief Power down earpiece
+ * @channel_index Channel-index
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_power_down_earpiece(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_DA = 0;
+
+ /* Check if Earpiece PowerDown request is mono channel */
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "EARPIECE should have mono channel");
+ return -EINVAL;
+ }
+
+ /* Check if Earpiece is already powered down or DA1 being used by HS */
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ if (!(initialVal_DA & EN_DA1))
+ return 0;
+
+ /* Power Down EAR class-AB driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG,
+ 0, EN_EAR_MASK);
+ if (0 != error) {
+ dev_err(dev, "Power Down EAR class-AB driver %d", error);
+ return error;
+ }
+
+ /* Power Down EAR DAC and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG,
+ 0, EN_EAR_MASK);
+ if (0 != error) {
+ dev_err(dev,
+ "Power Down EAR DAC and digital path %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0, DA1_TO_HSL);
+ if (0 != error) {
+ dev_err(dev,
+ "Clear DA_IN1 path mixed with sidetone FIR %d",
+ error);
+ return error;
+ }
+
+ /* Disable DA1 */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ 0, EN_DA1);
+ if (0 != error) {
+ dev_err(dev, "Disable DA1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA1_REG, 0,
+ SLOT08_FOR_DA_PATH);
+ if (0 != error) {
+ dev_err(dev,
+ "Data sent to DA1 cleared from Slot 08 %d", error);
+ return error;
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+/**
+ * @brief Mute earpiece
+ * @channel_index Channel-index
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_mute_earpiece(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+
+ /* Check if Earpiece Mute request is mono channel */
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "EARPIECE should have mono channel");
+ return -EINVAL;
+ }
+
+ /* Mute Earpiece */
+ error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG,
+ EN_EAR_MASK | EN_EAR_DAC_MASK, 0);
+ if (0 != error) {
+ dev_err(dev, "Mute Earpiece %d", error);
+ return error;
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+/**
+ * @brief Unmute earpiece
+ * @channel_index Channel-index
+ * @gain Gain index of earpiece
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_unmute_earpiece(enum AUDIOIO_CH_INDEX channel_index, int *gain,
+ struct device *dev)
+{
+ int error = 0;
+
+ /* Check if Earpiece UnMute request is mono channel */
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "EARPIECE should have mono channel");
+ return -EINVAL;
+ }
+
+ /* UnMute Earpiece */
+ error = HW_ACODEC_MODIFY_WRITE(MUTE_HS_EAR_REG, 0,
+ EN_EAR_MASK | EN_EAR_DAC_MASK);
+ if (0 != error) {
+ dev_err(dev, "UnMute Earpiece %d", error);
+ return error;
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+/**
+ * @brief Enables fading of earpiece
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_enable_fade_earpiece(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DA1_DIGITAL_GAIN_REG, 0, DIS_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for Ear %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(HSL_EAR_DIGITAL_GAIN_REG, 0,
+ DIS_DIG_GAIN_FADING);
+ if (0 != error) {
+ dev_err(dev,
+ "Enable fading for Digital Gain of Ear %d", error);
+ return error;
+ }
+
+ return error;
+}
+/**
+ * @brief Disables fading of earpiece
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_disable_fade_earpiece(struct device *dev)
+{
+ int error = 0;
+ error = HW_ACODEC_MODIFY_WRITE(DA1_DIGITAL_GAIN_REG, DIS_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for Ear %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Power up IHF on a specific channel
+ * @channel_index Channel-index
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_power_up_ihf(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_DA = 0;
+
+ /* Check if IHF PowerUp request is mono or Stereo channel */
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "IHF should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ if (channel_index & e_CHANNEL_1) {
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ if (EN_DA3 & initialVal_DA)
+ return 0;
+
+ /* Enable DA3 for IHFL */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA3_REG,
+ SLOT10_FOR_DA_PATH, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to DA3 from Slot 10 %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ EN_DA3, 0);
+ if (0 != error) {
+ dev_err(dev, "Power up IHFL %d", error);
+ return error;
+ }
+
+ /* Power Up HFL Class-D driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG,
+ EN_HFL_MASK, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up HFL Class-D Driver %d", error);
+ return error;
+ }
+
+ /* Power up HFL Class D driver and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG,
+ EN_HFL_MASK, 0);
+ if (0 != error) {
+ dev_err(dev,
+ "Power up HFL Class D driver & digital path %d",
+ error);
+ return error;
+ }
+ }
+
+ /* Enable DA4 for IHFR */
+ if (channel_index & e_CHANNEL_2) {
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ if (EN_DA4 & initialVal_DA)
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA4_REG,
+ SLOT11_FOR_DA_PATH, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to DA4 from Slot 11 %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ EN_DA4, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable DA4 %d", error);
+ return error;
+ }
+
+ /* Power Up HFR Class-D driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG,
+ EN_HFR_MASK, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up HFR Class-D Driver %d", error);
+ return error;
+ }
+
+ /* Power up HFR Class D driver and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG,
+ EN_HFR_MASK, 0);
+ if (0 != error) {
+ dev_err(dev,
+ "Power up HFR Class D driver and digital path %d",
+ error);
+ return error;
+ }
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+/**
+ * @brief Power down IHF on a specific channel
+ * @channel_index Channel-index
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_power_down_ihf(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_DA = 0;
+
+ /* Check if IHF Power Down request is mono or Stereo channel */
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "IHF should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ if (channel_index & e_CHANNEL_1) {
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ if (!(initialVal_DA & EN_DA3))
+ return 0;
+
+ /* Power Down HFL Class-D driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, 0,
+ EN_HFL_MASK);
+ if (0 != error) {
+ dev_err(dev, "Power Down HFL Class-D Driver %d",
+ error);
+ return error;
+ }
+
+ /* Power Down HFL Class D driver and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0,
+ EN_HFL_MASK);
+ if (0 != error) {
+ dev_err(dev,
+ "Power Down HFL Class D driver & digital path %d",
+ error);
+ return error;
+ }
+
+ /* Disable DA3 for IHFL */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ 0, EN_DA3);
+ if (0 != error) {
+ dev_err(dev, "Disable DA3 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA3_REG, 0,
+ SLOT10_FOR_DA_PATH);
+ if (0 != error) {
+ dev_err(dev,
+ "Data sent to DA3 cleared from Slot 10 %d",
+ error);
+ return error;
+ }
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+
+ /* Check if IHF is already powered Down */
+ if (!(initialVal_DA & EN_DA4))
+ return 0;
+
+ /* Power Down HFR Class-D Driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, 0,
+ EN_HFR_MASK);
+ if (0 != error) {
+ dev_err(dev, "Power Down HFR Class-D Driver %d",
+ error);
+ return error;
+ }
+
+ /* Power Down HFR Class D driver and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0,
+ EN_HFR_MASK);
+ if (0 != error) {
+ dev_err(dev,
+ "Power Down HFR Class D driver & digital path %d",
+ error);
+ return error;
+ }
+
+ /* Disable DA4 for IHFR */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ 0, EN_DA4);
+ if (0 != error) {
+ dev_err(dev, "Disable DA4 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA4_REG, 0,
+ SLOT11_FOR_DA_PATH);
+ if (0 != error) {
+ dev_err(dev,
+ "Data sent to DA4 cleared from Slot 11 %d",
+ error);
+ return error;
+ }
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+/**
+ * @brief Mute IHF on a specific channel
+ * @channel_index Channel-index
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_mute_ihf(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+
+ if ((channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ error = ste_audio_io_set_ihf_gain(channel_index, 0, -63,
+ 0, dev);
+ if (0 != error) {
+ dev_err(dev, "Mute ihf %d", error);
+ return error;
+ }
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+/**
+ * @brief Unmute IHF on a specific channel
+ * @channel_index Channel-index
+ * @gain Gain index of IHF
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_unmute_ihf(enum AUDIOIO_CH_INDEX channel_index, int *gain,
+ struct device *dev)
+{
+ int error = 0;
+
+ if (channel_index & e_CHANNEL_1) {
+ error = ste_audio_io_set_ihf_gain(channel_index, 0, gain[0],
+ 0, dev);
+ if (0 != error) {
+ dev_err(dev, "UnMute ihf %d", error);
+ return error;
+ }
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ error = ste_audio_io_set_ihf_gain(channel_index, 0, gain[1],
+ 0, dev);
+ if (0 != error) {
+ dev_err(dev, "UnMute ihf %d", error);
+ return error;
+ }
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+/**
+ * @brief Enable fading of IHF
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_enable_fade_ihf(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DA3_DIGITAL_GAIN_REG, 0, DIS_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for HFL %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DA4_DIGITAL_GAIN_REG, 0, DIS_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for HFR %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Disable fading of IHF
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_disable_fade_ihf(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DA3_DIGITAL_GAIN_REG, DIS_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for HFL %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DA4_DIGITAL_GAIN_REG, DIS_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for HFR %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Power up VIBL
+ * @channel_index Channel-index of VIBL
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_power_up_vibl(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_DA = 0;
+
+ /* Check if VibL PowerUp request is mono channel */
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "VibL should have mono channel");
+ return -EINVAL;
+ }
+
+ /* Try to allocate vibrator for audio left channel */
+ error = ste_audioio_vibrator_alloc(STE_AUDIOIO_CLIENT_AUDIO_L,
+ STE_AUDIOIO_CLIENT_AUDIO_R | STE_AUDIOIO_CLIENT_AUDIO_L);
+ if (error) {
+ dev_err(dev, " Couldn't allocate vibrator %d, client %d",
+ error, STE_AUDIOIO_CLIENT_AUDIO_L);
+ return error;
+ }
+
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+
+ /* Check if VibL is already powered up */
+ if (initialVal_DA & EN_DA5)
+ return 0;
+
+ /* Enable DA5 for vibl */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG,
+ SLOT12_FOR_DA_PATH, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to DA5 from Slot 12 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ EN_DA5, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable DA5 for VibL %d", error);
+ return error;
+ }
+
+ /* Power Up VibL Class-D driver */
+ error = HW_ACODEC_MODIFY_WRITE(
+ ANALOG_OUTPUT_ENABLE_REG, EN_VIBL_MASK, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up VibL Class-D Driver %d", error);
+ return error;
+ }
+
+ /* Power up VibL Class D driver and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG,
+ EN_VIBL_MASK, 0);
+ if (0 != error) {
+ dev_err(dev,
+ "Power up VibL Class D driver and digital path %d",
+ error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Power down VIBL
+ * @channel_index Channel-index of VIBL
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_power_down_vibl(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_DA = 0;
+
+ /* Check if VibL Power Down request is mono channel */
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "VibL should have mono channel");
+ return -EINVAL;
+ }
+
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+
+ /* Check if VibL is already powered down */
+ if (!(initialVal_DA & EN_DA5))
+ return 0;
+
+
+ /* Power Down VibL Class-D driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG,
+ 0, EN_VIBL_MASK);
+ if (0 != error) {
+ dev_err(dev, "Power Down VibL Class-D Driver %d", error);
+ return error;
+ }
+
+ /* Power Down VibL Class D driver and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0,
+ EN_VIBL_MASK);
+ if (0 != error) {
+ dev_err(dev,
+ "Power Down VibL Class D driver & digital path %d",
+ error);
+ return error;
+ }
+
+ /* Disable DA5 for VibL */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ 0, EN_DA5);
+ if (0 != error) {
+ dev_err(dev, "Disable DA5 for VibL %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG, 0,
+ SLOT12_FOR_DA_PATH);
+ if (0 != error) {
+ dev_err(dev,
+ "Data sent to DA5 cleared from Slot 12 %d", error);
+ return error;
+ }
+
+ /* Release vibrator */
+ ste_audioio_vibrator_release(STE_AUDIOIO_CLIENT_AUDIO_L);
+
+ return error;
+}
+/**
+ * @brief Enable fading of VIBL
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_enable_fade_vibl(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DA5_DIGITAL_GAIN_REG, 0, DIS_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for VibL %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Disable fading of VIBL
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_disable_fade_vibl(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DA5_DIGITAL_GAIN_REG, DIS_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for VibL %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Power up VIBR
+ * @channel_index Channel-index of VIBR
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_power_up_vibr(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_DA = 0;
+
+ /* Check if VibR PowerUp request is mono channel */
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "VibR should have mono channel");
+ return -EINVAL;
+ }
+
+ /* Try to allocate vibrator for audio right channel */
+ error = ste_audioio_vibrator_alloc(STE_AUDIOIO_CLIENT_AUDIO_R,
+ STE_AUDIOIO_CLIENT_AUDIO_R | STE_AUDIOIO_CLIENT_AUDIO_L);
+ if (error) {
+ dev_err(dev, " Couldn't allocate vibrator %d, client %d",
+ error, STE_AUDIOIO_CLIENT_AUDIO_R);
+ return error;
+ }
+
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+
+ /* Check if VibR is already powered up */
+ if (initialVal_DA & EN_DA6)
+ return 0;
+
+ /* Enable DA6 for vibr */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG,
+ SLOT13_FOR_DA_PATH, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to DA5 from Slot 13 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(
+ DIGITAL_DA_CHANNELS_ENABLE_REG, EN_DA6, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable DA6 for VibR %d", error);
+ return error;
+ }
+
+ /* Power Up VibR Class-D driver */
+ error = HW_ACODEC_MODIFY_WRITE(
+ ANALOG_OUTPUT_ENABLE_REG, EN_VIBR_MASK, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up VibR Class-D Driver %d", error);
+ return error;
+ }
+
+ /* Power up VibR Class D driver and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG,
+ EN_VIBR_MASK, 0);
+ if (0 != error) {
+ dev_err(dev,
+ "Power up VibR Class D driver & digital path %d",
+ error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Power down VIBR
+ * @channel_index Channel-index of VIBR
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_power_down_vibr(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_DA = 0;
+
+ /* Check if VibR PowerDown request is mono channel */
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "VibR should have mono channel");
+ return -EINVAL;
+ }
+
+ initialVal_DA = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+
+ /* Check if VibR is already powered down */
+ if (!(initialVal_DA & EN_DA6))
+ return 0;
+
+
+ /* Power Down VibR Class-D driver */
+ error = HW_ACODEC_MODIFY_WRITE(ANALOG_OUTPUT_ENABLE_REG, 0,
+ EN_VIBR_MASK);
+ if (0 != error) {
+ dev_err(dev, "Power Down VibR Class-D Driver %d", error);
+ return error;
+ }
+
+ /* Power Down VibR Class D driver and digital path */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_OUTPUT_ENABLE_REG, 0,
+ EN_VIBR_MASK);
+ if (0 != error) {
+ dev_err(dev,
+ "Power Down VibR Class D driver & digital path %d",
+ error);
+ return error;
+ }
+
+ /* Disable DA6 for VibR */
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_DA_CHANNELS_ENABLE_REG,
+ 0, EN_DA6);
+ if (0 != error) {
+ dev_err(dev, "Disable DA6 for VibR %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG, 0,
+ SLOT13_FOR_DA_PATH);
+ if (0 != error) {
+ dev_err(dev, "Data sent to DA5 cleared from Slot 13 %d",
+ error);
+ return error;
+ }
+
+ /* Release vibrator */
+ ste_audioio_vibrator_release(STE_AUDIOIO_CLIENT_AUDIO_R);
+
+ return error;
+}
+/**
+ * @brief Enable fading of VIBR
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_enable_fade_vibr(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DA6_DIGITAL_GAIN_REG, 0, DIS_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for VibR %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Disable fading of VIBR
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_disable_fade_vibr(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DA6_DIGITAL_GAIN_REG, DIS_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for VibR %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Power up MIC1A
+ * @channel_index Channel-index of MIC1A
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_power_up_mic1a(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_AD = 0;
+
+ /* Check if Mic1 PowerUp request is mono channel */
+
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "MIC1 should have mono channel");
+ return -EINVAL;
+ }
+
+ initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ /* Check if Mic1 is already powered up or used by Dmic3 */
+ if (EN_AD3 & initialVal_AD)
+ return 0;
+
+ error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, DATA_FROM_AD_OUT3);
+ if (0 != error) {
+ dev_err(dev, "Slot 02 outputs data from AD_OUT3 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ EN_AD3, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable AD3 for Mic1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0,
+ SEL_DMIC3_FOR_AD_OUT3);
+ if (0 != error) {
+ dev_err(dev, "Select ADC1 for AD_OUT3 %d", error);
+ return error;
+ }
+
+ /* Select MIC1A */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0,
+ SEL_MIC1B_CLR_MIC1A);
+ if (0 != error) {
+ dev_err(dev, "Select MIC1A %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, EN_MIC1, 0);
+ if (0 != error) {
+ dev_err(dev, "Power up Mic1 %d", error);
+ return error;
+ }
+
+ /* Power Up ADC1 */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, POWER_UP_ADC1, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up ADC1 %d", error);
+ return error;
+ }
+
+return error;
+}
+/**
+ * @brief Power down MIC1A
+ * @channel_index Channel-index of MIC1A
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_power_down_mic1a(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_AD = 0;
+
+ /* Check if Mic1 PowerDown request is mono channel */
+
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "Mic1 should have mono channel");
+ return -EINVAL;
+ }
+
+ initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ /* Check if Mic1 is already powered down or used by Dmic3 */
+ if (!(initialVal_AD & EN_AD3))
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, EN_MIC1);
+ if (0 != error) {
+ dev_err(dev, "Power Down Mic1 %d", error);
+ return error;
+ }
+
+ /* Power Down ADC1 */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0, POWER_UP_ADC1);
+ if (0 != error) {
+ dev_err(dev, "Power Down ADC1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ 0, EN_AD3);
+ if (0 != error) {
+ dev_err(dev, "Disable AD3 for Mic1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG, 0,
+ DATA_FROM_AD_OUT3);
+ if (0 != error) {
+ dev_err(dev, "Slot 02 outputs data cleared from AD_OUT3 %d",
+ error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Mute MIC1A
+ * @channel_index Channel-index of MIC1A
+ * @return 0 on success otherwise negative error code
+ */
+
+
+int ste_audio_io_mute_mic1a(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "MIC1 should have mono channel");
+ return -EINVAL;
+ }
+
+ /* Mute mic1 */
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, MUT_MIC1, 0);
+ if (0 != error) {
+ dev_err(dev, "Mute Mic1 %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Unmute MIC1A
+ * @channel_index Channel-index of MIC1A
+ * @gain Gain index of MIC1A
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_unmute_mic1a(enum AUDIOIO_CH_INDEX channel_index, int *gain,
+ struct device *dev)
+{
+ int error = 0;
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "Mic1 should have mono channel");
+ return -EINVAL;
+ }
+ /* UnMute mic1 */
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, MUT_MIC1);
+ if (0 != error) {
+ dev_err(dev, "UnMute Mic1 %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Enable fading of MIC1A
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_enable_fade_mic1a(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(AD3_DIGITAL_GAIN_REG, 0, DIS_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for Mic1 %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Disable fading of MIC1A
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_disable_fade_mic1a(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(AD3_DIGITAL_GAIN_REG, DIS_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for Mic1 %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Power up MIC1B
+ * @channel_index Channel-index of MIC1B
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_power_up_mic1b(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error;
+ unsigned char initialVal_AD = 0;
+
+ error = regulator_enable(regulator_avsource);
+ if (0 != error) {
+ dev_err(dev, "regulator avsource enable failed = %d", error);
+ return error;
+ }
+ /* GPIO35 settings to enable MIC 1B input instead of TVOUT */
+ error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_DIR5_REG,
+ GPIO35_DIR_OUTPUT, 0);
+ if (0 != error) {
+ dev_err(dev, "setting AB8500_GPIO_DIR5_REG reg %d", error);
+ return error;
+ }
+ error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_OUT5_REG,
+ GPIO35_DIR_OUTPUT, 0);
+ if (0 != error) {
+ dev_err(dev, "setting AB8500_GPIO_OUT5_REG reg %d", error);
+ return error;
+ }
+
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "Mic1 should have mono channel");
+ return -EINVAL;
+ }
+
+ initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG);
+ /* Check if Mic1 is already powered up or used by Dmic3 */
+ if (EN_AD3 & initialVal_AD)
+ return 0;
+
+ error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, DATA_FROM_AD_OUT3);
+ if (0 != error) {
+ dev_err(dev, "Slot 02 outputs data from AD_OUT3 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ EN_AD3, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable AD3 for Mic1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0,
+ SEL_DMIC3_FOR_AD_OUT3);
+ if (0 != error) {
+ dev_err(dev, "Select ADC1 for AD_OUT3 %d", error);
+ return error;
+ }
+
+ /* Select MIC1B */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, SEL_MIC1B_CLR_MIC1A,
+ 0);
+ if (0 != error) {
+ dev_err(dev, "Select MIC1B %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, EN_MIC1, 0);
+ if (0 != error) {
+ dev_err(dev, "Power up Mic1 %d", error);
+ return error;
+ }
+
+ /* Power Up ADC1 */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, POWER_UP_ADC1, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up ADC1 %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Power down MIC1B
+ * @channel_index Channel-index of MIC1B
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_power_down_mic1b(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error;
+ unsigned char initialVal_AD = 0;
+
+ /* Check if Mic1 PowerDown request is mono channel */
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "Mic1 should have mono channel");
+ return -EINVAL;
+ }
+
+ initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+
+ /* Check if Mic1 is already powered down or used by Dmic3 */
+ if (!(initialVal_AD & EN_AD3))
+ return 0;
+
+
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, EN_MIC1);
+ if (0 != error) {
+ dev_err(dev, "Power Down Mic1 %d", error);
+ return error;
+ }
+
+ /* Power Down ADC1 */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0, POWER_UP_ADC1);
+ if (0 != error) {
+ dev_err(dev, "Power Down ADC1 %d", error);
+ return error;
+ }
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, 0,
+ EN_AD3);
+ if (0 != error) {
+ dev_err(dev, "Disable AD3 for Mic1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG, 0,
+ DATA_FROM_AD_OUT3);
+ if (0 != error) {
+ dev_err(dev, "Slot 02 outputs data cleared from AD_OUT3 %d",
+ error);
+ return error;
+ }
+
+ /* undo GPIO35 settings */
+ error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_DIR5_REG,
+ 0, GPIO35_DIR_OUTPUT);
+ if (0 != error) {
+ dev_err(dev, "resetting AB8500_GPIO_DIR5_REG reg %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_OUT5_REG,
+ 0, GPIO35_DIR_OUTPUT);
+ if (0 != error) {
+ dev_err(dev, "resetting AB8500_GPIO_OUT5_REG reg %d", error);
+ return error;
+ }
+
+ error = regulator_disable(regulator_avsource);
+ if (0 != error) {
+ dev_err(dev, "regulator avsource disable failed = %d", error);
+ return error;
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+
+/**
+ * @brief enable hardware loop of mic1b
+ * @chnl_index Channel-index of MIC1B
+ * @hw_loop type of hardware loop
+ * @loop_gain gain value to be used in hardware loop
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_enable_loop_mic1b(enum AUDIOIO_CH_INDEX chnl_index,
+ enum AUDIOIO_HAL_HW_LOOPS hw_loop,
+ int loop_gain, struct device *dev,
+ void *cookie)
+{
+ int error;
+ struct transducer_context_t *trnsdr;
+ trnsdr = (struct transducer_context_t *)cookie;
+
+ switch (hw_loop) {
+ /* Check if HSL is active */
+ case AUDIOIO_SIDETONE_LOOP:
+ if (!(trnsdr[HS_CH].is_power_up[e_CHANNEL_1])
+ && !(trnsdr[EAR_CH].is_power_up[e_CHANNEL_1])) {
+ error = -EFAULT;
+ dev_err(dev,
+ "HS or Earpiece not powered up error = %d",
+ error);
+ return error;
+ }
+
+ /* For ch1, Power On STFIR1, data comes from AD3*/
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2,
+ FIR1_FROMAD3, 0);
+ if (error)
+ dev_err(dev, "FIR1 data comes from AD_OUT3 %d",
+ error);
+ error = HW_REG_WRITE(SIDETONE_FIR1_GAIN_REG, loop_gain);
+ if (error) {
+ dev_err(dev,
+ "Set FIR1 Gain index = %d",
+ error);
+ return error;
+ }
+ break;
+ default:
+ error = -EINVAL;
+ dev_err(dev, "loop not supported %d", error);
+ }
+ return error;
+}
+
+/**
+ * @brief disable hardware loop of mic1b
+ * @chnl_index Channel-index of MIC1B
+ * @hw_loop type of hardware loop
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_disable_loop_mic1b(enum AUDIOIO_CH_INDEX chnl_index,
+ enum AUDIOIO_HAL_HW_LOOPS hw_loop,
+ struct device *dev, void *cookie)
+{
+ int error;
+ struct transducer_context_t *trnsdr;
+ trnsdr = (struct transducer_context_t *)cookie;
+
+ switch (hw_loop) {
+ /* Check if HSL is active */
+ case AUDIOIO_SIDETONE_LOOP:
+ if (!trnsdr[HS_CH].is_power_up[e_CHANNEL_1]
+ && !trnsdr[EAR_CH].is_power_up[e_CHANNEL_1]) {
+ error = -EFAULT;
+ dev_err(dev, "HS or Earpiece not powered up, err = %d",
+ error);
+ return error;
+ }
+
+ /* For ch1, Power down STFIR1, data comes from AD3*/
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2,
+ 0, FIR1_FROMAD3);
+ if (error) {
+ dev_err(dev, "FIR1 data comes from AD_OUT3, err = %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(FILTERS_CONTROL_REG,
+ 0, FIR_FILTERCONTROL);
+ if (error) {
+ dev_err(dev,
+ "ST FIR Filters disable failed %d", error);
+ return error;
+ }
+ break;
+ default:
+ error = -EINVAL;
+ dev_err(dev, "loop not supported %d", error);
+ }
+ return error;
+}
+
+/**
+ * @brief Power up MIC2
+ * @channel_index Channel-index of MIC2
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_power_up_mic2(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_AD = 0;
+
+ /* Check if Mic2 PowerUp request is mono channel */
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "Mic2 should have mono channel");
+ return -EINVAL;
+ }
+
+ initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+
+ /* Check if Mic2 is already powered up or used by LINR or Dmic2 */
+ if (EN_AD2 & initialVal_AD)
+ return 0;
+
+
+ error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, DATA_FROM_AD_OUT2);
+ if (0 != error) {
+ dev_err(dev, "Slot 01 outputs data from AD_OUT2 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, EN_AD2,
+ 0);
+ if (0 != error) {
+ dev_err(dev, "Enable AD2 for Mic2 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0,
+ SEL_DMIC2_FOR_AD_OUT2);
+ if (0 != error) {
+ dev_err(dev, "Select ADC2 for AD_OUT2 %d", error);
+ return error;
+ }
+
+ /* Select mic2 */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0,
+ SEL_LINR_CLR_MIC2);
+ if (0 != error) {
+ dev_err(dev, "Select MIC2 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, EN_MIC2, 0);
+ if (0 != error) {
+ dev_err(dev, "Power up Mic2 %d", error);
+ return error;
+ }
+
+ /* Power Up ADC1 */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, POWER_UP_ADC2, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up ADC2 %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Power down MIC2
+ * @channel_index Channel-index of MIC2
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_power_down_mic2(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_AD = 0;
+
+ /* Check if Mic2 PowerDown request is mono channel */
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "Mic2 should have mono channel");
+ return -EINVAL;
+ }
+
+ initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+
+ /* Check if Mic2 is already powered down or used by LINR or Dmic2 */
+ if (!(initialVal_AD & EN_AD2))
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, EN_MIC2);
+ if (0 != error) {
+ dev_err(dev, "Power Down Mic2 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ 0, EN_AD2);
+ if (0 != error) {
+ dev_err(dev, "Disable AD2 for Mic2 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, 0,
+ (DATA_FROM_AD_OUT2<<4));
+ if (0 != error) {
+ dev_err(dev, "Slot 01 outputs data cleared from AD_OUT2 %d",
+ error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Mute MIC2
+ * @channel_index Channel-index of MIC2
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_mute_mic2(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "Mic2 should have mono channel");
+ return -EINVAL;
+ }
+
+ /* Mute mic2 */
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, MUT_MIC2, 0);
+ if (0 != error) {
+ dev_err(dev, "Mute Mic2 %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Unmute MIC2
+ * @channel_index Channel-index of MIC2
+ * @gain Gain index of MIC2
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_unmute_mic2(enum AUDIOIO_CH_INDEX channel_index, int *gain,
+ struct device *dev)
+{
+ int error = 0;
+ if (!(channel_index & e_CHANNEL_1)) {
+ dev_err(dev, "Mic2 should have mono channel");
+ return -EINVAL;
+ }
+ /* UnMute mic2 */
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0, MUT_MIC2);
+ if (0 != error) {
+ dev_err(dev, "UnMute Mic2 %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Enable fading of MIC2
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_enable_fade_mic2(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(AD2_DIGITAL_GAIN_REG, 0, DIS_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for Mic2 %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Disable fading of MIC2
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_disable_fade_mic2(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(AD2_DIGITAL_GAIN_REG, DIS_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for Mic2 %d", error);
+ return error;
+ }
+
+ return error;
+}
+/**
+ * @brief Power up LinIn
+ * @channel_index Channel-index of LinIn
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_power_up_lin(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_AD = 0;
+
+ /* Check if LinIn PowerUp request is mono or Stereo channel */
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "LinIn should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ /* Enable AD1 for LinInL */
+ if (channel_index & e_CHANNEL_1) {
+ initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ if (initialVal_AD & EN_AD1)
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG,
+ DATA_FROM_AD_OUT1, 0);
+ if (0 != error) {
+ dev_err(dev, "Slot 00 outputs data from AD_OUT1 %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ EN_AD1, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable AD1 for LinInL %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0,
+ SEL_DMIC1_FOR_AD_OUT1);
+ if (0 != error) {
+ dev_err(dev, "Select ADC3 for AD_OUT1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(
+ LINE_IN_MIC_CONF_REG, EN_LIN_IN_L, 0);
+ if (0 != error) {
+ dev_err(dev, "Power up LinInL %d", error);
+ return error;
+ }
+
+ /* Power Up ADC3 */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG,
+ POWER_UP_ADC3, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up ADC3 %d", error);
+ return error;
+ }
+ }
+ /* Enable AD2 for LinInR */
+
+ if (channel_index & e_CHANNEL_2) {
+ initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ if (EN_AD2 & initialVal_AD)
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG,
+ (DATA_FROM_AD_OUT2<<4), 0);
+ if (0 != error) {
+ dev_err(dev, "Slot 01 outputs data from AD_OUT2 %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ EN_AD2, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable AD2 LinInR %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1, 0,
+ SEL_DMIC2_FOR_AD_OUT2);
+ if (0 != error) {
+ dev_err(dev, "Select ADC2 for AD_OUT2 %d", error);
+ return error;
+ }
+
+ /* Select LinInR */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG,
+ SEL_LINR_CLR_MIC2, 0);
+ if (0 != error) {
+ dev_err(dev, "Select LinInR %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG,
+ EN_LIN_IN_R, 0);
+ if (0 != error) {
+ dev_err(dev, "Power up LinInR %d", error);
+ return error;
+ }
+
+ /* Power Up ADC2 */
+ error = HW_ACODEC_MODIFY_WRITE(
+ ADC_DAC_ENABLE_REG, POWER_UP_ADC2, 0);
+ if (0 != error) {
+ dev_err(dev, "Power Up ADC2 %d", error);
+ return error;
+ }
+ }
+ return error;
+}
+/**
+ * @brief Power down LinIn
+ * @channel_index Channel-index of LinIn
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_power_down_lin(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_AD = 0;
+
+ /* Check if LinIn PowerDown request is mono or Stereo channel */
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "LinIn should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ /* Enable AD1 for LinInL */
+ if (channel_index & e_CHANNEL_1) {
+ initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ if (!(initialVal_AD & EN_AD1))
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0,
+ EN_LIN_IN_L);
+ if (0 != error) {
+ dev_err(dev, "Power Down LinInL %d", error);
+ return error;
+ }
+
+ /* Power Down ADC3 */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0,
+ POWER_UP_ADC3);
+ if (0 != error) {
+ dev_err(dev, "Power Down ADC3 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ 0, EN_AD1);
+ if (0 != error) {
+ dev_err(dev, "Disable AD1 for LinInL %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, 0,
+ DATA_FROM_AD_OUT1);
+ if (0 != error) {
+ dev_err(dev,
+ "Slot 00 outputs data cleared from AD_OUT1 %d",
+ error);
+ return error;
+ }
+ }
+
+ /* Enable AD2 for LinInR */
+ if (channel_index & e_CHANNEL_2) {
+ initialVal_AD = HW_REG_READ(DIGITAL_DA_CHANNELS_ENABLE_REG);
+ if (!(initialVal_AD & EN_AD2))
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0,
+ EN_LIN_IN_R);
+ if (0 != error) {
+ dev_err(dev, "Power Down LinInR %d", error);
+ return error;
+ }
+
+ /* Power Down ADC2 */
+ error = HW_ACODEC_MODIFY_WRITE(ADC_DAC_ENABLE_REG, 0,
+ POWER_UP_ADC2);
+ if (0 != error) {
+ dev_err(dev, "Power Down ADC2 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ 0, EN_AD2);
+ if (0 != error) {
+ dev_err(dev, "Disable AD2 LinInR %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, 0,
+ (DATA_FROM_AD_OUT2<<4));
+ if (0 != error) {
+ dev_err(dev,
+ "Slot01 outputs data cleared from AD_OUT2 %d",
+ error);
+ return error;
+ }
+ }
+ return error;
+}
+/**
+ * @brief Mute LinIn
+ * @channel_index Channel-index of LinIn
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_mute_lin(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "LinIn should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ if (channel_index & e_CHANNEL_1) {
+ /* Mute LinInL */
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG,
+ MUT_LIN_IN_L, 0);
+ if (0 != error) {
+ dev_err(dev, "Mute LinInL %d", error);
+ return error;
+ }
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ /* Mute LinInR */
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG,
+ MUT_LIN_IN_R,
+ 0);
+ if (0 != error) {
+ dev_err(dev, "Mute LinInR %d", error);
+ return error;
+ }
+ }
+ return error;
+}
+/**
+ * @brief Unmute LinIn
+ * @channel_index Channel-index of LinIn
+ * @gain Gain index of LinIn
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_unmute_lin(enum AUDIOIO_CH_INDEX channel_index, int *gain,
+ struct device *dev)
+{
+ int error = 0;
+
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "LinIn should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ if (channel_index & e_CHANNEL_1) {
+ /* UnMute LinInL */
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0,
+ MUT_LIN_IN_L);
+ if (0 != error) {
+ dev_err(dev, "UnMute LinInL %d", error);
+ return error;
+ }
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ /* UnMute LinInR */
+ error = HW_ACODEC_MODIFY_WRITE(LINE_IN_MIC_CONF_REG, 0,
+ MUT_LIN_IN_R);
+ if (0 != error) {
+ dev_err(dev, "UnMute LinInR %d", error);
+ return error;
+ }
+ }
+ return error;
+}
+/**
+ * @brief Enables fading of LinIn
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_enable_fade_lin(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(AD1_DIGITAL_GAIN_REG, 0, DIS_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for LinInL %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AD2_DIGITAL_GAIN_REG, 0, DIS_FADING);
+ if (0 != error) {
+ dev_err(dev, "Enable fading for LinInR %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Disables fading of LinIn
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_disable_fade_lin(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(AD1_DIGITAL_GAIN_REG, DIS_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for LinInL %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AD2_DIGITAL_GAIN_REG, DIS_FADING, 0);
+ if (0 != error) {
+ dev_err(dev, "Disable fading for LinInR %d", error);
+ return error;
+ }
+ return error;
+}
+/**
+ * @brief Power Up DMIC12 LinIn
+ * @channel_index Channel-index of DMIC12
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_power_up_dmic12(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_AD = 0;
+
+ /* Check if DMic12 request is mono or Stereo */
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "DMic12 does not support more than 2 channels");
+
+ return -EINVAL;
+ }
+
+ /* Setting Direction for GPIO pins on AB8500 */
+ error = HW_REG_WRITE(AB8500_GPIO_DIR4_REG, GPIO27_DIR_OUTPUT);
+ if (0 != error) {
+ dev_err(dev, "Setting Direction for GPIO pins on AB8500 %d",
+ error);
+ return error;
+ }
+
+ /* Enable AD1 for Dmic1 */
+ if (channel_index & e_CHANNEL_1) {
+ /* Check if DMIC1 is already powered up or used by LinInL */
+ initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG);
+ if (initialVal_AD & EN_AD1)
+ return 0;
+
+ error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG,
+ DATA_FROM_AD_OUT1);
+ if (0 != error) {
+ dev_err(dev, "Slot 00 outputs data from AD_OUT1 %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ EN_AD1, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable AD1 for DMIC1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1,
+ SEL_DMIC1_FOR_AD_OUT1, 0);
+ if (0 != error) {
+ dev_err(dev, "Select DMIC1 for AD_OUT1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, EN_DMIC1, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable DMIC1 %d", error);
+ return error;
+ }
+ }
+ /* Enable AD2 for Dmic2 */
+
+ if (channel_index & e_CHANNEL_2) {
+ /* Check if DMIC2 is already powered up
+ or used by Mic2 or LinInR */
+ initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG);
+ if (initialVal_AD & EN_AD2)
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG,
+ (DATA_FROM_AD_OUT2<<4), 0);
+ if (0 != error) {
+ dev_err(dev, "Slot 01 outputs data from AD_OUT2 %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ EN_AD2, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable AD2 for DMIC2 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1,
+ SEL_DMIC2_FOR_AD_OUT2, 0);
+ if (0 != error) {
+ dev_err(dev, "Select DMIC2 for AD_OUT2 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, EN_DMIC2, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable DMIC2 %d", error);
+ return error;
+ }
+ }
+
+ return error;
+}
+/**
+ * @brief Power down DMIC12 LinIn
+ * @channel_index Channel-index of DMIC12
+ * @return 0 on success otherwise negative error code
+ */
+
+
+int ste_audio_io_power_down_dmic12(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_AD = 0;
+
+ /* Check if DMic12 request is mono or Stereo or multi channel */
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "DMic12 does not support more than 2 channels");
+
+ return -EINVAL;
+ }
+
+ /* Setting Direction for GPIO pins on AB8500 */
+ error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_DIR4_REG, 0,
+ GPIO27_DIR_OUTPUT);
+ if (0 != error) {
+ dev_err(dev, "Clearing Direction for GPIO pins on AB8500 %d",
+ error);
+ return error;
+ }
+ /* Enable AD1 for Dmic1 */
+ if (channel_index & e_CHANNEL_1) {
+ /* Check if DMIC1 is already powered Down or used by LinInL */
+ initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG);
+ if (!(initialVal_AD & EN_AD1))
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, 0, EN_DMIC1);
+ if (0 != error) {
+ dev_err(dev, "Enable DMIC1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ 0, EN_AD1);
+ if (0 != error) {
+ dev_err(dev, "Disable AD1 for DMIC1 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, 0,
+ DATA_FROM_AD_OUT1);
+ if (0 != error) {
+ dev_err(dev,
+ "Slot 00 outputs data cleared from AD_OUT1 %d",
+ error);
+ return error;
+ }
+ }
+
+ /* Enable AD2 for Dmic2 */
+ if (channel_index & e_CHANNEL_2) {
+ /* MIC2 is already powered Down or used by Mic2 or LinInR */
+ initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG);
+ if (!(initialVal_AD & EN_AD2))
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, 0, EN_DMIC2);
+ if (0 != error) {
+ dev_err(dev, "Enable DMIC2 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ 0, EN_AD2);
+ if (0 != error) {
+ dev_err(dev, "Disable AD2 for DMIC2 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT0_1_REG, 0,
+ (DATA_FROM_AD_OUT2<<4));
+ if (0 != error) {
+ dev_err(dev,
+ "Slot 01 outputs data cleared from AD_OUT2 %d",
+ error);
+ return error;
+ }
+ }
+ return error;
+}
+/**
+ * @brief Get headset gain
+ * @left_volume
+ * @right_volume
+ * @return 0 on success otherwise negative error code
+ */
+
+
+int ste_audio_io_get_headset_gain(int *left_volume, int *right_volume,
+ u16 gain_index, struct device *dev)
+{
+ int i = 0;
+ if (gain_index == 0) {
+
+ *left_volume = 0 - HW_REG_READ(DA1_DIGITAL_GAIN_REG);
+ *right_volume = 0 - HW_REG_READ(DA2_DIGITAL_GAIN_REG);
+
+ }
+
+ if (gain_index == 1) {
+ *left_volume = 8 - HW_REG_READ(HSL_EAR_DIGITAL_GAIN_REG);
+ *right_volume = 8 - HW_REG_READ(HSR_DIGITAL_GAIN_REG);
+ }
+
+ if (gain_index == 2) {
+ i = (HW_REG_READ(ANALOG_HS_GAIN_REG)>>4);
+ *left_volume = hs_analog_gain_table[i];
+ i = (HW_REG_READ(ANALOG_HS_GAIN_REG) & MASK_QUARTET0);
+ *right_volume = hs_analog_gain_table[i];
+ }
+ return 0;
+}
+/**
+ * @brief Get earpiece gain
+ * @left_volume
+ * @right_volume
+ * @return 0 on success otherwise negative error code
+ */
+
+
+int ste_audio_io_get_earpiece_gain(int *left_volume, int *right_volume,
+ u16 gain_index, struct device *dev)
+{
+ if (0 == gain_index)
+ *left_volume = 0 - HW_REG_READ(DA1_DIGITAL_GAIN_REG);
+ if (1 == gain_index)
+ *left_volume = 8 - HW_REG_READ(HSL_EAR_DIGITAL_GAIN_REG);
+ return 0;
+}
+/**
+ * @brief Get ihf gain
+ * @left_volume
+ * @right_volume
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_get_ihf_gain(int *left_volume, int *right_volume,
+ u16 gain_index, struct device *dev)
+{
+
+ *left_volume = 0 - HW_REG_READ(DA3_DIGITAL_GAIN_REG);
+ *right_volume = 0 - HW_REG_READ(DA4_DIGITAL_GAIN_REG);
+ return 0;
+}
+/**
+ * @brief Get vibl gain
+ * @left_volume
+ * @right_volume
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_get_vibl_gain(int *left_volume, int *right_volume,
+ u16 gain_index, struct device *dev)
+{
+
+ *left_volume = 0 - HW_REG_READ(DA5_DIGITAL_GAIN_REG);
+
+ return 0;
+}
+/**
+ * @brief Get vibr gain
+ * @left_volume
+ * @right_volume
+ * @return 0 on success otherwise negative error code
+ */
+
+
+int ste_audio_io_get_vibr_gain(int *left_volume, int *right_volume,
+ u16 gain_index, struct device *dev)
+{
+
+ *right_volume = 0 - HW_REG_READ(DA6_DIGITAL_GAIN_REG);
+ return 0;
+}
+/**
+ * @brief Get MIC1A & MIC2A gain
+ * @left_volume
+ * @right_volume
+ * @return 0 on success otherwise negative error code
+ */
+
+
+int ste_audio_io_get_mic1a_gain(int *left_volume, int *right_volume,
+ u16 gain_index, struct device *dev)
+{
+ if (gain_index == 0)
+ *left_volume = 31 - HW_REG_READ(AD3_DIGITAL_GAIN_REG);
+ if (gain_index == 1)
+ *left_volume = HW_REG_READ(ANALOG_MIC1_GAIN_REG);
+
+ return 0;
+}
+/**
+ * @brief Get MIC2 gain
+ * @left_volume
+ * @right_volume
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_get_mic2_gain(int *left_volume, int *right_volume,
+ u16 gain_index, struct device *dev)
+{
+ if (gain_index == 0)
+ *left_volume = 31 - HW_REG_READ(AD2_DIGITAL_GAIN_REG);
+ if (gain_index == 1)
+ *left_volume = HW_REG_READ(ANALOG_MIC2_GAIN_REG);
+
+ return 0;
+}
+/**
+ * @brief Get Lin IN gain
+ * @left_volume
+ * @right_volume
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_get_lin_gain(int *left_volume, int *right_volume,
+ u16 gain_index, struct device *dev)
+{
+ if (gain_index == 0) {
+ *left_volume = 31 - HW_REG_READ(AD1_DIGITAL_GAIN_REG);
+ *right_volume = 31 - HW_REG_READ(AD2_DIGITAL_GAIN_REG);
+ }
+
+ if (gain_index == 0) {
+ *left_volume = 2 * ((HW_REG_READ(ANALOG_HS_GAIN_REG)>>4) - 5);
+ *right_volume = 2 * (HW_REG_READ(ANALOG_LINE_IN_GAIN_REG) - 5);
+ }
+
+ return 0;
+}
+/**
+ * @brief Get DMIC12 gain
+ * @left_volume
+ * @right_volume
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_get_dmic12_gain(int *left_volume, int *right_volume,
+ u16 gain_index, struct device *dev)
+{
+
+ *left_volume = HW_REG_READ(AD1_DIGITAL_GAIN_REG);
+
+ *right_volume = HW_REG_READ(AD2_DIGITAL_GAIN_REG);
+
+ return 0;
+}
+/**
+ * @brief Get DMIC34 gain
+ * @left_volume
+ * @right_volume
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_get_dmic34_gain(int *left_volume, int *right_volume,
+ u16 gain_index, struct device *dev)
+{
+ *left_volume = HW_REG_READ(AD3_DIGITAL_GAIN_REG);
+ *right_volume = HW_REG_READ(AD4_DIGITAL_GAIN_REG);
+
+ return 0;
+}
+/**
+ * @brief Get DMIC56 gain
+ * @left_volume
+ * @right_volume
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_get_dmic56_gain(int *left_volume, int *right_volume,
+ u16 gain_index, struct device *dev)
+{
+ *left_volume = HW_REG_READ(AD5_DIGITAL_GAIN_REG);
+
+ *right_volume = HW_REG_READ(AD6_DIGITAL_GAIN_REG);
+ return 0;
+}
+/**
+ * @brief Set gain of headset along a specified channel
+ * @channel_index Channel-index of headset
+ * @gain_index Gain index of headset
+ * @gain_value Gain value of headset
+ * @linear
+ * @return 0 on success otherwise negative error code
+ */
+
+
+int ste_audio_io_set_headset_gain(enum AUDIOIO_CH_INDEX channel_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initial_val = 0;
+ int i = 0;
+ int acodec_device_id;
+
+ acodec_device_id = abx500_get_chip_id(dev);
+
+ if (channel_index & e_CHANNEL_1) {
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 0 - gain_value;
+
+ initial_val = HW_REG_READ(DA1_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(DA1_DIGITAL_GAIN_REG,
+ ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain HSL gainindex = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+
+ if (gain_index == 1) {
+ int gain = 0;
+ gain = 8 - gain_value;
+
+ initial_val = HW_REG_READ(HSL_EAR_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(HSL_EAR_DIGITAL_GAIN_REG,
+ ((initial_val & (~HS_DIGITAL_GAIN_MASK)) | (gain &
+ HS_DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain HSL gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+
+ if (gain_index == 2) {
+ /* Set Analog gain */
+ int gain = -1;
+
+ if (gain_value % 2) {
+ gain_value -= 1;
+ dev_err(dev,
+ "Odd Gain received.Fixing it to 2dB step gain_value = %d",
+ gain_value);
+ }
+ /* Fix for 4dB step gains. Select one lower value */
+ if (gain_value == -22)
+ gain_value = -24;
+
+ if (gain_value == -26)
+ gain_value = -28;
+
+ if (gain_value == -30)
+ gain_value = -32;
+
+ for (i = 0 ; i < 16; i++) {
+ if (hs_analog_gain_table[i] == gain_value) {
+ gain = i<<4;
+ break;
+ }
+ }
+ if (gain == -1)
+ return -1;
+
+ if ((AB8500_REV_10 == acodec_device_id) ||
+ (AB8500_REV_11 == acodec_device_id)) {
+ if (!gain)
+ gain = 0x10;
+ gain = 0xF0 - gain;
+ }
+ initial_val = HW_REG_READ(ANALOG_HS_GAIN_REG);
+
+ /* Write gain */
+ error = HW_REG_WRITE(ANALOG_HS_GAIN_REG, ((initial_val &
+ (~L_ANALOG_GAIN_MASK)) | (gain & L_ANALOG_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain HSL gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+
+ /* for HSR */
+ if (channel_index & e_CHANNEL_2) {
+ /* Set Gain HSR */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 0 - gain_value;
+
+ initial_val = HW_REG_READ(DA2_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(DA2_DIGITAL_GAIN_REG, ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain HSR gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+
+ if (gain_index == 1) {
+ int gain = 0;
+ gain = 8 - gain_value;
+
+ initial_val = HW_REG_READ(HSR_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(HSR_DIGITAL_GAIN_REG, ((initial_val
+ & (~HS_DIGITAL_GAIN_MASK)) | (gain &
+ HS_DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain HSR gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+
+ }
+
+ if (gain_index == 2) {
+ /* Set Analog gain */
+ int gain = -1;
+
+ if (gain_value % 2) {
+ gain_value -= 1;
+ dev_err(dev,
+ "Odd Gain received.Fixing it to 2dB step gain_value = %d",
+ gain_value);
+ }
+ /* Fix for 4dB step gains. Select one lower value */
+ if (gain_value == -22)
+ gain_value = -24;
+
+ if (gain_value == -26)
+ gain_value = -28;
+
+ if (gain_value == -30)
+ gain_value = -32;
+
+ for (i = 0 ; i < 16 ; i++) {
+ if (hs_analog_gain_table[i] == gain_value) {
+ gain = i;
+ break;
+ }
+ }
+ if (gain == -1)
+ return -1;
+
+ if ((AB8500_REV_10 == acodec_device_id) ||
+ (AB8500_REV_11 == acodec_device_id)) {
+ if (!gain)
+ gain = 1;
+ gain = 0x0F - gain;
+ }
+ initial_val = HW_REG_READ(ANALOG_HS_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(ANALOG_HS_GAIN_REG, ((initial_val &
+ (~R_ANALOG_GAIN_MASK)) | (gain & R_ANALOG_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain HSR gainindex = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+/**
+ * @brief Set gain of earpiece
+ * @channel_index Channel-index of earpiece
+ * @gain_index Gain index of earpiece
+ * @gain_value Gain value of earpiece
+ * @linear
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_set_earpiece_gain(enum AUDIOIO_CH_INDEX channel_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initial_val = 0;
+ if (channel_index & e_CHANNEL_1) {
+ if (0 == gain_index) {
+ int gain = 0;
+ gain = 0 - gain_value;
+
+ initial_val = HW_REG_READ(DA1_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(DA1_DIGITAL_GAIN_REG, ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain Ear gainindex = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+
+ if (gain_index == 1) {
+ int gain = 0;
+ gain = 8 - gain_value;
+
+ initial_val = HW_REG_READ(HSL_EAR_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(HSL_EAR_DIGITAL_GAIN_REG,
+ ((initial_val & (~HS_DIGITAL_GAIN_MASK)) | (gain &
+ HS_DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain Ear gainindex = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+/**
+ * @brief Set gain of vibl
+ * @channel_index Channel-index of vibl
+ * @gain_index Gain index of vibl
+ * @gain_value Gain value of vibl
+ * @linear
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_set_vibl_gain(enum AUDIOIO_CH_INDEX channel_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev)
+{
+
+ int error = 0;
+ unsigned char initial_val = 0;
+
+ if (channel_index & e_CHANNEL_1) {
+ /* Set Gain vibl */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 0 - gain_value;
+
+ initial_val = HW_REG_READ(DA5_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(DA5_DIGITAL_GAIN_REG, ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain VibL gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+ return error;
+}
+/**
+ * @brief Set gain of vibr
+ * @channel_index Channel-index of vibr
+ * @gain_index Gain index of vibr
+ * @gain_value Gain value of vibr
+ * @linear
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_set_vibr_gain(enum AUDIOIO_CH_INDEX channel_index,
+ u16 gain_index, int gain_value,
+ u32 linear,
+ struct device *dev)
+{
+
+ int error = 0;
+ unsigned char initial_val = 0;
+
+ if (channel_index & e_CHANNEL_1) {
+ /* Set Gain vibr */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 0 - gain_value;
+
+ initial_val = HW_REG_READ(DA6_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(DA6_DIGITAL_GAIN_REG,
+ ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain VibR gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+ return error;
+}
+/**
+ * @brief Set gain of ihf along a specified channel
+ * @channel_index Channel-index of ihf
+ * @gain_index Gain index of ihf
+ * @gain_value Gain value of ihf
+ * @linear
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_set_ihf_gain(enum AUDIOIO_CH_INDEX channel_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initial_val = 0;
+
+ if (channel_index & e_CHANNEL_1) {
+ /* Set Gain IHFL */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 0 - gain_value;
+
+ initial_val = HW_REG_READ(DA3_DIGITAL_GAIN_REG);
+ error = HW_REG_WRITE(DA3_DIGITAL_GAIN_REG, ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain IHFL gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+
+ }
+ }
+ if (channel_index & e_CHANNEL_2) {
+ /* Set Gain IHFR */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 0 - gain_value;
+
+ initial_val = HW_REG_READ(DA4_DIGITAL_GAIN_REG);
+ error = HW_REG_WRITE(DA4_DIGITAL_GAIN_REG, ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain IHFR gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+
+ return error;
+}
+/**
+ * @brief Set gain of MIC1A & MIC1B
+ * @channel_index Channel-index of MIC1
+ * @gain_index Gain index of MIC1
+ * @gain_value Gain value of MIC1
+ * @linear
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_set_mic1a_gain(enum AUDIOIO_CH_INDEX channel_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initial_val = 0;
+
+ if (channel_index & e_CHANNEL_1) {
+ /* Set Gain mic1 */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 31 - gain_value;
+
+ initial_val = HW_REG_READ(AD3_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(AD3_DIGITAL_GAIN_REG, ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain Mic1 gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+
+ }
+
+ if (gain_index == 1) {
+ /* Set Analog gain */
+ initial_val = HW_REG_READ(ANALOG_MIC1_GAIN_REG);
+
+ /* Write gain */
+ error = HW_REG_WRITE(ANALOG_MIC1_GAIN_REG, ((initial_val
+ & (~MIC_ANALOG_GAIN_MASK)) | (gain_value &
+ MIC_ANALOG_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain Mic1 gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+ return error;
+}
+/**
+ * @brief Set gain of MIC2
+ * @channel_index Channel-index of MIC2
+ * @gain_index Gain index of MIC2
+ * @gain_value Gain value of MIC2
+ * @linear
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_set_mic2_gain(enum AUDIOIO_CH_INDEX channel_index,
+ u16 gain_index, int gain_value,
+ u32 linear,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initial_val = 0;
+
+ if (channel_index & e_CHANNEL_1) {
+ /* Set Gain mic2 */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 31 - gain_value;
+
+ initial_val = HW_REG_READ(AD2_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(AD2_DIGITAL_GAIN_REG, ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain Mic2 gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+
+ }
+
+ if (gain_index == 1) {
+ /* Set Analog gain */
+ initial_val = HW_REG_READ(ANALOG_MIC2_GAIN_REG);
+
+ /* Write gain */
+ error = HW_REG_WRITE(ANALOG_MIC2_GAIN_REG, ((initial_val
+ & (~MIC_ANALOG_GAIN_MASK)) | (gain_value &
+ MIC_ANALOG_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain Mic2 gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+ return error;
+}
+/**
+ * @brief Set gain of Lin IN along a specified channel
+ * @channel_index Channel-index of Lin In
+ * @gain_index Gain index of Lin In
+ * @gain_value Gain value of Lin In
+ * @linear
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_set_lin_gain(enum AUDIOIO_CH_INDEX channel_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initial_val = 0;
+
+ if (channel_index & e_CHANNEL_1) {
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 31 - gain_value;
+
+ initial_val = HW_REG_READ(AD1_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(AD1_DIGITAL_GAIN_REG,
+ ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain LinInL gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+
+ }
+
+ if (gain_index == 1) {
+ int gain = 0;
+ /*
+ * Converting -10 to 20 range into 0 - 15
+ * & shifting it left by 4 bits
+ */
+ gain = ((gain_value/2) + 5)<<4;
+
+ initial_val = HW_REG_READ(ANALOG_LINE_IN_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(ANALOG_LINE_IN_GAIN_REG,
+ ((initial_val & (~L_ANALOG_GAIN_MASK)) | (gain &
+ L_ANALOG_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain LinInL gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ /* Set Gain LinInR */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 31 - gain_value;
+
+ initial_val = HW_REG_READ(AD2_DIGITAL_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(AD2_DIGITAL_GAIN_REG,
+ ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain LinInR gain index = %d%d",
+ gain_index, error);
+ return error;
+ }
+ }
+ if (gain_index == 1) {
+ int gain = 0;
+ /* Converting -10 to 20 range into 0 - 15 */
+ gain = ((gain_value/2) + 5);
+
+ initial_val = HW_REG_READ(ANALOG_LINE_IN_GAIN_REG);
+ /* Write gain */
+ error = HW_REG_WRITE(ANALOG_LINE_IN_GAIN_REG,
+ ((initial_val & (~R_ANALOG_GAIN_MASK)) | (gain &
+ R_ANALOG_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain LinInR gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+
+ return error;
+}
+/**
+ * @brief Set gain of DMIC12 along a specified channel
+ * @channel_index Channel-index of DMIC12
+ * @gain_index Gain index of DMIC12
+ * @gain_value Gain value of DMIC12
+ * @linear
+ * @return 0 on success otherwise negative error code
+ */
+
+int ste_audio_io_set_dmic12_gain(enum AUDIOIO_CH_INDEX channel_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initial_val = 0;
+
+ if (channel_index & e_CHANNEL_1) {
+ /* Set Gain Dmic1 */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 31 - gain_value;
+
+ initial_val = HW_REG_READ(AD1_DIGITAL_GAIN_REG);
+ error = HW_REG_WRITE(AD1_DIGITAL_GAIN_REG,
+ ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain DMic1 gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+ if (channel_index & e_CHANNEL_2) {
+ /* Set Gain Dmic2 */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 31 - gain_value;
+
+ initial_val = HW_REG_READ(AD2_DIGITAL_GAIN_REG);
+ error = HW_REG_WRITE(AD2_DIGITAL_GAIN_REG,
+ ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain DMic2 gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+ return error;
+}
+
+int ste_audio_io_switch_to_burst_mode_headset(int burst_fifo_switch_frame,
+ struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_INT_CONTROL_REG,
+ WAKEUP_SIGNAL_SAMPLE_COUNT, 0);
+ if (0 != error)
+ return error;
+
+ error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_LENGTH_REG,
+ BURST_FIFO_TRANSFER_LENGTH, 0);
+ if (0 != error)
+ return error;
+
+ error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_CONTROL_REG,
+ (BURST_FIFO_INF_RUNNING | BURST_FIFO_INF_IN_MASTER_MODE
+ |PRE_BIT_CLK0_COUNT), 0);
+ if (0 != error)
+ return error;
+
+ error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_WAKE_UP_DELAY_REG,
+ BURST_FIFO_WAKUP_DEALAY, 0);
+ if (0 != error)
+ return error;
+
+ error = HW_REG_WRITE(BURST_FIFO_SWITCH_FRAME_REG,
+ burst_fifo_switch_frame);
+ if (0 != error)
+ return error;
+
+ error = HW_ACODEC_MODIFY_WRITE(TDM_IF_BYPASS_B_FIFO_REG,
+ IF0_BFifoEn, 0);
+ if (0 != error)
+ return error;
+
+ return error;
+}
+int ste_audio_io_switch_to_normal_mode_headset(
+ struct device *dev)
+{
+ int error = 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(TDM_IF_BYPASS_B_FIFO_REG, 0,
+ IF0_BFifoEn);
+ if (0 != error)
+ return error;
+
+ error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_INT_CONTROL_REG,
+ 0, WAKEUP_SIGNAL_SAMPLE_COUNT);
+ if (0 != error)
+ return error;
+
+ error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_LENGTH_REG,
+ 0, BURST_FIFO_TRANSFER_LENGTH);
+ if (0 != error)
+ return error;
+
+ error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_CONTROL_REG, 0,
+ (BURST_FIFO_INF_RUNNING | BURST_FIFO_INF_IN_MASTER_MODE
+ |PRE_BIT_CLK0_COUNT));
+ if (0 != error)
+ return error;
+
+ error = HW_ACODEC_MODIFY_WRITE(BURST_FIFO_WAKE_UP_DELAY_REG,
+ 0, BURST_FIFO_WAKUP_DEALAY);
+ if (0 != error)
+ return error;
+
+ return error;
+}
+
+
+int ste_audio_io_mute_vibl(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ return 0;
+}
+
+int ste_audio_io_unmute_vibl(enum AUDIOIO_CH_INDEX channel_index, int *gain,
+ struct device *dev)
+{
+ return 0;
+}
+
+int ste_audio_io_mute_vibr(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ return 0;
+}
+int ste_audio_io_unmute_vibr(enum AUDIOIO_CH_INDEX channel_index, int *gain,
+ struct device *dev)
+{
+ return 0;
+}
+
+int ste_audio_io_mute_dmic12(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ if ((channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ error = ste_audio_io_set_dmic12_gain(channel_index, 0, -32,
+ 0, dev);
+ if (0 != error) {
+ dev_err(dev, "Mute dmic12 %d", error);
+ return error;
+ }
+ }
+
+ return error;
+
+}
+
+int ste_audio_io_unmute_dmic12(enum AUDIOIO_CH_INDEX channel_index, int *gain,
+ struct device *dev)
+{
+ int error = 0;
+ if ((channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ error = ste_audio_io_set_dmic12_gain(channel_index,
+ 0, gain[0], 0, dev);
+ if (0 != error) {
+ dev_err(dev, "UnMute dmic12 %d", error);
+ return error;
+ }
+ }
+ return error;
+}
+int ste_audio_io_enable_fade_dmic12(struct device *dev)
+{
+ return 0;
+}
+
+int ste_audio_io_disable_fade_dmic12(struct device *dev)
+{
+ return 0;
+}
+
+/**
+ * @brief enable hardware loop of dmic12
+ * @chnl_index Channel-index of dmic12
+ * @hw_loop type of hardware loop
+ * @loop_gain gain value to be used in hardware loop
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_enable_loop_dmic12(enum AUDIOIO_CH_INDEX chnl_index,
+ enum AUDIOIO_HAL_HW_LOOPS hw_loop,
+ int loop_gain, struct device *dev,
+ void *cookie)
+{
+ int error = 0;
+ struct transducer_context_t *trnsdr;
+ trnsdr = (struct transducer_context_t *)cookie;
+
+ switch (hw_loop) {
+ /* Check if HSL is active */
+ case AUDIOIO_SIDETONE_LOOP:
+ if (!trnsdr[HS_CH].is_power_up[e_CHANNEL_1]
+ && !trnsdr[EAR_CH].is_power_up[e_CHANNEL_1]) {
+ error = -EFAULT;
+ dev_err(dev,
+ "Sidetone enable needs HS or Earpiece powered up, err = %d",
+ error);
+ return error;
+ }
+
+ if (chnl_index & e_CHANNEL_1) {
+ /* For ch1, Power On STFIR1, data comes from AD1*/
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2,
+ 0, FIR1_FROMAD1);
+ if (error) {
+ dev_err(dev, "FIR1 data comes from AD_OUT1 %d",
+ error);
+ return error;
+ }
+
+ error = HW_REG_WRITE(SIDETONE_FIR1_GAIN_REG, loop_gain);
+ if (error) {
+ dev_err(dev,
+ "Set FIR1 Gain index = %d", error);
+ return error;
+ }
+ }
+
+ if (chnl_index & e_CHANNEL_2) {
+ /* For ch2, Power On STFIR1, data comes from AD2*/
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2,
+ 0, FIR1_FROMAD2);
+ if (error) {
+ dev_err(dev, "FIR1 data comes from AD_OUT2 %d",
+ error);
+ return error;
+ }
+ error = HW_REG_WRITE(SIDETONE_FIR2_GAIN_REG, loop_gain);
+ if (error) {
+ dev_err(dev,
+ "Set FIR2 Gain error = %d", error);
+ return error;
+ }
+ }
+ break;
+ default:
+ error = -EINVAL;
+ dev_err(dev, "loop not supported %d", error);
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+
+/**
+ * @brief disable hardware loop of dmic12
+ * @chnl_index Channel-index of dmic12
+ * @hw_loop type of hardware loop
+ * @return 0 on success otherwise negative error code
+ */
+int ste_audio_io_disable_loop_dmic12(enum AUDIOIO_CH_INDEX chnl_index,
+ enum AUDIOIO_HAL_HW_LOOPS hw_loop,
+ struct device *dev, void *cookie)
+{
+ int error = -EINVAL;
+ struct transducer_context_t *trnsdr;
+ trnsdr = (struct transducer_context_t *)cookie;
+
+ switch (hw_loop) {
+ /* Check if HSL is active */
+ case AUDIOIO_SIDETONE_LOOP:
+ if (!trnsdr[HS_CH].is_power_up[e_CHANNEL_1]
+ && !trnsdr[EAR_CH].is_power_up[e_CHANNEL_1]) {
+ error = -EFAULT;
+ dev_err(dev,
+ "Sidetone disable needs HS or Earpiece powered up, err = %d",
+ error);
+ return error;
+ }
+
+ if (chnl_index & e_CHANNEL_1) {
+ /* For ch1, Power On STFIR1, data comes from AD1*/
+ error =
+ HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2,
+ 0, FIR1_FROMAD1);
+ if (error)
+ dev_err(dev, "FIR1 data comes from AD_OUT1 %d",
+ error);
+ }
+
+ if (chnl_index & e_CHANNEL_2) {
+ /* For ch2, Power On STFIR1, data comes from AD2*/
+ error =
+ HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG2,
+ 0, FIR1_FROMAD2);
+ if (error)
+ dev_err(dev, "FIR1 data comes from AD_OUT2 %d",
+ error);
+ }
+ error = HW_ACODEC_MODIFY_WRITE(FILTERS_CONTROL_REG,
+ 0, FIR_FILTERCONTROL);
+ if (error) {
+ dev_err(dev,
+ "ST FIR Filters disable failed %d", error);
+ return error;
+ }
+ break;
+ default:
+ dev_err(dev, "loop not supported %d", error);
+ }
+ dump_acodec_registers(__func__, dev);
+ return error;
+}
+
+int ste_audio_io_power_up_dmic34(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_AD = 0;
+
+ /* Check if DMic34 request is mono or Stereo */
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "DMic34 does not support more than 2 channels");
+ return -EINVAL;
+ }
+
+ /* Setting Direction for GPIO pins on AB8500 */
+ error = HW_REG_WRITE(AB8500_GPIO_DIR4_REG, GPIO29_DIR_OUTPUT);
+ if (0 != error) {
+ dev_err(dev, "Setting Direction for GPIO pins on AB8500 %d",
+ error);
+ return error;
+ }
+
+ if (channel_index & e_CHANNEL_1) {
+ /* Check if DMIC3 is already powered up or used by Mic1A
+ or Mic1B */
+ initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG);
+
+ if (initialVal_AD & (EN_AD3))
+ return 0;
+
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG,
+ DATA_FROM_AD_OUT3, 0);
+ if (0 != error) {
+ dev_err(dev, "Slot 02 outputs data from AD_OUT3 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG, EN_AD3,
+ 0);
+ if (0 != error) {
+ dev_err(dev, "Enable AD3 for DMIC3 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_MUXES_REG1,
+ SEL_DMIC3_FOR_AD_OUT3,
+ 0);
+ if (0 != error) {
+ dev_err(dev, "Select DMIC3 for AD_OUT3 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, EN_DMIC3, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable DMIC3 %d", error);
+ return error;
+ }
+}
+
+ /* Enable AD4 for Dmic4 */
+ if (channel_index & e_CHANNEL_2) {
+ /* Check if DMIC4 is already powered up */
+ if (initialVal_AD & (EN_AD4))
+ return 0;
+
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG,
+ (DATA_FROM_AD_OUT4<<4), 0);
+ if (0 != error) {
+ dev_err(dev, "Slot 03 outputs data from AD_OUT4 %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ EN_AD4, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable AD4 for DMIC4 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, EN_DMIC4, 0);
+ if (0 != error) {
+ dev_err(dev, "Enable DMIC4 %d", error);
+ return error;
+ }
+ }
+ return error;
+ }
+
+int ste_audio_io_power_down_dmic34(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_AD = 0;
+
+
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "DMic34 does not support more than 2 channels");
+ return -EINVAL;
+ }
+
+ /* Setting Direction for GPIO pins on AB8500 */
+ error = HW_ACODEC_MODIFY_WRITE(AB8500_GPIO_DIR4_REG, 0,
+ GPIO29_DIR_OUTPUT);
+ if (0 != error) {
+ dev_err(dev, "Clearing Direction for GPIO pins on AB8500 %d",
+ error);
+ return error;
+ }
+
+ /* Enable AD1 for Dmic1 */
+ if (channel_index & e_CHANNEL_1) {
+ /* Check if DMIC3 is already powered Down or used by Mic1A
+ or Mic1B */
+ initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG);
+ if (!(initialVal_AD & EN_AD3))
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, 0, EN_DMIC3);
+ if (0 != error) {
+ dev_err(dev, "Enable DMIC3 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ 0,
+ EN_AD3);
+ if (0 != error) {
+ dev_err(dev, "Disable AD3 for DMIC3 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG, 0,
+ DATA_FROM_AD_OUT3);
+ if (0 != error) {
+ dev_err(dev,
+ "Slot 02 outputs data cleared from AD_OUT3 %d",
+ error);
+ return error;
+ }
+ }
+
+ /* Enable AD4 for Dmic4 */
+ if (channel_index & e_CHANNEL_2) {
+ /* Check if DMIC4 is already powered down */
+ initialVal_AD = HW_REG_READ(DIGITAL_AD_CHANNELS_ENABLE_REG);
+ if (!(initialVal_AD & EN_AD4))
+ return 0;
+
+ error = HW_ACODEC_MODIFY_WRITE(DMIC_ENABLE_REG, 0, EN_DMIC4);
+ if (0 != error) {
+ dev_err(dev, "Enable DMIC4 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(DIGITAL_AD_CHANNELS_ENABLE_REG,
+ 0, EN_AD4);
+ if (0 != error) {
+ dev_err(dev, "Disable AD4 for DMIC4 %d", error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT2_3_REG, 0,
+ (DATA_FROM_AD_OUT4<<4));
+ if (0 != error) {
+ dev_err(dev,
+ "Slot 03 outputs data cleared from AD_OUT4 %d",
+ error);
+ return error;
+ }
+ }
+ return error;
+}
+int ste_audio_io_set_dmic34_gain(enum AUDIOIO_CH_INDEX channel_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initial_val = 0;
+
+ if (channel_index & e_CHANNEL_1) {
+ /* Set Gain Dmic3 */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 31 - gain_value;
+
+ initial_val = HW_REG_READ(AD3_DIGITAL_GAIN_REG);
+ error = HW_REG_WRITE(AD3_DIGITAL_GAIN_REG,
+ ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain DMic3 gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ /* Set Gain Dmic4 */
+ if (gain_index == 0) {
+ int gain = 0;
+ gain = 31 - gain_value;
+
+ initial_val = HW_REG_READ(AD4_DIGITAL_GAIN_REG);
+ error = HW_REG_WRITE(AD4_DIGITAL_GAIN_REG,
+ ((initial_val
+ & (~DIGITAL_GAIN_MASK)) | (gain & DIGITAL_GAIN_MASK)));
+
+ if (0 != error) {
+ dev_err(dev,
+ "Set Gain DMic4 gain index = %d %d",
+ gain_index, error);
+ return error;
+ }
+ }
+ }
+
+ return error;
+}
+int ste_audio_io_mute_dmic34(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ if ((channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ error = ste_audio_io_set_dmic34_gain(channel_index, 0, -32,
+ 0, dev);
+ if (0 != error) {
+ dev_err(dev, "Mute dmic34 %d", error);
+ return error;
+ }
+ }
+ return error;
+}
+int ste_audio_io_unmute_dmic34(enum AUDIOIO_CH_INDEX channel_index, int *gain,
+ struct device *dev)
+{
+ int error = 0;
+ if ((channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ error = ste_audio_io_set_dmic34_gain(channel_index,
+ 0, gain[0], 0, dev);
+ if (0 != error) {
+ dev_err(dev, "UnMute dmic34 %d", error);
+ return error;
+ }
+ }
+ return error;
+}
+int ste_audio_io_enable_fade_dmic34(struct device *dev)
+{
+ return 0;
+}
+
+int ste_audio_io_disable_fade_dmic34(struct device *dev)
+{
+ return 0;
+}
+
+int ste_audio_io_power_up_dmic56(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ return 0;
+}
+int ste_audio_io_power_down_dmic56(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ return 0;
+}
+int ste_audio_io_set_dmic56_gain(enum AUDIOIO_CH_INDEX channel_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev)
+{
+ return 0;
+}
+int ste_audio_io_mute_dmic56(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ return 0;
+}
+int ste_audio_io_unmute_dmic56(enum AUDIOIO_CH_INDEX channel_index, int *gain,
+ struct device *dev)
+{
+ return 0;
+}
+int ste_audio_io_enable_fade_dmic56(struct device *dev)
+{
+ return 0;
+}
+
+int ste_audio_io_disable_fade_dmic56(struct device *dev)
+{
+ return 0;
+}
+
+int ste_audio_io_configure_if1(struct device *dev)
+{
+ int error = 0;
+
+ error = HW_REG_WRITE(IF1_CONF_REG, IF_DELAYED |
+ I2S_LEFT_ALIGNED_FORMAT | WORD_LENGTH_16);
+ if (error != 0) {
+ dev_err(dev,
+ "Configure IF1: I2S Format 16 Bits word length error = %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(TDM_IF_BYPASS_B_FIFO_REG, IF1_MASTER, 0);
+ if (error != 0) {
+ dev_err(dev,
+ "Configure IF1: IF1 master error = %d",
+ error);
+ return error;
+ }
+
+ error = HW_ACODEC_MODIFY_WRITE(IF0_IF1_MASTER_CONF_REG,
+ EN_FSYNC_BITCLK1, 0);
+ if (error != 0) {
+ dev_err(dev,
+ "ConfigIF1 bitclk is 32x48KHz, enable Fsync1 and Bitclk1 error = %d",
+ error);
+ return error;
+ }
+ return error;
+}
+
+int ste_audio_io_power_up_fmrx(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal = 0;
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "FMRX should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ ste_audio_io_configure_if1(dev);
+
+ if (channel_index & e_CHANNEL_1) {
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA7_REG,
+ SLOT24_FOR_DA_PATH, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to DA_IN7 from Slot 24 %d",
+ error);
+ return error;
+ }
+ /* DA_IN7 to AD_OUT8 path */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG,
+ SEL_AD_OUT8_FROM_DAIN7, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to AD_OUT5 from DA_IN7 %d",
+ error);
+ return error;
+ }
+
+ initialVal = HW_REG_READ(AD_ALLOCATION_TO_SLOT6_7_REG);
+ error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT6_7_REG,
+ ((initialVal & MASK_QUARTET1)|SEL_IF6_FROM_AD_OUT5));
+ if (0 != error) {
+ dev_err(dev, "Data sent to IF slot 6 from AD_OUT5 %d",
+ error);
+ return error;
+ }
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA8_REG,
+ SLOT25_FOR_DA_PATH, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to DA_IN8 from Slot 25 %d",
+ error);
+ return error;
+ }
+
+ /* DA_IN7 to AD_OUT8 path */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG,
+ SEL_AD_OUT6_FROM_DAIN8, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to AD_OUT6 from DA_IN8 %d",
+ error);
+ return error;
+ }
+
+ initialVal = HW_REG_READ(AD_ALLOCATION_TO_SLOT6_7_REG);
+
+ error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT6_7_REG,
+ (initialVal & MASK_QUARTET0)|SEL_IF7_FROM_AD_OUT6);
+ /* 5x is written */
+ if (0 != error) {
+ dev_err(dev, "Data sent to IF7 from AD_OUT6 %d",
+ error);
+ return error;
+ }
+ }
+ return error;
+}
+int ste_audio_io_power_down_fmrx(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "FMRX should have mono or stereo channels");
+ return -EINVAL;
+ }
+ if (channel_index & e_CHANNEL_1) {
+ /* data sent to DA7 input of DA filter form IF1 */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA7_REG, 0,
+ SLOT24_FOR_DA_PATH);
+ if (0 != error) {
+ dev_err(dev, "Clearing Data sent to DA_IN7 from Slot 24 %d",
+ error);
+ return error;
+ }
+ /* DA_IN7 to AD_OUT8 path */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG, 0,
+ SEL_AD_OUT8_FROM_DAIN7);
+ if (0 != error) {
+ dev_err(dev, "Clearing Data sent to AD_OUT5 from DA_IN7 %d",
+ error);
+ return error;
+ }
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT6_7_REG, 0,
+ SEL_IF6_FROM_AD_OUT5);
+ if (0 != error) {
+ dev_err(dev,
+ "Clearing Data sent to IF slot 6 from AD_OUT5 %d",
+ error);
+ return error;
+ }
+}
+
+ if (channel_index & e_CHANNEL_2) {
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA8_REG, 0,
+ SLOT25_FOR_DA_PATH);
+ if (0 != error) {
+ dev_err(dev,
+ "Clearing Data sent to DA_IN8 from Slot 25 %d",
+ error);
+ return error;
+ }
+
+ /* DA_IN7 to AD_OUT8 path */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG, 0,
+ SEL_AD_OUT6_FROM_DAIN8);
+ if (0 != error) {
+ dev_err(dev,
+ "Clearing Data sent to AD_OUT6 from DA_IN8 %d",
+ error);
+ return error;
+ }
+ error = HW_ACODEC_MODIFY_WRITE(AD_ALLOCATION_TO_SLOT6_7_REG, 0,
+ SEL_IF7_FROM_AD_OUT6);
+ if (0 != error) {
+ dev_err(dev,
+ "Clearing Data sent to IF7 from AD_OUT6 %d",
+ error);
+ return error;
+ }
+ }
+ return error;
+}
+
+int ste_audio_io_power_up_fmtx(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal = 0;
+
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "FMTX should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ ste_audio_io_configure_if1(dev);
+
+ if (channel_index & e_CHANNEL_1) {
+ /* data sent to DA7 input of DA filter form IF1 14 slot */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA7_REG,
+ SLOT14_FOR_DA_PATH, 0);
+ if (0 != error) {
+ dev_err(dev,
+ "Data sent to DA_IN7 from Slot 14 %d", error);
+ return error;
+ }
+ /* DA_IN7 to AD_OUT5 path */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG,
+ SEL_AD_OUT5_FROM_DAIN7, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to AD_OUT5 from DA_IN7 %d",
+ error);
+ return error;
+ }
+
+ initialVal = HW_REG_READ(AD_ALLOCATION_TO_SLOT16_17_REG);
+ error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT16_17_REG,
+ (initialVal & MASK_QUARTET1)|SEL_IF6_FROM_AD_OUT5);
+ if (0 != error) {
+ dev_err(dev, "Data sent to IF16 from AD_OUT5 %d",
+ error);
+ return error;
+ }
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ /* data sent to DA8 input of DA filter */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA8_REG,
+ SLOT15_FOR_DA_PATH, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to DA_IN8 from Slot 15 %d",
+ error);
+ return error;
+ }
+
+ /* DA_IN8 to AD_OUT6 path */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG,
+ SEL_AD_OUT6_FROM_DAIN8, 0);
+ if (0 != error) {
+ dev_err(dev, "Data sent to AD_OUT6 from DA_IN8 %d",
+ error);
+ return error;
+ }
+
+ initialVal = HW_REG_READ(AD_ALLOCATION_TO_SLOT16_17_REG);
+ error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT16_17_REG,
+ (initialVal & MASK_QUARTET0)|SEL_IF17_FROM_AD_OUT6);
+ if (0 != error) {
+ dev_err(dev, "Data sent to IF17 from AD_OUT6 %d",
+ error);
+ return error;
+ }
+ }
+ return error;
+}
+
+int ste_audio_io_power_down_fmtx(enum AUDIOIO_CH_INDEX channel_index,
+ struct device *dev)
+{
+ int error = 0;
+ unsigned char initialVal_AD = 0;
+
+ if (!(channel_index & (e_CHANNEL_1 | e_CHANNEL_2))) {
+ dev_err(dev, "FMTX should have mono or stereo channels");
+ return -EINVAL;
+ }
+
+ if (channel_index & e_CHANNEL_1) {
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA7_REG, 0,
+ SLOT14_FOR_DA_PATH);
+ if (0 != error) {
+ dev_err(dev,
+ "Clearing Data sent to DA_IN7 from Slot 14 %d",
+ error);
+ return error;
+ }
+ /* DA_IN7 to AD_OUT8 path */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA5_REG, 0,
+ SEL_AD_OUT5_FROM_DAIN7);
+ if (0 != error) {
+ dev_err(dev,
+ "Clearing Data sent to AD_OUT5 from DA_IN7 %d",
+ error);
+ return error;
+ }
+ error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT16_17_REG,
+ SEL_IF6_FROM_AD_OUT5);
+ if (0 != error) {
+ dev_err(dev,
+ "Clearing Data sent to IF16 from AD_OUT8 %d",
+ error);
+ return error;
+ }
+ }
+
+ if (channel_index & e_CHANNEL_2) {
+ /* data sent to DA8 input of DA filter */
+ initialVal_AD = HW_REG_READ(SLOT_SELECTION_TO_DA8_REG);
+
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA8_REG, 0,
+ SLOT15_FOR_DA_PATH);
+ if (0 != error) {
+ dev_err(dev,
+ "Clearing Data sent to DA_IN8 from Slot 15 %d",
+ error);
+ return error;
+ }
+
+ /* DA_IN7 to AD_OUT8 path */
+ error = HW_ACODEC_MODIFY_WRITE(SLOT_SELECTION_TO_DA6_REG, 0,
+ SEL_AD_OUT6_FROM_DAIN8);
+ if (0 != error) {
+ dev_err(dev,
+ "Clearing Data sent to AD_OUT6 from DA_IN8 %d",
+ error);
+ return error;
+ }
+ error = HW_REG_WRITE(AD_ALLOCATION_TO_SLOT16_17_REG,
+ SEL_IF17_FROM_AD_OUT6);
+ if (0 != error) {
+ dev_err(dev,
+ "Clearing Data sent to IF17 from AD_OUT6 %d",
+ error);
+ return error;
+ }
+ }
+ return error;
+}
+int ste_audio_io_power_up_bluetooth(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev)
+{
+ int error = 0;
+ struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
+ struct ab8500_platform_data *pdata = dev_get_platdata(ab8500->dev);
+ if (bluetooth_power_up_count++)
+ return error;
+
+ if (pdata) {
+ if (pdata->audio) {
+ error = pdata->audio->ste_gpio_altf_init();
+ if (error == 0) {
+ clk_ptr_msp0 = clk_get_sys("msp0", NULL);
+ if (!IS_ERR(clk_ptr_msp0)) {
+ error = clk_enable(clk_ptr_msp0);
+ return error;
+ } else
+ return -EFAULT;
+ }
+ }
+ }
+ return error;
+}
+
+int ste_audio_io_power_down_bluetooth(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev)
+{
+ int error = 0;
+ struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
+ struct ab8500_platform_data *pdata = dev_get_platdata(ab8500->dev);
+
+ if (--bluetooth_power_up_count)
+ return error;
+
+ if (pdata) {
+ if (pdata->audio) {
+ error = pdata->audio->ste_gpio_altf_exit();
+ if (error == 0) {
+ clk_disable(clk_ptr_msp0);
+ clk_put(clk_ptr_msp0);
+ }
+ }
+ }
+ return error;
+}
+
+int dump_acodec_registers(const char *str, struct device *dev)
+{
+ int reg_count = REVISION_REG & 0xff;
+ if (1 == acodec_reg_dump) {
+ u8 i = 0;
+ dev_info(dev, "\n func : %s\n", str);
+ for (i = 0; i <= reg_count; i++)
+ dev_info(dev,
+ "block = 0x0D, adr = %x = %x\n",
+ i, HW_REG_READ((AB8500_AUDIO << 8) | i));
+ }
+ str = str; /* keep compiler happy */
+ return 0;
+}
+
+int debug_audioio(int x)
+{
+
+ if (1 == x)
+ acodec_reg_dump = 1;
+ else
+ acodec_reg_dump = 0;
+ return 0;
+}
+
+
+
+
+
diff --git a/drivers/misc/audio_io_dev/ste_audio_io_func.h b/drivers/misc/audio_io_dev/ste_audio_io_func.h
new file mode 100644
index 00000000000..b0f73c26225
--- /dev/null
+++ b/drivers/misc/audio_io_dev/ste_audio_io_func.h
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#ifndef _AUDIOIO_FUNC_H_
+#define _AUDIOIO_FUNC_H_
+
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <mach/ste_audio_io_ioctl.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+
+#define AB8500_REV_10 0x10
+#define AB8500_REV_11 0x11
+#define AB8500_REV_20 0x20
+
+#define AB8500_CTRL3_REG 0x00000200
+#define AB8500_GPIO_DIR4_REG 0x00001013
+#define AB8500_GPIO_DIR5_REG 0x00001014
+#define AB8500_GPIO_OUT5_REG 0x00001024
+
+extern struct platform_device *ste_audio_io_device;
+extern struct regulator *regulator_avsource;
+
+int dump_acodec_registers(const char *, struct device *dev);
+int debug_audioio(int x);
+
+#define AB8500_BLOCK_ADDR(address) ((address >> 8) & 0xff)
+#define AB8500_OFFSET_ADDR(address) (address & 0xff)
+
+static inline unsigned char HW_REG_READ(unsigned short reg)
+{
+ unsigned char ret;
+ int err;
+
+ err = abx500_get_register_interruptible(&ste_audio_io_device->dev,
+ AB8500_BLOCK_ADDR(reg),
+ AB8500_OFFSET_ADDR(reg),
+ &ret);
+ if (err < 0)
+ return err;
+ else
+ return ret;
+}
+
+static inline int HW_REG_WRITE(unsigned short reg, unsigned char data)
+{
+ return abx500_set_register_interruptible(&ste_audio_io_device->dev,
+ AB8500_BLOCK_ADDR(reg),
+ AB8500_OFFSET_ADDR(reg),
+ data);
+}
+
+unsigned int ab8500_acodec_modify_write(unsigned int reg, u8 mask_set,
+ u8 mask_clear);
+
+#define HW_ACODEC_MODIFY_WRITE(reg, mask_set, mask_clear)\
+ ab8500_acodec_modify_write(reg, mask_set, mask_clear)
+
+unsigned int ab8500_modify_write(unsigned int reg, u8 mask_set, u8 mask_clear);
+
+int ste_audio_io_power_up_headset(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_headset(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_headset_query(struct device *dev);
+int ste_audio_io_set_headset_gain(enum AUDIOIO_CH_INDEX chnl_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_headset_gain(int *, int *, u16,
+ struct device *dev);
+int ste_audio_io_mute_headset(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_headset(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_headset_state(struct device *dev);
+int ste_audio_io_enable_fade_headset(struct device *dev);
+int ste_audio_io_disable_fade_headset(struct device *dev);
+int ste_audio_io_switch_to_burst_mode_headset(int burst_fifo_switch_frame,
+ struct device *dev);
+int ste_audio_io_switch_to_normal_mode_headset(
+ struct device *dev);
+
+int ste_audio_io_power_up_earpiece(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_earpiece(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_earpiece_query(struct device *dev);
+int ste_audio_io_set_earpiece_gain(enum AUDIOIO_CH_INDEX chnl_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_earpiece_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_earpiece(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_earpiece(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_earpiece_state(struct device *dev);
+int ste_audio_io_enable_fade_earpiece(struct device *dev);
+int ste_audio_io_disable_fade_earpiece(struct device *dev);
+
+int ste_audio_io_power_up_ihf(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_ihf(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_ihf_query(struct device *dev);
+int ste_audio_io_set_ihf_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index,
+ int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_ihf_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_ihf(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_ihf(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_ihf_state(struct device *dev);
+int ste_audio_io_enable_fade_ihf(struct device *dev);
+int ste_audio_io_disable_fade_ihf(struct device *dev);
+
+int ste_audio_io_power_up_vibl(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_vibl(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_vibl_query(struct device *dev);
+int ste_audio_io_set_vibl_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index,
+ int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_vibl_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_vibl(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_vibl(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_vibl_state(struct device *dev);
+int ste_audio_io_enable_fade_vibl(struct device *dev);
+int ste_audio_io_disable_fade_vibl(struct device *dev);
+
+int ste_audio_io_power_up_vibr(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_vibr(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_vibr_query(struct device *dev);
+int ste_audio_io_set_vibr_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index,
+ int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_vibr_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_vibr(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_vibr(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_vibr_state(struct device *dev);
+int ste_audio_io_enable_fade_vibr(struct device *dev);
+int ste_audio_io_disable_fade_vibr(struct device *dev);
+
+int ste_audio_io_power_up_mic1a(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_mic1a(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_mic1a_query(struct device *dev);
+int ste_audio_io_set_mic1a_gain(enum AUDIOIO_CH_INDEX chnl_index,
+ u16 gain_index, int gain_value, u32 linear, struct device *dev);
+int ste_audio_io_get_mic1a_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_mic1a(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_mic1a(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_mic1a_state(struct device *dev);
+int ste_audio_io_enable_fade_mic1a(struct device *dev);
+int ste_audio_io_disable_fade_mic1a(struct device *dev);
+
+/*
+ *** Mic1b ***
+ */
+int ste_audio_io_power_up_mic1b(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_mic1b(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_mic1b_query(struct device *dev);
+int ste_audio_io_set_mic1b_gain(enum AUDIOIO_CH_INDEX chnl_index,
+ u16 gain_index, int gain_value, u32 linear, struct device *dev);
+int ste_audio_io_get_mic1b_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_mic1b(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_mic1b(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_mic1b_state(struct device *dev);
+int ste_audio_io_enable_fade_mic1b(struct device *dev);
+int ste_audio_io_disable_fade_mic1b(struct device *dev);
+int ste_audio_io_enable_loop_mic1b(enum AUDIOIO_CH_INDEX chnl_index,
+ enum AUDIOIO_HAL_HW_LOOPS,
+ int loop_gain, struct device *dev,
+ void *cookie);
+int ste_audio_io_disable_loop_mic1b(enum AUDIOIO_CH_INDEX chnl_index,
+ enum AUDIOIO_HAL_HW_LOOPS hw_loop,
+ struct device *dev, void *cookie);
+/*
+ *** Mic2 ***
+ */
+int ste_audio_io_power_up_mic2(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_mic2(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_mic2_query(struct device *dev);
+int ste_audio_io_set_mic2_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index,
+ int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_mic2_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_mic2(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_mic2(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_mic2_state(struct device *dev);
+int ste_audio_io_enable_fade_mic2(struct device *dev);
+int ste_audio_io_disable_fade_mic2(struct device *dev);
+
+int ste_audio_io_power_up_lin(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_lin(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_lin_query(struct device *dev);
+int ste_audio_io_set_lin_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index,
+ int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_lin_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_lin(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_lin(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_lin_state(struct device *dev);
+int ste_audio_io_enable_fade_lin(struct device *dev);
+int ste_audio_io_disable_fade_lin(struct device *dev);
+
+int ste_audio_io_power_up_dmic12(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_dmic12(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_dmic12_query(struct device *dev);
+int ste_audio_io_set_dmic12_gain(enum AUDIOIO_CH_INDEX chnl_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_dmic12_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_dmic12(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_dmic12(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_dmic12_state(struct device *dev);
+int ste_audio_io_enable_fade_dmic12(struct device *dev);
+int ste_audio_io_disable_fade_dmic12(struct device *dev);
+int ste_audio_io_enable_loop_dmic12(enum AUDIOIO_CH_INDEX chnl_index,
+ enum AUDIOIO_HAL_HW_LOOPS,
+ int loop_gain, struct device *dev,
+ void *cookie);
+int ste_audio_io_disable_loop_dmic12(enum AUDIOIO_CH_INDEX chnl_index,
+ enum AUDIOIO_HAL_HW_LOOPS hw_loop,
+ struct device *dev, void *cookie);
+
+int ste_audio_io_power_up_dmic34(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_dmic34(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_dmic34_query(struct device *dev);
+int ste_audio_io_set_dmic34_gain(enum AUDIOIO_CH_INDEX chnl_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_dmic34_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_dmic34(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_dmic34(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_dmic34_state(struct device *dev);
+int ste_audio_io_enable_fade_dmic34(struct device *dev);
+int ste_audio_io_disable_fade_dmic34(struct device *dev);
+
+int ste_audio_io_power_up_dmic56(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_dmic56(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_dmic56_query(struct device *dev);
+int ste_audio_io_set_dmic56_gain(enum AUDIOIO_CH_INDEX chnl_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_dmic56_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_dmic56(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_dmic56(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_dmic56_state(struct device *dev);
+int ste_audio_io_enable_fade_dmic56(struct device *dev);
+int ste_audio_io_disable_fade_dmic56(struct device *dev);
+
+int ste_audio_io_power_up_fmrx(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_fmrx(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_fmrx_query(struct device *dev);
+int ste_audio_io_set_fmrx_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index,
+ int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_fmrx_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_fmrx(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_fmrx(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_fmrx_state(struct device *dev);
+int ste_audio_io_enable_fade_fmrx(struct device *dev);
+int ste_audio_io_disable_fade_fmrx(struct device *dev);
+
+int ste_audio_io_power_up_fmtx(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_fmtx(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_fmtx_query(struct device *dev);
+int ste_audio_io_set_fmtx_gain(enum AUDIOIO_CH_INDEX chnl_index, u16 gain_index,
+ int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_fmtx_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_fmtx(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_fmtx(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_fmtx_state(struct device *dev);
+int ste_audio_io_enable_fade_fmtx(struct device *dev);
+int ste_audio_io_disable_fade_fmtx(struct device *dev);
+
+int ste_audio_io_power_up_bluetooth(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_down_bluetooth(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_power_state_bluetooth_query(struct device *dev);
+int ste_audio_io_set_bluetooth_gain(enum AUDIOIO_CH_INDEX chnl_index,
+ u16 gain_index, int gain_value, u32 linear,
+ struct device *dev);
+int ste_audio_io_get_bluetooth_gain(int*, int*, u16,
+ struct device *dev);
+int ste_audio_io_mute_bluetooth(enum AUDIOIO_CH_INDEX chnl_index,
+ struct device *dev);
+int ste_audio_io_unmute_bluetooth(enum AUDIOIO_CH_INDEX chnl_index, int *gain,
+ struct device *dev);
+int ste_audio_io_mute_bluetooth_state(struct device *dev);
+int ste_audio_io_enable_fade_bluetooth(struct device *dev);
+int ste_audio_io_disable_fade_bluetooth(struct device *dev);
+
+
+#endif
+
diff --git a/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.c b/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.c
new file mode 100644
index 00000000000..3e9f54f2c6b
--- /dev/null
+++ b/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+
+#include <linux/types.h>
+#include "ste_audio_io_hwctrl_common.h"
+
+/* Number of channels for each transducer */
+const uint transducer_no_of_channels[MAX_NO_TRANSDUCERS] = {
+ 1, /* Earpiece */
+ 2, /* HS */
+ 2, /* IHF */
+ 1, /* VibL */
+ 1, /* VibR */
+ 1, /* Mic1A */
+ 1, /* Mic1B */
+ 1, /* Mic2 */
+ 2, /* LinIn */
+ 2, /* DMIC12 */
+ 2, /* DMIC34 */
+ 2, /* /DMIC56 */
+ 4 /* MultiMic */
+ };
+
+/* Maximum number of gains in each transducer path
+ (all channels of a specific transducer have same max no of gains) */
+const uint transducer_no_of_gains[MAX_NO_TRANSDUCERS] = {
+ 2, /* Ear g3 and g4 */
+ 3, /* HS g3 and g4 and analog */
+ 1, /* IHF g3 */
+ 1, /* VibL g3 */
+ 1, /* VibR g3 */
+ 2, /* Mic1A g1 and analog */
+ 2, /* Mic1A g1 and analog */
+ 2, /* Mic2 g1 and analog */
+ 2, /* LinIn g1 and analog */
+ 1, /* DMIC12 g1 */
+ 1, /* DMIC34 g1 */
+ 1, /* DMIC56 g1 */
+ 1 /* MultiMic g1 */
+ };
+
+const uint transducer_no_Of_supported_loop_indexes[MAX_NO_TRANSDUCERS] = {
+ 0x09,/* Ear0x01|0x08*/
+ 0x38770,/*{0x01|0x10|0x20|0x40|0x100*/
+ /*|0x200|0x400|0x8000|0x10000|0x20000}, HS*/
+ 0x86,/*IHF*/
+ 0x0,/*VibL*/
+ 0x0,/*VibR*/
+ 0x0,/*Mic1A*/
+ 0x01,/*Mic1B Sidetone is controlled*/
+ 0x0,/*Mic2*/
+ 0x0,/*LinIn*/
+ 0x0,/*DMIC12*/
+ 0x0,/*DMIC34*/
+ 0x0,/*DMIC56*/
+ 0x01,/*MultiMic Sidetone is controlled*/
+ 0x0,/*FMRx*/
+ 0x0/*FMTx*/
+ };
+
+const uint transducer_max_no_Of_supported_loops[MAX_NO_TRANSDUCERS] = {
+ 0,/*Ear Sidetone*/
+ 2,/*HS SideTone LININ_HS LININR_HSR LININ_HSL*/
+ 1,/*IHF TO BE DEFINED*/
+ 0,/*VibL TO BE DEFINED*/
+ 0,/*VibR TO BE DEFINED*/
+ 1,/*Mic1A TO BE DEFINED*/
+ 1,/*Mic1B SIDETONE TO BE DEFINED*/
+ 1,/*Mic2 TO BE DEFINED*/
+ 0, /* LinIn */
+ 1,/*DMIC12-ANC*/
+ 0,/*DMIC34-ANC*/
+ 0, /* DMIC56 */
+ 1,/*MultiMic-SIDETONE ANC*/
+ 0,/*FMRx*/
+ 0/*FMTx*/
+ };
+
+const uint max_no_of_loop_gains[MAX_NO_TRANSDUCERS] = {
+ 0,/*Earpiece*/
+ 2,/*HS*/
+ 0,
+ 0,
+ 0,
+ 0,
+ 2,/*Mic1B-Sidetone 2 gains*/
+ 0,
+ 0,
+ 2,/*DMIC12*/
+ 0,
+ 0,
+ 2,/*Multimic, Sidetone max no gains = 2*/
+ 0,
+ 0
+ };
+
+
+struct gain_descriptor_t gain_descriptor[MAX_NO_TRANSDUCERS]\
+ [MAX_NO_CHANNELS][MAX_NO_GAINS] = {
+ /* gainIndex=0 1 2
+ EDestinationEar */
+ {{{-63, 0, 1}, {-1, 8, 1}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */
+
+ /* EDestinationHS */
+ {{{-63, 0, 1}, {-1, 8, 1}, {-32, 4, 2} } , /* channelIndex=0 */
+ {{-63, 0, 1}, {-1, 8, 1}, {-32, 4, 2} } , /* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } , /* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } , /* channelIndex=3 */
+
+ /* EDestinationIHF */
+ {{{-63, 0, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{-63, 0, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */
+
+ /* EDestinationVibL */
+ {{{-63, 0, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */
+
+ /* EDestinationVibR */
+ {{{-63, 0, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */
+
+ /* ESourceMic1A */
+ {{{-32, 31, 1}, {0, 31, 1}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */
+
+ /* ESourceMic1B */
+ {{{-32, 31, 1}, {0, 31, 1}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */
+
+ /* ESourceMic2 */
+ {{{-32, 31, 1}, {0, 31, 1}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */
+
+ /* ESourceLin */
+ {{{-32, 31, 1}, {-10, 20, 2}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{-32, 31, 1}, {-10, 20, 2}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */
+
+ /* ESourceDMic12 */
+ {{{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */
+
+ /* ESourceDMic34 */
+ {{{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */
+
+ /* ESourceDMic56 */
+ {{{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=2 */
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0} } } ,/* channelIndex=3 */
+
+ /* ESourceMultiMic */
+ {{{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=0 */
+ {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } ,/* channelIndex=1 */
+ {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} },/* channelIndex=2 */
+ {{-32, 31, 1}, {0, 0, 0}, {0, 0, 0} } } /* channelIndex=3 */
+};
+
+
+const int hs_analog_gain_table[16] = {4, 2, 0, -2, -4, -6, -8, -10,
+ -12, -14, -16, -18, -20, -24, -28, -32};
+
+
+
diff --git a/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.h b/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.h
new file mode 100644
index 00000000000..cc2bfe21d81
--- /dev/null
+++ b/drivers/misc/audio_io_dev/ste_audio_io_hwctrl_common.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Deepak KARDA/ deepak.karda@stericsson.com for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#ifndef __AUDIOIO_HWCTRL_COMMON_H__
+#define __AUDIOIO_HWCTRL_COMMON_H__
+
+#include <linux/types.h>
+#include <mach/ste_audio_io_ioctl.h>
+/*
+ * Defines
+ */
+
+#define MAX_GAIN 100
+#define MIN_GAIN 0
+#define MAX_NO_CHANNELS 4
+#define MAX_NO_GAINS 3
+#define MAX_NO_LOOPS 1
+#define MAX_NO_LOOP_GAINS 1
+
+struct gain_descriptor_t {
+ int min_gain;
+ int max_gain;
+ uint gain_step;
+};
+
+
+/* Number of channels for each transducer */
+extern const uint transducer_no_of_channels[MAX_NO_TRANSDUCERS];
+
+/*
+ * Maximum number of gains in each transducer path
+ * all channels of a specific transducer have same max no of gains
+ */
+extern const uint transducer_no_of_gains[MAX_NO_TRANSDUCERS];
+
+/* Maximum number of supported loops for each transducer */
+extern const uint transducer_no_Of_supported_loop_indexes[MAX_NO_TRANSDUCERS];
+extern const uint transducer_max_no_Of_supported_loops[MAX_NO_TRANSDUCERS];
+extern const uint max_no_of_loop_gains[MAX_NO_TRANSDUCERS];
+extern const int hs_analog_gain_table[16] ;
+
+extern struct gain_descriptor_t gain_descriptor[MAX_NO_TRANSDUCERS]\
+ [MAX_NO_CHANNELS][MAX_NO_GAINS];
+
+#endif
+
+/* End of audio_io_hwctrl_common.h */
diff --git a/drivers/misc/bh1780gli.c b/drivers/misc/bh1780gli.c
new file mode 100644
index 00000000000..9d0b4b9ee94
--- /dev/null
+++ b/drivers/misc/bh1780gli.c
@@ -0,0 +1,306 @@
+/*
+ * bh1780gli.c
+ * ROHM Ambient Light Sensor Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#define BH1780_REG_CONTROL 0x80
+#define BH1780_REG_PARTID 0x8A
+#define BH1780_REG_MANFID 0x8B
+#define BH1780_REG_DLOW 0x8C
+#define BH1780_REG_DHIGH 0x8D
+
+#define BH1780_REVMASK (0xf)
+#define BH1780_POWMASK (0x3)
+#define BH1780_POFF (0x0)
+#define BH1780_PON (0x3)
+
+/* power on settling time in ms */
+#define BH1780_PON_DELAY 2
+
+struct bh1780_data {
+ struct i2c_client *client;
+ struct regulator *regulator;
+ int power_state;
+ /* lock for sysfs operations */
+ struct mutex lock;
+};
+
+static int bh1780_write(struct bh1780_data *ddata, u8 reg, u8 val, char *msg)
+{
+ int ret = i2c_smbus_write_byte_data(ddata->client, reg, val);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_write_byte_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
+}
+
+static int bh1780_read(struct bh1780_data *ddata, u8 reg, char *msg)
+{
+ int ret = i2c_smbus_read_byte_data(ddata->client, reg);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "i2c_smbus_read_byte_data failed error %d\
+ Register (%s)\n", ret, msg);
+ return ret;
+}
+
+static ssize_t bh1780_show_lux(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bh1780_data *ddata = platform_get_drvdata(pdev);
+ int lsb, msb;
+
+ if (ddata->power_state == BH1780_POFF)
+ return -EINVAL;
+
+ lsb = bh1780_read(ddata, BH1780_REG_DLOW, "DLOW");
+ if (lsb < 0)
+ return lsb;
+
+ msb = bh1780_read(ddata, BH1780_REG_DHIGH, "DHIGH");
+ if (msb < 0)
+ return msb;
+
+ return sprintf(buf, "%d\n", (msb << 8) | lsb);
+}
+
+static ssize_t bh1780_show_power_state(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bh1780_data *ddata = platform_get_drvdata(pdev);
+
+ /* we already maintain a sw state */
+ return sprintf(buf, "%d\n", ddata->power_state);
+}
+
+static ssize_t bh1780_store_power_state(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bh1780_data *ddata = platform_get_drvdata(pdev);
+ long val;
+ int error;
+
+ error = strict_strtoul(buf, 0, &val);
+ if (error)
+ return error;
+
+ if (val < BH1780_POFF || val > BH1780_PON)
+ return -EINVAL;
+
+ if (ddata->power_state == val)
+ return count;
+
+ mutex_lock(&ddata->lock);
+
+ if (ddata->power_state == BH1780_POFF)
+ regulator_enable(ddata->regulator);
+
+ error = bh1780_write(ddata, BH1780_REG_CONTROL, val, "CONTROL");
+ if (error < 0) {
+ mutex_unlock(&ddata->lock);
+ regulator_disable(ddata->regulator);
+ return error;
+ }
+
+ if (val == BH1780_POFF)
+ regulator_disable(ddata->regulator);
+
+ msleep(BH1780_PON_DELAY);
+ ddata->power_state = val;
+ mutex_unlock(&ddata->lock);
+
+ return count;
+}
+
+static DEVICE_ATTR(lux, S_IRUGO, bh1780_show_lux, NULL);
+
+static DEVICE_ATTR(power_state, S_IWUGO | S_IRUGO,
+ bh1780_show_power_state, bh1780_store_power_state);
+
+static struct attribute *bh1780_attributes[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_lux.attr,
+ NULL
+};
+
+static const struct attribute_group bh1780_attr_group = {
+ .attrs = bh1780_attributes,
+};
+
+static int __devinit bh1780_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct bh1780_data *ddata = NULL;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) {
+ ret = -EIO;
+ return ret;
+ }
+
+ ddata = kzalloc(sizeof(struct bh1780_data), GFP_KERNEL);
+ if (ddata == NULL) {
+ dev_err(&client->dev, "failed to alloc ddata\n");
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ ddata->client = client;
+ i2c_set_clientdata(client, ddata);
+ dev_set_name(&client->dev, "bh1780");
+
+ ddata->regulator = regulator_get(&client->dev, "v-als");
+ if (IS_ERR(ddata->regulator)) {
+ dev_err(&client->dev, "failed to get regulator\n");
+ ret = PTR_ERR(ddata->regulator);
+ goto free_ddata;
+ }
+
+ regulator_enable(ddata->regulator);
+
+ ret = bh1780_read(ddata, BH1780_REG_PARTID, "PART ID");
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to read part ID\n");
+ goto put_regulator;
+ }
+
+ regulator_disable(ddata->regulator);
+ ddata->power_state = BH1780_POFF;
+
+ dev_info(&client->dev, "Ambient Light Sensor, Rev : %d\n",
+ (ret & BH1780_REVMASK));
+
+ mutex_init(&ddata->lock);
+
+ ret = sysfs_create_group(&client->dev.kobj, &bh1780_attr_group);
+ if (ret)
+ goto put_regulator;
+
+ return 0;
+
+put_regulator:
+ regulator_disable(ddata->regulator);
+ regulator_put(ddata->regulator);
+free_ddata:
+ kfree(ddata);
+ return ret;
+}
+
+static int __devexit bh1780_remove(struct i2c_client *client)
+{
+ struct bh1780_data *ddata;
+
+ ddata = i2c_get_clientdata(client);
+ sysfs_remove_group(&client->dev.kobj, &bh1780_attr_group);
+ i2c_set_clientdata(client, NULL);
+ kfree(ddata);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bh1780_suspend(struct device *dev)
+{
+ struct bh1780_data *ddata = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (ddata->power_state == BH1780_POFF)
+ return 0;
+
+ ret = bh1780_write(ddata, BH1780_REG_CONTROL, BH1780_POFF,
+ "CONTROL");
+ if (ret < 0)
+ return ret;
+
+ regulator_disable(ddata->regulator);
+
+ return 0;
+}
+
+static int bh1780_resume(struct device *dev)
+{
+ struct bh1780_data *ddata = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (ddata->power_state == BH1780_POFF)
+ return 0;
+
+ regulator_enable(ddata->regulator);
+
+ ret = bh1780_write(ddata, BH1780_REG_CONTROL, ddata->power_state,
+ "CONTROL");
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct dev_pm_ops bh1780_dev_pm_ops = {
+ .suspend = bh1780_suspend,
+ .resume = bh1780_resume,
+};
+
+#endif /* CONFIG_PM */
+
+static const struct i2c_device_id bh1780_id[] = {
+ { "bh1780", 0 },
+ { },
+};
+
+static struct i2c_driver bh1780_driver = {
+ .probe = bh1780_probe,
+ .remove = bh1780_remove,
+ .id_table = bh1780_id,
+ .driver = {
+ .name = "bh1780",
+#ifdef CONFIG_PM
+ .pm = &bh1780_dev_pm_ops,
+#endif
+ },
+};
+
+static int __init bh1780_init(void)
+{
+ return i2c_add_driver(&bh1780_driver);
+}
+
+static void __exit bh1780_exit(void)
+{
+ i2c_del_driver(&bh1780_driver);
+}
+
+module_init(bh1780_init)
+module_exit(bh1780_exit)
+
+MODULE_DESCRIPTION("BH1780GLI Ambient Light Sensor Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
diff --git a/drivers/misc/db8500-modem-trace.c b/drivers/misc/db8500-modem-trace.c
new file mode 100644
index 00000000000..0d739fb4694
--- /dev/null
+++ b/drivers/misc/db8500-modem-trace.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors: Michel JAOUEN <michel.jaouen@stericsson.com>
+ * Maxime COQUELIN <maxime.coquelin-nonst@stericsson.com>
+ * for ST-Ericsson
+ * License terms: GNU General Public License (GPL), version 2
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include <linux/mman.h>
+#include <linux/db8500-modem-trace.h>
+
+#include <mach/hardware.h>
+
+#define DEVICE_NAME "db8500-modem-trace"
+
+/* activation of this flag triggers an initialization of 2 buffers
+ * 4kbytes , id 0xdeadbeef
+ * and 16Kbytes id 0xfadafada
+ * we assume that platform provides minimum 20Kbytes. */
+
+struct trace {
+ u32 start;
+ u32 end;
+ u32 mdm_base;
+ u32 ape_base;
+ void __iomem *area;
+ /* this spinlock to forbid concurrent access on the same trace buffer */
+ spinlock_t lock;
+ struct device *dev;
+ struct miscdevice misc_dev;
+};
+
+struct trace_modem {
+ u32 phys_addr;
+ u8 filler;
+};
+
+static struct trace *trace_priv;
+
+
+/* all this definition are linked to modem interface */
+#define MODEM_MARKER 0x88
+/* free marker is also written on filler */
+#define FREE_MARKER 0xa5
+#define FREE_MARKER_2 0xa5a5
+#define READ_MARKER 0x5a
+
+struct buffer_header {
+ u8 pattern;
+ u8 filler;
+ u16 head_size;
+};
+
+
+static int trace_read(unsigned long arg)
+{
+ struct modem_trace_req req;
+ struct buffer_header *pt;
+ char tmp_char;
+
+ if (copy_from_user(&req, (struct modem_trace_req *)arg,
+ sizeof(struct modem_trace_req)))
+ return -EFAULT;
+
+ /* compute Modem physical address to APE physical address range */
+ if (req.phys_addr < trace_priv->mdm_base) {
+ dev_err(trace_priv->dev, "MODEM ADDR uncorrect\n");
+ return -EINVAL;
+ }
+ req.phys_addr += trace_priv->ape_base - trace_priv->mdm_base;
+
+ /* check request is in the range and aligned */
+ if ((req.phys_addr % 4 != 0)
+ || (req.phys_addr < trace_priv->start)
+ || (req.phys_addr + req.size) >= trace_priv->end) {
+ dev_err(trace_priv->dev, "req out of range %x %x\n",
+ req.phys_addr, req.size);
+ return -EINVAL;
+ }
+
+ /* perform access to memory area */
+ pt = (struct buffer_header *)((u32)trace_priv->area +
+ req.phys_addr - trace_priv->start);
+
+ /* in case of several request coming on same trace buffer take a
+ * spinlock */
+ spin_lock(&trace_priv->lock);
+ if (pt->pattern != MODEM_MARKER) {
+ /* pattern and size not matching */
+ dev_err(trace_priv->dev, "req not matching filler %x/%x \
+ or/and pattern %x\n", req.filler, pt->filler,
+ pt->pattern);
+ spin_unlock(&trace_priv->lock);
+ return -EINVAL;
+ }
+ /* mark pattern as read and unlock spin */
+ pt->pattern = READ_MARKER;
+ spin_unlock(&trace_priv->lock);
+
+ req.size -= copy_to_user(req.buff, pt, req.size);
+
+ pt->pattern = FREE_MARKER;
+ pt->filler = FREE_MARKER;
+ tmp_char = MODEM_MARKER;
+
+ /* Update marker for trace tool */
+ if (copy_to_user(req.buff, &tmp_char, 1))
+ return -EFAULT;
+
+ /* Update effective written size */
+ if (copy_to_user((struct modem_trace_req *)arg, &req,
+ sizeof(struct modem_trace_req)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int trace_mmapdump(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long vma_start = vma->vm_start;
+
+ if (vma->vm_flags & VM_WRITE)
+ return -EPERM;
+
+ if ((vma->vm_end - vma->vm_start) <
+ (trace_priv->end - trace_priv->start))
+ return -EINVAL;
+ if (remap_pfn_range(vma,
+ vma_start,
+ trace_priv->start >> PAGE_SHIFT,
+ trace_priv->end - trace_priv->start,
+ vma->vm_page_prot))
+ return -EAGAIN;
+ return 0;
+}
+
+static long trace_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ long ret = 0;
+ void __user *argp = (void __user *)arg;
+ unsigned long size = trace_priv->end-trace_priv->start;
+
+ switch (cmd) {
+ case TM_GET_DUMPINFO:
+ ret = put_user(size, (unsigned long *)argp);
+ break;
+ case TM_TRACE_REQ:
+ ret = trace_read(arg);
+ break;
+
+ default:
+ ret = -EPERM;
+ break;
+ }
+ return ret;
+}
+
+static const struct file_operations trace_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = trace_ioctl,
+ .mmap = trace_mmapdump
+};
+
+static int trace_probe(struct platform_device *pdev)
+{
+ int rv = 0;
+ struct db8500_trace_platform_data *pdata = pdev->dev.platform_data;
+ /* retrieve area descriptor from platform device ressource */
+ struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if ((mem->start == 0) && (mem->end == 0)) {
+ rv = -EINVAL;
+ goto out;
+ }
+
+ if ((pdata->ape_base == 0) || (pdata->modem_base == 0)) {
+ rv = -EINVAL;
+ goto out;
+ }
+
+ trace_priv = kzalloc(sizeof(*trace_priv), GFP_ATOMIC);
+ if (!trace_priv) {
+ rv = -ENOMEM;
+ goto out;
+ }
+
+ trace_priv->dev = &pdev->dev;
+ trace_priv->misc_dev.minor = MISC_DYNAMIC_MINOR;
+ trace_priv->misc_dev.name = DEVICE_NAME;
+ trace_priv->misc_dev.fops = &trace_fops;
+ trace_priv->area = (void __iomem *)ioremap_nocache(mem->start,
+ mem->end - mem->start);
+ if (!trace_priv->area) {
+ rv = -ENOMEM;
+ goto outfree;
+ }
+
+ trace_priv->start = mem->start;
+ trace_priv->end = mem->end;
+
+ trace_priv->mdm_base = pdata->modem_base;
+ trace_priv->ape_base = pdata->ape_base;
+
+ /* spin allowing smp access for reading/writing trace buffer header */
+ spin_lock_init(&trace_priv->lock);
+
+ rv = misc_register(&trace_priv->misc_dev);
+ if (rv) {
+ dev_err(&pdev->dev, "can't misc_register\n");
+ goto outunmap;
+ }
+
+ return rv;
+
+outunmap:
+ iounmap(trace_priv->area);
+outfree:
+ kfree(trace_priv);
+out:
+ return rv;
+
+}
+
+static int trace_remove(struct platform_device *pdev)
+{
+ int rv = 0;
+
+ if (trace_priv) {
+ rv = misc_deregister(&trace_priv->misc_dev);
+ iounmap(trace_priv->area);
+ kfree(trace_priv);
+ }
+
+ return rv;
+}
+
+static struct platform_driver trace_driver = {
+ .probe = trace_probe,
+ .remove = trace_remove,
+ .driver = {
+ .name = "db8500-modem-trace",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int trace_init(void)
+{
+ platform_driver_register(&trace_driver);
+ return 0;
+}
+static void trace_exit(void)
+{
+ platform_driver_unregister(&trace_driver);
+}
+module_init(trace_init);
+module_exit(trace_exit);
+
+MODULE_AUTHOR("ST-Ericsson");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/dbx500-mloader.c b/drivers/misc/dbx500-mloader.c
new file mode 100644
index 00000000000..c3ec8b67983
--- /dev/null
+++ b/drivers/misc/dbx500-mloader.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Ludovic Barre <ludovic.barre@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include <linux/mman.h>
+#include <linux/io.h>
+
+#include <mach/mloader-dbx500.h>
+#include <linux/mloader.h>
+
+#define DEVICE_NAME "dbx500_mloader_fw"
+
+struct mloader_priv {
+ struct platform_device *pdev;
+ struct dbx500_mloader_pdata *pdata;
+ struct miscdevice misc_dev;
+ u32 aeras_size;
+};
+
+static struct mloader_priv *mloader_priv;
+
+static int mloader_fw_send(struct dbx500_ml_fw *fw_info)
+{
+ const struct firmware *fw;
+ unsigned long size;
+ unsigned long phys_start;
+ void *fw_data;
+ void *vaddr;
+ void __iomem *ioaddr;
+ int ret;
+
+ ret = request_firmware(&fw, fw_info->name, &mloader_priv->pdev->dev);
+ if (ret) {
+ dev_err(&mloader_priv->pdev->dev, "request firmware failed\n");
+ goto out;
+ }
+
+ if (fw->size > (fw_info->area->size - fw_info->offset)) {
+ dev_err(&mloader_priv->pdev->dev,
+ "fw:%s is too big for:%s\n",
+ fw_info->name, fw_info->area->name);
+ ret = -EINVAL;
+ goto err_fw;
+ }
+
+ size = PAGE_ALIGN(fw->size);
+ phys_start = fw_info->area->start + fw_info->offset;
+ phys_start &= PAGE_MASK;
+ ioaddr = ioremap(phys_start, size);
+ if (!ioaddr) {
+ dev_err(&mloader_priv->pdev->dev,
+ "failed remap memory region.\n");
+ ret = -EINVAL;
+ goto err_fw;
+ }
+
+ vaddr = ioaddr + (fw_info->offset & ~PAGE_MASK);
+ fw_data = (void *)fw->data;
+ memcpy_toio(vaddr, fw_data, fw->size);
+ iounmap(ioaddr);
+
+err_fw:
+ release_firmware(fw);
+out:
+ return ret;
+}
+
+static int mloader_fw_upload(void)
+{
+ int i, ret;
+ struct dbx500_mloader_pdata *pdata = mloader_priv->pdata;
+
+ for (i = 0; i < pdata->nr_fws; i++) {
+ ret = mloader_fw_send(&pdata->fws[i]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_err(&mloader_priv->pdev->dev,
+ "Failed to upload %s firmware", pdata->fws[i].name);
+ return ret;
+}
+
+static int mloader_fw_mmapdump(struct file *file, struct vm_area_struct *vma)
+{
+ int i;
+ unsigned long dump_size = 0;
+ unsigned long vma_start = vma->vm_start;
+
+ if (vma->vm_flags & VM_WRITE)
+ return -EPERM;
+
+ for (i = 0 ; i < mloader_priv->pdata->nr_areas ; i++)
+ dump_size += mloader_priv->pdata->areas[i].size;
+
+ if ((vma->vm_end - vma->vm_start) < dump_size)
+ return -EINVAL;
+
+ for (i = 0 ; i < mloader_priv->pdata->nr_areas ; i++) {
+ if (remap_pfn_range(vma,
+ vma_start,
+ mloader_priv->pdata->areas[i].start >> PAGE_SHIFT,
+ mloader_priv->pdata->areas[i].size,
+ vma->vm_page_prot))
+ return -EAGAIN;
+ vma_start += mloader_priv->pdata->areas[i].size;
+ }
+ return 0;
+}
+
+static void mloader_fw_dumpinfo(struct dump_image *images)
+{
+ u32 offset = 0;
+ int i;
+
+ for (i = 0 ; i < mloader_priv->pdata->nr_areas ; i++) {
+ strncpy(images[i].name,
+ mloader_priv->pdata->areas[i].name, MAX_NAME);
+ images[i].name[MAX_NAME-1] = 0;
+ images[i].offset = offset;
+ images[i].size = mloader_priv->pdata->areas[i].size;
+ offset += mloader_priv->pdata->areas[i].size;
+ }
+}
+
+static long mloader_fw_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ long ret = 0;
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case ML_UPLOAD:
+ ret = mloader_fw_upload();
+ break;
+ case ML_GET_NBIMAGES:
+ ret = put_user(mloader_priv->pdata->nr_areas,
+ (unsigned long __user *)argp);
+ break;
+ case ML_GET_DUMPINFO: {
+ struct dump_image *dump_images;
+ dump_images = kzalloc(mloader_priv->pdata->nr_areas
+ * sizeof(struct dump_image), GFP_ATOMIC);
+ mloader_fw_dumpinfo(dump_images);
+ ret = copy_to_user(argp, (void *) dump_images,
+ mloader_priv->pdata->nr_areas
+ * sizeof(struct dump_image)) ? -EFAULT : 0;
+ kfree(dump_images);
+ break;
+ }
+ default:
+ ret = -EPERM;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct file_operations modem_fw_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = mloader_fw_ioctl,
+ .mmap = mloader_fw_mmapdump,
+};
+
+static int __devinit mloader_fw_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int i;
+
+ mloader_priv = kzalloc(sizeof(*mloader_priv), GFP_ATOMIC);
+ if (!mloader_priv) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mloader_priv->pdev = pdev;
+ mloader_priv->pdata = pdev->dev.platform_data;
+
+ mloader_priv->misc_dev.minor = MISC_DYNAMIC_MINOR;
+ mloader_priv->misc_dev.name = DEVICE_NAME;
+ mloader_priv->misc_dev.fops = &modem_fw_fops;
+ ret = misc_register(&mloader_priv->misc_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't misc_register\n");
+ goto err_free_priv;
+ }
+
+ dev_info(&mloader_priv->pdev->dev, "mloader device register\n");
+
+ for (i = 0 ; i < mloader_priv->pdata->nr_areas ; i++) {
+ dev_dbg(&mloader_priv->pdev->dev,
+ "Area:%d (name:%s start:%x size:%x)\n",
+ i, mloader_priv->pdata->areas[i].name,
+ mloader_priv->pdata->areas[i].start,
+ mloader_priv->pdata->areas[i].size);
+ }
+
+ for (i = 0 ; i < mloader_priv->pdata->nr_fws ; i++) {
+ dev_dbg(&mloader_priv->pdev->dev,
+ "Firmware:%d (name:%s offset:%x "
+ "area_name:%s area_start:%x area_size:%x)\n",
+ i, mloader_priv->pdata->fws[i].name,
+ mloader_priv->pdata->fws[i].offset,
+ mloader_priv->pdata->fws[i].area->name,
+ mloader_priv->pdata->fws[i].area->start,
+ mloader_priv->pdata->fws[i].area->size);
+ }
+
+ return ret;
+
+err_free_priv:
+ kfree(mloader_priv);
+out:
+ return ret;
+}
+
+static int __devexit mloader_fw_remove(struct platform_device *pdev)
+{
+ int err;
+
+ err = misc_register(&mloader_priv->misc_dev);
+ if (err < 0)
+ dev_err(&pdev->dev, "can't misc_deregister, %d\n", err);
+
+ kfree(mloader_priv);
+
+ return err;
+}
+
+static struct platform_driver mloader_fw_driver = {
+ .driver.name = DEVICE_NAME,
+ .driver.owner = THIS_MODULE,
+ .probe = mloader_fw_probe,
+ .remove = __devexit_p(mloader_fw_remove),
+};
+
+static int __init mloader_fw_init(void)
+{
+ return platform_driver_register(&mloader_fw_driver);
+}
+
+static void __exit mloader_fw_exit(void)
+{
+ kfree(mloader_priv);
+ platform_driver_unregister(&mloader_fw_driver);
+}
+
+module_init(mloader_fw_init);
+module_exit(mloader_fw_exit);
+MODULE_DESCRIPTION("ST-Ericsson modem loader firmware");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ludovic Barre <ludovic.barre@stericsson.com>");
diff --git a/drivers/misc/dispdev/Makefile b/drivers/misc/dispdev/Makefile
new file mode 100644
index 00000000000..11dc7611d26
--- /dev/null
+++ b/drivers/misc/dispdev/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DISPDEV) += dispdev.o
diff --git a/drivers/misc/dispdev/dispdev.c b/drivers/misc/dispdev/dispdev.c
new file mode 100644
index 00000000000..969bb2a1bc1
--- /dev/null
+++ b/drivers/misc/dispdev/dispdev.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Display output device driver
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+
+#include <linux/dispdev.h>
+#include <linux/hwmem.h>
+#include <video/mcde_dss.h>
+
+#define MAX_BUFFERS 4
+
+static LIST_HEAD(dev_list);
+static DEFINE_MUTEX(dev_list_lock);
+
+enum buffer_state {
+ BUF_UNUSED = 0,
+ BUF_QUEUED,
+ BUF_ACTIVATED,
+/*TODO:waitfordone BUF_DEACTIVATED,*/
+ BUF_FREE,
+ BUF_DEQUEUED,
+};
+
+struct dispdev_buffer {
+ struct hwmem_alloc *alloc;
+ u32 size;
+ enum buffer_state state;
+ u32 paddr; /* if pinned */
+};
+
+struct dispdev {
+ bool open;
+ struct mutex lock;
+ struct miscdevice mdev;
+ struct list_head list;
+ struct mcde_display_device *ddev;
+ struct mcde_overlay *ovly;
+ struct dispdev_config config;
+ bool overlay;
+ struct dispdev_buffer buffers[MAX_BUFFERS];
+ wait_queue_head_t waitq_dq;
+};
+
+int dispdev_open(struct inode *inode, struct file *file)
+{
+ int ret;
+ struct dispdev *dd = NULL;
+
+ mutex_lock(&dev_list_lock);
+ list_for_each_entry(dd, &dev_list, list)
+ if (dd->mdev.minor == iminor(inode))
+ break;
+
+ if (&dd->list == &dev_list) {
+ mutex_unlock(&dev_list_lock);
+ return -ENODEV;
+ }
+
+ if (dd->open) {
+ mutex_unlock(&dev_list_lock);
+ return -EBUSY;
+ }
+
+ dd->open = true;
+
+ mutex_unlock(&dev_list_lock);
+
+ ret = mcde_dss_enable_overlay(dd->ovly);
+ if (ret)
+ return ret;
+
+ file->private_data = dd;
+
+ return 0;
+}
+
+int dispdev_release(struct inode *inode, struct file *file)
+{
+ int i;
+ struct dispdev *dd = NULL;
+
+ mutex_lock(&dev_list_lock);
+ list_for_each_entry(dd, &dev_list, list)
+ if (dd->mdev.minor == iminor(inode))
+ break;
+ mutex_unlock(&dev_list_lock);
+
+ if (&dd->list == &dev_list)
+ return -ENODEV;
+
+ /* TODO: Make sure it waits for completion */
+ mcde_dss_disable_overlay(dd->ovly);
+ for (i = 0; i < MAX_BUFFERS; i++) {
+ if (dd->buffers[i].paddr)
+ hwmem_unpin(dd->buffers[i].alloc);
+ if (dd->buffers[i].alloc)
+ hwmem_release(dd->buffers[i].alloc);
+ dd->buffers[i].alloc = NULL;
+ dd->buffers[i].state = BUF_UNUSED;
+ dd->buffers[i].size = 0;
+ dd->buffers[i].paddr = 0;
+ }
+ dd->open = false;
+ wake_up(&dd->waitq_dq);
+
+ return 0;
+}
+
+static enum mcde_ovly_pix_fmt get_ovly_fmt(enum dispdev_fmt fmt)
+{
+ switch (fmt) {
+ default:
+ case DISPDEV_FMT_RGB565:
+ return MCDE_OVLYPIXFMT_RGB565;
+ case DISPDEV_FMT_RGB888:
+ return MCDE_OVLYPIXFMT_RGB888;
+ case DISPDEV_FMT_RGBA8888:
+ return MCDE_OVLYPIXFMT_RGBA8888;
+ case DISPDEV_FMT_RGBX8888:
+ return MCDE_OVLYPIXFMT_RGBX8888;
+ case DISPDEV_FMT_YUV422:
+ return MCDE_OVLYPIXFMT_YCbCr422;
+ }
+}
+
+static void get_ovly_info(struct dispdev_config *cfg,
+ struct mcde_overlay_info *info, bool overlay)
+{
+ info->paddr = 0;
+ info->stride = cfg->stride;
+ info->fmt = get_ovly_fmt(cfg->format);
+ info->src_x = 0;
+ info->src_y = 0;
+ info->dst_x = cfg->x;
+ info->dst_y = cfg->y;
+ info->dst_z = cfg->z;
+ info->w = cfg->width;
+ info->h = cfg->height;
+ info->dirty.x = 0;
+ info->dirty.y = 0;
+ info->dirty.w = cfg->width;
+ info->dirty.h = cfg->height;
+}
+
+static int dispdev_set_config(struct dispdev *dd, struct dispdev_config *cfg)
+{
+ int ret;
+ struct mcde_overlay_info info;
+
+ if (memcmp(&dd->config, cfg, sizeof(struct dispdev_config)) == 0)
+ return 0;
+
+ /* Allow change of z,x and y */
+
+ get_ovly_info(cfg, &info, dd->overlay);
+ ret = mcde_dss_apply_overlay(dd->ovly, &info);
+ if (ret)
+ return ret;
+
+ dd->config = *cfg;
+
+ return 0;
+}
+
+static int find_buf(struct dispdev *dd, enum buffer_state state)
+{
+ int i;
+ for (i = 0; i < MAX_BUFFERS; i++)
+ if (dd->buffers[i].state == state)
+ return i;
+ return -1;
+}
+
+static int dispdev_register_buffer(struct dispdev *dd, s32 hwmem_name)
+{
+ int ret;
+ struct dispdev_buffer *buf;
+ enum hwmem_mem_type memtype;
+ enum hwmem_access access;
+
+ ret = find_buf(dd, BUF_UNUSED);
+ if (ret < 0)
+ return -ENOMEM;
+ buf = &dd->buffers[ret];
+ buf->alloc = hwmem_resolve_by_name(hwmem_name);
+ if (IS_ERR(buf->alloc)) {
+ ret = PTR_ERR(buf->alloc);
+ goto resolve_failed;
+ }
+
+ hwmem_get_info(buf->alloc, &buf->size, &memtype, &access);
+
+ if (!(access & HWMEM_ACCESS_READ) ||
+ memtype != HWMEM_MEM_CONTIGUOUS_SYS) {
+ ret = -EACCES;
+ goto invalid_mem;
+ }
+
+ buf->state = BUF_FREE;
+ goto out;
+invalid_mem:
+ hwmem_release(buf->alloc);
+resolve_failed:
+out:
+ return ret;
+}
+
+static int dispdev_unregister_buffer(struct dispdev *dd, u32 buf_idx)
+{
+ struct dispdev_buffer *buf = &dd->buffers[buf_idx];
+
+ if (buf_idx >= ARRAY_SIZE(dd->buffers))
+ return -EINVAL;
+
+ if (buf->state == BUF_UNUSED)
+ return -EINVAL;
+
+ if (buf->state == BUF_ACTIVATED) {
+ struct mcde_overlay_info info;
+ /* TODO Wait for frame done */
+ get_ovly_info(&dd->config, &info, dd->overlay);
+ mcde_dss_apply_overlay(dd->ovly, &info);
+ mcde_dss_update_overlay(dd->ovly, false);
+ hwmem_unpin(dd->buffers[buf_idx].alloc);
+ }
+
+ hwmem_release(buf->alloc);
+ buf->state = BUF_UNUSED;
+ buf->alloc = NULL;
+ buf->size = 0;
+
+ return 0;
+}
+
+static int dispdev_queue_buffer(struct dispdev *dd, u32 buf_idx)
+{
+ int ret, i;
+ struct mcde_overlay_info info;
+ struct hwmem_mem_chunk mem_chunk;
+ size_t mem_chunk_length = 1;
+ struct hwmem_region rgn = { .offset = 0, .count = 1, .start = 0 };
+ struct hwmem_alloc *alloc;
+
+ if (buf_idx >= ARRAY_SIZE(dd->buffers) ||
+ dd->buffers[buf_idx].state != BUF_DEQUEUED)
+ return -EINVAL;
+
+ alloc = dd->buffers[buf_idx].alloc;
+ get_ovly_info(&dd->config, &info, dd->overlay);
+ ret = hwmem_pin(alloc, &mem_chunk, &mem_chunk_length);
+ if (ret) {
+ dev_warn(dd->mdev.this_device, "Pin failed, %d\n", ret);
+ return -EINVAL;
+ }
+
+ rgn.size = rgn.end = dd->buffers[buf_idx].size;
+ ret = hwmem_set_domain(alloc, HWMEM_ACCESS_READ,
+ HWMEM_DOMAIN_SYNC, &rgn);
+ if (ret)
+ dev_warn(dd->mdev.this_device, "Set domain failed, %d\n", ret);
+
+ i = find_buf(dd, BUF_ACTIVATED);
+ if (i >= 0) {
+ /* TODO Wait for frame done */
+ dd->buffers[i].state = BUF_FREE;
+ wake_up(&dd->waitq_dq);
+ }
+
+ info.paddr = mem_chunk.paddr;
+ mcde_dss_apply_overlay(dd->ovly, &info);
+ mcde_dss_update_overlay(dd->ovly, false);
+
+ dd->buffers[buf_idx].state = BUF_ACTIVATED;
+
+ return 0;
+}
+
+static int dispdev_dequeue_buffer(struct dispdev *dd)
+{
+ int i;
+
+ i = find_buf(dd, BUF_FREE);
+ if (i < 0) {
+ if (find_buf(dd, BUF_ACTIVATED) < 0)
+ return -EINVAL;
+
+ wait_event(dd->waitq_dq, (i = find_buf(dd, BUF_FREE)) >= 0);
+ }
+ hwmem_unpin(dd->buffers[i].alloc);
+ dd->buffers[i].state = BUF_DEQUEUED;
+
+ return i;
+}
+
+int dispdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+ struct dispdev *dd = (struct dispdev *)file->private_data;
+
+ mutex_lock(&dd->lock);
+
+ switch (cmd) {
+ case DISPDEV_SET_CONFIG_IOC:
+ {
+ struct dispdev_config cfg;
+ if (copy_from_user(&cfg, (void __user *)arg,
+ sizeof(cfg)))
+ ret = -EFAULT;
+ else
+ ret = dispdev_set_config(dd, &cfg);
+ }
+ break;
+ case DISPDEV_GET_CONFIG_IOC:
+ ret = copy_to_user((void __user *)arg, &dd->config,
+ sizeof(dd->config));
+ if (ret)
+ ret = -EFAULT;
+ break;
+ case DISPDEV_REGISTER_BUFFER_IOC:
+ ret = dispdev_register_buffer(dd, (s32)arg);
+ break;
+ case DISPDEV_UNREGISTER_BUFFER_IOC:
+ ret = dispdev_unregister_buffer(dd, (u32)arg);
+ break;
+ case DISPDEV_QUEUE_BUFFER_IOC:
+ ret = dispdev_queue_buffer(dd, (u32)arg);
+ break;
+ case DISPDEV_DEQUEUE_BUFFER_IOC:
+ ret = dispdev_dequeue_buffer(dd);
+ break;
+ default:
+ ret = -ENOSYS;
+ }
+
+ mutex_unlock(&dd->lock);
+
+ return ret;
+}
+
+static const struct file_operations dispdev_fops = {
+ .open = dispdev_open,
+ .release = dispdev_release,
+ .ioctl = dispdev_ioctl,
+};
+
+static void init_dispdev(struct dispdev *dd, struct mcde_display_device *ddev,
+ const char *name, bool overlay)
+{
+ u16 w, h;
+ int rotation;
+
+ mutex_init(&dd->lock);
+ INIT_LIST_HEAD(&dd->list);
+ dd->ddev = ddev;
+ dd->overlay = overlay;
+ mcde_dss_get_native_resolution(ddev, &w, &h);
+ rotation = mcde_dss_get_rotation(ddev);
+
+ if ((rotation == MCDE_DISPLAY_ROT_90_CCW) ||
+ (rotation == MCDE_DISPLAY_ROT_90_CW)) {
+ dd->config.width = h;
+ dd->config.height = w;
+ } else {
+ dd->config.width = w;
+ dd->config.height = h;
+ }
+ dd->config.format = DISPDEV_FMT_RGB565;
+ dd->config.stride = sizeof(u16) * w;
+ dd->config.z = 0;
+ init_waitqueue_head(&dd->waitq_dq);
+ dd->mdev.minor = MISC_DYNAMIC_MINOR;
+ dd->mdev.name = name;
+ dd->mdev.fops = &dispdev_fops;
+ pr_info("%s: name=%s w=%d, h=%d, fmt=%d, stride=%d\n", __func__, name,
+ dd->config.width, dd->config.height, dd->config.format,
+ dd->config.stride);
+}
+
+int dispdev_create(struct mcde_display_device *ddev, bool overlay)
+{
+ int ret = 0;
+ struct dispdev *dd;
+ struct mcde_video_mode vmode;
+ struct mcde_overlay_info info;
+
+ static int counter;
+ char *name = "dispdev0";
+
+ dd = kzalloc(sizeof(struct dispdev), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ sprintf(name, "%s%d", DISPDEV_DEFAULT_DEVICE_PREFIX, counter++);
+ init_dispdev(dd, ddev, name, overlay);
+
+ get_ovly_info(&dd->config, &info, overlay);
+
+ if (!overlay) {
+ ret = mcde_dss_enable_display(ddev);
+ if (ret)
+ goto fail_enable_display;
+ mcde_dss_get_video_mode(ddev, &vmode);
+ mcde_dss_try_video_mode(ddev, &vmode);
+ ret = mcde_dss_set_video_mode(ddev, &vmode);
+ if (ret)
+ goto fail_set_video_mode;
+ mcde_dss_set_pixel_format(ddev, info.fmt);
+ mcde_dss_apply_channel(ddev);
+ }
+
+ dd->ovly = mcde_dss_create_overlay(ddev, &info);
+ if (!dd->ovly) {
+ ret = -ENOMEM;
+ goto fail_create_ovly;
+ }
+
+ ret = misc_register(&dd->mdev);
+ if (ret)
+ goto fail_register_misc;
+ mutex_lock(&dev_list_lock);
+ list_add_tail(&dd->list, &dev_list);
+ mutex_unlock(&dev_list_lock);
+
+ goto out;
+
+fail_register_misc:
+ mcde_dss_destroy_overlay(dd->ovly);
+fail_create_ovly:
+ if (!overlay)
+ mcde_dss_disable_display(ddev);
+fail_set_video_mode:
+fail_enable_display:
+ kfree(dd);
+out:
+ return ret;
+}
+
+void dispdev_destroy(struct mcde_display_device *ddev)
+{
+ struct dispdev *dd;
+ struct dispdev *tmp;
+
+ mutex_lock(&dev_list_lock);
+ list_for_each_entry_safe(dd, tmp, &dev_list, list) {
+ if (dd->ddev == ddev) {
+ list_del(&dd->list);
+ misc_deregister(&dd->mdev);
+ mcde_dss_destroy_overlay(dd->ovly);
+ /*
+ * TODO: Uncomment when DSS has reference
+ * counting of enable/disable
+ */
+ /* mcde_dss_disable_display(dd->ddev); */
+ kfree(dd);
+ break;
+ }
+ }
+ mutex_unlock(&dev_list_lock);
+}
+
+static void dispdev_destroy_all(void)
+{
+ struct dispdev *dd;
+ struct dispdev *tmp;
+
+ mutex_lock(&dev_list_lock);
+ list_for_each_entry_safe(dd, tmp, &dev_list, list) {
+ list_del(&dd->list);
+ misc_deregister(&dd->mdev);
+ mcde_dss_destroy_overlay(dd->ovly);
+ /*
+ * TODO: Uncomment when DSS has reference
+ * counting of enable/disable
+ */
+ /* mcde_dss_disable_display(dd->ddev); */
+ kfree(dd);
+ }
+ mutex_unlock(&dev_list_lock);
+
+ mutex_destroy(&dev_list_lock);
+}
+
+static int __init dispdev_init(void)
+{
+ pr_info("%s\n", __func__);
+
+ mutex_init(&dev_list_lock);
+
+ return 0;
+}
+module_init(dispdev_init);
+
+static void __exit dispdev_exit(void)
+{
+ dispdev_destroy_all();
+ pr_info("%s\n", __func__);
+}
+module_exit(dispdev_exit);
+
+MODULE_AUTHOR("Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Display output device driver");
+
diff --git a/drivers/misc/hwmem/Makefile b/drivers/misc/hwmem/Makefile
new file mode 100644
index 00000000000..c307616a181
--- /dev/null
+++ b/drivers/misc/hwmem/Makefile
@@ -0,0 +1,3 @@
+hwmem-objs := hwmem-main.o hwmem-ioctl.o cache_handler.o contig_alloc.o
+
+obj-$(CONFIG_HWMEM) += hwmem.o
diff --git a/drivers/misc/hwmem/cache_handler.c b/drivers/misc/hwmem/cache_handler.c
new file mode 100644
index 00000000000..e0ab4ee6cf8
--- /dev/null
+++ b/drivers/misc/hwmem/cache_handler.c
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Cache handler
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/hwmem.h>
+
+#include <asm/pgtable.h>
+
+#include <mach/dcache.h>
+
+#include "cache_handler.h"
+
+#define U32_MAX (~(u32)0)
+
+enum hwmem_alloc_flags cachi_get_cache_settings(
+ enum hwmem_alloc_flags requested_cache_settings);
+void cachi_set_pgprot_cache_options(enum hwmem_alloc_flags cache_settings,
+ pgprot_t *pgprot);
+
+static void sync_buf_pre_cpu(struct cach_buf *buf, enum hwmem_access access,
+ struct hwmem_region *region);
+static void sync_buf_post_cpu(struct cach_buf *buf,
+ enum hwmem_access next_access, struct hwmem_region *next_region);
+
+static void invalidate_cpu_cache(struct cach_buf *buf,
+ struct cach_range *range_2b_used);
+static void clean_cpu_cache(struct cach_buf *buf,
+ struct cach_range *range_2b_used);
+static void flush_cpu_cache(struct cach_buf *buf,
+ struct cach_range *range_2b_used);
+
+static void null_range(struct cach_range *range);
+static void expand_range(struct cach_range *range,
+ struct cach_range *range_2_add);
+/*
+ * Expands range to one of enclosing_range's two edges. The function will
+ * choose which of enclosing_range's edges to expand range to in such a
+ * way that the size of range is minimized. range must be located inside
+ * enclosing_range.
+ */
+static void expand_range_2_edge(struct cach_range *range,
+ struct cach_range *enclosing_range);
+static void shrink_range(struct cach_range *range,
+ struct cach_range *range_2_remove);
+static bool is_non_empty_range(struct cach_range *range);
+static void intersect_range(struct cach_range *range_1,
+ struct cach_range *range_2, struct cach_range *intersection);
+/* Align_up restrictions apply here to */
+static void align_range_up(struct cach_range *range, u32 alignment);
+static u32 range_length(struct cach_range *range);
+static void region_2_range(struct hwmem_region *region, u32 buffer_size,
+ struct cach_range *range);
+
+static void *offset_2_vaddr(struct cach_buf *buf, u32 offset);
+static u32 offset_2_paddr(struct cach_buf *buf, u32 offset);
+
+/* Saturates, might return unaligned values when that happens */
+static u32 align_up(u32 value, u32 alignment);
+static u32 align_down(u32 value, u32 alignment);
+
+/*
+ * Exported functions
+ */
+
+void cach_init_buf(struct cach_buf *buf, enum hwmem_alloc_flags cache_settings,
+ u32 size)
+{
+ buf->vstart = NULL;
+ buf->pstart = 0;
+ buf->size = size;
+
+ buf->cache_settings = cachi_get_cache_settings(cache_settings);
+}
+
+void cach_set_buf_addrs(struct cach_buf *buf, void* vaddr, u32 paddr)
+{
+ bool tmp;
+
+ buf->vstart = vaddr;
+ buf->pstart = paddr;
+
+ if (buf->cache_settings & HWMEM_ALLOC_HINT_CACHED) {
+ /*
+ * Keep whatever is in the cache. This way we avoid an
+ * unnecessary synch if CPU is the first user.
+ */
+ buf->range_in_cpu_cache.start = 0;
+ buf->range_in_cpu_cache.end = buf->size;
+ align_range_up(&buf->range_in_cpu_cache,
+ get_dcache_granularity());
+ buf->range_dirty_in_cpu_cache.start = 0;
+ buf->range_dirty_in_cpu_cache.end = buf->size;
+ align_range_up(&buf->range_dirty_in_cpu_cache,
+ get_dcache_granularity());
+ } else {
+ flush_cpu_dcache(buf->vstart, buf->pstart, buf->size, false,
+ &tmp);
+ drain_cpu_write_buf();
+
+ null_range(&buf->range_in_cpu_cache);
+ null_range(&buf->range_dirty_in_cpu_cache);
+ }
+ null_range(&buf->range_invalid_in_cpu_cache);
+}
+
+void cach_set_pgprot_cache_options(struct cach_buf *buf, pgprot_t *pgprot)
+{
+ cachi_set_pgprot_cache_options(buf->cache_settings, pgprot);
+}
+
+void cach_set_domain(struct cach_buf *buf, enum hwmem_access access,
+ enum hwmem_domain domain, struct hwmem_region *region)
+{
+ struct hwmem_region *__region;
+ struct hwmem_region full_region;
+
+ if (region != NULL) {
+ __region = region;
+ } else {
+ full_region.offset = 0;
+ full_region.count = 1;
+ full_region.start = 0;
+ full_region.end = buf->size;
+ full_region.size = buf->size;
+
+ __region = &full_region;
+ }
+
+ switch (domain) {
+ case HWMEM_DOMAIN_SYNC:
+ sync_buf_post_cpu(buf, access, __region);
+
+ break;
+
+ case HWMEM_DOMAIN_CPU:
+ sync_buf_pre_cpu(buf, access, __region);
+
+ break;
+ }
+}
+
+/*
+ * Local functions
+ */
+
+enum hwmem_alloc_flags __attribute__((weak)) cachi_get_cache_settings(
+ enum hwmem_alloc_flags requested_cache_settings)
+{
+ static const u32 CACHE_ON_FLAGS_MASK = HWMEM_ALLOC_HINT_CACHED |
+ HWMEM_ALLOC_HINT_CACHE_WB | HWMEM_ALLOC_HINT_CACHE_WT |
+ HWMEM_ALLOC_HINT_CACHE_NAOW | HWMEM_ALLOC_HINT_CACHE_AOW |
+ HWMEM_ALLOC_HINT_INNER_AND_OUTER_CACHE |
+ HWMEM_ALLOC_HINT_INNER_CACHE_ONLY;
+ /* We don't know the cache setting so we assume worst case. */
+ static const u32 CACHE_SETTING = HWMEM_ALLOC_HINT_WRITE_COMBINE |
+ HWMEM_ALLOC_HINT_CACHED | HWMEM_ALLOC_HINT_CACHE_WB |
+ HWMEM_ALLOC_HINT_CACHE_AOW |
+ HWMEM_ALLOC_HINT_INNER_AND_OUTER_CACHE;
+
+ if (requested_cache_settings & CACHE_ON_FLAGS_MASK)
+ return CACHE_SETTING;
+ else if (requested_cache_settings & HWMEM_ALLOC_HINT_WRITE_COMBINE ||
+ (requested_cache_settings & HWMEM_ALLOC_HINT_UNCACHED &&
+ !(requested_cache_settings &
+ HWMEM_ALLOC_HINT_NO_WRITE_COMBINE)))
+ return HWMEM_ALLOC_HINT_WRITE_COMBINE;
+ else if (requested_cache_settings &
+ (HWMEM_ALLOC_HINT_NO_WRITE_COMBINE |
+ HWMEM_ALLOC_HINT_UNCACHED))
+ return 0;
+ else
+ /* Nothing specified, use cached */
+ return CACHE_SETTING;
+}
+
+void __attribute__((weak)) cachi_set_pgprot_cache_options(
+ enum hwmem_alloc_flags cache_settings, pgprot_t *pgprot)
+{
+ if (cache_settings & HWMEM_ALLOC_HINT_CACHED)
+ *pgprot = *pgprot; /* To silence compiler and checkpatch */
+ else if (cache_settings & HWMEM_ALLOC_HINT_WRITE_COMBINE)
+ *pgprot = pgprot_writecombine(*pgprot);
+ else
+ *pgprot = pgprot_noncached(*pgprot);
+}
+
+bool __attribute__((weak)) speculative_data_prefetch(void)
+{
+ /* We don't know so we go with the safe alternative */
+ return true;
+}
+
+static void sync_buf_pre_cpu(struct cach_buf *buf, enum hwmem_access access,
+ struct hwmem_region *region)
+{
+ bool write = access & HWMEM_ACCESS_WRITE;
+ bool read = access & HWMEM_ACCESS_READ;
+
+ if (!write && !read)
+ return;
+
+ if (buf->cache_settings & HWMEM_ALLOC_HINT_CACHED) {
+ struct cach_range region_range;
+
+ region_2_range(region, buf->size, &region_range);
+
+ if (read || (write && buf->cache_settings &
+ HWMEM_ALLOC_HINT_CACHE_WB))
+ /* Perform defered invalidates */
+ invalidate_cpu_cache(buf, &region_range);
+ if (read || (write && buf->cache_settings &
+ HWMEM_ALLOC_HINT_CACHE_AOW))
+ expand_range(&buf->range_in_cpu_cache, &region_range);
+ if (write && buf->cache_settings & HWMEM_ALLOC_HINT_CACHE_WB) {
+ struct cach_range dirty_range_addition;
+
+ if (buf->cache_settings & HWMEM_ALLOC_HINT_CACHE_AOW)
+ dirty_range_addition = region_range;
+ else
+ intersect_range(&buf->range_in_cpu_cache,
+ &region_range, &dirty_range_addition);
+
+ expand_range(&buf->range_dirty_in_cpu_cache,
+ &dirty_range_addition);
+ }
+ }
+ if (buf->cache_settings & HWMEM_ALLOC_HINT_WRITE_COMBINE) {
+ if (write)
+ buf->in_cpu_write_buf = true;
+ }
+}
+
+static void sync_buf_post_cpu(struct cach_buf *buf,
+ enum hwmem_access next_access, struct hwmem_region *next_region)
+{
+ bool write = next_access & HWMEM_ACCESS_WRITE;
+ bool read = next_access & HWMEM_ACCESS_READ;
+ struct cach_range region_range;
+
+ if (!write && !read)
+ return;
+
+ region_2_range(next_region, buf->size, &region_range);
+
+ if (write) {
+ if (speculative_data_prefetch()) {
+ /* Defer invalidate */
+ struct cach_range intersection;
+
+ intersect_range(&buf->range_in_cpu_cache,
+ &region_range, &intersection);
+
+ expand_range(&buf->range_invalid_in_cpu_cache,
+ &intersection);
+
+ clean_cpu_cache(buf, &region_range);
+ } else {
+ flush_cpu_cache(buf, &region_range);
+ }
+ }
+ if (read)
+ clean_cpu_cache(buf, &region_range);
+
+ if (buf->in_cpu_write_buf) {
+ drain_cpu_write_buf();
+
+ buf->in_cpu_write_buf = false;
+ }
+}
+
+static void invalidate_cpu_cache(struct cach_buf *buf, struct cach_range *range)
+{
+ struct cach_range intersection;
+
+ intersect_range(&buf->range_invalid_in_cpu_cache, range,
+ &intersection);
+ if (is_non_empty_range(&intersection)) {
+ bool flushed_everything;
+
+ expand_range_2_edge(&intersection,
+ &buf->range_invalid_in_cpu_cache);
+
+ /*
+ * Cache handler never uses invalidate to discard data in the
+ * cache so we can use flush instead which is considerably
+ * faster for large buffers.
+ */
+ flush_cpu_dcache(
+ offset_2_vaddr(buf, intersection.start),
+ offset_2_paddr(buf, intersection.start),
+ range_length(&intersection),
+ buf->cache_settings &
+ HWMEM_ALLOC_HINT_INNER_CACHE_ONLY,
+ &flushed_everything);
+
+ if (flushed_everything) {
+ null_range(&buf->range_invalid_in_cpu_cache);
+ null_range(&buf->range_dirty_in_cpu_cache);
+ } else {
+ /*
+ * No need to shrink range_in_cpu_cache as invalidate
+ * is only used when we can't keep track of what's in
+ * the CPU cache.
+ */
+ shrink_range(&buf->range_invalid_in_cpu_cache,
+ &intersection);
+ }
+ }
+}
+
+static void clean_cpu_cache(struct cach_buf *buf, struct cach_range *range)
+{
+ struct cach_range intersection;
+
+ intersect_range(&buf->range_dirty_in_cpu_cache, range, &intersection);
+ if (is_non_empty_range(&intersection)) {
+ bool cleaned_everything;
+
+ expand_range_2_edge(&intersection,
+ &buf->range_dirty_in_cpu_cache);
+
+ clean_cpu_dcache(
+ offset_2_vaddr(buf, intersection.start),
+ offset_2_paddr(buf, intersection.start),
+ range_length(&intersection),
+ buf->cache_settings &
+ HWMEM_ALLOC_HINT_INNER_CACHE_ONLY,
+ &cleaned_everything);
+
+ if (cleaned_everything)
+ null_range(&buf->range_dirty_in_cpu_cache);
+ else
+ shrink_range(&buf->range_dirty_in_cpu_cache,
+ &intersection);
+ }
+}
+
+static void flush_cpu_cache(struct cach_buf *buf, struct cach_range *range)
+{
+ struct cach_range intersection;
+
+ intersect_range(&buf->range_in_cpu_cache, range, &intersection);
+ if (is_non_empty_range(&intersection)) {
+ bool flushed_everything;
+
+ expand_range_2_edge(&intersection, &buf->range_in_cpu_cache);
+
+ flush_cpu_dcache(
+ offset_2_vaddr(buf, intersection.start),
+ offset_2_paddr(buf, intersection.start),
+ range_length(&intersection),
+ buf->cache_settings &
+ HWMEM_ALLOC_HINT_INNER_CACHE_ONLY,
+ &flushed_everything);
+
+ if (flushed_everything) {
+ if (!speculative_data_prefetch())
+ null_range(&buf->range_in_cpu_cache);
+ null_range(&buf->range_dirty_in_cpu_cache);
+ null_range(&buf->range_invalid_in_cpu_cache);
+ } else {
+ if (!speculative_data_prefetch())
+ shrink_range(&buf->range_in_cpu_cache,
+ &intersection);
+ shrink_range(&buf->range_dirty_in_cpu_cache,
+ &intersection);
+ shrink_range(&buf->range_invalid_in_cpu_cache,
+ &intersection);
+ }
+ }
+}
+
+static void null_range(struct cach_range *range)
+{
+ range->start = U32_MAX;
+ range->end = 0;
+}
+
+static void expand_range(struct cach_range *range,
+ struct cach_range *range_2_add)
+{
+ range->start = min(range->start, range_2_add->start);
+ range->end = max(range->end, range_2_add->end);
+}
+
+/*
+ * Expands range to one of enclosing_range's two edges. The function will
+ * choose which of enclosing_range's edges to expand range to in such a
+ * way that the size of range is minimized. range must be located inside
+ * enclosing_range.
+ */
+static void expand_range_2_edge(struct cach_range *range,
+ struct cach_range *enclosing_range)
+{
+ u32 space_on_low_side = range->start - enclosing_range->start;
+ u32 space_on_high_side = enclosing_range->end - range->end;
+
+ if (space_on_low_side < space_on_high_side)
+ range->start = enclosing_range->start;
+ else
+ range->end = enclosing_range->end;
+}
+
+static void shrink_range(struct cach_range *range,
+ struct cach_range *range_2_remove)
+{
+ if (range_2_remove->start > range->start)
+ range->end = min(range->end, range_2_remove->start);
+ else
+ range->start = max(range->start, range_2_remove->end);
+
+ if (range->start >= range->end)
+ null_range(range);
+}
+
+static bool is_non_empty_range(struct cach_range *range)
+{
+ return range->end > range->start;
+}
+
+static void intersect_range(struct cach_range *range_1,
+ struct cach_range *range_2, struct cach_range *intersection)
+{
+ intersection->start = max(range_1->start, range_2->start);
+ intersection->end = min(range_1->end, range_2->end);
+
+ if (intersection->start >= intersection->end)
+ null_range(intersection);
+}
+
+/* Align_up restrictions apply here to */
+static void align_range_up(struct cach_range *range, u32 alignment)
+{
+ if (!is_non_empty_range(range))
+ return;
+
+ range->start = align_down(range->start, alignment);
+ range->end = align_up(range->end, alignment);
+}
+
+static u32 range_length(struct cach_range *range)
+{
+ if (is_non_empty_range(range))
+ return range->end - range->start;
+ else
+ return 0;
+}
+
+static void region_2_range(struct hwmem_region *region, u32 buffer_size,
+ struct cach_range *range)
+{
+ /*
+ * We don't care about invalid regions, instead we limit the region's
+ * range to the buffer's range. This should work good enough, worst
+ * case we synch the entire buffer when we get an invalid region which
+ * is acceptable.
+ */
+ range->start = region->offset + region->start;
+ range->end = min(region->offset + (region->count * region->size) -
+ (region->size - region->end), buffer_size);
+ if (range->start >= range->end) {
+ null_range(range);
+ return;
+ }
+
+ align_range_up(range, get_dcache_granularity());
+}
+
+static void *offset_2_vaddr(struct cach_buf *buf, u32 offset)
+{
+ return (void *)((u32)buf->vstart + offset);
+}
+
+static u32 offset_2_paddr(struct cach_buf *buf, u32 offset)
+{
+ return buf->pstart + offset;
+}
+
+/* Saturates, might return unaligned values when that happens */
+static u32 align_up(u32 value, u32 alignment)
+{
+ u32 remainder = value % alignment;
+ u32 value_2_add;
+
+ if (remainder == 0)
+ return value;
+
+ value_2_add = alignment - remainder;
+
+ if (value_2_add > U32_MAX - value) /* Will overflow */
+ return U32_MAX;
+
+ return value + value_2_add;
+}
+
+static u32 align_down(u32 value, u32 alignment)
+{
+ u32 remainder = value % alignment;
+ if (remainder == 0)
+ return value;
+
+ return value - remainder;
+}
diff --git a/drivers/misc/hwmem/cache_handler.h b/drivers/misc/hwmem/cache_handler.h
new file mode 100644
index 00000000000..792105196fa
--- /dev/null
+++ b/drivers/misc/hwmem/cache_handler.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Cache handler
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+/*
+ * Cache handler can not handle simultaneous execution! The caller has to
+ * ensure such a situation does not occur.
+ */
+
+#ifndef _CACHE_HANDLER_H_
+#define _CACHE_HANDLER_H_
+
+#include <linux/types.h>
+#include <linux/hwmem.h>
+
+/*
+ * To not have to double all datatypes we've used hwmem datatypes. If someone
+ * want's to use cache handler but not hwmem then we'll have to define our own
+ * datatypes.
+ */
+
+struct cach_range {
+ u32 start; /* Inclusive */
+ u32 end; /* Exclusive */
+};
+
+/*
+ * Internal, do not touch!
+ */
+struct cach_buf {
+ void *vstart;
+ u32 pstart;
+ u32 size;
+
+ /* Remaining hints are active */
+ enum hwmem_alloc_flags cache_settings;
+
+ bool in_cpu_write_buf;
+ struct cach_range range_in_cpu_cache;
+ struct cach_range range_dirty_in_cpu_cache;
+ struct cach_range range_invalid_in_cpu_cache;
+};
+
+void cach_init_buf(struct cach_buf *buf,
+ enum hwmem_alloc_flags cache_settings, u32 size);
+
+void cach_set_buf_addrs(struct cach_buf *buf, void* vaddr, u32 paddr);
+
+void cach_set_pgprot_cache_options(struct cach_buf *buf, pgprot_t *pgprot);
+
+void cach_set_domain(struct cach_buf *buf, enum hwmem_access access,
+ enum hwmem_domain domain, struct hwmem_region *region);
+
+#endif /* _CACHE_HANDLER_H_ */
diff --git a/drivers/misc/hwmem/contig_alloc.c b/drivers/misc/hwmem/contig_alloc.c
new file mode 100644
index 00000000000..bc71ca08f0f
--- /dev/null
+++ b/drivers/misc/hwmem/contig_alloc.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Contiguous memory allocator
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>,
+ * Johan Mossberg <johan.xx.mossberg@stericsson.com> for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <asm/sizes.h>
+
+#define MAX_INSTANCE_NAME_LENGTH 31
+
+struct alloc {
+ struct list_head list;
+
+ bool in_use;
+ phys_addr_t paddr;
+ size_t size;
+};
+
+struct instance {
+ struct list_head list;
+
+ char name[MAX_INSTANCE_NAME_LENGTH + 1];
+
+ phys_addr_t region_paddr;
+ void *region_kaddr;
+ size_t region_size;
+
+ struct list_head alloc_list;
+
+#ifdef CONFIG_DEBUG_FS
+ struct inode *debugfs_inode;
+#endif /* #ifdef CONFIG_DEBUG_FS */
+};
+
+static LIST_HEAD(instance_list);
+
+static DEFINE_MUTEX(lock);
+
+void *cona_create(const char *name, phys_addr_t region_paddr,
+ size_t region_size);
+void *cona_alloc(void *instance, size_t size);
+void cona_free(void *instance, void *alloc);
+phys_addr_t cona_get_alloc_paddr(void *alloc);
+void *cona_get_alloc_kaddr(void *instance, void *alloc);
+size_t cona_get_alloc_size(void *alloc);
+
+static int init_alloc_list(struct instance *instance);
+static void clean_alloc_list(struct instance *instance);
+static struct alloc *find_free_alloc_bestfit(struct instance *instance,
+ size_t size);
+static struct alloc *split_allocation(struct alloc *alloc,
+ size_t new_alloc_size);
+static phys_addr_t get_alloc_offset(struct instance *instance,
+ struct alloc *alloc);
+
+void *cona_create(const char *name, phys_addr_t region_paddr,
+ size_t region_size)
+{
+ int ret;
+ struct instance *instance;
+ struct vm_struct *vm_area;
+
+ if (region_size == 0)
+ return ERR_PTR(-EINVAL);
+
+ instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+ if (instance == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(instance->name, name, MAX_INSTANCE_NAME_LENGTH + 1);
+ /* Truncate name if necessary */
+ instance->name[MAX_INSTANCE_NAME_LENGTH] = '\0';
+ instance->region_paddr = region_paddr;
+ instance->region_size = region_size;
+
+ vm_area = get_vm_area(region_size, VM_IOREMAP);
+ if (vm_area == NULL) {
+ printk(KERN_WARNING "CONA: Failed to allocate %u bytes"
+ " kernel virtual memory", region_size);
+ ret = -ENOMSG;
+ goto vmem_alloc_failed;
+ }
+ instance->region_kaddr = vm_area->addr;
+
+ INIT_LIST_HEAD(&instance->alloc_list);
+ ret = init_alloc_list(instance);
+ if (ret < 0)
+ goto init_alloc_list_failed;
+
+ mutex_lock(&lock);
+ list_add_tail(&instance->list, &instance_list);
+ mutex_unlock(&lock);
+
+ return instance;
+
+init_alloc_list_failed:
+ vm_area = remove_vm_area(instance->region_kaddr);
+ if (vm_area == NULL)
+ printk(KERN_ERR "CONA: Failed to free kernel virtual memory,"
+ " resource leak!\n");
+
+ kfree(vm_area);
+vmem_alloc_failed:
+ kfree(instance);
+
+ return ERR_PTR(ret);
+}
+
+void *cona_alloc(void *instance, size_t size)
+{
+ struct instance *instance_l = (struct instance *)instance;
+ struct alloc *alloc;
+
+ if (size == 0)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&lock);
+
+ alloc = find_free_alloc_bestfit(instance_l, size);
+ if (IS_ERR(alloc))
+ goto out;
+ if (size < alloc->size) {
+ alloc = split_allocation(alloc, size);
+ if (IS_ERR(alloc))
+ goto out;
+ } else {
+ alloc->in_use = true;
+ }
+
+out:
+ mutex_unlock(&lock);
+
+ return alloc;
+}
+
+void cona_free(void *instance, void *alloc)
+{
+ struct instance *instance_l = (struct instance *)instance;
+ struct alloc *alloc_l = (struct alloc *)alloc;
+ struct alloc *other;
+
+ mutex_lock(&lock);
+
+ alloc_l->in_use = false;
+
+ other = list_entry(alloc_l->list.prev, struct alloc, list);
+ if ((alloc_l->list.prev != &instance_l->alloc_list) &&
+ !other->in_use) {
+ other->size += alloc_l->size;
+ list_del(&alloc_l->list);
+ kfree(alloc_l);
+ alloc_l = other;
+ }
+ other = list_entry(alloc_l->list.next, struct alloc, list);
+ if ((alloc_l->list.next != &instance_l->alloc_list) &&
+ !other->in_use) {
+ alloc_l->size += other->size;
+ list_del(&other->list);
+ kfree(other);
+ }
+
+ mutex_unlock(&lock);
+}
+
+phys_addr_t cona_get_alloc_paddr(void *alloc)
+{
+ return ((struct alloc *)alloc)->paddr;
+}
+
+void *cona_get_alloc_kaddr(void *instance, void *alloc)
+{
+ struct instance *instance_l = (struct instance *)instance;
+
+ return instance_l->region_kaddr + get_alloc_offset(instance_l,
+ (struct alloc *)alloc);
+}
+
+size_t cona_get_alloc_size(void *alloc)
+{
+ return ((struct alloc *)alloc)->size;
+}
+
+static int init_alloc_list(struct instance *instance)
+{
+ /*
+ * Hack to not get any allocs that cross a 64MiB boundary as B2R2 can't
+ * handle that.
+ */
+ int ret;
+ u32 curr_pos = instance->region_paddr;
+ u32 region_end = instance->region_paddr + instance->region_size;
+ u32 next_64mib_boundary = (curr_pos + SZ_64M) & ~(SZ_64M - 1);
+ struct alloc *alloc;
+
+ if (PAGE_SIZE >= SZ_64M) {
+ printk(KERN_WARNING "CONA: PAGE_SIZE >= 64MiB\n");
+ return -ENOMSG;
+ }
+
+ while (next_64mib_boundary < region_end) {
+ if (next_64mib_boundary - curr_pos > PAGE_SIZE) {
+ alloc = kzalloc(sizeof(struct alloc), GFP_KERNEL);
+ if (alloc == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ alloc->paddr = curr_pos;
+ alloc->size = next_64mib_boundary - curr_pos -
+ PAGE_SIZE;
+ alloc->in_use = false;
+ list_add_tail(&alloc->list, &instance->alloc_list);
+ curr_pos = alloc->paddr + alloc->size;
+ }
+
+ alloc = kzalloc(sizeof(struct alloc), GFP_KERNEL);
+ if (alloc == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ alloc->paddr = curr_pos;
+ alloc->size = PAGE_SIZE;
+ alloc->in_use = true;
+ list_add_tail(&alloc->list, &instance->alloc_list);
+ curr_pos = alloc->paddr + alloc->size;
+
+ next_64mib_boundary += SZ_64M;
+ }
+
+ alloc = kzalloc(sizeof(struct alloc), GFP_KERNEL);
+ if (alloc == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ alloc->paddr = curr_pos;
+ alloc->size = region_end - curr_pos;
+ alloc->in_use = false;
+ list_add_tail(&alloc->list, &instance->alloc_list);
+
+ return 0;
+
+error:
+ clean_alloc_list(instance);
+
+ return ret;
+}
+
+static void clean_alloc_list(struct instance *instance)
+{
+ while (list_empty(&instance->alloc_list) == 0) {
+ struct alloc *i = list_first_entry(&instance->alloc_list,
+ struct alloc, list);
+
+ list_del(&i->list);
+
+ kfree(i);
+ }
+}
+
+static struct alloc *find_free_alloc_bestfit(struct instance *instance,
+ size_t size)
+{
+ size_t best_diff = ~(size_t)0;
+ struct alloc *alloc = NULL, *i;
+
+ list_for_each_entry(i, &instance->alloc_list, list) {
+ size_t diff = i->size - size;
+ if (i->in_use || i->size < size)
+ continue;
+ if (diff < best_diff) {
+ alloc = i;
+ best_diff = diff;
+ }
+ }
+
+ return alloc != NULL ? alloc : ERR_PTR(-ENOMEM);
+}
+
+static struct alloc *split_allocation(struct alloc *alloc,
+ size_t new_alloc_size)
+{
+ struct alloc *new_alloc;
+
+ new_alloc = kzalloc(sizeof(struct alloc), GFP_KERNEL);
+ if (new_alloc == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ new_alloc->in_use = true;
+ new_alloc->paddr = alloc->paddr;
+ new_alloc->size = new_alloc_size;
+ alloc->size -= new_alloc_size;
+ alloc->paddr += new_alloc_size;
+
+ list_add_tail(&new_alloc->list, &alloc->list);
+
+ return new_alloc;
+}
+
+static phys_addr_t get_alloc_offset(struct instance *instance,
+ struct alloc *alloc)
+{
+ return alloc->paddr - instance->region_paddr;
+}
+
+/* Debug */
+
+#ifdef CONFIG_DEBUG_FS
+
+static int print_alloc(struct alloc *alloc, char **buf, size_t buf_size);
+static struct instance *get_instance_from_file(struct file *file);
+static int debugfs_allocs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos);
+
+static const struct file_operations debugfs_allocs_fops = {
+ .owner = THIS_MODULE,
+ .read = debugfs_allocs_read,
+};
+
+static int print_alloc(struct alloc *alloc, char **buf, size_t buf_size)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ size_t buf_size_l;
+ if (i == 0)
+ buf_size_l = 0;
+ else
+ buf_size_l = buf_size;
+
+ ret = snprintf(*buf, buf_size_l, "paddr: %10x\tsize: %10u\t"
+ "in use: %1u\n", alloc->paddr, alloc->size,
+ alloc->in_use);
+ if (ret < 0)
+ return -ENOMSG;
+ else if (ret + 1 > buf_size)
+ return -EINVAL;
+ }
+
+ *buf += ret;
+
+ return 0;
+}
+
+static struct instance *get_instance_from_file(struct file *file)
+{
+ struct instance *curr_instance;
+
+ list_for_each_entry(curr_instance, &instance_list, list) {
+ if (file->f_dentry->d_inode == curr_instance->debugfs_inode)
+ return curr_instance;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+static int debugfs_allocs_read(struct file *file, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ /*
+ * We assume the supplied buffer and PAGE_SIZE is large enough to hold
+ * information about at least one alloc, if not no data will be
+ * returned.
+ */
+
+ int ret;
+ struct instance *instance;
+ struct alloc *curr_alloc;
+ char *local_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ char *local_buf_pos = local_buf;
+ size_t available_space = min((size_t)PAGE_SIZE, count);
+ /* private_data is intialized to NULL in open which I assume is 0. */
+ void **curr_pos = &file->private_data;
+ size_t bytes_read;
+
+ if (local_buf == NULL)
+ return -ENOMEM;
+
+ mutex_lock(&lock);
+
+ instance = get_instance_from_file(file);
+ if (IS_ERR(instance)) {
+ ret = PTR_ERR(instance);
+ goto out;
+ }
+
+ list_for_each_entry(curr_alloc, &instance->alloc_list, list) {
+ phys_addr_t alloc_offset = get_alloc_offset(instance,
+ curr_alloc);
+ if (alloc_offset < (phys_addr_t)*curr_pos)
+ continue;
+
+ ret = print_alloc(curr_alloc, &local_buf_pos, available_space -
+ (size_t)(local_buf_pos - local_buf));
+ if (ret == -EINVAL) /* No more room */
+ break;
+ else if (ret < 0)
+ goto out;
+
+ /*
+ * There could be an overflow issue here in the unlikely case
+ * where the region is placed at the end of the address range
+ * and the last alloc is 1 byte large. Since this is debug code
+ * and that case most likely never will happen I've chosen to
+ * defer fixing it till it happens.
+ */
+ *curr_pos = (void *)(alloc_offset + 1);
+ }
+
+ bytes_read = (size_t)(local_buf_pos - local_buf);
+
+ ret = copy_to_user(buf, local_buf, bytes_read);
+ if (ret < 0)
+ goto out;
+
+ ret = bytes_read;
+
+out:
+ kfree(local_buf);
+
+ mutex_unlock(&lock);
+
+ return ret;
+}
+
+static int __init init_debugfs(void)
+{
+ struct instance *curr_instance;
+ struct dentry *debugfs_root_dir = debugfs_create_dir("cona", NULL);
+
+ mutex_lock(&lock);
+
+ list_for_each_entry(curr_instance, &instance_list, list) {
+ struct dentry *file_dentry;
+ char tmp_str[MAX_INSTANCE_NAME_LENGTH + 7 + 1];
+ tmp_str[0] = '\0';
+ strcat(tmp_str, curr_instance->name);
+ strcat(tmp_str, "_allocs");
+ file_dentry = debugfs_create_file(tmp_str, 0444,
+ debugfs_root_dir, 0, &debugfs_allocs_fops);
+ if (file_dentry != NULL)
+ curr_instance->debugfs_inode = file_dentry->d_inode;
+ }
+
+ mutex_unlock(&lock);
+
+ return 0;
+}
+/*
+ * Must be executed after all instances have been created, hence the
+ * late_initcall.
+ */
+late_initcall(init_debugfs);
+
+#endif /* #ifdef CONFIG_DEBUG_FS */
diff --git a/drivers/misc/hwmem/hwmem-ioctl.c b/drivers/misc/hwmem/hwmem-ioctl.c
new file mode 100644
index 00000000000..e9e50de78bd
--- /dev/null
+++ b/drivers/misc/hwmem/hwmem-ioctl.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Hardware memory driver, hwmem
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/mm_types.h>
+#include <linux/hwmem.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+
+static int hwmem_open(struct inode *inode, struct file *file);
+static int hwmem_ioctl_mmap(struct file *file, struct vm_area_struct *vma);
+static int hwmem_release_fop(struct inode *inode, struct file *file);
+static long hwmem_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+static unsigned long hwmem_get_unmapped_area(struct file *file,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags);
+
+static const struct file_operations hwmem_fops = {
+ .open = hwmem_open,
+ .mmap = hwmem_ioctl_mmap,
+ .unlocked_ioctl = hwmem_ioctl,
+ .release = hwmem_release_fop,
+ .get_unmapped_area = hwmem_get_unmapped_area,
+};
+
+static struct miscdevice hwmem_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "hwmem",
+ .fops = &hwmem_fops,
+};
+
+struct hwmem_file {
+ struct mutex lock;
+ struct idr idr; /* id -> struct hwmem_alloc*, ref counted */
+ struct hwmem_alloc *fd_alloc; /* Ref counted */
+};
+
+static s32 create_id(struct hwmem_file *hwfile, struct hwmem_alloc *alloc)
+{
+ int id, ret;
+
+ while (true) {
+ if (idr_pre_get(&hwfile->idr, GFP_KERNEL) == 0)
+ return -ENOMEM;
+
+ ret = idr_get_new_above(&hwfile->idr, alloc, 1, &id);
+ if (ret == 0)
+ break;
+ else if (ret != -EAGAIN)
+ return -ENOMEM;
+ }
+
+ /*
+ * IDR always returns the lowest free id so there is no wrapping issue
+ * because of this.
+ */
+ if (id >= (s32)1 << (31 - PAGE_SHIFT)) {
+ dev_err(hwmem_device.this_device, "Out of IDs!\n");
+ idr_remove(&hwfile->idr, id);
+ return -ENOMSG;
+ }
+
+ return (s32)id << PAGE_SHIFT;
+}
+
+static void remove_id(struct hwmem_file *hwfile, s32 id)
+{
+ idr_remove(&hwfile->idr, id >> PAGE_SHIFT);
+}
+
+static struct hwmem_alloc *resolve_id(struct hwmem_file *hwfile, s32 id)
+{
+ struct hwmem_alloc *alloc;
+
+ alloc = id ? idr_find(&hwfile->idr, id >> PAGE_SHIFT) :
+ hwfile->fd_alloc;
+ if (alloc == NULL)
+ alloc = ERR_PTR(-EINVAL);
+
+ return alloc;
+}
+
+static s32 alloc(struct hwmem_file *hwfile, struct hwmem_alloc_request *req)
+{
+ s32 ret = 0;
+ struct hwmem_alloc *alloc;
+
+ alloc = hwmem_alloc(req->size, req->flags, req->default_access,
+ req->mem_type);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ ret = create_id(hwfile, alloc);
+ if (ret < 0)
+ hwmem_release(alloc);
+
+ return ret;
+}
+
+static int alloc_fd(struct hwmem_file *hwfile, struct hwmem_alloc_request *req)
+{
+ struct hwmem_alloc *alloc;
+
+ if (hwfile->fd_alloc)
+ return -EINVAL;
+
+ alloc = hwmem_alloc(req->size, req->flags, req->default_access,
+ req->mem_type);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ hwfile->fd_alloc = alloc;
+
+ return 0;
+}
+
+static int release(struct hwmem_file *hwfile, s32 id)
+{
+ struct hwmem_alloc *alloc;
+
+ if (id == 0)
+ return -EINVAL;
+
+ alloc = resolve_id(hwfile, id);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ remove_id(hwfile, id);
+ hwmem_release(alloc);
+
+ return 0;
+}
+
+static int set_cpu_domain(struct hwmem_file *hwfile,
+ struct hwmem_set_domain_request *req)
+{
+ struct hwmem_alloc *alloc;
+
+ alloc = resolve_id(hwfile, req->id);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ return hwmem_set_domain(alloc, req->access, HWMEM_DOMAIN_CPU,
+ (struct hwmem_region *)&req->region);
+}
+
+static int set_sync_domain(struct hwmem_file *hwfile,
+ struct hwmem_set_domain_request *req)
+{
+ struct hwmem_alloc *alloc;
+
+ alloc = resolve_id(hwfile, req->id);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ return hwmem_set_domain(alloc, req->access, HWMEM_DOMAIN_SYNC,
+ (struct hwmem_region *)&req->region);
+}
+
+static int pin(struct hwmem_file *hwfile, struct hwmem_pin_request *req)
+{
+ int ret;
+ struct hwmem_alloc *alloc;
+ enum hwmem_mem_type mem_type;
+ struct hwmem_mem_chunk mem_chunk;
+ size_t mem_chunk_length = 1;
+
+ alloc = resolve_id(hwfile, req->id);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ hwmem_get_info(alloc, NULL, &mem_type, NULL);
+ if (mem_type != HWMEM_MEM_CONTIGUOUS_SYS)
+ return -EINVAL;
+
+ ret = hwmem_pin(alloc, &mem_chunk, &mem_chunk_length);
+ if (ret < 0)
+ return ret;
+
+ req->phys_addr = mem_chunk.paddr;
+
+ return 0;
+}
+
+static int unpin(struct hwmem_file *hwfile, s32 id)
+{
+ struct hwmem_alloc *alloc;
+
+ alloc = resolve_id(hwfile, id);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ hwmem_unpin(alloc);
+
+ return 0;
+}
+
+static int set_access(struct hwmem_file *hwfile,
+ struct hwmem_set_access_request *req)
+{
+ struct hwmem_alloc *alloc;
+
+ alloc = resolve_id(hwfile, req->id);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ return hwmem_set_access(alloc, req->access, req->pid);
+}
+
+static int get_info(struct hwmem_file *hwfile,
+ struct hwmem_get_info_request *req)
+{
+ struct hwmem_alloc *alloc;
+
+ alloc = resolve_id(hwfile, req->id);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ hwmem_get_info(alloc, &req->size, &req->mem_type, &req->access);
+
+ return 0;
+}
+
+static s32 export(struct hwmem_file *hwfile, s32 id)
+{
+ s32 ret;
+ struct hwmem_alloc *alloc;
+ enum hwmem_access access;
+
+ alloc = resolve_id(hwfile, id);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ /*
+ * The user could be about to send the buffer to a driver but
+ * there is a chance the current thread group don't have import rights
+ * if it gained access to the buffer via a inter-process fd transfer
+ * (fork, Android binder), if this is the case the driver will not be
+ * able to resolve the buffer name. To avoid this situation we give the
+ * current thread group import rights. This will not breach the
+ * security as the process already has access to the buffer (otherwise
+ * it would not be able to get here).
+ */
+ hwmem_get_info(alloc, NULL, NULL, &access);
+
+ ret = hwmem_set_access(alloc, (access | HWMEM_ACCESS_IMPORT),
+ task_tgid_nr(current));
+ if (ret < 0)
+ return ret;
+
+ return hwmem_get_name(alloc);
+}
+
+static s32 import(struct hwmem_file *hwfile, s32 name)
+{
+ s32 ret = 0;
+ struct hwmem_alloc *alloc;
+ enum hwmem_access access;
+
+ alloc = hwmem_resolve_by_name(name);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ /* Check access permissions for process */
+ hwmem_get_info(alloc, NULL, NULL, &access);
+ if (!(access & HWMEM_ACCESS_IMPORT)) {
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = create_id(hwfile, alloc);
+ if (ret < 0)
+ goto error;
+
+ return ret;
+
+error:
+ hwmem_release(alloc);
+
+ return ret;
+}
+
+static int import_fd(struct hwmem_file *hwfile, s32 name)
+{
+ int ret;
+ struct hwmem_alloc *alloc;
+ enum hwmem_access access;
+
+ if (hwfile->fd_alloc)
+ return -EINVAL;
+
+ alloc = hwmem_resolve_by_name(name);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ /* Check access permissions for process */
+ hwmem_get_info(alloc, NULL, NULL, &access);
+ if (!(access & HWMEM_ACCESS_IMPORT)) {
+ ret = -EPERM;
+ goto error;
+ }
+
+ hwfile->fd_alloc = alloc;
+
+ return 0;
+
+error:
+ hwmem_release(alloc);
+
+ return ret;
+}
+
+static int hwmem_open(struct inode *inode, struct file *file)
+{
+ struct hwmem_file *hwfile;
+
+ hwfile = kzalloc(sizeof(struct hwmem_file), GFP_KERNEL);
+ if (hwfile == NULL)
+ return -ENOMEM;
+
+ idr_init(&hwfile->idr);
+ mutex_init(&hwfile->lock);
+ file->private_data = hwfile;
+
+ return 0;
+}
+
+static int hwmem_ioctl_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int ret;
+ struct hwmem_file *hwfile = (struct hwmem_file *)file->private_data;
+ struct hwmem_alloc *alloc;
+
+ mutex_lock(&hwfile->lock);
+
+ alloc = resolve_id(hwfile, (s32)vma->vm_pgoff << PAGE_SHIFT);
+ if (IS_ERR(alloc)) {
+ ret = PTR_ERR(alloc);
+ goto out;
+ }
+
+ ret = hwmem_mmap(alloc, vma);
+
+out:
+ mutex_unlock(&hwfile->lock);
+
+ return ret;
+}
+
+static int hwmem_release_idr_for_each_wrapper(int id, void *ptr, void *data)
+{
+ hwmem_release((struct hwmem_alloc *)ptr);
+
+ return 0;
+}
+
+static int hwmem_release_fop(struct inode *inode, struct file *file)
+{
+ struct hwmem_file *hwfile = (struct hwmem_file *)file->private_data;
+
+ idr_for_each(&hwfile->idr, hwmem_release_idr_for_each_wrapper, NULL);
+ idr_remove_all(&hwfile->idr);
+ idr_destroy(&hwfile->idr);
+
+ if (hwfile->fd_alloc)
+ hwmem_release(hwfile->fd_alloc);
+
+ mutex_destroy(&hwfile->lock);
+
+ kfree(hwfile);
+
+ return 0;
+}
+
+static long hwmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOSYS;
+ struct hwmem_file *hwfile = (struct hwmem_file *)file->private_data;
+
+ mutex_lock(&hwfile->lock);
+
+ switch (cmd) {
+ case HWMEM_ALLOC_IOC:
+ {
+ struct hwmem_alloc_request req;
+ if (copy_from_user(&req, (void __user *)arg,
+ sizeof(struct hwmem_alloc_request)))
+ ret = -EFAULT;
+ else
+ ret = alloc(hwfile, &req);
+ }
+ break;
+ case HWMEM_ALLOC_FD_IOC:
+ {
+ struct hwmem_alloc_request req;
+ if (copy_from_user(&req, (void __user *)arg,
+ sizeof(struct hwmem_alloc_request)))
+ ret = -EFAULT;
+ else
+ ret = alloc_fd(hwfile, &req);
+ }
+ break;
+ case HWMEM_RELEASE_IOC:
+ ret = release(hwfile, (s32)arg);
+ break;
+ case HWMEM_SET_CPU_DOMAIN_IOC:
+ {
+ struct hwmem_set_domain_request req;
+ if (copy_from_user(&req, (void __user *)arg,
+ sizeof(struct hwmem_set_domain_request)))
+ ret = -EFAULT;
+ else
+ ret = set_cpu_domain(hwfile, &req);
+ }
+ break;
+ case HWMEM_SET_SYNC_DOMAIN_IOC:
+ {
+ struct hwmem_set_domain_request req;
+ if (copy_from_user(&req, (void __user *)arg,
+ sizeof(struct hwmem_set_domain_request)))
+ ret = -EFAULT;
+ else
+ ret = set_sync_domain(hwfile, &req);
+ }
+ break;
+ case HWMEM_PIN_IOC:
+ {
+ struct hwmem_pin_request req;
+ if (copy_from_user(&req, (void __user *)arg,
+ sizeof(struct hwmem_pin_request)))
+ ret = -EFAULT;
+ else
+ ret = pin(hwfile, &req);
+ if (ret == 0 && copy_to_user((void __user *)arg, &req,
+ sizeof(struct hwmem_pin_request)))
+ ret = -EFAULT;
+ }
+ break;
+ case HWMEM_UNPIN_IOC:
+ ret = unpin(hwfile, (s32)arg);
+ break;
+ case HWMEM_SET_ACCESS_IOC:
+ {
+ struct hwmem_set_access_request req;
+ if (copy_from_user(&req, (void __user *)arg,
+ sizeof(struct hwmem_set_access_request)))
+ ret = -EFAULT;
+ else
+ ret = set_access(hwfile, &req);
+ }
+ break;
+ case HWMEM_GET_INFO_IOC:
+ {
+ struct hwmem_get_info_request req;
+ if (copy_from_user(&req, (void __user *)arg,
+ sizeof(struct hwmem_get_info_request)))
+ ret = -EFAULT;
+ else
+ ret = get_info(hwfile, &req);
+ if (ret == 0 && copy_to_user((void __user *)arg, &req,
+ sizeof(struct hwmem_get_info_request)))
+ ret = -EFAULT;
+ }
+ break;
+ case HWMEM_EXPORT_IOC:
+ ret = export(hwfile, (s32)arg);
+ break;
+ case HWMEM_IMPORT_IOC:
+ ret = import(hwfile, (s32)arg);
+ break;
+ case HWMEM_IMPORT_FD_IOC:
+ ret = import_fd(hwfile, (s32)arg);
+ break;
+ }
+
+ mutex_unlock(&hwfile->lock);
+
+ return ret;
+}
+
+static unsigned long hwmem_get_unmapped_area(struct file *file,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags)
+{
+ /*
+ * pgoff will not be valid as it contains a buffer id (right shifted
+ * PAGE_SHIFT bits). To not confuse get_unmapped_area we'll not pass
+ * on file or pgoff.
+ */
+ return current->mm->get_unmapped_area(NULL, addr, len, 0, flags);
+}
+
+int __init hwmem_ioctl_init(void)
+{
+ if (PAGE_SHIFT < 1 || PAGE_SHIFT > 30 || sizeof(size_t) != 4 ||
+ sizeof(int) > 4 || sizeof(enum hwmem_alloc_flags) != 4 ||
+ sizeof(enum hwmem_access) != 4 ||
+ sizeof(enum hwmem_mem_type) != 4) {
+ dev_err(hwmem_device.this_device, "PAGE_SHIFT < 1 || PAGE_SHIFT"
+ " > 30 || sizeof(size_t) != 4 || sizeof(int) > 4 ||"
+ " sizeof(enum hwmem_alloc_flags) != 4 || sizeof(enum"
+ " hwmem_access) != 4 || sizeof(enum hwmem_mem_type)"
+ " != 4\n");
+ return -ENOMSG;
+ }
+ if (PAGE_SHIFT > 15)
+ dev_warn(hwmem_device.this_device, "Due to the page size only"
+ " %u id:s per file instance are available\n",
+ ((u32)1 << (31 - PAGE_SHIFT)) - 1);
+
+ return misc_register(&hwmem_device);
+}
+
+void __exit hwmem_ioctl_exit(void)
+{
+ misc_deregister(&hwmem_device);
+}
diff --git a/drivers/misc/hwmem/hwmem-main.c b/drivers/misc/hwmem/hwmem-main.c
new file mode 100644
index 00000000000..9162ff4fc91
--- /dev/null
+++ b/drivers/misc/hwmem/hwmem-main.c
@@ -0,0 +1,725 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Hardware memory driver, hwmem
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>,
+ * Johan Mossberg <johan.xx.mossberg@stericsson.com> for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/idr.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/pid.h>
+#include <linux/list.h>
+#include <linux/hwmem.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/kallsyms.h>
+#include <linux/vmalloc.h>
+#include "cache_handler.h"
+
+#define S32_MAX 2147483647
+
+struct hwmem_alloc_threadg_info {
+ struct list_head list;
+
+ struct pid *threadg_pid; /* Ref counted */
+
+ enum hwmem_access access;
+};
+
+struct hwmem_alloc {
+ struct list_head list;
+
+ atomic_t ref_cnt;
+
+ enum hwmem_alloc_flags flags;
+ struct hwmem_mem_type_struct *mem_type;
+
+ void *allocator_hndl;
+ phys_addr_t paddr;
+ void *kaddr;
+ size_t size;
+ s32 name;
+
+ /* Access control */
+ enum hwmem_access default_access;
+ struct list_head threadg_info_list;
+
+ /* Cache handling */
+ struct cach_buf cach_buf;
+
+#ifdef CONFIG_DEBUG_FS
+ /* Debug */
+ void *creator;
+ pid_t creator_tgid;
+#endif /* #ifdef CONFIG_DEBUG_FS */
+};
+
+static struct platform_device *hwdev;
+
+static LIST_HEAD(alloc_list);
+static DEFINE_IDR(global_idr);
+static DEFINE_MUTEX(lock);
+
+static void vm_open(struct vm_area_struct *vma);
+static void vm_close(struct vm_area_struct *vma);
+static struct vm_operations_struct vm_ops = {
+ .open = vm_open,
+ .close = vm_close,
+};
+
+static void kunmap_alloc(struct hwmem_alloc *alloc);
+
+/* Helpers */
+
+static void destroy_alloc_threadg_info(
+ struct hwmem_alloc_threadg_info *info)
+{
+ if (info->threadg_pid)
+ put_pid(info->threadg_pid);
+
+ kfree(info);
+}
+
+static void clean_alloc_threadg_info_list(struct hwmem_alloc *alloc)
+{
+ struct hwmem_alloc_threadg_info *info;
+ struct hwmem_alloc_threadg_info *tmp;
+
+ list_for_each_entry_safe(info, tmp, &(alloc->threadg_info_list),
+ list) {
+ list_del(&info->list);
+ destroy_alloc_threadg_info(info);
+ }
+}
+
+static enum hwmem_access get_access(struct hwmem_alloc *alloc)
+{
+ struct hwmem_alloc_threadg_info *info;
+ struct pid *my_pid;
+ bool found = false;
+
+ my_pid = find_get_pid(task_tgid_nr(current));
+ if (!my_pid)
+ return 0;
+
+ list_for_each_entry(info, &(alloc->threadg_info_list), list) {
+ if (info->threadg_pid == my_pid) {
+ found = true;
+ break;
+ }
+ }
+
+ put_pid(my_pid);
+
+ if (found)
+ return info->access;
+ else
+ return alloc->default_access;
+}
+
+static void clear_alloc_mem(struct hwmem_alloc *alloc)
+{
+ cach_set_domain(&alloc->cach_buf, HWMEM_ACCESS_WRITE,
+ HWMEM_DOMAIN_CPU, NULL);
+
+ memset(alloc->kaddr, 0, alloc->size);
+}
+
+static void destroy_alloc(struct hwmem_alloc *alloc)
+{
+ list_del(&alloc->list);
+
+ if (alloc->name != 0) {
+ idr_remove(&global_idr, alloc->name);
+ alloc->name = 0;
+ }
+
+ clean_alloc_threadg_info_list(alloc);
+
+ kunmap_alloc(alloc);
+
+ if (!IS_ERR_OR_NULL(alloc->allocator_hndl))
+ alloc->mem_type->allocator_api.free(
+ alloc->mem_type->allocator_instance,
+ alloc->allocator_hndl);
+
+ kfree(alloc);
+}
+
+static int kmap_alloc(struct hwmem_alloc *alloc)
+{
+ int ret;
+ pgprot_t pgprot;
+ void *alloc_kaddr;
+
+ alloc_kaddr = alloc->mem_type->allocator_api.get_alloc_kaddr(
+ alloc->mem_type->allocator_instance, alloc->allocator_hndl);
+ if (IS_ERR(alloc_kaddr))
+ return PTR_ERR(alloc_kaddr);
+
+ pgprot = PAGE_KERNEL;
+ cach_set_pgprot_cache_options(&alloc->cach_buf, &pgprot);
+
+ ret = ioremap_page_range((unsigned long)alloc_kaddr,
+ (unsigned long)alloc_kaddr + alloc->size, alloc->paddr, pgprot);
+ if (ret < 0) {
+ dev_warn(&hwdev->dev, "Failed to map %#x - %#x", alloc->paddr,
+ alloc->paddr + alloc->size);
+ return ret;
+ }
+
+ alloc->kaddr = alloc_kaddr;
+
+ return 0;
+}
+
+static void kunmap_alloc(struct hwmem_alloc *alloc)
+{
+ if (alloc->kaddr == NULL)
+ return;
+
+ unmap_kernel_range((unsigned long)alloc->kaddr, alloc->size);
+
+ alloc->kaddr = NULL;
+}
+
+static struct hwmem_mem_type_struct *resolve_mem_type(
+ enum hwmem_mem_type mem_type)
+{
+ unsigned int i;
+ for (i = 0; i < hwmem_num_mem_types; i++) {
+ if (hwmem_mem_types[i].id == mem_type)
+ return &hwmem_mem_types[i];
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+/* HWMEM API */
+
+struct hwmem_alloc *hwmem_alloc(size_t size, enum hwmem_alloc_flags flags,
+ enum hwmem_access def_access, enum hwmem_mem_type mem_type)
+{
+ int ret;
+ struct hwmem_alloc *alloc;
+
+ if (hwdev == NULL) {
+ printk(KERN_ERR "HWMEM: Badly configured\n");
+ return ERR_PTR(-ENOMSG);
+ }
+
+ if (size == 0)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&lock);
+
+ size = PAGE_ALIGN(size);
+
+ alloc = kzalloc(sizeof(struct hwmem_alloc), GFP_KERNEL);
+ if (alloc == NULL) {
+ ret = -ENOMEM;
+ goto alloc_alloc_failed;
+ }
+
+ INIT_LIST_HEAD(&alloc->list);
+ atomic_inc(&alloc->ref_cnt);
+ alloc->flags = flags;
+ alloc->default_access = def_access;
+ INIT_LIST_HEAD(&alloc->threadg_info_list);
+ alloc->creator = __builtin_return_address(0);
+ alloc->creator_tgid = task_tgid_nr(current);
+
+ alloc->mem_type = resolve_mem_type(mem_type);
+ if (IS_ERR(alloc->mem_type)) {
+ ret = PTR_ERR(alloc->mem_type);
+ goto resolve_mem_type_failed;
+ }
+
+ alloc->allocator_hndl = alloc->mem_type->allocator_api.alloc(
+ alloc->mem_type->allocator_instance, size);
+ if (IS_ERR(alloc->allocator_hndl)) {
+ ret = PTR_ERR(alloc->allocator_hndl);
+ goto allocator_failed;
+ }
+
+ alloc->paddr = alloc->mem_type->allocator_api.get_alloc_paddr(
+ alloc->allocator_hndl);
+ alloc->size = alloc->mem_type->allocator_api.get_alloc_size(
+ alloc->allocator_hndl);
+
+ cach_init_buf(&alloc->cach_buf, alloc->flags, alloc->size);
+ ret = kmap_alloc(alloc);
+ if (ret < 0)
+ goto kmap_alloc_failed;
+ cach_set_buf_addrs(&alloc->cach_buf, alloc->kaddr, alloc->paddr);
+
+ list_add_tail(&alloc->list, &alloc_list);
+
+ clear_alloc_mem(alloc);
+
+ goto out;
+
+kmap_alloc_failed:
+allocator_failed:
+resolve_mem_type_failed:
+ destroy_alloc(alloc);
+alloc_alloc_failed:
+ alloc = ERR_PTR(ret);
+
+out:
+ mutex_unlock(&lock);
+
+ return alloc;
+}
+EXPORT_SYMBOL(hwmem_alloc);
+
+void hwmem_release(struct hwmem_alloc *alloc)
+{
+ mutex_lock(&lock);
+
+ if (atomic_dec_and_test(&alloc->ref_cnt))
+ destroy_alloc(alloc);
+
+ mutex_unlock(&lock);
+}
+EXPORT_SYMBOL(hwmem_release);
+
+int hwmem_set_domain(struct hwmem_alloc *alloc, enum hwmem_access access,
+ enum hwmem_domain domain, struct hwmem_region *region)
+{
+ mutex_lock(&lock);
+
+ cach_set_domain(&alloc->cach_buf, access, domain, region);
+
+ mutex_unlock(&lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(hwmem_set_domain);
+
+int hwmem_pin(struct hwmem_alloc *alloc, struct hwmem_mem_chunk *mem_chunks,
+ u32 *mem_chunks_length)
+{
+ if (*mem_chunks_length < 1) {
+ *mem_chunks_length = 1;
+ return -ENOSPC;
+ }
+
+ mutex_lock(&lock);
+
+ mem_chunks[0].paddr = alloc->paddr;
+ mem_chunks[0].size = alloc->size;
+ *mem_chunks_length = 1;
+
+ mutex_unlock(&lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(hwmem_pin);
+
+void hwmem_unpin(struct hwmem_alloc *alloc)
+{
+}
+EXPORT_SYMBOL(hwmem_unpin);
+
+static void vm_open(struct vm_area_struct *vma)
+{
+ atomic_inc(&((struct hwmem_alloc *)vma->vm_private_data)->ref_cnt);
+}
+
+static void vm_close(struct vm_area_struct *vma)
+{
+ hwmem_release((struct hwmem_alloc *)vma->vm_private_data);
+}
+
+int hwmem_mmap(struct hwmem_alloc *alloc, struct vm_area_struct *vma)
+{
+ int ret = 0;
+ unsigned long vma_size = vma->vm_end - vma->vm_start;
+ enum hwmem_access access;
+ mutex_lock(&lock);
+
+ access = get_access(alloc);
+
+ /* Check permissions */
+ if ((!(access & HWMEM_ACCESS_WRITE) &&
+ (vma->vm_flags & VM_WRITE)) ||
+ (!(access & HWMEM_ACCESS_READ) &&
+ (vma->vm_flags & VM_READ))) {
+ ret = -EPERM;
+ goto illegal_access;
+ }
+
+ if (vma_size > alloc->size) {
+ ret = -EINVAL;
+ goto illegal_size;
+ }
+
+ /*
+ * We don't want Linux to do anything (merging etc) with our VMAs as
+ * the offset is not necessarily valid
+ */
+ vma->vm_flags |= VM_SPECIAL;
+ cach_set_pgprot_cache_options(&alloc->cach_buf, &vma->vm_page_prot);
+ vma->vm_private_data = (void *)alloc;
+ atomic_inc(&alloc->ref_cnt);
+ vma->vm_ops = &vm_ops;
+
+ ret = remap_pfn_range(vma, vma->vm_start, alloc->paddr >> PAGE_SHIFT,
+ min(vma_size, (unsigned long)alloc->size), vma->vm_page_prot);
+ if (ret < 0)
+ goto map_failed;
+
+ goto out;
+
+map_failed:
+ atomic_dec(&alloc->ref_cnt);
+illegal_size:
+illegal_access:
+
+out:
+ mutex_unlock(&lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(hwmem_mmap);
+
+void *hwmem_kmap(struct hwmem_alloc *alloc)
+{
+ void *ret;
+
+ mutex_lock(&lock);
+
+ ret = alloc->kaddr;
+
+ mutex_unlock(&lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(hwmem_kmap);
+
+void hwmem_kunmap(struct hwmem_alloc *alloc)
+{
+}
+EXPORT_SYMBOL(hwmem_kunmap);
+
+int hwmem_set_access(struct hwmem_alloc *alloc,
+ enum hwmem_access access, pid_t pid_nr)
+{
+ int ret;
+ struct hwmem_alloc_threadg_info *info;
+ struct pid *pid;
+ bool found = false;
+
+ pid = find_get_pid(pid_nr);
+ if (!pid) {
+ ret = -EINVAL;
+ goto error_get_pid;
+ }
+
+ list_for_each_entry(info, &(alloc->threadg_info_list), list) {
+ if (info->threadg_pid == pid) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ ret = -ENOMEM;
+ goto error_alloc_info;
+ }
+
+ info->threadg_pid = pid;
+ info->access = access;
+
+ list_add_tail(&(info->list), &(alloc->threadg_info_list));
+ } else {
+ info->access = access;
+ }
+
+ return 0;
+
+error_alloc_info:
+ put_pid(pid);
+error_get_pid:
+ return ret;
+}
+EXPORT_SYMBOL(hwmem_set_access);
+
+void hwmem_get_info(struct hwmem_alloc *alloc, u32 *size,
+ enum hwmem_mem_type *mem_type, enum hwmem_access *access)
+{
+ mutex_lock(&lock);
+
+ if (size != NULL)
+ *size = alloc->size;
+ if (mem_type != NULL)
+ *mem_type = alloc->mem_type->id;
+ if (access != NULL)
+ *access = get_access(alloc);
+
+ mutex_unlock(&lock);
+}
+EXPORT_SYMBOL(hwmem_get_info);
+
+s32 hwmem_get_name(struct hwmem_alloc *alloc)
+{
+ int ret = 0, name;
+
+ mutex_lock(&lock);
+
+ if (alloc->name != 0) {
+ ret = alloc->name;
+ goto out;
+ }
+
+ while (true) {
+ if (idr_pre_get(&global_idr, GFP_KERNEL) == 0) {
+ ret = -ENOMEM;
+ goto pre_get_id_failed;
+ }
+
+ ret = idr_get_new_above(&global_idr, alloc, 1, &name);
+ if (ret == 0)
+ break;
+ else if (ret != -EAGAIN)
+ goto get_id_failed;
+ }
+
+ if (name > S32_MAX) {
+ ret = -ENOMSG;
+ goto overflow;
+ }
+
+ alloc->name = name;
+
+ ret = name;
+ goto out;
+
+overflow:
+ idr_remove(&global_idr, name);
+get_id_failed:
+pre_get_id_failed:
+
+out:
+ mutex_unlock(&lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(hwmem_get_name);
+
+struct hwmem_alloc *hwmem_resolve_by_name(s32 name)
+{
+ struct hwmem_alloc *alloc;
+
+ mutex_lock(&lock);
+
+ alloc = idr_find(&global_idr, name);
+ if (alloc == NULL) {
+ alloc = ERR_PTR(-EINVAL);
+ goto find_failed;
+ }
+ atomic_inc(&alloc->ref_cnt);
+
+ goto out;
+
+find_failed:
+
+out:
+ mutex_unlock(&lock);
+
+ return alloc;
+}
+EXPORT_SYMBOL(hwmem_resolve_by_name);
+
+/* Debug */
+
+#ifdef CONFIG_DEBUG_FS
+
+static int debugfs_allocs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos);
+
+static const struct file_operations debugfs_allocs_fops = {
+ .owner = THIS_MODULE,
+ .read = debugfs_allocs_read,
+};
+
+static int print_alloc(struct hwmem_alloc *alloc, char **buf, size_t buf_size)
+{
+ int ret;
+ char creator[KSYM_SYMBOL_LEN];
+ int i;
+
+ if (sprint_symbol(creator, (unsigned long)alloc->creator) < 0)
+ creator[0] = '\0';
+
+ for (i = 0; i < 2; i++) {
+ size_t buf_size_l;
+ if (i == 0)
+ buf_size_l = 0;
+ else
+ buf_size_l = buf_size;
+
+ ret = snprintf(*buf, buf_size_l,
+ "%#x\n"
+ "\tSize: %u\n"
+ "\tMemory type: %u\n"
+ "\tName: %#x\n"
+ "\tReference count: %i\n"
+ "\tAllocation flags: %#x\n"
+ "\t$ settings: %#x\n"
+ "\tDefault access: %#x\n"
+ "\tPhysical address: %#x\n"
+ "\tKernel virtual address: %#x\n"
+ "\tCreator: %s\n"
+ "\tCreator thread group id: %u\n",
+ (unsigned int)alloc, alloc->size, alloc->mem_type->id,
+ alloc->name, atomic_read(&alloc->ref_cnt),
+ alloc->flags, alloc->cach_buf.cache_settings,
+ alloc->default_access, alloc->paddr,
+ (unsigned int)alloc->kaddr, creator,
+ alloc->creator_tgid);
+ if (ret < 0)
+ return -ENOMSG;
+ else if (ret + 1 > buf_size)
+ return -EINVAL;
+ }
+
+ *buf += ret;
+
+ return 0;
+}
+
+static int debugfs_allocs_read(struct file *file, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ /*
+ * We assume the supplied buffer and PAGE_SIZE is large enough to hold
+ * information about at least one alloc, if not no data will be
+ * returned.
+ */
+
+ int ret;
+ size_t i = 0;
+ struct hwmem_alloc *curr_alloc;
+ char *local_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ char *local_buf_pos = local_buf;
+ size_t available_space = min((size_t)PAGE_SIZE, count);
+ /* private_data is intialized to NULL in open which I assume is 0. */
+ void **curr_pos = &file->private_data;
+ size_t bytes_read;
+
+ if (local_buf == NULL)
+ return -ENOMEM;
+
+ mutex_lock(&lock);
+
+ list_for_each_entry(curr_alloc, &alloc_list, list) {
+ if (i++ < (size_t)*curr_pos)
+ continue;
+
+ ret = print_alloc(curr_alloc, &local_buf_pos, available_space -
+ (size_t)(local_buf_pos - local_buf));
+ if (ret == -EINVAL) /* No more room */
+ break;
+ else if (ret < 0)
+ goto out;
+
+ *curr_pos = (void *)i;
+ }
+
+ bytes_read = (size_t)(local_buf_pos - local_buf);
+
+ ret = copy_to_user(buf, local_buf, bytes_read);
+ if (ret < 0)
+ goto out;
+
+ ret = bytes_read;
+
+out:
+ kfree(local_buf);
+
+ mutex_unlock(&lock);
+
+ return ret;
+}
+
+static void init_debugfs(void)
+{
+ /* Hwmem is never unloaded so dropping the dentrys is ok. */
+ struct dentry *debugfs_root_dir = debugfs_create_dir("hwmem", NULL);
+ (void)debugfs_create_file("allocs", 0444, debugfs_root_dir, 0,
+ &debugfs_allocs_fops);
+}
+
+#endif /* #ifdef CONFIG_DEBUG_FS */
+
+/* Module */
+
+extern int hwmem_ioctl_init(void);
+
+static int __devinit hwmem_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (hwdev) {
+ dev_err(&pdev->dev, "Probed multiple times\n");
+ return -EINVAL;
+ }
+
+ hwdev = pdev;
+
+ /*
+ * No need to flush the caches here. If we can keep track of the cache
+ * content then none of our memory will be in the caches, if we can't
+ * keep track of the cache content we always assume all our memory is
+ * in the caches.
+ */
+
+ ret = hwmem_ioctl_init();
+ if (ret < 0)
+ dev_warn(&pdev->dev, "Failed to start hwmem-ioctl, continuing"
+ " anyway\n");
+
+#ifdef CONFIG_DEBUG_FS
+ init_debugfs();
+#endif
+
+ dev_info(&pdev->dev, "Probed OK\n");
+
+ return 0;
+}
+
+static struct platform_driver hwmem_driver = {
+ .probe = hwmem_probe,
+ .driver = {
+ .name = "hwmem",
+ },
+};
+
+static int __init hwmem_init(void)
+{
+ return platform_driver_register(&hwmem_driver);
+}
+subsys_initcall(hwmem_init);
+
+MODULE_AUTHOR("Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Hardware memory driver");
+
diff --git a/drivers/misc/i2s/Kconfig b/drivers/misc/i2s/Kconfig
new file mode 100644
index 00000000000..569818caa5d
--- /dev/null
+++ b/drivers/misc/i2s/Kconfig
@@ -0,0 +1,19 @@
+#
+# U8500 I2S HW kernel configuration
+#
+config STM_I2S
+ bool "U8500 I2S hardware driver"
+ depends on ARCH_U8500 && STE_DMA40
+ default y
+ ---help---
+ If you say Y here, you will enable the U8500 I2S hardware driver.
+
+ If unsure, say N.
+config STM_MSP_I2S
+ tristate "U8500 MSP_I2S hardware driver"
+ depends on ARCH_U8500 && STE_DMA40 && STM_I2S
+ default y
+ ---help---
+ If you say Y here, you will enable the U8500 MSP_I2S hardware driver.
+
+ If unsure, say N.
diff --git a/drivers/misc/i2s/Makefile b/drivers/misc/i2s/Makefile
new file mode 100644
index 00000000000..75d361d5deb
--- /dev/null
+++ b/drivers/misc/i2s/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for I2S drivers
+#
+
+nmdk_i2s-objs := i2s.o
+obj-$(CONFIG_STM_I2S) += nmdk_i2s.o
+obj-$(CONFIG_STM_MSP_I2S) += msp_i2s.o
diff --git a/drivers/misc/i2s/i2s.c b/drivers/misc/i2s/i2s.c
new file mode 100644
index 00000000000..b4c243b7cb2
--- /dev/null
+++ b/drivers/misc/i2s/i2s.c
@@ -0,0 +1,631 @@
+/*----------------------------------------------------------------------------*/
+/* copyright STMicroelectronics, 2007. */
+/* */
+/* This program is free software; you can redistribute it and/or modify it */
+/* under the terms of the GNU General Public License as published by the Free */
+/* Software Foundation; either version 2.1 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, see <http://www.gnu.org/licenses/>. */
+/*----------------------------------------------------------------------------*/
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/cache.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/i2s/i2s.h>
+#include <linux/platform_device.h>
+
+/*******************************************************************************/
+static DEFINE_MUTEX(core_lock);
+
+static void i2sdev_release(struct device *dev)
+{
+ struct i2s_device *i2s = to_i2s_device(dev);
+
+ if (i2s->controller)
+ put_device(&(i2s->controller->dev));
+ kfree(dev);
+}
+static ssize_t
+modalias_show(struct device *dev, struct device_attribute *a, char *buf)
+{
+ const struct i2s_device *i2s = to_i2s_device(dev);
+ return sprintf(buf, "%s\n", i2s->modalias);
+}
+
+static struct device_attribute i2s_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+/* modalias support makes "modprobe $MODALIAS" new-style hotplug work,
+ * and the sysfs version makes coldplug work too.
+ */
+static const struct i2s_device_id *i2s_match_id(const struct i2s_device_id *id,
+ const struct i2s_device *device)
+{
+ while (id->name[0]) {
+ if (strcmp(device->modalias, id->name) == 0)
+ return id;
+ id++;
+ }
+ return NULL;
+}
+
+static int i2s_match_device(struct device *dev, struct device_driver *drv)
+{
+ const struct i2s_device *device = to_i2s_device(dev);
+ struct i2s_driver *driver = to_i2s_driver(drv);
+ if (driver->id_table)
+ return i2s_match_id(driver->id_table, device) != NULL;
+ return 0;
+}
+
+static int i2s_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ const struct i2s_device *i2s = to_i2s_device(dev);
+
+ add_uevent_var(env, "MODALIAS=%s", i2s->modalias);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int i2s_suspend(struct device *dev, pm_message_t message)
+{
+ int value = 0;
+ struct i2s_driver *drv = to_i2s_driver(dev->driver);
+
+ /* suspend will stop irqs and dma; no more i/o */
+ if (drv) {
+ if (drv->suspend)
+ value = drv->suspend(to_i2s_device(dev), message);
+ else
+ dev_dbg(dev, "... can't suspend\n");
+ }
+ return value;
+}
+
+static int i2s_resume(struct device *dev)
+{
+ int value = 0;
+ struct i2s_driver *drv = to_i2s_driver(dev->driver);
+
+ /* resume may restart the i/o queue */
+ if (drv) {
+ if (drv->resume)
+ value = drv->resume(to_i2s_device(dev));
+ else
+ dev_dbg(dev, "... can't resume\n");
+ }
+ return value;
+}
+
+#else
+#define i2s_suspend NULL
+#define i2s_resume NULL
+#endif
+
+/*This bus is designed to handle various protocols supported by the MSP- ARM Primecell IP
+ * such as
+ * I2s, PCM, AC97, TDM .... (refer to the data sheet for the complete list.
+ * Current MSP driver has the above ones coded.
+ * */
+struct bus_type i2s_bus_type = {
+ .name = "i2s",
+ .dev_attrs = i2s_dev_attrs,
+ .match = i2s_match_device,
+ .uevent = i2s_uevent,
+ .suspend = i2s_suspend,
+ .resume = i2s_resume,
+};
+
+EXPORT_SYMBOL_GPL(i2s_bus_type);
+
+static int i2s_drv_probe(struct device *dev)
+{
+ const struct i2s_driver *sdrv = to_i2s_driver(dev->driver);
+
+ return sdrv->probe(to_i2s_device(dev));
+}
+
+static int i2s_drv_remove(struct device *dev)
+{
+ const struct i2s_driver *sdrv = to_i2s_driver(dev->driver);
+
+ return sdrv->remove(to_i2s_device(dev));
+}
+
+static void i2s_drv_shutdown(struct device *dev)
+{
+ const struct i2s_driver *sdrv = to_i2s_driver(dev->driver);
+
+ sdrv->shutdown(to_i2s_device(dev));
+}
+
+/**
+ * i2s_register_driver - register a I2S driver
+ * @sdrv: the driver to register
+ * Context: can sleep
+ */
+int i2s_register_driver(struct i2s_driver *sdrv)
+{
+ sdrv->driver.bus = &i2s_bus_type;
+ if (sdrv->probe)
+ sdrv->driver.probe = i2s_drv_probe;
+ if (sdrv->remove)
+ sdrv->driver.remove = i2s_drv_remove;
+ if (sdrv->shutdown)
+ sdrv->driver.shutdown = i2s_drv_shutdown;
+ return driver_register(&sdrv->driver);
+}
+
+EXPORT_SYMBOL_GPL(i2s_register_driver);
+
+/******************************************************************************/
+struct board_i2s_combined_info {
+ struct i2s_board_info board_info;
+ struct i2s_device *i2s_dev_p;
+};
+struct boardinfo {
+ struct list_head list;
+ unsigned n_board_info;
+ struct board_i2s_combined_info board_i2s_info[0];
+};
+
+static LIST_HEAD(board_list);
+static DEFINE_MUTEX(board_lock);
+
+/*
+ * Get an i2s device. Used in MSP LTP tests.
+ */
+struct i2s_device *i2s_get_device_from_boardinfo(int chip_select)
+{
+ struct boardinfo *bi;
+ struct i2s_device *i2s_dev_p = NULL;
+
+ mutex_lock(&board_lock);
+ list_for_each_entry(bi, &board_list, list) {
+ struct board_i2s_combined_info *chip = bi->board_i2s_info;
+ unsigned n;
+
+ for (n = bi->n_board_info; n > 0; n--, chip++)
+ if (chip->board_info.chip_select == chip_select) {
+ i2s_dev_p = chip->i2s_dev_p;
+ break;
+ }
+ if (i2s_dev_p != NULL)
+ break;
+ }
+ mutex_unlock(&board_lock);
+
+ return i2s_dev_p;
+}
+
+EXPORT_SYMBOL_GPL(i2s_get_device_from_boardinfo);
+
+/* I2S devices should normally not be created by I2S device drivers; that
+ * would make them board-specific. Similarly with I2S master drivers.
+ * Device registration normally goes into like arch/.../mach.../board-YYY.c
+ * with other readonly (flashable) information about mainboard devices.
+ */
+struct i2s_device *i2s_alloc_device(struct device *device)
+{
+ struct i2s_device *i2s;
+ struct device *dev = device->parent;
+
+ get_device(device);
+ i2s = kzalloc(sizeof *i2s, GFP_KERNEL);
+ if (!i2s) {
+ dev_err(dev, "cannot alloc i2s_device\n");
+ return NULL;
+ }
+
+ i2s->dev.parent = dev;
+ i2s->dev.bus = &i2s_bus_type;
+ i2s->dev.release = i2sdev_release;
+ device_initialize(&i2s->dev);
+ return i2s;
+}
+
+EXPORT_SYMBOL_GPL(i2s_alloc_device);
+
+/**
+ * i2s_add_device - Add i2s_device allocated with i2s_alloc_device
+ * @i2s: i2s_device to register
+ *
+ * Companion function to i2s_alloc_device. Devices allocated with
+ * i2s_alloc_device can be added onto the i2s bus with this function.
+ *
+ * Returns 0 on success; negative errno on failure
+ */
+int i2s_add_device(struct i2s_device *i2s)
+{
+ static DEFINE_MUTEX(i2s_add_lock);
+ struct device *dev = i2s->dev.parent;
+ int status;
+
+ dev_set_name(&i2s->dev, "%s.%u", "i2s", i2s->chip_select);
+
+ mutex_lock(&i2s_add_lock);
+
+ if (bus_find_device_by_name(&i2s_bus_type, NULL, dev_name(&i2s->dev))
+ != NULL) {
+ dev_err(dev, "chipselect %d already in use\n",
+ i2s->chip_select);
+ status = -EBUSY;
+ goto done;
+ }
+
+ /* Device may be bound to an active driver when this returns */
+ status = device_add(&i2s->dev);
+ if (status < 0)
+ dev_err(dev, "can't %s %s, status %d\n",
+ "add", dev_name(&i2s->dev), status);
+ else
+ dev_dbg(dev, "registered child %s\n", dev_name(&i2s->dev));
+
+ done:
+ mutex_unlock(&i2s_add_lock);
+ return status;
+}
+
+EXPORT_SYMBOL_GPL(i2s_add_device);
+
+/**
+ * i2s_new_device - instantiate one new I2S device
+ * @i2s_cont: Controller to which device is connected
+ * @chip: Describes the I2S device
+ * Context: can sleep
+ *
+ * On typical mainboards, this is purely internal; and it's not needed
+ * after board init creates the hard-wired devices. Some development
+ * platforms may not be able to use i2s_register_board_info though, and
+ * this is exported so that driver could add devices (which it would
+ * learn about out-of-band).
+ *
+ * Returns the new device, or NULL.
+ */
+struct i2s_device *i2s_new_device(struct i2s_controller *i2s_cont,
+ struct i2s_board_info *chip)
+{
+ struct i2s_device *proxy;
+ int status;
+
+ /* NOTE: caller did any chip->bus_num checks necessary.
+ *
+ * Also, unless we change the return value convention to use
+ * error-or-pointer (not NULL-or-pointer), troubleshootability
+ * suggests syslogged diagnostics are best here (ugh).
+ */
+
+ proxy = i2s_alloc_device(&i2s_cont->dev);
+ if (!proxy)
+ return NULL;
+
+ WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
+
+ proxy->chip_select = chip->chip_select;
+ strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
+ proxy->dev.platform_data = (void *)chip->platform_data;
+ proxy->controller = i2s_cont;
+
+ status = i2s_add_device(proxy);
+ if (status < 0) {
+ kfree(proxy);
+ return NULL;
+ }
+
+ return proxy;
+}
+
+EXPORT_SYMBOL_GPL(i2s_new_device);
+
+/**
+ * i2s_register_board_info - register I2S devices for a given board
+ * @info: array of chip descriptors
+ * @n: how many descriptors are provided
+ * Context: can sleep
+ *
+ * Board-specific early init code calls this (probably during arch_initcall)
+ * with segments of the I2S device table. Any device nodes are created later,
+ * after the relevant parent I2S controller (id) is defined. We keep
+ * this table of devices forever, so that reloading a controller driver will
+ * not make Linux forget about these hard-wired devices.
+ *
+ */
+int __init
+i2s_register_board_info(struct i2s_board_info const *info, unsigned n)
+{
+ int i;
+ struct boardinfo *bi;
+
+ bi = kmalloc(sizeof(*bi) + (n * sizeof(struct board_i2s_combined_info)), GFP_KERNEL);
+ if (!bi)
+ return -ENOMEM;
+ bi->n_board_info = n;
+
+ for (i = 0; i < n; i++)
+ memcpy(&bi->board_i2s_info[i].board_info, &info[i], sizeof *info);
+
+ mutex_lock(&board_lock);
+ list_add_tail(&bi->list, &board_list);
+ mutex_unlock(&board_lock);
+ return 0;
+}
+
+/**
+ * scan_boardinfo - Scan, creates and registered new i2s device structure.
+ * @i2s_cont: i2s controller structure
+ * Context: process
+ *
+ * It will scan the device list that may be registered statically using
+ * register_board_info func in arch specific directory and call
+ * i2s_new_device to create and registered i2s device over i2s bus. It is
+ * called by i2s_add_controller function.
+ *
+ * Returns void.
+ */
+static void scan_boardinfo(struct i2s_controller *i2s_cont)
+{
+ struct boardinfo *bi;
+
+ mutex_lock(&board_lock);
+ list_for_each_entry(bi, &board_list, list) {
+ struct board_i2s_combined_info *chip = bi->board_i2s_info;
+ unsigned n;
+
+ for (n = bi->n_board_info; n > 0; n--, chip++) {
+ if (chip->board_info.chip_select != i2s_cont->id)
+ continue;
+ /* NOTE: this relies on i2s_new_device to
+ * issue diagnostics when given bogus inputs
+ */
+ chip->i2s_dev_p = i2s_new_device(i2s_cont, &chip->board_info);
+ }
+ }
+ mutex_unlock(&board_lock);
+}
+
+/******************************************************************************/
+/**I2S Controller inittialization*/
+static void i2s_controller_dev_release(struct device *dev)
+{
+ struct i2s_controller *i2s_cont;
+ i2s_cont = container_of(dev, struct i2s_controller, dev);
+ kfree(i2s_cont);
+}
+
+static ssize_t
+show_controller_name(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2s_controller *cont = to_i2s_controller(dev);
+ return sprintf(buf, "%s\n", cont->name);
+}
+
+static struct device_attribute i2s_controller_attrs[] = {
+ __ATTR(name, S_IRUGO, show_controller_name, NULL),
+ {},
+};
+
+static struct class i2s_controller_class = {
+ .owner = THIS_MODULE,
+ .name = "i2s-controller",
+ .dev_attrs = i2s_controller_attrs,
+};
+
+static int i2s_register_controller(struct i2s_controller *cont)
+{
+ int res = 0;
+ mutex_init(&cont->bus_lock);
+
+ mutex_lock(&core_lock);
+
+ /* Add the controller to the driver core.
+ * If the parent pointer is not set up,
+ * we add this controller to the host bus.
+ */
+ if (cont->dev.parent == NULL) {
+ cont->dev.parent = &platform_bus;
+ pr_debug("I2S controller driver [%s] forgot to specify "
+ "physical device\n", cont->name);
+ }
+ dev_set_name(&cont->dev, "I2Scrlr-%d", cont->id);
+ cont->dev.release = &i2s_controller_dev_release;
+ cont->dev.class = &i2s_controller_class;
+ res = device_register(&cont->dev);
+ if (res)
+ goto out_unlock;
+
+ dev_dbg(&cont->dev, "controller [%s] registered\n", cont->name);
+ scan_boardinfo(cont);
+ out_unlock:
+ mutex_unlock(&core_lock);
+ return res;
+}
+
+/**
+ * i2s_add_controller - declare i2s controller, use dynamic bus number
+ * @controller: the controller to add
+ * Context: can sleep
+ *
+ */
+int i2s_add_controller(struct i2s_controller *controller)
+{
+ return i2s_register_controller(controller);
+}
+
+EXPORT_SYMBOL(i2s_add_controller);
+
+static int __unregister(struct device *dev, void *controller_dev)
+{
+ /* note: before about 2.6.14-rc1 this would corrupt memory: */
+ if (dev != controller_dev)
+ i2s_unregister_device(to_i2s_device(dev));
+ return 0;
+}
+
+/**
+ * i2s_del_controller - unregister I2S controller
+ * @cont: the controller being unregistered
+ * Context: can sleep
+ *
+ * This unregisters an I2S controller which was previously registered
+ * by @i2s_add_controller.
+ */
+int i2s_del_controller(struct i2s_controller *cont)
+{
+ int res = 0;
+ int dummy;
+ mutex_lock(&core_lock);
+
+ dummy = device_for_each_child(cont->dev.parent, &cont->dev,
+ __unregister);
+ device_unregister(&cont->dev);
+ mutex_unlock(&core_lock);
+ return res;
+}
+
+EXPORT_SYMBOL(i2s_del_controller);
+
+/******************************************************************************/
+/*I2S interface apis*/
+
+/**
+ * i2s_transfer - Main i2s transfer function.
+ * @i2s_cont: i2s controller structure passed by client driver.
+ * @message: i2s message structure contains transceive info.
+ * Context: process or interrupt.
+ *
+ * This API is called by client i2s driver as i2s_xfer funtion. It will handle
+ * main i2s transfer over i2s bus. The controller should registered its own
+ * functions using i2s algorithm structure.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+int i2s_transfer(struct i2s_controller *i2s_cont, struct i2s_message *message)
+{
+ return i2s_cont->algo->cont_transfer(i2s_cont, message);
+
+}
+
+EXPORT_SYMBOL(i2s_transfer);
+
+/**
+ * i2s_cleanup - Close the current i2s connection btw controller and client.
+ * @i2s_cont: i2s controller structure
+ * @flag: It indicates the functionality that needs to be disabled.
+ * Context: process
+ *
+ * This API will disable and reset the controller's configuration. Reset the
+ * controller so that i2s client driver can reconfigure with new configuration.
+ * Controller should release all the necessary resources which was acquired
+ * during setup.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+int i2s_cleanup(struct i2s_controller *i2s_cont, i2s_flag flag)
+{
+ int status = 0;
+ status = i2s_cont->algo->cont_cleanup(i2s_cont, flag);
+ if (status)
+ return -1;
+ else
+ return 0;
+}
+
+EXPORT_SYMBOL(i2s_cleanup);
+
+/**
+ * i2s_setup - configures and enables the I2S controller.
+ * @i2s_cont: i2s controller sent by i2s device.
+ * @config: specifies the configuration parameters.
+ *
+ * This function configures the I2S controller with the client configuration.
+ * Controller was already registered on I2S bus by some master controller
+ * driver.
+ *
+ * Returns error(-1) in case of failure else success(0)
+ */
+int i2s_setup(struct i2s_controller *i2s_cont, void *config)
+{
+ return i2s_cont->algo->cont_setup(i2s_cont, config);
+}
+
+EXPORT_SYMBOL(i2s_setup);
+
+/**
+ * i2s_hw_status - Get the current hw status for the i2s controller.
+ * @i2s_cont: i2s controller structure passed by client driver.
+ * Context: process or interrupt.
+ *
+ * This API is called by client i2s driver to find out current hw status.
+ * The controller should registered its own functions using i2s algorithm structure.
+ *
+ * Returns current hw status register.
+ */
+int i2s_hw_status(struct i2s_controller *i2s_cont)
+{
+ return i2s_cont->algo->cont_hw_status(i2s_cont);
+}
+
+/**
+ * i2s_get_pointer - Get the current dma_addr_t for the i2s controller.
+ * @i2s_cont: i2s controller structure passed by client driver.
+ * @i2s_direction: Specifies TX or RX direction.
+ * Context: process or interrupt.
+ *
+ * This API is called by client i2s driver to return a dma_addr_t corresponding
+ * to the position of the DMA-controller.
+ * The controller should registered its own functions using i2s algorithm structure.
+ *
+ * Returns current hw status register.
+ */
+dma_addr_t i2s_get_pointer(struct i2s_controller *i2s_cont,
+ enum i2s_direction_t i2s_direction)
+{
+ return i2s_cont->algo->cont_get_pointer(i2s_cont, i2s_direction);
+}
+
+/******************************************************************************/
+
+static int __init i2s_init(void)
+{
+ int status;
+
+ status = bus_register(&i2s_bus_type);
+ if (status < 0)
+ goto err0;
+
+ status = class_register(&i2s_controller_class);
+ if (status < 0)
+ goto err1;
+ return 0;
+
+ err1:
+ bus_unregister(&i2s_bus_type);
+ err0:
+ return status;
+}
+
+static void __exit i2s_exit(void)
+{
+ class_unregister(&i2s_controller_class);
+ bus_unregister(&i2s_bus_type);
+}
+
+subsys_initcall(i2s_init);
+module_exit(i2s_exit);
+
+MODULE_AUTHOR("Sandeep Kaushik, <sandeep-mmc.kaushik@st.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/i2s/msp_i2s.c b/drivers/misc/i2s/msp_i2s.c
new file mode 100644
index 00000000000..6cd04a0b699
--- /dev/null
+++ b/drivers/misc/i2s/msp_i2s.c
@@ -0,0 +1,2046 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/pfn.h>
+
+#include <linux/regulator/consumer.h>
+#include <mach/prcmu-fw-api.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <linux/dmaengine.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <linux/i2s/i2s.h>
+#include <mach/msp.h>
+#include <linux/dma-mapping.h>
+
+struct regulator *msp_vape_supply;
+
+#define STM_MSP_NAME "STM_MSP"
+#define MSP_NAME "msp"
+#define DRIVER_DEBUG_PFX "MSP"
+#define DRIVER_DEBUG CONFIG_STM_MSP_DEBUG
+#define DRIVER_DBG "MSP"
+#define NMDK_DBG /* message level */
+
+extern struct driver_debug_st DBG_ST;
+ /* Protocol desciptors */
+static const struct msp_protocol_desc protocol_desc_tab[] = {
+ I2S_PROTOCOL_DESC,
+ PCM_PROTOCOL_DESC,
+ PCM_COMPAND_PROTOCOL_DESC,
+ AC97_PROTOCOL_DESC,
+ SPI_MASTER_PROTOCOL_DESC,
+ SPI_SLAVE_PROTOCOL_DESC,
+};
+
+/* Local static functions */
+static int msp_dma_xfer(struct msp *msp, struct i2s_message *msg);
+static int msp_polling_xfer(struct msp *msp, struct i2s_message *msg);
+static int msp_interrupt_xfer(struct msp *msp, struct i2s_message *msg);
+static int msp_start_dma(struct msp *msp, int transmit, dma_addr_t data,
+ size_t bytes);
+static int configure_protocol(struct msp *msp,
+ struct msp_config *config);
+static int configure_clock(struct msp *msp,
+ struct msp_config *config);
+static int configure_multichannel(struct msp *msp,
+ struct msp_config *config);
+static int stm_msp_configure_enable(struct i2s_controller *i2s_cont,
+ void *configuration);
+static int stm_msp_transceive_data(struct i2s_controller *i2s_cont,
+ struct i2s_message *message);
+
+static int stm_msp_disable(struct msp *msp, int direction,
+ i2s_flag flag);
+static int stm_msp_close(struct i2s_controller *i2s_cont, i2s_flag flag);
+static int stm_msp_hw_status(struct i2s_controller *i2s_cont);
+static dma_addr_t stm_msp_get_pointer(struct i2s_controller *i2s_cont,
+ enum i2s_direction_t i2s_direction);
+
+#define I2S_DEVICE "i2s_device"
+static struct i2s_algorithm i2s_algo = {
+ .cont_setup = stm_msp_configure_enable,
+ .cont_transfer = stm_msp_transceive_data,
+ .cont_cleanup = stm_msp_close,
+ .cont_hw_status = stm_msp_hw_status,
+ .cont_get_pointer = stm_msp_get_pointer,
+};
+
+/**
+ * stm_msp_write - writel a value to specified register
+ * @value: value
+ * @reg: pointer to register' address
+ * Context: atomic(can be both process and interrupt)
+ * Returns void.
+ */
+static inline void stm_msp_write(u32 value, void __iomem *reg)
+{
+ writel(value, reg);
+}
+
+/**
+ * stm_msp_read - readl a value to specified register
+ * @reg: pointer to register' address
+ * Context: atomic(can be both process and interrupt)
+ * Returns u32 register's value.
+ */
+static inline u32 stm_msp_read(void __iomem *reg)
+{
+ return readl(reg);
+}
+
+static void u8_msp_read(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->rx_offset < message->rxbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ RX_FIFO_EMPTY)) {
+ message->rx_offset += 1;
+ *(u8 *) message->rxdata =
+ (u8) stm_msp_read(xfer_data->msp->registers + MSP_DR);
+ message->rxdata += 1;
+ }
+}
+
+static void u16_msp_read(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->rx_offset < message->rxbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ RX_FIFO_EMPTY)) {
+ message->rx_offset += 2;
+ *(u16 *) message->rxdata =
+ (u16) stm_msp_read(xfer_data->msp->registers + MSP_DR);
+ message->rxdata += 2;
+ }
+}
+
+/**
+ * u32_msp_read - Msp 32bit read function.
+ * @xfer_data: transfer data structure.
+ *
+ * It reads 32bit data from msp receive fifo until it gets empty.
+ *
+ * Returns void.
+ */
+static void u32_msp_read(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->rx_offset < message->rxbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ RX_FIFO_EMPTY)) {
+ *(u32 *) message->rxdata =
+ (u32) stm_msp_read(xfer_data->msp->registers + MSP_DR);
+ message->rx_offset += 4;
+ message->rxdata += 4;
+ }
+}
+static void u8_msp_write(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->tx_offset < message->txbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ TX_FIFO_FULL)) {
+ message->tx_offset += 1;
+ stm_msp_write(*(u8 *) message->txdata,
+ xfer_data->msp->registers + MSP_DR);
+ message->txdata += 1;
+ }
+}
+
+static void u16_msp_write(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->tx_offset < message->txbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ TX_FIFO_FULL)) {
+ message->tx_offset += 2;
+ stm_msp_write(*(u16 *) message->txdata,
+ xfer_data->msp->registers + MSP_DR);
+ message->txdata += 2;
+ }
+}
+
+/**
+ * u32_msp_write - Msp 32bit write function.
+ * @xfer_data: transfer data structure.
+ *
+ * It writes 32bit data to msp transmit fifo until it gets full.
+ *
+ * Returns void.
+ */
+static void u32_msp_write(struct trans_data *xfer_data)
+{
+ struct i2s_message *message = &xfer_data->message;
+ while ((message->tx_offset < message->txbytes) &&
+ !((stm_msp_read(xfer_data->msp->registers + MSP_FLR)) &
+ TX_FIFO_FULL)) {
+ message->tx_offset += 4;
+ stm_msp_write(*(u32 *) message->txdata,
+ xfer_data->msp->registers + MSP_DR);
+ message->txdata += 4;
+ }
+}
+
+/**
+ * set_transmit_protocol_descriptor - Set the Transmit Configuration register.
+ * @msp: main msp controller structure.
+ * @protocol_desc: pointer to protocol descriptor structure.
+ * @data_size: Run time configurable element length.
+ *
+ * It will setup transmit configuration register of msp.
+ * Various values related to a particular protocol can be set like, elemnet
+ * length, frame length, endianess etc.
+ *
+ * Returns void.
+ */
+static void set_transmit_protocol_descriptor(struct msp *msp,
+ struct msp_protocol_desc
+ *protocol_desc,
+ enum msp_data_size data_size)
+{
+ u32 temp_reg = 0;
+
+ temp_reg |= MSP_P2_ENABLE_BIT(protocol_desc->tx_phase_mode);
+ temp_reg |= MSP_P2_START_MODE_BIT(protocol_desc->tx_phase2_start_mode);
+ temp_reg |= MSP_P1_FRAME_LEN_BITS(protocol_desc->tx_frame_length_1);
+ temp_reg |= MSP_P2_FRAME_LEN_BITS(protocol_desc->tx_frame_length_2);
+ if (msp->def_elem_len) {
+ temp_reg |=
+ MSP_P1_ELEM_LEN_BITS(protocol_desc->tx_element_length_1);
+ temp_reg |=
+ MSP_P2_ELEM_LEN_BITS(protocol_desc->tx_element_length_2);
+ if (protocol_desc->tx_element_length_1 ==
+ protocol_desc->tx_element_length_2) {
+ msp->actual_data_size =
+ protocol_desc->tx_element_length_1;
+ } else {
+ msp->actual_data_size = data_size;
+ }
+ } else {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+ msp->actual_data_size = data_size;
+ }
+ temp_reg |= MSP_DATA_DELAY_BITS(protocol_desc->tx_data_delay);
+ temp_reg |=
+ MSP_SET_ENDIANNES_BIT(protocol_desc->tx_bit_transfer_format);
+ temp_reg |= MSP_FRAME_SYNC_POL(protocol_desc->tx_frame_sync_pol);
+ temp_reg |= MSP_DATA_WORD_SWAP(protocol_desc->tx_half_word_swap);
+ temp_reg |= MSP_SET_COMPANDING_MODE(protocol_desc->compression_mode);
+ temp_reg |= MSP_SET_FRAME_SYNC_IGNORE(protocol_desc->frame_sync_ignore);
+
+ stm_msp_write(temp_reg, msp->registers + MSP_TCF);
+}
+
+/**
+ * set_receive_protocol_descriptor - Set the Receive Configuration register.
+ * @msp: main msp controller structure.
+ * @protocol_desc: pointer to protocol descriptor structure.
+ * @data_size: Run time configurable element length.
+ *
+ * It will setup receive configuration register of msp.
+ * Various values related to a particular protocol can be set like, elemnet
+ * length, frame length, endianess etc.
+ *
+ * Returns void.
+ */
+static void set_receive_protocol_descriptor(struct msp *msp,
+ struct msp_protocol_desc
+ *protocol_desc,
+ enum msp_data_size
+ data_size)
+{
+ u32 temp_reg = 0;
+
+ temp_reg |= MSP_P2_ENABLE_BIT(protocol_desc->rx_phase_mode);
+ temp_reg |= MSP_P2_START_MODE_BIT(protocol_desc->rx_phase2_start_mode);
+ temp_reg |= MSP_P1_FRAME_LEN_BITS(protocol_desc->rx_frame_length_1);
+ temp_reg |= MSP_P2_FRAME_LEN_BITS(protocol_desc->rx_frame_length_2);
+ if (msp->def_elem_len) {
+ temp_reg |=
+ MSP_P1_ELEM_LEN_BITS(protocol_desc->rx_element_length_1);
+ temp_reg |=
+ MSP_P2_ELEM_LEN_BITS(protocol_desc->rx_element_length_2);
+ if (protocol_desc->rx_element_length_1 ==
+ protocol_desc->rx_element_length_2) {
+ msp->actual_data_size =
+ protocol_desc->rx_element_length_1;
+ } else {
+ msp->actual_data_size = data_size;
+ }
+ } else {
+ temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+ temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+ msp->actual_data_size = data_size;
+ }
+
+ temp_reg |= MSP_DATA_DELAY_BITS(protocol_desc->rx_data_delay);
+ temp_reg |=
+ MSP_SET_ENDIANNES_BIT(protocol_desc->rx_bit_transfer_format);
+ temp_reg |= MSP_FRAME_SYNC_POL(protocol_desc->rx_frame_sync_pol);
+ temp_reg |= MSP_DATA_WORD_SWAP(protocol_desc->rx_half_word_swap);
+ temp_reg |= MSP_SET_COMPANDING_MODE(protocol_desc->expansion_mode);
+ temp_reg |= MSP_SET_FRAME_SYNC_IGNORE(protocol_desc->frame_sync_ignore);
+
+ stm_msp_write(temp_reg, msp->registers + MSP_RCF);
+
+}
+
+/**
+ * configure_protocol - Configures transmit and receive protocol.
+ * @msp: main msp controller structure.
+ * @config: configuration structure passed by client driver
+ *
+ * This will configure transmit and receive protocol decriptors.
+ *
+ * Returns error(-1) on failure else success(0).
+ */
+static int configure_protocol(struct msp *msp,
+ struct msp_config *config)
+{
+ int direction;
+ struct msp_protocol_desc *protocol_desc;
+ enum msp_data_size data_size;
+ u32 temp_reg = 0;
+
+ data_size = config->data_size;
+ msp->def_elem_len = config->def_elem_len;
+ direction = config->direction;
+ if (config->default_protocol_desc == 1) {
+ if (config->protocol >= MSP_INVALID_PROTOCOL) {
+ printk(KERN_ERR
+ "invalid protocol in configure_protocol()\n");
+ return -EINVAL;
+ }
+ protocol_desc =
+ (struct msp_protocol_desc *)&protocol_desc_tab[config->
+ protocol];
+ } else {
+ protocol_desc =
+ (struct msp_protocol_desc *)&config->protocol_desc;
+ }
+
+ if (data_size < MSP_DATA_BITS_DEFAULT
+ || data_size > MSP_DATA_BITS_32) {
+ printk(KERN_ERR
+ "invalid data size requested in configure_protocol()\n");
+ return -EINVAL;
+ }
+
+ switch (direction) {
+ case MSP_TRANSMIT_MODE:
+ set_transmit_protocol_descriptor(msp, protocol_desc, data_size);
+ break;
+ case MSP_RECEIVE_MODE:
+ set_receive_protocol_descriptor(msp, protocol_desc, data_size);
+ break;
+ case MSP_BOTH_T_R_MODE:
+ set_transmit_protocol_descriptor(msp, protocol_desc, data_size);
+ set_receive_protocol_descriptor(msp, protocol_desc, data_size);
+ break;
+ default:
+ printk(KERN_ERR "Invalid direction given\n");
+ return -EINVAL;
+ }
+ /* The below code is needed for both Rx and Tx path can't separate
+ * them.
+ */
+ temp_reg = stm_msp_read(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING;
+ temp_reg |= MSP_TX_CLKPOL_BIT(protocol_desc->tx_clock_pol);
+ stm_msp_write(temp_reg, msp->registers + MSP_GCR);
+ temp_reg = stm_msp_read(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING;
+ temp_reg |= MSP_RX_CLKPOL_BIT(protocol_desc->rx_clock_pol);
+ stm_msp_write(temp_reg, msp->registers + MSP_GCR);
+
+ return 0;
+}
+
+/**
+ * configure_clock - Set clock in sample rate generator.
+ * @msp: main msp controller structure.
+ * @config: configuration structure passed by client driver
+ *
+ * This will set the frame width and period. Also enable sample rate generator
+ *
+ * Returns error(-1) on failure else success(0).
+ */
+static int configure_clock(struct msp *msp,
+ struct msp_config *config)
+{
+
+ u32 dummy;
+ u32 frame_per = 0;
+ u32 sck_div = 0;
+ u32 frame_width = 0;
+ u32 temp_reg = 0;
+ u32 bit_clock = 0;
+ struct msp_protocol_desc *protocol_desc = NULL;
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~(SRG_ENABLE))), msp->registers + MSP_GCR);
+
+ if (config->default_protocol_desc) {
+ protocol_desc =
+ (struct msp_protocol_desc *)&protocol_desc_tab[config->
+ protocol];
+ } else {
+ protocol_desc =
+ (struct msp_protocol_desc *)&config->protocol_desc;
+ }
+
+ switch (config->protocol) {
+ case MSP_PCM_PROTOCOL:
+ case MSP_PCM_COMPAND_PROTOCOL:
+ frame_width = protocol_desc->frame_width;
+ sck_div =
+ config->input_clock_freq / (config->frame_freq *
+ (protocol_desc->
+ total_clocks_for_one_frame));
+ frame_per = protocol_desc->frame_period;
+ break;
+ case MSP_I2S_PROTOCOL:
+ frame_width = protocol_desc->frame_width;
+ sck_div =
+ config->input_clock_freq / (config->frame_freq *
+ (protocol_desc->
+ total_clocks_for_one_frame));
+ frame_per = protocol_desc->frame_period;
+
+ break;
+ case MSP_AC97_PROTOCOL:
+ /* Not supported */
+ printk(KERN_WARNING "AC97 protocol not supported\n");
+ return -ENOSYS;
+ default:
+ printk(KERN_ERR "Invalid mode attempted for setting clocks\n");
+ return -EINVAL;
+ }
+
+ temp_reg = (sck_div - 1) & SCK_DIV_MASK;
+ temp_reg |= FRAME_WIDTH_BITS(frame_width);
+ temp_reg |= FRAME_PERIOD_BITS(frame_per);
+ stm_msp_write(temp_reg, msp->registers + MSP_SRG);
+
+ /* Input clock frequency value configured is in MHz/1000 */
+ bit_clock = (config->input_clock_freq * 1000)/(sck_div + 1);
+
+ /* If the bit clock is higher than 19.2MHz, Vape should be run in 100% OPP */
+ /* Only consider OPP 100% when bit-clock is used, i.e. MSP master mode */
+ if ((bit_clock > 19200000) && ((config->tx_clock_sel != 0) || (config->rx_clock_sel != 0))) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "msp_i2s", 100);
+ msp->vape_opp_constraint = 1;
+ } else {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "msp_i2s", 50);
+ msp->vape_opp_constraint = 0;
+ }
+
+ /* Wait a bit */
+ dummy = ((stm_msp_read(msp->registers + MSP_SRG)) >> FRWID_SHIFT) & 0x0000003F;
+
+ /* Enable clock */
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) | ((SRG_ENABLE))),
+ msp->registers + MSP_GCR);
+
+ /* Another wait */
+ dummy =
+ ((stm_msp_read(msp->registers + MSP_SRG)) >> FRWID_SHIFT) &
+ 0x0000003F;
+ return 0;
+}
+
+/**
+ * configure_multichannel - Enable multichannel support for transmit & receive.
+ * @msp: main msp controller structure.
+ * @config: configuration structure passed by client driver
+ *
+ * This will enable multichannel support for transmit and receive.
+ * It will set Receive comparator also if configured.
+ *
+ * Returns error(-1) on failure else success(0).
+ */
+static int configure_multichannel(struct msp *msp,
+ struct msp_config *config)
+{
+ struct msp_protocol_desc *protocol_desc;
+ struct msp_multichannel_config *mult_config;
+ if (config->default_protocol_desc == 1) {
+ if (config->protocol >= MSP_INVALID_PROTOCOL) {
+ printk(KERN_ERR
+ "invalid protocol in configure_protocol()\n");
+ return -EINVAL;
+ }
+ protocol_desc =
+ (struct msp_protocol_desc *)&protocol_desc_tab[config->
+ protocol];
+ } else {
+ protocol_desc =
+ (struct msp_protocol_desc *)&config->protocol_desc;
+ }
+ mult_config = &config->multichannel_config;
+ if (true == mult_config->tx_multichannel_enable) {
+ if (MSP_SINGLE_PHASE == protocol_desc->tx_phase_mode) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_MCR) |
+ ((mult_config->
+ tx_multichannel_enable << TMCEN_BIT) &
+ (0x0000020))),
+ msp->registers + MSP_MCR);
+ stm_msp_write(mult_config->tx_channel_0_enable,
+ msp->registers + MSP_TCE0);
+ stm_msp_write(mult_config->tx_channel_1_enable,
+ msp->registers + MSP_TCE1);
+ stm_msp_write(mult_config->tx_channel_2_enable,
+ msp->registers + MSP_TCE2);
+ stm_msp_write(mult_config->tx_channel_3_enable,
+ msp->registers + MSP_TCE3);
+ } else {
+ printk(KERN_ERR "Not in authorised mode\n");
+ return -1;
+ }
+ }
+ if (true == mult_config->rx_multichannel_enable) {
+ if (MSP_SINGLE_PHASE == protocol_desc->rx_phase_mode) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_MCR) |
+ ((mult_config->
+ rx_multichannel_enable << RMCEN_BIT) &
+ (0x0000001))),
+ msp->registers + MSP_MCR);
+ stm_msp_write(mult_config->rx_channel_0_enable,
+ msp->registers + MSP_RCE0);
+ stm_msp_write(mult_config->rx_channel_1_enable,
+ msp->registers + MSP_RCE1);
+ stm_msp_write(mult_config->rx_channel_2_enable,
+ msp->registers + MSP_RCE2);
+ stm_msp_write(mult_config->rx_channel_3_enable,
+ msp->registers + MSP_RCE3);
+ } else {
+ printk(KERN_ERR "Not in authorised mode\n");
+ return -1;
+ }
+ if (mult_config->rx_comparison_enable_mode) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_MCR) |
+ ((mult_config->
+ rx_comparison_enable_mode << RCMPM_BIT)
+ & (0x0000018))),
+ msp->registers + MSP_MCR);
+
+ stm_msp_write(mult_config->comparison_mask,
+ msp->registers + MSP_RCM);
+ stm_msp_write(mult_config->comparison_value,
+ msp->registers + MSP_RCV);
+
+ }
+ }
+ return 0;
+
+}
+
+/**
+ * configure_dma - configure dma channel for transmit or receive.
+ * @msp: msp structure
+ * @config: configuration structure.
+ * Context: process
+ *
+ * It will configure dma channels and request them in Logical mode for both
+ * transmit and recevie modes.It also register the respective callback handlers
+ * for DMA.
+ *
+ * Returns void.
+ */
+void configure_dma(struct msp *msp, struct msp_config *config)
+{
+ struct stedma40_chan_cfg *rx_dma_info = msp->dma_cfg_rx;
+ struct stedma40_chan_cfg *tx_dma_info = msp->dma_cfg_tx;
+ dma_cap_mask_t mask;
+
+ if (config->direction == MSP_TRANSMIT_MODE
+ || config->direction == MSP_BOTH_T_R_MODE) {
+
+ if (msp->tx_pipeid != NULL) {
+ dma_release_channel(msp->tx_pipeid);
+ msp->tx_pipeid = NULL;
+ }
+
+ if (config->data_size == MSP_DATA_BITS_32)
+ tx_dma_info->src_info.data_width = STEDMA40_WORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_16)
+ tx_dma_info->src_info.data_width
+ = STEDMA40_HALFWORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_8)
+ tx_dma_info->src_info.data_width
+ = STEDMA40_BYTE_WIDTH;
+ else
+ printk(KERN_ERR "Wrong data size\n");
+
+ if (config->data_size == MSP_DATA_BITS_32)
+ tx_dma_info->dst_info.data_width = STEDMA40_WORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_16)
+ tx_dma_info->dst_info.data_width
+ = STEDMA40_HALFWORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_8)
+ tx_dma_info->dst_info.data_width
+ = STEDMA40_BYTE_WIDTH;
+ else
+ printk(KERN_ERR "Wrong data size\n");
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ msp->tx_pipeid = dma_request_channel(mask, stedma40_filter,
+ tx_dma_info);
+ }
+ if (config->direction == MSP_RECEIVE_MODE
+ || config->direction == MSP_BOTH_T_R_MODE) {
+
+ if (msp->rx_pipeid != NULL) {
+ dma_release_channel(msp->rx_pipeid);
+ msp->rx_pipeid = NULL;
+ }
+
+ if (config->data_size == MSP_DATA_BITS_32)
+ rx_dma_info->src_info.data_width = STEDMA40_WORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_16)
+ rx_dma_info->src_info.data_width
+ = STEDMA40_HALFWORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_8)
+ rx_dma_info->src_info.data_width = STEDMA40_BYTE_WIDTH;
+ else
+ printk(KERN_ERR "Wrong data size\n");
+
+ if (config->data_size == MSP_DATA_BITS_32)
+ rx_dma_info->dst_info.data_width = STEDMA40_WORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_16)
+ rx_dma_info->dst_info.data_width
+ = STEDMA40_HALFWORD_WIDTH;
+ else if (config->data_size == MSP_DATA_BITS_8)
+ rx_dma_info->dst_info.data_width = STEDMA40_BYTE_WIDTH;
+ else
+ printk(KERN_ERR "Wrong data size\n");
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ msp->rx_pipeid = dma_request_channel(mask, stedma40_filter,
+ rx_dma_info);
+ }
+
+}
+
+/**
+ * msp_enable - Setup the msp configuration.
+ * @msp: msp data contains main msp structure.
+ * @config: configuration structure sent by i2s client driver.
+ * Context: process
+ *
+ * Main msp configuring functions to configure msp in accordance with msp
+ * protocol descriptor, configuring msp clock,setup transfer mode selected by
+ * user like DMA, interrupt or polling and in the end enable RX and Tx path.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int msp_enable(struct msp *msp, struct msp_config *config)
+{
+ int status = 0;
+ int state;
+
+ /* Check msp state whether in RUN or CONFIGURED Mode */
+ state = msp->msp_state;
+ if (state == MSP_STATE_IDLE) {
+ if (msp->plat_init) {
+ status = msp->plat_init();
+ if (status) {
+ printk(KERN_ERR "Error in msp_i2s_init,"
+ " status is %d\n", status);
+ return status;
+ }
+ }
+ }
+
+ /* Configure msp with protocol dependent settings */
+ configure_protocol(msp, config);
+ configure_clock(msp, config);
+ if (config->multichannel_configured == 1) {
+ status = configure_multichannel(msp, config);
+ if (status)
+ printk(KERN_ERR "multichannel can't be configured\n");
+ }
+ msp->work_mode = config->work_mode;
+
+ if (msp->work_mode == MSP_DMA_MODE && !msp->dma_cfg_rx) {
+ switch (config->direction) {
+ case MSP_RECEIVE_MODE:
+ case MSP_BOTH_T_R_MODE:
+ dev_err(&msp->i2s_cont->dev, "RX DMA not available");
+ return -EINVAL;
+ }
+ }
+
+ if (msp->work_mode == MSP_DMA_MODE && !msp->dma_cfg_tx) {
+ switch (config->direction) {
+ case MSP_TRANSMIT_MODE:
+ case MSP_BOTH_T_R_MODE:
+ dev_err(&msp->i2s_cont->dev, "TX DMA not available");
+ return -EINVAL;
+ }
+ }
+
+ switch (config->direction) {
+ case MSP_TRANSMIT_MODE:
+ /*Currently they are ignored
+ stm_msp_write((stm_msp_read(msp->registers + MSP_IMSC) |
+ TRANSMIT_UNDERRUN_ERR_INT |
+ TRANSMIT_FRAME_SYNC_ERR_INT),
+ msp->registers + MSP_IMSC); */
+ if (config->work_mode == MSP_DMA_MODE) {
+ stm_msp_write(stm_msp_read(msp->registers + MSP_DMACR) |
+ TX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+
+ msp->xfer_data.tx_handler = config->handler;
+ msp->xfer_data.tx_callback_data =
+ config->tx_callback_data;
+ configure_dma(msp, config);
+ }
+ if (config->work_mode == MSP_POLLING_MODE) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (TX_ENABLE)), msp->registers + MSP_GCR);
+ }
+ if (msp->work_mode != MSP_DMA_MODE) {
+ switch (msp->actual_data_size) {
+ case MSP_DATA_BITS_8:
+ msp->write = u8_msp_write;
+ break;
+ case MSP_DATA_BITS_10:
+ case MSP_DATA_BITS_12:
+ case MSP_DATA_BITS_14:
+ case MSP_DATA_BITS_16:
+ msp->write = u16_msp_write;
+ break;
+ case MSP_DATA_BITS_20:
+ case MSP_DATA_BITS_24:
+ case MSP_DATA_BITS_32:
+ default:
+ msp->write = u32_msp_write;
+ break;
+ }
+ msp->xfer_data.tx_handler = config->handler;
+ msp->xfer_data.tx_callback_data =
+ config->tx_callback_data;
+ msp->xfer_data.rx_callback_data =
+ config->rx_callback_data;
+ msp->xfer_data.msp = msp;
+ }
+ break;
+ case MSP_RECEIVE_MODE:
+ /*Currently they are ignored
+ stm_msp_write(stm_msp_read(msp->registers + MSP_IMSC) |
+ RECEIVE_OVERRUN_ERROR_INT | RECEIVE_FRAME_SYNC_ERR_INT,
+ msp->registers + MSP_IMSC); */
+ if (config->work_mode == MSP_DMA_MODE) {
+ stm_msp_write(stm_msp_read(msp->registers + MSP_DMACR) |
+ RX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+
+ msp->xfer_data.rx_handler = config->handler;
+ msp->xfer_data.rx_callback_data =
+ config->rx_callback_data;
+
+ configure_dma(msp, config);
+ }
+ if (config->work_mode == MSP_POLLING_MODE) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (RX_ENABLE)), msp->registers + MSP_GCR);
+ }
+ if (msp->work_mode != MSP_DMA_MODE) {
+ switch (msp->actual_data_size) {
+ case MSP_DATA_BITS_8:
+ msp->read = u8_msp_read;
+ break;
+ case MSP_DATA_BITS_10:
+ case MSP_DATA_BITS_12:
+ case MSP_DATA_BITS_14:
+ case MSP_DATA_BITS_16:
+ msp->read = u16_msp_read;
+ break;
+ case MSP_DATA_BITS_20:
+ case MSP_DATA_BITS_24:
+ case MSP_DATA_BITS_32:
+ default:
+ msp->read = u32_msp_read;
+ break;
+ }
+ msp->xfer_data.rx_handler = config->handler;
+ msp->xfer_data.tx_callback_data =
+ config->tx_callback_data;
+ msp->xfer_data.rx_callback_data =
+ config->rx_callback_data;
+ msp->xfer_data.msp = msp;
+ }
+
+ break;
+ case MSP_BOTH_T_R_MODE:
+ /*Currently they are ignored
+ stm_msp_write(stm_msp_read(msp->registers + MSP_IMSC) |
+ RECEIVE_OVERRUN_ERROR_INT | RECEIVE_FRAME_SYNC_ERR_INT |
+ TRANSMIT_UNDERRUN_ERR_INT | TRANSMIT_FRAME_SYNC_ERR_INT ,
+ msp->registers + MSP_IMSC); */
+ if (config->work_mode == MSP_DMA_MODE) {
+ stm_msp_write(stm_msp_read(msp->registers + MSP_DMACR) |
+ RX_DMA_ENABLE | TX_DMA_ENABLE,
+ msp->registers + MSP_DMACR);
+
+ msp->xfer_data.tx_handler = config->handler;
+ msp->xfer_data.rx_handler = config->handler;
+ msp->xfer_data.tx_callback_data =
+ config->tx_callback_data;
+ msp->xfer_data.rx_callback_data =
+ config->rx_callback_data;
+
+ configure_dma(msp, config);
+ }
+ if (config->work_mode == MSP_POLLING_MODE) {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (TX_ENABLE)), msp->registers + MSP_GCR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (RX_ENABLE)), msp->registers + MSP_GCR);
+ }
+ if (msp->work_mode != MSP_DMA_MODE) {
+ switch (msp->actual_data_size) {
+ case MSP_DATA_BITS_8:
+ msp->read = u8_msp_read;
+ msp->write = u8_msp_write;
+ break;
+ case MSP_DATA_BITS_10:
+ case MSP_DATA_BITS_12:
+ case MSP_DATA_BITS_14:
+ case MSP_DATA_BITS_16:
+ msp->read = u16_msp_read;
+ msp->write = u16_msp_write;
+ break;
+ case MSP_DATA_BITS_20:
+ case MSP_DATA_BITS_24:
+ case MSP_DATA_BITS_32:
+ default:
+ msp->read = u32_msp_read;
+ msp->write = u32_msp_write;
+ break;
+ }
+ msp->xfer_data.tx_handler = config->handler;
+ msp->xfer_data.rx_handler = config->handler;
+ msp->xfer_data.tx_callback_data =
+ config->tx_callback_data;
+ msp->xfer_data.rx_callback_data =
+ config->rx_callback_data;
+ msp->xfer_data.msp = msp;
+ }
+
+ break;
+ default:
+ printk(KERN_ERR "Invalid direction parameter\n");
+ if (msp->plat_exit)
+ msp->plat_exit();
+ status = -EINVAL;
+ return status;
+ }
+
+ switch (config->work_mode) {
+ case MSP_DMA_MODE:
+ msp->transfer = msp_dma_xfer;
+ break;
+ case MSP_POLLING_MODE:
+ msp->transfer = msp_polling_xfer;
+ break;
+ case MSP_INTERRUPT_MODE:
+ msp->transfer = msp_interrupt_xfer;
+ break;
+ default:
+ msp->transfer = NULL;
+ }
+
+ stm_msp_write(config->iodelay, msp->registers + MSP_IODLY);
+
+ /* enable frame generation logic */
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (FRAME_GEN_ENABLE)), msp->registers + MSP_GCR);
+
+ return status;
+}
+
+/**
+ * flush_rx_fifo - Flush Rx fifo MSP controller.
+ * @msp: msp structure.
+ *
+ * This function flush the rx fifo of msp controller.
+ *
+ * Returns error(-1) in case of failure else success(0)
+ */
+static void flush_rx_fifo(struct msp *msp)
+{
+ u32 dummy = 0;
+ u32 limit = 32;
+ u32 cur = stm_msp_read(msp->registers + MSP_GCR);
+ stm_msp_write(cur | RX_ENABLE, msp->registers + MSP_GCR);
+ while (!(stm_msp_read(msp->registers + MSP_FLR) & RX_FIFO_EMPTY)
+ && limit--) {
+ dummy = stm_msp_read(msp->registers + MSP_DR);
+ }
+ stm_msp_write(cur, msp->registers + MSP_GCR);
+}
+
+/**
+ * flush_tx_fifo - Flush Tx fifo MSP controller.
+ * @msp: msp structure.
+ *
+ * This function flush the tx fifo using test intergration register to read data
+ * from tx fifo directly.
+ *
+ * Returns error(-1) in case of failure else success(0)
+ */
+static void flush_tx_fifo(struct msp *msp)
+{
+ u32 dummy = 0;
+ u32 limit = 32;
+ u32 cur = stm_msp_read(msp->registers + MSP_GCR);
+ stm_msp_write(cur | TX_ENABLE, msp->registers + MSP_GCR);
+ stm_msp_write(0x3, msp->registers + MSP_ITCR);
+ while (!(stm_msp_read(msp->registers + MSP_FLR) & TX_FIFO_EMPTY)
+ && limit--) {
+ dummy = stm_msp_read(msp->registers + MSP_TSTDR);
+ }
+ stm_msp_write(0x0, msp->registers + MSP_ITCR);
+ stm_msp_write(cur, msp->registers + MSP_GCR);
+}
+
+/**
+ * stm_msp_configure_enable - configures and enables the MSP controller.
+ * @i2s_cont: i2s controller sent by i2s device.
+ * @configuration: specifies the configuration parameters.
+ *
+ * This function configures the msp controller with the client configuration.
+ *
+ * Returns error(-1) in case of failure else success(0)
+ */
+static int stm_msp_configure_enable(struct i2s_controller *i2s_cont,
+ void *configuration)
+{
+ u32 old_reg;
+ u32 new_reg;
+ u32 mask;
+ int res;
+ struct msp_config *config =
+ (struct msp_config *)configuration;
+ struct msp *msp = (struct msp *)i2s_cont->data;
+
+ if (in_interrupt()) {
+ printk(KERN_ERR
+ "can't call configure_enable in interrupt context\n");
+ return -1;
+ }
+
+ /* Two simultanous configuring msp is avoidable */
+ down(&msp->lock);
+ switch (msp->users) {
+ case 0:
+ res = regulator_enable(msp_vape_supply);
+ if (res != 0) {
+ dev_err(&msp->i2s_cont->dev,
+ "Failed to enable regulator\n");
+ up(&msp->lock);
+ return res;
+ }
+
+ clk_enable(msp->clk);
+ msp->direction = config->direction;
+ break;
+ case 1:
+ if (msp->direction == MSP_BOTH_T_R_MODE ||
+ config->direction == msp->direction ||
+ config->direction == MSP_BOTH_T_R_MODE) {
+ dev_notice(&i2s_cont->dev, "%s: MSP in use in the "
+ "desired direction.\n", __func__);
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ msp->direction = MSP_BOTH_T_R_MODE;
+ break;
+ default:
+ dev_notice(&i2s_cont->dev, "%s: MSP in use in both "
+ "directions.\n", __func__);
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ msp->users++;
+
+ /* First do the global config register */
+ mask =
+ RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FRAME_SYNC_MASK |
+ TX_FRAME_SYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK |
+ RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK |
+ LOOPBACK_MASK | TX_EXTRA_DELAY_MASK;
+
+ new_reg =
+ (config->tx_clock_sel | config->rx_clock_sel | config->
+ rx_frame_sync_pol | config->tx_frame_sync_pol | config->
+ rx_frame_sync_sel | config->tx_frame_sync_sel | config->
+ rx_fifo_config | config->tx_fifo_config | config->
+ srg_clock_sel | config->loopback_enable | config->tx_data_enable);
+
+ old_reg = stm_msp_read(msp->registers + MSP_GCR);
+ old_reg &= ~mask;
+ new_reg |= old_reg;
+ stm_msp_write(new_reg, msp->registers + MSP_GCR);
+
+ if (msp_enable(msp, config) != 0) {
+ printk(KERN_ERR "error enabling MSP\n");
+ return -EBUSY;
+ }
+ if (config->loopback_enable & 0x80)
+ msp->loopback_enable = 1;
+ /*Sometimes FIFO doesn't gets empty hence limit is provided */
+ flush_tx_fifo(msp);
+ /*This has been added in order to fix fifo flush problem
+ When last xfer occurs some data remains in fifo. In order to
+ flush that data delay is needed */
+ msleep(10);
+ /* wait for fifo to flush */
+ flush_rx_fifo(msp);
+
+ /* RX_BUSY take a while to clear */
+ msleep(10);
+
+ msp->msp_state = MSP_STATE_CONFIGURED;
+ up(&msp->lock);
+ return 0;
+}
+
+static int msp_start_dma(struct msp *msp, int transmit, dma_addr_t data,
+ size_t bytes)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist sg;
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, pfn_to_page(PFN_DOWN(data)), bytes,
+ offset_in_page(data));
+ sg_dma_address(&sg) = data;
+ sg_dma_len(&sg) = bytes;
+
+ if (transmit) {
+ if (!msp->tx_pipeid)
+ return -EINVAL;
+
+ desc = msp->tx_pipeid->device->
+ device_prep_slave_sg(msp->tx_pipeid,
+ &sg, 1, DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT
+ | DMA_CTRL_ACK);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->callback = msp->xfer_data.tx_handler;
+ desc->callback_param = msp->xfer_data.tx_callback_data;
+ desc->tx_submit(desc);
+ dma_async_issue_pending(msp->tx_pipeid);
+ } else {
+ if (!msp->rx_pipeid)
+ return -EINVAL;
+
+ desc = msp->rx_pipeid->device->
+ device_prep_slave_sg(msp->rx_pipeid,
+ &sg, 1, DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT
+ | DMA_CTRL_ACK);
+ if (!desc)
+ return -EBUSY;
+
+ desc->callback = msp->xfer_data.rx_handler;
+ desc->callback_param = msp->xfer_data.rx_callback_data;
+ desc->tx_submit(desc);
+ dma_async_issue_pending(msp->rx_pipeid);
+ }
+
+ return 0;
+}
+
+static int msp_single_dma_tx(struct msp *msp, dma_addr_t data, size_t bytes)
+{
+ int status;
+ status = msp_start_dma(msp, 1, data, bytes);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) | (TX_ENABLE)),
+ msp->registers + MSP_GCR);
+ return status;
+}
+
+static int msp_single_dma_rx(struct msp *msp, dma_addr_t data, size_t bytes)
+{
+ int status;
+ status = msp_start_dma(msp, 0, data, bytes);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) | (RX_ENABLE)),
+ msp->registers + MSP_GCR);
+ return status;
+}
+
+static void msp_cyclic_dma_start(struct msp *msp,
+ struct scatterlist *sg,
+ int sg_len,
+ enum dma_data_direction direction)
+{
+ struct stedma40_cyclic_desc *cdesc;
+ int ret;
+ struct dma_chan *pipeid = (direction == DMA_TO_DEVICE) ?
+ msp->tx_pipeid :
+ msp->rx_pipeid;
+
+ cdesc = stedma40_cyclic_prep_sg(pipeid,
+ sg,
+ sg_len,
+ direction,
+ DMA_PREP_INTERRUPT);
+ if (IS_ERR(cdesc)) {
+ pr_err("%s: Error: stedma40_cyclic_prep_sg failed (%ld)!\n",
+ __func__,
+ PTR_ERR(cdesc));
+ return;
+ }
+
+ cdesc->period_callback = (direction == DMA_TO_DEVICE) ?
+ msp->xfer_data.tx_handler :
+ msp->xfer_data.rx_handler;
+ cdesc->period_callback_param = (direction == DMA_TO_DEVICE) ?
+ msp->xfer_data.tx_callback_data :
+ msp->xfer_data.rx_callback_data;
+
+ ret = stedma40_cyclic_start(pipeid);
+ if (ret) {
+ pr_err("%s: stedma40_cyclic_start failed (%d)!\n", __func__, ret);
+ goto free;
+ }
+
+ msp->infinite = true;
+
+ return;
+
+free:
+ stedma40_cyclic_free(pipeid);
+}
+
+/* Legacy function. Used by HATS driver. */
+static void msp_loopback_inf_start_dma(struct msp *msp,
+ dma_addr_t data,
+ size_t bytes)
+{
+ struct stedma40_cyclic_desc *rxcdesc;
+ struct stedma40_cyclic_desc *txcdesc;
+ struct scatterlist rxsg[2];
+ struct scatterlist txsg[2];
+ size_t len = bytes >> 1;
+ int ret;
+
+ sg_init_table(rxsg, ARRAY_SIZE(rxsg));
+ sg_init_table(txsg, ARRAY_SIZE(txsg));
+
+ sg_dma_len(&rxsg[0]) = len;
+ sg_dma_len(&rxsg[1]) = len;
+ sg_dma_len(&txsg[0]) = len;
+ sg_dma_len(&txsg[1]) = len;
+
+ sg_dma_address(&rxsg[0]) = data;
+ sg_dma_address(&rxsg[1]) = data + len;
+
+ sg_dma_address(&txsg[0]) = data + len;
+ sg_dma_address(&txsg[1]) = data;
+
+ rxcdesc = stedma40_cyclic_prep_sg(msp->rx_pipeid,
+ rxsg, ARRAY_SIZE(rxsg),
+ DMA_FROM_DEVICE, 0);
+ if (IS_ERR(rxcdesc))
+ return;
+
+ txcdesc = stedma40_cyclic_prep_sg(msp->tx_pipeid,
+ txsg, ARRAY_SIZE(txsg),
+ DMA_TO_DEVICE, 0);
+ if (IS_ERR(txcdesc))
+ goto free_rx;
+
+ ret = stedma40_cyclic_start(msp->rx_pipeid);
+ if (ret)
+ goto free_tx;
+
+ ret = stedma40_cyclic_start(msp->tx_pipeid);
+ if (ret)
+ goto stop_rx;
+
+ msp->infinite = true;
+
+ return;
+
+stop_rx:
+ stedma40_cyclic_stop(msp->rx_pipeid);
+free_tx:
+ stedma40_cyclic_free(msp->tx_pipeid);
+free_rx:
+ stedma40_cyclic_free(msp->rx_pipeid);
+}
+
+/**
+ * msp_dma_xfer - Handles DMA transfers over i2s bus.
+ * @msp: main msp structure.
+ * @msg: i2s_message contains info about transmit and receive data.
+ * Context: process
+ *
+ * This will first check whether data buffer is dmaable or not.
+ * Call dma_map_single apis etc to make it dmaable dma. Starts the dma transfer
+ * for TX and RX parallely and wait for it to get completed.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int msp_dma_xfer(struct msp *msp, struct i2s_message *msg)
+{
+ int status = 0;
+
+ switch (msg->i2s_transfer_mode) {
+ default:
+ case I2S_TRANSFER_MODE_SINGLE_DMA:
+ if (msg->i2s_direction == I2S_DIRECTION_RX ||
+ msg->i2s_direction == I2S_DIRECTION_BOTH)
+ if (msg->rxdata && (msg->rxbytes > 0)) {
+ if (!msg->dma_flag)
+ msg->rxdata =
+ (void *)dma_map_single(NULL,
+ msg->rxdata,
+ msg->rxbytes,
+ DMA_FROM_DEVICE
+ );
+ status = msp_single_dma_rx(msp,
+ (dma_addr_t)msg->rxdata,
+ msg->rxbytes);
+ }
+ if (msg->i2s_direction == I2S_DIRECTION_TX ||
+ msg->i2s_direction == I2S_DIRECTION_BOTH)
+ if (msg->txdata && (msg->txbytes > 0)) {
+ if (!msg->dma_flag)
+ msg->txdata =
+ (void *)dma_map_single(NULL,
+ msg->txdata,
+ msg->txbytes,
+ DMA_TO_DEVICE);
+ status = msp_single_dma_tx(msp,
+ (dma_addr_t)msg->txdata,
+ msg->txbytes);
+ }
+ break;
+
+ case I2S_TRANSFER_MODE_CYCLIC_DMA:
+ if (msg->i2s_direction == I2S_DIRECTION_TX) {
+ msp_cyclic_dma_start(msp,
+ msg->sg,
+ msg->sg_len,
+ DMA_TO_DEVICE);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (TX_ENABLE)),
+ msp->registers + MSP_GCR);
+ } else {
+ msp_cyclic_dma_start(msp,
+ msg->sg,
+ msg->sg_len,
+ DMA_FROM_DEVICE);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (RX_ENABLE)),
+ msp->registers + MSP_GCR);
+ }
+ break;
+
+ case I2S_TRANSFER_MODE_INF_LOOPBACK:
+ msp_loopback_inf_start_dma(msp,
+ (dma_addr_t)msg->rxdata,
+ msg->rxbytes);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (RX_ENABLE)),
+ msp->registers + MSP_GCR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (TX_ENABLE)),
+ msp->registers + MSP_GCR);
+ break;
+ }
+
+ return status;
+}
+
+#if 0
+/**
+ * msp_handle_irq - Interrupt handler routine.
+ * @irq: irq no.
+ * @dev_id: device structure registered in request irq.
+ *
+ * Returns error(-1) on failure else success(0).
+ */
+static irqreturn_t msp_handle_irq(int irq, void *dev_id)
+{
+ u32 irq_status;
+ struct msp *msp = (struct msp *)dev_id;
+ struct i2s_message *message = &msp->xfer_data.message;
+ u32 irq_mask = 0;
+ irq_status = stm_msp_read(msp->registers + MSP_MIS);
+ irq_mask = stm_msp_read(msp->registers + MSP_IMSC);
+/* Disable the interrupt to prevent immediate recurrence */
+ stm_msp_write(stm_msp_read(msp->registers + MSP_IMSC) & ~irq_status,
+ msp->registers + MSP_IMSC);
+
+/* Clear the interrupt */
+ stm_msp_write(irq_status, msp->registers + MSP_ICR);
+/* Check for an error condition */
+ msp->msp_io_error = irq_status & (RECEIVE_OVERRUN_ERROR_INT |
+ RECEIVE_FRAME_SYNC_ERR_INT |
+ TRANSMIT_UNDERRUN_ERR_INT |
+ TRANSMIT_FRAME_SYNC_ERR_INT);
+
+ /*Currently they are ignored */
+ if (irq_status & RECEIVE_OVERRUN_ERROR_INT)
+ ;
+ if (irq_status & TRANSMIT_UNDERRUN_ERR_INT)
+ ;
+
+ /* This code has been added basically to support loopback mode
+ * Basically Transmit interrupt is not disabled even after its
+ * completion so that receive fifo gets an additional interrupt
+ */
+ if (irq_mask & (RECEIVE_SERVICE_INT)
+ && (irq_mask & (TRANSMIT_SERVICE_INT)) && (msp->loopback_enable)) {
+ if (msp->read)
+ msp->read(&msp->xfer_data);
+ if (msp->write)
+ msp->write(&msp->xfer_data);
+ if (message->rx_offset >= message->rxbytes) {
+ if (msp->xfer_data.rx_handler)
+ msp->xfer_data.rx_handler(msp->
+ xfer_data.
+ rx_callback_data,
+ message->rx_offset);
+ msp->xfer_data.rx_handler = NULL;
+ return IRQ_HANDLED;
+ }
+
+ if (message->tx_offset >= message->txbytes) {
+ if (msp->xfer_data.tx_handler)
+ msp->xfer_data.tx_handler(msp->xfer_data.
+ tx_callback_data,
+ message->tx_offset);
+ msp->xfer_data.tx_handler = NULL;
+ }
+ stm_msp_write(irq_mask, msp->registers + MSP_IMSC);
+ return IRQ_HANDLED;
+ }
+
+ if (irq_status & RECEIVE_SERVICE_INT) {
+ if (msp->read)
+ msp->read(&msp->xfer_data);
+ if (message->rx_offset >= message->rxbytes) {
+ irq_mask &= ~RECEIVE_SERVICE_INT;
+ stm_msp_write(irq_mask, msp->registers + MSP_IMSC);
+ if (msp->xfer_data.rx_handler)
+ msp->xfer_data.rx_handler(msp->
+ xfer_data.
+ rx_callback_data,
+ message->rx_offset);
+ if (!(irq_status & TRANSMIT_SERVICE_INT))
+ return IRQ_HANDLED;
+ }
+ }
+ if (irq_status & TRANSMIT_SERVICE_INT) {
+ if (msp->write)
+ msp->write(&msp->xfer_data);
+ if (message->tx_offset >= message->txbytes) {
+ irq_mask &= ~TRANSMIT_SERVICE_INT;
+ stm_msp_write(irq_mask, msp->registers + MSP_IMSC);
+ if (msp->xfer_data.tx_handler)
+ msp->xfer_data.tx_handler(msp->xfer_data.
+ tx_callback_data,
+ message->tx_offset);
+ return IRQ_HANDLED;
+ }
+ }
+ stm_msp_write(irq_mask, msp->registers + MSP_IMSC);
+ return IRQ_HANDLED;
+
+}
+#endif
+
+/**
+ * msp_interrupt_xfer - Handles Interrupt transfers over i2s bus.
+ * @msp: main msp structure.
+ * @msg: i2s_message contains info about transmit and receive data.
+ * Context: Process or interrupt.
+ *
+ * This implements transfer and receive functions used in interrupt mode.
+ * This can be used in interrupt context if a callback handler is registered
+ * by client driver. This has been to improve performance in interrupt mode.
+ * Hence can't use sleep in this function.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int msp_interrupt_xfer(struct msp *msp, struct i2s_message *msg)
+{
+ struct i2s_message *message;
+ u32 irq_mask = 0;
+
+ if (msg->i2s_transfer_mode != I2S_TRANSFER_MODE_NON_DMA)
+ return -EINVAL;
+
+ if (msg->txbytes) {
+ msp->xfer_data.message.txbytes = msg->txbytes;
+ msp->xfer_data.message.txdata = msg->txdata;
+ msp->xfer_data.message.tx_offset = 0;
+ }
+ if (msg->rxbytes) {
+ msp->xfer_data.message.rxbytes = msg->rxbytes;
+ msp->xfer_data.message.rxdata = msg->rxdata;
+ msp->xfer_data.message.rx_offset = 0;
+ }
+ message = &msp->xfer_data.message;
+ if ((message->txdata == NULL || message->txbytes == 0)
+ && (message->rxdata == NULL || message->rxbytes == 0)) {
+ printk(KERN_ERR
+ "transmit_receive_data is NULL with bytes > 0\n");
+ return -EINVAL;
+ }
+
+ msp->msp_io_error = 0;
+
+ if (message->tx_offset < message->txbytes) {
+ irq_mask |= TRANSMIT_SERVICE_INT;
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (TX_ENABLE)), msp->registers + MSP_GCR);
+ }
+ if (message->rx_offset < message->rxbytes) {
+ irq_mask |= RECEIVE_SERVICE_INT;
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (RX_ENABLE)), msp->registers + MSP_GCR);
+ }
+ stm_msp_write((stm_msp_read(msp->registers + MSP_IMSC) |
+ irq_mask), msp->registers + MSP_IMSC);
+ return 0;
+}
+
+/**
+ * func_notify_timer - Handles Polling hang issue over i2s bus.
+ * @data: main msp data address
+ * Context: Interrupt.
+ *
+ * This is used to handle error condition in transfer and receive function used
+ * in polling mode.
+ * Sometimes due to passing wrong protocol desc , polling transfer may hang.
+ * To prevent this, timer is added.
+ *
+ * Returns void.
+ */
+static void func_notify_timer(unsigned long data)
+{
+ struct msp *msp = (struct msp *)data;
+ if (msp->polling_flag) {
+ msp->msp_io_error = 1;
+ printk(KERN_ERR
+ "Polling is taking two much time, may be it got hang\n");
+ del_timer(&msp->notify_timer);
+ }
+}
+
+/**
+ * msp_polling_xfer - Handles Polling transfers over i2s bus.
+ * @msp: main msp structure.
+ * @msg: i2s_message contains info about transmit and receive data.
+ * Context: Process.
+ *
+ * This implements transfer and receive functions used in polling mode. This is
+ * blocking fucntion.
+ * It is recommended to use interrupt or dma mode for better performance rather
+ * than the polling mode.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int msp_polling_xfer(struct msp *msp, struct i2s_message *msg)
+{
+ struct i2s_message *message;
+ u32 time_expire = 0;
+ u32 tr_ex = 0, rr_ex = 0;
+ u32 msec_jiffies = 0;
+
+ if (msg->i2s_transfer_mode != I2S_TRANSFER_MODE_NON_DMA)
+ return -EINVAL;
+
+ if (msg->txbytes) {
+ msp->xfer_data.message.txbytes = msg->txbytes;
+ msp->xfer_data.message.txdata = msg->txdata;
+ msp->xfer_data.message.tx_offset = 0;
+ tr_ex = msg->txbytes;
+ }
+ if (msg->rxbytes) {
+ msp->xfer_data.message.rxbytes = msg->rxbytes;
+ msp->xfer_data.message.rxdata = msg->rxdata;
+ msp->xfer_data.message.rx_offset = 0;
+ rr_ex = msg->rxbytes;
+ }
+ message = &msp->xfer_data.message;
+ time_expire = (tr_ex + rr_ex) / 1024;
+ if (!time_expire)
+ msec_jiffies = 500;
+ else
+ msec_jiffies = time_expire * 500;
+ msp->notify_timer.expires = jiffies + msecs_to_jiffies(msec_jiffies);
+ down(&msp->lock);
+ if (message->txdata == NULL && message->txbytes > 0) {
+ printk(KERN_ERR
+ "transmit_receive_data is NULL with bytes > 0\n");
+ return -EINVAL;
+ }
+
+ if (message->rxdata == NULL && message->rxbytes > 0) {
+ printk(KERN_ERR
+ "transmit_receive_data is NULL with bytes > 0\n");
+ return -EINVAL;
+ }
+ msp->msp_io_error = 0;
+ msp->polling_flag = 1;
+ add_timer(&msp->notify_timer);
+ while (message->tx_offset < message->txbytes
+ || message->rx_offset < message->rxbytes) {
+ if (msp->msp_io_error)
+ break;
+ if (msp->read)
+ msp->read(&msp->xfer_data);
+ if (msp->write)
+ msp->write(&msp->xfer_data);
+ }
+ msp->polling_flag = 0;
+ del_timer(&msp->notify_timer);
+ up(&msp->lock);
+ return message->txbytes + message->rxbytes;
+}
+
+/**
+ * stm_msp_transceive_data - Main i2s transfer function.
+ * @i2s_cont: i2s controller structure passed by client driver.
+ * @message: i2s message structure contains transceive info.
+ * Context: process or interrupt.
+ *
+ * This function is registered over i2s_xfer funtions. It will handle main i2s
+ * transfer over i2s bus in various modes.It call msp transfer function on which
+ * suitable transfer function is already registered i.e dma ,interrupt or
+ * polling function.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int stm_msp_transceive_data(struct i2s_controller *i2s_cont,
+ struct i2s_message *message)
+{
+ int status = 0;
+ struct msp *msp = (struct msp *)i2s_cont->data;
+
+ if (!message || (msp->msp_state == MSP_STATE_IDLE)) {
+ printk(KERN_ERR "Message is NULL\n");
+ return -EPERM;
+ }
+
+ msp->msp_state = MSP_STATE_RUN;
+ if (msp->transfer)
+ status = msp->transfer(msp, message);
+
+ if (msp->msp_state == MSP_STATE_RUN)
+ msp->msp_state = MSP_STATE_CONFIGURED;
+
+ return status;
+}
+
+/**
+ * msp_disable_receive - Disable receive functionality.
+ * @msp: main msp structure.
+ * Context: process.
+ *
+ * This function will disable msp controller's receive functionality like dma,
+ * interrupt receive data buffer all are disabled.
+ *
+ * Returns void.
+ */
+static void msp_disable_receive(struct msp *msp)
+{
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~RX_ENABLE)), msp->registers + MSP_GCR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_DMACR) &
+ (~RX_DMA_ENABLE)), msp->registers + MSP_DMACR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_IMSC) &
+ (~
+ (RECEIVE_SERVICE_INT |
+ RECEIVE_OVERRUN_ERROR_INT))),
+ msp->registers + MSP_IMSC);
+ msp->xfer_data.message.rxbytes = 0;
+ msp->xfer_data.message.rx_offset = 0;
+ msp->xfer_data.message.rxdata = NULL;
+ msp->read = NULL;
+
+}
+
+/**
+ * msp_disable_transmit - Disable transmit functionality.
+ * @msp: main msp structure.
+ * Context: process.
+ *
+ * This function will disable msp controller's transmit functionality like dma,
+ * interrupt transmit data buffer all are disabled.
+ *
+ * Returns void.
+ */
+static void msp_disable_transmit(struct msp *msp)
+{
+
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~TX_ENABLE)), msp->registers + MSP_GCR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_DMACR) &
+ (~TX_DMA_ENABLE)), msp->registers + MSP_DMACR);
+ stm_msp_write((stm_msp_read(msp->registers + MSP_IMSC) &
+ (~
+ (TRANSMIT_SERVICE_INT |
+ TRANSMIT_UNDERRUN_ERR_INT))),
+ msp->registers + MSP_IMSC);
+ msp->xfer_data.message.txbytes = 0;
+ msp->xfer_data.message.tx_offset = 0;
+ msp->xfer_data.message.txdata = NULL;
+ msp->write = NULL;
+
+}
+
+/**
+ * stm_msp_disable - disable the given msp controller
+ * @msp: specifies the msp contoller data
+ * @direction: specifies the transmit/receive direction
+ * @flag: It indicates the functionality that needs to be disabled.
+ *
+ * Returns error(-1) in case of failure else success(0)
+ */
+static int stm_msp_disable(struct msp *msp, int direction, i2s_flag flag)
+{
+ int limit = 32;
+ u32 dummy = 0;
+ int status = 0;
+ if (!
+ (stm_msp_read(msp->registers + MSP_GCR) &
+ ((TX_ENABLE | RX_ENABLE)))) {
+ return 0;
+ }
+ if (msp->work_mode == MSP_DMA_MODE) {
+ if (flag == DISABLE_ALL || flag == DISABLE_TRANSMIT) {
+ if (msp->tx_pipeid != NULL) {
+ if (msp->infinite) {
+ stedma40_cyclic_stop(msp->tx_pipeid);
+ stedma40_cyclic_free(msp->tx_pipeid);
+ }
+ msp->tx_pipeid->device->
+ device_control(msp->tx_pipeid,
+ DMA_TERMINATE_ALL, 0);
+ dma_release_channel(msp->tx_pipeid);
+ msp->tx_pipeid = NULL;
+ }
+ }
+ if ((flag == DISABLE_ALL || flag == DISABLE_RECEIVE)) {
+ if (msp->rx_pipeid != NULL) {
+ if (msp->infinite) {
+ stedma40_cyclic_stop(msp->rx_pipeid);
+ stedma40_cyclic_free(msp->rx_pipeid);
+ }
+
+ msp->rx_pipeid->device->
+ device_control(msp->rx_pipeid,
+ DMA_TERMINATE_ALL, 0);
+ dma_release_channel(msp->rx_pipeid);
+ msp->rx_pipeid = NULL;
+ }
+ }
+
+ msp->infinite = false;
+ }
+ if (flag == DISABLE_TRANSMIT)
+ msp_disable_transmit(msp);
+ else if (flag == DISABLE_RECEIVE)
+ msp_disable_receive(msp);
+ else {
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) |
+ (LOOPBACK_MASK)), msp->registers + MSP_GCR);
+ /* Flush Tx fifo */
+ while ((!
+ (stm_msp_read(msp->registers + MSP_FLR) &
+ TX_FIFO_EMPTY)) && limit--)
+ dummy = stm_msp_read(msp->registers + MSP_DR);
+
+ /* Disable Transmit channel */
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~TX_ENABLE)), msp->registers + MSP_GCR);
+ limit = 32;
+ /* Flush Rx Fifo */
+ while ((!
+ (stm_msp_read(msp->registers + MSP_FLR) &
+ RX_FIFO_EMPTY)) && limit--)
+ dummy = stm_msp_read(msp->registers + MSP_DR);
+ /* Disable Loopback and Receive channel */
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~(RX_ENABLE | LOOPBACK_MASK))),
+ msp->registers + MSP_GCR);
+ /*This has been added in order to fix fifo flush problem
+ When last xfer occurs some data remains in fifo. In order to
+ flush that data delay is needed */
+ msleep(10);
+ msp_disable_transmit(msp);
+ msp_disable_receive(msp);
+
+ }
+
+ /* disable sample rate and frame generators */
+ if (flag == DISABLE_ALL) {
+ msp->msp_state = MSP_STATE_IDLE;
+ stm_msp_write((stm_msp_read(msp->registers + MSP_GCR) &
+ (~(FRAME_GEN_ENABLE | SRG_ENABLE))),
+ msp->registers + MSP_GCR);
+ memset(&msp->xfer_data, 0, sizeof(struct trans_data));
+ if (msp->plat_exit)
+ status = msp->plat_exit();
+ if (status)
+ printk(KERN_ERR "Error in msp_i2s_exit\n");
+ if (msp->work_mode == MSP_POLLING_MODE
+ && msp->msp_state == MSP_STATE_RUN) {
+ up(&msp->lock);
+ }
+ msp->transfer = NULL;
+ stm_msp_write(0, msp->registers + MSP_GCR);
+ stm_msp_write(0, msp->registers + MSP_TCF);
+ stm_msp_write(0, msp->registers + MSP_RCF);
+ stm_msp_write(0, msp->registers + MSP_DMACR);
+ stm_msp_write(0, msp->registers + MSP_SRG);
+ stm_msp_write(0, msp->registers + MSP_MCR);
+ stm_msp_write(0, msp->registers + MSP_RCM);
+ stm_msp_write(0, msp->registers + MSP_RCV);
+ stm_msp_write(0, msp->registers + MSP_TCE0);
+ stm_msp_write(0, msp->registers + MSP_TCE1);
+ stm_msp_write(0, msp->registers + MSP_TCE2);
+ stm_msp_write(0, msp->registers + MSP_TCE3);
+ stm_msp_write(0, msp->registers + MSP_RCE0);
+ stm_msp_write(0, msp->registers + MSP_RCE1);
+ stm_msp_write(0, msp->registers + MSP_RCE2);
+ stm_msp_write(0, msp->registers + MSP_RCE3);
+ }
+ return status;
+}
+
+/**
+ * stm_msp_close - Close the current i2s connection btw controller and client.
+ * @i2s_cont: i2s controller structure
+ * @flag: It indicates the functionality that needs to be disabled.
+ * Context: process
+ *
+ * It will call msp_disable and reset the msp configuration. Disables Rx and Tx
+ * channels, free gpio irqs and interrupt pins.
+ * Called by i2s client driver to indicate the completion of use of i2s bus.
+ * It is registered on i2s_close function.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int stm_msp_close(struct i2s_controller *i2s_cont, i2s_flag flag)
+{
+ int status = 0;
+ struct msp *msp = (struct msp *)i2s_cont->data;
+ down(&msp->lock);
+ if (msp->users == 0) {
+ pr_err("MSP already closed!\n");
+ status = -EINVAL;
+ goto end;
+ }
+ dev_dbg(&i2s_cont->dev, "%s: users = %d, flag = %d.\n",
+ __func__, msp->users, flag);
+ /* We need to call it twice for DISABLE_ALL*/
+ msp->users = flag == DISABLE_ALL ? 0 : msp->users - 1;
+ if (msp->users)
+ status = stm_msp_disable(msp, MSP_BOTH_T_R_MODE, flag);
+ else {
+ status = stm_msp_disable(msp, MSP_BOTH_T_R_MODE, DISABLE_ALL);
+ clk_disable(msp->clk);
+ status = regulator_disable(msp_vape_supply);
+ if (status != 0) {
+ dev_err(&msp->i2s_cont->dev,
+ "Failed to disable regulator\n");
+ clk_enable(msp->clk);
+ goto end;
+ }
+ }
+ if (status)
+ goto end;
+ if (msp->users)
+ msp->direction = flag == DISABLE_TRANSMIT ?
+ MSP_RECEIVE_MODE : MSP_TRANSMIT_MODE;
+
+ if (msp->vape_opp_constraint == 1) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "msp_i2s", 50);
+ msp->vape_opp_constraint = 0;
+ }
+end:
+ up(&msp->lock);
+ return status;
+
+}
+
+static int stm_msp_hw_status(struct i2s_controller *i2s_cont)
+{
+ struct msp *msp = (struct msp *)i2s_cont->data;
+
+ int status = stm_msp_read(msp->registers + MSP_RIS) & 0xee;
+ if (status)
+ stm_msp_write(status, msp->registers + MSP_ICR);
+
+ return status;
+}
+
+static dma_addr_t stm_msp_get_pointer(struct i2s_controller *i2s_cont,
+ enum i2s_direction_t i2s_direction)
+{
+ struct msp *msp = (struct msp *)i2s_cont->data;
+ return (i2s_direction == I2S_DIRECTION_TX) ?
+ stedma40_get_src_addr(msp->tx_pipeid) :
+ stedma40_get_dst_addr(msp->rx_pipeid);
+}
+
+ /*Platform driver's functions */
+/**
+ * msp_probe - Probe function
+ * @pdev: platform device structure.
+ * Context: process
+ *
+ * Probe function of msp platform driver.Handles allocation of memory and irq
+ * resource. It creates i2s_controller and one i2s_device per msp controller.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+int msp_probe(struct platform_device *pdev)
+{
+ int status = 0;
+ struct device *dev;
+ s16 platform_num = 0;
+ struct resource *res = NULL;
+ int irq;
+ struct i2s_controller *i2s_cont;
+ struct msp_i2s_platform_data *platform_data;
+ struct msp *msp;
+
+ if (!pdev)
+ return -EPERM;
+ msp = kzalloc(sizeof(*msp), GFP_KERNEL);
+
+ platform_data = (struct msp_i2s_platform_data *)pdev->dev.platform_data;
+
+ msp->id = platform_data->id;
+ msp->plat_init = platform_data->msp_i2s_init;
+ msp->plat_exit = platform_data->msp_i2s_exit;
+
+ msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx;
+ msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx;
+
+ dev = &pdev->dev;
+ platform_num = msp->id - 1;
+
+ init_MUTEX(&msp->lock);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "probe - MEM resources not defined\n");
+ status = -EINVAL;
+ goto free_msp;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ status = -EINVAL;
+ goto free_msp;
+ }
+ msp->irq = irq;
+
+ msp->registers = ioremap(res->start, (res->end - res->start + 1));
+ if (msp->registers == NULL) {
+ status = -EINVAL;
+ goto free_msp;
+ }
+
+ msp_vape_supply = regulator_get(NULL, "v-ape");
+ if (IS_ERR(msp_vape_supply)) {
+ status = PTR_ERR(msp_vape_supply);
+ printk(KERN_WARNING "msp i2s : failed to get v-ape supply\n");
+ goto free_irq;
+ }
+ prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, "msp_i2s", 50);
+ msp->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(msp->clk)) {
+ status = PTR_ERR(msp->clk);
+ goto free_irq;
+ }
+
+ init_timer(&msp->notify_timer);
+ msp->notify_timer.expires = jiffies + msecs_to_jiffies(1000);
+ msp->notify_timer.function = func_notify_timer;
+ msp->notify_timer.data = (unsigned long)msp;
+
+ msp->rx_pipeid = NULL;
+ msp->tx_pipeid = NULL;
+ msp->read = NULL;
+ msp->write = NULL;
+ msp->transfer = NULL;
+ msp->msp_state = MSP_STATE_IDLE;
+ msp->loopback_enable = 0;
+
+ dev_set_drvdata(&pdev->dev, msp);
+ /* I2S Controller is allocated and added in I2S controller class. */
+ i2s_cont =
+ (struct i2s_controller *)kzalloc(sizeof(*i2s_cont), GFP_KERNEL);
+ if (!i2s_cont) {
+ dev_err(&pdev->dev, "i2s controller alloc failed \n");
+ status = -EINVAL;
+ goto del_timer;
+ }
+ i2s_cont->dev.parent = dev;
+ i2s_cont->algo = &i2s_algo;
+ i2s_cont->data = (void *)msp;
+ i2s_cont->id = platform_num;
+ snprintf(i2s_cont->name, sizeof(i2s_cont->name),
+ "MSP_I2S.%04x", platform_num);
+
+ status = i2s_add_controller(i2s_cont);
+ if (status) {
+ dev_err(&pdev->dev, "i2s add controller failed (%d)\n", status);
+ goto free_cont;
+ }
+ msp->i2s_cont = i2s_cont;
+ return status;
+free_cont:
+ kfree(msp->i2s_cont);
+del_timer:
+ del_timer_sync(&msp->notify_timer);
+ clk_put(msp->clk);
+free_irq:
+ iounmap(msp->registers);
+free_msp:
+ kfree(msp);
+ return status;
+}
+
+/**
+ * msp_remove - remove function
+ * @pdev: platform device structure.
+ * Context: process
+ *
+ * remove function of msp platform driver.Handles dellocation of memory and irq
+ * resource. It deletes i2s_controller and one i2s_device per msp controller
+ * created in msp_probe.
+ *
+ * Returns error(-1) in case of failure or success(0).
+ */
+static int msp_remove(struct platform_device *pdev)
+{
+ struct msp *msp =
+ (struct msp *)dev_get_drvdata(&pdev->dev);
+ int status = 0;
+ i2s_del_controller(msp->i2s_cont);
+ del_timer_sync(&msp->notify_timer);
+ clk_put(msp->clk);
+ iounmap(msp->registers);
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "msp_i2s");
+ regulator_put(msp_vape_supply);
+ kfree(msp);
+ return status;
+}
+#ifdef CONFIG_PM
+/**
+ * msp_suspend - MSP suspend function registered with PM framework.
+ * @pdev: Reference to platform device structure of the device
+ * @state: power mgmt state.
+ *
+ * This function is invoked when the system is going into sleep, called
+ * by the power management framework of the linux kernel.
+ * Nothing is required as controller is configured with every transfer.
+ * It is assumed that no active tranfer is in progress at this time.
+ * Client driver should make sure of this.
+ *
+ */
+
+int msp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct msp *msp =
+ (struct msp *)dev_get_drvdata(&pdev->dev);
+
+ down(&msp->lock);
+ if (msp->users > 0) {
+ up(&msp->lock);
+ return -EBUSY;
+ }
+ up(&msp->lock);
+
+ return 0;
+}
+/**
+ * msp_resume - MSP Resume function registered with PM framework.
+ * @pdev: Reference to platform device structure of the device
+ *
+ * This function is invoked when the system is coming out of sleep, called
+ * by the power management framework of the linux kernel.
+ * Nothing is required.
+ *
+ */
+
+int msp_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define msp_suspend NULL
+#define msp_resume NULL
+#endif
+
+static struct platform_driver msp_i2s_driver = {
+ .probe = msp_probe,
+ .remove = msp_remove,
+ .suspend = msp_suspend,
+ .resume = msp_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "MSP_I2S",
+ },
+};
+
+static int __init stm_msp_mod_init(void)
+{
+ return platform_driver_register(&msp_i2s_driver);
+}
+
+static void __exit stm_msp_exit(void)
+{
+ platform_driver_unregister(&msp_i2s_driver);
+ return;
+}
+
+module_init(stm_msp_mod_init);
+module_exit(stm_msp_exit);
+
+MODULE_AUTHOR("Sandeep Kaushik");
+MODULE_DESCRIPTION("STM MSP driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/i2s/msp_i2s.h b/drivers/misc/i2s/msp_i2s.h
new file mode 100644
index 00000000000..3fcb92867e2
--- /dev/null
+++ b/drivers/misc/i2s/msp_i2s.h
@@ -0,0 +1,362 @@
+/*----------------------------------------------------------------------------------*/
+/* copyright STMicroelectronics, 2007. */
+/* */
+/* This program is free software; you can redistribute it and/or modify it under */
+/* the terms of the GNU General Public License as published by the Free */
+/* Software Foundation; either version 2.1 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, see <http://www.gnu.org/licenses/>. */
+/*----------------------------------------------------------------------------------*/
+
+
+#ifndef STM_MSP_HEADER
+#define STM_MSP_HEADER
+
+#define MSP_DR 0x00
+#define MSP_GCR 0x04
+#define MSP_TCF 0x08
+#define MSP_RCF 0x0c
+#define MSP_SRG 0x10
+#define MSP_FLR 0x14
+#define MSP_DMACR 0x18
+
+#define MSP_IMSC 0x20
+#define MSP_RIS 0x24
+#define MSP_MIS 0x28
+#define MSP_ICR 0x2c
+#define MSP_MCR 0x30
+#define MSP_RCV 0x34
+#define MSP_RCM 0x38
+
+#define MSP_TCE0 0x40
+#define MSP_TCE1 0x44
+#define MSP_TCE2 0x48
+#define MSP_TCE3 0x4c
+
+#define MSP_RCE0 0x60
+#define MSP_RCE1 0x64
+#define MSP_RCE2 0x68
+#define MSP_RCE3 0x6c
+
+#define MSP_ITCR 0x80
+#define MSP_ITIP 0x84
+#define MSP_ITOP 0x88
+#define MSP_TSTDR 0x8c
+
+#define MSP_PID0 0xfe0
+#define MSP_PID1 0xfe4
+#define MSP_PID2 0xfe8
+#define MSP_PID3 0xfec
+
+#define MSP_CID0 0xff0
+#define MSP_CID1 0xff4
+#define MSP_CID2 0xff8
+#define MSP_CID3 0xffc
+
+
+/* Single or dual phase mode */
+enum
+{
+ MSP_SINGLE_PHASE,
+ MSP_DUAL_PHASE
+};
+
+
+/* Transmit/Receive shifter status
+-----------------------------------*/
+enum
+{
+ MSP_SxHIFTER_IDLE = 0,
+ MSP_SHIFTER_WORKING = 1
+};
+
+
+/* Transmit/Receive FIFO status
+---------------------------------*/
+enum
+{
+ MSP_FIFO_FULL,
+ MSP_FIFO_PART_FILLED,
+ MSP_FIFO_EMPTY
+};
+
+
+/* Frame length
+------------------*/
+enum
+{
+ MSP_FRAME_LENGTH_1 = 0,
+ MSP_FRAME_LENGTH_2 = 1,
+ MSP_FRAME_LENGTH_4 = 3,
+ MSP_FRAME_LENGTH_8 = 7,
+ MSP_FRAME_LENGTH_12 = 11,
+ MSP_FRAME_LENGTH_16 = 15,
+ MSP_FRAME_LENGTH_20 = 19,
+ MSP_FRAME_LENGTH_32 = 31,
+ MSP_FRAME_LENGTH_48 = 47,
+ MSP_FRAME_LENGTH_64 = 63
+};
+
+/* Element length */
+enum
+{
+ MSP_ELEM_LENGTH_8 = 0,
+ MSP_ELEM_LENGTH_10 = 1,
+ MSP_ELEM_LENGTH_12 = 2,
+ MSP_ELEM_LENGTH_14 = 3,
+ MSP_ELEM_LENGTH_16 = 4,
+ MSP_ELEM_LENGTH_20 = 5,
+ MSP_ELEM_LENGTH_24 = 6,
+ MSP_ELEM_LENGTH_32 = 7
+};
+
+
+/* Data delay (in bit clock cycles)
+---------------------------------------*/
+enum
+{
+ MSP_DELAY_0 = 0,
+ MSP_DELAY_1 = 1,
+ MSP_DELAY_2 = 2,
+ MSP_DELAY_3 = 3
+};
+
+
+/* Configurations of clocks (transmit, receive or sample rate generator)
+-------------------------------------------------------------------------*/
+enum
+{
+ MSP_RISING_EDGE = 0,
+ MSP_FALLING_EDGE = 1
+};
+
+/* Protocol dependant parameters list */
+struct msp_protocol_desc
+{
+ u32 phase_mode;
+ u32 frame_len_1;
+ u32 frame_len_2;
+ u32 element_len_1;
+ u32 element_len_2;
+ u32 data_delay;
+ u32 tx_clock_edge;
+ u32 rx_clock_edge;
+};
+#define RX_ENABLE_MASK 0x00000001
+#define RX_FIFO_ENABLE_MASK 0x00000002
+#define RX_FRAME_SYNC_MASK 0x00000004
+#define DIRECT_COMPANDING_MASK 0x00000008
+#define RX_SYNC_SEL_MASK 0x00000010
+#define RX_CLK_POL_MASK 0x00000020
+#define RX_CLK_SEL_MASK 0x00000040
+#define LOOPBACK_MASK 0x00000080
+#define TX_ENABLE_MASK 0x00000100
+#define TX_FIFO_ENABLE_MASK 0x00000200
+#define TX_FRAME_SYNC_MASK 0x00000400
+#define TX_MSP_TDR_TSR 0x00000800
+#define TX_SYNC_SEL_MASK 0x00001800
+#define TX_CLK_POL_MASK 0x00002000
+#define TX_CLK_SEL_MASK 0x00004000
+#define TX_EXTRA_DELAY_MASK 0x00008000
+#define SRG_ENABLE_MASK 0x00010000
+#define SRG_CLK_POL_MASK 0x00020000
+#define SRG_CLK_SEL_MASK 0x000C0000
+#define FRAME_GEN_EN_MASK 0x00100000
+#define SPI_CLK_MODE_MASK 0x00600000
+#define SPI_BURST_MODE_MASK 0x00800000
+
+#define RXEN_BIT 0
+#define RFFEN_BIT 1
+#define RFSPOL_BIT 2
+#define DCM_BIT 3
+#define RFSSEL_BIT 4
+#define RCKPOL_BIT 5
+#define RCKSEL_BIT 6
+#define LBM_BIT 7
+#define TXEN_BIT 8
+#define TFFEN_BIT 9
+#define TFSPOL_BIT 10
+#define TFSSEL_BIT 11
+#define TCKPOL_BIT 13
+#define TCKSEL_BIT 14
+#define TXDDL_BIT 15
+#define SGEN_BIT 16
+#define SCKPOL_BIT 17
+#define SCKSEL_BIT 18
+#define FGEN_BIT 20
+#define SPICKM_BIT 21
+
+#define msp_rx_clkpol_bit(n) ((n & 1) << RCKPOL_BIT)
+#define msp_tx_clkpol_bit(n) ((n & 1) << TCKPOL_BIT)
+#define msp_spi_clk_mode_bits(n) ((n & 3) << SPICKM_BIT)
+
+
+/* Use this to clear the clock mode bits to non-spi */
+#define MSP_NON_SPI_CLK_MASK 0x00600000
+
+#define P1ELEN_BIT 0
+#define P1FLEN_BIT 3
+#define DTYP_BIT 10
+#define ENDN_BIT 12
+#define DDLY_BIT 13
+#define FSIG_BIT 15
+#define P2ELEN_BIT 16
+#define P2FLEN_BIT 19
+#define P2SM_BIT 26
+#define P2EN_BIT 27
+
+#define msp_p1_elem_len_bits(n) (n & 0x00000007)
+#define msp_p2_elem_len_bits(n) (((n) << P2ELEN_BIT) & 0x00070000)
+#define msp_p1_frame_len_bits(n) (((n) << P1FLEN_BIT) & 0x00000378)
+#define msp_p2_frame_len_bits(n) (((n) << P2FLEN_BIT) & 0x03780000)
+#define msp_data_delay_bits(n) (((n) << DDLY_BIT) & 0x00003000)
+#define msp_data_type_bits(n) (((n) << DTYP_BIT) & 0x00000600)
+#define msp_p2_start_mode_bit(n) (n << P2SM_BIT)
+#define msp_p2_enable_bit(n) (n << P2EN_BIT)
+
+/* Flag register
+--------------------*/
+#define RX_BUSY 0x00000001
+#define RX_FIFO_EMPTY 0x00000002
+#define RX_FIFO_FULL 0x00000004
+#define TX_BUSY 0x00000008
+#define TX_FIFO_EMPTY 0x00000010
+#define TX_FIFO_FULL 0x00000020
+
+#define RBUSY_BIT 0
+#define RFE_BIT 1
+#define RFU_BIT 2
+#define TBUSY_BIT 3
+#define TFE_BIT 4
+#define TFU_BIT 5
+
+/* Multichannel control register
+---------------------------------*/
+#define RMCEN_BIT 0
+#define RMCSF_BIT 1
+#define RCMPM_BIT 3
+#define TMCEN_BIT 5
+#define TNCSF_BIT 6
+
+/* Sample rate generator register
+------------------------------------*/
+#define SCKDIV_BIT 0
+#define FRWID_BIT 10
+#define FRPER_BIT 16
+
+#define SCK_DIV_MASK 0x0000003FF
+#define frame_width_bits(n) (((n) << FRWID_BIT) &0x0000FC00)
+#define frame_period_bits(n) (((n) << FRPER_BIT) &0x1FFF0000)
+
+
+/* DMA controller register
+---------------------------*/
+#define RX_DMA_ENABLE 0x00000001
+#define TX_DMA_ENABLE 0x00000002
+
+#define RDMAE_BIT 0
+#define TDMAE_BIT 1
+
+/*Interrupt Register
+-----------------------------------------*/
+#define RECEIVE_SERVICE_INT 0x00000001
+#define RECEIVE_OVERRUN_ERROR_INT 0x00000002
+#define RECEIVE_FRAME_SYNC_ERR_INT 0x00000004
+#define RECEIVE_FRAME_SYNC_INT 0x00000008
+#define TRANSMIT_SERVICE_INT 0x00000010
+#define TRANSMIT_UNDERRUN_ERR_INT 0x00000020
+#define TRANSMIT_FRAME_SYNC_ERR_INT 0x00000040
+#define TRANSMIT_FRAME_SYNC_INT 0x00000080
+#define ALL_INT 0x000000ff
+
+/* Protocol configuration values
+* I2S: Single phase, 16 bits, 2 words per frame
+-----------------------------------------------*/
+#define I2S_PROTOCOL_DESC \
+{ \
+ MSP_SINGLE_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_ELEM_LENGTH_32, \
+ MSP_ELEM_LENGTH_32, \
+ MSP_DELAY_1, \
+ MSP_FALLING_EDGE, \
+ MSP_FALLING_EDGE \
+}
+
+#define PCM_PROTOCOL_DESC \
+{ \
+ MSP_SINGLE_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_ELEM_LENGTH_16, \
+ MSP_ELEM_LENGTH_16, \
+ MSP_DATA_DELAY, \
+ MSP_TX_CLOCK_EDGE, \
+ MSP_RX_CLOCK_EDGE \
+}
+
+/* Companded PCM: Single phase, 8 bits, 1 word per frame
+--------------------------------------------------------*/
+#define PCM_COMPAND_PROTOCOL_DESC \
+{ \
+ MSP_SINGLE_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_DELAY_0, \
+ MSP_RISING_EDGE, \
+ MSP_FALLING_EDGE \
+}
+
+/* AC97: Double phase, 1 element of 16 bits during first phase,
+* 12 elements of 20 bits in second phase.
+--------------------------------------------------------------*/
+#define AC97_PROTOCOL_DESC \
+{ \
+ MSP_DUAL_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_12, \
+ MSP_ELEM_LENGTH_16, \
+ MSP_ELEM_LENGTH_20, \
+ MSP_DELAY_1, \
+ MSP_RISING_EDGE, \
+ MSP_FALLING_EDGE \
+}
+
+#define SPI_MASTER_PROTOCOL_DESC \
+{ \
+ MSP_SINGLE_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_DELAY_1, \
+ MSP_FALLING_EDGE, \
+ MSP_RISING_EDGE \
+}
+#define SPI_SLAVE_PROTOCOL_DESC \
+{ \
+ MSP_SINGLE_PHASE, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_FRAME_LENGTH_1, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_ELEM_LENGTH_8, \
+ MSP_DELAY_1, \
+ MSP_FALLING_EDGE, \
+ MSP_RISING_EDGE \
+}
+
+#define MSP_FRAME_PERIOD_IN_MONO_MODE 256
+#define MSP_FRAME_PERIOD_IN_STEREO_MODE 32
+#define MSP_FRAME_WIDTH_IN_STEREO_MODE 16
+
+#endif
+
diff --git a/drivers/misc/iface_stat.c b/drivers/misc/iface_stat.c
new file mode 100644
index 00000000000..bd16449f7d8
--- /dev/null
+++ b/drivers/misc/iface_stat.c
@@ -0,0 +1,234 @@
+/* drivers/misc/iface_stat.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/in.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/iface_stat.h>
+
+static LIST_HEAD(iface_list);
+static struct proc_dir_entry *iface_stat_procdir;
+
+struct iface_stat {
+ struct list_head if_link;
+ char *iface_name;
+ unsigned long tx_bytes;
+ unsigned long rx_bytes;
+ unsigned long tx_packets;
+ unsigned long rx_packets;
+ bool active;
+};
+
+static int read_proc_entry(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len;
+ unsigned long value;
+ char *p = page;
+ unsigned long *iface_entry = (unsigned long *) data;
+ if (!data)
+ return 0;
+
+ value = (unsigned long) (*iface_entry);
+ p += sprintf(p, "%lu\n", value);
+ len = (p - page) - off;
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+ return len;
+}
+
+static int read_proc_bool_entry(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len;
+ bool value;
+ char *p = page;
+ unsigned long *iface_entry = (unsigned long *) data;
+ if (!data)
+ return 0;
+
+ value = (bool) (*iface_entry);
+ p += sprintf(p, "%u\n", value ? 1 : 0);
+ len = (p - page) - off;
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+ return len;
+}
+
+/* Find the entry for tracking the specified interface. */
+static struct iface_stat *get_iface_stat(const char *ifname)
+{
+ struct iface_stat *iface_entry;
+ if (!ifname)
+ return NULL;
+
+ list_for_each_entry(iface_entry, &iface_list, if_link) {
+ if (!strcmp(iface_entry->iface_name, ifname))
+ return iface_entry;
+ }
+ return NULL;
+}
+
+/*
+ * Create a new entry for tracking the specified interface.
+ * Do nothing if the entry already exists.
+ * Called when an interface is configured with a valid IP address.
+ */
+void create_iface_stat(const struct in_device *in_dev)
+{
+ struct iface_stat *new_iface;
+ struct proc_dir_entry *proc_entry;
+ const struct net_device *dev;
+ const char *ifname;
+ struct iface_stat *entry;
+ __be32 ipaddr = 0;
+ struct in_ifaddr *ifa = NULL;
+
+ ASSERT_RTNL(); /* No need for separate locking */
+
+ dev = in_dev->dev;
+ if (!dev) {
+ pr_err("iface_stat: This should never happen.\n");
+ return;
+ }
+
+ ifname = dev->name;
+ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next)
+ if (!strcmp(dev->name, ifa->ifa_label))
+ break;
+
+ if (ifa)
+ ipaddr = ifa->ifa_local;
+ else {
+ pr_err("iface_stat: Interface not found.\n");
+ return;
+ }
+
+ entry = get_iface_stat(dev->name);
+ if (entry != NULL) {
+ pr_debug("iface_stat: Already monitoring device %s\n", ifname);
+ if (ipv4_is_loopback(ipaddr)) {
+ entry->active = false;
+ pr_debug("iface_stat: Disabling monitor for "
+ "loopback device %s\n", ifname);
+ } else {
+ entry->active = true;
+ pr_debug("iface_stat: Re-enabling monitor for "
+ "device %s with ip %pI4\n",
+ ifname, &ipaddr);
+ }
+ return;
+ } else if (ipv4_is_loopback(ipaddr)) {
+ pr_debug("iface_stat: Ignoring monitor for "
+ "loopback device %s with ip %pI4\n",
+ ifname, &ipaddr);
+ return;
+ }
+
+ /* Create a new entry for tracking the specified interface. */
+ new_iface = kmalloc(sizeof(struct iface_stat), GFP_KERNEL);
+ if (new_iface == NULL)
+ return;
+
+ new_iface->iface_name = kmalloc((strlen(ifname)+1)*sizeof(char),
+ GFP_KERNEL);
+ if (new_iface->iface_name == NULL) {
+ kfree(new_iface);
+ return;
+ }
+
+ strcpy(new_iface->iface_name, ifname);
+ /* Counters start at 0, so we can track 4GB of network traffic. */
+ new_iface->tx_bytes = 0;
+ new_iface->rx_bytes = 0;
+ new_iface->rx_packets = 0;
+ new_iface->tx_packets = 0;
+ new_iface->active = true;
+
+ /* Append the newly created iface stat struct to the list. */
+ list_add_tail(&new_iface->if_link, &iface_list);
+ proc_entry = proc_mkdir(ifname, iface_stat_procdir);
+
+ /* Keep reference to iface_stat so we know where to read stats from. */
+ create_proc_read_entry("tx_bytes", S_IRUGO, proc_entry,
+ read_proc_entry, &new_iface->tx_bytes);
+
+ create_proc_read_entry("rx_bytes", S_IRUGO, proc_entry,
+ read_proc_entry, &new_iface->rx_bytes);
+
+ create_proc_read_entry("tx_packets", S_IRUGO, proc_entry,
+ read_proc_entry, &new_iface->tx_packets);
+
+ create_proc_read_entry("rx_packets", S_IRUGO, proc_entry,
+ read_proc_entry, &new_iface->rx_packets);
+
+ create_proc_read_entry("active", S_IRUGO, proc_entry,
+ read_proc_bool_entry, &new_iface->active);
+
+ pr_debug("iface_stat: Now monitoring device %s with ip %pI4\n",
+ ifname, &ipaddr);
+}
+
+/*
+ * Update stats for the specified interface. Do nothing if the entry
+ * does not exist (when a device was never configured with an IP address).
+ * Called when an device is being unregistered.
+ */
+void iface_stat_update(struct net_device *dev)
+{
+ const struct net_device_stats *stats = dev_get_stats(dev);
+ struct iface_stat *entry;
+
+ ASSERT_RTNL();
+
+ entry = get_iface_stat(dev->name);
+ if (entry == NULL) {
+ pr_debug("iface_stat: dev %s monitor not found\n", dev->name);
+ return;
+ }
+
+ if (entry->active) { /* FIXME: Support for more than 4GB */
+ entry->tx_bytes += stats->tx_bytes;
+ entry->tx_packets += stats->tx_packets;
+ entry->rx_bytes += stats->rx_bytes;
+ entry->rx_packets += stats->rx_packets;
+ entry->active = false;
+ pr_debug("iface_stat: Updating stats for "
+ "dev %s which went down\n", dev->name);
+ } else
+ pr_debug("iface_stat: Didn't update stats for "
+ "dev %s which went down\n", dev->name);
+}
+
+static int __init iface_stat_init(void)
+{
+ iface_stat_procdir = proc_mkdir("iface_stat", NULL);
+ if (!iface_stat_procdir) {
+ pr_err("iface_stat: failed to create proc entry\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+device_initcall(iface_stat_init);
diff --git a/drivers/misc/kernel_debugger.c b/drivers/misc/kernel_debugger.c
new file mode 100644
index 00000000000..44c19fd2f26
--- /dev/null
+++ b/drivers/misc/kernel_debugger.c
@@ -0,0 +1,79 @@
+/* drivers/android/kernel_debugger.c
+ *
+ * Guts of the kernel debugger.
+ * Needs something to actually push commands to it.
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/sysrq.h>
+#include <linux/kernel_debugger.h>
+
+#define dprintf(fmt...) (ctxt->printf(ctxt->cookie, fmt))
+
+static void do_ps(struct kdbg_ctxt *ctxt)
+{
+ struct task_struct *g, *p;
+ unsigned state;
+ static const char stat_nam[] = "RSDTtZX";
+
+ dprintf("pid ppid prio task pc\n");
+ read_lock(&tasklist_lock);
+ do_each_thread(g, p) {
+ state = p->state ? __ffs(p->state) + 1 : 0;
+ dprintf("%5d %5d %4d ", p->pid, p->parent->pid, p->prio);
+ dprintf("%-13.13s %c", p->comm,
+ state >= sizeof(stat_nam) ? '?' : stat_nam[state]);
+ if (state == TASK_RUNNING)
+ dprintf(" running\n");
+ else
+ dprintf(" %08lx\n", thread_saved_pc(p));
+ } while_each_thread(g, p);
+ read_unlock(&tasklist_lock);
+}
+
+int log_buf_copy(char *dest, int idx, int len);
+extern int do_syslog(int type, char __user *bug, int count);
+static void do_sysrq(struct kdbg_ctxt *ctxt, char rq)
+{
+ char buf[128];
+ int ret;
+ int idx = 0;
+ do_syslog(5 /* clear */, NULL, 0);
+ handle_sysrq(rq, NULL);
+ while (1) {
+ ret = log_buf_copy(buf, idx, sizeof(buf) - 1);
+ if (ret <= 0)
+ break;
+ buf[ret] = 0;
+ dprintf("%s", buf);
+ idx += ret;
+ }
+}
+
+int kernel_debugger(struct kdbg_ctxt *ctxt, char *cmd)
+{
+ if (!strcmp(cmd, "ps"))
+ do_ps(ctxt);
+ if (!strcmp(cmd, "sysrq"))
+ do_sysrq(ctxt, 'h');
+ if (!strncmp(cmd, "sysrq ", 6))
+ do_sysrq(ctxt, cmd[6]);
+
+ return 0;
+}
+
diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c
new file mode 100644
index 00000000000..63435389c54
--- /dev/null
+++ b/drivers/misc/mbox.c
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Stefan Nilsson <stefan.xk.nilsson@stericsson.com> for ST-Ericsson.
+ * Author: Martin Persson <martin.persson@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+/*
+ * Mailbox nomenclature:
+ *
+ * APE MODEM
+ * mbox pairX
+ * ..........................
+ * . .
+ * . peer .
+ * . send ---- .
+ * . --> | | .
+ * . | | .
+ * . ---- .
+ * . .
+ * . local .
+ * . rec ---- .
+ * . | | <-- .
+ * . | | .
+ * . ---- .
+ * .........................
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/completion.h>
+#include <mach/mbox.h>
+
+#define MBOX_NAME "mbox"
+
+#define MBOX_FIFO_DATA 0x000
+#define MBOX_FIFO_ADD 0x004
+#define MBOX_FIFO_REMOVE 0x008
+#define MBOX_FIFO_THRES_FREE 0x00C
+#define MBOX_FIFO_THRES_OCCUP 0x010
+#define MBOX_FIFO_STATUS 0x014
+
+#define MBOX_DISABLE_IRQ 0x4
+#define MBOX_ENABLE_IRQ 0x0
+#define MBOX_LATCH 1
+
+/* Global list of all mailboxes */
+static struct list_head mboxs = LIST_HEAD_INIT(mboxs);
+
+static struct mbox *get_mbox_with_id(u8 id)
+{
+ u8 i;
+ struct list_head *pos = &mboxs;
+ for (i = 0; i <= id; i++)
+ pos = pos->next;
+
+ return (struct mbox *) list_entry(pos, struct mbox, list);
+}
+
+int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block)
+{
+ int res = 0;
+
+ spin_lock(&mbox->lock);
+
+ dev_dbg(&(mbox->pdev->dev),
+ "About to buffer 0x%X to mailbox 0x%X."
+ " ri = %d, wi = %d\n",
+ mbox_msg, (u32)mbox, mbox->read_index,
+ mbox->write_index);
+
+ /* Check if write buffer is full */
+ while (((mbox->write_index + 1) % MBOX_BUF_SIZE) == mbox->read_index) {
+ if (!block) {
+ dev_dbg(&(mbox->pdev->dev),
+ "Buffer full in non-blocking call! "
+ "Returning -ENOMEM!\n");
+ res = -ENOMEM;
+ goto exit;
+ }
+ spin_unlock(&mbox->lock);
+ dev_dbg(&(mbox->pdev->dev),
+ "Buffer full in blocking call! Sleeping...\n");
+ mbox->client_blocked = 1;
+ wait_for_completion(&mbox->buffer_available);
+ dev_dbg(&(mbox->pdev->dev),
+ "Blocking send was woken up! Trying again...\n");
+ spin_lock(&mbox->lock);
+ }
+
+ mbox->buffer[mbox->write_index] = mbox_msg;
+ mbox->write_index = (mbox->write_index + 1) % MBOX_BUF_SIZE;
+
+ /*
+ * Indicate that we want an IRQ as soon as there is a slot
+ * in the FIFO
+ */
+ writel(MBOX_ENABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE);
+
+exit:
+ spin_unlock(&mbox->lock);
+ return res;
+}
+EXPORT_SYMBOL(mbox_send);
+
+#if defined(CONFIG_DEBUG_FS)
+/*
+ * Expected input: <value> <nbr sends>
+ * Example: "echo 0xdeadbeef 4 > mbox-node" sends 0xdeadbeef 4 times
+ */
+static ssize_t mbox_write_fifo(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ unsigned long mbox_mess;
+ unsigned long nbr_sends;
+ unsigned long i;
+ char int_buf[16];
+ char *token;
+ char *val;
+
+ struct mbox *mbox = (struct mbox *) dev->platform_data;
+
+ strncpy((char *) &int_buf, buf, sizeof(int_buf));
+ token = (char *) &int_buf;
+
+ /* Parse message */
+ val = strsep(&token, " ");
+ if ((val == NULL) || (strict_strtoul(val, 16, &mbox_mess) != 0))
+ mbox_mess = 0xDEADBEEF;
+
+ val = strsep(&token, " ");
+ if ((val == NULL) || (strict_strtoul(val, 10, &nbr_sends) != 0))
+ nbr_sends = 1;
+
+ dev_dbg(dev, "Will write 0x%lX %ld times using data struct at 0x%X\n",
+ mbox_mess, nbr_sends, (u32) mbox);
+
+ for (i = 0; i < nbr_sends; i++)
+ mbox_send(mbox, mbox_mess, true);
+
+ return count;
+}
+
+static ssize_t mbox_read_fifo(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int mbox_value;
+ struct mbox *mbox = (struct mbox *) dev->platform_data;
+
+ if ((readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7) <= 0)
+ return sprintf(buf, "Mailbox is empty\n");
+
+ mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA);
+ writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE));
+
+ return sprintf(buf, "0x%X\n", mbox_value);
+}
+
+static DEVICE_ATTR(fifo, S_IWUGO | S_IRUGO, mbox_read_fifo, mbox_write_fifo);
+
+static int mbox_show(struct seq_file *s, void *data)
+{
+ struct list_head *pos;
+ u8 mbox_index = 0;
+
+ list_for_each(pos, &mboxs) {
+ struct mbox *m =
+ (struct mbox *) list_entry(pos, struct mbox, list);
+ if (m == NULL) {
+ seq_printf(s,
+ "Unable to retrieve mailbox %d\n",
+ mbox_index);
+ continue;
+ }
+
+ spin_lock(&m->lock);
+ if ((m->virtbase_peer == NULL) || (m->virtbase_local == NULL)) {
+ seq_printf(s, "MAILBOX %d not setup or corrupt\n",
+ mbox_index);
+ spin_unlock(&m->lock);
+ continue;
+ }
+
+ seq_printf(s,
+ "===========================\n"
+ " MAILBOX %d\n"
+ " PEER MAILBOX DUMP\n"
+ "---------------------------\n"
+ "FIFO: 0x%X (%d)\n"
+ "Free Threshold: 0x%.2X (%d)\n"
+ "Occupied Threshold: 0x%.2X (%d)\n"
+ "Status: 0x%.2X (%d)\n"
+ " Free spaces (ot): %d (%d)\n"
+ " Occup spaces (ot): %d (%d)\n"
+ "===========================\n"
+ " LOCAL MAILBOX DUMP\n"
+ "---------------------------\n"
+ "FIFO: 0x%.X (%d)\n"
+ "Free Threshold: 0x%.2X (%d)\n"
+ "Occupied Threshold: 0x%.2X (%d)\n"
+ "Status: 0x%.2X (%d)\n"
+ " Free spaces (ot): %d (%d)\n"
+ " Occup spaces (ot): %d (%d)\n"
+ "===========================\n"
+ "write_index: %d\n"
+ "read_index : %d\n"
+ "===========================\n"
+ "\n",
+ mbox_index,
+ readl(m->virtbase_peer + MBOX_FIFO_DATA),
+ readl(m->virtbase_peer + MBOX_FIFO_DATA),
+ readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE),
+ readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE),
+ readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP),
+ readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP),
+ readl(m->virtbase_peer + MBOX_FIFO_STATUS),
+ readl(m->virtbase_peer + MBOX_FIFO_STATUS),
+ (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 4) & 0x7,
+ (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 7) & 0x1,
+ (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 0) & 0x7,
+ (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 3) & 0x1,
+ readl(m->virtbase_local + MBOX_FIFO_DATA),
+ readl(m->virtbase_local + MBOX_FIFO_DATA),
+ readl(m->virtbase_local + MBOX_FIFO_THRES_FREE),
+ readl(m->virtbase_local + MBOX_FIFO_THRES_FREE),
+ readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP),
+ readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP),
+ readl(m->virtbase_local + MBOX_FIFO_STATUS),
+ readl(m->virtbase_local + MBOX_FIFO_STATUS),
+ (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 4) & 0x7,
+ (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 7) & 0x1,
+ (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 0) & 0x7,
+ (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 3) & 0x1,
+ m->write_index, m->read_index);
+ mbox_index++;
+ spin_unlock(&m->lock);
+ }
+
+ return 0;
+}
+
+static int mbox_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mbox_show, NULL);
+}
+
+static const struct file_operations mbox_operations = {
+ .owner = THIS_MODULE,
+ .open = mbox_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+static irqreturn_t mbox_irq(int irq, void *arg)
+{
+ u32 mbox_value;
+ int nbr_occup;
+ int nbr_free;
+ struct mbox *mbox = (struct mbox *) arg;
+
+ spin_lock(&mbox->lock);
+
+ dev_dbg(&(mbox->pdev->dev),
+ "mbox IRQ [%d] received. ri = %d, wi = %d\n",
+ irq, mbox->read_index, mbox->write_index);
+
+ /*
+ * Check if we have any outgoing messages, and if there is space for
+ * them in the FIFO.
+ */
+ if (mbox->read_index != mbox->write_index) {
+ /*
+ * Check by reading FREE for LOCAL since that indicates
+ * OCCUP for PEER
+ */
+ nbr_free = (readl(mbox->virtbase_local + MBOX_FIFO_STATUS)
+ >> 4) & 0x7;
+ dev_dbg(&(mbox->pdev->dev),
+ "Status indicates %d empty spaces in the FIFO!\n",
+ nbr_free);
+
+ while ((nbr_free > 0) &&
+ (mbox->read_index != mbox->write_index)) {
+ /* Write the message and latch it into the FIFO */
+ writel(mbox->buffer[mbox->read_index],
+ (mbox->virtbase_peer + MBOX_FIFO_DATA));
+ writel(MBOX_LATCH,
+ (mbox->virtbase_peer + MBOX_FIFO_ADD));
+ dev_dbg(&(mbox->pdev->dev),
+ "Wrote message 0x%X to addr 0x%X\n",
+ mbox->buffer[mbox->read_index],
+ (u32) (mbox->virtbase_peer + MBOX_FIFO_DATA));
+
+ nbr_free--;
+ mbox->read_index =
+ (mbox->read_index + 1) % MBOX_BUF_SIZE;
+ }
+
+ /*
+ * Check if we still want IRQ:s when there is free
+ * space to send
+ */
+ if (mbox->read_index != mbox->write_index) {
+ dev_dbg(&(mbox->pdev->dev),
+ "Still have messages to send, but FIFO full. "
+ "Request IRQ again!\n");
+ writel(MBOX_ENABLE_IRQ,
+ mbox->virtbase_peer + MBOX_FIFO_THRES_FREE);
+ } else {
+ dev_dbg(&(mbox->pdev->dev),
+ "No more messages to send. "
+ "Do not request IRQ again!\n");
+ writel(MBOX_DISABLE_IRQ,
+ mbox->virtbase_peer + MBOX_FIFO_THRES_FREE);
+ }
+
+ /*
+ * Check if we can signal any blocked clients that it is OK to
+ * start buffering again
+ */
+ if (mbox->client_blocked &&
+ (((mbox->write_index + 1) % MBOX_BUF_SIZE)
+ != mbox->read_index)) {
+ dev_dbg(&(mbox->pdev->dev),
+ "Waking up blocked client\n");
+ complete(&mbox->buffer_available);
+ mbox->client_blocked = 0;
+ }
+ }
+
+ /* Check if we have any incoming messages */
+ nbr_occup = readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7;
+ if (nbr_occup == 0)
+ goto exit;
+
+ if (mbox->cb == NULL) {
+ dev_dbg(&(mbox->pdev->dev), "No receive callback registered, "
+ "leaving %d incoming messages in fifo!\n", nbr_occup);
+ goto exit;
+ }
+
+ /* Read and acknowledge the message */
+ mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA);
+ writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE));
+
+ /* Notify consumer of new mailbox message */
+ dev_dbg(&(mbox->pdev->dev), "Calling callback for message 0x%X!\n",
+ mbox_value);
+ mbox->cb(mbox_value, mbox->client_data);
+
+exit:
+ dev_dbg(&(mbox->pdev->dev), "Exit mbox IRQ. ri = %d, wi = %d\n",
+ mbox->read_index, mbox->write_index);
+ spin_unlock(&mbox->lock);
+
+ return IRQ_HANDLED;
+}
+
+/* Setup is executed once for each mbox pair */
+struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv)
+{
+ struct resource *resource;
+ int irq;
+ int res;
+ struct mbox *mbox;
+
+ mbox = get_mbox_with_id(mbox_id);
+ if (mbox == NULL) {
+ dev_err(&(mbox->pdev->dev), "Incorrect mailbox id: %d!\n",
+ mbox_id);
+ goto exit;
+ }
+
+ /*
+ * Check if mailbox has been allocated to someone else,
+ * otherwise allocate it
+ */
+ if (mbox->allocated) {
+ dev_err(&(mbox->pdev->dev), "Mailbox number %d is busy!\n",
+ mbox_id);
+ mbox = NULL;
+ goto exit;
+ }
+ mbox->allocated = true;
+
+ dev_dbg(&(mbox->pdev->dev), "Initiating mailbox number %d: 0x%X...\n",
+ mbox_id, (u32)mbox);
+
+ mbox->client_data = priv;
+ mbox->cb = mbox_cb;
+
+ /* Get addr for peer mailbox and ioremap it */
+ resource = platform_get_resource_byname(mbox->pdev,
+ IORESOURCE_MEM,
+ "mbox_peer");
+ if (resource == NULL) {
+ dev_err(&(mbox->pdev->dev),
+ "Unable to retrieve mbox peer resource\n");
+ mbox = NULL;
+ goto exit;
+ }
+ dev_dbg(&(mbox->pdev->dev),
+ "Resource name: %s start: 0x%X, end: 0x%X\n",
+ resource->name, resource->start, resource->end);
+ mbox->virtbase_peer =
+ ioremap(resource->start, resource->end - resource->start);
+ if (!mbox->virtbase_peer) {
+ dev_err(&(mbox->pdev->dev), "Unable to ioremap peer mbox\n");
+ mbox = NULL;
+ goto exit;
+ }
+ dev_dbg(&(mbox->pdev->dev),
+ "ioremapped peer physical: (0x%X-0x%X) to virtual: 0x%X\n",
+ resource->start, resource->end, (u32) mbox->virtbase_peer);
+
+ /* Get addr for local mailbox and ioremap it */
+ resource = platform_get_resource_byname(mbox->pdev,
+ IORESOURCE_MEM,
+ "mbox_local");
+ if (resource == NULL) {
+ dev_err(&(mbox->pdev->dev),
+ "Unable to retrieve mbox local resource\n");
+ mbox = NULL;
+ goto exit;
+ }
+ dev_dbg(&(mbox->pdev->dev),
+ "Resource name: %s start: 0x%X, end: 0x%X\n",
+ resource->name, resource->start, resource->end);
+ mbox->virtbase_local =
+ ioremap(resource->start, resource->end - resource->start);
+ if (!mbox->virtbase_local) {
+ dev_err(&(mbox->pdev->dev), "Unable to ioremap local mbox\n");
+ mbox = NULL;
+ goto exit;
+ }
+ dev_dbg(&(mbox->pdev->dev),
+ "ioremapped local physical: (0x%X-0x%X) to virtual: 0x%X\n",
+ resource->start, resource->end, (u32) mbox->virtbase_peer);
+
+ init_completion(&mbox->buffer_available);
+ mbox->client_blocked = 0;
+
+ /* Get IRQ for mailbox and allocate it */
+ irq = platform_get_irq_byname(mbox->pdev, "mbox_irq");
+ if (irq < 0) {
+ dev_err(&(mbox->pdev->dev),
+ "Unable to retrieve mbox irq resource\n");
+ mbox = NULL;
+ goto exit;
+ }
+
+ dev_dbg(&(mbox->pdev->dev), "Allocating irq %d...\n", irq);
+ res = request_irq(irq, mbox_irq, 0, mbox->name, (void *) mbox);
+ if (res < 0) {
+ dev_err(&(mbox->pdev->dev),
+ "Unable to allocate mbox irq %d\n", irq);
+ mbox = NULL;
+ goto exit;
+ }
+
+ /* Set up mailbox to not launch IRQ on free space in mailbox */
+ writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE);
+
+ /*
+ * Set up mailbox to launch IRQ on new message if we have
+ * a callback set. If not, do not raise IRQ, but keep message
+ * in FIFO for manual retrieval
+ */
+ if (mbox_cb != NULL)
+ writel(MBOX_ENABLE_IRQ,
+ mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP);
+ else
+ writel(MBOX_DISABLE_IRQ,
+ mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP);
+
+#if defined(CONFIG_DEBUG_FS)
+ res = device_create_file(&(mbox->pdev->dev), &dev_attr_fifo);
+ if (res != 0)
+ dev_warn(&(mbox->pdev->dev),
+ "Unable to create mbox sysfs entry");
+
+ (void) debugfs_create_file("mbox", S_IFREG | S_IRUGO, NULL,
+ NULL, &mbox_operations);
+#endif
+
+ dev_info(&(mbox->pdev->dev),
+ "Mailbox driver with index %d initated!\n", mbox_id);
+
+exit:
+ return mbox;
+}
+EXPORT_SYMBOL(mbox_setup);
+
+
+int __init mbox_probe(struct platform_device *pdev)
+{
+ struct mbox local_mbox;
+ struct mbox *mbox;
+ int res = 0;
+ dev_dbg(&(pdev->dev), "Probing mailbox (pdev = 0x%X)...\n", (u32) pdev);
+
+ memset(&local_mbox, 0x0, sizeof(struct mbox));
+
+ /* Associate our mbox data with the platform device */
+ res = platform_device_add_data(pdev,
+ (void *) &local_mbox,
+ sizeof(struct mbox));
+ if (res != 0) {
+ dev_err(&(pdev->dev),
+ "Unable to allocate driver platform data!\n");
+ goto exit;
+ }
+
+ mbox = (struct mbox *) pdev->dev.platform_data;
+ mbox->pdev = pdev;
+ mbox->write_index = 0;
+ mbox->read_index = 0;
+
+ INIT_LIST_HEAD(&(mbox->list));
+ list_add_tail(&(mbox->list), &mboxs);
+
+ sprintf(mbox->name, "%s", MBOX_NAME);
+ spin_lock_init(&mbox->lock);
+
+ dev_info(&(pdev->dev), "Mailbox driver loaded\n");
+
+exit:
+ return res;
+}
+
+static struct platform_driver mbox_driver = {
+ .driver = {
+ .name = MBOX_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mbox_init(void)
+{
+ return platform_driver_probe(&mbox_driver, mbox_probe);
+}
+
+module_init(mbox_init);
+
+void __exit mbox_exit(void)
+{
+ platform_driver_unregister(&mbox_driver);
+}
+
+module_exit(mbox_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MBOX driver");
diff --git a/drivers/misc/mbox_channels-db5500.c b/drivers/misc/mbox_channels-db5500.c
new file mode 100644
index 00000000000..78801487e4b
--- /dev/null
+++ b/drivers/misc/mbox_channels-db5500.c
@@ -0,0 +1,1137 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Mailbox Logical Driver
+ *
+ * Author: Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com> for ST-Ericsson.
+ * Bibek Basu ,bibek.basu@stericsson.com>
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <mach/mbox.h>
+#include <mach/mbox_channels-db5500.h>
+#include <linux/io.h>
+
+/* Defines start sequence number for given mailbox channel */
+#define CHANNEL_START_SEQUENCE_NUMBER 0x80
+
+/* Defines number of channels per mailbox unit */
+#define CHANNELS_PER_MBOX_UNIT 256
+
+/*
+ * This macro builds mbox channel PDU header with following format:
+ * ---------------------------------------------------------------------------
+ * | | | | |
+ * | Sequence nmbr | Type | Length | Destination logical channel number |
+ * | | | | |
+ * ---------------------------------------------------------------------------
+ * 31 24 20 16 0
+ *
+ */
+#define BUILD_HEADER(chan, len, type, seq_no) \
+ ((chan) | (((len) & 0xf) << 16) | \
+ (((type) & 0xf) << 20) | ((seq_no) << 24))
+
+/* Returns type from mbox message header */
+#define GET_TYPE(mbox_msg) (((mbox_msg) >> 20) & 0xf)
+
+/* Returns channel number from mbox message header */
+#define GET_CHANNEL(mbox_msg) ((mbox_msg) & 0xffff)
+
+/* Returns length of payload from mbox message header */
+#define GET_LENGTH(mbox_msg) (((mbox_msg) >> 16) & 0xf)
+
+/* Returns sequence number from mbox message header */
+#define GET_SEQ_NUMBER(mbox_msg) (((mbox_msg) >> 24)
+
+/* Number of buffers */
+#define NUM_DSP_BUFFER 3
+
+/* circular buffer indicator */
+static int buffer_index;
+
+enum mbox_msg{
+ MBOX_CLOSE,
+ MBOX_OPEN,
+ MBOX_SEND,
+ MBOX_CAST,
+ MBOX_ACK,
+ MBOX_NAK,
+};
+
+enum mbox_dir {
+ MBOX_TX,
+ MBOX_RX,
+};
+
+struct mbox_channel_mapping {
+ u16 chan_base;
+ u8 mbox_id;
+ enum mbox_dir direction;
+};
+
+/* This table maps mbox logical channel to mbox id and direction */
+static struct mbox_channel_mapping channel_mappings[] = {
+ {0x500, 2, MBOX_RX}, /* channel 5 maps to mbox 0.1, dsp->app (unsec) */
+ {0x900, 2, MBOX_TX}, /* channel 9 maps to mbox 0.0, app->dsp (unsec) */
+};
+
+/* This table specifies mailbox ids which mbox channels module will use */
+static u8 mbox_ids[] = {
+ 2, /* app <-> dsp (unsec) */
+};
+
+/**
+ * struct mbox_unit_status - current status of mbox unit
+ * @mbox_id : holds mbox unit identification number
+ * @mbox : holds mbox pointer after mbox_register() call
+ * @tx_chans : holds list of open tx mbox channels
+ * @tx_lock: lock for tx channel
+ * @rx_chans : holds list of open rx mbox channels
+ * @rx_lock: lock for rx channel
+ */
+struct mbox_unit_status {
+ u8 mbox_id;
+ struct mbox *mbox;
+ struct list_head tx_chans;
+ spinlock_t tx_lock;
+ struct list_head rx_chans;
+ spinlock_t rx_lock;
+};
+
+static struct {
+ struct platform_device *pdev;
+ struct mbox_unit_status mbox_unit[ARRAY_SIZE(mbox_ids)];
+} channels;
+
+/* This structure describes pending element for mbox tx channel */
+struct pending_elem {
+ struct list_head list;
+ u32 *data;
+ u8 length;
+};
+
+struct rx_pending_elem {
+ u32 buffer[MAILBOX_NR_OF_DATAWORDS];
+ u8 length;
+ void *priv;
+};
+
+struct rx_pending_elem rx_pending[NUM_DSP_BUFFER + 1];
+
+/* This structure holds list of pending elements for mbox tx channel */
+struct tx_channel {
+ struct list_head pending;
+};
+
+/* Specific status for mbox rx channel */
+struct rx_channel {
+ struct list_head pending;
+ spinlock_t lock;
+ u32 buffer[MAILBOX_NR_OF_DATAWORDS];
+ u8 index;
+ u8 length;
+};
+
+/* This structure holds status of mbox channel - common for tx and rx */
+struct channel_status {
+ struct list_head list;
+ u16 channel;
+ int state;
+ mbox_channel_cb_t *cb;
+ void *priv;
+ u8 seq_number;
+ bool with_ack;
+ struct rx_channel rx;
+ struct tx_channel tx;
+ struct work_struct receive_msg;
+ struct work_struct open_msg;
+ struct work_struct cast_msg;
+ struct mutex lock;
+};
+
+/* Checks if provided channel number is valid */
+static bool check_channel(u16 channel, enum mbox_dir direction)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(channel_mappings); i++) {
+ if ((channel >= channel_mappings[i].chan_base) &&
+ (channel < channel_mappings[i].chan_base +
+ CHANNELS_PER_MBOX_UNIT)) {
+ /* Check if direction of given channel is correct*/
+ if (channel_mappings[i].direction == direction)
+ return true;
+ else
+ break;
+ }
+ }
+ return false;
+}
+
+/* get the tx channel corresponding to the given rx channel */
+static u16 get_tx_channel(u16 channel)
+{
+ int i;
+ int relative_chan = 0;
+ int mbox_id = 0xFF;
+ u16 tx_channel = 0xFF;
+
+ for (i = 0; i < ARRAY_SIZE(channel_mappings); i++) {
+ if ((channel >= channel_mappings[i].chan_base) &&
+ (channel < channel_mappings[i].chan_base +
+ CHANNELS_PER_MBOX_UNIT)) {
+ /* Check if direction of given channel is correct*/
+ relative_chan = channel - channel_mappings[i].chan_base;
+ mbox_id = channel_mappings[i].mbox_id;
+
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(channel_mappings); i++) {
+ if ((mbox_id == channel_mappings[i].mbox_id) &&
+ (channel_mappings[i].direction == MBOX_TX))
+ tx_channel = channel_mappings[i].chan_base +
+ relative_chan;
+ }
+ return tx_channel;
+}
+
+/* Returns mbox unit id for given mbox channel */
+static int get_mbox_id(u16 channel)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(channel_mappings); i++) {
+ if ((channel >= channel_mappings[i].chan_base) &&
+ (channel < channel_mappings[i].chan_base +
+ CHANNELS_PER_MBOX_UNIT)) {
+ return channel_mappings[i].mbox_id;
+ }
+ }
+ /* There is no mbox unit registered for given channel */
+ return -EINVAL;
+}
+
+/* Returns mbox structure saved after mbox_register() call */
+static struct mbox *get_mbox(u16 channel)
+{
+ int i;
+ int mbox_id = get_mbox_id(channel);
+
+ if (mbox_id < 0) {
+ dev_err(&channels.pdev->dev, "couldn't get mbox id\n");
+ return NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(channels.mbox_unit); i++) {
+ if (channels.mbox_unit[i].mbox_id == mbox_id)
+ return channels.mbox_unit[i].mbox;
+ }
+ return NULL;
+}
+
+/* Returns pointer to rx mbox channels list for given mbox unit */
+static struct list_head *get_rx_list(u8 mbox_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mbox_ids); i++) {
+ if (channels.mbox_unit[i].mbox_id == mbox_id)
+ return &channels.mbox_unit[i].rx_chans;
+ }
+ return NULL;
+}
+
+/* Returns pointer to tx mbox channels list for given mbox unit */
+static struct list_head *get_tx_list(u8 mbox_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mbox_ids); i++) {
+ if (channels.mbox_unit[i].mbox_id == mbox_id)
+ return &channels.mbox_unit[i].tx_chans;
+ }
+ return NULL;
+}
+
+static int send_pdu(struct channel_status *chan_status, int command,
+ u16 channel)
+{
+ struct mbox *mbox;
+ u32 header = 0;
+ int ret = 0;
+
+ /* SEND PDU is not supported */
+ if (command == MBOX_SEND) {
+ dev_err(&channels.pdev->dev, "SEND command not implemented\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ mbox = get_mbox(chan_status->channel);
+ if (mbox == NULL) {
+ dev_err(&channels.pdev->dev, "couldn't get mailbox\n");
+ ret = -ENOSYS;
+ goto exit;
+ }
+ /* For CAST type send all pending messages */
+ if (command == MBOX_CAST) {
+ struct list_head *pos, *n;
+
+ /* Send all pending messages from TX channel */
+ list_for_each_safe(pos, n, &chan_status->tx.pending) {
+ struct pending_elem *pending =
+ list_entry(pos, struct pending_elem, list);
+ int i;
+
+ header = BUILD_HEADER(channel,
+ pending->length,
+ command,
+ chan_status->seq_number);
+
+ ret = mbox_send(mbox, header, true);
+ if (ret < 0) {
+ dev_err(&channels.pdev->dev,
+ "failed to send header, err=%d\n", ret);
+ goto exit;
+ }
+
+ for (i = 0; i < pending->length; i++) {
+ ret = mbox_send(mbox, pending->data[i], true);
+ if (ret < 0) {
+ dev_err(&channels.pdev->dev,
+ "failed to send header, err=%d\n", ret);
+ goto exit;
+ }
+ }
+
+ /* Call client's callback that data is already sent */
+ if (chan_status->cb)
+ chan_status->cb(pending->data, pending->length,
+ chan_status->priv);
+ else
+ dev_err(&channels.pdev->dev,
+ "%s no callback provided\n", __func__);
+
+ /* Increment sequence number */
+ chan_status->seq_number++;
+
+ /* Remove and free element from the list */
+ list_del(&pending->list);
+ kfree(pending);
+ }
+ } else {
+ header = BUILD_HEADER(channel, 0,
+ command, chan_status->seq_number);
+
+ ret = mbox_send(mbox, header, true);
+ if (ret < 0)
+ dev_err(&channels.pdev->dev, "failed to send header\n");
+ /* Increment sequence number */
+ chan_status->seq_number++;
+ }
+
+exit:
+ return ret;
+}
+
+void mbox_handle_receive_msg(struct work_struct *work)
+{
+ struct channel_status *rx_chan = container_of(work,
+ struct channel_status,
+ receive_msg);
+
+ /* Call client's callback and reset state */
+ if (rx_chan->cb) {
+ static int rx_pending_count;
+
+ if (rx_pending_count == NUM_DSP_BUFFER)
+ rx_pending_count = 0;
+ else
+ rx_pending_count++;
+ rx_chan->cb(rx_pending[rx_pending_count].buffer,
+ rx_pending[rx_pending_count].length,
+ rx_pending[rx_pending_count].priv);
+ buffer_index--;
+ } else {
+ dev_err(&channels.pdev->dev,
+ "%s no callback provided\n", __func__);
+ }
+
+}
+
+void mbox_handle_open_msg(struct work_struct *work)
+{
+ struct channel_status *tx_chan = container_of(work,
+ struct channel_status,
+ open_msg);
+ /* Change channel state to OPEN */
+ tx_chan->state = MBOX_OPEN;
+ /* If pending list not empty, start sending data */
+ if (!list_empty(&tx_chan->tx.pending))
+ send_pdu(tx_chan, MBOX_CAST, tx_chan->channel);
+}
+
+void mbox_handle_cast_msg(struct work_struct *work)
+{
+ struct channel_status *rx_chan = container_of(work,
+ struct channel_status,
+ cast_msg);
+ /* Check if channel is opened */
+ if (rx_chan->state == MBOX_CLOSE) {
+ /* Peer sent message to closed channel */
+ dev_err(&channels.pdev->dev,
+ "channel in wrong state\n");
+ }
+}
+
+static bool handle_receive_msg(u32 mbox_msg, struct list_head *rx_list)
+{
+ struct list_head *pos;
+ struct channel_status *tmp;
+ int i;
+ static int rx_pending_count;
+ struct channel_status *rx_chan = NULL;
+ struct mbox_unit_status *mbox_unit = container_of(rx_list,
+ struct mbox_unit_status,
+ rx_chans);
+ spin_lock(&mbox_unit->rx_lock);
+ list_for_each(pos, rx_list) {
+ tmp = list_entry(pos, struct channel_status, list);
+ if (tmp->state == MBOX_SEND ||
+ tmp->state == MBOX_CAST)
+ /* Received message is payload */
+ rx_chan = tmp;
+ }
+ /* Received message is header */
+ spin_unlock(&mbox_unit->rx_lock);
+ if (rx_chan) {
+ /* Store received data in RX channel buffer */
+ rx_chan->rx.buffer[rx_chan->rx.index++] = mbox_msg;
+ /* Check if it's last data of PDU */
+ if (rx_chan->rx.index == rx_chan->rx.length) {
+ if (rx_pending_count == NUM_DSP_BUFFER)
+ rx_pending_count = 0;
+ else
+ rx_pending_count++;
+ for (i = 0; i < MAILBOX_NR_OF_DATAWORDS; i++) {
+ rx_pending[rx_pending_count].buffer[i] =
+ rx_chan->rx.buffer[i];
+ }
+ rx_pending[rx_pending_count].length =
+ rx_chan->rx.length;
+ rx_pending[rx_pending_count].priv = rx_chan->priv;
+ rx_chan->rx.index = 0;
+ rx_chan->rx.length = 0;
+ rx_chan->state = MBOX_OPEN;
+ rx_chan->seq_number++;
+ buffer_index++;
+ if (buffer_index >= NUM_DSP_BUFFER)
+ dev_err(&channels.pdev->dev,
+ "rxbuf overflow%d\n", buffer_index);
+ schedule_work(&rx_chan->receive_msg);
+ }
+ dev_dbg(&channels.pdev->dev, "%s OK\n", __func__);
+ return true;
+ }
+ return false;
+}
+
+static void handle_open_msg(u16 channel, u8 mbox_id)
+{
+ struct list_head *tx_list, *pos;
+ struct channel_status *tmp;
+ struct channel_status *tx_chan = NULL;
+ struct mbox_unit_status *mbox_unit;
+ channel = get_tx_channel(channel);
+ dev_dbg(&channels.pdev->dev, "%s mbox_id %d\tchannel %x\n",
+ __func__, mbox_id, channel);
+ /* Get TX channesenx for given mbox unit */
+ tx_list = get_tx_list(mbox_id);
+ if (tx_list == NULL) {
+ dev_err(&channels.pdev->dev, "given mbox id is not valid %d\n",
+ mbox_id);
+ return;
+ }
+ mbox_unit = container_of(tx_list, struct mbox_unit_status, tx_chans);
+ /* Search for channel in tx list */
+ spin_lock(&mbox_unit->tx_lock);
+ list_for_each(pos, tx_list) {
+ tmp = list_entry(pos, struct channel_status, list);
+ dev_dbg(&channels.pdev->dev, "tmp->channel=%d\n",
+ tmp->channel);
+ if (tmp->channel == channel)
+ tx_chan = tmp;
+ }
+ spin_unlock(&mbox_unit->tx_lock);
+ if (tx_chan) {
+ schedule_work(&tx_chan->open_msg);
+ } else {
+ /* No tx channel found on the list, allocate new element */
+ tx_chan = kzalloc(sizeof(*tx_chan), GFP_KERNEL);
+ if (tx_chan == NULL) {
+ dev_err(&channels.pdev->dev,
+ "failed to allocate memory\n");
+ return;
+ }
+
+ /* Fill initial data and add this element to tx list */
+ tx_chan->channel = get_tx_channel(channel);
+ tx_chan->state = MBOX_OPEN;
+ tx_chan->seq_number = CHANNEL_START_SEQUENCE_NUMBER;
+ INIT_LIST_HEAD(&tx_chan->tx.pending);
+ INIT_WORK(&tx_chan->open_msg, mbox_handle_open_msg);
+ INIT_WORK(&tx_chan->cast_msg, mbox_handle_cast_msg);
+ INIT_WORK(&tx_chan->receive_msg, mbox_handle_receive_msg);
+ mutex_init(&tx_chan->lock);
+ spin_lock(&mbox_unit->tx_lock);
+ list_add_tail(&tx_chan->list, tx_list);
+ spin_unlock(&mbox_unit->tx_lock);
+ }
+}
+
+static void handle_cast_msg(u16 channel, struct list_head *rx_list,
+ u32 mbox_msg, bool send)
+{
+ struct list_head *pos;
+ struct channel_status *tmp;
+ struct channel_status *rx_chan = NULL;
+ struct mbox_unit_status *mbox_unit = container_of(rx_list,
+ struct mbox_unit_status,
+ rx_chans);
+ dev_dbg(&channels.pdev->dev, " %s\n", __func__);
+ /* Search for channel in rx list */
+ spin_lock(&mbox_unit->rx_lock);
+ list_for_each(pos, rx_list) {
+ tmp = list_entry(pos, struct channel_status, list);
+ if (tmp->channel == channel)
+ rx_chan = tmp;
+ }
+ spin_unlock(&mbox_unit->rx_lock);
+
+ if (rx_chan) {
+ rx_chan->rx.buffer[0] = mbox_msg;
+ rx_chan->with_ack = send;
+ rx_chan->rx.length = GET_LENGTH(rx_chan->rx.buffer[0]);
+ if (rx_chan->rx.length <= MAILBOX_NR_OF_DATAWORDS &&
+ rx_chan->rx.length > 0) {
+ rx_chan->rx.index = 0;
+ rx_chan->state = MBOX_CAST;
+ }
+ schedule_work(&rx_chan->cast_msg);
+ } else {
+ /* Channel not found, peer sent wrong message */
+ dev_err(&channels.pdev->dev, "channel %d doesn't exist\n",
+ channel);
+ }
+}
+
+/*
+ * This callback is called whenever mbox unit receives data.
+ * priv parameter holds mbox unit id.
+ */
+static void mbox_cb(u32 mbox_msg, void *priv)
+{
+ u8 mbox_id = *(u8 *)priv;
+ struct list_head *rx_list;
+ u8 type = GET_TYPE(mbox_msg);
+ u16 channel = GET_CHANNEL(mbox_msg);
+
+ dev_dbg(&channels.pdev->dev, "%s type %d\t, mbox_msg %x\n",
+ __func__, type, mbox_msg);
+ /* Get RX channels list for given mbox unit */
+ rx_list = get_rx_list(mbox_id);
+ if (rx_list == NULL) {
+ dev_err(&channels.pdev->dev, "given mbox id is not valid %d\n",
+ mbox_id);
+ return;
+ }
+
+ /* If received message is payload this function will take care of it */
+ if (handle_receive_msg(mbox_msg, rx_list))
+ return;
+
+ /* Received message is header as no RX channel is in SEND/CAST state */
+ switch (type) {
+ case MBOX_CLOSE:
+ /* Not implemented */
+ break;
+ case MBOX_OPEN:
+ handle_open_msg(channel, mbox_id);
+ break;
+ case MBOX_SEND:
+ handle_cast_msg(channel, rx_list, mbox_msg, true);
+ break;
+ case MBOX_CAST:
+ handle_cast_msg(channel, rx_list, mbox_msg, false);
+ break;
+ case MBOX_ACK:
+ case MBOX_NAK:
+ /* Not implemented */
+ break;
+ }
+}
+
+int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv)
+{
+ struct channel_status *rx_chan;
+ struct list_head *pos, *rx_list;
+ int res = 0;
+ struct mbox_unit_status *mbox_unit;
+
+ dev_dbg(&channels.pdev->dev, " %s channel = %d\n", __func__, channel);
+ /* Closing of channels is not implemented */
+ if (cb == NULL) {
+ dev_err(&channels.pdev->dev,
+ "channel close is not implemented\n");
+ res = -EINVAL;
+ goto exit;
+ }
+
+ /* Check if provided channel number is valid */
+ if (!check_channel(channel, MBOX_RX)) {
+ dev_err(&channels.pdev->dev, "wrong mbox channel number %d\n",
+ channel);
+ res = -EINVAL;
+ goto exit;
+ }
+
+ rx_list = get_rx_list(get_mbox_id(channel));
+ if (rx_list == NULL) {
+ dev_err(&channels.pdev->dev, "given mbox id is not valid\n");
+ res = -EINVAL;
+ goto exit;
+ }
+
+ mbox_unit = container_of(rx_list, struct mbox_unit_status, rx_chans);
+
+ /* Check if channel is already registered */
+ spin_lock(&mbox_unit->rx_lock);
+ list_for_each(pos, rx_list) {
+ rx_chan = list_entry(pos, struct channel_status, list);
+
+ if (rx_chan->channel == channel) {
+ dev_dbg(&channels.pdev->dev,
+ "channel already registered\n");
+ rx_chan->cb = cb;
+ rx_chan->priv = priv;
+ spin_unlock(&mbox_unit->rx_lock);
+ goto exit;
+ }
+ }
+ spin_unlock(&mbox_unit->rx_lock);
+
+ rx_chan = kzalloc(sizeof(*rx_chan), GFP_KERNEL);
+ if (rx_chan == NULL) {
+ dev_err(&channels.pdev->dev,
+ "couldn't allocate channel status\n");
+ res = -ENOMEM;
+ goto exit;
+ }
+
+ /* Fill out newly allocated element and add it to rx list */
+ rx_chan->channel = channel;
+ rx_chan->cb = cb;
+ rx_chan->priv = priv;
+ rx_chan->seq_number = CHANNEL_START_SEQUENCE_NUMBER;
+ mutex_init(&rx_chan->lock);
+ INIT_LIST_HEAD(&rx_chan->rx.pending);
+ INIT_WORK(&rx_chan->open_msg, mbox_handle_open_msg);
+ INIT_WORK(&rx_chan->cast_msg, mbox_handle_cast_msg);
+ INIT_WORK(&rx_chan->receive_msg, mbox_handle_receive_msg);
+ spin_lock(&mbox_unit->rx_lock);
+ list_add_tail(&rx_chan->list, rx_list);
+ spin_unlock(&mbox_unit->rx_lock);
+
+ mutex_lock(&rx_chan->lock);
+ res = send_pdu(rx_chan, MBOX_OPEN, get_tx_channel(rx_chan->channel));
+ if (res) {
+ dev_err(&channels.pdev->dev, "failed to send OPEN command\n");
+ spin_lock(&mbox_unit->rx_lock);
+ list_del(&rx_chan->list);
+ spin_unlock(&mbox_unit->rx_lock);
+ kfree(rx_chan);
+ } else {
+ rx_chan->seq_number++;
+ rx_chan->state = MBOX_OPEN;
+ }
+ mutex_unlock(&rx_chan->lock);
+exit:
+ return res;
+}
+EXPORT_SYMBOL(mbox_channel_register);
+
+int mbox_channel_send(struct mbox_channel_msg *msg)
+{
+ struct list_head *pos, *tx_list;
+ struct channel_status *tmp = NULL;
+ struct channel_status *tx_chan = NULL;
+ struct pending_elem *pending;
+ struct mbox_unit_status *mbox_unit;
+
+ if (msg->length > MAILBOX_NR_OF_DATAWORDS || msg->length == 0) {
+ dev_err(&channels.pdev->dev, "data length incorrect\n");
+ return -EINVAL;
+ }
+
+ if (!check_channel(msg->channel, MBOX_TX)) {
+ dev_err(&channels.pdev->dev, "wrong channel number %d\n",
+ msg->channel);
+ return -EINVAL;
+ }
+
+ tx_list = get_tx_list(get_mbox_id(msg->channel));
+ if (tx_list == NULL) {
+ dev_err(&channels.pdev->dev, "given mbox id is not valid\n");
+ return -EINVAL;
+ }
+
+ mbox_unit = container_of(tx_list, struct mbox_unit_status, tx_chans);
+
+ spin_lock(&mbox_unit->tx_lock);
+ dev_dbg(&channels.pdev->dev, "send:tx_list=%x\tmbox_unit=%x\n",
+ (u32)tx_list, (u32)mbox_unit);
+ list_for_each(pos, tx_list) {
+ tmp = list_entry(pos, struct channel_status, list);
+ if (tmp->channel == msg->channel)
+ tx_chan = tmp;
+ }
+ spin_unlock(&mbox_unit->tx_lock);
+ /* Allocate pending element and add it to the list */
+ pending = kzalloc(sizeof(*pending), GFP_KERNEL);
+ if (pending == NULL) {
+ dev_err(&channels.pdev->dev,
+ "couldn't allocate memory for pending\n");
+ return -ENOMEM;
+ }
+ pending->data = msg->data;
+ pending->length = msg->length;
+
+ if (tx_chan) {
+ mutex_lock(&tx_chan->lock);
+ list_add_tail(&pending->list, &tx_chan->tx.pending);
+ tx_chan->cb = msg->cb;
+ tx_chan->priv = msg->priv;
+ /* If channel is already opened start sending data */
+ if (tx_chan->state == MBOX_OPEN)
+ send_pdu(tx_chan, MBOX_CAST, tx_chan->channel);
+ /* Stop processing here */
+ mutex_unlock(&tx_chan->lock);
+ } else {
+ /* No channel found on the list, allocate new element */
+ tx_chan = kzalloc(sizeof(*tx_chan), GFP_KERNEL);
+ if (tx_chan == NULL) {
+ dev_err(&channels.pdev->dev,
+ "couldn't allocate memory for \
+ tx_chan\n");
+ return -ENOMEM;
+ }
+ tx_chan->channel = msg->channel;
+ tx_chan->cb = msg->cb;
+ tx_chan->priv = msg->priv;
+ tx_chan->state = MBOX_CLOSE;
+ tx_chan->seq_number = CHANNEL_START_SEQUENCE_NUMBER;
+ INIT_LIST_HEAD(&tx_chan->tx.pending);
+ INIT_WORK(&tx_chan->open_msg, mbox_handle_open_msg);
+ INIT_WORK(&tx_chan->cast_msg, mbox_handle_cast_msg);
+ INIT_WORK(&tx_chan->receive_msg, mbox_handle_receive_msg);
+ mutex_init(&tx_chan->lock);
+ spin_lock(&mbox_unit->tx_lock);
+ list_add_tail(&tx_chan->list, tx_list);
+ spin_unlock(&mbox_unit->tx_lock);
+ mutex_lock(&tx_chan->lock);
+ list_add_tail(&pending->list, &tx_chan->tx.pending);
+ mutex_unlock(&tx_chan->lock);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(mbox_channel_send);
+
+static void revoke_pending_msgs(struct channel_status *tx_chan)
+{
+ struct list_head *pos, *n;
+ struct pending_elem *pending;
+
+ list_for_each_safe(pos, n, &tx_chan->tx.pending) {
+ pending = list_entry(pos, struct pending_elem, list);
+
+ if (tx_chan->cb)
+ tx_chan->cb(pending->data, pending->length,
+ tx_chan->priv);
+ else
+ dev_err(&channels.pdev->dev,
+ "%s no callback provided\n", __func__);
+ list_del(&pending->list);
+ kfree(pending);
+ }
+}
+
+/* Clear all pending messages from TX channel */
+int mbox_channel_revoke_messages(u16 channel)
+{
+ struct list_head *pos, *tx_list;
+ struct channel_status *tmp;
+ struct channel_status *tx_chan = NULL;
+ struct mbox_unit_status *mbox_unit;
+ int res = 0;
+
+ if (!check_channel(channel, MBOX_TX)) {
+ dev_err(&channels.pdev->dev,
+ "wrong channel number %d\n", channel);
+ return -EINVAL;
+ }
+
+ tx_list = get_tx_list(get_mbox_id(channel));
+ if (tx_list == NULL) {
+ dev_err(&channels.pdev->dev, "given mbox id is not valid\n");
+ return -EINVAL;
+ }
+
+ mbox_unit = container_of(tx_list, struct mbox_unit_status, tx_chans);
+
+ spin_lock(&mbox_unit->tx_lock);
+ list_for_each(pos, tx_list) {
+ tmp = list_entry(pos, struct channel_status, list);
+ if (tmp->channel == channel)
+ tx_chan = tmp;
+ }
+ spin_unlock(&mbox_unit->tx_lock);
+
+ if (tx_chan) {
+ mutex_lock(&tx_chan->lock);
+ revoke_pending_msgs(tx_chan);
+ mutex_unlock(&tx_chan->lock);
+ dev_dbg(&channels.pdev->dev, "channel %d cleared\n",
+ channel);
+ } else {
+ dev_err(&channels.pdev->dev, "no channel found\n");
+ res = -EINVAL;
+ }
+
+ dev_dbg(&channels.pdev->dev, "%s exiting %d\n", __func__, res);
+ return res;
+}
+EXPORT_SYMBOL(mbox_channel_revoke_messages);
+
+#if defined(CONFIG_DEBUG_FS)
+#define MBOXTEST_DEBUG 1
+#ifdef MBOXTEST_DEBUG
+#define DBG_TEST(x) x
+#else
+#define DBG_TEST(x)
+#endif
+
+#define MBOX_TEST_MAX_WORDS 3
+#define MBOX_RX_CHAN 0x500
+#define MBOX_TX_RX_CHANNEL_DIFF 0x400
+#define MBOX_MAX_NUM_TRANSFER 30000
+static int registration_done;
+/**
+ * struct mboxtest_data - mbox test via debugfs information
+ * @rx_buff: Buffer for incomming data
+ * @rx_pointer: Ptr to actual RX data buff
+ * @tx_buff Buffer for outgoing data
+ * @tx_pointer: Ptr to actual TX data buff
+ * @tx_done: TX Transfer done indicator
+ * @rx_done: RX Transfer done indicator
+ * @received Received words
+ * @xfer_words: Num of bytes in actual trf
+ * @xfers: Number of transfers
+ * @words Number of total words
+ * @channel: Channel test number
+ */
+struct mboxtest_data {
+ unsigned int *rx_buff;
+ unsigned int *rx_pointer;
+ unsigned int *tx_buff;
+ unsigned int *tx_pointer;
+ struct completion tx_done;
+ struct completion rx_done;
+ int received;
+ int xfer_words;
+ int xfers;
+ int words;
+ int channel;
+};
+
+static void mboxtest_receive_cb(u32 *data, u32 len, void *arg)
+{
+ struct mboxtest_data *mboxtest = (struct mboxtest_data *) arg;
+ int i;
+
+ printk(KERN_INFO "receive_cb.. data.= 0x%X, len = %d\n",
+ *data, len);
+
+ for (i = 0; i < len; i++)
+ *(mboxtest->rx_pointer++) = *(data++);
+
+ mboxtest->received += len;
+
+ printk(KERN_INFO "received = %d, words = %d\n",
+ mboxtest->received, mboxtest->words);
+ if (mboxtest->received >= mboxtest->words)
+ complete(&mboxtest->rx_done);
+ dev_dbg(&channels.pdev->dev, "%s exiting\n", __func__);
+}
+
+static void mboxtest_send_cb(u32 *data, u32 len, void *arg)
+{
+ struct mboxtest_data *mboxtest = (struct mboxtest_data *) arg;
+
+ printk(KERN_INFO "send_cb.. data.= 0x%X, len = %d\n",
+ *data, len);
+
+ complete(&mboxtest->tx_done);
+ dev_dbg(&channels.pdev->dev, "kernel:mboxtest_send_cb exiting\n");
+}
+
+static int mboxtest_transmit(struct mboxtest_data *mboxtest)
+{
+ int status = 0;
+ struct mbox_channel_msg msg;
+
+ dev_dbg(&channels.pdev->dev, "%s entering\n", __func__);
+ init_completion(&mboxtest->tx_done);
+
+ msg.channel = mboxtest->channel;
+ msg.data = mboxtest->tx_pointer;
+ msg.length = mboxtest->words;
+ msg.cb = mboxtest_send_cb;
+ msg.priv = mboxtest;
+
+ status = mbox_channel_send(&msg);
+ if (!status) {
+ mboxtest->tx_pointer += mboxtest->xfer_words;
+ wait_for_completion(&mboxtest->tx_done);
+ }
+
+ dev_dbg(&channels.pdev->dev, "%s exiting %d\n",
+ __func__, status);
+ return status;
+}
+
+static int transfer_test(struct mboxtest_data *mboxtest)
+{
+ int status = 0;
+ int len = 0;
+ int i;
+
+ len = mboxtest->words;
+
+ dev_dbg(&channels.pdev->dev, "%s enterring\n", __func__);
+ /* Allocate buffers */
+ mboxtest->rx_buff = kzalloc(sizeof(unsigned int) * len, GFP_KERNEL);
+ if (!mboxtest->rx_buff) {
+ DBG_TEST(printk(KERN_INFO
+ "Cannot allocate mbox rx memory\n"));
+ status = -ENOMEM;
+ goto err1;
+ }
+ memset(mboxtest->rx_buff, '\0', sizeof(unsigned int) * len);
+
+ mboxtest->tx_buff = kzalloc(sizeof(unsigned int) * len, GFP_KERNEL);
+ if (!mboxtest->tx_buff) {
+ DBG_TEST(printk(KERN_INFO
+ "Cannot allocate mbox tx memory\n"));
+ status = -ENOMEM;
+ goto err2;
+ }
+ memset(mboxtest->tx_buff, '\0', sizeof(unsigned int) * len);
+
+ /* Generate data */
+ get_random_bytes((unsigned char *)mboxtest->tx_buff,
+ sizeof(unsigned int) * len);
+ /* Set pointers */
+ mboxtest->tx_pointer = mboxtest->tx_buff;
+ mboxtest->rx_pointer = mboxtest->rx_buff;
+ mboxtest->received = 0;
+ init_completion(&mboxtest->rx_done);
+
+ /* Start tx transfer test transfer */
+ status = mboxtest_transmit(mboxtest);
+ DBG_TEST(printk(KERN_INFO "xfer_words=%d\n",
+ mboxtest->xfer_words));
+ if (!status)
+ wait_for_completion(&mboxtest->rx_done);
+ for (i = 0; i < len; i++)
+ DBG_TEST(printk(KERN_INFO "%d -> TX:0x%X, RX:0x%X\n", i,
+ mboxtest->tx_buff[i], mboxtest->rx_buff[i]));
+
+ dev_dbg(&channels.pdev->dev, "%s exiting %d\n", __func__, status);
+ return status;
+err2:
+ kfree(mboxtest->rx_buff);
+err1:
+ return status;
+}
+
+static int mboxtest_prepare(struct mboxtest_data *mboxtest)
+{
+ int err = 0;
+
+ mboxtest->xfers = MBOX_MAX_NUM_TRANSFER;
+ /* Calculate number of bytes in each transfer */
+ mboxtest->xfer_words = mboxtest->words / mboxtest->xfers;
+
+ /* Trim to maxiumum data words per transfer */
+ if (mboxtest->xfer_words > MBOX_TEST_MAX_WORDS) {
+ DBG_TEST(printk(KERN_INFO "Recalculating xfers ...\n"));
+ mboxtest->xfer_words = MBOX_TEST_MAX_WORDS;
+ if (mboxtest->words % mboxtest->xfer_words)
+ mboxtest->xfers = (mboxtest->words /
+ mboxtest->xfer_words) + 1;
+ else
+ mboxtest->xfers = (mboxtest->words /
+ mboxtest->xfer_words);
+ }
+
+ DBG_TEST(printk(KERN_INFO "Params: chan=0x%X words=%d, xfers=%d\n",
+ mboxtest->channel, mboxtest->words,
+ mboxtest->xfers));
+
+ if (mbox_channel_register(mboxtest->channel,
+ mboxtest_receive_cb, mboxtest)) {
+ DBG_TEST(printk(KERN_INFO "Cannot register mbox channel\n"));
+ err = -ENOMEM;
+ goto err;
+ }
+
+ registration_done = true;
+ return 0;
+err:
+ kfree(mboxtest);
+ return err;
+}
+
+struct mboxtest_data mboxtest;
+/*
+ * Expected input: <nbr_channel> <nbr_word>
+ * Example: "echo 500 2"
+ */
+static ssize_t mbox_write_channel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ unsigned long nbr_channel;
+ unsigned long nbr_word;
+ char int_buf[16];
+ char *token;
+ char *val;
+
+ strncpy((char *) &int_buf, buf, sizeof(int_buf));
+ token = (char *) &int_buf;
+
+ /* Parse message */
+ val = strsep(&token, " ");
+ if ((val == NULL) || (strict_strtoul(val, 16, &nbr_channel) != 0))
+ nbr_channel = MBOX_RX_CHAN;
+
+ val = strsep(&token, " ");
+ if ((val == NULL) || (strict_strtoul(val, 16, &nbr_word) != 0))
+ nbr_word = 2;
+
+ dev_dbg(dev, "Will setup logical channel %ld\n", nbr_channel);
+ mboxtest.channel = nbr_channel;
+ mboxtest.words = nbr_word;
+
+ if (!registration_done)
+ mboxtest_prepare(&mboxtest);
+ else
+ dev_dbg(&channels.pdev->dev, "already registration done\n");
+
+ return count;
+}
+
+static ssize_t mbox_read_channel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+
+ unsigned long i;
+ static bool config_done;
+
+ if (!config_done) {
+ config_done = true;
+ mboxtest.channel += MBOX_TX_RX_CHANNEL_DIFF;
+ }
+ dev_dbg(dev, "Will transfer %d words %d times at channel 0x%x\n",
+ mboxtest.words, mboxtest.xfers, mboxtest.channel);
+ for (i = 0; i < mboxtest.xfers; i++)
+ transfer_test(&mboxtest);
+
+ return 1;
+}
+static DEVICE_ATTR(channel, S_IWUGO | S_IRUGO, mbox_read_channel,
+ mbox_write_channel);
+
+#endif
+
+static int __init mbox_channel_probe(struct platform_device *pdev)
+{
+ int i, ret = 0;
+ struct mbox *mbox;
+
+ dev_dbg(&(pdev->dev), "Probing mailbox (pdev = 0x%X)...\n", (u32)pdev);
+
+ /* Register to given mailbox units (ids) */
+ for (i = 0; i < ARRAY_SIZE(mbox_ids); i++) {
+ mbox = mbox_setup(mbox_ids[i], mbox_cb, &mbox_ids[i]);
+ if (mbox == NULL) {
+ dev_err(&(pdev->dev), "Unable to setup mailbox %d\n",
+ mbox_ids[i]);
+ ret = -EBUSY;
+ goto exit;
+ }
+ channels.mbox_unit[i].mbox_id = mbox_ids[i];
+ channels.mbox_unit[i].mbox = mbox;
+ INIT_LIST_HEAD(&channels.mbox_unit[i].rx_chans);
+ INIT_LIST_HEAD(&channels.mbox_unit[i].tx_chans);
+ spin_lock_init(&channels.mbox_unit[i].rx_lock);
+ spin_lock_init(&channels.mbox_unit[i].tx_lock);
+ }
+
+ channels.pdev = pdev;
+
+ dev_dbg(&(pdev->dev), "Mailbox channel driver loaded\n");
+#if defined(CONFIG_DEBUG_FS)
+ ret = device_create_file(&(pdev->dev), &dev_attr_channel);
+ if (ret != 0)
+ dev_warn(&(pdev->dev),
+ "Unable to create mbox_channel sysfs entry");
+
+
+#endif
+exit:
+ return ret;
+}
+
+static struct platform_driver mbox_channel_driver = {
+ .driver = {
+ .name = "mbox_channel",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mbox_channel_init(void)
+{
+ platform_device_register_simple("mbox_channel", 0, NULL, 0);
+
+ return platform_driver_probe(&mbox_channel_driver, mbox_channel_probe);
+}
+module_init(mbox_channel_init);
+
+static void __exit mbox_channel_exit(void)
+{
+ platform_driver_unregister(&mbox_channel_driver);
+}
+module_exit(mbox_channel_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MBOX channels driver");
diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c
new file mode 100644
index 00000000000..0bda86bad25
--- /dev/null
+++ b/drivers/misc/pmem.c
@@ -0,0 +1,1408 @@
+/* drivers/android/pmem.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/android_pmem.h>
+#include <linux/mempolicy.h>
+#include <linux/sched.h>
+#include <linux/dma-mapping.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/cacheflush.h>
+
+#define PMEM_MAX_DEVICES 10
+#define PMEM_MAX_ORDER 128
+#define PMEM_MIN_ALLOC PAGE_SIZE
+
+#define PMEM_DEBUG 0
+
+/* indicates that a refernce to this file has been taken via get_pmem_file,
+ * the file should not be released until put_pmem_file is called */
+#define PMEM_FLAGS_BUSY 0x1
+/* indicates that this is a suballocation of a larger master range */
+#define PMEM_FLAGS_CONNECTED 0x1 << 1
+/* indicates this is a master and not a sub allocation and that it is mmaped */
+#define PMEM_FLAGS_MASTERMAP 0x1 << 2
+/* submap and unsubmap flags indicate:
+ * 00: subregion has never been mmaped
+ * 10: subregion has been mmaped, reference to the mm was taken
+ * 11: subretion has ben released, refernece to the mm still held
+ * 01: subretion has been released, reference to the mm has been released
+ */
+#define PMEM_FLAGS_SUBMAP 0x1 << 3
+#define PMEM_FLAGS_UNSUBMAP 0x1 << 4
+
+
+struct pmem_data {
+ /* in alloc mode: an index into the bitmap
+ * in no_alloc mode: the size of the allocation */
+ int index;
+ /* see flags above for descriptions */
+ unsigned int flags;
+ /* protects this data field, if the mm_mmap sem will be held at the
+ * same time as this sem, the mm sem must be taken first (as this is
+ * the order for vma_open and vma_close ops */
+ struct rw_semaphore sem;
+ /* info about the mmaping process */
+ struct vm_area_struct *vma;
+ /* task struct of the mapping process */
+ struct task_struct *task;
+ /* process id of teh mapping process */
+ pid_t pid;
+ /* file descriptor of the master */
+ int master_fd;
+ /* file struct of the master */
+ struct file *master_file;
+ /* a list of currently available regions if this is a suballocation */
+ struct list_head region_list;
+ /* a linked list of data so we can access them for debugging */
+ struct list_head list;
+#if PMEM_DEBUG
+ int ref;
+#endif
+};
+
+struct pmem_bits {
+ unsigned allocated:1; /* 1 if allocated, 0 if free */
+ unsigned order:7; /* size of the region in pmem space */
+};
+
+struct pmem_region_node {
+ struct pmem_region region;
+ struct list_head list;
+};
+
+#define PMEM_DEBUG_MSGS 0
+#if PMEM_DEBUG_MSGS
+#define DLOG(fmt,args...) \
+ do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
+ ##args); } \
+ while (0)
+#else
+#define DLOG(x...) do {} while (0)
+#endif
+
+struct pmem_info {
+ struct miscdevice dev;
+ /* physical start address of the remaped pmem space */
+ unsigned long base;
+ /* vitual start address of the remaped pmem space */
+ unsigned char __iomem *vbase;
+ /* total size of the pmem space */
+ unsigned long size;
+ /* number of entries in the pmem space */
+ unsigned long num_entries;
+ /* pfn of the garbage page in memory */
+ unsigned long garbage_pfn;
+ /* index of the garbage page in the pmem space */
+ int garbage_index;
+ /* the bitmap for the region indicating which entries are allocated
+ * and which are free */
+ struct pmem_bits *bitmap;
+ /* indicates the region should not be managed with an allocator */
+ unsigned no_allocator;
+ /* indicates maps of this region should be cached, if a mix of
+ * cached and uncached is desired, set this and open the device with
+ * O_SYNC to get an uncached region */
+ unsigned cached;
+ unsigned buffered;
+ /* in no_allocator mode the first mapper gets the whole space and sets
+ * this flag */
+ unsigned allocated;
+ /* for debugging, creates a list of pmem file structs, the
+ * data_list_sem should be taken before pmem_data->sem if both are
+ * needed */
+ struct semaphore data_list_sem;
+ struct list_head data_list;
+ /* pmem_sem protects the bitmap array
+ * a write lock should be held when modifying entries in bitmap
+ * a read lock should be held when reading data from bits or
+ * dereferencing a pointer into bitmap
+ *
+ * pmem_data->sem protects the pmem data of a particular file
+ * Many of the function that require the pmem_data->sem have a non-
+ * locking version for when the caller is already holding that sem.
+ *
+ * IF YOU TAKE BOTH LOCKS TAKE THEM IN THIS ORDER:
+ * down(pmem_data->sem) => down(bitmap_sem)
+ */
+ struct rw_semaphore bitmap_sem;
+
+ long (*ioctl)(struct file *, unsigned int, unsigned long);
+ int (*release)(struct inode *, struct file *);
+};
+
+static struct pmem_info pmem[PMEM_MAX_DEVICES];
+static int id_count;
+
+#define PMEM_IS_FREE(id, index) !(pmem[id].bitmap[index].allocated)
+#define PMEM_ORDER(id, index) pmem[id].bitmap[index].order
+#define PMEM_BUDDY_INDEX(id, index) (index ^ (1 << PMEM_ORDER(id, index)))
+#define PMEM_NEXT_INDEX(id, index) (index + (1 << PMEM_ORDER(id, index)))
+#define PMEM_OFFSET(index) (index * PMEM_MIN_ALLOC)
+#define PMEM_START_ADDR(id, index) (PMEM_OFFSET(index) + pmem[id].base)
+#define PMEM_LEN(id, index) ((1 << PMEM_ORDER(id, index)) * PMEM_MIN_ALLOC)
+#define PMEM_END_ADDR(id, index) (PMEM_START_ADDR(id, index) + \
+ PMEM_LEN(id, index))
+#define PMEM_START_VADDR(id, index) (PMEM_OFFSET(id, index) + pmem[id].vbase)
+#define PMEM_END_VADDR(id, index) (PMEM_START_VADDR(id, index) + \
+ PMEM_LEN(id, index))
+#define PMEM_REVOKED(data) (data->flags & PMEM_FLAGS_REVOKED)
+#define PMEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK)))
+#define PMEM_IS_SUBMAP(data) ((data->flags & PMEM_FLAGS_SUBMAP) && \
+ (!(data->flags & PMEM_FLAGS_UNSUBMAP)))
+
+static int pmem_release(struct inode *, struct file *);
+static int pmem_mmap(struct file *, struct vm_area_struct *);
+static int pmem_open(struct inode *, struct file *);
+static long pmem_ioctl(struct file *, unsigned int, unsigned long);
+
+struct file_operations pmem_fops = {
+ .release = pmem_release,
+ .mmap = pmem_mmap,
+ .open = pmem_open,
+ .unlocked_ioctl = pmem_ioctl,
+};
+
+static int get_id(struct file *file)
+{
+ return MINOR(file->f_dentry->d_inode->i_rdev);
+}
+
+int is_pmem_file(struct file *file)
+{
+ int id;
+
+ if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode))
+ return 0;
+ id = get_id(file);
+ if (unlikely(id >= PMEM_MAX_DEVICES))
+ return 0;
+ if (unlikely(file->f_dentry->d_inode->i_rdev !=
+ MKDEV(MISC_MAJOR, pmem[id].dev.minor)))
+ return 0;
+ return 1;
+}
+
+static int has_allocation(struct file *file)
+{
+ struct pmem_data *data;
+ /* check is_pmem_file first if not accessed via pmem_file_ops */
+
+ if (unlikely(!file->private_data))
+ return 0;
+ data = (struct pmem_data *)file->private_data;
+ if (unlikely(data->index < 0))
+ return 0;
+ return 1;
+}
+
+static int is_master_owner(struct file *file)
+{
+ struct file *master_file;
+ struct pmem_data *data;
+ int put_needed, ret = 0;
+
+ if (!is_pmem_file(file) || !has_allocation(file))
+ return 0;
+ data = (struct pmem_data *)file->private_data;
+ if (PMEM_FLAGS_MASTERMAP & data->flags)
+ return 1;
+ master_file = fget_light(data->master_fd, &put_needed);
+ if (master_file && data->master_file == master_file)
+ ret = 1;
+ fput_light(master_file, put_needed);
+ return ret;
+}
+
+static int pmem_free(int id, int index)
+{
+ /* caller should hold the write lock on pmem_sem! */
+ int buddy, curr = index;
+ DLOG("index %d\n", index);
+
+ if (pmem[id].no_allocator) {
+ pmem[id].allocated--;
+ return 0;
+ }
+ /* clean up the bitmap, merging any buddies */
+ pmem[id].bitmap[curr].allocated = 0;
+ /* find a slots buddy Buddy# = Slot# ^ (1 << order)
+ * if the buddy is also free merge them
+ * repeat until the buddy is not free or end of the bitmap is reached
+ */
+ do {
+ buddy = PMEM_BUDDY_INDEX(id, curr);
+ if (PMEM_IS_FREE(id, buddy) &&
+ PMEM_ORDER(id, buddy) == PMEM_ORDER(id, curr)) {
+ PMEM_ORDER(id, buddy)++;
+ PMEM_ORDER(id, curr)++;
+ curr = min(buddy, curr);
+ } else {
+ break;
+ }
+ } while (curr < pmem[id].num_entries);
+
+ return 0;
+}
+
+static void pmem_revoke(struct file *file, struct pmem_data *data);
+
+static int pmem_release(struct inode *inode, struct file *file)
+{
+ struct pmem_data *data = (struct pmem_data *)file->private_data;
+ struct pmem_region_node *region_node;
+ struct list_head *elt, *elt2;
+ int id = get_id(file), ret = 0;
+
+
+ down(&pmem[id].data_list_sem);
+ /* if this file is a master, revoke all the memory in the connected
+ * files */
+ if (PMEM_FLAGS_MASTERMAP & data->flags) {
+ struct pmem_data *sub_data;
+ list_for_each(elt, &pmem[id].data_list) {
+ sub_data = list_entry(elt, struct pmem_data, list);
+ down_read(&sub_data->sem);
+ if (PMEM_IS_SUBMAP(sub_data) &&
+ file == sub_data->master_file) {
+ up_read(&sub_data->sem);
+ pmem_revoke(file, sub_data);
+ } else
+ up_read(&sub_data->sem);
+ }
+ }
+ list_del(&data->list);
+ up(&pmem[id].data_list_sem);
+
+
+ down_write(&data->sem);
+
+ /* if its not a conencted file and it has an allocation, free it */
+ if (!(PMEM_FLAGS_CONNECTED & data->flags) && has_allocation(file)) {
+ down_write(&pmem[id].bitmap_sem);
+ ret = pmem_free(id, data->index);
+ up_write(&pmem[id].bitmap_sem);
+ }
+
+ /* if this file is a submap (mapped, connected file), downref the
+ * task struct */
+ if (PMEM_FLAGS_SUBMAP & data->flags)
+ if (data->task) {
+ put_task_struct(data->task);
+ data->task = NULL;
+ }
+
+ file->private_data = NULL;
+
+ list_for_each_safe(elt, elt2, &data->region_list) {
+ region_node = list_entry(elt, struct pmem_region_node, list);
+ list_del(elt);
+ kfree(region_node);
+ }
+ BUG_ON(!list_empty(&data->region_list));
+
+ up_write(&data->sem);
+ kfree(data);
+ if (pmem[id].release)
+ ret = pmem[id].release(inode, file);
+
+ return ret;
+}
+
+static int pmem_open(struct inode *inode, struct file *file)
+{
+ struct pmem_data *data;
+ int id = get_id(file);
+ int ret = 0;
+
+ DLOG("current %u file %p(%d)\n", current->pid, file, file_count(file));
+
+ data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL);
+ if (!data) {
+ printk("pmem: unable to allocate memory for pmem metadata.");
+ return -1;
+ }
+ data->flags = 0;
+ data->index = -1;
+ data->task = NULL;
+ data->vma = NULL;
+ data->pid = 0;
+ data->master_file = NULL;
+#if PMEM_DEBUG
+ data->ref = 0;
+#endif
+ INIT_LIST_HEAD(&data->region_list);
+ init_rwsem(&data->sem);
+
+ file->private_data = data;
+ INIT_LIST_HEAD(&data->list);
+
+ down(&pmem[id].data_list_sem);
+ list_add(&data->list, &pmem[id].data_list);
+ up(&pmem[id].data_list_sem);
+ return ret;
+}
+
+static unsigned long pmem_order(unsigned long len)
+{
+ int i;
+
+ len = (len + PMEM_MIN_ALLOC - 1)/PMEM_MIN_ALLOC;
+ len--;
+ for (i = 0; i < sizeof(len)*8; i++)
+ if (len >> i == 0)
+ break;
+ return i;
+}
+
+static int pmem_allocate(int id, unsigned long len)
+{
+ /* caller should hold the write lock on pmem_sem! */
+ /* return the corresponding pdata[] entry */
+ int curr = 0;
+ int end = pmem[id].num_entries;
+ int best_fit = -1;
+ unsigned long order = pmem_order(len);
+
+ if (pmem[id].no_allocator) {
+ DLOG("no allocator");
+ if (len > pmem[id].size)
+ return -1;
+ if (pmem[id].allocated)
+ printk(KERN_WARNING "pmem: warning pmem[%d].allocated=%d\n",
+ id, pmem[id].allocated);
+ pmem[id].allocated++;
+ return len;
+ }
+
+ if (order > PMEM_MAX_ORDER)
+ return -1;
+ DLOG("order %lx\n", order);
+
+ /* look through the bitmap:
+ * if you find a free slot of the correct order use it
+ * otherwise, use the best fit (smallest with size > order) slot
+ */
+ while (curr < end) {
+ if (PMEM_IS_FREE(id, curr)) {
+ if (PMEM_ORDER(id, curr) == (unsigned char)order) {
+ /* set the not free bit and clear others */
+ best_fit = curr;
+ break;
+ }
+ if (PMEM_ORDER(id, curr) > (unsigned char)order &&
+ (best_fit < 0 ||
+ PMEM_ORDER(id, curr) < PMEM_ORDER(id, best_fit)))
+ best_fit = curr;
+ }
+ curr = PMEM_NEXT_INDEX(id, curr);
+ }
+
+ /* if best_fit < 0, there are no suitable slots,
+ * return an error
+ */
+ if (best_fit < 0) {
+ printk("pmem: no space left to allocate!\n");
+ return -1;
+ }
+
+ /* now partition the best fit:
+ * split the slot into 2 buddies of order - 1
+ * repeat until the slot is of the correct order
+ */
+ while (PMEM_ORDER(id, best_fit) > (unsigned char)order) {
+ int buddy;
+ PMEM_ORDER(id, best_fit) -= 1;
+ buddy = PMEM_BUDDY_INDEX(id, best_fit);
+ PMEM_ORDER(id, buddy) = PMEM_ORDER(id, best_fit);
+ }
+ pmem[id].bitmap[best_fit].allocated = 1;
+ return best_fit;
+}
+
+static pgprot_t phys_mem_access_prot(struct file *file, pgprot_t vma_prot)
+{
+ int id = get_id(file);
+#ifdef pgprot_noncached
+ if (pmem[id].cached == 0 || file->f_flags & O_SYNC)
+ return pgprot_noncached(vma_prot);
+#endif
+#ifdef pgprot_ext_buffered
+ else if (pmem[id].buffered)
+ return pgprot_ext_buffered(vma_prot);
+#endif
+ return vma_prot;
+}
+
+static unsigned long pmem_start_addr(int id, struct pmem_data *data)
+{
+ if (pmem[id].no_allocator)
+ return PMEM_START_ADDR(id, 0);
+ else
+ return PMEM_START_ADDR(id, data->index);
+
+}
+
+static void *pmem_start_vaddr(int id, struct pmem_data *data)
+{
+ return pmem_start_addr(id, data) - pmem[id].base + pmem[id].vbase;
+}
+
+static unsigned long pmem_len(int id, struct pmem_data *data)
+{
+ if (pmem[id].no_allocator)
+ return data->index;
+ else
+ return PMEM_LEN(id, data->index);
+}
+
+static int pmem_map_garbage(int id, struct vm_area_struct *vma,
+ struct pmem_data *data, unsigned long offset,
+ unsigned long len)
+{
+ int i, garbage_pages = len >> PAGE_SHIFT;
+
+ vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP | VM_SHARED | VM_WRITE;
+ for (i = 0; i < garbage_pages; i++) {
+ if (vm_insert_pfn(vma, vma->vm_start + offset + (i * PAGE_SIZE),
+ pmem[id].garbage_pfn))
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static int pmem_unmap_pfn_range(int id, struct vm_area_struct *vma,
+ struct pmem_data *data, unsigned long offset,
+ unsigned long len)
+{
+ int garbage_pages;
+ DLOG("unmap offset %lx len %lx\n", offset, len);
+
+ BUG_ON(!PMEM_IS_PAGE_ALIGNED(len));
+
+ garbage_pages = len >> PAGE_SHIFT;
+ zap_page_range(vma, vma->vm_start + offset, len, NULL);
+ pmem_map_garbage(id, vma, data, offset, len);
+ return 0;
+}
+
+static int pmem_map_pfn_range(int id, struct vm_area_struct *vma,
+ struct pmem_data *data, unsigned long offset,
+ unsigned long len)
+{
+ DLOG("map offset %lx len %lx\n", offset, len);
+ BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_start));
+ BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_end));
+ BUG_ON(!PMEM_IS_PAGE_ALIGNED(len));
+ BUG_ON(!PMEM_IS_PAGE_ALIGNED(offset));
+
+ if (io_remap_pfn_range(vma, vma->vm_start + offset,
+ (pmem_start_addr(id, data) + offset) >> PAGE_SHIFT,
+ len, vma->vm_page_prot)) {
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static int pmem_remap_pfn_range(int id, struct vm_area_struct *vma,
+ struct pmem_data *data, unsigned long offset,
+ unsigned long len)
+{
+ /* hold the mm semp for the vma you are modifying when you call this */
+ BUG_ON(!vma);
+ zap_page_range(vma, vma->vm_start + offset, len, NULL);
+ return pmem_map_pfn_range(id, vma, data, offset, len);
+}
+
+static void pmem_vma_open(struct vm_area_struct *vma)
+{
+ struct file *file = vma->vm_file;
+ struct pmem_data *data = file->private_data;
+ int id = get_id(file);
+ /* this should never be called as we don't support copying pmem
+ * ranges via fork */
+ BUG_ON(!has_allocation(file));
+ down_write(&data->sem);
+ /* remap the garbage pages, forkers don't get access to the data */
+ pmem_unmap_pfn_range(id, vma, data, 0, vma->vm_start - vma->vm_end);
+ up_write(&data->sem);
+}
+
+static void pmem_vma_close(struct vm_area_struct *vma)
+{
+ struct file *file = vma->vm_file;
+ struct pmem_data *data = file->private_data;
+
+ DLOG("current %u ppid %u file %p count %d\n", current->pid,
+ current->parent->pid, file, file_count(file));
+ if (unlikely(!is_pmem_file(file) || !has_allocation(file))) {
+ printk(KERN_WARNING "pmem: something is very wrong, you are "
+ "closing a vm backing an allocation that doesn't "
+ "exist!\n");
+ return;
+ }
+ down_write(&data->sem);
+ if (data->vma == vma) {
+ data->vma = NULL;
+ if ((data->flags & PMEM_FLAGS_CONNECTED) &&
+ (data->flags & PMEM_FLAGS_SUBMAP))
+ data->flags |= PMEM_FLAGS_UNSUBMAP;
+ }
+ /* the kernel is going to free this vma now anyway */
+ up_write(&data->sem);
+}
+
+static struct vm_operations_struct vm_ops = {
+ .open = pmem_vma_open,
+ .close = pmem_vma_close,
+};
+
+static int pmem_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct pmem_data *data;
+ int index;
+ unsigned long vma_size = vma->vm_end - vma->vm_start;
+ int ret = 0, id = get_id(file);
+
+ if (vma->vm_pgoff || !PMEM_IS_PAGE_ALIGNED(vma_size)) {
+#if PMEM_DEBUG
+ printk(KERN_ERR "pmem: mmaps must be at offset zero, aligned"
+ " and a multiple of pages_size.\n");
+#endif
+ return -EINVAL;
+ }
+
+ data = (struct pmem_data *)file->private_data;
+ down_write(&data->sem);
+
+ /* if file->private_data == unalloced, alloc*/
+ if (data && data->index == -1) {
+ down_write(&pmem[id].bitmap_sem);
+ index = pmem_allocate(id, vma->vm_end - vma->vm_start);
+ up_write(&pmem[id].bitmap_sem);
+ data->index = index;
+ }
+ /* either no space was available or an error occured */
+ if (!has_allocation(file)) {
+ ret = -EINVAL;
+ printk("pmem: could not find allocation for map.\n");
+ goto error;
+ }
+
+ if (pmem_len(id, data) < vma_size) {
+#if PMEM_DEBUG
+ printk(KERN_WARNING "pmem: mmap size [%lu] does not match"
+ "size of backing region [%lu].\n", vma_size,
+ pmem_len(id, data));
+#endif
+ ret = -EINVAL;
+ goto error;
+ }
+
+ vma->vm_pgoff = pmem_start_addr(id, data) >> PAGE_SHIFT;
+ vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_page_prot);
+
+ if (data->flags & PMEM_FLAGS_CONNECTED) {
+ struct pmem_region_node *region_node;
+ struct list_head *elt;
+ if (pmem_map_garbage(id, vma, data, 0, vma_size)) {
+ printk("pmem: mmap failed in kernel!\n");
+ ret = -EAGAIN;
+ goto error;
+ }
+ list_for_each(elt, &data->region_list) {
+ region_node = list_entry(elt, struct pmem_region_node,
+ list);
+ DLOG("remapping file: %p %lx %lx\n", file,
+ region_node->region.offset,
+ region_node->region.len);
+ if (pmem_remap_pfn_range(id, vma, data,
+ region_node->region.offset,
+ region_node->region.len)) {
+ ret = -EAGAIN;
+ goto error;
+ }
+ }
+ data->flags |= PMEM_FLAGS_SUBMAP;
+ get_task_struct(current->group_leader);
+ data->task = current->group_leader;
+ data->vma = vma;
+#if PMEM_DEBUG
+ data->pid = current->pid;
+#endif
+ DLOG("submmapped file %p vma %p pid %u\n", file, vma,
+ current->pid);
+ } else {
+ if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) {
+ printk(KERN_INFO "pmem: mmap failed in kernel!\n");
+ ret = -EAGAIN;
+ goto error;
+ }
+ data->flags |= PMEM_FLAGS_MASTERMAP;
+ data->pid = current->pid;
+ }
+ vma->vm_ops = &vm_ops;
+error:
+ up_write(&data->sem);
+ return ret;
+}
+
+/* the following are the api for accessing pmem regions by other drivers
+ * from inside the kernel */
+int get_pmem_user_addr(struct file *file, unsigned long *start,
+ unsigned long *len)
+{
+ struct pmem_data *data;
+ if (!is_pmem_file(file) || !has_allocation(file)) {
+#if PMEM_DEBUG
+ printk(KERN_INFO "pmem: requested pmem data from invalid"
+ "file.\n");
+#endif
+ return -1;
+ }
+ data = (struct pmem_data *)file->private_data;
+ down_read(&data->sem);
+ if (data->vma) {
+ *start = data->vma->vm_start;
+ *len = data->vma->vm_end - data->vma->vm_start;
+ } else {
+ *start = 0;
+ *len = 0;
+ }
+ up_read(&data->sem);
+ return 0;
+}
+
+int get_pmem_addr(struct file *file, unsigned long *start,
+ unsigned long *vstart, unsigned long *len)
+{
+ struct pmem_data *data;
+ int id;
+
+ if (!is_pmem_file(file) || !has_allocation(file)) {
+ return -1;
+ }
+
+ data = (struct pmem_data *)file->private_data;
+ if (data->index == -1) {
+#if PMEM_DEBUG
+ printk(KERN_INFO "pmem: requested pmem data from file with no "
+ "allocation.\n");
+ return -1;
+#endif
+ }
+ id = get_id(file);
+
+ down_read(&data->sem);
+ *start = pmem_start_addr(id, data);
+ *len = pmem_len(id, data);
+ *vstart = (unsigned long)pmem_start_vaddr(id, data);
+ up_read(&data->sem);
+#if PMEM_DEBUG
+ down_write(&data->sem);
+ data->ref++;
+ up_write(&data->sem);
+#endif
+ return 0;
+}
+
+int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart,
+ unsigned long *len, struct file **filp)
+{
+ struct file *file;
+
+ file = fget(fd);
+ if (unlikely(file == NULL)) {
+ printk(KERN_INFO "pmem: requested data from file descriptor "
+ "that doesn't exist.");
+ return -1;
+ }
+
+ if (get_pmem_addr(file, start, vstart, len))
+ goto end;
+
+ if (filp)
+ *filp = file;
+ return 0;
+end:
+ fput(file);
+ return -1;
+}
+
+void put_pmem_file(struct file *file)
+{
+ struct pmem_data *data;
+ int id;
+
+ if (!is_pmem_file(file))
+ return;
+ id = get_id(file);
+ data = (struct pmem_data *)file->private_data;
+#if PMEM_DEBUG
+ down_write(&data->sem);
+ if (data->ref == 0) {
+ printk("pmem: pmem_put > pmem_get %s (pid %d)\n",
+ pmem[id].dev.name, data->pid);
+ BUG();
+ }
+ data->ref--;
+ up_write(&data->sem);
+#endif
+ fput(file);
+}
+
+void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len)
+{
+ struct pmem_data *data;
+ int id;
+ void *vaddr;
+ struct pmem_region_node *region_node;
+ struct list_head *elt;
+ void *flush_start, *flush_end;
+
+ if (!is_pmem_file(file) || !has_allocation(file)) {
+ return;
+ }
+
+ id = get_id(file);
+ data = (struct pmem_data *)file->private_data;
+ if (!pmem[id].cached || file->f_flags & O_SYNC)
+ return;
+
+ down_read(&data->sem);
+ vaddr = pmem_start_vaddr(id, data);
+ /* if this isn't a submmapped file, flush the whole thing */
+ if (unlikely(!(data->flags & PMEM_FLAGS_CONNECTED))) {
+ dmac_flush_range(vaddr, vaddr + pmem_len(id, data));
+ goto end;
+ }
+ /* otherwise, flush the region of the file we are drawing */
+ list_for_each(elt, &data->region_list) {
+ region_node = list_entry(elt, struct pmem_region_node, list);
+ if ((offset >= region_node->region.offset) &&
+ ((offset + len) <= (region_node->region.offset +
+ region_node->region.len))) {
+ flush_start = vaddr + region_node->region.offset;
+ flush_end = flush_start + region_node->region.len;
+ dmac_flush_range(flush_start, flush_end);
+ break;
+ }
+ }
+end:
+ up_read(&data->sem);
+}
+
+static int pmem_connect(unsigned long connect, struct file *file)
+{
+ struct pmem_data *data = (struct pmem_data *)file->private_data;
+ struct pmem_data *src_data;
+ struct file *src_file;
+ int ret = 0, put_needed;
+
+ down_write(&data->sem);
+ /* retrieve the src file and check it is a pmem file with an alloc */
+ src_file = fget_light(connect, &put_needed);
+ DLOG("connect %p to %p\n", file, src_file);
+ if (!src_file) {
+ printk("pmem: src file not found!\n");
+ ret = -EINVAL;
+ goto err_no_file;
+ }
+ if (unlikely(!is_pmem_file(src_file) || !has_allocation(src_file))) {
+ printk(KERN_INFO "pmem: src file is not a pmem file or has no "
+ "alloc!\n");
+ ret = -EINVAL;
+ goto err_bad_file;
+ }
+ src_data = (struct pmem_data *)src_file->private_data;
+
+ if (has_allocation(file) && (data->index != src_data->index)) {
+ printk("pmem: file is already mapped but doesn't match this"
+ " src_file!\n");
+ ret = -EINVAL;
+ goto err_bad_file;
+ }
+ data->index = src_data->index;
+ data->flags |= PMEM_FLAGS_CONNECTED;
+ data->master_fd = connect;
+ data->master_file = src_file;
+
+err_bad_file:
+ fput_light(src_file, put_needed);
+err_no_file:
+ up_write(&data->sem);
+ return ret;
+}
+
+static void pmem_unlock_data_and_mm(struct pmem_data *data,
+ struct mm_struct *mm)
+{
+ up_write(&data->sem);
+ if (mm != NULL) {
+ up_write(&mm->mmap_sem);
+ mmput(mm);
+ }
+}
+
+static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data,
+ struct mm_struct **locked_mm)
+{
+ int ret = 0;
+ struct mm_struct *mm = NULL;
+ *locked_mm = NULL;
+lock_mm:
+ down_read(&data->sem);
+ if (PMEM_IS_SUBMAP(data)) {
+ mm = get_task_mm(data->task);
+ if (!mm) {
+#if PMEM_DEBUG
+ printk("pmem: can't remap task is gone!\n");
+#endif
+ up_read(&data->sem);
+ return -1;
+ }
+ }
+ up_read(&data->sem);
+
+ if (mm)
+ down_write(&mm->mmap_sem);
+
+ down_write(&data->sem);
+ /* check that the file didn't get mmaped before we could take the
+ * data sem, this should be safe b/c you can only submap each file
+ * once */
+ if (PMEM_IS_SUBMAP(data) && !mm) {
+ pmem_unlock_data_and_mm(data, mm);
+ goto lock_mm;
+ }
+ /* now check that vma.mm is still there, it could have been
+ * deleted by vma_close before we could get the data->sem */
+ if ((data->flags & PMEM_FLAGS_UNSUBMAP) && (mm != NULL)) {
+ /* might as well release this */
+ if (data->flags & PMEM_FLAGS_SUBMAP) {
+ put_task_struct(data->task);
+ data->task = NULL;
+ /* lower the submap flag to show the mm is gone */
+ data->flags &= ~(PMEM_FLAGS_SUBMAP);
+ }
+ pmem_unlock_data_and_mm(data, mm);
+ return -1;
+ }
+ *locked_mm = mm;
+ return ret;
+}
+
+int pmem_remap(struct pmem_region *region, struct file *file,
+ unsigned operation)
+{
+ int ret;
+ struct pmem_region_node *region_node;
+ struct mm_struct *mm = NULL;
+ struct list_head *elt, *elt2;
+ int id = get_id(file);
+ struct pmem_data *data = (struct pmem_data *)file->private_data;
+
+ /* pmem region must be aligned on a page boundry */
+ if (unlikely(!PMEM_IS_PAGE_ALIGNED(region->offset) ||
+ !PMEM_IS_PAGE_ALIGNED(region->len))) {
+#if PMEM_DEBUG
+ printk("pmem: request for unaligned pmem suballocation "
+ "%lx %lx\n", region->offset, region->len);
+#endif
+ return -EINVAL;
+ }
+
+ /* if userspace requests a region of len 0, there's nothing to do */
+ if (region->len == 0)
+ return 0;
+
+ /* lock the mm and data */
+ ret = pmem_lock_data_and_mm(file, data, &mm);
+ if (ret)
+ return 0;
+
+ /* only the owner of the master file can remap the client fds
+ * that back in it */
+ if (!is_master_owner(file)) {
+#if PMEM_DEBUG
+ printk("pmem: remap requested from non-master process\n");
+#endif
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* check that the requested range is within the src allocation */
+ if (unlikely((region->offset > pmem_len(id, data)) ||
+ (region->len > pmem_len(id, data)) ||
+ (region->offset + region->len > pmem_len(id, data)))) {
+#if PMEM_DEBUG
+ printk(KERN_INFO "pmem: suballoc doesn't fit in src_file!\n");
+#endif
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (operation == PMEM_MAP) {
+ region_node = kmalloc(sizeof(struct pmem_region_node),
+ GFP_KERNEL);
+ if (!region_node) {
+ ret = -ENOMEM;
+#if PMEM_DEBUG
+ printk(KERN_INFO "No space to allocate metadata!");
+#endif
+ goto err;
+ }
+ region_node->region = *region;
+ list_add(&region_node->list, &data->region_list);
+ } else if (operation == PMEM_UNMAP) {
+ int found = 0;
+ list_for_each_safe(elt, elt2, &data->region_list) {
+ region_node = list_entry(elt, struct pmem_region_node,
+ list);
+ if (region->len == 0 ||
+ (region_node->region.offset == region->offset &&
+ region_node->region.len == region->len)) {
+ list_del(elt);
+ kfree(region_node);
+ found = 1;
+ }
+ }
+ if (!found) {
+#if PMEM_DEBUG
+ printk("pmem: Unmap region does not map any mapped "
+ "region!");
+#endif
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ if (data->vma && PMEM_IS_SUBMAP(data)) {
+ if (operation == PMEM_MAP)
+ ret = pmem_remap_pfn_range(id, data->vma, data,
+ region->offset, region->len);
+ else if (operation == PMEM_UNMAP)
+ ret = pmem_unmap_pfn_range(id, data->vma, data,
+ region->offset, region->len);
+ }
+
+err:
+ pmem_unlock_data_and_mm(data, mm);
+ return ret;
+}
+
+static void pmem_revoke(struct file *file, struct pmem_data *data)
+{
+ struct pmem_region_node *region_node;
+ struct list_head *elt, *elt2;
+ struct mm_struct *mm = NULL;
+ int id = get_id(file);
+ int ret = 0;
+
+ data->master_file = NULL;
+ ret = pmem_lock_data_and_mm(file, data, &mm);
+ /* if lock_data_and_mm fails either the task that mapped the fd, or
+ * the vma that mapped it have already gone away, nothing more
+ * needs to be done */
+ if (ret)
+ return;
+ /* unmap everything */
+ /* delete the regions and region list nothing is mapped any more */
+ if (data->vma)
+ list_for_each_safe(elt, elt2, &data->region_list) {
+ region_node = list_entry(elt, struct pmem_region_node,
+ list);
+ pmem_unmap_pfn_range(id, data->vma, data,
+ region_node->region.offset,
+ region_node->region.len);
+ list_del(elt);
+ kfree(region_node);
+ }
+ /* delete the master file */
+ pmem_unlock_data_and_mm(data, mm);
+}
+
+static void pmem_get_size(struct pmem_region *region, struct file *file)
+{
+ struct pmem_data *data = (struct pmem_data *)file->private_data;
+ int id = get_id(file);
+
+ if (!has_allocation(file)) {
+ region->offset = 0;
+ region->len = 0;
+ return;
+ } else {
+ region->offset = pmem_start_addr(id, data);
+ region->len = pmem_len(id, data);
+ }
+ DLOG("offset %lx len %lx\n", region->offset, region->len);
+}
+
+
+static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct pmem_data *data;
+ int id = get_id(file);
+
+ switch (cmd) {
+ case PMEM_GET_PHYS:
+ {
+ struct pmem_region region;
+ DLOG("get_phys\n");
+ if (!has_allocation(file)) {
+ region.offset = 0;
+ region.len = 0;
+ } else {
+ data = (struct pmem_data *)file->private_data;
+ region.offset = pmem_start_addr(id, data);
+ region.len = pmem_len(id, data);
+ }
+ /* printk(KERN_INFO "pmem: request for physical address of pmem region "
+ "from process %d.\n", current->pid);*/
+ if (copy_to_user((void __user *)arg, &region,
+ sizeof(struct pmem_region)))
+ return -EFAULT;
+ break;
+ }
+ case PMEM_MAP:
+ {
+ struct pmem_region region;
+ if (copy_from_user(&region, (void __user *)arg,
+ sizeof(struct pmem_region)))
+ return -EFAULT;
+ data = (struct pmem_data *)file->private_data;
+ return pmem_remap(&region, file, PMEM_MAP);
+ }
+ break;
+ case PMEM_UNMAP:
+ {
+ struct pmem_region region;
+ if (copy_from_user(&region, (void __user *)arg,
+ sizeof(struct pmem_region)))
+ return -EFAULT;
+ data = (struct pmem_data *)file->private_data;
+ return pmem_remap(&region, file, PMEM_UNMAP);
+ break;
+ }
+ case PMEM_GET_SIZE:
+ {
+ struct pmem_region region;
+ DLOG("get_size\n");
+ pmem_get_size(&region, file);
+ if (copy_to_user((void __user *)arg, &region,
+ sizeof(struct pmem_region)))
+ return -EFAULT;
+ break;
+ }
+ case PMEM_GET_TOTAL_SIZE:
+ {
+ struct pmem_region region;
+ DLOG("get total size\n");
+ region.offset = 0;
+ get_id(file);
+ region.len = pmem[id].size;
+ if (copy_to_user((void __user *)arg, &region,
+ sizeof(struct pmem_region)))
+ return -EFAULT;
+ break;
+ }
+ case PMEM_ALLOCATE:
+ {
+ if (has_allocation(file))
+ return -EINVAL;
+ data = (struct pmem_data *)file->private_data;
+ data->index = pmem_allocate(id, arg);
+ break;
+ }
+ case PMEM_CONNECT:
+ DLOG("connect\n");
+ return pmem_connect(arg, file);
+ break;
+#if 0 /* Removing android-2.6.32 PMEM_CACHE_FLUSH in favour of STE one */
+ case PMEM_CACHE_FLUSH:
+ {
+ struct pmem_region region;
+ DLOG("flush\n");
+ if (copy_from_user(&region, (void __user *)arg,
+ sizeof(struct pmem_region)))
+ return -EFAULT;
+ flush_pmem_file(file, region.offset, region.len);
+ break;
+ }
+#endif
+ case PMEM_CACHE_FLUSH:
+ {
+ struct pmem_cache_flush_region region;
+ struct vm_area_struct *vma = NULL;
+
+ if (copy_from_user(&region, (void __user *)arg,
+ sizeof(struct pmem_cache_flush_region)))
+ return -EFAULT;
+
+ vma = find_vma(current->mm, region.LogicalAddress);
+ if (vma )
+ {
+ data = (struct pmem_data *)file->private_data;
+ down_read(&data->sem);
+ dmac_flush_range((void *)region.LogicalAddress,
+ (void *)(region.LogicalAddress+region.size));
+#ifdef CONFIG_OUTER_CACHE
+ outer_flush_range(region.PhysicalAddress,
+ region.PhysicalAddress+region.size);
+#endif
+ up_read(&data->sem);
+ }
+ break;
+ }
+ case PMEM_CACHE_CLEAN:
+ {
+ struct pmem_cache_flush_region region;
+ struct vm_area_struct *vma = NULL;
+
+ if (copy_from_user(&region, (void __user *)arg,
+ sizeof(struct pmem_cache_flush_region)))
+ return -EFAULT;
+
+ vma = find_vma(current->mm, region.LogicalAddress);
+ if (vma )
+ {
+ data = (struct pmem_data *)file->private_data;
+ down_read(&data->sem);
+ dmac_flush_range((void*)region.LogicalAddress,
+ (void*)(region.LogicalAddress + region.size));
+#ifdef CONFIG_OUTER_CACHE
+ outer_clean_range(region.PhysicalAddress,
+ region.PhysicalAddress+region.size);
+#endif
+ up_read(&data->sem);
+ }
+ break;
+ }
+ case PMEM_CACHE_INVALIDATE:
+ {
+ struct pmem_cache_flush_region region;
+ struct vm_area_struct *vma = NULL;
+
+ if (copy_from_user(&region, (void __user *)arg,
+ sizeof(struct pmem_cache_flush_region)))
+ return -EFAULT;
+
+ vma = find_vma(current->mm, region.LogicalAddress);
+ if (vma )
+ {
+ data = (struct pmem_data *)file->private_data;
+ down_read(&data->sem);
+ dmac_unmap_area((void *)region.LogicalAddress,
+ region.size, DMA_FROM_DEVICE);
+#ifdef CONFIG_OUTER_CACHE
+ outer_inv_range(region.PhysicalAddress,
+ region.PhysicalAddress+region.size);
+#endif
+ up_read(&data->sem);
+ }
+ break;
+ }
+ default:
+ if (pmem[id].ioctl)
+ return pmem[id].ioctl(file, cmd, arg);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#if PMEM_DEBUG
+static ssize_t debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t debug_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct list_head *elt, *elt2;
+ struct pmem_data *data;
+ struct pmem_region_node *region_node;
+ int id = (int)file->private_data;
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0;
+
+ DLOG("debug open\n");
+ n = scnprintf(buffer, debug_bufmax,
+ "pid #: mapped regions (offset, len) (offset,len)...\n");
+
+ down(&pmem[id].data_list_sem);
+ list_for_each(elt, &pmem[id].data_list) {
+ data = list_entry(elt, struct pmem_data, list);
+ down_read(&data->sem);
+ n += scnprintf(buffer + n, debug_bufmax - n, "pid %u:",
+ data->pid);
+ list_for_each(elt2, &data->region_list) {
+ region_node = list_entry(elt2, struct pmem_region_node,
+ list);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "(%lx,%lx) ",
+ region_node->region.offset,
+ region_node->region.len);
+ }
+ n += scnprintf(buffer + n, debug_bufmax - n, "\n");
+ up_read(&data->sem);
+ }
+ up(&pmem[id].data_list_sem);
+
+ n++;
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static struct file_operations debug_fops = {
+ .read = debug_read,
+ .open = debug_open,
+};
+#endif
+
+#if 0
+static struct miscdevice pmem_dev = {
+ .name = "pmem",
+ .fops = &pmem_fops,
+};
+#endif
+
+int pmem_setup(struct android_pmem_platform_data *pdata,
+ long (*ioctl)(struct file *, unsigned int, unsigned long),
+ int (*release)(struct inode *, struct file *))
+{
+ int err = 0;
+ int i, index = 0;
+ int id = id_count;
+ id_count++;
+
+ pmem[id].no_allocator = pdata->no_allocator;
+ pmem[id].cached = pdata->cached;
+ pmem[id].buffered = pdata->buffered;
+ pmem[id].base = pdata->start;
+ pmem[id].size = pdata->size;
+ pmem[id].ioctl = ioctl;
+ pmem[id].release = release;
+ init_rwsem(&pmem[id].bitmap_sem);
+ init_MUTEX(&pmem[id].data_list_sem);
+ INIT_LIST_HEAD(&pmem[id].data_list);
+ pmem[id].dev.name = pdata->name;
+ pmem[id].dev.minor = id;
+ pmem[id].dev.fops = &pmem_fops;
+ printk(KERN_INFO "%s: %d init\n", pdata->name, pdata->cached);
+
+ err = misc_register(&pmem[id].dev);
+ if (err) {
+ printk(KERN_ALERT "Unable to register pmem driver!\n");
+ goto err_cant_register_device;
+ }
+ pmem[id].num_entries = pmem[id].size / PMEM_MIN_ALLOC;
+
+ pmem[id].bitmap = kmalloc(pmem[id].num_entries *
+ sizeof(struct pmem_bits), GFP_KERNEL);
+ if (!pmem[id].bitmap)
+ goto err_no_mem_for_metadata;
+
+ memset(pmem[id].bitmap, 0, sizeof(struct pmem_bits) *
+ pmem[id].num_entries);
+
+ for (i = sizeof(pmem[id].num_entries) * 8 - 1; i >= 0; i--) {
+ if ((pmem[id].num_entries) & 1<<i) {
+ PMEM_ORDER(id, index) = i;
+ index = PMEM_NEXT_INDEX(id, index);
+ }
+ }
+
+ if (pmem[id].cached)
+ pmem[id].vbase = ioremap_cached(pmem[id].base,
+ pmem[id].size);
+#ifdef ioremap_ext_buffered
+ else if (pmem[id].buffered)
+ pmem[id].vbase = ioremap_ext_buffered(pmem[id].base,
+ pmem[id].size);
+#endif
+ else
+ pmem[id].vbase = ioremap(pmem[id].base, pmem[id].size);
+
+ if (pmem[id].vbase == 0)
+ goto error_cant_remap;
+
+ pmem[id].garbage_pfn = page_to_pfn(alloc_page(GFP_KERNEL));
+ if (pmem[id].no_allocator)
+ pmem[id].allocated = 0;
+
+#if PMEM_DEBUG
+ debugfs_create_file(pdata->name, S_IFREG | S_IRUGO, NULL, (void *)id,
+ &debug_fops);
+#endif
+ return 0;
+error_cant_remap:
+ kfree(pmem[id].bitmap);
+err_no_mem_for_metadata:
+ misc_deregister(&pmem[id].dev);
+err_cant_register_device:
+ return -1;
+}
+
+static int pmem_probe(struct platform_device *pdev)
+{
+ struct android_pmem_platform_data *pdata;
+
+ if (!pdev || !pdev->dev.platform_data) {
+ printk(KERN_ALERT "Unable to probe pmem!\n");
+ return -1;
+ }
+ pdata = pdev->dev.platform_data;
+ return pmem_setup(pdata, NULL, NULL);
+}
+
+
+static int pmem_remove(struct platform_device *pdev)
+{
+ int id = pdev->id;
+ __free_page(pfn_to_page(pmem[id].garbage_pfn));
+ misc_deregister(&pmem[id].dev);
+ return 0;
+}
+
+static struct platform_driver pmem_driver = {
+ .probe = pmem_probe,
+ .remove = pmem_remove,
+ .driver = { .name = "android_pmem" }
+};
+
+
+static int __init pmem_init(void)
+{
+ return platform_driver_register(&pmem_driver);
+}
+
+static void __exit pmem_exit(void)
+{
+ platform_driver_unregister(&pmem_driver);
+}
+
+module_init(pmem_init);
+module_exit(pmem_exit);
+
diff --git a/drivers/misc/shrm/Kconfig b/drivers/misc/shrm/Kconfig
new file mode 100644
index 00000000000..fffee1c703e
--- /dev/null
+++ b/drivers/misc/shrm/Kconfig
@@ -0,0 +1,49 @@
+#
+# SHM HW kernel configuration
+#
+config U8500_SHRM
+ tristate "U8500 SHRM hardware driver"
+ depends on ARCH_U8500 && PHONET
+ default Y
+ ---help---
+ If you say Y here, you will enable the STN8500 SHM hardware driver.
+
+ If unsure, say N.
+choice
+ prompt "Modem Image Version"
+ depends on U8500_SHRM
+ default SHRM_V1_UPDATES_VERSION
+
+ config SHRM_ED_V1_VERSION
+ depends on U8500_SHRM
+ bool "SHRM ED / V1 "
+ help
+ Modem Images with ED/V1 updates
+
+ config SHRM_V1_UPDATES_VERSION
+ depends on U8500_SHRM
+ bool "SHRM V1 UPDATES"
+ help
+ Modem Images with V1 Updates
+
+endchoice
+
+config U8500_SHRM_LOOP_BACK
+ tristate "U8500 SHRM loopback"
+ depends on U8500_SHRM
+ default n
+ ---help---
+ If you say Y here, you will enable the shm loopback
+
+ If unsure, say N.
+
+config U8500_SHRM_MODEM_SILENT_RESET
+ bool "U8500 SHRM Modem Silent Reset"
+ depends on U8500_SHRM
+ default n
+ ---help---
+ If you say Y here, you will enable the modem silent reset feature
+
+ If unsure, say N.
+
+
diff --git a/drivers/misc/shrm/Makefile b/drivers/misc/shrm/Makefile
new file mode 100644
index 00000000000..8115c24920b
--- /dev/null
+++ b/drivers/misc/shrm/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for SHRM drivers
+#
+
+ifdef CONFIG_PHONET
+u8500_shrm-objs := modem_shrm_driver.o shrm_fifo.o shrm_protocol.o
+else
+u8500_shrm-objs := shrm_driver.o shrm_fifo.o shrm_protocol.o
+endif
+
+obj-$(CONFIG_U8500_SHRM) += u8500_shrm.o
diff --git a/drivers/misc/shrm/modem_shrm_driver.c b/drivers/misc/shrm/modem_shrm_driver.c
new file mode 100644
index 00000000000..29368950256
--- /dev/null
+++ b/drivers/misc/shrm/modem_shrm_driver.c
@@ -0,0 +1,666 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Biju Das <biju.das@stericsson.com> for ST-Ericsson
+ * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com> for ST-Ericsson
+ * Author: Arun Murthy <arun.murthy@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/smp_lock.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/io.h>
+
+#include <mach/isa_ioctl.h>
+#include <mach/shrm_driver.h>
+#include <mach/shrm_private.h>
+#include <mach/shrm_config.h>
+#include <mach/shrm_net.h>
+#include <mach/shrm.h>
+
+#include <linux/skbuff.h>
+#ifdef CONFIG_HIGH_RES_TIMERS
+#include <linux/hrtimer.h>
+static struct hrtimer timer;
+#endif
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/phonet.h>
+
+/* debug functionality */
+#define ISA_DEBUG 0
+
+#define PHONET_TASKLET
+#define MAX_RCV_LEN 2048
+
+void do_phonet_rcv_tasklet(unsigned long unused);
+struct tasklet_struct phonet_rcv_tasklet;
+
+/**
+ * audio_receive() - Receive audio channel completion callback
+ * @shrm: pointer to shrm device information structure
+ * @data: message pointer
+ * @n_bytes: message size
+ * @l2_header: L2 header/device ID 2->audio, 5->audio_loopback
+ *
+ * This fucntion is called from the audio receive handler. Copies the audio
+ * message from the FIFO to the AUDIO queue. The message is later copied from
+ * this queue to the user buffer through the char or net interface read
+ * operation.
+ */
+static int audio_receive(struct shrm_dev *shrm, void *data,
+ u32 n_bytes, u8 l2_header)
+{
+ u32 size = 0;
+ int ret = 0;
+ int idx;
+ u8 *psrc;
+ struct message_queue *q;
+ struct isadev_context *audiodev;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ idx = shrm_get_cdev_index(l2_header);
+ if (idx < 0) {
+ dev_err(shrm->dev, "failed to get index\n");
+ return idx;
+ }
+ audiodev = &shrm->isa_context->isadev[idx];
+ q = &audiodev->dl_queue;
+ spin_lock(&q->update_lock);
+ /* Memcopy RX data first */
+ if ((q->writeptr+n_bytes) >= q->size) {
+ psrc = (u8 *)data;
+ size = (q->size-q->writeptr);
+ /* Copy First Part of msg */
+ memcpy((q->fifo_base+q->writeptr), psrc, size);
+ psrc += size;
+ /* Copy Second Part of msg at the top of fifo */
+ memcpy(q->fifo_base, psrc, (n_bytes-size));
+ } else {
+ memcpy((q->fifo_base+q->writeptr), data, n_bytes);
+ }
+ ret = add_msg_to_queue(q, n_bytes);
+ spin_unlock(&q->update_lock);
+ if (ret < 0)
+ dev_err(shrm->dev, "Adding a msg to message queue failed");
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return ret;
+}
+
+/**
+ * common_receive() - Receive common channel completion callback
+ * @shrm: pointer to the shrm device information structure
+ * @data: message pointer
+ * @n_bytes: message size
+ * @l2_header: L2 header / device ID
+ *
+ * This function is called from the receive handler to copy the respective
+ * ISI, RPC, SECURITY message to its respective queue. The message is then
+ * copied from queue to the user buffer on char net interface read operation.
+ */
+static int common_receive(struct shrm_dev *shrm, void *data,
+ u32 n_bytes, u8 l2_header)
+{
+ u32 size = 0;
+ int ret = 0;
+ int idx;
+ u8 *psrc;
+ struct message_queue *q;
+ struct isadev_context *isa_dev;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ idx = shrm_get_cdev_index(l2_header);
+ if (idx < 0) {
+ dev_err(shrm->dev, "failed to get index\n");
+ return idx;
+ }
+ isa_dev = &shrm->isa_context->isadev[idx];
+ q = &isa_dev->dl_queue;
+ spin_lock(&q->update_lock);
+ /* Memcopy RX data first */
+ if ((q->writeptr+n_bytes) >= q->size) {
+ dev_dbg(shrm->dev, "Inside Loop Back\n");
+ psrc = (u8 *)data;
+ size = (q->size-q->writeptr);
+ /* Copy First Part of msg */
+ memcpy((q->fifo_base+q->writeptr), psrc, size);
+ psrc += size;
+ /* Copy Second Part of msg at the top of fifo */
+ memcpy(q->fifo_base, psrc, (n_bytes-size));
+ } else {
+ memcpy((q->fifo_base+q->writeptr), data, n_bytes);
+ }
+ ret = add_msg_to_queue(q, n_bytes);
+ spin_unlock(&q->update_lock);
+ if (ret < 0) {
+ dev_err(shrm->dev, "Adding a msg to message queue failed");
+ return ret;
+ }
+
+
+ if (l2_header == ISI_MESSAGING) {
+ if (shrm->netdev_flag_up) {
+ dev_dbg(shrm->dev,
+ "scheduling the phonet tasklet from %s!\n",
+ __func__);
+ tasklet_schedule(&phonet_rcv_tasklet);
+ }
+ dev_dbg(shrm->dev,
+ "Out of phonet tasklet %s!!!\n", __func__);
+ }
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return ret;
+}
+
+/**
+ * rx_common_l2msg_handler() - common channel receive handler
+ * @l2_header: L2 header
+ * @msg: pointer to the receive buffer
+ * @length: length of the msg to read
+ * @shrm: pointer to shrm device information structure
+ *
+ * This function is called to receive the message from CaMsgPendingNotification
+ * interrupt handler.
+ */
+static void rx_common_l2msg_handler(u8 l2_header,
+ void *msg, u32 length,
+ struct shrm_dev *shrm)
+{
+ int ret = 0;
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ ret = common_receive(shrm, msg, length, l2_header);
+ if (ret < 0)
+ dev_err(shrm->dev,
+ "common receive with l2 header %d failed\n", l2_header);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+/**
+ * rx_audio_l2msg_handler() - audio channel receive handler
+ * @l2_header: L2 header
+ * @msg: pointer to the receive buffer
+ * @length: length of the msg to read
+ * @shrm: pointer to shrm device information structure
+ *
+ * This function is called to receive the message from CaMsgPendingNotification
+ * interrupt handler.
+ */
+static void rx_audio_l2msg_handler(u8 l2_header,
+ void *msg, u32 length,
+ struct shrm_dev *shrm)
+{
+ int ret = 0;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ ret = audio_receive(shrm, msg, length, l2_header);
+ if (ret < 0)
+ dev_err(shrm->dev, "audio receive failed\n");
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+static int __init shm_initialise_irq(struct shrm_dev *shrm)
+{
+ int err = 0;
+
+ err = shrm_protocol_init(shrm,
+ rx_common_l2msg_handler, rx_audio_l2msg_handler);
+ if (err < 0) {
+ dev_err(shrm->dev, "SHM Protocol Init Failure\n");
+ return err;
+ }
+
+ err = request_irq(shrm->ca_wake_irq,
+ ca_wake_irq_handler, IRQF_TRIGGER_RISING,
+ "ca_wake-up", shrm);
+ if (err < 0) {
+ dev_err(shrm->dev,
+ "Unable to allocate shm tx interrupt line\n");
+ free_irq(shrm->ca_wake_irq, shrm);
+ return err;
+ }
+
+ err = request_irq(shrm->ac_read_notif_0_irq,
+ ac_read_notif_0_irq_handler, 0,
+ "ac_read_notif_0", shrm);
+
+ if (err < 0) {
+ dev_err(shrm->dev,
+ "error ac_read_notif_0_irq interrupt line\n");
+ goto irq_err1;
+ }
+
+ err = request_irq(shrm->ac_read_notif_1_irq,
+ ac_read_notif_1_irq_handler, 0,
+ "ac_read_notif_1", shrm);
+
+ if (err < 0) {
+ dev_err(shrm->dev,
+ "error ac_read_notif_1_irq interrupt line\n");
+ goto irq_err2;
+ }
+
+ err = request_irq(shrm->ca_msg_pending_notif_0_irq,
+ ca_msg_pending_notif_0_irq_handler, 0,
+ "ca_msg_pending_notif_0", shrm);
+
+ if (err < 0) {
+ dev_err(shrm->dev,
+ "error ca_msg_pending_notif_0_irq line\n");
+ goto irq_err3;
+ }
+
+ err = request_irq(shrm->ca_msg_pending_notif_1_irq,
+ ca_msg_pending_notif_1_irq_handler, 0,
+ "ca_msg_pending_notif_1", shrm);
+
+ if (err < 0) {
+ dev_err(shrm->dev,
+ "error ca_msg_pending_notif_1_irq interrupt line\n");
+ goto irq_err4;
+ }
+ return err;
+irq_err4:
+ free_irq(shrm->ca_msg_pending_notif_0_irq, shrm);
+irq_err3:
+ free_irq(shrm->ac_read_notif_1_irq, shrm);
+irq_err2:
+ free_irq(shrm->ac_read_notif_0_irq, shrm);
+irq_err1:
+ free_irq(shrm->ca_wake_irq, shrm);
+ return err;
+}
+
+static void free_shm_irq(struct shrm_dev *shrm)
+{
+ free_irq(shrm->ca_wake_irq, shrm);
+ free_irq(shrm->ac_read_notif_0_irq, shrm);
+ free_irq(shrm->ac_read_notif_1_irq, shrm);
+ free_irq(shrm->ca_msg_pending_notif_0_irq, shrm);
+ free_irq(shrm->ca_msg_pending_notif_1_irq, shrm);
+}
+
+
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+static enum hrtimer_restart callback(struct hrtimer *timer)
+{
+ return HRTIMER_NORESTART;
+}
+#endif
+
+void do_phonet_rcv_tasklet(unsigned long unused)
+{
+ ssize_t ret;
+ struct shrm_dev *shrm = (struct shrm_dev *)unused;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ for (;;) {
+ ret = shrm_net_receive(shrm->ndev);
+ if (ret == 0) {
+ dev_dbg(shrm->dev, "len is zero, queue empty\n");
+ break;
+ }
+ if (ret < 0) {
+ dev_err(shrm->dev, "len < 0 !!! error!!!\n");
+ break;
+ }
+ }
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+static int shrm_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct resource *res;
+ struct shrm_dev *shrm = NULL;
+
+ shrm = kzalloc(sizeof(struct shrm_dev), GFP_KERNEL);
+ if (shrm == NULL) {
+ dev_err(&pdev->dev,
+ "Could not allocate memory for struct shm_dev\n");
+ return -ENOMEM;
+ }
+
+ shrm->dev = &pdev->dev;
+ /* initialise the SHM */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(shrm->dev,
+ "Unable to map Ca Wake up interrupt\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+ shrm->ca_wake_irq = res->start;
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+
+ if (!res) {
+ dev_err(shrm->dev,
+ "Unable to map APE_Read_notif_common IRQ base\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+ shrm->ac_read_notif_0_irq = res->start;
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+
+ if (!res) {
+ dev_err(shrm->dev,
+ "Unable to map APE_Read_notif_audio IRQ base\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+ shrm->ac_read_notif_1_irq = res->start;
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 3);
+
+ if (!res) {
+ dev_err(shrm->dev,
+ "Unable to map Cmt_msg_pending_notif_common IRQbase\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+ shrm->ca_msg_pending_notif_0_irq = res->start;
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 4);
+
+ if (!res) {
+ dev_err(shrm->dev,
+ "Unable to map Cmt_msg_pending_notif_audio IRQ base\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+ shrm->ca_msg_pending_notif_1_irq = res->start;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ dev_err(shrm->dev,
+ "Could not get SHM IO memory information\n");
+ err = -ENODEV;
+ goto rollback_intr;
+ }
+ shrm->intr_base = (void __iomem *)ioremap_nocache(res->start,
+ res->end - res->start + 1);
+ if (!(shrm->intr_base)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+ shrm->ape_common_fifo_base_phy =
+ (u32 *)U8500_SHM_FIFO_APE_COMMON_BASE;
+ shrm->ape_common_fifo_base =
+ (void __iomem *)ioremap_nocache(
+ U8500_SHM_FIFO_APE_COMMON_BASE,
+ SHM_FIFO_0_SIZE);
+ shrm->ape_common_fifo_size = (SHM_FIFO_0_SIZE)/4;
+
+ if (!(shrm->ape_common_fifo_base)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_ape_common_fifo_base;
+ }
+ shrm->cmt_common_fifo_base_phy =
+ (u32 *)U8500_SHM_FIFO_CMT_COMMON_BASE;
+ shrm->cmt_common_fifo_base =
+ (void __iomem *)ioremap_nocache(
+ U8500_SHM_FIFO_CMT_COMMON_BASE, SHM_FIFO_0_SIZE);
+ shrm->cmt_common_fifo_size = (SHM_FIFO_0_SIZE)/4;
+
+ if (!(shrm->cmt_common_fifo_base)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_cmt_common_fifo_base;
+ }
+ shrm->ape_audio_fifo_base_phy =
+ (u32 *)U8500_SHM_FIFO_APE_AUDIO_BASE;
+ shrm->ape_audio_fifo_base =
+ (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_APE_AUDIO_BASE,
+ SHM_FIFO_1_SIZE);
+ shrm->ape_audio_fifo_size = (SHM_FIFO_1_SIZE)/4;
+
+ if (!(shrm->ape_audio_fifo_base)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_ape_audio_fifo_base;
+ }
+ shrm->cmt_audio_fifo_base_phy =
+ (u32 *)U8500_SHM_FIFO_CMT_AUDIO_BASE;
+ shrm->cmt_audio_fifo_base =
+ (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_CMT_AUDIO_BASE,
+ SHM_FIFO_1_SIZE);
+ shrm->cmt_audio_fifo_size = (SHM_FIFO_1_SIZE)/4;
+
+ if (!(shrm->cmt_audio_fifo_base)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_cmt_audio_fifo_base;
+ }
+ shrm->ac_common_shared_wptr =
+ (void __iomem *)ioremap(SHM_ACFIFO_0_WRITE_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ac_common_shared_wptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_ac_common_shared_wptr;
+ }
+ shrm->ac_common_shared_rptr =
+ (void __iomem *)ioremap(SHM_ACFIFO_0_READ_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ac_common_shared_rptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+ shrm->ca_common_shared_wptr =
+ (void __iomem *)ioremap(SHM_CAFIFO_0_WRITE_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ca_common_shared_wptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+ shrm->ca_common_shared_rptr =
+ (void __iomem *)ioremap(SHM_CAFIFO_0_READ_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ca_common_shared_rptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+ shrm->ac_audio_shared_wptr =
+ (void __iomem *)ioremap(SHM_ACFIFO_1_WRITE_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ac_audio_shared_wptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+ shrm->ac_audio_shared_rptr =
+ (void __iomem *)ioremap(SHM_ACFIFO_1_READ_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ac_audio_shared_rptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+ shrm->ca_audio_shared_wptr =
+ (void __iomem *)ioremap(SHM_CAFIFO_1_WRITE_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ca_audio_shared_wptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+ shrm->ca_audio_shared_rptr =
+ (void __iomem *)ioremap(SHM_CAFIFO_1_READ_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ca_audio_shared_rptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+
+ if (isa_init(shrm) != 0) {
+ dev_err(shrm->dev, "Driver Initialization Error\n");
+ err = -EBUSY;
+ }
+ /* install handlers and tasklets */
+ if (shm_initialise_irq(shrm)) {
+ dev_err(shrm->dev,
+ "shm error in interrupt registration\n");
+ goto rollback_irq;
+ }
+#ifdef CONFIG_HIGH_RES_TIMERS
+ hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ timer.function = callback;
+ hrtimer_start(&timer, ktime_set(0, 2*NSEC_PER_MSEC), HRTIMER_MODE_REL);
+#endif
+ err = shrm_register_netdev(shrm);
+ if (err < 0)
+ goto rollback_irq;
+
+ tasklet_init(&phonet_rcv_tasklet, do_phonet_rcv_tasklet, 0);
+ phonet_rcv_tasklet.data = (unsigned long)shrm;
+
+ platform_set_drvdata(pdev, shrm);
+
+ return err;
+rollback_irq:
+ free_shm_irq(shrm);
+rollback_map:
+ iounmap(shrm->ac_common_shared_wptr);
+ iounmap(shrm->ac_common_shared_rptr);
+ iounmap(shrm->ca_common_shared_wptr);
+ iounmap(shrm->ca_common_shared_rptr);
+ iounmap(shrm->ac_audio_shared_wptr);
+ iounmap(shrm->ac_audio_shared_rptr);
+ iounmap(shrm->ca_audio_shared_wptr);
+ iounmap(shrm->ca_audio_shared_rptr);
+rollback_ac_common_shared_wptr:
+ iounmap(shrm->cmt_audio_fifo_base);
+rollback_cmt_audio_fifo_base:
+ iounmap(shrm->ape_audio_fifo_base);
+rollback_ape_audio_fifo_base:
+ iounmap(shrm->cmt_common_fifo_base);
+rollback_cmt_common_fifo_base:
+ iounmap(shrm->ape_common_fifo_base);
+rollback_ape_common_fifo_base:
+ iounmap(shrm->intr_base);
+rollback_intr:
+ kfree(shrm);
+ return err;
+}
+
+static int __exit shrm_remove(struct platform_device *pdev)
+{
+ struct shrm_dev *shrm = platform_get_drvdata(pdev);
+
+ free_shm_irq(shrm);
+ iounmap(shrm->intr_base);
+ iounmap(shrm->ape_common_fifo_base);
+ iounmap(shrm->cmt_common_fifo_base);
+ iounmap(shrm->ape_audio_fifo_base);
+ iounmap(shrm->cmt_audio_fifo_base);
+ iounmap(shrm->ac_common_shared_wptr);
+ iounmap(shrm->ac_common_shared_rptr);
+ iounmap(shrm->ca_common_shared_wptr);
+ iounmap(shrm->ca_common_shared_rptr);
+ iounmap(shrm->ac_audio_shared_wptr);
+ iounmap(shrm->ac_audio_shared_rptr);
+ iounmap(shrm->ca_audio_shared_wptr);
+ iounmap(shrm->ca_audio_shared_rptr);
+ shrm_unregister_netdev(shrm);
+ isa_exit(shrm);
+ kfree(shrm);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * u8500_shrm_suspend() - This routine puts the SHRM in to sustend state.
+ * @dev: pointer to device structure.
+ *
+ * This routine checks the current ongoing communication with Modem by
+ * examining the ca_wake state and prevents suspend if modem communication
+ * is on-going.
+ * If ca_wake = 1 (high), modem comm. is on-going; don't suspend
+ * If ca_wake = 0 (low), no comm. with modem on-going.Allow suspend
+ */
+int u8500_shrm_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct shrm_dev *shrm = platform_get_drvdata(pdev);
+ int err;
+
+ dev_dbg(&pdev->dev, "%s called...\n", __func__);
+ dev_dbg(&pdev->dev, "ca_wake_req_state = %x\n",
+ get_ca_wake_req_state());
+
+ /* if ca_wake_req is high, prevent system suspend */
+ if (!get_ca_wake_req_state()) {
+ err = shrm_suspend_netdev(shrm->ndev);
+ return err;
+ } else
+ return -EBUSY;
+}
+
+/**
+ * u8500_shrm_resume() - This routine resumes the SHRM from suspend state.
+ * @dev: pointer to device structure
+ *
+ * This routine restore back the current state of the SHRM
+ */
+int u8500_shrm_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct shrm_dev *shrm = platform_get_drvdata(pdev);
+ int err;
+
+ dev_dbg(&pdev->dev, "%s called...\n", __func__);
+ err = shrm_resume_netdev(shrm->ndev);
+
+ return err;
+}
+
+static const struct dev_pm_ops shrm_dev_pm_ops = {
+ .suspend_noirq = u8500_shrm_suspend,
+ .resume_noirq = u8500_shrm_resume,
+};
+#endif
+
+static struct platform_driver shrm_driver = {
+ .remove = __exit_p(shrm_remove),
+ .driver = {
+ .name = "u8500_shrm",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &shrm_dev_pm_ops,
+#endif
+ },
+};
+
+static int __init shrm_driver_init(void)
+{
+ return platform_driver_probe(&shrm_driver, shrm_probe);
+}
+
+static void __exit shrm_driver_exit(void)
+{
+ platform_driver_unregister(&shrm_driver);
+}
+
+module_init(shrm_driver_init);
+module_exit(shrm_driver_exit);
+
+MODULE_AUTHOR("Biju Das, Kumar Sanghvi, Arun Murthy");
+MODULE_DESCRIPTION("Shared Memory Modem Driver Interface");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/shrm/shrm_driver.c b/drivers/misc/shrm/shrm_driver.c
new file mode 100644
index 00000000000..6277794608a
--- /dev/null
+++ b/drivers/misc/shrm/shrm_driver.c
@@ -0,0 +1,1439 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Biju Das <biju.das@stericsson.com> for ST-Ericsson
+ * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com> for ST-Ericsson
+ * Author: Arun Murthy <arun.murthy@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#define DEBUG
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/smp_lock.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <mach/isa_ioctl.h>
+#include <mach/shrm_driver.h>
+#include <mach/shrm_private.h>
+#include <mach/shrm_config.h>
+#include <mach/shrm.h>
+
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+#include <linux/hrtimer.h>
+static struct hrtimer timer;
+#endif
+
+
+#define NAME "IPC_ISA"
+#define ISA_DEVICES 4
+/**debug functionality*/
+#define ISA_DEBUG 0
+
+#define ISI_MESSAGING (0)
+#define RPC_MESSAGING (1)
+#define AUDIO_MESSAGING (2)
+#define SECURITY_MESSAGING (3)
+
+#define SIZE_OF_FIFO (512*1024)
+
+static u8 message_fifo[4][SIZE_OF_FIFO];
+
+static u8 wr_isi_msg[10*1024];
+static u8 wr_rpc_msg[10*1024];
+static u8 wr_sec_msg[10*1024];
+static u8 wr_audio_msg[10*1024];
+
+/* global data */
+/*
+ * int major:This variable is exported to user as module_param to specify
+ * major number at load time
+ */
+static int major;
+module_param(major, int, 0);
+MODULE_PARM_DESC(major, "Major device number");
+/* global fops mutex */
+static DEFINE_MUTEX(isa_lock);
+rx_cb common_rx;
+rx_cb audio_rx;
+
+
+static int isi_receive(struct shrm_dev *shrm, void *data, u32 n_bytes);
+static int rpc_receive(struct shrm_dev *shrm, void *data, u32 n_bytes);
+static int audio_receive(struct shrm_dev *shrm, void *data, u32 n_bytes);
+static int security_receive(struct shrm_dev *shrm,
+ void *data, u32 n_bytes);
+
+static void rx_common_l2msg_handler(u8 l2_header,
+ void *msg, u32 length,
+ struct shrm_dev *shrm)
+{
+ int ret = 0;
+#ifdef CONFIG_U8500_SHRM_LOOP_BACK
+ u8 *pdata;
+#endif
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ switch (l2_header) {
+ case ISI_MESSAGING:
+ ret = isi_receive(shrm, msg, length);
+ if (ret < 0)
+ dev_err(shrm->dev, "isi receive failed\n");
+ break;
+ case RPC_MESSAGING:
+ ret = rpc_receive(shrm, msg, length);
+ if (ret < 0)
+ dev_err(shrm->dev, "rpc receive failed\n");
+ break;
+ case SECURITY_MESSAGING:
+ ret = security_receive(shrm, msg, length);
+ if (ret < 0)
+ dev_err(shrm->dev,
+ "security receive failed\n");
+ break;
+#ifdef CONFIG_U8500_SHRM_LOOP_BACK
+ case COMMMON_LOOPBACK_MESSAGING:
+ pdata = (u8 *)msg;
+ if ((*pdata == 0x50) || (*pdata == 0xAF)) {
+ ret = isi_receive(shrm, msg, length);
+ if (ret < 0)
+ dev_err(shrm->dev, "isi receive failed\n");
+ } else if ((*pdata == 0x0A) || (*pdata == 0xF5)) {
+ ret = rpc_receive(shrm, msg, length);
+ if (ret < 0)
+ dev_err(shrm->dev, "rpc receive failed\n");
+ } else if ((*pdata == 0xFF) || (*pdata == 0x00)) {
+ ret = security_receive(shrm, msg, length);
+ if (ret < 0)
+ dev_err(shrm->dev,
+ "security receive failed\n");
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+static void rx_audio_l2msg_handler(u8 l2_header,
+ void *msg, u32 length,
+ struct shrm_dev *shrm)
+{
+ int ret = 0;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ audio_receive(shrm, msg, length);
+ if (ret < 0)
+ dev_err(shrm->dev, "audio receive failed\n");
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+static int __init shm_initialise_irq(struct shrm_dev *shrm)
+{
+ int err = 0;
+
+ shrm_protocol_init(shrm,
+ rx_common_l2msg_handler, rx_audio_l2msg_handler);
+
+ err = request_irq(shrm->ca_wake_irq,
+ ca_wake_irq_handler, IRQF_TRIGGER_RISING,
+ "ca_wake-up", shrm);
+ if (err < 0) {
+ dev_err(shrm->dev,
+ "Unable to allocate shm tx interrupt line\n");
+ return err;
+ }
+
+ err = request_irq(shrm->ac_read_notif_0_irq,
+ ac_read_notif_0_irq_handler, 0,
+ "ac_read_notif_0", shrm);
+ if (err < 0) {
+ dev_err(shrm->dev,
+ "error ac_read_notif_0_irq interrupt line\n");
+ goto irq_err1;
+ }
+
+ err = request_irq(shrm->ac_read_notif_1_irq,
+ ac_read_notif_1_irq_handler, 0,
+ "ac_read_notif_1", shrm);
+ if (err < 0) {
+ dev_err(shrm->dev,
+ "error ac_read_notif_1_irq interrupt line\n");
+ goto irq_err2;
+ }
+
+ err = request_irq(shrm->ca_msg_pending_notif_0_irq,
+ ca_msg_pending_notif_0_irq_handler, 0,
+ "ca_msg_pending_notif_0", shrm);
+ if (err < 0) {
+ dev_err(shrm->dev,
+ "error ca_msg_pending_notif_0_irq line\n");
+ goto irq_err3;
+ }
+
+ err = request_irq(shrm->ca_msg_pending_notif_1_irq,
+ ca_msg_pending_notif_1_irq_handler, 0,
+ "ca_msg_pending_notif_1", shrm);
+ if (err < 0) {
+ dev_err(shrm->dev,
+ "error ca_msg_pending_notif_1_irq interrupt line\n");
+ goto irq_err4;
+ }
+
+ return err;
+
+irq_err4:
+ free_irq(shrm->ca_msg_pending_notif_0_irq, shrm);
+irq_err3:
+ free_irq(shrm->ac_read_notif_1_irq, shrm);
+irq_err2:
+ free_irq(shrm->ac_read_notif_0_irq, shrm);
+irq_err1:
+ free_irq(shrm->ca_wake_irq, shrm);
+ return err;
+}
+
+static void free_shm_irq(struct shrm_dev *shrm)
+{
+ free_irq(shrm->ca_wake_irq, shrm);
+ free_irq(shrm->ac_read_notif_0_irq, shrm);
+ free_irq(shrm->ac_read_notif_1_irq, shrm);
+ free_irq(shrm->ca_msg_pending_notif_0_irq, shrm);
+ free_irq(shrm->ca_msg_pending_notif_1_irq, shrm);
+}
+
+/**
+ * create_queue() - To create FIFO for Tx and Rx message buffering.
+ * @q: message queue.
+ * @devicetype: device type 0-isi,1-rpc,2-audio,3-security.
+ *
+ * This function creates a FIFO buffer of n_bytes size using
+ * dma_alloc_coherent(). It also initializes all queue handling
+ * locks, queue management pointers. It also initializes message list
+ * which occupies this queue.
+ *
+ * It return -ENOMEM in case of no memory.
+ */
+static int create_queue(struct message_queue *q, u32 devicetype,
+ struct shrm_dev *shrm)
+{
+ q->fifo_base = (u8 *)&message_fifo[devicetype];
+ q->size = SIZE_OF_FIFO;
+ q->readptr = 0;
+ q->writeptr = 0;
+ q->no = 0;
+ q->shrm = shrm;
+ spin_lock_init(&q->update_lock);
+ INIT_LIST_HEAD(&q->msg_list);
+ init_waitqueue_head(&q->wq_readable);
+ atomic_set(&q->q_rp, 0);
+
+ return 0;
+}
+/**
+ * delete_queue() - To delete FIFO and assiciated memory.
+ * @q: message queue
+ *
+ * This function deletes FIFO created using create_queue() function.
+ * It resets queue management pointers.
+ */
+static void delete_queue(struct message_queue *q)
+{
+ q->size = 0;
+ q->readptr = 0;
+ q->writeptr = 0;
+}
+
+/**
+ * add_msg_to_queue() - Add a message inside inside queue
+ *
+ * @q: message queue
+ * @size: size in bytes
+ *
+ * This function tries to allocate n_bytes of size in FIFO q.
+ * It returns negative number when no memory can be allocated
+ * currently.
+ */
+int add_msg_to_queue(struct message_queue *q, u32 size)
+{
+ struct queue_element *new_msg = NULL;
+ struct shrm_dev *shrm = q->shrm;
+
+ dev_dbg(shrm->dev, "%s IN q->writeptr=%d\n",
+ __func__, q->writeptr);
+ new_msg = kmalloc(sizeof(struct queue_element),
+ GFP_KERNEL|GFP_ATOMIC);
+
+ if (new_msg == NULL) {
+ dev_err(shrm->dev, "memory overflow inside while(1)\n");
+ return -ENOMEM;
+ }
+ new_msg->offset = q->writeptr;
+ new_msg->size = size;
+ new_msg->no = q->no++;
+
+ /* check for overflow condition */
+ if (q->readptr <= q->writeptr) {
+ if (((q->writeptr-q->readptr) + size) >= q->size) {
+ dev_err(shrm->dev, "Buffer overflow !!\n");
+ BUG_ON(((q->writeptr-q->readptr) + size) >= q->size);
+ }
+ } else {
+ if ((q->writeptr + size) >= q->readptr) {
+ dev_err(shrm->dev, "Buffer overflow !!\n");
+ BUG_ON((q->writeptr + size) >= q->readptr);
+ }
+ }
+ q->writeptr = (q->writeptr + size) % q->size;
+ if (list_empty(&q->msg_list)) {
+ list_add_tail(&new_msg->entry, &q->msg_list);
+ /* There can be 2 blocking calls read and another select */
+
+ atomic_set(&q->q_rp, 1);
+ wake_up_interruptible(&q->wq_readable);
+ } else
+ list_add_tail(&new_msg->entry, &q->msg_list);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return 0;
+}
+
+/**
+ * remove_msg_from_queue() - To remove a message from the msg queue.
+ *
+ * @q: message queue
+ *
+ * This function delets a message from the message list associated with message
+ * queue q and also updates read ptr.
+ * If the message list is empty, then, event is set to block the select and
+ * read calls of the paricular queue.
+ *
+ * The message list is FIFO style and message is always added to tail and
+ * removed from head.
+ */
+
+int remove_msg_from_queue(struct message_queue *q)
+{
+ struct queue_element *old_msg = NULL;
+ struct shrm_dev *shrm = q->shrm;
+ struct list_head *msg;
+
+ dev_dbg(shrm->dev, "%s IN q->readptr %d\n",
+ __func__, q->readptr);
+
+ list_for_each(msg, &q->msg_list) {
+ old_msg = list_entry(msg, struct queue_element, entry);
+ if (old_msg == NULL) {
+ dev_err(shrm->dev, ":no message found\n");
+ return -EFAULT;
+ }
+ break;
+ }
+ list_del(msg);
+ q->readptr = (q->readptr + old_msg->size) % q->size;
+ if (list_empty(&q->msg_list)) {
+ dev_dbg(shrm->dev, "List is empty setting RP= 0\n");
+ atomic_set(&q->q_rp, 0);
+ }
+ kfree(old_msg);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return 0;
+}
+
+/**
+ * get_size_of_new_msg() - retrieve new message from message list
+ *
+ * @q: message queue
+ *
+ * This function will retrieve most recent message from the corresponding
+ * queue list. New message is always retrieved from head side.
+ * It returns new message no, offset if FIFO and size.
+ */
+int get_size_of_new_msg(struct message_queue *q)
+{
+ struct queue_element *new_msg = NULL;
+ struct list_head *msg_list;
+ struct shrm_dev *shrm = q->shrm;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ spin_lock_bh(&q->update_lock);
+ list_for_each(msg_list, &q->msg_list) {
+ new_msg = list_entry(msg_list, struct queue_element, entry);
+ if (new_msg == NULL) {
+ spin_unlock_bh(&q->update_lock);
+ dev_err(shrm->dev, "no message found\n");
+ return -1;
+ }
+ break;
+ }
+ spin_unlock_bh(&q->update_lock);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return new_msg->size;
+}
+
+/**
+ * isi_receive() - Rx Completion callback
+ *
+ * @data:message pointer
+ * @n_bytes:message size
+ *
+ * This function is a callback to indicate ISI message reception is complete.
+ * It updates Writeptr of the Fifo
+ */
+static int isi_receive(struct shrm_dev *shrm,
+ void *data, u32 n_bytes)
+{
+ u32 size = 0;
+ int ret = 0;
+ u8 *psrc;
+ struct message_queue *q;
+ struct isadev_context *isidev = &shrm->isa_context->isadev[0];
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ q = &isidev->dl_queue;
+ spin_lock(&q->update_lock);
+ /* Memcopy RX data first */
+ if ((q->writeptr+n_bytes) >= q->size) {
+ dev_dbg(shrm->dev, "Inside Loop Back\n");
+ psrc = (u8 *)data;
+ size = (q->size-q->writeptr);
+ /* Copy First Part of msg */
+ memcpy((q->fifo_base+q->writeptr), psrc, size);
+ psrc += size;
+ /* Copy Second Part of msg at the top of fifo */
+ memcpy(q->fifo_base, psrc, (n_bytes-size));
+ } else {
+ memcpy((q->fifo_base+q->writeptr), data, n_bytes);
+ }
+ ret = add_msg_to_queue(q, n_bytes);
+ if (ret < 0)
+ dev_err(shrm->dev, "Adding msg to message queue failed\n");
+ spin_unlock(&q->update_lock);
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return ret;
+}
+
+/**
+ * rpc_receive() - Rx Completion callback
+ *
+ * @data:message pointer
+ * @n_bytes:message size
+ *
+ * This function is a callback to indicate RPC message reception is complete.
+ * It updates Writeptr of the Fifo
+ */
+static int rpc_receive(struct shrm_dev *shrm,
+ void *data, u32 n_bytes)
+{
+ u32 size = 0;
+ int ret = 0;
+ u8 *psrc;
+ struct message_queue *q;
+ struct isadev_context *rpcdev = &shrm->isa_context->isadev[1];
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ q = &rpcdev->dl_queue;
+ spin_lock(&q->update_lock);
+ /* Memcopy RX data first */
+ if ((q->writeptr+n_bytes) >= q->size) {
+ psrc = (u8 *)data;
+ size = (q->size-q->writeptr);
+ /* Copy First Part of msg */
+ memcpy((q->fifo_base+q->writeptr), psrc, size);
+ psrc += size;
+ /* Copy Second Part of msg at the top of fifo */
+ memcpy(q->fifo_base, psrc, (n_bytes-size));
+ } else {
+ memcpy((q->fifo_base+q->writeptr), data, n_bytes);
+ }
+
+ ret = add_msg_to_queue(q, n_bytes);
+ if (ret < 0)
+ dev_err(shrm->dev, "Adding msg to message queue failed\n");
+ spin_unlock(&q->update_lock);
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return ret;
+}
+
+/**
+ * audio_receive() - Rx Completion callback
+ *
+ * @data:message pointer
+ * @n_bytes:message size
+ *
+ * This function is a callback to indicate audio message reception is complete.
+ * It updates Writeptr of the Fifo
+ */
+static int audio_receive(struct shrm_dev *shrm,
+ void *data, u32 n_bytes)
+{
+ u32 size = 0;
+ int ret = 0;
+ u8 *psrc;
+ struct message_queue *q;
+ struct isadev_context *audiodev = &shrm->isa_context->isadev[2];
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ q = &audiodev->dl_queue;
+ spin_lock(&q->update_lock);
+ /* Memcopy RX data first */
+ if ((q->writeptr+n_bytes) >= q->size) {
+ psrc = (u8 *)data;
+ size = (q->size-q->writeptr);
+ /* Copy First Part of msg */
+ memcpy((q->fifo_base+q->writeptr), psrc, size);
+ psrc += size;
+ /* Copy Second Part of msg at the top of fifo */
+ memcpy(q->fifo_base, psrc, (n_bytes-size));
+ } else {
+ memcpy((q->fifo_base+q->writeptr), data, n_bytes);
+ }
+ ret = add_msg_to_queue(q, n_bytes);
+ if (ret < 0)
+ dev_err(shrm->dev, "Adding msg to message queue failed\n");
+ spin_unlock(&q->update_lock);
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return ret;
+}
+
+/**
+ * security_receive() - Rx Completion callback
+ *
+ * @data:message pointer
+ * @n_bytes: message size
+ *
+ * This function is a callback to indicate security message reception
+ * is complete.It updates Writeptr of the Fifo
+ */
+static int security_receive(struct shrm_dev *shrm,
+ void *data, u32 n_bytes)
+{
+ u32 size = 0;
+ int ret = 0;
+ u8 *psrc;
+ struct message_queue *q;
+ struct isadev_context *secdev = &shrm->isa_context->isadev[3];
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ q = &secdev->dl_queue;
+ spin_lock(&q->update_lock);
+ /* Memcopy RX data first */
+ if ((q->writeptr+n_bytes) >= q->size) {
+ psrc = (u8 *)data;
+ size = (q->size-q->writeptr);
+ /* Copy First Part of msg */
+ memcpy((q->fifo_base+q->writeptr), psrc, size);
+ psrc += size;
+ /* Copy Second Part of msg at the top of fifo */
+ memcpy(q->fifo_base, psrc, (n_bytes-size));
+ } else {
+ memcpy((q->fifo_base+q->writeptr), data, n_bytes);
+ }
+ ret = add_msg_to_queue(q, n_bytes);
+ if (ret < 0)
+ dev_err(shrm->dev, "Adding msg to message queue failed\n");
+ spin_unlock(&q->update_lock);
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return ret;
+}
+
+
+/**
+ * isa_select() - Select Interface
+ *
+ * @filp:file descriptor pointer
+ * @wait:poll_table_struct pointer
+ *
+ * This function is used to perform non-blocking read operations. It allows
+ * a process to determine whether it can read from one or more open files
+ * without blocking. These calls can also block a process until any of a
+ * given set of file descriptors becomes available for reading.
+ * If a file is ready to read, POLLIN | POLLRDNORM bitmask is returned.
+ * The driver method is called whenever the user-space program performs a select
+ * system call involving a file descriptor associated with the driver.
+ */
+static u32 isa_select(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct isadev_context *isadev = filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+ struct message_queue *q;
+ u32 mask = 0;
+ u32 m = iminor(filp->f_path.dentry->d_inode);
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (isadev->device_id != m)
+ return -1;
+ q = &isadev->dl_queue;
+ poll_wait(filp, &q->wq_readable, wait);
+ if (atomic_read(&q->q_rp) == 1)
+ mask = POLLIN | POLLRDNORM;
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return mask;
+}
+
+/**
+ * isa_read() - Read from device
+ *
+ * @filp:file descriptor
+ * @buf:user buffer pointer
+ * @len:size of requested data transfer
+ * @ppos:not used
+ *
+ * This function is called whenever user calls read() system call.
+ * It reads a oldest message from queue and copies it into user buffer and
+ * returns its size.
+ * If there is no message present in queue, then it blocks until new data is
+ * available.
+ */
+ssize_t isa_read(struct file *filp, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct isadev_context *isadev = (struct isadev_context *)
+ filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+ struct message_queue *q;
+ char *psrc;
+ u32 msgsize;
+ u32 size = 0;
+ int ret = 0;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (len <= 0)
+ return -EFAULT;
+ q = &isadev->dl_queue;
+
+ spin_lock_bh(&q->update_lock);
+ if (list_empty(&q->msg_list)) {
+ spin_unlock_bh(&q->update_lock);
+ if (wait_event_interruptible(q->wq_readable,
+ atomic_read(&q->q_rp) == 1)) {
+ return -ERESTARTSYS;
+ }
+ } else
+ spin_unlock_bh(&q->update_lock);
+
+ msgsize = get_size_of_new_msg(q);
+ if ((q->readptr+msgsize) >= q->size) {
+ dev_dbg(shrm->dev, "Inside Loop Back\n");
+ psrc = (char *)buf;
+ size = (q->size-q->readptr);
+ /* Copy First Part of msg */
+ if (copy_to_user(psrc,
+ (u8 *)(q->fifo_base+q->readptr),
+ size)) {
+ dev_err(shrm->dev, "copy_to_user failed\n");
+ return -EFAULT;
+ }
+ psrc += size;
+ /* Copy Second Part of msg at the top of fifo */
+ if (copy_to_user(psrc,
+ (u8 *)(q->fifo_base),
+ (msgsize-size))) {
+ dev_err(shrm->dev, "copy_to_user failed\n");
+ return -EFAULT;
+ }
+ } else {
+ if (copy_to_user(buf,
+ (u8 *)(q->fifo_base+q->readptr),
+ msgsize)) {
+ dev_err(shrm->dev, "copy_to_user failed\n");
+ return -EFAULT;
+ }
+ }
+
+ spin_lock_bh(&q->update_lock);
+ ret = remove_msg_from_queue(q);
+ if (ret < 0) {
+ dev_err(shrm->dev,
+ "Removing msg from message queue failed\n");
+ msgsize = ret;
+ }
+ spin_unlock_bh(&q->update_lock);
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return msgsize;
+}
+/**
+ * isa_write() - Write to device
+ *
+ * @filp:file descriptor
+ * @buf:user buffer pointer
+ * @len:size of requested data transfer
+ * @ppos:not used
+ *
+ * This function is called whenever user calls write() system call.
+ * It checks if there is space available in queue, and copies the message
+ * inside queue. If there is no space, it blocks until space becomes available.
+ * It also schedules transfer thread to transmit the newly added message.
+ */
+static ssize_t isa_write(struct file *filp, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct isadev_context *isadev = filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+ struct message_queue *q;
+ int err, ret;
+ void *addr = 0;
+ u8 l2_header = 0;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ if (len <= 0)
+ return -EFAULT;
+ q = &isadev->dl_queue;
+
+ switch (isadev->device_id) {
+ case ISI_MESSAGING:
+ dev_dbg(shrm->dev, "ISI\n");
+ addr = (void *)wr_isi_msg;
+#ifdef CONFIG_U8500_SHRM_LOOP_BACK
+ dev_dbg(shrm->dev, "Loopback\n");
+ l2_header = COMMON_LOOPBACK_MESSAGING;
+#else
+ l2_header = isadev->device_id;
+#endif
+ break;
+ case RPC_MESSAGING:
+ dev_dbg(shrm->dev, "RPC\n");
+ addr = (void *)wr_rpc_msg;
+#ifdef CONFIG_U8500_SHRM_LOOP_BACK
+ l2_header = COMMON_LOOPBACK_MESSAGING;
+#else
+ l2_header = isadev->device_id;
+#endif
+ break;
+ case AUDIO_MESSAGING:
+ dev_dbg(shrm->dev, "Audio\n");
+ addr = (void *)wr_audio_msg;
+#ifdef CONFIG_U8500_SHRM_LOOP_BACK
+ l2_header = AUDIO_LOOPBACK_MESSAGING;
+#else
+ l2_header = isadev->device_id;
+#endif
+
+ break;
+ case SECURITY_MESSAGING:
+ dev_dbg(shrm->dev, "Security\n");
+ addr = (void *)wr_sec_msg;
+#ifdef CONFIG_U8500_SHRM_LOOP_BACK
+ l2_header = COMMON_LOOPBACK_MESSAGING;
+#else
+ l2_header = isadev->device_id;
+#endif
+ break;
+ default:
+ dev_dbg(shrm->dev, "Wrong device\n");
+ return -EFAULT;
+ }
+
+ if (copy_from_user(addr, buf, len)) {
+ dev_err(shrm->dev, "copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ /* Write msg to Fifo */
+ if (isadev->device_id == 2) {
+ mutex_lock(&shrm->isa_context->tx_audio_mutex);
+ err = shm_write_msg(shrm, l2_header, addr, len);
+ if (!err)
+ ret = len;
+ else
+ ret = err;
+ mutex_unlock(&shrm->isa_context->tx_audio_mutex);
+ } else {
+ spin_lock_bh(&shrm->isa_context->common_tx);
+ err = shm_write_msg(shrm, l2_header, addr, len);
+ if (!err)
+ ret = len;
+ else
+ ret = err;
+ spin_unlock_bh(&shrm->isa_context->common_tx);
+ }
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return ret;
+}
+
+/**
+ * isa_ioctl() - To handle different ioctl commands supported by driver.
+ *
+ * @inode: structure is used by the kernel internally to represent files
+ * @filp:file descriptor pointer
+ * @cmd:ioctl command
+ * @arg:input param
+ *
+ * Following ioctls are supported by this driver.
+ * DLP_IOCTL_ALLOCATE_BUFFER - To allocate buffer for new uplink message.
+ * This ioctl is called with required message size. It returns offset for
+ * the allocates space in the queue. DLP_IOCTL_PUT_MESSAGE - To indicate
+ * new uplink message available in queuq for transmission. Message is copied
+ * from offset location returned by previous ioctl before calling this ioctl.
+ * DLP_IOCTL_GET_MESSAGE - To check if any downlink message is available in
+ * queue. It returns offset for new message inside queue.
+ * DLP_IOCTL_DEALLOCATE_BUFFER - To deallocate any buffer allocate for
+ * downlink message once the message is copied. Message is copied from offset
+ * location returned by previous ioctl before calling this ioctl.
+ */
+static int isa_ioctl(struct inode *inode, struct file *filp,
+ unsigned cmd, unsigned long arg)
+{
+ int err = 0;
+ struct isadev_context *isadev = filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+ u32 m = iminor(inode);
+
+ if (isadev->device_id != m)
+ return -1;
+
+ switch (cmd) {
+ case DLP_IOC_ALLOCATE_BUFFER:
+ dev_dbg(shrm->dev, "DLP_IOC_ALLOCATE_BUFFER\n");
+ break;
+ case DLP_IOC_PUT_MESSAGE:
+ dev_dbg(shrm->dev, "DLP_IOC_PUT_MESSAGE\n");
+ break;
+ case DLP_IOC_GET_MESSAGE:
+ dev_dbg(shrm->dev, "DLP_IOC_GET_MESSAGE\n");
+ break;
+ case DLP_IOC_DEALLOCATE_BUFFER:
+ dev_dbg(shrm->dev, "DLP_IOC_DEALLOCATE_BUFFER\n");
+ break;
+ default:
+ dev_dbg(shrm->dev, "Unknown IOCTL\n");
+ err = -1;
+ break;
+ }
+ return err;
+}
+/**
+ * isa_mmap() - Maps kernel queue memory to user space.
+ *
+ * @filp:file descriptor pointer
+ * @vma:virtual area memory structure.
+ *
+ * This function maps kernel FIFO into user space. This function
+ * shall be called twice to map both uplink and downlink buffers.
+ */
+static int isa_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct isadev_context *isadev = filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+
+ u32 m = iminor(filp->f_path.dentry->d_inode);
+ dev_dbg(shrm->dev, "%s %dIN\n", __func__, m);
+
+ isadev = (struct isadev_context *)filp->private_data;
+ return 0;
+}
+
+/**
+ * isa_close() - Close device file
+ *
+ * @inode:structure is used by the kernel internally to represent files
+ * @filp:device file descriptor
+ *
+ * This function deletes structues associated with this file, deletes
+ * queues, flushes and destroys workqueus and closes this file.
+ * It also unregisters itself from l2mux driver.
+ */
+static int isa_close(struct inode *inode, struct file *filp)
+{
+ struct isadev_context *isadev = filp->private_data;
+ struct shrm_dev *shrm = isadev->dl_queue.shrm;
+ struct isa_driver_context *isa_context = shrm->isa_context;
+ u8 m;
+
+ mutex_lock(&isa_lock);
+ m = iminor(filp->f_path.dentry->d_inode);
+ dev_dbg(shrm->dev, "%s IN %d", __func__, m);
+
+ if (atomic_dec_and_test(&isa_context->is_open[m])) {
+ atomic_inc(&isa_context->is_open[m]);
+ dev_err(shrm->dev, "Device not opened yet\n");
+ mutex_unlock(&isa_lock);
+ return -ENODEV;
+ }
+ atomic_set(&isa_context->is_open[m], 1);
+
+ dev_dbg(shrm->dev, "isadev->device_id %d", isadev->device_id);
+ dev_dbg(shrm->dev, "Closed %d device\n", m);
+
+ if (m == ISI_MESSAGING)
+ dev_dbg(shrm->dev, "Closed ISI_MESSAGING Device\n");
+ else if (m == RPC_MESSAGING)
+ dev_dbg(shrm->dev, "Closed RPC_MESSAGING Device\n");
+ else if (m == AUDIO_MESSAGING)
+ dev_dbg(shrm->dev, "Closed AUDIO_MESSAGING Device\n");
+ else if (m == SECURITY_MESSAGING)
+ dev_dbg(shrm->dev, "Closed SECURITY_MESSAGING Device\n");
+ else
+ dev_dbg(shrm->dev, NAME ":No such device present\n");
+
+ mutex_unlock(&isa_lock);
+ return 0;
+}
+/**
+ * isa_open() - Open device file
+ *
+ * @inode: structure is used by the kernel internally to represent files
+ * @filp: device file descriptor
+ *
+ * This function performs initialization tasks needed to open SHM channel.
+ * Following tasks are performed.
+ * -return if device is already opened
+ * -create uplink FIFO
+ * -create downlink FIFO
+ * -init delayed workqueue thread
+ * -register to l2mux driver
+ */
+static int isa_open(struct inode *inode, struct file *filp)
+{
+ int err = 0;
+ u8 m;
+ struct isadev_context *isadev;
+ struct isa_driver_context *isa_context = container_of(
+ inode->i_cdev,
+ struct isa_driver_context,
+ cdev);
+ struct shrm_dev *shrm = isa_context->isadev->dl_queue.shrm;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (get_boot_state() != BOOT_DONE) {
+ dev_err(shrm->dev, "Boot is not done\n");
+ return -EBUSY;
+ }
+ mutex_lock(&isa_lock);
+ m = iminor(inode);
+
+ if ((m != ISI_MESSAGING) && (m != RPC_MESSAGING) &&
+ (m != AUDIO_MESSAGING) && (m != SECURITY_MESSAGING)) {
+ dev_err(shrm->dev, "No such device present\n");
+ mutex_unlock(&isa_lock);
+ return -ENODEV;
+ }
+ if (!atomic_dec_and_test(&isa_context->is_open[m])) {
+ atomic_inc(&isa_context->is_open[m]);
+ dev_err(shrm->dev, "Device already opened\n");
+ mutex_unlock(&isa_lock);
+ return -EBUSY;
+ }
+
+ if (m == ISI_MESSAGING)
+ dev_dbg(shrm->dev, "Open ISI_MESSAGING Device\n");
+ else if (m == RPC_MESSAGING)
+ dev_dbg(shrm->dev, "Open RPC_MESSAGING Device\n");
+ else if (m == AUDIO_MESSAGING)
+ dev_dbg(shrm->dev, "Open AUDIO_MESSAGING Device\n");
+ else if (m == SECURITY_MESSAGING)
+ dev_dbg(shrm->dev, "Open SECURITY_MESSAGING Device\n");
+ else
+ dev_dbg(shrm->dev, ":No such device present\n");
+
+ isadev = &isa_context->isadev[m];
+ if (filp != NULL)
+ filp->private_data = isadev;
+
+ mutex_unlock(&isa_lock);
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return err;
+}
+
+const struct file_operations isa_fops = {
+ .owner = THIS_MODULE,
+ .open = isa_open,
+ .release = isa_close,
+ .ioctl = isa_ioctl,
+ .mmap = isa_mmap,
+ .read = isa_read,
+ .write = isa_write,
+ .poll = isa_select,
+};
+
+/**
+ * isa_init() - module insertion function
+ *
+ * This function registers module as a character driver using
+ * register_chrdev_region() or alloc_chrdev_region. It adds this
+ * driver to system using cdev_add() call. Major number is dynamically
+ * allocated using alloc_chrdev_region() by default or left to user to specify
+ * it during load time. For this variable major is used as module_param
+ * Nodes to be created using
+ * mknod /dev/isi c $major 0
+ * mknod /dev/rpc c $major 1
+ * mknod /dev/audio c $major 2
+ * mknod /dev/sec c $major 3
+ */
+int isa_init(struct shrm_dev *shrm)
+{
+ dev_t dev_id;
+ int retval, no_dev;
+ struct isadev_context *isadev;
+ struct isa_driver_context *isa_context;
+
+ isa_context = kzalloc(sizeof(struct isa_driver_context),
+ GFP_KERNEL);
+ shrm->isa_context = isa_context;
+ if (isa_context == NULL) {
+ dev_err(shrm->dev, "Failed to alloc memory\n");
+ return -ENOMEM;
+ }
+
+ if (major) {
+ dev_id = MKDEV(major, 0);
+ retval = register_chrdev_region(dev_id, ISA_DEVICES, NAME);
+ } else {
+ retval = alloc_chrdev_region(&dev_id, 0, ISA_DEVICES, NAME);
+ major = MAJOR(dev_id);
+ }
+
+ dev_dbg(shrm->dev, "major %d\n", major);
+
+ cdev_init(&isa_context->cdev, &isa_fops);
+ isa_context->cdev.owner = THIS_MODULE;
+ retval = cdev_add(&isa_context->cdev, dev_id, ISA_DEVICES);
+ if (retval) {
+ dev_err(shrm->dev, "Failed to add char device\n");
+ return retval;
+ }
+
+ for (no_dev = 0; no_dev < ISA_DEVICES; no_dev++)
+ atomic_set(&isa_context->is_open[no_dev], 1);
+
+ isa_context->isadev = kzalloc(sizeof
+ (struct isadev_context)*ISA_DEVICES,
+ GFP_KERNEL);
+ if (isa_context->isadev == NULL) {
+ dev_err(shrm->dev, "Failed to alloc memory\n");
+ return -ENOMEM;
+ }
+ for (no_dev = 0; no_dev < ISA_DEVICES; no_dev++) {
+ isadev = &isa_context->isadev[no_dev];
+ isadev->device_id = no_dev;
+ retval = create_queue(&isadev->dl_queue,
+ isadev->device_id, shrm);
+ if (retval < 0) {
+ dev_err(shrm->dev, "create dl_queue failed\n");
+ delete_queue(&isadev->dl_queue);
+ kfree(isadev);
+ return retval;
+ }
+ }
+ mutex_init(&isa_context->tx_audio_mutex);
+ spin_lock_init(&isa_context->common_tx);
+
+ dev_err(shrm->dev, "SHRM char driver added\n");
+
+ return retval;
+}
+
+void isa_exit(struct shrm_dev *shrm)
+{
+ int no_dev;
+ struct isadev_context *isadev;
+ struct isa_driver_context *isa_context = shrm->isa_context;
+ dev_t dev_id = MKDEV(major, 0);
+
+ for (no_dev = 0; no_dev < ISA_DEVICES; no_dev++) {
+ isadev = &isa_context->isadev[no_dev];
+ delete_queue(&isadev->dl_queue);
+ kfree(isadev);
+ }
+
+ cdev_del(&isa_context->cdev);
+ unregister_chrdev_region(dev_id, ISA_DEVICES);
+ kfree(isa_context);
+
+ dev_err(shrm->dev, "SHRM char driver removed\n");
+}
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+static enum hrtimer_restart callback(struct hrtimer *timer)
+{
+ return HRTIMER_NORESTART;
+}
+#endif
+
+
+static int __init shrm_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct resource *res;
+ struct shrm_dev *shrm = NULL;
+
+ if (pdev == NULL) {
+ dev_err(shrm->dev,
+ "No device/platform_data found on shm device\n");
+ return -ENODEV;
+ }
+
+
+ shrm = kzalloc(sizeof(struct shrm_dev), GFP_KERNEL);
+ if (shrm == NULL) {
+ dev_err(shrm->dev,
+ "Could not allocate memory for struct shm_dev\n");
+ return -ENOMEM;
+ }
+ shrm->dev = &pdev->dev;
+
+ /* initialise the SHM */
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(shrm->dev, "Unable to map Ca Wake up interrupt\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+ shrm->ca_wake_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res) {
+ dev_err(shrm->dev,
+ "Unable to map APE_Read_notif_common IRQ base\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+ shrm->ac_read_notif_0_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+ if (!res) {
+ dev_err(shrm->dev,
+ "Unable to map APE_Read_notif_audio IRQ base\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+ shrm->ac_read_notif_1_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 3);
+ if (!res) {
+ dev_err(shrm->dev,
+ "Unable to map Cmt_msg_pending_notif_common IRQ base\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+ shrm->ca_msg_pending_notif_0_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 4);
+ if (!res) {
+ dev_err(shrm->dev,
+ "Unable to map Cmt_msg_pending_notif_audio IRQ base\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+ shrm->ca_msg_pending_notif_1_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(shrm->dev,
+ "Could not get SHM IO memory information\n");
+ err = -ENODEV;
+ goto rollback_intr;
+ }
+
+ shrm->intr_base = (void __iomem *)ioremap_nocache(res->start,
+ res->end - res->start + 1);
+
+ if (!(shrm->intr_base)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_intr;
+ }
+
+ shrm->ape_common_fifo_base_phy =
+ (u32 *)U8500_SHM_FIFO_APE_COMMON_BASE;
+ shrm->ape_common_fifo_base =
+ (void __iomem *)ioremap_nocache(
+ U8500_SHM_FIFO_APE_COMMON_BASE,
+ SHM_FIFO_0_SIZE);
+ shrm->ape_common_fifo_size = (SHM_FIFO_0_SIZE)/4;
+
+ if (!(shrm->ape_common_fifo_base)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_ape_common_fifo_base;
+ }
+
+ shrm->cmt_common_fifo_base_phy =
+ (u32 *)U8500_SHM_FIFO_CMT_COMMON_BASE;
+
+ shrm->cmt_common_fifo_base =
+ (void __iomem *)ioremap_nocache(
+ U8500_SHM_FIFO_CMT_COMMON_BASE, SHM_FIFO_0_SIZE);
+ shrm->cmt_common_fifo_size = (SHM_FIFO_0_SIZE)/4;
+
+ if (!(shrm->cmt_common_fifo_base)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_cmt_common_fifo_base;
+ }
+
+ shrm->ape_audio_fifo_base_phy =
+ (u32 *)U8500_SHM_FIFO_APE_AUDIO_BASE;
+ shrm->ape_audio_fifo_base =
+ (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_APE_AUDIO_BASE,
+ SHM_FIFO_1_SIZE);
+ shrm->ape_audio_fifo_size = (SHM_FIFO_1_SIZE)/4;
+
+ if (!(shrm->ape_audio_fifo_base)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_ape_audio_fifo_base;
+ }
+
+ shrm->cmt_audio_fifo_base_phy =
+ (u32 *)U8500_SHM_FIFO_CMT_AUDIO_BASE;
+ shrm->cmt_audio_fifo_base =
+ (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_CMT_AUDIO_BASE,
+ SHM_FIFO_1_SIZE);
+ shrm->cmt_audio_fifo_size = (SHM_FIFO_1_SIZE)/4;
+
+ if (!(shrm->cmt_audio_fifo_base)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_cmt_audio_fifo_base;
+ }
+
+ shrm->ac_common_shared_wptr =
+ (void __iomem *)ioremap(SHM_ACFIFO_0_WRITE_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ac_common_shared_wptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_ac_common_shared_wptr;
+ }
+
+ shrm->ac_common_shared_rptr =
+ (void __iomem *)ioremap(SHM_ACFIFO_0_READ_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ac_common_shared_rptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+
+
+ shrm->ca_common_shared_wptr =
+ (void __iomem *)ioremap(SHM_CAFIFO_0_WRITE_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ca_common_shared_wptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+
+ shrm->ca_common_shared_rptr =
+ (void __iomem *)ioremap(SHM_CAFIFO_0_READ_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ca_common_shared_rptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+
+
+ shrm->ac_audio_shared_wptr =
+ (void __iomem *)ioremap(SHM_ACFIFO_1_WRITE_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ac_audio_shared_wptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+
+
+ shrm->ac_audio_shared_rptr =
+ (void __iomem *)ioremap(SHM_ACFIFO_1_READ_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ac_audio_shared_rptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+
+
+ shrm->ca_audio_shared_wptr =
+ (void __iomem *)ioremap(SHM_CAFIFO_1_WRITE_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ca_audio_shared_wptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+
+
+ shrm->ca_audio_shared_rptr =
+ (void __iomem *)ioremap(SHM_CAFIFO_1_READ_AMCU, SHM_PTR_SIZE);
+
+ if (!(shrm->ca_audio_shared_rptr)) {
+ dev_err(shrm->dev, "Unable to map register base\n");
+ err = -EBUSY;
+ goto rollback_map;
+ }
+
+
+ if (isa_init(shrm) != 0) {
+ dev_err(shrm->dev, "Driver Initialization Error\n");
+ err = -EBUSY;
+ }
+ /* install handlers and tasklets */
+ if (shm_initialise_irq(shrm)) {
+ dev_err(shrm->dev, "shm error in interrupt registration\n");
+ goto rollback_irq;
+ }
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+ hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ timer.function = callback;
+
+ hrtimer_start(&timer, ktime_set(0, 2*NSEC_PER_MSEC), HRTIMER_MODE_REL);
+#endif
+
+ return err;
+
+rollback_irq:
+ free_shm_irq(shrm);
+rollback_map:
+ iounmap(shrm->ac_common_shared_wptr);
+ iounmap(shrm->ac_common_shared_rptr);
+ iounmap(shrm->ca_common_shared_wptr);
+ iounmap(shrm->ca_common_shared_rptr);
+ iounmap(shrm->ac_audio_shared_wptr);
+ iounmap(shrm->ac_audio_shared_rptr);
+ iounmap(shrm->ca_audio_shared_wptr);
+ iounmap(shrm->ca_audio_shared_rptr);
+rollback_ac_common_shared_wptr:
+ iounmap(shrm->cmt_audio_fifo_base);
+rollback_cmt_audio_fifo_base:
+ iounmap(shrm->ape_audio_fifo_base);
+rollback_ape_audio_fifo_base:
+ iounmap(shrm->cmt_common_fifo_base);
+rollback_cmt_common_fifo_base:
+ iounmap(shrm->ape_common_fifo_base);
+rollback_ape_common_fifo_base:
+ iounmap(shrm->intr_base);
+rollback_intr:
+ kfree(shrm);
+ return err;
+}
+
+static int __exit shrm_remove(struct platform_device *pdev)
+{
+ struct shrm_dev *shrm = platform_get_drvdata(pdev);
+
+ free_shm_irq(shrm);
+ iounmap(shrm->intr_base);
+ iounmap(shrm->ape_common_fifo_base);
+ iounmap(shrm->cmt_common_fifo_base);
+ iounmap(shrm->ape_audio_fifo_base);
+ iounmap(shrm->cmt_audio_fifo_base);
+ iounmap(shrm->ac_common_shared_wptr);
+ iounmap(shrm->ac_common_shared_rptr);
+ iounmap(shrm->ca_common_shared_wptr);
+ iounmap(shrm->ca_common_shared_rptr);
+ iounmap(shrm->ac_audio_shared_wptr);
+ iounmap(shrm->ac_audio_shared_rptr);
+ iounmap(shrm->ca_audio_shared_wptr);
+ iounmap(shrm->ca_audio_shared_rptr);
+ kfree(shrm);
+ isa_exit(shrm);
+
+ return 0;
+}
+#ifdef CONFIG_PM
+
+/**
+ * u8500_shrm_suspend() - This routine puts the SHRM in to sustend state.
+ * @pdev: platform device.
+ *
+ * This routine checks the current ongoing communication with Modem by
+ * examining the ca_wake state and prevents suspend if modem communication
+ * is on-going.
+ * If ca_wake = 1 (high), modem comm. is on-going; don't suspend
+ * If ca_wake = 0 (low), no comm. with modem on-going.Allow suspend
+ */
+int u8500_shrm_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct shrm_dev *shrm = platform_get_drvdata(pdev);
+
+ dev_dbg(shrm->dev, "%s called...\n", __func__);
+ dev_dbg(shrm->dev, "\n ca_wake_req_state = %x\n",
+ get_ca_wake_req_state());
+ /* if ca_wake_req is high, prevent system suspend */
+ if (get_ca_wake_req_state())
+ return -EBUSY;
+ else
+ return 0;
+}
+
+/**
+ * u8500_shrm_resume() - This routine resumes the SHRM from sustend state.
+ * @pdev: platform device.
+ *
+ * This routine restore back the current state of the SHRM
+ */
+int u8500_shrm_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct shrm_dev *shrm = platform_get_drvdata(pdev);
+
+ dev_dbg(shrm->dev, "%s called...\n", __func__);
+ /* TODO:
+ * As of now, no state save takes place in suspend.
+ * So, nothing to restore in resume.
+ * Simply return as of now.
+ * State saved in suspend should be restored here.
+ */
+
+ return 0;
+}
+
+static const struct dev_pm_ops shrm_dev_pm_ops = {
+ .suspend = u8500_shrm_suspend,
+ .resume = u8500_shrm_resume,
+};
+#endif
+
+static struct platform_driver shrm_driver = {
+ .remove = __exit_p(shrm_remove),
+ .driver = {
+ .name = "u8500_shrm",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &shrm_dev_pm_ops,
+#endif
+ },
+};
+
+static int __init shrm_driver_init(void)
+{
+ return platform_driver_probe(&shrm_driver, shrm_probe);
+}
+
+static void __exit shrm_driver_exit(void)
+{
+ platform_driver_unregister(&shrm_driver);
+}
+
+module_init(shrm_driver_init);
+module_exit(shrm_driver_exit);
+
+MODULE_AUTHOR("Biju Das");
+MODULE_DESCRIPTION("Shared Memory Modem Driver Interface");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/shrm/shrm_fifo.c b/drivers/misc/shrm/shrm_fifo.c
new file mode 100644
index 00000000000..cbe0949a56d
--- /dev/null
+++ b/drivers/misc/shrm/shrm_fifo.c
@@ -0,0 +1,827 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Biju Das <biju.das@stericsson.com> for ST-Ericsson
+ * Author: Kumar Sanghavi <kumar.sanghvi@stericsson.com> for ST-Ericsson
+ * Author: Arun Murthy <arun.murthy@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <mach/shrm.h>
+#include <mach/shrm_driver.h>
+#include <mach/shrm_private.h>
+#include <mach/shrm_net.h>
+
+#define L1_BOOT_INFO_REQ 1
+#define L1_BOOT_INFO_RESP 2
+#define L1_NORMAL_MSG 3
+#define L1_HEADER_MASK 28
+#define L1_MAPID_MASK 0xF0000000
+#define CONFIG_OFFSET 8
+#define COUNTER_OFFSET 20
+#define L2_HEADER_SIZE 4
+#define L2_HEADER_OFFSET 24
+#define MASK_0_15_BIT 0xFF
+#define MASK_16_31_BIT 0xFF00
+#define MASK_16_27_BIT 0xFFF0000
+#define MASK_0_39_BIT 0xFFFFF
+#define MASK_40_55_BIT 0xFF00000
+#define MASK_8_16_BIT 0x0000FF00
+#define MSG_LEN_OFFSET 16
+#define SHRM_VER 2
+#define ca_ist_inactivity_timer 100 /*100ms */
+#define ca_csc_inactivity_timer 100 /*100ms */
+
+static u8 msg_audio_counter;
+static u8 msg_common_counter;
+
+struct fifo_write_params ape_shm_fifo_0;
+struct fifo_write_params ape_shm_fifo_1;
+struct fifo_read_params cmt_shm_fifo_0;
+struct fifo_read_params cmt_shm_fifo_1;
+
+
+static u8 cmt_read_notif_0_send;
+static u8 cmt_read_notif_1_send;
+
+void shm_fifo_init(struct shrm_dev *shrm)
+{
+ ape_shm_fifo_0.writer_local_wptr = 0;
+ ape_shm_fifo_0.writer_local_rptr = 0;
+ *((u32 *)shrm->ac_common_shared_wptr) = 0;
+ *((u32 *)shrm->ac_common_shared_rptr) = 0;
+ ape_shm_fifo_0.shared_wptr = 0;
+ ape_shm_fifo_0.shared_rptr = 0;
+ ape_shm_fifo_0.availablesize = shrm->ape_common_fifo_size;
+ ape_shm_fifo_0.end_addr_fifo = shrm->ape_common_fifo_size;
+ ape_shm_fifo_0.fifo_virtual_addr = shrm->ape_common_fifo_base;
+ spin_lock_init(&ape_shm_fifo_0.fifo_update_lock);
+
+
+ cmt_shm_fifo_0.reader_local_rptr = 0;
+ cmt_shm_fifo_0.reader_local_wptr = 0;
+ cmt_shm_fifo_0.shared_wptr =
+ *((u32 *)shrm->ca_common_shared_wptr);
+ cmt_shm_fifo_0.shared_rptr =
+ *((u32 *)shrm->ca_common_shared_rptr);
+ cmt_shm_fifo_0.availablesize = shrm->cmt_common_fifo_size;
+ cmt_shm_fifo_0.end_addr_fifo = shrm->cmt_common_fifo_size;
+ cmt_shm_fifo_0.fifo_virtual_addr = shrm->cmt_common_fifo_base;
+
+ ape_shm_fifo_1.writer_local_wptr = 0;
+ ape_shm_fifo_1.writer_local_rptr = 0;
+ ape_shm_fifo_1.shared_wptr = 0;
+ ape_shm_fifo_1.shared_rptr = 0;
+ *((u32 *)shrm->ac_audio_shared_wptr) = 0;
+ *((u32 *)shrm->ac_audio_shared_rptr) = 0;
+ ape_shm_fifo_1.availablesize = shrm->ape_audio_fifo_size;
+ ape_shm_fifo_1.end_addr_fifo = shrm->ape_audio_fifo_size;
+ ape_shm_fifo_1.fifo_virtual_addr = shrm->ape_audio_fifo_base;
+ spin_lock_init(&ape_shm_fifo_1.fifo_update_lock);
+
+ cmt_shm_fifo_1.reader_local_rptr = 0;
+ cmt_shm_fifo_1.reader_local_wptr = 0;
+ cmt_shm_fifo_1.shared_wptr =
+ *((u32 *)shrm->ca_audio_shared_wptr);
+ cmt_shm_fifo_1.shared_rptr =
+ *((u32 *)shrm->ca_audio_shared_rptr);
+ cmt_shm_fifo_1.availablesize = shrm->cmt_audio_fifo_size;
+ cmt_shm_fifo_1.end_addr_fifo = shrm->cmt_audio_fifo_size;
+ cmt_shm_fifo_1.fifo_virtual_addr = shrm->cmt_audio_fifo_base;
+ msg_audio_counter = 0;
+ msg_common_counter = 0;
+}
+
+u8 read_boot_info_req(struct shrm_dev *shrm,
+ u32 *config,
+ u32 *version)
+{
+ struct fifo_read_params *fifo = &cmt_shm_fifo_0;
+ u32 *msg;
+ u32 header = 0;
+ u8 msgtype;
+
+ /* Read L1 header read content of reader_local_rptr */
+ msg = (u32 *)
+ (fifo->reader_local_rptr + fifo->fifo_virtual_addr);
+ header = *msg;
+ msgtype = (header & L1_MAPID_MASK) >> L1_MSG_MAPID_OFFSET;
+ if (msgtype != L1_BOOT_INFO_REQ) {
+ dev_err(shrm->dev, "Read_Boot_Info_Req Fatal ERROR\n");
+ BUG();
+ }
+ *config = (header >> CONFIG_OFFSET) & MASK_0_15_BIT;
+ *version = header & MASK_0_15_BIT;
+ fifo->reader_local_rptr += 1;
+
+ return 1;
+}
+
+void write_boot_info_resp(struct shrm_dev *shrm, u32 config,
+ u32 version)
+{
+ struct fifo_write_params *fifo = &ape_shm_fifo_0;
+ u32 *msg;
+ u8 msg_length;
+ version = SHRM_VER;
+
+ spin_lock_bh(&fifo->fifo_update_lock);
+ /* Read L1 header read content of reader_local_rptr */
+ msg = (u32 *)
+ (fifo->writer_local_wptr+fifo->fifo_virtual_addr);
+ if (version < 1) {
+ *msg = ((L1_BOOT_INFO_RESP << L1_MSG_MAPID_OFFSET) |
+ ((config << CONFIG_OFFSET) & MASK_16_31_BIT)
+ | (version & MASK_0_15_BIT));
+ msg_length = 1;
+ } else {
+ *msg = ((L1_BOOT_INFO_RESP << L1_MSG_MAPID_OFFSET) |
+ ((0x8 << MSG_LEN_OFFSET) & MASK_16_27_BIT) |
+ ((config << CONFIG_OFFSET) & MASK_8_16_BIT)|
+ version);
+ msg++;
+ *msg = ca_ist_inactivity_timer;
+ msg++;
+ *msg = ca_csc_inactivity_timer;
+ msg_length = L1_NORMAL_MSG;
+ }
+ fifo->writer_local_wptr += msg_length;
+ fifo->availablesize -= msg_length;
+ spin_unlock_bh(&fifo->fifo_update_lock);
+}
+
+/**
+ * shm_write_msg_to_fifo() - write message to FIFO
+ * @shrm: pointer to shrm device information structure
+ * @channel: audio or common channel
+ * @l2header: L2 header or device ID
+ * @addr: pointer to write buffer address
+ * @length: length of mst to write
+ *
+ * Function Which Writes the data into Fifo in IPC zone
+ * It is called from shm_write_msg. This function will copy the msg
+ * from the kernel buffer to FIFO. There are 4 kernel buffers from where
+ * the data is to copied to FIFO one for each of the messages ISI, RPC,
+ * AUDIO and SECURITY. ISI, RPC and SECURITY messages are pushed to FIFO
+ * in commmon channel and AUDIO message is pushed onto audio channel FIFO.
+ */
+int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel,
+ u8 l2header, void *addr, u32 length)
+{
+ struct fifo_write_params *fifo = NULL;
+ u32 l1_header = 0, l2_header = 0;
+ u32 requiredsize;
+ u32 size = 0;
+ u32 *msg;
+ u8 *src;
+
+ if (channel == COMMON_CHANNEL)
+ fifo = &ape_shm_fifo_0;
+ else if (channel == AUDIO_CHANNEL)
+ fifo = &ape_shm_fifo_1;
+ else {
+ dev_err(shrm->dev, "invalid channel\n");
+ return -EINVAL;
+ }
+
+ /* L2 size in 32b */
+ requiredsize = ((length + 3) / 4);
+ /* Add size of L1 & L2 header */
+ requiredsize += 2;
+
+ /* if availablesize = or < requiredsize then error */
+ if (fifo->availablesize <= requiredsize) {
+ /* Fatal ERROR - should never happens */
+ dev_dbg(shrm->dev, "wr_wptr= %x\n",
+ fifo->writer_local_wptr);
+ dev_dbg(shrm->dev, "wr_rptr= %x\n",
+ fifo->writer_local_rptr);
+ dev_dbg(shrm->dev, "shared_wptr= %x\n",
+ fifo->shared_wptr);
+ dev_dbg(shrm->dev, "shared_rptr= %x\n",
+ fifo->shared_rptr);
+ dev_dbg(shrm->dev, "availsize= %x\n",
+ fifo->availablesize);
+ dev_dbg(shrm->dev, "end__fifo= %x\n",
+ fifo->end_addr_fifo);
+ dev_warn(shrm->dev, "Modem is busy, please wait."
+ " c_cnt = %d; a_cnt = %d\n", msg_common_counter,
+ msg_audio_counter);
+ if (channel == COMMON_CHANNEL) {
+ dev_warn(shrm->dev,
+ "Modem is lagging behind in reading."
+ "Stopping n/w dev queue\n");
+ shrm_stop_netdev(shrm->ndev);
+ }
+
+ return -EAGAIN;
+ }
+
+ if (channel == COMMON_CHANNEL) {
+ /* build L1 header */
+ l1_header = ((L1_NORMAL_MSG << L1_MSG_MAPID_OFFSET) |
+ (((msg_common_counter++) << COUNTER_OFFSET)
+ & MASK_40_55_BIT) |
+ ((length + L2_HEADER_SIZE) & MASK_0_39_BIT));
+ } else if (channel == AUDIO_CHANNEL) {
+ /* build L1 header */
+ l1_header = ((L1_NORMAL_MSG << L1_MSG_MAPID_OFFSET) |
+ (((msg_audio_counter++) << COUNTER_OFFSET)
+ & MASK_40_55_BIT) |
+ ((length + L2_HEADER_SIZE) & MASK_0_39_BIT));
+ }
+
+ /*
+ * Need to take care race condition for fifo->availablesize
+ * & fifo->writer_local_rptr with Ac_Read_notification interrupt.
+ * One option could be use stack variable for LocalRptr and recompute
+ * fifo->availablesize,based on flag enabled in the
+ * Ac_read_notification
+ */
+ l2_header = ((l2header << L2_HEADER_OFFSET) |
+ ((length) & MASK_0_39_BIT));
+ spin_lock_bh(&fifo->fifo_update_lock);
+ /* Check Local Rptr is less than or equal to Local WPtr */
+ if (fifo->writer_local_rptr <= fifo->writer_local_wptr) {
+ msg = (u32 *)
+ (fifo->fifo_virtual_addr+fifo->writer_local_wptr);
+
+ /* check enough place bewteen writer_local_wptr & end of FIFO */
+ if ((fifo->end_addr_fifo-fifo->writer_local_wptr) >=
+ requiredsize) {
+ /* Add L1 header and L2 header */
+ *msg = l1_header;
+ msg++;
+ *msg = l2_header;
+ msg++;
+
+ /* copy the l2 message in 1 memcpy */
+ memcpy((void *)msg, addr, length);
+ /* UpdateWptr */
+ fifo->writer_local_wptr += requiredsize;
+ fifo->availablesize -= requiredsize;
+ fifo->writer_local_wptr %= fifo->end_addr_fifo;
+ } else {
+ /*
+ * message is split between and of FIFO and beg of FIFO
+ * copy first part from writer_local_wptr to end of FIFO
+ */
+ size = fifo->end_addr_fifo-fifo->writer_local_wptr;
+
+ if (size == 1) {
+ /* Add L1 header */
+ *msg = l1_header;
+ msg++;
+ /* UpdateWptr */
+ fifo->writer_local_wptr = 0;
+ fifo->availablesize -= size;
+ /*
+ * copy second part from beg of FIFO
+ * with remaining part of msg
+ */
+ msg = (u32 *)
+ fifo->fifo_virtual_addr;
+ *msg = l2_header;
+ msg++;
+
+ /* copy the l3 message in 1 memcpy */
+ memcpy((void *)msg, addr, length);
+ /* UpdateWptr */
+ fifo->writer_local_wptr +=
+ requiredsize-size;
+ fifo->availablesize -=
+ (requiredsize-size);
+ } else if (size == 2) {
+ /* Add L1 header and L2 header */
+ *msg = l1_header;
+ msg++;
+ *msg = l2_header;
+ msg++;
+
+ /* UpdateWptr */
+ fifo->writer_local_wptr = 0;
+ fifo->availablesize -= size;
+
+ /*
+ * copy second part from beg of FIFO
+ * with remaining part of msg
+ */
+ msg = (u32 *)
+ fifo->fifo_virtual_addr;
+ /* copy the l3 message in 1 memcpy */
+ memcpy((void *)msg, addr, length);
+
+ /* UpdateWptr */
+ fifo->writer_local_wptr +=
+ requiredsize-size;
+ fifo->availablesize -=
+ (requiredsize-size);
+ } else {
+ /* Add L1 header and L2 header */
+ *msg = l1_header;
+ msg++;
+ *msg = l2_header;
+ msg++;
+
+ /* copy the l2 message in 1 memcpy */
+ memcpy((void *)msg, addr, (size-2)*4);
+
+
+ /* UpdateWptr */
+ fifo->writer_local_wptr = 0;
+ fifo->availablesize -= size;
+
+ /*
+ * copy second part from beg of FIFO
+ * with remaining part of msg
+ */
+ msg = (u32 *)fifo->fifo_virtual_addr;
+ src = (u8 *)addr+((size - 2) * 4);
+ memcpy((void *)msg, src,
+ (length-((size - 2) * 4)));
+
+ /* UpdateWptr */
+ fifo->writer_local_wptr +=
+ requiredsize-size;
+ fifo->availablesize -=
+ (requiredsize-size);
+ }
+
+ }
+ } else {
+ /* writer_local_rptr > writer_local_wptr */
+ msg = (u32 *)
+ (fifo->fifo_virtual_addr+fifo->writer_local_wptr);
+ /* Add L1 header and L2 header */
+ *msg = l1_header;
+ msg++;
+ *msg = l2_header;
+ msg++;
+ /*
+ * copy message possbile between writer_local_wptr up
+ * to writer_local_rptr copy the l3 message in 1 memcpy
+ */
+ memcpy((void *)msg, addr, length);
+
+ /* UpdateWptr */
+ fifo->writer_local_wptr += requiredsize;
+ fifo->availablesize -= requiredsize;
+
+ }
+ spin_unlock_bh(&fifo->fifo_update_lock);
+ return length;
+}
+
+/**
+ * read_one_l2msg_common() - read message from common channel
+ * @shrm: pointer to shrm device information structure
+ * @l2_msg: pointer to the read L2 message buffer
+ * @len: message length
+ *
+ * This function read one message from the FIFO and returns l2 header type
+ */
+u8 read_one_l2msg_common(struct shrm_dev *shrm,
+ u8 *l2_msg, u32 *len)
+{
+ struct fifo_read_params *fifo = &cmt_shm_fifo_0;
+
+ u32 *msg;
+ u32 l1_header = 0;
+ u32 l2_header = 0;
+ u32 length;
+ u8 msgtype;
+ u32 msg_size;
+ u32 size = 0;
+
+ /* Read L1 header read content of reader_local_rptr */
+ msg = (u32 *)
+ (fifo->reader_local_rptr+fifo->fifo_virtual_addr);
+ l1_header = *msg++;
+ msgtype = (l1_header & 0xF0000000) >> L1_HEADER_MASK;
+
+ if (msgtype != L1_NORMAL_MSG) {
+ /* Fatal ERROR - should never happens */
+ dev_dbg(shrm->dev, "wr_wptr= %x\n",
+ fifo->reader_local_wptr);
+ dev_dbg(shrm->dev, "wr_rptr= %x\n",
+ fifo->reader_local_rptr);
+ dev_dbg(shrm->dev, "shared_wptr= %x\n",
+ fifo->shared_wptr);
+ dev_dbg(shrm->dev, "shared_rptr= %x\n",
+ fifo->shared_rptr);
+ dev_dbg(shrm->dev, "availsize= %x\n",
+ fifo->availablesize);
+ dev_dbg(shrm->dev, "end_fifo= %x\n",
+ fifo->end_addr_fifo);
+ /* Fatal ERROR - should never happens */
+ dev_crit(shrm->dev, "Fatal ERROR - should never happen\n");
+ BUG();
+ }
+ if (fifo->reader_local_rptr == (fifo->end_addr_fifo-1)) {
+ l2_header = (*((u32 *)fifo->fifo_virtual_addr));
+ length = l2_header & MASK_0_39_BIT;
+ } else {
+ /* Read L2 header,Msg size & content of reader_local_rptr */
+ l2_header = *msg;
+ length = l2_header & MASK_0_39_BIT;
+ }
+
+ *len = length;
+ msg_size = ((length + 3) / 4);
+ msg_size += 2;
+
+ if (fifo->reader_local_rptr + msg_size <=
+ fifo->end_addr_fifo) {
+ /* Skip L2 header */
+ msg++;
+
+ /* read msg between reader_local_rptr and end of FIFO */
+ memcpy((void *)l2_msg, (void *)msg, length);
+ /* UpdateLocalRptr */
+ fifo->reader_local_rptr += msg_size;
+ fifo->reader_local_rptr %= fifo->end_addr_fifo;
+ } else {
+ /*
+ * msg split between end of FIFO and beg copy first
+ * part of msg read msg between reader_local_rptr
+ * and end of FIFO
+ */
+ size = fifo->end_addr_fifo-fifo->reader_local_rptr;
+ if (size == 1) {
+ msg = (u32 *)(fifo->fifo_virtual_addr);
+ /* Skip L2 header */
+ msg++;
+ memcpy((void *)l2_msg, (void *)(msg), length);
+ } else if (size == 2) {
+ /* Skip L2 header */
+ msg++;
+ msg = (u32 *)(fifo->fifo_virtual_addr);
+ memcpy((void *)l2_msg,
+ (void *)(msg), length);
+ } else {
+ /* Skip L2 header */
+ msg++;
+ memcpy((void *)l2_msg, (void *)msg, ((size - 2) * 4));
+ /* copy second part of msg */
+ l2_msg += ((size - 2) * 4);
+ msg = (u32 *)(fifo->fifo_virtual_addr);
+ memcpy((void *)l2_msg, (void *)(msg),
+ (length-((size - 2) * 4)));
+ }
+ fifo->reader_local_rptr =
+ (fifo->reader_local_rptr+msg_size) %
+ fifo->end_addr_fifo;
+ }
+ return (l2_header>>L2_HEADER_OFFSET) & MASK_0_15_BIT;
+ }
+
+u8 read_remaining_messages_common()
+{
+ struct fifo_read_params *fifo = &cmt_shm_fifo_0;
+ /*
+ * There won't be any Race condition reader_local_rptr &
+ * fifo->reader_local_wptr with CaMsgpending Notification Interrupt
+ */
+ return ((fifo->reader_local_rptr != fifo->reader_local_wptr) ? 1 : 0);
+}
+
+u8 read_one_l2msg_audio(struct shrm_dev *shrm,
+ u8 *l2_msg, u32 *len)
+{
+ struct fifo_read_params *fifo = &cmt_shm_fifo_1;
+
+ u32 *msg;
+ u32 l1_header = 0;
+ u32 l2_header = 0;
+ u32 length;
+ u8 msgtype;
+ u32 msg_size;
+ u32 size = 0;
+
+ /* Read L1 header read content of reader_local_rptr */
+ msg = (u32 *)
+ (fifo->reader_local_rptr+fifo->fifo_virtual_addr);
+ l1_header = *msg++;
+ msgtype = (l1_header & 0xF0000000) >> L1_HEADER_MASK;
+
+ if (msgtype != L1_NORMAL_MSG) {
+ /* Fatal ERROR - should never happens */
+ dev_dbg(shrm->dev, "wr_local_wptr= %x\n",
+ fifo->reader_local_wptr);
+ dev_dbg(shrm->dev, "wr_local_rptr= %x\n",
+ fifo->reader_local_rptr);
+ dev_dbg(shrm->dev, "shared_wptr= %x\n",
+ fifo->shared_wptr);
+ dev_dbg(shrm->dev, "shared_rptr= %x\n",
+ fifo->shared_rptr);
+ dev_dbg(shrm->dev, "availsize=%x\n",
+ fifo->availablesize);
+ dev_dbg(shrm->dev, "end_fifo= %x\n",
+ fifo->end_addr_fifo);
+ /* Fatal ERROR - should never happens */
+ dev_crit(shrm->dev, "Fatal ERROR - should never happen\n");
+ BUG();
+ }
+ if (fifo->reader_local_rptr == (fifo->end_addr_fifo-1)) {
+ l2_header = (*((u32 *)fifo->fifo_virtual_addr));
+ length = l2_header & MASK_0_39_BIT;
+ } else {
+ /* Read L2 header,Msg size & content of reader_local_rptr */
+ l2_header = *msg;
+ length = l2_header & MASK_0_39_BIT;
+ }
+
+ *len = length;
+ msg_size = ((length + 3) / 4);
+ msg_size += 2;
+
+ if (fifo->reader_local_rptr + msg_size <=
+ fifo->end_addr_fifo) {
+ /* Skip L2 header */
+ msg++;
+ /* read msg between reader_local_rptr and end of FIFO */
+ memcpy((void *)l2_msg, (void *)msg, length);
+ /* UpdateLocalRptr */
+ fifo->reader_local_rptr += msg_size;
+ fifo->reader_local_rptr %= fifo->end_addr_fifo;
+ } else {
+
+ /*
+ * msg split between end of FIFO and beg
+ * copy first part of msg
+ * read msg between reader_local_rptr and end of FIFO
+ */
+ size = fifo->end_addr_fifo-fifo->reader_local_rptr;
+ if (size == 1) {
+ msg = (u32 *)(fifo->fifo_virtual_addr);
+ /* Skip L2 header */
+ msg++;
+ memcpy((void *)l2_msg, (void *)(msg), length);
+ } else if (size == 2) {
+ /* Skip L2 header */
+ msg++;
+ msg = (u32 *)(fifo->fifo_virtual_addr);
+ memcpy((void *)l2_msg, (void *)(msg), length);
+ } else {
+ /* Skip L2 header */
+ msg++;
+ memcpy((void *)l2_msg, (void *)msg, ((size - 2) * 4));
+ /* copy second part of msg */
+ l2_msg += ((size - 2) * 4);
+ msg = (u32 *)(fifo->fifo_virtual_addr);
+ memcpy((void *)l2_msg, (void *)(msg),
+ (length-((size - 2) * 4)));
+ }
+ fifo->reader_local_rptr =
+ (fifo->reader_local_rptr+msg_size) %
+ fifo->end_addr_fifo;
+
+ }
+ return (l2_header>>L2_HEADER_OFFSET) & MASK_0_15_BIT;
+ }
+
+u8 read_remaining_messages_audio()
+{
+ struct fifo_read_params *fifo = &cmt_shm_fifo_1;
+
+ return ((fifo->reader_local_rptr != fifo->reader_local_wptr) ?
+ 1 : 0);
+}
+
+u8 is_the_only_one_unread_message(struct shrm_dev *shrm,
+ u8 channel, u32 length)
+{
+ struct fifo_write_params *fifo = NULL;
+ u32 messagesize = 0;
+ u8 is_only_one_unread_msg = 0;
+
+ if (channel == COMMON_CHANNEL)
+ fifo = &ape_shm_fifo_0;
+ else /* channel = AUDIO_CHANNEL */
+ fifo = &ape_shm_fifo_1;
+
+ /* L3 size in 32b */
+ messagesize = ((length + 3) / 4);
+ /* Add size of L1 & L2 header */
+ messagesize += 2;
+ /*
+ * possibility of race condition with Ac Read notification interrupt.
+ * need to check ?
+ */
+ if (fifo->writer_local_wptr > fifo->writer_local_rptr)
+ is_only_one_unread_msg =
+ ((fifo->writer_local_rptr + messagesize) ==
+ fifo->writer_local_wptr) ? 1 : 0;
+ else
+ /* Msg split between end of fifo and starting of Fifo */
+ is_only_one_unread_msg =
+ (((fifo->writer_local_rptr + messagesize) %
+ fifo->end_addr_fifo) == fifo->writer_local_wptr) ?
+ 1 : 0;
+
+ return is_only_one_unread_msg;
+}
+
+void update_ca_common_local_wptr(struct shrm_dev *shrm)
+{
+ /*
+ * update CA common reader local write pointer with the
+ * shared write pointer
+ */
+ struct fifo_read_params *fifo = &cmt_shm_fifo_0;
+
+ fifo->shared_wptr =
+ (*((u32 *)shrm->ca_common_shared_wptr));
+ fifo->reader_local_wptr = fifo->shared_wptr;
+}
+
+void update_ca_audio_local_wptr(struct shrm_dev *shrm)
+{
+ /*
+ * update CA audio reader local write pointer with the
+ * shared write pointer
+ */
+ struct fifo_read_params *fifo = &cmt_shm_fifo_1;
+
+ fifo->shared_wptr =
+ (*((u32 *)shrm->ca_audio_shared_wptr));
+ fifo->reader_local_wptr = fifo->shared_wptr;
+}
+
+void update_ac_common_local_rptr(struct shrm_dev *shrm)
+{
+ /*
+ * update AC common writer local read pointer with the
+ * shared read pointer
+ */
+ struct fifo_write_params *fifo;
+ u32 free_space = 0;
+
+ fifo = &ape_shm_fifo_0;
+
+ spin_lock_bh(&fifo->fifo_update_lock);
+ fifo->shared_rptr =
+ (*((u32 *)shrm->ac_common_shared_rptr));
+
+ if (fifo->shared_rptr >= fifo->writer_local_rptr)
+ free_space =
+ (fifo->shared_rptr-fifo->writer_local_rptr);
+ else {
+ free_space =
+ (fifo->end_addr_fifo-fifo->writer_local_rptr);
+ free_space += fifo->shared_rptr;
+ }
+
+ /* Chance of race condition of below variables with write_msg */
+ fifo->availablesize += free_space;
+ fifo->writer_local_rptr = fifo->shared_rptr;
+ spin_unlock_bh(&fifo->fifo_update_lock);
+}
+
+void update_ac_audio_local_rptr(struct shrm_dev *shrm)
+{
+ /*
+ * update AC audio writer local read pointer with the
+ * shared read pointer
+ */
+ struct fifo_write_params *fifo;
+ u32 free_space = 0;
+
+ fifo = &ape_shm_fifo_1;
+ spin_lock_bh(&fifo->fifo_update_lock);
+ fifo->shared_rptr =
+ (*((u32 *)shrm->ac_audio_shared_rptr));
+
+ if (fifo->shared_rptr >= fifo->writer_local_rptr)
+ free_space =
+ (fifo->shared_rptr-fifo->writer_local_rptr);
+ else {
+ free_space =
+ (fifo->end_addr_fifo-fifo->writer_local_rptr);
+ free_space += fifo->shared_rptr;
+ }
+
+ /* Chance of race condition of below variables with write_msg */
+ fifo->availablesize += free_space;
+ fifo->writer_local_rptr = fifo->shared_rptr;
+ spin_unlock_bh(&fifo->fifo_update_lock);
+}
+
+void update_ac_common_shared_wptr(struct shrm_dev *shrm)
+{
+ /*
+ * update AC common shared write pointer with the
+ * local write pointer
+ */
+ struct fifo_write_params *fifo;
+
+ fifo = &ape_shm_fifo_0;
+ spin_lock_bh(&fifo->fifo_update_lock);
+ /* Update shared pointer fifo offset of the IPC zone */
+ (*((u32 *)shrm->ac_common_shared_wptr)) =
+ fifo->writer_local_wptr;
+
+ fifo->shared_wptr = fifo->writer_local_wptr;
+ spin_unlock_bh(&fifo->fifo_update_lock);
+}
+
+void update_ac_audio_shared_wptr(struct shrm_dev *shrm)
+{
+ /*
+ * update AC audio shared write pointer with the
+ * local write pointer
+ */
+ struct fifo_write_params *fifo;
+
+ fifo = &ape_shm_fifo_1;
+ spin_lock_bh(&fifo->fifo_update_lock);
+ /* Update shared pointer fifo offset of the IPC zone */
+ (*((u32 *)shrm->ac_audio_shared_wptr)) =
+ fifo->writer_local_wptr;
+ fifo->shared_wptr = fifo->writer_local_wptr;
+ spin_unlock_bh(&fifo->fifo_update_lock);
+}
+
+void update_ca_common_shared_rptr(struct shrm_dev *shrm)
+{
+ /*
+ * update CA common shared read pointer with the
+ * local read pointer
+ */
+ struct fifo_read_params *fifo;
+
+ fifo = &cmt_shm_fifo_0;
+
+ /* Update shared pointer fifo offset of the IPC zone */
+ (*((u32 *)shrm->ca_common_shared_rptr)) =
+ fifo->reader_local_rptr;
+ fifo->shared_rptr = fifo->reader_local_rptr;
+}
+
+void update_ca_audio_shared_rptr(struct shrm_dev *shrm)
+{
+ /*
+ * update CA audio shared read pointer with the
+ * local read pointer
+ */
+ struct fifo_read_params *fifo;
+
+ fifo = &cmt_shm_fifo_1;
+
+ /* Update shared pointer fifo offset of the IPC zone */
+ (*((u32 *)shrm->ca_audio_shared_rptr)) =
+ fifo->reader_local_rptr;
+ fifo->shared_rptr = fifo->reader_local_rptr;
+}
+
+void get_reader_pointers(u8 channel_type, u32 *reader_local_rptr,
+ u32 *reader_local_wptr, u32 *shared_rptr)
+{
+ struct fifo_read_params *fifo = NULL;
+
+ if (channel_type == COMMON_CHANNEL)
+ fifo = &cmt_shm_fifo_0;
+ else /* channel_type = AUDIO_CHANNEL */
+ fifo = &cmt_shm_fifo_1;
+
+ *reader_local_rptr = fifo->reader_local_rptr;
+ *reader_local_wptr = fifo->reader_local_wptr;
+ *shared_rptr = fifo->shared_rptr;
+}
+
+void get_writer_pointers(u8 channel_type, u32 *writer_local_rptr,
+ u32 *writer_local_wptr, u32 *shared_wptr)
+{
+ struct fifo_write_params *fifo = NULL;
+
+ if (channel_type == COMMON_CHANNEL)
+ fifo = &ape_shm_fifo_0;
+ else /* channel_type = AUDIO_CHANNEL */
+ fifo = &ape_shm_fifo_1;
+
+ spin_lock_bh(&fifo->fifo_update_lock);
+ *writer_local_rptr = fifo->writer_local_rptr;
+ *writer_local_wptr = fifo->writer_local_wptr;
+ *shared_wptr = fifo->shared_wptr;
+ spin_unlock_bh(&fifo->fifo_update_lock);
+}
+
+void set_ca_msg_0_read_notif_send(u8 val)
+{
+ cmt_read_notif_0_send = val;
+}
+
+u8 get_ca_msg_0_read_notif_send(void)
+{
+ return cmt_read_notif_0_send;
+}
+
+void set_ca_msg_1_read_notif_send(u8 val)
+{
+ cmt_read_notif_1_send = val;
+}
+
+u8 get_ca_msg_1_read_notif_send(void)
+{
+ return cmt_read_notif_1_send;
+}
diff --git a/drivers/misc/shrm/shrm_protocol.c b/drivers/misc/shrm/shrm_protocol.c
new file mode 100644
index 00000000000..0598c62812f
--- /dev/null
+++ b/drivers/misc/shrm/shrm_protocol.c
@@ -0,0 +1,1194 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Biju Das <biju.das@stericsson.com> for ST-Ericsson
+ * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com> for ST-Ericsson
+ * Author: Arun Murthy <arun.murthy@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/netlink.h>
+#include <linux/workqueue.h>
+
+#include <mach/shrm.h>
+#include <mach/shrm_driver.h>
+#include <mach/shrm_private.h>
+#include <mach/shrm_net.h>
+#include <mach/prcmu-fw-api.h>
+#include <mach/prcmu-regs.h>
+#include <mach/suspend.h>
+#include <mach/reboot_reasons.h>
+
+#define L2_HEADER_ISI 0x0
+#define L2_HEADER_RPC 0x1
+#define L2_HEADER_AUDIO 0x2
+#define L2_HEADER_SECURITY 0x3
+#define L2_HEADER_COMMON_SIMPLE_LOOPBACK 0xC0
+#define L2_HEADER_COMMON_ADVANCED_LOOPBACK 0xC1
+#define L2_HEADER_AUDIO_SIMPLE_LOOPBACK 0x80
+#define L2_HEADER_AUDIO_ADVANCED_LOOPBACK 0x81
+#define MAX_PAYLOAD 1024
+
+static u8 boot_state = BOOT_INIT;
+static u8 recieve_common_msg[8*1024];
+static u8 recieve_audio_msg[8*1024];
+static received_msg_handler rx_common_handler;
+static received_msg_handler rx_audio_handler;
+static struct hrtimer timer;
+static char is_earlydrop;
+struct sock *shrm_nl_sk;
+
+static char shrm_common_tx_state = SHRM_SLEEP_STATE;
+static char shrm_common_rx_state = SHRM_SLEEP_STATE;
+static char shrm_audio_tx_state = SHRM_SLEEP_STATE;
+static char shrm_audio_rx_state = SHRM_SLEEP_STATE;
+
+static atomic_t ac_sleep_disable_count = ATOMIC_INIT(0);
+static struct shrm_dev *shm_dev;
+
+/* Spin lock and tasklet declaration */
+DECLARE_TASKLET(shm_ca_0_tasklet, shm_ca_msgpending_0_tasklet, 0);
+DECLARE_TASKLET(shm_ca_1_tasklet, shm_ca_msgpending_1_tasklet, 0);
+DECLARE_TASKLET(shm_ac_read_0_tasklet, shm_ac_read_notif_0_tasklet, 0);
+DECLARE_TASKLET(shm_ac_read_1_tasklet, shm_ac_read_notif_1_tasklet, 0);
+
+static DEFINE_MUTEX(ac_state_mutex);
+
+static DEFINE_SPINLOCK(ca_common_lock);
+static DEFINE_SPINLOCK(ca_audio_lock);
+static DEFINE_SPINLOCK(ca_wake_req_lock);
+static DEFINE_SPINLOCK(boot_lock);
+
+enum shrm_nl {
+ SHRM_NL_MOD_RESET = 1,
+ SHRM_NL_MOD_QUERY_STATE,
+ SHRM_NL_USER_MOD_RESET,
+ SHRM_NL_STATUS_MOD_ONLINE,
+ SHRM_NL_STATUS_MOD_OFFLINE,
+};
+
+static void shm_ac_sleep_req_work(struct work_struct *work)
+{
+ mutex_lock(&ac_state_mutex);
+ if (atomic_read(&ac_sleep_disable_count) == 0)
+ prcmu_ac_sleep_req();
+ mutex_unlock(&ac_state_mutex);
+}
+
+static void shm_ac_wake_req_work(struct work_struct *work)
+{
+ mutex_lock(&ac_state_mutex);
+ prcmu_ac_wake_req();
+ mutex_unlock(&ac_state_mutex);
+}
+
+static u32 get_host_accessport_val(void)
+{
+ u32 prcm_hostaccess;
+
+ prcm_hostaccess = readl(PRCM_HOSTACCESS_REQ);
+ wmb();
+ prcm_hostaccess = prcm_hostaccess & 0x01;
+
+ return prcm_hostaccess;
+}
+static enum hrtimer_restart callback(struct hrtimer *timer)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ca_wake_req_lock, flags);
+ if (((shrm_common_rx_state == SHRM_IDLE) ||
+ (shrm_common_rx_state == SHRM_SLEEP_STATE))
+ && ((shrm_common_tx_state == SHRM_IDLE) ||
+ (shrm_common_tx_state == SHRM_SLEEP_STATE))
+ && ((shrm_audio_rx_state == SHRM_IDLE) ||
+ (shrm_audio_rx_state == SHRM_SLEEP_STATE))
+ && ((shrm_audio_tx_state == SHRM_IDLE) ||
+ (shrm_audio_tx_state == SHRM_SLEEP_STATE))) {
+
+ shrm_common_rx_state = SHRM_SLEEP_STATE;
+ shrm_audio_rx_state = SHRM_SLEEP_STATE;
+ shrm_common_tx_state = SHRM_SLEEP_STATE;
+ shrm_audio_tx_state = SHRM_SLEEP_STATE;
+
+ queue_work(shm_dev->shm_ac_sleep_wq,
+ &shm_dev->shm_ac_sleep_req);
+
+ }
+ spin_unlock_irqrestore(&ca_wake_req_lock, flags);
+
+ return HRTIMER_NORESTART;
+}
+
+int nl_send_multicast_message(int msg, gfp_t gfp_mask)
+{
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh = NULL;
+ int err;
+
+ /* prepare netlink message */
+ skb = alloc_skb(NLMSG_SPACE(MAX_PAYLOAD), gfp_mask);
+ if (!skb) {
+ dev_err(shm_dev->dev, "%s:alloc_skb failed\n", __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ nlh = (struct nlmsghdr *)skb->data;
+ nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
+ dev_dbg(shm_dev->dev, "nlh->nlmsg_len = %d\n", nlh->nlmsg_len);
+
+ nlh->nlmsg_pid = 0; /* from kernel */
+ nlh->nlmsg_flags = 0;
+ *(int *)NLMSG_DATA(nlh) = msg;
+ skb_put(skb, MAX_PAYLOAD);
+ /* sender is in group 1<<0 */
+ NETLINK_CB(skb).pid = 0; /* from kernel */
+ /* to mcast group 1<<0 */
+ NETLINK_CB(skb).dst_group = 1;
+
+ /*multicast the message to all listening processes*/
+ err = netlink_broadcast(shrm_nl_sk, skb, 0, 1, gfp_mask);
+ dev_dbg(shm_dev->dev, "ret val from nl-multicast = %d\n", err);
+
+out:
+ return err;
+}
+
+static void nl_send_unicast_message(int dst_pid)
+{
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh = NULL;
+ int err;
+ int bt_state;
+ unsigned long flags;
+
+ dev_info(shm_dev->dev, "Sending unicast message\n");
+
+ /* prepare the NL message for unicast */
+ skb = alloc_skb(NLMSG_SPACE(MAX_PAYLOAD), GFP_KERNEL);
+ if (!skb) {
+ dev_err(shm_dev->dev, "%s:alloc_skb failed\n", __func__);
+ return;
+ }
+
+ nlh = (struct nlmsghdr *)skb->data;
+ nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
+ dev_dbg(shm_dev->dev, "nlh->nlmsg_len = %d\n", nlh->nlmsg_len);
+
+ nlh->nlmsg_pid = 0; /* from kernel */
+ nlh->nlmsg_flags = 0;
+
+ spin_lock_irqsave(&boot_lock, flags);
+ bt_state = boot_state;
+ spin_unlock_irqrestore(&boot_lock, flags);
+
+ if (bt_state == BOOT_DONE)
+ *(int *)NLMSG_DATA(nlh) = SHRM_NL_STATUS_MOD_ONLINE;
+ else
+ *(int *)NLMSG_DATA(nlh) = SHRM_NL_STATUS_MOD_OFFLINE;
+
+ skb_put(skb, MAX_PAYLOAD);
+ /* sender is in group 1<<0 */
+ NETLINK_CB(skb).pid = 0; /* from kernel */
+ NETLINK_CB(skb).dst_group = 0;
+
+ /*unicast the message to the querying processes*/
+ err = netlink_unicast(shrm_nl_sk, skb, dst_pid, MSG_DONTWAIT);
+ dev_dbg(shm_dev->dev, "ret val from nl-unicast = %d\n", err);
+}
+
+
+static int check_modem_in_reset(void)
+{
+ u8 bt_state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&boot_lock, flags);
+ bt_state = boot_state;
+ spin_unlock_irqrestore(&boot_lock, flags);
+
+#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET
+ if (bt_state != BOOT_UNKNOWN)
+ return 0;
+ else
+ return -ENODEV;
+#else
+ /*
+ * this check won't be applicable and won't work correctly
+ * if modem-silent-feature is not enabled
+ * so, simply return 0
+ */
+ return 0;
+#endif
+}
+
+void shm_ca_msgpending_0_tasklet(unsigned long tasklet_data)
+{
+ struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data;
+ u32 reader_local_rptr;
+ u32 reader_local_wptr;
+ u32 shared_rptr;
+ u32 config = 0, version = 0;
+ unsigned long flags;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ /* Interprocess locking */
+ spin_lock(&ca_common_lock);
+
+ /* Update_reader_local_wptr with shared_wptr */
+ update_ca_common_local_wptr(shrm);
+ get_reader_pointers(COMMON_CHANNEL, &reader_local_rptr,
+ &reader_local_wptr, &shared_rptr);
+
+ set_ca_msg_0_read_notif_send(0);
+
+ if (boot_state == BOOT_DONE) {
+ shrm_common_rx_state = SHRM_PTR_FREE;
+
+ if (reader_local_rptr != shared_rptr)
+ ca_msg_read_notification_0(shrm);
+ if (reader_local_rptr != reader_local_wptr)
+ receive_messages_common(shrm);
+ get_reader_pointers(COMMON_CHANNEL, &reader_local_rptr,
+ &reader_local_wptr, &shared_rptr);
+ if (reader_local_rptr == reader_local_wptr)
+ shrm_common_rx_state = SHRM_IDLE;
+ } else {
+ /* BOOT phase.only a BOOT_RESP should be in FIFO */
+ if (boot_state != BOOT_INFO_SYNC) {
+ if (!read_boot_info_req(shrm, &config, &version)) {
+ dev_err(shrm->dev,
+ "Unable to read boot state\n");
+ BUG();
+ }
+ /* SendReadNotification */
+ ca_msg_read_notification_0(shrm);
+ /*
+ * Check the version number before
+ * sending Boot info response
+ */
+
+ /* send MsgPending notification */
+ write_boot_info_resp(shrm, config, version);
+ spin_lock_irqsave(&boot_lock, flags);
+ boot_state = BOOT_INFO_SYNC;
+ spin_unlock_irqrestore(&boot_lock, flags);
+ dev_info(shrm->dev, "BOOT_INFO_SYNC\n");
+ queue_work(shrm->shm_common_ch_wr_wq,
+ &shrm->send_ac_msg_pend_notify_0);
+ } else {
+ ca_msg_read_notification_0(shrm);
+ dev_info(shrm->dev,
+ "BOOT_INFO_SYNC\n");
+ }
+ }
+ /* Interprocess locking */
+ spin_unlock(&ca_common_lock);
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+void shm_ca_msgpending_1_tasklet(unsigned long tasklet_data)
+{
+ struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data;
+ u32 reader_local_rptr;
+ u32 reader_local_wptr;
+ u32 shared_rptr;
+
+ /*
+ * This function is called when CaMsgPendingNotification Trigerred
+ * by CMU. It means that CMU has wrote a message into Ca Audio FIFO
+ */
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown\n",
+ __func__);
+ return;
+ }
+
+ /* Interprocess locking */
+ spin_lock(&ca_audio_lock);
+
+ /* Update_reader_local_wptr(with shared_wptr) */
+ update_ca_audio_local_wptr(shrm);
+ get_reader_pointers(AUDIO_CHANNEL, &reader_local_rptr,
+ &reader_local_wptr, &shared_rptr);
+
+ set_ca_msg_1_read_notif_send(0);
+
+ if (boot_state != BOOT_DONE) {
+ dev_err(shrm->dev, "Boot Error\n");
+ return;
+ }
+ shrm_audio_rx_state = SHRM_PTR_FREE;
+ /* Check we already read the message */
+ if (reader_local_rptr != shared_rptr)
+ ca_msg_read_notification_1(shrm);
+ if (reader_local_rptr != reader_local_wptr)
+ receive_messages_audio(shrm);
+
+ get_reader_pointers(AUDIO_CHANNEL, &reader_local_rptr,
+ &reader_local_wptr, &shared_rptr);
+ if (reader_local_rptr == reader_local_wptr)
+ shrm_audio_rx_state = SHRM_IDLE;
+
+ /* Interprocess locking */
+ spin_unlock(&ca_audio_lock);
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+void shm_ac_read_notif_0_tasklet(unsigned long tasklet_data)
+{
+ struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data;
+ u32 writer_local_rptr;
+ u32 writer_local_wptr;
+ u32 shared_wptr;
+ unsigned long flags;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ /* Update writer_local_rptrwith shared_rptr */
+ update_ac_common_local_rptr(shrm);
+ get_writer_pointers(COMMON_CHANNEL, &writer_local_rptr,
+ &writer_local_wptr, &shared_wptr);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown\n",
+ __func__);
+ return;
+ }
+
+ if (boot_state == BOOT_INFO_SYNC) {
+ /* BOOT_RESP sent by APE has been received by CMT */
+ spin_lock_irqsave(&boot_lock, flags);
+ boot_state = BOOT_DONE;
+ spin_unlock_irqrestore(&boot_lock, flags);
+ dev_info(shrm->dev, "IPC_ISA BOOT_DONE\n");
+
+ if (shrm->msr_flag) {
+ shrm_start_netdev(shrm->ndev);
+ shrm->msr_flag = 0;
+
+ /* multicast that modem is online */
+ nl_send_multicast_message(SHRM_NL_STATUS_MOD_ONLINE, GFP_ATOMIC);
+ }
+
+ } else if (boot_state == BOOT_DONE) {
+ if (writer_local_rptr != writer_local_wptr) {
+ shrm_common_tx_state = SHRM_PTR_FREE;
+ queue_work(shrm->shm_common_ch_wr_wq,
+ &shrm->send_ac_msg_pend_notify_0);
+ } else {
+ shrm_common_tx_state = SHRM_IDLE;
+ shrm_restart_netdev(shrm->ndev);
+ }
+ } else {
+ dev_err(shrm->dev, "Invalid boot state\n");
+ }
+ /* start timer here */
+ hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
+ atomic_dec(&ac_sleep_disable_count);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+void shm_ac_read_notif_1_tasklet(unsigned long tasklet_data)
+{
+ struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data;
+ u32 writer_local_rptr;
+ u32 writer_local_wptr;
+ u32 shared_wptr;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown\n",
+ __func__);
+ return;
+ }
+
+ /* Update writer_local_rptr(with shared_rptr) */
+ update_ac_audio_local_rptr(shrm);
+ get_writer_pointers(AUDIO_CHANNEL, &writer_local_rptr,
+ &writer_local_wptr, &shared_wptr);
+ if (boot_state != BOOT_DONE) {
+ dev_err(shrm->dev, "Error Case in boot state\n");
+ return;
+ }
+ if (writer_local_rptr != writer_local_wptr) {
+ shrm_audio_tx_state = SHRM_PTR_FREE;
+ queue_work(shrm->shm_audio_ch_wr_wq,
+ &shrm->send_ac_msg_pend_notify_1);
+ } else {
+ shrm_audio_tx_state = SHRM_IDLE;
+ }
+ /* start timer here */
+ hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
+ atomic_dec(&ac_sleep_disable_count);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+void shm_ca_sleep_req_work(struct work_struct *work)
+{
+ dev_dbg(shm_dev->dev, "%s:IRQ_PRCMU_CA_SLEEP\n", __func__);
+
+ shrm_common_rx_state = SHRM_IDLE;
+ shrm_audio_rx_state = SHRM_IDLE;
+
+ writel((1<<GOP_CA_WAKE_ACK_BIT),
+ shm_dev->intr_base + GOP_SET_REGISTER_BASE);
+
+ hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
+#ifdef CONFIG_UX500_SUSPEND
+ suspend_unblock_sleep();
+#endif
+ atomic_dec(&ac_sleep_disable_count);
+}
+
+void shm_ca_wake_req_work(struct work_struct *work)
+{
+ struct shrm_dev *shrm = container_of(work,
+ struct shrm_dev, shm_ca_wake_req);
+
+ /* initialize the FIFO Variables */
+ if (boot_state == BOOT_INIT)
+ shm_fifo_init(shrm);
+
+ mutex_lock(&ac_state_mutex);
+ prcmu_ac_wake_req();
+ mutex_unlock(&ac_state_mutex);
+
+ /* send ca_wake_ack_interrupt to CMU */
+ if (!get_host_accessport_val())
+ BUG();
+ writel((1<<GOP_CA_WAKE_ACK_BIT),
+ shm_dev->intr_base + GOP_SET_REGISTER_BASE);
+}
+#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET
+static int shrm_modem_reset_sequence(void)
+{
+ int err;
+ unsigned long flags;
+
+ /*
+ * disable irqs
+ * very much needed for user-space initiated
+ * modem-reset
+ */
+ disable_irq_nosync(shm_dev->ac_read_notif_0_irq);
+ disable_irq_nosync(shm_dev->ac_read_notif_1_irq);
+ disable_irq_nosync(shm_dev->ca_msg_pending_notif_0_irq);
+ disable_irq_nosync(shm_dev->ca_msg_pending_notif_1_irq);
+ disable_irq_nosync(IRQ_PRCMU_CA_WAKE);
+ disable_irq_nosync(IRQ_PRCMU_CA_SLEEP);
+
+
+ /* update the boot_state */
+ spin_lock_irqsave(&boot_lock, flags);
+ boot_state = BOOT_UNKNOWN;
+
+ /*
+ * put a barrier over here to make sure boot_state is updated
+ * else, it is seen that some of already executing modem
+ * irqs or tasklets fail the protocol checks and will ultimately
+ * try to acces the modem causing system to hang.
+ * This is particularly seen with user-space initiated modem reset
+ */
+ wmb();
+ spin_unlock_irqrestore(&boot_lock, flags);
+
+ hrtimer_cancel(&timer);
+
+ /*
+ * keep the count to 0 so that we can bring down the line
+ * for normal ac-wake and ac-sleep logic
+ */
+ atomic_set(&ac_sleep_disable_count, 0);
+
+ /* workaround for MSR */
+ queue_work(shm_dev->shm_ac_wake_wq,
+ &shm_dev->shm_ac_wake_req);
+
+ /* stop network queue */
+ shrm_stop_netdev(shm_dev->ndev);
+
+ /* reset char device queues */
+ shrm_char_reset_queues(shm_dev);
+
+ /* reset protocol states */
+ shrm_common_tx_state = SHRM_SLEEP_STATE;
+ shrm_common_rx_state = SHRM_SLEEP_STATE;
+ shrm_audio_tx_state = SHRM_SLEEP_STATE;
+ shrm_audio_rx_state = SHRM_SLEEP_STATE;
+
+ /* set the msr flag */
+ shm_dev->msr_flag = 1;
+
+ /* multicast that modem is going to reset */
+ err = nl_send_multicast_message(SHRM_NL_MOD_RESET, GFP_ATOMIC);
+
+ /* reset the boot state */
+ spin_lock_irqsave(&boot_lock, flags);
+ boot_state = BOOT_INIT;
+ spin_unlock_irqrestore(&boot_lock, flags);
+
+ /* re-enable irqs */
+ enable_irq(shm_dev->ac_read_notif_0_irq);
+ enable_irq(shm_dev->ac_read_notif_1_irq);
+ enable_irq(shm_dev->ca_msg_pending_notif_0_irq);
+ enable_irq(shm_dev->ca_msg_pending_notif_1_irq);
+ enable_irq(IRQ_PRCMU_CA_WAKE);
+ enable_irq(IRQ_PRCMU_CA_SLEEP);
+
+ return err;
+}
+#endif
+
+static void shrm_modem_reset_callback(unsigned long irq)
+{
+ dev_err(shm_dev->dev, "Received mod_reset_req interrupt\n");
+
+#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET
+ {
+ int err;
+ dev_info(shm_dev->dev, "Initiating Modem silent reset\n");
+
+ err = shrm_modem_reset_sequence();
+ if (err)
+ dev_err(shm_dev->dev,
+ "Failed multicast of modem reset\n");
+ }
+#else
+ dev_info(shm_dev->dev, "Modem in reset loop, doing System reset\n");
+
+ /* Call the PRCMU reset API */
+ prcmu_system_reset(SW_RESET_NO_ARGUMENT);
+#endif
+}
+
+DECLARE_TASKLET(shrm_sw_reset_callback, shrm_modem_reset_callback,
+ IRQ_PRCMU_MODEM_SW_RESET_REQ);
+
+static irqreturn_t shrm_prcmu_irq_handler(int irq, void *data)
+{
+ struct shrm_dev *shrm = data;
+
+ switch (irq) {
+ case IRQ_PRCMU_CA_WAKE:
+#ifdef CONFIG_UX500_SUSPEND
+ suspend_block_sleep();
+#endif
+ if (shrm->msr_flag)
+ atomic_set(&ac_sleep_disable_count, 0);
+ atomic_inc(&ac_sleep_disable_count);
+ queue_work(shrm->shm_ca_wake_wq, &shrm->shm_ca_wake_req);
+ break;
+ case IRQ_PRCMU_CA_SLEEP:
+ queue_work(shrm->shm_ca_wake_wq, &shrm->shm_ca_sleep_req);
+ break;
+ case IRQ_PRCMU_MODEM_SW_RESET_REQ:
+ tasklet_schedule(&shrm_sw_reset_callback);
+ break;
+ default:
+ dev_err(shrm->dev, "%s: => IRQ %d\n", __func__, irq);
+ return IRQ_NONE;
+ }
+ return IRQ_HANDLED;
+}
+
+static void send_ac_msg_pend_notify_0_work(struct work_struct *work)
+{
+ struct shrm_dev *shrm = container_of(work, struct shrm_dev,
+ send_ac_msg_pend_notify_0);
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ update_ac_common_shared_wptr(shrm);
+
+ mutex_lock(&ac_state_mutex);
+ atomic_inc(&ac_sleep_disable_count);
+ prcmu_ac_wake_req();
+ mutex_unlock(&ac_state_mutex);
+
+ if (!get_host_accessport_val())
+ BUG();
+
+ /* Trigger AcMsgPendingNotification to CMU */
+ writel((1<<GOP_COMMON_AC_MSG_PENDING_NOTIFICATION_BIT),
+ shrm->intr_base + GOP_SET_REGISTER_BASE);
+
+ if (shrm_common_tx_state == SHRM_PTR_FREE)
+ shrm_common_tx_state = SHRM_PTR_BUSY;
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+static void send_ac_msg_pend_notify_1_work(struct work_struct *work)
+{
+ struct shrm_dev *shrm = container_of(work, struct shrm_dev,
+ send_ac_msg_pend_notify_1);
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ /* Update shared_wptr with writer_local_wptr) */
+ update_ac_audio_shared_wptr(shrm);
+
+ mutex_lock(&ac_state_mutex);
+ atomic_inc(&ac_sleep_disable_count);
+ prcmu_ac_wake_req();
+ mutex_unlock(&ac_state_mutex);
+
+ if (!get_host_accessport_val())
+ BUG();
+
+ /* Trigger AcMsgPendingNotification to CMU */
+ writel((1<<GOP_AUDIO_AC_MSG_PENDING_NOTIFICATION_BIT),
+ shrm->intr_base + GOP_SET_REGISTER_BASE);
+
+ if (shrm_audio_tx_state == SHRM_PTR_FREE)
+ shrm_audio_tx_state = SHRM_PTR_BUSY;
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+void shm_nl_receive(struct sk_buff *skb)
+{
+ struct nlmsghdr *nlh = NULL;
+ int msg;
+
+ dev_dbg(shm_dev->dev, "Received NL msg from user-space\n");
+
+ nlh = (struct nlmsghdr *)skb->data;
+ msg = *((int *)(NLMSG_DATA(nlh)));
+ switch (msg) {
+ case SHRM_NL_MOD_QUERY_STATE:
+ dev_info(shm_dev->dev, "mod-query-state from user-space\n");
+ nl_send_unicast_message(nlh->nlmsg_pid);
+ break;
+
+ case SHRM_NL_USER_MOD_RESET:
+ dev_info(shm_dev->dev, "user-space inited mod-reset-req\n");
+ dev_info(shm_dev->dev, "PCRMU resets modem\n");
+ prcmu_modem_reset();
+ break;
+
+ default:
+ dev_err(shm_dev->dev, "Invalid NL msg from user-space\n");
+ break;
+ };
+}
+
+int shrm_protocol_init(struct shrm_dev *shrm,
+ received_msg_handler common_rx_handler,
+ received_msg_handler audio_rx_handler)
+{
+ int err;
+
+ shm_dev = shrm;
+ boot_state = BOOT_INIT;
+ dev_info(shrm->dev, "IPC_ISA BOOT_INIT\n");
+ rx_common_handler = common_rx_handler;
+ rx_audio_handler = audio_rx_handler;
+ atomic_set(&ac_sleep_disable_count, 0);
+
+ is_earlydrop = cpu_is_u8500ed();
+ if (is_earlydrop != 0x01) {
+ hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ timer.function = callback;
+ }
+
+ shrm->shm_common_ch_wr_wq = create_singlethread_workqueue
+ ("shm_common_channel_irq");
+ if (!shrm->shm_common_ch_wr_wq) {
+ dev_err(shrm->dev, "failed to create work queue\n");
+ return -ENOMEM;
+ }
+ shrm->shm_audio_ch_wr_wq = create_singlethread_workqueue
+ ("shm_audio_channel_irq");
+ if (!shrm->shm_audio_ch_wr_wq) {
+ dev_err(shrm->dev, "failed to create work queue\n");
+ err = -ENOMEM;
+ goto free_wq1;
+ }
+ shrm->shm_ac_wake_wq = create_singlethread_workqueue("shm_ac_wake_req");
+ if (!shrm->shm_ac_wake_wq) {
+ dev_err(shrm->dev, "failed to create work queue\n");
+ err = -ENOMEM;
+ goto free_wq2;
+ }
+ shrm->shm_ca_wake_wq = create_singlethread_workqueue("shm_ca_wake_req");
+ if (!shrm->shm_ac_wake_wq) {
+ dev_err(shrm->dev, "failed to create work queue\n");
+ err = -ENOMEM;
+ goto free_wq3;
+ }
+ shrm->shm_ac_sleep_wq = create_singlethread_workqueue
+ ("shm_ac_sleep_req");
+ if (!shrm->shm_ac_sleep_wq) {
+ dev_err(shrm->dev, "failed to create work queue\n");
+ err = -ENOMEM;
+ goto free_wq4;
+ }
+ INIT_WORK(&shrm->send_ac_msg_pend_notify_0,
+ send_ac_msg_pend_notify_0_work);
+ INIT_WORK(&shrm->send_ac_msg_pend_notify_1,
+ send_ac_msg_pend_notify_1_work);
+ INIT_WORK(&shrm->shm_ca_wake_req, shm_ca_wake_req_work);
+ INIT_WORK(&shrm->shm_ca_sleep_req, shm_ca_sleep_req_work);
+ INIT_WORK(&shrm->shm_ac_sleep_req, shm_ac_sleep_req_work);
+ INIT_WORK(&shrm->shm_ac_wake_req, shm_ac_wake_req_work);
+
+ /* set tasklet data */
+ shm_ca_0_tasklet.data = (unsigned long)shrm;
+ shm_ca_1_tasklet.data = (unsigned long)shrm;
+
+ err = request_irq(IRQ_PRCMU_CA_SLEEP, shrm_prcmu_irq_handler,
+ IRQF_NO_SUSPEND, "ca-sleep", shrm);
+ if (err < 0) {
+ dev_err(shm_dev->dev, "Failed alloc IRQ_PRCMU_CA_SLEEP.\n");
+ goto free_wq5;
+ }
+
+ err = request_irq(IRQ_PRCMU_CA_WAKE, shrm_prcmu_irq_handler,
+ IRQF_NO_SUSPEND, "ca-wake", shrm);
+ if (err < 0) {
+ dev_err(shm_dev->dev, "Failed alloc IRQ_PRCMU_CA_WAKE.\n");
+ goto drop2;
+ }
+
+ err = request_irq(IRQ_PRCMU_MODEM_SW_RESET_REQ, shrm_prcmu_irq_handler,
+ IRQF_NO_SUSPEND, "modem-sw-reset-req", shrm);
+ if (err < 0) {
+ dev_err(shm_dev->dev,
+ "Failed alloc IRQ_PRCMU_MODEM_SW_RESET_REQ.\n");
+ goto drop1;
+ }
+
+#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET
+ /* init netlink socket for user-space communication */
+ shrm_nl_sk = netlink_kernel_create(NULL, NETLINK_SHRM, 1,
+ shm_nl_receive, NULL, THIS_MODULE);
+
+ if (!shrm_nl_sk) {
+ dev_err(shm_dev->dev, "netlink socket creation failed\n");
+ goto drop;
+ }
+#endif
+
+ return 0;
+
+#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET
+drop:
+ free_irq(IRQ_PRCMU_MODEM_SW_RESET_REQ, NULL);
+#endif
+drop1:
+ free_irq(IRQ_PRCMU_CA_WAKE, NULL);
+drop2:
+ free_irq(IRQ_PRCMU_CA_SLEEP, NULL);
+free_wq5:
+ destroy_workqueue(shrm->shm_ac_sleep_wq);
+free_wq4:
+ destroy_workqueue(shrm->shm_ca_wake_wq);
+free_wq3:
+ destroy_workqueue(shrm->shm_ac_wake_wq);
+free_wq2:
+ destroy_workqueue(shrm->shm_audio_ch_wr_wq);
+free_wq1:
+ destroy_workqueue(shrm->shm_common_ch_wr_wq);
+ return err;
+}
+
+void shrm_protocol_deinit(struct shrm_dev *shrm)
+{
+ free_irq(IRQ_PRCMU_CA_SLEEP, NULL);
+ free_irq(IRQ_PRCMU_CA_WAKE, NULL);
+ free_irq(IRQ_PRCMU_MODEM_SW_RESET_REQ, NULL);
+ flush_scheduled_work();
+ destroy_workqueue(shrm->shm_common_ch_wr_wq);
+ destroy_workqueue(shrm->shm_audio_ch_wr_wq);
+ destroy_workqueue(shrm->shm_ac_wake_wq);
+ destroy_workqueue(shrm->shm_ca_wake_wq);
+ destroy_workqueue(shrm->shm_ac_sleep_wq);
+}
+
+int get_ca_wake_req_state(void)
+{
+ return ((atomic_read(&ac_sleep_disable_count) > 0) ||
+ prcmu_is_ac_wake_requested());
+}
+
+irqreturn_t ca_wake_irq_handler(int irq, void *ctrlr)
+{
+ struct shrm_dev *shrm = ctrlr;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+ /* initialize the FIFO Variables */
+ if (boot_state == BOOT_INIT)
+ shm_fifo_init(shrm);
+
+ dev_dbg(shrm->dev, "Inside ca_wake_irq_handler\n");
+
+ /* Clear the interrupt */
+ writel((1 << GOP_CA_WAKE_REQ_BIT),
+ shrm->intr_base + GOP_CLEAR_REGISTER_BASE);
+
+ /* send ca_wake_ack_interrupt to CMU */
+ writel((1 << GOP_CA_WAKE_ACK_BIT),
+ shrm->intr_base + GOP_SET_REGISTER_BASE);
+
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return IRQ_HANDLED;
+}
+
+
+irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr)
+{
+ struct shrm_dev *shrm = ctrlr;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+
+ shm_ac_read_0_tasklet.data = (unsigned long)shrm;
+ tasklet_schedule(&shm_ac_read_0_tasklet);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+
+ /* Clear the interrupt */
+ writel((1 << GOP_COMMON_AC_READ_NOTIFICATION_BIT),
+ shrm->intr_base + GOP_CLEAR_REGISTER_BASE);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return IRQ_HANDLED;
+}
+
+irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr)
+{
+ struct shrm_dev *shrm = ctrlr;
+
+ dev_dbg(shrm->dev, "%s IN+\n", __func__);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+
+ shm_ac_read_1_tasklet.data = (unsigned long)shrm;
+ tasklet_schedule(&shm_ac_read_1_tasklet);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+
+ /* Clear the interrupt */
+ writel((1 << GOP_AUDIO_AC_READ_NOTIFICATION_BIT),
+ shrm->intr_base + GOP_CLEAR_REGISTER_BASE);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return IRQ_HANDLED;
+}
+
+irqreturn_t ca_msg_pending_notif_0_irq_handler(int irq, void *ctrlr)
+{
+ struct shrm_dev *shrm = ctrlr;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+
+ tasklet_schedule(&shm_ca_0_tasklet);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+
+ /* Clear the interrupt */
+ writel((1 << GOP_COMMON_CA_MSG_PENDING_NOTIFICATION_BIT),
+ shrm->intr_base + GOP_CLEAR_REGISTER_BASE);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return IRQ_HANDLED;
+}
+
+irqreturn_t ca_msg_pending_notif_1_irq_handler(int irq, void *ctrlr)
+{
+ struct shrm_dev *shrm = ctrlr;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+
+ tasklet_schedule(&shm_ca_1_tasklet);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+
+ /* Clear the interrupt */
+ writel((1<<GOP_AUDIO_CA_MSG_PENDING_NOTIFICATION_BIT),
+ shrm->intr_base+GOP_CLEAR_REGISTER_BASE);
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return IRQ_HANDLED;
+
+}
+
+/**
+ * shm_write_msg() - write message to shared memory
+ * @shrm: pointer to the shrm device information structure
+ * @l2_header: L2 header
+ * @addr: pointer to the message
+ * @length: length of the message to be written
+ *
+ * This function is called from net or char interface driver write operation.
+ * Prior to calling this function the message is copied from the user space
+ * buffer to the kernel buffer. This function based on the l2 header routes
+ * the message to the respective channel and FIFO. Then makes a call to the
+ * fifo write function where the message is written to the physical device.
+ */
+int shm_write_msg(struct shrm_dev *shrm, u8 l2_header,
+ void *addr, u32 length)
+{
+ u8 channel = 0;
+ int ret;
+
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (boot_state != BOOT_DONE) {
+ dev_err(shrm->dev,
+ "error after boot done call this fn\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if ((l2_header == L2_HEADER_ISI) ||
+ (l2_header == L2_HEADER_RPC) ||
+ (l2_header == L2_HEADER_SECURITY) ||
+ (l2_header == L2_HEADER_COMMON_SIMPLE_LOOPBACK) ||
+ (l2_header == L2_HEADER_COMMON_ADVANCED_LOOPBACK)) {
+ channel = 0;
+ if (shrm_common_tx_state == SHRM_SLEEP_STATE)
+ shrm_common_tx_state = SHRM_PTR_FREE;
+ else if (shrm_common_tx_state == SHRM_IDLE)
+ shrm_common_tx_state = SHRM_PTR_FREE;
+
+ } else if ((l2_header == L2_HEADER_AUDIO) ||
+ (l2_header == L2_HEADER_AUDIO_SIMPLE_LOOPBACK) ||
+ (l2_header == L2_HEADER_AUDIO_ADVANCED_LOOPBACK)) {
+ if (shrm_audio_tx_state == SHRM_SLEEP_STATE)
+ shrm_audio_tx_state = SHRM_PTR_FREE;
+ else if (shrm_audio_tx_state == SHRM_IDLE)
+ shrm_audio_tx_state = SHRM_PTR_FREE;
+
+ channel = 1;
+ } else {
+ ret = -ENODEV;
+ goto out;
+ }
+ ret = shm_write_msg_to_fifo(shrm, channel, l2_header, addr, length);
+ if (ret < 0) {
+ dev_err(shrm->dev, "write message to fifo failed\n");
+ return ret;
+ }
+ /*
+ * notify only if new msg copied is the only unread one
+ * otherwise it means that reading process is ongoing
+ */
+ if (is_the_only_one_unread_message(shrm, channel, length)) {
+
+ /* Send Message Pending Noitication to CMT */
+ if (channel == 0)
+ queue_work(shrm->shm_common_ch_wr_wq,
+ &shrm->send_ac_msg_pend_notify_0);
+ else
+ queue_work(shrm->shm_audio_ch_wr_wq,
+ &shrm->send_ac_msg_pend_notify_1);
+
+ }
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+ return 0;
+
+out:
+ return ret;
+}
+
+void ca_msg_read_notification_0(struct shrm_dev *shrm)
+{
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (get_ca_msg_0_read_notif_send() == 0) {
+ update_ca_common_shared_rptr(shrm);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return;
+ }
+
+ /* Trigger CaMsgReadNotification to CMU */
+ writel((1 << GOP_COMMON_CA_READ_NOTIFICATION_BIT),
+ shrm->intr_base + GOP_SET_REGISTER_BASE);
+ set_ca_msg_0_read_notif_send(1);
+ shrm_common_rx_state = SHRM_PTR_BUSY;
+ }
+
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+void ca_msg_read_notification_1(struct shrm_dev *shrm)
+{
+ dev_dbg(shrm->dev, "%s IN\n", __func__);
+
+ if (get_ca_msg_1_read_notif_send() == 0) {
+ update_ca_audio_shared_rptr(shrm);
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return;
+ }
+
+ /* Trigger CaMsgReadNotification to CMU */
+ writel((1<<GOP_AUDIO_CA_READ_NOTIFICATION_BIT),
+ shrm->intr_base+GOP_SET_REGISTER_BASE);
+ set_ca_msg_1_read_notif_send(1);
+ shrm_audio_rx_state = SHRM_PTR_BUSY;
+ }
+ dev_dbg(shrm->dev, "%s OUT\n", __func__);
+}
+
+/**
+ * receive_messages_common - receive common channnel msg from
+ * CMT(Cellular Mobile Terminal)
+ * @shrm: pointer to shrm device information structure
+ *
+ * The messages sent from CMT to APE are written to the respective FIFO
+ * and an interrupt is triggered by the CMT. This ca message pending
+ * interrupt calls this function. This function sends a read notification
+ * acknowledgement to the CMT and calls the common channel receive handler
+ * where the messsage is copied to the respective(ISI, RPC, SECURIT) queue
+ * based on the message l2 header.
+ */
+void receive_messages_common(struct shrm_dev *shrm)
+{
+ u8 l2_header;
+ u32 len;
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return;
+ }
+
+ l2_header = read_one_l2msg_common(shrm, recieve_common_msg, &len);
+ /* Send Recieve_Call_back to Upper Layer */
+ if (!rx_common_handler) {
+ dev_err(shrm->dev, "common_rx_handler is Null\n");
+ BUG();
+ }
+ (*rx_common_handler)(l2_header, &recieve_common_msg, len,
+ shrm);
+ /* SendReadNotification */
+ ca_msg_read_notification_0(shrm);
+
+ while (read_remaining_messages_common()) {
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return;
+ }
+
+ l2_header = read_one_l2msg_common(shrm, recieve_common_msg,
+ &len);
+ /* Send Recieve_Call_back to Upper Layer */
+ (*rx_common_handler)(l2_header,
+ &recieve_common_msg, len,
+ shrm);
+ }
+}
+
+/**
+ * receive_messages_audio() - receive audio message from CMT
+ * @shrm: pointer to shrm device information structure
+ *
+ * The messages sent from CMT to APE are written to the respective FIFO
+ * and an interrupt is triggered by the CMT. This ca message pending
+ * interrupt calls this function. This function sends a read notification
+ * acknowledgement to the CMT and calls the common channel receive handler
+ * where the messsage is copied to the audio queue.
+ */
+void receive_messages_audio(struct shrm_dev *shrm)
+{
+ u8 l2_header;
+ u32 len;
+
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return;
+ }
+
+ l2_header = read_one_l2msg_audio(shrm, recieve_audio_msg, &len);
+ /* Send Recieve_Call_back to Upper Layer */
+
+ if (!rx_audio_handler) {
+ dev_crit(shrm->dev, "audio_rx_handler is Null\n");
+ BUG();
+ }
+ (*rx_audio_handler)(l2_header, &recieve_audio_msg,
+ len, shrm);
+
+ /* SendReadNotification */
+ ca_msg_read_notification_1(shrm);
+ while (read_remaining_messages_audio()) {
+ if (check_modem_in_reset()) {
+ dev_err(shrm->dev, "%s:Modem state reset or unknown.\n",
+ __func__);
+ return;
+ }
+
+ l2_header = read_one_l2msg_audio(shrm,
+ recieve_audio_msg, &len);
+ /* Send Recieve_Call_back to Upper Layer */
+ (*rx_audio_handler)(l2_header,
+ &recieve_audio_msg, len,
+ shrm);
+ }
+}
+
+u8 get_boot_state()
+{
+ return boot_state;
+}
diff --git a/drivers/misc/stm.c b/drivers/misc/stm.c
new file mode 100644
index 00000000000..3782d837291
--- /dev/null
+++ b/drivers/misc/stm.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <mach/prcmu-regs.h>
+#include <trace/stm.h>
+
+#define STM_CLOCK_SHIFT 6
+#define STM_CLOCK_MASK 0x1C0
+#define STM_ENABLE_MASK 0x23D
+/* Software mode for all cores except PRCMU that doesn't support SW */
+#define STM_MMC_DEFAULT 0x20
+
+/* STM Registers */
+#define STM_CR (stm.virtbase)
+#define STM_MMC (stm.virtbase + 0x008)
+#define STM_TER (stm.virtbase + 0x010)
+#define STMPERIPHID0 (stm.virtbase + 0xFC0)
+#define STMPERIPHID1 (stm.virtbase + 0xFC8)
+#define STMPERIPHID2 (stm.virtbase + 0xFD0)
+#define STMPERIPHID3 (stm.virtbase + 0xFD8)
+#define STMPCELLID0 (stm.virtbase + 0xFE0)
+#define STMPCELLID1 (stm.virtbase + 0xFE8)
+#define STMPCELLID2 (stm.virtbase + 0xFF0)
+#define STMPCELLID3 (stm.virtbase + 0xFF8)
+
+static struct stm_device {
+ struct stm_platform_data *pdata;
+ void __iomem *virtbase;
+ volatile struct stm_channel __iomem *channels;
+ /* Used to register the allocated channels */
+ DECLARE_BITMAP(ch_bitmap, STM_NUMBER_OF_CHANNEL);
+} stm;
+
+static DEFINE_MUTEX(lock);
+
+static char *mipi60;
+module_param(mipi60, charp, S_IRUGO);
+MODULE_PARM_DESC(mipi60, "Trace to output on probe2 of mipi60 "
+ "('ape' or 'none')");
+
+static char *mipi34 = "modem";
+module_param(mipi34, charp, S_IRUGO);
+MODULE_PARM_DESC(mipi34, "Trace to output on mipi34 ('ape' or 'modem')");
+
+#define IS_APE_ON_MIPI34 (mipi34 && !strcmp(mipi34, "ape"))
+#define IS_APE_ON_MIPI60 (mipi60 && !strcmp(mipi60, "ape"))
+
+static int stm_open(struct inode *inode, struct file *file)
+{
+ file->private_data = kzalloc(sizeof(stm.ch_bitmap), GFP_KERNEL);
+ if (file->private_data == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static int stm_release(struct inode *inode, struct file *filp)
+{
+ bitmap_andnot(stm.ch_bitmap, stm.ch_bitmap, filp->private_data,
+ STM_NUMBER_OF_CHANNEL);
+ kfree(filp->private_data);
+ return 0;
+}
+
+static int stm_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ /*
+ * Don't allow a mapping that covers more than the STM channels
+ * 4096 == sizeof(struct stm_channels)
+ */
+ if ((vma->vm_end - vma->vm_start) > SZ_4K)
+ return -EINVAL;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (io_remap_pfn_range(vma, vma->vm_start,
+ stm.pdata->channels_phys_base>>PAGE_SHIFT,
+ SZ_4K, vma->vm_page_prot))
+ return -EAGAIN ;
+
+ return 0;
+}
+
+void stm_disable_src(void)
+{
+ mutex_lock(&lock);
+ writel(0x0, STM_CR); /* stop clock */
+ writel(STM_MMC_DEFAULT, STM_MMC);
+ writel(0x0, STM_TER); /* Disable cores */
+ mutex_unlock(&lock);
+}
+EXPORT_SYMBOL(stm_disable_src);
+
+int stm_set_ckdiv(enum clock_div v)
+{
+ unsigned int val;
+
+ mutex_lock(&lock);
+ val = readl(STM_CR);
+ val &= ~STM_CLOCK_MASK;
+ writel(val | ((v << STM_CLOCK_SHIFT) & STM_CLOCK_MASK), STM_CR);
+ mutex_unlock(&lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(stm_set_ckdiv);
+
+unsigned int stm_get_cr(void)
+{
+ return readl(STM_CR);
+}
+EXPORT_SYMBOL(stm_get_cr);
+
+int stm_enable_src(unsigned int v)
+{
+ unsigned int val;
+ mutex_lock(&lock);
+ val = readl(STM_CR);
+ val &= ~STM_CLOCK_MASK;
+ /* middle possible clock */
+ writel(val & (STM_CLOCK_DIV8 << STM_CLOCK_SHIFT), STM_CR);
+ writel(STM_MMC_DEFAULT, STM_MMC);
+ writel((v & STM_ENABLE_MASK), STM_TER);
+ mutex_unlock(&lock);
+ return 0;
+}
+EXPORT_SYMBOL(stm_enable_src);
+
+static int stm_get_channel(struct file *filp, int __user *arg)
+{
+ int c, err;
+
+ /* Look for a free channel */
+ do {
+ c = find_first_zero_bit(stm.ch_bitmap, STM_NUMBER_OF_CHANNEL);
+ } while ((c < STM_NUMBER_OF_CHANNEL)
+ && test_and_set_bit(c, stm.ch_bitmap));
+
+ if (c < STM_NUMBER_OF_CHANNEL) {
+ /* One free found ! */
+ err = put_user(c, arg);
+ if (err) {
+ clear_bit(c, stm.ch_bitmap);
+ } else {
+ /* Register it in the context of the file */
+ unsigned long *local_bitmap = filp->private_data;
+ if (local_bitmap)
+ set_bit(c, local_bitmap);
+ }
+ } else {
+ err = -ENOMEM;
+ }
+ return err;
+}
+
+static int stm_free_channel(struct file *filp, int channel)
+{
+ if ((channel < 0) || (channel >= STM_NUMBER_OF_CHANNEL))
+ return -EINVAL;
+ clear_bit(channel, stm.ch_bitmap);
+ if (filp->private_data)
+ clear_bit(channel, filp->private_data);
+ return 0;
+}
+
+static long stm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+
+ switch (cmd) {
+
+ case STM_DISABLE:
+ stm_disable_src();
+ break;
+
+ case STM_SET_CLOCK_DIV:
+ err = stm_set_ckdiv((enum clock_div) arg);
+ break;
+
+ case STM_GET_CTRL_REG:
+ err = put_user(stm_get_cr(), (unsigned int *)arg);
+ break;
+
+ case STM_ENABLE_SRC:
+ err = stm_enable_src(arg);
+ break;
+
+ case STM_DISABLE_MIPI34_MODEM:
+ stm.pdata->ste_disable_modem_on_mipi34();
+ break;
+
+ case STM_ENABLE_MIPI34_MODEM:
+ stm.pdata->ste_enable_modem_on_mipi34();
+ break;
+
+ case STM_GET_FREE_CHANNEL:
+ err = stm_get_channel(filp, (int *)arg);
+ break;
+
+ case STM_RELEASE_CHANNEL:
+ err = stm_free_channel(filp, arg);
+ break;
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+#define DEFLLTFUN(size) \
+ void stm_trace_##size(unsigned char channel, uint##size##_t data) \
+ { \
+ (__chk_io_ptr(&(stm.channels[channel].no_stamp##size))), \
+ *(volatile uint##size##_t __force *) \
+ (&(stm.channels[channel].no_stamp##size)) = data;\
+ } \
+ EXPORT_SYMBOL(stm_trace_##size); \
+ void stm_tracet_##size(unsigned char channel, uint##size##_t data) \
+ { \
+ (__chk_io_ptr(&(stm.channels[channel].stamp##size))), \
+ *(volatile uint##size##_t __force *) \
+ (&(stm.channels[channel].stamp##size)) = data; } \
+ EXPORT_SYMBOL(stm_tracet_##size)
+
+DEFLLTFUN(8);
+DEFLLTFUN(16);
+DEFLLTFUN(32);
+DEFLLTFUN(64);
+
+static const struct file_operations stm_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = stm_ioctl,
+ .open = stm_open,
+ .llseek = no_llseek,
+ .release = stm_release,
+ .mmap = stm_mmap,
+};
+
+static struct miscdevice stm_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = STM_DEV_NAME,
+ .fops = &stm_fops
+};
+
+static int __devinit stm_probe(struct platform_device *pdev)
+{
+ int err;
+
+ if (!pdev || !pdev->dev.platform_data) {
+ pr_alert("No device/platform_data found on STM driver\n");
+ return -ENODEV;
+ }
+
+ stm.pdata = pdev->dev.platform_data;
+
+ /* Reserve channels if necessary */
+ if (stm.pdata->channels_reserved) {
+ int i = 0;
+ while (stm.pdata->channels_reserved[i] != -1) {
+ set_bit(stm.pdata->channels_reserved[i], stm.ch_bitmap);
+ i++;
+ }
+ }
+
+ err = misc_register(&stm_misc);
+ if (err) {
+ dev_alert(&pdev->dev, "Unable to register misc driver!\n");
+ return err;
+ }
+
+ stm.virtbase = ioremap_nocache(stm.pdata->regs_phys_base, SZ_4K);
+ if (stm.virtbase == NULL) {
+ err = -EIO;
+ dev_err(&pdev->dev, "could not remap STM Register\n");
+ goto fail_init;
+ }
+
+ stm.channels =
+ ioremap_nocache(stm.pdata->channels_phys_base,
+ STM_NUMBER_OF_CHANNEL*sizeof(*stm.channels));
+ if (stm.channels == NULL) {
+ dev_err(&pdev->dev, "could not remap STM Msg register\n");
+ goto fail_init;
+ }
+
+ /* Check chip IDs if necessary */
+ if (stm.pdata->periph_id && stm.pdata->cell_id) {
+ u32 periph_id, cell_id;
+
+ periph_id = (readb(STMPERIPHID0)<<24) +
+ (readb(STMPERIPHID1)<<16) +
+ (readb(STMPERIPHID2)<<8) +
+ readb(STMPERIPHID3);
+ cell_id = (readb(STMPCELLID0)<<24) +
+ (readb(STMPCELLID1)<<16) +
+ (readb(STMPCELLID2)<<8) +
+ readb(STMPCELLID3);
+ /* Ignore periph id2 field verification */
+ if ((stm.pdata->periph_id & 0xFFFF00FF)
+ != (periph_id & 0xFFFF00FF) ||
+ stm.pdata->cell_id != cell_id) {
+ dev_err(&pdev->dev, "STM-Trace not supported\n");
+ dev_err(&pdev->dev, "periph_id=%x\n", periph_id);
+ dev_err(&pdev->dev, "pcell_id=%x\n", cell_id);
+ err = -EINVAL;
+ goto fail_init;
+ }
+ }
+
+ if (IS_APE_ON_MIPI60) {
+ if (IS_APE_ON_MIPI34) {
+ dev_info(&pdev->dev, "Can't not enable APE trace on "
+ "mipi34 and mipi60-probe2: disabling APE on"
+ " mipi34\n");
+ mipi34 = "modem";
+ }
+ if (stm.pdata->ste_gpio_enable_ape_modem_mipi60) {
+ err = stm.pdata->ste_gpio_enable_ape_modem_mipi60();
+ if (err)
+ dev_err(&pdev->dev, "can't enable MIPI60\n");
+ }
+ }
+
+ if (IS_APE_ON_MIPI34) {
+ if (stm.pdata->ste_disable_modem_on_mipi34)
+ stm.pdata->ste_disable_modem_on_mipi34();
+ } else {
+ if (stm.pdata->ste_enable_modem_on_mipi34)
+ stm.pdata->ste_enable_modem_on_mipi34();
+ }
+
+ if (stm.pdata->ste_gpio_enable_mipi34) {
+ err = stm.pdata->ste_gpio_enable_mipi34();
+ if (err) {
+ dev_err(&pdev->dev, "failed to set GPIO_ALT_TRACE\n");
+ goto fail_init;
+ }
+ }
+
+ if (stm.pdata->masters_enabled)
+ stm_enable_src(stm.pdata->masters_enabled);
+ dev_info(&pdev->dev, "STM-Trace driver probed successfully\n");
+ return 0;
+fail_init:
+ if (stm.virtbase)
+ iounmap(stm.virtbase);
+
+ if (stm.channels)
+ iounmap(stm.channels);
+ misc_deregister(&stm_misc);
+
+ return err;
+}
+
+static int __devexit stm_remove(struct platform_device *pdev)
+{
+ struct stm_platform_data *pdata;
+ pdata = pdev->dev.platform_data;
+
+ if (pdata->ste_gpio_disable_mipi34)
+ pdata->ste_gpio_disable_mipi34();
+
+ stm_disable_src();
+
+ if (stm.virtbase)
+ iounmap(stm.virtbase);
+
+ if (stm.channels)
+ iounmap(stm.channels);
+
+ misc_deregister(&stm_misc);
+ return 0;
+}
+
+static struct platform_driver stm_driver = {
+ .probe = stm_probe,
+ .remove = __devexit_p(stm_remove),
+ .driver = {
+ .name = "stm",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init stm_init(void)
+{
+ return platform_driver_register(&stm_driver);
+}
+
+static void __exit stm_exit(void)
+{
+ platform_driver_unregister(&stm_driver);
+}
+
+module_init(stm_init);
+module_exit(stm_exit);
+
+MODULE_AUTHOR("Paul Ghaleb - ST Microelectronics");
+MODULE_AUTHOR("Pierre Peiffer - ST-Ericsson");
+MODULE_DESCRIPTION("Ux500 System Trace Module driver");
+MODULE_ALIAS("stm");
+MODULE_ALIAS("stm-trace");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c
new file mode 100644
index 00000000000..f5fd7038779
--- /dev/null
+++ b/drivers/misc/uid_stat.c
@@ -0,0 +1,186 @@
+/* drivers/misc/uid_stat.c
+ *
+ * Copyright (C) 2008 - 2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/atomic.h>
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/uid_stat.h>
+#include <net/activity_stats.h>
+
+static DEFINE_SPINLOCK(uid_lock);
+static LIST_HEAD(uid_list);
+static struct proc_dir_entry *parent;
+
+struct uid_stat {
+ struct list_head link;
+ uid_t uid;
+ atomic_t tcp_rcv;
+ atomic_t tcp_snd;
+ atomic_t tcp_rcv_pkt;
+ atomic_t tcp_snd_pkt;
+ atomic_t udp_rcv;
+ atomic_t udp_snd;
+ atomic_t udp_rcv_pkt;
+ atomic_t udp_snd_pkt;
+};
+
+static int read_proc_entry(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len;
+ unsigned int value;
+ char *p = page;
+ atomic_t *uid_entry = (atomic_t *) data;
+ if (!data)
+ return 0;
+
+ value = (unsigned int) (atomic_read(uid_entry) + INT_MIN);
+ p += sprintf(p, "%u\n", value);
+ len = (p - page) - off;
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+ return len;
+}
+
+/* Find or create a new entry for tracking the specified uid. */
+static struct uid_stat *get_uid_stat(uid_t uid) {
+ unsigned long flags;
+ struct uid_stat *uid_entry;
+ struct uid_stat *new_uid;
+ struct proc_dir_entry *proc_entry;
+ char uid_s[32];
+
+ spin_lock_irqsave(&uid_lock, flags);
+ list_for_each_entry(uid_entry, &uid_list, link) {
+ if (uid_entry->uid == uid) {
+ spin_unlock_irqrestore(&uid_lock, flags);
+ return uid_entry;
+ }
+ }
+ spin_unlock_irqrestore(&uid_lock, flags);
+
+ /* Create a new entry for tracking the specified uid. */
+ if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL)
+ return NULL;
+
+ new_uid->uid = uid;
+ /* Counters start at INT_MIN, so we can track 4GB of network traffic. */
+ atomic_set(&new_uid->tcp_rcv, INT_MIN);
+ atomic_set(&new_uid->tcp_snd, INT_MIN);
+ atomic_set(&new_uid->tcp_snd_pkt, INT_MIN);
+ atomic_set(&new_uid->tcp_rcv_pkt, INT_MIN);
+ atomic_set(&new_uid->udp_rcv, INT_MIN);
+ atomic_set(&new_uid->udp_snd, INT_MIN);
+ atomic_set(&new_uid->udp_snd_pkt, INT_MIN);
+ atomic_set(&new_uid->udp_rcv_pkt, INT_MIN);
+
+ /* Append the newly created uid stat struct to the list. */
+ spin_lock_irqsave(&uid_lock, flags);
+ list_add_tail(&new_uid->link, &uid_list);
+ spin_unlock_irqrestore(&uid_lock, flags);
+
+ sprintf(uid_s, "%d", uid);
+ proc_entry = proc_mkdir(uid_s, parent);
+
+ /* Keep reference to uid_stat so we know what uid to read stats from. */
+ create_proc_read_entry("tcp_snd", S_IRUGO, proc_entry, read_proc_entry,
+ (void *) &new_uid->tcp_snd);
+
+ create_proc_read_entry("tcp_rcv", S_IRUGO, proc_entry, read_proc_entry,
+ (void *) &new_uid->tcp_rcv);
+
+ create_proc_read_entry("tcp_snd_pkt", S_IRUGO, proc_entry, read_proc_entry,
+ (void *) &new_uid->tcp_snd_pkt);
+
+ create_proc_read_entry("tcp_rcv_pkt", S_IRUGO, proc_entry, read_proc_entry,
+ (void *) &new_uid->tcp_rcv_pkt);
+
+ create_proc_read_entry("udp_snd", S_IRUGO, proc_entry, read_proc_entry,
+ (void *) &new_uid->udp_snd);
+
+ create_proc_read_entry("udp_rcv", S_IRUGO, proc_entry, read_proc_entry,
+ (void *) &new_uid->udp_rcv);
+
+ create_proc_read_entry("udp_snd_pkt", S_IRUGO, proc_entry, read_proc_entry,
+ (void *) &new_uid->udp_snd_pkt);
+
+ create_proc_read_entry("udp_rcv_pkt", S_IRUGO, proc_entry, read_proc_entry,
+ (void *) &new_uid->udp_rcv_pkt);
+
+ return new_uid;
+}
+
+int uid_stat_tcp_snd(uid_t uid, int size) {
+ struct uid_stat *entry;
+ activity_stats_update();
+ if ((entry = get_uid_stat(uid)) == NULL) {
+ return -1;
+ }
+ atomic_add(size, &entry->tcp_snd);
+ atomic_inc(&entry->tcp_snd_pkt);
+ return 0;
+}
+
+int uid_stat_tcp_rcv(uid_t uid, int size) {
+ struct uid_stat *entry;
+ activity_stats_update();
+ if ((entry = get_uid_stat(uid)) == NULL) {
+ return -1;
+ }
+ atomic_add(size, &entry->tcp_rcv);
+ atomic_inc(&entry->tcp_rcv_pkt);
+ return 0;
+}
+
+int uid_stat_udp_snd(uid_t uid, int size) {
+ struct uid_stat *entry;
+ activity_stats_update();
+ if ((entry = get_uid_stat(uid)) == NULL) {
+ return -1;
+ }
+ atomic_add(size, &entry->udp_snd);
+ atomic_inc(&entry->udp_snd_pkt);
+ return 0;
+}
+
+int uid_stat_udp_rcv(uid_t uid, int size) {
+ struct uid_stat *entry;
+ activity_stats_update();
+ if ((entry = get_uid_stat(uid)) == NULL) {
+ return -1;
+ }
+ atomic_add(size, &entry->udp_rcv);
+ atomic_inc(&entry->udp_rcv_pkt);
+ return 0;
+}
+
+static int __init uid_stat_init(void)
+{
+ parent = proc_mkdir("uid_stat", NULL);
+ if (!parent) {
+ pr_err("uid_stat: failed to create proc entry\n");
+ return -1;
+ }
+ return 0;
+}
+
+__initcall(uid_stat_init);
diff --git a/drivers/misc/wl127x-rfkill.c b/drivers/misc/wl127x-rfkill.c
new file mode 100644
index 00000000000..f5b95152948
--- /dev/null
+++ b/drivers/misc/wl127x-rfkill.c
@@ -0,0 +1,121 @@
+/*
+ * Bluetooth TI wl127x rfkill power control via GPIO
+ *
+ * Copyright (C) 2009 Motorola, Inc.
+ * Copyright (C) 2008 Texas Instruments
+ * Initial code: Pavan Savoy <pavan.savoy@gmail.com> (wl127x_power.c)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/rfkill.h>
+#include <linux/platform_device.h>
+#include <linux/wl127x-rfkill.h>
+
+static int wl127x_rfkill_set_power(void *data, enum rfkill_state state)
+{
+ int nshutdown_gpio = (int) data;
+
+ switch (state) {
+ case RFKILL_STATE_UNBLOCKED:
+ gpio_set_value(nshutdown_gpio, 1);
+ break;
+ case RFKILL_STATE_SOFT_BLOCKED:
+ gpio_set_value(nshutdown_gpio, 0);
+ break;
+ default:
+ printk(KERN_ERR "invalid bluetooth rfkill state %d\n", state);
+ }
+ return 0;
+}
+
+static int wl127x_rfkill_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data;
+ enum rfkill_state default_state = RFKILL_STATE_SOFT_BLOCKED; /* off */
+
+ rc = gpio_request(pdata->nshutdown_gpio, "wl127x_nshutdown_gpio");
+ if (unlikely(rc))
+ return rc;
+
+ rc = gpio_direction_output(pdata->nshutdown_gpio, 0);
+ if (unlikely(rc))
+ return rc;
+
+ rfkill_set_default(RFKILL_TYPE_BLUETOOTH, default_state);
+ wl127x_rfkill_set_power(NULL, default_state);
+
+ pdata->rfkill = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH);
+ if (unlikely(!pdata->rfkill))
+ return -ENOMEM;
+
+ pdata->rfkill->name = "wl127x";
+ pdata->rfkill->state = default_state;
+ /* userspace cannot take exclusive control */
+ pdata->rfkill->user_claim_unsupported = 1;
+ pdata->rfkill->user_claim = 0;
+ pdata->rfkill->data = (void *) pdata->nshutdown_gpio;
+ pdata->rfkill->toggle_radio = wl127x_rfkill_set_power;
+
+ rc = rfkill_register(pdata->rfkill);
+
+ if (unlikely(rc))
+ rfkill_free(pdata->rfkill);
+
+ return 0;
+}
+
+static int wl127x_rfkill_remove(struct platform_device *pdev)
+{
+ struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data;
+
+ rfkill_unregister(pdata->rfkill);
+ rfkill_free(pdata->rfkill);
+ gpio_free(pdata->nshutdown_gpio);
+
+ return 0;
+}
+
+static struct platform_driver wl127x_rfkill_platform_driver = {
+ .probe = wl127x_rfkill_probe,
+ .remove = wl127x_rfkill_remove,
+ .driver = {
+ .name = "wl127x-rfkill",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init wl127x_rfkill_init(void)
+{
+ return platform_driver_register(&wl127x_rfkill_platform_driver);
+}
+
+static void __exit wl127x_rfkill_exit(void)
+{
+ platform_driver_unregister(&wl127x_rfkill_platform_driver);
+}
+
+module_init(wl127x_rfkill_init);
+module_exit(wl127x_rfkill_exit);
+
+MODULE_ALIAS("platform:wl127x");
+MODULE_DESCRIPTION("wl127x-rfkill");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index 3f2a912659a..cc9d7e77c25 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -14,6 +14,23 @@ config MMC_BLOCK
mount the filesystem. Almost everyone wishing MMC support
should say Y or M here.
+config MMC_BLOCK_MINORS
+ int "Number of minors per block device"
+ range 4 256
+ default 8
+ help
+ Number of minors per block device. One is needed for every
+ partition on the disk (plus one for the whole disk).
+
+ Number of total MMC minors available is 256, so your number
+ of supported block devices will be limited to 256 divided
+ by this number.
+
+ Default is 8 to be backwards compatible with previous
+ hardwired device numbering.
+
+ If unsure, say 8 here.
+
config MMC_BLOCK_BOUNCE
bool "Use bounce buffer for simple hosts"
depends on MMC_BLOCK
@@ -32,6 +49,15 @@ config MMC_BLOCK_BOUNCE
If unsure, say Y here.
+config MMC_BLOCK_DEFERRED_RESUME
+ bool "Deferr MMC layer resume until I/O is requested"
+ depends on MMC_BLOCK
+ default n
+ help
+ Say Y here to enable deferred MMC resume until I/O
+ is requested. This will reduce overall resume latency and
+ save power when theres an SD card inserted but not being used.
+
config SDIO_UART
tristate "SDIO UART/GPS class support"
help
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index cb9fbc83b09..ed17a3d1499 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -43,14 +43,27 @@
#include "queue.h"
MODULE_ALIAS("mmc:block");
+#ifdef MODULE_PARAM_PREFIX
+#undef MODULE_PARAM_PREFIX
+#endif
+#define MODULE_PARAM_PREFIX "mmcblk."
+
+static DEFINE_MUTEX(block_mutex);
/*
- * max 8 partitions per card
+ * The defaults come from config options but can be overriden by module
+ * or bootarg options.
*/
-#define MMC_SHIFT 3
-#define MMC_NUM_MINORS (256 >> MMC_SHIFT)
+static int perdev_minors = CONFIG_MMC_BLOCK_MINORS;
-static DECLARE_BITMAP(dev_use, MMC_NUM_MINORS);
+/*
+ * We've only got one major, so number of mmcblk devices is
+ * limited to 256 / number of minors per device.
+ */
+static int max_devices;
+
+/* 256 minors, so at most 256 separate devices */
+static DECLARE_BITMAP(dev_use, 256);
/*
* There is one mmc_blk_data per slot.
@@ -66,6 +79,9 @@ struct mmc_blk_data {
static DEFINE_MUTEX(open_lock);
+module_param(perdev_minors, int, 0444);
+MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
+
static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
{
struct mmc_blk_data *md;
@@ -87,10 +103,10 @@ static void mmc_blk_put(struct mmc_blk_data *md)
md->usage--;
if (md->usage == 0) {
int devmaj = MAJOR(disk_devt(md->disk));
- int devidx = MINOR(disk_devt(md->disk)) >> MMC_SHIFT;
+ int devidx = MINOR(disk_devt(md->disk)) / perdev_minors;
if (!devmaj)
- devidx = md->disk->first_minor >> MMC_SHIFT;
+ devidx = md->disk->first_minor / perdev_minors;
blk_cleanup_queue(md->queue.queue);
@@ -242,6 +258,24 @@ static u32 get_card_status(struct mmc_card *card, struct request *req)
return cmd.resp[0];
}
+static int
+mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
+{
+ int err;
+
+ mmc_claim_host(card->host);
+ err = mmc_set_blocklen(card, 512);
+ mmc_release_host(card->host);
+
+ if (err) {
+ printk(KERN_ERR "%s: unable to set block size to 512: %d\n",
+ md->disk->disk_name, err);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
@@ -249,6 +283,13 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
struct mmc_blk_request brq;
int ret = 1, disable_multi = 0;
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ if (mmc_bus_needs_resume(card->host)) {
+ mmc_resume_bus(card->host);
+ mmc_blk_set_blksize(md, card);
+ }
+#endif
+
mmc_claim_host(card->host);
do {
@@ -299,7 +340,6 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
readcmd = MMC_READ_SINGLE_BLOCK;
writecmd = MMC_WRITE_BLOCK;
}
-
if (rq_data_dir(req) == READ) {
brq.cmd.opcode = readcmd;
brq.data.flags |= MMC_DATA_READ;
@@ -352,6 +392,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
continue;
}
status = get_card_status(card, req);
+ } else if (disable_multi == 1) {
+ disable_multi = 0;
}
if (brq.cmd.error) {
@@ -482,8 +524,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
struct mmc_blk_data *md;
int devidx, ret;
- devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);
- if (devidx >= MMC_NUM_MINORS)
+ devidx = find_first_zero_bit(dev_use, max_devices);
+ if (devidx >= max_devices)
return ERR_PTR(-ENOSPC);
__set_bit(devidx, dev_use);
@@ -500,7 +542,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
*/
md->read_only = mmc_blk_readonly(card);
- md->disk = alloc_disk(1 << MMC_SHIFT);
+ md->disk = alloc_disk(perdev_minors);
if (md->disk == NULL) {
ret = -ENOMEM;
goto err_kfree;
@@ -517,11 +559,12 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
md->queue.data = md;
md->disk->major = MMC_BLOCK_MAJOR;
- md->disk->first_minor = devidx << MMC_SHIFT;
+ md->disk->first_minor = devidx * perdev_minors;
md->disk->fops = &mmc_bdops;
md->disk->private_data = md;
md->disk->queue = md->queue.queue;
md->disk->driverfs_dev = &card->dev;
+ md->disk->flags = GENHD_FL_EXT_DEVT;
/*
* As discussed on lkml, GENHD_FL_REMOVABLE should:
@@ -563,37 +606,10 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
return ERR_PTR(ret);
}
-static int
-mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
-{
- struct mmc_command cmd;
- int err;
-
- /* Block-addressed cards ignore MMC_SET_BLOCKLEN. */
- if (mmc_card_blockaddr(card))
- return 0;
-
- mmc_claim_host(card->host);
- cmd.opcode = MMC_SET_BLOCKLEN;
- cmd.arg = 512;
- cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
- err = mmc_wait_for_cmd(card->host, &cmd, 5);
- mmc_release_host(card->host);
-
- if (err) {
- printk(KERN_ERR "%s: unable to set block size to %d: %d\n",
- md->disk->disk_name, cmd.arg, err);
- return -EINVAL;
- }
-
- return 0;
-}
-
static int mmc_blk_probe(struct mmc_card *card)
{
struct mmc_blk_data *md;
int err;
-
char cap_str[10];
/*
@@ -617,6 +633,9 @@ static int mmc_blk_probe(struct mmc_card *card)
cap_str, md->read_only ? "(ro)" : "");
mmc_set_drvdata(card, md);
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ mmc_set_bus_resume_policy(card->host, 1);
+#endif
add_disk(md->disk);
return 0;
@@ -641,6 +660,9 @@ static void mmc_blk_remove(struct mmc_card *card)
mmc_blk_put(md);
}
mmc_set_drvdata(card, NULL);
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ mmc_set_bus_resume_policy(card->host, 0);
+#endif
}
#ifdef CONFIG_PM
@@ -659,7 +681,9 @@ static int mmc_blk_resume(struct mmc_card *card)
struct mmc_blk_data *md = mmc_get_drvdata(card);
if (md) {
+#ifndef CONFIG_MMC_BLOCK_DEFERRED_RESUME
mmc_blk_set_blksize(md, card);
+#endif
mmc_queue_resume(&md->queue);
}
return 0;
@@ -683,6 +707,11 @@ static int __init mmc_blk_init(void)
{
int res;
+ if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
+ pr_info("mmcblk: using %d minors per device\n", perdev_minors);
+
+ max_devices = 256 / perdev_minors;
+
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
if (res)
goto out;
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 445d7db2277..785b4822a0a 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -44,17 +44,7 @@ struct mmc_test_card {
*/
static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
{
- struct mmc_command cmd;
- int ret;
-
- cmd.opcode = MMC_SET_BLOCKLEN;
- cmd.arg = size;
- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
- ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_set_blocklen(test->card, size);
}
/*
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index d6ded247d94..bd7da55cce4 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -132,7 +132,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
#ifdef CONFIG_MMC_BLOCK_BOUNCE
- if (host->max_hw_segs == 1) {
+ if (host->max_segs == 1) {
unsigned int bouncesz;
bouncesz = MMC_QUEUE_BOUNCESZ;
@@ -182,16 +182,16 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
blk_queue_bounce_limit(mq->queue, limit);
blk_queue_max_hw_sectors(mq->queue,
min(host->max_blk_count, host->max_req_size / 512));
- blk_queue_max_segments(mq->queue, host->max_hw_segs);
+ blk_queue_max_segments(mq->queue, host->max_segs);
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
mq->sg = kmalloc(sizeof(struct scatterlist) *
- host->max_phys_segs, GFP_KERNEL);
+ host->max_segs, GFP_KERNEL);
if (!mq->sg) {
ret = -ENOMEM;
goto cleanup_queue;
}
- sg_init_table(mq->sg, host->max_phys_segs);
+ sg_init_table(mq->sg, host->max_segs);
}
init_MUTEX(&mq->thread_sem);
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index bb22ffd76ef..0eba6658233 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -16,3 +16,20 @@ config MMC_UNSAFE_RESUME
This option sets a default which can be overridden by the
module parameter "removable=0" or "removable=1".
+
+config MMC_EMBEDDED_SDIO
+ boolean "MMC embedded SDIO device support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ help
+ If you say Y here, support will be added for embedded SDIO
+ devices which do not contain the necessary enumeration
+ support in hardware to be properly detected.
+
+config MMC_PARANOID_SD_INIT
+ bool "Enable paranoid SD card initialization (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ help
+ If you say Y here, the MMC layer will be extra paranoid
+ about re-trying SD init requests. This can be a useful
+ work-around for buggy controllers and hardware. Enable
+ if you are experiencing issues with SD detection.
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 49d9dcaeca4..58adef173ba 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -245,14 +245,16 @@ int mmc_add_card(struct mmc_card *card)
}
if (mmc_host_is_spi(card->host)) {
- printk(KERN_INFO "%s: new %s%s card on SPI\n",
+ printk(KERN_INFO "%s: new %s%s%s card on SPI\n",
mmc_hostname(card->host),
mmc_card_highspeed(card) ? "high speed " : "",
+ mmc_card_ddr_mode(card) ? "DDR " : "",
type);
} else {
- printk(KERN_INFO "%s: new %s%s card at address %04x\n",
+ printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
mmc_hostname(card->host),
mmc_card_highspeed(card) ? "high speed " : "",
+ mmc_card_ddr_mode(card) ? "DDR " : "",
type, card->rca);
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 83e7543b55d..221e0e76384 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -22,6 +22,7 @@
#include <linux/scatterlist.h>
#include <linux/log2.h>
#include <linux/regulator/consumer.h>
+#include <linux/wakelock.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -386,6 +387,8 @@ int mmc_host_enable(struct mmc_host *host)
if (host->ops->enable) {
int err;
+ wake_lock(&host->wakelock);
+
host->en_dis_recurs = 1;
err = host->ops->enable(host);
host->en_dis_recurs = 0;
@@ -393,6 +396,7 @@ int mmc_host_enable(struct mmc_host *host)
if (err) {
pr_debug("%s: enable error %d\n",
mmc_hostname(host), err);
+ wake_unlock(&host->wakelock);
return err;
}
}
@@ -420,6 +424,8 @@ static int mmc_host_do_disable(struct mmc_host *host, int lazy)
mmc_schedule_delayed_work(&host->disable, delay);
}
+ if (err == 0)
+ wake_unlock(&host->wakelock);
}
host->enabled = 0;
return 0;
@@ -522,7 +528,7 @@ int mmc_try_claim_host(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_try_claim_host);
-static void mmc_do_release_host(struct mmc_host *host)
+void mmc_do_release_host(struct mmc_host *host)
{
unsigned long flags;
@@ -537,6 +543,7 @@ static void mmc_do_release_host(struct mmc_host *host)
wake_up(&host->wq);
}
}
+EXPORT_SYMBOL(mmc_do_release_host);
void mmc_host_deeper_disable(struct work_struct *work)
{
@@ -650,14 +657,24 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
}
/*
- * Change data bus width of a host.
+ * Change data bus width and DDR mode of a host.
*/
-void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
+void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
+ unsigned int ddr)
{
host->ios.bus_width = width;
+ host->ios.ddr = ddr;
mmc_set_ios(host);
}
+/*
+ * Change data bus width of a host.
+ */
+void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
+{
+ mmc_set_bus_width_ddr(host, width, MMC_SDR_MODE);
+}
+
/**
* mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number
* @vdd: voltage (mV)
@@ -771,8 +788,9 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask);
/**
* mmc_regulator_set_ocr - set regulator to match host->ios voltage
- * @vdd_bit: zero for power off, else a bit number (host->ios.vdd)
+ * @mmc: the host to regulate
* @supply: regulator to use
+ * @vdd_bit: zero for power off, else a bit number (host->ios.vdd)
*
* Returns zero on success, else negative errno.
*
@@ -780,15 +798,12 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask);
* a particular supply voltage. This would normally be called from the
* set_ios() method.
*/
-int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit)
+int mmc_regulator_set_ocr(struct mmc_host *mmc,
+ struct regulator *supply,
+ unsigned short vdd_bit)
{
int result = 0;
int min_uV, max_uV;
- int enabled;
-
- enabled = regulator_is_enabled(supply);
- if (enabled < 0)
- return enabled;
if (vdd_bit) {
int tmp;
@@ -819,17 +834,25 @@ int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit)
else
result = 0;
- if (result == 0 && !enabled)
+ if (result == 0 && !mmc->regulator_enabled) {
result = regulator_enable(supply);
- } else if (enabled) {
+ if (!result)
+ mmc->regulator_enabled = true;
+ }
+ } else if (mmc->regulator_enabled) {
result = regulator_disable(supply);
+ if (result == 0)
+ mmc->regulator_enabled = false;
}
+ if (result)
+ dev_err(mmc_dev(mmc),
+ "could not set regulator OCR (%d)\n", result);
return result;
}
EXPORT_SYMBOL(mmc_regulator_set_ocr);
-#endif
+#endif /* CONFIG_REGULATOR */
/*
* Mask off any voltages we don't support and select
@@ -907,12 +930,7 @@ static void mmc_power_up(struct mmc_host *host)
*/
mmc_delay(10);
- if (host->f_min > 400000) {
- pr_warning("%s: Minimum clock frequency too high for "
- "identification mode\n", mmc_hostname(host));
- host->ios.clock = host->f_min;
- } else
- host->ios.clock = 400000;
+ host->ios.clock = host->f_min;
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host);
@@ -928,6 +946,13 @@ static void mmc_power_off(struct mmc_host *host)
{
host->ios.clock = 0;
host->ios.vdd = 0;
+
+ /*
+ * Reset ocr mask to be the highest possible voltage supported for
+ * this mmc host. This value will be used at next power up.
+ */
+ host->ocr = 1 << (fls(host->ocr_avail) - 1);
+
if (!mmc_host_is_spi(host)) {
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.chip_select = MMC_CS_DONTCARE;
@@ -977,6 +1002,36 @@ static inline void mmc_bus_put(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags);
}
+int mmc_resume_bus(struct mmc_host *host)
+{
+ unsigned long flags;
+
+ if (!mmc_bus_needs_resume(host))
+ return -EINVAL;
+
+ printk("%s: Starting deferred resume\n", mmc_hostname(host));
+ spin_lock_irqsave(&host->lock, flags);
+ host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME;
+ host->rescan_disable = 0;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ mmc_bus_get(host);
+ if (host->bus_ops && !host->bus_dead) {
+ mmc_power_up(host);
+ BUG_ON(!host->bus_ops->resume);
+ host->bus_ops->resume(host);
+ }
+
+ if (host->bus_ops->detect && !host->bus_dead)
+ host->bus_ops->detect(host);
+
+ mmc_bus_put(host);
+ printk("%s: Deferred resume completed\n", mmc_hostname(host));
+ return 0;
+}
+
+EXPORT_SYMBOL(mmc_resume_bus);
+
/*
* Assign a mmc bus handler to a host. Only one bus handler may control a
* host at any given time.
@@ -1050,6 +1105,20 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
EXPORT_SYMBOL(mmc_detect_change);
+int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
+{
+ struct mmc_command cmd;
+
+ if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card))
+ return 0;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_SET_BLOCKLEN;
+ cmd.arg = blocklen;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ return mmc_wait_for_cmd(card->host, &cmd, 5);
+}
+EXPORT_SYMBOL(mmc_set_blocklen);
void mmc_rescan(struct work_struct *work)
{
@@ -1058,6 +1127,7 @@ void mmc_rescan(struct work_struct *work)
u32 ocr;
int err;
unsigned long flags;
+ int extend_wakelock = 0;
spin_lock_irqsave(&host->lock, flags);
@@ -1075,6 +1145,12 @@ void mmc_rescan(struct work_struct *work)
if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead)
host->bus_ops->detect(host);
+ /* If the card was removed the bus will be marked
+ * as dead - extend the wakelock so userspace
+ * can respond */
+ if (host->bus_dead)
+ extend_wakelock = 1;
+
mmc_bus_put(host);
@@ -1112,6 +1188,7 @@ void mmc_rescan(struct work_struct *work)
if (!err) {
if (mmc_attach_sdio(host, ocr))
mmc_power_off(host);
+ extend_wakelock = 1;
goto out;
}
@@ -1122,6 +1199,7 @@ void mmc_rescan(struct work_struct *work)
if (!err) {
if (mmc_attach_sd(host, ocr))
mmc_power_off(host);
+ extend_wakelock = 1;
goto out;
}
@@ -1132,6 +1210,7 @@ void mmc_rescan(struct work_struct *work)
if (!err) {
if (mmc_attach_mmc(host, ocr))
mmc_power_off(host);
+ extend_wakelock = 1;
goto out;
}
@@ -1139,6 +1218,9 @@ void mmc_rescan(struct work_struct *work)
mmc_power_off(host);
out:
+ if (extend_wakelock)
+ wake_lock_timeout(&host->wakelock, HZ / 2);
+
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
}
@@ -1268,6 +1350,9 @@ int mmc_suspend_host(struct mmc_host *host)
{
int err = 0;
+ if (mmc_bus_needs_resume(host))
+ return 0;
+
if (host->caps & MMC_CAP_DISABLE)
cancel_delayed_work(&host->disable);
cancel_delayed_work(&host->detect);
@@ -1275,7 +1360,8 @@ int mmc_suspend_host(struct mmc_host *host)
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
- if (host->bus_ops->suspend)
+ if (host->bus_ops->suspend &&
+ (host->bus_resume_flags & MMC_NEEDS_UNSAFE_RESUME))
err = host->bus_ops->suspend(host);
}
mmc_bus_put(host);
@@ -1297,6 +1383,12 @@ int mmc_resume_host(struct mmc_host *host)
int err = 0;
mmc_bus_get(host);
+ if (mmc_bus_manual_resume(host)) {
+ host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME;
+ mmc_bus_put(host);
+ return 0;
+ }
+
if (host->bus_ops && !host->bus_dead) {
if (!(host->pm_flags & MMC_PM_KEEP_POWER)) {
mmc_power_up(host);
@@ -1334,6 +1426,10 @@ int mmc_pm_notify(struct notifier_block *notify_block,
case PM_SUSPEND_PREPARE:
spin_lock_irqsave(&host->lock, flags);
+ if (mmc_bus_needs_resume(host)) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ break;
+ }
host->rescan_disable = 1;
spin_unlock_irqrestore(&host->lock, flags);
cancel_delayed_work_sync(&host->detect);
@@ -1355,6 +1451,10 @@ int mmc_pm_notify(struct notifier_block *notify_block,
case PM_POST_HIBERNATION:
spin_lock_irqsave(&host->lock, flags);
+ if (mmc_bus_manual_resume(host)) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ break;
+ }
host->rescan_disable = 0;
spin_unlock_irqrestore(&host->lock, flags);
mmc_detect_change(host, 0);
@@ -1365,6 +1465,22 @@ int mmc_pm_notify(struct notifier_block *notify_block,
}
#endif
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+void mmc_set_embedded_sdio_data(struct mmc_host *host,
+ struct sdio_cis *cis,
+ struct sdio_cccr *cccr,
+ struct sdio_embedded_func *funcs,
+ int num_funcs)
+{
+ host->embedded_sdio_data.cis = cis;
+ host->embedded_sdio_data.cccr = cccr;
+ host->embedded_sdio_data.funcs = funcs;
+ host->embedded_sdio_data.num_funcs = num_funcs;
+}
+
+EXPORT_SYMBOL(mmc_set_embedded_sdio_data);
+#endif
+
static int __init mmc_init(void)
{
int ret;
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index a811c52a165..eccd0f4e52c 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -33,6 +33,8 @@ void mmc_set_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
+void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
+ unsigned int ddr);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index d80cfdc8edd..53351f29860 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -90,12 +90,15 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->pm_notify.notifier_call = mmc_pm_notify;
#endif
+ snprintf(host->wakelock_name, sizeof(host->wakelock_name),
+ "mmc%d_delay_work", host->index);
+ wake_lock_init(&host->wakelock, WAKE_LOCK_SUSPEND, host->wakelock_name);
+
/*
* By default, hosts do not support SGIO or large requests.
* They have to set these according to their abilities.
*/
- host->max_hw_segs = 1;
- host->max_phys_segs = 1;
+ host->max_segs = 1;
host->max_seg_size = PAGE_CACHE_SIZE;
host->max_req_size = PAGE_CACHE_SIZE;
@@ -137,7 +140,8 @@ int mmc_add_host(struct mmc_host *host)
#endif
mmc_start_host(host);
- register_pm_notifier(&host->pm_notify);
+ if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
+ register_pm_notifier(&host->pm_notify);
return 0;
}
@@ -154,7 +158,9 @@ EXPORT_SYMBOL(mmc_add_host);
*/
void mmc_remove_host(struct mmc_host *host)
{
- unregister_pm_notifier(&host->pm_notify);
+ if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
+ unregister_pm_notifier(&host->pm_notify);
+
mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS
@@ -176,6 +182,7 @@ EXPORT_SYMBOL(mmc_remove_host);
*/
void mmc_free_host(struct mmc_host *host)
{
+ wake_lock_destroy(&host->wakelock);
spin_lock(&mmc_host_lock);
idr_remove(&mmc_host_idr, host->index);
spin_unlock(&mmc_host_lock);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 89f7a25b7ac..eddba13ced0 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -114,17 +114,18 @@ static int mmc_decode_cid(struct mmc_card *card)
static int mmc_decode_csd(struct mmc_card *card)
{
struct mmc_csd *csd = &card->csd;
- unsigned int e, m, csd_struct;
+ unsigned int e, m;
u32 *resp = card->raw_csd;
/*
* We only understand CSD structure v1.1 and v1.2.
* v1.2 has extra information in bits 15, 11 and 10.
+ * We also support eMMC v4.4 & v4.41.
*/
- csd_struct = UNSTUFF_BITS(resp, 126, 2);
- if (csd_struct != 1 && csd_struct != 2) {
+ csd->structure = UNSTUFF_BITS(resp, 126, 2);
+ if (csd->structure == 0) {
printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
- mmc_hostname(card->host), csd_struct);
+ mmc_hostname(card->host), csd->structure);
return -EINVAL;
}
@@ -207,11 +208,22 @@ static int mmc_read_ext_csd(struct mmc_card *card)
goto out;
}
+ /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
+ if (card->csd.structure == 3) {
+ int ext_csd_struct = ext_csd[EXT_CSD_STRUCTURE];
+ if (ext_csd_struct > 2) {
+ printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
+ "version %d\n", mmc_hostname(card->host),
+ ext_csd_struct);
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
if (card->ext_csd.rev > 5) {
- printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
- "version %d\n", mmc_hostname(card->host),
- card->ext_csd.rev);
+ printk(KERN_ERR "%s: unrecognised EXT_CSD revision %d\n",
+ mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL;
goto out;
}
@@ -222,11 +234,28 @@ static int mmc_read_ext_csd(struct mmc_card *card)
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
- if (card->ext_csd.sectors)
+
+ /* Cards with density > 2GiB are sector addressed */
+ if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512)
mmc_card_set_blockaddr(card);
}
switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
+ case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
+ EXT_CSD_CARD_TYPE_26:
+ card->ext_csd.hs_max_dtr = 52000000;
+ card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_52;
+ break;
+ case EXT_CSD_CARD_TYPE_DDR_1_2V | EXT_CSD_CARD_TYPE_52 |
+ EXT_CSD_CARD_TYPE_26:
+ card->ext_csd.hs_max_dtr = 52000000;
+ card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_1_2V;
+ break;
+ case EXT_CSD_CARD_TYPE_DDR_1_8V | EXT_CSD_CARD_TYPE_52 |
+ EXT_CSD_CARD_TYPE_26:
+ card->ext_csd.hs_max_dtr = 52000000;
+ card->ext_csd.card_type = EXT_CSD_CARD_TYPE_DDR_1_8V;
+ break;
case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
card->ext_csd.hs_max_dtr = 52000000;
break;
@@ -303,7 +332,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
struct mmc_card *card;
- int err;
+ int err, ddr = 0;
u32 cid[4];
unsigned int max_dtr;
@@ -444,17 +473,35 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
mmc_set_clock(host, max_dtr);
/*
- * Activate wide bus (if supported).
+ * Indicate DDR mode (if supported).
+ */
+ if (mmc_card_highspeed(card)) {
+ if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
+ && (host->caps & (MMC_CAP_1_8V_DDR)))
+ ddr = MMC_1_8V_DDR_MODE;
+ else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
+ && (host->caps & (MMC_CAP_1_2V_DDR)))
+ ddr = MMC_1_2V_DDR_MODE;
+ }
+
+ /*
+ * Activate wide bus and DDR (if supported).
*/
if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
unsigned ext_csd_bit, bus_width;
if (host->caps & MMC_CAP_8_BIT_DATA) {
- ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
+ if (ddr)
+ ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8;
+ else
+ ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
bus_width = MMC_BUS_WIDTH_8;
} else {
- ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
+ if (ddr)
+ ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4;
+ else
+ ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
bus_width = MMC_BUS_WIDTH_4;
}
@@ -465,12 +512,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
goto free_card;
if (err) {
- printk(KERN_WARNING "%s: switch to bus width %d "
+ printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
"failed\n", mmc_hostname(card->host),
- 1 << bus_width);
+ 1 << bus_width, ddr);
err = 0;
} else {
- mmc_set_bus_width(card->host, bus_width);
+ if (ddr)
+ mmc_card_set_ddr_mode(card);
+ else
+ ddr = MMC_SDR_MODE;
+
+ mmc_set_bus_width_ddr(card->host, bus_width, ddr);
}
}
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 326447c9ede..91ab05aa1d4 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -202,6 +202,7 @@ int mmc_set_relative_addr(struct mmc_card *card)
{
int err;
struct mmc_command cmd;
+ u32 status;
BUG_ON(!card);
BUG_ON(!card->host);
@@ -216,6 +217,28 @@ int mmc_set_relative_addr(struct mmc_card *card)
if (err)
return err;
+ /* Must check status to be sure of no errors */
+ do {
+ err = mmc_send_status(card, &status);
+ if (err)
+ return err;
+ if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ break;
+ if (mmc_host_is_spi(card->host))
+ break;
+ } while (R1_CURRENT_STATE(status) == 7);
+
+ if (mmc_host_is_spi(card->host)) {
+ if (status & R1_SPI_ILLEGAL_COMMAND)
+ return -EBADMSG;
+ } else {
+ if (status & 0xFDFFA000)
+ printk(KERN_WARNING "%s: unexpected status %#x after "
+ "switch", mmc_hostname(card->host), status);
+ if (status & R1_SWITCH_ERROR)
+ return -EBADMSG;
+ }
+
return 0;
}
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 5eac21df480..f5e29edbec0 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -337,7 +337,9 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
int err;
u32 cid[4];
unsigned int max_dtr;
-
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries;
+#endif
BUG_ON(!host);
WARN_ON(!host->claimed);
@@ -440,11 +442,29 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
err = mmc_decode_scr(card);
if (err < 0)
goto free_card;
-
/*
* Fetch switch information from card.
*/
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ for (retries = 1; retries <= 3; retries++) {
+ err = mmc_read_switch(card);
+ if (!err) {
+ if (retries > 1) {
+ printk(KERN_WARNING
+ "%s: recovered\n",
+ mmc_hostname(host));
+ }
+ break;
+ } else {
+ printk(KERN_WARNING
+ "%s: read switch failed (attempt %d)\n",
+ mmc_hostname(host), retries);
+ }
+ }
+#else
err = mmc_read_switch(card);
+#endif
+
if (err)
goto free_card;
}
@@ -539,18 +559,36 @@ static void mmc_sd_remove(struct mmc_host *host)
*/
static void mmc_sd_detect(struct mmc_host *host)
{
- int err;
+ int err = 0;
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries = 5;
+#endif
BUG_ON(!host);
BUG_ON(!host->card);
-
+
mmc_claim_host(host);
/*
* Just check if our card has been removed.
*/
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ while(retries) {
+ err = mmc_send_status(host->card, NULL);
+ if (err) {
+ retries--;
+ udelay(5);
+ continue;
+ }
+ break;
+ }
+ if (!retries) {
+ printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n",
+ __func__, mmc_hostname(host), err);
+ }
+#else
err = mmc_send_status(host->card, NULL);
-
+#endif
mmc_release_host(host);
if (err) {
@@ -588,12 +626,31 @@ static int mmc_sd_suspend(struct mmc_host *host)
static int mmc_sd_resume(struct mmc_host *host)
{
int err;
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries;
+#endif
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ retries = 5;
+ while (retries) {
+ err = mmc_sd_init_card(host, host->ocr, host->card);
+
+ if (err) {
+ printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n",
+ mmc_hostname(host), err, retries);
+ mdelay(5);
+ retries--;
+ continue;
+ }
+ break;
+ }
+#else
err = mmc_sd_init_card(host, host->ocr, host->card);
+#endif
mmc_release_host(host);
return err;
@@ -640,6 +697,9 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host)
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{
int err;
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ int retries;
+#endif
BUG_ON(!host);
WARN_ON(!host->claimed);
@@ -688,9 +748,27 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
/*
* Detect and init the card.
*/
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
+ retries = 5;
+ while (retries) {
+ err = mmc_sd_init_card(host, host->ocr, NULL);
+ if (err) {
+ retries--;
+ continue;
+ }
+ break;
+ }
+
+ if (!retries) {
+ printk(KERN_ERR "%s: mmc_sd_init_card() failure (err = %d)\n",
+ mmc_hostname(host), err);
+ goto err;
+ }
+#else
err = mmc_sd_init_card(host, host->ocr, NULL);
if (err)
goto err;
+#endif
mmc_release_host(host);
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index b9dee28ee7d..04e68fe3bcc 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -24,6 +24,10 @@
#include "sdio_ops.h"
#include "sdio_cis.h"
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#include <linux/mmc/sdio_ids.h>
+#endif
+
static int sdio_read_fbr(struct sdio_func *func)
{
int ret;
@@ -309,6 +313,13 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
if (err)
goto remove;
+ /*
+ * Update oldcard with the new RCA received from the
+ * SDIO device.
+ */
+ if (oldcard)
+ oldcard->rca = card->rca;
+
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
@@ -321,19 +332,35 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
goto remove;
}
- /*
- * Read the common registers.
- */
- err = sdio_read_cccr(card);
- if (err)
- goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.cccr)
+ memcpy(&card->cccr, host->embedded_sdio_data.cccr, sizeof(struct sdio_cccr));
+ else {
+#endif
+ /*
+ * Read the common registers.
+ */
+ err = sdio_read_cccr(card);
+ if (err)
+ goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
- /*
- * Read the common CIS tuples.
- */
- err = sdio_read_common_cis(card);
- if (err)
- goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.cis)
+ memcpy(&card->cis, host->embedded_sdio_data.cis, sizeof(struct sdio_cis));
+ else {
+#endif
+ /*
+ * Read the common CIS tuples.
+ */
+ err = sdio_read_common_cis(card);
+ if (err)
+ goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
if (oldcard) {
int same = (card->cis.vendor == oldcard->cis.vendor &&
@@ -344,7 +371,6 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
goto err;
}
card = oldcard;
- return 0;
}
/*
@@ -515,11 +541,26 @@ static int mmc_sdio_resume(struct mmc_host *host)
return err;
}
+static void mmc_sdio_power_restore(struct mmc_host *host)
+{
+ int err;
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+ err = mmc_sdio_init_card(host, host->ocr, host->card,
+ (host->pm_flags & MMC_PM_KEEP_POWER));
+ if (!err && host->sdio_irqs)
+ mmc_signal_sdio_irq(host);
+ mmc_release_host(host);
+}
+
static const struct mmc_bus_ops mmc_sdio_ops = {
.remove = mmc_sdio_remove,
.detect = mmc_sdio_detect,
.suspend = mmc_sdio_suspend,
.resume = mmc_sdio_resume,
+ .power_restore = mmc_sdio_power_restore,
};
@@ -573,6 +614,11 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
funcs = (ocr & 0x70000000) >> 28;
card->sdio_funcs = 0;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.funcs)
+ card->sdio_funcs = funcs = host->embedded_sdio_data.num_funcs;
+#endif
+
/*
* If needed, disconnect card detection pull-up resistor.
*/
@@ -584,9 +630,27 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
* Initialize (but don't add) all present functions.
*/
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
- err = sdio_init_func(host->card, i + 1);
- if (err)
- goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.funcs) {
+ struct sdio_func *tmp;
+
+ tmp = sdio_alloc_func(host->card);
+ if (IS_ERR(tmp))
+ goto remove;
+ tmp->num = (i + 1);
+ card->sdio_func[i] = tmp;
+ tmp->class = host->embedded_sdio_data.funcs[i].f_class;
+ tmp->max_blksize = host->embedded_sdio_data.funcs[i].f_maxblksize;
+ tmp->vendor = card->cis.vendor;
+ tmp->device = card->cis.device;
+ } else {
+#endif
+ err = sdio_init_func(host->card, i + 1);
+ if (err)
+ goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
}
mmc_release_host(host);
@@ -628,3 +692,82 @@ err:
return err;
}
+int sdio_reset_comm(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ u32 ocr;
+ int err;
+
+ printk("%s():\n", __func__);
+ mmc_claim_host(host);
+
+ mmc_go_idle(host);
+
+ mmc_set_clock(host, host->f_min);
+
+ err = mmc_send_io_op_cond(host, 0, &ocr);
+ if (err)
+ goto err;
+
+ host->ocr = mmc_select_voltage(host, ocr);
+ if (!host->ocr) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ err = mmc_send_io_op_cond(host, host->ocr, &ocr);
+ if (err)
+ goto err;
+
+ if (mmc_host_is_spi(host)) {
+ err = mmc_spi_set_crc(host, use_spi_crc);
+ if (err)
+ goto err;
+ }
+
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_send_relative_addr(host, &card->rca);
+ if (err)
+ goto err;
+ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+ }
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_select_card(card);
+ if (err)
+ goto err;
+ }
+
+ /*
+ * Switch to high-speed (if supported).
+ */
+ err = sdio_enable_hs(card);
+ if (err)
+ goto err;
+
+ /*
+ * Change to the card's maximum speed.
+ */
+ if (mmc_card_highspeed(card)) {
+ /*
+ * The SDIO specification doesn't mention how
+ * the CIS transfer speed register relates to
+ * high-speed, but it seems that 50 MHz is
+ * mandatory.
+ */
+ mmc_set_clock(host, 50000000);
+ } else {
+ mmc_set_clock(host, card->cis.max_dtr);
+ }
+
+ err = sdio_enable_wide(card);
+ if (err)
+ goto err;
+ mmc_release_host(host);
+ return 0;
+err:
+ printk("%s: Error resetting SDIO communications (%d)\n",
+ mmc_hostname(host), err);
+ mmc_release_host(host);
+ return err;
+}
+EXPORT_SYMBOL(sdio_reset_comm);
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 4a890dcb95a..f59f9255448 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -21,6 +21,10 @@
#include "sdio_cis.h"
#include "sdio_bus.h"
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#include <linux/mmc/host.h>
+#endif
+
/* show configuration fields */
#define sdio_config_attr(field, format_string) \
static ssize_t \
@@ -200,7 +204,14 @@ static void sdio_release_func(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
- sdio_free_func_cis(func);
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ /*
+ * If this device is embedded then we never allocated
+ * cis tables for this func
+ */
+ if (!func->card->host->embedded_sdio_data.funcs)
+#endif
+ sdio_free_func_cis(func);
if (func->info)
kfree(func->info);
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index 0f687cdeb06..a2767401182 100644..100755
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -338,6 +338,10 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
while (remainder > 0) {
size = min(remainder, sdio_max_byte_size(func));
+ // Some of host controllers does not support non-power-of-two block sizes
+ if (func->card->host->caps & MMC_CAP_POWER_OF_TWO_BLKSIZE)
+ size = 1 << (fls(size) - 1);
+
ret = mmc_io_rw_extended(func->card, write, func->num, addr,
incr_addr, buf, 1, size);
if (ret)
@@ -383,6 +387,39 @@ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret)
EXPORT_SYMBOL_GPL(sdio_readb);
/**
+ * sdio_readb_ext - read a single byte from a SDIO function
+ * @func: SDIO function to access
+ * @addr: address to read
+ * @err_ret: optional status value from transfer
+ * @in: value to add to argument
+ *
+ * Reads a single byte from the address space of a given SDIO
+ * function. If there is a problem reading the address, 0xff
+ * is returned and @err_ret will contain the error code.
+ */
+unsigned char sdio_readb_ext(struct sdio_func *func, unsigned int addr,
+ int *err_ret, unsigned in)
+{
+ int ret;
+ unsigned char val;
+
+ BUG_ON(!func);
+
+ if (err_ret)
+ *err_ret = 0;
+
+ ret = mmc_io_rw_direct(func->card, 0, func->num, addr, (u8)in, &val);
+ if (ret) {
+ if (err_ret)
+ *err_ret = ret;
+ return 0xFF;
+ }
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(sdio_readb_ext);
+
+/**
* sdio_writeb - write a single byte to a SDIO function
* @func: SDIO function to access
* @b: byte to write
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
index bb192f90e8e..96f81237d75 100644
--- a/drivers/mmc/core/sdio_irq.c
+++ b/drivers/mmc/core/sdio_irq.c
@@ -27,11 +27,29 @@
#include "sdio_ops.h"
+#define SDIO_IDLE_POLL_PERIOD 10
+
static int process_sdio_pending_irqs(struct mmc_card *card)
{
int i, ret, count;
unsigned char pending;
+ /*
+ * Optimization, if there is only 1 function registered
+ * and IRQ:s are supported and currently enabled, then
+ * we can assume that this actually is an IRQ and we can
+ * call the registered IRQ handler directly without
+ * checking the CCCR registers.
+ */
+ if ((card->host->caps & MMC_CAP_SDIO_IRQ) &&
+ card->host->sdio_irqs && (card->sdio_funcs == 1)) {
+ struct sdio_func *func = card->sdio_func[0];
+ if (func && func->irq_handler) {
+ func->irq_handler(func);
+ return 1;
+ }
+ }
+
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
if (ret) {
printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n",
@@ -65,89 +83,50 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
return ret;
}
-static int sdio_irq_thread(void *_host)
+static void sdio_irq_work_func(struct work_struct *work)
{
- struct mmc_host *host = _host;
- struct sched_param param = { .sched_priority = 1 };
- unsigned long period, idle_period;
+ struct mmc_host *host = (struct mmc_host *)
+ container_of(work, struct mmc_host, sdio_irq_work.work);
int ret;
- sched_setscheduler(current, SCHED_FIFO, &param);
-
/*
- * We want to allow for SDIO cards to work even on non SDIO
- * aware hosts. One thing that non SDIO host cannot do is
- * asynchronous notification of pending SDIO card interrupts
- * hence we poll for them in that case.
+ * We claim the host here on drivers behalf for a couple
+ * reasons:
+ *
+ * 1) it is already needed to retrieve the CCCR_INTx;
+ * 2) we want the driver(s) to clear the IRQ condition ASAP;
+ * 3) we need to control the abort condition locally.
+ *
+ * Just like traditional hard IRQ handlers, we expect SDIO
+ * IRQ handlers to be quick and to the point, so that the
+ * holding of the host lock does not cover too much work
+ * that doesn't require that lock to be held.
*/
- idle_period = msecs_to_jiffies(10);
- period = (host->caps & MMC_CAP_SDIO_IRQ) ?
- MAX_SCHEDULE_TIMEOUT : idle_period;
+ mmc_claim_host(host);
- pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",
- mmc_hostname(host), period);
+ ret = process_sdio_pending_irqs(host->card);
- do {
- /*
- * We claim the host here on drivers behalf for a couple
- * reasons:
- *
- * 1) it is already needed to retrieve the CCCR_INTx;
- * 2) we want the driver(s) to clear the IRQ condition ASAP;
- * 3) we need to control the abort condition locally.
- *
- * Just like traditional hard IRQ handlers, we expect SDIO
- * IRQ handlers to be quick and to the point, so that the
- * holding of the host lock does not cover too much work
- * that doesn't require that lock to be held.
- */
- ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
- if (ret)
- break;
- ret = process_sdio_pending_irqs(host->card);
- mmc_release_host(host);
-
- /*
- * Give other threads a chance to run in the presence of
- * errors.
- */
- if (ret < 0) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (!kthread_should_stop())
- schedule_timeout(HZ);
- set_current_state(TASK_RUNNING);
- }
+ mmc_release_host(host);
+ if (host->caps & MMC_CAP_SDIO_IRQ)
+ host->ops->enable_sdio_irq(host, true);
+ else {
/*
* Adaptive polling frequency based on the assumption
* that an interrupt will be closely followed by more.
* This has a substantial benefit for network devices.
*/
- if (!(host->caps & MMC_CAP_SDIO_IRQ)) {
- if (ret > 0)
- period /= 2;
- else {
- period++;
- if (period > idle_period)
- period = idle_period;
- }
+ if (ret > 0)
+ host->sdio_poll_period /= 2;
+ else {
+ host->sdio_poll_period++;
+ if (host->sdio_poll_period > SDIO_IDLE_POLL_PERIOD)
+ host->sdio_poll_period = SDIO_IDLE_POLL_PERIOD;
}
-
- set_current_state(TASK_INTERRUPTIBLE);
- if (host->caps & MMC_CAP_SDIO_IRQ)
- host->ops->enable_sdio_irq(host, 1);
- if (!kthread_should_stop())
- schedule_timeout(period);
- set_current_state(TASK_RUNNING);
- } while (!kthread_should_stop());
-
- if (host->caps & MMC_CAP_SDIO_IRQ)
- host->ops->enable_sdio_irq(host, 0);
-
- pr_debug("%s: IRQ thread exiting with code %d\n",
- mmc_hostname(host), ret);
-
- return ret;
+ queue_delayed_work(host->sdio_irq_workqueue,
+ &host->sdio_irq_work,
+ msecs_to_jiffies(host->sdio_poll_period));
+ }
}
static int sdio_card_irq_get(struct mmc_card *card)
@@ -157,14 +136,28 @@ static int sdio_card_irq_get(struct mmc_card *card)
WARN_ON(!host->claimed);
if (!host->sdio_irqs++) {
- atomic_set(&host->sdio_irq_thread_abort, 0);
- host->sdio_irq_thread =
- kthread_run(sdio_irq_thread, host, "ksdioirqd/%s",
- mmc_hostname(host));
- if (IS_ERR(host->sdio_irq_thread)) {
- int err = PTR_ERR(host->sdio_irq_thread);
+ host->sdio_irq_workqueue =
+ create_singlethread_workqueue("sdio_irq_workqueue");
+ if (host->sdio_irq_workqueue == NULL) {
host->sdio_irqs--;
- return err;
+ return -ENOMEM;
+ }
+
+ INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work_func);
+
+ /*
+ * We want to allow for SDIO cards to work even on non SDIO
+ * aware hosts. One thing that non SDIO host cannot do is
+ * asynchronous notification of pending SDIO card interrupts
+ * hence we poll for them in that case.
+ */
+ if (host->caps & MMC_CAP_SDIO_IRQ)
+ host->ops->enable_sdio_irq(host, true);
+ else {
+ host->sdio_poll_period = SDIO_IDLE_POLL_PERIOD;
+ queue_delayed_work(host->sdio_irq_workqueue,
+ &host->sdio_irq_work,
+ msecs_to_jiffies(host->sdio_poll_period));
}
}
@@ -179,8 +172,9 @@ static int sdio_card_irq_put(struct mmc_card *card)
BUG_ON(host->sdio_irqs < 1);
if (!--host->sdio_irqs) {
- atomic_set(&host->sdio_irq_thread_abort, 1);
- kthread_stop(host->sdio_irq_thread);
+ host->ops->enable_sdio_irq(host, false);
+ destroy_workqueue(host->sdio_irq_workqueue);
+ host->sdio_irq_workqueue = NULL;
}
return 0;
diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c
index dea36d9c22e..4e3b3004013 100644
--- a/drivers/mmc/core/sdio_ops.c
+++ b/drivers/mmc/core/sdio_ops.c
@@ -152,10 +152,15 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
cmd.arg |= fn << 28;
cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
cmd.arg |= addr << 9;
- if (blocks == 1 && blksz <= 512)
- cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */
+
+ if (blocks == 1 && blksz < 512)
+ cmd.arg |= blksz; /* byte mode */
+ else if (blocks == 1 && blksz == 512 &&
+ !(card->host->caps & MMC_CAP_BROKEN_SDIO_CMD53))
+ cmd.arg |= 0; /* byte mode, 0=512 */
else
cmd.arg |= 0x08000000 | blocks; /* block mode */
+
cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
data.blksz = blksz;
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index 5f3a599ead0..80a95b49513 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -946,8 +946,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
mmc->max_blk_size = MCI_MAXBLKSIZE;
mmc->max_blk_count = MCI_BLKATONCE;
mmc->max_req_size = MCI_BUFSIZE;
- mmc->max_phys_segs = MCI_BLKATONCE;
- mmc->max_hw_segs = MCI_BLKATONCE;
+ mmc->max_segs = MCI_BLKATONCE;
mmc->max_seg_size = MCI_BUFSIZE;
host = mmc_priv(mmc);
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index 95ef864ad8f..ac7989f1240 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -1618,8 +1618,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
if (slot_data->bus_width >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;
- mmc->max_hw_segs = 64;
- mmc->max_phys_segs = 64;
+ mmc->max_segs = 64;
mmc->max_req_size = 32768 * 512;
mmc->max_blk_size = 32768;
mmc->max_blk_count = 512;
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index c8da5d30a86..59b2265a41d 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -998,7 +998,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
mmc->f_max = 24000000;
mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE;
- mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT;
+ mmc->max_segs = AU1XMMC_DESCRIPTOR_COUNT;
mmc->max_blk_size = 2048;
mmc->max_blk_count = 512;
diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c
index 4b0e677d729..bac7d62866b 100644
--- a/drivers/mmc/host/bfin_sdh.c
+++ b/drivers/mmc/host/bfin_sdh.c
@@ -469,7 +469,7 @@ static int __devinit sdh_probe(struct platform_device *pdev)
}
mmc->ops = &sdh_ops;
- mmc->max_phys_segs = 32;
+ mmc->max_segs = 32;
mmc->max_seg_size = 1 << 16;
mmc->max_blk_size = 1 << 11;
mmc->max_blk_count = 1 << 11;
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 33d9f1b0086..e15547cf701 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -138,7 +138,7 @@
/*
* One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units,
* and we handle up to MAX_NR_SG segments. MMC_BLOCK_BOUNCE kicks in only
- * for drivers with max_hw_segs == 1, making the segments bigger (64KB)
+ * for drivers with max_segs == 1, making the segments bigger (64KB)
* than the page or two that's otherwise typical. nr_sg (passed from
* platform data) == 16 gives at least the same throughput boost, using
* EDMA transfer linkage instead of spending CPU time copying pages.
@@ -1239,8 +1239,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
* Each hw_seg uses one EDMA parameter RAM slot, always one
* channel and then usually some linked slots.
*/
- mmc->max_hw_segs = 1 + host->n_link;
- mmc->max_phys_segs = mmc->max_hw_segs;
+ mmc->max_segs = 1 + host->n_link;
/* EDMA limit per hw segment (one or two MBytes) */
mmc->max_seg_size = MAX_CCNT * rw_threshold;
@@ -1250,8 +1249,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
mmc->max_blk_count = 65535; /* NBLK is 16 bits */
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
- dev_dbg(mmc_dev(host->mmc), "max_phys_segs=%d\n", mmc->max_phys_segs);
- dev_dbg(mmc_dev(host->mmc), "max_hw_segs=%d\n", mmc->max_hw_segs);
+ dev_dbg(mmc_dev(host->mmc), "max_segs=%d\n", mmc->max_segs);
dev_dbg(mmc_dev(host->mmc), "max_blk_size=%d\n", mmc->max_blk_size);
dev_dbg(mmc_dev(host->mmc), "max_req_size=%d\n", mmc->max_req_size);
dev_dbg(mmc_dev(host->mmc), "max_seg_size=%d\n", mmc->max_seg_size);
diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c
index 9a68ff4353a..24f24c86b71 100644
--- a/drivers/mmc/host/imxmmc.c
+++ b/drivers/mmc/host/imxmmc.c
@@ -965,8 +965,7 @@ static int __init imxmci_probe(struct platform_device *pdev)
mmc->caps = MMC_CAP_4_BIT_DATA;
/* MMC core transfer sizes tunable parameters */
- mmc->max_hw_segs = 64;
- mmc->max_phys_segs = 64;
+ mmc->max_segs = 64;
mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */
mmc->max_req_size = 64*512; /* default PAGE_CACHE_SIZE */
mmc->max_blk_size = 2048;
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index ad847a24a67..b49cf4727ae 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -1382,8 +1382,7 @@ static int mmc_spi_probe(struct spi_device *spi)
mmc->ops = &mmc_spi_ops;
mmc->max_blk_size = MMC_SPI_BLOCKSIZE;
- mmc->max_hw_segs = MMC_SPI_BLOCKSATONCE;
- mmc->max_phys_segs = MMC_SPI_BLOCKSATONCE;
+ mmc->max_segs = MMC_SPI_BLOCKSATONCE;
mmc->max_req_size = MMC_SPI_BLOCKSATONCE * MMC_SPI_BLOCKSIZE;
mmc->max_blk_count = MMC_SPI_BLOCKSATONCE;
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 2ed435bd4b6..92a5f73854c 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -2,7 +2,7 @@
* linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver
*
* Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
- * Copyright (C) 2010 ST-Ericsson AB.
+ * Copyright (C) 2010 ST-Ericsson SA
*
* 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
@@ -18,44 +18,249 @@
#include <linux/err.h>
#include <linux/highmem.h>
#include <linux/log2.h>
+#include <linux/pm_runtime.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/pm.h>
#include <linux/amba/bus.h>
#include <linux/clk.h>
#include <linux/scatterlist.h>
#include <linux/gpio.h>
-#include <linux/amba/mmci.h>
#include <linux/regulator/consumer.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/amba/mmci.h>
-#include <asm/cacheflush.h>
-#include <asm/div64.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/sizes.h>
+#ifdef CONFIG_ARCH_U8500
+/* Temporary solution to find out if HW is db8500 v1 or v2. */
+#include <mach/hardware.h>
+#endif
+
#include "mmci.h"
#define DRIVER_NAME "mmci-pl18x"
static unsigned int fmax = 515633;
+static unsigned int dataread_delay_clks = 7500000;
+
+/**
+ * struct variant_data - MMCI variant-specific quirks
+ * @clkreg: default value for MCICLOCK register
+ * @clkreg_enable: enable value for MMCICLOCK register
+ * @dmareg_enable: enable value for MMCIDATACTRL register
+ * @datalength_bits: number of bits in the MMCIDATALENGTH register
+ * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
+ * is asserted (likewise for RX)
+ * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
+ * is asserted (likewise for RX)
+ * @txsize_threshold: Sets DMA burst size to minimal if transfer size is
+ * less or equal to this threshold. This shall be specified in
+ * number of bytes. Set 0 for no burst compensation
+ * @broken_blockend: the MCI_DATABLOCKEND is broken on the hardware
+ * and will not work at all.
+ * @sdio: variant supports SDIO
+ * @st_clkdiv: true if using a ST-specific clock divider algorithm
+ * @pwrreg_powerup: power up value for MMCIPOWER register
+ * @signal_direction: input/out direction of bus signals can be indicated,
+ * this is usually used by e.g. voltage level translators.
+ * @non_power_of_2_blksize: variant supports block sizes that are not
+ * a power of two.
+ */
+struct variant_data {
+ unsigned int clkreg;
+ unsigned int clkreg_enable;
+ unsigned int dmareg_enable;
+ unsigned int datalength_bits;
+ unsigned int fifosize;
+ unsigned int fifohalfsize;
+ unsigned int txsize_threshold;
+ bool broken_blockend;
+ bool sdio;
+ bool st_clkdiv;
+ unsigned int pwrreg_powerup;
+ bool signal_direction;
+ bool non_power_of_2_blksize;
+};
+
+static struct variant_data variant_arm = {
+ .fifosize = 16 * 4,
+ .fifohalfsize = 8 * 4,
+ .datalength_bits = 16,
+ .pwrreg_powerup = MCI_PWR_UP,
+};
+
+static struct variant_data variant_u300 = {
+ .fifosize = 16 * 4,
+ .fifohalfsize = 8 * 4,
+ .clkreg_enable = 1 << 13, /* HWFCEN */
+ .datalength_bits = 16,
+ .sdio = true,
+ .pwrreg_powerup = MCI_PWR_ON,
+ .signal_direction = true,
+};
+
+static struct variant_data variant_ux500 = {
+ .fifosize = 30 * 4,
+ .fifohalfsize = 8 * 4,
+ .txsize_threshold = 16,
+ .clkreg = MCI_CLK_ENABLE,
+ .clkreg_enable = 1 << 14, /* HWFCEN */
+ .dmareg_enable = 1 << 12, /* DMAREQCTRL */
+ .datalength_bits = 24,
+ .broken_blockend = true,
+ .sdio = true,
+ .st_clkdiv = true,
+ .pwrreg_powerup = MCI_PWR_ON,
+ .signal_direction = true,
+ .non_power_of_2_blksize = true,
+};
+/*
+ * Debugfs
+ */
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static int mmci_regs_show(struct seq_file *seq, void *v)
+{
+ struct mmci_host *host = seq->private;
+ unsigned long iflags;
+ u32 pwr, clk, arg, cmd, rspcmd, r0, r1, r2, r3;
+ u32 dtimer, dlength, dctrl, dcnt;
+ u32 sta, clear, mask0, mask1, fifocnt, fifo;
+
+ mmc_host_enable(host->mmc);
+ spin_lock_irqsave(&host->lock, iflags);
+
+ pwr = readl(host->base + MMCIPOWER);
+ clk = readl(host->base + MMCICLOCK);
+ arg = readl(host->base + MMCIARGUMENT);
+ cmd = readl(host->base + MMCICOMMAND);
+ rspcmd = readl(host->base + MMCIRESPCMD);
+ r0 = readl(host->base + MMCIRESPONSE0);
+ r1 = readl(host->base + MMCIRESPONSE1);
+ r2 = readl(host->base + MMCIRESPONSE2);
+ r3 = readl(host->base + MMCIRESPONSE3);
+ dtimer = readl(host->base + MMCIDATATIMER);
+ dlength = readl(host->base + MMCIDATALENGTH);
+ dctrl = readl(host->base + MMCIDATACTRL);
+ dcnt = readl(host->base + MMCIDATACNT);
+ sta = readl(host->base + MMCISTATUS);
+ clear = readl(host->base + MMCICLEAR);
+ mask0 = readl(host->base + MMCIMASK0);
+ mask1 = readl(host->base + MMCIMASK1);
+ fifocnt = readl(host->base + MMCIFIFOCNT);
+ fifo = readl(host->base + MMCIFIFO);
+
+ spin_unlock_irqrestore(&host->lock, iflags);
+ mmc_host_disable(host->mmc);
+
+ seq_printf(seq, "\033[1;34mMMCI registers\033[0m\n");
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_power", pwr);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_clock", clk);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_arg", arg);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_cmd", cmd);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_respcmd", rspcmd);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_resp0", r0);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_resp1", r1);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_resp2", r2);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_resp3", r3);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_datatimer", dtimer);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_datalen", dlength);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_datactrl", dctrl);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_datacnt", dcnt);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_status", sta);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_iclear", clear);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_imask0", mask0);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_imask1", mask1);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_fifocnt", fifocnt);
+ seq_printf(seq, "%-20s:0x%x\n", "mmci_fifo", fifo);
+
+ return 0;
+}
+
+static int mmci_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mmci_regs_show, inode->i_private);
+}
+
+static const struct file_operations mmci_fops_regs = {
+ .owner = THIS_MODULE,
+ .open = mmci_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void mmci_debugfs_create(struct mmci_host *host)
+{
+ host->debug_regs = debugfs_create_file("regs", S_IRUGO,
+ host->mmc->debugfs_root, host,
+ &mmci_fops_regs);
+
+ if (IS_ERR(host->debug_regs))
+ dev_err(mmc_dev(host->mmc),
+ "failed to create debug regs file\n");
+}
+
+static void mmci_debugfs_remove(struct mmci_host *host)
+{
+ debugfs_remove(host->debug_regs);
+}
+
+#else
+static inline void mmci_debugfs_create(struct mmci_host *host) { }
+static inline void mmci_debugfs_remove(struct mmci_host *host) { }
+#endif
+
+/*
+ * Uggly hack! This must be removed soon!!
+ *
+ * u8500_sdio_detect_card() - Initiates card scan for sdio host.
+ * This is required to initiate card rescan from sdio client device driver.
+ *
+ * sdio_host_ptr - Host pointer to save SDIO host data structure
+ * (will only work when the SDIO device is probed as the last MMCI device).
+ */
+static struct mmci_host *sdio_host_ptr;
+void u8500_sdio_detect_card(void)
+{
+ struct mmci_host *host = sdio_host_ptr;
+ if (sdio_host_ptr && host->mmc)
+ mmc_detect_change(host->mmc, msecs_to_jiffies(10));
+
+ return;
+}
+EXPORT_SYMBOL(u8500_sdio_detect_card);
/*
* This must be called with host->lock held
*/
static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
{
- u32 clk = 0;
+ struct variant_data *variant = host->variant;
+ u32 clk = variant->clkreg;
if (desired) {
if (desired >= host->mclk) {
- clk = MCI_CLK_BYPASS;
+ clk = MCI_CLK_BYPASS | MCI_NEG_EDGE;
host->cclk = host->mclk;
+ } else if (variant->st_clkdiv) {
+ clk = ((host->mclk + desired - 1) / desired) - 2;
+ if (clk >= 256)
+ clk = 255;
+ host->cclk = host->mclk / (clk + 2);
} else {
clk = host->mclk / (2 * desired) - 1;
if (clk >= 256)
clk = 255;
host->cclk = host->mclk / (2 * (clk + 1));
}
- if (host->hw_designer == AMBA_VENDOR_ST)
- clk |= MCI_ST_FCEN; /* Bug fix in ST IP block */
+
+ clk |= variant->clkreg_enable;
clk |= MCI_CLK_ENABLE;
/* This hasn't proven to be worthwhile */
/* clk |= MCI_CLK_PWRSAVE; */
@@ -91,63 +296,62 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
spin_lock(&host->lock);
}
-static void mmci_stop_data(struct mmci_host *host)
-{
- writel(0, host->base + MMCIDATACTRL);
- writel(0, host->base + MMCIMASK1);
- host->data = NULL;
-}
-
-static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
+static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
{
- unsigned int datactrl, timeout, irqmask;
- unsigned long long clks;
- void __iomem *base;
- int blksz_bits;
+ void __iomem *base = host->base;
- dev_dbg(mmc_dev(host->mmc), "blksz %04x blks %04x flags %08x\n",
- data->blksz, data->blocks, data->flags);
+ if (host->singleirq) {
+ unsigned int mask0 = readl(base + MMCIMASK0);
- host->data = data;
- host->size = data->blksz;
- host->data_xfered = 0;
+ mask0 &= ~MCI_IRQ1MASK;
+ mask0 |= mask;
- mmci_init_sg(host, data);
+ writel(mask0, base + MMCIMASK0);
+ }
- clks = (unsigned long long)data->timeout_ns * host->cclk;
- do_div(clks, 1000000000UL);
+ writel(mask, base + MMCIMASK1);
+}
- timeout = data->timeout_clks + (unsigned int)clks;
+static void mmci_stop_data(struct mmci_host *host)
+{
+ u32 clk;
+ unsigned int datactrl = 0;
- base = host->base;
- writel(timeout, base + MMCIDATATIMER);
- writel(host->size, base + MMCIDATALENGTH);
+ /*
+ * The ST Micro variants has a special bit
+ * to enable SDIO mode. This bit must remain set even when not
+ * doing data transfers, otherwise no SDIO interrupts can be
+ * received.
+ */
+ if (host->variant->sdio &&
+ host->mmc->card &&
+ mmc_card_sdio(host->mmc->card))
+ datactrl |= MCI_ST_DPSM_SDIOEN;
- blksz_bits = ffs(data->blksz) - 1;
- BUG_ON(1 << blksz_bits != data->blksz);
+ writel(datactrl, host->base + MMCIDATACTRL);
+ mmci_set_mask1(host, 0);
- datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
- if (data->flags & MMC_DATA_READ) {
- datactrl |= MCI_DPSM_DIRECTION;
- irqmask = MCI_RXFIFOHALFFULLMASK;
+ /* Needed for DDR */
+ if (host->mmc->card && mmc_card_ddr_mode(host->mmc->card)) {
+ clk = readl(host->base + MMCICLOCK);
+ clk &= ~(MCI_NEG_EDGE);
- /*
- * If we have less than a FIFOSIZE of bytes to transfer,
- * trigger a PIO interrupt as soon as any data is available.
- */
- if (host->size < MCI_FIFOSIZE)
- irqmask |= MCI_RXDATAAVLBLMASK;
- } else {
- /*
- * We don't actually need to include "FIFO empty" here
- * since its implicit in "FIFO half empty".
- */
- irqmask = MCI_TXFIFOHALFEMPTYMASK;
+ writel(clk, (host->base + MMCICLOCK));
}
- writel(datactrl, base + MMCIDATACTRL);
- writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
- writel(irqmask, base + MMCIMASK1);
+ host->data = NULL;
+}
+
+static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
+{
+ unsigned int flags = SG_MITER_ATOMIC;
+
+ if (data->flags & MMC_DATA_READ)
+ flags |= SG_MITER_TO_SG;
+ else
+ flags |= SG_MITER_FROM_SG;
+
+ sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
}
static void
@@ -179,49 +383,499 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
}
static void
-mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
- unsigned int status)
+mmci_complete_data_xfer(struct mmci_host *host)
{
- if (status & MCI_DATABLOCKEND) {
- host->data_xfered += data->blksz;
-#ifdef CONFIG_ARCH_U300
+ struct mmc_data *data = host->data;
+
+ if ((host->size == 0) || data->error) {
+
+ /*
+ * Variants with broken blockend flags and as well dma
+ * transfers handles the end of the entire transfer here.
+ */
+ if (host->last_blockend && !data->error)
+ host->data_xfered = data->blksz * data->blocks;
+
+ mmci_stop_data(host);
+
+ if (!data->stop)
+ mmci_request_end(host, data->mrq);
+ else
+ mmci_start_command(host, data->stop, 0);
+ }
+}
+
+/*
+ * All the DMA operation mode stuff goes inside this ifdef.
+ * This assumes that you have a generic DMA device interface,
+ * no custom DMA interfaces are supported.
+ */
+#ifdef CONFIG_DMA_ENGINE
+static void __devinit mmci_setup_dma(struct mmci_host *host)
+{
+ struct mmci_platform_data *plat = host->plat;
+ dma_cap_mask_t mask;
+
+ if (!plat || !plat->dma_filter) {
+ dev_err(mmc_dev(host->mmc), "no DMA platform data!\n");
+ return;
+ }
+
+ /* Try to acquire a generic DMA engine slave channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ /*
+ * If only an RX channel is specified, the driver will
+ * attempt to use it bidirectionally, however if it is
+ * is specified but cannot be located, DMA will be disabled.
+ */
+ host->dma_rx_channel = dma_request_channel(mask,
+ plat->dma_filter,
+ plat->dma_rx_param);
+ /* E.g if no DMA hardware is present */
+ if (!host->dma_rx_channel) {
+ dev_err(mmc_dev(host->mmc), "no RX DMA channel!\n");
+ return;
+ }
+ if (plat->dma_tx_param) {
+ host->dma_tx_channel = dma_request_channel(mask,
+ plat->dma_filter,
+ plat->dma_tx_param);
+ if (!host->dma_tx_channel) {
+ dma_release_channel(host->dma_rx_channel);
+ host->dma_rx_channel = NULL;
+ return;
+ }
+ } else {
+ host->dma_tx_channel = host->dma_rx_channel;
+ }
+ host->dma_enable = true;
+ dev_info(mmc_dev(host->mmc), "use DMA channels DMA RX %s, DMA TX %s\n",
+ dma_chan_name(host->dma_rx_channel),
+ dma_chan_name(host->dma_tx_channel));
+}
+
+/*
+ * This is used in __devinit or __devexit so inline it
+ * so it can be discarded.
+ */
+static inline void mmci_disable_dma(struct mmci_host *host)
+{
+ if (host->dma_rx_channel)
+ dma_release_channel(host->dma_rx_channel);
+ if (host->dma_tx_channel)
+ dma_release_channel(host->dma_tx_channel);
+ host->dma_enable = false;
+}
+
+static void mmci_dma_data_end(struct mmci_host *host)
+{
+ struct mmc_data *data = host->data;
+
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ (data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ host->dma_on_current_xfer = false;
+}
+
+static void mmci_dma_terminate(struct mmci_host *host)
+{
+ struct mmc_data *data = host->data;
+ struct dma_chan *chan;
+
+ dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
+ if (data->flags & MMC_DATA_READ)
+ chan = host->dma_rx_channel;
+ else
+ chan = host->dma_tx_channel;
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ (data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+ host->dma_on_current_xfer = false;
+}
+
+static void mmci_dma_callback(void *arg)
+{
+ unsigned long flags;
+ struct mmci_host *host = arg;
+
+ dev_vdbg(mmc_dev(host->mmc), "DMA transfer done!\n");
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ mmci_dma_data_end(host);
+
+ /* Mark that the entire data is transferred for this dma transfer. */
+ host->size = 0;
+
+ /*
+ * Make sure MMCI has received MCI_DATAEND before
+ * completing the data transfer.
+ */
+ if (host->dataend)
+ mmci_complete_data_xfer(host);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
+{
+ struct variant_data *variant = host->variant;
+ struct dma_slave_config rx_conf = {
+ .src_addr = host->phybase + MMCIFIFO,
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .direction = DMA_FROM_DEVICE,
+ .src_maxburst = variant->fifohalfsize >> 2, /* # of words */
+ };
+ struct dma_slave_config tx_conf = {
+ .dst_addr = host->phybase + MMCIFIFO,
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .direction = DMA_TO_DEVICE,
+ .dst_maxburst = variant->fifohalfsize >> 2, /* # of words */
+ };
+ struct mmc_data *data = host->data;
+ enum dma_data_direction direction;
+ struct dma_chan *chan;
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist *sg;
+ dma_cookie_t cookie;
+ int i;
+ unsigned int irqmask0;
+ int sg_len;
+
+ /* If less than or equal to the fifo size, don't bother with DMA */
+ if (host->size <= variant->fifosize)
+ return -EINVAL;
+
+ datactrl |= MCI_DPSM_DMAENABLE;
+ datactrl |= variant->dmareg_enable;
+
+ if (data->flags & MMC_DATA_READ) {
+ if (host->size <= variant->txsize_threshold)
+ rx_conf.src_maxburst = 1;
+
+ direction = DMA_FROM_DEVICE;
+ chan = host->dma_rx_channel;
+ chan->device->device_control(chan, DMA_SLAVE_CONFIG,
+ (unsigned long) &rx_conf);
+ } else {
+ if (host->size <= variant->txsize_threshold)
+ tx_conf.dst_maxburst = 1;
+
+ direction = DMA_TO_DEVICE;
+ chan = host->dma_tx_channel;
+ chan->device->device_control(chan, DMA_SLAVE_CONFIG,
+ (unsigned long) &tx_conf);
+ }
+
+ /* Check for weird stuff in the sg list */
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ dev_vdbg(mmc_dev(host->mmc),
+ "MMCI SGlist %d dir %d: length: %08x\n",
+ i, direction, sg->length);
+ if (sg->offset & 3 || sg->length & 3)
+ return -EINVAL;
+ }
+
+ sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, direction);
+ if (!sg_len)
+ goto map_err;
+
+ desc = chan->device->device_prep_slave_sg(chan,
+ data->sg, sg_len, direction,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc)
+ goto unmap_exit;
+
+ desc->callback = mmci_dma_callback;
+ desc->callback_param = host;
+ host->dma_desc = desc;
+ dev_vdbg(mmc_dev(host->mmc), "Submit MMCI DMA job, sglen %d "
+ "blksz %04x blks %04x flags %08x\n",
+ data->sg_len, data->blksz, data->blocks, data->flags);
+ cookie = desc->tx_submit(desc);
+
+ /* Here overloaded DMA controllers may fail */
+ if (dma_submit_error(cookie))
+ goto unmap_exit;
+
+ host->dma_on_current_xfer = true;
+ chan->device->device_issue_pending(chan);
+
+ /*
+ * MMCI monitors both MCI_DATAEND and the DMA callback.
+ * Both events must occur before the transfer is considered
+ * to be completed. MCI_DATABLOCKEND is not used in DMA mode.
+ */
+ host->last_blockend = true;
+ irqmask0 = readl(host->base + MMCIMASK0);
+ irqmask0 &= ~MCI_DATABLOCKENDMASK;
+ writel(irqmask0, host->base + MMCIMASK0);
+
+ /* Trigger the DMA transfer */
+ writel(datactrl, host->base + MMCIDATACTRL);
+ return 0;
+
+unmap_exit:
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, direction);
+map_err:
+ chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+ return -ENOMEM;
+}
+#else
+/* Blank functions if the DMA engine is not available */
+static inline void mmci_setup_dma(struct mmci_host *host)
+{
+}
+
+static inline void mmci_disable_dma(struct mmci_host *host)
+{
+}
+
+static inline void mmci_dma_data_end(struct mmci_host *host)
+{
+}
+
+static inline void mmci_dma_terminate(struct mmci_host *host)
+{
+}
+
+static inline int mmci_dma_start_data(struct mmci_host *host,
+ unsigned int datactrl)
+{
+ return -ENOSYS;
+}
+#endif
+
+static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
+{
+ struct variant_data *variant = host->variant;
+ unsigned int datactrl, timeout, irqmask0, irqmask1;
+ unsigned int clkcycle_ns;
+ void __iomem *base;
+ int blksz_bits;
+ u32 clk;
+
+ dev_dbg(mmc_dev(host->mmc), "blksz %04x blks %04x flags %08x\n",
+ data->blksz, data->blocks, data->flags);
+
+ host->data = data;
+ host->size = data->blksz * data->blocks;
+ host->data_xfered = 0;
+ host->last_blockend = false;
+ host->dataend = false;
+ host->cache_len = 0;
+ host->cache = 0;
+
+ clkcycle_ns = 1000000000 / host->cclk;
+ timeout = data->timeout_ns / clkcycle_ns;
+ timeout += data->timeout_clks;
+
+ if (data->flags & MMC_DATA_READ) {
/*
- * On the U300 some signal or other is
- * badly routed so that a data write does
- * not properly terminate with a MCI_DATAEND
- * status flag. This quirk will make writes
- * work again.
+ * Since the read command is sent after we have setup
+ * the data transfer we must increase the data timeout.
+ * Unfortunately this is not enough since some cards
+ * does not seem to stick to what is stated in their
+ * CSD for TAAC and NSAC.
*/
- if (data->flags & MMC_DATA_WRITE)
- status |= MCI_DATAEND;
+ timeout += dataread_delay_clks;
+ }
+
+ base = host->base;
+ writel(timeout, base + MMCIDATATIMER);
+ writel(host->size, base + MMCIDATALENGTH);
+
+ blksz_bits = ffs(data->blksz) - 1;
+
+#ifdef CONFIG_ARCH_U8500
+ /* Temporary solution for db8500v2. */
+ if (cpu_is_u8500v20_or_later())
+ datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
+ else
#endif
+ datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
+
+ if (data->flags & MMC_DATA_READ)
+ datactrl |= MCI_DPSM_DIRECTION;
+
+ if (host->mmc->card && mmc_card_ddr_mode(host->mmc->card)) {
+ datactrl |= MCI_ST_DPSM_DDRMODE;
+
+ /* Needed for DDR */
+ clk = readl(base + MMCICLOCK);
+ clk |= MCI_NEG_EDGE;
+
+ writel(clk, (base + MMCICLOCK));
+ }
+
+ if (variant->sdio &&
+ host->mmc->card &&
+ mmc_card_sdio(host->mmc->card)) {
+ /*
+ * The ST Micro variants has a special bit
+ * to enable SDIO mode. This bit is set the first time
+ * a SDIO data transfer is done and must remain set
+ * after the data transfer is completed. The reason is
+ * because of otherwise no SDIO interrupts can be
+ * received.
+ */
+ datactrl |= MCI_ST_DPSM_SDIOEN;
+
+ /*
+ * The ST Micro variant for SDIO transfer sizes
+ * less than or equal to 8 bytes needs to have clock
+ * H/W flow control disabled. Since flow control is
+ * not really needed for anything that fits in the
+ * FIFO, we can disable it for any write smaller
+ * than the FIFO size.
+ */
+ if ((host->size <= variant->fifosize) &&
+ (data->flags & MMC_DATA_WRITE))
+ writel(readl(host->base + MMCICLOCK) &
+ ~variant->clkreg_enable,
+ host->base + MMCICLOCK);
+ else
+ writel(readl(host->base + MMCICLOCK) |
+ variant->clkreg_enable,
+ host->base + MMCICLOCK);
+ }
+
+ if (host->dma_enable) {
+ int ret;
+
+ /*
+ * Attempt to use DMA operation mode, if this
+ * should fail, fall back to PIO mode
+ */
+ ret = mmci_dma_start_data(host, datactrl);
+ if (!ret)
+ return;
+ }
+
+ /* IRQ mode, map the SG list for CPU reading/writing */
+ mmci_init_sg(host, data);
+
+ if (data->flags & MMC_DATA_READ) {
+ irqmask1 = MCI_RXFIFOHALFFULLMASK;
+
+ /*
+ * If we have less than a FIFOSIZE of bytes to
+ * transfer, trigger a PIO interrupt as soon as any
+ * data is available.
+ */
+ if (host->size < variant->fifosize)
+ irqmask1 |= MCI_RXDATAAVLBLMASK;
+ } else {
+ /*
+ * We don't actually need to include "FIFO empty" here
+ * since its implicit in "FIFO half empty".
+ */
+ irqmask1 = MCI_TXFIFOHALFEMPTYMASK;
+ }
+
+ /* Setup IRQ */
+ irqmask0 = readl(base + MMCIMASK0);
+ if (variant->broken_blockend) {
+ host->last_blockend = true;
+ irqmask0 &= ~MCI_DATABLOCKENDMASK;
+ } else {
+ irqmask0 |= MCI_DATABLOCKENDMASK;
}
- if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
- dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ (status %08x)\n", status);
+ writel(irqmask0, base + MMCIMASK0);
+ mmci_set_mask1(host, irqmask1);
+
+ /* Start the data transfer */
+ writel(datactrl, base + MMCIDATACTRL);
+}
+
+static void
+mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
+ unsigned int status)
+{
+ struct variant_data *variant = host->variant;
+
+ /* First check for errors */
+ if (status & MCI_DATA_ERR) {
+ dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ (status %08x)\n",
+ status);
if (status & MCI_DATACRCFAIL)
data->error = -EILSEQ;
else if (status & MCI_DATATIMEOUT)
data->error = -ETIMEDOUT;
else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
data->error = -EIO;
- status |= MCI_DATAEND;
/*
* We hit an error condition. Ensure that any data
- * partially written to a page is properly coherent.
+ * partially written to a page is properly coherent,
+ * unless we're using DMA.
*/
- if (host->sg_len && data->flags & MMC_DATA_READ)
- flush_dcache_page(sg_page(host->sg_ptr));
+ if (host->dma_on_current_xfer)
+ mmci_dma_terminate(host);
+ else if (data->flags & MMC_DATA_READ) {
+ struct sg_mapping_iter *sg_miter = &host->sg_miter;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (sg_miter_next(sg_miter)) {
+ flush_dcache_page(sg_miter->page);
+ sg_miter_stop(sg_miter);
+ }
+ local_irq_restore(flags);
+ }
}
- if (status & MCI_DATAEND) {
- mmci_stop_data(host);
- if (!data->stop) {
- mmci_request_end(host, data->mrq);
- } else {
- mmci_start_command(host, data->stop, 0);
+ /*
+ * On ARM variants in PIO mode, MCI_DATABLOCKEND
+ * is always sent first, and we increase the
+ * transfered number of bytes for that IRQ. Then
+ * MCI_DATAEND follows and we conclude the transaction.
+ *
+ * On the Ux500 single-IRQ variant MCI_DATABLOCKEND
+ * doesn't seem to immediately clear from the status,
+ * so we can't use it keep count when only one irq is
+ * used because the irq will hit for other reasons, and
+ * then the flag is still up. So we use the MCI_DATAEND
+ * IRQ at the end of the entire transfer because
+ * MCI_DATABLOCKEND is broken.
+ *
+ * In the U300, the IRQs can arrive out-of-order,
+ * e.g. MCI_DATABLOCKEND sometimes arrives after MCI_DATAEND,
+ * so for this case we use the flags "last_blockend" and
+ * "dataend" to make sure both IRQs have arrived before
+ * concluding the transaction. (This does not apply
+ * to the Ux500 which doesn't fire MCI_DATABLOCKEND
+ * at all.) In DMA mode it suffers from the same problem
+ * as the Ux500.
+ */
+ if (status & MCI_DATABLOCKEND) {
+ /*
+ * Just being a little over-cautious, we do not
+ * use this progressive update if the hardware blockend
+ * flag is unreliable: since it can stay high between
+ * IRQs it will corrupt the transfer counter.
+ */
+ if (!variant->broken_blockend && !host->dma_on_current_xfer) {
+ host->data_xfered += data->blksz;
+
+ if (host->data_xfered == data->blksz * data->blocks)
+ host->last_blockend = true;
}
}
+
+ if (status & MCI_DATAEND)
+ host->dataend = true;
+
+ /*
+ * On variants with broken blockend we shall only wait for dataend,
+ * on others we must sync with the blockend signal since they can
+ * appear out-of-order.
+ */
+ if ((host->dataend && host->last_blockend) || data->error)
+ mmci_complete_data_xfer(host);
}
static void
@@ -237,22 +891,24 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
cmd->resp[2] = readl(base + MMCIRESPONSE2);
cmd->resp[3] = readl(base + MMCIRESPONSE3);
- if (status & MCI_CMDTIMEOUT) {
+ if (status & MCI_CMDTIMEOUT)
cmd->error = -ETIMEDOUT;
- } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
+ else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC)
cmd->error = -EILSEQ;
- }
if (!cmd->data || cmd->error) {
- if (host->data)
+ if (host->data) {
+ if (host->dma_on_current_xfer)
+ mmci_dma_terminate(host);
mmci_stop_data(host);
+ }
mmci_request_end(host, cmd->mrq);
- } else if (!(cmd->data->flags & MMC_DATA_READ)) {
+ } else if (!(cmd->data->flags & MMC_DATA_READ))
mmci_start_data(host, cmd->data);
- }
}
-static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
+static int mmci_pio_read(struct mmci_host *host, char *buffer,
+ unsigned int remain)
{
void __iomem *base = host->base;
char *ptr = buffer;
@@ -268,7 +924,24 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema
if (count <= 0)
break;
- readsl(base + MMCIFIFO, ptr, count >> 2);
+ /*
+ * SDIO especially may want to receive something that is
+ * not divisible by 4 (as opposed to card sectors
+ * etc). Therefore make sure we always read the last bytes
+ * out of the FIFO.
+ */
+ switch (count) {
+ case 1:
+ case 3:
+ readsb(base + MMCIFIFO, ptr, count);
+ break;
+ case 2:
+ readsw(base + MMCIFIFO, ptr, 1);
+ break;
+ default:
+ readsl(base + MMCIFIFO, ptr, count >> 2);
+ break;
+ }
ptr += count;
remain -= count;
@@ -283,21 +956,96 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema
return ptr - buffer;
}
-static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status)
+static int mmci_pio_write(struct mmci_host *host, char *buffer,
+ unsigned int remain, u32 status)
{
+ struct variant_data *variant = host->variant;
void __iomem *base = host->base;
char *ptr = buffer;
+ unsigned int data_left = host->size;
+ unsigned int count, maxcnt;
+ char *cache_ptr;
+ int i;
+
do {
- unsigned int count, maxcnt;
+ maxcnt = status & MCI_TXFIFOEMPTY ?
+ variant->fifosize : variant->fifohalfsize;
+
+ /*
+ * A write to the FIFO must always be done of 4 bytes aligned
+ * data. If the buffer is not 4 bytes aligned we must pad the
+ * data, but this must only be done for the final write for the
+ * entire data transfer, otherwise we will corrupt the data.
+ * Thus a buffer cache of four bytes is needed to temporary
+ * store data.
+ */
+
+ if (host->cache_len) {
+ cache_ptr = (char *)&host->cache;
+ cache_ptr = cache_ptr + host->cache_len;
+ data_left += host->cache_len;
+
+ while ((host->cache_len < 4) && (remain > 0)) {
+ *cache_ptr = *ptr;
+ cache_ptr++;
+ ptr++;
+ host->cache_len++;
+ remain--;
+ }
+
+ if ((host->cache_len == 4) ||
+ (data_left == host->cache_len)) {
+
+ writesl(base + MMCIFIFO, &host->cache, 1);
+ if (data_left == host->cache_len)
+ break;
+
+ host->cache = 0;
+ host->cache_len = 0;
+ maxcnt -= 4;
+ data_left -= 4;
+ }
+
+ if (remain == 0)
+ break;
+ }
- maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : MCI_FIFOHALFSIZE;
count = min(remain, maxcnt);
- writesl(base + MMCIFIFO, ptr, count >> 2);
+ if (!(count % 4) || (data_left == count)) {
+ /*
+ * The data is either 4-bytes aligned or it is the
+ * last data to write. It is thus fine to potentially
+ * pad the data if needed.
+ */
+ writesl(base + MMCIFIFO, ptr, (count + 3) >> 2);
+ ptr += count;
+ remain -= count;
+ data_left -= count;
- ptr += count;
- remain -= count;
+ } else {
+
+ host->cache_len = count % 4;
+ count = (count >> 2) << 2;
+
+ if (count)
+ writesl(base + MMCIFIFO, ptr, count >> 2);
+
+ ptr += count;
+ remain -= count;
+ data_left -= count;
+
+ i = 0;
+ cache_ptr = (char *)&host->cache;
+ while (i < host->cache_len) {
+ *cache_ptr = *ptr;
+ cache_ptr++;
+ ptr++;
+ remain--;
+ i++;
+ }
+ }
if (remain == 0)
break;
@@ -314,15 +1062,19 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem
static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
{
struct mmci_host *host = dev_id;
+ struct sg_mapping_iter *sg_miter = &host->sg_miter;
+ struct variant_data *variant = host->variant;
void __iomem *base = host->base;
+ unsigned long flags;
u32 status;
status = readl(base + MMCISTATUS);
dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status);
+ local_irq_save(flags);
+
do {
- unsigned long flags;
unsigned int remain, len;
char *buffer;
@@ -336,11 +1088,11 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL)))
break;
- /*
- * Map the current scatter buffer.
- */
- buffer = mmci_kmap_atomic(host, &flags) + host->sg_off;
- remain = host->sg_ptr->length - host->sg_off;
+ if (!sg_miter_next(sg_miter))
+ break;
+
+ buffer = sg_miter->addr;
+ remain = sg_miter->length;
len = 0;
if (status & MCI_RXACTIVE)
@@ -348,47 +1100,42 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
if (status & MCI_TXACTIVE)
len = mmci_pio_write(host, buffer, remain, status);
- /*
- * Unmap the buffer.
- */
- mmci_kunmap_atomic(host, buffer, &flags);
+ sg_miter->consumed = len;
- host->sg_off += len;
host->size -= len;
remain -= len;
if (remain)
break;
- /*
- * If we were reading, and we have completed this
- * page, ensure that the data cache is coherent.
- */
if (status & MCI_RXACTIVE)
- flush_dcache_page(sg_page(host->sg_ptr));
-
- if (!mmci_next_sg(host))
- break;
+ flush_dcache_page(sg_miter->page);
status = readl(base + MMCISTATUS);
} while (1);
+ sg_miter_stop(sg_miter);
+
+ local_irq_restore(flags);
+
/*
* If we're nearing the end of the read, switch to
* "any data available" mode.
*/
- if (status & MCI_RXACTIVE && host->size < MCI_FIFOSIZE)
- writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1);
+ if (status & MCI_RXACTIVE && host->size < variant->fifosize)
+ mmci_set_mask1(host, MCI_RXDATAAVLBLMASK);
- /*
- * If we run out of data, disable the data IRQs; this
- * prevents a race where the FIFO becomes empty before
- * the chip itself has disabled the data path, and
- * stops us racing with our data end IRQ.
- */
+ /* If we run out of data, disable the data IRQs. */
if (host->size == 0) {
- writel(0, base + MMCIMASK1);
- writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0);
+ mmci_set_mask1(host, 0);
+
+ /*
+ * If we already received MCI_DATAEND and the last
+ * MCI_DATABLOCKEND, the entire data transfer shall
+ * be completed.
+ */
+ if (host->dataend && host->last_blockend)
+ mmci_complete_data_xfer(host);
}
return IRQ_HANDLED;
@@ -401,6 +1148,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
{
struct mmci_host *host = dev_id;
u32 status;
+ int sdio_irq = 0;
int ret = 0;
spin_lock(&host->lock);
@@ -410,18 +1158,28 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
struct mmc_data *data;
status = readl(host->base + MMCISTATUS);
+
+ if (host->singleirq) {
+ if (status & readl(host->base + MMCIMASK1))
+ mmci_pio_irq(irq, dev_id);
+
+ status &= ~MCI_IRQ1MASK;
+ }
+
status &= readl(host->base + MMCIMASK0);
writel(status, host->base + MMCICLEAR);
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
+ if (status & MCI_SDIOIT)
+ sdio_irq = 1;
+
data = host->data;
- if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|
- MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data)
+ if (status & MCI_DATA_IRQ && data)
mmci_data_irq(host, data, status);
cmd = host->cmd;
- if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
+ if (status & MCI_CMD_IRQ && cmd)
mmci_cmd_irq(host, cmd, status);
ret = 1;
@@ -429,17 +1187,27 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
spin_unlock(&host->lock);
+ if (sdio_irq)
+ mmc_signal_sdio_irq(host->mmc);
+
return IRQ_RETVAL(ret);
}
static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct mmci_host *host = mmc_priv(mmc);
+ struct variant_data *variant = host->variant;
unsigned long flags;
WARN_ON(host->mrq != NULL);
- if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
+ if (mrq->data &&
+ (!variant->non_power_of_2_blksize ||
+#ifdef CONFIG_ARCH_U8500
+ !cpu_is_u8500v20_or_later() ||
+#endif
+ (mmc->card && mmc_card_ddr_mode(mmc->card))) &&
+ !is_power_of_2(mrq->data->blksz)) {
dev_err(mmc_dev(mmc), "unsupported block size (%d bytes)\n",
mrq->data->blksz);
mrq->cmd->error = -EINVAL;
@@ -462,41 +1230,81 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct mmci_host *host = mmc_priv(mmc);
+ struct variant_data *variant = host->variant;
u32 pwr = 0;
unsigned long flags;
+ int ret;
+
+ if (host->plat->vdd_handler)
+ host->plat->vdd_handler(mmc_dev(mmc), ios->vdd,
+ ios->power_mode);
switch (ios->power_mode) {
case MMC_POWER_OFF:
- if(host->vcc &&
- regulator_is_enabled(host->vcc))
- regulator_disable(host->vcc);
+ if (host->vcard)
+ ret = mmc_regulator_set_ocr(mmc, host->vcard, 0);
break;
case MMC_POWER_UP:
-#ifdef CONFIG_REGULATOR
- if (host->vcc)
- /* This implicitly enables the regulator */
- mmc_regulator_set_ocr(host->vcc, ios->vdd);
-#endif
+ if (host->vcard) {
+ ret = mmc_regulator_set_ocr(mmc, host->vcard, ios->vdd);
+ if (ret) {
+ dev_err(mmc_dev(mmc), "unable to set OCR\n");
+ /*
+ * The .set_ios() function in the mmc_host_ops
+ * struct return void, and failing to set the
+ * power should be rare so we print an error
+ * and return here.
+ */
+ return;
+ }
+ }
+
/*
- * The translate_vdd function is not used if you have
- * an external regulator, or your design is really weird.
- * Using it would mean sending in power control BOTH using
- * a regulator AND the 4 MMCIPWR bits. If we don't have
- * a regulator, we might have some other platform specific
- * power control behind this translate function.
+ * The ST Micro variant doesn't have the PL180s MCI_PWR_UP
+ * and instead uses MCI_PWR_ON so apply whatever value is
+ * configured in the variant data.
*/
- if (!host->vcc && host->plat->translate_vdd)
- pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
- /* The ST version does not have this, fall through to POWER_ON */
- if (host->hw_designer != AMBA_VENDOR_ST) {
- pwr |= MCI_PWR_UP;
- break;
- }
+ pwr |= variant->pwrreg_powerup;
+
+ break;
case MMC_POWER_ON:
pwr |= MCI_PWR_ON;
break;
}
+ if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) {
+ /*
+ * The ST Micro variant has some additional bits
+ * indicating signal direction for the signals in
+ * the SD/MMC bus and feedback-clock usage. The
+ * fall-throughs in the code below are intentional.
+ */
+
+ if (host->plat->sigdir & MMCI_ST_DIRFBCLK)
+ pwr |= MCI_ST_FBCLKEN;
+ if (host->plat->sigdir & MMCI_ST_DIRCMD)
+ pwr |= MCI_ST_CMDDIREN;
+
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_8:
+ if (host->plat->sigdir & MMCI_ST_DIRDAT74)
+ pwr |= MCI_ST_DATA74DIREN;
+ case MMC_BUS_WIDTH_4:
+ if (host->plat->sigdir & MMCI_ST_DIRDAT31)
+ pwr |= MCI_ST_DATA31DIREN;
+ if (host->plat->sigdir & MMCI_ST_DIRDAT2)
+ pwr |= MCI_ST_DATA2DIREN;
+ case MMC_BUS_WIDTH_1:
+ if (host->plat->sigdir & MMCI_ST_DIRDAT0)
+ pwr |= MCI_ST_DATA0DIREN;
+ break;
+ default:
+ dev_err(mmc_dev(mmc), "unsupported MMC bus width %d\n",
+ ios->bus_width);
+ break;
+ }
+ }
+
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
if (host->hw_designer != AMBA_VENDOR_ST)
pwr |= MCI_ROD;
@@ -505,7 +1313,8 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* The ST Micro variant use the ROD bit for something
* else and only has OD (Open Drain).
*/
- pwr |= MCI_OD;
+ if (mmc->card && (mmc->card->type == MMC_TYPE_MMC))
+ pwr |= MCI_OD;
}
}
@@ -528,18 +1337,23 @@ static int mmci_get_ro(struct mmc_host *mmc)
if (host->gpio_wp == -ENOSYS)
return -ENOSYS;
- return gpio_get_value(host->gpio_wp);
+ return gpio_get_value_cansleep(host->gpio_wp);
}
static int mmci_get_cd(struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
+ struct mmci_platform_data *plat = host->plat;
unsigned int status;
- if (host->gpio_cd == -ENOSYS)
- status = host->plat->status(mmc_dev(host->mmc));
- else
- status = !gpio_get_value(host->gpio_cd);
+ if (host->gpio_cd == -ENOSYS) {
+ if (!plat->status)
+ return 1; /* Assume always present */
+
+ status = plat->status(mmc_dev(host->mmc));
+ } else
+ status = !!gpio_get_value_cansleep(host->gpio_cd)
+ ^ plat->cd_invert;
/*
* Use positive logic throughout - status is zero for no card,
@@ -548,28 +1362,156 @@ static int mmci_get_cd(struct mmc_host *mmc)
return status;
}
+#ifdef CONFIG_PM
+static int mmci_enable(struct mmc_host *mmc)
+{
+ unsigned long flags;
+ struct mmci_host *host = mmc_priv(mmc);
+ int ret = 0;
+
+ clk_enable(host->clk);
+ if (host->vcc) {
+ ret = regulator_enable(host->vcc);
+ if (ret) {
+ dev_err(mmc_dev(host->mmc),
+ "failed to enable regulator %s\n",
+ host->plat->vcc);
+ return ret;
+ }
+ }
+
+ if (pm_runtime_get_sync(mmc->parent) < 0)
+ dev_err(mmc_dev(mmc), "failed pm_runtime_get_sync\n");
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Restore registers for POWER, CLOCK and IRQMASK0 */
+ writel(host->clk_reg, host->base + MMCICLOCK);
+ writel(host->pwr_reg, host->base + MMCIPOWER);
+ writel(host->irqmask0_reg, host->base + MMCIMASK0);
+
+ if (host->variant->sdio &&
+ host->mmc->card &&
+ mmc_card_sdio(host->mmc->card)) {
+ /*
+ * The ST Micro variants has a special bit in the DATACTRL
+ * register to enable SDIO mode. This bit must be set otherwise
+ * no SDIO interrupts can be received.
+ */
+ writel(MCI_ST_DPSM_SDIOEN, host->base + MMCIDATACTRL);
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ /* Restore settings done by the vdd_handler. */
+ if (host->plat->vdd_handler)
+ host->plat->vdd_handler(mmc_dev(mmc),
+ mmc->ios.vdd,
+ mmc->ios.power_mode);
+
+ /*
+ * To be able to handle specific wake up scenarios for each host,
+ * the following function shall be implemented.
+ */
+ if (host->plat->wakeup_handler)
+ host->plat->wakeup_handler(host->mmc, true);
+
+ return ret;
+}
+
+static int mmci_disable(struct mmc_host *mmc, int lazy)
+{
+ unsigned long flags;
+ struct mmci_host *host = mmc_priv(mmc);
+
+ /*
+ * To be able to handle specific shutdown scenarios for each host,
+ * the following function shall be implemented.
+ */
+ if (host->plat->wakeup_handler)
+ host->plat->wakeup_handler(host->mmc, false);
+
+ /*
+ * Let the vdd_handler act on a POWER_OFF to potentially do some
+ * power save actions.
+ */
+ if (host->plat->vdd_handler)
+ host->plat->vdd_handler(mmc_dev(mmc), 0, MMC_POWER_OFF);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Save registers for POWER, CLOCK and IRQMASK0 */
+ host->irqmask0_reg = readl(host->base + MMCIMASK0);
+ host->pwr_reg = readl(host->base + MMCIPOWER);
+ host->clk_reg = readl(host->base + MMCICLOCK);
+
+ /*
+ * Make sure we do not get any interrupts when we disabled the
+ * clock and the regulator and as well make sure to clear the
+ * registers for clock and power.
+ */
+ writel(0, host->base + MMCIMASK0);
+ writel(0, host->base + MMCIPOWER);
+ writel(0, host->base + MMCICLOCK);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (pm_runtime_put_sync(mmc->parent) < 0)
+ dev_err(mmc_dev(mmc), "failed pm_runtime_put_sync\n");
+
+ clk_disable(host->clk);
+ if (host->vcc) {
+ regulator_disable(host->vcc);
+ }
+
+ return 0;
+}
+#else
+#define mmci_enable NULL
+#define mmci_disable NULL
+#endif
+
+static irqreturn_t mmci_cd_irq(int irq, void *dev_id)
+{
+ struct mmci_host *host = dev_id;
+
+ mmc_detect_change(host->mmc, msecs_to_jiffies(500));
+
+ return IRQ_HANDLED;
+}
+
+static void mmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ unsigned long flags;
+ unsigned int mask0;
+ struct mmci_host *host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ mask0 = readl(host->base + MMCIMASK0);
+ if (enable)
+ mask0 |= MCI_SDIOIT;
+ else
+ mask0 &= ~MCI_SDIOIT;
+ writel(mask0, host->base + MMCIMASK0);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
static const struct mmc_host_ops mmci_ops = {
.request = mmci_request,
.set_ios = mmci_set_ios,
.get_ro = mmci_get_ro,
.get_cd = mmci_get_cd,
+ .enable = mmci_enable,
+ .disable = mmci_disable,
+ .enable_sdio_irq = mmci_enable_sdio_irq,
};
-static void mmci_check_status(unsigned long data)
-{
- struct mmci_host *host = (struct mmci_host *)data;
- unsigned int status = mmci_get_cd(host->mmc);
-
- if (status ^ host->oldstat)
- mmc_detect_change(host->mmc, 0);
-
- host->oldstat = status;
- mod_timer(&host->timer, jiffies + HZ);
-}
-
static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
{
struct mmci_platform_data *plat = dev->dev.platform_data;
+ struct variant_data *variant = id->data;
struct mmci_host *host;
struct mmc_host *mmc;
int ret;
@@ -592,9 +1534,16 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
host = mmc_priv(mmc);
host->mmc = mmc;
+ host->plat = plat;
+ host->variant = variant;
host->gpio_wp = -ENOSYS;
host->gpio_cd = -ENOSYS;
+ host->gpio_cd_irq = -1;
+
+ host->irqmask0_reg = 0;
+ host->pwr_reg = 0;
+ host->clk_reg = 0;
host->hw_designer = amba_manf(dev);
host->hw_revision = amba_rev(dev);
@@ -608,11 +1557,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
goto host_free;
}
- ret = clk_enable(host->clk);
- if (ret)
- goto clk_free;
-
- host->plat = plat;
host->mclk = clk_get_rate(host->clk);
/*
* According to the spec, mclk is max 100 MHz,
@@ -622,15 +1566,16 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
if (host->mclk > 100000000) {
ret = clk_set_rate(host->clk, 100000000);
if (ret < 0)
- goto clk_disable;
+ goto clk_free;
host->mclk = clk_get_rate(host->clk);
dev_dbg(mmc_dev(mmc), "eventual mclk rate: %u Hz\n",
host->mclk);
}
+ host->phybase = dev->res.start;
host->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!host->base) {
ret = -ENOMEM;
- goto clk_disable;
+ goto clk_free;
}
mmc->ops = &mmci_ops;
@@ -649,14 +1594,31 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
mmc->f_max = min(host->mclk, fmax);
dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
-#ifdef CONFIG_REGULATOR
- /* If we're using the regulator framework, try to fetch a regulator */
- host->vcc = regulator_get(&dev->dev, "vmmc");
- if (IS_ERR(host->vcc))
- host->vcc = NULL;
- else {
- int mask = mmc_regulator_get_ocrmask(host->vcc);
+ /* Host regulator */
+ if (plat->vcc) {
+ host->vcc = regulator_get(&dev->dev, plat->vcc);
+ ret = IS_ERR(host->vcc);
+ if (ret) {
+ host->vcc = NULL;
+ goto clk_free;
+ }
+ dev_dbg(mmc_dev(host->mmc), "fetched regulator %s\n",
+ plat->vcc);
+ }
+ /* Card regulator (could be a level shifter) */
+ if (plat->vcard) {
+ int mask;
+ host->vcard = regulator_get(&dev->dev, plat->vcard);
+ ret = IS_ERR(host->vcard);
+ if (ret) {
+ host->vcard = NULL;
+ goto vcc_free;
+ }
+ dev_dbg(mmc_dev(host->mmc), "fetched regulator %s\n",
+ plat->vcard);
+
+ mask = mmc_regulator_get_ocrmask(host->vcard);
if (mask < 0)
dev_err(&dev->dev, "error getting OCR mask (%d)\n",
mask);
@@ -667,30 +1629,42 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
"Provided ocr_mask/setpower will not be used "
"(using regulator instead)\n");
}
- }
-#endif
- /* Fall back to platform data if no regulator is found */
- if (host->vcc == NULL)
+
+ } else {
+ /* Use platform data if no regulator for vcard is used */
mmc->ocr_avail = plat->ocr_mask;
+ }
+
+ /* Use platform capabilities */
mmc->caps = plat->capabilities;
+ /* Set disable timeout if supported */
+ if (mmc->caps & MMC_CAP_DISABLE)
+ mmc_set_disable_delay(mmc, plat->disable);
+
+ /* We support these PM capabilities. */
+ mmc->pm_caps = MMC_PM_KEEP_POWER;
+
/*
* We can do SGIO
*/
- mmc->max_hw_segs = 16;
- mmc->max_phys_segs = NR_SG;
+ mmc->max_segs = NR_SG;
/*
- * Since we only have a 16-bit data length register, we must
- * ensure that we don't exceed 2^16-1 bytes in a single request.
+ * Since only a certain number of bits are valid in the data length
+ * register, we must ensure that we don't exceed 2^num-1 bytes in a
+ * single request.
*/
- mmc->max_req_size = 65535;
+ mmc->max_req_size = (1 << variant->datalength_bits) - 1;
/*
- * Set the maximum segment size. Since we aren't doing DMA
- * (yet) we are only limited by the data length register.
+ * Set the maximum segment size. Right now DMA sets the
+ * limit and not the data length register. Thus until the DMA
+ * driver not handles this, the segment size is limited by DMA.
+ * DMA limit: src_addr_width x (64 KB -1). src_addr_width
+ * can be 1.
*/
- mmc->max_seg_size = mmc->max_req_size;
+ mmc->max_seg_size = 65535;
/*
* Block size can be up to 2048 bytes, but must be a power of two.
@@ -704,10 +1678,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
spin_lock_init(&host->lock);
- writel(0, host->base + MMCIMASK0);
- writel(0, host->base + MMCIMASK1);
- writel(0xfff, host->base + MMCICLEAR);
-
if (gpio_is_valid(plat->gpio_cd)) {
ret = gpio_request(plat->gpio_cd, DRIVER_NAME " (cd)");
if (ret == 0)
@@ -716,6 +1686,13 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
host->gpio_cd = plat->gpio_cd;
else if (ret != -ENOSYS)
goto err_gpio_cd;
+
+ ret = request_any_context_irq(gpio_to_irq(plat->gpio_cd),
+ mmci_cd_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ DRIVER_NAME " (cd)", host);
+ if (ret >= 0)
+ host->gpio_cd_irq = gpio_to_irq(plat->gpio_cd);
}
if (gpio_is_valid(plat->gpio_wp)) {
ret = gpio_request(plat->gpio_wp, DRIVER_NAME " (wp)");
@@ -727,45 +1704,70 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
goto err_gpio_wp;
}
- ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
+ if ((host->plat->status || host->gpio_cd != -ENOSYS)
+ && host->gpio_cd_irq < 0)
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+ mmci_setup_dma(host);
+
+ ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED,
+ DRIVER_NAME " (cmd)", host);
if (ret)
goto unmap;
- ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host);
- if (ret)
- goto irq0_free;
+ if (dev->irq[1] == NO_IRQ)
+ host->singleirq = true;
+ else {
+ ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED,
+ DRIVER_NAME " (pio)", host);
+ if (ret)
+ goto irq0_free;
+ }
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ /* Prepare IRQMASK0 */
+ host->irqmask0_reg = MCI_IRQENABLE;
+ if (host->variant->broken_blockend)
+ host->irqmask0_reg &= ~MCI_DATABLOCKEND;
amba_set_drvdata(dev, mmc);
- host->oldstat = mmci_get_cd(host->mmc);
+
+ pm_runtime_enable(mmc->parent);
+ if (pm_runtime_get_sync(mmc->parent) < 0)
+ dev_err(mmc_dev(mmc), "failed pm_runtime_get_sync\n");
+ if (pm_runtime_put_sync(mmc->parent) < 0)
+ dev_err(mmc_dev(mmc), "failed pm_runtime_put_sync\n");
mmc_add_host(mmc);
- dev_info(&dev->dev, "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n",
- mmc_hostname(mmc), amba_rev(dev), amba_config(dev),
- (unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]);
+ dev_info(&dev->dev,
+ "%s: MMCI/PL180 manf %x rev %x cfg %02x at 0x%016llx\n",
+ mmc_hostname(mmc), amba_manf(dev), amba_rev(dev),
+ amba_config(dev), (unsigned long long)dev->res.start);
+ dev_info(&dev->dev, "IRQ %d, %d (pio)\n", dev->irq[0], dev->irq[1]);
- init_timer(&host->timer);
- host->timer.data = (unsigned long)host;
- host->timer.function = mmci_check_status;
- host->timer.expires = jiffies + HZ;
- add_timer(&host->timer);
+ /* Ugly hack for u8500_sdio_detect_card, to be removed soon. */
+ sdio_host_ptr = host;
+
+ mmci_debugfs_create(host);
return 0;
irq0_free:
free_irq(dev->irq[0], host);
unmap:
+ mmci_disable_dma(host);
if (host->gpio_wp != -ENOSYS)
gpio_free(host->gpio_wp);
err_gpio_wp:
+ if (host->gpio_cd_irq >= 0)
+ free_irq(host->gpio_cd_irq, host);
if (host->gpio_cd != -ENOSYS)
gpio_free(host->gpio_cd);
err_gpio_cd:
iounmap(host->base);
- clk_disable:
- clk_disable(host->clk);
+ vcc_free:
+ if (host->vcc)
+ regulator_put(host->vcc);
clk_free:
clk_put(host->clk);
host_free:
@@ -785,8 +1787,12 @@ static int __devexit mmci_remove(struct amba_device *dev)
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
- del_timer_sync(&host->timer);
+ pm_runtime_disable(mmc->parent);
+ if (host->vcc)
+ regulator_enable(host->vcc);
+
+ mmci_debugfs_remove(host);
mmc_remove_host(mmc);
writel(0, host->base + MMCIMASK0);
@@ -795,11 +1801,15 @@ static int __devexit mmci_remove(struct amba_device *dev)
writel(0, host->base + MMCICOMMAND);
writel(0, host->base + MMCIDATACTRL);
+ mmci_disable_dma(host);
free_irq(dev->irq[0], host);
- free_irq(dev->irq[1], host);
+ if (!host->singleirq)
+ free_irq(dev->irq[1], host);
if (host->gpio_wp != -ENOSYS)
gpio_free(host->gpio_wp);
+ if (host->gpio_cd_irq >= 0)
+ free_irq(host->gpio_cd_irq, host);
if (host->gpio_cd != -ENOSYS)
gpio_free(host->gpio_cd);
@@ -807,9 +1817,15 @@ static int __devexit mmci_remove(struct amba_device *dev)
clk_disable(host->clk);
clk_put(host->clk);
- if (regulator_is_enabled(host->vcc))
+ if (host->vcc) {
regulator_disable(host->vcc);
- regulator_put(host->vcc);
+ regulator_put(host->vcc);
+ }
+
+ if (host->vcard) {
+ mmc_regulator_set_ocr(mmc, host->vcard, 0);
+ regulator_put(host->vcard);
+ }
mmc_free_host(mmc);
@@ -826,11 +1842,38 @@ static int mmci_suspend(struct amba_device *dev, pm_message_t state)
int ret = 0;
if (mmc) {
- struct mmci_host *host = mmc_priv(mmc);
+ /*
+ * The host must be claimed to prevent request handling etc.
+ * Also make sure to not sleep, since we must return with a
+ * response quite quickly.
+ */
+ if (mmc_try_claim_host(mmc)) {
+ struct mmci_host *host = mmc_priv(mmc);
+
+ if (mmc->enabled) {
+ /*
+ * Do not suspend if the host has not been
+ * disabled.
+ */
+ mmc_do_release_host(mmc);
+ ret = -EBUSY;
+ } else if ((!(host->plat->pm_flags & MMC_PM_KEEP_POWER))
+ && (!(mmc->pm_flags & MMC_PM_KEEP_POWER))) {
+ /* Cut the power to the card to save current. */
+ mmc_host_enable(mmc);
+ mmc_power_save_host(mmc);
+ mmc_host_disable(mmc);
+ host->pwr_reg = 0;
+ host->clk_reg = 0;
+ }
- ret = mmc_suspend_host(mmc);
- if (ret == 0)
- writel(0, host->base + MMCIMASK0);
+ } else {
+ /*
+ * We did not manage to claim the host, thus someone
+ * is still using it. Do not suspend.
+ */
+ ret = -EBUSY;
+ }
}
return ret;
@@ -839,17 +1882,26 @@ static int mmci_suspend(struct amba_device *dev, pm_message_t state)
static int mmci_resume(struct amba_device *dev)
{
struct mmc_host *mmc = amba_get_drvdata(dev);
- int ret = 0;
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ /* We expect the host to be claimed. */
+ WARN_ON(!mmc->claimed);
- ret = mmc_resume_host(mmc);
+ /* Restore power and re-initialize the card. */
+ if ((!(host->plat->pm_flags & MMC_PM_KEEP_POWER)) &&
+ (!(mmc->pm_flags & MMC_PM_KEEP_POWER))) {
+ mmc_host_enable(mmc);
+ mmc_power_restore_host(mmc);
+ mmc_host_disable(mmc);
+ }
+
+ /* Release the host to provide access to it again. */
+ mmc_do_release_host(mmc);
}
- return ret;
+ return 0;
}
#else
#define mmci_suspend NULL
@@ -860,19 +1912,28 @@ static struct amba_id mmci_ids[] = {
{
.id = 0x00041180,
.mask = 0x000fffff,
+ .data = &variant_arm,
},
{
.id = 0x00041181,
.mask = 0x000fffff,
+ .data = &variant_arm,
},
/* ST Micro variants */
{
.id = 0x00180180,
.mask = 0x00ffffff,
+ .data = &variant_u300,
},
{
.id = 0x00280180,
.mask = 0x00ffffff,
+ .data = &variant_u300,
+ },
+ {
+ .id = 0x00480180,
+ .mask = 0x00ffffff,
+ .data = &variant_ux500,
},
{ 0, 0 },
};
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index d77062e5e3a..87d35cc1fea 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -11,15 +11,15 @@
#define MCI_PWR_OFF 0x00
#define MCI_PWR_UP 0x02
#define MCI_PWR_ON 0x03
-#define MCI_DATA2DIREN (1 << 2)
-#define MCI_CMDDIREN (1 << 3)
-#define MCI_DATA0DIREN (1 << 4)
-#define MCI_DATA31DIREN (1 << 5)
+#define MCI_ST_DATA2DIREN (1 << 2)
+#define MCI_ST_CMDDIREN (1 << 3)
+#define MCI_ST_DATA0DIREN (1 << 4)
+#define MCI_ST_DATA31DIREN (1 << 5)
#define MCI_OD (1 << 6)
#define MCI_ROD (1 << 7)
/* The ST Micro version does not have ROD */
-#define MCI_FBCLKEN (1 << 7)
-#define MCI_DATA74DIREN (1 << 8)
+#define MCI_ST_FBCLKEN (1 << 7)
+#define MCI_ST_DATA74DIREN (1 << 8)
#define MMCICLOCK 0x004
#define MCI_CLK_ENABLE (1 << 8)
@@ -28,8 +28,8 @@
#define MCI_4BIT_BUS (1 << 11)
/* 8bit wide buses supported in ST Micro versions */
#define MCI_ST_8BIT_BUS (1 << 12)
-/* HW flow control on the ST Micro version */
-#define MCI_ST_FCEN (1 << 13)
+#define MCI_NEG_EDGE (1 << 13)
+#define MCI_CLK_INV (1 << 15)
#define MMCIARGUMENT 0x008
#define MMCICOMMAND 0x00c
@@ -56,10 +56,16 @@
#define MCI_DPSM_MODE (1 << 2)
#define MCI_DPSM_DMAENABLE (1 << 3)
#define MCI_DPSM_BLOCKSIZE (1 << 4)
-#define MCI_DPSM_RWSTART (1 << 8)
-#define MCI_DPSM_RWSTOP (1 << 9)
-#define MCI_DPSM_RWMOD (1 << 10)
-#define MCI_DPSM_SDIOEN (1 << 11)
+/* Control register extensions in the ST Micro U300 and Ux500 versions */
+#define MCI_ST_DPSM_RWSTART (1 << 8)
+#define MCI_ST_DPSM_RWSTOP (1 << 9)
+#define MCI_ST_DPSM_RWMOD (1 << 10)
+#define MCI_ST_DPSM_SDIOEN (1 << 11)
+/* Control register extensions in the ST Micro Ux500 versions */
+#define MCI_ST_DPSM_DMAREQCTL (1 << 12)
+#define MCI_ST_DPSM_DBOOTMODEEN (1 << 13)
+#define MCI_ST_DPSM_BUSYMODE (1 << 14)
+#define MCI_ST_DPSM_DDRMODE (1 << 15)
#define MMCIDATACNT 0x030
#define MMCISTATUS 0x034
@@ -133,20 +139,28 @@
#define MCI_IRQENABLE \
(MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \
MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \
- MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATABLOCKENDMASK)
+ MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK)
-/*
- * The size of the FIFO in bytes.
- */
-#define MCI_FIFOSIZE (16*4)
-
-#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
+#define MCI_DATA_ERR \
+ (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)
+#define MCI_DATA_IRQ (MCI_DATA_ERR|MCI_DATAEND|MCI_DATABLOCKEND)
+#define MCI_CMD_ERR (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)
+#define MCI_CMD_IRQ (MCI_CMD_ERR|MCI_CMDRESPEND|MCI_CMDSENT)
+
+/* These interrupts are directed to IRQ1 when two IRQ lines are available */
+#define MCI_IRQ1MASK \
+ (MCI_RXFIFOHALFFULLMASK | MCI_RXDATAAVLBLMASK | \
+ MCI_TXFIFOHALFEMPTYMASK)
-#define NR_SG 16
+#define NR_SG 128
struct clk;
+struct variant_data;
+struct dma_chan;
+struct dma_async_tx_descriptor;
struct mmci_host {
+ phys_addr_t phybase;
void __iomem *base;
struct mmc_request *mrq;
struct mmc_command *cmd;
@@ -155,6 +169,8 @@ struct mmci_host {
struct clk *clk;
int gpio_cd;
int gpio_wp;
+ int gpio_cd_irq;
+ bool singleirq;
unsigned int data_xfered;
@@ -164,6 +180,7 @@ struct mmci_host {
unsigned int cclk;
u32 pwr;
struct mmci_platform_data *plat;
+ struct variant_data *variant;
u8 hw_designer;
u8 hw_revision:4;
@@ -171,42 +188,34 @@ struct mmci_host {
struct timer_list timer;
unsigned int oldstat;
- unsigned int sg_len;
+ bool last_blockend;
+ bool dataend;
+
+ /* register cache */
+ unsigned int irqmask0_reg;
+ unsigned int pwr_reg;
+ unsigned int clk_reg;
/* pio stuff */
- struct scatterlist *sg_ptr;
- unsigned int sg_off;
+ struct sg_mapping_iter sg_miter;
unsigned int size;
+ unsigned int cache;
+ unsigned int cache_len;
+
struct regulator *vcc;
+ struct regulator *vcard;
+
+ /* DMA stuff */
+ bool dma_enable;
+ bool dma_on_current_xfer;
+#ifdef CONFIG_DMA_ENGINE
+ struct dma_chan *dma_rx_channel;
+ struct dma_chan *dma_tx_channel;
+ struct dma_async_tx_descriptor *dma_desc;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debug_regs;
+#endif
};
-static inline void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
-{
- /*
- * Ideally, we want the higher levels to pass us a scatter list.
- */
- host->sg_len = data->sg_len;
- host->sg_ptr = data->sg;
- host->sg_off = 0;
-}
-
-static inline int mmci_next_sg(struct mmci_host *host)
-{
- host->sg_ptr++;
- host->sg_off = 0;
- return --host->sg_len;
-}
-
-static inline char *mmci_kmap_atomic(struct mmci_host *host, unsigned long *flags)
-{
- struct scatterlist *sg = host->sg_ptr;
-
- local_irq_save(*flags);
- return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
-}
-
-static inline void mmci_kunmap_atomic(struct mmci_host *host, void *buffer, unsigned long *flags)
-{
- kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
- local_irq_restore(*flags);
-}
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 24e09454e52..dee02020489 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -1200,8 +1200,7 @@ msmsdcc_probe(struct platform_device *pdev)
mmc->caps |= MMC_CAP_SDIO_IRQ;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
- mmc->max_phys_segs = NR_SG;
- mmc->max_hw_segs = NR_SG;
+ mmc->max_segs = NR_SG;
mmc->max_blk_size = 4096; /* MCI_DATA_CTL BLOCKSIZE up to 4096 */
mmc->max_blk_count = 65536;
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index 366eefa77c5..a5bf60e01af 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -742,8 +742,7 @@ static int __init mvsd_probe(struct platform_device *pdev)
mmc->max_blk_size = 2048;
mmc->max_blk_count = 65535;
- mmc->max_hw_segs = 1;
- mmc->max_phys_segs = 1;
+ mmc->max_segs = 1;
mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index d9d4a72e0ec..6feec7f5172 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -786,8 +786,7 @@ static int mxcmci_probe(struct platform_device *pdev)
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
/* MMC core transfer sizes tunable parameters */
- mmc->max_hw_segs = 64;
- mmc->max_phys_segs = 64;
+ mmc->max_segs = 64;
mmc->max_blk_size = 2048;
mmc->max_blk_count = 65535;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index d98ddcfac5e..0c7e37f496e 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -1335,8 +1335,7 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
* NOTE max_seg_size assumption that small blocks aren't
* normally used (except e.g. for reading SD registers).
*/
- mmc->max_phys_segs = 32;
- mmc->max_hw_segs = 32;
+ mmc->max_segs = 32;
mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */
mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index b032828c612..674269f820c 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -248,9 +248,9 @@ static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on,
mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
if (power_on)
- ret = mmc_regulator_set_ocr(host->vcc, vdd);
+ ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
else
- ret = mmc_regulator_set_ocr(host->vcc, 0);
+ ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
if (mmc_slot(host).after_set_reg)
mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
@@ -289,18 +289,23 @@ static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on,
* chips/cards need an interface voltage rail too.
*/
if (power_on) {
- ret = mmc_regulator_set_ocr(host->vcc, vdd);
+ ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
/* Enable interface voltage rail, if needed */
if (ret == 0 && host->vcc_aux) {
ret = regulator_enable(host->vcc_aux);
if (ret < 0)
- ret = mmc_regulator_set_ocr(host->vcc, 0);
+ ret = mmc_regulator_set_ocr(host->mmc,
+ host->vcc, 0);
}
} else {
+ /* Shut down the rail */
if (host->vcc_aux)
ret = regulator_disable(host->vcc_aux);
- if (ret == 0)
- ret = mmc_regulator_set_ocr(host->vcc, 0);
+ if (!ret) {
+ /* Then proceed to shut down the local regulator */
+ ret = mmc_regulator_set_ocr(host->mmc,
+ host->vcc, 0);
+ }
}
if (mmc_slot(host).after_set_reg)
@@ -341,9 +346,9 @@ static int omap_hsmmc_23_set_sleep(struct device *dev, int slot, int sleep,
if (cardsleep) {
/* VCC can be turned off if card is asleep */
if (sleep)
- err = mmc_regulator_set_ocr(host->vcc, 0);
+ err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
else
- err = mmc_regulator_set_ocr(host->vcc, vdd);
+ err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
} else
err = regulator_set_mode(host->vcc, mode);
if (err)
@@ -2085,8 +2090,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
/* Since we do only SG emulation, we can have as many segs
* as we want. */
- mmc->max_phys_segs = 1024;
- mmc->max_hw_segs = 1024;
+ mmc->max_segs = 1024;
mmc->max_blk_size = 512; /* Block Length at max can be 1024 */
mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 0a4e43f3714..7257738fd7d 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -99,14 +99,25 @@ static inline void pxamci_init_ocr(struct pxamci_host *host)
}
}
-static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd)
+static inline int pxamci_set_power(struct pxamci_host *host,
+ unsigned char power_mode,
+ unsigned int vdd)
{
int on;
-#ifdef CONFIG_REGULATOR
- if (host->vcc)
- mmc_regulator_set_ocr(host->vcc, vdd);
-#endif
+ if (host->vcc) {
+ int ret;
+
+ if (power_mode == MMC_POWER_UP) {
+ ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
+ if (ret)
+ return ret;
+ } else if (power_mode == MMC_POWER_OFF) {
+ ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
+ if (ret)
+ return ret;
+ }
+ }
if (!host->vcc && host->pdata &&
gpio_is_valid(host->pdata->gpio_power)) {
on = ((1 << vdd) & host->pdata->ocr_mask);
@@ -115,6 +126,8 @@ static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd)
}
if (!host->vcc && host->pdata && host->pdata->setpower)
host->pdata->setpower(mmc_dev(host->mmc), vdd);
+
+ return 0;
}
static void pxamci_stop_clock(struct pxamci_host *host)
@@ -490,9 +503,21 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
if (host->power_mode != ios->power_mode) {
+ int ret;
+
host->power_mode = ios->power_mode;
- pxamci_set_power(host, ios->vdd);
+ ret = pxamci_set_power(host, ios->power_mode, ios->vdd);
+ if (ret) {
+ dev_err(mmc_dev(mmc), "unable to set power\n");
+ /*
+ * The .set_ios() function in the mmc_host_ops
+ * struct return void, and failing to set the
+ * power should be rare so we print an error and
+ * return here.
+ */
+ return;
+ }
if (ios->power_mode == MMC_POWER_ON)
host->cmdat |= CMDAT_INIT;
@@ -503,8 +528,8 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
host->cmdat &= ~CMDAT_SD_4DAT;
- pr_debug("PXAMCI: clkrt = %x cmdat = %x\n",
- host->clkrt, host->cmdat);
+ dev_dbg(mmc_dev(mmc), "PXAMCI: clkrt = %x cmdat = %x\n",
+ host->clkrt, host->cmdat);
}
static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable)
@@ -576,7 +601,7 @@ static int pxamci_probe(struct platform_device *pdev)
* We can do SG-DMA, but we don't because we never know how much
* data we successfully wrote to the card.
*/
- mmc->max_phys_segs = NR_SG;
+ mmc->max_segs = NR_SG;
/*
* Our hardware DMA can handle a maximum of one page per SG entry.
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 2e16e0a90a5..262b3882cfc 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -1736,8 +1736,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev)
mmc->max_req_size = 4095 * 512;
mmc->max_seg_size = mmc->max_req_size;
- mmc->max_phys_segs = 128;
- mmc->max_hw_segs = 128;
+ mmc->max_segs = 128;
dbg(host, dbg_debug,
"probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index c6d1bd8d4ac..0f77a6d27bd 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1823,12 +1823,11 @@ int sdhci_add_host(struct sdhci_host *host)
* can do scatter/gather or not.
*/
if (host->flags & SDHCI_USE_ADMA)
- mmc->max_hw_segs = 128;
+ mmc->max_segs = 128;
else if (host->flags & SDHCI_USE_SDMA)
- mmc->max_hw_segs = 1;
+ mmc->max_segs = 1;
else /* PIO */
- mmc->max_hw_segs = 128;
- mmc->max_phys_segs = 128;
+ mmc->max_segs = 128;
/*
* Maximum number of sectors in one transfer. Limited by DMA boundary
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 5d3f824bb5a..0f06b800281 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -846,8 +846,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
mmc->caps = MMC_CAP_MMC_HIGHSPEED;
if (pd->caps)
mmc->caps |= pd->caps;
- mmc->max_phys_segs = 128;
- mmc->max_hw_segs = 128;
+ mmc->max_segs = 128;
mmc->max_blk_size = 512;
mmc->max_blk_count = 65535;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index cec99958b65..457c26ea09d 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -978,11 +978,10 @@ static int tifm_sd_probe(struct tifm_dev *sock)
mmc->f_max = 24000000;
mmc->max_blk_count = 2048;
- mmc->max_hw_segs = mmc->max_blk_count;
+ mmc->max_segs = mmc->max_blk_count;
mmc->max_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE);
mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size;
mmc->max_req_size = mmc->max_seg_size;
- mmc->max_phys_segs = mmc->max_hw_segs;
sock->card_event = tifm_sd_card_event;
sock->data_event = tifm_sd_data_event;
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index 19f2d72dbca..9ed84ddb478 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -1050,8 +1050,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host)
mmc->ops = &via_sdc_ops;
/*Hardware cannot do scatter lists*/
- mmc->max_hw_segs = 1;
- mmc->max_phys_segs = 1;
+ mmc->max_segs = 1;
mmc->max_blk_size = VIA_CRDR_MAX_BLOCK_LENGTH;
mmc->max_blk_count = VIA_CRDR_MAX_BLOCK_COUNT;
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index 0012f5d13d2..7fca0a386ba 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -1235,8 +1235,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev)
* Maximum number of segments. Worst case is one sector per segment
* so this will be 64kB/512.
*/
- mmc->max_hw_segs = 128;
- mmc->max_phys_segs = 128;
+ mmc->max_segs = 128;
/*
* Maximum request size. Also limited by 64KiB buffer.
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index ffc3720929f..391ccf6485b 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,3 +1,10 @@
+config MTD_NAND_IDS
+ tristate "Include chip ids for known NAND devices."
+ depends on MTD
+ help
+ Useful for NAND drivers that do not use the NAND subsystem but
+ still like to take advantage of the known chip information.
+
config MTD_NAND_ECC
tristate
@@ -122,9 +129,6 @@ config MTD_NAND_OMAP_PREFETCH_DMA
or in DMA interrupt mode.
Say y for DMA mode or MPU mode will be used
-config MTD_NAND_IDS
- tristate
-
config MTD_NAND_RICOH
tristate "Ricoh xD card reader"
default n
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 5bcc34a5115..a98c45eba94 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3021,6 +3021,44 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
return 0;
}
+static void nand_panic_wait(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ int i;
+
+ if (chip->state != FL_READY)
+ for (i = 0; i < 40; i++) {
+ if (chip->dev_ready(mtd))
+ break;
+ mdelay(10);
+ }
+ chip->state = FL_READY;
+}
+
+static int nand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct nand_chip *chip = mtd->priv;
+ int ret;
+
+ /* Do not allow reads past end of device */
+ if ((to + len) > mtd->size)
+ return -EINVAL;
+ if (!len)
+ return 0;
+
+ nand_panic_wait(mtd);
+
+ chip->ops.len = len;
+ chip->ops.datbuf = (uint8_t *)buf;
+ chip->ops.oobbuf = NULL;
+
+ ret = nand_do_write_ops(mtd, to, &chip->ops);
+
+ *retlen = chip->ops.retlen;
+ return ret;
+}
+
/**
* nand_scan_tail - [NAND Interface] Scan for the NAND device
@@ -3230,6 +3268,7 @@ int nand_scan_tail(struct mtd_info *mtd)
mtd->panic_write = panic_nand_write;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
+ mtd->panic_write = nand_panic_write;
mtd->sync = nand_sync;
mtd->lock = NULL;
mtd->unlock = NULL;
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index ce2fcdd4ab9..416d7d258e6 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -3184,6 +3184,23 @@ config PPPOL2TP
used by ISPs and enterprises to tunnel PPP traffic over UDP
tunnels. L2TP is replacing PPTP for VPN uses.
+config PPPOLAC
+ tristate "PPP on L2TP Access Concentrator"
+ depends on PPP && INET
+ help
+ L2TP (RFC 2661) is a tunneling protocol widely used in virtual private
+ networks. This driver handles L2TP data packets between a UDP socket
+ and a PPP channel, but only permits one session per socket. Thus it is
+ fairly simple and suited for clients.
+
+config PPPOPNS
+ tristate "PPP on PPTP Network Server"
+ depends on PPP && INET
+ help
+ PPTP (RFC 2637) is a tunneling protocol widely used in virtual private
+ networks. This driver handles PPTP data packets between a RAW socket
+ and a PPP channel. It is fairly simple and easy to use.
+
config SLIP
tristate "SLIP (serial line) support"
---help---
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 0a0512ae77d..f38144c2da1 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -162,6 +162,8 @@ obj-$(CONFIG_PPP_BSDCOMP) += bsd_comp.o
obj-$(CONFIG_PPP_MPPE) += ppp_mppe.o
obj-$(CONFIG_PPPOE) += pppox.o pppoe.o
obj-$(CONFIG_PPPOL2TP) += pppox.o
+obj-$(CONFIG_PPPOLAC) += pppox.o pppolac.o
+obj-$(CONFIG_PPPOPNS) += pppox.o pppopns.o
obj-$(CONFIG_SLIP) += slip.o
obj-$(CONFIG_SLHC) += slhc.o
@@ -295,3 +297,7 @@ obj-$(CONFIG_WIMAX) += wimax/
obj-$(CONFIG_CAIF) += caif/
obj-$(CONFIG_OCTEON_MGMT_ETHERNET) += octeon/
+
+ifdef CONFIG_PHONET
+obj-$(CONFIG_U8500_SHRM) += u8500_shrm.o
+endif
diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig
index 0b28e010769..f04fd9d1026 100644
--- a/drivers/net/caif/Kconfig
+++ b/drivers/net/caif/Kconfig
@@ -2,16 +2,41 @@
# CAIF physical drivers
#
-if CAIF
-
comment "CAIF transport drivers"
config CAIF_TTY
tristate "CAIF TTY transport driver"
+ depends on CAIF
default n
---help---
The CAIF TTY transport driver is a Line Discipline (ldisc)
identified as N_CAIF. When this ldisc is opened from user space
it will redirect the TTY's traffic into the CAIF stack.
-endif # CAIF
+config CAIF_SPI_SLAVE
+ tristate "CAIF SPI transport driver for slave interface"
+ depends on CAIF && HAS_DMA
+ default n
+ ---help---
+ The CAIF Link layer SPI Protocol driver for Slave SPI interface.
+ This driver implements a platform driver to accommodate for a
+ platform specific SPI device. A sample CAIF SPI Platform device is
+ provided in Documentation/networking/caif/spi_porting.txt
+
+config CAIF_SPI_SYNC
+ bool "Next command and length in start of frame"
+ depends on CAIF_SPI_SLAVE
+ default n
+ ---help---
+ Putting the next command and length in the start of the frame can
+ help to synchronize to the next transfer in case of over or under-runs.
+ This option also needs to be enabled on the modem.
+
+config CAIF_HSI
+ tristate "CAIF HSI transport driver"
+ depends on CAIF
+ default n
+ ---help---
+ The caif low level driver for CAIF over HSI.
+ Be aware that if you enable this then you also need to
+ enable a low-level HSI driver. \ No newline at end of file
diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile
index 52b6d1f826f..f11339abe65 100644
--- a/drivers/net/caif/Makefile
+++ b/drivers/net/caif/Makefile
@@ -1,12 +1,11 @@
-ifeq ($(CONFIG_CAIF_DEBUG),1)
-CAIF_DBG_FLAGS := -DDEBUG
-endif
-
-KBUILD_EXTRA_SYMBOLS=net/caif/Module.symvers
-
-ccflags-y := $(CAIF_FLAGS) $(CAIF_DBG_FLAGS)
-clean-dirs:= .tmp_versions
-clean-files:= Module.symvers modules.order *.cmd *~ \
+ccflags-$(CONFIG_CAIF_DEBUG) := -DDEBUG
# Serial interface
obj-$(CONFIG_CAIF_TTY) += caif_serial.o
+
+# SPI slave physical interfaces module
+cfspi_slave-objs := caif_spi.o caif_spi_slave.o
+obj-$(CONFIG_CAIF_SPI_SLAVE) += cfspi_slave.o
+
+# HSI interface
+obj-$(CONFIG_CAIF_HSI) += caif_hsi.o
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
new file mode 100644
index 00000000000..73f78623cfe
--- /dev/null
+++ b/drivers/net/caif/caif_hsi.c
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Contact: Sjur Brendeland <sjur.brandeland@stericsson.com>
+ * Author: Daniel Martensson <Daniel.Martensson@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/if_arp.h>
+#include <net/caif/caif_hsi.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Daniel Martensson<daniel.martensson@stericsson.com>");
+MODULE_DESCRIPTION("CAIF HSI driver");
+
+/* Returns the number of padding bytes for alignment. */
+#define PAD_POW2(x, pow) ((((x)&((pow)-1)) == 0) ? 0 : \
+ (((pow)-((x)&((pow)-1)))))
+
+/*
+ * HSI padding options.
+ * Warning: must be a base of 2 (& operation used) and can not be zero !
+ */
+static int hsi_head_align = 4;
+module_param(hsi_head_align, int, S_IRUGO);
+MODULE_PARM_DESC(hsi_head_align, "HSI head alignment.");
+
+static int hsi_tail_align = 4;
+module_param(hsi_tail_align, int, S_IRUGO);
+MODULE_PARM_DESC(hsi_tail_align, "HSI tail alignment.");
+
+/*
+ * HSI link layer flowcontrol thresholds.
+ * Warning: A high threshold value migth increase throughput but it will at
+ * the same time prevent channel prioritization and increase the risk of
+ * flooding the modem. The high threshold should be above the low.
+ */
+static int hsi_high_threshold = 100;
+module_param(hsi_high_threshold, int, S_IRUGO);
+MODULE_PARM_DESC(hsi_high_threshold, "HSI high threshold (FLOW OFF).");
+
+static int hsi_low_threshold = 50;
+module_param(hsi_low_threshold, int, S_IRUGO);
+MODULE_PARM_DESC(hsi_low_threshold, "HSI high threshold (FLOW ON).");
+
+#define ON 1
+#define OFF 0
+
+/*
+ * Threshold values for the HSI packet queue. Flowcontrol will be asserted
+ * when the number of packets exceeds HIGH_WATER_MARK. It will not be
+ * de-asserted before the number of packets drops below LOW_WATER_MARK.
+ */
+#define LOW_WATER_MARK hsi_low_threshold
+#define HIGH_WATER_MARK hsi_high_threshold
+
+static LIST_HEAD(cfhsi_list);
+static spinlock_t cfhsi_list_lock;
+
+static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
+{
+ int nfrms = 0;
+ int pld_len = 0;
+ struct sk_buff *skb;
+ u8 *pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ;
+
+ skb = skb_peek(&cfhsi->qhead);
+ if (!skb)
+ return 0;
+
+ /* Check if we can embed a CAIF frame. */
+ if (skb->len < CFHSI_MAX_EMB_FRM_SZ) {
+ struct caif_payload_info *info;
+ int hpad = 0;
+ int tpad = 0;
+
+ /* Calculate needed head alignment and tail alignment. */
+ info = (struct caif_payload_info *)&skb->cb;
+
+ hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align);
+ tpad = PAD_POW2((skb->len + hpad), hsi_tail_align);
+
+ /* Check if frame still fits with added alignment. */
+ if ((skb->len + hpad + tpad) <= CFHSI_MAX_EMB_FRM_SZ) {
+ u8 *pemb = desc->emb_frm;
+ skb = skb_dequeue(&cfhsi->qhead);
+ desc->offset = CFHSI_DESC_SHORT_SZ;
+ *pemb = (u8)(hpad - 1);
+ pemb += hpad;
+
+ /* Copy in embedded CAIF frame. */
+ skb_copy_bits(skb, 0, pemb, skb->len);
+ kfree_skb(skb);
+ }
+ } else {
+ /* Clear offset. */
+ desc->offset = 0;
+ }
+
+
+ /* Create payload CAIF frames. */
+ pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ;
+ while (skb_peek(&cfhsi->qhead) && nfrms < CFHSI_MAX_PKTS) {
+ struct caif_payload_info *info;
+ int hpad = 0;
+ int tpad = 0;
+
+ skb = skb_dequeue(&cfhsi->qhead);
+
+ /* Calculate needed head alignment and tail alignment. */
+ info = (struct caif_payload_info *)&skb->cb;
+
+ hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align);
+ tpad = PAD_POW2((skb->len + hpad), hsi_tail_align);
+
+ /* Fill in CAIF frame length in descriptor. */
+ desc->cffrm_len[nfrms] = hpad + skb->len + tpad;
+
+ /* Fill head padding information. */
+ *pfrm = (u8)(hpad - 1);
+ pfrm += hpad;
+
+ /* Copy in CAIF frame. */
+ skb_copy_bits(skb, 0, pfrm, skb->len);
+ kfree_skb(skb);
+
+ /* Update payload length. */
+ pld_len += desc->cffrm_len[nfrms];
+
+ /* Update frame pointer. */
+ pfrm += skb->len + tpad;
+
+ /* Update number of frames. */
+ nfrms++;
+ }
+
+ /* Unused length fields should be zero-filled (according to SPEC). */
+ while (nfrms < CFHSI_MAX_PKTS) {
+ desc->cffrm_len[nfrms] = 0x0000;
+ nfrms++;
+ }
+
+ /* Check if we can piggy-back another descriptor. */
+ skb = skb_peek(&cfhsi->qhead);
+ if (skb)
+ desc->header |= CFHSI_PIGGY_DESC;
+ else
+ desc->header &= ~CFHSI_PIGGY_DESC;
+
+ return CFHSI_DESC_SZ + pld_len;
+}
+
+static void cfhsi_tx_done_cb(struct cfhsi_drv *drv)
+{
+ struct cfhsi *cfhsi = NULL;
+ struct cfhsi_desc *desc = NULL;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int len = 0;
+
+ cfhsi = container_of(drv, struct cfhsi, drv);
+ desc = (struct cfhsi_desc *)cfhsi->tx_buf;
+
+ spin_lock_irqsave(&cfhsi->lock, flags);
+
+ /*
+ * Send flow on if flow off has been previously signalled
+ * and number of packets is below low water mark.
+ */
+ if (cfhsi->flow_off_sent && cfhsi->qhead.qlen <= cfhsi->q_low_mark &&
+ cfhsi->cfdev.flowctrl) {
+ cfhsi->flow_off_sent = 0;
+ cfhsi->cfdev.flowctrl(cfhsi->ndev, ON);
+ }
+
+ skb = skb_peek(&cfhsi->qhead);
+ if (!skb) {
+ cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
+ spin_unlock_irqrestore(&cfhsi->lock, flags);
+ return;
+ }
+
+
+ /* Create HSI frame. */
+ len = cfhsi_tx_frm(desc, cfhsi);
+ BUG_ON(!len);
+ spin_unlock_irqrestore(&cfhsi->lock, flags);
+
+ /* Set up new transfer. */
+ cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev);
+}
+
+static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
+{
+ int xfer_sz = 0;
+ int nfrms = 0;
+ u16 *plen = NULL;
+ u8 *pfrm = NULL;
+
+ /* Sanity check header and offset. */
+ BUG_ON(desc->header & ~CFHSI_PIGGY_DESC);
+ BUG_ON(desc->offset > CFHSI_MAX_EMB_FRM_SZ);
+
+ /* Check for embedded CAIF frame. */
+ if (desc->offset) {
+ struct sk_buff *skb = NULL;
+ u8 *dst = NULL;
+ int len = 0;
+ pfrm = ((u8 *)desc) + desc->offset;
+
+ /* Remove offset padding. */
+ pfrm += *pfrm + 1;
+
+ /* Read length of CAIF frame (little endian). */
+ len = *pfrm;
+ len |= ((*(pfrm+1)) << 8) & 0xFF00;
+ /* Add FCS fields. */
+ len += 2;
+
+ /* Allocate SKB (OK even in IRQ context). */
+ skb = netdev_alloc_skb(cfhsi->ndev, len + 1);
+ if (skb == NULL)
+ goto err;
+
+ dst = skb_put(skb, len);
+ memcpy(dst, pfrm, len);
+
+ skb->protocol = htons(ETH_P_CAIF);
+ skb_reset_mac_header(skb);
+ skb->dev = cfhsi->ndev;
+
+ /*
+ * We are called from a arch specific platform device.
+ * Unfortunately we don't know what context we're
+ * running in. HSI might well run in a work queue as
+ * the HSI protocol might require the driver to sleep.
+ */
+ if (in_interrupt())
+ (void)netif_rx(skb);
+ else
+ (void)netif_rx_ni(skb);
+
+ /* Update statistics. */
+ cfhsi->ndev->stats.rx_packets++;
+ cfhsi->ndev->stats.rx_bytes += len;
+ }
+
+ /* Calculate transfer length. */
+ plen = desc->cffrm_len;
+ while (nfrms < CFHSI_MAX_PKTS && *plen) {
+ xfer_sz += *plen;
+ plen++;
+ nfrms++;
+ }
+
+ /* Check for piggy-backed descriptor. */
+ if (desc->header & CFHSI_PIGGY_DESC)
+ xfer_sz += CFHSI_DESC_SZ;
+
+err:
+ return xfer_sz;
+}
+
+static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
+{
+ int rx_sz = 0;
+ int nfrms = 0;
+ u16 *plen = NULL;
+ u8 *pfrm = NULL;
+
+ /* Sanity check header and offset. */
+ BUG_ON(desc->header & ~CFHSI_PIGGY_DESC);
+ BUG_ON(desc->offset > CFHSI_MAX_EMB_FRM_SZ);
+
+ /* Set frame pointer to start of payload. */
+ pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ;
+ plen = desc->cffrm_len;
+ while (nfrms < CFHSI_MAX_PKTS && *plen) {
+ struct sk_buff *skb;
+ u8 *dst = NULL;
+ u8 *pcffrm = NULL;
+ int len = 0;
+
+ BUG_ON(desc->cffrm_len[nfrms] > CFHSI_MAX_PAYLOAD_SZ);
+
+ /* CAIF frame starts after head padding. */
+ pcffrm = pfrm + *pfrm + 1;
+
+ /* Read length of CAIF frame (little endian). */
+ len = *pcffrm;
+ len |= ((*(pcffrm + 1)) << 8) & 0xFF00;
+ /* Add FCS fields. */
+ len += 2;
+
+ /* Allocate SKB (OK even in IRQ context). */
+ skb = netdev_alloc_skb(cfhsi->ndev, len + 1);
+ if (skb == NULL)
+ goto err;
+
+ dst = skb_put(skb, len);
+ memcpy(dst, pcffrm, len);
+
+ skb->protocol = htons(ETH_P_CAIF);
+ skb_reset_mac_header(skb);
+ skb->dev = cfhsi->ndev;
+
+ /*
+ * As explained above we're called from a platform
+ * device, and don't know the context we're running in.
+ */
+ if (in_interrupt())
+ (void)netif_rx(skb);
+ else
+ (void)netif_rx_ni(skb);
+
+ /* Update statistics. */
+ cfhsi->ndev->stats.rx_packets++;
+ cfhsi->ndev->stats.rx_bytes += len;
+
+ pfrm += *plen;
+ rx_sz += *plen;
+ plen++;
+ nfrms++;
+ }
+
+err:
+ return rx_sz;
+}
+
+static void cfhsi_rx_done_cb(struct cfhsi_drv *drv)
+{
+ struct cfhsi *cfhsi = NULL;
+ struct cfhsi_desc *desc = NULL;
+ int len = 0;
+ u8 *ptr = NULL;
+
+ cfhsi = container_of(drv, struct cfhsi, drv);
+ desc = (struct cfhsi_desc *)cfhsi->rx_buf;
+
+ if (cfhsi->rx_state == CFHSI_RX_STATE_DESC)
+ len = cfhsi_rx_desc(desc, cfhsi);
+ else {
+ int pld_len = cfhsi_rx_pld(desc, cfhsi);
+
+ if (desc->header & CFHSI_PIGGY_DESC) {
+ struct cfhsi_desc *piggy_desc;
+ piggy_desc = (struct cfhsi_desc *)(desc->emb_frm +
+ CFHSI_MAX_EMB_FRM_SZ + pld_len);
+
+ /* Extract piggy-backed descriptor. */
+ len = cfhsi_rx_desc(piggy_desc, cfhsi);
+
+ /*
+ * Copy needed information from the piggy-backed
+ * descriptor to the descriptor in the start.
+ */
+ memcpy((u8 *)desc, (u8 *)piggy_desc,
+ CFHSI_DESC_SHORT_SZ);
+ }
+ }
+
+ if (len) {
+ cfhsi->rx_state = CFHSI_RX_STATE_PAYLOAD;
+ ptr = cfhsi->rx_buf + CFHSI_DESC_SZ;
+ } else {
+ len = CFHSI_DESC_SZ;
+ cfhsi->rx_state = CFHSI_RX_STATE_DESC;
+ ptr = cfhsi->rx_buf;
+ }
+
+ /* Set up new transfer. */
+ cfhsi->dev->cfhsi_rx(ptr, len, cfhsi->dev);
+}
+
+static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct cfhsi *cfhsi = NULL;
+ unsigned long flags;
+ int start_xfer = 0;
+
+ if (!dev)
+ return -EINVAL;
+
+ cfhsi = netdev_priv(dev);
+
+ spin_lock_irqsave(&cfhsi->lock, flags);
+
+ skb_queue_tail(&cfhsi->qhead, skb);
+
+ /* Send flow off if number of packets is above high water mark. */
+ if (!cfhsi->flow_off_sent &&
+ cfhsi->qhead.qlen > cfhsi->q_high_mark &&
+ cfhsi->cfdev.flowctrl) {
+ cfhsi->flow_off_sent = 1;
+ cfhsi->cfdev.flowctrl(cfhsi->ndev, OFF);
+ }
+
+ if (cfhsi->tx_state == CFHSI_TX_STATE_IDLE) {
+ cfhsi->tx_state = CFHSI_TX_STATE_XFER;
+ start_xfer = 1;
+ }
+
+ if (start_xfer) {
+ struct cfhsi_desc *desc = (struct cfhsi_desc *) cfhsi->tx_buf;
+ int len;
+
+ /* Create HSI frame. */
+ len = cfhsi_tx_frm(desc, cfhsi);
+ BUG_ON(!len);
+
+ spin_unlock_irqrestore(&cfhsi->lock, flags);
+
+ /* Set up new transfer. */
+ cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev);
+ return 0;
+ }
+
+ spin_unlock_irqrestore(&cfhsi->lock, flags);
+ return 0;
+}
+
+static int cfhsi_open(struct net_device *dev)
+{
+ netif_wake_queue(dev);
+
+ return 0;
+}
+
+static int cfhsi_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+
+ return 0;
+}
+static const struct net_device_ops cfhsi_ops = {
+ .ndo_open = cfhsi_open,
+ .ndo_stop = cfhsi_close,
+ .ndo_start_xmit = cfhsi_xmit
+};
+
+static void cfhsi_setup(struct net_device *dev)
+{
+ struct cfhsi *cfhsi = netdev_priv(dev);
+ dev->features = 0;
+ dev->netdev_ops = &cfhsi_ops;
+ dev->type = ARPHRD_CAIF;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+ dev->mtu = CFHSI_MAX_PAYLOAD_SZ;
+ dev->tx_queue_len = 0;
+ dev->destructor = free_netdev;
+ skb_queue_head_init(&cfhsi->qhead);
+ cfhsi->cfdev.link_select = CAIF_LINK_HIGH_BANDW;
+ cfhsi->cfdev.use_frag = false;
+ cfhsi->cfdev.use_stx = false;
+ cfhsi->cfdev.use_fcs = false;
+ cfhsi->ndev = dev;
+}
+
+int cfhsi_probe(struct platform_device *pdev)
+{
+ struct cfhsi *cfhsi = NULL;
+ struct net_device *ndev;
+ struct cfhsi_dev *dev;
+ int res;
+
+ ndev = alloc_netdev(sizeof(struct cfhsi), "cfhsi%d", cfhsi_setup);
+ if (!ndev) {
+ pr_info("cfhsi_probe: alloc_netdev failed.\n");
+ return -ENODEV;
+ }
+
+ cfhsi = netdev_priv(ndev);
+ netif_stop_queue(ndev);
+ cfhsi->ndev = ndev;
+ cfhsi->pdev = pdev;
+
+ /* Initialize state vaiables. */
+ cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
+ cfhsi->rx_state = CFHSI_RX_STATE_DESC;
+
+ /* Set flow info */
+ cfhsi->flow_off_sent = 0;
+ cfhsi->q_low_mark = LOW_WATER_MARK;
+ cfhsi->q_high_mark = HIGH_WATER_MARK;
+
+ /* Assign the HSI device. */
+ dev = (struct cfhsi_dev *)pdev->dev.platform_data;
+ cfhsi->dev = dev;
+
+ /* Assign the driver to this HSI device. */
+ dev->drv = &cfhsi->drv;
+
+ /*
+ * Allocate a TX buffer with the size of a HSI packet descriptors
+ * and the necessary room for CAIF payload frames.
+ */
+ cfhsi->tx_buf = kzalloc(CFHSI_BUF_SZ_TX, GFP_KERNEL);
+ if (!cfhsi->tx_buf) {
+ netdev_err(cfhsi->ndev, "cfhsi: failed to allocate TX buffer.\n");
+ res = -ENODEV;
+ goto err_alloc_tx;
+ }
+
+ /*
+ * Allocate a RX buffer with the size of two HSI packet descriptors and
+ * the necessary room for CAIF payload frames.
+ */
+ cfhsi->rx_buf = kzalloc(CFHSI_BUF_SZ_RX, GFP_KERNEL);
+ if (!cfhsi->rx_buf) {
+ netdev_err(cfhsi->ndev, "cfhsi: failed to allocate RX buffer.\n");
+ res = -ENODEV;
+ goto err_alloc_rx;
+ }
+
+ /* Initialize spin locks. */
+ spin_lock_init(&cfhsi->lock);
+
+ /* Set up the driver. */
+ cfhsi->drv.tx_done_cb = cfhsi_tx_done_cb;
+ cfhsi->drv.rx_done_cb = cfhsi_rx_done_cb;
+
+ /* Add CAIF HSI device to list. */
+ spin_lock(&cfhsi_list_lock);
+ list_add_tail(&cfhsi->list, &cfhsi_list);
+ spin_unlock(&cfhsi_list_lock);
+
+ /* Register network device. */
+ res = register_netdev(ndev);
+ if (res) {
+ netdev_err(cfhsi->ndev, "cfhsi: Reg. error: %d.\n", res);
+ goto err_net_reg;
+ }
+
+ /* Start an initial read operation. */
+ cfhsi->dev->cfhsi_rx(cfhsi->rx_buf, CFHSI_DESC_SZ, cfhsi->dev);
+
+ return res;
+
+err_net_reg:
+ kfree(cfhsi->rx_buf);
+err_alloc_rx:
+ kfree(cfhsi->tx_buf);
+err_alloc_tx:
+ free_netdev(ndev);
+
+ return res;
+}
+
+int cfhsi_remove(struct platform_device *pdev)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ struct cfhsi *cfhsi = NULL;
+ struct cfhsi_dev *dev;
+
+ dev = (struct cfhsi_dev *)pdev->dev.platform_data;
+ spin_lock(&cfhsi_list_lock);
+ list_for_each_safe(list_node, n, &cfhsi_list) {
+ cfhsi = list_entry(list_node, struct cfhsi, list);
+ /* Find the corresponding device. */
+ if (cfhsi->dev == dev) {
+ /* Remove from list. */
+ list_del(list_node);
+ /* Free buffers. */
+ kfree(cfhsi->tx_buf);
+ kfree(cfhsi->rx_buf);
+ unregister_netdev(cfhsi->ndev);
+ spin_unlock(&cfhsi_list_lock);
+ return 0;
+ }
+ }
+ spin_unlock(&cfhsi_list_lock);
+ return -ENODEV;
+}
+
+struct platform_driver cfhsi_plat_drv = {
+ .probe = cfhsi_probe,
+ .remove = cfhsi_remove,
+ .driver = {
+ .name = "cfhsi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static void __exit cfhsi_exit_module(void)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ struct cfhsi *cfhsi = NULL;
+
+ list_for_each_safe(list_node, n, &cfhsi_list) {
+ cfhsi = list_entry(list_node, struct cfhsi, list);
+ platform_device_unregister(cfhsi->pdev);
+ }
+
+ /* Unregister platform driver. */
+ platform_driver_unregister(&cfhsi_plat_drv);
+}
+
+static int __init cfhsi_init_module(void)
+{
+ int result;
+
+ /* Initialize spin lock. */
+ spin_lock_init(&cfhsi_list_lock);
+
+ /* Register platform driver. */
+ result = platform_driver_register(&cfhsi_plat_drv);
+ if (result) {
+ pr_warning("Could not register platform HSI driver.\n");
+ goto err_dev_register;
+ }
+
+ return result;
+
+err_dev_register:
+ return result;
+}
+
+module_init(cfhsi_init_module);
+module_exit(cfhsi_exit_module);
diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c
index 09257ca8f56..318821b14fe 100644
--- a/drivers/net/caif/caif_serial.c
+++ b/drivers/net/caif/caif_serial.c
@@ -174,6 +174,7 @@ static void ldisc_receive(struct tty_struct *tty, const u8 *data,
struct ser_device *ser;
int ret;
u8 *p;
+
ser = tty->disc_data;
/*
@@ -181,6 +182,7 @@ static void ldisc_receive(struct tty_struct *tty, const u8 *data,
* This is not yet handled.
*/
+ BUG_ON(ser->dev == NULL);
/*
* Workaround for garbage at start of transmission,
@@ -221,6 +223,7 @@ static int handle_tx(struct ser_device *ser)
struct tty_struct *tty;
struct sk_buff *skb;
int tty_wr, len, room;
+
tty = ser->tty;
ser->tx_started = true;
@@ -281,6 +284,7 @@ error:
static int caif_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ser_device *ser;
+
BUG_ON(dev == NULL);
ser = netdev_priv(dev);
@@ -299,6 +303,7 @@ static int caif_xmit(struct sk_buff *skb, struct net_device *dev)
static void ldisc_tx_wakeup(struct tty_struct *tty)
{
struct ser_device *ser;
+
ser = tty->disc_data;
BUG_ON(ser == NULL);
BUG_ON(ser->tty != tty);
@@ -348,6 +353,7 @@ static void ldisc_close(struct tty_struct *tty)
struct ser_device *ser = tty->disc_data;
/* Remove may be called inside or outside of rtnl_lock */
int islocked = rtnl_is_locked();
+
if (!islocked)
rtnl_lock();
/* device is freed automagically by net-sysfs */
@@ -374,6 +380,7 @@ static struct tty_ldisc_ops caif_ldisc = {
static int register_ldisc(void)
{
int result;
+
result = tty_register_ldisc(N_CAIF, &caif_ldisc);
if (result < 0) {
pr_err("cannot register CAIF ldisc=%d err=%d\n", N_CAIF,
@@ -391,12 +398,12 @@ static const struct net_device_ops netdev_ops = {
static void caifdev_setup(struct net_device *dev)
{
struct ser_device *serdev = netdev_priv(dev);
+
dev->features = 0;
dev->netdev_ops = &netdev_ops;
dev->type = ARPHRD_CAIF;
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
dev->mtu = CAIF_MAX_MTU;
- dev->hard_header_len = CAIF_NEEDED_HEADROOM;
dev->tx_queue_len = 0;
dev->destructor = free_netdev;
skb_queue_head_init(&serdev->head);
@@ -410,8 +417,6 @@ static void caifdev_setup(struct net_device *dev)
static int caif_net_open(struct net_device *dev)
{
- struct ser_device *ser;
- ser = netdev_priv(dev);
netif_wake_queue(dev);
return 0;
}
@@ -425,6 +430,7 @@ static int caif_net_close(struct net_device *dev)
static int __init caif_ser_init(void)
{
int ret;
+
ret = register_ldisc();
debugfsdir = debugfs_create_dir("caif_serial", NULL);
return ret;
@@ -435,6 +441,7 @@ static void __exit caif_ser_exit(void)
struct ser_device *ser = NULL;
struct list_head *node;
struct list_head *_tmp;
+
list_for_each_safe(node, _tmp, &ser_list) {
ser = list_entry(node, struct ser_device, node);
dev_close(ser->dev);
diff --git a/drivers/net/caif/caif_spi.c b/drivers/net/caif/caif_spi.c
new file mode 100644
index 00000000000..3a2b764bd9f
--- /dev/null
+++ b/drivers/net/caif/caif_spi.c
@@ -0,0 +1,883 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/debugfs.h>
+#include <linux/if_arp.h>
+#include <net/caif/caif_layer.h>
+#include <net/caif/caif_spi.h>
+
+#ifndef CONFIG_CAIF_SPI_SYNC
+#define FLAVOR "Flavour: Vanilla.\n"
+#else
+#define FLAVOR "Flavour: Master CMD&LEN at start.\n"
+#endif /* CONFIG_CAIF_SPI_SYNC */
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Daniel Martensson<daniel.martensson@stericsson.com>");
+MODULE_DESCRIPTION("CAIF SPI driver");
+
+/* Returns the number of padding bytes for alignment. */
+#define PAD_POW2(x, pow) ((((x)&((pow)-1))==0) ? 0 : (((pow)-((x)&((pow)-1)))))
+
+static int spi_loop;
+module_param(spi_loop, bool, S_IRUGO);
+MODULE_PARM_DESC(spi_loop, "SPI running in loopback mode.");
+
+/* SPI frame alignment. */
+module_param(spi_frm_align, int, S_IRUGO);
+MODULE_PARM_DESC(spi_frm_align, "SPI frame alignment.");
+
+/*
+ * SPI padding options.
+ * Warning: must be a base of 2 (& operation used) and can not be zero !
+ */
+module_param(spi_up_head_align, int, S_IRUGO);
+MODULE_PARM_DESC(spi_up_head_align, "SPI uplink head alignment.");
+
+module_param(spi_up_tail_align, int, S_IRUGO);
+MODULE_PARM_DESC(spi_up_tail_align, "SPI uplink tail alignment.");
+
+module_param(spi_down_head_align, int, S_IRUGO);
+MODULE_PARM_DESC(spi_down_head_align, "SPI downlink head alignment.");
+
+module_param(spi_down_tail_align, int, S_IRUGO);
+MODULE_PARM_DESC(spi_down_tail_align, "SPI downlink tail alignment.");
+
+#ifdef CONFIG_ARM
+#define BYTE_HEX_FMT "%02X"
+#else
+#define BYTE_HEX_FMT "%02hhX"
+#endif
+
+#define SPI_MAX_PAYLOAD_SIZE 4096
+/*
+ * Threshold values for the SPI packet queue. Flowcontrol will be asserted
+ * when the number of packets exceeds HIGH_WATER_MARK. It will not be
+ * deasserted before the number of packets drops below LOW_WATER_MARK.
+ */
+#define LOW_WATER_MARK 100
+#define HIGH_WATER_MARK (LOW_WATER_MARK*5)
+
+#ifdef CONFIG_UML
+
+/*
+ * We sometimes use UML for debugging, but it cannot handle
+ * dma_alloc_coherent so we have to wrap it.
+ */
+static inline void *dma_alloc(dma_addr_t *daddr)
+{
+ return kmalloc(SPI_DMA_BUF_LEN, GFP_KERNEL);
+}
+
+static inline void dma_free(void *cpu_addr, dma_addr_t handle)
+{
+ kfree(cpu_addr);
+}
+
+#else
+
+static inline void *dma_alloc(dma_addr_t *daddr)
+{
+ return dma_alloc_coherent(NULL, SPI_DMA_BUF_LEN, daddr,
+ GFP_KERNEL);
+}
+
+static inline void dma_free(void *cpu_addr, dma_addr_t handle)
+{
+ dma_free_coherent(NULL, SPI_DMA_BUF_LEN, cpu_addr, handle);
+}
+#endif /* CONFIG_UML */
+
+#ifdef CONFIG_DEBUG_FS
+
+#define DEBUGFS_BUF_SIZE 4096
+
+static struct dentry *dbgfs_root;
+
+static inline void driver_debugfs_create(void)
+{
+ dbgfs_root = debugfs_create_dir(cfspi_spi_driver.driver.name, NULL);
+}
+
+static inline void driver_debugfs_remove(void)
+{
+ debugfs_remove(dbgfs_root);
+}
+
+static inline void dev_debugfs_rem(struct cfspi *cfspi)
+{
+ debugfs_remove(cfspi->dbgfs_frame);
+ debugfs_remove(cfspi->dbgfs_state);
+ debugfs_remove(cfspi->dbgfs_dir);
+}
+
+static int dbgfs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t dbgfs_state(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char *buf;
+ int len = 0;
+ ssize_t size;
+ struct cfspi *cfspi = file->private_data;
+
+ buf = kzalloc(DEBUGFS_BUF_SIZE, GFP_KERNEL);
+ if (!buf)
+ return 0;
+
+ /* Print out debug information. */
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "CAIF SPI debug information:\n");
+
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len), FLAVOR);
+
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "STATE: %d\n", cfspi->dbg_state);
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "Previous CMD: 0x%x\n", cfspi->pcmd);
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "Current CMD: 0x%x\n", cfspi->cmd);
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "Previous TX len: %d\n", cfspi->tx_ppck_len);
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "Previous RX len: %d\n", cfspi->rx_ppck_len);
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "Current TX len: %d\n", cfspi->tx_cpck_len);
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "Current RX len: %d\n", cfspi->rx_cpck_len);
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "Next TX len: %d\n", cfspi->tx_npck_len);
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "Next RX len: %d\n", cfspi->rx_npck_len);
+
+ if (len > DEBUGFS_BUF_SIZE)
+ len = DEBUGFS_BUF_SIZE;
+
+ size = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+
+ return size;
+}
+
+static ssize_t print_frame(char *buf, size_t size, char *frm,
+ size_t count, size_t cut)
+{
+ int len = 0;
+ int i;
+ for (i = 0; i < count; i++) {
+ len += snprintf((buf + len), (size - len),
+ "[0x" BYTE_HEX_FMT "]",
+ frm[i]);
+ if ((i == cut) && (count > (cut * 2))) {
+ /* Fast forward. */
+ i = count - cut;
+ len += snprintf((buf + len), (size - len),
+ "--- %u bytes skipped ---\n",
+ (int)(count - (cut * 2)));
+ }
+
+ if ((!(i % 10)) && i) {
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "\n");
+ }
+ }
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len), "\n");
+ return len;
+}
+
+static ssize_t dbgfs_frame(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char *buf;
+ int len = 0;
+ ssize_t size;
+ struct cfspi *cfspi;
+
+ cfspi = file->private_data;
+ buf = kzalloc(DEBUGFS_BUF_SIZE, GFP_KERNEL);
+ if (!buf)
+ return 0;
+
+ /* Print out debug information. */
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "Current frame:\n");
+
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "Tx data (Len: %d):\n", cfspi->tx_cpck_len);
+
+ len += print_frame((buf + len), (DEBUGFS_BUF_SIZE - len),
+ cfspi->xfer.va_tx[0],
+ (cfspi->tx_cpck_len + SPI_CMD_SZ), 100);
+
+ len += snprintf((buf + len), (DEBUGFS_BUF_SIZE - len),
+ "Rx data (Len: %d):\n", cfspi->rx_cpck_len);
+
+ len += print_frame((buf + len), (DEBUGFS_BUF_SIZE - len),
+ cfspi->xfer.va_rx,
+ (cfspi->rx_cpck_len + SPI_CMD_SZ), 100);
+
+ size = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+
+ return size;
+}
+
+static const struct file_operations dbgfs_state_fops = {
+ .open = dbgfs_open,
+ .read = dbgfs_state,
+ .owner = THIS_MODULE
+};
+
+static const struct file_operations dbgfs_frame_fops = {
+ .open = dbgfs_open,
+ .read = dbgfs_frame,
+ .owner = THIS_MODULE
+};
+
+static inline void dev_debugfs_add(struct cfspi *cfspi)
+{
+ cfspi->dbgfs_dir = debugfs_create_dir(cfspi->pdev->name, dbgfs_root);
+ cfspi->dbgfs_state = debugfs_create_file("state", S_IRUGO,
+ cfspi->dbgfs_dir, cfspi,
+ &dbgfs_state_fops);
+ cfspi->dbgfs_frame = debugfs_create_file("frame", S_IRUGO,
+ cfspi->dbgfs_dir, cfspi,
+ &dbgfs_frame_fops);
+}
+
+inline void cfspi_dbg_state(struct cfspi *cfspi, int state)
+{
+ cfspi->dbg_state = state;
+};
+#else
+
+static inline void driver_debugfs_create(void)
+{
+}
+
+static inline void driver_debugfs_remove(void)
+{
+}
+
+static inline void dev_debugfs_add(struct cfspi *cfspi)
+{
+}
+
+static inline void dev_debugfs_rem(struct cfspi *cfspi)
+{
+}
+
+inline void cfspi_dbg_state(struct cfspi *cfspi, int state)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static LIST_HEAD(cfspi_list);
+static spinlock_t cfspi_list_lock;
+
+/* SPI uplink head alignment. */
+static ssize_t show_up_head_align(struct device_driver *driver, char *buf)
+{
+ return sprintf(buf, "%d\n", spi_up_head_align);
+}
+
+static DRIVER_ATTR(up_head_align, S_IRUSR, show_up_head_align, NULL);
+
+/* SPI uplink tail alignment. */
+static ssize_t show_up_tail_align(struct device_driver *driver, char *buf)
+{
+ return sprintf(buf, "%d\n", spi_up_tail_align);
+}
+
+static DRIVER_ATTR(up_tail_align, S_IRUSR, show_up_tail_align, NULL);
+
+/* SPI downlink head alignment. */
+static ssize_t show_down_head_align(struct device_driver *driver, char *buf)
+{
+ return sprintf(buf, "%d\n", spi_down_head_align);
+}
+
+static DRIVER_ATTR(down_head_align, S_IRUSR, show_down_head_align, NULL);
+
+/* SPI downlink tail alignment. */
+static ssize_t show_down_tail_align(struct device_driver *driver, char *buf)
+{
+ return sprintf(buf, "%d\n", spi_down_tail_align);
+}
+
+static DRIVER_ATTR(down_tail_align, S_IRUSR, show_down_tail_align, NULL);
+
+/* SPI frame alignment. */
+static ssize_t show_frame_align(struct device_driver *driver, char *buf)
+{
+ return sprintf(buf, "%d\n", spi_frm_align);
+}
+
+static DRIVER_ATTR(frame_align, S_IRUSR, show_frame_align, NULL);
+
+int cfspi_xmitfrm(struct cfspi *cfspi, u8 *buf, size_t len)
+{
+ u8 *dst = buf;
+ caif_assert(buf);
+
+ if (cfspi->slave && !cfspi->slave_talked)
+ cfspi->slave_talked = true;
+
+ do {
+ struct sk_buff *skb;
+ struct caif_payload_info *info;
+ int spad = 0;
+ int epad;
+
+ skb = skb_dequeue(&cfspi->chead);
+ if (!skb)
+ break;
+
+ /*
+ * Calculate length of frame including SPI padding.
+ * The payload position is found in the control buffer.
+ */
+ info = (struct caif_payload_info *)&skb->cb;
+
+ /*
+ * Compute head offset i.e. number of bytes to add to
+ * get the start of the payload aligned.
+ */
+ if (spi_up_head_align > 1) {
+ spad = 1 + PAD_POW2((info->hdr_len + 1), spi_up_head_align);
+ *dst = (u8)(spad - 1);
+ dst += spad;
+ }
+
+ /* Copy in CAIF frame. */
+ skb_copy_bits(skb, 0, dst, skb->len);
+ dst += skb->len;
+ cfspi->ndev->stats.tx_packets++;
+ cfspi->ndev->stats.tx_bytes += skb->len;
+
+ /*
+ * Compute tail offset i.e. number of bytes to add to
+ * get the complete CAIF frame aligned.
+ */
+ epad = PAD_POW2((skb->len + spad), spi_up_tail_align);
+ dst += epad;
+
+ dev_kfree_skb(skb);
+
+ } while ((dst - buf) < len);
+
+ return dst - buf;
+}
+
+int cfspi_xmitlen(struct cfspi *cfspi)
+{
+ struct sk_buff *skb = NULL;
+ int frm_len = 0;
+ int pkts = 0;
+
+ /*
+ * Decommit previously commited frames.
+ * skb_queue_splice_tail(&cfspi->chead,&cfspi->qhead)
+ */
+ while (skb_peek(&cfspi->chead)) {
+ skb = skb_dequeue_tail(&cfspi->chead);
+ skb_queue_head(&cfspi->qhead, skb);
+ }
+
+ do {
+ struct caif_payload_info *info = NULL;
+ int spad = 0;
+ int epad = 0;
+
+ skb = skb_dequeue(&cfspi->qhead);
+ if (!skb)
+ break;
+
+ /*
+ * Calculate length of frame including SPI padding.
+ * The payload position is found in the control buffer.
+ */
+ info = (struct caif_payload_info *)&skb->cb;
+
+ /*
+ * Compute head offset i.e. number of bytes to add to
+ * get the start of the payload aligned.
+ */
+ if (spi_up_head_align > 1)
+ spad = 1 + PAD_POW2((info->hdr_len + 1), spi_up_head_align);
+
+ /*
+ * Compute tail offset i.e. number of bytes to add to
+ * get the complete CAIF frame aligned.
+ */
+ epad = PAD_POW2((skb->len + spad), spi_up_tail_align);
+
+ if ((skb->len + spad + epad + frm_len) <= CAIF_MAX_SPI_FRAME) {
+ skb_queue_tail(&cfspi->chead, skb);
+ pkts++;
+ frm_len += skb->len + spad + epad;
+ } else {
+ /* Put back packet. */
+ skb_queue_head(&cfspi->qhead, skb);
+ break;
+ }
+ } while (pkts <= CAIF_MAX_SPI_PKTS);
+
+ /*
+ * Send flow on if previously sent flow off
+ * and now go below the low water mark
+ */
+ if (cfspi->flow_off_sent && cfspi->qhead.qlen < cfspi->qd_low_mark &&
+ cfspi->cfdev.flowctrl) {
+ cfspi->flow_off_sent = 0;
+ cfspi->cfdev.flowctrl(cfspi->ndev, 1);
+ }
+
+ return frm_len;
+}
+
+static void cfspi_ss_cb(bool assert, struct cfspi_ifc *ifc)
+{
+ struct cfspi *cfspi = (struct cfspi *)ifc->priv;
+
+ /*
+ * The slave device is the master on the link. Interrupts before the
+ * slave has transmitted are considered spurious.
+ */
+ if (cfspi->slave && !cfspi->slave_talked) {
+ printk(KERN_WARNING "CFSPI: Spurious SS interrupt.\n");
+ return;
+ }
+
+ if (!in_interrupt())
+ spin_lock(&cfspi->lock);
+ if (assert) {
+ set_bit(SPI_SS_ON, &cfspi->state);
+ set_bit(SPI_XFER, &cfspi->state);
+ } else {
+ set_bit(SPI_SS_OFF, &cfspi->state);
+ }
+ if (!in_interrupt())
+ spin_unlock(&cfspi->lock);
+
+ /* Wake up the xfer thread. */
+ if (assert)
+ wake_up_interruptible(&cfspi->wait);
+}
+
+static void cfspi_xfer_done_cb(struct cfspi_ifc *ifc)
+{
+ struct cfspi *cfspi = (struct cfspi *)ifc->priv;
+
+ /* Transfer done, complete work queue */
+ complete(&cfspi->comp);
+}
+
+static int cfspi_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct cfspi *cfspi = NULL;
+ unsigned long flags;
+ if (!dev)
+ return -EINVAL;
+
+ cfspi = netdev_priv(dev);
+
+ skb_queue_tail(&cfspi->qhead, skb);
+
+ spin_lock_irqsave(&cfspi->lock, flags);
+ if (!test_and_set_bit(SPI_XFER, &cfspi->state)) {
+ /* Wake up xfer thread. */
+ wake_up_interruptible(&cfspi->wait);
+ }
+ spin_unlock_irqrestore(&cfspi->lock, flags);
+
+ /* Send flow off if number of bytes is above high water mark */
+ if (!cfspi->flow_off_sent &&
+ cfspi->qhead.qlen > cfspi->qd_high_mark &&
+ cfspi->cfdev.flowctrl) {
+ cfspi->flow_off_sent = 1;
+ cfspi->cfdev.flowctrl(cfspi->ndev, 0);
+ }
+
+ return 0;
+}
+
+int cfspi_rxfrm(struct cfspi *cfspi, u8 *buf, size_t len)
+{
+ u8 *src = buf;
+
+ caif_assert(buf != NULL);
+
+ do {
+ int res;
+ struct sk_buff *skb = NULL;
+ int spad = 0;
+ int epad = 0;
+ u8 *dst = NULL;
+ int pkt_len = 0;
+
+ /*
+ * Compute head offset i.e. number of bytes added to
+ * get the start of the payload aligned.
+ */
+ if (spi_down_head_align > 1) {
+ spad = 1 + *src;
+ src += spad;
+ }
+
+ /* Read length of CAIF frame (little endian). */
+ pkt_len = *src;
+ pkt_len |= ((*(src+1)) << 8) & 0xFF00;
+ pkt_len += 2; /* Add FCS fields. */
+
+ /* Get a suitable caif packet and copy in data. */
+
+ skb = netdev_alloc_skb(cfspi->ndev, pkt_len + 1);
+ caif_assert(skb != NULL);
+
+ dst = skb_put(skb, pkt_len);
+ memcpy(dst, src, pkt_len);
+ src += pkt_len;
+
+ skb->protocol = htons(ETH_P_CAIF);
+ skb_reset_mac_header(skb);
+ skb->dev = cfspi->ndev;
+
+ /*
+ * Push received packet up the stack.
+ */
+ if (!spi_loop)
+ res = netif_rx_ni(skb);
+ else
+ res = cfspi_xmit(skb, cfspi->ndev);
+
+ if (!res) {
+ cfspi->ndev->stats.rx_packets++;
+ cfspi->ndev->stats.rx_bytes += pkt_len;
+ } else
+ cfspi->ndev->stats.rx_dropped++;
+
+ /*
+ * Compute tail offset i.e. number of bytes added to
+ * get the complete CAIF frame aligned.
+ */
+ epad = PAD_POW2((pkt_len + spad), spi_down_tail_align);
+ src += epad;
+ } while ((src - buf) < len);
+
+ return src - buf;
+}
+
+static int cfspi_open(struct net_device *dev)
+{
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static int cfspi_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return 0;
+}
+
+static int cfspi_init(struct net_device *dev)
+{
+ int res = 0;
+ struct cfspi *cfspi = netdev_priv(dev);
+
+ /* Set flow info. */
+ cfspi->flow_off_sent = 0;
+ cfspi->qd_low_mark = LOW_WATER_MARK;
+ cfspi->qd_high_mark = HIGH_WATER_MARK;
+
+ /* Set slave info. */
+ if (!strncmp(cfspi_spi_driver.driver.name, "cfspi_sspi", 10)) {
+ cfspi->slave = true;
+ cfspi->slave_talked = false;
+ } else {
+ cfspi->slave = false;
+ cfspi->slave_talked = false;
+ }
+
+ /* Allocate DMA buffers. */
+ cfspi->xfer.va_tx[0] = dma_alloc(&cfspi->xfer.pa_tx[0]);
+ if (!cfspi->xfer.va_tx[0]) {
+ printk(KERN_WARNING
+ "CFSPI: failed to allocate dma TX buffer.\n");
+ res = -ENODEV;
+ goto err_dma_alloc_tx_0;
+ }
+
+ cfspi->xfer.va_rx = dma_alloc(&cfspi->xfer.pa_rx);
+
+ if (!cfspi->xfer.va_rx) {
+ printk(KERN_WARNING
+ "CFSPI: failed to allocate dma TX buffer.\n");
+ res = -ENODEV;
+ goto err_dma_alloc_rx;
+ }
+
+ /* Initialize the work queue. */
+ INIT_WORK(&cfspi->work, cfspi_xfer);
+
+ /* Initialize spin locks. */
+ spin_lock_init(&cfspi->lock);
+
+ /* Initialize flow control state. */
+ cfspi->flow_stop = false;
+
+ /* Initialize wait queue. */
+ init_waitqueue_head(&cfspi->wait);
+
+ /* Create work thread. */
+ cfspi->wq = create_singlethread_workqueue(dev->name);
+ if (!cfspi->wq) {
+ printk(KERN_WARNING "CFSPI: failed to create work queue.\n");
+ res = -ENODEV;
+ goto err_create_wq;
+ }
+
+ /* Initialize work queue. */
+ init_completion(&cfspi->comp);
+
+ /* Create debugfs entries. */
+ dev_debugfs_add(cfspi);
+
+ /* Set up the ifc. */
+ cfspi->ifc.ss_cb = cfspi_ss_cb;
+ cfspi->ifc.xfer_done_cb = cfspi_xfer_done_cb;
+ cfspi->ifc.priv = cfspi;
+
+ /* Add CAIF SPI device to list. */
+ spin_lock(&cfspi_list_lock);
+ list_add_tail(&cfspi->list, &cfspi_list);
+ spin_unlock(&cfspi_list_lock);
+
+ /* Schedule the work queue. */
+ queue_work(cfspi->wq, &cfspi->work);
+
+ return 0;
+
+ err_create_wq:
+ dma_free(cfspi->xfer.va_rx, cfspi->xfer.pa_rx);
+ err_dma_alloc_rx:
+ dma_free(cfspi->xfer.va_tx[0], cfspi->xfer.pa_tx[0]);
+ err_dma_alloc_tx_0:
+ return res;
+}
+
+static void cfspi_uninit(struct net_device *dev)
+{
+ struct cfspi *cfspi = netdev_priv(dev);
+
+ /* Remove from list. */
+ spin_lock(&cfspi_list_lock);
+ list_del(&cfspi->list);
+ spin_unlock(&cfspi_list_lock);
+
+ cfspi->ndev = NULL;
+ /* Free DMA buffers. */
+ dma_free(cfspi->xfer.va_rx, cfspi->xfer.pa_rx);
+ dma_free(cfspi->xfer.va_tx[0], cfspi->xfer.pa_tx[0]);
+ set_bit(SPI_TERMINATE, &cfspi->state);
+ wake_up_interruptible(&cfspi->wait);
+ destroy_workqueue(cfspi->wq);
+ /* Destroy debugfs directory and files. */
+ dev_debugfs_rem(cfspi);
+ return;
+}
+
+static const struct net_device_ops cfspi_ops = {
+ .ndo_open = cfspi_open,
+ .ndo_stop = cfspi_close,
+ .ndo_init = cfspi_init,
+ .ndo_uninit = cfspi_uninit,
+ .ndo_start_xmit = cfspi_xmit
+};
+
+static void cfspi_setup(struct net_device *dev)
+{
+ struct cfspi *cfspi = netdev_priv(dev);
+ dev->features = 0;
+ dev->netdev_ops = &cfspi_ops;
+ dev->type = ARPHRD_CAIF;
+ dev->flags = IFF_NOARP | IFF_POINTOPOINT;
+ dev->tx_queue_len = 0;
+ dev->mtu = SPI_MAX_PAYLOAD_SIZE;
+ dev->destructor = free_netdev;
+ skb_queue_head_init(&cfspi->qhead);
+ skb_queue_head_init(&cfspi->chead);
+ cfspi->cfdev.link_select = CAIF_LINK_HIGH_BANDW;
+ cfspi->cfdev.use_frag = false;
+ cfspi->cfdev.use_stx = false;
+ cfspi->cfdev.use_fcs = false;
+ cfspi->ndev = dev;
+}
+
+int cfspi_spi_probe(struct platform_device *pdev)
+{
+ struct cfspi *cfspi = NULL;
+ struct net_device *ndev;
+ struct cfspi_dev *dev;
+ int res;
+ dev = (struct cfspi_dev *)pdev->dev.platform_data;
+
+ ndev = alloc_netdev(sizeof(struct cfspi),
+ "cfspi%d", cfspi_setup);
+ if (!dev)
+ return -ENODEV;
+
+ cfspi = netdev_priv(ndev);
+ netif_stop_queue(ndev);
+ cfspi->ndev = ndev;
+ cfspi->pdev = pdev;
+
+ /* Assign the SPI device. */
+ cfspi->dev = dev;
+ /* Assign the device ifc to this SPI interface. */
+ dev->ifc = &cfspi->ifc;
+
+ /* Register network device. */
+ res = register_netdev(ndev);
+ if (res) {
+ printk(KERN_ERR "CFSPI: Reg. error: %d.\n", res);
+ goto err_net_reg;
+ }
+ return res;
+
+ err_net_reg:
+ free_netdev(ndev);
+
+ return res;
+}
+
+int cfspi_spi_remove(struct platform_device *pdev)
+{
+ /* Everything is done in cfspi_uninit(). */
+ return 0;
+}
+
+static void __exit cfspi_exit_module(void)
+{
+ struct list_head *list_node;
+ struct list_head *n;
+ struct cfspi *cfspi = NULL;
+
+ list_for_each_safe(list_node, n, &cfspi_list) {
+ cfspi = list_entry(list_node, struct cfspi, list);
+ unregister_netdev(cfspi->ndev);
+ }
+
+ /* Destroy sysfs files. */
+ driver_remove_file(&cfspi_spi_driver.driver,
+ &driver_attr_up_head_align);
+ driver_remove_file(&cfspi_spi_driver.driver,
+ &driver_attr_up_tail_align);
+ driver_remove_file(&cfspi_spi_driver.driver,
+ &driver_attr_down_head_align);
+ driver_remove_file(&cfspi_spi_driver.driver,
+ &driver_attr_down_tail_align);
+ driver_remove_file(&cfspi_spi_driver.driver, &driver_attr_frame_align);
+ /* Unregister platform driver. */
+ platform_driver_unregister(&cfspi_spi_driver);
+ /* Destroy debugfs root directory. */
+ driver_debugfs_remove();
+}
+
+static int __init cfspi_init_module(void)
+{
+ int result;
+
+ /* Initialize spin lock. */
+ spin_lock_init(&cfspi_list_lock);
+
+ /* Register platform driver. */
+ result = platform_driver_register(&cfspi_spi_driver);
+ if (result) {
+ printk(KERN_ERR "Could not register platform SPI driver.\n");
+ goto err_dev_register;
+ }
+
+ /* Create sysfs files. */
+ result =
+ driver_create_file(&cfspi_spi_driver.driver,
+ &driver_attr_up_head_align);
+ if (result) {
+ printk(KERN_ERR "Sysfs creation failed 1.\n");
+ goto err_create_up_head_align;
+ }
+
+ result =
+ driver_create_file(&cfspi_spi_driver.driver,
+ &driver_attr_up_tail_align);
+ if (result) {
+ printk(KERN_ERR "Sysfs creation failed 2.\n");
+ goto err_create_up_tail_align;
+ }
+
+ result =
+ driver_create_file(&cfspi_spi_driver.driver,
+ &driver_attr_down_head_align);
+ if (result) {
+ printk(KERN_ERR "Sysfs creation failed 3.\n");
+ goto err_create_down_head_align;
+ }
+
+ result =
+ driver_create_file(&cfspi_spi_driver.driver,
+ &driver_attr_down_tail_align);
+ if (result) {
+ printk(KERN_ERR "Sysfs creation failed 4.\n");
+ goto err_create_down_tail_align;
+ }
+
+ result =
+ driver_create_file(&cfspi_spi_driver.driver,
+ &driver_attr_frame_align);
+ if (result) {
+ printk(KERN_ERR "Sysfs creation failed 5.\n");
+ goto err_create_frame_align;
+ }
+ driver_debugfs_create();
+ return result;
+
+ err_create_frame_align:
+ driver_remove_file(&cfspi_spi_driver.driver,
+ &driver_attr_down_tail_align);
+ err_create_down_tail_align:
+ driver_remove_file(&cfspi_spi_driver.driver,
+ &driver_attr_down_head_align);
+ err_create_down_head_align:
+ driver_remove_file(&cfspi_spi_driver.driver,
+ &driver_attr_up_tail_align);
+ err_create_up_tail_align:
+ driver_remove_file(&cfspi_spi_driver.driver,
+ &driver_attr_up_head_align);
+ err_create_up_head_align:
+ err_dev_register:
+ return result;
+}
+
+module_init(cfspi_init_module);
+module_exit(cfspi_exit_module);
diff --git a/drivers/net/caif/caif_spi_slave.c b/drivers/net/caif/caif_spi_slave.c
new file mode 100644
index 00000000000..9017a47ff3b
--- /dev/null
+++ b/drivers/net/caif/caif_spi_slave.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com
+ * Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/semaphore.h>
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/debugfs.h>
+#include <net/caif/caif_spi.h>
+
+#ifndef CONFIG_CAIF_SPI_SYNC
+#define SPI_DATA_POS 0
+static inline int forward_to_spi_cmd(struct cfspi *cfspi)
+{
+ return cfspi->rx_cpck_len;
+}
+#else
+#define SPI_DATA_POS SPI_CMD_SZ
+static inline int forward_to_spi_cmd(struct cfspi *cfspi)
+{
+ return 0;
+}
+#endif
+
+int spi_frm_align = 2;
+
+/*
+ * SPI padding options.
+ * Warning: must be a base of 2 (& operation used) and can not be zero !
+ */
+int spi_up_head_align = 1 << 1;
+int spi_up_tail_align = 1 << 0;
+int spi_down_head_align = 1 << 2;
+int spi_down_tail_align = 1 << 1;
+
+#ifdef CONFIG_DEBUG_FS
+static inline void debugfs_store_prev(struct cfspi *cfspi)
+{
+ /* Store previous command for debugging reasons.*/
+ cfspi->pcmd = cfspi->cmd;
+ /* Store previous transfer. */
+ cfspi->tx_ppck_len = cfspi->tx_cpck_len;
+ cfspi->rx_ppck_len = cfspi->rx_cpck_len;
+}
+#else
+static inline void debugfs_store_prev(struct cfspi *cfspi)
+{
+}
+#endif
+
+void cfspi_xfer(struct work_struct *work)
+{
+ struct cfspi *cfspi;
+ u8 *ptr = NULL;
+ unsigned long flags;
+ int ret;
+ cfspi = container_of(work, struct cfspi, work);
+
+ /* Initialize state. */
+ cfspi->cmd = SPI_CMD_EOT;
+
+ for (;;) {
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_WAITING);
+
+ /* Wait for master talk or transmit event. */
+ wait_event_interruptible(cfspi->wait,
+ test_bit(SPI_XFER, &cfspi->state) ||
+ test_bit(SPI_TERMINATE, &cfspi->state));
+
+ if (test_bit(SPI_TERMINATE, &cfspi->state))
+ return;
+
+#if CFSPI_DBG_PREFILL
+ /* Prefill buffers for easier debugging. */
+ memset(cfspi->xfer.va_tx[0], 0xFF, SPI_DMA_BUF_LEN);
+ memset(cfspi->xfer.va_rx, 0xFF, SPI_DMA_BUF_LEN);
+#endif /* CFSPI_DBG_PREFILL */
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_AWAKE);
+
+ /* Check whether we have a committed frame. */
+ if (cfspi->tx_cpck_len) {
+ int len;
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_FETCH_PKT);
+
+ /* Copy commited SPI frames after the SPI indication. */
+ ptr = (u8 *) cfspi->xfer.va_tx[0];
+ ptr += SPI_IND_SZ;
+ len = cfspi_xmitfrm(cfspi, ptr, cfspi->tx_cpck_len);
+ WARN_ON(len != cfspi->tx_cpck_len);
+ }
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_GET_NEXT);
+
+ /* Get length of next frame to commit. */
+ cfspi->tx_npck_len = cfspi_xmitlen(cfspi);
+
+ WARN_ON(cfspi->tx_npck_len > SPI_DMA_BUF_LEN);
+
+ /*
+ * Add indication and length at the beginning of the frame,
+ * using little endian.
+ */
+ ptr = (u8 *) cfspi->xfer.va_tx[0];
+ *ptr++ = SPI_CMD_IND;
+ *ptr++ = (SPI_CMD_IND & 0xFF00) >> 8;
+ *ptr++ = cfspi->tx_npck_len & 0x00FF;
+ *ptr++ = (cfspi->tx_npck_len & 0xFF00) >> 8;
+
+ /* Calculate length of DMAs. */
+ cfspi->xfer.tx_dma_len = cfspi->tx_cpck_len + SPI_IND_SZ;
+ cfspi->xfer.rx_dma_len = cfspi->rx_cpck_len + SPI_CMD_SZ;
+
+ /* Add SPI TX frame alignment padding, if necessary. */
+ if (cfspi->tx_cpck_len &&
+ (cfspi->xfer.tx_dma_len % spi_frm_align)) {
+
+ cfspi->xfer.tx_dma_len += spi_frm_align -
+ (cfspi->xfer.tx_dma_len % spi_frm_align);
+ }
+
+ /* Add SPI RX frame alignment padding, if necessary. */
+ if (cfspi->rx_cpck_len &&
+ (cfspi->xfer.rx_dma_len % spi_frm_align)) {
+
+ cfspi->xfer.rx_dma_len += spi_frm_align -
+ (cfspi->xfer.rx_dma_len % spi_frm_align);
+ }
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_INIT_XFER);
+
+ /* Start transfer. */
+ ret = cfspi->dev->init_xfer(&cfspi->xfer, cfspi->dev);
+ WARN_ON(ret);
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_WAIT_ACTIVE);
+
+ /*
+ * TODO: We might be able to make an assumption if this is the
+ * first loop. Make sure that minimum toggle time is respected.
+ */
+ udelay(MIN_TRANSITION_TIME_USEC);
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_SIG_ACTIVE);
+
+ /* Signal that we are ready to recieve data. */
+ cfspi->dev->sig_xfer(true, cfspi->dev);
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_WAIT_XFER_DONE);
+
+ /* Wait for transfer completion. */
+ wait_for_completion(&cfspi->comp);
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_XFER_DONE);
+
+ if (cfspi->cmd == SPI_CMD_EOT) {
+ /*
+ * Clear the master talk bit. A xfer is always at
+ * least two bursts.
+ */
+ clear_bit(SPI_SS_ON, &cfspi->state);
+ }
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_WAIT_INACTIVE);
+
+ /* Make sure that the minimum toggle time is respected. */
+ if (SPI_XFER_TIME_USEC(cfspi->xfer.tx_dma_len,
+ cfspi->dev->clk_mhz) <
+ MIN_TRANSITION_TIME_USEC) {
+
+ udelay(MIN_TRANSITION_TIME_USEC -
+ SPI_XFER_TIME_USEC
+ (cfspi->xfer.tx_dma_len, cfspi->dev->clk_mhz));
+ }
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_SIG_INACTIVE);
+
+ /* De-assert transfer signal. */
+ cfspi->dev->sig_xfer(false, cfspi->dev);
+
+ /* Check whether we received a CAIF packet. */
+ if (cfspi->rx_cpck_len) {
+ int len;
+
+ cfspi_dbg_state(cfspi, CFSPI_STATE_DELIVER_PKT);
+
+ /* Parse SPI frame. */
+ ptr = ((u8 *)(cfspi->xfer.va_rx + SPI_DATA_POS));
+
+ len = cfspi_rxfrm(cfspi, ptr, cfspi->rx_cpck_len);
+ WARN_ON(len != cfspi->rx_cpck_len);
+ }
+
+ /* Check the next SPI command and length. */
+ ptr = (u8 *) cfspi->xfer.va_rx;
+
+ ptr += forward_to_spi_cmd(cfspi);
+
+ cfspi->cmd = *ptr++;
+ cfspi->cmd |= ((*ptr++) << 8) & 0xFF00;
+ cfspi->rx_npck_len = *ptr++;
+ cfspi->rx_npck_len |= ((*ptr++) << 8) & 0xFF00;
+
+ WARN_ON(cfspi->rx_npck_len > SPI_DMA_BUF_LEN);
+ WARN_ON(cfspi->cmd > SPI_CMD_EOT);
+
+ debugfs_store_prev(cfspi);
+
+ /* Check whether the master issued an EOT command. */
+ if (cfspi->cmd == SPI_CMD_EOT) {
+ /* Reset state. */
+ cfspi->tx_cpck_len = 0;
+ cfspi->rx_cpck_len = 0;
+ } else {
+ /* Update state. */
+ cfspi->tx_cpck_len = cfspi->tx_npck_len;
+ cfspi->rx_cpck_len = cfspi->rx_npck_len;
+ }
+
+ /*
+ * Check whether we need to clear the xfer bit.
+ * Spin lock needed for packet insertion.
+ * Test and clear of different bits
+ * are not supported.
+ */
+ spin_lock_irqsave(&cfspi->lock, flags);
+ if (cfspi->cmd == SPI_CMD_EOT && !cfspi_xmitlen(cfspi)
+ && !test_bit(SPI_SS_ON, &cfspi->state))
+ clear_bit(SPI_XFER, &cfspi->state);
+
+ spin_unlock_irqrestore(&cfspi->lock, flags);
+ }
+}
+
+struct platform_driver cfspi_spi_driver = {
+ .probe = cfspi_spi_probe,
+ .remove = cfspi_spi_remove,
+ .driver = {
+ .name = "cfspi_sspi",
+ .owner = THIS_MODULE,
+ },
+};
diff --git a/drivers/net/pppolac.c b/drivers/net/pppolac.c
new file mode 100644
index 00000000000..af3202a920a
--- /dev/null
+++ b/drivers/net/pppolac.c
@@ -0,0 +1,380 @@
+/* drivers/net/pppolac.c
+ *
+ * Driver for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661)
+ *
+ * Copyright (C) 2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+/* This driver handles L2TP data packets between a UDP socket and a PPP channel.
+ * To keep things simple, only one session per socket is permitted. Packets are
+ * sent via the socket, so it must keep connected to the same address. One must
+ * not set sequencing in ICCN but let LNS controll it. Currently this driver
+ * only works on IPv4 due to the lack of UDP encapsulation support in IPv6. */
+
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+#include <linux/file.h>
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/udp.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_channel.h>
+#include <net/tcp_states.h>
+#include <asm/uaccess.h>
+
+#define L2TP_CONTROL_BIT 0x80
+#define L2TP_LENGTH_BIT 0x40
+#define L2TP_SEQUENCE_BIT 0x08
+#define L2TP_OFFSET_BIT 0x02
+#define L2TP_VERSION 0x02
+#define L2TP_VERSION_MASK 0x0F
+
+#define PPP_ADDR 0xFF
+#define PPP_CTRL 0x03
+
+union unaligned {
+ __u32 u32;
+} __attribute__((packed));
+
+static inline union unaligned *unaligned(void *ptr)
+{
+ return (union unaligned *)ptr;
+}
+
+static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb)
+{
+ struct sock *sk = (struct sock *)sk_udp->sk_user_data;
+ struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac;
+ __u8 bits;
+ __u8 *ptr;
+
+ /* Drop the packet if it is too short. */
+ if (skb->len < sizeof(struct udphdr) + 6)
+ goto drop;
+
+ /* Put it back if it is a control packet. */
+ if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_BIT)
+ return opt->backlog_rcv(sk_udp, skb);
+
+ /* Skip UDP header. */
+ skb_pull(skb, sizeof(struct udphdr));
+
+ /* Check the version. */
+ if ((skb->data[1] & L2TP_VERSION_MASK) != L2TP_VERSION)
+ goto drop;
+ bits = skb->data[0];
+ ptr = &skb->data[2];
+
+ /* Check the length if it is present. */
+ if (bits & L2TP_LENGTH_BIT) {
+ if ((ptr[0] << 8 | ptr[1]) != skb->len)
+ goto drop;
+ ptr += 2;
+ }
+
+ /* Skip all fields including optional ones. */
+ if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_BIT ? 4 : 0) +
+ (bits & L2TP_LENGTH_BIT ? 2 : 0) +
+ (bits & L2TP_OFFSET_BIT ? 2 : 0)))
+ goto drop;
+
+ /* Skip the offset padding if it is present. */
+ if (bits & L2TP_OFFSET_BIT &&
+ !skb_pull(skb, skb->data[-2] << 8 | skb->data[-1]))
+ goto drop;
+
+ /* Check the tunnel and the session. */
+ if (unaligned(ptr)->u32 != opt->local)
+ goto drop;
+
+ /* Check the sequence if it is present. According to RFC 2661 section
+ * 5.4, the only thing to do is to update opt->sequencing. */
+ opt->sequencing = bits & L2TP_SEQUENCE_BIT;
+
+ /* Skip PPP address and control if they are present. */
+ if (skb->len >= 2 && skb->data[0] == PPP_ADDR &&
+ skb->data[1] == PPP_CTRL)
+ skb_pull(skb, 2);
+
+ /* Fix PPP protocol if it is compressed. */
+ if (skb->len >= 1 && skb->data[0] & 1)
+ skb_push(skb, 1)[0] = 0;
+
+ /* Finally, deliver the packet to PPP channel. */
+ skb_orphan(skb);
+ ppp_input(&pppox_sk(sk)->chan, skb);
+ return NET_RX_SUCCESS;
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb)
+{
+ sock_hold(sk_udp);
+ sk_receive_skb(sk_udp, skb, 0);
+ return 0;
+}
+
+static struct sk_buff_head delivery_queue;
+
+static void pppolac_xmit_core(struct work_struct *delivery_work)
+{
+ mm_segment_t old_fs = get_fs();
+ struct sk_buff *skb;
+
+ set_fs(KERNEL_DS);
+ while ((skb = skb_dequeue(&delivery_queue))) {
+ struct sock *sk_udp = skb->sk;
+ struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len};
+ struct msghdr msg = {
+ .msg_iov = (struct iovec *)&iov,
+ .msg_iovlen = 1,
+ .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT,
+ };
+ sk_udp->sk_prot->sendmsg(NULL, sk_udp, &msg, skb->len);
+ kfree_skb(skb);
+ }
+ set_fs(old_fs);
+}
+
+static DECLARE_WORK(delivery_work, pppolac_xmit_core);
+
+static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+{
+ struct sock *sk_udp = (struct sock *)chan->private;
+ struct pppolac_opt *opt = &pppox_sk(sk_udp->sk_user_data)->proto.lac;
+
+ /* Install PPP address and control. */
+ skb_push(skb, 2);
+ skb->data[0] = PPP_ADDR;
+ skb->data[1] = PPP_CTRL;
+
+ /* Install L2TP header. */
+ if (opt->sequencing) {
+ skb_push(skb, 10);
+ skb->data[0] = L2TP_SEQUENCE_BIT;
+ skb->data[6] = opt->sequence >> 8;
+ skb->data[7] = opt->sequence;
+ skb->data[8] = 0;
+ skb->data[9] = 0;
+ opt->sequence++;
+ } else {
+ skb_push(skb, 6);
+ skb->data[0] = 0;
+ }
+ skb->data[1] = L2TP_VERSION;
+ unaligned(&skb->data[2])->u32 = opt->remote;
+
+ /* Now send the packet via the delivery queue. */
+ skb_set_owner_w(skb, sk_udp);
+ skb_queue_tail(&delivery_queue, skb);
+ schedule_work(&delivery_work);
+ return 1;
+}
+
+/******************************************************************************/
+
+static struct ppp_channel_ops pppolac_channel_ops = {
+ .start_xmit = pppolac_xmit,
+};
+
+static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr,
+ int addrlen, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct pppox_sock *po = pppox_sk(sk);
+ struct sockaddr_pppolac *addr = (struct sockaddr_pppolac *)useraddr;
+ struct socket *sock_udp = NULL;
+ struct sock *sk_udp;
+ int error;
+
+ if (addrlen != sizeof(struct sockaddr_pppolac) ||
+ !addr->local.tunnel || !addr->local.session ||
+ !addr->remote.tunnel || !addr->remote.session) {
+ return -EINVAL;
+ }
+
+ lock_sock(sk);
+ error = -EALREADY;
+ if (sk->sk_state != PPPOX_NONE)
+ goto out;
+
+ sock_udp = sockfd_lookup(addr->udp_socket, &error);
+ if (!sock_udp)
+ goto out;
+ sk_udp = sock_udp->sk;
+ lock_sock(sk_udp);
+
+ /* Remove this check when IPv6 supports UDP encapsulation. */
+ error = -EAFNOSUPPORT;
+ if (sk_udp->sk_family != AF_INET)
+ goto out;
+ error = -EPROTONOSUPPORT;
+ if (sk_udp->sk_protocol != IPPROTO_UDP)
+ goto out;
+ error = -EDESTADDRREQ;
+ if (sk_udp->sk_state != TCP_ESTABLISHED)
+ goto out;
+ error = -EBUSY;
+ if (udp_sk(sk_udp)->encap_type || sk_udp->sk_user_data)
+ goto out;
+ if (!sk_udp->sk_bound_dev_if) {
+ struct dst_entry *dst = sk_dst_get(sk_udp);
+ error = -ENODEV;
+ if (!dst)
+ goto out;
+ sk_udp->sk_bound_dev_if = dst->dev->ifindex;
+ dst_release(dst);
+ }
+
+ po->chan.hdrlen = 12;
+ po->chan.private = sk_udp;
+ po->chan.ops = &pppolac_channel_ops;
+ po->chan.mtu = PPP_MTU - 80;
+ po->proto.lac.local = unaligned(&addr->local)->u32;
+ po->proto.lac.remote = unaligned(&addr->remote)->u32;
+ po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv;
+
+ error = ppp_register_channel(&po->chan);
+ if (error)
+ goto out;
+
+ sk->sk_state = PPPOX_CONNECTED;
+ udp_sk(sk_udp)->encap_type = UDP_ENCAP_L2TPINUDP;
+ udp_sk(sk_udp)->encap_rcv = pppolac_recv;
+ sk_udp->sk_backlog_rcv = pppolac_recv_core;
+ sk_udp->sk_user_data = sk;
+out:
+ if (sock_udp) {
+ release_sock(sk_udp);
+ if (error)
+ sockfd_put(sock_udp);
+ }
+ release_sock(sk);
+ return error;
+}
+
+static int pppolac_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (!sk)
+ return 0;
+
+ lock_sock(sk);
+ if (sock_flag(sk, SOCK_DEAD)) {
+ release_sock(sk);
+ return -EBADF;
+ }
+
+ if (sk->sk_state != PPPOX_NONE) {
+ struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private;
+ lock_sock(sk_udp);
+ pppox_unbind_sock(sk);
+ udp_sk(sk_udp)->encap_type = 0;
+ udp_sk(sk_udp)->encap_rcv = NULL;
+ sk_udp->sk_backlog_rcv = pppox_sk(sk)->proto.lac.backlog_rcv;
+ sk_udp->sk_user_data = NULL;
+ release_sock(sk_udp);
+ sockfd_put(sk_udp->sk_socket);
+ }
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+ release_sock(sk);
+ sock_put(sk);
+ return 0;
+}
+
+/******************************************************************************/
+
+static struct proto pppolac_proto = {
+ .name = "PPPOLAC",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct pppox_sock),
+};
+
+static struct proto_ops pppolac_proto_ops = {
+ .family = PF_PPPOX,
+ .owner = THIS_MODULE,
+ .release = pppolac_release,
+ .bind = sock_no_bind,
+ .connect = pppolac_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = sock_no_poll,
+ .ioctl = pppox_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .mmap = sock_no_mmap,
+};
+
+static int pppolac_create(struct net *net, struct socket *sock)
+{
+ struct sock *sk;
+
+ sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppolac_proto);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sock->state = SS_UNCONNECTED;
+ sock->ops = &pppolac_proto_ops;
+ sk->sk_protocol = PX_PROTO_OLAC;
+ sk->sk_state = PPPOX_NONE;
+ return 0;
+}
+
+/******************************************************************************/
+
+static struct pppox_proto pppolac_pppox_proto = {
+ .create = pppolac_create,
+ .owner = THIS_MODULE,
+};
+
+static int __init pppolac_init(void)
+{
+ int error;
+
+ error = proto_register(&pppolac_proto, 0);
+ if (error)
+ return error;
+
+ error = register_pppox_proto(PX_PROTO_OLAC, &pppolac_pppox_proto);
+ if (error)
+ proto_unregister(&pppolac_proto);
+ else
+ skb_queue_head_init(&delivery_queue);
+ return error;
+}
+
+static void __exit pppolac_exit(void)
+{
+ unregister_pppox_proto(PX_PROTO_OLAC);
+ proto_unregister(&pppolac_proto);
+}
+
+module_init(pppolac_init);
+module_exit(pppolac_exit);
+
+MODULE_DESCRIPTION("PPP on L2TP Access Concentrator (PPPoLAC)");
+MODULE_AUTHOR("Chia-chi Yeh <chiachi@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/pppopns.c b/drivers/net/pppopns.c
new file mode 100644
index 00000000000..298097127c9
--- /dev/null
+++ b/drivers/net/pppopns.c
@@ -0,0 +1,357 @@
+/* drivers/net/pppopns.c
+ *
+ * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637)
+ *
+ * Copyright (C) 2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+/* This driver handles PPTP data packets between a RAW socket and a PPP channel.
+ * The socket is created in the kernel space and connected to the same address
+ * of the control socket. To keep things simple, packets are always sent with
+ * sequence but without acknowledgement. This driver should work on both IPv4
+ * and IPv6. */
+
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+#include <linux/file.h>
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/ppp_defs.h>
+#include <linux/if.h>
+#include <linux/if_ppp.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_channel.h>
+#include <asm/uaccess.h>
+
+#define GRE_HEADER_SIZE 8
+
+#define PPTP_GRE_BITS htons(0x2001)
+#define PPTP_GRE_BITS_MASK htons(0xEF7F)
+#define PPTP_GRE_SEQ_BIT htons(0x1000)
+#define PPTP_GRE_ACK_BIT htons(0x0080)
+#define PPTP_GRE_TYPE htons(0x880B)
+
+#define PPP_ADDR 0xFF
+#define PPP_CTRL 0x03
+
+struct header {
+ __u16 bits;
+ __u16 type;
+ __u16 length;
+ __u16 call;
+ __u32 sequence;
+} __attribute__((packed));
+
+static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb)
+{
+ struct sock *sk = (struct sock *)sk_raw->sk_user_data;
+ struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns;
+ struct header *hdr;
+
+ /* Skip transport header */
+ skb_pull(skb, skb_transport_header(skb) - skb->data);
+
+ /* Drop the packet if it is too short. */
+ if (skb->len < GRE_HEADER_SIZE)
+ goto drop;
+
+ /* Check the header. */
+ hdr = (struct header *)skb->data;
+ if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local ||
+ (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS)
+ goto drop;
+
+ /* Skip all fields including optional ones. */
+ if (!skb_pull(skb, GRE_HEADER_SIZE +
+ (hdr->bits & PPTP_GRE_SEQ_BIT ? 4 : 0) +
+ (hdr->bits & PPTP_GRE_ACK_BIT ? 4 : 0)))
+ goto drop;
+
+ /* Check the length. */
+ if (skb->len != ntohs(hdr->length))
+ goto drop;
+
+ /* Skip PPP address and control if they are present. */
+ if (skb->len >= 2 && skb->data[0] == PPP_ADDR &&
+ skb->data[1] == PPP_CTRL)
+ skb_pull(skb, 2);
+
+ /* Fix PPP protocol if it is compressed. */
+ if (skb->len >= 1 && skb->data[0] & 1)
+ skb_push(skb, 1)[0] = 0;
+
+ /* Finally, deliver the packet to PPP channel. */
+ skb_orphan(skb);
+ ppp_input(&pppox_sk(sk)->chan, skb);
+ return NET_RX_SUCCESS;
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static void pppopns_recv(struct sock *sk_raw, int length)
+{
+ struct sk_buff *skb;
+ while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) {
+ sock_hold(sk_raw);
+ sk_receive_skb(sk_raw, skb, 0);
+ }
+}
+
+static struct sk_buff_head delivery_queue;
+
+static void pppopns_xmit_core(struct work_struct *delivery_work)
+{
+ mm_segment_t old_fs = get_fs();
+ struct sk_buff *skb;
+
+ set_fs(KERNEL_DS);
+ while ((skb = skb_dequeue(&delivery_queue))) {
+ struct sock *sk_raw = skb->sk;
+ struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len};
+ struct msghdr msg = {
+ .msg_iov = (struct iovec *)&iov,
+ .msg_iovlen = 1,
+ .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT,
+ };
+ sk_raw->sk_prot->sendmsg(NULL, sk_raw, &msg, skb->len);
+ kfree_skb(skb);
+ }
+ set_fs(old_fs);
+}
+
+static DECLARE_WORK(delivery_work, pppopns_xmit_core);
+
+static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+{
+ struct sock *sk_raw = (struct sock *)chan->private;
+ struct pppopns_opt *opt = &pppox_sk(sk_raw->sk_user_data)->proto.pns;
+ struct header *hdr;
+ __u16 length;
+
+ /* Install PPP address and control. */
+ skb_push(skb, 2);
+ skb->data[0] = PPP_ADDR;
+ skb->data[1] = PPP_CTRL;
+ length = skb->len;
+
+ /* Install PPTP GRE header. */
+ hdr = (struct header *)skb_push(skb, 12);
+ hdr->bits = PPTP_GRE_BITS | PPTP_GRE_SEQ_BIT;
+ hdr->type = PPTP_GRE_TYPE;
+ hdr->length = htons(length);
+ hdr->call = opt->remote;
+ hdr->sequence = htonl(opt->sequence);
+ opt->sequence++;
+
+ /* Now send the packet via the delivery queue. */
+ skb_set_owner_w(skb, sk_raw);
+ skb_queue_tail(&delivery_queue, skb);
+ schedule_work(&delivery_work);
+ return 1;
+}
+
+/******************************************************************************/
+
+static struct ppp_channel_ops pppopns_channel_ops = {
+ .start_xmit = pppopns_xmit,
+};
+
+static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr,
+ int addrlen, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct pppox_sock *po = pppox_sk(sk);
+ struct sockaddr_pppopns *addr = (struct sockaddr_pppopns *)useraddr;
+ struct sockaddr_storage ss;
+ struct socket *sock_tcp = NULL;
+ struct socket *sock_raw = NULL;
+ struct sock *sk_tcp;
+ struct sock *sk_raw;
+ int error;
+
+ if (addrlen != sizeof(struct sockaddr_pppopns))
+ return -EINVAL;
+
+ lock_sock(sk);
+ error = -EALREADY;
+ if (sk->sk_state != PPPOX_NONE)
+ goto out;
+
+ sock_tcp = sockfd_lookup(addr->tcp_socket, &error);
+ if (!sock_tcp)
+ goto out;
+ sk_tcp = sock_tcp->sk;
+ error = -EPROTONOSUPPORT;
+ if (sk_tcp->sk_protocol != IPPROTO_TCP)
+ goto out;
+ addrlen = sizeof(struct sockaddr_storage);
+ error = kernel_getpeername(sock_tcp, (struct sockaddr *)&ss, &addrlen);
+ if (error)
+ goto out;
+ if (!sk_tcp->sk_bound_dev_if) {
+ struct dst_entry *dst = sk_dst_get(sk_tcp);
+ error = -ENODEV;
+ if (!dst)
+ goto out;
+ sk_tcp->sk_bound_dev_if = dst->dev->ifindex;
+ dst_release(dst);
+ }
+
+ error = sock_create(ss.ss_family, SOCK_RAW, IPPROTO_GRE, &sock_raw);
+ if (error)
+ goto out;
+ sk_raw = sock_raw->sk;
+ sk_raw->sk_bound_dev_if = sk_tcp->sk_bound_dev_if;
+ error = kernel_connect(sock_raw, (struct sockaddr *)&ss, addrlen, 0);
+ if (error)
+ goto out;
+
+ po->chan.hdrlen = 14;
+ po->chan.private = sk_raw;
+ po->chan.ops = &pppopns_channel_ops;
+ po->chan.mtu = PPP_MTU - 80;
+ po->proto.pns.local = addr->local;
+ po->proto.pns.remote = addr->remote;
+ po->proto.pns.data_ready = sk_raw->sk_data_ready;
+ po->proto.pns.backlog_rcv = sk_raw->sk_backlog_rcv;
+
+ error = ppp_register_channel(&po->chan);
+ if (error)
+ goto out;
+
+ sk->sk_state = PPPOX_CONNECTED;
+ lock_sock(sk_raw);
+ sk_raw->sk_data_ready = pppopns_recv;
+ sk_raw->sk_backlog_rcv = pppopns_recv_core;
+ sk_raw->sk_user_data = sk;
+ release_sock(sk_raw);
+out:
+ if (sock_tcp)
+ sockfd_put(sock_tcp);
+ if (error && sock_raw)
+ sock_release(sock_raw);
+ release_sock(sk);
+ return error;
+}
+
+static int pppopns_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (!sk)
+ return 0;
+
+ lock_sock(sk);
+ if (sock_flag(sk, SOCK_DEAD)) {
+ release_sock(sk);
+ return -EBADF;
+ }
+
+ if (sk->sk_state != PPPOX_NONE) {
+ struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private;
+ lock_sock(sk_raw);
+ pppox_unbind_sock(sk);
+ sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready;
+ sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv;
+ sk_raw->sk_user_data = NULL;
+ release_sock(sk_raw);
+ sock_release(sk_raw->sk_socket);
+ }
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+ release_sock(sk);
+ sock_put(sk);
+ return 0;
+}
+
+/******************************************************************************/
+
+static struct proto pppopns_proto = {
+ .name = "PPPOPNS",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct pppox_sock),
+};
+
+static struct proto_ops pppopns_proto_ops = {
+ .family = PF_PPPOX,
+ .owner = THIS_MODULE,
+ .release = pppopns_release,
+ .bind = sock_no_bind,
+ .connect = pppopns_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = sock_no_poll,
+ .ioctl = pppox_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .mmap = sock_no_mmap,
+};
+
+static int pppopns_create(struct net *net, struct socket *sock)
+{
+ struct sock *sk;
+
+ sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppopns_proto);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sock->state = SS_UNCONNECTED;
+ sock->ops = &pppopns_proto_ops;
+ sk->sk_protocol = PX_PROTO_OPNS;
+ sk->sk_state = PPPOX_NONE;
+ return 0;
+}
+
+/******************************************************************************/
+
+static struct pppox_proto pppopns_pppox_proto = {
+ .create = pppopns_create,
+ .owner = THIS_MODULE,
+};
+
+static int __init pppopns_init(void)
+{
+ int error;
+
+ error = proto_register(&pppopns_proto, 0);
+ if (error)
+ return error;
+
+ error = register_pppox_proto(PX_PROTO_OPNS, &pppopns_pppox_proto);
+ if (error)
+ proto_unregister(&pppopns_proto);
+ else
+ skb_queue_head_init(&delivery_queue);
+ return error;
+}
+
+static void __exit pppopns_exit(void)
+{
+ unregister_pppox_proto(PX_PROTO_OPNS);
+ proto_unregister(&pppopns_proto);
+}
+
+module_init(pppopns_init);
+module_exit(pppopns_exit);
+
+MODULE_DESCRIPTION("PPP on PPTP Network Server (PPPoPNS)");
+MODULE_AUTHOR("Chia-chi Yeh <chiachi@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c
index 7a7b01a0fff..8250ecf0391 100644
--- a/drivers/net/smsc911x.c
+++ b/drivers/net/smsc911x.c
@@ -119,12 +119,15 @@ struct smsc911x_data {
static inline u32 __smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
{
+ void __iomem *addr = pdata->ioaddr;
+ int shift = pdata->config.shift;
+
if (pdata->config.flags & SMSC911X_USE_32BIT)
- return readl(pdata->ioaddr + reg);
+ return readl(addr + (reg << shift));
if (pdata->config.flags & SMSC911X_USE_16BIT)
- return ((readw(pdata->ioaddr + reg) & 0xFFFF) |
- ((readw(pdata->ioaddr + reg + 2) & 0xFFFF) << 16));
+ return ((readw(addr + (reg << shift)) & 0xFFFF) |
+ ((readw(addr + ((reg + 2) << shift)) & 0xFFFF) << 16));
BUG();
return 0;
@@ -145,14 +148,17 @@ static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
static inline void __smsc911x_reg_write(struct smsc911x_data *pdata, u32 reg,
u32 val)
{
+ void __iomem *addr = pdata->ioaddr;
+ int shift = pdata->config.shift;
+
if (pdata->config.flags & SMSC911X_USE_32BIT) {
- writel(val, pdata->ioaddr + reg);
+ writel(val, addr + (reg << shift));
return;
}
if (pdata->config.flags & SMSC911X_USE_16BIT) {
- writew(val & 0xFFFF, pdata->ioaddr + reg);
- writew((val >> 16) & 0xFFFF, pdata->ioaddr + reg + 2);
+ writew(val & 0xFFFF, addr + (reg << shift));
+ writew((val >> 16) & 0xFFFF, addr + ((reg + 2) << shift));
return;
}
@@ -186,7 +192,8 @@ smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf,
}
if (pdata->config.flags & SMSC911X_USE_32BIT) {
- writesl(pdata->ioaddr + TX_DATA_FIFO, buf, wordcount);
+ writesl(pdata->ioaddr + (TX_DATA_FIFO << pdata->config.shift),
+ buf, wordcount);
goto out;
}
@@ -218,7 +225,8 @@ smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf,
}
if (pdata->config.flags & SMSC911X_USE_32BIT) {
- readsl(pdata->ioaddr + RX_DATA_FIFO, buf, wordcount);
+ readsl(pdata->ioaddr + (RX_DATA_FIFO << pdata->config.shift),
+ buf, wordcount);
goto out;
}
@@ -2002,7 +2010,8 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
dev->irq = irq_res->start;
irq_flags = irq_res->flags & IRQF_TRIGGER_MASK;
- pdata->ioaddr = ioremap_nocache(res->start, res_size);
+ pdata->ioaddr = ioremap_nocache(res->start,
+ (res_size << config->shift));
/* copy config parameters across to pdata */
memcpy(&pdata->config, config, sizeof(pdata->config));
diff --git a/drivers/net/u8500_shrm.c b/drivers/net/u8500_shrm.c
new file mode 100644
index 00000000000..488f96f2c04
--- /dev/null
+++ b/drivers/net/u8500_shrm.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2009
+ *
+ * Author: Biju Das <biju.das@stericsson.com> for ST-Ericsson
+ * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com> for ST-Ericsson
+ * Author: Arun Murthy <arun.murthy@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/phonet.h>
+#include <linux/if_phonet.h>
+#include <linux/if_arp.h>
+#include <net/sock.h>
+#include <net/phonet/phonet.h>
+#include <net/phonet/pep.h>
+
+#include <mach/shrm_driver.h>
+#include <mach/shrm_private.h>
+#include <mach/shrm_config.h>
+#include <mach/shrm_net.h>
+#include <mach/shrm.h>
+
+/**
+ * shrm_net_receive() - receive data and copy to user space buffer
+ * @dev: pointer to the network device structure
+ *
+ * Copy data from ISI queue to the user space buffer.
+ */
+int shrm_net_receive(struct net_device *dev)
+{
+ struct sk_buff *skb;
+ struct isadev_context *isadev;
+ struct message_queue *q;
+ u32 msgsize;
+ u32 size = 0;
+ struct shrm_net_iface_priv *net_iface_priv =
+ (struct shrm_net_iface_priv *)netdev_priv(dev);
+ struct shrm_dev *shrm = net_iface_priv->shrm_device;
+
+ isadev = &shrm->isa_context->isadev[ISI_MESSAGING];
+ q = &isadev->dl_queue;
+
+ spin_lock_bh(&q->update_lock);
+ if (list_empty(&q->msg_list)) {
+ spin_unlock_bh(&q->update_lock);
+ dev_dbg(shrm->dev, "Empty Shrm queue\n");
+ return 0;
+ }
+ spin_unlock_bh(&q->update_lock);
+
+ msgsize = get_size_of_new_msg(q);
+ if (msgsize <= 0)
+ return msgsize;
+
+ /*
+ * The packet has been retrieved from the transmission
+ * medium. Build an skb around it, so upper layers can handle it
+ */
+ skb = dev_alloc_skb(msgsize);
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_notice(shrm->dev,
+ "isa rx: low on mem - packet dropped\n");
+ dev->stats.rx_dropped++;
+ goto out;
+ }
+
+ if ((q->readptr+msgsize) >= q->size) {
+ size = (q->size-q->readptr);
+ /*Copy First Part of msg*/
+ skb_copy_to_linear_data(skb,
+ (u8 *)(q->fifo_base + q->readptr), size);
+ skb_put(skb, size);
+
+ /*Copy Second Part of msg at the top of fifo*/
+ skb_copy_to_linear_data_offset(skb, size,
+ (u8 *)(q->fifo_base), (msgsize - size));
+ skb_put(skb, msgsize-size);
+
+ } else {
+ skb_copy_to_linear_data(skb,
+ (u8 *)(q->fifo_base+q->readptr), msgsize);
+ skb_put(skb, msgsize);
+ }
+
+ spin_lock_bh(&q->update_lock);
+ remove_msg_from_queue(q);
+ spin_unlock_bh(&q->update_lock);
+
+ skb_reset_mac_header(skb);
+ __skb_pull(skb, dev->hard_header_len);
+ /*Write metadata, and then pass to the receive level*/
+ skb->dev = dev;/*kmalloc(sizeof(struct net_device), GFP_ATOMIC);*/
+ skb->protocol = htons(ETH_P_PHONET);
+ skb->priority = 0;
+ skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
+ if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) {
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += msgsize;
+ } else
+ dev->stats.rx_dropped++;
+
+ return msgsize;
+out:
+ return -ENOMEM;
+}
+
+static int netdev_isa_open(struct net_device *dev)
+{
+ struct shrm_net_iface_priv *net_iface_priv =
+ (struct shrm_net_iface_priv *)netdev_priv(dev);
+ struct shrm_dev *shrm = net_iface_priv->shrm_device;
+
+ shrm->netdev_flag_up = 1;
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static int netdev_isa_close(struct net_device *dev)
+{
+ struct shrm_net_iface_priv *net_iface_priv =
+ (struct shrm_net_iface_priv *)netdev_priv(dev);
+ struct shrm_dev *shrm = net_iface_priv->shrm_device;
+
+ shrm->netdev_flag_up = 0;
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+ return 0;
+}
+
+static int netdev_isa_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct if_phonet_req *req = (struct if_phonet_req *)ifr;
+
+ switch (cmd) {
+ case SIOCPNGAUTOCONF:
+ req->ifr_phonet_autoconf.device = PN_DEV_HOST;
+ return 0;
+ }
+ return -ENOIOCTLCMD;
+}
+
+static struct net_device_stats *netdev_isa_stats(struct net_device *dev)
+{
+ return &dev->stats;
+}
+
+/**
+ * netdev_isa_write() - write through the net interface
+ * @skb: pointer to the socket buffer
+ * @dev: pointer to the network device structure
+ *
+ * Copies data(ISI message) from the user buffer to the kernel buffer and
+ * schedule transfer thread to transmit the message to the modem via FIFO.
+ */
+static netdev_tx_t netdev_isa_write(struct sk_buff *skb, struct net_device *dev)
+{
+ int err;
+ int retval = 0;
+ struct shrm_net_iface_priv *net_iface_priv =
+ (struct shrm_net_iface_priv *)netdev_priv(dev);
+ struct shrm_dev *shrm = net_iface_priv->shrm_device;
+
+ /*
+ * FIXME:
+ * U8500 modem requires that Pipe created/enabled Indication should
+ * be sent from the port corresponding to GPRS socket.
+ * Also, the U8500 modem does not implement Pipe controller
+ * which takes care of port manipulations for GPRS traffic.
+ *
+ * Now, APE has GPRS socket and the socket for sending
+ * Indication msgs bound to different ports.
+ * Phonet stack does not allow an indication msg to be sent
+ * from GPRS socket, since Phonet stack assumes the presence
+ * of Pipe controller in modem.
+ *
+ * So, due to lack of Pipe controller implementation in the
+ * U8500 modem, carry out the port manipulation related to
+ * GPRS traffic here.
+ * Ideally, it should be done either by Pipe controller in
+ * modem OR some implementation of Pipe controller on APE side
+ */
+ if (skb->data[RESOURCE_ID_INDEX] == PN_PIPE) {
+ if ((skb->data[MSG_ID_INDEX] == PNS_PIPE_CREATED_IND) ||
+ (skb->data[MSG_ID_INDEX] == PNS_PIPE_ENABLED_IND) ||
+ (skb->data[MSG_ID_INDEX] == PNS_PIPE_DISABLED_IND))
+ skb->data[SRC_OBJ_INDEX] = skb->data[PIPE_HDL_INDEX];
+ }
+
+ spin_lock_bh(&shrm->isa_context->common_tx);
+ err = shm_write_msg(shrm, ISI_MESSAGING, skb->data,
+ skb->len);
+ if (!err) {
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ retval = NETDEV_TX_OK;
+ dev_kfree_skb(skb);
+ } else {
+ dev->stats.tx_dropped++;
+ retval = NETDEV_TX_BUSY;
+ }
+ spin_unlock_bh(&shrm->isa_context->common_tx);
+
+ return retval;
+}
+
+static const struct net_device_ops shrm_netdev_ops = {
+ .ndo_open = netdev_isa_open,
+ .ndo_stop = netdev_isa_close,
+ .ndo_do_ioctl = netdev_isa_ioctl,
+ .ndo_start_xmit = netdev_isa_write,
+ .ndo_get_stats = netdev_isa_stats,
+};
+
+static void shm_net_init(struct net_device *dev)
+{
+ struct shrm_net_iface_priv *net_iface_priv;
+
+ dev->netdev_ops = &shrm_netdev_ops;
+ dev->header_ops = &phonet_header_ops;
+ dev->type = ARPHRD_PHONET;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+ dev->mtu = PHONET_MAX_MTU;
+ dev->hard_header_len = SHRM_HLEN;
+ dev->addr_len = PHONET_ALEN;
+ dev->tx_queue_len = PN_TX_QUEUE_LEN;
+ dev->destructor = free_netdev;
+ dev->dev_addr[0] = PN_LINK_ADDR;
+ net_iface_priv = netdev_priv(dev);
+ memset(net_iface_priv, 0 , sizeof(struct shrm_net_iface_priv));
+}
+
+int shrm_register_netdev(struct shrm_dev *shrm)
+{
+ struct net_device *nw_device;
+ struct shrm_net_iface_priv *net_iface_priv;
+ char *devname = "shrm%d";
+ int err;
+
+ /* allocate the net device */
+ nw_device = shrm->ndev = alloc_netdev(
+ sizeof(struct shrm_net_iface_priv),
+ devname, shm_net_init);
+ if (nw_device == NULL) {
+ dev_err(shrm->dev, "Failed to allocate SHRM Netdev\n");
+ return -ENOMEM;
+ }
+ err = register_netdev(shrm->ndev);
+ if (err) {
+ dev_err(shrm->dev, "Err %i in reg shrm-netdev\n", err);
+ free_netdev(shrm->ndev);
+ return -ENODEV;
+ }
+ dev_info(shrm->dev, "Registered shrm netdev\n");
+
+ net_iface_priv = (struct shrm_net_iface_priv *)netdev_priv(nw_device);
+ net_iface_priv->shrm_device = shrm;
+ net_iface_priv->iface_num = 0;
+
+ return err;
+}
+
+int shrm_stop_netdev(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return 0;
+}
+
+int shrm_restart_netdev(struct net_device *dev)
+{
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ return 0;
+}
+
+int shrm_start_netdev(struct net_device *dev)
+{
+ struct shrm_net_iface_priv *net_iface_priv =
+ (struct shrm_net_iface_priv *)netdev_priv(dev);
+ struct shrm_dev *shrm = net_iface_priv->shrm_device;
+
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+ netif_start_queue(dev);
+ shrm->netdev_flag_up = 1;
+ return 0;
+}
+
+int shrm_suspend_netdev(struct net_device *dev)
+{
+ if (netif_running(dev)) {
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+ }
+ netif_device_detach(dev);
+
+ return 0;
+}
+
+int shrm_resume_netdev(struct net_device *dev)
+{
+ netif_device_attach(dev);
+ if (netif_running(dev)) {
+ netif_carrier_on(dev);
+ netif_wake_queue(dev);
+ }
+
+ return 0;
+}
+
+void shrm_unregister_netdev(struct shrm_dev *shrm)
+{
+ unregister_netdev(shrm->ndev);
+}
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 174e3442d51..a37a8293f43 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -271,6 +271,7 @@ config MWL8K
source "drivers/net/wireless/ath/Kconfig"
source "drivers/net/wireless/b43/Kconfig"
source "drivers/net/wireless/b43legacy/Kconfig"
+source "drivers/net/wireless/bcm4329/Kconfig"
source "drivers/net/wireless/hostap/Kconfig"
source "drivers/net/wireless/ipw2x00/Kconfig"
source "drivers/net/wireless/iwlwifi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 5d4ce4d2b32..06cf827833c 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -52,3 +52,5 @@ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
obj-$(CONFIG_WL12XX) += wl12xx/
obj-$(CONFIG_IWM) += iwmc3200wifi/
+
+obj-$(CONFIG_BCM4329) += bcm4329/
diff --git a/drivers/net/wireless/bcm4329/Kconfig b/drivers/net/wireless/bcm4329/Kconfig
new file mode 100644
index 00000000000..ca5760d3238
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/Kconfig
@@ -0,0 +1,27 @@
+config BCM4329
+ tristate "Broadcom 4329 wireless cards support"
+ depends on MMC
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ ---help---
+ This module adds support for wireless adapters based on
+ Broadcom 4329 chipset.
+
+ This driver uses the kernel's wireless extensions subsystem.
+
+ If you choose to build a module, it'll be called dhd. Say M if
+ unsure.
+
+config BCM4329_FW_PATH
+ depends on BCM4329
+ string "Firmware path"
+ default "/system/etc/firmware/fw_bcm4329.bin"
+ ---help---
+ Path to the firmware file.
+
+config BCM4329_NVRAM_PATH
+ depends on BCM4329
+ string "NVRAM path"
+ default "/proc/calibration"
+ ---help---
+ Path to the calibration file.
diff --git a/drivers/net/wireless/bcm4329/Makefile b/drivers/net/wireless/bcm4329/Makefile
new file mode 100644
index 00000000000..f808fb21256
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/Makefile
@@ -0,0 +1,21 @@
+# bcm4329
+DHDCFLAGS = -DLINUX -DBCMDRIVER -DBCMDONGLEHOST -DDHDTHREAD -DBCMWPA2 \
+ -DUNRELEASEDCHIP -Dlinux -DDHD_SDALIGN=64 -DMAX_HDR_READ=64 \
+ -DDHD_FIRSTREAD=64 -DDHD_GPL -DDHD_SCHED -DBDC -DTOE -DDHD_BCMEVENTS \
+ -DSHOW_EVENTS -DBCMSDIO -DDHD_GPL -DBCMLXSDMMC -DBCMPLATFORM_BUS \
+ -Wall -Wstrict-prototypes -Werror -DOOB_INTR_ONLY -DCUSTOMER_HW2 \
+ -DDHD_USE_STATIC_BUF -DMMC_SDIO_ABORT -DDHD_DEBUG_TRAP -DSOFTAP \
+ -DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT -DPKT_FILTER_SUPPORT \
+ -DGET_CUSTOM_MAC_ENABLE -DSET_RANDOM_MAC_SOFTAP -DCSCAN -DHW_OOB \
+ -DKEEP_ALIVE -DCONFIG_US_NON_DFS_CHANNELS_ONLY \
+ -Idrivers/net/wireless/bcm4329 -Idrivers/net/wireless/bcm4329/include
+
+DHDOFILES = dhd_linux.o linux_osl.o bcmutils.o dhd_common.o dhd_custom_gpio.o \
+ wl_iw.o siutils.o sbutils.o aiutils.o hndpmu.o bcmwifi.o dhd_sdio.o \
+ dhd_linux_sched.o dhd_cdc.o bcmsdh_sdmmc.o bcmsdh.o bcmsdh_linux.o \
+ bcmsdh_sdmmc_linux.o
+
+obj-$(CONFIG_BCM4329) += bcm4329.o
+bcm4329-objs += $(DHDOFILES)
+EXTRA_CFLAGS = $(DHDCFLAGS)
+EXTRA_LDFLAGS += --strip-debug
diff --git a/drivers/net/wireless/bcm4329/aiutils.c b/drivers/net/wireless/bcm4329/aiutils.c
new file mode 100644
index 00000000000..df48ac0d83d
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/aiutils.c
@@ -0,0 +1,686 @@
+/*
+ * Misc utility routines for accessing chip-specific features
+ * of the SiliconBackplane-based Broadcom chips.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: aiutils.c,v 1.6.4.7.4.6 2010/04/21 20:43:47 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <pcicfg.h>
+
+#include "siutils_priv.h"
+
+STATIC uint32
+get_asd(si_t *sih, uint32 *eromptr, uint sp, uint ad, uint st,
+ uint32 *addrl, uint32 *addrh, uint32 *sizel, uint32 *sizeh);
+
+
+/* EROM parsing */
+
+static uint32
+get_erom_ent(si_t *sih, uint32 *eromptr, uint32 mask, uint32 match)
+{
+ uint32 ent;
+ uint inv = 0, nom = 0;
+
+ while (TRUE) {
+ ent = R_REG(si_osh(sih), (uint32 *)(uintptr)(*eromptr));
+ *eromptr += sizeof(uint32);
+
+ if (mask == 0)
+ break;
+
+ if ((ent & ER_VALID) == 0) {
+ inv++;
+ continue;
+ }
+
+ if (ent == (ER_END | ER_VALID))
+ break;
+
+ if ((ent & mask) == match)
+ break;
+
+ nom++;
+ }
+
+ SI_MSG(("%s: Returning ent 0x%08x\n", __FUNCTION__, ent));
+ if (inv + nom)
+ SI_MSG((" after %d invalid and %d non-matching entries\n", inv, nom));
+ return ent;
+}
+
+STATIC uint32
+get_asd(si_t *sih, uint32 *eromptr, uint sp, uint ad, uint st,
+ uint32 *addrl, uint32 *addrh, uint32 *sizel, uint32 *sizeh)
+{
+ uint32 asd, sz, szd;
+
+ asd = get_erom_ent(sih, eromptr, ER_VALID, ER_VALID);
+ if (((asd & ER_TAG1) != ER_ADD) ||
+ (((asd & AD_SP_MASK) >> AD_SP_SHIFT) != sp) ||
+ ((asd & AD_ST_MASK) != st)) {
+ /* This is not what we want, "push" it back */
+ *eromptr -= sizeof(uint32);
+ return 0;
+ }
+ *addrl = asd & AD_ADDR_MASK;
+ if (asd & AD_AG32)
+ *addrh = get_erom_ent(sih, eromptr, 0, 0);
+ else
+ *addrh = 0;
+ *sizeh = 0;
+ sz = asd & AD_SZ_MASK;
+ if (sz == AD_SZ_SZD) {
+ szd = get_erom_ent(sih, eromptr, 0, 0);
+ *sizel = szd & SD_SZ_MASK;
+ if (szd & SD_SG32)
+ *sizeh = get_erom_ent(sih, eromptr, 0, 0);
+ } else
+ *sizel = AD_SZ_BASE << (sz >> AD_SZ_SHIFT);
+
+ SI_MSG((" SP %d, ad %d: st = %d, 0x%08x_0x%08x @ 0x%08x_0x%08x\n",
+ sp, ad, st, *sizeh, *sizel, *addrh, *addrl));
+
+ return asd;
+}
+
+/* parse the enumeration rom to identify all cores */
+void
+ai_scan(si_t *sih, void *regs, uint devid)
+{
+ si_info_t *sii = SI_INFO(sih);
+ chipcregs_t *cc = (chipcregs_t *)regs;
+ uint32 erombase, eromptr, eromlim;
+
+ erombase = R_REG(sii->osh, &cc->eromptr);
+
+ switch (BUSTYPE(sih->bustype)) {
+ case SI_BUS:
+ eromptr = (uintptr)REG_MAP(erombase, SI_CORE_SIZE);
+ break;
+
+ case PCI_BUS:
+ /* Set wrappers address */
+ sii->curwrap = (void *)((uintptr)regs + SI_CORE_SIZE);
+
+ /* Now point the window at the erom */
+ OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, erombase);
+ eromptr = (uint32)(uintptr)regs;
+ break;
+
+ case SPI_BUS:
+ case SDIO_BUS:
+ eromptr = erombase;
+ break;
+
+ case PCMCIA_BUS:
+ default:
+ SI_ERROR(("Don't know how to do AXI enumertion on bus %d\n", sih->bustype));
+ ASSERT(0);
+ return;
+ }
+ eromlim = eromptr + ER_REMAPCONTROL;
+
+ SI_MSG(("ai_scan: regs = 0x%p, erombase = 0x%08x, eromptr = 0x%08x, eromlim = 0x%08x\n",
+ regs, erombase, eromptr, eromlim));
+ while (eromptr < eromlim) {
+ uint32 cia, cib, base, cid, mfg, crev, nmw, nsw, nmp, nsp;
+ uint32 mpd, asd, addrl, addrh, sizel, sizeh;
+ uint i, j, idx;
+ bool br;
+
+ br = FALSE;
+
+ /* Grok a component */
+ cia = get_erom_ent(sih, &eromptr, ER_TAG, ER_CI);
+ if (cia == (ER_END | ER_VALID)) {
+ SI_MSG(("Found END of erom after %d cores\n", sii->numcores));
+ return;
+ }
+ base = eromptr - sizeof(uint32);
+ cib = get_erom_ent(sih, &eromptr, 0, 0);
+
+ if ((cib & ER_TAG) != ER_CI) {
+ SI_ERROR(("CIA not followed by CIB\n"));
+ goto error;
+ }
+
+ cid = (cia & CIA_CID_MASK) >> CIA_CID_SHIFT;
+ mfg = (cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT;
+ crev = (cib & CIB_REV_MASK) >> CIB_REV_SHIFT;
+ nmw = (cib & CIB_NMW_MASK) >> CIB_NMW_SHIFT;
+ nsw = (cib & CIB_NSW_MASK) >> CIB_NSW_SHIFT;
+ nmp = (cib & CIB_NMP_MASK) >> CIB_NMP_SHIFT;
+ nsp = (cib & CIB_NSP_MASK) >> CIB_NSP_SHIFT;
+
+ SI_MSG(("Found component 0x%04x/0x%4x rev %d at erom addr 0x%08x, with nmw = %d, "
+ "nsw = %d, nmp = %d & nsp = %d\n",
+ mfg, cid, crev, base, nmw, nsw, nmp, nsp));
+
+ if (((mfg == MFGID_ARM) && (cid == DEF_AI_COMP)) || (nsp == 0))
+ continue;
+ if ((nmw + nsw == 0)) {
+ /* A component which is not a core */
+ if (cid == OOB_ROUTER_CORE_ID) {
+ asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE,
+ &addrl, &addrh, &sizel, &sizeh);
+ if (asd != 0) {
+ sii->common_info->oob_router = addrl;
+ }
+ }
+ continue;
+ }
+
+ idx = sii->numcores;
+/* sii->eromptr[idx] = base; */
+ sii->common_info->cia[idx] = cia;
+ sii->common_info->cib[idx] = cib;
+ sii->common_info->coreid[idx] = cid;
+
+ for (i = 0; i < nmp; i++) {
+ mpd = get_erom_ent(sih, &eromptr, ER_VALID, ER_VALID);
+ if ((mpd & ER_TAG) != ER_MP) {
+ SI_ERROR(("Not enough MP entries for component 0x%x\n", cid));
+ goto error;
+ }
+ SI_MSG((" Master port %d, mp: %d id: %d\n", i,
+ (mpd & MPD_MP_MASK) >> MPD_MP_SHIFT,
+ (mpd & MPD_MUI_MASK) >> MPD_MUI_SHIFT));
+ }
+
+ /* First Slave Address Descriptor should be port 0:
+ * the main register space for the core
+ */
+ asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, &addrl, &addrh, &sizel, &sizeh);
+ if (asd == 0) {
+ /* Try again to see if it is a bridge */
+ asd = get_asd(sih, &eromptr, 0, 0, AD_ST_BRIDGE, &addrl, &addrh,
+ &sizel, &sizeh);
+ if (asd != 0)
+ br = TRUE;
+ else
+ if ((addrh != 0) || (sizeh != 0) || (sizel != SI_CORE_SIZE)) {
+ SI_ERROR(("First Slave ASD for core 0x%04x malformed "
+ "(0x%08x)\n", cid, asd));
+ goto error;
+ }
+ }
+ sii->common_info->coresba[idx] = addrl;
+ sii->common_info->coresba_size[idx] = sizel;
+ /* Get any more ASDs in port 0 */
+ j = 1;
+ do {
+ asd = get_asd(sih, &eromptr, 0, j, AD_ST_SLAVE, &addrl, &addrh,
+ &sizel, &sizeh);
+ if ((asd != 0) && (j == 1) && (sizel == SI_CORE_SIZE))
+ sii->common_info->coresba2[idx] = addrl;
+ sii->common_info->coresba2_size[idx] = sizel;
+ j++;
+ } while (asd != 0);
+
+ /* Go through the ASDs for other slave ports */
+ for (i = 1; i < nsp; i++) {
+ j = 0;
+ do {
+ asd = get_asd(sih, &eromptr, i, j++, AD_ST_SLAVE, &addrl, &addrh,
+ &sizel, &sizeh);
+ } while (asd != 0);
+ if (j == 0) {
+ SI_ERROR((" SP %d has no address descriptors\n", i));
+ goto error;
+ }
+ }
+
+ /* Now get master wrappers */
+ for (i = 0; i < nmw; i++) {
+ asd = get_asd(sih, &eromptr, i, 0, AD_ST_MWRAP, &addrl, &addrh,
+ &sizel, &sizeh);
+ if (asd == 0) {
+ SI_ERROR(("Missing descriptor for MW %d\n", i));
+ goto error;
+ }
+ if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) {
+ SI_ERROR(("Master wrapper %d is not 4KB\n", i));
+ goto error;
+ }
+ if (i == 0)
+ sii->common_info->wrapba[idx] = addrl;
+ }
+
+ /* And finally slave wrappers */
+ for (i = 0; i < nsw; i++) {
+ uint fwp = (nsp == 1) ? 0 : 1;
+ asd = get_asd(sih, &eromptr, fwp + i, 0, AD_ST_SWRAP, &addrl, &addrh,
+ &sizel, &sizeh);
+ if (asd == 0) {
+ SI_ERROR(("Missing descriptor for SW %d\n", i));
+ goto error;
+ }
+ if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) {
+ SI_ERROR(("Slave wrapper %d is not 4KB\n", i));
+ goto error;
+ }
+ if ((nmw == 0) && (i == 0))
+ sii->common_info->wrapba[idx] = addrl;
+ }
+
+ /* Don't record bridges */
+ if (br)
+ continue;
+
+ /* Done with core */
+ sii->numcores++;
+ }
+
+ SI_ERROR(("Reached end of erom without finding END"));
+
+error:
+ sii->numcores = 0;
+ return;
+}
+
+/* This function changes the logical "focus" to the indicated core.
+ * Return the current core's virtual address.
+ */
+void *
+ai_setcoreidx(si_t *sih, uint coreidx)
+{
+ si_info_t *sii = SI_INFO(sih);
+ uint32 addr = sii->common_info->coresba[coreidx];
+ uint32 wrap = sii->common_info->wrapba[coreidx];
+ void *regs;
+
+ if (coreidx >= sii->numcores)
+ return (NULL);
+
+ /*
+ * If the user has provided an interrupt mask enabled function,
+ * then assert interrupts are disabled before switching the core.
+ */
+ ASSERT((sii->intrsenabled_fn == NULL) || !(*(sii)->intrsenabled_fn)((sii)->intr_arg));
+
+ switch (BUSTYPE(sih->bustype)) {
+ case SI_BUS:
+ /* map new one */
+ if (!sii->common_info->regs[coreidx]) {
+ sii->common_info->regs[coreidx] = REG_MAP(addr, SI_CORE_SIZE);
+ ASSERT(GOODREGS(sii->common_info->regs[coreidx]));
+ }
+ sii->curmap = regs = sii->common_info->regs[coreidx];
+ if (!sii->common_info->wrappers[coreidx]) {
+ sii->common_info->wrappers[coreidx] = REG_MAP(wrap, SI_CORE_SIZE);
+ ASSERT(GOODREGS(sii->common_info->wrappers[coreidx]));
+ }
+ sii->curwrap = sii->common_info->wrappers[coreidx];
+ break;
+
+
+ case SPI_BUS:
+ case SDIO_BUS:
+ sii->curmap = regs = (void *)((uintptr)addr);
+ sii->curwrap = (void *)((uintptr)wrap);
+ break;
+
+ case PCMCIA_BUS:
+ default:
+ ASSERT(0);
+ regs = NULL;
+ break;
+ }
+
+ sii->curmap = regs;
+ sii->curidx = coreidx;
+
+ return regs;
+}
+
+/* Return the number of address spaces in current core */
+int
+ai_numaddrspaces(si_t *sih)
+{
+ return 2;
+}
+
+/* Return the address of the nth address space in the current core */
+uint32
+ai_addrspace(si_t *sih, uint asidx)
+{
+ si_info_t *sii;
+ uint cidx;
+
+ sii = SI_INFO(sih);
+ cidx = sii->curidx;
+
+ if (asidx == 0)
+ return sii->common_info->coresba[cidx];
+ else if (asidx == 1)
+ return sii->common_info->coresba2[cidx];
+ else {
+ SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n",
+ __FUNCTION__, asidx));
+ return 0;
+ }
+}
+
+/* Return the size of the nth address space in the current core */
+uint32
+ai_addrspacesize(si_t *sih, uint asidx)
+{
+ si_info_t *sii;
+ uint cidx;
+
+ sii = SI_INFO(sih);
+ cidx = sii->curidx;
+
+ if (asidx == 0)
+ return sii->common_info->coresba_size[cidx];
+ else if (asidx == 1)
+ return sii->common_info->coresba2_size[cidx];
+ else {
+ SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n",
+ __FUNCTION__, asidx));
+ return 0;
+ }
+}
+
+uint
+ai_flag(si_t *sih)
+{
+ si_info_t *sii;
+ aidmp_t *ai;
+
+ sii = SI_INFO(sih);
+ ai = sii->curwrap;
+
+ return (R_REG(sii->osh, &ai->oobselouta30) & 0x1f);
+}
+
+void
+ai_setint(si_t *sih, int siflag)
+{
+}
+
+void
+ai_write_wrap_reg(si_t *sih, uint32 offset, uint32 val)
+{
+ si_info_t *sii = SI_INFO(sih);
+ aidmp_t *ai = sii->curwrap;
+ W_REG(sii->osh, (uint32 *)((uint8 *)ai+offset), val);
+ return;
+}
+
+uint
+ai_corevendor(si_t *sih)
+{
+ si_info_t *sii;
+ uint32 cia;
+
+ sii = SI_INFO(sih);
+ cia = sii->common_info->cia[sii->curidx];
+ return ((cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT);
+}
+
+uint
+ai_corerev(si_t *sih)
+{
+ si_info_t *sii;
+ uint32 cib;
+
+ sii = SI_INFO(sih);
+ cib = sii->common_info->cib[sii->curidx];
+ return ((cib & CIB_REV_MASK) >> CIB_REV_SHIFT);
+}
+
+bool
+ai_iscoreup(si_t *sih)
+{
+ si_info_t *sii;
+ aidmp_t *ai;
+
+ sii = SI_INFO(sih);
+ ai = sii->curwrap;
+
+ return (((R_REG(sii->osh, &ai->ioctrl) & (SICF_FGC | SICF_CLOCK_EN)) == SICF_CLOCK_EN) &&
+ ((R_REG(sii->osh, &ai->resetctrl) & AIRC_RESET) == 0));
+}
+
+/*
+ * Switch to 'coreidx', issue a single arbitrary 32bit register mask&set operation,
+ * switch back to the original core, and return the new value.
+ *
+ * When using the silicon backplane, no fidleing with interrupts or core switches are needed.
+ *
+ * Also, when using pci/pcie, we can optimize away the core switching for pci registers
+ * and (on newer pci cores) chipcommon registers.
+ */
+uint
+ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val)
+{
+ uint origidx = 0;
+ uint32 *r = NULL;
+ uint w;
+ uint intr_val = 0;
+ bool fast = FALSE;
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ ASSERT(GOODIDX(coreidx));
+ ASSERT(regoff < SI_CORE_SIZE);
+ ASSERT((val & ~mask) == 0);
+
+ if (coreidx >= SI_MAXCORES)
+ return 0;
+
+ if (BUSTYPE(sih->bustype) == SI_BUS) {
+ /* If internal bus, we can always get at everything */
+ fast = TRUE;
+ /* map if does not exist */
+ if (!sii->common_info->wrappers[coreidx]) {
+ sii->common_info->regs[coreidx] =
+ REG_MAP(sii->common_info->coresba[coreidx], SI_CORE_SIZE);
+ ASSERT(GOODREGS(sii->common_info->regs[coreidx]));
+ }
+ r = (uint32 *)((uchar *)sii->common_info->regs[coreidx] + regoff);
+ } else if (BUSTYPE(sih->bustype) == PCI_BUS) {
+ /* If pci/pcie, we can get at pci/pcie regs and on newer cores to chipc */
+
+ if ((sii->common_info->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) {
+ /* Chipc registers are mapped at 12KB */
+
+ fast = TRUE;
+ r = (uint32 *)((char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff);
+ } else if (sii->pub.buscoreidx == coreidx) {
+ /* pci registers are at either in the last 2KB of an 8KB window
+ * or, in pcie and pci rev 13 at 8KB
+ */
+ fast = TRUE;
+ if (SI_FAST(sii))
+ r = (uint32 *)((char *)sii->curmap +
+ PCI_16KB0_PCIREGS_OFFSET + regoff);
+ else
+ r = (uint32 *)((char *)sii->curmap +
+ ((regoff >= SBCONFIGOFF) ?
+ PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) +
+ regoff);
+ }
+ }
+
+ if (!fast) {
+ INTR_OFF(sii, intr_val);
+
+ /* save current core index */
+ origidx = si_coreidx(&sii->pub);
+
+ /* switch core */
+ r = (uint32*) ((uchar*) ai_setcoreidx(&sii->pub, coreidx) + regoff);
+ }
+ ASSERT(r != NULL);
+
+ /* mask and set */
+ if (mask || val) {
+ w = (R_REG(sii->osh, r) & ~mask) | val;
+ W_REG(sii->osh, r, w);
+ }
+
+ /* readback */
+ w = R_REG(sii->osh, r);
+
+ if (!fast) {
+ /* restore core index */
+ if (origidx != coreidx)
+ ai_setcoreidx(&sii->pub, origidx);
+
+ INTR_RESTORE(sii, intr_val);
+ }
+
+ return (w);
+}
+
+void
+ai_core_disable(si_t *sih, uint32 bits)
+{
+ si_info_t *sii;
+ volatile uint32 dummy;
+ aidmp_t *ai;
+
+ sii = SI_INFO(sih);
+
+ ASSERT(GOODREGS(sii->curwrap));
+ ai = sii->curwrap;
+
+ /* if core is already in reset, just return */
+ if (R_REG(sii->osh, &ai->resetctrl) & AIRC_RESET)
+ return;
+
+ W_REG(sii->osh, &ai->ioctrl, bits);
+ dummy = R_REG(sii->osh, &ai->ioctrl);
+ OSL_DELAY(10);
+
+ W_REG(sii->osh, &ai->resetctrl, AIRC_RESET);
+ OSL_DELAY(1);
+}
+
+/* reset and re-enable a core
+ * inputs:
+ * bits - core specific bits that are set during and after reset sequence
+ * resetbits - core specific bits that are set only during reset sequence
+ */
+void
+ai_core_reset(si_t *sih, uint32 bits, uint32 resetbits)
+{
+ si_info_t *sii;
+ aidmp_t *ai;
+ volatile uint32 dummy;
+
+ sii = SI_INFO(sih);
+ ASSERT(GOODREGS(sii->curwrap));
+ ai = sii->curwrap;
+
+ /*
+ * Must do the disable sequence first to work for arbitrary current core state.
+ */
+ ai_core_disable(sih, (bits | resetbits));
+
+ /*
+ * Now do the initialization sequence.
+ */
+ W_REG(sii->osh, &ai->ioctrl, (bits | SICF_FGC | SICF_CLOCK_EN));
+ dummy = R_REG(sii->osh, &ai->ioctrl);
+ W_REG(sii->osh, &ai->resetctrl, 0);
+ OSL_DELAY(1);
+
+ W_REG(sii->osh, &ai->ioctrl, (bits | SICF_CLOCK_EN));
+ dummy = R_REG(sii->osh, &ai->ioctrl);
+ OSL_DELAY(1);
+}
+
+
+void
+ai_core_cflags_wo(si_t *sih, uint32 mask, uint32 val)
+{
+ si_info_t *sii;
+ aidmp_t *ai;
+ uint32 w;
+
+ sii = SI_INFO(sih);
+ ASSERT(GOODREGS(sii->curwrap));
+ ai = sii->curwrap;
+
+ ASSERT((val & ~mask) == 0);
+
+ if (mask || val) {
+ w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val);
+ W_REG(sii->osh, &ai->ioctrl, w);
+ }
+}
+
+uint32
+ai_core_cflags(si_t *sih, uint32 mask, uint32 val)
+{
+ si_info_t *sii;
+ aidmp_t *ai;
+ uint32 w;
+
+ sii = SI_INFO(sih);
+ ASSERT(GOODREGS(sii->curwrap));
+ ai = sii->curwrap;
+
+ ASSERT((val & ~mask) == 0);
+
+ if (mask || val) {
+ w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val);
+ W_REG(sii->osh, &ai->ioctrl, w);
+ }
+
+ return R_REG(sii->osh, &ai->ioctrl);
+}
+
+uint32
+ai_core_sflags(si_t *sih, uint32 mask, uint32 val)
+{
+ si_info_t *sii;
+ aidmp_t *ai;
+ uint32 w;
+
+ sii = SI_INFO(sih);
+ ASSERT(GOODREGS(sii->curwrap));
+ ai = sii->curwrap;
+
+ ASSERT((val & ~mask) == 0);
+ ASSERT((mask & ~SISF_CORE_BITS) == 0);
+
+ if (mask || val) {
+ w = ((R_REG(sii->osh, &ai->iostatus) & ~mask) | val);
+ W_REG(sii->osh, &ai->iostatus, w);
+ }
+
+ return R_REG(sii->osh, &ai->iostatus);
+}
diff --git a/drivers/net/wireless/bcm4329/bcmpcispi.c b/drivers/net/wireless/bcm4329/bcmpcispi.c
new file mode 100644
index 00000000000..1a8b6717f92
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmpcispi.c
@@ -0,0 +1,630 @@
+/*
+ * Broadcom SPI over PCI-SPI Host Controller, low-level hardware driver
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmpcispi.c,v 1.22.2.4.4.5.6.1 2010/08/13 00:26:05 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmutils.h>
+
+#include <sdio.h> /* SDIO Specs */
+#include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h> /* to get msglevel bit values */
+
+#include <pcicfg.h>
+#include <bcmsdspi.h>
+#include <bcmspi.h>
+#include <bcmpcispi.h> /* BRCM PCI-SPI Host Controller Register definitions */
+
+
+/* ndis_osl.h needs to do a runtime check of the osh to map
+ * R_REG/W_REG to bus specific access similar to linux_osl.h.
+ * Until then...
+ */
+/* linux */
+
+#define SPIPCI_RREG R_REG
+#define SPIPCI_WREG W_REG
+
+
+#define SPIPCI_ANDREG(osh, r, v) SPIPCI_WREG(osh, (r), (SPIPCI_RREG(osh, r) & (v)))
+#define SPIPCI_ORREG(osh, r, v) SPIPCI_WREG(osh, (r), (SPIPCI_RREG(osh, r) | (v)))
+
+
+int bcmpcispi_dump = 0; /* Set to dump complete trace of all SPI bus transactions */
+
+typedef struct spih_info_ {
+ uint bar0; /* BAR0 of PCI Card */
+ uint bar1; /* BAR1 of PCI Card */
+ osl_t *osh; /* osh handle */
+ spih_pciregs_t *pciregs; /* PCI Core Registers */
+ spih_regs_t *regs; /* SPI Controller Registers */
+ uint8 rev; /* PCI Card Revision ID */
+} spih_info_t;
+
+
+/* Attach to PCI-SPI Host Controller Hardware */
+bool
+spi_hw_attach(sdioh_info_t *sd)
+{
+ osl_t *osh;
+ spih_info_t *si;
+
+ sd_trace(("%s: enter\n", __FUNCTION__));
+
+ osh = sd->osh;
+
+ if ((si = (spih_info_t *)MALLOC(osh, sizeof(spih_info_t))) == NULL) {
+ sd_err(("%s: out of memory, malloced %d bytes\n", __FUNCTION__, MALLOCED(osh)));
+ return FALSE;
+ }
+
+ bzero(si, sizeof(spih_info_t));
+
+ sd->controller = si;
+
+ si->osh = sd->osh;
+ si->rev = OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_REV, 4) & 0xFF;
+
+ if (si->rev < 3) {
+ sd_err(("Host controller %d not supported, please upgrade to rev >= 3\n", si->rev));
+ MFREE(osh, si, sizeof(spih_info_t));
+ return (FALSE);
+ }
+
+ sd_err(("Attaching to Generic PCI SPI Host Controller Rev %d\n", si->rev));
+
+ /* FPGA Revision < 3 not supported by driver anymore. */
+ ASSERT(si->rev >= 3);
+
+ si->bar0 = sd->bar0;
+
+ /* Rev < 10 PciSpiHost has 2 BARs:
+ * BAR0 = PCI Core Registers
+ * BAR1 = PciSpiHost Registers (all other cores on backplane)
+ *
+ * Rev 10 and up use a different PCI core which only has a single
+ * BAR0 which contains the PciSpiHost Registers.
+ */
+ if (si->rev < 10) {
+ si->pciregs = (spih_pciregs_t *)spi_reg_map(osh,
+ (uintptr)si->bar0,
+ sizeof(spih_pciregs_t));
+ sd_err(("Mapped PCI Core regs to BAR0 at %p\n", si->pciregs));
+
+ si->bar1 = OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_BAR1, 4);
+ si->regs = (spih_regs_t *)spi_reg_map(osh,
+ (uintptr)si->bar1,
+ sizeof(spih_regs_t));
+ sd_err(("Mapped SPI Controller regs to BAR1 at %p\n", si->regs));
+ } else {
+ si->regs = (spih_regs_t *)spi_reg_map(osh,
+ (uintptr)si->bar0,
+ sizeof(spih_regs_t));
+ sd_err(("Mapped SPI Controller regs to BAR0 at %p\n", si->regs));
+ si->pciregs = NULL;
+ }
+ /* Enable SPI Controller, 16.67MHz SPI Clock */
+ SPIPCI_WREG(osh, &si->regs->spih_ctrl, 0x000000d1);
+
+ /* Set extended feature register to defaults */
+ SPIPCI_WREG(osh, &si->regs->spih_ext, 0x00000000);
+
+ /* Set GPIO CS# High (de-asserted) */
+ SPIPCI_WREG(osh, &si->regs->spih_gpio_data, SPIH_CS);
+
+ /* set GPIO[0] to output for CS# */
+ /* set GPIO[1] to output for power control */
+ /* set GPIO[2] to input for card detect */
+ SPIPCI_WREG(osh, &si->regs->spih_gpio_ctrl, (SPIH_CS | SPIH_SLOT_POWER));
+
+ /* Clear out the Read FIFO in case there is any stuff left in there from a previous run. */
+ while ((SPIPCI_RREG(osh, &si->regs->spih_stat) & SPIH_RFEMPTY) == 0) {
+ SPIPCI_RREG(osh, &si->regs->spih_data);
+ }
+
+ /* Wait for power to stabilize to the SDIO Card (100msec was insufficient) */
+ OSL_DELAY(250000);
+
+ /* Check card detect on FPGA Revision >= 4 */
+ if (si->rev >= 4) {
+ if (SPIPCI_RREG(osh, &si->regs->spih_gpio_data) & SPIH_CARD_DETECT) {
+ sd_err(("%s: no card detected in SD slot\n", __FUNCTION__));
+ spi_reg_unmap(osh, (uintptr)si->regs, sizeof(spih_regs_t));
+ if (si->pciregs) {
+ spi_reg_unmap(osh, (uintptr)si->pciregs, sizeof(spih_pciregs_t));
+ }
+ MFREE(osh, si, sizeof(spih_info_t));
+ return FALSE;
+ }
+ }
+
+ /* Interrupts are level sensitive */
+ SPIPCI_WREG(osh, &si->regs->spih_int_edge, 0x80000000);
+
+ /* Interrupts are active low. */
+ SPIPCI_WREG(osh, &si->regs->spih_int_pol, 0x40000004);
+
+ /* Enable interrupts through PCI Core. */
+ if (si->pciregs) {
+ SPIPCI_WREG(osh, &si->pciregs->ICR, PCI_INT_PROP_EN);
+ }
+
+ sd_trace(("%s: exit\n", __FUNCTION__));
+ return TRUE;
+}
+
+/* Detach and return PCI-SPI Hardware to unconfigured state */
+bool
+spi_hw_detach(sdioh_info_t *sd)
+{
+ spih_info_t *si = (spih_info_t *)sd->controller;
+ osl_t *osh = si->osh;
+ spih_regs_t *regs = si->regs;
+ spih_pciregs_t *pciregs = si->pciregs;
+
+ sd_trace(("%s: enter\n", __FUNCTION__));
+
+ SPIPCI_WREG(osh, &regs->spih_ctrl, 0x00000010);
+ SPIPCI_WREG(osh, &regs->spih_gpio_ctrl, 0x00000000); /* Disable GPIO for CS# */
+ SPIPCI_WREG(osh, &regs->spih_int_mask, 0x00000000); /* Clear Intmask */
+ SPIPCI_WREG(osh, &regs->spih_hex_disp, 0x0000DEAF);
+ SPIPCI_WREG(osh, &regs->spih_int_edge, 0x00000000);
+ SPIPCI_WREG(osh, &regs->spih_int_pol, 0x00000000);
+ SPIPCI_WREG(osh, &regs->spih_hex_disp, 0x0000DEAD);
+
+ /* Disable interrupts through PCI Core. */
+ if (si->pciregs) {
+ SPIPCI_WREG(osh, &pciregs->ICR, 0x00000000);
+ spi_reg_unmap(osh, (uintptr)pciregs, sizeof(spih_pciregs_t));
+ }
+ spi_reg_unmap(osh, (uintptr)regs, sizeof(spih_regs_t));
+
+ MFREE(osh, si, sizeof(spih_info_t));
+
+ sd->controller = NULL;
+
+ sd_trace(("%s: exit\n", __FUNCTION__));
+ return TRUE;
+}
+
+/* Switch between internal (PCI) and external clock oscillator */
+static bool
+sdspi_switch_clock(sdioh_info_t *sd, bool ext_clk)
+{
+ spih_info_t *si = (spih_info_t *)sd->controller;
+ osl_t *osh = si->osh;
+ spih_regs_t *regs = si->regs;
+
+ /* Switch to desired clock, and reset the PLL. */
+ SPIPCI_WREG(osh, &regs->spih_pll_ctrl, ext_clk ? SPIH_EXT_CLK : 0);
+
+ SPINWAIT(((SPIPCI_RREG(osh, &regs->spih_pll_status) & SPIH_PLL_LOCKED)
+ != SPIH_PLL_LOCKED), 1000);
+ if ((SPIPCI_RREG(osh, &regs->spih_pll_status) & SPIH_PLL_LOCKED) != SPIH_PLL_LOCKED) {
+ sd_err(("%s: timeout waiting for PLL to lock\n", __FUNCTION__));
+ return (FALSE);
+ }
+ return (TRUE);
+
+}
+
+/* Configure PCI-SPI Host Controller's SPI Clock rate as a divisor into the
+ * base clock rate. The base clock is either the PCI Clock (33MHz) or the
+ * external clock oscillator at U17 on the PciSpiHost.
+ */
+bool
+spi_start_clock(sdioh_info_t *sd, uint16 div)
+{
+ spih_info_t *si = (spih_info_t *)sd->controller;
+ osl_t *osh = si->osh;
+ spih_regs_t *regs = si->regs;
+ uint32 t, espr, disp;
+ uint32 disp_xtal_freq;
+ bool ext_clock = FALSE;
+ char disp_string[5];
+
+ if (div > 2048) {
+ sd_err(("%s: divisor %d too large; using max of 2048\n", __FUNCTION__, div));
+ div = 2048;
+ } else if (div & (div - 1)) { /* Not a power of 2? */
+ /* Round up to a power of 2 */
+ while ((div + 1) & div)
+ div |= div >> 1;
+ div++;
+ }
+
+ /* For FPGA Rev >= 5, the use of an external clock oscillator is supported.
+ * If the oscillator is populated, use it to provide the SPI base clock,
+ * otherwise, default to the PCI clock as the SPI base clock.
+ */
+ if (si->rev >= 5) {
+ uint32 clk_tick;
+ /* Enable the External Clock Oscillator as PLL clock source. */
+ if (!sdspi_switch_clock(sd, TRUE)) {
+ sd_err(("%s: error switching to external clock\n", __FUNCTION__));
+ }
+
+ /* Check to make sure the external clock is running. If not, then it
+ * is not populated on the card, so we will default to the PCI clock.
+ */
+ clk_tick = SPIPCI_RREG(osh, &regs->spih_clk_count);
+ if (clk_tick == SPIPCI_RREG(osh, &regs->spih_clk_count)) {
+
+ /* Switch back to the PCI clock as the clock source. */
+ if (!sdspi_switch_clock(sd, FALSE)) {
+ sd_err(("%s: error switching to external clock\n", __FUNCTION__));
+ }
+ } else {
+ ext_clock = TRUE;
+ }
+ }
+
+ /* Hack to allow hot-swapping oscillators:
+ * 1. Force PCI clock as clock source, using sd_divisor of 0.
+ * 2. Swap oscillator
+ * 3. Set desired sd_divisor (will switch to external oscillator as clock source.
+ */
+ if (div == 0) {
+ ext_clock = FALSE;
+ div = 2;
+
+ /* Select PCI clock as the clock source. */
+ if (!sdspi_switch_clock(sd, FALSE)) {
+ sd_err(("%s: error switching to external clock\n", __FUNCTION__));
+ }
+
+ sd_err(("%s: Ok to hot-swap oscillators.\n", __FUNCTION__));
+ }
+
+ /* If using the external oscillator, read the clock frequency from the controller
+ * The value read is in units of 10000Hz, and it's not a nice round number because
+ * it is calculated by the FPGA. So to make up for that, we round it off.
+ */
+ if (ext_clock == TRUE) {
+ uint32 xtal_freq;
+
+ OSL_DELAY(1000);
+ xtal_freq = SPIPCI_RREG(osh, &regs->spih_xtal_freq) * 10000;
+
+ sd_info(("%s: Oscillator is %dHz\n", __FUNCTION__, xtal_freq));
+
+
+ disp_xtal_freq = xtal_freq / 10000;
+
+ /* Round it off to a nice number. */
+ if ((disp_xtal_freq % 100) > 50) {
+ disp_xtal_freq += 100;
+ }
+
+ disp_xtal_freq = (disp_xtal_freq / 100) * 100;
+ } else {
+ sd_err(("%s: no external oscillator installed, using PCI clock.\n", __FUNCTION__));
+ disp_xtal_freq = 3333;
+ }
+
+ /* Convert the SPI Clock frequency to BCD format. */
+ sprintf(disp_string, "%04d", disp_xtal_freq / div);
+
+ disp = (disp_string[0] - '0') << 12;
+ disp |= (disp_string[1] - '0') << 8;
+ disp |= (disp_string[2] - '0') << 4;
+ disp |= (disp_string[3] - '0');
+
+ /* Select the correct ESPR register value based on the divisor. */
+ switch (div) {
+ case 1: espr = 0x0; break;
+ case 2: espr = 0x1; break;
+ case 4: espr = 0x2; break;
+ case 8: espr = 0x5; break;
+ case 16: espr = 0x3; break;
+ case 32: espr = 0x4; break;
+ case 64: espr = 0x6; break;
+ case 128: espr = 0x7; break;
+ case 256: espr = 0x8; break;
+ case 512: espr = 0x9; break;
+ case 1024: espr = 0xa; break;
+ case 2048: espr = 0xb; break;
+ default: espr = 0x0; ASSERT(0); break;
+ }
+
+ t = SPIPCI_RREG(osh, &regs->spih_ctrl);
+ t &= ~3;
+ t |= espr & 3;
+ SPIPCI_WREG(osh, &regs->spih_ctrl, t);
+
+ t = SPIPCI_RREG(osh, &regs->spih_ext);
+ t &= ~3;
+ t |= (espr >> 2) & 3;
+ SPIPCI_WREG(osh, &regs->spih_ext, t);
+
+ SPIPCI_WREG(osh, &regs->spih_hex_disp, disp);
+
+ /* For Rev 8, writing to the PLL_CTRL register resets
+ * the PLL, and it can re-acquire in 200uS. For
+ * Rev 7 and older, we use a software delay to allow
+ * the PLL to re-acquire, which takes more than 2mS.
+ */
+ if (si->rev < 8) {
+ /* Wait for clock to settle. */
+ OSL_DELAY(5000);
+ }
+
+ sd_info(("%s: SPI_CTRL=0x%08x SPI_EXT=0x%08x\n",
+ __FUNCTION__,
+ SPIPCI_RREG(osh, &regs->spih_ctrl),
+ SPIPCI_RREG(osh, &regs->spih_ext)));
+
+ return TRUE;
+}
+
+/* Configure PCI-SPI Host Controller High-Speed Clocking mode setting */
+bool
+spi_controller_highspeed_mode(sdioh_info_t *sd, bool hsmode)
+{
+ spih_info_t *si = (spih_info_t *)sd->controller;
+ osl_t *osh = si->osh;
+ spih_regs_t *regs = si->regs;
+
+ if (si->rev >= 10) {
+ if (hsmode) {
+ SPIPCI_ORREG(osh, &regs->spih_ext, 0x10);
+ } else {
+ SPIPCI_ANDREG(osh, &regs->spih_ext, ~0x10);
+ }
+ }
+
+ return TRUE;
+}
+
+/* Disable device interrupt */
+void
+spi_devintr_off(sdioh_info_t *sd)
+{
+ spih_info_t *si = (spih_info_t *)sd->controller;
+ osl_t *osh = si->osh;
+ spih_regs_t *regs = si->regs;
+
+ sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+ if (sd->use_client_ints) {
+ sd->intmask &= ~SPIH_DEV_INTR;
+ SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask); /* Clear Intmask */
+ }
+}
+
+/* Enable device interrupt */
+void
+spi_devintr_on(sdioh_info_t *sd)
+{
+ spih_info_t *si = (spih_info_t *)sd->controller;
+ osl_t *osh = si->osh;
+ spih_regs_t *regs = si->regs;
+
+ ASSERT(sd->lockcount == 0);
+ sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+ if (sd->use_client_ints) {
+ if (SPIPCI_RREG(osh, &regs->spih_ctrl) & 0x02) {
+ /* Ack in case one was pending but is no longer... */
+ SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_DEV_INTR);
+ }
+ sd->intmask |= SPIH_DEV_INTR;
+ /* Set device intr in Intmask */
+ SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask);
+ }
+}
+
+/* Check to see if an interrupt belongs to the PCI-SPI Host or a SPI Device */
+bool
+spi_check_client_intr(sdioh_info_t *sd, int *is_dev_intr)
+{
+ spih_info_t *si = (spih_info_t *)sd->controller;
+ osl_t *osh = si->osh;
+ spih_regs_t *regs = si->regs;
+ bool ours = FALSE;
+
+ uint32 raw_int, cur_int;
+ ASSERT(sd);
+
+ if (is_dev_intr)
+ *is_dev_intr = FALSE;
+ raw_int = SPIPCI_RREG(osh, &regs->spih_int_status);
+ cur_int = raw_int & sd->intmask;
+ if (cur_int & SPIH_DEV_INTR) {
+ if (sd->client_intr_enabled && sd->use_client_ints) {
+ sd->intrcount++;
+ ASSERT(sd->intr_handler);
+ ASSERT(sd->intr_handler_arg);
+ (sd->intr_handler)(sd->intr_handler_arg);
+ if (is_dev_intr)
+ *is_dev_intr = TRUE;
+ } else {
+ sd_trace(("%s: Not ready for intr: enabled %d, handler 0x%p\n",
+ __FUNCTION__, sd->client_intr_enabled, sd->intr_handler));
+ }
+ SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_DEV_INTR);
+ SPIPCI_RREG(osh, &regs->spih_int_status);
+ ours = TRUE;
+ } else if (cur_int & SPIH_CTLR_INTR) {
+ /* Interrupt is from SPI FIFO... just clear and ack it... */
+ sd_trace(("%s: SPI CTLR interrupt: raw_int 0x%08x cur_int 0x%08x\n",
+ __FUNCTION__, raw_int, cur_int));
+
+ /* Clear the interrupt in the SPI_STAT register */
+ SPIPCI_WREG(osh, &regs->spih_stat, 0x00000080);
+
+ /* Ack the interrupt in the interrupt controller */
+ SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_CTLR_INTR);
+ SPIPCI_RREG(osh, &regs->spih_int_status);
+
+ ours = TRUE;
+ } else if (cur_int & SPIH_WFIFO_INTR) {
+ sd_trace(("%s: SPI WR FIFO Empty interrupt: raw_int 0x%08x cur_int 0x%08x\n",
+ __FUNCTION__, raw_int, cur_int));
+
+ /* Disable the FIFO Empty Interrupt */
+ sd->intmask &= ~SPIH_WFIFO_INTR;
+ SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask);
+
+ sd->local_intrcount++;
+ sd->got_hcint = TRUE;
+ ours = TRUE;
+ } else {
+ /* Not an error: can share interrupts... */
+ sd_trace(("%s: Not my interrupt: raw_int 0x%08x cur_int 0x%08x\n",
+ __FUNCTION__, raw_int, cur_int));
+ ours = FALSE;
+ }
+
+ return ours;
+}
+
+static void
+hexdump(char *pfx, unsigned char *msg, int msglen)
+{
+ int i, col;
+ char buf[80];
+
+ ASSERT(strlen(pfx) + 49 <= sizeof(buf));
+
+ col = 0;
+
+ for (i = 0; i < msglen; i++, col++) {
+ if (col % 16 == 0)
+ strcpy(buf, pfx);
+ sprintf(buf + strlen(buf), "%02x", msg[i]);
+ if ((col + 1) % 16 == 0)
+ printf("%s\n", buf);
+ else
+ sprintf(buf + strlen(buf), " ");
+ }
+
+ if (col % 16 != 0)
+ printf("%s\n", buf);
+}
+
+/* Send/Receive an SPI Packet */
+void
+spi_sendrecv(sdioh_info_t *sd, uint8 *msg_out, uint8 *msg_in, int msglen)
+{
+ spih_info_t *si = (spih_info_t *)sd->controller;
+ osl_t *osh = si->osh;
+ spih_regs_t *regs = si->regs;
+ uint32 count;
+ uint32 spi_data_out;
+ uint32 spi_data_in;
+ bool yield;
+
+ sd_trace(("%s: enter\n", __FUNCTION__));
+
+ if (bcmpcispi_dump) {
+ printf("SENDRECV(len=%d)\n", msglen);
+ hexdump(" OUT: ", msg_out, msglen);
+ }
+
+#ifdef BCMSDYIELD
+ /* Only yield the CPU and wait for interrupt on Rev 8 and newer FPGA images. */
+ yield = ((msglen > 500) && (si->rev >= 8));
+#else
+ yield = FALSE;
+#endif /* BCMSDYIELD */
+
+ ASSERT(msglen % 4 == 0);
+
+
+ SPIPCI_ANDREG(osh, &regs->spih_gpio_data, ~SPIH_CS); /* Set GPIO CS# Low (asserted) */
+
+ for (count = 0; count < (uint32)msglen/4; count++) {
+ spi_data_out = ((uint32)((uint32 *)msg_out)[count]);
+ SPIPCI_WREG(osh, &regs->spih_data, spi_data_out);
+ }
+
+#ifdef BCMSDYIELD
+ if (yield) {
+ /* Ack the interrupt in the interrupt controller */
+ SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_WFIFO_INTR);
+ SPIPCI_RREG(osh, &regs->spih_int_status);
+
+ /* Enable the FIFO Empty Interrupt */
+ sd->intmask |= SPIH_WFIFO_INTR;
+ sd->got_hcint = FALSE;
+ SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask);
+
+ }
+#endif /* BCMSDYIELD */
+
+ /* Wait for write fifo to empty... */
+ SPIPCI_ANDREG(osh, &regs->spih_gpio_data, ~0x00000020); /* Set GPIO 5 Low */
+
+ if (yield) {
+ ASSERT((SPIPCI_RREG(sd->osh, &regs->spih_stat) & SPIH_WFEMPTY) == 0);
+ }
+
+ spi_waitbits(sd, yield);
+ SPIPCI_ORREG(osh, &regs->spih_gpio_data, 0x00000020); /* Set GPIO 5 High (de-asserted) */
+
+ for (count = 0; count < (uint32)msglen/4; count++) {
+ spi_data_in = SPIPCI_RREG(osh, &regs->spih_data);
+ ((uint32 *)msg_in)[count] = spi_data_in;
+ }
+
+ /* Set GPIO CS# High (de-asserted) */
+ SPIPCI_ORREG(osh, &regs->spih_gpio_data, SPIH_CS);
+
+ if (bcmpcispi_dump) {
+ hexdump(" IN : ", msg_in, msglen);
+ }
+}
+
+void
+spi_spinbits(sdioh_info_t *sd)
+{
+ spih_info_t *si = (spih_info_t *)sd->controller;
+ osl_t *osh = si->osh;
+ spih_regs_t *regs = si->regs;
+ uint spin_count; /* Spin loop bound check */
+
+ spin_count = 0;
+ while ((SPIPCI_RREG(sd->osh, &regs->spih_stat) & SPIH_WFEMPTY) == 0) {
+ if (spin_count > SPI_SPIN_BOUND) {
+ sd_err(("%s: SPIH_WFEMPTY spin bits out of bound %u times \n",
+ __FUNCTION__, spin_count));
+ ASSERT(FALSE);
+ }
+ spin_count++;
+ }
+
+ /* Wait for SPI Transfer state machine to return to IDLE state.
+ * The state bits are only implemented in Rev >= 5 FPGA. These
+ * bits are hardwired to 00 for Rev < 5, so this check doesn't cause
+ * any problems.
+ */
+ spin_count = 0;
+ while ((SPIPCI_RREG(osh, &regs->spih_stat) & SPIH_STATE_MASK) != 0) {
+ if (spin_count > SPI_SPIN_BOUND) {
+ sd_err(("%s: SPIH_STATE_MASK spin bits out of bound %u times \n",
+ __FUNCTION__, spin_count));
+ ASSERT(FALSE);
+ }
+ spin_count++;
+ }
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdh.c b/drivers/net/wireless/bcm4329/bcmsdh.c
new file mode 100644
index 00000000000..4bf5889e5a6
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdh.c
@@ -0,0 +1,652 @@
+/*
+ * BCMSDH interface glue
+ * implement bcmsdh API for SDIOH driver
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh.c,v 1.35.2.1.4.8.6.13 2010/04/06 03:26:57 Exp $
+ */
+/* ****************** BCMSDH Interface Functions *************************** */
+
+#include <typedefs.h>
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <bcmutils.h>
+#include <hndsoc.h>
+#include <siutils.h>
+#include <osl.h>
+
+#include <bcmsdh.h> /* BRCM API for SDIO clients (such as wl, dhd) */
+#include <bcmsdbus.h> /* common SDIO/controller interface */
+#include <sbsdio.h> /* BRCM sdio device core */
+
+#include <sdio.h> /* sdio spec */
+
+#define SDIOH_API_ACCESS_RETRY_LIMIT 2
+const uint bcmsdh_msglevel = BCMSDH_ERROR_VAL;
+
+
+struct bcmsdh_info
+{
+ bool init_success; /* underlying driver successfully attached */
+ void *sdioh; /* handler for sdioh */
+ uint32 vendevid; /* Target Vendor and Device ID on SD bus */
+ osl_t *osh;
+ bool regfail; /* Save status of last reg_read/reg_write call */
+ uint32 sbwad; /* Save backplane window address */
+};
+/* local copy of bcm sd handler */
+bcmsdh_info_t * l_bcmsdh = NULL;
+
+#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
+extern int
+sdioh_enable_hw_oob_intr(void *sdioh, bool enable);
+
+void
+bcmsdh_enable_hw_oob_intr(bcmsdh_info_t *sdh, bool enable)
+{
+ sdioh_enable_hw_oob_intr(sdh->sdioh, enable);
+}
+#endif
+
+bcmsdh_info_t *
+bcmsdh_attach(osl_t *osh, void *cfghdl, void **regsva, uint irq)
+{
+ bcmsdh_info_t *bcmsdh;
+
+ if ((bcmsdh = (bcmsdh_info_t *)MALLOC(osh, sizeof(bcmsdh_info_t))) == NULL) {
+ BCMSDH_ERROR(("bcmsdh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh)));
+ return NULL;
+ }
+ bzero((char *)bcmsdh, sizeof(bcmsdh_info_t));
+
+ /* save the handler locally */
+ l_bcmsdh = bcmsdh;
+
+ if (!(bcmsdh->sdioh = sdioh_attach(osh, cfghdl, irq))) {
+ bcmsdh_detach(osh, bcmsdh);
+ return NULL;
+ }
+
+ bcmsdh->osh = osh;
+ bcmsdh->init_success = TRUE;
+
+ *regsva = (uint32 *)SI_ENUM_BASE;
+
+ /* Report the BAR, to fix if needed */
+ bcmsdh->sbwad = SI_ENUM_BASE;
+ return bcmsdh;
+}
+
+int
+bcmsdh_detach(osl_t *osh, void *sdh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+ if (bcmsdh != NULL) {
+ if (bcmsdh->sdioh) {
+ sdioh_detach(osh, bcmsdh->sdioh);
+ bcmsdh->sdioh = NULL;
+ }
+ MFREE(osh, bcmsdh, sizeof(bcmsdh_info_t));
+ }
+
+ l_bcmsdh = NULL;
+ return 0;
+}
+
+int
+bcmsdh_iovar_op(void *sdh, const char *name,
+ void *params, int plen, void *arg, int len, bool set)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ return sdioh_iovar_op(bcmsdh->sdioh, name, params, plen, arg, len, set);
+}
+
+bool
+bcmsdh_intr_query(void *sdh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+ bool on;
+
+ ASSERT(bcmsdh);
+ status = sdioh_interrupt_query(bcmsdh->sdioh, &on);
+ if (SDIOH_API_SUCCESS(status))
+ return FALSE;
+ else
+ return on;
+}
+
+int
+bcmsdh_intr_enable(void *sdh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+ ASSERT(bcmsdh);
+
+ status = sdioh_interrupt_set(bcmsdh->sdioh, TRUE);
+ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+int
+bcmsdh_intr_disable(void *sdh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+ ASSERT(bcmsdh);
+
+ status = sdioh_interrupt_set(bcmsdh->sdioh, FALSE);
+ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+int
+bcmsdh_intr_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+ ASSERT(bcmsdh);
+
+ status = sdioh_interrupt_register(bcmsdh->sdioh, fn, argh);
+ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+int
+bcmsdh_intr_dereg(void *sdh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+ ASSERT(bcmsdh);
+
+ status = sdioh_interrupt_deregister(bcmsdh->sdioh);
+ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+#if defined(DHD_DEBUG)
+bool
+bcmsdh_intr_pending(void *sdh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+ ASSERT(sdh);
+ return sdioh_interrupt_pending(bcmsdh->sdioh);
+}
+#endif
+
+
+int
+bcmsdh_devremove_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh)
+{
+ ASSERT(sdh);
+
+ /* don't support yet */
+ return BCME_UNSUPPORTED;
+}
+
+uint8
+bcmsdh_cfg_read(void *sdh, uint fnc_num, uint32 addr, int *err)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+ int32 retry = 0;
+#endif
+ uint8 data = 0;
+
+ if (!bcmsdh)
+ bcmsdh = l_bcmsdh;
+
+ ASSERT(bcmsdh->init_success);
+
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+ do {
+ if (retry) /* wait for 1 ms till bus get settled down */
+ OSL_DELAY(1000);
+#endif
+ status = sdioh_cfg_read(bcmsdh->sdioh, fnc_num, addr, (uint8 *)&data);
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+ } while (!SDIOH_API_SUCCESS(status) && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT));
+#endif
+ if (err)
+ *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR);
+
+ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint8data = 0x%x\n", __FUNCTION__,
+ fnc_num, addr, data));
+
+ return data;
+}
+
+void
+bcmsdh_cfg_write(void *sdh, uint fnc_num, uint32 addr, uint8 data, int *err)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+ int32 retry = 0;
+#endif
+
+ if (!bcmsdh)
+ bcmsdh = l_bcmsdh;
+
+ ASSERT(bcmsdh->init_success);
+
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+ do {
+ if (retry) /* wait for 1 ms till bus get settled down */
+ OSL_DELAY(1000);
+#endif
+ status = sdioh_cfg_write(bcmsdh->sdioh, fnc_num, addr, (uint8 *)&data);
+#ifdef SDIOH_API_ACCESS_RETRY_LIMIT
+ } while (!SDIOH_API_SUCCESS(status) && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT));
+#endif
+ if (err)
+ *err = SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR;
+
+ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint8data = 0x%x\n", __FUNCTION__,
+ fnc_num, addr, data));
+}
+
+uint32
+bcmsdh_cfg_read_word(void *sdh, uint fnc_num, uint32 addr, int *err)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+ uint32 data = 0;
+
+ if (!bcmsdh)
+ bcmsdh = l_bcmsdh;
+
+ ASSERT(bcmsdh->init_success);
+
+ status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_READ, fnc_num,
+ addr, &data, 4);
+
+ if (err)
+ *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR);
+
+ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint32data = 0x%x\n", __FUNCTION__,
+ fnc_num, addr, data));
+
+ return data;
+}
+
+void
+bcmsdh_cfg_write_word(void *sdh, uint fnc_num, uint32 addr, uint32 data, int *err)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+
+ if (!bcmsdh)
+ bcmsdh = l_bcmsdh;
+
+ ASSERT(bcmsdh->init_success);
+
+ status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_WRITE, fnc_num,
+ addr, &data, 4);
+
+ if (err)
+ *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR);
+
+ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint32data = 0x%x\n", __FUNCTION__, fnc_num,
+ addr, data));
+}
+
+
+int
+bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+
+ uint8 *tmp_buf, *tmp_ptr;
+ uint8 *ptr;
+ bool ascii = func & ~0xf;
+ func &= 0x7;
+
+ if (!bcmsdh)
+ bcmsdh = l_bcmsdh;
+
+ ASSERT(bcmsdh->init_success);
+ ASSERT(cis);
+ ASSERT(length <= SBSDIO_CIS_SIZE_LIMIT);
+
+ status = sdioh_cis_read(bcmsdh->sdioh, func, cis, length);
+
+ if (ascii) {
+ /* Move binary bits to tmp and format them into the provided buffer. */
+ if ((tmp_buf = (uint8 *)MALLOC(bcmsdh->osh, length)) == NULL) {
+ BCMSDH_ERROR(("%s: out of memory\n", __FUNCTION__));
+ return BCME_NOMEM;
+ }
+ bcopy(cis, tmp_buf, length);
+ for (tmp_ptr = tmp_buf, ptr = cis; ptr < (cis + length - 4); tmp_ptr++) {
+ ptr += sprintf((char*)ptr, "%.2x ", *tmp_ptr & 0xff);
+ if ((((tmp_ptr - tmp_buf) + 1) & 0xf) == 0)
+ ptr += sprintf((char *)ptr, "\n");
+ }
+ MFREE(bcmsdh->osh, tmp_buf, length);
+ }
+
+ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+
+static int
+bcmsdhsdio_set_sbaddr_window(void *sdh, uint32 address)
+{
+ int err = 0;
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
+ (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
+ if (!err)
+ bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID,
+ (address >> 16) & SBSDIO_SBADDRMID_MASK, &err);
+ if (!err)
+ bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH,
+ (address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err);
+
+
+ return err;
+}
+
+uint32
+bcmsdh_reg_read(void *sdh, uint32 addr, uint size)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+ uint32 word = 0;
+ uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+
+ BCMSDH_INFO(("%s:fun = 1, addr = 0x%x, ", __FUNCTION__, addr));
+
+ if (!bcmsdh)
+ bcmsdh = l_bcmsdh;
+
+ ASSERT(bcmsdh->init_success);
+
+ if (bar0 != bcmsdh->sbwad) {
+ if (bcmsdhsdio_set_sbaddr_window(bcmsdh, bar0))
+ return 0xFFFFFFFF;
+
+ bcmsdh->sbwad = bar0;
+ }
+
+ addr &= SBSDIO_SB_OFT_ADDR_MASK;
+ if (size == 4)
+ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL,
+ SDIOH_READ, SDIO_FUNC_1, addr, &word, size);
+
+ bcmsdh->regfail = !(SDIOH_API_SUCCESS(status));
+
+ BCMSDH_INFO(("uint32data = 0x%x\n", word));
+
+ /* if ok, return appropriately masked word */
+ if (SDIOH_API_SUCCESS(status)) {
+ switch (size) {
+ case sizeof(uint8):
+ return (word & 0xff);
+ case sizeof(uint16):
+ return (word & 0xffff);
+ case sizeof(uint32):
+ return word;
+ default:
+ bcmsdh->regfail = TRUE;
+
+ }
+ }
+
+ /* otherwise, bad sdio access or invalid size */
+ BCMSDH_ERROR(("%s: error reading addr 0x%04x size %d\n", __FUNCTION__, addr, size));
+ return 0xFFFFFFFF;
+}
+
+uint32
+bcmsdh_reg_write(void *sdh, uint32 addr, uint size, uint32 data)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+ uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+ int err = 0;
+
+ BCMSDH_INFO(("%s:fun = 1, addr = 0x%x, uint%ddata = 0x%x\n",
+ __FUNCTION__, addr, size*8, data));
+
+ if (!bcmsdh)
+ bcmsdh = l_bcmsdh;
+
+ ASSERT(bcmsdh->init_success);
+
+ if (bar0 != bcmsdh->sbwad) {
+ if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, bar0)))
+ return err;
+
+ bcmsdh->sbwad = bar0;
+ }
+
+ addr &= SBSDIO_SB_OFT_ADDR_MASK;
+ if (size == 4)
+ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+ status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_WRITE, SDIO_FUNC_1,
+ addr, &data, size);
+ bcmsdh->regfail = !(SDIOH_API_SUCCESS(status));
+
+ if (SDIOH_API_SUCCESS(status))
+ return 0;
+
+ BCMSDH_ERROR(("%s: error writing 0x%08x to addr 0x%04x size %d\n",
+ __FUNCTION__, data, addr, size));
+ return 0xFFFFFFFF;
+}
+
+bool
+bcmsdh_regfail(void *sdh)
+{
+ return ((bcmsdh_info_t *)sdh)->regfail;
+}
+
+int
+bcmsdh_recv_buf(void *sdh, uint32 addr, uint fn, uint flags,
+ uint8 *buf, uint nbytes, void *pkt,
+ bcmsdh_cmplt_fn_t complete, void *handle)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+ uint incr_fix;
+ uint width;
+ uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+ int err = 0;
+
+ ASSERT(bcmsdh);
+ ASSERT(bcmsdh->init_success);
+
+ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n",
+ __FUNCTION__, fn, addr, nbytes));
+
+ /* Async not implemented yet */
+ ASSERT(!(flags & SDIO_REQ_ASYNC));
+ if (flags & SDIO_REQ_ASYNC)
+ return BCME_UNSUPPORTED;
+
+ if (bar0 != bcmsdh->sbwad) {
+ if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, bar0)))
+ return err;
+
+ bcmsdh->sbwad = bar0;
+ }
+
+ addr &= SBSDIO_SB_OFT_ADDR_MASK;
+
+ incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
+ width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
+ if (width == 4)
+ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, incr_fix,
+ SDIOH_READ, fn, addr, width, nbytes, buf, pkt);
+
+ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR);
+}
+
+int
+bcmsdh_send_buf(void *sdh, uint32 addr, uint fn, uint flags,
+ uint8 *buf, uint nbytes, void *pkt,
+ bcmsdh_cmplt_fn_t complete, void *handle)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+ uint incr_fix;
+ uint width;
+ uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+ int err = 0;
+
+ ASSERT(bcmsdh);
+ ASSERT(bcmsdh->init_success);
+
+ BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n",
+ __FUNCTION__, fn, addr, nbytes));
+
+ /* Async not implemented yet */
+ ASSERT(!(flags & SDIO_REQ_ASYNC));
+ if (flags & SDIO_REQ_ASYNC)
+ return BCME_UNSUPPORTED;
+
+ if (bar0 != bcmsdh->sbwad) {
+ if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, bar0)))
+ return err;
+
+ bcmsdh->sbwad = bar0;
+ }
+
+ addr &= SBSDIO_SB_OFT_ADDR_MASK;
+
+ incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
+ width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
+ if (width == 4)
+ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, incr_fix,
+ SDIOH_WRITE, fn, addr, width, nbytes, buf, pkt);
+
+ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+int
+bcmsdh_rwdata(void *sdh, uint rw, uint32 addr, uint8 *buf, uint nbytes)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ SDIOH_API_RC status;
+
+ ASSERT(bcmsdh);
+ ASSERT(bcmsdh->init_success);
+ ASSERT((addr & SBSDIO_SBWINDOW_MASK) == 0);
+
+ addr &= SBSDIO_SB_OFT_ADDR_MASK;
+ addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, SDIOH_DATA_INC,
+ (rw ? SDIOH_WRITE : SDIOH_READ), SDIO_FUNC_1,
+ addr, 4, nbytes, buf, NULL);
+
+ return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR);
+}
+
+int
+bcmsdh_abort(void *sdh, uint fn)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+ return sdioh_abort(bcmsdh->sdioh, fn);
+}
+
+int
+bcmsdh_start(void *sdh, int stage)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+ return sdioh_start(bcmsdh->sdioh, stage);
+}
+
+int
+bcmsdh_stop(void *sdh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+ return sdioh_stop(bcmsdh->sdioh);
+}
+
+
+int
+bcmsdh_query_device(void *sdh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+ bcmsdh->vendevid = (VENDOR_BROADCOM << 16) | 0;
+ return (bcmsdh->vendevid);
+}
+
+uint
+bcmsdh_query_iofnum(void *sdh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+ if (!bcmsdh)
+ bcmsdh = l_bcmsdh;
+
+ return (sdioh_query_iofnum(bcmsdh->sdioh));
+}
+
+int
+bcmsdh_reset(bcmsdh_info_t *sdh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+ return sdioh_sdio_reset(bcmsdh->sdioh);
+}
+
+void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh)
+{
+ ASSERT(sdh);
+ return sdh->sdioh;
+}
+
+/* Function to pass device-status bits to DHD. */
+uint32
+bcmsdh_get_dstatus(void *sdh)
+{
+ return 0;
+}
+uint32
+bcmsdh_cur_sbwad(void *sdh)
+{
+ bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh;
+
+ if (!bcmsdh)
+ bcmsdh = l_bcmsdh;
+
+ return (bcmsdh->sbwad);
+}
+
+void
+bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev)
+{
+ return;
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdh_linux.c b/drivers/net/wireless/bcm4329/bcmsdh_linux.c
new file mode 100644
index 00000000000..cdb2c5e1f1e
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdh_linux.c
@@ -0,0 +1,733 @@
+/*
+ * SDIO access interface for drivers - linux specific (pci only)
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh_linux.c,v 1.42.10.10.2.14.4.2 2010/09/15 00:30:11 Exp $
+ */
+
+/**
+ * @file bcmsdh_linux.c
+ */
+
+#define __UNDEF_NO_VERSION__
+
+#include <typedefs.h>
+#include <linuxver.h>
+
+#include <linux/pci.h>
+#include <linux/completion.h>
+
+#include <osl.h>
+#include <pcicfg.h>
+#include <bcmdefs.h>
+#include <bcmdevs.h>
+
+#if defined(OOB_INTR_ONLY)
+#include <linux/irq.h>
+extern void dhdsdio_isr(void * args);
+#include <bcmutils.h>
+#include <dngl_stats.h>
+#include <dhd.h>
+#endif /* defined(OOB_INTR_ONLY) */
+#if defined(CONFIG_MACH_SANDGATE2G) || defined(CONFIG_MACH_LOGICPD_PXA270)
+#if !defined(BCMPLATFORM_BUS)
+#define BCMPLATFORM_BUS
+#endif /* !defined(BCMPLATFORM_BUS) */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19))
+#include <linux/platform_device.h>
+#endif /* KERNEL_VERSION(2, 6, 19) */
+#endif /* CONFIG_MACH_SANDGATE2G || CONFIG_MACH_LOGICPD_PXA270 */
+
+/**
+ * SDIO Host Controller info
+ */
+typedef struct bcmsdh_hc bcmsdh_hc_t;
+
+struct bcmsdh_hc {
+ bcmsdh_hc_t *next;
+#ifdef BCMPLATFORM_BUS
+ struct device *dev; /* platform device handle */
+#else
+ struct pci_dev *dev; /* pci device handle */
+#endif /* BCMPLATFORM_BUS */
+ osl_t *osh;
+ void *regs; /* SDIO Host Controller address */
+ bcmsdh_info_t *sdh; /* SDIO Host Controller handle */
+ void *ch;
+ unsigned int oob_irq;
+ unsigned long oob_flags; /* OOB Host specifiction as edge and etc */
+ bool oob_irq_registered;
+#if defined(OOB_INTR_ONLY)
+ spinlock_t irq_lock;
+#endif
+};
+static bcmsdh_hc_t *sdhcinfo = NULL;
+
+/* driver info, initialized when bcmsdh_register is called */
+static bcmsdh_driver_t drvinfo = {NULL, NULL};
+
+/* debugging macros */
+#define SDLX_MSG(x)
+
+/**
+ * Checks to see if vendor and device IDs match a supported SDIO Host Controller.
+ */
+bool
+bcmsdh_chipmatch(uint16 vendor, uint16 device)
+{
+ /* Add other vendors and devices as required */
+
+#ifdef BCMSDIOH_STD
+ /* Check for Arasan host controller */
+ if (vendor == VENDOR_SI_IMAGE) {
+ return (TRUE);
+ }
+ /* Check for BRCM 27XX Standard host controller */
+ if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) {
+ return (TRUE);
+ }
+ /* Check for BRCM Standard host controller */
+ if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) {
+ return (TRUE);
+ }
+ /* Check for TI PCIxx21 Standard host controller */
+ if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) {
+ return (TRUE);
+ }
+ if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) {
+ return (TRUE);
+ }
+ /* Ricoh R5C822 Standard SDIO Host */
+ if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) {
+ return (TRUE);
+ }
+ /* JMicron Standard SDIO Host */
+ if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) {
+ return (TRUE);
+ }
+
+#endif /* BCMSDIOH_STD */
+#ifdef BCMSDIOH_SPI
+ /* This is the PciSpiHost. */
+ if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
+ printf("Found PCI SPI Host Controller\n");
+ return (TRUE);
+ }
+
+#endif /* BCMSDIOH_SPI */
+
+ return (FALSE);
+}
+
+#if defined(BCMPLATFORM_BUS)
+#if defined(BCMLXSDMMC)
+/* forward declarations */
+int bcmsdh_probe(struct device *dev);
+int bcmsdh_remove(struct device *dev);
+
+EXPORT_SYMBOL(bcmsdh_probe);
+EXPORT_SYMBOL(bcmsdh_remove);
+
+#else
+/* forward declarations */
+static int __devinit bcmsdh_probe(struct device *dev);
+static int __devexit bcmsdh_remove(struct device *dev);
+#endif /* BCMLXSDMMC */
+
+#ifndef BCMLXSDMMC
+static struct device_driver bcmsdh_driver = {
+ .name = "pxa2xx-mci",
+ .bus = &platform_bus_type,
+ .probe = bcmsdh_probe,
+ .remove = bcmsdh_remove,
+ .suspend = NULL,
+ .resume = NULL,
+ };
+#endif /* BCMLXSDMMC */
+
+#ifndef BCMLXSDMMC
+static
+#endif /* BCMLXSDMMC */
+int bcmsdh_probe(struct device *dev)
+{
+ osl_t *osh = NULL;
+ bcmsdh_hc_t *sdhc = NULL;
+ ulong regs = 0;
+ bcmsdh_info_t *sdh = NULL;
+#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
+ struct platform_device *pdev;
+ struct resource *r;
+#endif /* BCMLXSDMMC */
+ int irq = 0;
+ uint32 vendevid;
+ unsigned long irq_flags = 0;
+
+#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
+ pdev = to_platform_device(dev);
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!r || irq == NO_IRQ)
+ return -ENXIO;
+#endif /* BCMLXSDMMC */
+
+#if defined(OOB_INTR_ONLY)
+#ifdef HW_OOB
+ irq_flags = \
+ IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE;
+#else
+ irq_flags = IRQF_TRIGGER_FALLING;
+#endif /* HW_OOB */
+ irq = dhd_customer_oob_irq_map(&irq_flags);
+ if (irq < 0) {
+ SDLX_MSG(("%s: Host irq is not defined\n", __FUNCTION__));
+ return 1;
+ }
+#endif /* defined(OOB_INTR_ONLY) */
+ /* allocate SDIO Host Controller state info */
+ if (!(osh = osl_attach(dev, PCI_BUS, FALSE))) {
+ SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__));
+ goto err;
+ }
+ if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) {
+ SDLX_MSG(("%s: out of memory, allocated %d bytes\n",
+ __FUNCTION__,
+ MALLOCED(osh)));
+ goto err;
+ }
+ bzero(sdhc, sizeof(bcmsdh_hc_t));
+ sdhc->osh = osh;
+
+ sdhc->dev = (void *)dev;
+
+#ifdef BCMLXSDMMC
+ if (!(sdh = bcmsdh_attach(osh, (void *)0,
+ (void **)&regs, irq))) {
+ SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__));
+ goto err;
+ }
+#else
+ if (!(sdh = bcmsdh_attach(osh, (void *)r->start,
+ (void **)&regs, irq))) {
+ SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__));
+ goto err;
+ }
+#endif /* BCMLXSDMMC */
+ sdhc->sdh = sdh;
+ sdhc->oob_irq = irq;
+ sdhc->oob_flags = irq_flags;
+ sdhc->oob_irq_registered = FALSE; /* to make sure.. */
+#if defined(OOB_INTR_ONLY)
+ spin_lock_init(&sdhc->irq_lock);
+#endif
+
+ /* chain SDIO Host Controller info together */
+ sdhc->next = sdhcinfo;
+ sdhcinfo = sdhc;
+ /* Read the vendor/device ID from the CIS */
+ vendevid = bcmsdh_query_device(sdh);
+
+ /* try to attach to the target device */
+ if (!(sdhc->ch = drvinfo.attach((vendevid >> 16),
+ (vendevid & 0xFFFF), 0, 0, 0, 0,
+ (void *)regs, NULL, sdh))) {
+ SDLX_MSG(("%s: device attach failed\n", __FUNCTION__));
+ goto err;
+ }
+
+ return 0;
+
+ /* error handling */
+err:
+ if (sdhc) {
+ if (sdhc->sdh)
+ bcmsdh_detach(sdhc->osh, sdhc->sdh);
+ MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
+ }
+ if (osh)
+ osl_detach(osh);
+ return -ENODEV;
+}
+
+#ifndef BCMLXSDMMC
+static
+#endif /* BCMLXSDMMC */
+int bcmsdh_remove(struct device *dev)
+{
+ bcmsdh_hc_t *sdhc, *prev;
+ osl_t *osh;
+
+ sdhc = sdhcinfo;
+ drvinfo.detach(sdhc->ch);
+ bcmsdh_detach(sdhc->osh, sdhc->sdh);
+ /* find the SDIO Host Controller state for this pdev and take it out from the list */
+ for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) {
+ if (sdhc->dev == (void *)dev) {
+ if (prev)
+ prev->next = sdhc->next;
+ else
+ sdhcinfo = NULL;
+ break;
+ }
+ prev = sdhc;
+ }
+ if (!sdhc) {
+ SDLX_MSG(("%s: failed\n", __FUNCTION__));
+ return 0;
+ }
+
+
+ /* release SDIO Host Controller info */
+ osh = sdhc->osh;
+ MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
+ osl_detach(osh);
+
+#if !defined(BCMLXSDMMC)
+ dev_set_drvdata(dev, NULL);
+#endif /* !defined(BCMLXSDMMC) */
+
+ return 0;
+}
+
+#else /* BCMPLATFORM_BUS */
+
+#if !defined(BCMLXSDMMC)
+/* forward declarations for PCI probe and remove functions. */
+static int __devinit bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
+static void __devexit bcmsdh_pci_remove(struct pci_dev *pdev);
+
+/**
+ * pci id table
+ */
+static struct pci_device_id bcmsdh_pci_devid[] __devinitdata = {
+ { vendor: PCI_ANY_ID,
+ device: PCI_ANY_ID,
+ subvendor: PCI_ANY_ID,
+ subdevice: PCI_ANY_ID,
+ class: 0,
+ class_mask: 0,
+ driver_data: 0,
+ },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, bcmsdh_pci_devid);
+
+/**
+ * SDIO Host Controller pci driver info
+ */
+static struct pci_driver bcmsdh_pci_driver = {
+ node: {},
+ name: "bcmsdh",
+ id_table: bcmsdh_pci_devid,
+ probe: bcmsdh_pci_probe,
+ remove: bcmsdh_pci_remove,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+ save_state: NULL,
+#endif
+ suspend: NULL,
+ resume: NULL,
+};
+
+
+extern uint sd_pci_slot; /* Force detection to a particular PCI */
+ /* slot only . Allows for having multiple */
+ /* WL devices at once in a PC */
+ /* Only one instance of dhd will be */
+ /* usable at a time */
+ /* Upper word is bus number, */
+ /* lower word is slot number */
+ /* Default value of 0xFFFFffff turns this */
+ /* off */
+module_param(sd_pci_slot, uint, 0);
+
+
+/**
+ * Detect supported SDIO Host Controller and attach if found.
+ *
+ * Determine if the device described by pdev is a supported SDIO Host
+ * Controller. If so, attach to it and attach to the target device.
+ */
+static int __devinit
+bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ osl_t *osh = NULL;
+ bcmsdh_hc_t *sdhc = NULL;
+ ulong regs;
+ bcmsdh_info_t *sdh = NULL;
+ int rc;
+
+ if (sd_pci_slot != 0xFFFFffff) {
+ if (pdev->bus->number != (sd_pci_slot>>16) ||
+ PCI_SLOT(pdev->devfn) != (sd_pci_slot&0xffff)) {
+ SDLX_MSG(("%s: %s: bus %X, slot %X, vend %X, dev %X\n",
+ __FUNCTION__,
+ bcmsdh_chipmatch(pdev->vendor, pdev->device) ?
+ "Found compatible SDIOHC" :
+ "Probing unknown device",
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ pdev->vendor, pdev->device));
+ return -ENODEV;
+ }
+ SDLX_MSG(("%s: %s: bus %X, slot %X, vendor %X, device %X (good PCI location)\n",
+ __FUNCTION__,
+ bcmsdh_chipmatch(pdev->vendor, pdev->device) ?
+ "Using compatible SDIOHC" :
+ "WARNING, forced use of unkown device",
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ pdev->vendor, pdev->device));
+ }
+
+ if ((pdev->vendor == VENDOR_TI) && ((pdev->device == PCIXX21_FLASHMEDIA_ID) ||
+ (pdev->device == PCIXX21_FLASHMEDIA0_ID))) {
+ uint32 config_reg;
+
+ SDLX_MSG(("%s: Disabling TI FlashMedia Controller.\n", __FUNCTION__));
+ if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) {
+ SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__));
+ goto err;
+ }
+
+ config_reg = OSL_PCI_READ_CONFIG(osh, 0x4c, 4);
+
+ /*
+ * Set MMC_SD_DIS bit in FlashMedia Controller.
+ * Disbling the SD/MMC Controller in the FlashMedia Controller
+ * allows the Standard SD Host Controller to take over control
+ * of the SD Slot.
+ */
+ config_reg |= 0x02;
+ OSL_PCI_WRITE_CONFIG(osh, 0x4c, 4, config_reg);
+ osl_detach(osh);
+ }
+ /* match this pci device with what we support */
+ /* we can't solely rely on this to believe it is our SDIO Host Controller! */
+ if (!bcmsdh_chipmatch(pdev->vendor, pdev->device)) {
+ return -ENODEV;
+ }
+
+ /* this is a pci device we might support */
+ SDLX_MSG(("%s: Found possible SDIO Host Controller: bus %d slot %d func %d irq %d\n",
+ __FUNCTION__,
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn), pdev->irq));
+
+ /* use bcmsdh_query_device() to get the vendor ID of the target device so
+ * it will eventually appear in the Broadcom string on the console
+ */
+
+ /* allocate SDIO Host Controller state info */
+ if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) {
+ SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__));
+ goto err;
+ }
+ if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) {
+ SDLX_MSG(("%s: out of memory, allocated %d bytes\n",
+ __FUNCTION__,
+ MALLOCED(osh)));
+ goto err;
+ }
+ bzero(sdhc, sizeof(bcmsdh_hc_t));
+ sdhc->osh = osh;
+
+ sdhc->dev = pdev;
+
+ /* map to address where host can access */
+ pci_set_master(pdev);
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ SDLX_MSG(("%s: Cannot enable PCI device\n", __FUNCTION__));
+ goto err;
+ }
+ if (!(sdh = bcmsdh_attach(osh, (void *)(uintptr)pci_resource_start(pdev, 0),
+ (void **)&regs, pdev->irq))) {
+ SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__));
+ goto err;
+ }
+
+ sdhc->sdh = sdh;
+
+ /* try to attach to the target device */
+ if (!(sdhc->ch = drvinfo.attach(VENDOR_BROADCOM, /* pdev->vendor, */
+ bcmsdh_query_device(sdh) & 0xFFFF, 0, 0, 0, 0,
+ (void *)regs, NULL, sdh))) {
+ SDLX_MSG(("%s: device attach failed\n", __FUNCTION__));
+ goto err;
+ }
+
+ /* chain SDIO Host Controller info together */
+ sdhc->next = sdhcinfo;
+ sdhcinfo = sdhc;
+
+ return 0;
+
+ /* error handling */
+err:
+ if (sdhc->sdh)
+ bcmsdh_detach(sdhc->osh, sdhc->sdh);
+ if (sdhc)
+ MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
+ if (osh)
+ osl_detach(osh);
+ return -ENODEV;
+}
+
+
+/**
+ * Detach from target devices and SDIO Host Controller
+ */
+static void __devexit
+bcmsdh_pci_remove(struct pci_dev *pdev)
+{
+ bcmsdh_hc_t *sdhc, *prev;
+ osl_t *osh;
+
+ /* find the SDIO Host Controller state for this pdev and take it out from the list */
+ for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) {
+ if (sdhc->dev == pdev) {
+ if (prev)
+ prev->next = sdhc->next;
+ else
+ sdhcinfo = NULL;
+ break;
+ }
+ prev = sdhc;
+ }
+ if (!sdhc)
+ return;
+
+ drvinfo.detach(sdhc->ch);
+
+ bcmsdh_detach(sdhc->osh, sdhc->sdh);
+
+ /* release SDIO Host Controller info */
+ osh = sdhc->osh;
+ MFREE(osh, sdhc, sizeof(bcmsdh_hc_t));
+ osl_detach(osh);
+}
+#endif /* BCMLXSDMMC */
+#endif /* BCMPLATFORM_BUS */
+
+extern int sdio_function_init(void);
+
+int
+bcmsdh_register(bcmsdh_driver_t *driver)
+{
+ int error = 0;
+
+ drvinfo = *driver;
+
+#if defined(BCMPLATFORM_BUS)
+#if defined(BCMLXSDMMC)
+ SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n"));
+ error = sdio_function_init();
+#else
+ SDLX_MSG(("Intel PXA270 SDIO Driver\n"));
+ error = driver_register(&bcmsdh_driver);
+#endif /* defined(BCMLXSDMMC) */
+ return error;
+#endif /* defined(BCMPLATFORM_BUS) */
+
+#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+ if (!(error = pci_module_init(&bcmsdh_pci_driver)))
+ return 0;
+#else
+ if (!(error = pci_register_driver(&bcmsdh_pci_driver)))
+ return 0;
+#endif
+
+ SDLX_MSG(("%s: pci_module_init failed 0x%x\n", __FUNCTION__, error));
+#endif /* BCMPLATFORM_BUS */
+
+ return error;
+}
+
+extern void sdio_function_cleanup(void);
+
+void
+bcmsdh_unregister(void)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+ if (bcmsdh_pci_driver.node.next)
+#endif
+
+#if defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
+ driver_unregister(&bcmsdh_driver);
+#endif
+#if defined(BCMLXSDMMC)
+ sdio_function_cleanup();
+#endif /* BCMLXSDMMC */
+#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
+ pci_unregister_driver(&bcmsdh_pci_driver);
+#endif /* BCMPLATFORM_BUS */
+}
+
+#if defined(OOB_INTR_ONLY)
+void bcmsdh_oob_intr_set(bool enable)
+{
+ static bool curstate = 1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdhcinfo->irq_lock, flags);
+ if (curstate != enable) {
+ if (enable)
+ enable_irq(sdhcinfo->oob_irq);
+ else
+ disable_irq_nosync(sdhcinfo->oob_irq);
+ curstate = enable;
+ }
+ spin_unlock_irqrestore(&sdhcinfo->irq_lock, flags);
+}
+
+static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
+{
+ dhd_pub_t *dhdp;
+
+ dhdp = (dhd_pub_t *)dev_get_drvdata(sdhcinfo->dev);
+
+ bcmsdh_oob_intr_set(0);
+
+ if (dhdp == NULL) {
+ SDLX_MSG(("Out of band GPIO interrupt fired way too early\n"));
+ return IRQ_HANDLED;
+ }
+
+ dhdsdio_isr((void *)dhdp->bus);
+
+ return IRQ_HANDLED;
+}
+
+int bcmsdh_register_oob_intr(void * dhdp)
+{
+ int error = 0;
+
+ SDLX_MSG(("%s Enter\n", __FUNCTION__));
+
+/* Example of HW_OOB for HW2: please refer to your host specifiction */
+/* sdhcinfo->oob_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; */
+
+ dev_set_drvdata(sdhcinfo->dev, dhdp);
+
+ if (!sdhcinfo->oob_irq_registered) {
+ SDLX_MSG(("%s IRQ=%d Type=%X \n", __FUNCTION__, \
+ (int)sdhcinfo->oob_irq, (int)sdhcinfo->oob_flags));
+ /* Refer to customer Host IRQ docs about proper irqflags definition */
+ error = request_irq(sdhcinfo->oob_irq, wlan_oob_irq, sdhcinfo->oob_flags,
+ "bcmsdh_sdmmc", NULL);
+ if (error)
+ return -ENODEV;
+
+ set_irq_wake(sdhcinfo->oob_irq, 1);
+ sdhcinfo->oob_irq_registered = TRUE;
+ }
+
+ return 0;
+}
+
+void bcmsdh_set_irq(int flag)
+{
+ if (sdhcinfo->oob_irq_registered) {
+ SDLX_MSG(("%s Flag = %d", __FUNCTION__, flag));
+ if (flag) {
+ enable_irq(sdhcinfo->oob_irq);
+ enable_irq_wake(sdhcinfo->oob_irq);
+ } else {
+ disable_irq_wake(sdhcinfo->oob_irq);
+ disable_irq(sdhcinfo->oob_irq);
+ }
+ }
+}
+
+void bcmsdh_unregister_oob_intr(void)
+{
+ SDLX_MSG(("%s: Enter\n", __FUNCTION__));
+
+ set_irq_wake(sdhcinfo->oob_irq, 0);
+ disable_irq(sdhcinfo->oob_irq); /* just in case.. */
+ free_irq(sdhcinfo->oob_irq, NULL);
+ sdhcinfo->oob_irq_registered = FALSE;
+}
+#endif /* defined(OOB_INTR_ONLY) */
+/* Module parameters specific to each host-controller driver */
+
+extern uint sd_msglevel; /* Debug message level */
+module_param(sd_msglevel, uint, 0);
+
+extern uint sd_power; /* 0 = SD Power OFF, 1 = SD Power ON. */
+module_param(sd_power, uint, 0);
+
+extern uint sd_clock; /* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */
+module_param(sd_clock, uint, 0);
+
+extern uint sd_divisor; /* Divisor (-1 means external clock) */
+module_param(sd_divisor, uint, 0);
+
+extern uint sd_sdmode; /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
+module_param(sd_sdmode, uint, 0);
+
+extern uint sd_hiok; /* Ok to use hi-speed mode */
+module_param(sd_hiok, uint, 0);
+
+extern uint sd_f2_blocksize;
+module_param(sd_f2_blocksize, int, 0);
+
+
+#ifdef BCMSDH_MODULE
+EXPORT_SYMBOL(bcmsdh_attach);
+EXPORT_SYMBOL(bcmsdh_detach);
+EXPORT_SYMBOL(bcmsdh_intr_query);
+EXPORT_SYMBOL(bcmsdh_intr_enable);
+EXPORT_SYMBOL(bcmsdh_intr_disable);
+EXPORT_SYMBOL(bcmsdh_intr_reg);
+EXPORT_SYMBOL(bcmsdh_intr_dereg);
+
+#if defined(DHD_DEBUG)
+EXPORT_SYMBOL(bcmsdh_intr_pending);
+#endif
+
+EXPORT_SYMBOL(bcmsdh_devremove_reg);
+EXPORT_SYMBOL(bcmsdh_cfg_read);
+EXPORT_SYMBOL(bcmsdh_cfg_write);
+EXPORT_SYMBOL(bcmsdh_cis_read);
+EXPORT_SYMBOL(bcmsdh_reg_read);
+EXPORT_SYMBOL(bcmsdh_reg_write);
+EXPORT_SYMBOL(bcmsdh_regfail);
+EXPORT_SYMBOL(bcmsdh_send_buf);
+EXPORT_SYMBOL(bcmsdh_recv_buf);
+
+EXPORT_SYMBOL(bcmsdh_rwdata);
+EXPORT_SYMBOL(bcmsdh_abort);
+EXPORT_SYMBOL(bcmsdh_query_device);
+EXPORT_SYMBOL(bcmsdh_query_iofnum);
+EXPORT_SYMBOL(bcmsdh_iovar_op);
+EXPORT_SYMBOL(bcmsdh_register);
+EXPORT_SYMBOL(bcmsdh_unregister);
+EXPORT_SYMBOL(bcmsdh_chipmatch);
+EXPORT_SYMBOL(bcmsdh_reset);
+
+EXPORT_SYMBOL(bcmsdh_get_dstatus);
+EXPORT_SYMBOL(bcmsdh_cfg_read_word);
+EXPORT_SYMBOL(bcmsdh_cfg_write_word);
+EXPORT_SYMBOL(bcmsdh_cur_sbwad);
+EXPORT_SYMBOL(bcmsdh_chipinfo);
+
+#endif /* BCMSDH_MODULE */
diff --git a/drivers/net/wireless/bcm4329/bcmsdh_sdmmc.c b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc.c
new file mode 100644
index 00000000000..031367b8f18
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc.c
@@ -0,0 +1,1304 @@
+/*
+ * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh_sdmmc.c,v 1.1.2.5.6.30.4.1 2010/09/02 23:12:21 Exp $
+ */
+#include <typedefs.h>
+
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <bcmutils.h>
+#include <osl.h>
+#include <sdio.h> /* SDIO Device and Protocol Specs */
+#include <sdioh.h> /* SDIO Host Controller Specification */
+#include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h> /* ioctl/iovars */
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+
+#include <dngl_stats.h>
+#include <dhd.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+#include <linux/suspend.h>
+extern volatile bool dhd_mmc_suspend;
+#endif
+#include "bcmsdh_sdmmc.h"
+
+#ifndef BCMSDH_MODULE
+extern int sdio_function_init(void);
+extern void sdio_function_cleanup(void);
+#endif /* BCMSDH_MODULE */
+
+#if !defined(OOB_INTR_ONLY)
+static void IRQHandler(struct sdio_func *func);
+static void IRQHandlerF2(struct sdio_func *func);
+#endif /* !defined(OOB_INTR_ONLY) */
+static int sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr);
+extern int sdio_reset_comm(struct mmc_card *card);
+
+extern PBCMSDH_SDMMC_INSTANCE gInstance;
+
+uint sd_sdmode = SDIOH_MODE_SD4; /* Use SD4 mode by default */
+uint sd_f2_blocksize = 512; /* Default blocksize */
+
+uint sd_divisor = 2; /* Default 48MHz/2 = 24MHz */
+
+uint sd_power = 1; /* Default to SD Slot powered ON */
+uint sd_clock = 1; /* Default to SD Clock turned ON */
+uint sd_hiok = FALSE; /* Don't use hi-speed mode by default */
+uint sd_msglevel = 0x01;
+uint sd_use_dma = TRUE;
+DHD_PM_RESUME_WAIT_INIT(sdioh_request_byte_wait);
+DHD_PM_RESUME_WAIT_INIT(sdioh_request_word_wait);
+DHD_PM_RESUME_WAIT_INIT(sdioh_request_packet_wait);
+DHD_PM_RESUME_WAIT_INIT(sdioh_request_buffer_wait);
+
+#define DMA_ALIGN_MASK 0x03
+
+int sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data);
+
+static int
+sdioh_sdmmc_card_enablefuncs(sdioh_info_t *sd)
+{
+ int err_ret;
+ uint32 fbraddr;
+ uint8 func;
+
+ sd_trace(("%s\n", __FUNCTION__));
+
+ /* Get the Card's common CIS address */
+ sd->com_cis_ptr = sdioh_sdmmc_get_cisaddr(sd, SDIOD_CCCR_CISPTR_0);
+ sd->func_cis_ptr[0] = sd->com_cis_ptr;
+ sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
+
+ /* Get the Card's function CIS (for each function) */
+ for (fbraddr = SDIOD_FBR_STARTADDR, func = 1;
+ func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
+ sd->func_cis_ptr[func] = sdioh_sdmmc_get_cisaddr(sd, SDIOD_FBR_CISPTR_0 + fbraddr);
+ sd_info(("%s: Function %d CIS Ptr = 0x%x\n",
+ __FUNCTION__, func, sd->func_cis_ptr[func]));
+ }
+
+ sd->func_cis_ptr[0] = sd->com_cis_ptr;
+ sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
+
+ /* Enable Function 1 */
+ sdio_claim_host(gInstance->func[1]);
+ err_ret = sdio_enable_func(gInstance->func[1]);
+ sdio_release_host(gInstance->func[1]);
+ if (err_ret) {
+ sd_err(("bcmsdh_sdmmc: Failed to enable F1 Err: 0x%08x", err_ret));
+ }
+
+ return FALSE;
+}
+
+/*
+ * Public entry points & extern's
+ */
+extern sdioh_info_t *
+sdioh_attach(osl_t *osh, void *bar0, uint irq)
+{
+ sdioh_info_t *sd;
+ int err_ret;
+
+ sd_trace(("%s\n", __FUNCTION__));
+
+ if (gInstance == NULL) {
+ sd_err(("%s: SDIO Device not present\n", __FUNCTION__));
+ return NULL;
+ }
+
+ if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) {
+ sd_err(("sdioh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh)));
+ return NULL;
+ }
+ bzero((char *)sd, sizeof(sdioh_info_t));
+ sd->osh = osh;
+ if (sdioh_sdmmc_osinit(sd) != 0) {
+ sd_err(("%s:sdioh_sdmmc_osinit() failed\n", __FUNCTION__));
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return NULL;
+ }
+
+ sd->num_funcs = 2;
+ sd->sd_blockmode = TRUE;
+ sd->use_client_ints = TRUE;
+ sd->client_block_size[0] = 64;
+
+ gInstance->sd = sd;
+
+ /* Claim host controller */
+ sdio_claim_host(gInstance->func[1]);
+
+ sd->client_block_size[1] = 64;
+ err_ret = sdio_set_block_size(gInstance->func[1], 64);
+ if (err_ret) {
+ sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n"));
+ }
+
+ /* Release host controller F1 */
+ sdio_release_host(gInstance->func[1]);
+
+ if (gInstance->func[2]) {
+ /* Claim host controller F2 */
+ sdio_claim_host(gInstance->func[2]);
+
+ sd->client_block_size[2] = sd_f2_blocksize;
+ err_ret = sdio_set_block_size(gInstance->func[2], sd_f2_blocksize);
+ if (err_ret) {
+ sd_err(("bcmsdh_sdmmc: Failed to set F2 blocksize to %d\n",
+ sd_f2_blocksize));
+ }
+
+ /* Release host controller F2 */
+ sdio_release_host(gInstance->func[2]);
+ }
+
+ sdioh_sdmmc_card_enablefuncs(sd);
+
+ sd_trace(("%s: Done\n", __FUNCTION__));
+ return sd;
+}
+
+
+extern SDIOH_API_RC
+sdioh_detach(osl_t *osh, sdioh_info_t *sd)
+{
+ sd_trace(("%s\n", __FUNCTION__));
+
+ if (sd) {
+
+ /* Disable Function 2 */
+ sdio_claim_host(gInstance->func[2]);
+ sdio_disable_func(gInstance->func[2]);
+ sdio_release_host(gInstance->func[2]);
+
+ /* Disable Function 1 */
+ sdio_claim_host(gInstance->func[1]);
+ sdio_disable_func(gInstance->func[1]);
+ sdio_release_host(gInstance->func[1]);
+
+ /* deregister irq */
+ sdioh_sdmmc_osfree(sd);
+
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ }
+ return SDIOH_API_RC_SUCCESS;
+}
+
+#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
+
+extern SDIOH_API_RC
+sdioh_enable_func_intr(void)
+{
+ uint8 reg;
+ int err;
+
+ if (gInstance->func[0]) {
+ sdio_claim_host(gInstance->func[0]);
+
+ reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err);
+ if (err) {
+ sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
+ sdio_release_host(gInstance->func[0]);
+ return SDIOH_API_RC_FAIL;
+ }
+
+ /* Enable F1 and F2 interrupts, set master enable */
+ reg |= (INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN | INTR_CTL_MASTER_EN);
+
+ sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err);
+ sdio_release_host(gInstance->func[0]);
+
+ if (err) {
+ sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
+ return SDIOH_API_RC_FAIL;
+ }
+ }
+
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_disable_func_intr(void)
+{
+ uint8 reg;
+ int err;
+
+ if (gInstance->func[0]) {
+ sdio_claim_host(gInstance->func[0]);
+ reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err);
+ if (err) {
+ sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
+ sdio_release_host(gInstance->func[0]);
+ return SDIOH_API_RC_FAIL;
+ }
+
+ reg &= ~(INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN);
+ /* Disable master interrupt with the last function interrupt */
+ if (!(reg & 0xFE))
+ reg = 0;
+ sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err);
+
+ sdio_release_host(gInstance->func[0]);
+ if (err) {
+ sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
+ return SDIOH_API_RC_FAIL;
+ }
+ }
+ return SDIOH_API_RC_SUCCESS;
+}
+#endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */
+
+/* Configure callback to client when we recieve client interrupt */
+extern SDIOH_API_RC
+sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+ if (fn == NULL) {
+ sd_err(("%s: interrupt handler is NULL, not registering\n", __FUNCTION__));
+ return SDIOH_API_RC_FAIL;
+ }
+#if !defined(OOB_INTR_ONLY)
+ sd->intr_handler = fn;
+ sd->intr_handler_arg = argh;
+ sd->intr_handler_valid = TRUE;
+
+ /* register and unmask irq */
+ if (gInstance->func[2]) {
+ sdio_claim_host(gInstance->func[2]);
+ sdio_claim_irq(gInstance->func[2], IRQHandlerF2);
+ sdio_release_host(gInstance->func[2]);
+ }
+
+ if (gInstance->func[1]) {
+ sdio_claim_host(gInstance->func[1]);
+ sdio_claim_irq(gInstance->func[1], IRQHandler);
+ sdio_release_host(gInstance->func[1]);
+ }
+#elif defined(HW_OOB)
+ sdioh_enable_func_intr();
+#endif /* defined(OOB_INTR_ONLY) */
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_deregister(sdioh_info_t *sd)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+
+#if !defined(OOB_INTR_ONLY)
+ if (gInstance->func[1]) {
+ /* register and unmask irq */
+ sdio_claim_host(gInstance->func[1]);
+ sdio_release_irq(gInstance->func[1]);
+ sdio_release_host(gInstance->func[1]);
+ }
+
+ if (gInstance->func[2]) {
+ /* Claim host controller F2 */
+ sdio_claim_host(gInstance->func[2]);
+ sdio_release_irq(gInstance->func[2]);
+ /* Release host controller F2 */
+ sdio_release_host(gInstance->func[2]);
+ }
+
+ sd->intr_handler_valid = FALSE;
+ sd->intr_handler = NULL;
+ sd->intr_handler_arg = NULL;
+#elif defined(HW_OOB)
+ sdioh_disable_func_intr();
+#endif /* !defined(OOB_INTR_ONLY) */
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+ *onoff = sd->client_intr_enabled;
+ return SDIOH_API_RC_SUCCESS;
+}
+
+#if defined(DHD_DEBUG)
+extern bool
+sdioh_interrupt_pending(sdioh_info_t *sd)
+{
+ return (0);
+}
+#endif
+
+uint
+sdioh_query_iofnum(sdioh_info_t *sd)
+{
+ return sd->num_funcs;
+}
+
+/* IOVar table */
+enum {
+ IOV_MSGLEVEL = 1,
+ IOV_BLOCKMODE,
+ IOV_BLOCKSIZE,
+ IOV_DMA,
+ IOV_USEINTS,
+ IOV_NUMINTS,
+ IOV_NUMLOCALINTS,
+ IOV_HOSTREG,
+ IOV_DEVREG,
+ IOV_DIVISOR,
+ IOV_SDMODE,
+ IOV_HISPEED,
+ IOV_HCIREGS,
+ IOV_POWER,
+ IOV_CLOCK,
+ IOV_RXCHAIN
+};
+
+const bcm_iovar_t sdioh_iovars[] = {
+ {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 },
+ {"sd_blockmode", IOV_BLOCKMODE, 0, IOVT_BOOL, 0 },
+ {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */
+ {"sd_dma", IOV_DMA, 0, IOVT_BOOL, 0 },
+ {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 },
+ {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 },
+ {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 },
+ {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
+ {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
+ {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 },
+ {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 },
+ {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 },
+ {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100},
+ {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0 },
+ {"sd_rxchain", IOV_RXCHAIN, 0, IOVT_BOOL, 0 },
+ {NULL, 0, 0, 0, 0 }
+};
+
+int
+sdioh_iovar_op(sdioh_info_t *si, const char *name,
+ void *params, int plen, void *arg, int len, bool set)
+{
+ const bcm_iovar_t *vi = NULL;
+ int bcmerror = 0;
+ int val_size;
+ int32 int_val = 0;
+ bool bool_val;
+ uint32 actionid;
+
+ ASSERT(name);
+ ASSERT(len >= 0);
+
+ /* Get must have return space; Set does not take qualifiers */
+ ASSERT(set || (arg && len));
+ ASSERT(!set || (!params && !plen));
+
+ sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name));
+
+ if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) {
+ bcmerror = BCME_UNSUPPORTED;
+ goto exit;
+ }
+
+ if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0)
+ goto exit;
+
+ /* Set up params so get and set can share the convenience variables */
+ if (params == NULL) {
+ params = arg;
+ plen = len;
+ }
+
+ if (vi->type == IOVT_VOID)
+ val_size = 0;
+ else if (vi->type == IOVT_BUFFER)
+ val_size = len;
+ else
+ val_size = sizeof(int);
+
+ if (plen >= (int)sizeof(int_val))
+ bcopy(params, &int_val, sizeof(int_val));
+
+ bool_val = (int_val != 0) ? TRUE : FALSE;
+
+ actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+ switch (actionid) {
+ case IOV_GVAL(IOV_MSGLEVEL):
+ int_val = (int32)sd_msglevel;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_MSGLEVEL):
+ sd_msglevel = int_val;
+ break;
+
+ case IOV_GVAL(IOV_BLOCKMODE):
+ int_val = (int32)si->sd_blockmode;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_BLOCKMODE):
+ si->sd_blockmode = (bool)int_val;
+ /* Haven't figured out how to make non-block mode with DMA */
+ break;
+
+ case IOV_GVAL(IOV_BLOCKSIZE):
+ if ((uint32)int_val > si->num_funcs) {
+ bcmerror = BCME_BADARG;
+ break;
+ }
+ int_val = (int32)si->client_block_size[int_val];
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_BLOCKSIZE):
+ {
+ uint func = ((uint32)int_val >> 16);
+ uint blksize = (uint16)int_val;
+ uint maxsize;
+
+ if (func > si->num_funcs) {
+ bcmerror = BCME_BADARG;
+ break;
+ }
+
+ switch (func) {
+ case 0: maxsize = 32; break;
+ case 1: maxsize = BLOCK_SIZE_4318; break;
+ case 2: maxsize = BLOCK_SIZE_4328; break;
+ default: maxsize = 0;
+ }
+ if (blksize > maxsize) {
+ bcmerror = BCME_BADARG;
+ break;
+ }
+ if (!blksize) {
+ blksize = maxsize;
+ }
+
+ /* Now set it */
+ si->client_block_size[func] = blksize;
+
+ break;
+ }
+
+ case IOV_GVAL(IOV_RXCHAIN):
+ int_val = FALSE;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_DMA):
+ int_val = (int32)si->sd_use_dma;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_DMA):
+ si->sd_use_dma = (bool)int_val;
+ break;
+
+ case IOV_GVAL(IOV_USEINTS):
+ int_val = (int32)si->use_client_ints;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_USEINTS):
+ si->use_client_ints = (bool)int_val;
+ if (si->use_client_ints)
+ si->intmask |= CLIENT_INTR;
+ else
+ si->intmask &= ~CLIENT_INTR;
+
+ break;
+
+ case IOV_GVAL(IOV_DIVISOR):
+ int_val = (uint32)sd_divisor;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_DIVISOR):
+ sd_divisor = int_val;
+ break;
+
+ case IOV_GVAL(IOV_POWER):
+ int_val = (uint32)sd_power;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_POWER):
+ sd_power = int_val;
+ break;
+
+ case IOV_GVAL(IOV_CLOCK):
+ int_val = (uint32)sd_clock;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_CLOCK):
+ sd_clock = int_val;
+ break;
+
+ case IOV_GVAL(IOV_SDMODE):
+ int_val = (uint32)sd_sdmode;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_SDMODE):
+ sd_sdmode = int_val;
+ break;
+
+ case IOV_GVAL(IOV_HISPEED):
+ int_val = (uint32)sd_hiok;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_HISPEED):
+ sd_hiok = int_val;
+ break;
+
+ case IOV_GVAL(IOV_NUMINTS):
+ int_val = (int32)si->intrcount;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_NUMLOCALINTS):
+ int_val = (int32)0;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_HOSTREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+
+ if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) {
+ sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset));
+ bcmerror = BCME_BADARG;
+ break;
+ }
+
+ sd_trace(("%s: rreg%d at offset %d\n", __FUNCTION__,
+ (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32),
+ sd_ptr->offset));
+ if (sd_ptr->offset & 1)
+ int_val = 8; /* sdioh_sdmmc_rreg8(si, sd_ptr->offset); */
+ else if (sd_ptr->offset & 2)
+ int_val = 16; /* sdioh_sdmmc_rreg16(si, sd_ptr->offset); */
+ else
+ int_val = 32; /* sdioh_sdmmc_rreg(si, sd_ptr->offset); */
+
+ bcopy(&int_val, arg, sizeof(int_val));
+ break;
+ }
+
+ case IOV_SVAL(IOV_HOSTREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+
+ if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) {
+ sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset));
+ bcmerror = BCME_BADARG;
+ break;
+ }
+
+ sd_trace(("%s: wreg%d value 0x%08x at offset %d\n", __FUNCTION__, sd_ptr->value,
+ (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32),
+ sd_ptr->offset));
+ break;
+ }
+
+ case IOV_GVAL(IOV_DEVREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+ uint8 data = 0;
+
+ if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) {
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+
+ int_val = (int)data;
+ bcopy(&int_val, arg, sizeof(int_val));
+ break;
+ }
+
+ case IOV_SVAL(IOV_DEVREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+ uint8 data = (uint8)sd_ptr->value;
+
+ if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) {
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+ break;
+ }
+
+ default:
+ bcmerror = BCME_UNSUPPORTED;
+ break;
+ }
+exit:
+
+ return bcmerror;
+}
+
+#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
+
+SDIOH_API_RC
+sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable)
+{
+ SDIOH_API_RC status;
+ uint8 data;
+
+ if (enable)
+ data = 3; /* enable hw oob interrupt */
+ else
+ data = 4; /* disable hw oob interrupt */
+ data |= 4; /* Active HIGH */
+
+ status = sdioh_request_byte(sd, SDIOH_WRITE, 0, 0xf2, &data);
+ return status;
+}
+#endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */
+
+extern SDIOH_API_RC
+sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+ SDIOH_API_RC status;
+ /* No lock needed since sdioh_request_byte does locking */
+ status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
+ return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+ /* No lock needed since sdioh_request_byte does locking */
+ SDIOH_API_RC status;
+ status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
+ return status;
+}
+
+static int
+sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr)
+{
+ /* read 24 bits and return valid 17 bit addr */
+ int i;
+ uint32 scratch, regdata;
+ uint8 *ptr = (uint8 *)&scratch;
+ for (i = 0; i < 3; i++) {
+ if ((sdioh_sdmmc_card_regread (sd, 0, regaddr, 1, &regdata)) != SUCCESS)
+ sd_err(("%s: Can't read!\n", __FUNCTION__));
+
+ *ptr++ = (uint8) regdata;
+ regaddr++;
+ }
+
+ /* Only the lower 17-bits are valid */
+ scratch = ltoh32(scratch);
+ scratch &= 0x0001FFFF;
+ return (scratch);
+}
+
+extern SDIOH_API_RC
+sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length)
+{
+ uint32 count;
+ int offset;
+ uint32 foo;
+ uint8 *cis = cisd;
+
+ sd_trace(("%s: Func = %d\n", __FUNCTION__, func));
+
+ if (!sd->func_cis_ptr[func]) {
+ bzero(cis, length);
+ sd_err(("%s: no func_cis_ptr[%d]\n", __FUNCTION__, func));
+ return SDIOH_API_RC_FAIL;
+ }
+
+ sd_err(("%s: func_cis_ptr[%d]=0x%04x\n", __FUNCTION__, func, sd->func_cis_ptr[func]));
+
+ for (count = 0; count < length; count++) {
+ offset = sd->func_cis_ptr[func] + count;
+ if (sdioh_sdmmc_card_regread (sd, 0, offset, 1, &foo) < 0) {
+ sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
+ return SDIOH_API_RC_FAIL;
+ }
+
+ *cis = (uint8)(foo & 0xff);
+ cis++;
+ }
+
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte)
+{
+ int err_ret;
+
+ sd_info(("%s: rw=%d, func=%d, addr=0x%05x\n", __FUNCTION__, rw, func, regaddr));
+
+ DHD_PM_RESUME_WAIT(sdioh_request_byte_wait);
+ DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
+ if(rw) { /* CMD52 Write */
+ if (func == 0) {
+ /* Can only directly write to some F0 registers. Handle F2 enable
+ * as a special case.
+ */
+ if (regaddr == SDIOD_CCCR_IOEN) {
+ if (gInstance->func[2]) {
+ sdio_claim_host(gInstance->func[2]);
+ if (*byte & SDIO_FUNC_ENABLE_2) {
+ /* Enable Function 2 */
+ err_ret = sdio_enable_func(gInstance->func[2]);
+ if (err_ret) {
+ sd_err(("bcmsdh_sdmmc: enable F2 failed:%d",
+ err_ret));
+ }
+ } else {
+ /* Disable Function 2 */
+ err_ret = sdio_disable_func(gInstance->func[2]);
+ if (err_ret) {
+ sd_err(("bcmsdh_sdmmc: Disab F2 failed:%d",
+ err_ret));
+ }
+ }
+ sdio_release_host(gInstance->func[2]);
+ }
+ }
+#if defined(MMC_SDIO_ABORT)
+ /* to allow abort command through F1 */
+ else if (regaddr == SDIOD_CCCR_IOABORT) {
+ sdio_claim_host(gInstance->func[func]);
+ /*
+ * this sdio_f0_writeb() can be replaced with another api
+ * depending upon MMC driver change.
+ * As of this time, this is temporaray one
+ */
+ sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret);
+ sdio_release_host(gInstance->func[func]);
+ }
+#endif /* MMC_SDIO_ABORT */
+ else if (regaddr < 0xF0) {
+ sd_err(("bcmsdh_sdmmc: F0 Wr:0x%02x: write disallowed\n", regaddr));
+ } else {
+ /* Claim host controller, perform F0 write, and release */
+ sdio_claim_host(gInstance->func[func]);
+ sdio_f0_writeb(gInstance->func[func], *byte, regaddr, &err_ret);
+ sdio_release_host(gInstance->func[func]);
+ }
+ } else {
+ /* Claim host controller, perform Fn write, and release */
+ sdio_claim_host(gInstance->func[func]);
+ sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret);
+ sdio_release_host(gInstance->func[func]);
+ }
+ } else { /* CMD52 Read */
+ /* Claim host controller, perform Fn read, and release */
+ sdio_claim_host(gInstance->func[func]);
+
+ if (func == 0) {
+ *byte = sdio_f0_readb(gInstance->func[func], regaddr, &err_ret);
+ } else {
+ *byte = sdio_readb(gInstance->func[func], regaddr, &err_ret);
+ }
+
+ sdio_release_host(gInstance->func[func]);
+ }
+
+ if (err_ret) {
+ sd_err(("bcmsdh_sdmmc: Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n",
+ rw ? "Write" : "Read", func, regaddr, *byte, err_ret));
+ }
+
+ return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+extern SDIOH_API_RC
+sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr,
+ uint32 *word, uint nbytes)
+{
+ int err_ret = SDIOH_API_RC_FAIL;
+
+ if (func == 0) {
+ sd_err(("%s: Only CMD52 allowed to F0.\n", __FUNCTION__));
+ return SDIOH_API_RC_FAIL;
+ }
+
+ sd_info(("%s: cmd_type=%d, rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
+ __FUNCTION__, cmd_type, rw, func, addr, nbytes));
+
+ DHD_PM_RESUME_WAIT(sdioh_request_word_wait);
+ DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
+ /* Claim host controller */
+ sdio_claim_host(gInstance->func[func]);
+
+ if(rw) { /* CMD52 Write */
+ if (nbytes == 4) {
+ sdio_writel(gInstance->func[func], *word, addr, &err_ret);
+ } else if (nbytes == 2) {
+ sdio_writew(gInstance->func[func], (*word & 0xFFFF), addr, &err_ret);
+ } else {
+ sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes));
+ }
+ } else { /* CMD52 Read */
+ if (nbytes == 4) {
+ *word = sdio_readl(gInstance->func[func], addr, &err_ret);
+ } else if (nbytes == 2) {
+ *word = sdio_readw(gInstance->func[func], addr, &err_ret) & 0xFFFF;
+ } else {
+ sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes));
+ }
+ }
+
+ /* Release host controller */
+ sdio_release_host(gInstance->func[func]);
+
+ if (err_ret) {
+ sd_err(("bcmsdh_sdmmc: Failed to %s word, Err: 0x%08x",
+ rw ? "Write" : "Read", err_ret));
+ }
+
+ return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+static SDIOH_API_RC
+sdioh_request_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func,
+ uint addr, void *pkt)
+{
+ bool fifo = (fix_inc == SDIOH_DATA_FIX);
+ uint32 SGCount = 0;
+ int err_ret = 0;
+
+ void *pnext;
+
+ sd_trace(("%s: Enter\n", __FUNCTION__));
+
+ ASSERT(pkt);
+ DHD_PM_RESUME_WAIT(sdioh_request_packet_wait);
+ DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
+
+ /* Claim host controller */
+ sdio_claim_host(gInstance->func[func]);
+ for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) {
+ uint pkt_len = PKTLEN(sd->osh, pnext);
+ pkt_len += 3;
+ pkt_len &= 0xFFFFFFFC;
+
+#ifdef CONFIG_MMC_MSM7X00A
+ if ((pkt_len % 64) == 32) {
+ sd_trace(("%s: Rounding up TX packet +=32\n", __FUNCTION__));
+ pkt_len += 32;
+ }
+#endif /* CONFIG_MMC_MSM7X00A */
+ /* Make sure the packet is aligned properly. If it isn't, then this
+ * is the fault of sdioh_request_buffer() which is supposed to give
+ * us something we can work with.
+ */
+ ASSERT(((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) == 0);
+
+ if ((write) && (!fifo)) {
+ err_ret = sdio_memcpy_toio(gInstance->func[func], addr,
+ ((uint8*)PKTDATA(sd->osh, pnext)),
+ pkt_len);
+ } else if (write) {
+ err_ret = sdio_memcpy_toio(gInstance->func[func], addr,
+ ((uint8*)PKTDATA(sd->osh, pnext)),
+ pkt_len);
+ } else if (fifo) {
+ err_ret = sdio_readsb(gInstance->func[func],
+ ((uint8*)PKTDATA(sd->osh, pnext)),
+ addr,
+ pkt_len);
+ } else {
+ err_ret = sdio_memcpy_fromio(gInstance->func[func],
+ ((uint8*)PKTDATA(sd->osh, pnext)),
+ addr,
+ pkt_len);
+ }
+
+ if (err_ret) {
+ sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
+ __FUNCTION__,
+ (write) ? "TX" : "RX",
+ pnext, SGCount, addr, pkt_len, err_ret));
+ } else {
+ sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n",
+ __FUNCTION__,
+ (write) ? "TX" : "RX",
+ pnext, SGCount, addr, pkt_len));
+ }
+
+ if (!fifo) {
+ addr += pkt_len;
+ }
+ SGCount ++;
+
+ }
+
+ /* Release host controller */
+ sdio_release_host(gInstance->func[func]);
+
+ sd_trace(("%s: Exit\n", __FUNCTION__));
+ return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+
+/*
+ * This function takes a buffer or packet, and fixes everything up so that in the
+ * end, a DMA-able packet is created.
+ *
+ * A buffer does not have an associated packet pointer, and may or may not be aligned.
+ * A packet may consist of a single packet, or a packet chain. If it is a packet chain,
+ * then all the packets in the chain must be properly aligned. If the packet data is not
+ * aligned, then there may only be one packet, and in this case, it is copied to a new
+ * aligned packet.
+ *
+ */
+extern SDIOH_API_RC
+sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, uint func,
+ uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt)
+{
+ SDIOH_API_RC Status;
+ void *mypkt = NULL;
+
+ sd_trace(("%s: Enter\n", __FUNCTION__));
+
+ DHD_PM_RESUME_WAIT(sdioh_request_buffer_wait);
+ DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
+ /* Case 1: we don't have a packet. */
+ if (pkt == NULL) {
+ sd_data(("%s: Creating new %s Packet, len=%d\n",
+ __FUNCTION__, write ? "TX" : "RX", buflen_u));
+#ifdef DHD_USE_STATIC_BUF
+ if (!(mypkt = PKTGET_STATIC(sd->osh, buflen_u, write ? TRUE : FALSE))) {
+#else
+ if (!(mypkt = PKTGET(sd->osh, buflen_u, write ? TRUE : FALSE))) {
+#endif /* DHD_USE_STATIC_BUF */
+ sd_err(("%s: PKTGET failed: len %d\n",
+ __FUNCTION__, buflen_u));
+ return SDIOH_API_RC_FAIL;
+ }
+
+ /* For a write, copy the buffer data into the packet. */
+ if (write) {
+ bcopy(buffer, PKTDATA(sd->osh, mypkt), buflen_u);
+ }
+
+ Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt);
+
+ /* For a read, copy the packet data back to the buffer. */
+ if (!write) {
+ bcopy(PKTDATA(sd->osh, mypkt), buffer, buflen_u);
+ }
+#ifdef DHD_USE_STATIC_BUF
+ PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE);
+#else
+ PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE);
+#endif /* DHD_USE_STATIC_BUF */
+ } else if (((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) != 0) {
+ /* Case 2: We have a packet, but it is unaligned. */
+
+ /* In this case, we cannot have a chain. */
+ ASSERT(PKTNEXT(sd->osh, pkt) == NULL);
+
+ sd_data(("%s: Creating aligned %s Packet, len=%d\n",
+ __FUNCTION__, write ? "TX" : "RX", PKTLEN(sd->osh, pkt)));
+#ifdef DHD_USE_STATIC_BUF
+ if (!(mypkt = PKTGET_STATIC(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) {
+#else
+ if (!(mypkt = PKTGET(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) {
+#endif /* DHD_USE_STATIC_BUF */
+ sd_err(("%s: PKTGET failed: len %d\n",
+ __FUNCTION__, PKTLEN(sd->osh, pkt)));
+ return SDIOH_API_RC_FAIL;
+ }
+
+ /* For a write, copy the buffer data into the packet. */
+ if (write) {
+ bcopy(PKTDATA(sd->osh, pkt),
+ PKTDATA(sd->osh, mypkt),
+ PKTLEN(sd->osh, pkt));
+ }
+
+ Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt);
+
+ /* For a read, copy the packet data back to the buffer. */
+ if (!write) {
+ bcopy(PKTDATA(sd->osh, mypkt),
+ PKTDATA(sd->osh, pkt),
+ PKTLEN(sd->osh, mypkt));
+ }
+#ifdef DHD_USE_STATIC_BUF
+ PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE);
+#else
+ PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE);
+#endif /* DHD_USE_STATIC_BUF */
+ } else { /* case 3: We have a packet and it is aligned. */
+ sd_data(("%s: Aligned %s Packet, direct DMA\n",
+ __FUNCTION__, write ? "Tx" : "Rx"));
+ Status = sdioh_request_packet(sd, fix_inc, write, func, addr, pkt);
+ }
+
+ return (Status);
+}
+
+/* this function performs "abort" for both of host & device */
+extern int
+sdioh_abort(sdioh_info_t *sd, uint func)
+{
+#if defined(MMC_SDIO_ABORT)
+ char t_func = (char) func;
+#endif /* defined(MMC_SDIO_ABORT) */
+ sd_trace(("%s: Enter\n", __FUNCTION__));
+
+#if defined(MMC_SDIO_ABORT)
+ /* issue abort cmd52 command through F1 */
+ sdioh_request_byte(sd, SD_IO_OP_WRITE, SDIO_FUNC_0, SDIOD_CCCR_IOABORT, &t_func);
+#endif /* defined(MMC_SDIO_ABORT) */
+
+ sd_trace(("%s: Exit\n", __FUNCTION__));
+ return SDIOH_API_RC_SUCCESS;
+}
+
+/* Reset and re-initialize the device */
+int sdioh_sdio_reset(sdioh_info_t *si)
+{
+ sd_trace(("%s: Enter\n", __FUNCTION__));
+ sd_trace(("%s: Exit\n", __FUNCTION__));
+ return SDIOH_API_RC_SUCCESS;
+}
+
+/* Disable device interrupt */
+void
+sdioh_sdmmc_devintr_off(sdioh_info_t *sd)
+{
+ sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+ sd->intmask &= ~CLIENT_INTR;
+}
+
+/* Enable device interrupt */
+void
+sdioh_sdmmc_devintr_on(sdioh_info_t *sd)
+{
+ sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+ sd->intmask |= CLIENT_INTR;
+}
+
+/* Read client card reg */
+int
+sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+
+ if ((func == 0) || (regsize == 1)) {
+ uint8 temp = 0;
+
+ sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp);
+ *data = temp;
+ *data &= 0xff;
+ sd_data(("%s: byte read data=0x%02x\n",
+ __FUNCTION__, *data));
+ } else {
+ sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, data, regsize);
+ if (regsize == 2)
+ *data &= 0xffff;
+
+ sd_data(("%s: word read data=0x%08x\n",
+ __FUNCTION__, *data));
+ }
+
+ return SUCCESS;
+}
+
+#if !defined(OOB_INTR_ONLY)
+/* bcmsdh_sdmmc interrupt handler */
+static void IRQHandler(struct sdio_func *func)
+{
+ sdioh_info_t *sd;
+
+ sd_trace(("bcmsdh_sdmmc: ***IRQHandler\n"));
+ sd = gInstance->sd;
+
+ ASSERT(sd != NULL);
+ sdio_release_host(gInstance->func[0]);
+
+ if (sd->use_client_ints) {
+ sd->intrcount++;
+ ASSERT(sd->intr_handler);
+ ASSERT(sd->intr_handler_arg);
+ (sd->intr_handler)(sd->intr_handler_arg);
+ } else {
+ sd_err(("bcmsdh_sdmmc: ***IRQHandler\n"));
+
+ sd_err(("%s: Not ready for intr: enabled %d, handler %p\n",
+ __FUNCTION__, sd->client_intr_enabled, sd->intr_handler));
+ }
+
+ sdio_claim_host(gInstance->func[0]);
+}
+
+/* bcmsdh_sdmmc interrupt handler for F2 (dummy handler) */
+static void IRQHandlerF2(struct sdio_func *func)
+{
+ sdioh_info_t *sd;
+
+ sd_trace(("bcmsdh_sdmmc: ***IRQHandlerF2\n"));
+
+ sd = gInstance->sd;
+
+ ASSERT(sd != NULL);
+}
+#endif /* !defined(OOB_INTR_ONLY) */
+
+#ifdef NOTUSED
+/* Write client card reg */
+static int
+sdioh_sdmmc_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data)
+{
+
+ if ((func == 0) || (regsize == 1)) {
+ uint8 temp;
+
+ temp = data & 0xff;
+ sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp);
+ sd_data(("%s: byte write data=0x%02x\n",
+ __FUNCTION__, data));
+ } else {
+ if (regsize == 2)
+ data &= 0xffff;
+
+ sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, &data, regsize);
+
+ sd_data(("%s: word write data=0x%08x\n",
+ __FUNCTION__, data));
+ }
+
+ return SUCCESS;
+}
+#endif /* NOTUSED */
+
+int
+sdioh_start(sdioh_info_t *si, int stage)
+{
+ int ret;
+ sdioh_info_t *sd = gInstance->sd;
+
+ /* Need to do this stages as we can't enable the interrupt till
+ downloading of the firmware is complete, other wise polling
+ sdio access will come in way
+ */
+ if (gInstance->func[0]) {
+ if (stage == 0) {
+ /* Since the power to the chip is killed, we will have
+ re enumerate the device again. Set the block size
+ and enable the fucntion 1 for in preparation for
+ downloading the code
+ */
+ /* sdio_reset_comm() - has been fixed in latest kernel/msm.git for Linux
+ 2.6.27. The implementation prior to that is buggy, and needs broadcom's
+ patch for it
+ */
+ if ((ret = sdio_reset_comm(gInstance->func[0]->card)))
+ sd_err(("%s Failed, error = %d\n", __FUNCTION__, ret));
+ else {
+ sd->num_funcs = 2;
+ sd->sd_blockmode = TRUE;
+ sd->use_client_ints = TRUE;
+ sd->client_block_size[0] = 64;
+
+ /* Claim host controller */
+ sdio_claim_host(gInstance->func[1]);
+
+ sd->client_block_size[1] = 64;
+ if (sdio_set_block_size(gInstance->func[1], 64)) {
+ sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n"));
+ }
+
+ /* Release host controller F1 */
+ sdio_release_host(gInstance->func[1]);
+
+ if (gInstance->func[2]) {
+ /* Claim host controller F2 */
+ sdio_claim_host(gInstance->func[2]);
+
+ sd->client_block_size[2] = sd_f2_blocksize;
+ if (sdio_set_block_size(gInstance->func[2],
+ sd_f2_blocksize)) {
+ sd_err(("bcmsdh_sdmmc: Failed to set F2 "
+ "blocksize to %d\n", sd_f2_blocksize));
+ }
+
+ /* Release host controller F2 */
+ sdio_release_host(gInstance->func[2]);
+ }
+
+ sdioh_sdmmc_card_enablefuncs(sd);
+ }
+ } else {
+#if !defined(OOB_INTR_ONLY)
+ sdio_claim_host(gInstance->func[0]);
+ sdio_claim_irq(gInstance->func[2], IRQHandlerF2);
+ sdio_claim_irq(gInstance->func[1], IRQHandler);
+ sdio_release_host(gInstance->func[0]);
+#else /* defined(OOB_INTR_ONLY) */
+#if defined(HW_OOB)
+ sdioh_enable_func_intr();
+#endif
+ bcmsdh_oob_intr_set(TRUE);
+#endif /* !defined(OOB_INTR_ONLY) */
+ }
+ }
+ else
+ sd_err(("%s Failed\n", __FUNCTION__));
+
+ return (0);
+}
+
+int
+sdioh_stop(sdioh_info_t *si)
+{
+ /* MSM7201A Android sdio stack has bug with interrupt
+ So internaly within SDIO stack they are polling
+ which cause issue when device is turned off. So
+ unregister interrupt with SDIO stack to stop the
+ polling
+ */
+ if (gInstance->func[0]) {
+#if !defined(OOB_INTR_ONLY)
+ sdio_claim_host(gInstance->func[0]);
+ sdio_release_irq(gInstance->func[1]);
+ sdio_release_irq(gInstance->func[2]);
+ sdio_release_host(gInstance->func[0]);
+#else /* defined(OOB_INTR_ONLY) */
+#if defined(HW_OOB)
+ sdioh_disable_func_intr();
+#endif
+ bcmsdh_oob_intr_set(FALSE);
+#endif /* !defined(OOB_INTR_ONLY) */
+ }
+ else
+ sd_err(("%s Failed\n", __FUNCTION__));
+ return (0);
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c
new file mode 100644
index 00000000000..8992a4267f9
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c
@@ -0,0 +1,273 @@
+/*
+ * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh_sdmmc_linux.c,v 1.1.2.5.6.17 2010/08/13 00:36:19 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmutils.h>
+#include <sdio.h> /* SDIO Specs */
+#include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h> /* to get msglevel bit values */
+
+#include <linux/sched.h> /* request_irq() */
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+
+#if !defined(SDIO_VENDOR_ID_BROADCOM)
+#define SDIO_VENDOR_ID_BROADCOM 0x02d0
+#endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */
+
+#define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000
+
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)
+#define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB 0x0492 /* BCM94325SDGWB */
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4325)
+#define SDIO_DEVICE_ID_BROADCOM_4325 0x0493
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4329)
+#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4319)
+#define SDIO_DEVICE_ID_BROADCOM_4319 0x4319
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
+
+#include <bcmsdh_sdmmc.h>
+
+#include <dhd_dbg.h>
+
+extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd);
+extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd);
+
+int sdio_function_init(void);
+void sdio_function_cleanup(void);
+
+#define DESCRIPTION "bcmsdh_sdmmc Driver"
+#define AUTHOR "Broadcom Corporation"
+
+/* module param defaults */
+static int clockoverride = 0;
+
+module_param(clockoverride, int, 0644);
+MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
+
+PBCMSDH_SDMMC_INSTANCE gInstance;
+
+/* Maximum number of bcmsdh_sdmmc devices supported by driver */
+#define BCMSDH_SDMMC_MAX_DEVICES 1
+
+extern int bcmsdh_probe(struct device *dev);
+extern int bcmsdh_remove(struct device *dev);
+struct device sdmmc_dev;
+
+static int bcmsdh_sdmmc_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ int ret = 0;
+ static struct sdio_func sdio_func_0;
+ sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
+ sd_trace(("sdio_bcmsdh: func->class=%x\n", func->class));
+ sd_trace(("sdio_vendor: 0x%04x\n", func->vendor));
+ sd_trace(("sdio_device: 0x%04x\n", func->device));
+ sd_trace(("Function#: 0x%04x\n", func->num));
+
+ if (func->num == 1) {
+ sdio_func_0.num = 0;
+ sdio_func_0.card = func->card;
+ gInstance->func[0] = &sdio_func_0;
+ if(func->device == 0x4) { /* 4318 */
+ gInstance->func[2] = NULL;
+ sd_trace(("NIC found, calling bcmsdh_probe...\n"));
+ ret = bcmsdh_probe(&sdmmc_dev);
+ }
+ }
+
+ gInstance->func[func->num] = func;
+
+ if (func->num == 2) {
+ sd_trace(("F2 found, calling bcmsdh_probe...\n"));
+ ret = bcmsdh_probe(&sdmmc_dev);
+ }
+
+ return ret;
+}
+
+static void bcmsdh_sdmmc_remove(struct sdio_func *func)
+{
+ sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
+ sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
+ sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
+ sd_info(("sdio_device: 0x%04x\n", func->device));
+ sd_info(("Function#: 0x%04x\n", func->num));
+
+ if (func->num == 2) {
+ sd_trace(("F2 found, calling bcmsdh_remove...\n"));
+ bcmsdh_remove(&sdmmc_dev);
+ }
+}
+
+/* devices we support, null terminated */
+static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
+ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) },
+ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) },
+ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) },
+ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) },
+ { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) },
+ { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
+
+static struct sdio_driver bcmsdh_sdmmc_driver = {
+ .probe = bcmsdh_sdmmc_probe,
+ .remove = bcmsdh_sdmmc_remove,
+ .name = "bcmsdh_sdmmc",
+ .id_table = bcmsdh_sdmmc_ids,
+ };
+
+struct sdos_info {
+ sdioh_info_t *sd;
+ spinlock_t lock;
+};
+
+
+int
+sdioh_sdmmc_osinit(sdioh_info_t *sd)
+{
+ struct sdos_info *sdos;
+
+ sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info));
+ sd->sdos_info = (void*)sdos;
+ if (sdos == NULL)
+ return BCME_NOMEM;
+
+ sdos->sd = sd;
+ spin_lock_init(&sdos->lock);
+ return BCME_OK;
+}
+
+void
+sdioh_sdmmc_osfree(sdioh_info_t *sd)
+{
+ struct sdos_info *sdos;
+ ASSERT(sd && sd->sdos_info);
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+ MFREE(sd->osh, sdos, sizeof(struct sdos_info));
+}
+
+/* Interrupt enable/disable */
+SDIOH_API_RC
+sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
+{
+ ulong flags;
+ struct sdos_info *sdos;
+
+ sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+ ASSERT(sdos);
+
+#if !defined(OOB_INTR_ONLY)
+ if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
+ sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
+ return SDIOH_API_RC_FAIL;
+ }
+#endif /* !defined(OOB_INTR_ONLY) */
+
+ /* Ensure atomicity for enable/disable calls */
+ spin_lock_irqsave(&sdos->lock, flags);
+
+ sd->client_intr_enabled = enable;
+ if (enable) {
+ sdioh_sdmmc_devintr_on(sd);
+ } else {
+ sdioh_sdmmc_devintr_off(sd);
+ }
+
+ spin_unlock_irqrestore(&sdos->lock, flags);
+
+ return SDIOH_API_RC_SUCCESS;
+}
+
+
+#ifdef BCMSDH_MODULE
+static int __init
+bcmsdh_module_init(void)
+{
+ int error = 0;
+ sdio_function_init();
+ return error;
+}
+
+static void __exit
+bcmsdh_module_cleanup(void)
+{
+ sdio_function_cleanup();
+}
+
+module_init(bcmsdh_module_init);
+module_exit(bcmsdh_module_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(DESCRIPTION);
+MODULE_AUTHOR(AUTHOR);
+
+#endif /* BCMSDH_MODULE */
+/*
+ * module init
+*/
+int sdio_function_init(void)
+{
+ int error = 0;
+ sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
+
+ gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL);
+ if (!gInstance)
+ return -ENOMEM;
+
+ bzero(&sdmmc_dev, sizeof(sdmmc_dev));
+ error = sdio_register_driver(&bcmsdh_sdmmc_driver);
+
+
+ return error;
+}
+
+/*
+ * module cleanup
+*/
+extern int bcmsdh_remove(struct device *dev);
+void sdio_function_cleanup(void)
+{
+ sd_trace(("%s Enter\n", __FUNCTION__));
+
+
+ sdio_unregister_driver(&bcmsdh_sdmmc_driver);
+
+ if (gInstance)
+ kfree(gInstance);
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdspi.c b/drivers/net/wireless/bcm4329/bcmsdspi.c
new file mode 100644
index 00000000000..636539be5ea
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdspi.c
@@ -0,0 +1,1596 @@
+/*
+ * Broadcom BCMSDH to SPI Protocol Conversion Layer
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdspi.c,v 1.14.4.2.4.4.6.5 2010/03/10 03:09:48 Exp $
+ */
+
+#include <typedefs.h>
+
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <bcmutils.h>
+#include <osl.h>
+#include <siutils.h>
+#include <sdio.h> /* SDIO Device and Protocol Specs */
+#include <sdioh.h> /* SDIO Host Controller Specification */
+#include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h> /* ioctl/iovars */
+
+#include <pcicfg.h>
+
+
+#include <bcmsdspi.h>
+#include <bcmspi.h>
+
+#include <proto/sdspi.h>
+
+#define SD_PAGE 4096
+
+/* Globals */
+
+uint sd_msglevel = SDH_ERROR_VAL;
+uint sd_hiok = FALSE; /* Use hi-speed mode if available? */
+uint sd_sdmode = SDIOH_MODE_SPI; /* Use SD4 mode by default */
+uint sd_f2_blocksize = 512; /* Default blocksize */
+
+uint sd_divisor = 2; /* Default 33MHz/2 = 16MHz for dongle */
+uint sd_power = 1; /* Default to SD Slot powered ON */
+uint sd_clock = 1; /* Default to SD Clock turned ON */
+uint sd_crc = 0; /* Default to SPI CRC Check turned OFF */
+uint sd_pci_slot = 0xFFFFffff; /* Used to force selection of a particular PCI slot */
+
+uint sd_toctl = 7;
+
+/* Prototypes */
+static bool sdspi_start_power(sdioh_info_t *sd);
+static int sdspi_set_highspeed_mode(sdioh_info_t *sd, bool HSMode);
+static int sdspi_card_enablefuncs(sdioh_info_t *sd);
+static void sdspi_cmd_getrsp(sdioh_info_t *sd, uint32 *rsp_buffer, int count);
+static int sdspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd, uint32 arg,
+ uint32 *data, uint32 datalen);
+static int sdspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr,
+ int regsize, uint32 *data);
+static int sdspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr,
+ int regsize, uint32 data);
+static int sdspi_driver_init(sdioh_info_t *sd);
+static bool sdspi_reset(sdioh_info_t *sd, bool host_reset, bool client_reset);
+static int sdspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo,
+ uint32 addr, int nbytes, uint32 *data);
+static int sdspi_abort(sdioh_info_t *sd, uint func);
+
+static int set_client_block_size(sdioh_info_t *sd, int func, int blocksize);
+
+static uint8 sdspi_crc7(unsigned char* p, uint32 len);
+static uint16 sdspi_crc16(unsigned char* p, uint32 len);
+static int sdspi_crc_onoff(sdioh_info_t *sd, bool use_crc);
+
+/*
+ * Public entry points & extern's
+ */
+extern sdioh_info_t *
+sdioh_attach(osl_t *osh, void *bar0, uint irq)
+{
+ sdioh_info_t *sd;
+
+ sd_trace(("%s\n", __FUNCTION__));
+ if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) {
+ sd_err(("sdioh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh)));
+ return NULL;
+ }
+ bzero((char *)sd, sizeof(sdioh_info_t));
+ sd->osh = osh;
+
+ if (spi_osinit(sd) != 0) {
+ sd_err(("%s: spi_osinit() failed\n", __FUNCTION__));
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return NULL;
+ }
+
+ sd->bar0 = (uintptr)bar0;
+ sd->irq = irq;
+ sd->intr_handler = NULL;
+ sd->intr_handler_arg = NULL;
+ sd->intr_handler_valid = FALSE;
+
+ /* Set defaults */
+ sd->sd_blockmode = FALSE;
+ sd->use_client_ints = TRUE;
+ sd->sd_use_dma = FALSE; /* DMA Not supported */
+
+ /* Haven't figured out how to make bytemode work with dma */
+ if (!sd->sd_blockmode)
+ sd->sd_use_dma = 0;
+
+ if (!spi_hw_attach(sd)) {
+ sd_err(("%s: spi_hw_attach() failed\n", __FUNCTION__));
+ spi_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return NULL;
+ }
+
+ if (sdspi_driver_init(sd) != SUCCESS) {
+ if (sdspi_driver_init(sd) != SUCCESS) {
+ sd_err(("%s:sdspi_driver_init() failed()\n", __FUNCTION__));
+ spi_hw_detach(sd);
+ spi_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return (NULL);
+ }
+ }
+
+ if (spi_register_irq(sd, irq) != SUCCESS) {
+ sd_err(("%s: spi_register_irq() failed for irq = %d\n", __FUNCTION__, irq));
+ spi_hw_detach(sd);
+ spi_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return (NULL);
+ }
+
+ sd_trace(("%s: Done\n", __FUNCTION__));
+ return sd;
+}
+
+extern SDIOH_API_RC
+sdioh_detach(osl_t *osh, sdioh_info_t *sd)
+{
+ sd_trace(("%s\n", __FUNCTION__));
+
+ if (sd) {
+ if (sd->card_init_done)
+ sdspi_reset(sd, 1, 1);
+
+ sd_info(("%s: detaching from hardware\n", __FUNCTION__));
+ spi_free_irq(sd->irq, sd);
+ spi_hw_detach(sd);
+ spi_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ }
+
+ return SDIOH_API_RC_SUCCESS;
+}
+
+/* Configure callback to client when we recieve client interrupt */
+extern SDIOH_API_RC
+sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+
+ sd->intr_handler = fn;
+ sd->intr_handler_arg = argh;
+ sd->intr_handler_valid = TRUE;
+
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_deregister(sdioh_info_t *sd)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+
+ sd->intr_handler_valid = FALSE;
+ sd->intr_handler = NULL;
+ sd->intr_handler_arg = NULL;
+
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+
+ *onoff = sd->client_intr_enabled;
+
+ return SDIOH_API_RC_SUCCESS;
+}
+
+#if defined(DHD_DEBUG)
+extern bool
+sdioh_interrupt_pending(sdioh_info_t *sd)
+{
+ return 0;
+}
+#endif
+
+uint
+sdioh_query_iofnum(sdioh_info_t *sd)
+{
+ return sd->num_funcs;
+}
+
+/* IOVar table */
+enum {
+ IOV_MSGLEVEL = 1,
+ IOV_BLOCKMODE,
+ IOV_BLOCKSIZE,
+ IOV_DMA,
+ IOV_USEINTS,
+ IOV_NUMINTS,
+ IOV_NUMLOCALINTS,
+ IOV_HOSTREG,
+ IOV_DEVREG,
+ IOV_DIVISOR,
+ IOV_SDMODE,
+ IOV_HISPEED,
+ IOV_HCIREGS,
+ IOV_POWER,
+ IOV_CLOCK,
+ IOV_CRC
+};
+
+const bcm_iovar_t sdioh_iovars[] = {
+ {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 },
+ {"sd_blockmode", IOV_BLOCKMODE, 0, IOVT_BOOL, 0 },
+ {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */
+ {"sd_dma", IOV_DMA, 0, IOVT_BOOL, 0 },
+ {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 },
+ {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 },
+ {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 },
+ {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
+ {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
+ {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 },
+ {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 },
+ {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 },
+ {"sd_crc", IOV_CRC, 0, IOVT_UINT32, 0 },
+ {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100},
+ {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0},
+ {NULL, 0, 0, 0, 0 }
+};
+
+int
+sdioh_iovar_op(sdioh_info_t *si, const char *name,
+ void *params, int plen, void *arg, int len, bool set)
+{
+ const bcm_iovar_t *vi = NULL;
+ int bcmerror = 0;
+ int val_size;
+ int32 int_val = 0;
+ bool bool_val;
+ uint32 actionid;
+
+ ASSERT(name);
+ ASSERT(len >= 0);
+
+ /* Get must have return space; Set does not take qualifiers */
+ ASSERT(set || (arg && len));
+ ASSERT(!set || (!params && !plen));
+
+ sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name));
+
+ if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) {
+ bcmerror = BCME_UNSUPPORTED;
+ goto exit;
+ }
+
+ if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0)
+ goto exit;
+
+ /* Set up params so get and set can share the convenience variables */
+ if (params == NULL) {
+ params = arg;
+ plen = len;
+ }
+
+ if (vi->type == IOVT_VOID)
+ val_size = 0;
+ else if (vi->type == IOVT_BUFFER)
+ val_size = len;
+ else
+ val_size = sizeof(int);
+
+ if (plen >= (int)sizeof(int_val))
+ bcopy(params, &int_val, sizeof(int_val));
+
+ bool_val = (int_val != 0) ? TRUE : FALSE;
+
+ actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+ switch (actionid) {
+ case IOV_GVAL(IOV_MSGLEVEL):
+ int_val = (int32)sd_msglevel;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_MSGLEVEL):
+ sd_msglevel = int_val;
+ break;
+
+ case IOV_GVAL(IOV_BLOCKMODE):
+ int_val = (int32)si->sd_blockmode;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_BLOCKMODE):
+ si->sd_blockmode = (bool)int_val;
+ /* Haven't figured out how to make non-block mode with DMA */
+ if (!si->sd_blockmode)
+ si->sd_use_dma = 0;
+ break;
+
+ case IOV_GVAL(IOV_BLOCKSIZE):
+ if ((uint32)int_val > si->num_funcs) {
+ bcmerror = BCME_BADARG;
+ break;
+ }
+ int_val = (int32)si->client_block_size[int_val];
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_BLOCKSIZE):
+ {
+ uint func = ((uint32)int_val >> 16);
+ uint blksize = (uint16)int_val;
+ uint maxsize;
+
+ if (func > si->num_funcs) {
+ bcmerror = BCME_BADARG;
+ break;
+ }
+
+ switch (func) {
+ case 0: maxsize = 32; break;
+ case 1: maxsize = BLOCK_SIZE_4318; break;
+ case 2: maxsize = BLOCK_SIZE_4328; break;
+ default: maxsize = 0;
+ }
+ if (blksize > maxsize) {
+ bcmerror = BCME_BADARG;
+ break;
+ }
+ if (!blksize) {
+ blksize = maxsize;
+ }
+
+ /* Now set it */
+ spi_lock(si);
+ bcmerror = set_client_block_size(si, func, blksize);
+ spi_unlock(si);
+ break;
+ }
+
+ case IOV_GVAL(IOV_DMA):
+ int_val = (int32)si->sd_use_dma;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_DMA):
+ si->sd_use_dma = (bool)int_val;
+ break;
+
+ case IOV_GVAL(IOV_USEINTS):
+ int_val = (int32)si->use_client_ints;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_USEINTS):
+ break;
+
+ case IOV_GVAL(IOV_DIVISOR):
+ int_val = (uint32)sd_divisor;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_DIVISOR):
+ sd_divisor = int_val;
+ if (!spi_start_clock(si, (uint16)sd_divisor)) {
+ sd_err(("set clock failed!\n"));
+ bcmerror = BCME_ERROR;
+ }
+ break;
+
+ case IOV_GVAL(IOV_POWER):
+ int_val = (uint32)sd_power;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_POWER):
+ sd_power = int_val;
+ break;
+
+ case IOV_GVAL(IOV_CLOCK):
+ int_val = (uint32)sd_clock;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_CLOCK):
+ sd_clock = int_val;
+ break;
+
+ case IOV_GVAL(IOV_CRC):
+ int_val = (uint32)sd_crc;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_CRC):
+ /* Apply new setting, but don't change sd_crc until
+ * after the CRC-mode is selected in the device. This
+ * is required because the software must generate a
+ * correct CRC for the CMD59 in order to be able to
+ * turn OFF the CRC.
+ */
+ sdspi_crc_onoff(si, int_val ? 1 : 0);
+ sd_crc = int_val;
+ break;
+
+ case IOV_GVAL(IOV_SDMODE):
+ int_val = (uint32)sd_sdmode;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_SDMODE):
+ sd_sdmode = int_val;
+ break;
+
+ case IOV_GVAL(IOV_HISPEED):
+ int_val = (uint32)sd_hiok;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_HISPEED):
+ sd_hiok = int_val;
+
+ if (!sdspi_set_highspeed_mode(si, (bool)sd_hiok)) {
+ sd_err(("Failed changing highspeed mode to %d.\n", sd_hiok));
+ bcmerror = BCME_ERROR;
+ return ERROR;
+ }
+ break;
+
+ case IOV_GVAL(IOV_NUMINTS):
+ int_val = (int32)si->intrcount;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_NUMLOCALINTS):
+ int_val = (int32)si->local_intrcount;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_HOSTREG):
+ {
+ break;
+ }
+
+ case IOV_SVAL(IOV_HOSTREG):
+ {
+ sd_err(("IOV_HOSTREG unsupported\n"));
+ break;
+ }
+
+ case IOV_GVAL(IOV_DEVREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+ uint8 data;
+
+ if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) {
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+
+ int_val = (int)data;
+ bcopy(&int_val, arg, sizeof(int_val));
+ break;
+ }
+
+ case IOV_SVAL(IOV_DEVREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+ uint8 data = (uint8)sd_ptr->value;
+
+ if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) {
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+ break;
+ }
+
+
+ default:
+ bcmerror = BCME_UNSUPPORTED;
+ break;
+ }
+exit:
+
+ return bcmerror;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+ SDIOH_API_RC status;
+ /* No lock needed since sdioh_request_byte does locking */
+ status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
+ return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+ /* No lock needed since sdioh_request_byte does locking */
+ SDIOH_API_RC status;
+ status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
+ return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length)
+{
+ uint32 count;
+ int offset;
+ uint32 foo;
+ uint8 *cis = cisd;
+
+ sd_trace(("%s: Func = %d\n", __FUNCTION__, func));
+
+ if (!sd->func_cis_ptr[func]) {
+ bzero(cis, length);
+ return SDIOH_API_RC_FAIL;
+ }
+
+ spi_lock(sd);
+ *cis = 0;
+ for (count = 0; count < length; count++) {
+ offset = sd->func_cis_ptr[func] + count;
+ if (sdspi_card_regread (sd, 0, offset, 1, &foo) < 0) {
+ sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
+ spi_unlock(sd);
+ return SDIOH_API_RC_FAIL;
+ }
+ *cis = (uint8)(foo & 0xff);
+ cis++;
+ }
+ spi_unlock(sd);
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte)
+{
+ int status;
+ uint32 cmd_arg;
+ uint32 rsp5;
+
+ spi_lock(sd);
+
+ cmd_arg = 0;
+ cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, rw == SDIOH_READ ? 0 : 1);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+ cmd_arg = SFIELD(cmd_arg, CMD52_DATA, rw == SDIOH_READ ? 0 : *byte);
+
+ sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x\n", __FUNCTION__, rw, func, regaddr));
+
+ if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma,
+ SDIOH_CMD_52, cmd_arg, NULL, 0)) != SUCCESS) {
+ spi_unlock(sd);
+ return status;
+ }
+
+ sdspi_cmd_getrsp(sd, &rsp5, 1);
+ if (rsp5 != 0x00) {
+ sd_err(("%s: rsp5 flags is 0x%x func=%d\n",
+ __FUNCTION__, rsp5, func));
+ /* ASSERT(0); */
+ spi_unlock(sd);
+ return SDIOH_API_RC_FAIL;
+ }
+
+ if (rw == SDIOH_READ)
+ *byte = sd->card_rsp_data >> 24;
+
+ spi_unlock(sd);
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr,
+ uint32 *word, uint nbytes)
+{
+ int status;
+
+ spi_lock(sd);
+
+ if (rw == SDIOH_READ)
+ status = sdspi_card_regread(sd, func, addr, nbytes, word);
+ else
+ status = sdspi_card_regwrite(sd, func, addr, nbytes, *word);
+
+ spi_unlock(sd);
+ return (status == SUCCESS ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+extern SDIOH_API_RC
+sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint rw, uint func,
+ uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt)
+{
+ int len;
+ int buflen = (int)buflen_u;
+ bool fifo = (fix_inc == SDIOH_DATA_FIX);
+
+ spi_lock(sd);
+
+ ASSERT(reg_width == 4);
+ ASSERT(buflen_u < (1 << 30));
+ ASSERT(sd->client_block_size[func]);
+
+ sd_data(("%s: %c len %d r_cnt %d t_cnt %d, pkt @0x%p\n",
+ __FUNCTION__, rw == SDIOH_READ ? 'R' : 'W',
+ buflen_u, sd->r_cnt, sd->t_cnt, pkt));
+
+ /* Break buffer down into blocksize chunks:
+ * Bytemode: 1 block at a time.
+ */
+ while (buflen > 0) {
+ if (sd->sd_blockmode) {
+ /* Max xfer is Page size */
+ len = MIN(SD_PAGE, buflen);
+
+ /* Round down to a block boundry */
+ if (buflen > sd->client_block_size[func])
+ len = (len/sd->client_block_size[func]) *
+ sd->client_block_size[func];
+ } else {
+ /* Byte mode: One block at a time */
+ len = MIN(sd->client_block_size[func], buflen);
+ }
+
+ if (sdspi_card_buf(sd, rw, func, fifo, addr, len, (uint32 *)buffer) != SUCCESS) {
+ spi_unlock(sd);
+ return SDIOH_API_RC_FAIL;
+ }
+ buffer += len;
+ buflen -= len;
+ if (!fifo)
+ addr += len;
+ }
+ spi_unlock(sd);
+ return SDIOH_API_RC_SUCCESS;
+}
+
+static int
+sdspi_abort(sdioh_info_t *sd, uint func)
+{
+ uint8 spi_databuf[] = { 0x74, 0x80, 0x00, 0x0C, 0xFF, 0x95, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ uint8 spi_rspbuf[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ int err = 0;
+
+ sd_err(("Sending SPI Abort to F%d\n", func));
+ spi_databuf[4] = func & 0x7;
+ /* write to function 0, addr 6 (IOABORT) func # in 3 LSBs. */
+ spi_sendrecv(sd, spi_databuf, spi_rspbuf, sizeof(spi_databuf));
+
+ return err;
+}
+
+extern int
+sdioh_abort(sdioh_info_t *sd, uint fnum)
+{
+ int ret;
+
+ spi_lock(sd);
+ ret = sdspi_abort(sd, fnum);
+ spi_unlock(sd);
+
+ return ret;
+}
+
+int
+sdioh_start(sdioh_info_t *sd, int stage)
+{
+ return SUCCESS;
+}
+
+int
+sdioh_stop(sdioh_info_t *sd)
+{
+ return SUCCESS;
+}
+
+
+/*
+ * Private/Static work routines
+ */
+static bool
+sdspi_reset(sdioh_info_t *sd, bool host_reset, bool client_reset)
+{
+ if (!sd)
+ return TRUE;
+
+ spi_lock(sd);
+ /* Reset client card */
+ if (client_reset && (sd->adapter_slot != -1)) {
+ if (sdspi_card_regwrite(sd, 0, SDIOD_CCCR_IOABORT, 1, 0x8) != SUCCESS)
+ sd_err(("%s: Cannot write to card reg 0x%x\n",
+ __FUNCTION__, SDIOD_CCCR_IOABORT));
+ else
+ sd->card_rca = 0;
+ }
+
+ /* The host reset is a NOP in the sd-spi case. */
+ if (host_reset) {
+ sd->sd_mode = SDIOH_MODE_SPI;
+ }
+ spi_unlock(sd);
+ return TRUE;
+}
+
+static int
+sdspi_host_init(sdioh_info_t *sd)
+{
+ sdspi_reset(sd, 1, 0);
+
+ /* Default power on mode is SD1 */
+ sd->sd_mode = SDIOH_MODE_SPI;
+ sd->polled_mode = TRUE;
+ sd->host_init_done = TRUE;
+ sd->card_init_done = FALSE;
+ sd->adapter_slot = 1;
+
+ return (SUCCESS);
+}
+
+#define CMD0_RETRIES 3
+#define CMD5_RETRIES 10
+
+static int
+get_ocr(sdioh_info_t *sd, uint32 *cmd_arg, uint32 *cmd_rsp)
+{
+ uint32 rsp5;
+ int retries, status;
+
+ /* First issue a CMD0 to get the card into SPI mode. */
+ for (retries = 0; retries <= CMD0_RETRIES; retries++) {
+ if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma,
+ SDIOH_CMD_0, *cmd_arg, NULL, 0)) != SUCCESS) {
+ sd_err(("%s: No response to CMD0\n", __FUNCTION__));
+ continue;
+ }
+
+ sdspi_cmd_getrsp(sd, &rsp5, 1);
+
+ if (GFIELD(rsp5, SPI_RSP_ILL_CMD)) {
+ printf("%s: Card already initialized (continuing)\n", __FUNCTION__);
+ break;
+ }
+
+ if (GFIELD(rsp5, SPI_RSP_IDLE)) {
+ printf("%s: Card in SPI mode\n", __FUNCTION__);
+ break;
+ }
+ }
+
+ if (retries > CMD0_RETRIES) {
+ sd_err(("%s: Too many retries for CMD0\n", __FUNCTION__));
+ return ERROR;
+ }
+
+ /* Get the Card's Operation Condition. */
+ /* Occasionally the board takes a while to become ready. */
+ for (retries = 0; retries <= CMD5_RETRIES; retries++) {
+ if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma,
+ SDIOH_CMD_5, *cmd_arg, NULL, 0)) != SUCCESS) {
+ sd_err(("%s: No response to CMD5\n", __FUNCTION__));
+ continue;
+ }
+
+ printf("CMD5 response data was: 0x%08x\n", sd->card_rsp_data);
+
+ if (GFIELD(sd->card_rsp_data, RSP4_CARD_READY)) {
+ printf("%s: Card ready\n", __FUNCTION__);
+ break;
+ }
+ }
+
+ if (retries > CMD5_RETRIES) {
+ sd_err(("%s: Too many retries for CMD5\n", __FUNCTION__));
+ return ERROR;
+ }
+
+ *cmd_rsp = sd->card_rsp_data;
+
+ sdspi_crc_onoff(sd, sd_crc ? 1 : 0);
+
+ return (SUCCESS);
+}
+
+static int
+sdspi_crc_onoff(sdioh_info_t *sd, bool use_crc)
+{
+ uint32 args;
+ int status;
+
+ args = use_crc ? 1 : 0;
+ if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma,
+ SDIOH_CMD_59, args, NULL, 0)) != SUCCESS) {
+ sd_err(("%s: No response to CMD59\n", __FUNCTION__));
+ }
+
+ sd_info(("CMD59 response data was: 0x%08x\n", sd->card_rsp_data));
+
+ sd_err(("SD-SPI CRC turned %s\n", use_crc ? "ON" : "OFF"));
+ return (SUCCESS);
+}
+
+static int
+sdspi_client_init(sdioh_info_t *sd)
+{
+ uint8 fn_ints;
+
+ sd_trace(("%s: Powering up slot %d\n", __FUNCTION__, sd->adapter_slot));
+
+ /* Start at ~400KHz clock rate for initialization */
+ if (!spi_start_clock(sd, 128)) {
+ sd_err(("spi_start_clock failed\n"));
+ return ERROR;
+ }
+
+ if (!sdspi_start_power(sd)) {
+ sd_err(("sdspi_start_power failed\n"));
+ return ERROR;
+ }
+
+ if (sd->num_funcs == 0) {
+ sd_err(("%s: No IO funcs!\n", __FUNCTION__));
+ return ERROR;
+ }
+
+ sdspi_card_enablefuncs(sd);
+
+ set_client_block_size(sd, 1, BLOCK_SIZE_4318);
+ fn_ints = INTR_CTL_FUNC1_EN;
+
+ if (sd->num_funcs >= 2) {
+ set_client_block_size(sd, 2, sd_f2_blocksize /* BLOCK_SIZE_4328 */);
+ fn_ints |= INTR_CTL_FUNC2_EN;
+ }
+
+ /* Enable/Disable Client interrupts */
+ /* Turn on here but disable at host controller */
+ if (sdspi_card_regwrite(sd, 0, SDIOD_CCCR_INTEN, 1,
+ (fn_ints | INTR_CTL_MASTER_EN)) != SUCCESS) {
+ sd_err(("%s: Could not enable ints in CCCR\n", __FUNCTION__));
+ return ERROR;
+ }
+
+ /* Switch to High-speed clocking mode if both host and device support it */
+ sdspi_set_highspeed_mode(sd, (bool)sd_hiok);
+
+ /* After configuring for High-Speed mode, set the desired clock rate. */
+ if (!spi_start_clock(sd, (uint16)sd_divisor)) {
+ sd_err(("spi_start_clock failed\n"));
+ return ERROR;
+ }
+
+ sd->card_init_done = TRUE;
+
+ return SUCCESS;
+}
+
+static int
+sdspi_set_highspeed_mode(sdioh_info_t *sd, bool HSMode)
+{
+ uint32 regdata;
+ int status;
+ bool hsmode;
+
+ if (HSMode == TRUE) {
+
+ sd_err(("Attempting to enable High-Speed mode.\n"));
+
+ if ((status = sdspi_card_regread(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+ 1, &regdata)) != SUCCESS) {
+ return status;
+ }
+ if (regdata & SDIO_SPEED_SHS) {
+ sd_err(("Device supports High-Speed mode.\n"));
+
+ regdata |= SDIO_SPEED_EHS;
+
+ sd_err(("Writing %08x to Card at %08x\n",
+ regdata, SDIOD_CCCR_SPEED_CONTROL));
+ if ((status = sdspi_card_regwrite(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+ 1, regdata)) != BCME_OK) {
+ return status;
+ }
+
+ hsmode = 1;
+
+ sd_err(("High-speed clocking mode enabled.\n"));
+ }
+ else {
+ sd_err(("Device does not support High-Speed Mode.\n"));
+ hsmode = 0;
+ }
+ } else {
+ if ((status = sdspi_card_regread(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+ 1, &regdata)) != SUCCESS) {
+ return status;
+ }
+
+ regdata = ~SDIO_SPEED_EHS;
+
+ sd_err(("Writing %08x to Card at %08x\n",
+ regdata, SDIOD_CCCR_SPEED_CONTROL));
+ if ((status = sdspi_card_regwrite(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+ 1, regdata)) != BCME_OK) {
+ return status;
+ }
+
+ sd_err(("Low-speed clocking mode enabled.\n"));
+ hsmode = 0;
+ }
+
+ spi_controller_highspeed_mode(sd, hsmode);
+
+ return TRUE;
+}
+
+bool
+sdspi_start_power(sdioh_info_t *sd)
+{
+ uint32 cmd_arg;
+ uint32 cmd_rsp;
+
+ sd_trace(("%s\n", __FUNCTION__));
+
+ /* Get the Card's Operation Condition. Occasionally the board
+ * takes a while to become ready
+ */
+
+ cmd_arg = 0;
+ if (get_ocr(sd, &cmd_arg, &cmd_rsp) != SUCCESS) {
+ sd_err(("%s: Failed to get OCR; bailing\n", __FUNCTION__));
+ return FALSE;
+ }
+
+ sd_err(("mem_present = %d\n", GFIELD(cmd_rsp, RSP4_MEM_PRESENT)));
+ sd_err(("num_funcs = %d\n", GFIELD(cmd_rsp, RSP4_NUM_FUNCS)));
+ sd_err(("card_ready = %d\n", GFIELD(cmd_rsp, RSP4_CARD_READY)));
+ sd_err(("OCR = 0x%x\n", GFIELD(cmd_rsp, RSP4_IO_OCR)));
+
+ /* Verify that the card supports I/O mode */
+ if (GFIELD(cmd_rsp, RSP4_NUM_FUNCS) == 0) {
+ sd_err(("%s: Card does not support I/O\n", __FUNCTION__));
+ return ERROR;
+ }
+
+ sd->num_funcs = GFIELD(cmd_rsp, RSP4_NUM_FUNCS);
+
+ /* Examine voltage: Arasan only supports 3.3 volts,
+ * so look for 3.2-3.3 Volts and also 3.3-3.4 volts.
+ */
+
+ if ((GFIELD(cmd_rsp, RSP4_IO_OCR) & (0x3 << 20)) == 0) {
+ sd_err(("This client does not support 3.3 volts!\n"));
+ return ERROR;
+ }
+
+
+ return TRUE;
+}
+
+static int
+sdspi_driver_init(sdioh_info_t *sd)
+{
+ sd_trace(("%s\n", __FUNCTION__));
+
+ if ((sdspi_host_init(sd)) != SUCCESS) {
+ return ERROR;
+ }
+
+ if (sdspi_client_init(sd) != SUCCESS) {
+ return ERROR;
+ }
+
+ return SUCCESS;
+}
+
+static int
+sdspi_card_enablefuncs(sdioh_info_t *sd)
+{
+ int status;
+ uint32 regdata;
+ uint32 regaddr, fbraddr;
+ uint8 func;
+ uint8 *ptr;
+
+ sd_trace(("%s\n", __FUNCTION__));
+ /* Get the Card's common CIS address */
+ ptr = (uint8 *) &sd->com_cis_ptr;
+ for (regaddr = SDIOD_CCCR_CISPTR_0; regaddr <= SDIOD_CCCR_CISPTR_2; regaddr++) {
+ if ((status = sdspi_card_regread (sd, 0, regaddr, 1, &regdata)) != SUCCESS)
+ return status;
+
+ *ptr++ = (uint8) regdata;
+ }
+
+ /* Only the lower 17-bits are valid */
+ sd->com_cis_ptr &= 0x0001FFFF;
+ sd->func_cis_ptr[0] = sd->com_cis_ptr;
+ sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
+
+ /* Get the Card's function CIS (for each function) */
+ for (fbraddr = SDIOD_FBR_STARTADDR, func = 1;
+ func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
+ ptr = (uint8 *) &sd->func_cis_ptr[func];
+ for (regaddr = SDIOD_FBR_CISPTR_0; regaddr <= SDIOD_FBR_CISPTR_2; regaddr++) {
+ if ((status = sdspi_card_regread (sd, 0, regaddr + fbraddr, 1, &regdata))
+ != SUCCESS)
+ return status;
+
+ *ptr++ = (uint8) regdata;
+ }
+
+ /* Only the lower 17-bits are valid */
+ sd->func_cis_ptr[func] &= 0x0001FFFF;
+ sd_info(("%s: Function %d CIS Ptr = 0x%x\n",
+ __FUNCTION__, func, sd->func_cis_ptr[func]));
+ }
+
+ sd_info(("%s: write ESCI bit\n", __FUNCTION__));
+ /* Enable continuous SPI interrupt (ESCI bit) */
+ sdspi_card_regwrite(sd, 0, SDIOD_CCCR_BICTRL, 1, 0x60);
+
+ sd_info(("%s: enable f1\n", __FUNCTION__));
+ /* Enable function 1 on the card */
+ regdata = SDIO_FUNC_ENABLE_1;
+ if ((status = sdspi_card_regwrite(sd, 0, SDIOD_CCCR_IOEN, 1, regdata)) != SUCCESS)
+ return status;
+
+ sd_info(("%s: done\n", __FUNCTION__));
+ return SUCCESS;
+}
+
+/* Read client card reg */
+static int
+sdspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+ int status;
+ uint32 cmd_arg;
+ uint32 rsp5;
+
+ cmd_arg = 0;
+
+ if ((func == 0) || (regsize == 1)) {
+ cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, SDIOH_XFER_TYPE_READ);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+ cmd_arg = SFIELD(cmd_arg, CMD52_DATA, 0);
+
+ if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma, SDIOH_CMD_52, cmd_arg, NULL, 0))
+ != SUCCESS)
+ return status;
+
+ sdspi_cmd_getrsp(sd, &rsp5, 1);
+
+ if (rsp5 != 0x00)
+ sd_err(("%s: rsp5 flags is 0x%x\t %d\n",
+ __FUNCTION__, rsp5, func));
+
+ *data = sd->card_rsp_data >> 24;
+ } else {
+ cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, regsize);
+ cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+ cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+ cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_READ);
+
+ sd->data_xfer_count = regsize;
+
+ /* sdspi_cmd_issue() returns with the command complete bit
+ * in the ISR already cleared
+ */
+ if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma, SDIOH_CMD_53, cmd_arg, NULL, 0))
+ != SUCCESS)
+ return status;
+
+ sdspi_cmd_getrsp(sd, &rsp5, 1);
+
+ if (rsp5 != 0x00)
+ sd_err(("%s: rsp5 flags is 0x%x\t %d\n",
+ __FUNCTION__, rsp5, func));
+
+ *data = sd->card_rsp_data;
+ if (regsize == 2) {
+ *data &= 0xffff;
+ }
+
+ sd_info(("%s: CMD53 func %d, addr 0x%x, size %d, data 0x%08x\n",
+ __FUNCTION__, func, regaddr, regsize, *data));
+
+
+ }
+
+ return SUCCESS;
+}
+
+/* write a client register */
+static int
+sdspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data)
+{
+ int status;
+ uint32 cmd_arg, rsp5, flags;
+
+ cmd_arg = 0;
+
+ if ((func == 0) || (regsize == 1)) {
+ cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+ cmd_arg = SFIELD(cmd_arg, CMD52_DATA, data & 0xff);
+ if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma, SDIOH_CMD_52, cmd_arg, NULL, 0))
+ != SUCCESS)
+ return status;
+
+ sdspi_cmd_getrsp(sd, &rsp5, 1);
+ flags = GFIELD(rsp5, RSP5_FLAGS);
+ if (flags && (flags != 0x10))
+ sd_err(("%s: rsp5.rsp5.flags = 0x%x, expecting 0x10\n",
+ __FUNCTION__, flags));
+ }
+ else {
+ cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, regsize);
+ cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+ cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+ cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+
+ sd->data_xfer_count = regsize;
+ sd->cmd53_wr_data = data;
+
+ sd_info(("%s: CMD53 func %d, addr 0x%x, size %d, data 0x%08x\n",
+ __FUNCTION__, func, regaddr, regsize, data));
+
+ /* sdspi_cmd_issue() returns with the command complete bit
+ * in the ISR already cleared
+ */
+ if ((status = sdspi_cmd_issue(sd, sd->sd_use_dma, SDIOH_CMD_53, cmd_arg, NULL, 0))
+ != SUCCESS)
+ return status;
+
+ sdspi_cmd_getrsp(sd, &rsp5, 1);
+
+ if (rsp5 != 0x00)
+ sd_err(("%s: rsp5 flags = 0x%x, expecting 0x00\n",
+ __FUNCTION__, rsp5));
+
+ }
+ return SUCCESS;
+}
+
+void
+sdspi_cmd_getrsp(sdioh_info_t *sd, uint32 *rsp_buffer, int count /* num 32 bit words */)
+{
+ *rsp_buffer = sd->card_response;
+}
+
+int max_errors = 0;
+
+#define SPI_MAX_PKT_LEN 768
+uint8 spi_databuf[SPI_MAX_PKT_LEN];
+uint8 spi_rspbuf[SPI_MAX_PKT_LEN];
+
+/* datalen is used for CMD53 length only (0 for sd->data_xfer_count) */
+static int
+sdspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd, uint32 arg,
+ uint32 *data, uint32 datalen)
+{
+ uint32 cmd_reg;
+ uint32 cmd_arg = arg;
+ uint8 cmd_crc = 0x95; /* correct CRC for CMD0 and don't care for others. */
+ uint16 dat_crc;
+ uint8 cmd52data = 0;
+ uint32 i, j;
+ uint32 spi_datalen = 0;
+ uint32 spi_pre_cmd_pad = 0;
+ uint32 spi_max_response_pad = 128;
+
+ cmd_reg = 0;
+ cmd_reg = SFIELD(cmd_reg, SPI_DIR, 1);
+ cmd_reg = SFIELD(cmd_reg, SPI_CMD_INDEX, cmd);
+
+ if (GFIELD(cmd_arg, CMD52_RW_FLAG) == 1) { /* Same for CMD52 and CMD53 */
+ cmd_reg = SFIELD(cmd_reg, SPI_RW, 1);
+ }
+
+ switch (cmd) {
+ case SDIOH_CMD_59: /* CRC_ON_OFF (SPI Mode Only) - Response R1 */
+ cmd52data = arg & 0x1;
+ case SDIOH_CMD_0: /* Set Card to Idle State - No Response */
+ case SDIOH_CMD_5: /* Send Operation condition - Response R4 */
+ sd_trace(("%s: CMD%d\n", __FUNCTION__, cmd));
+ spi_datalen = 44;
+ spi_pre_cmd_pad = 12;
+ spi_max_response_pad = 28;
+ break;
+
+ case SDIOH_CMD_3: /* Ask card to send RCA - Response R6 */
+ case SDIOH_CMD_7: /* Select card - Response R1 */
+ case SDIOH_CMD_15: /* Set card to inactive state - Response None */
+ sd_err(("%s: CMD%d is invalid for SPI Mode.\n", __FUNCTION__, cmd));
+ return ERROR;
+ break;
+
+ case SDIOH_CMD_52: /* IO R/W Direct (single byte) - Response R5 */
+ cmd52data = GFIELD(cmd_arg, CMD52_DATA);
+ cmd_arg = arg;
+ cmd_reg = SFIELD(cmd_reg, SPI_FUNC, GFIELD(cmd_arg, CMD52_FUNCTION));
+ cmd_reg = SFIELD(cmd_reg, SPI_ADDR, GFIELD(cmd_arg, CMD52_REG_ADDR));
+ /* Display trace for byte write */
+ if (GFIELD(cmd_arg, CMD52_RW_FLAG) == 1) {
+ sd_trace(("%s: CMD52: Wr F:%d @0x%04x=%02x\n",
+ __FUNCTION__,
+ GFIELD(cmd_arg, CMD52_FUNCTION),
+ GFIELD(cmd_arg, CMD52_REG_ADDR),
+ cmd52data));
+ }
+
+ spi_datalen = 32;
+ spi_max_response_pad = 28;
+
+ break;
+ case SDIOH_CMD_53: /* IO R/W Extended (multiple bytes/blocks) */
+ cmd_arg = arg;
+ cmd_reg = SFIELD(cmd_reg, SPI_FUNC, GFIELD(cmd_arg, CMD53_FUNCTION));
+ cmd_reg = SFIELD(cmd_reg, SPI_ADDR, GFIELD(cmd_arg, CMD53_REG_ADDR));
+ cmd_reg = SFIELD(cmd_reg, SPI_BLKMODE, 0);
+ cmd_reg = SFIELD(cmd_reg, SPI_OPCODE, GFIELD(cmd_arg, CMD53_OP_CODE));
+ cmd_reg = SFIELD(cmd_reg, SPI_STUFF0, (sd->data_xfer_count>>8));
+ cmd52data = (uint8)sd->data_xfer_count;
+
+ /* Set upper bit in byte count if necessary, but don't set it for 512 bytes. */
+ if ((sd->data_xfer_count > 255) && (sd->data_xfer_count < 512)) {
+ cmd_reg |= 1;
+ }
+
+ if (GFIELD(cmd_reg, SPI_RW) == 1) { /* Write */
+ spi_max_response_pad = 32;
+ spi_datalen = (sd->data_xfer_count + spi_max_response_pad) & 0xFFFC;
+ } else { /* Read */
+
+ spi_max_response_pad = 32;
+ spi_datalen = (sd->data_xfer_count + spi_max_response_pad) & 0xFFFC;
+ }
+ sd_trace(("%s: CMD53: %s F:%d @0x%04x len=0x%02x\n",
+ __FUNCTION__,
+ (GFIELD(cmd_reg, SPI_RW) == 1 ? "Wr" : "Rd"),
+ GFIELD(cmd_arg, CMD53_FUNCTION),
+ GFIELD(cmd_arg, CMD53_REG_ADDR),
+ cmd52data));
+ break;
+
+ default:
+ sd_err(("%s: Unknown command %d\n", __FUNCTION__, cmd));
+ return ERROR;
+ }
+
+ /* Set up and issue the SDIO command */
+ memset(spi_databuf, SDSPI_IDLE_PAD, spi_datalen);
+ spi_databuf[spi_pre_cmd_pad + 0] = (cmd_reg & 0xFF000000) >> 24;
+ spi_databuf[spi_pre_cmd_pad + 1] = (cmd_reg & 0x00FF0000) >> 16;
+ spi_databuf[spi_pre_cmd_pad + 2] = (cmd_reg & 0x0000FF00) >> 8;
+ spi_databuf[spi_pre_cmd_pad + 3] = (cmd_reg & 0x000000FF);
+ spi_databuf[spi_pre_cmd_pad + 4] = cmd52data;
+
+ /* Generate CRC7 for command, if CRC is enabled, otherwise, a
+ * default CRC7 of 0x95, which is correct for CMD0, is used.
+ */
+ if (sd_crc) {
+ cmd_crc = sdspi_crc7(&spi_databuf[spi_pre_cmd_pad], 5);
+ }
+ spi_databuf[spi_pre_cmd_pad + 5] = cmd_crc;
+#define SPI_STOP_TRAN 0xFD
+
+ /* for CMD53 Write, put the data into the output buffer */
+ if ((cmd == SDIOH_CMD_53) && (GFIELD(cmd_arg, CMD53_RW_FLAG) == 1)) {
+ if (datalen != 0) {
+ spi_databuf[spi_pre_cmd_pad + 9] = SDSPI_IDLE_PAD;
+ spi_databuf[spi_pre_cmd_pad + 10] = SDSPI_START_BLOCK;
+
+ for (i = 0; i < sd->data_xfer_count; i++) {
+ spi_databuf[i + 11 + spi_pre_cmd_pad] = ((uint8 *)data)[i];
+ }
+ if (sd_crc) {
+ dat_crc = sdspi_crc16(&spi_databuf[spi_pre_cmd_pad+11], i);
+ } else {
+ dat_crc = 0xAAAA;
+ }
+ spi_databuf[i + 11 + spi_pre_cmd_pad] = (dat_crc >> 8) & 0xFF;
+ spi_databuf[i + 12 + spi_pre_cmd_pad] = dat_crc & 0xFF;
+ } else if (sd->data_xfer_count == 2) {
+ spi_databuf[spi_pre_cmd_pad + 9] = SDSPI_IDLE_PAD;
+ spi_databuf[spi_pre_cmd_pad + 10] = SDSPI_START_BLOCK;
+ spi_databuf[spi_pre_cmd_pad + 11] = sd->cmd53_wr_data & 0xFF;
+ spi_databuf[spi_pre_cmd_pad + 12] = (sd->cmd53_wr_data & 0x0000FF00) >> 8;
+ if (sd_crc) {
+ dat_crc = sdspi_crc16(&spi_databuf[spi_pre_cmd_pad+11], 2);
+ } else {
+ dat_crc = 0x22AA;
+ }
+ spi_databuf[spi_pre_cmd_pad + 13] = (dat_crc >> 8) & 0xFF;
+ spi_databuf[spi_pre_cmd_pad + 14] = (dat_crc & 0xFF);
+ } else if (sd->data_xfer_count == 4) {
+ spi_databuf[spi_pre_cmd_pad + 9] = SDSPI_IDLE_PAD;
+ spi_databuf[spi_pre_cmd_pad + 10] = SDSPI_START_BLOCK;
+ spi_databuf[spi_pre_cmd_pad + 11] = sd->cmd53_wr_data & 0xFF;
+ spi_databuf[spi_pre_cmd_pad + 12] = (sd->cmd53_wr_data & 0x0000FF00) >> 8;
+ spi_databuf[spi_pre_cmd_pad + 13] = (sd->cmd53_wr_data & 0x00FF0000) >> 16;
+ spi_databuf[spi_pre_cmd_pad + 14] = (sd->cmd53_wr_data & 0xFF000000) >> 24;
+ if (sd_crc) {
+ dat_crc = sdspi_crc16(&spi_databuf[spi_pre_cmd_pad+11], 4);
+ } else {
+ dat_crc = 0x44AA;
+ }
+ spi_databuf[spi_pre_cmd_pad + 15] = (dat_crc >> 8) & 0xFF;
+ spi_databuf[spi_pre_cmd_pad + 16] = (dat_crc & 0xFF);
+ } else {
+ printf("CMD53 Write: size %d unsupported\n", sd->data_xfer_count);
+ }
+ }
+
+ spi_sendrecv(sd, spi_databuf, spi_rspbuf, spi_datalen);
+
+ for (i = spi_pre_cmd_pad + SDSPI_COMMAND_LEN; i < spi_max_response_pad; i++) {
+ if ((spi_rspbuf[i] & SDSPI_START_BIT_MASK) == 0) {
+ break;
+ }
+ }
+
+ if (i == spi_max_response_pad) {
+ sd_err(("%s: Did not get a response for CMD%d\n", __FUNCTION__, cmd));
+ return ERROR;
+ }
+
+ /* Extract the response. */
+ sd->card_response = spi_rspbuf[i];
+
+ /* for CMD53 Read, find the start of the response data... */
+ if ((cmd == SDIOH_CMD_53) && (GFIELD(cmd_arg, CMD52_RW_FLAG) == 0)) {
+ for (; i < spi_max_response_pad; i++) {
+ if (spi_rspbuf[i] == SDSPI_START_BLOCK) {
+ break;
+ }
+ }
+
+ if (i == spi_max_response_pad) {
+ printf("Did not get a start of data phase for CMD%d\n", cmd);
+ max_errors++;
+ sdspi_abort(sd, GFIELD(cmd_arg, CMD53_FUNCTION));
+ }
+ sd->card_rsp_data = spi_rspbuf[i+1];
+ sd->card_rsp_data |= spi_rspbuf[i+2] << 8;
+ sd->card_rsp_data |= spi_rspbuf[i+3] << 16;
+ sd->card_rsp_data |= spi_rspbuf[i+4] << 24;
+
+ if (datalen != 0) {
+ i++;
+ for (j = 0; j < sd->data_xfer_count; j++) {
+ ((uint8 *)data)[j] = spi_rspbuf[i+j];
+ }
+ if (sd_crc) {
+ uint16 recv_crc;
+
+ recv_crc = spi_rspbuf[i+j] << 8 | spi_rspbuf[i+j+1];
+ dat_crc = sdspi_crc16((uint8 *)data, datalen);
+ if (dat_crc != recv_crc) {
+ sd_err(("%s: Incorrect data CRC: expected 0x%04x, "
+ "received 0x%04x\n",
+ __FUNCTION__, dat_crc, recv_crc));
+ }
+ }
+ }
+ return SUCCESS;
+ }
+
+ sd->card_rsp_data = spi_rspbuf[i+4];
+ sd->card_rsp_data |= spi_rspbuf[i+3] << 8;
+ sd->card_rsp_data |= spi_rspbuf[i+2] << 16;
+ sd->card_rsp_data |= spi_rspbuf[i+1] << 24;
+
+ /* Display trace for byte read */
+ if ((cmd == SDIOH_CMD_52) && (GFIELD(cmd_arg, CMD52_RW_FLAG) == 0)) {
+ sd_trace(("%s: CMD52: Rd F:%d @0x%04x=%02x\n",
+ __FUNCTION__,
+ GFIELD(cmd_arg, CMD53_FUNCTION),
+ GFIELD(cmd_arg, CMD53_REG_ADDR),
+ sd->card_rsp_data >> 24));
+ }
+
+ return SUCCESS;
+}
+
+/*
+ * On entry: if single-block or non-block, buffer size <= block size.
+ * If multi-block, buffer size is unlimited.
+ * Question is how to handle the left-overs in either single- or multi-block.
+ * I think the caller should break the buffer up so this routine will always
+ * use block size == buffer size to handle the end piece of the buffer
+ */
+
+static int
+sdspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo, uint32 addr, int nbytes, uint32 *data)
+{
+ int status;
+ uint32 cmd_arg;
+ uint32 rsp5;
+ int num_blocks, blocksize;
+ bool local_blockmode, local_dma;
+ bool read = rw == SDIOH_READ ? 1 : 0;
+
+ ASSERT(nbytes);
+
+ cmd_arg = 0;
+ sd_data(("%s: %s 53 func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+ __FUNCTION__, read ? "Rd" : "Wr", func, fifo ? "FIXED" : "INCR",
+ addr, nbytes, sd->r_cnt, sd->t_cnt));
+
+ if (read) sd->r_cnt++; else sd->t_cnt++;
+
+ local_blockmode = sd->sd_blockmode;
+ local_dma = sd->sd_use_dma;
+
+ /* Don't bother with block mode on small xfers */
+ if (nbytes < sd->client_block_size[func]) {
+ sd_info(("setting local blockmode to false: nbytes (%d) != block_size (%d)\n",
+ nbytes, sd->client_block_size[func]));
+ local_blockmode = FALSE;
+ local_dma = FALSE;
+ }
+
+ if (local_blockmode) {
+ blocksize = MIN(sd->client_block_size[func], nbytes);
+ num_blocks = nbytes/blocksize;
+ cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, num_blocks);
+ cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 1);
+ } else {
+ num_blocks = 1;
+ blocksize = nbytes;
+ cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, nbytes);
+ cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+ }
+
+ if (fifo)
+ cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 0);
+ else
+ cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+
+ cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, addr);
+ if (read)
+ cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_READ);
+ else
+ cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+
+ sd->data_xfer_count = nbytes;
+ if ((func == 2) && (fifo == 1)) {
+ sd_data(("%s: %s 53 func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+ __FUNCTION__, read ? "Rd" : "Wr", func, fifo ? "FIXED" : "INCR",
+ addr, nbytes, sd->r_cnt, sd->t_cnt));
+ }
+
+ /* sdspi_cmd_issue() returns with the command complete bit
+ * in the ISR already cleared
+ */
+ if ((status = sdspi_cmd_issue(sd, local_dma,
+ SDIOH_CMD_53, cmd_arg,
+ data, nbytes)) != SUCCESS) {
+ sd_err(("%s: cmd_issue failed for %s\n", __FUNCTION__, (read ? "read" : "write")));
+ return status;
+ }
+
+ sdspi_cmd_getrsp(sd, &rsp5, 1);
+
+ if (rsp5 != 0x00) {
+ sd_err(("%s: rsp5 flags = 0x%x, expecting 0x00\n",
+ __FUNCTION__, rsp5));
+ return ERROR;
+ }
+
+ return SUCCESS;
+}
+
+static int
+set_client_block_size(sdioh_info_t *sd, int func, int block_size)
+{
+ int base;
+ int err = 0;
+
+ sd_err(("%s: Setting block size %d, func %d\n", __FUNCTION__, block_size, func));
+ sd->client_block_size[func] = block_size;
+
+ /* Set the block size in the SDIO Card register */
+ base = func * SDIOD_FBR_SIZE;
+ err = sdspi_card_regwrite(sd, 0, base + SDIOD_CCCR_BLKSIZE_0, 1, block_size & 0xff);
+ if (!err) {
+ err = sdspi_card_regwrite(sd, 0, base + SDIOD_CCCR_BLKSIZE_1, 1,
+ (block_size >> 8) & 0xff);
+ }
+
+ /*
+ * Do not set the block size in the SDIO Host register; that
+ * is func dependent and will get done on an individual
+ * transaction basis.
+ */
+
+ return (err ? BCME_SDIO_ERROR : 0);
+}
+
+/* Reset and re-initialize the device */
+int
+sdioh_sdio_reset(sdioh_info_t *si)
+{
+ si->card_init_done = FALSE;
+ return sdspi_client_init(si);
+}
+
+#define CRC7_POLYNOM 0x09
+#define CRC7_CRCHIGHBIT 0x40
+
+static uint8 sdspi_crc7(unsigned char* p, uint32 len)
+{
+ uint8 c, j, bit, crc = 0;
+ uint32 i;
+
+ for (i = 0; i < len; i++) {
+ c = *p++;
+ for (j = 0x80; j; j >>= 1) {
+ bit = crc & CRC7_CRCHIGHBIT;
+ crc <<= 1;
+ if (c & j) bit ^= CRC7_CRCHIGHBIT;
+ if (bit) crc ^= CRC7_POLYNOM;
+ }
+ }
+
+ /* Convert the CRC7 to an 8-bit SD CRC */
+ crc = (crc << 1) | 1;
+
+ return (crc);
+}
+
+#define CRC16_POLYNOM 0x1021
+#define CRC16_CRCHIGHBIT 0x8000
+
+static uint16 sdspi_crc16(unsigned char* p, uint32 len)
+{
+ uint32 i;
+ uint16 j, c, bit;
+ uint16 crc = 0;
+
+ for (i = 0; i < len; i++) {
+ c = *p++;
+ for (j = 0x80; j; j >>= 1) {
+ bit = crc & CRC16_CRCHIGHBIT;
+ crc <<= 1;
+ if (c & j) bit ^= CRC16_CRCHIGHBIT;
+ if (bit) crc ^= CRC16_POLYNOM;
+ }
+ }
+
+ return (crc);
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdspi_linux.c b/drivers/net/wireless/bcm4329/bcmsdspi_linux.c
new file mode 100644
index 00000000000..e2e0ca6abe4
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdspi_linux.c
@@ -0,0 +1,252 @@
+/*
+ * Broadcom SPI Host Controller Driver - Linux Per-port
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdspi_linux.c,v 1.7.2.1.4.3 2008/06/30 21:09:36 Exp $
+ */
+
+#include <typedefs.h>
+#include <pcicfg.h>
+#include <bcmutils.h>
+
+#include <sdio.h> /* SDIO Specs */
+#include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h> /* to get msglevel bit values */
+
+#include <linux/sched.h> /* request_irq(), free_irq() */
+
+#include <bcmsdspi.h>
+#include <bcmspi.h>
+
+extern uint sd_crc;
+module_param(sd_crc, uint, 0);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define KERNEL26
+#endif
+
+struct sdos_info {
+ sdioh_info_t *sd;
+ spinlock_t lock;
+ wait_queue_head_t intr_wait_queue;
+};
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define BLOCKABLE() (!in_atomic())
+#else
+#define BLOCKABLE() (!in_interrupt())
+#endif
+
+/* Interrupt handler */
+static irqreturn_t
+sdspi_isr(int irq, void *dev_id
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+, struct pt_regs *ptregs
+#endif
+)
+{
+ sdioh_info_t *sd;
+ struct sdos_info *sdos;
+ bool ours;
+
+ sd = (sdioh_info_t *)dev_id;
+ sd->local_intrcount++;
+
+ if (!sd->card_init_done) {
+ sd_err(("%s: Hey Bogus intr...not even initted: irq %d\n", __FUNCTION__, irq));
+ return IRQ_RETVAL(FALSE);
+ } else {
+ ours = spi_check_client_intr(sd, NULL);
+
+ /* For local interrupts, wake the waiting process */
+ if (ours && sd->got_hcint) {
+ sdos = (struct sdos_info *)sd->sdos_info;
+ wake_up_interruptible(&sdos->intr_wait_queue);
+ }
+
+ return IRQ_RETVAL(ours);
+ }
+}
+
+/* Register with Linux for interrupts */
+int
+spi_register_irq(sdioh_info_t *sd, uint irq)
+{
+ sd_trace(("Entering %s: irq == %d\n", __FUNCTION__, irq));
+ if (request_irq(irq, sdspi_isr, IRQF_SHARED, "bcmsdspi", sd) < 0) {
+ sd_err(("%s: request_irq() failed\n", __FUNCTION__));
+ return ERROR;
+ }
+ return SUCCESS;
+}
+
+/* Free Linux irq */
+void
+spi_free_irq(uint irq, sdioh_info_t *sd)
+{
+ free_irq(irq, sd);
+}
+
+/* Map Host controller registers */
+
+uint32 *
+spi_reg_map(osl_t *osh, uintptr addr, int size)
+{
+ return (uint32 *)REG_MAP(addr, size);
+}
+
+void
+spi_reg_unmap(osl_t *osh, uintptr addr, int size)
+{
+ REG_UNMAP((void*)(uintptr)addr);
+}
+
+int
+spi_osinit(sdioh_info_t *sd)
+{
+ struct sdos_info *sdos;
+
+ sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info));
+ sd->sdos_info = (void*)sdos;
+ if (sdos == NULL)
+ return BCME_NOMEM;
+
+ sdos->sd = sd;
+ spin_lock_init(&sdos->lock);
+ init_waitqueue_head(&sdos->intr_wait_queue);
+ return BCME_OK;
+}
+
+void
+spi_osfree(sdioh_info_t *sd)
+{
+ struct sdos_info *sdos;
+ ASSERT(sd && sd->sdos_info);
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+ MFREE(sd->osh, sdos, sizeof(struct sdos_info));
+}
+
+/* Interrupt enable/disable */
+SDIOH_API_RC
+sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
+{
+ ulong flags;
+ struct sdos_info *sdos;
+
+ sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+ ASSERT(sdos);
+
+ if (!(sd->host_init_done && sd->card_init_done)) {
+ sd_err(("%s: Card & Host are not initted - bailing\n", __FUNCTION__));
+ return SDIOH_API_RC_FAIL;
+ }
+
+ if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
+ sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
+ return SDIOH_API_RC_FAIL;
+ }
+
+ /* Ensure atomicity for enable/disable calls */
+ spin_lock_irqsave(&sdos->lock, flags);
+
+ sd->client_intr_enabled = enable;
+ if (enable && !sd->lockcount)
+ spi_devintr_on(sd);
+ else
+ spi_devintr_off(sd);
+
+ spin_unlock_irqrestore(&sdos->lock, flags);
+
+ return SDIOH_API_RC_SUCCESS;
+}
+
+/* Protect against reentrancy (disable device interrupts while executing) */
+void
+spi_lock(sdioh_info_t *sd)
+{
+ ulong flags;
+ struct sdos_info *sdos;
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+ ASSERT(sdos);
+
+ sd_trace(("%s: %d\n", __FUNCTION__, sd->lockcount));
+
+ spin_lock_irqsave(&sdos->lock, flags);
+ if (sd->lockcount) {
+ sd_err(("%s: Already locked!\n", __FUNCTION__));
+ ASSERT(sd->lockcount == 0);
+ }
+ spi_devintr_off(sd);
+ sd->lockcount++;
+ spin_unlock_irqrestore(&sdos->lock, flags);
+}
+
+/* Enable client interrupt */
+void
+spi_unlock(sdioh_info_t *sd)
+{
+ ulong flags;
+ struct sdos_info *sdos;
+
+ sd_trace(("%s: %d, %d\n", __FUNCTION__, sd->lockcount, sd->client_intr_enabled));
+ ASSERT(sd->lockcount > 0);
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+ ASSERT(sdos);
+
+ spin_lock_irqsave(&sdos->lock, flags);
+ if (--sd->lockcount == 0 && sd->client_intr_enabled) {
+ spi_devintr_on(sd);
+ }
+ spin_unlock_irqrestore(&sdos->lock, flags);
+}
+
+void spi_waitbits(sdioh_info_t *sd, bool yield)
+{
+ struct sdos_info *sdos;
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+
+#ifndef BCMSDYIELD
+ ASSERT(!yield);
+#endif
+ sd_trace(("%s: yield %d canblock %d\n",
+ __FUNCTION__, yield, BLOCKABLE()));
+
+ /* Clear the "interrupt happened" flag and last intrstatus */
+ sd->got_hcint = FALSE;
+
+#ifdef BCMSDYIELD
+ if (yield && BLOCKABLE()) {
+ /* Wait for the indication, the interrupt will be masked when the ISR fires. */
+ wait_event_interruptible(sdos->intr_wait_queue, (sd->got_hcint));
+ } else
+#endif /* BCMSDYIELD */
+ {
+ spi_spinbits(sd);
+ }
+
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdstd.c b/drivers/net/wireless/bcm4329/bcmsdstd.c
new file mode 100644
index 00000000000..0ca1f8ff8a2
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdstd.c
@@ -0,0 +1,3127 @@
+/*
+ * 'Standard' SDIO HOST CONTROLLER driver
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdstd.c,v 1.64.4.1.4.4.2.18 2010/08/17 17:00:48 Exp $
+ */
+
+#include <typedefs.h>
+
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <bcmutils.h>
+#include <osl.h>
+#include <siutils.h>
+#include <sdio.h> /* SDIO Device and Protocol Specs */
+#include <sdioh.h> /* SDIO Host Controller Specification */
+#include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h> /* ioctl/iovars */
+#include <pcicfg.h>
+
+
+#define SD_PAGE_BITS 12
+#define SD_PAGE (1 << SD_PAGE_BITS)
+
+#include <bcmsdstd.h>
+
+/* Globals */
+uint sd_msglevel = SDH_ERROR_VAL;
+uint sd_hiok = TRUE; /* Use hi-speed mode if available? */
+uint sd_sdmode = SDIOH_MODE_SD4; /* Use SD4 mode by default */
+uint sd_f2_blocksize = 64; /* Default blocksize */
+
+#ifdef BCMSDYIELD
+bool sd_yieldcpu = TRUE; /* Allow CPU yielding for buffer requests */
+uint sd_minyield = 0; /* Minimum xfer size to allow CPU yield */
+bool sd_forcerb = FALSE; /* Force sync readback in intrs_on/off */
+#endif
+
+uint sd_divisor = 2; /* Default 48MHz/2 = 24MHz */
+
+uint sd_power = 1; /* Default to SD Slot powered ON */
+uint sd_clock = 1; /* Default to SD Clock turned ON */
+uint sd_pci_slot = 0xFFFFffff; /* Used to force selection of a particular PCI slot */
+uint8 sd_dma_mode = DMA_MODE_SDMA; /* Default to SDMA for now */
+
+uint sd_toctl = 7;
+
+static bool trap_errs = FALSE;
+
+static const char *dma_mode_description[] = { "PIO", "SDMA", "ADMA1", "32b ADMA2", "64b ADMA2" };
+
+/* Prototypes */
+static bool sdstd_start_clock(sdioh_info_t *sd, uint16 divisor);
+static bool sdstd_start_power(sdioh_info_t *sd);
+static bool sdstd_bus_width(sdioh_info_t *sd, int width);
+static int sdstd_set_highspeed_mode(sdioh_info_t *sd, bool HSMode);
+static int sdstd_set_dma_mode(sdioh_info_t *sd, int8 dma_mode);
+static int sdstd_card_enablefuncs(sdioh_info_t *sd);
+static void sdstd_cmd_getrsp(sdioh_info_t *sd, uint32 *rsp_buffer, int count);
+static int sdstd_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd, uint32 arg);
+static int sdstd_card_regread(sdioh_info_t *sd, int func, uint32 regaddr,
+ int regsize, uint32 *data);
+static int sdstd_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr,
+ int regsize, uint32 data);
+static int sdstd_driver_init(sdioh_info_t *sd);
+static bool sdstd_reset(sdioh_info_t *sd, bool host_reset, bool client_reset);
+static int sdstd_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo,
+ uint32 addr, int nbytes, uint32 *data);
+static int sdstd_abort(sdioh_info_t *sd, uint func);
+static int sdstd_check_errs(sdioh_info_t *sdioh_info, uint32 cmd, uint32 arg);
+static int set_client_block_size(sdioh_info_t *sd, int func, int blocksize);
+static void sd_map_dma(sdioh_info_t * sd);
+static void sd_unmap_dma(sdioh_info_t * sd);
+static void sd_clear_adma_dscr_buf(sdioh_info_t *sd);
+static void sd_fill_dma_data_buf(sdioh_info_t *sd, uint8 data);
+static void sd_create_adma_descriptor(sdioh_info_t *sd,
+ uint32 index, uint32 addr_phys,
+ uint16 length, uint16 flags);
+static void sd_dump_adma_dscr(sdioh_info_t *sd);
+static void sdstd_dumpregs(sdioh_info_t *sd);
+
+
+/*
+ * Private register access routines.
+ */
+
+/* 16 bit PCI regs */
+
+extern uint16 sdstd_rreg16(sdioh_info_t *sd, uint reg);
+uint16
+sdstd_rreg16(sdioh_info_t *sd, uint reg)
+{
+
+ volatile uint16 data = *(volatile uint16 *)(sd->mem_space + reg);
+ sd_ctrl(("16: R Reg 0x%02x, Data 0x%x\n", reg, data));
+ return data;
+}
+
+extern void sdstd_wreg16(sdioh_info_t *sd, uint reg, uint16 data);
+void
+sdstd_wreg16(sdioh_info_t *sd, uint reg, uint16 data)
+{
+ *(volatile uint16 *)(sd->mem_space + reg) = (uint16)data;
+ sd_ctrl(("16: W Reg 0x%02x, Data 0x%x\n", reg, data));
+}
+
+static void
+sdstd_or_reg16(sdioh_info_t *sd, uint reg, uint16 val)
+{
+ volatile uint16 data = *(volatile uint16 *)(sd->mem_space + reg);
+ sd_ctrl(("16: OR Reg 0x%02x, Val 0x%x\n", reg, val));
+ data |= val;
+ *(volatile uint16 *)(sd->mem_space + reg) = (uint16)data;
+
+}
+static void
+sdstd_mod_reg16(sdioh_info_t *sd, uint reg, int16 mask, uint16 val)
+{
+
+ volatile uint16 data = *(volatile uint16 *)(sd->mem_space + reg);
+ sd_ctrl(("16: MOD Reg 0x%02x, Mask 0x%x, Val 0x%x\n", reg, mask, val));
+ data &= ~mask;
+ data |= (val & mask);
+ *(volatile uint16 *)(sd->mem_space + reg) = (uint16)data;
+}
+
+
+/* 32 bit PCI regs */
+static uint32
+sdstd_rreg(sdioh_info_t *sd, uint reg)
+{
+ volatile uint32 data = *(volatile uint32 *)(sd->mem_space + reg);
+ sd_ctrl(("32: R Reg 0x%02x, Data 0x%x\n", reg, data));
+ return data;
+}
+static inline void
+sdstd_wreg(sdioh_info_t *sd, uint reg, uint32 data)
+{
+ *(volatile uint32 *)(sd->mem_space + reg) = (uint32)data;
+ sd_ctrl(("32: W Reg 0x%02x, Data 0x%x\n", reg, data));
+
+}
+
+/* 8 bit PCI regs */
+static inline void
+sdstd_wreg8(sdioh_info_t *sd, uint reg, uint8 data)
+{
+ *(volatile uint8 *)(sd->mem_space + reg) = (uint8)data;
+ sd_ctrl(("08: W Reg 0x%02x, Data 0x%x\n", reg, data));
+}
+static uint8
+sdstd_rreg8(sdioh_info_t *sd, uint reg)
+{
+ volatile uint8 data = *(volatile uint8 *)(sd->mem_space + reg);
+ sd_ctrl(("08: R Reg 0x%02x, Data 0x%x\n", reg, data));
+ return data;
+}
+
+/*
+ * Private work routines
+ */
+
+sdioh_info_t *glob_sd;
+
+/*
+ * Public entry points & extern's
+ */
+extern sdioh_info_t *
+sdioh_attach(osl_t *osh, void *bar0, uint irq)
+{
+ sdioh_info_t *sd;
+
+ sd_trace(("%s\n", __FUNCTION__));
+ if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) {
+ sd_err(("sdioh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh)));
+ return NULL;
+ }
+ bzero((char *)sd, sizeof(sdioh_info_t));
+ glob_sd = sd;
+ sd->osh = osh;
+ if (sdstd_osinit(sd) != 0) {
+ sd_err(("%s:sdstd_osinit() failed\n", __FUNCTION__));
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return NULL;
+ }
+ sd->mem_space = (volatile char *)sdstd_reg_map(osh, (uintptr)bar0, SDIOH_REG_WINSZ);
+ sd_init_dma(sd);
+ sd->irq = irq;
+ if (sd->mem_space == NULL) {
+ sd_err(("%s:ioremap() failed\n", __FUNCTION__));
+ sdstd_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return NULL;
+ }
+ sd_info(("%s:sd->mem_space = %p\n", __FUNCTION__, sd->mem_space));
+ sd->intr_handler = NULL;
+ sd->intr_handler_arg = NULL;
+ sd->intr_handler_valid = FALSE;
+
+ /* Set defaults */
+ sd->sd_blockmode = TRUE;
+ sd->use_client_ints = TRUE;
+ sd->sd_dma_mode = sd_dma_mode;
+
+ if (!sd->sd_blockmode)
+ sd->sd_dma_mode = DMA_MODE_NONE;
+
+ if (sdstd_driver_init(sd) != SUCCESS) {
+ /* If host CPU was reset without resetting SD bus or
+ SD device, the device will still have its RCA but
+ driver no longer knows what it is (since driver has been restarted).
+ go through once to clear the RCA and a gain reassign it.
+ */
+ sd_info(("driver_init failed - Reset RCA and try again\n"));
+ if (sdstd_driver_init(sd) != SUCCESS) {
+ sd_err(("%s:driver_init() failed()\n", __FUNCTION__));
+ if (sd->mem_space) {
+ sdstd_reg_unmap(osh, (uintptr)sd->mem_space, SDIOH_REG_WINSZ);
+ sd->mem_space = NULL;
+ }
+ sdstd_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return (NULL);
+ }
+ }
+
+ OSL_DMADDRWIDTH(osh, 32);
+
+ /* Always map DMA buffers, so we can switch between DMA modes. */
+ sd_map_dma(sd);
+
+ if (sdstd_register_irq(sd, irq) != SUCCESS) {
+ sd_err(("%s: sdstd_register_irq() failed for irq = %d\n", __FUNCTION__, irq));
+ sdstd_free_irq(sd->irq, sd);
+ if (sd->mem_space) {
+ sdstd_reg_unmap(osh, (uintptr)sd->mem_space, SDIOH_REG_WINSZ);
+ sd->mem_space = NULL;
+ }
+
+ sdstd_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return (NULL);
+ }
+
+ sd_trace(("%s: Done\n", __FUNCTION__));
+ return sd;
+}
+
+extern SDIOH_API_RC
+sdioh_detach(osl_t *osh, sdioh_info_t *sd)
+{
+ sd_trace(("%s\n", __FUNCTION__));
+ if (sd) {
+ sd_unmap_dma(sd);
+ sdstd_wreg16(sd, SD_IntrSignalEnable, 0);
+ sd_trace(("%s: freeing irq %d\n", __FUNCTION__, sd->irq));
+ sdstd_free_irq(sd->irq, sd);
+ if (sd->card_init_done)
+ sdstd_reset(sd, 1, 1);
+ if (sd->mem_space) {
+ sdstd_reg_unmap(osh, (uintptr)sd->mem_space, SDIOH_REG_WINSZ);
+ sd->mem_space = NULL;
+ }
+
+ sdstd_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ }
+ return SDIOH_API_RC_SUCCESS;
+}
+
+/* Configure callback to client when we receive client interrupt */
+extern SDIOH_API_RC
+sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+ sd->intr_handler = fn;
+ sd->intr_handler_arg = argh;
+ sd->intr_handler_valid = TRUE;
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_deregister(sdioh_info_t *sd)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+ sd->intr_handler_valid = FALSE;
+ sd->intr_handler = NULL;
+ sd->intr_handler_arg = NULL;
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+ *onoff = sd->client_intr_enabled;
+ return SDIOH_API_RC_SUCCESS;
+}
+
+#if defined(DHD_DEBUG)
+extern bool
+sdioh_interrupt_pending(sdioh_info_t *sd)
+{
+ uint16 intrstatus;
+ intrstatus = sdstd_rreg16(sd, SD_IntrStatus);
+ return !!(intrstatus & CLIENT_INTR);
+}
+#endif
+
+uint
+sdioh_query_iofnum(sdioh_info_t *sd)
+{
+ return sd->num_funcs;
+}
+
+/* IOVar table */
+enum {
+ IOV_MSGLEVEL = 1,
+ IOV_BLOCKMODE,
+ IOV_BLOCKSIZE,
+ IOV_DMA,
+ IOV_USEINTS,
+ IOV_NUMINTS,
+ IOV_NUMLOCALINTS,
+ IOV_HOSTREG,
+ IOV_DEVREG,
+ IOV_DIVISOR,
+ IOV_SDMODE,
+ IOV_HISPEED,
+ IOV_HCIREGS,
+ IOV_POWER,
+ IOV_YIELDCPU,
+ IOV_MINYIELD,
+ IOV_FORCERB,
+ IOV_CLOCK
+};
+
+const bcm_iovar_t sdioh_iovars[] = {
+ {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 },
+ {"sd_blockmode", IOV_BLOCKMODE, 0, IOVT_BOOL, 0 },
+ {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */
+ {"sd_dma", IOV_DMA, 0, IOVT_UINT32, 0 },
+#ifdef BCMSDYIELD
+ {"sd_yieldcpu", IOV_YIELDCPU, 0, IOVT_BOOL, 0 },
+ {"sd_minyield", IOV_MINYIELD, 0, IOVT_UINT32, 0 },
+ {"sd_forcerb", IOV_FORCERB, 0, IOVT_BOOL, 0 },
+#endif
+ {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 },
+ {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 },
+ {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 },
+ {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
+ {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
+ {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 },
+ {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 },
+ {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 },
+ {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100},
+ {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0},
+ {NULL, 0, 0, 0, 0 }
+};
+
+int
+sdioh_iovar_op(sdioh_info_t *si, const char *name,
+ void *params, int plen, void *arg, int len, bool set)
+{
+ const bcm_iovar_t *vi = NULL;
+ int bcmerror = 0;
+ int val_size;
+ int32 int_val = 0;
+ bool bool_val;
+ uint32 actionid;
+
+ ASSERT(name);
+ ASSERT(len >= 0);
+
+ /* Get must have return space; Set does not take qualifiers */
+ ASSERT(set || (arg && len));
+ ASSERT(!set || (!params && !plen));
+
+ sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name));
+
+ if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) {
+ bcmerror = BCME_UNSUPPORTED;
+ goto exit;
+ }
+
+ if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0)
+ goto exit;
+
+ /* Set up params so get and set can share the convenience variables */
+ if (params == NULL) {
+ params = arg;
+ plen = len;
+ }
+
+ if (vi->type == IOVT_VOID)
+ val_size = 0;
+ else if (vi->type == IOVT_BUFFER)
+ val_size = len;
+ else
+ val_size = sizeof(int);
+
+ if (plen >= (int)sizeof(int_val))
+ bcopy(params, &int_val, sizeof(int_val));
+
+ bool_val = (int_val != 0) ? TRUE : FALSE;
+
+ actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+ switch (actionid) {
+ case IOV_GVAL(IOV_MSGLEVEL):
+ int_val = (int32)sd_msglevel;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_MSGLEVEL):
+ sd_msglevel = int_val;
+ break;
+
+ case IOV_GVAL(IOV_BLOCKMODE):
+ int_val = (int32)si->sd_blockmode;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_BLOCKMODE):
+ si->sd_blockmode = (bool)int_val;
+ /* Haven't figured out how to make non-block mode with DMA */
+ if (!si->sd_blockmode)
+ si->sd_dma_mode = DMA_MODE_NONE;
+ break;
+
+#ifdef BCMSDYIELD
+ case IOV_GVAL(IOV_YIELDCPU):
+ int_val = sd_yieldcpu;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_YIELDCPU):
+ sd_yieldcpu = (bool)int_val;
+ break;
+
+ case IOV_GVAL(IOV_MINYIELD):
+ int_val = sd_minyield;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_MINYIELD):
+ sd_minyield = (bool)int_val;
+ break;
+
+ case IOV_GVAL(IOV_FORCERB):
+ int_val = sd_forcerb;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_FORCERB):
+ sd_forcerb = (bool)int_val;
+ break;
+#endif /* BCMSDYIELD */
+
+ case IOV_GVAL(IOV_BLOCKSIZE):
+ if ((uint32)int_val > si->num_funcs) {
+ bcmerror = BCME_BADARG;
+ break;
+ }
+ int_val = (int32)si->client_block_size[int_val];
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_BLOCKSIZE):
+ {
+ uint func = ((uint32)int_val >> 16);
+ uint blksize = (uint16)int_val;
+ uint maxsize;
+
+ if (func > si->num_funcs) {
+ bcmerror = BCME_BADARG;
+ break;
+ }
+
+ switch (func) {
+ case 0: maxsize = 32; break;
+ case 1: maxsize = BLOCK_SIZE_4318; break;
+ case 2: maxsize = BLOCK_SIZE_4328; break;
+ default: maxsize = 0;
+ }
+ if (blksize > maxsize) {
+ bcmerror = BCME_BADARG;
+ break;
+ }
+ if (!blksize) {
+ blksize = maxsize;
+ }
+
+ /* Now set it */
+ sdstd_lock(si);
+ bcmerror = set_client_block_size(si, func, blksize);
+ sdstd_unlock(si);
+ break;
+ }
+
+ case IOV_GVAL(IOV_DMA):
+ int_val = (int32)si->sd_dma_mode;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_DMA):
+ si->sd_dma_mode = (char)int_val;
+ sdstd_set_dma_mode(si, si->sd_dma_mode);
+ break;
+
+ case IOV_GVAL(IOV_USEINTS):
+ int_val = (int32)si->use_client_ints;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_USEINTS):
+ si->use_client_ints = (bool)int_val;
+ if (si->use_client_ints)
+ si->intmask |= CLIENT_INTR;
+ else
+ si->intmask &= ~CLIENT_INTR;
+ break;
+
+ case IOV_GVAL(IOV_DIVISOR):
+ int_val = (uint32)sd_divisor;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_DIVISOR):
+ sd_divisor = int_val;
+ if (!sdstd_start_clock(si, (uint16)sd_divisor)) {
+ sd_err(("set clock failed!\n"));
+ bcmerror = BCME_ERROR;
+ }
+ break;
+
+ case IOV_GVAL(IOV_POWER):
+ int_val = (uint32)sd_power;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_POWER):
+ sd_power = int_val;
+ if (sd_power == 1) {
+ if (sdstd_driver_init(si) != SUCCESS) {
+ sd_err(("set SD Slot power failed!\n"));
+ bcmerror = BCME_ERROR;
+ } else {
+ sd_err(("SD Slot Powered ON.\n"));
+ }
+ } else {
+ uint8 pwr = 0;
+
+ pwr = SFIELD(pwr, PWR_BUS_EN, 0);
+ sdstd_wreg8(si, SD_PwrCntrl, pwr); /* Set Voltage level */
+ sd_err(("SD Slot Powered OFF.\n"));
+ }
+ break;
+
+ case IOV_GVAL(IOV_CLOCK):
+ int_val = (uint32)sd_clock;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_CLOCK):
+ sd_clock = int_val;
+ if (sd_clock == 1) {
+ sd_info(("SD Clock turned ON.\n"));
+ if (!sdstd_start_clock(si, (uint16)sd_divisor)) {
+ sd_err(("sdstd_start_clock failed\n"));
+ bcmerror = BCME_ERROR;
+ }
+ } else {
+ /* turn off HC clock */
+ sdstd_wreg16(si, SD_ClockCntrl,
+ sdstd_rreg16(si, SD_ClockCntrl) & ~((uint16)0x4));
+
+ sd_info(("SD Clock turned OFF.\n"));
+ }
+ break;
+
+ case IOV_GVAL(IOV_SDMODE):
+ int_val = (uint32)sd_sdmode;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_SDMODE):
+ sd_sdmode = int_val;
+
+ if (!sdstd_bus_width(si, sd_sdmode)) {
+ sd_err(("sdstd_bus_width failed\n"));
+ bcmerror = BCME_ERROR;
+ }
+ break;
+
+ case IOV_GVAL(IOV_HISPEED):
+ int_val = (uint32)sd_hiok;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_HISPEED):
+ sd_hiok = int_val;
+ bcmerror = sdstd_set_highspeed_mode(si, (bool)sd_hiok);
+ break;
+
+ case IOV_GVAL(IOV_NUMINTS):
+ int_val = (int32)si->intrcount;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_NUMLOCALINTS):
+ int_val = (int32)si->local_intrcount;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_HOSTREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+
+ if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) {
+ sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset));
+ bcmerror = BCME_BADARG;
+ break;
+ }
+
+ sd_trace(("%s: rreg%d at offset %d\n", __FUNCTION__,
+ (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32),
+ sd_ptr->offset));
+ if (sd_ptr->offset & 1)
+ int_val = sdstd_rreg8(si, sd_ptr->offset);
+ else if (sd_ptr->offset & 2)
+ int_val = sdstd_rreg16(si, sd_ptr->offset);
+ else
+ int_val = sdstd_rreg(si, sd_ptr->offset);
+
+ bcopy(&int_val, arg, sizeof(int_val));
+ break;
+ }
+
+ case IOV_SVAL(IOV_HOSTREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+
+ if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) {
+ sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset));
+ bcmerror = BCME_BADARG;
+ break;
+ }
+
+ sd_trace(("%s: wreg%d value 0x%08x at offset %d\n", __FUNCTION__, sd_ptr->value,
+ (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32),
+ sd_ptr->offset));
+ if (sd_ptr->offset & 1)
+ sdstd_wreg8(si, sd_ptr->offset, (uint8)sd_ptr->value);
+ else if (sd_ptr->offset & 2)
+ sdstd_wreg16(si, sd_ptr->offset, (uint16)sd_ptr->value);
+ else
+ sdstd_wreg(si, sd_ptr->offset, (uint32)sd_ptr->value);
+
+ break;
+ }
+
+ case IOV_GVAL(IOV_DEVREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+ uint8 data;
+
+ if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) {
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+
+ int_val = (int)data;
+ bcopy(&int_val, arg, sizeof(int_val));
+ break;
+ }
+
+ case IOV_SVAL(IOV_DEVREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+ uint8 data = (uint8)sd_ptr->value;
+
+ if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) {
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+ break;
+ }
+
+
+ default:
+ bcmerror = BCME_UNSUPPORTED;
+ break;
+ }
+exit:
+
+ return bcmerror;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+ SDIOH_API_RC status;
+ /* No lock needed since sdioh_request_byte does locking */
+ status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
+ return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+ /* No lock needed since sdioh_request_byte does locking */
+ SDIOH_API_RC status;
+ status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
+ return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length)
+{
+ uint32 count;
+ int offset;
+ uint32 foo;
+ uint8 *cis = cisd;
+
+ sd_trace(("%s: Func = %d\n", __FUNCTION__, func));
+
+ if (!sd->func_cis_ptr[func]) {
+ bzero(cis, length);
+ return SDIOH_API_RC_FAIL;
+ }
+
+ sdstd_lock(sd);
+ *cis = 0;
+ for (count = 0; count < length; count++) {
+ offset = sd->func_cis_ptr[func] + count;
+ if (sdstd_card_regread(sd, 0, offset, 1, &foo)) {
+ sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
+ sdstd_unlock(sd);
+ return SDIOH_API_RC_FAIL;
+ }
+ *cis = (uint8)(foo & 0xff);
+ cis++;
+ }
+ sdstd_unlock(sd);
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte)
+{
+ int status;
+ uint32 cmd_arg;
+ uint32 rsp5;
+
+ sdstd_lock(sd);
+ cmd_arg = 0;
+ cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, rw == SDIOH_READ ? 0 : 1);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+ cmd_arg = SFIELD(cmd_arg, CMD52_DATA, rw == SDIOH_READ ? 0 : *byte);
+
+ if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_52, cmd_arg)) != SUCCESS) {
+ sdstd_unlock(sd);
+ return status;
+ }
+
+ sdstd_cmd_getrsp(sd, &rsp5, 1);
+ if (sdstd_rreg16 (sd, SD_ErrorIntrStatus) != 0) {
+ sd_err(("%s: 1: ErrorintrStatus 0x%x\n",
+ __FUNCTION__, sdstd_rreg16(sd, SD_ErrorIntrStatus)));
+ }
+ if (GFIELD(rsp5, RSP5_FLAGS) != 0x10)
+ sd_err(("%s: rsp5 flags is 0x%x\t %d\n",
+ __FUNCTION__, GFIELD(rsp5, RSP5_FLAGS), func));
+
+ if (GFIELD(rsp5, RSP5_STUFF))
+ sd_err(("%s: rsp5 stuff is 0x%x: should be 0\n",
+ __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+
+ if (rw == SDIOH_READ)
+ *byte = GFIELD(rsp5, RSP5_DATA);
+
+ sdstd_unlock(sd);
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr,
+ uint32 *word, uint nbytes)
+{
+ int status;
+ bool swap = FALSE;
+
+ sdstd_lock(sd);
+
+ if (rw == SDIOH_READ) {
+ status = sdstd_card_regread(sd, func, addr, nbytes, word);
+ if (swap)
+ *word = BCMSWAP32(*word);
+ } else {
+ if (swap)
+ *word = BCMSWAP32(*word);
+ status = sdstd_card_regwrite(sd, func, addr, nbytes, *word);
+ }
+
+ sdstd_unlock(sd);
+ return (status == SUCCESS ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+extern SDIOH_API_RC
+sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint rw, uint func,
+ uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt)
+{
+ int len;
+ int buflen = (int)buflen_u;
+ bool fifo = (fix_inc == SDIOH_DATA_FIX);
+ uint8 *localbuf = NULL, *tmpbuf = NULL;
+ uint tmplen = 0;
+ bool local_blockmode = sd->sd_blockmode;
+
+ sdstd_lock(sd);
+
+ ASSERT(reg_width == 4);
+ ASSERT(buflen_u < (1 << 30));
+ ASSERT(sd->client_block_size[func]);
+
+ sd_data(("%s: %c len %d r_cnt %d t_cnt %d, pkt @0x%p\n",
+ __FUNCTION__, rw == SDIOH_READ ? 'R' : 'W',
+ buflen_u, sd->r_cnt, sd->t_cnt, pkt));
+
+ /* Break buffer down into blocksize chunks:
+ * Bytemode: 1 block at a time.
+ * Blockmode: Multiples of blocksizes at a time w/ max of SD_PAGE.
+ * Both: leftovers are handled last (will be sent via bytemode).
+ */
+ while (buflen > 0) {
+ if (local_blockmode) {
+ /* Max xfer is Page size */
+ len = MIN(SD_PAGE, buflen);
+
+ /* Round down to a block boundry */
+ if (buflen > sd->client_block_size[func])
+ len = (len/sd->client_block_size[func]) *
+ sd->client_block_size[func];
+ if ((func == SDIO_FUNC_1) && ((len % 4) == 3) && (rw == SDIOH_WRITE)) {
+ tmplen = len;
+ sd_err(("%s: Rounding up buffer to mod4 length.\n", __FUNCTION__));
+ len++;
+ tmpbuf = buffer;
+ if ((localbuf = (uint8 *)MALLOC(sd->osh, len)) == NULL) {
+ sd_err(("out of memory, malloced %d bytes\n",
+ MALLOCED(sd->osh)));
+ sdstd_unlock(sd);
+ return SDIOH_API_RC_FAIL;
+ }
+ bcopy(buffer, localbuf, len);
+ buffer = localbuf;
+ }
+ } else {
+ /* Byte mode: One block at a time */
+ len = MIN(sd->client_block_size[func], buflen);
+ }
+
+ if (sdstd_card_buf(sd, rw, func, fifo, addr, len, (uint32 *)buffer) != SUCCESS) {
+ sdstd_unlock(sd);
+ return SDIOH_API_RC_FAIL;
+ }
+
+ if (local_blockmode) {
+ if ((func == SDIO_FUNC_1) && ((tmplen % 4) == 3) && (rw == SDIOH_WRITE)) {
+ if (localbuf)
+ MFREE(sd->osh, localbuf, len);
+ len--;
+ buffer = tmpbuf;
+ sd_err(("%s: Restoring back buffer ptr and len.\n", __FUNCTION__));
+ }
+ }
+
+ buffer += len;
+ buflen -= len;
+ if (!fifo)
+ addr += len;
+ }
+ sdstd_unlock(sd);
+ return SDIOH_API_RC_SUCCESS;
+}
+
+static
+int sdstd_abort(sdioh_info_t *sd, uint func)
+{
+ int err = 0;
+ int retries;
+
+ uint16 cmd_reg;
+ uint32 cmd_arg;
+ uint32 rsp5;
+ uint8 rflags;
+
+ uint16 int_reg = 0;
+ uint16 plain_intstatus;
+
+ /* Argument is write to F0 (CCCR) IOAbort with function number */
+ cmd_arg = 0;
+ cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, SDIO_FUNC_0);
+ cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, SDIOD_CCCR_IOABORT);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, SD_IO_OP_WRITE);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+ cmd_arg = SFIELD(cmd_arg, CMD52_DATA, func);
+
+ /* Command is CMD52 write */
+ cmd_reg = 0;
+ cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48_BUSY);
+ cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 1);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 1);
+ cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_ABORT);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX, SDIOH_CMD_52);
+
+ if (sd->sd_mode == SDIOH_MODE_SPI) {
+ cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+ }
+
+ /* Wait for CMD_INHIBIT to go away as per spec section 3.6.1.1 */
+ retries = RETRIES_SMALL;
+ while (GFIELD(sdstd_rreg(sd, SD_PresentState), PRES_CMD_INHIBIT)) {
+ if (retries == RETRIES_SMALL)
+ sd_err(("%s: Waiting for Command Inhibit, state 0x%08x\n",
+ __FUNCTION__, sdstd_rreg(sd, SD_PresentState)));
+ if (!--retries) {
+ sd_err(("%s: Command Inhibit timeout, state 0x%08x\n",
+ __FUNCTION__, sdstd_rreg(sd, SD_PresentState)));
+ if (trap_errs)
+ ASSERT(0);
+ err = BCME_SDIO_ERROR;
+ goto done;
+ }
+ }
+
+ /* Clear errors from any previous commands */
+ if ((plain_intstatus = sdstd_rreg16(sd, SD_ErrorIntrStatus)) != 0) {
+ sd_err(("abort: clearing errstat 0x%04x\n", plain_intstatus));
+ sdstd_wreg16(sd, SD_ErrorIntrStatus, plain_intstatus);
+ }
+ plain_intstatus = sdstd_rreg16(sd, SD_IntrStatus);
+ if (plain_intstatus & ~(SFIELD(0, INTSTAT_CARD_INT, 1))) {
+ sd_err(("abort: intstatus 0x%04x\n", plain_intstatus));
+ if (GFIELD(plain_intstatus, INTSTAT_CMD_COMPLETE)) {
+ sd_err(("SDSTD_ABORT: CMD COMPLETE SET BEFORE COMMAND GIVEN!!!\n"));
+ }
+ if (GFIELD(plain_intstatus, INTSTAT_CARD_REMOVAL)) {
+ sd_err(("SDSTD_ABORT: INTSTAT_CARD_REMOVAL\n"));
+ err = BCME_NODEVICE;
+ goto done;
+ }
+ }
+
+ /* Issue the command */
+ sdstd_wreg(sd, SD_Arg0, cmd_arg);
+ sdstd_wreg16(sd, SD_Command, cmd_reg);
+
+ /* In interrupt mode return, expect later CMD_COMPLETE interrupt */
+ if (!sd->polled_mode)
+ return err;
+
+ /* Otherwise, wait for the command to complete */
+ retries = RETRIES_LARGE;
+ do {
+ int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+ } while (--retries &&
+ (GFIELD(int_reg, INTSTAT_ERROR_INT) == 0) &&
+ (GFIELD(int_reg, INTSTAT_CMD_COMPLETE) == 0));
+
+ /* If command completion fails, do a cmd reset and note the error */
+ if (!retries) {
+ sd_err(("%s: CMD_COMPLETE timeout: intr 0x%04x err 0x%04x state 0x%08x\n",
+ __FUNCTION__, int_reg,
+ sdstd_rreg16(sd, SD_ErrorIntrStatus),
+ sdstd_rreg(sd, SD_PresentState)));
+
+ sdstd_wreg8(sd, SD_SoftwareReset, SFIELD(0, SW_RESET_CMD, 1));
+ retries = RETRIES_LARGE;
+ do {
+ sd_trace(("%s: waiting for CMD line reset\n", __FUNCTION__));
+ } while ((GFIELD(sdstd_rreg8(sd, SD_SoftwareReset),
+ SW_RESET_CMD)) && retries--);
+
+ if (!retries) {
+ sd_err(("%s: Timeout waiting for CMD line reset\n", __FUNCTION__));
+ }
+
+ if (trap_errs)
+ ASSERT(0);
+
+ err = BCME_SDIO_ERROR;
+ }
+
+ /* Clear Command Complete interrupt */
+ int_reg = SFIELD(0, INTSTAT_CMD_COMPLETE, 1);
+ sdstd_wreg16(sd, SD_IntrStatus, int_reg);
+
+ /* Check for Errors */
+ if ((plain_intstatus = sdstd_rreg16 (sd, SD_ErrorIntrStatus)) != 0) {
+ sd_err(("%s: ErrorintrStatus: 0x%x, "
+ "(intrstatus = 0x%x, present state 0x%x) clearing\n",
+ __FUNCTION__, plain_intstatus,
+ sdstd_rreg16(sd, SD_IntrStatus),
+ sdstd_rreg(sd, SD_PresentState)));
+
+ sdstd_wreg16(sd, SD_ErrorIntrStatus, plain_intstatus);
+
+ sdstd_wreg8(sd, SD_SoftwareReset, SFIELD(0, SW_RESET_DAT, 1));
+ retries = RETRIES_LARGE;
+ do {
+ sd_trace(("%s: waiting for DAT line reset\n", __FUNCTION__));
+ } while ((GFIELD(sdstd_rreg8(sd, SD_SoftwareReset),
+ SW_RESET_DAT)) && retries--);
+
+ if (!retries) {
+ sd_err(("%s: Timeout waiting for DAT line reset\n", __FUNCTION__));
+ }
+
+ if (trap_errs)
+ ASSERT(0);
+
+ /* ABORT is dataless, only cmd errs count */
+ if (plain_intstatus & ERRINT_CMD_ERRS)
+ err = BCME_SDIO_ERROR;
+ }
+
+ /* If command failed don't bother looking at response */
+ if (err)
+ goto done;
+
+ /* Otherwise, check the response */
+ sdstd_cmd_getrsp(sd, &rsp5, 1);
+ rflags = GFIELD(rsp5, RSP5_FLAGS);
+
+ if (rflags & SD_RSP_R5_ERRBITS) {
+ sd_err(("%s: R5 flags include errbits: 0x%02x\n", __FUNCTION__, rflags));
+
+ /* The CRC error flag applies to the previous command */
+ if (rflags & (SD_RSP_R5_ERRBITS & ~SD_RSP_R5_COM_CRC_ERROR)) {
+ err = BCME_SDIO_ERROR;
+ goto done;
+ }
+ }
+
+ if (((rflags & (SD_RSP_R5_IO_CURRENTSTATE0 | SD_RSP_R5_IO_CURRENTSTATE1)) != 0x10) &&
+ ((rflags & (SD_RSP_R5_IO_CURRENTSTATE0 | SD_RSP_R5_IO_CURRENTSTATE1)) != 0x20)) {
+ sd_err(("%s: R5 flags has bad state: 0x%02x\n", __FUNCTION__, rflags));
+ err = BCME_SDIO_ERROR;
+ goto done;
+ }
+
+ if (GFIELD(rsp5, RSP5_STUFF)) {
+ sd_err(("%s: rsp5 stuff is 0x%x: should be 0\n",
+ __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+ err = BCME_SDIO_ERROR;
+ goto done;
+ }
+
+done:
+ if (err == BCME_NODEVICE)
+ return err;
+
+ sdstd_wreg8(sd, SD_SoftwareReset,
+ SFIELD(SFIELD(0, SW_RESET_DAT, 1), SW_RESET_CMD, 1));
+
+ retries = RETRIES_LARGE;
+ do {
+ rflags = sdstd_rreg8(sd, SD_SoftwareReset);
+ if (!GFIELD(rflags, SW_RESET_DAT) && !GFIELD(rflags, SW_RESET_CMD))
+ break;
+ } while (--retries);
+
+ if (!retries) {
+ sd_err(("%s: Timeout waiting for DAT/CMD reset: 0x%02x\n",
+ __FUNCTION__, rflags));
+ err = BCME_SDIO_ERROR;
+ }
+
+ return err;
+}
+
+extern int
+sdioh_abort(sdioh_info_t *sd, uint fnum)
+{
+ int ret;
+
+ sdstd_lock(sd);
+ ret = sdstd_abort(sd, fnum);
+ sdstd_unlock(sd);
+
+ return ret;
+}
+
+int
+sdioh_start(sdioh_info_t *sd, int stage)
+{
+ return SUCCESS;
+}
+
+int
+sdioh_stop(sdioh_info_t *sd)
+{
+ return SUCCESS;
+}
+
+static int
+sdstd_check_errs(sdioh_info_t *sdioh_info, uint32 cmd, uint32 arg)
+{
+ uint16 regval;
+ uint retries;
+ uint function = 0;
+
+ /* If no errors, we're done */
+ if ((regval = sdstd_rreg16(sdioh_info, SD_ErrorIntrStatus)) == 0)
+ return SUCCESS;
+
+ sd_info(("%s: ErrorIntrStatus 0x%04x (clearing), IntrStatus 0x%04x PresentState 0x%08x\n",
+ __FUNCTION__, regval, sdstd_rreg16(sdioh_info, SD_IntrStatus),
+ sdstd_rreg(sdioh_info, SD_PresentState)));
+ sdstd_wreg16(sdioh_info, SD_ErrorIntrStatus, regval);
+
+ /* On command error, issue CMD reset */
+ if (regval & ERRINT_CMD_ERRS) {
+ sd_trace(("%s: issuing CMD reset\n", __FUNCTION__));
+ sdstd_wreg8(sdioh_info, SD_SoftwareReset, SFIELD(0, SW_RESET_CMD, 1));
+ for (retries = RETRIES_LARGE; retries; retries--)
+ if (!(GFIELD(sdstd_rreg8(sdioh_info, SD_SoftwareReset), SW_RESET_CMD)))
+ break;
+ if (!retries) {
+ sd_err(("%s: Timeout waiting for CMD line reset\n", __FUNCTION__));
+ }
+ }
+
+ /* On data error, issue DAT reset */
+ if (regval & ERRINT_DATA_ERRS) {
+ sd_trace(("%s: issuing DAT reset\n", __FUNCTION__));
+ sdstd_wreg8(sdioh_info, SD_SoftwareReset, SFIELD(0, SW_RESET_DAT, 1));
+ for (retries = RETRIES_LARGE; retries; retries--)
+ if (!(GFIELD(sdstd_rreg8(sdioh_info, SD_SoftwareReset), SW_RESET_DAT)))
+ break;
+ if (!retries) {
+ sd_err(("%s: Timeout waiting for DAT line reset\n", __FUNCTION__));
+ }
+ }
+
+ /* For an IO command (CMD52 or CMD53) issue an abort to the appropriate function */
+ if (cmd == SDIOH_CMD_53)
+ function = GFIELD(arg, CMD53_FUNCTION);
+ else if (cmd == SDIOH_CMD_52)
+ function = GFIELD(arg, CMD52_FUNCTION);
+ if (function) {
+ sd_trace(("%s: requesting abort for function %d after cmd %d\n",
+ __FUNCTION__, function, cmd));
+ sdstd_abort(sdioh_info, function);
+ }
+
+ if (trap_errs)
+ ASSERT(0);
+
+ return ERROR;
+}
+
+
+
+/*
+ * Private/Static work routines
+ */
+static bool
+sdstd_reset(sdioh_info_t *sd, bool host_reset, bool client_reset)
+{
+ int retries = RETRIES_LARGE;
+ uchar regval;
+
+ if (!sd)
+ return TRUE;
+
+ sdstd_lock(sd);
+ /* Reset client card */
+ if (client_reset && (sd->adapter_slot != -1)) {
+ if (sdstd_card_regwrite(sd, 0, SDIOD_CCCR_IOABORT, 1, 0x8) != SUCCESS)
+ sd_err(("%s: Cannot write to card reg 0x%x\n",
+ __FUNCTION__, SDIOD_CCCR_IOABORT));
+ else
+ sd->card_rca = 0;
+ }
+
+ /* Reset host controller */
+ if (host_reset) {
+ regval = SFIELD(0, SW_RESET_ALL, 1);
+ sdstd_wreg8(sd, SD_SoftwareReset, regval);
+ do {
+ sd_trace(("%s: waiting for reset\n", __FUNCTION__));
+ } while ((sdstd_rreg8(sd, SD_SoftwareReset) & regval) && retries--);
+
+ if (!retries) {
+ sd_err(("%s: Timeout waiting for host reset\n", __FUNCTION__));
+ sdstd_unlock(sd);
+ return (FALSE);
+ }
+
+ /* A reset should reset bus back to 1 bit mode */
+ sd->sd_mode = SDIOH_MODE_SD1;
+ sdstd_set_dma_mode(sd, sd->sd_dma_mode);
+ }
+ sdstd_unlock(sd);
+ return TRUE;
+}
+
+/* Disable device interrupt */
+void
+sdstd_devintr_off(sdioh_info_t *sd)
+{
+ sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+ if (sd->use_client_ints) {
+ sd->intmask &= ~CLIENT_INTR;
+ sdstd_wreg16(sd, SD_IntrSignalEnable, sd->intmask);
+ sdstd_rreg16(sd, SD_IntrSignalEnable); /* Sync readback */
+ }
+}
+
+/* Enable device interrupt */
+void
+sdstd_devintr_on(sdioh_info_t *sd)
+{
+ ASSERT(sd->lockcount == 0);
+ sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
+ if (sd->use_client_ints) {
+ uint16 status = sdstd_rreg16(sd, SD_IntrStatusEnable);
+ sdstd_wreg16(sd, SD_IntrStatusEnable, SFIELD(status, INTSTAT_CARD_INT, 0));
+ sdstd_wreg16(sd, SD_IntrStatusEnable, status);
+
+ sd->intmask |= CLIENT_INTR;
+ sdstd_wreg16(sd, SD_IntrSignalEnable, sd->intmask);
+ sdstd_rreg16(sd, SD_IntrSignalEnable); /* Sync readback */
+ }
+}
+
+#ifdef BCMSDYIELD
+/* Enable/disable other interrupts */
+void
+sdstd_intrs_on(sdioh_info_t *sd, uint16 norm, uint16 err)
+{
+ if (err) {
+ norm = SFIELD(norm, INTSTAT_ERROR_INT, 1);
+ sdstd_wreg16(sd, SD_ErrorIntrSignalEnable, err);
+ }
+
+ sd->intmask |= norm;
+ sdstd_wreg16(sd, SD_IntrSignalEnable, sd->intmask);
+ if (sd_forcerb)
+ sdstd_rreg16(sd, SD_IntrSignalEnable); /* Sync readback */
+}
+
+void
+sdstd_intrs_off(sdioh_info_t *sd, uint16 norm, uint16 err)
+{
+ if (err) {
+ norm = SFIELD(norm, INTSTAT_ERROR_INT, 1);
+ sdstd_wreg16(sd, SD_ErrorIntrSignalEnable, 0);
+ }
+
+ sd->intmask &= ~norm;
+ sdstd_wreg16(sd, SD_IntrSignalEnable, sd->intmask);
+ if (sd_forcerb)
+ sdstd_rreg16(sd, SD_IntrSignalEnable); /* Sync readback */
+}
+#endif /* BCMSDYIELD */
+
+static int
+sdstd_host_init(sdioh_info_t *sd)
+{
+ int num_slots, full_slot;
+ uint8 reg8;
+
+ uint32 card_ins;
+ int slot, first_bar = 0;
+ bool detect_slots = FALSE;
+ uint bar;
+
+ /* Check for Arasan ID */
+ if ((OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) & 0xFFFF) == VENDOR_SI_IMAGE) {
+ sd_info(("%s: Found Arasan Standard SDIO Host Controller\n", __FUNCTION__));
+ sd->controller_type = SDIOH_TYPE_ARASAN_HDK;
+ detect_slots = TRUE;
+ } else if ((OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) & 0xFFFF) == VENDOR_BROADCOM) {
+ sd_info(("%s: Found Broadcom 27xx Standard SDIO Host Controller\n", __FUNCTION__));
+ sd->controller_type = SDIOH_TYPE_BCM27XX;
+ detect_slots = FALSE;
+ } else if ((OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) & 0xFFFF) == VENDOR_TI) {
+ sd_info(("%s: Found TI PCIxx21 Standard SDIO Host Controller\n", __FUNCTION__));
+ sd->controller_type = SDIOH_TYPE_TI_PCIXX21;
+ detect_slots = TRUE;
+ } else if ((OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) & 0xFFFF) == VENDOR_RICOH) {
+ sd_info(("%s: Ricoh Co Ltd R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter\n",
+ __FUNCTION__));
+ sd->controller_type = SDIOH_TYPE_RICOH_R5C822;
+ detect_slots = TRUE;
+ } else if ((OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) & 0xFFFF) == VENDOR_JMICRON) {
+ sd_info(("%s: JMicron Standard SDIO Host Controller\n",
+ __FUNCTION__));
+ sd->controller_type = SDIOH_TYPE_JMICRON;
+ detect_slots = TRUE;
+ } else {
+ return ERROR;
+ }
+
+ /*
+ * Determine num of slots
+ * Search each slot
+ */
+
+ first_bar = OSL_PCI_READ_CONFIG(sd->osh, SD_SlotInfo, 4) & 0x7;
+ num_slots = (OSL_PCI_READ_CONFIG(sd->osh, SD_SlotInfo, 4) & 0xff) >> 4;
+ num_slots &= 7;
+ num_slots++; /* map bits to num slots according to spec */
+
+ if (OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_VID, 4) ==
+ ((SDIOH_FPGA_ID << 16) | VENDOR_BROADCOM)) {
+ sd_err(("%s: Found Broadcom Standard SDIO Host Controller FPGA\n", __FUNCTION__));
+ /* Set BAR0 Window to SDIOSTH core */
+ OSL_PCI_WRITE_CONFIG(sd->osh, PCI_BAR0_WIN, 4, 0x18001000);
+
+ /* Set defaults particular to this controller. */
+ detect_slots = TRUE;
+ num_slots = 1;
+ first_bar = 0;
+
+ /* Controller supports ADMA2, so turn it on here. */
+ sd->sd_dma_mode = DMA_MODE_ADMA2;
+ }
+
+ /* Map in each slot on the board and query it to see if a
+ * card is inserted. Use the first populated slot found.
+ */
+ if (sd->mem_space) {
+ sdstd_reg_unmap(sd->osh, (uintptr)sd->mem_space, SDIOH_REG_WINSZ);
+ sd->mem_space = NULL;
+ }
+
+ full_slot = -1;
+
+ for (slot = 0; slot < num_slots; slot++) {
+ bar = OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_BAR0 + (4*(slot + first_bar)), 4);
+ sd->mem_space = (volatile char *)sdstd_reg_map(sd->osh,
+ (uintptr)bar, SDIOH_REG_WINSZ);
+
+ sd->adapter_slot = -1;
+
+ if (detect_slots) {
+ card_ins = GFIELD(sdstd_rreg(sd, SD_PresentState), PRES_CARD_PRESENT);
+ } else {
+ card_ins = TRUE;
+ }
+
+ if (card_ins) {
+ sd_info(("%s: SDIO slot %d: Full\n", __FUNCTION__, slot));
+ if (full_slot < 0)
+ full_slot = slot;
+ } else {
+ sd_info(("%s: SDIO slot %d: Empty\n", __FUNCTION__, slot));
+ }
+
+ if (sd->mem_space) {
+ sdstd_reg_unmap(sd->osh, (uintptr)sd->mem_space, SDIOH_REG_WINSZ);
+ sd->mem_space = NULL;
+ }
+ }
+
+ if (full_slot < 0) {
+ sd_err(("No slots on SDIO controller are populated\n"));
+ return -1;
+ }
+
+ bar = OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_BAR0 + (4*(full_slot + first_bar)), 4);
+ sd->mem_space = (volatile char *)sdstd_reg_map(sd->osh, (uintptr)bar, SDIOH_REG_WINSZ);
+
+ sd_err(("Using slot %d at BAR%d [0x%08x] mem_space 0x%p\n",
+ full_slot,
+ (full_slot + first_bar),
+ OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_BAR0 + (4*(full_slot + first_bar)), 4),
+ sd->mem_space));
+
+
+ sd->adapter_slot = full_slot;
+
+ sd->version = sdstd_rreg16(sd, SD_HostControllerVersion) & 0xFF;
+ switch (sd->version) {
+ case 0:
+ sd_err(("Host Controller version 1.0, Vendor Revision: 0x%02x\n",
+ sdstd_rreg16(sd, SD_HostControllerVersion) >> 8));
+ break;
+ case 1:
+ case 2:
+ sd_err(("Host Controller version 2.0, Vendor Revision: 0x%02x\n",
+ sdstd_rreg16(sd, SD_HostControllerVersion) >> 8));
+ break;
+ default:
+ sd_err(("%s: Host Controller version 0x%02x not supported.\n",
+ __FUNCTION__, sd->version));
+ break;
+ }
+
+ sd->caps = sdstd_rreg(sd, SD_Capabilities); /* Cache this for later use */
+ sd->curr_caps = sdstd_rreg(sd, SD_MaxCurCap);
+
+ sdstd_set_dma_mode(sd, sd->sd_dma_mode);
+
+
+ sdstd_reset(sd, 1, 0);
+
+ /* Read SD4/SD1 mode */
+ if ((reg8 = sdstd_rreg8(sd, SD_HostCntrl))) {
+ if (reg8 & SD4_MODE) {
+ sd_err(("%s: Host cntrlr already in 4 bit mode: 0x%x\n",
+ __FUNCTION__, reg8));
+ }
+ }
+
+ /* Default power on mode is SD1 */
+ sd->sd_mode = SDIOH_MODE_SD1;
+ sd->polled_mode = TRUE;
+ sd->host_init_done = TRUE;
+ sd->card_init_done = FALSE;
+ sd->adapter_slot = full_slot;
+
+ return (SUCCESS);
+}
+#define CMD5_RETRIES 200
+static int
+get_ocr(sdioh_info_t *sd, uint32 *cmd_arg, uint32 *cmd_rsp)
+{
+ int retries, status;
+
+ /* Get the Card's Operation Condition. Occasionally the board
+ * takes a while to become ready
+ */
+ retries = CMD5_RETRIES;
+ do {
+ *cmd_rsp = 0;
+ if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_5, *cmd_arg))
+ != SUCCESS) {
+ sd_err(("%s: CMD5 failed\n", __FUNCTION__));
+ return status;
+ }
+ sdstd_cmd_getrsp(sd, cmd_rsp, 1);
+ if (!GFIELD(*cmd_rsp, RSP4_CARD_READY))
+ sd_trace(("%s: Waiting for card to become ready\n", __FUNCTION__));
+ } while ((!GFIELD(*cmd_rsp, RSP4_CARD_READY)) && --retries);
+ if (!retries)
+ return ERROR;
+
+ return (SUCCESS);
+}
+
+static int
+sdstd_client_init(sdioh_info_t *sd)
+{
+ uint32 cmd_arg, cmd_rsp;
+ int status;
+ uint8 fn_ints;
+
+
+ sd_trace(("%s: Powering up slot %d\n", __FUNCTION__, sd->adapter_slot));
+
+ /* Clear any pending ints */
+ sdstd_wreg16(sd, SD_IntrStatus, 0x1ff);
+ sdstd_wreg16(sd, SD_ErrorIntrStatus, 0x0fff);
+
+ /* Enable both Normal and Error Status. This does not enable
+ * interrupts, it only enables the status bits to
+ * become 'live'
+ */
+ sdstd_wreg16(sd, SD_IntrStatusEnable, 0x1ff);
+ sdstd_wreg16(sd, SD_ErrorIntrStatusEnable, 0xffff);
+
+ sdstd_wreg16(sd, SD_IntrSignalEnable, 0); /* Disable ints for now. */
+
+ /* Start at ~400KHz clock rate for initialization */
+ if (!sdstd_start_clock(sd, 128)) {
+ sd_err(("sdstd_start_clock failed\n"));
+ return ERROR;
+ }
+ if (!sdstd_start_power(sd)) {
+ sd_err(("sdstd_start_power failed\n"));
+ return ERROR;
+ }
+
+ if (sd->num_funcs == 0) {
+ sd_err(("%s: No IO funcs!\n", __FUNCTION__));
+ return ERROR;
+ }
+
+ /* In SPI mode, issue CMD0 first */
+ if (sd->sd_mode == SDIOH_MODE_SPI) {
+ cmd_arg = 0;
+ if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_0, cmd_arg))
+ != SUCCESS) {
+ sd_err(("BCMSDIOH: cardinit: CMD0 failed!\n"));
+ return status;
+ }
+ }
+
+ if (sd->sd_mode != SDIOH_MODE_SPI) {
+ uint16 rsp6_status;
+
+ /* Card is operational. Ask it to send an RCA */
+ cmd_arg = 0;
+ if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_3, cmd_arg))
+ != SUCCESS) {
+ sd_err(("%s: CMD3 failed!\n", __FUNCTION__));
+ return status;
+ }
+
+ /* Verify the card status returned with the cmd response */
+ sdstd_cmd_getrsp(sd, &cmd_rsp, 1);
+ rsp6_status = GFIELD(cmd_rsp, RSP6_STATUS);
+ if (GFIELD(rsp6_status, RSP6STAT_COM_CRC_ERROR) ||
+ GFIELD(rsp6_status, RSP6STAT_ILLEGAL_CMD) ||
+ GFIELD(rsp6_status, RSP6STAT_ERROR)) {
+ sd_err(("%s: CMD3 response error. Response = 0x%x!\n",
+ __FUNCTION__, rsp6_status));
+ return ERROR;
+ }
+
+ /* Save the Card's RCA */
+ sd->card_rca = GFIELD(cmd_rsp, RSP6_IO_RCA);
+ sd_info(("RCA is 0x%x\n", sd->card_rca));
+
+ if (rsp6_status)
+ sd_err(("raw status is 0x%x\n", rsp6_status));
+
+ /* Select the card */
+ cmd_arg = SFIELD(0, CMD7_RCA, sd->card_rca);
+ if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_7, cmd_arg))
+ != SUCCESS) {
+ sd_err(("%s: CMD7 failed!\n", __FUNCTION__));
+ return status;
+ }
+ sdstd_cmd_getrsp(sd, &cmd_rsp, 1);
+ if (cmd_rsp != SDIOH_CMD7_EXP_STATUS) {
+ sd_err(("%s: CMD7 response error. Response = 0x%x!\n",
+ __FUNCTION__, cmd_rsp));
+ return ERROR;
+ }
+ }
+
+ sdstd_card_enablefuncs(sd);
+
+ if (!sdstd_bus_width(sd, sd_sdmode)) {
+ sd_err(("sdstd_bus_width failed\n"));
+ return ERROR;
+ }
+
+ set_client_block_size(sd, 1, BLOCK_SIZE_4318);
+ fn_ints = INTR_CTL_FUNC1_EN;
+
+ if (sd->num_funcs >= 2) {
+ set_client_block_size(sd, 2, sd_f2_blocksize /* BLOCK_SIZE_4328 */);
+ fn_ints |= INTR_CTL_FUNC2_EN;
+ }
+
+ /* Enable/Disable Client interrupts */
+ /* Turn on here but disable at host controller? */
+ if (sdstd_card_regwrite(sd, 0, SDIOD_CCCR_INTEN, 1,
+ (fn_ints | INTR_CTL_MASTER_EN)) != SUCCESS) {
+ sd_err(("%s: Could not enable ints in CCCR\n", __FUNCTION__));
+ return ERROR;
+ }
+
+ /* Switch to High-speed clocking mode if both host and device support it */
+ sdstd_set_highspeed_mode(sd, (bool)sd_hiok);
+
+ /* After configuring for High-Speed mode, set the desired clock rate. */
+ if (!sdstd_start_clock(sd, (uint16)sd_divisor)) {
+ sd_err(("sdstd_start_clock failed\n"));
+ return ERROR;
+ }
+
+ sd->card_init_done = TRUE;
+
+ return SUCCESS;
+}
+
+static int
+sdstd_set_highspeed_mode(sdioh_info_t *sd, bool HSMode)
+{
+ uint32 regdata;
+ int status;
+ uint8 reg8;
+
+ reg8 = sdstd_rreg8(sd, SD_HostCntrl);
+
+
+ if (HSMode == TRUE) {
+ if (sd_hiok && (GFIELD(sd->caps, CAP_HIGHSPEED)) == 0) {
+ sd_err(("Host Controller does not support hi-speed mode.\n"));
+ return BCME_ERROR;
+ }
+
+ sd_info(("Attempting to enable High-Speed mode.\n"));
+
+ if ((status = sdstd_card_regread(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+ 1, &regdata)) != SUCCESS) {
+ return BCME_SDIO_ERROR;
+ }
+ if (regdata & SDIO_SPEED_SHS) {
+ sd_info(("Device supports High-Speed mode.\n"));
+
+ regdata |= SDIO_SPEED_EHS;
+
+ sd_info(("Writing %08x to Card at %08x\n",
+ regdata, SDIOD_CCCR_SPEED_CONTROL));
+ if ((status = sdstd_card_regwrite(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+ 1, regdata)) != BCME_OK) {
+ return BCME_SDIO_ERROR;
+ }
+
+ if ((status = sdstd_card_regread(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+ 1, &regdata)) != BCME_OK) {
+ return BCME_SDIO_ERROR;
+ }
+
+ sd_info(("Read %08x to Card at %08x\n", regdata, SDIOD_CCCR_SPEED_CONTROL));
+
+ reg8 = SFIELD(reg8, HOST_HI_SPEED_EN, 1);
+
+ sd_err(("High-speed clocking mode enabled.\n"));
+ }
+ else {
+ sd_err(("Device does not support High-Speed Mode.\n"));
+ reg8 = SFIELD(reg8, HOST_HI_SPEED_EN, 0);
+ }
+ } else {
+ /* Force off device bit */
+ if ((status = sdstd_card_regread(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+ 1, &regdata)) != BCME_OK) {
+ return status;
+ }
+ if (regdata & SDIO_SPEED_EHS) {
+ regdata &= ~SDIO_SPEED_EHS;
+ if ((status = sdstd_card_regwrite(sd, 0, SDIOD_CCCR_SPEED_CONTROL,
+ 1, regdata)) != BCME_OK) {
+ return status;
+ }
+ }
+
+ sd_err(("High-speed clocking mode disabled.\n"));
+ reg8 = SFIELD(reg8, HOST_HI_SPEED_EN, 0);
+ }
+
+ sdstd_wreg8(sd, SD_HostCntrl, reg8);
+
+ return BCME_OK;
+}
+
+/* Select DMA Mode:
+ * If dma_mode == DMA_MODE_AUTO, pick the "best" mode.
+ * Otherwise, pick the selected mode if supported.
+ * If not supported, use PIO mode.
+ */
+static int
+sdstd_set_dma_mode(sdioh_info_t *sd, int8 dma_mode)
+{
+ uint8 reg8, dma_sel_bits = SDIOH_SDMA_MODE;
+ int8 prev_dma_mode = sd->sd_dma_mode;
+
+ switch (prev_dma_mode) {
+ case DMA_MODE_AUTO:
+ sd_dma(("%s: Selecting best DMA mode supported by controller.\n",
+ __FUNCTION__));
+ if (GFIELD(sd->caps, CAP_ADMA2)) {
+ sd->sd_dma_mode = DMA_MODE_ADMA2;
+ dma_sel_bits = SDIOH_ADMA2_MODE;
+ } else if (GFIELD(sd->caps, CAP_ADMA1)) {
+ sd->sd_dma_mode = DMA_MODE_ADMA1;
+ dma_sel_bits = SDIOH_ADMA1_MODE;
+ } else if (GFIELD(sd->caps, CAP_DMA)) {
+ sd->sd_dma_mode = DMA_MODE_SDMA;
+ } else {
+ sd->sd_dma_mode = DMA_MODE_NONE;
+ }
+ break;
+ case DMA_MODE_NONE:
+ sd->sd_dma_mode = DMA_MODE_NONE;
+ break;
+ case DMA_MODE_SDMA:
+ if (GFIELD(sd->caps, CAP_DMA)) {
+ sd->sd_dma_mode = DMA_MODE_SDMA;
+ } else {
+ sd_err(("%s: SDMA not supported by controller.\n", __FUNCTION__));
+ sd->sd_dma_mode = DMA_MODE_NONE;
+ }
+ break;
+ case DMA_MODE_ADMA1:
+ if (GFIELD(sd->caps, CAP_ADMA1)) {
+ sd->sd_dma_mode = DMA_MODE_ADMA1;
+ dma_sel_bits = SDIOH_ADMA1_MODE;
+ } else {
+ sd_err(("%s: ADMA1 not supported by controller.\n", __FUNCTION__));
+ sd->sd_dma_mode = DMA_MODE_NONE;
+ }
+ break;
+ case DMA_MODE_ADMA2:
+ if (GFIELD(sd->caps, CAP_ADMA2)) {
+ sd->sd_dma_mode = DMA_MODE_ADMA2;
+ dma_sel_bits = SDIOH_ADMA2_MODE;
+ } else {
+ sd_err(("%s: ADMA2 not supported by controller.\n", __FUNCTION__));
+ sd->sd_dma_mode = DMA_MODE_NONE;
+ }
+ break;
+ case DMA_MODE_ADMA2_64:
+ sd_err(("%s: 64b ADMA2 not supported by driver.\n", __FUNCTION__));
+ sd->sd_dma_mode = DMA_MODE_NONE;
+ break;
+ default:
+ sd_err(("%s: Unsupported DMA Mode %d requested.\n", __FUNCTION__,
+ prev_dma_mode));
+ sd->sd_dma_mode = DMA_MODE_NONE;
+ break;
+ }
+
+ /* clear SysAddr, only used for SDMA */
+ sdstd_wreg(sd, SD_SysAddr, 0);
+
+ sd_err(("%s: %s mode selected.\n", __FUNCTION__, dma_mode_description[sd->sd_dma_mode]));
+
+ reg8 = sdstd_rreg8(sd, SD_HostCntrl);
+ reg8 = SFIELD(reg8, HOST_DMA_SEL, dma_sel_bits);
+ sdstd_wreg8(sd, SD_HostCntrl, reg8);
+ sd_dma(("%s: SD_HostCntrl=0x%02x\n", __FUNCTION__, reg8));
+
+ return BCME_OK;
+}
+
+
+bool
+sdstd_start_clock(sdioh_info_t *sd, uint16 new_sd_divisor)
+{
+ uint rc, count;
+ uint16 divisor;
+
+ /* turn off HC clock */
+ sdstd_wreg16(sd, SD_ClockCntrl,
+ sdstd_rreg16(sd, SD_ClockCntrl) & ~((uint16)0x4)); /* Disable the HC clock */
+
+ /* Set divisor */
+
+ divisor = (new_sd_divisor >> 1) << 8;
+
+ sd_info(("Clock control is 0x%x\n", sdstd_rreg16(sd, SD_ClockCntrl)));
+ sdstd_mod_reg16(sd, SD_ClockCntrl, 0xff00, divisor);
+ sd_info(("%s: Using clock divisor of %d (regval 0x%04x)\n", __FUNCTION__,
+ new_sd_divisor, divisor));
+
+ sd_info(("Primary Clock Freq = %d MHz\n", GFIELD(sd->caps, CAP_TO_CLKFREQ)));
+
+ if (GFIELD(sd->caps, CAP_TO_CLKFREQ) == 50) {
+ sd_info(("%s: Resulting SDIO clock is %d %s\n", __FUNCTION__,
+ ((50 % new_sd_divisor) ? (50000 / new_sd_divisor) : (50 / new_sd_divisor)),
+ ((50 % new_sd_divisor) ? "KHz" : "MHz")));
+ } else if (GFIELD(sd->caps, CAP_TO_CLKFREQ) == 48) {
+ sd_info(("%s: Resulting SDIO clock is %d %s\n", __FUNCTION__,
+ ((48 % new_sd_divisor) ? (48000 / new_sd_divisor) : (48 / new_sd_divisor)),
+ ((48 % new_sd_divisor) ? "KHz" : "MHz")));
+ } else if (GFIELD(sd->caps, CAP_TO_CLKFREQ) == 33) {
+ sd_info(("%s: Resulting SDIO clock is %d %s\n", __FUNCTION__,
+ ((33 % new_sd_divisor) ? (33000 / new_sd_divisor) : (33 / new_sd_divisor)),
+ ((33 % new_sd_divisor) ? "KHz" : "MHz")));
+
+ } else if (sd->controller_type == SDIOH_TYPE_BCM27XX) {
+ } else {
+ sd_err(("Need to determine divisor for %d MHz clocks\n",
+ GFIELD(sd->caps, CAP_TO_CLKFREQ)));
+ sd_err(("Consult SD Host Controller Spec: Clock Control Register\n"));
+ return (FALSE);
+ }
+
+ sdstd_or_reg16(sd, SD_ClockCntrl, 0x1); /* Enable the clock */
+
+ /* Wait for clock to stabilize */
+ rc = (sdstd_rreg16(sd, SD_ClockCntrl) & 2);
+ count = 0;
+ while (!rc) {
+ OSL_DELAY(1);
+ sd_info(("Waiting for clock to become stable 0x%x\n", rc));
+ rc = (sdstd_rreg16(sd, SD_ClockCntrl) & 2);
+ count++;
+ if (count > 10000) {
+ sd_err(("%s:Clocks failed to stabilize after %u attempts",
+ __FUNCTION__, count));
+ return (FALSE);
+ }
+ }
+ /* Turn on clock */
+ sdstd_or_reg16(sd, SD_ClockCntrl, 0x4);
+
+ /* Set timeout control (adjust default value based on divisor).
+ * Disabling timeout interrupts during setting is advised by host spec.
+ */
+ {
+ uint16 regdata;
+ uint toval;
+
+ toval = sd_toctl;
+ divisor = new_sd_divisor;
+
+ while (toval && !(divisor & 1)) {
+ toval -= 1;
+ divisor >>= 1;
+ }
+
+ regdata = sdstd_rreg16(sd, SD_ErrorIntrStatusEnable);
+ sdstd_wreg16(sd, SD_ErrorIntrStatusEnable, (regdata & ~ERRINT_DATA_TIMEOUT_BIT));
+ sdstd_wreg8(sd, SD_TimeoutCntrl, (uint8)toval);
+ sdstd_wreg16(sd, SD_ErrorIntrStatusEnable, regdata);
+ }
+
+ OSL_DELAY(2);
+
+ sd_info(("Final Clock control is 0x%x\n", sdstd_rreg16(sd, SD_ClockCntrl)));
+
+ return TRUE;
+}
+
+bool
+sdstd_start_power(sdioh_info_t *sd)
+{
+ char *s;
+ uint32 cmd_arg;
+ uint32 cmd_rsp;
+ uint8 pwr = 0;
+ int volts;
+
+ volts = 0;
+ s = NULL;
+ if (GFIELD(sd->caps, CAP_VOLT_1_8)) {
+ volts = 5;
+ s = "1.8";
+ }
+ if (GFIELD(sd->caps, CAP_VOLT_3_0)) {
+ volts = 6;
+ s = "3.0";
+ }
+ if (GFIELD(sd->caps, CAP_VOLT_3_3)) {
+ volts = 7;
+ s = "3.3";
+ }
+
+ pwr = SFIELD(pwr, PWR_VOLTS, volts);
+ pwr = SFIELD(pwr, PWR_BUS_EN, 1);
+ sdstd_wreg8(sd, SD_PwrCntrl, pwr); /* Set Voltage level */
+ sd_info(("Setting Bus Power to %s Volts\n", s));
+
+ /* Wait for power to stabilize, Dongle takes longer than NIC. */
+ OSL_DELAY(250000);
+
+ /* Get the Card's Operation Condition. Occasionally the board
+ * takes a while to become ready
+ */
+ cmd_arg = 0;
+ cmd_rsp = 0;
+ if (get_ocr(sd, &cmd_arg, &cmd_rsp) != SUCCESS) {
+ sd_err(("%s: Failed to get OCR bailing\n", __FUNCTION__));
+ sdstd_reset(sd, 0, 1);
+ return FALSE;
+ }
+
+ sd_info(("mem_present = %d\n", GFIELD(cmd_rsp, RSP4_MEM_PRESENT)));
+ sd_info(("num_funcs = %d\n", GFIELD(cmd_rsp, RSP4_NUM_FUNCS)));
+ sd_info(("card_ready = %d\n", GFIELD(cmd_rsp, RSP4_CARD_READY)));
+ sd_info(("OCR = 0x%x\n", GFIELD(cmd_rsp, RSP4_IO_OCR)));
+
+ /* Verify that the card supports I/O mode */
+ if (GFIELD(cmd_rsp, RSP4_NUM_FUNCS) == 0) {
+ sd_err(("%s: Card does not support I/O\n", __FUNCTION__));
+ return ERROR;
+ }
+ sd->num_funcs = GFIELD(cmd_rsp, RSP4_NUM_FUNCS);
+
+ /* Examine voltage: Arasan only supports 3.3 volts,
+ * so look for 3.2-3.3 Volts and also 3.3-3.4 volts.
+ */
+
+ if ((GFIELD(cmd_rsp, RSP4_IO_OCR) & (0x3 << 20)) == 0) {
+ sd_err(("This client does not support 3.3 volts!\n"));
+ return ERROR;
+ }
+ sd_info(("Leaving bus power at 3.3 Volts\n"));
+
+ cmd_arg = SFIELD(0, CMD5_OCR, 0xfff000);
+ cmd_rsp = 0;
+ get_ocr(sd, &cmd_arg, &cmd_rsp);
+ sd_info(("OCR = 0x%x\n", GFIELD(cmd_rsp, RSP4_IO_OCR)));
+ return TRUE;
+}
+
+bool
+sdstd_bus_width(sdioh_info_t *sd, int new_mode)
+{
+ uint32 regdata;
+ int status;
+ uint8 reg8;
+
+ sd_trace(("%s\n", __FUNCTION__));
+ if (sd->sd_mode == new_mode) {
+ sd_info(("%s: Already at width %d\n", __FUNCTION__, new_mode));
+ /* Could exit, but continue just in case... */
+ }
+
+ /* Set client side via reg 0x7 in CCCR */
+ if ((status = sdstd_card_regread (sd, 0, SDIOD_CCCR_BICTRL, 1, &regdata)) != SUCCESS)
+ return (bool)status;
+ regdata &= ~BUS_SD_DATA_WIDTH_MASK;
+ if (new_mode == SDIOH_MODE_SD4) {
+ sd_info(("Changing to SD4 Mode\n"));
+ regdata |= SD4_MODE;
+ } else if (new_mode == SDIOH_MODE_SD1) {
+ sd_info(("Changing to SD1 Mode\n"));
+ } else {
+ sd_err(("SPI Mode not supported by Standard Host Controller\n"));
+ }
+
+ if ((status = sdstd_card_regwrite (sd, 0, SDIOD_CCCR_BICTRL, 1, regdata)) != SUCCESS)
+ return (bool)status;
+
+ /* Set host side via Host reg */
+ reg8 = sdstd_rreg8(sd, SD_HostCntrl) & ~SD4_MODE;
+ if (new_mode == SDIOH_MODE_SD4)
+ reg8 |= SD4_MODE;
+ sdstd_wreg8(sd, SD_HostCntrl, reg8);
+
+ sd->sd_mode = new_mode;
+
+ return TRUE;
+}
+
+static int
+sdstd_driver_init(sdioh_info_t *sd)
+{
+ sd_trace(("%s\n", __FUNCTION__));
+ if ((sdstd_host_init(sd)) != SUCCESS) {
+ return ERROR;
+ }
+
+ if (sdstd_client_init(sd) != SUCCESS) {
+ return ERROR;
+ }
+
+ return SUCCESS;
+}
+
+static int
+sdstd_get_cisaddr(sdioh_info_t *sd, uint32 regaddr)
+{
+ /* read 24 bits and return valid 17 bit addr */
+ int i;
+ uint32 scratch, regdata;
+ uint8 *ptr = (uint8 *)&scratch;
+ for (i = 0; i < 3; i++) {
+ if ((sdstd_card_regread (sd, 0, regaddr, 1, &regdata)) != SUCCESS)
+ sd_err(("%s: Can't read!\n", __FUNCTION__));
+
+ *ptr++ = (uint8) regdata;
+ regaddr++;
+ }
+ /* Only the lower 17-bits are valid */
+ scratch = ltoh32(scratch);
+ scratch &= 0x0001FFFF;
+ return (scratch);
+}
+
+static int
+sdstd_card_enablefuncs(sdioh_info_t *sd)
+{
+ int status;
+ uint32 regdata;
+ uint32 fbraddr;
+ uint8 func;
+
+ sd_trace(("%s\n", __FUNCTION__));
+
+ /* Get the Card's common CIS address */
+ sd->com_cis_ptr = sdstd_get_cisaddr(sd, SDIOD_CCCR_CISPTR_0);
+ sd->func_cis_ptr[0] = sd->com_cis_ptr;
+ sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
+
+ /* Get the Card's function CIS (for each function) */
+ for (fbraddr = SDIOD_FBR_STARTADDR, func = 1;
+ func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
+ sd->func_cis_ptr[func] = sdstd_get_cisaddr(sd, SDIOD_FBR_CISPTR_0 + fbraddr);
+ sd_info(("%s: Function %d CIS Ptr = 0x%x\n",
+ __FUNCTION__, func, sd->func_cis_ptr[func]));
+ }
+
+ /* Enable function 1 on the card */
+ regdata = SDIO_FUNC_ENABLE_1;
+ if ((status = sdstd_card_regwrite(sd, 0, SDIOD_CCCR_IOEN, 1, regdata)) != SUCCESS)
+ return status;
+
+ return SUCCESS;
+}
+
+/* Read client card reg */
+static int
+sdstd_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+ int status;
+ uint32 cmd_arg;
+ uint32 rsp5;
+
+
+ cmd_arg = 0;
+
+ if ((func == 0) || (regsize == 1)) {
+ cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, SDIOH_XFER_TYPE_READ);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+ cmd_arg = SFIELD(cmd_arg, CMD52_DATA, 0);
+
+ if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_52, cmd_arg))
+ != SUCCESS)
+ return status;
+
+ sdstd_cmd_getrsp(sd, &rsp5, 1);
+ if (sdstd_rreg16(sd, SD_ErrorIntrStatus) != 0) {
+ sd_err(("%s: 1: ErrorintrStatus 0x%x\n",
+ __FUNCTION__, sdstd_rreg16(sd, SD_ErrorIntrStatus)));
+ }
+
+ if (GFIELD(rsp5, RSP5_FLAGS) != 0x10)
+ sd_err(("%s: rsp5 flags is 0x%x\t %d\n",
+ __FUNCTION__, GFIELD(rsp5, RSP5_FLAGS), func));
+
+ if (GFIELD(rsp5, RSP5_STUFF))
+ sd_err(("%s: rsp5 stuff is 0x%x: should be 0\n",
+ __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+ *data = GFIELD(rsp5, RSP5_DATA);
+ } else {
+ cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, regsize);
+ cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+ cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+ cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_READ);
+
+ sd->data_xfer_count = regsize;
+
+ /* sdstd_cmd_issue() returns with the command complete bit
+ * in the ISR already cleared
+ */
+ if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_53, cmd_arg))
+ != SUCCESS)
+ return status;
+
+ sdstd_cmd_getrsp(sd, &rsp5, 1);
+
+ if (GFIELD(rsp5, RSP5_FLAGS) != 0x10)
+ sd_err(("%s: rsp5 flags is 0x%x\t %d\n",
+ __FUNCTION__, GFIELD(rsp5, RSP5_FLAGS), func));
+
+ if (GFIELD(rsp5, RSP5_STUFF))
+ sd_err(("%s: rsp5 stuff is 0x%x: should be 0\n",
+ __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+
+ if (sd->polled_mode) {
+ volatile uint16 int_reg;
+ int retries = RETRIES_LARGE;
+
+ /* Wait for Read Buffer to become ready */
+ do {
+ int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+ } while (--retries && (GFIELD(int_reg, INTSTAT_BUF_READ_READY) == 0));
+
+ if (!retries) {
+ sd_err(("%s: Timeout on Buf_Read_Ready: "
+ "intStat: 0x%x errint: 0x%x PresentState 0x%x\n",
+ __FUNCTION__, int_reg,
+ sdstd_rreg16(sd, SD_ErrorIntrStatus),
+ sdstd_rreg(sd, SD_PresentState)));
+ sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg);
+ return (ERROR);
+ }
+
+ /* Have Buffer Ready, so clear it and read the data */
+ sdstd_wreg16(sd, SD_IntrStatus, SFIELD(0, INTSTAT_BUF_READ_READY, 1));
+ if (regsize == 2)
+ *data = sdstd_rreg16(sd, SD_BufferDataPort0);
+ else
+ *data = sdstd_rreg(sd, SD_BufferDataPort0);
+
+ /* Check Status.
+ * After the data is read, the Transfer Complete bit should be on
+ */
+ retries = RETRIES_LARGE;
+ do {
+ int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+ } while (--retries && (GFIELD(int_reg, INTSTAT_XFER_COMPLETE) == 0));
+
+ /* Check for any errors from the data phase */
+ if (sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg))
+ return ERROR;
+
+ if (!retries) {
+ sd_err(("%s: Timeout on xfer complete: "
+ "intr 0x%04x err 0x%04x state 0x%08x\n",
+ __FUNCTION__, int_reg,
+ sdstd_rreg16(sd, SD_ErrorIntrStatus),
+ sdstd_rreg(sd, SD_PresentState)));
+ return (ERROR);
+ }
+
+ sdstd_wreg16(sd, SD_IntrStatus, SFIELD(0, INTSTAT_XFER_COMPLETE, 1));
+ }
+ }
+ if (sd->polled_mode) {
+ if (regsize == 2)
+ *data &= 0xffff;
+ }
+ return SUCCESS;
+}
+
+bool
+check_client_intr(sdioh_info_t *sd)
+{
+ uint16 raw_int, cur_int, old_int;
+
+ raw_int = sdstd_rreg16(sd, SD_IntrStatus);
+ cur_int = raw_int & sd->intmask;
+
+ if (!cur_int) {
+ /* Not an error -- might share interrupts... */
+ return FALSE;
+ }
+
+ if (GFIELD(cur_int, INTSTAT_CARD_INT)) {
+ old_int = sdstd_rreg16(sd, SD_IntrStatusEnable);
+ sdstd_wreg16(sd, SD_IntrStatusEnable, SFIELD(old_int, INTSTAT_CARD_INT, 0));
+
+ if (sd->client_intr_enabled && sd->use_client_ints) {
+ sd->intrcount++;
+ ASSERT(sd->intr_handler);
+ ASSERT(sd->intr_handler_arg);
+ (sd->intr_handler)(sd->intr_handler_arg);
+ } else {
+ sd_err(("%s: Not ready for intr: enabled %d, handler %p\n",
+ __FUNCTION__, sd->client_intr_enabled, sd->intr_handler));
+ }
+ sdstd_wreg16(sd, SD_IntrStatusEnable, old_int);
+ } else {
+ /* Local interrupt: disable, set flag, and save intrstatus */
+ sdstd_wreg16(sd, SD_IntrSignalEnable, 0);
+ sdstd_wreg16(sd, SD_ErrorIntrSignalEnable, 0);
+ sd->local_intrcount++;
+ sd->got_hcint = TRUE;
+ sd->last_intrstatus = cur_int;
+ }
+
+ return TRUE;
+}
+
+void
+sdstd_spinbits(sdioh_info_t *sd, uint16 norm, uint16 err)
+{
+ uint16 int_reg, err_reg;
+ int retries = RETRIES_LARGE;
+
+ do {
+ int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+ err_reg = sdstd_rreg16(sd, SD_ErrorIntrStatus);
+ } while (--retries && !(int_reg & norm) && !(err_reg & err));
+
+ norm |= sd->intmask;
+ if (err_reg & err)
+ norm = SFIELD(norm, INTSTAT_ERROR_INT, 1);
+ sd->last_intrstatus = int_reg & norm;
+}
+
+/* write a client register */
+static int
+sdstd_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data)
+{
+ int status;
+ uint32 cmd_arg, rsp5, flags;
+
+ cmd_arg = 0;
+
+ if ((func == 0) || (regsize == 1)) {
+ cmd_arg = SFIELD(cmd_arg, CMD52_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD52_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+ cmd_arg = SFIELD(cmd_arg, CMD52_RAW, 0);
+ cmd_arg = SFIELD(cmd_arg, CMD52_DATA, data & 0xff);
+ if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_52, cmd_arg))
+ != SUCCESS)
+ return status;
+
+ sdstd_cmd_getrsp(sd, &rsp5, 1);
+ flags = GFIELD(rsp5, RSP5_FLAGS);
+ if (flags && (flags != 0x10))
+ sd_err(("%s: rsp5.rsp5.flags = 0x%x, expecting 0x10\n",
+ __FUNCTION__, flags));
+ }
+ else {
+ cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, regsize);
+ cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+ cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+ cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+
+ sd->data_xfer_count = regsize;
+
+ /* sdstd_cmd_issue() returns with the command complete bit
+ * in the ISR already cleared
+ */
+ if ((status = sdstd_cmd_issue(sd, USE_DMA(sd), SDIOH_CMD_53, cmd_arg))
+ != SUCCESS)
+ return status;
+
+ sdstd_cmd_getrsp(sd, &rsp5, 1);
+
+ if (GFIELD(rsp5, RSP5_FLAGS) != 0x10)
+ sd_err(("%s: rsp5 flags = 0x%x, expecting 0x10\n",
+ __FUNCTION__, GFIELD(rsp5, RSP5_FLAGS)));
+ if (GFIELD(rsp5, RSP5_STUFF))
+ sd_err(("%s: rsp5 stuff is 0x%x: expecting 0\n",
+ __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+
+ if (sd->polled_mode) {
+ uint16 int_reg;
+ int retries = RETRIES_LARGE;
+
+ /* Wait for Write Buffer to become ready */
+ do {
+ int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+ } while (--retries && (GFIELD(int_reg, INTSTAT_BUF_WRITE_READY) == 0));
+
+ if (!retries) {
+ sd_err(("%s: Timeout on Buf_Write_Ready: intStat: 0x%x "
+ "errint: 0x%x PresentState 0x%x\n",
+ __FUNCTION__, int_reg,
+ sdstd_rreg16(sd, SD_ErrorIntrStatus),
+ sdstd_rreg(sd, SD_PresentState)));
+ sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg);
+ return (ERROR);
+ }
+ /* Clear Write Buf Ready bit */
+ int_reg = 0;
+ int_reg = SFIELD(int_reg, INTSTAT_BUF_WRITE_READY, 1);
+ sdstd_wreg16(sd, SD_IntrStatus, int_reg);
+
+ /* At this point we have Buffer Ready, so write the data */
+ if (regsize == 2)
+ sdstd_wreg16(sd, SD_BufferDataPort0, (uint16) data);
+ else
+ sdstd_wreg(sd, SD_BufferDataPort0, data);
+
+ /* Wait for Transfer Complete */
+ retries = RETRIES_LARGE;
+ do {
+ int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+ } while (--retries && (GFIELD(int_reg, INTSTAT_XFER_COMPLETE) == 0));
+
+ /* Check for any errors from the data phase */
+ if (sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg))
+ return ERROR;
+
+ if (retries == 0) {
+ sd_err(("%s: Timeout for xfer complete; State = 0x%x, "
+ "intr state=0x%x, Errintstatus 0x%x rcnt %d, tcnt %d\n",
+ __FUNCTION__, sdstd_rreg(sd, SD_PresentState),
+ int_reg, sdstd_rreg16(sd, SD_ErrorIntrStatus),
+ sd->r_cnt, sd->t_cnt));
+ }
+ /* Clear the status bits */
+ sdstd_wreg16(sd, SD_IntrStatus, SFIELD(int_reg, INTSTAT_CARD_INT, 0));
+ }
+ }
+ return SUCCESS;
+}
+
+void
+sdstd_cmd_getrsp(sdioh_info_t *sd, uint32 *rsp_buffer, int count /* num 32 bit words */)
+{
+ int rsp_count;
+ int respaddr = SD_Response0;
+
+ if (count > 4)
+ count = 4;
+
+ for (rsp_count = 0; rsp_count < count; rsp_count++) {
+ *rsp_buffer++ = sdstd_rreg(sd, respaddr);
+ respaddr += 4;
+ }
+}
+
+static int
+sdstd_cmd_issue(sdioh_info_t *sdioh_info, bool use_dma, uint32 cmd, uint32 arg)
+{
+ uint16 cmd_reg;
+ int retries;
+ uint32 cmd_arg;
+ uint16 xfer_reg = 0;
+
+
+ if ((sdioh_info->sd_mode == SDIOH_MODE_SPI) &&
+ ((cmd == SDIOH_CMD_3) || (cmd == SDIOH_CMD_7) || (cmd == SDIOH_CMD_15))) {
+ sd_err(("%s: Cmd %d is not for SPI\n", __FUNCTION__, cmd));
+ return ERROR;
+ }
+
+ retries = RETRIES_SMALL;
+ while ((GFIELD(sdstd_rreg(sdioh_info, SD_PresentState), PRES_CMD_INHIBIT)) && --retries) {
+ if (retries == RETRIES_SMALL)
+ sd_err(("%s: Waiting for Command Inhibit cmd = %d 0x%x\n",
+ __FUNCTION__, cmd, sdstd_rreg(sdioh_info, SD_PresentState)));
+ }
+ if (!retries) {
+ sd_err(("%s: Command Inhibit timeout\n", __FUNCTION__));
+ if (trap_errs)
+ ASSERT(0);
+ return ERROR;
+ }
+
+
+ cmd_reg = 0;
+ switch (cmd) {
+ case SDIOH_CMD_0: /* Set Card to Idle State - No Response */
+ sd_data(("%s: CMD%d\n", __FUNCTION__, cmd));
+ cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_NONE);
+ cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+ break;
+
+ case SDIOH_CMD_3: /* Ask card to send RCA - Response R6 */
+ sd_data(("%s: CMD%d\n", __FUNCTION__, cmd));
+ cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48);
+ cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+ break;
+
+ case SDIOH_CMD_5: /* Send Operation condition - Response R4 */
+ sd_data(("%s: CMD%d\n", __FUNCTION__, cmd));
+ cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48);
+ cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+ break;
+
+ case SDIOH_CMD_7: /* Select card - Response R1 */
+ sd_data(("%s: CMD%d\n", __FUNCTION__, cmd));
+ cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48);
+ cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 1);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 1);
+ cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+ break;
+
+ case SDIOH_CMD_15: /* Set card to inactive state - Response None */
+ sd_data(("%s: CMD%d\n", __FUNCTION__, cmd));
+ cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_NONE);
+ cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+ break;
+
+ case SDIOH_CMD_52: /* IO R/W Direct (single byte) - Response R5 */
+
+ sd_data(("%s: CMD52 func(%d) addr(0x%x) %s data(0x%x)\n",
+ __FUNCTION__,
+ GFIELD(arg, CMD52_FUNCTION),
+ GFIELD(arg, CMD52_REG_ADDR),
+ GFIELD(arg, CMD52_RW_FLAG) ? "W" : "R",
+ GFIELD(arg, CMD52_DATA)));
+
+ cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48);
+ cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 1);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 1);
+ cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+ break;
+
+ case SDIOH_CMD_53: /* IO R/W Extended (multiple bytes/blocks) */
+
+ sd_data(("%s: CMD53 func(%d) addr(0x%x) %s mode(%s) cnt(%d), %s\n",
+ __FUNCTION__,
+ GFIELD(arg, CMD53_FUNCTION),
+ GFIELD(arg, CMD53_REG_ADDR),
+ GFIELD(arg, CMD53_RW_FLAG) ? "W" : "R",
+ GFIELD(arg, CMD53_BLK_MODE) ? "Block" : "Byte",
+ GFIELD(arg, CMD53_BYTE_BLK_CNT),
+ GFIELD(arg, CMD53_OP_CODE) ? "Incrementing addr" : "Single addr"));
+
+ cmd_arg = arg;
+ xfer_reg = 0;
+
+ cmd_reg = SFIELD(cmd_reg, CMD_RESP_TYPE, RESP_TYPE_48);
+ cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 1);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 1);
+ cmd_reg = SFIELD(cmd_reg, CMD_DATA_EN, 1);
+ cmd_reg = SFIELD(cmd_reg, CMD_TYPE, CMD_TYPE_NORMAL);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX, cmd);
+
+ use_dma = USE_DMA(sdioh_info) && GFIELD(cmd_arg, CMD53_BLK_MODE);
+
+ if (GFIELD(cmd_arg, CMD53_BLK_MODE)) {
+ uint16 blocksize;
+ uint16 blockcount;
+ int func;
+
+ ASSERT(sdioh_info->sd_blockmode);
+
+ func = GFIELD(cmd_arg, CMD53_FUNCTION);
+ blocksize = MIN((int)sdioh_info->data_xfer_count,
+ sdioh_info->client_block_size[func]);
+ blockcount = GFIELD(cmd_arg, CMD53_BYTE_BLK_CNT);
+
+ /* data_xfer_cnt is already setup so that for multiblock mode,
+ * it is the entire buffer length. For non-block or single block,
+ * it is < 64 bytes
+ */
+ if (use_dma) {
+ switch (sdioh_info->sd_dma_mode) {
+ case DMA_MODE_SDMA:
+ sd_dma(("%s: SDMA: SysAddr reg was 0x%x now 0x%x\n",
+ __FUNCTION__, sdstd_rreg(sdioh_info, SD_SysAddr),
+ (uint32)sdioh_info->dma_phys));
+ sdstd_wreg(sdioh_info, SD_SysAddr, sdioh_info->dma_phys);
+ break;
+ case DMA_MODE_ADMA1:
+ case DMA_MODE_ADMA2:
+ sd_dma(("%s: ADMA: Using ADMA\n", __FUNCTION__));
+ sd_create_adma_descriptor(sdioh_info, 0,
+ sdioh_info->dma_phys, blockcount*blocksize,
+ ADMA2_ATTRIBUTE_VALID | ADMA2_ATTRIBUTE_END |
+ ADMA2_ATTRIBUTE_INT | ADMA2_ATTRIBUTE_ACT_TRAN);
+ /* Dump descriptor if DMA debugging is enabled. */
+ if (sd_msglevel & SDH_DMA_VAL) {
+ sd_dump_adma_dscr(sdioh_info);
+ }
+
+ sdstd_wreg(sdioh_info, SD_ADMA_SysAddr,
+ sdioh_info->adma2_dscr_phys);
+ break;
+ default:
+ sd_err(("%s: unsupported DMA mode %d.\n",
+ __FUNCTION__, sdioh_info->sd_dma_mode));
+ break;
+ }
+ }
+
+ sd_trace(("%s: Setting block count %d, block size %d bytes\n",
+ __FUNCTION__, blockcount, blocksize));
+ sdstd_wreg16(sdioh_info, SD_BlockSize, blocksize);
+ sdstd_wreg16(sdioh_info, SD_BlockCount, blockcount);
+
+ xfer_reg = SFIELD(xfer_reg, XFER_DMA_ENABLE, use_dma);
+
+ if (sdioh_info->client_block_size[func] != blocksize)
+ set_client_block_size(sdioh_info, 1, blocksize);
+
+ if (blockcount > 1) {
+ xfer_reg = SFIELD(xfer_reg, XFER_MULTI_BLOCK, 1);
+ xfer_reg = SFIELD(xfer_reg, XFER_BLK_COUNT_EN, 1);
+ xfer_reg = SFIELD(xfer_reg, XFER_CMD_12_EN, 0);
+ } else {
+ xfer_reg = SFIELD(xfer_reg, XFER_MULTI_BLOCK, 0);
+ xfer_reg = SFIELD(xfer_reg, XFER_BLK_COUNT_EN, 0);
+ xfer_reg = SFIELD(xfer_reg, XFER_CMD_12_EN, 0);
+ }
+
+ if (GFIELD(cmd_arg, CMD53_RW_FLAG) == SDIOH_XFER_TYPE_READ)
+ xfer_reg = SFIELD(xfer_reg, XFER_DATA_DIRECTION, 1);
+ else
+ xfer_reg = SFIELD(xfer_reg, XFER_DATA_DIRECTION, 0);
+
+ retries = RETRIES_SMALL;
+ while (GFIELD(sdstd_rreg(sdioh_info, SD_PresentState),
+ PRES_DAT_INHIBIT) && --retries)
+ sd_err(("%s: Waiting for Data Inhibit cmd = %d\n",
+ __FUNCTION__, cmd));
+ if (!retries) {
+ sd_err(("%s: Data Inhibit timeout\n", __FUNCTION__));
+ if (trap_errs)
+ ASSERT(0);
+ return ERROR;
+ }
+ sdstd_wreg16(sdioh_info, SD_TransferMode, xfer_reg);
+
+ } else { /* Non block mode */
+ uint16 bytes = GFIELD(cmd_arg, CMD53_BYTE_BLK_CNT);
+ /* The byte/block count field only has 9 bits,
+ * so, to do a 512-byte bytemode transfer, this
+ * field will contain 0, but we need to tell the
+ * controller we're transferring 512 bytes.
+ */
+ if (bytes == 0) bytes = 512;
+
+ if (use_dma)
+ sdstd_wreg(sdioh_info, SD_SysAddr, sdioh_info->dma_phys);
+
+ /* PCI: Transfer Mode register 0x0c */
+ xfer_reg = SFIELD(xfer_reg, XFER_DMA_ENABLE, bytes <= 4 ? 0 : use_dma);
+ xfer_reg = SFIELD(xfer_reg, XFER_CMD_12_EN, 0);
+ if (GFIELD(cmd_arg, CMD53_RW_FLAG) == SDIOH_XFER_TYPE_READ)
+ xfer_reg = SFIELD(xfer_reg, XFER_DATA_DIRECTION, 1);
+ else
+ xfer_reg = SFIELD(xfer_reg, XFER_DATA_DIRECTION, 0);
+ /* See table 2-8 Host Controller spec ver 1.00 */
+ xfer_reg = SFIELD(xfer_reg, XFER_BLK_COUNT_EN, 0); /* Dont care */
+ xfer_reg = SFIELD(xfer_reg, XFER_MULTI_BLOCK, 0);
+
+ sdstd_wreg16(sdioh_info, SD_BlockSize, bytes);
+
+ sdstd_wreg16(sdioh_info, SD_BlockCount, 1);
+
+ retries = RETRIES_SMALL;
+ while (GFIELD(sdstd_rreg(sdioh_info, SD_PresentState),
+ PRES_DAT_INHIBIT) && --retries)
+ sd_err(("%s: Waiting for Data Inhibit cmd = %d\n",
+ __FUNCTION__, cmd));
+ if (!retries) {
+ sd_err(("%s: Data Inhibit timeout\n", __FUNCTION__));
+ if (trap_errs)
+ ASSERT(0);
+ return ERROR;
+ }
+ sdstd_wreg16(sdioh_info, SD_TransferMode, xfer_reg);
+ }
+ break;
+
+ default:
+ sd_err(("%s: Unknown command\n", __FUNCTION__));
+ return ERROR;
+ }
+
+ if (sdioh_info->sd_mode == SDIOH_MODE_SPI) {
+ cmd_reg = SFIELD(cmd_reg, CMD_CRC_EN, 0);
+ cmd_reg = SFIELD(cmd_reg, CMD_INDEX_EN, 0);
+ }
+
+ /* Setup and issue the SDIO command */
+ sdstd_wreg(sdioh_info, SD_Arg0, arg);
+ sdstd_wreg16(sdioh_info, SD_Command, cmd_reg);
+
+ /* If we are in polled mode, wait for the command to complete.
+ * In interrupt mode, return immediately. The calling function will
+ * know that the command has completed when the CMDATDONE interrupt
+ * is asserted
+ */
+ if (sdioh_info->polled_mode) {
+ uint16 int_reg = 0;
+ int retries = RETRIES_LARGE;
+
+ do {
+ int_reg = sdstd_rreg16(sdioh_info, SD_IntrStatus);
+ } while (--retries &&
+ (GFIELD(int_reg, INTSTAT_ERROR_INT) == 0) &&
+ (GFIELD(int_reg, INTSTAT_CMD_COMPLETE) == 0));
+
+ if (!retries) {
+ sd_err(("%s: CMD_COMPLETE timeout: intrStatus: 0x%x "
+ "error stat 0x%x state 0x%x\n",
+ __FUNCTION__, int_reg,
+ sdstd_rreg16(sdioh_info, SD_ErrorIntrStatus),
+ sdstd_rreg(sdioh_info, SD_PresentState)));
+
+ /* Attempt to reset CMD line when we get a CMD timeout */
+ sdstd_wreg8(sdioh_info, SD_SoftwareReset, SFIELD(0, SW_RESET_CMD, 1));
+ retries = RETRIES_LARGE;
+ do {
+ sd_trace(("%s: waiting for CMD line reset\n", __FUNCTION__));
+ } while ((GFIELD(sdstd_rreg8(sdioh_info, SD_SoftwareReset),
+ SW_RESET_CMD)) && retries--);
+
+ if (!retries) {
+ sd_err(("%s: Timeout waiting for CMD line reset\n", __FUNCTION__));
+ }
+
+ if (trap_errs)
+ ASSERT(0);
+ return (ERROR);
+ }
+
+ /* Clear Command Complete interrupt */
+ int_reg = SFIELD(0, INTSTAT_CMD_COMPLETE, 1);
+ sdstd_wreg16(sdioh_info, SD_IntrStatus, int_reg);
+
+ /* Check for Errors */
+ if (sdstd_check_errs(sdioh_info, cmd, arg)) {
+ if (trap_errs)
+ ASSERT(0);
+ return ERROR;
+ }
+ }
+ return SUCCESS;
+}
+
+
+static int
+sdstd_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo, uint32 addr, int nbytes, uint32 *data)
+{
+ int status;
+ uint32 cmd_arg;
+ uint32 rsp5;
+ uint16 int_reg, int_bit;
+ uint flags;
+ int num_blocks, blocksize;
+ bool local_blockmode, local_dma;
+ bool read = rw == SDIOH_READ ? 1 : 0;
+ bool yield = FALSE;
+
+ ASSERT(nbytes);
+
+ cmd_arg = 0;
+
+ sd_data(("%s: %s 53 addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+ __FUNCTION__, read ? "Rd" : "Wr", addr, nbytes, sd->r_cnt, sd->t_cnt));
+
+ if (read) sd->r_cnt++; else sd->t_cnt++;
+
+ local_blockmode = sd->sd_blockmode;
+ local_dma = USE_DMA(sd);
+
+ /* Don't bother with block mode on small xfers */
+ if (nbytes < sd->client_block_size[func]) {
+ sd_data(("setting local blockmode to false: nbytes (%d) != block_size (%d)\n",
+ nbytes, sd->client_block_size[func]));
+ local_blockmode = FALSE;
+ local_dma = FALSE;
+ }
+
+ if (local_blockmode) {
+ blocksize = MIN(sd->client_block_size[func], nbytes);
+ num_blocks = nbytes/blocksize;
+ cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, num_blocks);
+ cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 1);
+ } else {
+ num_blocks = 1;
+ blocksize = nbytes;
+ cmd_arg = SFIELD(cmd_arg, CMD53_BYTE_BLK_CNT, nbytes);
+ cmd_arg = SFIELD(cmd_arg, CMD53_BLK_MODE, 0);
+ }
+
+ if (local_dma && !read) {
+ bcopy(data, sd->dma_buf, nbytes);
+ sd_sync_dma(sd, read, nbytes);
+ }
+
+ if (fifo)
+ cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 0);
+ else
+ cmd_arg = SFIELD(cmd_arg, CMD53_OP_CODE, 1);
+
+ cmd_arg = SFIELD(cmd_arg, CMD53_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, CMD53_REG_ADDR, addr);
+ if (read)
+ cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_READ);
+ else
+ cmd_arg = SFIELD(cmd_arg, CMD53_RW_FLAG, SDIOH_XFER_TYPE_WRITE);
+
+ sd->data_xfer_count = nbytes;
+
+ /* sdstd_cmd_issue() returns with the command complete bit
+ * in the ISR already cleared
+ */
+ if ((status = sdstd_cmd_issue(sd, local_dma, SDIOH_CMD_53, cmd_arg)) != SUCCESS) {
+ sd_err(("%s: cmd_issue failed for %s\n", __FUNCTION__, (read ? "read" : "write")));
+ return status;
+ }
+
+ sdstd_cmd_getrsp(sd, &rsp5, 1);
+
+ if ((flags = GFIELD(rsp5, RSP5_FLAGS)) != 0x10) {
+ sd_err(("%s: Rsp5: nbytes %d, dma %d blockmode %d, read %d "
+ "numblocks %d, blocksize %d\n",
+ __FUNCTION__, nbytes, local_dma, local_dma, read, num_blocks, blocksize));
+
+ if (flags & 1)
+ sd_err(("%s: rsp5: Command not accepted: arg out of range 0x%x, "
+ "bytes %d dma %d\n",
+ __FUNCTION__, flags, GFIELD(cmd_arg, CMD53_BYTE_BLK_CNT),
+ GFIELD(cmd_arg, CMD53_BLK_MODE)));
+ if (flags & 0x8)
+ sd_err(("%s: Rsp5: General Error\n", __FUNCTION__));
+
+ sd_err(("%s: rsp5 flags = 0x%x, expecting 0x10 returning error\n",
+ __FUNCTION__, flags));
+ if (trap_errs)
+ ASSERT(0);
+ return ERROR;
+ }
+
+ if (GFIELD(rsp5, RSP5_STUFF))
+ sd_err(("%s: rsp5 stuff is 0x%x: expecting 0\n",
+ __FUNCTION__, GFIELD(rsp5, RSP5_STUFF)));
+
+#ifdef BCMSDYIELD
+ yield = sd_yieldcpu && ((uint)nbytes >= sd_minyield);
+#endif
+
+ if (!local_dma) {
+ int bytes, i;
+ uint32 tmp;
+ for (i = 0; i < num_blocks; i++) {
+ int words;
+
+ /* Decide which status bit we're waiting for */
+ if (read)
+ int_bit = SFIELD(0, INTSTAT_BUF_READ_READY, 1);
+ else
+ int_bit = SFIELD(0, INTSTAT_BUF_WRITE_READY, 1);
+
+ /* If not on, wait for it (or for xfer error) */
+ int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+ if (!(int_reg & int_bit))
+ int_reg = sdstd_waitbits(sd, int_bit, ERRINT_TRANSFER_ERRS, yield);
+
+ /* Confirm we got the bit w/o error */
+ if (!(int_reg & int_bit) || GFIELD(int_reg, INTSTAT_ERROR_INT)) {
+ sd_err(("%s: Error or timeout for Buf_%s_Ready: intStat: 0x%x "
+ "errint: 0x%x PresentState 0x%x\n",
+ __FUNCTION__, read ? "Read" : "Write", int_reg,
+ sdstd_rreg16(sd, SD_ErrorIntrStatus),
+ sdstd_rreg(sd, SD_PresentState)));
+ sdstd_dumpregs(sd);
+ sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg);
+ return (ERROR);
+ }
+
+ /* Clear Buf Ready bit */
+ sdstd_wreg16(sd, SD_IntrStatus, int_bit);
+
+ /* At this point we have Buffer Ready, write the data 4 bytes at a time */
+ for (words = blocksize/4; words; words--) {
+ if (read)
+ *data = sdstd_rreg(sd, SD_BufferDataPort0);
+ else
+ sdstd_wreg(sd, SD_BufferDataPort0, *data);
+ data++;
+ }
+
+ bytes = blocksize % 4;
+
+ /* If no leftover bytes, go to next block */
+ if (!bytes)
+ continue;
+
+ switch (bytes) {
+ case 1:
+ /* R/W 8 bits */
+ if (read)
+ *(data++) = (uint32)(sdstd_rreg8(sd, SD_BufferDataPort0));
+ else
+ sdstd_wreg8(sd, SD_BufferDataPort0,
+ (uint8)(*(data++) & 0xff));
+ break;
+ case 2:
+ /* R/W 16 bits */
+ if (read)
+ *(data++) = (uint32)sdstd_rreg16(sd, SD_BufferDataPort0);
+ else
+ sdstd_wreg16(sd, SD_BufferDataPort0, (uint16)(*(data++)));
+ break;
+ case 3:
+ /* R/W 24 bits:
+ * SD_BufferDataPort0[0-15] | SD_BufferDataPort1[16-23]
+ */
+ if (read) {
+ tmp = (uint32)sdstd_rreg16(sd, SD_BufferDataPort0);
+ tmp |= ((uint32)(sdstd_rreg8(sd,
+ SD_BufferDataPort1)) << 16);
+ *(data++) = tmp;
+ } else {
+ tmp = *(data++);
+ sdstd_wreg16(sd, SD_BufferDataPort0, (uint16)tmp & 0xffff);
+ sdstd_wreg8(sd, SD_BufferDataPort1,
+ (uint8)((tmp >> 16) & 0xff));
+ }
+ break;
+ default:
+ sd_err(("%s: Unexpected bytes leftover %d\n",
+ __FUNCTION__, bytes));
+ ASSERT(0);
+ break;
+ }
+ }
+ } /* End PIO processing */
+
+ /* Wait for Transfer Complete or Transfer Error */
+ int_bit = SFIELD(0, INTSTAT_XFER_COMPLETE, 1);
+
+ /* If not on, wait for it (or for xfer error) */
+ int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+ if (!(int_reg & int_bit))
+ int_reg = sdstd_waitbits(sd, int_bit, ERRINT_TRANSFER_ERRS, yield);
+
+ /* Check for any errors from the data phase */
+ if (sdstd_check_errs(sd, SDIOH_CMD_53, cmd_arg))
+ return ERROR;
+
+ /* May have gotten a software timeout if not blocking? */
+ int_reg = sdstd_rreg16(sd, SD_IntrStatus);
+ if (!(int_reg & int_bit)) {
+ sd_err(("%s: Error or Timeout for xfer complete; %s, dma %d, State 0x%08x, "
+ "intr 0x%04x, Err 0x%04x, len = %d, rcnt %d, tcnt %d\n",
+ __FUNCTION__, read ? "R" : "W", local_dma,
+ sdstd_rreg(sd, SD_PresentState), int_reg,
+ sdstd_rreg16(sd, SD_ErrorIntrStatus), nbytes,
+ sd->r_cnt, sd->t_cnt));
+ sdstd_dumpregs(sd);
+ return ERROR;
+ }
+
+ /* Clear the status bits */
+ int_reg = int_bit;
+ if (local_dma) {
+ /* DMA Complete */
+ /* Reads in particular don't have DMA_COMPLETE set */
+ int_reg = SFIELD(int_reg, INTSTAT_DMA_INT, 1);
+ }
+ sdstd_wreg16(sd, SD_IntrStatus, int_reg);
+
+ /* Fetch data */
+ if (local_dma && read) {
+ sd_sync_dma(sd, read, nbytes);
+ bcopy(sd->dma_buf, data, nbytes);
+ }
+ return SUCCESS;
+}
+
+static int
+set_client_block_size(sdioh_info_t *sd, int func, int block_size)
+{
+ int base;
+ int err = 0;
+
+
+ sd_err(("%s: Setting block size %d, func %d\n", __FUNCTION__, block_size, func));
+ sd->client_block_size[func] = block_size;
+
+ /* Set the block size in the SDIO Card register */
+ base = func * SDIOD_FBR_SIZE;
+ err = sdstd_card_regwrite(sd, 0, base+SDIOD_CCCR_BLKSIZE_0, 1, block_size & 0xff);
+ if (!err) {
+ err = sdstd_card_regwrite(sd, 0, base+SDIOD_CCCR_BLKSIZE_1, 1,
+ (block_size >> 8) & 0xff);
+ }
+
+ /* Do not set the block size in the SDIO Host register, that
+ * is func dependent and will get done on an individual
+ * transaction basis
+ */
+
+ return (err ? BCME_SDIO_ERROR : 0);
+}
+
+/* Reset and re-initialize the device */
+int
+sdioh_sdio_reset(sdioh_info_t *si)
+{
+ uint8 hreg;
+
+ /* Reset the attached device (use slower clock for safety) */
+ sdstd_start_clock(si, 128);
+ sdstd_reset(si, 0, 1);
+
+ /* Reset portions of the host state accordingly */
+ hreg = sdstd_rreg8(si, SD_HostCntrl);
+ hreg = SFIELD(hreg, HOST_HI_SPEED_EN, 0);
+ hreg = SFIELD(hreg, HOST_DATA_WIDTH, 0);
+ si->sd_mode = SDIOH_MODE_SD1;
+
+ /* Reinitialize the card */
+ si->card_init_done = FALSE;
+ return sdstd_client_init(si);
+}
+
+
+static void
+sd_map_dma(sdioh_info_t * sd)
+{
+
+ void *va;
+
+ if ((va = DMA_ALLOC_CONSISTENT(sd->osh, SD_PAGE,
+ &sd->dma_start_phys, 0x12, 12)) == NULL) {
+ sd->sd_dma_mode = DMA_MODE_NONE;
+ sd->dma_start_buf = 0;
+ sd->dma_buf = (void *)0;
+ sd->dma_phys = 0;
+ sd->alloced_dma_size = SD_PAGE;
+ sd_err(("%s: DMA_ALLOC failed. Disabling DMA support.\n", __FUNCTION__));
+ } else {
+ sd->dma_start_buf = va;
+ sd->dma_buf = (void *)ROUNDUP((uintptr)va, SD_PAGE);
+ sd->dma_phys = ROUNDUP((sd->dma_start_phys), SD_PAGE);
+ sd->alloced_dma_size = SD_PAGE;
+ sd_err(("%s: Mapped DMA Buffer %dbytes @virt/phys: %p/0x%lx\n",
+ __FUNCTION__, sd->alloced_dma_size, sd->dma_buf, sd->dma_phys));
+ sd_fill_dma_data_buf(sd, 0xA5);
+ }
+
+ if ((va = DMA_ALLOC_CONSISTENT(sd->osh, SD_PAGE,
+ &sd->adma2_dscr_start_phys, 0x12, 12)) == NULL) {
+ sd->sd_dma_mode = DMA_MODE_NONE;
+ sd->adma2_dscr_start_buf = 0;
+ sd->adma2_dscr_buf = (void *)0;
+ sd->adma2_dscr_phys = 0;
+ sd->alloced_adma2_dscr_size = 0;
+ sd_err(("%s: DMA_ALLOC failed for descriptor buffer. "
+ "Disabling DMA support.\n", __FUNCTION__));
+ } else {
+ sd->adma2_dscr_start_buf = va;
+ sd->adma2_dscr_buf = (void *)ROUNDUP((uintptr)va, SD_PAGE);
+ sd->adma2_dscr_phys = ROUNDUP((sd->adma2_dscr_start_phys), SD_PAGE);
+ sd->alloced_adma2_dscr_size = SD_PAGE;
+ }
+
+ sd_err(("%s: Mapped ADMA2 Descriptor Buffer %dbytes @virt/phys: %p/0x%lx\n",
+ __FUNCTION__, sd->alloced_adma2_dscr_size, sd->adma2_dscr_buf,
+ sd->adma2_dscr_phys));
+ sd_clear_adma_dscr_buf(sd);
+}
+
+static void
+sd_unmap_dma(sdioh_info_t * sd)
+{
+ if (sd->dma_start_buf) {
+ DMA_FREE_CONSISTENT(sd->osh, sd->dma_start_buf, sd->alloced_dma_size,
+ sd->dma_start_phys, 0x12);
+ }
+
+ if (sd->adma2_dscr_start_buf) {
+ DMA_FREE_CONSISTENT(sd->osh, sd->adma2_dscr_start_buf, sd->alloced_adma2_dscr_size,
+ sd->adma2_dscr_start_phys, 0x12);
+ }
+}
+
+static void sd_clear_adma_dscr_buf(sdioh_info_t *sd)
+{
+ bzero((char *)sd->adma2_dscr_buf, SD_PAGE);
+ sd_dump_adma_dscr(sd);
+}
+
+static void sd_fill_dma_data_buf(sdioh_info_t *sd, uint8 data)
+{
+ memset((char *)sd->dma_buf, data, SD_PAGE);
+}
+
+
+static void sd_create_adma_descriptor(sdioh_info_t *sd, uint32 index,
+ uint32 addr_phys, uint16 length, uint16 flags)
+{
+ adma2_dscr_32b_t *adma2_dscr_table;
+ adma1_dscr_t *adma1_dscr_table;
+
+ adma2_dscr_table = sd->adma2_dscr_buf;
+ adma1_dscr_table = sd->adma2_dscr_buf;
+
+ switch (sd->sd_dma_mode) {
+ case DMA_MODE_ADMA2:
+ sd_dma(("%s: creating ADMA2 descriptor for index %d\n",
+ __FUNCTION__, index));
+
+ adma2_dscr_table[index].phys_addr = addr_phys;
+ adma2_dscr_table[index].len_attr = length << 16;
+ adma2_dscr_table[index].len_attr |= flags;
+ break;
+ case DMA_MODE_ADMA1:
+ /* ADMA1 requires two descriptors, one for len
+ * and the other for data transfer
+ */
+ index <<= 1;
+
+ sd_dma(("%s: creating ADMA1 descriptor for index %d\n",
+ __FUNCTION__, index));
+
+ adma1_dscr_table[index].phys_addr_attr = length << 12;
+ adma1_dscr_table[index].phys_addr_attr |= (ADMA1_ATTRIBUTE_ACT_SET |
+ ADMA2_ATTRIBUTE_VALID);
+ adma1_dscr_table[index+1].phys_addr_attr = addr_phys & 0xFFFFF000;
+ adma1_dscr_table[index+1].phys_addr_attr |= (flags & 0x3f);
+ break;
+ default:
+ sd_err(("%s: cannot create ADMA descriptor for DMA mode %d\n",
+ __FUNCTION__, sd->sd_dma_mode));
+ break;
+ }
+}
+
+
+static void sd_dump_adma_dscr(sdioh_info_t *sd)
+{
+ adma2_dscr_32b_t *adma2_dscr_table;
+ adma1_dscr_t *adma1_dscr_table;
+ uint32 i = 0;
+ uint16 flags;
+ char flags_str[32];
+
+ ASSERT(sd->adma2_dscr_buf != NULL);
+
+ adma2_dscr_table = sd->adma2_dscr_buf;
+ adma1_dscr_table = sd->adma2_dscr_buf;
+
+ switch (sd->sd_dma_mode) {
+ case DMA_MODE_ADMA2:
+ sd_err(("ADMA2 Descriptor Table (%dbytes) @virt/phys: %p/0x%lx\n",
+ SD_PAGE, sd->adma2_dscr_buf, sd->adma2_dscr_phys));
+ sd_err((" #[Descr VA ] Buffer PA | Len | Flags (5:4 2 1 0)"
+ " |\n"));
+ while (adma2_dscr_table->len_attr & ADMA2_ATTRIBUTE_VALID) {
+ flags = adma2_dscr_table->len_attr & 0xFFFF;
+ sprintf(flags_str, "%s%s%s%s",
+ ((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+ ADMA2_ATTRIBUTE_ACT_LINK) ? "LINK " :
+ ((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+ ADMA2_ATTRIBUTE_ACT_TRAN) ? "TRAN " :
+ ((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+ ADMA2_ATTRIBUTE_ACT_NOP) ? "NOP " : "RSV ",
+ (flags & ADMA2_ATTRIBUTE_INT ? "INT " : " "),
+ (flags & ADMA2_ATTRIBUTE_END ? "END " : " "),
+ (flags & ADMA2_ATTRIBUTE_VALID ? "VALID" : ""));
+ sd_err(("%2d[0x%p]: 0x%08x | 0x%04x | 0x%04x (%s) |\n",
+ i, adma2_dscr_table, adma2_dscr_table->phys_addr,
+ adma2_dscr_table->len_attr >> 16, flags, flags_str));
+ i++;
+
+ /* Follow LINK descriptors or skip to next. */
+ if ((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+ ADMA2_ATTRIBUTE_ACT_LINK) {
+ adma2_dscr_table = phys_to_virt(
+ adma2_dscr_table->phys_addr);
+ } else {
+ adma2_dscr_table++;
+ }
+
+ }
+ break;
+ case DMA_MODE_ADMA1:
+ sd_err(("ADMA1 Descriptor Table (%dbytes) @virt/phys: %p/0x%lx\n",
+ SD_PAGE, sd->adma2_dscr_buf, sd->adma2_dscr_phys));
+ sd_err((" #[Descr VA ] Buffer PA | Flags (5:4 2 1 0) |\n"));
+
+ for (i = 0; adma1_dscr_table->phys_addr_attr & ADMA2_ATTRIBUTE_VALID; i++) {
+ flags = adma1_dscr_table->phys_addr_attr & 0x3F;
+ sprintf(flags_str, "%s%s%s%s",
+ ((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+ ADMA2_ATTRIBUTE_ACT_LINK) ? "LINK " :
+ ((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+ ADMA2_ATTRIBUTE_ACT_TRAN) ? "TRAN " :
+ ((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+ ADMA2_ATTRIBUTE_ACT_NOP) ? "NOP " : "SET ",
+ (flags & ADMA2_ATTRIBUTE_INT ? "INT " : " "),
+ (flags & ADMA2_ATTRIBUTE_END ? "END " : " "),
+ (flags & ADMA2_ATTRIBUTE_VALID ? "VALID" : ""));
+ sd_err(("%2d[0x%p]: 0x%08x | 0x%04x | (%s) |\n",
+ i, adma1_dscr_table,
+ adma1_dscr_table->phys_addr_attr & 0xFFFFF000,
+ flags, flags_str));
+
+ /* Follow LINK descriptors or skip to next. */
+ if ((flags & ADMA2_ATTRIBUTE_ACT_LINK) ==
+ ADMA2_ATTRIBUTE_ACT_LINK) {
+ adma1_dscr_table = phys_to_virt(
+ adma1_dscr_table->phys_addr_attr & 0xFFFFF000);
+ } else {
+ adma1_dscr_table++;
+ }
+ }
+ break;
+ default:
+ sd_err(("Unknown DMA Descriptor Table Format.\n"));
+ break;
+ }
+}
+
+static void sdstd_dumpregs(sdioh_info_t *sd)
+{
+ sd_err(("IntrStatus: 0x%04x ErrorIntrStatus 0x%04x\n",
+ sdstd_rreg16(sd, SD_IntrStatus),
+ sdstd_rreg16(sd, SD_ErrorIntrStatus)));
+ sd_err(("IntrStatusEnable: 0x%04x ErrorIntrStatusEnable 0x%04x\n",
+ sdstd_rreg16(sd, SD_IntrStatusEnable),
+ sdstd_rreg16(sd, SD_ErrorIntrStatusEnable)));
+ sd_err(("IntrSignalEnable: 0x%04x ErrorIntrSignalEnable 0x%04x\n",
+ sdstd_rreg16(sd, SD_IntrSignalEnable),
+ sdstd_rreg16(sd, SD_ErrorIntrSignalEnable)));
+}
diff --git a/drivers/net/wireless/bcm4329/bcmsdstd_linux.c b/drivers/net/wireless/bcm4329/bcmsdstd_linux.c
new file mode 100644
index 00000000000..a8b98e2054a
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmsdstd_linux.c
@@ -0,0 +1,251 @@
+/*
+ * 'Standard' SDIO HOST CONTROLLER driver - linux portion
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdstd_linux.c,v 1.11.18.2.16.1 2010/08/17 17:03:13 Exp $
+ */
+
+#include <typedefs.h>
+#include <pcicfg.h>
+#include <bcmutils.h>
+#include <sdio.h> /* SDIO Specs */
+#include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h> /* to get msglevel bit values */
+
+#include <linux/sched.h> /* request_irq() */
+
+#include <bcmsdstd.h>
+
+struct sdos_info {
+ sdioh_info_t *sd;
+ spinlock_t lock;
+ wait_queue_head_t intr_wait_queue;
+};
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define BLOCKABLE() (!in_atomic())
+#else
+#define BLOCKABLE() (!in_interrupt())
+#endif
+
+/* Interrupt handler */
+static irqreturn_t
+sdstd_isr(int irq, void *dev_id
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+, struct pt_regs *ptregs
+#endif
+)
+{
+ sdioh_info_t *sd;
+ struct sdos_info *sdos;
+ bool ours;
+
+ sd = (sdioh_info_t *)dev_id;
+
+ if (!sd->card_init_done) {
+ sd_err(("%s: Hey Bogus intr...not even initted: irq %d\n", __FUNCTION__, irq));
+ return IRQ_RETVAL(FALSE);
+ } else {
+ ours = check_client_intr(sd);
+
+ /* For local interrupts, wake the waiting process */
+ if (ours && sd->got_hcint) {
+ sd_trace(("INTR->WAKE\n"));
+ sdos = (struct sdos_info *)sd->sdos_info;
+ wake_up_interruptible(&sdos->intr_wait_queue);
+ }
+ return IRQ_RETVAL(ours);
+ }
+}
+
+/* Register with Linux for interrupts */
+int
+sdstd_register_irq(sdioh_info_t *sd, uint irq)
+{
+ sd_trace(("Entering %s: irq == %d\n", __FUNCTION__, irq));
+ if (request_irq(irq, sdstd_isr, IRQF_SHARED, "bcmsdstd", sd) < 0) {
+ sd_err(("%s: request_irq() failed\n", __FUNCTION__));
+ return ERROR;
+ }
+ return SUCCESS;
+}
+
+/* Free Linux irq */
+void
+sdstd_free_irq(uint irq, sdioh_info_t *sd)
+{
+ free_irq(irq, sd);
+}
+
+/* Map Host controller registers */
+
+uint32 *
+sdstd_reg_map(osl_t *osh, int32 addr, int size)
+{
+ return (uint32 *)REG_MAP(addr, size);
+}
+
+void
+sdstd_reg_unmap(osl_t *osh, int32 addr, int size)
+{
+ REG_UNMAP((void*)(uintptr)addr);
+}
+
+int
+sdstd_osinit(sdioh_info_t *sd)
+{
+ struct sdos_info *sdos;
+
+ sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info));
+ sd->sdos_info = (void*)sdos;
+ if (sdos == NULL)
+ return BCME_NOMEM;
+
+ sdos->sd = sd;
+ spin_lock_init(&sdos->lock);
+ init_waitqueue_head(&sdos->intr_wait_queue);
+ return BCME_OK;
+}
+
+void
+sdstd_osfree(sdioh_info_t *sd)
+{
+ struct sdos_info *sdos;
+ ASSERT(sd && sd->sdos_info);
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+ MFREE(sd->osh, sdos, sizeof(struct sdos_info));
+}
+
+/* Interrupt enable/disable */
+SDIOH_API_RC
+sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
+{
+ ulong flags;
+ struct sdos_info *sdos;
+
+ sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+ ASSERT(sdos);
+
+ if (!(sd->host_init_done && sd->card_init_done)) {
+ sd_err(("%s: Card & Host are not initted - bailing\n", __FUNCTION__));
+ return SDIOH_API_RC_FAIL;
+ }
+
+ if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
+ sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
+ return SDIOH_API_RC_FAIL;
+ }
+
+ /* Ensure atomicity for enable/disable calls */
+ spin_lock_irqsave(&sdos->lock, flags);
+
+ sd->client_intr_enabled = enable;
+ if (enable && !sd->lockcount)
+ sdstd_devintr_on(sd);
+ else
+ sdstd_devintr_off(sd);
+
+ spin_unlock_irqrestore(&sdos->lock, flags);
+
+ return SDIOH_API_RC_SUCCESS;
+}
+
+/* Protect against reentrancy (disable device interrupts while executing) */
+void
+sdstd_lock(sdioh_info_t *sd)
+{
+ ulong flags;
+ struct sdos_info *sdos;
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+ ASSERT(sdos);
+
+ sd_trace(("%s: %d\n", __FUNCTION__, sd->lockcount));
+
+ spin_lock_irqsave(&sdos->lock, flags);
+ if (sd->lockcount) {
+ sd_err(("%s: Already locked! called from %p\n",
+ __FUNCTION__,
+ __builtin_return_address(0)));
+ ASSERT(sd->lockcount == 0);
+ }
+ sdstd_devintr_off(sd);
+ sd->lockcount++;
+ spin_unlock_irqrestore(&sdos->lock, flags);
+}
+
+/* Enable client interrupt */
+void
+sdstd_unlock(sdioh_info_t *sd)
+{
+ ulong flags;
+ struct sdos_info *sdos;
+
+ sd_trace(("%s: %d, %d\n", __FUNCTION__, sd->lockcount, sd->client_intr_enabled));
+ ASSERT(sd->lockcount > 0);
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+ ASSERT(sdos);
+
+ spin_lock_irqsave(&sdos->lock, flags);
+ if (--sd->lockcount == 0 && sd->client_intr_enabled) {
+ sdstd_devintr_on(sd);
+ }
+ spin_unlock_irqrestore(&sdos->lock, flags);
+}
+
+uint16
+sdstd_waitbits(sdioh_info_t *sd, uint16 norm, uint16 err, bool yield)
+{
+ struct sdos_info *sdos;
+
+ sdos = (struct sdos_info *)sd->sdos_info;
+
+#ifndef BCMSDYIELD
+ ASSERT(!yield);
+#endif
+ sd_trace(("%s: int 0x%02x err 0x%02x yield %d canblock %d\n",
+ __FUNCTION__, norm, err, yield, BLOCKABLE()));
+
+ /* Clear the "interrupt happened" flag and last intrstatus */
+ sd->got_hcint = FALSE;
+ sd->last_intrstatus = 0;
+
+#ifdef BCMSDYIELD
+ if (yield && BLOCKABLE()) {
+ /* Enable interrupts, wait for the indication, then disable */
+ sdstd_intrs_on(sd, norm, err);
+ wait_event_interruptible(sdos->intr_wait_queue, (sd->got_hcint));
+ sdstd_intrs_off(sd, norm, err);
+ } else
+#endif /* BCMSDYIELD */
+ {
+ sdstd_spinbits(sd, norm, err);
+ }
+
+ sd_trace(("%s: last_intrstatus 0x%04x\n", __FUNCTION__, sd->last_intrstatus));
+
+ return sd->last_intrstatus;
+}
diff --git a/drivers/net/wireless/bcm4329/bcmspibrcm.c b/drivers/net/wireless/bcm4329/bcmspibrcm.c
new file mode 100644
index 00000000000..0f131a40f4b
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmspibrcm.c
@@ -0,0 +1,1726 @@
+/*
+ * Broadcom BCMSDH to gSPI Protocol Conversion Layer
+ *
+ * Copyright (C) 2010, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
+ * the contents of this file may not be disclosed to third parties, copied
+ * or duplicated in any form, in whole or in part, without the prior
+ * written permission of Broadcom Corporation.
+ *
+ * $Id: bcmspibrcm.c,v 1.11.2.10.2.9.6.11 2009/05/21 13:21:57 Exp $
+ */
+
+#define HSMODE
+
+#include <typedefs.h>
+
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <bcmutils.h>
+#include <osl.h>
+#include <hndsoc.h>
+#include <siutils.h>
+#include <sbchipc.h>
+#include <sbsdio.h>
+#include <spid.h>
+
+#include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h> /* ioctl/iovars */
+#include <sdio.h>
+
+#include <pcicfg.h>
+
+
+#include <bcmspibrcm.h>
+#include <bcmspi.h>
+
+#define F0_RESPONSE_DELAY 16
+#define F1_RESPONSE_DELAY 16
+#define F2_RESPONSE_DELAY F0_RESPONSE_DELAY
+
+#define CMDLEN 4
+
+#define DWORDMODE_ON (sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 2) && (sd->dwordmode == TRUE)
+
+/* Globals */
+uint sd_msglevel = 0;
+
+uint sd_hiok = FALSE; /* Use hi-speed mode if available? */
+uint sd_sdmode = SDIOH_MODE_SPI; /* Use SD4 mode by default */
+uint sd_f2_blocksize = 64; /* Default blocksize */
+
+
+uint sd_divisor = 2;
+uint sd_power = 1; /* Default to SD Slot powered ON */
+uint sd_clock = 1; /* Default to SD Clock turned ON */
+uint sd_crc = 0; /* Default to SPI CRC Check turned OFF */
+
+uint8 spi_outbuf[SPI_MAX_PKT_LEN];
+uint8 spi_inbuf[SPI_MAX_PKT_LEN];
+
+/* 128bytes buffer is enough to clear data-not-available and program response-delay F0 bits
+ * assuming we will not exceed F0 response delay > 100 bytes at 48MHz.
+ */
+#define BUF2_PKT_LEN 128
+uint8 spi_outbuf2[BUF2_PKT_LEN];
+uint8 spi_inbuf2[BUF2_PKT_LEN];
+
+/* Prototypes */
+static bool bcmspi_test_card(sdioh_info_t *sd);
+static bool bcmspi_host_device_init_adapt(sdioh_info_t *sd);
+static int bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode);
+static int bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg,
+ uint32 *data, uint32 datalen);
+static int bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr,
+ int regsize, uint32 *data);
+static int bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr,
+ int regsize, uint32 data);
+static int bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr,
+ uint8 *data);
+static int bcmspi_driver_init(sdioh_info_t *sd);
+static int bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo,
+ uint32 addr, int nbytes, uint32 *data);
+static int bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize,
+ uint32 *data);
+static void bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer);
+static int bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg);
+
+/*
+ * Public entry points & extern's
+ */
+extern sdioh_info_t *
+sdioh_attach(osl_t *osh, void *bar0, uint irq)
+{
+ sdioh_info_t *sd;
+
+ sd_trace(("%s\n", __FUNCTION__));
+ if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) {
+ sd_err(("%s: out of memory, malloced %d bytes\n", __FUNCTION__, MALLOCED(osh)));
+ return NULL;
+ }
+ bzero((char *)sd, sizeof(sdioh_info_t));
+ sd->osh = osh;
+ if (spi_osinit(sd) != 0) {
+ sd_err(("%s: spi_osinit() failed\n", __FUNCTION__));
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return NULL;
+ }
+
+ sd->bar0 = bar0;
+ sd->irq = irq;
+ sd->intr_handler = NULL;
+ sd->intr_handler_arg = NULL;
+ sd->intr_handler_valid = FALSE;
+
+ /* Set defaults */
+ sd->use_client_ints = TRUE;
+ sd->sd_use_dma = FALSE; /* DMA Not supported */
+
+ /* Spi device default is 16bit mode, change to 4 when device is changed to 32bit
+ * mode
+ */
+ sd->wordlen = 2;
+
+ if (!spi_hw_attach(sd)) {
+ sd_err(("%s: spi_hw_attach() failed\n", __FUNCTION__));
+ spi_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return (NULL);
+ }
+
+ if (bcmspi_driver_init(sd) != SUCCESS) {
+ sd_err(("%s: bcmspi_driver_init() failed()\n", __FUNCTION__));
+ spi_hw_detach(sd);
+ spi_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return (NULL);
+ }
+
+ if (spi_register_irq(sd, irq) != SUCCESS) {
+ sd_err(("%s: spi_register_irq() failed for irq = %d\n", __FUNCTION__, irq));
+ spi_hw_detach(sd);
+ spi_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ return (NULL);
+ }
+
+ sd_trace(("%s: Done\n", __FUNCTION__));
+
+ return sd;
+}
+
+extern SDIOH_API_RC
+sdioh_detach(osl_t *osh, sdioh_info_t *sd)
+{
+ sd_trace(("%s\n", __FUNCTION__));
+ if (sd) {
+ sd_err(("%s: detaching from hardware\n", __FUNCTION__));
+ spi_free_irq(sd->irq, sd);
+ spi_hw_detach(sd);
+ spi_osfree(sd);
+ MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+ }
+ return SDIOH_API_RC_SUCCESS;
+}
+
+/* Configure callback to client when we recieve client interrupt */
+extern SDIOH_API_RC
+sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+ sd->intr_handler = fn;
+ sd->intr_handler_arg = argh;
+ sd->intr_handler_valid = TRUE;
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_deregister(sdioh_info_t *sd)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+ sd->intr_handler_valid = FALSE;
+ sd->intr_handler = NULL;
+ sd->intr_handler_arg = NULL;
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff)
+{
+ sd_trace(("%s: Entering\n", __FUNCTION__));
+ *onoff = sd->client_intr_enabled;
+ return SDIOH_API_RC_SUCCESS;
+}
+
+#if defined(DHD_DEBUG)
+extern bool
+sdioh_interrupt_pending(sdioh_info_t *sd)
+{
+ return 0;
+}
+#endif
+
+extern SDIOH_API_RC
+sdioh_query_device(sdioh_info_t *sd)
+{
+ /* Return a BRCM ID appropriate to the dongle class */
+ return (sd->num_funcs > 1) ? BCM4329_D11NDUAL_ID : BCM4318_D11G_ID;
+}
+
+/* Provide dstatus bits of spi-transaction for dhd layers. */
+extern uint32
+sdioh_get_dstatus(sdioh_info_t *sd)
+{
+ return sd->card_dstatus;
+}
+
+extern void
+sdioh_chipinfo(sdioh_info_t *sd, uint32 chip, uint32 chiprev)
+{
+ sd->chip = chip;
+ sd->chiprev = chiprev;
+}
+
+extern void
+sdioh_dwordmode(sdioh_info_t *sd, bool set)
+{
+ uint8 reg = 0;
+ int status;
+
+ if ((status = sdioh_request_byte(sd, SDIOH_READ, SPI_FUNC_0, SPID_STATUS_ENABLE, &reg)) !=
+ SUCCESS) {
+ sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__));
+ return;
+ }
+
+ if (set) {
+ reg |= DWORD_PKT_LEN_EN;
+ sd->dwordmode = TRUE;
+ sd->client_block_size[SPI_FUNC_2] = 4096; /* h2spi's limit is 4KB, we support 8KB */
+ } else {
+ reg &= ~DWORD_PKT_LEN_EN;
+ sd->dwordmode = FALSE;
+ sd->client_block_size[SPI_FUNC_2] = 2048;
+ }
+
+ if ((status = sdioh_request_byte(sd, SDIOH_WRITE, SPI_FUNC_0, SPID_STATUS_ENABLE, &reg)) !=
+ SUCCESS) {
+ sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__));
+ return;
+ }
+}
+
+
+uint
+sdioh_query_iofnum(sdioh_info_t *sd)
+{
+ return sd->num_funcs;
+}
+
+/* IOVar table */
+enum {
+ IOV_MSGLEVEL = 1,
+ IOV_BLOCKMODE,
+ IOV_BLOCKSIZE,
+ IOV_DMA,
+ IOV_USEINTS,
+ IOV_NUMINTS,
+ IOV_NUMLOCALINTS,
+ IOV_HOSTREG,
+ IOV_DEVREG,
+ IOV_DIVISOR,
+ IOV_SDMODE,
+ IOV_HISPEED,
+ IOV_HCIREGS,
+ IOV_POWER,
+ IOV_CLOCK,
+ IOV_SPIERRSTATS,
+ IOV_RESP_DELAY_ALL
+};
+
+const bcm_iovar_t sdioh_iovars[] = {
+ {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 },
+ {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */
+ {"sd_dma", IOV_DMA, 0, IOVT_BOOL, 0 },
+ {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 },
+ {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 },
+ {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 },
+ {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
+ {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
+ {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 },
+ {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 },
+ {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 },
+ {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100},
+ {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0},
+ {"spi_errstats", IOV_SPIERRSTATS, 0, IOVT_BUFFER, sizeof(struct spierrstats_t) },
+ {"spi_respdelay", IOV_RESP_DELAY_ALL, 0, IOVT_BOOL, 0 },
+ {NULL, 0, 0, 0, 0 }
+};
+
+int
+sdioh_iovar_op(sdioh_info_t *si, const char *name,
+ void *params, int plen, void *arg, int len, bool set)
+{
+ const bcm_iovar_t *vi = NULL;
+ int bcmerror = 0;
+ int val_size;
+ int32 int_val = 0;
+ bool bool_val;
+ uint32 actionid;
+/*
+ sdioh_regs_t *regs;
+*/
+
+ ASSERT(name);
+ ASSERT(len >= 0);
+
+ /* Get must have return space; Set does not take qualifiers */
+ ASSERT(set || (arg && len));
+ ASSERT(!set || (!params && !plen));
+
+ sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name));
+
+ if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) {
+ bcmerror = BCME_UNSUPPORTED;
+ goto exit;
+ }
+
+ if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0)
+ goto exit;
+
+ /* Set up params so get and set can share the convenience variables */
+ if (params == NULL) {
+ params = arg;
+ plen = len;
+ }
+
+ if (vi->type == IOVT_VOID)
+ val_size = 0;
+ else if (vi->type == IOVT_BUFFER)
+ val_size = len;
+ else
+ val_size = sizeof(int);
+
+ if (plen >= (int)sizeof(int_val))
+ bcopy(params, &int_val, sizeof(int_val));
+
+ bool_val = (int_val != 0) ? TRUE : FALSE;
+
+ actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+ switch (actionid) {
+ case IOV_GVAL(IOV_MSGLEVEL):
+ int_val = (int32)sd_msglevel;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_MSGLEVEL):
+ sd_msglevel = int_val;
+ break;
+
+ case IOV_GVAL(IOV_BLOCKSIZE):
+ if ((uint32)int_val > si->num_funcs) {
+ bcmerror = BCME_BADARG;
+ break;
+ }
+ int_val = (int32)si->client_block_size[int_val];
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_DMA):
+ int_val = (int32)si->sd_use_dma;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_DMA):
+ si->sd_use_dma = (bool)int_val;
+ break;
+
+ case IOV_GVAL(IOV_USEINTS):
+ int_val = (int32)si->use_client_ints;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_USEINTS):
+ break;
+
+ case IOV_GVAL(IOV_DIVISOR):
+ int_val = (uint32)sd_divisor;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_DIVISOR):
+ sd_divisor = int_val;
+ if (!spi_start_clock(si, (uint16)sd_divisor)) {
+ sd_err(("%s: set clock failed\n", __FUNCTION__));
+ bcmerror = BCME_ERROR;
+ }
+ break;
+
+ case IOV_GVAL(IOV_POWER):
+ int_val = (uint32)sd_power;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_POWER):
+ sd_power = int_val;
+ break;
+
+ case IOV_GVAL(IOV_CLOCK):
+ int_val = (uint32)sd_clock;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_CLOCK):
+ sd_clock = int_val;
+ break;
+
+ case IOV_GVAL(IOV_SDMODE):
+ int_val = (uint32)sd_sdmode;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_SDMODE):
+ sd_sdmode = int_val;
+ break;
+
+ case IOV_GVAL(IOV_HISPEED):
+ int_val = (uint32)sd_hiok;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_HISPEED):
+ sd_hiok = int_val;
+
+ if (!bcmspi_set_highspeed_mode(si, (bool)sd_hiok)) {
+ sd_err(("%s: Failed changing highspeed mode to %d.\n",
+ __FUNCTION__, sd_hiok));
+ bcmerror = BCME_ERROR;
+ return ERROR;
+ }
+ break;
+
+ case IOV_GVAL(IOV_NUMINTS):
+ int_val = (int32)si->intrcount;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_NUMLOCALINTS):
+ int_val = (int32)si->local_intrcount;
+ bcopy(&int_val, arg, val_size);
+ break;
+ case IOV_GVAL(IOV_DEVREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+ uint8 data;
+
+ if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) {
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+
+ int_val = (int)data;
+ bcopy(&int_val, arg, sizeof(int_val));
+ break;
+ }
+
+ case IOV_SVAL(IOV_DEVREG):
+ {
+ sdreg_t *sd_ptr = (sdreg_t *)params;
+ uint8 data = (uint8)sd_ptr->value;
+
+ if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) {
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+ break;
+ }
+
+
+ case IOV_GVAL(IOV_SPIERRSTATS):
+ {
+ bcopy(&si->spierrstats, arg, sizeof(struct spierrstats_t));
+ break;
+ }
+
+ case IOV_SVAL(IOV_SPIERRSTATS):
+ {
+ bzero(&si->spierrstats, sizeof(struct spierrstats_t));
+ break;
+ }
+
+ case IOV_GVAL(IOV_RESP_DELAY_ALL):
+ int_val = (int32)si->resp_delay_all;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_RESP_DELAY_ALL):
+ si->resp_delay_all = (bool)int_val;
+ int_val = STATUS_ENABLE|INTR_WITH_STATUS;
+ if (si->resp_delay_all)
+ int_val |= RESP_DELAY_ALL;
+ else {
+ if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_RESPONSE_DELAY, 1,
+ F1_RESPONSE_DELAY) != SUCCESS) {
+ sd_err(("%s: Unable to set response delay.\n", __FUNCTION__));
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+ }
+
+ if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_STATUS_ENABLE, 1, int_val)
+ != SUCCESS) {
+ sd_err(("%s: Unable to set response delay.\n", __FUNCTION__));
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+ break;
+
+ default:
+ bcmerror = BCME_UNSUPPORTED;
+ break;
+ }
+exit:
+
+ return bcmerror;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+ SDIOH_API_RC status;
+ /* No lock needed since sdioh_request_byte does locking */
+ status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
+ return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+ /* No lock needed since sdioh_request_byte does locking */
+ SDIOH_API_RC status;
+
+ if ((fnc_num == SPI_FUNC_1) && (addr == SBSDIO_FUNC1_FRAMECTRL)) {
+ uint8 dummy_data;
+ status = sdioh_cfg_read(sd, fnc_num, addr, &dummy_data);
+ if (status) {
+ sd_err(("sdioh_cfg_read() failed.\n"));
+ return status;
+ }
+ }
+
+ status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
+ return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length)
+{
+ uint32 count;
+ int offset;
+ uint32 cis_byte;
+ uint16 *cis = (uint16 *)cisd;
+ uint bar0 = SI_ENUM_BASE;
+ int status;
+ uint8 data;
+
+ sd_trace(("%s: Func %d\n", __FUNCTION__, func));
+
+ spi_lock(sd);
+
+ /* Set sb window address to 0x18000000 */
+ data = (bar0 >> 8) & SBSDIO_SBADDRLOW_MASK;
+ status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, &data);
+ if (status == SUCCESS) {
+ data = (bar0 >> 16) & SBSDIO_SBADDRMID_MASK;
+ status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, &data);
+ } else {
+ sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__));
+ spi_unlock(sd);
+ return (BCME_ERROR);
+ }
+ if (status == SUCCESS) {
+ data = (bar0 >> 24) & SBSDIO_SBADDRHIGH_MASK;
+ status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, &data);
+ } else {
+ sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__));
+ spi_unlock(sd);
+ return (BCME_ERROR);
+ }
+
+ offset = CC_OTP; /* OTP offset in chipcommon. */
+ for (count = 0; count < length/2; count++) {
+ if (bcmspi_card_regread (sd, SDIO_FUNC_1, offset, 2, &cis_byte) < 0) {
+ sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
+ spi_unlock(sd);
+ return (BCME_ERROR);
+ }
+
+ *cis = (uint16)cis_byte;
+ cis++;
+ offset += 2;
+ }
+
+ spi_unlock(sd);
+
+ return (BCME_OK);
+}
+
+extern SDIOH_API_RC
+sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte)
+{
+ int status;
+ uint32 cmd_arg;
+ uint32 dstatus;
+ uint32 data = (uint32)(*byte);
+
+ spi_lock(sd);
+
+ cmd_arg = 0;
+ cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */
+ cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, rw == SDIOH_READ ? 0 : 1);
+ cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1);
+
+ sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+ sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, rw, func,
+ regaddr, data));
+
+ if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma,
+ cmd_arg, &data, 1)) != SUCCESS) {
+ spi_unlock(sd);
+ return status;
+ }
+
+ if (rw == SDIOH_READ)
+ *byte = (uint8)data;
+
+ bcmspi_cmd_getdstatus(sd, &dstatus);
+ if (dstatus)
+ sd_trace(("dstatus =0x%x\n", dstatus));
+
+ spi_unlock(sd);
+ return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr,
+ uint32 *word, uint nbytes)
+{
+ int status;
+
+ spi_lock(sd);
+
+ if (rw == SDIOH_READ)
+ status = bcmspi_card_regread(sd, func, addr, nbytes, word);
+ else
+ status = bcmspi_card_regwrite(sd, func, addr, nbytes, *word);
+
+ spi_unlock(sd);
+ return (status == SUCCESS ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+extern SDIOH_API_RC
+sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint rw, uint func,
+ uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt)
+{
+ int len;
+ int buflen = (int)buflen_u;
+ bool fifo = (fix_inc == SDIOH_DATA_FIX);
+
+ spi_lock(sd);
+
+ ASSERT(reg_width == 4);
+ ASSERT(buflen_u < (1 << 30));
+ ASSERT(sd->client_block_size[func]);
+
+ sd_data(("%s: %c len %d r_cnt %d t_cnt %d, pkt @0x%p\n",
+ __FUNCTION__, rw == SDIOH_READ ? 'R' : 'W',
+ buflen_u, sd->r_cnt, sd->t_cnt, pkt));
+
+ /* Break buffer down into blocksize chunks. */
+ while (buflen > 0) {
+ len = MIN(sd->client_block_size[func], buflen);
+ if (bcmspi_card_buf(sd, rw, func, fifo, addr, len, (uint32 *)buffer) != SUCCESS) {
+ sd_err(("%s: bcmspi_card_buf %s failed\n",
+ __FUNCTION__, rw == SDIOH_READ ? "Read" : "Write"));
+ spi_unlock(sd);
+ return SDIOH_API_RC_FAIL;
+ }
+ buffer += len;
+ buflen -= len;
+ if (!fifo)
+ addr += len;
+ }
+ spi_unlock(sd);
+ return SDIOH_API_RC_SUCCESS;
+}
+
+/* This function allows write to gspi bus when another rd/wr function is deep down the call stack.
+ * Its main aim is to have simpler spi writes rather than recursive writes.
+ * e.g. When there is a need to program response delay on the fly after detecting the SPI-func
+ * this call will allow to program the response delay.
+ */
+static int
+bcmspi_card_byterewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 byte)
+{
+ uint32 cmd_arg;
+ uint32 datalen = 1;
+ uint32 hostlen;
+
+ cmd_arg = 0;
+
+ cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1);
+ cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */
+ cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, SPI_LEN, datalen);
+
+ sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+
+
+ /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen
+ * according to the wordlen mode(16/32bit) the device is in.
+ */
+ ASSERT(sd->wordlen == 4 || sd->wordlen == 2);
+ datalen = ROUNDUP(datalen, sd->wordlen);
+
+ /* Start by copying command in the spi-outbuffer */
+ if (sd->wordlen == 4) { /* 32bit spid */
+ *(uint32 *)spi_outbuf2 = bcmswap32(cmd_arg);
+ if (datalen & 0x3)
+ datalen += (4 - (datalen & 0x3));
+ } else if (sd->wordlen == 2) { /* 16bit spid */
+ *(uint16 *)spi_outbuf2 = bcmswap16(cmd_arg & 0xffff);
+ *(uint16 *)&spi_outbuf2[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16);
+ if (datalen & 0x1)
+ datalen++;
+ } else {
+ sd_err(("%s: Host is %d bit spid, could not create SPI command.\n",
+ __FUNCTION__, 8 * sd->wordlen));
+ return ERROR;
+ }
+
+ /* for Write, put the data into the output buffer */
+ if (datalen != 0) {
+ if (sd->wordlen == 4) { /* 32bit spid */
+ *(uint32 *)&spi_outbuf2[CMDLEN] = bcmswap32(byte);
+ } else if (sd->wordlen == 2) { /* 16bit spid */
+ *(uint16 *)&spi_outbuf2[CMDLEN] = bcmswap16(byte & 0xffff);
+ *(uint16 *)&spi_outbuf2[CMDLEN + 2] =
+ bcmswap16((byte & 0xffff0000) >> 16);
+ }
+ }
+
+ /* +4 for cmd, +4 for dstatus */
+ hostlen = datalen + 8;
+ hostlen += (4 - (hostlen & 0x3));
+ spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, hostlen);
+
+ /* Last 4bytes are dstatus. Device is configured to return status bits. */
+ if (sd->wordlen == 4) { /* 32bit spid */
+ sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]);
+ } else if (sd->wordlen == 2) { /* 16bit spid */
+ sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) |
+ (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16));
+ } else {
+ sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n",
+ __FUNCTION__, 8 * sd->wordlen));
+ return ERROR;
+ }
+
+ if (sd->card_dstatus)
+ sd_trace(("dstatus after byte rewrite = 0x%x\n", sd->card_dstatus));
+
+ return (BCME_OK);
+}
+
+/* Program the response delay corresponding to the spi function */
+static int
+bcmspi_prog_resp_delay(sdioh_info_t *sd, int func, uint8 resp_delay)
+{
+ if (sd->resp_delay_all == FALSE)
+ return (BCME_OK);
+
+ if (sd->prev_fun == func)
+ return (BCME_OK);
+
+ if (F0_RESPONSE_DELAY == F1_RESPONSE_DELAY)
+ return (BCME_OK);
+
+ bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_RESPONSE_DELAY, resp_delay);
+
+ /* Remember function for which to avoid reprogramming resp-delay in next iteration */
+ sd->prev_fun = func;
+
+ return (BCME_OK);
+
+}
+
+#define GSPI_RESYNC_PATTERN 0x0
+
+/* A resync pattern is a 32bit MOSI line with all zeros. Its a special command in gSPI.
+ * It resets the spi-bkplane logic so that all F1 related ping-pong buffer logic is
+ * synchronised and all queued resuests are cancelled.
+ */
+static int
+bcmspi_resync_f1(sdioh_info_t *sd)
+{
+ uint32 cmd_arg = GSPI_RESYNC_PATTERN, data = 0, datalen = 0;
+
+
+ /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen
+ * according to the wordlen mode(16/32bit) the device is in.
+ */
+ ASSERT(sd->wordlen == 4 || sd->wordlen == 2);
+ datalen = ROUNDUP(datalen, sd->wordlen);
+
+ /* Start by copying command in the spi-outbuffer */
+ *(uint32 *)spi_outbuf2 = cmd_arg;
+
+ /* for Write, put the data into the output buffer */
+ *(uint32 *)&spi_outbuf2[CMDLEN] = data;
+
+ /* +4 for cmd, +4 for dstatus */
+ spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, datalen + 8);
+
+ /* Last 4bytes are dstatus. Device is configured to return status bits. */
+ if (sd->wordlen == 4) { /* 32bit spid */
+ sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]);
+ } else if (sd->wordlen == 2) { /* 16bit spid */
+ sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) |
+ (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16));
+ } else {
+ sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n",
+ __FUNCTION__, 8 * sd->wordlen));
+ return ERROR;
+ }
+
+ if (sd->card_dstatus)
+ sd_trace(("dstatus after resync pattern write = 0x%x\n", sd->card_dstatus));
+
+ return (BCME_OK);
+}
+
+uint32 dstatus_count = 0;
+
+static int
+bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg)
+{
+ uint32 dstatus = sd->card_dstatus;
+ struct spierrstats_t *spierrstats = &sd->spierrstats;
+ int err = SUCCESS;
+
+ sd_trace(("cmd = 0x%x, dstatus = 0x%x\n", cmd_arg, dstatus));
+
+ /* Store dstatus of last few gSPI transactions */
+ spierrstats->dstatus[dstatus_count % NUM_PREV_TRANSACTIONS] = dstatus;
+ spierrstats->spicmd[dstatus_count % NUM_PREV_TRANSACTIONS] = cmd_arg;
+ dstatus_count++;
+
+ if (sd->card_init_done == FALSE)
+ return err;
+
+ if (dstatus & STATUS_DATA_NOT_AVAILABLE) {
+ spierrstats->dna++;
+ sd_trace(("Read data not available on F1 addr = 0x%x\n",
+ GFIELD(cmd_arg, SPI_REG_ADDR)));
+ /* Clear dna bit */
+ bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, DATA_UNAVAILABLE);
+ }
+
+ if (dstatus & STATUS_UNDERFLOW) {
+ spierrstats->rdunderflow++;
+ sd_err(("FIFO underflow happened due to current F2 read command.\n"));
+ }
+
+ if (dstatus & STATUS_OVERFLOW) {
+ spierrstats->wroverflow++;
+ sd_err(("FIFO overflow happened due to current (F1/F2) write command.\n"));
+ if ((sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 0)) {
+ bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, F1_OVERFLOW);
+ bcmspi_resync_f1(sd);
+ sd_err(("Recovering from F1 FIFO overflow.\n"));
+ } else {
+ err = ERROR_OF;
+ }
+ }
+
+ if (dstatus & STATUS_F2_INTR) {
+ spierrstats->f2interrupt++;
+ sd_trace(("Interrupt from F2. SW should clear corresponding IntStatus bits\n"));
+ }
+
+ if (dstatus & STATUS_F3_INTR) {
+ spierrstats->f3interrupt++;
+ sd_err(("Interrupt from F3. SW should clear corresponding IntStatus bits\n"));
+ }
+
+ if (dstatus & STATUS_HOST_CMD_DATA_ERR) {
+ spierrstats->hostcmddataerr++;
+ sd_err(("Error in CMD or Host data, detected by CRC/Checksum (optional)\n"));
+ }
+
+ if (dstatus & STATUS_F2_PKT_AVAILABLE) {
+ spierrstats->f2pktavailable++;
+ sd_trace(("Packet is available/ready in F2 TX FIFO\n"));
+ sd_trace(("Packet length = %d\n", sd->dwordmode ?
+ ((dstatus & STATUS_F2_PKT_LEN_MASK) >> (STATUS_F2_PKT_LEN_SHIFT - 2)) :
+ ((dstatus & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT)));
+ }
+
+ if (dstatus & STATUS_F3_PKT_AVAILABLE) {
+ spierrstats->f3pktavailable++;
+ sd_err(("Packet is available/ready in F3 TX FIFO\n"));
+ sd_err(("Packet length = %d\n",
+ (dstatus & STATUS_F3_PKT_LEN_MASK) >> STATUS_F3_PKT_LEN_SHIFT));
+ }
+
+ return err;
+}
+
+extern int
+sdioh_abort(sdioh_info_t *sd, uint func)
+{
+ return 0;
+}
+
+int
+sdioh_start(sdioh_info_t *sd, int stage)
+{
+ return SUCCESS;
+}
+
+int
+sdioh_stop(sdioh_info_t *sd)
+{
+ return SUCCESS;
+}
+
+
+
+/*
+ * Private/Static work routines
+ */
+static int
+bcmspi_host_init(sdioh_info_t *sd)
+{
+
+ /* Default power on mode */
+ sd->sd_mode = SDIOH_MODE_SPI;
+ sd->polled_mode = TRUE;
+ sd->host_init_done = TRUE;
+ sd->card_init_done = FALSE;
+ sd->adapter_slot = 1;
+
+ return (SUCCESS);
+}
+
+static int
+get_client_blocksize(sdioh_info_t *sd)
+{
+ uint32 regdata[2];
+ int status;
+
+ /* Find F1/F2/F3 max packet size */
+ if ((status = bcmspi_card_regread(sd, 0, SPID_F1_INFO_REG,
+ 8, regdata)) != SUCCESS) {
+ return status;
+ }
+
+ sd_trace(("pkt_size regdata[0] = 0x%x, regdata[1] = 0x%x\n",
+ regdata[0], regdata[1]));
+
+ sd->client_block_size[1] = (regdata[0] & F1_MAX_PKT_SIZE) >> 2;
+ sd_trace(("Func1 blocksize = %d\n", sd->client_block_size[1]));
+ ASSERT(sd->client_block_size[1] == BLOCK_SIZE_F1);
+
+ sd->client_block_size[2] = ((regdata[0] >> 16) & F2_MAX_PKT_SIZE) >> 2;
+ sd_trace(("Func2 blocksize = %d\n", sd->client_block_size[2]));
+ ASSERT(sd->client_block_size[2] == BLOCK_SIZE_F2);
+
+ sd->client_block_size[3] = (regdata[1] & F3_MAX_PKT_SIZE) >> 2;
+ sd_trace(("Func3 blocksize = %d\n", sd->client_block_size[3]));
+ ASSERT(sd->client_block_size[3] == BLOCK_SIZE_F3);
+
+ return 0;
+}
+
+static int
+bcmspi_client_init(sdioh_info_t *sd)
+{
+ uint32 status_en_reg = 0;
+ sd_trace(("%s: Powering up slot %d\n", __FUNCTION__, sd->adapter_slot));
+
+#ifdef HSMODE
+ if (!spi_start_clock(sd, (uint16)sd_divisor)) {
+ sd_err(("spi_start_clock failed\n"));
+ return ERROR;
+ }
+#else
+ /* Start at ~400KHz clock rate for initialization */
+ if (!spi_start_clock(sd, 128)) {
+ sd_err(("spi_start_clock failed\n"));
+ return ERROR;
+ }
+#endif /* HSMODE */
+
+ if (!bcmspi_host_device_init_adapt(sd)) {
+ sd_err(("bcmspi_host_device_init_adapt failed\n"));
+ return ERROR;
+ }
+
+ if (!bcmspi_test_card(sd)) {
+ sd_err(("bcmspi_test_card failed\n"));
+ return ERROR;
+ }
+
+ sd->num_funcs = SPI_MAX_IOFUNCS;
+
+ get_client_blocksize(sd);
+
+ /* Apply resync pattern cmd with all zeros to reset spi-bkplane F1 logic */
+ bcmspi_resync_f1(sd);
+
+ sd->dwordmode = FALSE;
+
+ bcmspi_card_regread(sd, 0, SPID_STATUS_ENABLE, 1, &status_en_reg);
+
+ sd_trace(("%s: Enabling interrupt with dstatus \n", __FUNCTION__));
+ status_en_reg |= INTR_WITH_STATUS;
+
+
+ if (bcmspi_card_regwrite(sd, SPI_FUNC_0, SPID_STATUS_ENABLE, 1,
+ status_en_reg & 0xff) != SUCCESS) {
+ sd_err(("%s: Unable to set response delay for all fun's.\n", __FUNCTION__));
+ return ERROR;
+ }
+
+
+#ifndef HSMODE
+ /* After configuring for High-Speed mode, set the desired clock rate. */
+ if (!spi_start_clock(sd, 4)) {
+ sd_err(("spi_start_clock failed\n"));
+ return ERROR;
+ }
+#endif /* HSMODE */
+
+ sd->card_init_done = TRUE;
+
+
+ return SUCCESS;
+}
+
+static int
+bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode)
+{
+ uint32 regdata;
+ int status;
+
+ if ((status = bcmspi_card_regread(sd, 0, SPID_CONFIG,
+ 4, &regdata)) != SUCCESS)
+ return status;
+
+ sd_trace(("In %s spih-ctrl = 0x%x \n", __FUNCTION__, regdata));
+
+
+ if (hsmode == TRUE) {
+ sd_trace(("Attempting to enable High-Speed mode.\n"));
+
+ if (regdata & HIGH_SPEED_MODE) {
+ sd_trace(("Device is already in High-Speed mode.\n"));
+ return status;
+ } else {
+ regdata |= HIGH_SPEED_MODE;
+ sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG));
+ if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG,
+ 4, regdata)) != SUCCESS) {
+ return status;
+ }
+ }
+ } else {
+ sd_trace(("Attempting to disable High-Speed mode.\n"));
+
+ if (regdata & HIGH_SPEED_MODE) {
+ regdata &= ~HIGH_SPEED_MODE;
+ sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG));
+ if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG,
+ 4, regdata)) != SUCCESS)
+ return status;
+ }
+ else {
+ sd_trace(("Device is already in Low-Speed mode.\n"));
+ return status;
+ }
+ }
+
+ spi_controller_highspeed_mode(sd, hsmode);
+
+ return TRUE;
+}
+
+#define bcmspi_find_curr_mode(sd) { \
+ sd->wordlen = 2; \
+ status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, &regdata); \
+ regdata &= 0xff; \
+ if ((regdata == 0xad) || (regdata == 0x5b) || \
+ (regdata == 0x5d) || (regdata == 0x5a)) \
+ break; \
+ sd->wordlen = 4; \
+ status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, &regdata); \
+ regdata &= 0xff; \
+ if ((regdata == 0xad) || (regdata == 0x5b) || \
+ (regdata == 0x5d) || (regdata == 0x5a)) \
+ break; \
+ sd_trace(("Silicon testability issue: regdata = 0x%x." \
+ " Expected 0xad, 0x5a, 0x5b or 0x5d.\n", regdata)); \
+ OSL_DELAY(100000); \
+}
+
+#define INIT_ADAPT_LOOP 100
+
+/* Adapt clock-phase-speed-bitwidth between host and device */
+static bool
+bcmspi_host_device_init_adapt(sdioh_info_t *sd)
+{
+ uint32 wrregdata, regdata = 0;
+ int status;
+ int i;
+
+ /* Due to a silicon testability issue, the first command from the Host
+ * to the device will get corrupted (first bit will be lost). So the
+ * Host should poll the device with a safe read request. ie: The Host
+ * should try to read F0 addr 0x14 using the Fixed address mode
+ * (This will prevent a unintended write command to be detected by device)
+ */
+ for (i = 0; i < INIT_ADAPT_LOOP; i++) {
+ /* If device was not power-cycled it will stay in 32bit mode with
+ * response-delay-all bit set. Alternate the iteration so that
+ * read either with or without response-delay for F0 to succeed.
+ */
+ bcmspi_find_curr_mode(sd);
+ sd->resp_delay_all = (i & 0x1) ? TRUE : FALSE;
+
+ bcmspi_find_curr_mode(sd);
+ sd->dwordmode = TRUE;
+
+ bcmspi_find_curr_mode(sd);
+ sd->dwordmode = FALSE;
+ }
+
+ /* Bail out, device not detected */
+ if (i == INIT_ADAPT_LOOP)
+ return FALSE;
+
+ /* Softreset the spid logic */
+ if ((sd->dwordmode) || (sd->wordlen == 4)) {
+ bcmspi_card_regwrite(sd, 0, SPID_RESET_BP, 1, RESET_ON_WLAN_BP_RESET|RESET_SPI);
+ bcmspi_card_regread(sd, 0, SPID_RESET_BP, 1, &regdata);
+ sd_trace(("reset reg read = 0x%x\n", regdata));
+ sd_trace(("dwordmode = %d, wordlen = %d, resp_delay_all = %d\n", sd->dwordmode,
+ sd->wordlen, sd->resp_delay_all));
+ /* Restore default state after softreset */
+ sd->wordlen = 2;
+ sd->dwordmode = FALSE;
+ }
+
+ if (sd->wordlen == 4) {
+ if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) !=
+ SUCCESS)
+ return FALSE;
+ if (regdata == TEST_RO_DATA_32BIT_LE) {
+ sd_trace(("Spid is already in 32bit LE mode. Value read = 0x%x\n",
+ regdata));
+ sd_trace(("Spid power was left on.\n"));
+ } else {
+ sd_err(("Spid power was left on but signature read failed."
+ " Value read = 0x%x\n", regdata));
+ return FALSE;
+ }
+ } else {
+ sd->wordlen = 2;
+
+#define CTRL_REG_DEFAULT 0x00010430 /* according to the host m/c */
+
+ wrregdata = (CTRL_REG_DEFAULT);
+ sd->resp_delay_all = TRUE;
+ if (sd->resp_delay_all == TRUE) {
+ /* Enable response delay for all */
+ wrregdata |= (RESP_DELAY_ALL << 16);
+ /* Program response delay value */
+ wrregdata &= 0xffff00ff;
+ wrregdata |= (F1_RESPONSE_DELAY << 8);
+ sd->prev_fun = SPI_FUNC_1;
+ bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata);
+ }
+
+ if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) != SUCCESS)
+ return FALSE;
+ sd_trace(("(we are still in 16bit mode) 32bit READ LE regdata = 0x%x\n", regdata));
+
+#ifndef HSMODE
+ wrregdata |= (CLOCK_PHASE | CLOCK_POLARITY);
+ wrregdata &= ~HIGH_SPEED_MODE;
+ bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata);
+#endif /* HSMODE */
+
+ for (i = 0; i < INIT_ADAPT_LOOP; i++) {
+ if ((regdata == 0xfdda7d5b) || (regdata == 0xfdda7d5a)) {
+ sd_trace(("0xfeedbead was leftshifted by 1-bit.\n"));
+ if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4,
+ &regdata)) != SUCCESS)
+ return FALSE;
+ }
+ OSL_DELAY(1000);
+ }
+
+
+ /* Change to host controller intr-polarity of active-low */
+ wrregdata &= ~INTR_POLARITY;
+ sd_trace(("(we are still in 16bit mode) 32bit Write LE reg-ctrl-data = 0x%x\n",
+ wrregdata));
+ /* Change to 32bit mode */
+ wrregdata |= WORD_LENGTH_32;
+ bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata);
+
+ /* Change command/data packaging in 32bit LE mode */
+ sd->wordlen = 4;
+
+ if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) != SUCCESS)
+ return FALSE;
+
+ if (regdata == TEST_RO_DATA_32BIT_LE) {
+ sd_trace(("Read spid passed. Value read = 0x%x\n", regdata));
+ sd_trace(("Spid had power-on cycle OR spi was soft-resetted \n"));
+ } else {
+ sd_err(("Stale spid reg values read as it was kept powered. Value read ="
+ "0x%x\n", regdata));
+ return FALSE;
+ }
+ }
+
+
+ return TRUE;
+}
+
+static bool
+bcmspi_test_card(sdioh_info_t *sd)
+{
+ uint32 regdata;
+ int status;
+
+ if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) != SUCCESS)
+ return FALSE;
+
+ if (regdata == (TEST_RO_DATA_32BIT_LE))
+ sd_trace(("32bit LE regdata = 0x%x\n", regdata));
+ else {
+ sd_trace(("Incorrect 32bit LE regdata = 0x%x\n", regdata));
+ return FALSE;
+ }
+
+
+#define RW_PATTERN1 0xA0A1A2A3
+#define RW_PATTERN2 0x4B5B6B7B
+
+ regdata = RW_PATTERN1;
+ if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS)
+ return FALSE;
+ regdata = 0;
+ if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, &regdata)) != SUCCESS)
+ return FALSE;
+ if (regdata != RW_PATTERN1) {
+ sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n",
+ RW_PATTERN1, regdata));
+ return FALSE;
+ } else
+ sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata));
+
+ regdata = RW_PATTERN2;
+ if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS)
+ return FALSE;
+ regdata = 0;
+ if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, &regdata)) != SUCCESS)
+ return FALSE;
+ if (regdata != RW_PATTERN2) {
+ sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n",
+ RW_PATTERN2, regdata));
+ return FALSE;
+ } else
+ sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata));
+
+ return TRUE;
+}
+
+static int
+bcmspi_driver_init(sdioh_info_t *sd)
+{
+ sd_trace(("%s\n", __FUNCTION__));
+ if ((bcmspi_host_init(sd)) != SUCCESS) {
+ return ERROR;
+ }
+
+ if (bcmspi_client_init(sd) != SUCCESS) {
+ return ERROR;
+ }
+
+ return SUCCESS;
+}
+
+/* Read device reg */
+static int
+bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+ int status;
+ uint32 cmd_arg, dstatus;
+
+ ASSERT(regsize);
+
+ if (func == 2)
+ sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n"));
+
+ cmd_arg = 0;
+ cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0);
+ cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */
+ cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize);
+
+ sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+ sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func,
+ regaddr, *data));
+
+ if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize))
+ != SUCCESS)
+ return status;
+
+ bcmspi_cmd_getdstatus(sd, &dstatus);
+ if (dstatus)
+ sd_trace(("dstatus =0x%x\n", dstatus));
+
+ return SUCCESS;
+}
+
+static int
+bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+
+ int status;
+ uint32 cmd_arg;
+ uint32 dstatus;
+
+ ASSERT(regsize);
+
+ if (func == 2)
+ sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n"));
+
+ cmd_arg = 0;
+ cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0);
+ cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0); /* Fixed access */
+ cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize);
+
+ sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+
+ if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize))
+ != SUCCESS)
+ return status;
+
+ sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func,
+ regaddr, *data));
+
+ bcmspi_cmd_getdstatus(sd, &dstatus);
+ sd_trace(("dstatus =0x%x\n", dstatus));
+ return SUCCESS;
+}
+
+/* write a device register */
+static int
+bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data)
+{
+ int status;
+ uint32 cmd_arg, dstatus;
+
+ ASSERT(regsize);
+
+ cmd_arg = 0;
+
+ cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1);
+ cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */
+ cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize);
+
+ sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+ sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 1, func,
+ regaddr, data));
+
+
+ if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, &data, regsize))
+ != SUCCESS)
+ return status;
+
+ bcmspi_cmd_getdstatus(sd, &dstatus);
+ if (dstatus)
+ sd_trace(("dstatus =0x%x\n", dstatus));
+
+ return SUCCESS;
+}
+
+/* write a device register - 1 byte */
+static int
+bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 *byte)
+{
+ int status;
+ uint32 cmd_arg;
+ uint32 dstatus;
+ uint32 data = (uint32)(*byte);
+
+ cmd_arg = 0;
+ cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */
+ cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+ cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1);
+ cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1);
+
+ sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+ sd_trace(("%s: func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, func,
+ regaddr, data));
+
+ if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma,
+ cmd_arg, &data, 1)) != SUCCESS) {
+ return status;
+ }
+
+ bcmspi_cmd_getdstatus(sd, &dstatus);
+ if (dstatus)
+ sd_trace(("dstatus =0x%x\n", dstatus));
+
+ return SUCCESS;
+}
+
+void
+bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer)
+{
+ *dstatus_buffer = sd->card_dstatus;
+}
+
+/* 'data' is of type uint32 whereas other buffers are of type uint8 */
+static int
+bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg,
+ uint32 *data, uint32 datalen)
+{
+ uint32 i, j;
+ uint8 resp_delay = 0;
+ int err = SUCCESS;
+ uint32 hostlen;
+ uint32 spilen = 0;
+ uint32 dstatus_idx = 0;
+ uint16 templen, buslen, len, *ptr = NULL;
+
+ sd_trace(("spi cmd = 0x%x\n", cmd_arg));
+
+ if (DWORDMODE_ON) {
+ spilen = GFIELD(cmd_arg, SPI_LEN);
+ if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_0) ||
+ (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_1))
+ dstatus_idx = spilen * 3;
+
+ if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) &&
+ (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) {
+ spilen = spilen << 2;
+ dstatus_idx = (spilen % 16) ? (16 - (spilen % 16)) : 0;
+ /* convert len to mod16 size */
+ spilen = ROUNDUP(spilen, 16);
+ cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2));
+ }
+ }
+
+ /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen
+ * according to the wordlen mode(16/32bit) the device is in.
+ */
+ if (sd->wordlen == 4) { /* 32bit spid */
+ *(uint32 *)spi_outbuf = bcmswap32(cmd_arg);
+ if (datalen & 0x3)
+ datalen += (4 - (datalen & 0x3));
+ } else if (sd->wordlen == 2) { /* 16bit spid */
+ *(uint16 *)spi_outbuf = bcmswap16(cmd_arg & 0xffff);
+ *(uint16 *)&spi_outbuf[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16);
+ if (datalen & 0x1)
+ datalen++;
+ if (datalen < 4)
+ datalen = ROUNDUP(datalen, 4);
+ } else {
+ sd_err(("Host is %d bit spid, could not create SPI command.\n",
+ 8 * sd->wordlen));
+ return ERROR;
+ }
+
+ /* for Write, put the data into the output buffer */
+ if (GFIELD(cmd_arg, SPI_RW_FLAG) == 1) {
+ /* We send len field of hw-header always a mod16 size, both from host and dongle */
+ if (DWORDMODE_ON) {
+ if (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) {
+ ptr = (uint16 *)&data[0];
+ templen = *ptr;
+ /* ASSERT(*ptr == ~*(ptr + 1)); */
+ templen = ROUNDUP(templen, 16);
+ *ptr = templen;
+ sd_trace(("actual tx len = %d\n", (uint16)(~*(ptr+1))));
+ }
+ }
+
+ if (datalen != 0) {
+ for (i = 0; i < datalen/4; i++) {
+ if (sd->wordlen == 4) { /* 32bit spid */
+ *(uint32 *)&spi_outbuf[i * 4 + CMDLEN] =
+ bcmswap32(data[i]);
+ } else if (sd->wordlen == 2) { /* 16bit spid */
+ *(uint16 *)&spi_outbuf[i * 4 + CMDLEN] =
+ bcmswap16(data[i] & 0xffff);
+ *(uint16 *)&spi_outbuf[i * 4 + CMDLEN + 2] =
+ bcmswap16((data[i] & 0xffff0000) >> 16);
+ }
+ }
+ }
+ }
+
+ /* Append resp-delay number of bytes and clock them out for F0/1/2 reads. */
+ if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) {
+ int func = GFIELD(cmd_arg, SPI_FUNCTION);
+ switch (func) {
+ case 0:
+ resp_delay = sd->resp_delay_all ? F0_RESPONSE_DELAY : 0;
+ break;
+ case 1:
+ resp_delay = F1_RESPONSE_DELAY;
+ break;
+ case 2:
+ resp_delay = sd->resp_delay_all ? F2_RESPONSE_DELAY : 0;
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ /* Program response delay */
+ bcmspi_prog_resp_delay(sd, func, resp_delay);
+ }
+
+ /* +4 for cmd and +4 for dstatus */
+ hostlen = datalen + 8 + resp_delay;
+ hostlen += dstatus_idx;
+ hostlen += (4 - (hostlen & 0x3));
+ spi_sendrecv(sd, spi_outbuf, spi_inbuf, hostlen);
+
+ /* for Read, get the data into the input buffer */
+ if (datalen != 0) {
+ if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) { /* if read cmd */
+ for (j = 0; j < datalen/4; j++) {
+ if (sd->wordlen == 4) { /* 32bit spid */
+ data[j] = bcmswap32(*(uint32 *)&spi_inbuf[j * 4 +
+ CMDLEN + resp_delay]);
+ } else if (sd->wordlen == 2) { /* 16bit spid */
+ data[j] = (bcmswap16(*(uint16 *)&spi_inbuf[j * 4 +
+ CMDLEN + resp_delay])) |
+ ((bcmswap16(*(uint16 *)&spi_inbuf[j * 4 +
+ CMDLEN + resp_delay + 2])) << 16);
+ }
+ }
+
+ if ((DWORDMODE_ON) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) {
+ ptr = (uint16 *)&data[0];
+ templen = *ptr;
+ buslen = len = ~(*(ptr + 1));
+ buslen = ROUNDUP(buslen, 16);
+ /* populate actual len in hw-header */
+ if (templen == buslen)
+ *ptr = len;
+ }
+ }
+ }
+
+ /* Restore back the len field of the hw header */
+ if (DWORDMODE_ON) {
+ if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) &&
+ (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) {
+ ptr = (uint16 *)&data[0];
+ *ptr = (uint16)(~*(ptr+1));
+ }
+ }
+
+ dstatus_idx += (datalen + CMDLEN + resp_delay);
+ /* Last 4bytes are dstatus. Device is configured to return status bits. */
+ if (sd->wordlen == 4) { /* 32bit spid */
+ sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf[dstatus_idx]);
+ } else if (sd->wordlen == 2) { /* 16bit spid */
+ sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx]) |
+ (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx + 2]) << 16));
+ } else {
+ sd_err(("Host is %d bit machine, could not read SPI dstatus.\n",
+ 8 * sd->wordlen));
+ return ERROR;
+ }
+ if (sd->card_dstatus == 0xffffffff) {
+ sd_err(("looks like not a GSPI device or device is not powered.\n"));
+ }
+
+ err = bcmspi_update_stats(sd, cmd_arg);
+
+ return err;
+
+}
+
+static int
+bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo,
+ uint32 addr, int nbytes, uint32 *data)
+{
+ int status;
+ uint32 cmd_arg;
+ bool write = rw == SDIOH_READ ? 0 : 1;
+ uint retries = 0;
+
+ bool enable;
+ uint32 spilen;
+
+ cmd_arg = 0;
+
+ ASSERT(nbytes);
+ ASSERT(nbytes <= sd->client_block_size[func]);
+
+ if (write) sd->t_cnt++; else sd->r_cnt++;
+
+ if (func == 2) {
+ /* Frame len check limited by gSPI. */
+ if ((nbytes > 2000) && write) {
+ sd_trace((">2KB write: F2 wr of %d bytes\n", nbytes));
+ }
+ /* ASSERT(nbytes <= 2048); Fix bigger len gspi issue and uncomment. */
+ /* If F2 fifo on device is not ready to receive data, don't do F2 transfer */
+ if (write) {
+ uint32 dstatus;
+ /* check F2 ready with cached one */
+ bcmspi_cmd_getdstatus(sd, &dstatus);
+ if ((dstatus & STATUS_F2_RX_READY) == 0) {
+ retries = WAIT_F2RXFIFORDY;
+ enable = 0;
+ while (retries-- && !enable) {
+ OSL_DELAY(WAIT_F2RXFIFORDY_DELAY * 1000);
+ bcmspi_card_regread(sd, SPI_FUNC_0, SPID_STATUS_REG, 4,
+ &dstatus);
+ if (dstatus & STATUS_F2_RX_READY)
+ enable = TRUE;
+ }
+ if (!enable) {
+ struct spierrstats_t *spierrstats = &sd->spierrstats;
+ spierrstats->f2rxnotready++;
+ sd_err(("F2 FIFO is not ready to receive data.\n"));
+ return ERROR;
+ }
+ sd_trace(("No of retries on F2 ready %d\n",
+ (WAIT_F2RXFIFORDY - retries)));
+ }
+ }
+ }
+
+ /* F2 transfers happen on 0 addr */
+ addr = (func == 2) ? 0 : addr;
+
+ /* In pio mode buffer is read using fixed address fifo in func 1 */
+ if ((func == 1) && (fifo))
+ cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0);
+ else
+ cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);
+
+ cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+ cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, addr);
+ cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, write);
+ spilen = sd->data_xfer_count = MIN(sd->client_block_size[func], nbytes);
+ if ((sd->dwordmode == TRUE) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) {
+ /* convert len to mod4 size */
+ spilen = spilen + ((spilen & 0x3) ? (4 - (spilen & 0x3)): 0);
+ cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2));
+ } else
+ cmd_arg = SFIELD(cmd_arg, SPI_LEN, spilen);
+
+ if ((func == 2) && (fifo == 1)) {
+ sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+ __FUNCTION__, write ? "Wr" : "Rd", func, "INCR",
+ addr, nbytes, sd->r_cnt, sd->t_cnt));
+ }
+
+ sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+ sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+ __FUNCTION__, write ? "Wd" : "Rd", func, "INCR",
+ addr, nbytes, sd->r_cnt, sd->t_cnt));
+
+
+ if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg,
+ data, nbytes)) != SUCCESS) {
+ sd_err(("%s: cmd_issue failed for %s\n", __FUNCTION__,
+ (write ? "write" : "read")));
+ return status;
+ }
+
+ /* gSPI expects that hw-header-len is equal to spi-command-len */
+ if ((func == 2) && (rw == SDIOH_WRITE) && (sd->dwordmode == FALSE)) {
+ ASSERT((uint16)sd->data_xfer_count == (uint16)(*data & 0xffff));
+ ASSERT((uint16)sd->data_xfer_count == (uint16)(~((*data & 0xffff0000) >> 16)));
+ }
+
+ if ((nbytes > 2000) && !write) {
+ sd_trace((">2KB read: F2 rd of %d bytes\n", nbytes));
+ }
+
+ return SUCCESS;
+}
+
+/* Reset and re-initialize the device */
+int
+sdioh_sdio_reset(sdioh_info_t *si)
+{
+ si->card_init_done = FALSE;
+ return bcmspi_client_init(si);
+}
diff --git a/drivers/net/wireless/bcm4329/bcmutils.c b/drivers/net/wireless/bcm4329/bcmutils.c
new file mode 100644
index 00000000000..43c04ee92f3
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmutils.c
@@ -0,0 +1,1838 @@
+/*
+ * Driver O/S-independent utility routines
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: bcmutils.c,v 1.210.4.5.2.4.6.19 2010/04/26 06:05:25 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <stdarg.h>
+#include <bcmutils.h>
+#ifdef BCMDRIVER
+#include <osl.h>
+#include <siutils.h>
+#else
+#include <stdio.h>
+#include <string.h>
+/* This case for external supplicant use */
+#if defined(BCMEXTSUP)
+#include <bcm_osl.h>
+#endif
+
+#endif /* BCMDRIVER */
+#include <bcmendian.h>
+#include <bcmdevs.h>
+#include <proto/ethernet.h>
+#include <proto/vlan.h>
+#include <proto/bcmip.h>
+#include <proto/802.1d.h>
+#include <proto/802.11.h>
+
+
+#ifdef BCMDRIVER
+
+
+/* copy a pkt buffer chain into a buffer */
+uint
+pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf)
+{
+ uint n, ret = 0;
+
+ if (len < 0)
+ len = 4096; /* "infinite" */
+
+ /* skip 'offset' bytes */
+ for (; p && offset; p = PKTNEXT(osh, p)) {
+ if (offset < (uint)PKTLEN(osh, p))
+ break;
+ offset -= PKTLEN(osh, p);
+ }
+
+ if (!p)
+ return 0;
+
+ /* copy the data */
+ for (; p && len; p = PKTNEXT(osh, p)) {
+ n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len);
+ bcopy(PKTDATA(osh, p) + offset, buf, n);
+ buf += n;
+ len -= n;
+ ret += n;
+ offset = 0;
+ }
+
+ return ret;
+}
+
+/* copy a buffer into a pkt buffer chain */
+uint
+pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf)
+{
+ uint n, ret = 0;
+
+ /* skip 'offset' bytes */
+ for (; p && offset; p = PKTNEXT(osh, p)) {
+ if (offset < (uint)PKTLEN(osh, p))
+ break;
+ offset -= PKTLEN(osh, p);
+ }
+
+ if (!p)
+ return 0;
+
+ /* copy the data */
+ for (; p && len; p = PKTNEXT(osh, p)) {
+ n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len);
+ bcopy(buf, PKTDATA(osh, p) + offset, n);
+ buf += n;
+ len -= n;
+ ret += n;
+ offset = 0;
+ }
+
+ return ret;
+}
+
+
+
+/* return total length of buffer chain */
+uint
+pkttotlen(osl_t *osh, void *p)
+{
+ uint total;
+
+ total = 0;
+ for (; p; p = PKTNEXT(osh, p))
+ total += PKTLEN(osh, p);
+ return (total);
+}
+
+/* return the last buffer of chained pkt */
+void *
+pktlast(osl_t *osh, void *p)
+{
+ for (; PKTNEXT(osh, p); p = PKTNEXT(osh, p))
+ ;
+
+ return (p);
+}
+
+/* count segments of a chained packet */
+uint
+pktsegcnt(osl_t *osh, void *p)
+{
+ uint cnt;
+
+ for (cnt = 0; p; p = PKTNEXT(osh, p))
+ cnt++;
+
+ return cnt;
+}
+
+
+/*
+ * osl multiple-precedence packet queue
+ * hi_prec is always >= the number of the highest non-empty precedence
+ */
+void *
+pktq_penq(struct pktq *pq, int prec, void *p)
+{
+ struct pktq_prec *q;
+
+ ASSERT(prec >= 0 && prec < pq->num_prec);
+ ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */
+
+ ASSERT(!pktq_full(pq));
+ ASSERT(!pktq_pfull(pq, prec));
+
+ q = &pq->q[prec];
+
+ if (q->head)
+ PKTSETLINK(q->tail, p);
+ else
+ q->head = p;
+
+ q->tail = p;
+ q->len++;
+
+ pq->len++;
+
+ if (pq->hi_prec < prec)
+ pq->hi_prec = (uint8)prec;
+
+ return p;
+}
+
+void *
+pktq_penq_head(struct pktq *pq, int prec, void *p)
+{
+ struct pktq_prec *q;
+
+ ASSERT(prec >= 0 && prec < pq->num_prec);
+ ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */
+
+ ASSERT(!pktq_full(pq));
+ ASSERT(!pktq_pfull(pq, prec));
+
+ q = &pq->q[prec];
+
+ if (q->head == NULL)
+ q->tail = p;
+
+ PKTSETLINK(p, q->head);
+ q->head = p;
+ q->len++;
+
+ pq->len++;
+
+ if (pq->hi_prec < prec)
+ pq->hi_prec = (uint8)prec;
+
+ return p;
+}
+
+void *
+pktq_pdeq(struct pktq *pq, int prec)
+{
+ struct pktq_prec *q;
+ void *p;
+
+ ASSERT(prec >= 0 && prec < pq->num_prec);
+
+ q = &pq->q[prec];
+
+ if ((p = q->head) == NULL)
+ return NULL;
+
+ if ((q->head = PKTLINK(p)) == NULL)
+ q->tail = NULL;
+
+ q->len--;
+
+ pq->len--;
+
+ PKTSETLINK(p, NULL);
+
+ return p;
+}
+
+void *
+pktq_pdeq_tail(struct pktq *pq, int prec)
+{
+ struct pktq_prec *q;
+ void *p, *prev;
+
+ ASSERT(prec >= 0 && prec < pq->num_prec);
+
+ q = &pq->q[prec];
+
+ if ((p = q->head) == NULL)
+ return NULL;
+
+ for (prev = NULL; p != q->tail; p = PKTLINK(p))
+ prev = p;
+
+ if (prev)
+ PKTSETLINK(prev, NULL);
+ else
+ q->head = NULL;
+
+ q->tail = prev;
+ q->len--;
+
+ pq->len--;
+
+ return p;
+}
+
+void
+pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir)
+{
+ struct pktq_prec *q;
+ void *p;
+
+ q = &pq->q[prec];
+ p = q->head;
+ while (p) {
+ q->head = PKTLINK(p);
+ PKTSETLINK(p, NULL);
+ PKTFREE(osh, p, dir);
+ q->len--;
+ pq->len--;
+ p = q->head;
+ }
+ ASSERT(q->len == 0);
+ q->tail = NULL;
+}
+
+bool
+pktq_pdel(struct pktq *pq, void *pktbuf, int prec)
+{
+ struct pktq_prec *q;
+ void *p;
+
+ ASSERT(prec >= 0 && prec < pq->num_prec);
+
+ if (!pktbuf)
+ return FALSE;
+
+ q = &pq->q[prec];
+
+ if (q->head == pktbuf) {
+ if ((q->head = PKTLINK(pktbuf)) == NULL)
+ q->tail = NULL;
+ } else {
+ for (p = q->head; p && PKTLINK(p) != pktbuf; p = PKTLINK(p))
+ ;
+ if (p == NULL)
+ return FALSE;
+
+ PKTSETLINK(p, PKTLINK(pktbuf));
+ if (q->tail == pktbuf)
+ q->tail = p;
+ }
+
+ q->len--;
+ pq->len--;
+ PKTSETLINK(pktbuf, NULL);
+ return TRUE;
+}
+
+void
+pktq_init(struct pktq *pq, int num_prec, int max_len)
+{
+ int prec;
+
+ ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC);
+
+ /* pq is variable size; only zero out what's requested */
+ bzero(pq, OFFSETOF(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec));
+
+ pq->num_prec = (uint16)num_prec;
+
+ pq->max = (uint16)max_len;
+
+ for (prec = 0; prec < num_prec; prec++)
+ pq->q[prec].max = pq->max;
+}
+
+void *
+pktq_deq(struct pktq *pq, int *prec_out)
+{
+ struct pktq_prec *q;
+ void *p;
+ int prec;
+
+ if (pq->len == 0)
+ return NULL;
+
+ while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
+ pq->hi_prec--;
+
+ q = &pq->q[prec];
+
+ if ((p = q->head) == NULL)
+ return NULL;
+
+ if ((q->head = PKTLINK(p)) == NULL)
+ q->tail = NULL;
+
+ q->len--;
+
+ pq->len--;
+
+ if (prec_out)
+ *prec_out = prec;
+
+ PKTSETLINK(p, NULL);
+
+ return p;
+}
+
+void *
+pktq_deq_tail(struct pktq *pq, int *prec_out)
+{
+ struct pktq_prec *q;
+ void *p, *prev;
+ int prec;
+
+ if (pq->len == 0)
+ return NULL;
+
+ for (prec = 0; prec < pq->hi_prec; prec++)
+ if (pq->q[prec].head)
+ break;
+
+ q = &pq->q[prec];
+
+ if ((p = q->head) == NULL)
+ return NULL;
+
+ for (prev = NULL; p != q->tail; p = PKTLINK(p))
+ prev = p;
+
+ if (prev)
+ PKTSETLINK(prev, NULL);
+ else
+ q->head = NULL;
+
+ q->tail = prev;
+ q->len--;
+
+ pq->len--;
+
+ if (prec_out)
+ *prec_out = prec;
+
+ PKTSETLINK(p, NULL);
+
+ return p;
+}
+
+void *
+pktq_peek(struct pktq *pq, int *prec_out)
+{
+ int prec;
+
+ if (pq->len == 0)
+ return NULL;
+
+ while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
+ pq->hi_prec--;
+
+ if (prec_out)
+ *prec_out = prec;
+
+ return (pq->q[prec].head);
+}
+
+void *
+pktq_peek_tail(struct pktq *pq, int *prec_out)
+{
+ int prec;
+
+ if (pq->len == 0)
+ return NULL;
+
+ for (prec = 0; prec < pq->hi_prec; prec++)
+ if (pq->q[prec].head)
+ break;
+
+ if (prec_out)
+ *prec_out = prec;
+
+ return (pq->q[prec].tail);
+}
+
+void
+pktq_flush(osl_t *osh, struct pktq *pq, bool dir)
+{
+ int prec;
+ for (prec = 0; prec < pq->num_prec; prec++)
+ pktq_pflush(osh, pq, prec, dir);
+ ASSERT(pq->len == 0);
+}
+
+/* Return sum of lengths of a specific set of precedences */
+int
+pktq_mlen(struct pktq *pq, uint prec_bmp)
+{
+ int prec, len;
+
+ len = 0;
+
+ for (prec = 0; prec <= pq->hi_prec; prec++)
+ if (prec_bmp & (1 << prec))
+ len += pq->q[prec].len;
+
+ return len;
+}
+
+/* Priority dequeue from a specific set of precedences */
+void *
+pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out)
+{
+ struct pktq_prec *q;
+ void *p;
+ int prec;
+
+ if (pq->len == 0)
+ return NULL;
+
+ while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL)
+ pq->hi_prec--;
+
+ while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL)
+ if (prec-- == 0)
+ return NULL;
+
+ q = &pq->q[prec];
+
+ if ((p = q->head) == NULL)
+ return NULL;
+
+ if ((q->head = PKTLINK(p)) == NULL)
+ q->tail = NULL;
+
+ q->len--;
+
+ if (prec_out)
+ *prec_out = prec;
+
+ pq->len--;
+
+ PKTSETLINK(p, NULL);
+
+ return p;
+}
+#endif /* BCMDRIVER */
+
+
+
+const unsigned char bcm_ctype[] = {
+ _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 0-7 */
+ _BCM_C, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C,
+ _BCM_C, /* 8-15 */
+ _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 16-23 */
+ _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 24-31 */
+ _BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 32-39 */
+ _BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 40-47 */
+ _BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D, /* 48-55 */
+ _BCM_D,_BCM_D,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 56-63 */
+ _BCM_P, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X,
+ _BCM_U|_BCM_X, _BCM_U, /* 64-71 */
+ _BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 72-79 */
+ _BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 80-87 */
+ _BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 88-95 */
+ _BCM_P, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X,
+ _BCM_L|_BCM_X, _BCM_L, /* 96-103 */
+ _BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 104-111 */
+ _BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 112-119 */
+ _BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_C, /* 120-127 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128-143 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144-159 */
+ _BCM_S|_BCM_SP, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,
+ _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 160-175 */
+ _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P,
+ _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 176-191 */
+ _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U,
+ _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, /* 192-207 */
+ _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_P, _BCM_U, _BCM_U, _BCM_U,
+ _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_L, /* 208-223 */
+ _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L,
+ _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, /* 224-239 */
+ _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_P, _BCM_L, _BCM_L, _BCM_L,
+ _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L /* 240-255 */
+};
+
+ulong
+bcm_strtoul(char *cp, char **endp, uint base)
+{
+ ulong result, last_result = 0, value;
+ bool minus;
+
+ minus = FALSE;
+
+ while (bcm_isspace(*cp))
+ cp++;
+
+ if (cp[0] == '+')
+ cp++;
+ else if (cp[0] == '-') {
+ minus = TRUE;
+ cp++;
+ }
+
+ if (base == 0) {
+ if (cp[0] == '0') {
+ if ((cp[1] == 'x') || (cp[1] == 'X')) {
+ base = 16;
+ cp = &cp[2];
+ } else {
+ base = 8;
+ cp = &cp[1];
+ }
+ } else
+ base = 10;
+ } else if (base == 16 && (cp[0] == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) {
+ cp = &cp[2];
+ }
+
+ result = 0;
+
+ while (bcm_isxdigit(*cp) &&
+ (value = bcm_isdigit(*cp) ? *cp-'0' : bcm_toupper(*cp)-'A'+10) < base) {
+ result = result*base + value;
+ /* Detected overflow */
+ if (result < last_result && !minus)
+ return (ulong)-1;
+ last_result = result;
+ cp++;
+ }
+
+ if (minus)
+ result = (ulong)(-(long)result);
+
+ if (endp)
+ *endp = (char *)cp;
+
+ return (result);
+}
+
+int
+bcm_atoi(char *s)
+{
+ return (int)bcm_strtoul(s, NULL, 10);
+}
+
+/* return pointer to location of substring 'needle' in 'haystack' */
+char*
+bcmstrstr(char *haystack, char *needle)
+{
+ int len, nlen;
+ int i;
+
+ if ((haystack == NULL) || (needle == NULL))
+ return (haystack);
+
+ nlen = strlen(needle);
+ len = strlen(haystack) - nlen + 1;
+
+ for (i = 0; i < len; i++)
+ if (memcmp(needle, &haystack[i], nlen) == 0)
+ return (&haystack[i]);
+ return (NULL);
+}
+
+char*
+bcmstrcat(char *dest, const char *src)
+{
+ char *p;
+
+ p = dest + strlen(dest);
+
+ while ((*p++ = *src++) != '\0')
+ ;
+
+ return (dest);
+}
+
+char*
+bcmstrncat(char *dest, const char *src, uint size)
+{
+ char *endp;
+ char *p;
+
+ p = dest + strlen(dest);
+ endp = p + size;
+
+ while (p != endp && (*p++ = *src++) != '\0')
+ ;
+
+ return (dest);
+}
+
+
+/****************************************************************************
+* Function: bcmstrtok
+*
+* Purpose:
+* Tokenizes a string. This function is conceptually similiar to ANSI C strtok(),
+* but allows strToken() to be used by different strings or callers at the same
+* time. Each call modifies '*string' by substituting a NULL character for the
+* first delimiter that is encountered, and updates 'string' to point to the char
+* after the delimiter. Leading delimiters are skipped.
+*
+* Parameters:
+* string (mod) Ptr to string ptr, updated by token.
+* delimiters (in) Set of delimiter characters.
+* tokdelim (out) Character that delimits the returned token. (May
+* be set to NULL if token delimiter is not required).
+*
+* Returns: Pointer to the next token found. NULL when no more tokens are found.
+*****************************************************************************
+*/
+char *
+bcmstrtok(char **string, const char *delimiters, char *tokdelim)
+{
+ unsigned char *str;
+ unsigned long map[8];
+ int count;
+ char *nextoken;
+
+ if (tokdelim != NULL) {
+ /* Prime the token delimiter */
+ *tokdelim = '\0';
+ }
+
+ /* Clear control map */
+ for (count = 0; count < 8; count++) {
+ map[count] = 0;
+ }
+
+ /* Set bits in delimiter table */
+ do {
+ map[*delimiters >> 5] |= (1 << (*delimiters & 31));
+ }
+ while (*delimiters++);
+
+ str = (unsigned char*)*string;
+
+ /* Find beginning of token (skip over leading delimiters). Note that
+ * there is no token iff this loop sets str to point to the terminal
+ * null (*str == '\0')
+ */
+ while (((map[*str >> 5] & (1 << (*str & 31))) && *str) || (*str == ' ')) {
+ str++;
+ }
+
+ nextoken = (char*)str;
+
+ /* Find the end of the token. If it is not the end of the string,
+ * put a null there.
+ */
+ for (; *str; str++) {
+ if (map[*str >> 5] & (1 << (*str & 31))) {
+ if (tokdelim != NULL) {
+ *tokdelim = *str;
+ }
+
+ *str++ = '\0';
+ break;
+ }
+ }
+
+ *string = (char*)str;
+
+ /* Determine if a token has been found. */
+ if (nextoken == (char *) str) {
+ return NULL;
+ }
+ else {
+ return nextoken;
+ }
+}
+
+
+#define xToLower(C) \
+ ((C >= 'A' && C <= 'Z') ? (char)((int)C - (int)'A' + (int)'a') : C)
+
+
+/****************************************************************************
+* Function: bcmstricmp
+*
+* Purpose: Compare to strings case insensitively.
+*
+* Parameters: s1 (in) First string to compare.
+* s2 (in) Second string to compare.
+*
+* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if
+* t1 > t2, when ignoring case sensitivity.
+*****************************************************************************
+*/
+int
+bcmstricmp(const char *s1, const char *s2)
+{
+ char dc, sc;
+
+ while (*s2 && *s1) {
+ dc = xToLower(*s1);
+ sc = xToLower(*s2);
+ if (dc < sc) return -1;
+ if (dc > sc) return 1;
+ s1++;
+ s2++;
+ }
+
+ if (*s1 && !*s2) return 1;
+ if (!*s1 && *s2) return -1;
+ return 0;
+}
+
+
+/****************************************************************************
+* Function: bcmstrnicmp
+*
+* Purpose: Compare to strings case insensitively, upto a max of 'cnt'
+* characters.
+*
+* Parameters: s1 (in) First string to compare.
+* s2 (in) Second string to compare.
+* cnt (in) Max characters to compare.
+*
+* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if
+* t1 > t2, when ignoring case sensitivity.
+*****************************************************************************
+*/
+int
+bcmstrnicmp(const char* s1, const char* s2, int cnt)
+{
+ char dc, sc;
+
+ while (*s2 && *s1 && cnt) {
+ dc = xToLower(*s1);
+ sc = xToLower(*s2);
+ if (dc < sc) return -1;
+ if (dc > sc) return 1;
+ s1++;
+ s2++;
+ cnt--;
+ }
+
+ if (!cnt) return 0;
+ if (*s1 && !*s2) return 1;
+ if (!*s1 && *s2) return -1;
+ return 0;
+}
+
+/* parse a xx:xx:xx:xx:xx:xx format ethernet address */
+int
+bcm_ether_atoe(char *p, struct ether_addr *ea)
+{
+ int i = 0;
+
+ for (;;) {
+ ea->octet[i++] = (char) bcm_strtoul(p, &p, 16);
+ if (!*p++ || i == 6)
+ break;
+ }
+
+ return (i == 6);
+}
+
+
+#if defined(CONFIG_USBRNDIS_RETAIL) || defined(NDIS_MINIPORT_DRIVER)
+/* registry routine buffer preparation utility functions:
+ * parameter order is like strncpy, but returns count
+ * of bytes copied. Minimum bytes copied is null char(1)/wchar(2)
+ */
+ulong
+wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen)
+{
+ ulong copyct = 1;
+ ushort i;
+
+ if (abuflen == 0)
+ return 0;
+
+ /* wbuflen is in bytes */
+ wbuflen /= sizeof(ushort);
+
+ for (i = 0; i < wbuflen; ++i) {
+ if (--abuflen == 0)
+ break;
+ *abuf++ = (char) *wbuf++;
+ ++copyct;
+ }
+ *abuf = '\0';
+
+ return copyct;
+}
+#endif /* CONFIG_USBRNDIS_RETAIL || NDIS_MINIPORT_DRIVER */
+
+char *
+bcm_ether_ntoa(const struct ether_addr *ea, char *buf)
+{
+ static const char template[] = "%02x:%02x:%02x:%02x:%02x:%02x";
+ snprintf(buf, 18, template,
+ ea->octet[0]&0xff, ea->octet[1]&0xff, ea->octet[2]&0xff,
+ ea->octet[3]&0xff, ea->octet[4]&0xff, ea->octet[5]&0xff);
+ return (buf);
+}
+
+char *
+bcm_ip_ntoa(struct ipv4_addr *ia, char *buf)
+{
+ snprintf(buf, 16, "%d.%d.%d.%d",
+ ia->addr[0], ia->addr[1], ia->addr[2], ia->addr[3]);
+ return (buf);
+}
+
+#ifdef BCMDRIVER
+
+void
+bcm_mdelay(uint ms)
+{
+ uint i;
+
+ for (i = 0; i < ms; i++) {
+ OSL_DELAY(1000);
+ }
+}
+
+
+
+
+
+
+#if defined(DHD_DEBUG)
+/* pretty hex print a pkt buffer chain */
+void
+prpkt(const char *msg, osl_t *osh, void *p0)
+{
+ void *p;
+
+ if (msg && (msg[0] != '\0'))
+ printf("%s:\n", msg);
+
+ for (p = p0; p; p = PKTNEXT(osh, p))
+ prhex(NULL, PKTDATA(osh, p), PKTLEN(osh, p));
+}
+#endif
+
+/* Takes an Ethernet frame and sets out-of-bound PKTPRIO.
+ * Also updates the inplace vlan tag if requested.
+ * For debugging, it returns an indication of what it did.
+ */
+uint
+pktsetprio(void *pkt, bool update_vtag)
+{
+ struct ether_header *eh;
+ struct ethervlan_header *evh;
+ uint8 *pktdata;
+ int priority = 0;
+ int rc = 0;
+
+ pktdata = (uint8 *) PKTDATA(NULL, pkt);
+ ASSERT(ISALIGNED((uintptr)pktdata, sizeof(uint16)));
+
+ eh = (struct ether_header *) pktdata;
+
+ if (ntoh16(eh->ether_type) == ETHER_TYPE_8021Q) {
+ uint16 vlan_tag;
+ int vlan_prio, dscp_prio = 0;
+
+ evh = (struct ethervlan_header *)eh;
+
+ vlan_tag = ntoh16(evh->vlan_tag);
+ vlan_prio = (int) (vlan_tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK;
+
+ if (ntoh16(evh->ether_type) == ETHER_TYPE_IP) {
+ uint8 *ip_body = pktdata + sizeof(struct ethervlan_header);
+ uint8 tos_tc = IP_TOS(ip_body);
+ dscp_prio = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);
+ }
+
+ /* DSCP priority gets precedence over 802.1P (vlan tag) */
+ if (dscp_prio != 0) {
+ priority = dscp_prio;
+ rc |= PKTPRIO_VDSCP;
+ } else {
+ priority = vlan_prio;
+ rc |= PKTPRIO_VLAN;
+ }
+ /*
+ * If the DSCP priority is not the same as the VLAN priority,
+ * then overwrite the priority field in the vlan tag, with the
+ * DSCP priority value. This is required for Linux APs because
+ * the VLAN driver on Linux, overwrites the skb->priority field
+ * with the priority value in the vlan tag
+ */
+ if (update_vtag && (priority != vlan_prio)) {
+ vlan_tag &= ~(VLAN_PRI_MASK << VLAN_PRI_SHIFT);
+ vlan_tag |= (uint16)priority << VLAN_PRI_SHIFT;
+ evh->vlan_tag = hton16(vlan_tag);
+ rc |= PKTPRIO_UPD;
+ }
+ } else if (ntoh16(eh->ether_type) == ETHER_TYPE_IP) {
+ uint8 *ip_body = pktdata + sizeof(struct ether_header);
+ uint8 tos_tc = IP_TOS(ip_body);
+ priority = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT);
+ rc |= PKTPRIO_DSCP;
+ }
+
+ ASSERT(priority >= 0 && priority <= MAXPRIO);
+ PKTSETPRIO(pkt, priority);
+ return (rc | priority);
+}
+
+static char bcm_undeferrstr[BCME_STRLEN];
+
+static const char *bcmerrorstrtable[] = BCMERRSTRINGTABLE;
+
+/* Convert the error codes into related error strings */
+const char *
+bcmerrorstr(int bcmerror)
+{
+ /* check if someone added a bcmerror code but forgot to add errorstring */
+ ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(bcmerrorstrtable) - 1));
+
+ if (bcmerror > 0 || bcmerror < BCME_LAST) {
+ snprintf(bcm_undeferrstr, BCME_STRLEN, "Undefined error %d", bcmerror);
+ return bcm_undeferrstr;
+ }
+
+ ASSERT(strlen(bcmerrorstrtable[-bcmerror]) < BCME_STRLEN);
+
+ return bcmerrorstrtable[-bcmerror];
+}
+
+
+
+/* iovar table lookup */
+const bcm_iovar_t*
+bcm_iovar_lookup(const bcm_iovar_t *table, const char *name)
+{
+ const bcm_iovar_t *vi;
+ const char *lookup_name;
+
+ /* skip any ':' delimited option prefixes */
+ lookup_name = strrchr(name, ':');
+ if (lookup_name != NULL)
+ lookup_name++;
+ else
+ lookup_name = name;
+
+ ASSERT(table != NULL);
+
+ for (vi = table; vi->name; vi++) {
+ if (!strcmp(vi->name, lookup_name))
+ return vi;
+ }
+ /* ran to end of table */
+
+ return NULL; /* var name not found */
+}
+
+int
+bcm_iovar_lencheck(const bcm_iovar_t *vi, void *arg, int len, bool set)
+{
+ int bcmerror = 0;
+
+ /* length check on io buf */
+ switch (vi->type) {
+ case IOVT_BOOL:
+ case IOVT_INT8:
+ case IOVT_INT16:
+ case IOVT_INT32:
+ case IOVT_UINT8:
+ case IOVT_UINT16:
+ case IOVT_UINT32:
+ /* all integers are int32 sized args at the ioctl interface */
+ if (len < (int)sizeof(int)) {
+ bcmerror = BCME_BUFTOOSHORT;
+ }
+ break;
+
+ case IOVT_BUFFER:
+ /* buffer must meet minimum length requirement */
+ if (len < vi->minlen) {
+ bcmerror = BCME_BUFTOOSHORT;
+ }
+ break;
+
+ case IOVT_VOID:
+ if (!set) {
+ /* Cannot return nil... */
+ bcmerror = BCME_UNSUPPORTED;
+ } else if (len) {
+ /* Set is an action w/o parameters */
+ bcmerror = BCME_BUFTOOLONG;
+ }
+ break;
+
+ default:
+ /* unknown type for length check in iovar info */
+ ASSERT(0);
+ bcmerror = BCME_UNSUPPORTED;
+ }
+
+ return bcmerror;
+}
+
+#endif /* BCMDRIVER */
+
+/*******************************************************************************
+ * crc8
+ *
+ * Computes a crc8 over the input data using the polynomial:
+ *
+ * x^8 + x^7 +x^6 + x^4 + x^2 + 1
+ *
+ * The caller provides the initial value (either CRC8_INIT_VALUE
+ * or the previous returned value) to allow for processing of
+ * discontiguous blocks of data. When generating the CRC the
+ * caller is responsible for complementing the final return value
+ * and inserting it into the byte stream. When checking, a final
+ * return value of CRC8_GOOD_VALUE indicates a valid CRC.
+ *
+ * Reference: Dallas Semiconductor Application Note 27
+ * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
+ * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
+ * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
+ *
+ * ****************************************************************************
+ */
+
+STATIC const uint8 crc8_table[256] = {
+ 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
+ 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
+ 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
+ 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
+ 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
+ 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
+ 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
+ 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
+ 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
+ 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
+ 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
+ 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
+ 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
+ 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
+ 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
+ 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
+ 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
+ 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
+ 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
+ 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
+ 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
+ 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
+ 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
+ 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
+ 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
+ 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
+ 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
+ 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
+ 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
+ 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
+ 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
+ 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F
+};
+
+#define CRC_INNER_LOOP(n, c, x) \
+ (c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff]
+
+uint8
+hndcrc8(
+ uint8 *pdata, /* pointer to array of data to process */
+ uint nbytes, /* number of input data bytes to process */
+ uint8 crc /* either CRC8_INIT_VALUE or previous return value */
+)
+{
+ /* hard code the crc loop instead of using CRC_INNER_LOOP macro
+ * to avoid the undefined and unnecessary (uint8 >> 8) operation.
+ */
+ while (nbytes-- > 0)
+ crc = crc8_table[(crc ^ *pdata++) & 0xff];
+
+ return crc;
+}
+
+/*******************************************************************************
+ * crc16
+ *
+ * Computes a crc16 over the input data using the polynomial:
+ *
+ * x^16 + x^12 +x^5 + 1
+ *
+ * The caller provides the initial value (either CRC16_INIT_VALUE
+ * or the previous returned value) to allow for processing of
+ * discontiguous blocks of data. When generating the CRC the
+ * caller is responsible for complementing the final return value
+ * and inserting it into the byte stream. When checking, a final
+ * return value of CRC16_GOOD_VALUE indicates a valid CRC.
+ *
+ * Reference: Dallas Semiconductor Application Note 27
+ * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
+ * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
+ * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
+ *
+ * ****************************************************************************
+ */
+
+static const uint16 crc16_table[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
+ 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
+ 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
+ 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
+ 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
+ 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
+ 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
+ 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
+ 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
+ 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
+ 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
+ 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
+ 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
+ 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
+ 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
+ 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
+ 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
+ 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
+ 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
+ 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
+ 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
+ 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
+ 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
+ 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
+ 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
+ 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
+ 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
+ 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
+ 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
+ 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
+ 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
+ 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
+};
+
+uint16
+hndcrc16(
+ uint8 *pdata, /* pointer to array of data to process */
+ uint nbytes, /* number of input data bytes to process */
+ uint16 crc /* either CRC16_INIT_VALUE or previous return value */
+)
+{
+ while (nbytes-- > 0)
+ CRC_INNER_LOOP(16, crc, *pdata++);
+ return crc;
+}
+
+STATIC const uint32 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
+};
+
+uint32
+hndcrc32(
+ uint8 *pdata, /* pointer to array of data to process */
+ uint nbytes, /* number of input data bytes to process */
+ uint32 crc /* either CRC32_INIT_VALUE or previous return value */
+)
+{
+ uint8 *pend;
+#ifdef __mips__
+ uint8 tmp[4];
+ ulong *tptr = (ulong *)tmp;
+
+ /* in case the beginning of the buffer isn't aligned */
+ pend = (uint8 *)((uint)(pdata + 3) & 0xfffffffc);
+ nbytes -= (pend - pdata);
+ while (pdata < pend)
+ CRC_INNER_LOOP(32, crc, *pdata++);
+
+ /* handle bulk of data as 32-bit words */
+ pend = pdata + (nbytes & 0xfffffffc);
+ while (pdata < pend) {
+ *tptr = *(ulong *)pdata;
+ pdata += sizeof(ulong *);
+ CRC_INNER_LOOP(32, crc, tmp[0]);
+ CRC_INNER_LOOP(32, crc, tmp[1]);
+ CRC_INNER_LOOP(32, crc, tmp[2]);
+ CRC_INNER_LOOP(32, crc, tmp[3]);
+ }
+
+ /* 1-3 bytes at end of buffer */
+ pend = pdata + (nbytes & 0x03);
+ while (pdata < pend)
+ CRC_INNER_LOOP(32, crc, *pdata++);
+#else
+ pend = pdata + nbytes;
+ while (pdata < pend)
+ CRC_INNER_LOOP(32, crc, *pdata++);
+#endif /* __mips__ */
+
+ return crc;
+}
+
+#ifdef notdef
+#define CLEN 1499 /* CRC Length */
+#define CBUFSIZ (CLEN+4)
+#define CNBUFS 5 /* # of bufs */
+
+void testcrc32(void)
+{
+ uint j, k, l;
+ uint8 *buf;
+ uint len[CNBUFS];
+ uint32 crcr;
+ uint32 crc32tv[CNBUFS] =
+ {0xd2cb1faa, 0xd385c8fa, 0xf5b4f3f3, 0x55789e20, 0x00343110};
+
+ ASSERT((buf = MALLOC(CBUFSIZ*CNBUFS)) != NULL);
+
+ /* step through all possible alignments */
+ for (l = 0; l <= 4; l++) {
+ for (j = 0; j < CNBUFS; j++) {
+ len[j] = CLEN;
+ for (k = 0; k < len[j]; k++)
+ *(buf + j*CBUFSIZ + (k+l)) = (j+k) & 0xff;
+ }
+
+ for (j = 0; j < CNBUFS; j++) {
+ crcr = crc32(buf + j*CBUFSIZ + l, len[j], CRC32_INIT_VALUE);
+ ASSERT(crcr == crc32tv[j]);
+ }
+ }
+
+ MFREE(buf, CBUFSIZ*CNBUFS);
+ return;
+}
+#endif /* notdef */
+
+/*
+ * Advance from the current 1-byte tag/1-byte length/variable-length value
+ * triple, to the next, returning a pointer to the next.
+ * If the current or next TLV is invalid (does not fit in given buffer length),
+ * NULL is returned.
+ * *buflen is not modified if the TLV elt parameter is invalid, or is decremented
+ * by the TLV parameter's length if it is valid.
+ */
+bcm_tlv_t *
+bcm_next_tlv(bcm_tlv_t *elt, int *buflen)
+{
+ int len;
+
+ /* validate current elt */
+ if (!bcm_valid_tlv(elt, *buflen))
+ return NULL;
+
+ /* advance to next elt */
+ len = elt->len;
+ elt = (bcm_tlv_t*)(elt->data + len);
+ *buflen -= (2 + len);
+
+ /* validate next elt */
+ if (!bcm_valid_tlv(elt, *buflen))
+ return NULL;
+
+ return elt;
+}
+
+/*
+ * Traverse a string of 1-byte tag/1-byte length/variable-length value
+ * triples, returning a pointer to the substring whose first element
+ * matches tag
+ */
+bcm_tlv_t *
+bcm_parse_tlvs(void *buf, int buflen, uint key)
+{
+ bcm_tlv_t *elt;
+ int totlen;
+
+ elt = (bcm_tlv_t*)buf;
+ totlen = buflen;
+
+ /* find tagged parameter */
+ while (totlen >= 2) {
+ int len = elt->len;
+
+ /* validate remaining totlen */
+ if ((elt->id == key) && (totlen >= (len + 2)))
+ return (elt);
+
+ elt = (bcm_tlv_t*)((uint8*)elt + (len + 2));
+ totlen -= (len + 2);
+ }
+
+ return NULL;
+}
+
+/*
+ * Traverse a string of 1-byte tag/1-byte length/variable-length value
+ * triples, returning a pointer to the substring whose first element
+ * matches tag. Stop parsing when we see an element whose ID is greater
+ * than the target key.
+ */
+bcm_tlv_t *
+bcm_parse_ordered_tlvs(void *buf, int buflen, uint key)
+{
+ bcm_tlv_t *elt;
+ int totlen;
+
+ elt = (bcm_tlv_t*)buf;
+ totlen = buflen;
+
+ /* find tagged parameter */
+ while (totlen >= 2) {
+ uint id = elt->id;
+ int len = elt->len;
+
+ /* Punt if we start seeing IDs > than target key */
+ if (id > key)
+ return (NULL);
+
+ /* validate remaining totlen */
+ if ((id == key) && (totlen >= (len + 2)))
+ return (elt);
+
+ elt = (bcm_tlv_t*)((uint8*)elt + (len + 2));
+ totlen -= (len + 2);
+ }
+ return NULL;
+}
+
+#if defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || defined(WLMSG_ASSOC) || \
+ defined(DHD_DEBUG)
+int
+bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len)
+{
+ int i;
+ char* p = buf;
+ char hexstr[16];
+ int slen = 0;
+ uint32 bit;
+ const char* name;
+
+ if (len < 2 || !buf)
+ return 0;
+
+ buf[0] = '\0';
+ len -= 1;
+
+ for (i = 0; flags != 0; i++) {
+ bit = bd[i].bit;
+ name = bd[i].name;
+ if (bit == 0 && flags) {
+ /* print any unnamed bits */
+ sprintf(hexstr, "0x%X", flags);
+ name = hexstr;
+ flags = 0; /* exit loop */
+ } else if ((flags & bit) == 0)
+ continue;
+ slen += strlen(name);
+ if (len < slen)
+ break;
+ if (p != buf) p += sprintf(p, " "); /* btwn flag space */
+ strcat(p, name);
+ p += strlen(name);
+ flags &= ~bit;
+ len -= slen;
+ slen = 1; /* account for btwn flag space */
+ }
+
+ /* indicate the str was too short */
+ if (flags != 0) {
+ if (len == 0)
+ p--; /* overwrite last char */
+ p += sprintf(p, ">");
+ }
+
+ return (int)(p - buf);
+}
+
+/* print bytes formatted as hex to a string. return the resulting string length */
+int
+bcm_format_hex(char *str, const void *bytes, int len)
+{
+ int i;
+ char *p = str;
+ const uint8 *src = (const uint8*)bytes;
+
+ for (i = 0; i < len; i++) {
+ p += sprintf(p, "%02X", *src);
+ src++;
+ }
+ return (int)(p - str);
+}
+
+/* pretty hex print a contiguous buffer */
+void
+prhex(const char *msg, uchar *buf, uint nbytes)
+{
+ char line[128], *p;
+ uint i;
+
+ if (msg && (msg[0] != '\0'))
+ printf("%s:\n", msg);
+
+ p = line;
+ for (i = 0; i < nbytes; i++) {
+ if (i % 16 == 0) {
+ p += sprintf(p, " %04d: ", i); /* line prefix */
+ }
+ p += sprintf(p, "%02x ", buf[i]);
+ if (i % 16 == 15) {
+ printf("%s\n", line); /* flush line */
+ p = line;
+ }
+ }
+
+ /* flush last partial line */
+ if (p != line)
+ printf("%s\n", line);
+}
+#endif
+
+
+/* Produce a human-readable string for boardrev */
+char *
+bcm_brev_str(uint32 brev, char *buf)
+{
+ if (brev < 0x100)
+ snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf);
+ else
+ snprintf(buf, 8, "%c%03x", ((brev & 0xf000) == 0x1000) ? 'P' : 'A', brev & 0xfff);
+
+ return (buf);
+}
+
+#define BUFSIZE_TODUMP_ATONCE 512 /* Buffer size */
+
+/* dump large strings to console */
+void
+printbig(char *buf)
+{
+ uint len, max_len;
+ char c;
+
+ len = strlen(buf);
+
+ max_len = BUFSIZE_TODUMP_ATONCE;
+
+ while (len > max_len) {
+ c = buf[max_len];
+ buf[max_len] = '\0';
+ printf("%s", buf);
+ buf[max_len] = c;
+
+ buf += max_len;
+ len -= max_len;
+ }
+ /* print the remaining string */
+ printf("%s\n", buf);
+ return;
+}
+
+/* routine to dump fields in a fileddesc structure */
+uint
+bcmdumpfields(bcmutl_rdreg_rtn read_rtn, void *arg0, uint arg1, struct fielddesc *fielddesc_array,
+ char *buf, uint32 bufsize)
+{
+ uint filled_len;
+ int len;
+ struct fielddesc *cur_ptr;
+
+ filled_len = 0;
+ cur_ptr = fielddesc_array;
+
+ while (bufsize > 1) {
+ if (cur_ptr->nameandfmt == NULL)
+ break;
+ len = snprintf(buf, bufsize, cur_ptr->nameandfmt,
+ read_rtn(arg0, arg1, cur_ptr->offset));
+ /* check for snprintf overflow or error */
+ if (len < 0 || (uint32)len >= bufsize)
+ len = bufsize - 1;
+ buf += len;
+ bufsize -= len;
+ filled_len += len;
+ cur_ptr++;
+ }
+ return filled_len;
+}
+
+uint
+bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen)
+{
+ uint len;
+
+ len = strlen(name) + 1;
+
+ if ((len + datalen) > buflen)
+ return 0;
+
+ strncpy(buf, name, buflen);
+
+ /* append data onto the end of the name string */
+ memcpy(&buf[len], data, datalen);
+ len += datalen;
+
+ return len;
+}
+
+/* Quarter dBm units to mW
+ * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
+ * Table is offset so the last entry is largest mW value that fits in
+ * a uint16.
+ */
+
+#define QDBM_OFFSET 153 /* Offset for first entry */
+#define QDBM_TABLE_LEN 40 /* Table size */
+
+/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
+ * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
+ */
+#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
+
+/* Largest mW value that will round down to the last table entry,
+ * QDBM_OFFSET + QDBM_TABLE_LEN-1.
+ * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
+ */
+#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
+
+static const uint16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
+/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
+/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
+/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
+/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
+/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
+/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
+};
+
+uint16
+bcm_qdbm_to_mw(uint8 qdbm)
+{
+ uint factor = 1;
+ int idx = qdbm - QDBM_OFFSET;
+
+ if (idx >= QDBM_TABLE_LEN) {
+ /* clamp to max uint16 mW value */
+ return 0xFFFF;
+ }
+
+ /* scale the qdBm index up to the range of the table 0-40
+ * where an offset of 40 qdBm equals a factor of 10 mW.
+ */
+ while (idx < 0) {
+ idx += 40;
+ factor *= 10;
+ }
+
+ /* return the mW value scaled down to the correct factor of 10,
+ * adding in factor/2 to get proper rounding.
+ */
+ return ((nqdBm_to_mW_map[idx] + factor/2) / factor);
+}
+
+uint8
+bcm_mw_to_qdbm(uint16 mw)
+{
+ uint8 qdbm;
+ int offset;
+ uint mw_uint = mw;
+ uint boundary;
+
+ /* handle boundary case */
+ if (mw_uint <= 1)
+ return 0;
+
+ offset = QDBM_OFFSET;
+
+ /* move mw into the range of the table */
+ while (mw_uint < QDBM_TABLE_LOW_BOUND) {
+ mw_uint *= 10;
+ offset -= 40;
+ }
+
+ for (qdbm = 0; qdbm < QDBM_TABLE_LEN-1; qdbm++) {
+ boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm+1] -
+ nqdBm_to_mW_map[qdbm])/2;
+ if (mw_uint < boundary) break;
+ }
+
+ qdbm += (uint8)offset;
+
+ return (qdbm);
+}
+
+
+uint
+bcm_bitcount(uint8 *bitmap, uint length)
+{
+ uint bitcount = 0, i;
+ uint8 tmp;
+ for (i = 0; i < length; i++) {
+ tmp = bitmap[i];
+ while (tmp) {
+ bitcount++;
+ tmp &= (tmp - 1);
+ }
+ }
+ return bitcount;
+}
+
+#ifdef BCMDRIVER
+
+/* Initialization of bcmstrbuf structure */
+void
+bcm_binit(struct bcmstrbuf *b, char *buf, uint size)
+{
+ b->origsize = b->size = size;
+ b->origbuf = b->buf = buf;
+}
+
+/* Buffer sprintf wrapper to guard against buffer overflow */
+int
+bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...)
+{
+ va_list ap;
+ int r;
+
+ va_start(ap, fmt);
+ r = vsnprintf(b->buf, b->size, fmt, ap);
+
+ /* Non Ansi C99 compliant returns -1,
+ * Ansi compliant return r >= b->size,
+ * bcmstdlib returns 0, handle all
+ */
+ if ((r == -1) || (r >= (int)b->size) || (r == 0)) {
+ b->size = 0;
+ } else {
+ b->size -= r;
+ b->buf += r;
+ }
+
+ va_end(ap);
+
+ return r;
+}
+
+void
+bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount)
+{
+ int i;
+
+ for (i = 0; i < num_bytes; i++) {
+ num[i] += amount;
+ if (num[i] >= amount)
+ break;
+ amount = 1;
+ }
+}
+
+int
+bcm_cmp_bytes(uchar *arg1, uchar *arg2, uint8 nbytes)
+{
+ int i;
+
+ for (i = nbytes - 1; i >= 0; i--) {
+ if (arg1[i] != arg2[i])
+ return (arg1[i] - arg2[i]);
+ }
+ return 0;
+}
+
+void
+bcm_print_bytes(char *name, const uchar *data, int len)
+{
+ int i;
+ int per_line = 0;
+
+ printf("%s: %d \n", name ? name : "", len);
+ for (i = 0; i < len; i++) {
+ printf("%02x ", *data++);
+ per_line++;
+ if (per_line == 16) {
+ per_line = 0;
+ printf("\n");
+ }
+ }
+ printf("\n");
+}
+
+/*
+ * buffer length needed for wlc_format_ssid
+ * 32 SSID chars, max of 4 chars for each SSID char "\xFF", plus NULL.
+ */
+
+#if defined(WLTINYDUMP) || defined(WLMSG_INFORM) || defined(WLMSG_ASSOC) || \
+ defined(WLMSG_PRPKT) || defined(WLMSG_WSEC)
+int
+bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len)
+{
+ uint i, c;
+ char *p = buf;
+ char *endp = buf + SSID_FMT_BUF_LEN;
+
+ if (ssid_len > DOT11_MAX_SSID_LEN) ssid_len = DOT11_MAX_SSID_LEN;
+
+ for (i = 0; i < ssid_len; i++) {
+ c = (uint)ssid[i];
+ if (c == '\\') {
+ *p++ = '\\';
+ *p++ = '\\';
+ } else if (bcm_isprint((uchar)c)) {
+ *p++ = (char)c;
+ } else {
+ p += snprintf(p, (endp - p), "\\x%02X", c);
+ }
+ }
+ *p = '\0';
+ ASSERT(p < endp);
+
+ return (int)(p - buf);
+}
+#endif
+
+#endif /* BCMDRIVER */
diff --git a/drivers/net/wireless/bcm4329/bcmwifi.c b/drivers/net/wireless/bcm4329/bcmwifi.c
new file mode 100644
index 00000000000..803acf842a2
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmwifi.c
@@ -0,0 +1,199 @@
+/*
+ * Misc utility routines used by kernel or app-level.
+ * Contents are wifi-specific, used by any kernel or app-level
+ * software that might want wifi things as it grows.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: bcmwifi.c,v 1.18.24.2.4.1 2009/09/25 00:32:01 Exp $
+ */
+
+
+#include <typedefs.h>
+
+#ifdef BCMDRIVER
+#include <osl.h>
+#include <bcmutils.h>
+#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
+#define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c))
+#else
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#endif
+#include <bcmwifi.h>
+
+#if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL))
+#include <bcmstdlib.h>
+#endif
+
+
+
+
+
+char *
+wf_chspec_ntoa(chanspec_t chspec, char *buf)
+{
+ const char *band, *bw, *sb;
+ uint channel;
+
+ band = "";
+ bw = "";
+ sb = "";
+ channel = CHSPEC_CHANNEL(chspec);
+
+ if ((CHSPEC_IS2G(chspec) && channel > CH_MAX_2G_CHANNEL) ||
+ (CHSPEC_IS5G(chspec) && channel <= CH_MAX_2G_CHANNEL))
+ band = (CHSPEC_IS2G(chspec)) ? "b" : "a";
+ if (CHSPEC_IS40(chspec)) {
+ if (CHSPEC_SB_UPPER(chspec)) {
+ sb = "u";
+ channel += CH_10MHZ_APART;
+ } else {
+ sb = "l";
+ channel -= CH_10MHZ_APART;
+ }
+ } else if (CHSPEC_IS10(chspec)) {
+ bw = "n";
+ }
+
+
+ snprintf(buf, 6, "%d%s%s%s", channel, band, bw, sb);
+ return (buf);
+}
+
+
+chanspec_t
+wf_chspec_aton(char *a)
+{
+ char *endp = NULL;
+ uint channel, band, bw, ctl_sb;
+ char c;
+
+ channel = strtoul(a, &endp, 10);
+
+
+ if (endp == a)
+ return 0;
+
+ if (channel > MAXCHANNEL)
+ return 0;
+
+ band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G);
+ bw = WL_CHANSPEC_BW_20;
+ ctl_sb = WL_CHANSPEC_CTL_SB_NONE;
+
+ a = endp;
+
+ c = tolower(a[0]);
+ if (c == '\0')
+ goto done;
+
+
+ if (c == 'a' || c == 'b') {
+ band = (c == 'a') ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G;
+ a++;
+ c = tolower(a[0]);
+ if (c == '\0')
+ goto done;
+ }
+
+
+ if (c == 'n') {
+ bw = WL_CHANSPEC_BW_10;
+ } else if (c == 'l') {
+ bw = WL_CHANSPEC_BW_40;
+ ctl_sb = WL_CHANSPEC_CTL_SB_LOWER;
+
+ if (channel <= (MAXCHANNEL - CH_20MHZ_APART))
+ channel += CH_10MHZ_APART;
+ else
+ return 0;
+ } else if (c == 'u') {
+ bw = WL_CHANSPEC_BW_40;
+ ctl_sb = WL_CHANSPEC_CTL_SB_UPPER;
+
+ if (channel > CH_20MHZ_APART)
+ channel -= CH_10MHZ_APART;
+ else
+ return 0;
+ } else {
+ return 0;
+ }
+
+done:
+ return (channel | band | bw | ctl_sb);
+}
+
+
+int
+wf_mhz2channel(uint freq, uint start_factor)
+{
+ int ch = -1;
+ uint base;
+ int offset;
+
+
+ if (start_factor == 0) {
+ if (freq >= 2400 && freq <= 2500)
+ start_factor = WF_CHAN_FACTOR_2_4_G;
+ else if (freq >= 5000 && freq <= 6000)
+ start_factor = WF_CHAN_FACTOR_5_G;
+ }
+
+ if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G)
+ return 14;
+
+ base = start_factor / 2;
+
+
+ if ((freq < base) || (freq > base + 1000))
+ return -1;
+
+ offset = freq - base;
+ ch = offset / 5;
+
+
+ if (offset != (ch * 5))
+ return -1;
+
+
+ if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13))
+ return -1;
+
+ return ch;
+}
+
+
+int
+wf_channel2mhz(uint ch, uint start_factor)
+{
+ int freq;
+
+ if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) ||
+ (ch <= 200))
+ freq = -1;
+ if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14))
+ freq = 2484;
+ else
+ freq = ch * 5 + start_factor / 2;
+
+ return freq;
+}
diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h
new file mode 100644
index 00000000000..1ddf1ff61e7
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd.h
@@ -0,0 +1,437 @@
+/*
+ * Header file describing the internal (inter-module) DHD interfaces.
+ *
+ * Provides type definitions and function prototypes used to link the
+ * DHD OS, bus, and protocol modules.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd.h,v 1.32.4.7.2.4.14.49.4.7 2010/11/12 22:48:36 Exp $
+ */
+
+/****************
+ * Common types *
+ */
+
+#ifndef _dhd_h_
+#define _dhd_h_
+
+#if defined(LINUX)
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+/* The kernel threading is sdio-specific */
+#else /* LINUX */
+#define ENOMEM 1
+#define EFAULT 2
+#define EINVAL 3
+#define EIO 4
+#define ETIMEDOUT 5
+#define ERESTARTSYS 6
+#endif /* LINUX */
+
+#include <wlioctl.h>
+
+#ifdef DHD_DEBUG
+#ifndef DHD_DEBUG_TRAP
+#define DHD_DEBUG_TRAP
+#endif
+#endif
+
+/* Forward decls */
+struct dhd_bus;
+struct dhd_prot;
+struct dhd_info;
+
+/* The level of bus communication with the dongle */
+enum dhd_bus_state {
+ DHD_BUS_DOWN, /* Not ready for frame transfers */
+ DHD_BUS_LOAD, /* Download access only (CPU reset) */
+ DHD_BUS_DATA /* Ready for frame transfers */
+};
+
+enum dhd_bus_wake_state {
+ WAKE_LOCK_OFF,
+ WAKE_LOCK_PRIV,
+ WAKE_LOCK_DPC,
+ WAKE_LOCK_IOCTL,
+ WAKE_LOCK_DOWNLOAD,
+ WAKE_LOCK_TMOUT,
+ WAKE_LOCK_WATCHDOG,
+ WAKE_LOCK_LINK_DOWN_TMOUT,
+ WAKE_LOCK_PNO_FIND_TMOUT,
+ WAKE_LOCK_SOFTAP_SET,
+ WAKE_LOCK_SOFTAP_STOP,
+ WAKE_LOCK_SOFTAP_START,
+ WAKE_LOCK_SOFTAP_THREAD,
+ WAKE_LOCK_MAX
+};
+enum dhd_prealloc_index {
+ DHD_PREALLOC_PROT = 0,
+ DHD_PREALLOC_RXBUF,
+ DHD_PREALLOC_DATABUF,
+ DHD_PREALLOC_OSL_BUF
+};
+#ifdef DHD_USE_STATIC_BUF
+extern void * dhd_os_prealloc(int section, unsigned long size);
+#endif
+/* Common structure for module and instance linkage */
+typedef struct dhd_pub {
+ /* Linkage ponters */
+ osl_t *osh; /* OSL handle */
+ struct dhd_bus *bus; /* Bus module handle */
+ struct dhd_prot *prot; /* Protocol module handle */
+ struct dhd_info *info; /* Info module handle */
+
+ /* Internal dhd items */
+ bool up; /* Driver up/down (to OS) */
+ bool txoff; /* Transmit flow-controlled */
+ bool dongle_reset; /* TRUE = DEVRESET put dongle into reset */
+ enum dhd_bus_state busstate;
+ uint hdrlen; /* Total DHD header length (proto + bus) */
+ uint maxctl; /* Max size rxctl request from proto to bus */
+ uint rxsz; /* Rx buffer size bus module should use */
+ uint8 wme_dp; /* wme discard priority */
+
+ /* Dongle media info */
+ bool iswl; /* Dongle-resident driver is wl */
+ ulong drv_version; /* Version of dongle-resident driver */
+ struct ether_addr mac; /* MAC address obtained from dongle */
+ dngl_stats_t dstats; /* Stats for dongle-based data */
+
+ /* Additional stats for the bus level */
+ ulong tx_packets; /* Data packets sent to dongle */
+ ulong tx_multicast; /* Multicast data packets sent to dongle */
+ ulong tx_errors; /* Errors in sending data to dongle */
+ ulong tx_ctlpkts; /* Control packets sent to dongle */
+ ulong tx_ctlerrs; /* Errors sending control frames to dongle */
+ ulong rx_packets; /* Packets sent up the network interface */
+ ulong rx_multicast; /* Multicast packets sent up the network interface */
+ ulong rx_errors; /* Errors processing rx data packets */
+ ulong rx_ctlpkts; /* Control frames processed from dongle */
+ ulong rx_ctlerrs; /* Errors in processing rx control frames */
+ ulong rx_dropped; /* Packets dropped locally (no memory) */
+ ulong rx_flushed; /* Packets flushed due to unscheduled sendup thread */
+ ulong wd_dpc_sched; /* Number of times dhd dpc scheduled by watchdog timer */
+
+ ulong rx_readahead_cnt; /* Number of packets where header read-ahead was used. */
+ ulong tx_realloc; /* Number of tx packets we had to realloc for headroom */
+ ulong fc_packets; /* Number of flow control pkts recvd */
+
+ /* Last error return */
+ int bcmerror;
+ uint tickcnt;
+
+ /* Last error from dongle */
+ int dongle_error;
+
+ /* Suspend disable flag and "in suspend" flag */
+ int suspend_disable_flag; /* "1" to disable all extra powersaving during suspend */
+ int in_suspend; /* flag set to 1 when early suspend called */
+#ifdef PNO_SUPPORT
+ int pno_enable; /* pno status : "1" is pno enable */
+#endif /* PNO_SUPPORT */
+ int dtim_skip; /* dtim skip , default 0 means wake each dtim */
+
+ /* Pkt filter defination */
+ char * pktfilter[100];
+ int pktfilter_count;
+
+ uint8 country_code[WLC_CNTRY_BUF_SZ];
+ char eventmask[WL_EVENTING_MASK_LEN];
+
+} dhd_pub_t;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+
+ #define DHD_PM_RESUME_WAIT_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
+ #define _DHD_PM_RESUME_WAIT(a, b) do { \
+ int retry = 0; \
+ smp_mb(); \
+ while (dhd_mmc_suspend && retry++ != b) { \
+ wait_event_interruptible_timeout(a, FALSE, HZ/100); \
+ } \
+ } while (0)
+ #define DHD_PM_RESUME_WAIT(a) _DHD_PM_RESUME_WAIT(a, 30)
+ #define DHD_PM_RESUME_WAIT_FOREVER(a) _DHD_PM_RESUME_WAIT(a, ~0)
+ #define DHD_PM_RESUME_RETURN_ERROR(a) do { if (dhd_mmc_suspend) return a; } while (0)
+ #define DHD_PM_RESUME_RETURN do { if (dhd_mmc_suspend) return; } while (0)
+
+ #define DHD_SPINWAIT_SLEEP_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
+ #define SPINWAIT_SLEEP(a, exp, us) do { \
+ uint countdown = (us) + 9999; \
+ while ((exp) && (countdown >= 10000)) { \
+ wait_event_interruptible_timeout(a, FALSE, HZ/100); \
+ countdown -= 10000; \
+ } \
+ } while (0)
+
+#else
+
+ #define DHD_PM_RESUME_WAIT_INIT(a)
+ #define DHD_PM_RESUME_WAIT(a)
+ #define DHD_PM_RESUME_WAIT_FOREVER(a)
+ #define DHD_PM_RESUME_RETURN_ERROR(a)
+ #define DHD_PM_RESUME_RETURN
+
+ #define DHD_SPINWAIT_SLEEP_INIT(a)
+ #define SPINWAIT_SLEEP(a, exp, us) do { \
+ uint countdown = (us) + 9; \
+ while ((exp) && (countdown >= 10)) { \
+ OSL_DELAY(10); \
+ countdown -= 10; \
+ } \
+ } while (0)
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
+
+#define DHD_IF_VIF 0x01 /* Virtual IF (Hidden from user) */
+
+/* Wakelock Functions */
+extern int dhd_os_wake_lock(dhd_pub_t *pub);
+extern int dhd_os_wake_unlock(dhd_pub_t *pub);
+extern int dhd_os_wake_lock_timeout(dhd_pub_t *pub);
+extern int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub);
+
+extern void dhd_os_start_lock(dhd_pub_t *pub);
+extern void dhd_os_start_unlock(dhd_pub_t *pub);
+extern unsigned long dhd_os_spin_lock(dhd_pub_t *pub);
+extern void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags);
+
+typedef struct dhd_if_event {
+ uint8 ifidx;
+ uint8 action;
+ uint8 flags;
+ uint8 bssidx;
+} dhd_if_event_t;
+
+/*
+ * Exported from dhd OS modules (dhd_linux/dhd_ndis)
+ */
+
+/* To allow osl_attach/detach calls from os-independent modules */
+osl_t *dhd_osl_attach(void *pdev, uint bustype);
+void dhd_osl_detach(osl_t *osh);
+
+/* Indication from bus module regarding presence/insertion of dongle.
+ * Return dhd_pub_t pointer, used as handle to OS module in later calls.
+ * Returned structure should have bus and prot pointers filled in.
+ * bus_hdrlen specifies required headroom for bus module header.
+ */
+extern dhd_pub_t *dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen);
+extern int dhd_net_attach(dhd_pub_t *dhdp, int idx);
+
+/* Indication from bus module regarding removal/absence of dongle */
+extern void dhd_detach(dhd_pub_t *dhdp);
+
+/* Indication from bus module to change flow-control state */
+extern void dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool on);
+
+extern bool dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec);
+
+/* Receive frame for delivery to OS. Callee disposes of rxp. */
+extern void dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *rxp, int numpkt);
+
+/* Return pointer to interface name */
+extern char *dhd_ifname(dhd_pub_t *dhdp, int idx);
+
+/* Request scheduling of the bus dpc */
+extern void dhd_sched_dpc(dhd_pub_t *dhdp);
+
+/* Notify tx completion */
+extern void dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success);
+
+/* Query ioctl */
+extern int dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
+
+/* OS independent layer functions */
+extern int dhd_os_proto_block(dhd_pub_t * pub);
+extern int dhd_os_proto_unblock(dhd_pub_t * pub);
+extern int dhd_os_ioctl_resp_wait(dhd_pub_t * pub, uint * condition, bool * pending);
+extern int dhd_os_ioctl_resp_wake(dhd_pub_t * pub);
+extern unsigned int dhd_os_get_ioctl_resp_timeout(void);
+extern void dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec);
+extern void * dhd_os_open_image(char * filename);
+extern int dhd_os_get_image_block(char * buf, int len, void * image);
+extern void dhd_os_close_image(void * image);
+extern void dhd_os_wd_timer(void *bus, uint wdtick);
+extern void dhd_os_sdlock(dhd_pub_t * pub);
+extern void dhd_os_sdunlock(dhd_pub_t * pub);
+extern void dhd_os_sdlock_txq(dhd_pub_t * pub);
+extern void dhd_os_sdunlock_txq(dhd_pub_t * pub);
+extern void dhd_os_sdlock_rxq(dhd_pub_t * pub);
+extern void dhd_os_sdunlock_rxq(dhd_pub_t * pub);
+extern void dhd_os_sdlock_sndup_rxq(dhd_pub_t * pub);
+extern void dhd_customer_gpio_wlan_ctrl(int onoff);
+extern int dhd_custom_get_mac_address(unsigned char *buf);
+extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub);
+extern void dhd_os_sdlock_eventq(dhd_pub_t * pub);
+extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub);
+#ifdef DHD_DEBUG
+extern int write_to_file(dhd_pub_t *dhd, uint8 *buf, int size);
+#endif /* DHD_DEBUG */
+#if defined(OOB_INTR_ONLY)
+extern int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr);
+#endif /* defined(OOB_INTR_ONLY) */
+extern void dhd_os_sdtxlock(dhd_pub_t * pub);
+extern void dhd_os_sdtxunlock(dhd_pub_t * pub);
+
+int setScheduler(struct task_struct *p, int policy, struct sched_param *param);
+
+typedef struct {
+ uint32 limit; /* Expiration time (usec) */
+ uint32 increment; /* Current expiration increment (usec) */
+ uint32 elapsed; /* Current elapsed time (usec) */
+ uint32 tick; /* O/S tick time (usec) */
+} dhd_timeout_t;
+
+extern void dhd_timeout_start(dhd_timeout_t *tmo, uint usec);
+extern int dhd_timeout_expired(dhd_timeout_t *tmo);
+
+extern int dhd_ifname2idx(struct dhd_info *dhd, char *name);
+extern uint8 *dhd_bssidx2bssid(dhd_pub_t *dhd, int idx);
+extern int wl_host_event(struct dhd_info *dhd, int *idx, void *pktdata,
+ wl_event_msg_t *, void **data_ptr);
+extern void wl_event_to_host_order(wl_event_msg_t * evt);
+
+extern void dhd_common_init(void);
+
+extern int dhd_add_if(struct dhd_info *dhd, int ifidx, void *handle,
+ char *name, uint8 *mac_addr, uint32 flags, uint8 bssidx);
+extern void dhd_del_if(struct dhd_info *dhd, int ifidx);
+
+extern void dhd_vif_add(struct dhd_info *dhd, int ifidx, char * name);
+extern void dhd_vif_del(struct dhd_info *dhd, int ifidx);
+
+extern void dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx);
+extern void dhd_vif_sendup(struct dhd_info *dhd, int ifidx, uchar *cp, int len);
+
+
+/* Send packet to dongle via data channel */
+extern int dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pkt);
+
+/* Send event to host */
+extern void dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data);
+extern int dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag);
+extern uint dhd_bus_status(dhd_pub_t *dhdp);
+extern int dhd_bus_start(dhd_pub_t *dhdp);
+
+extern void print_buf(void *pbuf, int len, int bytes_per_line);
+
+
+typedef enum cust_gpio_modes {
+ WLAN_RESET_ON,
+ WLAN_RESET_OFF,
+ WLAN_POWER_ON,
+ WLAN_POWER_OFF
+} cust_gpio_modes_t;
+
+extern int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag);
+extern int wl_iw_send_priv_event(struct net_device *dev, char *flag);
+extern int net_os_send_hang_message(struct net_device *dev);
+
+/*
+ * Insmod parameters for debug/test
+ */
+
+/* Watchdog timer interval */
+extern uint dhd_watchdog_ms;
+
+#if defined(DHD_DEBUG)
+/* Console output poll interval */
+extern uint dhd_console_ms;
+#endif /* defined(DHD_DEBUG) */
+
+/* Use interrupts */
+extern uint dhd_intr;
+
+/* Use polling */
+extern uint dhd_poll;
+
+/* ARP offload agent mode */
+extern uint dhd_arp_mode;
+
+/* ARP offload enable */
+extern uint dhd_arp_enable;
+
+/* Pkt filte enable control */
+extern uint dhd_pkt_filter_enable;
+
+/* Pkt filter init setup */
+extern uint dhd_pkt_filter_init;
+
+/* Pkt filter mode control */
+extern uint dhd_master_mode;
+
+/* Roaming mode control */
+extern uint dhd_roam;
+
+/* Roaming mode control */
+extern uint dhd_radio_up;
+
+/* Initial idletime ticks (may be -1 for immediate idle, 0 for no idle) */
+extern int dhd_idletime;
+#define DHD_IDLETIME_TICKS 1
+
+/* SDIO Drive Strength */
+extern uint dhd_sdiod_drive_strength;
+
+/* Override to force tx queueing all the time */
+extern uint dhd_force_tx_queueing;
+
+/* Default KEEP_ALIVE Period is 55 sec to prevent AP from sending Keep Alive probe frame */
+#define KEEP_ALIVE_PERIOD 55000
+#define NULL_PKT_STR "null_pkt"
+
+#ifdef SDTEST
+/* Echo packet generator (SDIO), pkts/s */
+extern uint dhd_pktgen;
+
+/* Echo packet len (0 => sawtooth, max 1800) */
+extern uint dhd_pktgen_len;
+#define MAX_PKTGEN_LEN 1800
+#endif
+
+
+/* optionally set by a module_param_string() */
+#define MOD_PARAM_PATHLEN 2048
+extern char fw_path[MOD_PARAM_PATHLEN];
+extern char nv_path[MOD_PARAM_PATHLEN];
+
+/* For supporting multiple interfaces */
+#define DHD_MAX_IFS 16
+#define DHD_DEL_IF -0xe
+#define DHD_BAD_IF -0xf
+
+
+extern void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar);
+extern void dhd_wait_event_wakeup(dhd_pub_t*dhd);
+
+#endif /* _dhd_h_ */
diff --git a/drivers/net/wireless/bcm4329/dhd_bus.h b/drivers/net/wireless/bcm4329/dhd_bus.h
new file mode 100644
index 00000000000..97af41b313d
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_bus.h
@@ -0,0 +1,93 @@
+/*
+ * Header file describing the internal (inter-module) DHD interfaces.
+ *
+ * Provides type definitions and function prototypes used to link the
+ * DHD OS, bus, and protocol modules.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_bus.h,v 1.4.6.3.2.3.6.7 2010/08/13 01:35:24 Exp $
+ */
+
+#ifndef _dhd_bus_h_
+#define _dhd_bus_h_
+
+/*
+ * Exported from dhd bus module (dhd_usb, dhd_sdio)
+ */
+
+/* Indicate (dis)interest in finding dongles. */
+extern int dhd_bus_register(void);
+extern void dhd_bus_unregister(void);
+
+/* Download firmware image and nvram image */
+extern bool dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh,
+ char *fw_path, char *nv_path);
+
+/* Stop bus module: clear pending frames, disable data flow */
+extern void dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex);
+
+/* Initialize bus module: prepare for communication w/dongle */
+extern int dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex);
+
+/* Send a data frame to the dongle. Callee disposes of txp. */
+extern int dhd_bus_txdata(struct dhd_bus *bus, void *txp);
+
+/* Send/receive a control message to/from the dongle.
+ * Expects caller to enforce a single outstanding transaction.
+ */
+extern int dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen);
+extern int dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen);
+
+/* Watchdog timer function */
+extern bool dhd_bus_watchdog(dhd_pub_t *dhd);
+
+#ifdef DHD_DEBUG
+/* Device console input function */
+extern int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen);
+#endif /* DHD_DEBUG */
+
+/* Deferred processing for the bus, return TRUE requests reschedule */
+extern bool dhd_bus_dpc(struct dhd_bus *bus);
+extern void dhd_bus_isr(bool * InterruptRecognized, bool * QueueMiniportHandleInterrupt, void *arg);
+
+
+/* Check for and handle local prot-specific iovar commands */
+extern int dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name,
+ void *params, int plen, void *arg, int len, bool set);
+
+/* Add bus dump output to a buffer */
+extern void dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf);
+
+/* Clear any bus counters */
+extern void dhd_bus_clearcounts(dhd_pub_t *dhdp);
+
+/* return the dongle chipid */
+extern uint dhd_bus_chip(struct dhd_bus *bus);
+
+/* Set user-specified nvram parameters. */
+extern void dhd_bus_set_nvram_params(struct dhd_bus * bus, const char *nvram_params);
+
+extern void *dhd_bus_pub(struct dhd_bus *bus);
+extern void *dhd_bus_txq(struct dhd_bus *bus);
+extern uint dhd_bus_hdrlen(struct dhd_bus *bus);
+
+#endif /* _dhd_bus_h_ */
diff --git a/drivers/net/wireless/bcm4329/dhd_cdc.c b/drivers/net/wireless/bcm4329/dhd_cdc.c
new file mode 100644
index 00000000000..61f6a6f393a
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_cdc.c
@@ -0,0 +1,522 @@
+/*
+ * DHD Protocol Module for CDC and BDC.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_cdc.c,v 1.22.4.2.4.7.2.41 2010/06/23 19:58:18 Exp $
+ *
+ * BDC is like CDC, except it includes a header for data packets to convey
+ * packet priority over the bus, and flags (e.g. to indicate checksum status
+ * for dongle offload).
+ */
+
+#include <typedefs.h>
+#include <osl.h>
+
+#include <bcmutils.h>
+#include <bcmcdc.h>
+#include <bcmendian.h>
+
+#include <dngl_stats.h>
+#include <dhd.h>
+#include <dhd_proto.h>
+#include <dhd_bus.h>
+#include <dhd_dbg.h>
+
+extern int dhd_preinit_ioctls(dhd_pub_t *dhd);
+
+/* Packet alignment for most efficient SDIO (can change based on platform) */
+#ifndef DHD_SDALIGN
+#define DHD_SDALIGN 32
+#endif
+#if !ISPOWEROF2(DHD_SDALIGN)
+#error DHD_SDALIGN is not a power of 2!
+#endif
+
+#define RETRIES 2 /* # of retries to retrieve matching ioctl response */
+#define BUS_HEADER_LEN (16+DHD_SDALIGN) /* Must be atleast SDPCM_RESERVE
+ * defined in dhd_sdio.c (amount of header tha might be added)
+ * plus any space that might be needed for alignment padding.
+ */
+#define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for
+ * round off at the end of buffer
+ */
+
+typedef struct dhd_prot {
+ uint16 reqid;
+ uint8 pending;
+ uint32 lastcmd;
+ uint8 bus_header[BUS_HEADER_LEN];
+ cdc_ioctl_t msg;
+ unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
+} dhd_prot_t;
+
+static int
+dhdcdc_msg(dhd_pub_t *dhd)
+{
+ dhd_prot_t *prot = dhd->prot;
+ int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
+ int ret;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ dhd_os_wake_lock(dhd);
+
+ /* NOTE : cdc->msg.len holds the desired length of the buffer to be
+ * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
+ * is actually sent to the dongle
+ */
+ if (len > CDC_MAX_MSG_SIZE)
+ len = CDC_MAX_MSG_SIZE;
+
+ /* Send request */
+ ret = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
+ dhd_os_wake_unlock(dhd);
+ return ret;
+}
+
+static int
+dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
+{
+ int ret;
+ dhd_prot_t *prot = dhd->prot;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ do {
+ ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, len+sizeof(cdc_ioctl_t));
+ if (ret < 0)
+ break;
+ } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
+
+ return ret;
+}
+
+int
+dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
+{
+ dhd_prot_t *prot = dhd->prot;
+ cdc_ioctl_t *msg = &prot->msg;
+ void *info;
+ int ret = 0, retries = 0;
+ uint32 id, flags = 0;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+ DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
+
+
+ /* Respond "bcmerror" and "bcmerrorstr" with local cache */
+ if (cmd == WLC_GET_VAR && buf)
+ {
+ if (!strcmp((char *)buf, "bcmerrorstr"))
+ {
+ strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN);
+ goto done;
+ }
+ else if (!strcmp((char *)buf, "bcmerror"))
+ {
+ *(int *)buf = dhd->dongle_error;
+ goto done;
+ }
+ }
+
+ memset(msg, 0, sizeof(cdc_ioctl_t));
+
+ msg->cmd = htol32(cmd);
+ msg->len = htol32(len);
+ msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
+ CDC_SET_IF_IDX(msg, ifidx);
+ msg->flags = htol32(msg->flags);
+
+ if (buf)
+ memcpy(prot->buf, buf, len);
+
+ if ((ret = dhdcdc_msg(dhd)) < 0) {
+ DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
+ goto done;
+ }
+
+retry:
+ /* wait for interrupt and get first fragment */
+ if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
+ goto done;
+
+ flags = ltoh32(msg->flags);
+ id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
+
+ if ((id < prot->reqid) && (++retries < RETRIES))
+ goto retry;
+ if (id != prot->reqid) {
+ DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
+ dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Check info buffer */
+ info = (void*)&msg[1];
+
+ /* Copy info buffer */
+ if (buf)
+ {
+ if (ret < (int)len)
+ len = ret;
+ memcpy(buf, info, len);
+ }
+
+ /* Check the ERROR flag */
+ if (flags & CDCF_IOC_ERROR)
+ {
+ ret = ltoh32(msg->status);
+ /* Cache error from dongle */
+ dhd->dongle_error = ret;
+ }
+
+done:
+ return ret;
+}
+
+int
+dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
+{
+ dhd_prot_t *prot = dhd->prot;
+ cdc_ioctl_t *msg = &prot->msg;
+ int ret = 0;
+ uint32 flags, id;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+ DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
+
+ memset(msg, 0, sizeof(cdc_ioctl_t));
+
+ msg->cmd = htol32(cmd);
+ msg->len = htol32(len);
+ msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET;
+ CDC_SET_IF_IDX(msg, ifidx);
+ msg->flags = htol32(msg->flags);
+
+ if (buf)
+ memcpy(prot->buf, buf, len);
+
+ if ((ret = dhdcdc_msg(dhd)) < 0)
+ goto done;
+
+ if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
+ goto done;
+
+ flags = ltoh32(msg->flags);
+ id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
+
+ if (id != prot->reqid) {
+ DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
+ dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Check the ERROR flag */
+ if (flags & CDCF_IOC_ERROR)
+ {
+ ret = ltoh32(msg->status);
+ /* Cache error from dongle */
+ dhd->dongle_error = ret;
+ }
+
+done:
+ return ret;
+}
+
+extern int dhd_bus_interface(struct dhd_bus *bus, uint arg, void* arg2);
+int
+dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
+{
+ dhd_prot_t *prot = dhd->prot;
+ int ret = -1;
+
+ if (dhd->busstate == DHD_BUS_DOWN) {
+ DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
+ return ret;
+ }
+ dhd_os_proto_block(dhd);
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ ASSERT(len <= WLC_IOCTL_MAXLEN);
+
+ if (len > WLC_IOCTL_MAXLEN)
+ goto done;
+
+ if (prot->pending == TRUE) {
+ DHD_TRACE(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
+ ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
+ (unsigned long)prot->lastcmd));
+ if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) {
+ DHD_TRACE(("iovar cmd=%s\n", (char*)buf));
+ }
+ goto done;
+ }
+
+ prot->pending = TRUE;
+ prot->lastcmd = ioc->cmd;
+ if (ioc->set)
+ ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len);
+ else {
+ ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len);
+ if (ret > 0)
+ ioc->used = ret - sizeof(cdc_ioctl_t);
+ }
+
+ /* Too many programs assume ioctl() returns 0 on success */
+ if (ret >= 0)
+ ret = 0;
+ else {
+ cdc_ioctl_t *msg = &prot->msg;
+ ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
+ }
+
+ /* Intercept the wme_dp ioctl here */
+ if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
+ int slen, val = 0;
+
+ slen = strlen("wme_dp") + 1;
+ if (len >= (int)(slen + sizeof(int)))
+ bcopy(((char *)buf + slen), &val, sizeof(int));
+ dhd->wme_dp = (uint8) ltoh32(val);
+ }
+
+ prot->pending = FALSE;
+
+done:
+ dhd_os_proto_unblock(dhd);
+
+ return ret;
+}
+
+int
+dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
+ void *params, int plen, void *arg, int len, bool set)
+{
+ return BCME_UNSUPPORTED;
+}
+
+void
+dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
+{
+ bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
+}
+
+
+void
+dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf)
+{
+#ifdef BDC
+ struct bdc_header *h;
+#endif /* BDC */
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+#ifdef BDC
+ /* Push BDC header used to convey priority for buses that don't */
+
+
+ PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN);
+
+ h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
+
+ h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
+ if (PKTSUMNEEDED(pktbuf))
+ h->flags |= BDC_FLAG_SUM_NEEDED;
+
+
+ h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK);
+ h->flags2 = 0;
+ h->rssi = 0;
+#endif /* BDC */
+ BDC_SET_IF_IDX(h, ifidx);
+}
+
+
+bool
+dhd_proto_fcinfo(dhd_pub_t *dhd, void *pktbuf, uint8 *fcbits)
+{
+#ifdef BDC
+ struct bdc_header *h;
+
+ if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
+ DHD_ERROR(("%s: rx data too short (%d < %d)\n",
+ __FUNCTION__, PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
+ return BCME_ERROR;
+ }
+
+ h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
+
+ *fcbits = h->priority >> BDC_PRIORITY_FC_SHIFT;
+ if ((h->flags2 & BDC_FLAG2_FC_FLAG) == BDC_FLAG2_FC_FLAG)
+ return TRUE;
+#endif
+ return FALSE;
+}
+
+
+int
+dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf)
+{
+#ifdef BDC
+ struct bdc_header *h;
+#endif
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+#ifdef BDC
+ /* Pop BDC header used to convey priority for buses that don't */
+
+ if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
+ DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
+ PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
+ return BCME_ERROR;
+ }
+
+ h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
+
+ if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) {
+ DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
+ __FUNCTION__, *ifidx));
+ return BCME_ERROR;
+ }
+
+ if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
+ DHD_ERROR(("%s: non-BDC packet received, flags 0x%x\n",
+ dhd_ifname(dhd, *ifidx), h->flags));
+ return BCME_ERROR;
+ }
+
+ if (h->flags & BDC_FLAG_SUM_GOOD) {
+ DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
+ dhd_ifname(dhd, *ifidx), h->flags));
+ PKTSETSUMGOOD(pktbuf, TRUE);
+ }
+
+ PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
+
+ PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
+#endif /* BDC */
+
+ return 0;
+}
+
+int
+dhd_prot_attach(dhd_pub_t *dhd)
+{
+ dhd_prot_t *cdc;
+
+#ifndef DHD_USE_STATIC_BUF
+ if (!(cdc = (dhd_prot_t *)MALLOC(dhd->osh, sizeof(dhd_prot_t)))) {
+ DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+ goto fail;
+ }
+#else
+ if (!(cdc = (dhd_prot_t *)dhd_os_prealloc(DHD_PREALLOC_PROT, sizeof(dhd_prot_t)))) {
+ DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+ goto fail;
+ }
+#endif /* DHD_USE_STATIC_BUF */
+ memset(cdc, 0, sizeof(dhd_prot_t));
+
+ /* ensure that the msg buf directly follows the cdc msg struct */
+ if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
+ DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
+ goto fail;
+ }
+
+ dhd->prot = cdc;
+#ifdef BDC
+ dhd->hdrlen += BDC_HEADER_LEN;
+#endif
+ dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
+ return 0;
+
+fail:
+#ifndef DHD_USE_STATIC_BUF
+ if (cdc != NULL)
+ MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
+#endif
+ return BCME_NOMEM;
+}
+
+/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */
+void
+dhd_prot_detach(dhd_pub_t *dhd)
+{
+#ifndef DHD_USE_STATIC_BUF
+ MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
+#endif
+ dhd->prot = NULL;
+}
+
+void
+dhd_prot_dstats(dhd_pub_t *dhd)
+{
+ /* No stats from dongle added yet, copy bus stats */
+ dhd->dstats.tx_packets = dhd->tx_packets;
+ dhd->dstats.tx_errors = dhd->tx_errors;
+ dhd->dstats.rx_packets = dhd->rx_packets;
+ dhd->dstats.rx_errors = dhd->rx_errors;
+ dhd->dstats.rx_dropped = dhd->rx_dropped;
+ dhd->dstats.multicast = dhd->rx_multicast;
+ return;
+}
+
+int
+dhd_prot_init(dhd_pub_t *dhd)
+{
+ int ret = 0;
+ char buf[128];
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ dhd_os_proto_block(dhd);
+
+ /* Get the device MAC address */
+ strcpy(buf, "cur_etheraddr");
+ ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
+ if (ret < 0) {
+ dhd_os_proto_unblock(dhd);
+ return ret;
+ }
+ memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN);
+
+ dhd_os_proto_unblock(dhd);
+
+#ifdef EMBEDDED_PLATFORM
+ ret = dhd_preinit_ioctls(dhd);
+#endif /* EMBEDDED_PLATFORM */
+
+ /* Always assumes wl for now */
+ dhd->iswl = TRUE;
+
+ return ret;
+}
+
+void
+dhd_prot_stop(dhd_pub_t *dhd)
+{
+ /* Nothing to do for CDC */
+}
diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c
new file mode 100644
index 00000000000..4331d39a68f
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_common.c
@@ -0,0 +1,2323 @@
+/*
+ * Broadcom Dongle Host Driver (DHD), common DHD core.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.20 2010/12/20 23:37:28 Exp $
+ */
+#include <typedefs.h>
+#include <osl.h>
+
+#include <epivers.h>
+#include <bcmutils.h>
+
+#include <bcmendian.h>
+#include <dngl_stats.h>
+#include <dhd.h>
+#include <dhd_bus.h>
+#include <dhd_proto.h>
+#include <dhd_dbg.h>
+#include <msgtrace.h>
+
+#include <wlioctl.h>
+
+#ifdef SET_RANDOM_MAC_SOFTAP
+#include <linux/random.h>
+#include <linux/jiffies.h>
+#endif
+
+#ifdef GET_CUSTOM_MAC_ENABLE
+int wifi_get_mac_addr(unsigned char *buf);
+#endif /* GET_CUSTOM_MAC_ENABLE */
+
+int dhd_msg_level;
+
+#include <wl_iw.h>
+
+char fw_path[MOD_PARAM_PATHLEN];
+char nv_path[MOD_PARAM_PATHLEN];
+
+/* Last connection success/failure status */
+uint32 dhd_conn_event;
+uint32 dhd_conn_status;
+uint32 dhd_conn_reason;
+
+#define htod32(i) i
+#define htod16(i) i
+#define dtoh32(i) i
+#define dtoh16(i) i
+
+extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
+extern void dhd_ind_scan_confirm(void *h, bool status);
+extern int dhd_wl_ioctl(dhd_pub_t *dhd, uint cmd, char *buf, uint buflen);
+void dhd_iscan_lock(void);
+void dhd_iscan_unlock(void);
+
+#if defined(SOFTAP)
+extern bool ap_fw_loaded;
+#endif
+#if defined(KEEP_ALIVE)
+int dhd_keep_alive_onoff(dhd_pub_t *dhd, int ka_on);
+#endif /* KEEP_ALIVE */
+
+/* Packet alignment for most efficient SDIO (can change based on platform) */
+#ifndef DHD_SDALIGN
+#define DHD_SDALIGN 32
+#endif
+#if !ISPOWEROF2(DHD_SDALIGN)
+#error DHD_SDALIGN is not a power of 2!
+#endif
+
+#ifdef DHD_DEBUG
+const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR "\nCompiled on "
+ __DATE__ " at " __TIME__;
+#else
+const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR;
+#endif
+
+void dhd_set_timer(void *bus, uint wdtick);
+
+/* IOVar table */
+enum {
+ IOV_VERSION = 1,
+ IOV_MSGLEVEL,
+ IOV_BCMERRORSTR,
+ IOV_BCMERROR,
+ IOV_WDTICK,
+ IOV_DUMP,
+#ifdef DHD_DEBUG
+ IOV_CONS,
+ IOV_DCONSOLE_POLL,
+#endif
+ IOV_CLEARCOUNTS,
+ IOV_LOGDUMP,
+ IOV_LOGCAL,
+ IOV_LOGSTAMP,
+ IOV_GPIOOB,
+ IOV_IOCTLTIMEOUT,
+ IOV_LAST
+};
+
+const bcm_iovar_t dhd_iovars[] = {
+ {"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(dhd_version) },
+#ifdef DHD_DEBUG
+ {"msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 },
+#endif /* DHD_DEBUG */
+ {"bcmerrorstr", IOV_BCMERRORSTR, 0, IOVT_BUFFER, BCME_STRLEN },
+ {"bcmerror", IOV_BCMERROR, 0, IOVT_INT8, 0 },
+ {"wdtick", IOV_WDTICK, 0, IOVT_UINT32, 0 },
+ {"dump", IOV_DUMP, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN },
+#ifdef DHD_DEBUG
+ {"dconpoll", IOV_DCONSOLE_POLL, 0, IOVT_UINT32, 0 },
+ {"cons", IOV_CONS, 0, IOVT_BUFFER, 0 },
+#endif
+ {"clearcounts", IOV_CLEARCOUNTS, 0, IOVT_VOID, 0 },
+ {"gpioob", IOV_GPIOOB, 0, IOVT_UINT32, 0 },
+ {"ioctl_timeout", IOV_IOCTLTIMEOUT, 0, IOVT_UINT32, 0 },
+ {NULL, 0, 0, 0, 0 }
+};
+
+void
+dhd_common_init(void)
+{
+ /* Init global variables at run-time, not as part of the declaration.
+ * This is required to support init/de-init of the driver. Initialization
+ * of globals as part of the declaration results in non-deterministic
+ * behaviour since the value of the globals may be different on the
+ * first time that the driver is initialized vs subsequent initializations.
+ */
+ dhd_msg_level = DHD_ERROR_VAL;
+#ifdef CONFIG_BCM4329_FW_PATH
+ strncpy(fw_path, CONFIG_BCM4329_FW_PATH, MOD_PARAM_PATHLEN-1);
+#else
+ fw_path[0] = '\0';
+#endif
+#ifdef CONFIG_BCM4329_NVRAM_PATH
+ strncpy(nv_path, CONFIG_BCM4329_NVRAM_PATH, MOD_PARAM_PATHLEN-1);
+#else
+ nv_path[0] = '\0';
+#endif
+}
+
+static int
+dhd_dump(dhd_pub_t *dhdp, char *buf, int buflen)
+{
+ char eabuf[ETHER_ADDR_STR_LEN];
+
+ struct bcmstrbuf b;
+ struct bcmstrbuf *strbuf = &b;
+
+ bcm_binit(strbuf, buf, buflen);
+
+ /* Base DHD info */
+ bcm_bprintf(strbuf, "%s\n", dhd_version);
+ bcm_bprintf(strbuf, "\n");
+ bcm_bprintf(strbuf, "pub.up %d pub.txoff %d pub.busstate %d\n",
+ dhdp->up, dhdp->txoff, dhdp->busstate);
+ bcm_bprintf(strbuf, "pub.hdrlen %d pub.maxctl %d pub.rxsz %d\n",
+ dhdp->hdrlen, dhdp->maxctl, dhdp->rxsz);
+ bcm_bprintf(strbuf, "pub.iswl %d pub.drv_version %ld pub.mac %s\n",
+ dhdp->iswl, dhdp->drv_version, bcm_ether_ntoa(&dhdp->mac, eabuf));
+ bcm_bprintf(strbuf, "pub.bcmerror %d tickcnt %d\n", dhdp->bcmerror, dhdp->tickcnt);
+
+ bcm_bprintf(strbuf, "dongle stats:\n");
+ bcm_bprintf(strbuf, "tx_packets %ld tx_bytes %ld tx_errors %ld tx_dropped %ld\n",
+ dhdp->dstats.tx_packets, dhdp->dstats.tx_bytes,
+ dhdp->dstats.tx_errors, dhdp->dstats.tx_dropped);
+ bcm_bprintf(strbuf, "rx_packets %ld rx_bytes %ld rx_errors %ld rx_dropped %ld\n",
+ dhdp->dstats.rx_packets, dhdp->dstats.rx_bytes,
+ dhdp->dstats.rx_errors, dhdp->dstats.rx_dropped);
+ bcm_bprintf(strbuf, "multicast %ld\n", dhdp->dstats.multicast);
+
+ bcm_bprintf(strbuf, "bus stats:\n");
+ bcm_bprintf(strbuf, "tx_packets %ld tx_multicast %ld tx_errors %ld\n",
+ dhdp->tx_packets, dhdp->tx_multicast, dhdp->tx_errors);
+ bcm_bprintf(strbuf, "tx_ctlpkts %ld tx_ctlerrs %ld\n",
+ dhdp->tx_ctlpkts, dhdp->tx_ctlerrs);
+ bcm_bprintf(strbuf, "rx_packets %ld rx_multicast %ld rx_errors %ld \n",
+ dhdp->rx_packets, dhdp->rx_multicast, dhdp->rx_errors);
+ bcm_bprintf(strbuf, "rx_ctlpkts %ld rx_ctlerrs %ld rx_dropped %ld rx_flushed %ld\n",
+ dhdp->rx_ctlpkts, dhdp->rx_ctlerrs, dhdp->rx_dropped, dhdp->rx_flushed);
+ bcm_bprintf(strbuf, "rx_readahead_cnt %ld tx_realloc %ld fc_packets %ld\n",
+ dhdp->rx_readahead_cnt, dhdp->tx_realloc, dhdp->fc_packets);
+ bcm_bprintf(strbuf, "wd_dpc_sched %ld\n", dhdp->wd_dpc_sched);
+ bcm_bprintf(strbuf, "\n");
+
+ /* Add any prot info */
+ dhd_prot_dump(dhdp, strbuf);
+ bcm_bprintf(strbuf, "\n");
+
+ /* Add any bus info */
+ dhd_bus_dump(dhdp, strbuf);
+
+ return (!strbuf->size ? BCME_BUFTOOSHORT : 0);
+}
+
+static int
+dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const char *name,
+ void *params, int plen, void *arg, int len, int val_size)
+{
+ int bcmerror = 0;
+ int32 int_val = 0;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0)
+ goto exit;
+
+ if (plen >= (int)sizeof(int_val))
+ bcopy(params, &int_val, sizeof(int_val));
+
+ switch (actionid) {
+ case IOV_GVAL(IOV_VERSION):
+ /* Need to have checked buffer length */
+ strncpy((char*)arg, dhd_version, len);
+ break;
+
+ case IOV_GVAL(IOV_MSGLEVEL):
+ int_val = (int32)dhd_msg_level;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_MSGLEVEL):
+ dhd_msg_level = int_val;
+ break;
+
+ case IOV_GVAL(IOV_BCMERRORSTR):
+ strncpy((char *)arg, bcmerrorstr(dhd_pub->bcmerror), BCME_STRLEN);
+ ((char *)arg)[BCME_STRLEN - 1] = 0x00;
+ break;
+
+ case IOV_GVAL(IOV_BCMERROR):
+ int_val = (int32)dhd_pub->bcmerror;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_WDTICK):
+ int_val = (int32)dhd_watchdog_ms;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_WDTICK):
+ if (!dhd_pub->up) {
+ bcmerror = BCME_NOTUP;
+ break;
+ }
+ dhd_os_wd_timer(dhd_pub, (uint)int_val);
+ break;
+
+ case IOV_GVAL(IOV_DUMP):
+ bcmerror = dhd_dump(dhd_pub, arg, len);
+ break;
+
+#ifdef DHD_DEBUG
+ case IOV_GVAL(IOV_DCONSOLE_POLL):
+ int_val = (int32)dhd_console_ms;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_DCONSOLE_POLL):
+ dhd_console_ms = (uint)int_val;
+ break;
+
+ case IOV_SVAL(IOV_CONS):
+ if (len > 0)
+ bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1);
+ break;
+#endif
+
+ case IOV_SVAL(IOV_CLEARCOUNTS):
+ dhd_pub->tx_packets = dhd_pub->rx_packets = 0;
+ dhd_pub->tx_errors = dhd_pub->rx_errors = 0;
+ dhd_pub->tx_ctlpkts = dhd_pub->rx_ctlpkts = 0;
+ dhd_pub->tx_ctlerrs = dhd_pub->rx_ctlerrs = 0;
+ dhd_pub->rx_dropped = 0;
+ dhd_pub->rx_readahead_cnt = 0;
+ dhd_pub->tx_realloc = 0;
+ dhd_pub->wd_dpc_sched = 0;
+ memset(&dhd_pub->dstats, 0, sizeof(dhd_pub->dstats));
+ dhd_bus_clearcounts(dhd_pub);
+ break;
+
+
+ case IOV_GVAL(IOV_IOCTLTIMEOUT): {
+ int_val = (int32)dhd_os_get_ioctl_resp_timeout();
+ bcopy(&int_val, arg, sizeof(int_val));
+ break;
+ }
+
+ case IOV_SVAL(IOV_IOCTLTIMEOUT): {
+ if (int_val <= 0)
+ bcmerror = BCME_BADARG;
+ else
+ dhd_os_set_ioctl_resp_timeout((unsigned int)int_val);
+ break;
+ }
+
+
+ default:
+ bcmerror = BCME_UNSUPPORTED;
+ break;
+ }
+
+exit:
+ return bcmerror;
+}
+
+/* Store the status of a connection attempt for later retrieval by an iovar */
+void
+dhd_store_conn_status(uint32 event, uint32 status, uint32 reason)
+{
+ /* Do not overwrite a WLC_E_PRUNE with a WLC_E_SET_SSID
+ * because an encryption/rsn mismatch results in both events, and
+ * the important information is in the WLC_E_PRUNE.
+ */
+ if (!(event == WLC_E_SET_SSID && status == WLC_E_STATUS_FAIL &&
+ dhd_conn_event == WLC_E_PRUNE)) {
+ dhd_conn_event = event;
+ dhd_conn_status = status;
+ dhd_conn_reason = reason;
+ }
+}
+
+bool
+dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec)
+{
+ void *p;
+ int eprec = -1; /* precedence to evict from */
+ bool discard_oldest;
+
+ /* Fast case, precedence queue is not full and we are also not
+ * exceeding total queue length
+ */
+ if (!pktq_pfull(q, prec) && !pktq_full(q)) {
+ pktq_penq(q, prec, pkt);
+ return TRUE;
+ }
+
+ /* Determine precedence from which to evict packet, if any */
+ if (pktq_pfull(q, prec))
+ eprec = prec;
+ else if (pktq_full(q)) {
+ p = pktq_peek_tail(q, &eprec);
+ ASSERT(p);
+ if (eprec > prec)
+ return FALSE;
+ }
+
+ /* Evict if needed */
+ if (eprec >= 0) {
+ /* Detect queueing to unconfigured precedence */
+ ASSERT(!pktq_pempty(q, eprec));
+ discard_oldest = AC_BITMAP_TST(dhdp->wme_dp, eprec);
+ if (eprec == prec && !discard_oldest)
+ return FALSE; /* refuse newer (incoming) packet */
+ /* Evict packet according to discard policy */
+ p = discard_oldest ? pktq_pdeq(q, eprec) : pktq_pdeq_tail(q, eprec);
+ if (p == NULL) {
+ DHD_ERROR(("%s: pktq_penq() failed, oldest %d.",
+ __FUNCTION__, discard_oldest));
+ ASSERT(p);
+ }
+
+ PKTFREE(dhdp->osh, p, TRUE);
+ }
+
+ /* Enqueue */
+ p = pktq_penq(q, prec, pkt);
+ if (p == NULL) {
+ DHD_ERROR(("%s: pktq_penq() failed.", __FUNCTION__));
+ ASSERT(p);
+ }
+
+ return TRUE;
+}
+
+static int
+dhd_iovar_op(dhd_pub_t *dhd_pub, const char *name,
+ void *params, int plen, void *arg, int len, bool set)
+{
+ int bcmerror = 0;
+ int val_size;
+ const bcm_iovar_t *vi = NULL;
+ uint32 actionid;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ ASSERT(name);
+ ASSERT(len >= 0);
+
+ /* Get MUST have return space */
+ ASSERT(set || (arg && len));
+
+ /* Set does NOT take qualifiers */
+ ASSERT(!set || (!params && !plen));
+
+ if ((vi = bcm_iovar_lookup(dhd_iovars, name)) == NULL) {
+ bcmerror = BCME_UNSUPPORTED;
+ goto exit;
+ }
+
+ DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__,
+ name, (set ? "set" : "get"), len, plen));
+
+ /* set up 'params' pointer in case this is a set command so that
+ * the convenience int and bool code can be common to set and get
+ */
+ if (params == NULL) {
+ params = arg;
+ plen = len;
+ }
+
+ if (vi->type == IOVT_VOID)
+ val_size = 0;
+ else if (vi->type == IOVT_BUFFER)
+ val_size = len;
+ else
+ /* all other types are integer sized */
+ val_size = sizeof(int);
+
+ actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+ bcmerror = dhd_doiovar(dhd_pub, vi, actionid, name, params, plen, arg, len, val_size);
+
+exit:
+ return bcmerror;
+}
+
+int
+dhd_ioctl(dhd_pub_t *dhd_pub, dhd_ioctl_t *ioc, void *buf, uint buflen)
+{
+ int bcmerror = 0;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (!buf) return BCME_BADARG;
+
+ switch (ioc->cmd) {
+ case DHD_GET_MAGIC:
+ if (buflen < sizeof(int))
+ bcmerror = BCME_BUFTOOSHORT;
+ else
+ *(int*)buf = DHD_IOCTL_MAGIC;
+ break;
+
+ case DHD_GET_VERSION:
+ if (buflen < sizeof(int))
+ bcmerror = -BCME_BUFTOOSHORT;
+ else
+ *(int*)buf = DHD_IOCTL_VERSION;
+ break;
+
+ case DHD_GET_VAR:
+ case DHD_SET_VAR: {
+ char *arg;
+ uint arglen;
+
+ /* scan past the name to any arguments */
+ for (arg = buf, arglen = buflen; *arg && arglen; arg++, arglen--);
+
+ if (*arg) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+
+ /* account for the NUL terminator */
+ arg++, arglen--;
+
+ /* call with the appropriate arguments */
+ if (ioc->cmd == DHD_GET_VAR)
+ bcmerror = dhd_iovar_op(dhd_pub, buf, arg, arglen,
+ buf, buflen, IOV_GET);
+ else
+ bcmerror = dhd_iovar_op(dhd_pub, buf, NULL, 0, arg, arglen, IOV_SET);
+ if (bcmerror != BCME_UNSUPPORTED)
+ break;
+
+ /* not in generic table, try protocol module */
+ if (ioc->cmd == DHD_GET_VAR)
+ bcmerror = dhd_prot_iovar_op(dhd_pub, buf, arg,
+ arglen, buf, buflen, IOV_GET);
+ else
+ bcmerror = dhd_prot_iovar_op(dhd_pub, buf,
+ NULL, 0, arg, arglen, IOV_SET);
+ if (bcmerror != BCME_UNSUPPORTED)
+ break;
+
+ /* if still not found, try bus module */
+ if (ioc->cmd == DHD_GET_VAR)
+ bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
+ arg, arglen, buf, buflen, IOV_GET);
+ else
+ bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
+ NULL, 0, arg, arglen, IOV_SET);
+
+ break;
+ }
+
+ default:
+ bcmerror = BCME_UNSUPPORTED;
+ }
+
+ return bcmerror;
+}
+
+
+#ifdef SHOW_EVENTS
+static void
+wl_show_host_event(wl_event_msg_t *event, void *event_data)
+{
+ uint i, status, reason;
+ bool group = FALSE, flush_txq = FALSE, link = FALSE;
+ char *auth_str, *event_name;
+ uchar *buf;
+ char err_msg[256], eabuf[ETHER_ADDR_STR_LEN];
+ static struct {uint event; char *event_name;} event_names[] = {
+ {WLC_E_SET_SSID, "SET_SSID"},
+ {WLC_E_JOIN, "JOIN"},
+ {WLC_E_START, "START"},
+ {WLC_E_AUTH, "AUTH"},
+ {WLC_E_AUTH_IND, "AUTH_IND"},
+ {WLC_E_DEAUTH, "DEAUTH"},
+ {WLC_E_DEAUTH_IND, "DEAUTH_IND"},
+ {WLC_E_ASSOC, "ASSOC"},
+ {WLC_E_ASSOC_IND, "ASSOC_IND"},
+ {WLC_E_REASSOC, "REASSOC"},
+ {WLC_E_REASSOC_IND, "REASSOC_IND"},
+ {WLC_E_DISASSOC, "DISASSOC"},
+ {WLC_E_DISASSOC_IND, "DISASSOC_IND"},
+ {WLC_E_QUIET_START, "START_QUIET"},
+ {WLC_E_QUIET_END, "END_QUIET"},
+ {WLC_E_BEACON_RX, "BEACON_RX"},
+ {WLC_E_LINK, "LINK"},
+ {WLC_E_MIC_ERROR, "MIC_ERROR"},
+ {WLC_E_NDIS_LINK, "NDIS_LINK"},
+ {WLC_E_ROAM, "ROAM"},
+ {WLC_E_TXFAIL, "TXFAIL"},
+ {WLC_E_PMKID_CACHE, "PMKID_CACHE"},
+ {WLC_E_RETROGRADE_TSF, "RETROGRADE_TSF"},
+ {WLC_E_PRUNE, "PRUNE"},
+ {WLC_E_AUTOAUTH, "AUTOAUTH"},
+ {WLC_E_EAPOL_MSG, "EAPOL_MSG"},
+ {WLC_E_SCAN_COMPLETE, "SCAN_COMPLETE"},
+ {WLC_E_ADDTS_IND, "ADDTS_IND"},
+ {WLC_E_DELTS_IND, "DELTS_IND"},
+ {WLC_E_BCNSENT_IND, "BCNSENT_IND"},
+ {WLC_E_BCNRX_MSG, "BCNRX_MSG"},
+ {WLC_E_BCNLOST_MSG, "BCNLOST_MSG"},
+ {WLC_E_ROAM_PREP, "ROAM_PREP"},
+ {WLC_E_PFN_NET_FOUND, "PNO_NET_FOUND"},
+ {WLC_E_PFN_NET_LOST, "PNO_NET_LOST"},
+ {WLC_E_RESET_COMPLETE, "RESET_COMPLETE"},
+ {WLC_E_JOIN_START, "JOIN_START"},
+ {WLC_E_ROAM_START, "ROAM_START"},
+ {WLC_E_ASSOC_START, "ASSOC_START"},
+ {WLC_E_IBSS_ASSOC, "IBSS_ASSOC"},
+ {WLC_E_RADIO, "RADIO"},
+ {WLC_E_PSM_WATCHDOG, "PSM_WATCHDOG"},
+ {WLC_E_PROBREQ_MSG, "PROBREQ_MSG"},
+ {WLC_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND"},
+ {WLC_E_PSK_SUP, "PSK_SUP"},
+ {WLC_E_COUNTRY_CODE_CHANGED, "COUNTRY_CODE_CHANGED"},
+ {WLC_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME"},
+ {WLC_E_ICV_ERROR, "ICV_ERROR"},
+ {WLC_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR"},
+ {WLC_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR"},
+ {WLC_E_TRACE, "TRACE"},
+ {WLC_E_ACTION_FRAME, "ACTION FRAME"},
+ {WLC_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"},
+ {WLC_E_IF, "IF"},
+ {WLC_E_RSSI, "RSSI"},
+ {WLC_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}
+ };
+ uint event_type, flags, auth_type, datalen;
+ event_type = ntoh32(event->event_type);
+ flags = ntoh16(event->flags);
+ status = ntoh32(event->status);
+ reason = ntoh32(event->reason);
+ auth_type = ntoh32(event->auth_type);
+ datalen = ntoh32(event->datalen);
+ /* debug dump of event messages */
+ sprintf(eabuf, "%02x:%02x:%02x:%02x:%02x:%02x",
+ (uchar)event->addr.octet[0]&0xff,
+ (uchar)event->addr.octet[1]&0xff,
+ (uchar)event->addr.octet[2]&0xff,
+ (uchar)event->addr.octet[3]&0xff,
+ (uchar)event->addr.octet[4]&0xff,
+ (uchar)event->addr.octet[5]&0xff);
+
+ event_name = "UNKNOWN";
+ for (i = 0; i < ARRAYSIZE(event_names); i++) {
+ if (event_names[i].event == event_type)
+ event_name = event_names[i].event_name;
+ }
+
+ DHD_EVENT(("EVENT: %s, event ID = %d\n", event_name, event_type));
+
+ if (flags & WLC_EVENT_MSG_LINK)
+ link = TRUE;
+ if (flags & WLC_EVENT_MSG_GROUP)
+ group = TRUE;
+ if (flags & WLC_EVENT_MSG_FLUSHTXQ)
+ flush_txq = TRUE;
+
+ switch (event_type) {
+ case WLC_E_START:
+ case WLC_E_DEAUTH:
+ case WLC_E_DISASSOC:
+ DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
+ break;
+
+ case WLC_E_ASSOC_IND:
+ case WLC_E_REASSOC_IND:
+ DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
+ break;
+
+ case WLC_E_ASSOC:
+ case WLC_E_REASSOC:
+ if (status == WLC_E_STATUS_SUCCESS) {
+ DHD_EVENT(("MACEVENT: %s, MAC %s, SUCCESS\n", event_name, eabuf));
+ } else if (status == WLC_E_STATUS_TIMEOUT) {
+ DHD_EVENT(("MACEVENT: %s, MAC %s, TIMEOUT\n", event_name, eabuf));
+ } else if (status == WLC_E_STATUS_FAIL) {
+ DHD_EVENT(("MACEVENT: %s, MAC %s, FAILURE, reason %d\n",
+ event_name, eabuf, (int)reason));
+ } else {
+ DHD_EVENT(("MACEVENT: %s, MAC %s, unexpected status %d\n",
+ event_name, eabuf, (int)status));
+ }
+ break;
+
+ case WLC_E_DEAUTH_IND:
+ case WLC_E_DISASSOC_IND:
+ DHD_EVENT(("MACEVENT: %s, MAC %s, reason %d\n", event_name, eabuf, (int)reason));
+ break;
+
+ case WLC_E_AUTH:
+ case WLC_E_AUTH_IND:
+ if (auth_type == DOT11_OPEN_SYSTEM)
+ auth_str = "Open System";
+ else if (auth_type == DOT11_SHARED_KEY)
+ auth_str = "Shared Key";
+ else {
+ sprintf(err_msg, "AUTH unknown: %d", (int)auth_type);
+ auth_str = err_msg;
+ }
+ if (event_type == WLC_E_AUTH_IND) {
+ DHD_EVENT(("MACEVENT: %s, MAC %s, %s\n", event_name, eabuf, auth_str));
+ } else if (status == WLC_E_STATUS_SUCCESS) {
+ DHD_EVENT(("MACEVENT: %s, MAC %s, %s, SUCCESS\n",
+ event_name, eabuf, auth_str));
+ } else if (status == WLC_E_STATUS_TIMEOUT) {
+ DHD_EVENT(("MACEVENT: %s, MAC %s, %s, TIMEOUT\n",
+ event_name, eabuf, auth_str));
+ } else if (status == WLC_E_STATUS_FAIL) {
+ DHD_EVENT(("MACEVENT: %s, MAC %s, %s, FAILURE, reason %d\n",
+ event_name, eabuf, auth_str, (int)reason));
+ }
+
+ break;
+
+ case WLC_E_JOIN:
+ case WLC_E_ROAM:
+ case WLC_E_SET_SSID:
+ if (status == WLC_E_STATUS_SUCCESS) {
+ DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
+ } else if (status == WLC_E_STATUS_FAIL) {
+ DHD_EVENT(("MACEVENT: %s, failed\n", event_name));
+ } else if (status == WLC_E_STATUS_NO_NETWORKS) {
+ DHD_EVENT(("MACEVENT: %s, no networks found\n", event_name));
+ } else {
+ DHD_EVENT(("MACEVENT: %s, unexpected status %d\n",
+ event_name, (int)status));
+ }
+ break;
+
+ case WLC_E_BEACON_RX:
+ if (status == WLC_E_STATUS_SUCCESS) {
+ DHD_EVENT(("MACEVENT: %s, SUCCESS\n", event_name));
+ } else if (status == WLC_E_STATUS_FAIL) {
+ DHD_EVENT(("MACEVENT: %s, FAIL\n", event_name));
+ } else {
+ DHD_EVENT(("MACEVENT: %s, status %d\n", event_name, status));
+ }
+ break;
+
+ case WLC_E_LINK:
+ DHD_EVENT(("MACEVENT: %s %s\n", event_name, link?"UP":"DOWN"));
+ break;
+
+ case WLC_E_MIC_ERROR:
+ DHD_EVENT(("MACEVENT: %s, MAC %s, Group %d, Flush %d\n",
+ event_name, eabuf, group, flush_txq));
+ break;
+
+ case WLC_E_ICV_ERROR:
+ case WLC_E_UNICAST_DECODE_ERROR:
+ case WLC_E_MULTICAST_DECODE_ERROR:
+ DHD_EVENT(("MACEVENT: %s, MAC %s\n",
+ event_name, eabuf));
+ break;
+
+ case WLC_E_TXFAIL:
+ DHD_EVENT(("MACEVENT: %s, RA %s\n", event_name, eabuf));
+ break;
+
+ case WLC_E_SCAN_COMPLETE:
+ case WLC_E_PMKID_CACHE:
+ DHD_EVENT(("MACEVENT: %s\n", event_name));
+ break;
+
+ case WLC_E_PFN_NET_FOUND:
+ case WLC_E_PFN_NET_LOST:
+ case WLC_E_PFN_SCAN_COMPLETE:
+ DHD_EVENT(("PNOEVENT: %s\n", event_name));
+ break;
+
+ case WLC_E_PSK_SUP:
+ case WLC_E_PRUNE:
+ DHD_EVENT(("MACEVENT: %s, status %d, reason %d\n",
+ event_name, (int)status, (int)reason));
+ break;
+
+ case WLC_E_TRACE:
+ {
+ static uint32 seqnum_prev = 0;
+ msgtrace_hdr_t hdr;
+ uint32 nblost;
+ char *s, *p;
+
+ buf = (uchar *) event_data;
+ memcpy(&hdr, buf, MSGTRACE_HDRLEN);
+
+ if (hdr.version != MSGTRACE_VERSION) {
+ printf("\nMACEVENT: %s [unsupported version --> "
+ "dhd version:%d dongle version:%d]\n",
+ event_name, MSGTRACE_VERSION, hdr.version);
+ /* Reset datalen to avoid display below */
+ datalen = 0;
+ break;
+ }
+
+ /* There are 2 bytes available at the end of data */
+ buf[MSGTRACE_HDRLEN + ntoh16(hdr.len)] = '\0';
+
+ if (ntoh32(hdr.discarded_bytes) || ntoh32(hdr.discarded_printf)) {
+ printf("\nWLC_E_TRACE: [Discarded traces in dongle -->"
+ "discarded_bytes %d discarded_printf %d]\n",
+ ntoh32(hdr.discarded_bytes), ntoh32(hdr.discarded_printf));
+ }
+
+ nblost = ntoh32(hdr.seqnum) - seqnum_prev - 1;
+ if (nblost > 0) {
+ printf("\nWLC_E_TRACE: [Event lost --> seqnum %d nblost %d\n",
+ ntoh32(hdr.seqnum), nblost);
+ }
+ seqnum_prev = ntoh32(hdr.seqnum);
+
+ /* Display the trace buffer. Advance from \n to \n to avoid display big
+ * printf (issue with Linux printk )
+ */
+ p = (char *)&buf[MSGTRACE_HDRLEN];
+ while ((s = strstr(p, "\n")) != NULL) {
+ *s = '\0';
+ printf("%s\n", p);
+ p = s + 1;
+ }
+ printf("%s\n", p);
+
+ /* Reset datalen to avoid display below */
+ datalen = 0;
+ }
+ break;
+
+
+ case WLC_E_RSSI:
+ DHD_EVENT(("MACEVENT: %s %d\n", event_name, ntoh32(*((int *)event_data))));
+ break;
+
+ default:
+ DHD_EVENT(("MACEVENT: %s %d, MAC %s, status %d, reason %d, auth %d\n",
+ event_name, event_type, eabuf, (int)status, (int)reason,
+ (int)auth_type));
+ break;
+ }
+
+ /* show any appended data */
+ if (datalen) {
+ buf = (uchar *) event_data;
+ DHD_EVENT((" data (%d) : ", datalen));
+ for (i = 0; i < datalen; i++)
+ DHD_EVENT((" 0x%02x ", *buf++));
+ DHD_EVENT(("\n"));
+ }
+}
+#endif /* SHOW_EVENTS */
+
+int
+wl_host_event(struct dhd_info *dhd, int *ifidx, void *pktdata,
+ wl_event_msg_t *event, void **data_ptr)
+{
+ /* check whether packet is a BRCM event pkt */
+ bcm_event_t *pvt_data = (bcm_event_t *)pktdata;
+ char *event_data;
+ uint32 type, status;
+ uint16 flags;
+ int evlen;
+
+ if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) {
+ DHD_ERROR(("%s: mismatched OUI, bailing\n", __FUNCTION__));
+ return (BCME_ERROR);
+ }
+
+ /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */
+ if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT) {
+ DHD_ERROR(("%s: mismatched subtype, bailing\n", __FUNCTION__));
+ return (BCME_ERROR);
+ }
+
+ *data_ptr = &pvt_data[1];
+ event_data = *data_ptr;
+
+ /* memcpy since BRCM event pkt may be unaligned. */
+ memcpy(event, &pvt_data->event, sizeof(wl_event_msg_t));
+
+ type = ntoh32_ua((void *)&event->event_type);
+ flags = ntoh16_ua((void *)&event->flags);
+ status = ntoh32_ua((void *)&event->status);
+ evlen = ntoh32_ua((void *)&event->datalen) + sizeof(bcm_event_t);
+
+ switch (type) {
+ case WLC_E_IF:
+ {
+ dhd_if_event_t *ifevent = (dhd_if_event_t *)event_data;
+ DHD_TRACE(("%s: if event\n", __FUNCTION__));
+
+ if (ifevent->ifidx > 0 && ifevent->ifidx < DHD_MAX_IFS)
+ {
+ if (ifevent->action == WLC_E_IF_ADD)
+ dhd_add_if(dhd, ifevent->ifidx,
+ NULL, event->ifname,
+ pvt_data->eth.ether_dhost,
+ ifevent->flags, ifevent->bssidx);
+ else
+ dhd_del_if(dhd, ifevent->ifidx);
+ } else {
+ DHD_ERROR(("%s: Invalid ifidx %d for %s\n",
+ __FUNCTION__, ifevent->ifidx, event->ifname));
+ }
+ }
+ /* send up the if event: btamp user needs it */
+ *ifidx = dhd_ifname2idx(dhd, event->ifname);
+ /* push up to external supp/auth */
+ dhd_event(dhd, (char *)pvt_data, evlen, *ifidx);
+ break;
+
+
+#ifdef P2P
+ case WLC_E_NDIS_LINK:
+ break;
+#endif
+ /* fall through */
+ /* These are what external supplicant/authenticator wants */
+ case WLC_E_LINK:
+ case WLC_E_ASSOC_IND:
+ case WLC_E_REASSOC_IND:
+ case WLC_E_DISASSOC_IND:
+ case WLC_E_MIC_ERROR:
+ default:
+ /* Fall through: this should get _everything_ */
+
+ *ifidx = dhd_ifname2idx(dhd, event->ifname);
+ /* push up to external supp/auth */
+ dhd_event(dhd, (char *)pvt_data, evlen, *ifidx);
+ DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n",
+ __FUNCTION__, type, flags, status));
+
+ /* put it back to WLC_E_NDIS_LINK */
+ if (type == WLC_E_NDIS_LINK) {
+ uint32 temp;
+
+ temp = ntoh32_ua((void *)&event->event_type);
+ DHD_TRACE(("Converted to WLC_E_LINK type %d\n", temp));
+
+ temp = ntoh32(WLC_E_NDIS_LINK);
+ memcpy((void *)(&pvt_data->event.event_type), &temp,
+ sizeof(pvt_data->event.event_type));
+ }
+ break;
+ }
+
+#ifdef SHOW_EVENTS
+ wl_show_host_event(event, event_data);
+#endif /* SHOW_EVENTS */
+
+ return (BCME_OK);
+}
+
+
+void
+wl_event_to_host_order(wl_event_msg_t *evt)
+{
+ /* Event struct members passed from dongle to host are stored in network
+ * byte order. Convert all members to host-order.
+ */
+ evt->event_type = ntoh32(evt->event_type);
+ evt->flags = ntoh16(evt->flags);
+ evt->status = ntoh32(evt->status);
+ evt->reason = ntoh32(evt->reason);
+ evt->auth_type = ntoh32(evt->auth_type);
+ evt->datalen = ntoh32(evt->datalen);
+ evt->version = ntoh16(evt->version);
+}
+
+void print_buf(void *pbuf, int len, int bytes_per_line)
+{
+ int i, j = 0;
+ unsigned char *buf = pbuf;
+
+ if (bytes_per_line == 0) {
+ bytes_per_line = len;
+ }
+
+ for (i = 0; i < len; i++) {
+ printf("%2.2x", *buf++);
+ j++;
+ if (j == bytes_per_line) {
+ printf("\n");
+ j = 0;
+ } else {
+ printf(":");
+ }
+ }
+ printf("\n");
+}
+
+#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
+
+#ifdef PKT_FILTER_SUPPORT
+/* Convert user's input in hex pattern to byte-size mask */
+static int
+wl_pattern_atoh(char *src, char *dst)
+{
+ int i;
+ if (strncmp(src, "0x", 2) != 0 &&
+ strncmp(src, "0X", 2) != 0) {
+ DHD_ERROR(("Mask invalid format. Needs to start with 0x\n"));
+ return -1;
+ }
+ src = src + 2; /* Skip past 0x */
+ if (strlen(src) % 2 != 0) {
+ DHD_ERROR(("Mask invalid format. Needs to be of even length\n"));
+ return -1;
+ }
+ for (i = 0; *src != '\0'; i++) {
+ char num[3];
+ strncpy(num, src, 2);
+ num[2] = '\0';
+ dst[i] = (uint8)strtoul(num, NULL, 16);
+ src += 2;
+ }
+ return i;
+}
+
+void
+dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode)
+{
+ char *argv[8];
+ int i = 0;
+ const char *str;
+ int buf_len;
+ int str_len;
+ char *arg_save = 0, *arg_org = 0;
+ int rc;
+ char buf[128];
+ wl_pkt_filter_enable_t enable_parm;
+ wl_pkt_filter_enable_t * pkt_filterp;
+
+ if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) {
+ DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+ goto fail;
+ }
+ arg_org = arg_save;
+ memcpy(arg_save, arg, strlen(arg) + 1);
+
+ argv[i] = bcmstrtok(&arg_save, " ", 0);
+
+ i = 0;
+ if (NULL == argv[i]) {
+ DHD_ERROR(("No args provided\n"));
+ goto fail;
+ }
+
+ str = "pkt_filter_enable";
+ str_len = strlen(str);
+ strncpy(buf, str, str_len);
+ buf[str_len] = '\0';
+ buf_len = str_len + 1;
+
+ pkt_filterp = (wl_pkt_filter_enable_t *)(buf + str_len + 1);
+
+ /* Parse packet filter id. */
+ enable_parm.id = htod32(strtoul(argv[i], NULL, 0));
+
+ /* Parse enable/disable value. */
+ enable_parm.enable = htod32(enable);
+
+ buf_len += sizeof(enable_parm);
+ memcpy((char *)pkt_filterp,
+ &enable_parm,
+ sizeof(enable_parm));
+
+ /* Enable/disable the specified filter. */
+ rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len);
+ rc = rc >= 0 ? 0 : rc;
+ if (rc)
+ DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
+ __FUNCTION__, arg, rc));
+ else
+ DHD_TRACE(("%s: successfully added pktfilter %s\n",
+ __FUNCTION__, arg));
+
+ /* Contorl the master mode */
+ bcm_mkiovar("pkt_filter_mode", (char *)&master_mode, 4, buf, sizeof(buf));
+ rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf));
+ rc = rc >= 0 ? 0 : rc;
+ if (rc)
+ DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
+ __FUNCTION__, arg, rc));
+
+fail:
+ if (arg_org)
+ MFREE(dhd->osh, arg_org, strlen(arg) + 1);
+}
+
+void
+dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg)
+{
+ const char *str;
+ wl_pkt_filter_t pkt_filter;
+ wl_pkt_filter_t *pkt_filterp;
+ int buf_len;
+ int str_len;
+ int rc;
+ uint32 mask_size;
+ uint32 pattern_size;
+ char *argv[8], * buf = 0;
+ int i = 0;
+ char *arg_save = 0, *arg_org = 0;
+#define BUF_SIZE 2048
+
+ if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) {
+ DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+ goto fail;
+ }
+
+ arg_org = arg_save;
+
+ if (!(buf = MALLOC(dhd->osh, BUF_SIZE))) {
+ DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
+ goto fail;
+ }
+
+ memcpy(arg_save, arg, strlen(arg) + 1);
+
+ if (strlen(arg) > BUF_SIZE) {
+ DHD_ERROR(("Not enough buffer %d < %d\n", (int)strlen(arg), (int)sizeof(buf)));
+ goto fail;
+ }
+
+ argv[i] = bcmstrtok(&arg_save, " ", 0);
+ while (argv[i++])
+ argv[i] = bcmstrtok(&arg_save, " ", 0);
+
+ i = 0;
+ if (NULL == argv[i]) {
+ DHD_ERROR(("No args provided\n"));
+ goto fail;
+ }
+
+ str = "pkt_filter_add";
+ str_len = strlen(str);
+ strncpy(buf, str, str_len);
+ buf[ str_len ] = '\0';
+ buf_len = str_len + 1;
+
+ pkt_filterp = (wl_pkt_filter_t *) (buf + str_len + 1);
+
+ /* Parse packet filter id. */
+ pkt_filter.id = htod32(strtoul(argv[i], NULL, 0));
+
+ if (NULL == argv[++i]) {
+ DHD_ERROR(("Polarity not provided\n"));
+ goto fail;
+ }
+
+ /* Parse filter polarity. */
+ pkt_filter.negate_match = htod32(strtoul(argv[i], NULL, 0));
+
+ if (NULL == argv[++i]) {
+ DHD_ERROR(("Filter type not provided\n"));
+ goto fail;
+ }
+
+ /* Parse filter type. */
+ pkt_filter.type = htod32(strtoul(argv[i], NULL, 0));
+
+ if (NULL == argv[++i]) {
+ DHD_ERROR(("Offset not provided\n"));
+ goto fail;
+ }
+
+ /* Parse pattern filter offset. */
+ pkt_filter.u.pattern.offset = htod32(strtoul(argv[i], NULL, 0));
+
+ if (NULL == argv[++i]) {
+ DHD_ERROR(("Bitmask not provided\n"));
+ goto fail;
+ }
+
+ /* Parse pattern filter mask. */
+ mask_size =
+ htod32(wl_pattern_atoh(argv[i], (char *) pkt_filterp->u.pattern.mask_and_pattern));
+
+ if (NULL == argv[++i]) {
+ DHD_ERROR(("Pattern not provided\n"));
+ goto fail;
+ }
+
+ /* Parse pattern filter pattern. */
+ pattern_size =
+ htod32(wl_pattern_atoh(argv[i],
+ (char *) &pkt_filterp->u.pattern.mask_and_pattern[mask_size]));
+
+ if (mask_size != pattern_size) {
+ DHD_ERROR(("Mask and pattern not the same size\n"));
+ goto fail;
+ }
+
+ pkt_filter.u.pattern.size_bytes = mask_size;
+ buf_len += WL_PKT_FILTER_FIXED_LEN;
+ buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size);
+
+ /* Keep-alive attributes are set in local variable (keep_alive_pkt), and
+ ** then memcpy'ed into buffer (keep_alive_pktp) since there is no
+ ** guarantee that the buffer is properly aligned.
+ */
+ memcpy((char *)pkt_filterp,
+ &pkt_filter,
+ WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN);
+
+ rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len);
+ rc = rc >= 0 ? 0 : rc;
+
+ if (rc)
+ DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
+ __FUNCTION__, arg, rc));
+ else
+ DHD_TRACE(("%s: successfully added pktfilter %s\n",
+ __FUNCTION__, arg));
+
+fail:
+ if (arg_org)
+ MFREE(dhd->osh, arg_org, strlen(arg) + 1);
+
+ if (buf)
+ MFREE(dhd->osh, buf, BUF_SIZE);
+}
+#endif
+
+#ifdef ARP_OFFLOAD_SUPPORT
+void
+dhd_arp_offload_set(dhd_pub_t * dhd, int arp_mode)
+{
+ char iovbuf[32];
+ int retcode;
+
+ bcm_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf));
+ retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+ retcode = retcode >= 0 ? 0 : retcode;
+ if (retcode)
+ DHD_TRACE(("%s: failed to set ARP offload mode to 0x%x, retcode = %d\n",
+ __FUNCTION__, arp_mode, retcode));
+ else
+ DHD_TRACE(("%s: successfully set ARP offload mode to 0x%x\n",
+ __FUNCTION__, arp_mode));
+}
+
+void
+dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable)
+{
+ char iovbuf[32];
+ int retcode;
+
+ bcm_mkiovar("arpoe", (char *)&arp_enable, 4, iovbuf, sizeof(iovbuf));
+ retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+ retcode = retcode >= 0 ? 0 : retcode;
+ if (retcode)
+ DHD_TRACE(("%s: failed to enabe ARP offload to %d, retcode = %d\n",
+ __FUNCTION__, arp_enable, retcode));
+ else
+ DHD_TRACE(("%s: successfully enabed ARP offload to %d\n",
+ __FUNCTION__, arp_enable));
+}
+#endif
+
+int
+dhd_preinit_ioctls(dhd_pub_t *dhd)
+{
+ char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */
+ uint up = 0;
+ char buf[128], *ptr;
+ uint power_mode = PM_FAST;
+ uint32 dongle_align = DHD_SDALIGN;
+ uint32 glom = 0;
+ uint bcn_timeout = 4;
+ int scan_assoc_time = 40;
+ int scan_unassoc_time = 40;
+ uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */
+#if defined(SOFTAP)
+ uint dtim = 1;
+#endif
+ int ret = 0;
+#ifdef GET_CUSTOM_MAC_ENABLE
+ struct ether_addr ea_addr;
+#endif /* GET_CUSTOM_MAC_ENABLE */
+
+ dhd_os_proto_block(dhd);
+
+#ifdef GET_CUSTOM_MAC_ENABLE
+ /*
+ ** Read MAC address from external customer place
+ ** NOTE that default mac address has to be present in otp or nvram file
+ ** to bring up firmware but unique per board mac address maybe provided
+ ** by customer code
+ */
+ ret = dhd_custom_get_mac_address(ea_addr.octet);
+ if (!ret) {
+ bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf));
+ ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf));
+ if (ret < 0) {
+ DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret));
+ } else
+ memcpy(dhd->mac.octet, (void *)&ea_addr, ETHER_ADDR_LEN);
+ }
+#endif /* GET_CUSTOM_MAC_ENABLE */
+
+#ifdef SET_RANDOM_MAC_SOFTAP
+ if (strstr(fw_path, "apsta") != NULL) {
+ uint rand_mac;
+
+ srandom32((uint)jiffies);
+ rand_mac = random32();
+ iovbuf[0] = 0x02; /* locally administered bit */
+ iovbuf[1] = 0x1A;
+ iovbuf[2] = 0x11;
+ iovbuf[3] = (unsigned char)(rand_mac & 0x0F) | 0xF0;
+ iovbuf[4] = (unsigned char)(rand_mac >> 8);
+ iovbuf[5] = (unsigned char)(rand_mac >> 16);
+
+ printk("Broadcom Dongle Host Driver mac=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ iovbuf[0], iovbuf[1], iovbuf[2], iovbuf[3], iovbuf[4], iovbuf[5]);
+
+ bcm_mkiovar("cur_etheraddr", (void *)iovbuf, ETHER_ADDR_LEN, buf, sizeof(buf));
+ ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf));
+ if (ret < 0) {
+ DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret));
+ } else
+ memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN);
+ }
+#endif /* SET_RANDOM_MAC_SOFTAP */
+
+ /* Set Country code */
+ if (dhd->country_code[0] != 0) {
+ if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_COUNTRY,
+ dhd->country_code, sizeof(dhd->country_code)) < 0) {
+ DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__));
+ }
+ }
+
+ /* Set Listen Interval */
+ bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf));
+ if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0)
+ DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret));
+
+ /* query for 'ver' to get version info from firmware */
+ memset(buf, 0, sizeof(buf));
+ ptr = buf;
+ bcm_mkiovar("ver", 0, 0, buf, sizeof(buf));
+ dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
+ bcmstrtok(&ptr, "\n", 0);
+ /* Print fw version info */
+ DHD_ERROR(("Firmware version = %s\n", buf));
+
+ /* Set PowerSave mode */
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode));
+
+ /* Match Host and Dongle rx alignment */
+ bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf));
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+ /* disable glom option per default */
+ bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf));
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+ /* Setup timeout if Beacons are lost and roam is off to report link down */
+ bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf));
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+ /* Enable/Disable build-in roaming to allowed ext supplicant to take of romaing */
+ bcm_mkiovar("roam_off", (char *)&dhd_roam, 4, iovbuf, sizeof(iovbuf));
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+#if defined(SOFTAP)
+ if (ap_fw_loaded == TRUE) {
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_DTIMPRD, (char *)&dtim, sizeof(dtim));
+ }
+#endif
+
+ if (dhd_roam == 0)
+ {
+ /* set internal roaming roaming parameters */
+ int roam_scan_period = 30; /* in sec */
+ int roam_fullscan_period = 120; /* in sec */
+ int roam_trigger = -85;
+ int roam_delta = 15;
+ int band;
+ int band_temp_set = WLC_BAND_2G;
+
+ if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_ROAM_SCAN_PERIOD, \
+ (char *)&roam_scan_period, sizeof(roam_scan_period)) < 0)
+ DHD_ERROR(("%s: roam scan setup failed\n", __FUNCTION__));
+
+ bcm_mkiovar("fullroamperiod", (char *)&roam_fullscan_period, \
+ 4, iovbuf, sizeof(iovbuf));
+ if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, \
+ iovbuf, sizeof(iovbuf)) < 0)
+ DHD_ERROR(("%s: roam fullscan setup failed\n", __FUNCTION__));
+
+ if (dhdcdc_query_ioctl(dhd, 0, WLC_GET_BAND, \
+ (char *)&band, sizeof(band)) < 0)
+ DHD_ERROR(("%s: roam delta setting failed\n", __FUNCTION__));
+ else {
+ if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_ALL))
+ {
+ /* temp set band to insert new roams values */
+ if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_BAND, \
+ (char *)&band_temp_set, sizeof(band_temp_set)) < 0)
+ DHD_ERROR(("%s: local band seting failed\n", __FUNCTION__));
+ }
+ if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_ROAM_DELTA, \
+ (char *)&roam_delta, sizeof(roam_delta)) < 0)
+ DHD_ERROR(("%s: roam delta setting failed\n", __FUNCTION__));
+
+ if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_ROAM_TRIGGER, \
+ (char *)&roam_trigger, sizeof(roam_trigger)) < 0)
+ DHD_ERROR(("%s: roam trigger setting failed\n", __FUNCTION__));
+
+ /* Restore original band settinngs */
+ if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_BAND, \
+ (char *)&band, sizeof(band)) < 0)
+ DHD_ERROR(("%s: Original band restore failed\n", __FUNCTION__));
+ }
+ }
+
+ /* Force STA UP */
+ if (dhd_radio_up)
+ dhdcdc_set_ioctl(dhd, 0, WLC_UP, (char *)&up, sizeof(up));
+
+ /* Setup event_msgs */
+ bcm_mkiovar("event_msgs", dhd->eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf));
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time,
+ sizeof(scan_assoc_time));
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time,
+ sizeof(scan_unassoc_time));
+
+#ifdef ARP_OFFLOAD_SUPPORT
+ /* Set and enable ARP offload feature */
+ if (dhd_arp_enable)
+ dhd_arp_offload_set(dhd, dhd_arp_mode);
+ dhd_arp_offload_enable(dhd, dhd_arp_enable);
+#endif /* ARP_OFFLOAD_SUPPORT */
+
+#ifdef PKT_FILTER_SUPPORT
+ {
+ int i;
+ /* Set up pkt filter */
+ if (dhd_pkt_filter_enable) {
+ for (i = 0; i < dhd->pktfilter_count; i++) {
+ dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
+ dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
+ dhd_pkt_filter_init, dhd_master_mode);
+ }
+ }
+ }
+#endif /* PKT_FILTER_SUPPORT */
+
+#if defined(KEEP_ALIVE)
+ {
+ /* Set Keep Alive : be sure to use FW with -keepalive */
+ int res;
+
+ if (ap_fw_loaded == FALSE) {
+ if ((res = dhd_keep_alive_onoff(dhd, 1)) < 0)
+ DHD_ERROR(("%s set keeplive failed %d\n", \
+ __FUNCTION__, res));
+ }
+ }
+#endif
+
+ dhd_os_proto_unblock(dhd);
+
+ return 0;
+}
+
+#ifdef SIMPLE_ISCAN
+
+uint iscan_thread_id;
+iscan_buf_t * iscan_chain = 0;
+
+iscan_buf_t *
+dhd_iscan_allocate_buf(dhd_pub_t *dhd, iscan_buf_t **iscanbuf)
+{
+ iscan_buf_t *iscanbuf_alloc = 0;
+ iscan_buf_t *iscanbuf_head;
+
+ dhd_iscan_lock();
+
+ iscanbuf_alloc = (iscan_buf_t*)MALLOC(dhd->osh, sizeof(iscan_buf_t));
+ if (iscanbuf_alloc == NULL)
+ goto fail;
+
+ iscanbuf_alloc->next = NULL;
+ iscanbuf_head = *iscanbuf;
+
+ DHD_ISCAN(("%s: addr of allocated node = 0x%X"
+ "addr of iscanbuf_head = 0x%X dhd = 0x%X\n",
+ __FUNCTION__, iscanbuf_alloc, iscanbuf_head, dhd));
+
+ if (iscanbuf_head == NULL) {
+ *iscanbuf = iscanbuf_alloc;
+ DHD_ISCAN(("%s: Head is allocated\n", __FUNCTION__));
+ goto fail;
+ }
+
+ while (iscanbuf_head->next)
+ iscanbuf_head = iscanbuf_head->next;
+
+ iscanbuf_head->next = iscanbuf_alloc;
+
+fail:
+ dhd_iscan_unlock();
+ return iscanbuf_alloc;
+}
+
+void
+dhd_iscan_free_buf(void *dhdp, iscan_buf_t *iscan_delete)
+{
+ iscan_buf_t *iscanbuf_free = 0;
+ iscan_buf_t *iscanbuf_prv = 0;
+ iscan_buf_t *iscanbuf_cur = iscan_chain;
+ dhd_pub_t *dhd = dhd_bus_pub(dhdp);
+
+ dhd_iscan_lock();
+ /* If iscan_delete is null then delete the entire
+ * chain or else delete specific one provided
+ */
+ if (!iscan_delete) {
+ while (iscanbuf_cur) {
+ iscanbuf_free = iscanbuf_cur;
+ iscanbuf_cur = iscanbuf_cur->next;
+ iscanbuf_free->next = 0;
+ MFREE(dhd->osh, iscanbuf_free, sizeof(iscan_buf_t));
+ }
+ iscan_chain = 0;
+ } else {
+ while (iscanbuf_cur) {
+ if (iscanbuf_cur == iscan_delete)
+ break;
+ iscanbuf_prv = iscanbuf_cur;
+ iscanbuf_cur = iscanbuf_cur->next;
+ }
+ if (iscanbuf_prv)
+ iscanbuf_prv->next = iscan_delete->next;
+
+ iscan_delete->next = 0;
+ MFREE(dhd->osh, iscan_delete, sizeof(iscan_buf_t));
+
+ if (!iscanbuf_prv)
+ iscan_chain = 0;
+ }
+ dhd_iscan_unlock();
+}
+
+iscan_buf_t *
+dhd_iscan_result_buf(void)
+{
+ return iscan_chain;
+}
+
+
+
+/*
+* print scan cache
+* print partial iscan_skip list differently
+*/
+int
+dhd_iscan_print_cache(iscan_buf_t *iscan_skip)
+{
+ int i = 0, l = 0;
+ iscan_buf_t *iscan_cur;
+ wl_iscan_results_t *list;
+ wl_scan_results_t *results;
+ wl_bss_info_t UNALIGNED *bi;
+
+ dhd_iscan_lock();
+
+ iscan_cur = dhd_iscan_result_buf();
+
+ while (iscan_cur) {
+ list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
+ if (!list)
+ break;
+
+ results = (wl_scan_results_t *)&list->results;
+ if (!results)
+ break;
+
+ if (results->version != WL_BSS_INFO_VERSION) {
+ DHD_ISCAN(("%s: results->version %d != WL_BSS_INFO_VERSION\n",
+ __FUNCTION__, results->version));
+ goto done;
+ }
+
+ bi = results->bss_info;
+ for (i = 0; i < results->count; i++) {
+ if (!bi)
+ break;
+
+ DHD_ISCAN(("%s[%2.2d:%2.2d] %X:%X:%X:%X:%X:%X\n",
+ iscan_cur != iscan_skip?"BSS":"bss", l, i,
+ bi->BSSID.octet[0], bi->BSSID.octet[1], bi->BSSID.octet[2],
+ bi->BSSID.octet[3], bi->BSSID.octet[4], bi->BSSID.octet[5]));
+
+ bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
+ }
+ iscan_cur = iscan_cur->next;
+ l++;
+ }
+
+done:
+ dhd_iscan_unlock();
+ return 0;
+}
+
+/*
+* delete disappeared AP from specific scan cache but skip partial list in iscan_skip
+*/
+int
+dhd_iscan_delete_bss(void *dhdp, void *addr, iscan_buf_t *iscan_skip)
+{
+ int i = 0, j = 0, l = 0;
+ iscan_buf_t *iscan_cur;
+ wl_iscan_results_t *list;
+ wl_scan_results_t *results;
+ wl_bss_info_t UNALIGNED *bi, *bi_new, *bi_next;
+
+ uchar *s_addr = addr;
+
+ dhd_iscan_lock();
+ DHD_ISCAN(("%s: BSS to remove %X:%X:%X:%X:%X:%X\n",
+ __FUNCTION__, s_addr[0], s_addr[1], s_addr[2],
+ s_addr[3], s_addr[4], s_addr[5]));
+
+ iscan_cur = dhd_iscan_result_buf();
+
+ while (iscan_cur) {
+ if (iscan_cur != iscan_skip) {
+ list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
+ if (!list)
+ break;
+
+ results = (wl_scan_results_t *)&list->results;
+ if (!results)
+ break;
+
+ if (results->version != WL_BSS_INFO_VERSION) {
+ DHD_ERROR(("%s: results->version %d != WL_BSS_INFO_VERSION\n",
+ __FUNCTION__, results->version));
+ goto done;
+ }
+
+ bi = results->bss_info;
+ for (i = 0; i < results->count; i++) {
+ if (!bi)
+ break;
+
+ if (!memcmp(bi->BSSID.octet, addr, ETHER_ADDR_LEN)) {
+ DHD_ISCAN(("%s: Del BSS[%2.2d:%2.2d] %X:%X:%X:%X:%X:%X\n",
+ __FUNCTION__, l, i, bi->BSSID.octet[0],
+ bi->BSSID.octet[1], bi->BSSID.octet[2],
+ bi->BSSID.octet[3], bi->BSSID.octet[4],
+ bi->BSSID.octet[5]));
+
+ bi_new = bi;
+ bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
+/*
+ if(bi && bi_new) {
+ bcopy(bi, bi_new, results->buflen -
+ dtoh32(bi_new->length));
+ results->buflen -= dtoh32(bi_new->length);
+ }
+*/
+ results->buflen -= dtoh32(bi_new->length);
+ results->count--;
+
+ for (j = i; j < results->count; j++) {
+ if (bi && bi_new) {
+ DHD_ISCAN(("%s: Moved up BSS[%2.2d:%2.2d]"
+ "%X:%X:%X:%X:%X:%X\n",
+ __FUNCTION__, l, j, bi->BSSID.octet[0],
+ bi->BSSID.octet[1], bi->BSSID.octet[2],
+ bi->BSSID.octet[3], bi->BSSID.octet[4],
+ bi->BSSID.octet[5]));
+
+ bi_next = (wl_bss_info_t *)((uintptr)bi +
+ dtoh32(bi->length));
+ bcopy(bi, bi_new, dtoh32(bi->length));
+ bi_new = (wl_bss_info_t *)((uintptr)bi_new +
+ dtoh32(bi_new->length));
+ bi = bi_next;
+ }
+ }
+
+ if (results->count == 0) {
+ /* Prune now empty partial scan list */
+ dhd_iscan_free_buf(dhdp, iscan_cur);
+ goto done;
+ }
+ break;
+ }
+ bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
+ }
+ }
+ iscan_cur = iscan_cur->next;
+ l++;
+ }
+
+done:
+ dhd_iscan_unlock();
+ return 0;
+}
+
+int
+dhd_iscan_remove_duplicates(void * dhdp, iscan_buf_t *iscan_cur)
+{
+ int i = 0;
+ wl_iscan_results_t *list;
+ wl_scan_results_t *results;
+ wl_bss_info_t UNALIGNED *bi, *bi_new, *bi_next;
+
+ dhd_iscan_lock();
+
+ DHD_ISCAN(("%s: Scan cache before delete\n",
+ __FUNCTION__));
+ dhd_iscan_print_cache(iscan_cur);
+
+ if (!iscan_cur)
+ goto done;
+
+ list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
+ if (!list)
+ goto done;
+
+ results = (wl_scan_results_t *)&list->results;
+ if (!results)
+ goto done;
+
+ if (results->version != WL_BSS_INFO_VERSION) {
+ DHD_ERROR(("%s: results->version %d != WL_BSS_INFO_VERSION\n",
+ __FUNCTION__, results->version));
+ goto done;
+ }
+
+ bi = results->bss_info;
+ for (i = 0; i < results->count; i++) {
+ if (!bi)
+ break;
+
+ DHD_ISCAN(("%s: Find dups for BSS[%2.2d] %X:%X:%X:%X:%X:%X\n",
+ __FUNCTION__, i, bi->BSSID.octet[0], bi->BSSID.octet[1], bi->BSSID.octet[2],
+ bi->BSSID.octet[3], bi->BSSID.octet[4], bi->BSSID.octet[5]));
+
+ dhd_iscan_delete_bss(dhdp, bi->BSSID.octet, iscan_cur);
+
+ bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
+ }
+
+done:
+ DHD_ISCAN(("%s: Scan cache after delete\n", __FUNCTION__));
+ dhd_iscan_print_cache(iscan_cur);
+ dhd_iscan_unlock();
+ return 0;
+}
+
+void
+dhd_iscan_ind_scan_confirm(void *dhdp, bool status)
+{
+
+ dhd_ind_scan_confirm(dhdp, status);
+}
+
+int
+dhd_iscan_request(void * dhdp, uint16 action)
+{
+ int rc;
+ wl_iscan_params_t params;
+ dhd_pub_t *dhd = dhd_bus_pub(dhdp);
+ char buf[WLC_IOCTL_SMLEN];
+
+
+ memset(&params, 0, sizeof(wl_iscan_params_t));
+ memcpy(&params.params.bssid, &ether_bcast, ETHER_ADDR_LEN);
+
+ params.params.bss_type = DOT11_BSSTYPE_ANY;
+ params.params.scan_type = DOT11_SCANTYPE_ACTIVE;
+
+ params.params.nprobes = htod32(-1);
+ params.params.active_time = htod32(-1);
+ params.params.passive_time = htod32(-1);
+ params.params.home_time = htod32(-1);
+ params.params.channel_num = htod32(0);
+
+ params.version = htod32(ISCAN_REQ_VERSION);
+ params.action = htod16(action);
+ params.scan_duration = htod16(0);
+
+ bcm_mkiovar("iscan", (char *)&params, sizeof(wl_iscan_params_t), buf, WLC_IOCTL_SMLEN);
+ rc = dhd_wl_ioctl(dhdp, WLC_SET_VAR, buf, WLC_IOCTL_SMLEN);
+
+ return rc;
+}
+
+static int
+dhd_iscan_get_partial_result(void *dhdp, uint *scan_count)
+{
+ wl_iscan_results_t *list_buf;
+ wl_iscan_results_t list;
+ wl_scan_results_t *results;
+ iscan_buf_t *iscan_cur;
+ int status = -1;
+ dhd_pub_t *dhd = dhd_bus_pub(dhdp);
+ int rc;
+
+
+ iscan_cur = dhd_iscan_allocate_buf(dhd, &iscan_chain);
+ if (!iscan_cur) {
+ DHD_ERROR(("%s: Failed to allocate node\n", __FUNCTION__));
+ dhd_iscan_free_buf(dhdp, 0);
+ dhd_iscan_request(dhdp, WL_SCAN_ACTION_ABORT);
+ goto fail;
+ }
+
+ dhd_iscan_lock();
+
+ memset(iscan_cur->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN);
+ list_buf = (wl_iscan_results_t*)iscan_cur->iscan_buf;
+ results = &list_buf->results;
+ results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE;
+ results->version = 0;
+ results->count = 0;
+
+ memset(&list, 0, sizeof(list));
+ list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN);
+ bcm_mkiovar("iscanresults", (char *)&list, WL_ISCAN_RESULTS_FIXED_SIZE,
+ iscan_cur->iscan_buf, WLC_IW_ISCAN_MAXLEN);
+ rc = dhd_wl_ioctl(dhdp, WLC_GET_VAR, iscan_cur->iscan_buf, WLC_IW_ISCAN_MAXLEN);
+
+ results->buflen = dtoh32(results->buflen);
+ results->version = dtoh32(results->version);
+ *scan_count = results->count = dtoh32(results->count);
+ status = dtoh32(list_buf->status);
+
+ dhd_iscan_unlock();
+
+ if (!(*scan_count))
+ dhd_iscan_free_buf(dhdp, iscan_cur);
+ else
+ dhd_iscan_remove_duplicates(dhdp, iscan_cur);
+
+
+fail:
+ return status;
+}
+
+#endif
+
+/* Function to estimate possible DTIM_SKIP value */
+int dhd_get_dtim_skip(dhd_pub_t *dhd)
+{
+ int bcn_li_dtim;
+ char buf[128];
+ int ret;
+ int dtim_assoc = 0;
+
+ if ((dhd->dtim_skip == 0) || (dhd->dtim_skip == 1))
+ bcn_li_dtim = 3;
+ else
+ bcn_li_dtim = dhd->dtim_skip;
+
+ /* Read DTIM value if associated */
+ memset(buf, 0, sizeof(buf));
+ bcm_mkiovar("dtim_assoc", 0, 0, buf, sizeof(buf));
+ if ((ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf))) < 0) {
+ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
+ bcn_li_dtim = 1;
+ goto exit;
+ }
+ else
+ dtim_assoc = dtoh32(*(int *)buf);
+
+ DHD_ERROR(("%s bcn_li_dtim=%d DTIM=%d Listen=%d\n", \
+ __FUNCTION__, bcn_li_dtim, dtim_assoc, LISTEN_INTERVAL));
+
+ /* if not assocated just eixt */
+ if (dtim_assoc == 0) {
+ goto exit;
+ }
+
+ /* check if sta listen interval fits into AP dtim */
+ if (dtim_assoc > LISTEN_INTERVAL) {
+ /* AP DTIM to big for our Listen Interval : no dtim skiping */
+ bcn_li_dtim = 1;
+ DHD_ERROR(("%s DTIM=%d > Listen=%d : too big ...\n", \
+ __FUNCTION__, dtim_assoc, LISTEN_INTERVAL));
+ goto exit;
+ }
+
+ if ((bcn_li_dtim * dtim_assoc) > LISTEN_INTERVAL) {
+ /* Round up dtim_skip to fit into STAs Listen Interval */
+ bcn_li_dtim = (int)(LISTEN_INTERVAL / dtim_assoc);
+ DHD_TRACE(("%s agjust dtim_skip as %d\n", __FUNCTION__, bcn_li_dtim));
+ }
+
+exit:
+ return bcn_li_dtim;
+}
+
+#ifdef PNO_SUPPORT
+int dhd_pno_clean(dhd_pub_t *dhd)
+{
+ char iovbuf[128];
+ int pfn_enabled = 0;
+ int iov_len = 0;
+ int ret;
+
+ /* Disable pfn */
+ iov_len = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf));
+ if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) >= 0) {
+ /* clear pfn */
+ iov_len = bcm_mkiovar("pfnclear", 0, 0, iovbuf, sizeof(iovbuf));
+ if (iov_len) {
+ if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len)) < 0) {
+ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
+ }
+ }
+ else {
+ ret = -1;
+ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, iov_len));
+ }
+ }
+ else
+ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
+
+ return ret;
+}
+
+int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled)
+{
+ char iovbuf[128];
+ int ret = -1;
+
+ if ((!dhd) && ((pfn_enabled != 0) || (pfn_enabled != 1))) {
+ DHD_ERROR(("%s error exit\n", __FUNCTION__));
+ return ret;
+ }
+
+ /* Enable/disable PNO */
+ if ((ret = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf))) > 0) {
+ if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) {
+ DHD_ERROR(("%s failed for error=%d\n", __FUNCTION__, ret));
+ return ret;
+ }
+ else {
+ dhd->pno_enable = pfn_enabled;
+ DHD_TRACE(("%s set pno as %d\n", __FUNCTION__, dhd->pno_enable));
+ }
+ }
+ else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, ret));
+
+ return ret;
+}
+
+/* Function to execute combined scan */
+int
+dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr)
+{
+ int err = -1;
+ char iovbuf[128];
+ int k, i;
+ wl_pfn_param_t pfn_param;
+ wl_pfn_t pfn_element;
+
+ DHD_TRACE(("%s nssid=%d nchan=%d\n", __FUNCTION__, nssid, scan_fr));
+
+ if ((!dhd) && (!ssids_local)) {
+ DHD_ERROR(("%s error exit\n", __FUNCTION__));
+ err = -1;
+ }
+
+ /* Check for broadcast ssid */
+ for (k = 0; k < nssid; k++) {
+ if (!ssids_local[k].SSID_len) {
+ DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO setting\n", k));
+ return err;
+ }
+ }
+/* #define PNO_DUMP 1 */
+#ifdef PNO_DUMP
+ {
+ int j;
+ for (j = 0; j < nssid; j++) {
+ DHD_ERROR(("%d: scan for %s size =%d\n", j,
+ ssids_local[j].SSID, ssids_local[j].SSID_len));
+ }
+ }
+#endif /* PNO_DUMP */
+
+ /* clean up everything */
+ if ((err = dhd_pno_clean(dhd)) < 0) {
+ DHD_ERROR(("%s failed error=%d\n", __FUNCTION__, err));
+ return err;
+ }
+ memset(&pfn_param, 0, sizeof(pfn_param));
+ memset(&pfn_element, 0, sizeof(pfn_element));
+
+ /* set pfn parameters */
+ pfn_param.version = htod32(PFN_VERSION);
+ pfn_param.flags = htod16((PFN_LIST_ORDER << SORT_CRITERIA_BIT));
+
+ /* set up pno scan fr */
+ if (scan_fr != 0)
+ pfn_param.scan_freq = htod32(scan_fr);
+
+ if (pfn_param.scan_freq > PNO_SCAN_MAX_FW) {
+ DHD_ERROR(("%s pno freq above %d sec\n", __FUNCTION__, PNO_SCAN_MAX_FW));
+ return err;
+ }
+
+ bcm_mkiovar("pfn_set", (char *)&pfn_param, sizeof(pfn_param), iovbuf, sizeof(iovbuf));
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+
+ /* set all pfn ssid */
+ for (i = 0; i < nssid; i++) {
+
+ pfn_element.bss_type = htod32(DOT11_BSSTYPE_INFRASTRUCTURE);
+ pfn_element.auth = (DOT11_OPEN_SYSTEM);
+ pfn_element.wpa_auth = htod32(WPA_AUTH_PFN_ANY);
+ pfn_element.wsec = htod32(0);
+ pfn_element.infra = htod32(1);
+
+ memcpy((char *)pfn_element.ssid.SSID, ssids_local[i].SSID, ssids_local[i].SSID_len);
+ pfn_element.ssid.SSID_len = ssids_local[i].SSID_len;
+
+ if ((err =
+ bcm_mkiovar("pfn_add", (char *)&pfn_element,
+ sizeof(pfn_element), iovbuf, sizeof(iovbuf))) > 0) {
+ if ((err =
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) {
+ DHD_ERROR(("%s failed for i=%d error=%d\n",
+ __FUNCTION__, i, err));
+ return err;
+ }
+ else
+ DHD_ERROR(("%s set OK with PNO time=%d\n", __FUNCTION__, \
+ pfn_param.scan_freq));
+ }
+ else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, err));
+ }
+
+ /* Enable PNO */
+ /* dhd_pno_enable(dhd, 1); */
+ return err;
+}
+
+int dhd_pno_get_status(dhd_pub_t *dhd)
+{
+ int ret = -1;
+
+ if (!dhd)
+ return ret;
+ else
+ return (dhd->pno_enable);
+}
+
+#endif /* PNO_SUPPORT */
+
+#if defined(KEEP_ALIVE)
+int dhd_keep_alive_onoff(dhd_pub_t *dhd, int ka_on)
+{
+ char buf[256];
+ char *buf_ptr = buf;
+ wl_keep_alive_pkt_t keep_alive_pkt;
+ char * str;
+ int str_len, buf_len;
+ int res = 0;
+ int keep_alive_period = KEEP_ALIVE_PERIOD; /* in ms */
+
+ DHD_TRACE(("%s: ka:%d\n", __FUNCTION__, ka_on));
+
+ if (ka_on) { /* on suspend */
+ keep_alive_pkt.period_msec = keep_alive_period;
+
+ } else {
+ /* on resume, turn off keep_alive packets */
+ keep_alive_pkt.period_msec = 0;
+ }
+
+ /* IOC var name */
+ str = "keep_alive";
+ str_len = strlen(str);
+ strncpy(buf, str, str_len);
+ buf[str_len] = '\0';
+ buf_len = str_len + 1;
+
+ /* set ptr to IOCTL payload after the var name */
+ buf_ptr += buf_len; /* include term Z */
+
+ /* copy Keep-alive attributes from local var keep_alive_pkt */
+ str = NULL_PKT_STR;
+ keep_alive_pkt.len_bytes = strlen(str);
+
+ memcpy(buf_ptr, &keep_alive_pkt, WL_KEEP_ALIVE_FIXED_LEN);
+ buf_ptr += WL_KEEP_ALIVE_FIXED_LEN;
+
+ /* copy packet data */
+ memcpy(buf_ptr, str, keep_alive_pkt.len_bytes);
+ buf_len += (WL_KEEP_ALIVE_FIXED_LEN + keep_alive_pkt.len_bytes);
+
+ res = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len);
+ return res;
+}
+#endif /* defined(KEEP_ALIVE) */
+
+#if defined(CSCAN)
+
+/* Androd ComboSCAN support */
+/*
+ * data parsing from ComboScan tlv list
+*/
+int
+wl_iw_parse_data_tlv(char** list_str, void *dst, int dst_size, const char token,
+ int input_size, int *bytes_left)
+{
+ char* str = *list_str;
+ uint16 short_temp;
+ uint32 int_temp;
+
+ if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) {
+ DHD_ERROR(("%s error paramters\n", __FUNCTION__));
+ return -1;
+ }
+
+ /* Clean all dest bytes */
+ memset(dst, 0, dst_size);
+ while (*bytes_left > 0) {
+
+ if (str[0] != token) {
+ DHD_TRACE(("%s NOT Type=%d get=%d left_parse=%d \n",
+ __FUNCTION__, token, str[0], *bytes_left));
+ return -1;
+ }
+
+ *bytes_left -= 1;
+ str += 1;
+
+ if (input_size == 1) {
+ memcpy(dst, str, input_size);
+ }
+ else if (input_size == 2) {
+ memcpy(dst, (char *)htod16(memcpy(&short_temp, str, input_size)),
+ input_size);
+ }
+ else if (input_size == 4) {
+ memcpy(dst, (char *)htod32(memcpy(&int_temp, str, input_size)),
+ input_size);
+ }
+
+ *bytes_left -= input_size;
+ str += input_size;
+ *list_str = str;
+ return 1;
+ }
+ return 1;
+}
+
+/*
+ * channel list parsing from cscan tlv list
+*/
+int
+wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list,
+ int channel_num, int *bytes_left)
+{
+ char* str = *list_str;
+ int idx = 0;
+
+ if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) {
+ DHD_ERROR(("%s error paramters\n", __FUNCTION__));
+ return -1;
+ }
+
+ while (*bytes_left > 0) {
+
+ if (str[0] != CSCAN_TLV_TYPE_CHANNEL_IE) {
+ *list_str = str;
+ DHD_TRACE(("End channel=%d left_parse=%d %d\n", idx, *bytes_left, str[0]));
+ return idx;
+ }
+ /* Get proper CSCAN_TLV_TYPE_CHANNEL_IE */
+ *bytes_left -= 1;
+ str += 1;
+
+ if (str[0] == 0) {
+ /* All channels */
+ channel_list[idx] = 0x0;
+ }
+ else {
+ channel_list[idx] = (uint16)str[0];
+ DHD_TRACE(("%s channel=%d \n", __FUNCTION__, channel_list[idx]));
+ }
+ *bytes_left -= 1;
+ str += 1;
+
+ if (idx++ > 255) {
+ DHD_ERROR(("%s Too many channels \n", __FUNCTION__));
+ return -1;
+ }
+ }
+
+ *list_str = str;
+ return idx;
+}
+
+/*
+ * SSIDs list parsing from cscan tlv list
+ */
+int
+wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, int max, int *bytes_left)
+{
+ char* str = *list_str;
+ int idx = 0;
+
+ if ((list_str == NULL) || (*list_str == NULL) || (*bytes_left < 0)) {
+ DHD_ERROR(("%s error paramters\n", __FUNCTION__));
+ return -1;
+ }
+
+ while (*bytes_left > 0) {
+
+ if (str[0] != CSCAN_TLV_TYPE_SSID_IE) {
+ *list_str = str;
+ DHD_TRACE(("nssid=%d left_parse=%d %d\n", idx, *bytes_left, str[0]));
+ return idx;
+ }
+
+ /* Get proper CSCAN_TLV_TYPE_SSID_IE */
+ *bytes_left -= 1;
+ str += 1;
+
+ if (str[0] == 0) {
+ /* Broadcast SSID */
+ ssid[idx].SSID_len = 0;
+ memset((char*)ssid[idx].SSID, 0x0, DOT11_MAX_SSID_LEN);
+ *bytes_left -= 1;
+ str += 1;
+
+ DHD_TRACE(("BROADCAST SCAN left=%d\n", *bytes_left));
+ }
+ else if (str[0] <= DOT11_MAX_SSID_LEN) {
+ /* Get proper SSID size */
+ ssid[idx].SSID_len = str[0];
+ *bytes_left -= 1;
+ str += 1;
+
+ /* Get SSID */
+ if (ssid[idx].SSID_len > *bytes_left) {
+ DHD_ERROR(("%s out of memory range len=%d but left=%d\n",
+ __FUNCTION__, ssid[idx].SSID_len, *bytes_left));
+ return -1;
+ }
+
+ memcpy((char*)ssid[idx].SSID, str, ssid[idx].SSID_len);
+
+ *bytes_left -= ssid[idx].SSID_len;
+ str += ssid[idx].SSID_len;
+
+ DHD_TRACE(("%s :size=%d left=%d\n",
+ (char*)ssid[idx].SSID, ssid[idx].SSID_len, *bytes_left));
+ }
+ else {
+ DHD_ERROR(("### SSID size more that %d\n", str[0]));
+ return -1;
+ }
+
+ if (idx++ > max) {
+ DHD_ERROR(("%s number of SSIDs more that %d\n", __FUNCTION__, idx));
+ return -1;
+ }
+ }
+
+ *list_str = str;
+ return idx;
+}
+
+/* Parse a comma-separated list from list_str into ssid array, starting
+ * at index idx. Max specifies size of the ssid array. Parses ssids
+ * and returns updated idx; if idx >= max not all fit, the excess have
+ * not been copied. Returns -1 on empty string, or on ssid too long.
+ */
+int
+wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int max)
+{
+ char* str, *ptr;
+
+ if ((list_str == NULL) || (*list_str == NULL))
+ return -1;
+
+ for (str = *list_str; str != NULL; str = ptr) {
+
+ /* check for next TAG */
+ if (!strncmp(str, GET_CHANNEL, strlen(GET_CHANNEL))) {
+ *list_str = str + strlen(GET_CHANNEL);
+ return idx;
+ }
+
+ if ((ptr = strchr(str, ',')) != NULL) {
+ *ptr++ = '\0';
+ }
+
+ if (strlen(str) > DOT11_MAX_SSID_LEN) {
+ DHD_ERROR(("ssid <%s> exceeds %d\n", str, DOT11_MAX_SSID_LEN));
+ return -1;
+ }
+
+ if (strlen(str) == 0)
+ ssid[idx].SSID_len = 0;
+
+ if (idx < max) {
+ strcpy((char*)ssid[idx].SSID, str);
+ ssid[idx].SSID_len = strlen(str);
+ }
+ idx++;
+ }
+ return idx;
+}
+
+/*
+ * Parse channel list from iwpriv CSCAN
+ */
+int
+wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num)
+{
+ int num;
+ int val;
+ char* str;
+ char* endptr = NULL;
+
+ if ((list_str == NULL)||(*list_str == NULL))
+ return -1;
+
+ str = *list_str;
+ num = 0;
+ while (strncmp(str, GET_NPROBE, strlen(GET_NPROBE))) {
+ val = (int)strtoul(str, &endptr, 0);
+ if (endptr == str) {
+ printf("could not parse channel number starting at"
+ " substring \"%s\" in list:\n%s\n",
+ str, *list_str);
+ return -1;
+ }
+ str = endptr + strspn(endptr, " ,");
+
+ if (num == channel_num) {
+ DHD_ERROR(("too many channels (more than %d) in channel list:\n%s\n",
+ channel_num, *list_str));
+ return -1;
+ }
+
+ channel_list[num++] = (uint16)val;
+ }
+ *list_str = str;
+ return num;
+}
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c
new file mode 100644
index 00000000000..8c6ec470b8b
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c
@@ -0,0 +1,179 @@
+/*
+* Customer code to add GPIO control during WLAN start/stop
+* Copyright (C) 1999-2010, Broadcom Corporation
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2 (the "GPL"),
+* available at http://www.broadcom.com/licenses/GPLv2.php, with the
+* following added to such license:
+*
+* As a special exception, the copyright holders of this software give you
+* permission to link this software with independent modules, and to copy and
+* distribute the resulting executable under terms of your choice, provided that
+* you also meet, for each linked independent module, the terms and conditions of
+* the license of that module. An independent module is a module which is not
+* derived from this software. The special exception does not apply to any
+* modifications of the software.
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a license
+* other than the GPL, without Broadcom's express prior written consent.
+*
+* $Id: dhd_custom_gpio.c,v 1.1.4.8.4.1 2010/09/02 23:13:16 Exp $
+*/
+
+
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+#include <bcmutils.h>
+
+#include <dngl_stats.h>
+#include <dhd.h>
+
+#include <wlioctl.h>
+#include <wl_iw.h>
+
+#define WL_ERROR(x) printf x
+#define WL_TRACE(x)
+
+#ifdef CUSTOMER_HW
+extern void bcm_wlan_power_off(int);
+extern void bcm_wlan_power_on(int);
+#endif /* CUSTOMER_HW */
+#ifdef CUSTOMER_HW2
+int wifi_set_carddetect(int on);
+int wifi_set_power(int on, unsigned long msec);
+int wifi_get_irq_number(unsigned long *irq_flags_ptr);
+int wifi_get_mac_addr(unsigned char *buf);
+#endif
+
+#if defined(OOB_INTR_ONLY)
+
+#if defined(BCMLXSDMMC)
+extern int sdioh_mmc_irq(int irq);
+#endif /* (BCMLXSDMMC) */
+
+#ifdef CUSTOMER_HW3
+#include <mach/gpio.h>
+#endif
+
+/* Customer specific Host GPIO defintion */
+static int dhd_oob_gpio_num = -1; /* GG 19 */
+
+module_param(dhd_oob_gpio_num, int, 0644);
+MODULE_PARM_DESC(dhd_oob_gpio_num, "DHD oob gpio number");
+
+int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr)
+{
+ int host_oob_irq = 0;
+
+#ifdef CUSTOMER_HW2
+ host_oob_irq = wifi_get_irq_number(irq_flags_ptr);
+
+#else /* for NOT CUSTOMER_HW2 */
+#if defined(CUSTOM_OOB_GPIO_NUM)
+ if (dhd_oob_gpio_num < 0) {
+ dhd_oob_gpio_num = CUSTOM_OOB_GPIO_NUM;
+ }
+#endif
+
+ if (dhd_oob_gpio_num < 0) {
+ WL_ERROR(("%s: ERROR customer specific Host GPIO is NOT defined \n",
+ __FUNCTION__));
+ return (dhd_oob_gpio_num);
+ }
+
+ WL_ERROR(("%s: customer specific Host GPIO number is (%d)\n",
+ __FUNCTION__, dhd_oob_gpio_num));
+
+#if defined CUSTOMER_HW
+ host_oob_irq = MSM_GPIO_TO_INT(dhd_oob_gpio_num);
+#elif defined CUSTOMER_HW3
+ gpio_request(dhd_oob_gpio_num, "oob irq");
+ host_oob_irq = gpio_to_irq(dhd_oob_gpio_num);
+ gpio_direction_input(dhd_oob_gpio_num);
+#endif /* CUSTOMER_HW */
+#endif /* CUSTOMER_HW2 */
+
+ return (host_oob_irq);
+}
+#endif /* defined(OOB_INTR_ONLY) */
+
+/* Customer function to control hw specific wlan gpios */
+void
+dhd_customer_gpio_wlan_ctrl(int onoff)
+{
+ switch (onoff) {
+ case WLAN_RESET_OFF:
+ WL_TRACE(("%s: call customer specific GPIO to insert WLAN RESET\n",
+ __FUNCTION__));
+#ifdef CUSTOMER_HW
+ bcm_wlan_power_off(2);
+#endif /* CUSTOMER_HW */
+#ifdef CUSTOMER_HW2
+ wifi_set_power(0, 0);
+#endif
+ WL_ERROR(("=========== WLAN placed in RESET ========\n"));
+ break;
+
+ case WLAN_RESET_ON:
+ WL_TRACE(("%s: callc customer specific GPIO to remove WLAN RESET\n",
+ __FUNCTION__));
+#ifdef CUSTOMER_HW
+ bcm_wlan_power_on(2);
+#endif /* CUSTOMER_HW */
+#ifdef CUSTOMER_HW2
+ wifi_set_power(1, 0);
+#endif
+ WL_ERROR(("=========== WLAN going back to live ========\n"));
+ break;
+
+ case WLAN_POWER_OFF:
+ WL_TRACE(("%s: call customer specific GPIO to turn off WL_REG_ON\n",
+ __FUNCTION__));
+#ifdef CUSTOMER_HW
+ bcm_wlan_power_off(1);
+#endif /* CUSTOMER_HW */
+ break;
+
+ case WLAN_POWER_ON:
+ WL_TRACE(("%s: call customer specific GPIO to turn on WL_REG_ON\n",
+ __FUNCTION__));
+#ifdef CUSTOMER_HW
+ bcm_wlan_power_on(1);
+ /* Lets customer power to get stable */
+ OSL_DELAY(50);
+#endif /* CUSTOMER_HW */
+ break;
+ }
+}
+
+#ifdef GET_CUSTOM_MAC_ENABLE
+/* Function to get custom MAC address */
+int
+dhd_custom_get_mac_address(unsigned char *buf)
+{
+ int ret = 0;
+
+ WL_TRACE(("%s Enter\n", __FUNCTION__));
+ if (!buf)
+ return -EINVAL;
+
+ /* Customer access to MAC address stored outside of DHD driver */
+#ifdef CUSTOMER_HW2
+ ret = wifi_get_mac_addr(buf);
+#endif
+
+#ifdef EXAMPLE_GET_MAC
+ /* EXAMPLE code */
+ {
+ struct ether_addr ea_example = {{0x00, 0x11, 0x22, 0x33, 0x44, 0xFF}};
+ bcopy((char *)&ea_example, buf, sizeof(struct ether_addr));
+ }
+#endif /* EXAMPLE_GET_MAC */
+
+ return ret;
+}
+#endif /* GET_CUSTOM_MAC_ENABLE */
diff --git a/drivers/net/wireless/bcm4329/dhd_dbg.h b/drivers/net/wireless/bcm4329/dhd_dbg.h
new file mode 100644
index 00000000000..b48c1d70f14
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_dbg.h
@@ -0,0 +1,100 @@
+/*
+ * Debug/trace/assert driver definitions for Dongle Host Driver.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_dbg.h,v 1.5.6.2.4.2.14.10 2010/05/21 21:49:38 Exp $
+ */
+
+#ifndef _dhd_dbg_
+#define _dhd_dbg_
+
+#ifdef DHD_DEBUG
+
+#define DHD_ERROR(args) do {if ((dhd_msg_level & DHD_ERROR_VAL) && (net_ratelimit())) \
+ printf args;} while (0)
+#define DHD_TRACE(args) do {if (dhd_msg_level & DHD_TRACE_VAL) printf args;} while (0)
+#define DHD_INFO(args) do {if (dhd_msg_level & DHD_INFO_VAL) printf args;} while (0)
+#define DHD_DATA(args) do {if (dhd_msg_level & DHD_DATA_VAL) printf args;} while (0)
+#define DHD_CTL(args) do {if (dhd_msg_level & DHD_CTL_VAL) printf args;} while (0)
+#define DHD_TIMER(args) do {if (dhd_msg_level & DHD_TIMER_VAL) printf args;} while (0)
+#define DHD_HDRS(args) do {if (dhd_msg_level & DHD_HDRS_VAL) printf args;} while (0)
+#define DHD_BYTES(args) do {if (dhd_msg_level & DHD_BYTES_VAL) printf args;} while (0)
+#define DHD_INTR(args) do {if (dhd_msg_level & DHD_INTR_VAL) printf args;} while (0)
+#define DHD_GLOM(args) do {if (dhd_msg_level & DHD_GLOM_VAL) printf args;} while (0)
+#define DHD_EVENT(args) do {if (dhd_msg_level & DHD_EVENT_VAL) printf args;} while (0)
+#define DHD_BTA(args) do {if (dhd_msg_level & DHD_BTA_VAL) printf args;} while (0)
+#define DHD_ISCAN(args) do {if (dhd_msg_level & DHD_ISCAN_VAL) printf args;} while (0)
+
+#define DHD_ERROR_ON() (dhd_msg_level & DHD_ERROR_VAL)
+#define DHD_TRACE_ON() (dhd_msg_level & DHD_TRACE_VAL)
+#define DHD_INFO_ON() (dhd_msg_level & DHD_INFO_VAL)
+#define DHD_DATA_ON() (dhd_msg_level & DHD_DATA_VAL)
+#define DHD_CTL_ON() (dhd_msg_level & DHD_CTL_VAL)
+#define DHD_TIMER_ON() (dhd_msg_level & DHD_TIMER_VAL)
+#define DHD_HDRS_ON() (dhd_msg_level & DHD_HDRS_VAL)
+#define DHD_BYTES_ON() (dhd_msg_level & DHD_BYTES_VAL)
+#define DHD_INTR_ON() (dhd_msg_level & DHD_INTR_VAL)
+#define DHD_GLOM_ON() (dhd_msg_level & DHD_GLOM_VAL)
+#define DHD_EVENT_ON() (dhd_msg_level & DHD_EVENT_VAL)
+#define DHD_BTA_ON() (dhd_msg_level & DHD_BTA_VAL)
+#define DHD_ISCAN_ON() (dhd_msg_level & DHD_ISCAN_VAL)
+
+#else /* DHD_DEBUG */
+
+#define DHD_ERROR(args) do {if (net_ratelimit()) printf args;} while (0)
+#define DHD_TRACE(args)
+#define DHD_INFO(args)
+#define DHD_DATA(args)
+#define DHD_CTL(args)
+#define DHD_TIMER(args)
+#define DHD_HDRS(args)
+#define DHD_BYTES(args)
+#define DHD_INTR(args)
+#define DHD_GLOM(args)
+#define DHD_EVENT(args)
+#define DHD_BTA(args)
+#define DHD_ISCAN(args)
+
+#define DHD_ERROR_ON() 0
+#define DHD_TRACE_ON() 0
+#define DHD_INFO_ON() 0
+#define DHD_DATA_ON() 0
+#define DHD_CTL_ON() 0
+#define DHD_TIMER_ON() 0
+#define DHD_HDRS_ON() 0
+#define DHD_BYTES_ON() 0
+#define DHD_INTR_ON() 0
+#define DHD_GLOM_ON() 0
+#define DHD_EVENT_ON() 0
+#define DHD_BTA_ON() 0
+#define DHD_ISCAN_ON() 0
+#endif /* DHD_DEBUG */
+
+#define DHD_LOG(args)
+
+#define DHD_NONE(args)
+extern int dhd_msg_level;
+
+/* Defines msg bits */
+#include <dhdioctl.h>
+
+#endif /* _dhd_dbg_ */
diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c
new file mode 100644
index 00000000000..c7ef3edd908
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_linux.c
@@ -0,0 +1,3308 @@
+/*
+ * Broadcom Dongle Host Driver (DHD), Linux-specific network interface
+ * Basically selected code segments from usb-cdc.c and usb-rndis.c
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_linux.c,v 1.65.4.9.2.12.2.104.4.35 2010/11/17 03:13:21 Exp $
+ */
+
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+#include <linux/platform_device.h>
+#endif
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <epivers.h>
+#include <bcmutils.h>
+#include <bcmendian.h>
+
+#include <proto/ethernet.h>
+#include <dngl_stats.h>
+#include <dhd.h>
+#include <dhd_bus.h>
+#include <dhd_proto.h>
+#include <dhd_dbg.h>
+#include <wl_iw.h>
+#ifdef CONFIG_HAS_WAKELOCK
+#include <linux/wakelock.h>
+#endif
+#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
+#include <linux/wlan_plat.h>
+
+struct semaphore wifi_control_sem;
+
+struct dhd_bus *g_bus;
+
+static struct wifi_platform_data *wifi_control_data = NULL;
+static struct resource *wifi_irqres = NULL;
+
+int wifi_get_irq_number(unsigned long *irq_flags_ptr)
+{
+ if (wifi_irqres) {
+ *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK;
+ return (int)wifi_irqres->start;
+ }
+#ifdef CUSTOM_OOB_GPIO_NUM
+ return CUSTOM_OOB_GPIO_NUM;
+#else
+ return -1;
+#endif
+}
+
+int wifi_set_carddetect(int on)
+{
+ printk("%s = %d\n", __FUNCTION__, on);
+ if (wifi_control_data && wifi_control_data->set_carddetect) {
+ wifi_control_data->set_carddetect(on);
+ }
+ return 0;
+}
+
+int wifi_set_power(int on, unsigned long msec)
+{
+ printk("%s = %d\n", __FUNCTION__, on);
+ if (wifi_control_data && wifi_control_data->set_power) {
+ wifi_control_data->set_power(on);
+ }
+ if (msec)
+ mdelay(msec);
+ return 0;
+}
+
+int wifi_set_reset(int on, unsigned long msec)
+{
+ DHD_TRACE(("%s = %d\n", __FUNCTION__, on));
+ if (wifi_control_data && wifi_control_data->set_reset) {
+ wifi_control_data->set_reset(on);
+ }
+ if (msec)
+ mdelay(msec);
+ return 0;
+}
+
+int wifi_get_mac_addr(unsigned char *buf)
+{
+ DHD_TRACE(("%s\n", __FUNCTION__));
+ if (!buf)
+ return -EINVAL;
+ if (wifi_control_data && wifi_control_data->get_mac_addr) {
+ return wifi_control_data->get_mac_addr(buf);
+ }
+ return -EOPNOTSUPP;
+}
+
+static int wifi_probe(struct platform_device *pdev)
+{
+ struct wifi_platform_data *wifi_ctrl =
+ (struct wifi_platform_data *)(pdev->dev.platform_data);
+
+ DHD_TRACE(("## %s\n", __FUNCTION__));
+ wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcm4329_wlan_irq");
+ wifi_control_data = wifi_ctrl;
+
+ wifi_set_power(1, 0); /* Power On */
+ wifi_set_carddetect(1); /* CardDetect (0->1) */
+
+ up(&wifi_control_sem);
+ return 0;
+}
+
+static int wifi_remove(struct platform_device *pdev)
+{
+ struct wifi_platform_data *wifi_ctrl =
+ (struct wifi_platform_data *)(pdev->dev.platform_data);
+
+ DHD_TRACE(("## %s\n", __FUNCTION__));
+ wifi_control_data = wifi_ctrl;
+
+ wifi_set_power(0, 0); /* Power Off */
+ wifi_set_carddetect(0); /* CardDetect (1->0) */
+
+ up(&wifi_control_sem);
+ return 0;
+}
+static int wifi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ DHD_TRACE(("##> %s\n", __FUNCTION__));
+ return 0;
+}
+static int wifi_resume(struct platform_device *pdev)
+{
+ DHD_TRACE(("##> %s\n", __FUNCTION__));
+ return 0;
+}
+
+static struct platform_driver wifi_device = {
+ .probe = wifi_probe,
+ .remove = wifi_remove,
+ .suspend = wifi_suspend,
+ .resume = wifi_resume,
+ .driver = {
+ .name = "bcm4329_wlan",
+ }
+};
+
+int wifi_add_dev(void)
+{
+ DHD_TRACE(("## Calling platform_driver_register\n"));
+ return platform_driver_register(&wifi_device);
+}
+
+void wifi_del_dev(void)
+{
+ DHD_TRACE(("## Unregister platform_driver_register\n"));
+ platform_driver_unregister(&wifi_device);
+}
+#endif /* defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+#include <linux/suspend.h>
+volatile bool dhd_mmc_suspend = FALSE;
+DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
+
+#if defined(OOB_INTR_ONLY)
+extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable);
+#endif /* defined(OOB_INTR_ONLY) */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+MODULE_LICENSE("GPL v2");
+#endif /* LinuxVer */
+
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15)
+const char *
+print_tainted()
+{
+ return "";
+}
+#endif /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */
+
+/* Linux wireless extension support */
+#if defined(CONFIG_WIRELESS_EXT)
+#include <wl_iw.h>
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
+
+#ifdef PKT_FILTER_SUPPORT
+extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
+extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
+#endif
+
+/* Interface control information */
+typedef struct dhd_if {
+ struct dhd_info *info; /* back pointer to dhd_info */
+ /* OS/stack specifics */
+ struct net_device *net;
+ struct net_device_stats stats;
+ int idx; /* iface idx in dongle */
+ int state; /* interface state */
+ uint subunit; /* subunit */
+ uint8 mac_addr[ETHER_ADDR_LEN]; /* assigned MAC address */
+ bool attached; /* Delayed attachment when unset */
+ bool txflowcontrol; /* Per interface flow control indicator */
+ char name[IFNAMSIZ+1]; /* linux interface name */
+} dhd_if_t;
+
+/* Local private structure (extension of pub) */
+typedef struct dhd_info {
+#if defined(CONFIG_WIRELESS_EXT)
+ wl_iw_t iw; /* wireless extensions state (must be first) */
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+ dhd_pub_t pub;
+
+ /* OS/stack specifics */
+ dhd_if_t *iflist[DHD_MAX_IFS];
+
+ struct semaphore proto_sem;
+ wait_queue_head_t ioctl_resp_wait;
+ struct timer_list timer;
+ bool wd_timer_valid;
+ struct tasklet_struct tasklet;
+ spinlock_t sdlock;
+ spinlock_t txqlock;
+ spinlock_t dhd_lock;
+
+ /* Thread based operation */
+ bool threads_only;
+ struct semaphore sdsem;
+ long watchdog_pid;
+ struct semaphore watchdog_sem;
+ struct completion watchdog_exited;
+ long dpc_pid;
+ struct semaphore dpc_sem;
+ struct completion dpc_exited;
+
+ /* Wakelocks */
+#ifdef CONFIG_HAS_WAKELOCK
+ struct wake_lock wl_wifi; /* Wifi wakelock */
+ struct wake_lock wl_rxwake; /* Wifi rx wakelock */
+#endif
+ spinlock_t wl_lock;
+ int wl_count;
+ int wl_packet;
+
+ int hang_was_sent; /* flag that message was send at least once */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+ struct mutex wl_start_lock; /* mutex when START called to prevent any other Linux calls */
+#endif
+ /* Thread to issue ioctl for multicast */
+ long sysioc_pid;
+ struct semaphore sysioc_sem;
+ struct completion sysioc_exited;
+ bool set_multicast;
+ bool set_macaddress;
+ struct ether_addr macvalue;
+ wait_queue_head_t ctrl_wait;
+ atomic_t pend_8021x_cnt;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+} dhd_info_t;
+
+/* Definitions to provide path to the firmware and nvram
+ * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt"
+ */
+char firmware_path[MOD_PARAM_PATHLEN];
+char nvram_path[MOD_PARAM_PATHLEN];
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+struct semaphore dhd_registration_sem;
+#define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
+/* load firmware and/or nvram values from the filesystem */
+module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0);
+module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0);
+
+/* Error bits */
+module_param(dhd_msg_level, int, 0);
+
+/* Spawn a thread for system ioctls (set mac, set mcast) */
+uint dhd_sysioc = TRUE;
+module_param(dhd_sysioc, uint, 0);
+
+/* Watchdog interval */
+uint dhd_watchdog_ms = 10;
+module_param(dhd_watchdog_ms, uint, 0);
+
+#ifdef DHD_DEBUG
+/* Console poll interval */
+uint dhd_console_ms = 0;
+module_param(dhd_console_ms, uint, 0);
+#endif /* DHD_DEBUG */
+
+/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */
+uint dhd_arp_mode = 0xb;
+module_param(dhd_arp_mode, uint, 0);
+
+/* ARP offload enable */
+uint dhd_arp_enable = TRUE;
+module_param(dhd_arp_enable, uint, 0);
+
+/* Global Pkt filter enable control */
+uint dhd_pkt_filter_enable = TRUE;
+module_param(dhd_pkt_filter_enable, uint, 0);
+
+/* Pkt filter init setup */
+uint dhd_pkt_filter_init = 0;
+module_param(dhd_pkt_filter_init, uint, 0);
+
+/* Pkt filter mode control */
+uint dhd_master_mode = TRUE;
+module_param(dhd_master_mode, uint, 1);
+
+/* Watchdog thread priority, -1 to use kernel timer */
+int dhd_watchdog_prio = 97;
+module_param(dhd_watchdog_prio, int, 0);
+
+/* DPC thread priority, -1 to use tasklet */
+int dhd_dpc_prio = 98;
+module_param(dhd_dpc_prio, int, 0);
+
+/* DPC thread priority, -1 to use tasklet */
+extern int dhd_dongle_memsize;
+module_param(dhd_dongle_memsize, int, 0);
+
+/* Control fw roaming */
+#ifdef CUSTOMER_HW2
+uint dhd_roam = 0;
+#else
+uint dhd_roam = 1;
+#endif
+
+/* Control radio state */
+uint dhd_radio_up = 1;
+
+/* Network inteface name */
+char iface_name[IFNAMSIZ];
+module_param_string(iface_name, iface_name, IFNAMSIZ, 0);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define DAEMONIZE(a) daemonize(a); \
+ allow_signal(SIGKILL); \
+ allow_signal(SIGTERM);
+#else /* Linux 2.4 (w/o preemption patch) */
+#define RAISE_RX_SOFTIRQ() \
+ cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
+#define DAEMONIZE(a) daemonize(); \
+ do { if (a) \
+ strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \
+ } while (0);
+#endif /* LINUX_VERSION_CODE */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define BLOCKABLE() (!in_atomic())
+#else
+#define BLOCKABLE() (!in_interrupt())
+#endif
+
+/* The following are specific to the SDIO dongle */
+
+/* IOCTL response timeout */
+int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT;
+
+/* Idle timeout for backplane clock */
+int dhd_idletime = DHD_IDLETIME_TICKS;
+module_param(dhd_idletime, int, 0);
+
+/* Use polling */
+uint dhd_poll = FALSE;
+module_param(dhd_poll, uint, 0);
+
+/* Use interrupts */
+uint dhd_intr = TRUE;
+module_param(dhd_intr, uint, 0);
+
+/* SDIO Drive Strength (in milliamps) */
+uint dhd_sdiod_drive_strength = 6;
+module_param(dhd_sdiod_drive_strength, uint, 0);
+
+/* Tx/Rx bounds */
+extern uint dhd_txbound;
+extern uint dhd_rxbound;
+module_param(dhd_txbound, uint, 0);
+module_param(dhd_rxbound, uint, 0);
+
+/* Deferred transmits */
+extern uint dhd_deferred_tx;
+module_param(dhd_deferred_tx, uint, 0);
+
+
+
+#ifdef SDTEST
+/* Echo packet generator (pkts/s) */
+uint dhd_pktgen = 0;
+module_param(dhd_pktgen, uint, 0);
+
+/* Echo packet len (0 => sawtooth, max 2040) */
+uint dhd_pktgen_len = 0;
+module_param(dhd_pktgen_len, uint, 0);
+#endif
+
+/* Version string to report */
+#ifdef DHD_DEBUG
+#ifndef SRCBASE
+#define SRCBASE "drivers/net/wireless/bcm4329"
+#endif
+#define DHD_COMPILED "\nCompiled in " SRCBASE
+#else
+#define DHD_COMPILED
+#endif
+
+static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR
+#ifdef DHD_DEBUG
+"\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__
+#endif
+;
+
+
+#if defined(CONFIG_WIRELESS_EXT)
+struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+static void dhd_dpc(ulong data);
+/* forward decl */
+extern int dhd_wait_pend8021x(struct net_device *dev);
+
+#ifdef TOE
+#ifndef BDC
+#error TOE requires BDC
+#endif /* !BDC */
+static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol);
+static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol);
+#endif /* TOE */
+
+static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
+ wl_event_msg_t *event_ptr, void **data_ptr);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored)
+{
+ int ret = NOTIFY_DONE;
+
+ switch (action) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ dhd_mmc_suspend = TRUE;
+ ret = NOTIFY_OK;
+ break;
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ dhd_mmc_suspend = FALSE;
+ ret = NOTIFY_OK;
+ break;
+ }
+ smp_mb();
+ return ret;
+}
+
+static struct notifier_block dhd_sleep_pm_notifier = {
+ .notifier_call = dhd_sleep_pm_callback,
+ .priority = 0
+};
+extern int register_pm_notifier(struct notifier_block *nb);
+extern int unregister_pm_notifier(struct notifier_block *nb);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
+
+static void dhd_set_packet_filter(int value, dhd_pub_t *dhd)
+{
+#ifdef PKT_FILTER_SUPPORT
+ DHD_TRACE(("%s: %d\n", __FUNCTION__, value));
+ /* 1 - Enable packet filter, only allow unicast packet to send up */
+ /* 0 - Disable packet filter */
+ if (dhd_pkt_filter_enable) {
+ int i;
+
+ for (i = 0; i < dhd->pktfilter_count; i++) {
+ dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
+ dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
+ value, dhd_master_mode);
+ }
+ }
+#endif
+}
+
+
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+static int dhd_set_suspend(int value, dhd_pub_t *dhd)
+{
+ int power_mode = PM_MAX;
+ /* wl_pkt_filter_enable_t enable_parm; */
+ char iovbuf[32];
+ int bcn_li_dtim = 3;
+#ifdef CUSTOMER_HW2
+ uint roamvar = 1;
+#endif /* CUSTOMER_HW2 */
+
+ DHD_TRACE(("%s: enter, value = %d in_suspend = %d\n",
+ __FUNCTION__, value, dhd->in_suspend));
+
+ if (dhd && dhd->up) {
+ if (value && dhd->in_suspend) {
+
+ /* Kernel suspended */
+ DHD_TRACE(("%s: force extra Suspend setting \n", __FUNCTION__));
+
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM,
+ (char *)&power_mode, sizeof(power_mode));
+
+ /* Enable packet filter, only allow unicast packet to send up */
+ dhd_set_packet_filter(1, dhd);
+
+ /* if dtim skip setup as default force it to wake each thrid dtim
+ * for better power saving.
+ * Note that side effect is chance to miss BC/MC packet
+ */
+ bcn_li_dtim = dhd_get_dtim_skip(dhd);
+ bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
+ 4, iovbuf, sizeof(iovbuf));
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+#ifdef CUSTOMER_HW2
+ /* Disable build-in roaming during suspend */
+ bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf));
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+#endif /* CUSTOMER_HW2 */
+
+ } else {
+
+ /* Kernel resumed */
+ DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__));
+
+ power_mode = PM_FAST;
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode,
+ sizeof(power_mode));
+
+ /* disable pkt filter */
+ dhd_set_packet_filter(0, dhd);
+
+ /* restore pre-suspend setting for dtim_skip */
+ bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip,
+ 4, iovbuf, sizeof(iovbuf));
+
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+#ifdef CUSTOMER_HW2
+ roamvar = dhd_roam;
+ bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf));
+ dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+#endif /* CUSTOMER_HW2 */
+ }
+ }
+
+ return 0;
+}
+
+static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val)
+{
+ dhd_pub_t *dhdp = &dhd->pub;
+
+ dhd_os_wake_lock(dhdp);
+ dhd_os_proto_block(dhdp);
+ /* Set flag when early suspend was called */
+ dhdp->in_suspend = val;
+ if (!dhdp->suspend_disable_flag)
+ dhd_set_suspend(val, dhdp);
+ dhd_os_proto_unblock(dhdp);
+ dhd_os_wake_unlock(dhdp);
+}
+
+static void dhd_early_suspend(struct early_suspend *h)
+{
+ struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
+
+ DHD_TRACE(("%s: enter\n", __FUNCTION__));
+
+ if (dhd)
+ dhd_suspend_resume_helper(dhd, 1);
+}
+
+static void dhd_late_resume(struct early_suspend *h)
+{
+ struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
+
+ DHD_TRACE(("%s: enter\n", __FUNCTION__));
+
+ if (dhd)
+ dhd_suspend_resume_helper(dhd, 0);
+}
+#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
+
+/*
+ * Generalized timeout mechanism. Uses spin sleep with exponential back-off until
+ * the sleep time reaches one jiffy, then switches over to task delay. Usage:
+ *
+ * dhd_timeout_start(&tmo, usec);
+ * while (!dhd_timeout_expired(&tmo))
+ * if (poll_something())
+ * break;
+ * if (dhd_timeout_expired(&tmo))
+ * fatal();
+ */
+
+void
+dhd_timeout_start(dhd_timeout_t *tmo, uint usec)
+{
+ tmo->limit = usec;
+ tmo->increment = 0;
+ tmo->elapsed = 0;
+ tmo->tick = 1000000 / HZ;
+}
+
+int
+dhd_timeout_expired(dhd_timeout_t *tmo)
+{
+ /* Does nothing the first call */
+ if (tmo->increment == 0) {
+ tmo->increment = 1;
+ return 0;
+ }
+
+ if (tmo->elapsed >= tmo->limit)
+ return 1;
+
+ /* Add the delay that's about to take place */
+ tmo->elapsed += tmo->increment;
+
+ if (tmo->increment < tmo->tick) {
+ OSL_DELAY(tmo->increment);
+ tmo->increment *= 2;
+ if (tmo->increment > tmo->tick)
+ tmo->increment = tmo->tick;
+ } else {
+ wait_queue_head_t delay_wait;
+ DECLARE_WAITQUEUE(wait, current);
+ int pending;
+ init_waitqueue_head(&delay_wait);
+ add_wait_queue(&delay_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ pending = signal_pending(current);
+ remove_wait_queue(&delay_wait, &wait);
+ set_current_state(TASK_RUNNING);
+ if (pending)
+ return 1; /* Interrupted */
+ }
+
+ return 0;
+}
+
+static int
+dhd_net2idx(dhd_info_t *dhd, struct net_device *net)
+{
+ int i = 0;
+
+ ASSERT(dhd);
+ while (i < DHD_MAX_IFS) {
+ if (dhd->iflist[i] && (dhd->iflist[i]->net == net))
+ return i;
+ i++;
+ }
+
+ return DHD_BAD_IF;
+}
+
+int
+dhd_ifname2idx(dhd_info_t *dhd, char *name)
+{
+ int i = DHD_MAX_IFS;
+
+ ASSERT(dhd);
+
+ if (name == NULL || *name == '\0')
+ return 0;
+
+ while (--i > 0)
+ if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ))
+ break;
+
+ DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name));
+
+ return i; /* default - the primary interface */
+}
+
+char *
+dhd_ifname(dhd_pub_t *dhdp, int ifidx)
+{
+ dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
+
+ ASSERT(dhd);
+
+ if (ifidx < 0 || ifidx >= DHD_MAX_IFS) {
+ DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx));
+ return "<if_bad>";
+ }
+
+ if (dhd->iflist[ifidx] == NULL) {
+ DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx));
+ return "<if_null>";
+ }
+
+ if (dhd->iflist[ifidx]->net)
+ return dhd->iflist[ifidx]->net->name;
+
+ return "<if_none>";
+}
+
+static void
+_dhd_set_multicast_list(dhd_info_t *dhd, int ifidx)
+{
+ struct net_device *dev;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+ struct netdev_hw_addr *ha;
+#else
+ struct dev_mc_list *mclist;
+#endif
+ uint32 allmulti, cnt;
+
+ wl_ioctl_t ioc;
+ char *buf, *bufp;
+ uint buflen;
+ int ret;
+
+ ASSERT(dhd && dhd->iflist[ifidx]);
+ dev = dhd->iflist[ifidx]->net;
+
+ netif_addr_lock_bh(dev);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+ cnt = netdev_mc_count(dev);
+#else
+ cnt = dev->mc_count;
+#endif
+ netif_addr_unlock_bh(dev);
+
+ /* Determine initial value of allmulti flag */
+ allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE;
+
+ /* Send down the multicast list first. */
+ buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN);
+ if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) {
+ DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n",
+ dhd_ifname(&dhd->pub, ifidx), cnt));
+ return;
+ }
+
+ strcpy(bufp, "mcast_list");
+ bufp += strlen("mcast_list") + 1;
+
+ cnt = htol32(cnt);
+ memcpy(bufp, &cnt, sizeof(cnt));
+ bufp += sizeof(cnt);
+
+ netif_addr_lock_bh(dev);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+ netdev_for_each_mc_addr(ha, dev) {
+ if (!cnt)
+ break;
+ memcpy(bufp, ha->addr, ETHER_ADDR_LEN);
+ bufp += ETHER_ADDR_LEN;
+ cnt--;
+ }
+#else
+ for (mclist = dev->mc_list;(mclist && (cnt > 0)); cnt--, mclist = mclist->next) {
+ memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN);
+ bufp += ETHER_ADDR_LEN;
+ }
+#endif
+ netif_addr_unlock_bh(dev);
+
+ memset(&ioc, 0, sizeof(ioc));
+ ioc.cmd = WLC_SET_VAR;
+ ioc.buf = buf;
+ ioc.len = buflen;
+ ioc.set = TRUE;
+
+ ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
+ if (ret < 0) {
+ DHD_ERROR(("%s: set mcast_list failed, cnt %d\n",
+ dhd_ifname(&dhd->pub, ifidx), cnt));
+ allmulti = cnt ? TRUE : allmulti;
+ }
+
+ MFREE(dhd->pub.osh, buf, buflen);
+
+ /* Now send the allmulti setting. This is based on the setting in the
+ * net_device flags, but might be modified above to be turned on if we
+ * were trying to set some addresses and dongle rejected it...
+ */
+
+ buflen = sizeof("allmulti") + sizeof(allmulti);
+ if (!(buf = MALLOC(dhd->pub.osh, buflen))) {
+ DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx)));
+ return;
+ }
+ allmulti = htol32(allmulti);
+
+ if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) {
+ DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n",
+ dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen));
+ MFREE(dhd->pub.osh, buf, buflen);
+ return;
+ }
+
+
+ memset(&ioc, 0, sizeof(ioc));
+ ioc.cmd = WLC_SET_VAR;
+ ioc.buf = buf;
+ ioc.len = buflen;
+ ioc.set = TRUE;
+
+ ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
+ if (ret < 0) {
+ DHD_ERROR(("%s: set allmulti %d failed\n",
+ dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
+ }
+
+ MFREE(dhd->pub.osh, buf, buflen);
+
+ /* Finally, pick up the PROMISC flag as well, like the NIC driver does */
+
+ allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE;
+ allmulti = htol32(allmulti);
+
+ memset(&ioc, 0, sizeof(ioc));
+ ioc.cmd = WLC_SET_PROMISC;
+ ioc.buf = &allmulti;
+ ioc.len = sizeof(allmulti);
+ ioc.set = TRUE;
+
+ ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
+ if (ret < 0) {
+ DHD_ERROR(("%s: set promisc %d failed\n",
+ dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
+ }
+}
+
+static int
+_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr)
+{
+ char buf[32];
+ wl_ioctl_t ioc;
+ int ret;
+
+ DHD_TRACE(("%s enter\n", __FUNCTION__));
+ if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) {
+ DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx)));
+ return -1;
+ }
+ memset(&ioc, 0, sizeof(ioc));
+ ioc.cmd = WLC_SET_VAR;
+ ioc.buf = buf;
+ ioc.len = 32;
+ ioc.set = TRUE;
+
+ ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
+ if (ret < 0) {
+ DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx)));
+ } else {
+ memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN);
+ }
+
+ return ret;
+}
+
+#ifdef SOFTAP
+extern struct net_device *ap_net_dev;
+/* semaphore that the soft AP CODE waits on */
+extern struct semaphore ap_eth_sema;
+#endif
+
+static void
+dhd_op_if(dhd_if_t *ifp)
+{
+ dhd_info_t *dhd;
+ int ret = 0, err = 0;
+#ifdef SOFTAP
+ unsigned long flags;
+#endif
+
+ ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */
+
+ dhd = ifp->info;
+
+ DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state));
+
+ switch (ifp->state) {
+ case WLC_E_IF_ADD:
+ /*
+ * Delete the existing interface before overwriting it
+ * in case we missed the WLC_E_IF_DEL event.
+ */
+ if (ifp->net != NULL) {
+ DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n",
+ __FUNCTION__, ifp->net->name));
+ netif_stop_queue(ifp->net);
+ unregister_netdev(ifp->net);
+ free_netdev(ifp->net);
+ }
+ /* Allocate etherdev, including space for private structure */
+ if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) {
+ DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
+ ret = -ENOMEM;
+ }
+ if (ret == 0) {
+ strcpy(ifp->net->name, ifp->name);
+ memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd));
+ if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) {
+ DHD_ERROR(("%s: dhd_net_attach failed, err %d\n",
+ __FUNCTION__, err));
+ ret = -EOPNOTSUPP;
+ } else {
+#ifdef SOFTAP
+ flags = dhd_os_spin_lock(&dhd->pub);
+ /* save ptr to wl0.1 netdev for use in wl_iw.c */
+ ap_net_dev = ifp->net;
+ /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */
+ up(&ap_eth_sema);
+ dhd_os_spin_unlock(&dhd->pub, flags);
+#endif
+ DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n",
+ current->pid, ifp->net->name));
+ ifp->state = 0;
+ }
+ }
+ break;
+ case WLC_E_IF_DEL:
+ if (ifp->net != NULL) {
+ DHD_TRACE(("\n%s: got 'WLC_E_IF_DEL' state\n", __FUNCTION__));
+ netif_stop_queue(ifp->net);
+ unregister_netdev(ifp->net);
+ ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */
+ }
+ break;
+ default:
+ DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state));
+ ASSERT(!ifp->state);
+ break;
+ }
+
+ if (ret < 0) {
+ if (ifp->net) {
+ free_netdev(ifp->net);
+ }
+ dhd->iflist[ifp->idx] = NULL;
+ MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
+#ifdef SOFTAP
+ flags = dhd_os_spin_lock(&dhd->pub);
+ if (ifp->net == ap_net_dev)
+ ap_net_dev = NULL; /* NULL SOFTAP global as well */
+ dhd_os_spin_unlock(&dhd->pub, flags);
+#endif /* SOFTAP */
+ }
+}
+
+static int
+_dhd_sysioc_thread(void *data)
+{
+ dhd_info_t *dhd = (dhd_info_t *)data;
+ int i;
+#ifdef SOFTAP
+ bool in_ap = FALSE;
+ unsigned long flags;
+#endif
+
+ DAEMONIZE("dhd_sysioc");
+
+ while (down_interruptible(&dhd->sysioc_sem) == 0) {
+ dhd_os_start_lock(&dhd->pub);
+ dhd_os_wake_lock(&dhd->pub);
+ for (i = 0; i < DHD_MAX_IFS; i++) {
+ if (dhd->iflist[i]) {
+ DHD_TRACE(("%s: interface %d\n",__FUNCTION__, i));
+#ifdef SOFTAP
+ flags = dhd_os_spin_lock(&dhd->pub);
+ in_ap = (ap_net_dev != NULL);
+ dhd_os_spin_unlock(&dhd->pub, flags);
+#endif /* SOFTAP */
+ if (dhd->iflist[i]->state)
+ dhd_op_if(dhd->iflist[i]);
+#ifdef SOFTAP
+ if (dhd->iflist[i] == NULL) {
+ DHD_TRACE(("%s: interface %d just been removed!\n\n", __FUNCTION__, i));
+ continue;
+ }
+
+ if (in_ap && dhd->set_macaddress) {
+ DHD_TRACE(("attempt to set MAC for %s in AP Mode blocked.\n", dhd->iflist[i]->net->name));
+ dhd->set_macaddress = FALSE;
+ continue;
+ }
+
+ if (in_ap && dhd->set_multicast) {
+ DHD_TRACE(("attempt to set MULTICAST list for %s in AP Mode blocked.\n", dhd->iflist[i]->net->name));
+ dhd->set_multicast = FALSE;
+ continue;
+ }
+#endif /* SOFTAP */
+ if (dhd->set_multicast) {
+ dhd->set_multicast = FALSE;
+ _dhd_set_multicast_list(dhd, i);
+ }
+ if (dhd->set_macaddress) {
+ dhd->set_macaddress = FALSE;
+ _dhd_set_mac_address(dhd, i, &dhd->macvalue);
+ }
+ }
+ }
+ dhd_os_wake_unlock(&dhd->pub);
+ dhd_os_start_unlock(&dhd->pub);
+ }
+ DHD_TRACE(("%s: stopped\n",__FUNCTION__));
+ complete_and_exit(&dhd->sysioc_exited, 0);
+}
+
+static int
+dhd_set_mac_address(struct net_device *dev, void *addr)
+{
+ int ret = 0;
+
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+ struct sockaddr *sa = (struct sockaddr *)addr;
+ int ifidx;
+
+ DHD_TRACE(("%s: Enter\n",__FUNCTION__));
+ ifidx = dhd_net2idx(dhd, dev);
+ if (ifidx == DHD_BAD_IF)
+ return -1;
+
+ ASSERT(dhd->sysioc_pid >= 0);
+ memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN);
+ dhd->set_macaddress = TRUE;
+ up(&dhd->sysioc_sem);
+
+ return ret;
+}
+
+static void
+dhd_set_multicast_list(struct net_device *dev)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+ int ifidx;
+
+ DHD_TRACE(("%s: Enter\n",__FUNCTION__));
+ ifidx = dhd_net2idx(dhd, dev);
+ if (ifidx == DHD_BAD_IF)
+ return;
+
+ ASSERT(dhd->sysioc_pid >= 0);
+ dhd->set_multicast = TRUE;
+ up(&dhd->sysioc_sem);
+}
+
+int
+dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf)
+{
+ int ret;
+ dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
+
+ /* Reject if down */
+ if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
+ return -ENODEV;
+ }
+
+ /* Update multicast statistic */
+ if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_ADDR_LEN) {
+ uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf);
+ struct ether_header *eh = (struct ether_header *)pktdata;
+
+ if (ETHER_ISMULTI(eh->ether_dhost))
+ dhdp->tx_multicast++;
+ if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X)
+ atomic_inc(&dhd->pend_8021x_cnt);
+ }
+
+ /* Look into the packet and update the packet priority */
+ if ((PKTPRIO(pktbuf) == 0))
+ pktsetprio(pktbuf, FALSE);
+
+ /* If the protocol uses a data header, apply it */
+ dhd_prot_hdrpush(dhdp, ifidx, pktbuf);
+
+ /* Use bus module to send data frame */
+#ifdef BCMDBUS
+ ret = dbus_send_pkt(dhdp->dbus, pktbuf, NULL /* pktinfo */);
+#else
+ ret = dhd_bus_txdata(dhdp->bus, pktbuf);
+#endif /* BCMDBUS */
+
+ return ret;
+}
+
+static int
+dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+ int ret;
+ void *pktbuf;
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+ int ifidx;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ dhd_os_wake_lock(&dhd->pub);
+
+ /* Reject if down */
+ if (!dhd->pub.up || (dhd->pub.busstate == DHD_BUS_DOWN)) {
+ DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d\n",
+ __FUNCTION__, dhd->pub.up, dhd->pub.busstate));
+ netif_stop_queue(net);
+ /* Send Event when bus down detected during data session */
+ if (dhd->pub.busstate == DHD_BUS_DOWN) {
+ DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__));
+ net_os_send_hang_message(net);
+ }
+ dhd_os_wake_unlock(&dhd->pub);
+ return -ENODEV;
+ }
+
+ ifidx = dhd_net2idx(dhd, net);
+ if (ifidx == DHD_BAD_IF) {
+ DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx));
+ netif_stop_queue(net);
+ dhd_os_wake_unlock(&dhd->pub);
+ return -ENODEV;
+ }
+
+ /* Make sure there's enough room for any header */
+ if (skb_headroom(skb) < dhd->pub.hdrlen) {
+ struct sk_buff *skb2;
+
+ DHD_INFO(("%s: insufficient headroom\n",
+ dhd_ifname(&dhd->pub, ifidx)));
+ dhd->pub.tx_realloc++;
+ skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen);
+ dev_kfree_skb(skb);
+ if ((skb = skb2) == NULL) {
+ DHD_ERROR(("%s: skb_realloc_headroom failed\n",
+ dhd_ifname(&dhd->pub, ifidx)));
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+
+ /* Convert to packet */
+ if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) {
+ DHD_ERROR(("%s: PKTFRMNATIVE failed\n",
+ dhd_ifname(&dhd->pub, ifidx)));
+ dev_kfree_skb_any(skb);
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf);
+
+done:
+ if (ret)
+ dhd->pub.dstats.tx_dropped++;
+ else
+ dhd->pub.tx_packets++;
+
+ dhd_os_wake_unlock(&dhd->pub);
+
+ /* Return ok: we always eat the packet */
+ return 0;
+}
+
+void
+dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state)
+{
+ struct net_device *net;
+ dhd_info_t *dhd = dhdp->info;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ dhdp->txoff = state;
+ ASSERT(dhd && dhd->iflist[ifidx]);
+ net = dhd->iflist[ifidx]->net;
+ if (state == ON)
+ netif_stop_queue(net);
+ else
+ netif_wake_queue(net);
+}
+
+void
+dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt)
+{
+ dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
+ struct sk_buff *skb;
+ uchar *eth;
+ uint len;
+ void * data, *pnext, *save_pktbuf;
+ int i;
+ dhd_if_t *ifp;
+ wl_event_msg_t event;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ save_pktbuf = pktbuf;
+
+ for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) {
+
+ pnext = PKTNEXT(dhdp->osh, pktbuf);
+ PKTSETNEXT(wl->sh.osh, pktbuf, NULL);
+
+
+ skb = PKTTONATIVE(dhdp->osh, pktbuf);
+
+ /* Get the protocol, maintain skb around eth_type_trans()
+ * The main reason for this hack is for the limitation of
+ * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len'
+ * to perform skb_pull inside vs ETH_HLEN. Since to avoid
+ * coping of the packet coming from the network stack to add
+ * BDC, Hardware header etc, during network interface registration
+ * we set the 'net->hard_header_len' to ETH_HLEN + extra space required
+ * for BDC, Hardware header etc. and not just the ETH_HLEN
+ */
+ eth = skb->data;
+ len = skb->len;
+
+ ifp = dhd->iflist[ifidx];
+ if (ifp == NULL)
+ ifp = dhd->iflist[0];
+
+ ASSERT(ifp);
+ skb->dev = ifp->net;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+
+ if (skb->pkt_type == PACKET_MULTICAST) {
+ dhd->pub.rx_multicast++;
+ }
+
+ skb->data = eth;
+ skb->len = len;
+
+ /* Strip header, count, deliver upward */
+ skb_pull(skb, ETH_HLEN);
+
+ /* Process special event packets and then discard them */
+ if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM)
+ dhd_wl_host_event(dhd, &ifidx,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ skb->mac_header,
+#else
+ skb->mac.raw,
+#endif
+ &event,
+ &data);
+
+ ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]);
+ if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state)
+ ifp = dhd->iflist[ifidx];
+
+ if (ifp->net)
+ ifp->net->last_rx = jiffies;
+
+ dhdp->dstats.rx_bytes += skb->len;
+ dhdp->rx_packets++; /* Local count */
+
+ if (in_interrupt()) {
+ netif_rx(skb);
+ } else {
+ /* If the receive is not processed inside an ISR,
+ * the softirqd must be woken explicitly to service
+ * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled
+ * by netif_rx_ni(), but in earlier kernels, we need
+ * to do it manually.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ netif_rx_ni(skb);
+#else
+ ulong flags;
+ netif_rx(skb);
+ local_irq_save(flags);
+ RAISE_RX_SOFTIRQ();
+ local_irq_restore(flags);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */
+ }
+ }
+ dhd_os_wake_lock_timeout_enable(dhdp);
+}
+
+void
+dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx)
+{
+ /* Linux version has nothing to do */
+ return;
+}
+
+void
+dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success)
+{
+ uint ifidx;
+ dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
+ struct ether_header *eh;
+ uint16 type;
+
+ dhd_prot_hdrpull(dhdp, &ifidx, txp);
+
+ eh = (struct ether_header *)PKTDATA(dhdp->osh, txp);
+ type = ntoh16(eh->ether_type);
+
+ if (type == ETHER_TYPE_802_1X)
+ atomic_dec(&dhd->pend_8021x_cnt);
+
+}
+
+static struct net_device_stats *
+dhd_get_stats(struct net_device *net)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+ dhd_if_t *ifp;
+ int ifidx;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ ifidx = dhd_net2idx(dhd, net);
+ if (ifidx == DHD_BAD_IF)
+ return NULL;
+
+ ifp = dhd->iflist[ifidx];
+ ASSERT(dhd && ifp);
+
+ if (dhd->pub.up) {
+ /* Use the protocol to get dongle stats */
+ dhd_prot_dstats(&dhd->pub);
+ }
+
+ /* Copy dongle stats to net device stats */
+ ifp->stats.rx_packets = dhd->pub.dstats.rx_packets;
+ ifp->stats.tx_packets = dhd->pub.dstats.tx_packets;
+ ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes;
+ ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes;
+ ifp->stats.rx_errors = dhd->pub.dstats.rx_errors;
+ ifp->stats.tx_errors = dhd->pub.dstats.tx_errors;
+ ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped;
+ ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped;
+ ifp->stats.multicast = dhd->pub.dstats.multicast;
+
+ return &ifp->stats;
+}
+
+static int
+dhd_watchdog_thread(void *data)
+{
+ dhd_info_t *dhd = (dhd_info_t *)data;
+
+ /* This thread doesn't need any user-level access,
+ * so get rid of all our resources
+ */
+#ifdef DHD_SCHED
+ if (dhd_watchdog_prio > 0) {
+ struct sched_param param;
+ param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)?
+ dhd_watchdog_prio:(MAX_RT_PRIO-1);
+ setScheduler(current, SCHED_FIFO, &param);
+ }
+#endif /* DHD_SCHED */
+
+ DAEMONIZE("dhd_watchdog");
+
+ /* Run until signal received */
+ while (1) {
+ if (down_interruptible (&dhd->watchdog_sem) == 0) {
+ dhd_os_sdlock(&dhd->pub);
+ if (dhd->pub.dongle_reset == FALSE) {
+ DHD_TIMER(("%s:\n", __FUNCTION__));
+ /* Call the bus module watchdog */
+ dhd_bus_watchdog(&dhd->pub);
+
+ /* Count the tick for reference */
+ dhd->pub.tickcnt++;
+
+ /* Reschedule the watchdog */
+ if (dhd->wd_timer_valid)
+ mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
+ }
+ dhd_os_sdunlock(&dhd->pub);
+ dhd_os_wake_unlock(&dhd->pub);
+ } else {
+ break;
+ }
+ }
+
+ complete_and_exit(&dhd->watchdog_exited, 0);
+}
+
+static void
+dhd_watchdog(ulong data)
+{
+ dhd_info_t *dhd = (dhd_info_t *)data;
+
+ dhd_os_wake_lock(&dhd->pub);
+ if (dhd->pub.dongle_reset) {
+ dhd_os_wake_unlock(&dhd->pub);
+ return;
+ }
+
+ if (dhd->watchdog_pid >= 0) {
+ up(&dhd->watchdog_sem);
+ return;
+ }
+
+ dhd_os_sdlock(&dhd->pub);
+ /* Call the bus module watchdog */
+ dhd_bus_watchdog(&dhd->pub);
+
+ /* Count the tick for reference */
+ dhd->pub.tickcnt++;
+
+ /* Reschedule the watchdog */
+ if (dhd->wd_timer_valid)
+ mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
+ dhd_os_sdunlock(&dhd->pub);
+ dhd_os_wake_unlock(&dhd->pub);
+}
+
+static int
+dhd_dpc_thread(void *data)
+{
+ dhd_info_t *dhd = (dhd_info_t *)data;
+
+ /* This thread doesn't need any user-level access,
+ * so get rid of all our resources
+ */
+#ifdef DHD_SCHED
+ if (dhd_dpc_prio > 0)
+ {
+ struct sched_param param;
+ param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1);
+ setScheduler(current, SCHED_FIFO, &param);
+ }
+#endif /* DHD_SCHED */
+
+ DAEMONIZE("dhd_dpc");
+
+ /* Run until signal received */
+ while (1) {
+ if (down_interruptible(&dhd->dpc_sem) == 0) {
+ /* Call bus dpc unless it indicated down (then clean stop) */
+ if (dhd->pub.busstate != DHD_BUS_DOWN) {
+ if (dhd_bus_dpc(dhd->pub.bus)) {
+ up(&dhd->dpc_sem);
+ }
+ else {
+ dhd_os_wake_unlock(&dhd->pub);
+ }
+ } else {
+ dhd_bus_stop(dhd->pub.bus, TRUE);
+ dhd_os_wake_unlock(&dhd->pub);
+ }
+ }
+ else
+ break;
+ }
+
+ complete_and_exit(&dhd->dpc_exited, 0);
+}
+
+static void
+dhd_dpc(ulong data)
+{
+ dhd_info_t *dhd;
+
+ dhd = (dhd_info_t *)data;
+
+ /* Call bus dpc unless it indicated down (then clean stop) */
+ if (dhd->pub.busstate != DHD_BUS_DOWN) {
+ if (dhd_bus_dpc(dhd->pub.bus))
+ tasklet_schedule(&dhd->tasklet);
+ } else {
+ dhd_bus_stop(dhd->pub.bus, TRUE);
+ }
+}
+
+void
+dhd_sched_dpc(dhd_pub_t *dhdp)
+{
+ dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
+
+ dhd_os_wake_lock(dhdp);
+ if (dhd->dpc_pid >= 0) {
+ up(&dhd->dpc_sem);
+ return;
+ }
+
+ tasklet_schedule(&dhd->tasklet);
+}
+
+#ifdef TOE
+/* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */
+static int
+dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol)
+{
+ wl_ioctl_t ioc;
+ char buf[32];
+ int ret;
+
+ memset(&ioc, 0, sizeof(ioc));
+
+ ioc.cmd = WLC_GET_VAR;
+ ioc.buf = buf;
+ ioc.len = (uint)sizeof(buf);
+ ioc.set = FALSE;
+
+ strcpy(buf, "toe_ol");
+ if ((ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
+ /* Check for older dongle image that doesn't support toe_ol */
+ if (ret == -EIO) {
+ DHD_ERROR(("%s: toe not supported by device\n",
+ dhd_ifname(&dhd->pub, ifidx)));
+ return -EOPNOTSUPP;
+ }
+
+ DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
+ return ret;
+ }
+
+ memcpy(toe_ol, buf, sizeof(uint32));
+ return 0;
+}
+
+/* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */
+static int
+dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol)
+{
+ wl_ioctl_t ioc;
+ char buf[32];
+ int toe, ret;
+
+ memset(&ioc, 0, sizeof(ioc));
+
+ ioc.cmd = WLC_SET_VAR;
+ ioc.buf = buf;
+ ioc.len = (uint)sizeof(buf);
+ ioc.set = TRUE;
+
+ /* Set toe_ol as requested */
+
+ strcpy(buf, "toe_ol");
+ memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32));
+
+ if ((ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
+ DHD_ERROR(("%s: could not set toe_ol: ret=%d\n",
+ dhd_ifname(&dhd->pub, ifidx), ret));
+ return ret;
+ }
+
+ /* Enable toe globally only if any components are enabled. */
+
+ toe = (toe_ol != 0);
+
+ strcpy(buf, "toe");
+ memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32));
+
+ if ((ret = dhd_prot_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
+ DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
+ return ret;
+ }
+
+ return 0;
+}
+#endif /* TOE */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+static void dhd_ethtool_get_drvinfo(struct net_device *net,
+ struct ethtool_drvinfo *info)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+
+ sprintf(info->driver, "wl");
+ sprintf(info->version, "%lu", dhd->pub.drv_version);
+}
+
+struct ethtool_ops dhd_ethtool_ops = {
+ .get_drvinfo = dhd_ethtool_get_drvinfo
+};
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
+
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
+static int
+dhd_ethtool(dhd_info_t *dhd, void *uaddr)
+{
+ struct ethtool_drvinfo info;
+ char drvname[sizeof(info.driver)];
+ uint32 cmd;
+#ifdef TOE
+ struct ethtool_value edata;
+ uint32 toe_cmpnt, csum_dir;
+ int ret;
+#endif
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ /* all ethtool calls start with a cmd word */
+ if (copy_from_user(&cmd, uaddr, sizeof (uint32)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case ETHTOOL_GDRVINFO:
+ /* Copy out any request driver name */
+ if (copy_from_user(&info, uaddr, sizeof(info)))
+ return -EFAULT;
+ strncpy(drvname, info.driver, sizeof(info.driver));
+ drvname[sizeof(info.driver)-1] = '\0';
+
+ /* clear struct for return */
+ memset(&info, 0, sizeof(info));
+ info.cmd = cmd;
+
+ /* if dhd requested, identify ourselves */
+ if (strcmp(drvname, "?dhd") == 0) {
+ sprintf(info.driver, "dhd");
+ strcpy(info.version, EPI_VERSION_STR);
+ }
+
+ /* otherwise, require dongle to be up */
+ else if (!dhd->pub.up) {
+ DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__));
+ return -ENODEV;
+ }
+
+ /* finally, report dongle driver type */
+ else if (dhd->pub.iswl)
+ sprintf(info.driver, "wl");
+ else
+ sprintf(info.driver, "xx");
+
+ sprintf(info.version, "%lu", dhd->pub.drv_version);
+ if (copy_to_user(uaddr, &info, sizeof(info)))
+ return -EFAULT;
+ DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__,
+ (int)sizeof(drvname), drvname, info.driver));
+ break;
+
+#ifdef TOE
+ /* Get toe offload components from dongle */
+ case ETHTOOL_GRXCSUM:
+ case ETHTOOL_GTXCSUM:
+ if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0)
+ return ret;
+
+ csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
+
+ edata.cmd = cmd;
+ edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;
+
+ if (copy_to_user(uaddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ break;
+
+ /* Set toe offload components in dongle */
+ case ETHTOOL_SRXCSUM:
+ case ETHTOOL_STXCSUM:
+ if (copy_from_user(&edata, uaddr, sizeof(edata)))
+ return -EFAULT;
+
+ /* Read the current settings, update and write back */
+ if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0)
+ return ret;
+
+ csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
+
+ if (edata.data != 0)
+ toe_cmpnt |= csum_dir;
+ else
+ toe_cmpnt &= ~csum_dir;
+
+ if ((ret = dhd_toe_set(dhd, 0, toe_cmpnt)) < 0)
+ return ret;
+
+ /* If setting TX checksum mode, tell Linux the new mode */
+ if (cmd == ETHTOOL_STXCSUM) {
+ if (edata.data)
+ dhd->iflist[0]->net->features |= NETIF_F_IP_CSUM;
+ else
+ dhd->iflist[0]->net->features &= ~NETIF_F_IP_CSUM;
+ }
+
+ break;
+#endif /* TOE */
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */
+
+static int
+dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+ dhd_ioctl_t ioc;
+ int bcmerror = 0;
+ int buflen = 0;
+ void *buf = NULL;
+ uint driver = 0;
+ int ifidx;
+ bool is_set_key_cmd;
+ int ret;
+
+ dhd_os_wake_lock(&dhd->pub);
+
+ ifidx = dhd_net2idx(dhd, net);
+ DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd));
+
+ if (ifidx == DHD_BAD_IF) {
+ dhd_os_wake_unlock(&dhd->pub);
+ return -1;
+ }
+
+#if defined(CONFIG_WIRELESS_EXT)
+ /* linux wireless extensions */
+ if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) {
+ /* may recurse, do NOT lock */
+ ret = wl_iw_ioctl(net, ifr, cmd);
+ dhd_os_wake_unlock(&dhd->pub);
+ return ret;
+ }
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
+ if (cmd == SIOCETHTOOL) {
+ ret = dhd_ethtool(dhd, (void*)ifr->ifr_data);
+ dhd_os_wake_unlock(&dhd->pub);
+ return ret;
+ }
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */
+
+ if (cmd != SIOCDEVPRIVATE) {
+ dhd_os_wake_unlock(&dhd->pub);
+ return -EOPNOTSUPP;
+ }
+
+ memset(&ioc, 0, sizeof(ioc));
+
+ /* Copy the ioc control structure part of ioctl request */
+ if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) {
+ bcmerror = -BCME_BADADDR;
+ goto done;
+ }
+
+ /* Copy out any buffer passed */
+ if (ioc.buf) {
+ buflen = MIN(ioc.len, DHD_IOCTL_MAXLEN);
+ /* optimization for direct ioctl calls from kernel */
+ /*
+ if (segment_eq(get_fs(), KERNEL_DS)) {
+ buf = ioc.buf;
+ } else {
+ */
+ {
+ if (!(buf = (char*)MALLOC(dhd->pub.osh, buflen))) {
+ bcmerror = -BCME_NOMEM;
+ goto done;
+ }
+ if (copy_from_user(buf, ioc.buf, buflen)) {
+ bcmerror = -BCME_BADADDR;
+ goto done;
+ }
+ }
+ }
+
+ /* To differentiate between wl and dhd read 4 more byes */
+ if ((copy_from_user(&driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t),
+ sizeof(uint)) != 0)) {
+ bcmerror = -BCME_BADADDR;
+ goto done;
+ }
+
+ if (!capable(CAP_NET_ADMIN)) {
+ bcmerror = -BCME_EPERM;
+ goto done;
+ }
+
+ /* check for local dhd ioctl and handle it */
+ if (driver == DHD_IOCTL_MAGIC) {
+ bcmerror = dhd_ioctl((void *)&dhd->pub, &ioc, buf, buflen);
+ if (bcmerror)
+ dhd->pub.bcmerror = bcmerror;
+ goto done;
+ }
+
+ /* send to dongle (must be up, and wl) */
+ if (dhd->pub.busstate != DHD_BUS_DATA) {
+ DHD_ERROR(("%s DONGLE_DOWN\n", __FUNCTION__));
+ bcmerror = BCME_DONGLE_DOWN;
+ goto done;
+ }
+
+ if (!dhd->pub.iswl) {
+ bcmerror = BCME_DONGLE_DOWN;
+ goto done;
+ }
+
+ /* Intercept WLC_SET_KEY IOCTL - serialize M4 send and set key IOCTL to
+ * prevent M4 encryption.
+ */
+ is_set_key_cmd = ((ioc.cmd == WLC_SET_KEY) ||
+ ((ioc.cmd == WLC_SET_VAR) &&
+ !(strncmp("wsec_key", ioc.buf, 9))) ||
+ ((ioc.cmd == WLC_SET_VAR) &&
+ !(strncmp("bsscfg:wsec_key", ioc.buf, 15))));
+ if (is_set_key_cmd) {
+ dhd_wait_pend8021x(net);
+ }
+
+ bcmerror = dhd_prot_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf, buflen);
+
+done:
+ if ((bcmerror == -ETIMEDOUT) || ((dhd->pub.busstate == DHD_BUS_DOWN) &&
+ (!dhd->pub.dongle_reset))) {
+ DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__));
+ net_os_send_hang_message(net);
+ }
+
+ if (!bcmerror && buf && ioc.buf) {
+ if (copy_to_user(ioc.buf, buf, buflen))
+ bcmerror = -EFAULT;
+ }
+
+ if (buf)
+ MFREE(dhd->pub.osh, buf, buflen);
+
+ dhd_os_wake_unlock(&dhd->pub);
+
+ return OSL_ERROR(bcmerror);
+}
+
+static int
+dhd_stop(struct net_device *net)
+{
+#if !defined(IGNORE_ETH0_DOWN)
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+
+ DHD_TRACE(("%s: Enter %s\n", __FUNCTION__, net->name));
+ if (dhd->pub.up == 0) {
+ return 0;
+ }
+
+ /* Set state and stop OS transmissions */
+ dhd->pub.up = 0;
+ netif_stop_queue(net);
+#else
+ DHD_ERROR(("BYPASS %s:due to BRCM compilation : under investigation ...\n", __FUNCTION__));
+#endif /* !defined(IGNORE_ETH0_DOWN) */
+
+ OLD_MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int
+dhd_open(struct net_device *net)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
+#ifdef TOE
+ uint32 toe_ol;
+#endif
+ int ifidx;
+
+ /* Force start if ifconfig_up gets called before START command */
+ wl_control_wl_start(net);
+
+ ifidx = dhd_net2idx(dhd, net);
+ DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx));
+
+ if ((dhd->iflist[ifidx]) && (dhd->iflist[ifidx]->state == WLC_E_IF_DEL)) {
+ DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__));
+ return -1;
+ }
+
+ if (ifidx == 0) { /* do it only for primary eth0 */
+
+ atomic_set(&dhd->pend_8021x_cnt, 0);
+
+ memcpy(net->dev_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN);
+
+#ifdef TOE
+ /* Get current TOE mode from dongle */
+ if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0)
+ dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM;
+ else
+ dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM;
+#endif
+ }
+ /* Allow transmit calls */
+ netif_start_queue(net);
+ dhd->pub.up = 1;
+
+ OLD_MOD_INC_USE_COUNT;
+ return 0;
+}
+
+osl_t *
+dhd_osl_attach(void *pdev, uint bustype)
+{
+ return osl_attach(pdev, bustype, TRUE);
+}
+
+void
+dhd_osl_detach(osl_t *osh)
+{
+ if (MALLOCED(osh)) {
+ DHD_ERROR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh)));
+ }
+ osl_detach(osh);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && 1
+ up(&dhd_registration_sem);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
+}
+
+int
+dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name,
+ uint8 *mac_addr, uint32 flags, uint8 bssidx)
+{
+ dhd_if_t *ifp;
+
+ DHD_TRACE(("%s: idx %d, handle->%p\n", __FUNCTION__, ifidx, handle));
+
+ ASSERT(dhd && (ifidx < DHD_MAX_IFS));
+
+ ifp = dhd->iflist[ifidx];
+ if (!ifp && !(ifp = MALLOC(dhd->pub.osh, sizeof(dhd_if_t)))) {
+ DHD_ERROR(("%s: OOM - dhd_if_t\n", __FUNCTION__));
+ return -ENOMEM;
+ }
+
+ memset(ifp, 0, sizeof(dhd_if_t));
+ ifp->info = dhd;
+ dhd->iflist[ifidx] = ifp;
+ strncpy(ifp->name, name, IFNAMSIZ);
+ ifp->name[IFNAMSIZ] = '\0';
+ if (mac_addr != NULL)
+ memcpy(&ifp->mac_addr, mac_addr, ETHER_ADDR_LEN);
+
+ if (handle == NULL) {
+ ifp->state = WLC_E_IF_ADD;
+ ifp->idx = ifidx;
+ ASSERT(dhd->sysioc_pid >= 0);
+ up(&dhd->sysioc_sem);
+ } else
+ ifp->net = (struct net_device *)handle;
+
+ return 0;
+}
+
+void
+dhd_del_if(dhd_info_t *dhd, int ifidx)
+{
+ dhd_if_t *ifp;
+
+ DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx));
+
+ ASSERT(dhd && ifidx && (ifidx < DHD_MAX_IFS));
+ ifp = dhd->iflist[ifidx];
+ if (!ifp) {
+ DHD_ERROR(("%s: Null interface\n", __FUNCTION__));
+ return;
+ }
+
+ ifp->state = WLC_E_IF_DEL;
+ ifp->idx = ifidx;
+ ASSERT(dhd->sysioc_pid >= 0);
+ up(&dhd->sysioc_sem);
+}
+
+dhd_pub_t *
+dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
+{
+ dhd_info_t *dhd = NULL;
+ struct net_device *net;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+ /* updates firmware nvram path if it was provided as module paramters */
+ if ((firmware_path != NULL) && (firmware_path[0] != '\0'))
+ strcpy(fw_path, firmware_path);
+ if ((nvram_path != NULL) && (nvram_path[0] != '\0'))
+ strcpy(nv_path, nvram_path);
+
+ /* Allocate etherdev, including space for private structure */
+ if (!(net = alloc_etherdev(sizeof(dhd)))) {
+ DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
+ goto fail;
+ }
+
+ /* Allocate primary dhd_info */
+ if (!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) {
+ DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__));
+ goto fail;
+ }
+
+ memset(dhd, 0, sizeof(dhd_info_t));
+
+ /*
+ * Save the dhd_info into the priv
+ */
+ memcpy(netdev_priv(net), &dhd, sizeof(dhd));
+ dhd->pub.osh = osh;
+
+ /* Set network interface name if it was provided as module parameter */
+ if (iface_name[0]) {
+ int len;
+ char ch;
+ strncpy(net->name, iface_name, IFNAMSIZ);
+ net->name[IFNAMSIZ - 1] = 0;
+ len = strlen(net->name);
+ ch = net->name[len - 1];
+ if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2))
+ strcat(net->name, "%d");
+ }
+
+ if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF)
+ goto fail;
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+ net->open = NULL;
+#else
+ net->netdev_ops = NULL;
+#endif
+
+ init_MUTEX(&dhd->proto_sem);
+ /* Initialize other structure content */
+ init_waitqueue_head(&dhd->ioctl_resp_wait);
+ init_waitqueue_head(&dhd->ctrl_wait);
+
+ /* Initialize the spinlocks */
+ spin_lock_init(&dhd->sdlock);
+ spin_lock_init(&dhd->txqlock);
+ spin_lock_init(&dhd->dhd_lock);
+
+ /* Initialize Wakelock stuff */
+ spin_lock_init(&dhd->wl_lock);
+ dhd->wl_count = 0;
+ dhd->wl_packet = 0;
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake");
+ wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake");
+#endif
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+ mutex_init(&dhd->wl_start_lock);
+#endif
+ /* Link to info module */
+ dhd->pub.info = dhd;
+
+ /* Link to bus module */
+ dhd->pub.bus = bus;
+ dhd->pub.hdrlen = bus_hdrlen;
+
+ /* Attach and link in the protocol */
+ if (dhd_prot_attach(&dhd->pub) != 0) {
+ DHD_ERROR(("dhd_prot_attach failed\n"));
+ goto fail;
+ }
+#if defined(CONFIG_WIRELESS_EXT)
+ /* Attach and link in the iw */
+ if (wl_iw_attach(net, (void *)&dhd->pub) != 0) {
+ DHD_ERROR(("wl_iw_attach failed\n"));
+ goto fail;
+ }
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+ /* Set up the watchdog timer */
+ init_timer(&dhd->timer);
+ dhd->timer.data = (ulong)dhd;
+ dhd->timer.function = dhd_watchdog;
+
+ /* Initialize thread based operation and lock */
+ init_MUTEX(&dhd->sdsem);
+ if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) {
+ dhd->threads_only = TRUE;
+ }
+ else {
+ dhd->threads_only = FALSE;
+ }
+
+ if (dhd_dpc_prio >= 0) {
+ /* Initialize watchdog thread */
+ sema_init(&dhd->watchdog_sem, 0);
+ init_completion(&dhd->watchdog_exited);
+ dhd->watchdog_pid = kernel_thread(dhd_watchdog_thread, dhd, 0);
+ } else {
+ dhd->watchdog_pid = -1;
+ }
+
+ /* Set up the bottom half handler */
+ if (dhd_dpc_prio >= 0) {
+ /* Initialize DPC thread */
+ sema_init(&dhd->dpc_sem, 0);
+ init_completion(&dhd->dpc_exited);
+ dhd->dpc_pid = kernel_thread(dhd_dpc_thread, dhd, 0);
+ } else {
+ tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd);
+ dhd->dpc_pid = -1;
+ }
+
+ if (dhd_sysioc) {
+ sema_init(&dhd->sysioc_sem, 0);
+ init_completion(&dhd->sysioc_exited);
+ dhd->sysioc_pid = kernel_thread(_dhd_sysioc_thread, dhd, 0);
+ } else {
+ dhd->sysioc_pid = -1;
+ }
+
+ /*
+ * Save the dhd_info into the priv
+ */
+ memcpy(netdev_priv(net), &dhd, sizeof(dhd));
+
+#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
+ g_bus = bus;
+#endif
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+ register_pm_notifier(&dhd_sleep_pm_notifier);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20;
+ dhd->early_suspend.suspend = dhd_early_suspend;
+ dhd->early_suspend.resume = dhd_late_resume;
+ register_early_suspend(&dhd->early_suspend);
+#endif
+
+ return &dhd->pub;
+
+fail:
+ if (net)
+ free_netdev(net);
+ if (dhd)
+ dhd_detach(&dhd->pub);
+
+ return NULL;
+}
+
+
+int
+dhd_bus_start(dhd_pub_t *dhdp)
+{
+ int ret = -1;
+ dhd_info_t *dhd = (dhd_info_t*)dhdp->info;
+#ifdef EMBEDDED_PLATFORM
+ char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */
+#endif /* EMBEDDED_PLATFORM */
+
+ ASSERT(dhd);
+
+ DHD_TRACE(("%s: \n", __FUNCTION__));
+
+ /* try to download image and nvram to the dongle */
+ if (dhd->pub.busstate == DHD_BUS_DOWN) {
+ if (!(dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh,
+ fw_path, nv_path))) {
+ DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n",
+ __FUNCTION__, fw_path, nv_path));
+ return -1;
+ }
+ }
+
+ /* Start the watchdog timer */
+ dhd->pub.tickcnt = 0;
+ dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms);
+
+ /* Bring up the bus */
+ if ((ret = dhd_bus_init(&dhd->pub, TRUE)) != 0) {
+ DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret));
+ return ret;
+ }
+#if defined(OOB_INTR_ONLY)
+ /* Host registration for OOB interrupt */
+ if (bcmsdh_register_oob_intr(dhdp)) {
+ dhd->wd_timer_valid = FALSE;
+ del_timer_sync(&dhd->timer);
+ DHD_ERROR(("%s Host failed to resgister for OOB\n", __FUNCTION__));
+ return -ENODEV;
+ }
+
+ /* Enable oob at firmware */
+ dhd_enable_oob_intr(dhd->pub.bus, TRUE);
+#endif /* defined(OOB_INTR_ONLY) */
+
+ /* If bus is not ready, can't come up */
+ if (dhd->pub.busstate != DHD_BUS_DATA) {
+ dhd->wd_timer_valid = FALSE;
+ del_timer_sync(&dhd->timer);
+ DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__));
+ return -ENODEV;
+ }
+
+#ifdef EMBEDDED_PLATFORM
+ bcm_mkiovar("event_msgs", dhdp->eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf));
+ dhdcdc_query_ioctl(dhdp, 0, WLC_GET_VAR, iovbuf, sizeof(iovbuf));
+ bcopy(iovbuf, dhdp->eventmask, WL_EVENTING_MASK_LEN);
+
+ setbit(dhdp->eventmask, WLC_E_SET_SSID);
+ setbit(dhdp->eventmask, WLC_E_PRUNE);
+ setbit(dhdp->eventmask, WLC_E_AUTH);
+ setbit(dhdp->eventmask, WLC_E_REASSOC);
+ setbit(dhdp->eventmask, WLC_E_REASSOC_IND);
+ setbit(dhdp->eventmask, WLC_E_DEAUTH_IND);
+ setbit(dhdp->eventmask, WLC_E_DISASSOC_IND);
+ setbit(dhdp->eventmask, WLC_E_DISASSOC);
+ setbit(dhdp->eventmask, WLC_E_JOIN);
+ setbit(dhdp->eventmask, WLC_E_ASSOC_IND);
+ setbit(dhdp->eventmask, WLC_E_PSK_SUP);
+ setbit(dhdp->eventmask, WLC_E_LINK);
+ setbit(dhdp->eventmask, WLC_E_NDIS_LINK);
+ setbit(dhdp->eventmask, WLC_E_MIC_ERROR);
+ setbit(dhdp->eventmask, WLC_E_PMKID_CACHE);
+ setbit(dhdp->eventmask, WLC_E_TXFAIL);
+ setbit(dhdp->eventmask, WLC_E_JOIN_START);
+ setbit(dhdp->eventmask, WLC_E_SCAN_COMPLETE);
+ setbit(dhdp->eventmask, WLC_E_RELOAD);
+#ifdef PNO_SUPPORT
+ setbit(dhdp->eventmask, WLC_E_PFN_NET_FOUND);
+#endif /* PNO_SUPPORT */
+
+/* enable dongle roaming event */
+ setbit(dhdp->eventmask, WLC_E_ROAM);
+
+ dhdp->pktfilter_count = 1;
+ /* Setup filter to allow only unicast */
+ dhdp->pktfilter[0] = "100 0 0 0 0x01 0x00";
+#endif /* EMBEDDED_PLATFORM */
+
+ /* Bus is ready, do any protocol initialization */
+ if ((ret = dhd_prot_init(&dhd->pub)) < 0)
+ return ret;
+
+ return 0;
+}
+
+int
+dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, int set)
+{
+ char buf[strlen(name) + 1 + cmd_len];
+ int len = sizeof(buf);
+ wl_ioctl_t ioc;
+ int ret;
+
+ len = bcm_mkiovar(name, cmd_buf, cmd_len, buf, len);
+
+ memset(&ioc, 0, sizeof(ioc));
+
+ ioc.cmd = set? WLC_SET_VAR : WLC_GET_VAR;
+ ioc.buf = buf;
+ ioc.len = len;
+ ioc.set = set;
+
+ ret = dhd_prot_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len);
+ if (!set && ret >= 0)
+ memcpy(cmd_buf, buf, cmd_len);
+
+ return ret;
+}
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31))
+static struct net_device_ops dhd_ops_pri = {
+ .ndo_open = dhd_open,
+ .ndo_stop = dhd_stop,
+ .ndo_get_stats = dhd_get_stats,
+ .ndo_do_ioctl = dhd_ioctl_entry,
+ .ndo_start_xmit = dhd_start_xmit,
+ .ndo_set_mac_address = dhd_set_mac_address,
+ .ndo_set_multicast_list = dhd_set_multicast_list,
+};
+
+static struct net_device_ops dhd_ops_virt = {
+ .ndo_get_stats = dhd_get_stats,
+ .ndo_do_ioctl = dhd_ioctl_entry,
+ .ndo_start_xmit = dhd_start_xmit,
+ .ndo_set_mac_address = dhd_set_mac_address,
+ .ndo_set_multicast_list = dhd_set_multicast_list,
+};
+#endif
+
+int
+dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
+{
+ dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
+ struct net_device *net;
+ uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33 };
+
+ DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx));
+
+ ASSERT(dhd && dhd->iflist[ifidx]);
+ net = dhd->iflist[ifidx]->net;
+
+ ASSERT(net);
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+ ASSERT(!net->open);
+ net->get_stats = dhd_get_stats;
+ net->do_ioctl = dhd_ioctl_entry;
+ net->hard_start_xmit = dhd_start_xmit;
+ net->set_mac_address = dhd_set_mac_address;
+ net->set_multicast_list = dhd_set_multicast_list;
+ net->open = net->stop = NULL;
+#else
+ ASSERT(!net->netdev_ops);
+ net->netdev_ops = &dhd_ops_virt;
+#endif
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+ net->open = dhd_open;
+ net->stop = dhd_stop;
+#else
+ net->netdev_ops = &dhd_ops_pri;
+#endif
+
+ /*
+ * We have to use the primary MAC for virtual interfaces
+ */
+ if (ifidx != 0) {
+ /* for virtual interfaces use the primary MAC */
+ memcpy(temp_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN);
+ }
+
+ if (ifidx == 1) {
+ DHD_TRACE(("%s ACCESS POINT MAC: \n", __FUNCTION__));
+ /* ACCESSPOINT INTERFACE CASE */
+ temp_addr[0] |= 0x02; /* set bit 2 , - Locally Administered address */
+ }
+ net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ net->ethtool_ops = &dhd_ethtool_ops;
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
+
+#if defined(CONFIG_WIRELESS_EXT)
+#if WIRELESS_EXT < 19
+ net->get_wireless_stats = dhd_get_wireless_stats;
+#endif /* WIRELESS_EXT < 19 */
+#if WIRELESS_EXT > 12
+ net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def;
+#endif /* WIRELESS_EXT > 12 */
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+ dhd->pub.rxsz = net->mtu + net->hard_header_len + dhd->pub.hdrlen;
+
+ memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN);
+
+ if (register_netdev(net) != 0) {
+ DHD_ERROR(("%s: couldn't register the net device\n", __FUNCTION__));
+ goto fail;
+ }
+
+ printf("%s: Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", net->name,
+ dhd->pub.mac.octet[0], dhd->pub.mac.octet[1], dhd->pub.mac.octet[2],
+ dhd->pub.mac.octet[3], dhd->pub.mac.octet[4], dhd->pub.mac.octet[5]);
+
+#if defined(CONFIG_WIRELESS_EXT)
+#if defined(CONFIG_FIRST_SCAN)
+#ifdef SOFTAP
+ if (ifidx == 0)
+ /* Don't call for SOFTAP Interface in SOFTAP MODE */
+ wl_iw_iscan_set_scan_broadcast_prep(net, 1);
+#else
+ wl_iw_iscan_set_scan_broadcast_prep(net, 1);
+#endif /* SOFTAP */
+#endif /* CONFIG_FIRST_SCAN */
+#endif /* CONFIG_WIRELESS_EXT */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ up(&dhd_registration_sem);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
+ return 0;
+
+fail:
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+ net->open = NULL;
+#else
+ net->netdev_ops = NULL;
+#endif
+ return BCME_ERROR;
+}
+
+void
+dhd_bus_detach(dhd_pub_t *dhdp)
+{
+ dhd_info_t *dhd;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (dhdp) {
+ dhd = (dhd_info_t *)dhdp->info;
+ if (dhd) {
+ /* Stop the protocol module */
+ dhd_prot_stop(&dhd->pub);
+
+ /* Stop the bus module */
+ dhd_bus_stop(dhd->pub.bus, TRUE);
+#if defined(OOB_INTR_ONLY)
+ bcmsdh_unregister_oob_intr();
+#endif /* defined(OOB_INTR_ONLY) */
+
+ /* Clear the watchdog timer */
+ dhd->wd_timer_valid = FALSE;
+ del_timer_sync(&dhd->timer);
+ }
+ }
+}
+
+void
+dhd_detach(dhd_pub_t *dhdp)
+{
+ dhd_info_t *dhd;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (dhdp) {
+ dhd = (dhd_info_t *)dhdp->info;
+ if (dhd) {
+ dhd_if_t *ifp;
+ int i;
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ if (dhd->early_suspend.suspend)
+ unregister_early_suspend(&dhd->early_suspend);
+#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
+#if defined(CONFIG_WIRELESS_EXT)
+ /* Attach and link in the iw */
+ wl_iw_detach();
+#endif
+ if (dhd->sysioc_pid >= 0) {
+ KILL_PROC(dhd->sysioc_pid, SIGTERM);
+ wait_for_completion(&dhd->sysioc_exited);
+ }
+
+ for (i = 1; i < DHD_MAX_IFS; i++)
+ if (dhd->iflist[i]) {
+ dhd->iflist[i]->state = WLC_E_IF_DEL;
+ dhd->iflist[i]->idx = i;
+ dhd_op_if(dhd->iflist[i]);
+ }
+
+ ifp = dhd->iflist[0];
+ ASSERT(ifp);
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+ if (ifp->net->open) {
+#else
+ if (ifp->net->netdev_ops == &dhd_ops_pri) {
+#endif
+ dhd_stop(ifp->net);
+ unregister_netdev(ifp->net);
+ }
+
+ if (dhd->watchdog_pid >= 0)
+ {
+ KILL_PROC(dhd->watchdog_pid, SIGTERM);
+ wait_for_completion(&dhd->watchdog_exited);
+ }
+
+ if (dhd->dpc_pid >= 0)
+ {
+ KILL_PROC(dhd->dpc_pid, SIGTERM);
+ wait_for_completion(&dhd->dpc_exited);
+ }
+ else
+ tasklet_kill(&dhd->tasklet);
+
+ dhd_bus_detach(dhdp);
+
+ if (dhdp->prot)
+ dhd_prot_detach(dhdp);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
+ unregister_pm_notifier(&dhd_sleep_pm_notifier);
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
+ free_netdev(ifp->net);
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_destroy(&dhd->wl_wifi);
+ wake_lock_destroy(&dhd->wl_rxwake);
+#endif
+ MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
+ MFREE(dhd->pub.osh, dhd, sizeof(*dhd));
+ }
+ }
+}
+
+static void __exit
+dhd_module_cleanup(void)
+{
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ dhd_bus_unregister();
+#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
+ wifi_del_dev();
+#endif
+ /* Call customer gpio to turn off power with WL_REG_ON signal */
+ dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
+}
+
+static int __init
+dhd_module_init(void)
+{
+ int error;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ /* Sanity check on the module parameters */
+ do {
+ /* Both watchdog and DPC as tasklets are ok */
+ if ((dhd_watchdog_prio < 0) && (dhd_dpc_prio < 0))
+ break;
+
+ /* If both watchdog and DPC are threads, TX must be deferred */
+ if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0) && dhd_deferred_tx)
+ break;
+
+ DHD_ERROR(("Invalid module parameters.\n"));
+ return -EINVAL;
+ } while (0);
+
+ /* Call customer gpio to turn on power with WL_REG_ON signal */
+ dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON);
+
+#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
+ sema_init(&wifi_control_sem, 0);
+
+ error = wifi_add_dev();
+ if (error) {
+ DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__));
+ goto fail_0;
+ }
+
+ /* Waiting callback after platform_driver_register is done or exit with error */
+ if (down_timeout(&wifi_control_sem, msecs_to_jiffies(5000)) != 0) {
+ error = -EINVAL;
+ DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__));
+ goto fail_1;
+ }
+#endif /* #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ sema_init(&dhd_registration_sem, 0);
+#endif
+
+ error = dhd_bus_register();
+
+ if (!error)
+ printf("\n%s\n", dhd_version);
+ else {
+ DHD_ERROR(("%s: sdio_register_driver failed\n", __FUNCTION__));
+ goto fail_1;
+ }
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ /*
+ * Wait till MMC sdio_register_driver callback called and made driver attach.
+ * It's needed to make sync up exit from dhd insmod and
+ * Kernel MMC sdio device callback registration
+ */
+ if (down_timeout(&dhd_registration_sem, msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) {
+ error = -EINVAL;
+ DHD_ERROR(("%s: sdio_register_driver timeout\n", __FUNCTION__));
+ goto fail_2;
+ }
+#endif
+ return error;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+fail_2:
+ dhd_bus_unregister();
+#endif
+fail_1:
+#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
+ wifi_del_dev();
+fail_0:
+#endif /* defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
+
+ /* Call customer gpio to turn off power with WL_REG_ON signal */
+ dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF);
+
+ return error;
+}
+
+module_init(dhd_module_init);
+module_exit(dhd_module_cleanup);
+
+/*
+ * OS specific functions required to implement DHD driver in OS independent way
+ */
+int
+dhd_os_proto_block(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+ if (dhd) {
+ down(&dhd->proto_sem);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+dhd_os_proto_unblock(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+ if (dhd) {
+ up(&dhd->proto_sem);
+ return 1;
+ }
+
+ return 0;
+}
+
+unsigned int
+dhd_os_get_ioctl_resp_timeout(void)
+{
+ return ((unsigned int)dhd_ioctl_timeout_msec);
+}
+
+void
+dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec)
+{
+ dhd_ioctl_timeout_msec = (int)timeout_msec;
+}
+
+int
+dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending)
+{
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+ DECLARE_WAITQUEUE(wait, current);
+ int timeout = dhd_ioctl_timeout_msec;
+
+ /* Convert timeout in millsecond to jiffies */
+ /* timeout = timeout * HZ / 1000; */
+ timeout = msecs_to_jiffies(timeout);
+
+ /* Wait until control frame is available */
+ add_wait_queue(&dhd->ioctl_resp_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ smp_mb();
+ while (!(*condition) && (!signal_pending(current) && timeout)) {
+ timeout = schedule_timeout(timeout);
+ smp_mb();
+ }
+
+ if (signal_pending(current))
+ *pending = TRUE;
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dhd->ioctl_resp_wait, &wait);
+
+ return timeout;
+}
+
+int
+dhd_os_ioctl_resp_wake(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+ if (waitqueue_active(&dhd->ioctl_resp_wait)) {
+ wake_up_interruptible(&dhd->ioctl_resp_wait);
+ }
+
+ return 0;
+}
+
+void
+dhd_os_wd_timer(void *bus, uint wdtick)
+{
+ dhd_pub_t *pub = bus;
+ dhd_info_t *dhd = (dhd_info_t *)pub->info;
+ unsigned long flags;
+ int del_timer_flag = FALSE;
+
+ flags = dhd_os_spin_lock(pub);
+
+ /* don't start the wd until fw is loaded */
+ if (pub->busstate != DHD_BUS_DOWN) {
+ if (wdtick) {
+ dhd_watchdog_ms = (uint)wdtick;
+ dhd->wd_timer_valid = TRUE;
+ /* Re arm the timer, at last watchdog period */
+ mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
+ } else if (dhd->wd_timer_valid == TRUE) {
+ /* Totally stop the timer */
+ dhd->wd_timer_valid = FALSE;
+ del_timer_flag = TRUE;
+ }
+ }
+ dhd_os_spin_unlock(pub, flags);
+ if (del_timer_flag) {
+ del_timer_sync(&dhd->timer);
+ }
+}
+
+void *
+dhd_os_open_image(char *filename)
+{
+ struct file *fp;
+
+ fp = filp_open(filename, O_RDONLY, 0);
+ /*
+ * 2.6.11 (FC4) supports filp_open() but later revs don't?
+ * Alternative:
+ * fp = open_namei(AT_FDCWD, filename, O_RD, 0);
+ * ???
+ */
+ if (IS_ERR(fp))
+ fp = NULL;
+
+ return fp;
+}
+
+int
+dhd_os_get_image_block(char *buf, int len, void *image)
+{
+ struct file *fp = (struct file *)image;
+ int rdlen;
+
+ if (!image)
+ return 0;
+
+ rdlen = kernel_read(fp, fp->f_pos, buf, len);
+ if (rdlen > 0)
+ fp->f_pos += rdlen;
+
+ return rdlen;
+}
+
+void
+dhd_os_close_image(void *image)
+{
+ if (image)
+ filp_close((struct file *)image, NULL);
+}
+
+
+void
+dhd_os_sdlock(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd;
+
+ dhd = (dhd_info_t *)(pub->info);
+
+ if (dhd->threads_only)
+ down(&dhd->sdsem);
+ else
+ spin_lock_bh(&dhd->sdlock);
+}
+
+void
+dhd_os_sdunlock(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd;
+
+ dhd = (dhd_info_t *)(pub->info);
+
+ if (dhd->threads_only)
+ up(&dhd->sdsem);
+ else
+ spin_unlock_bh(&dhd->sdlock);
+}
+
+void
+dhd_os_sdlock_txq(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd;
+
+ dhd = (dhd_info_t *)(pub->info);
+ spin_lock_bh(&dhd->txqlock);
+}
+
+void
+dhd_os_sdunlock_txq(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd;
+
+ dhd = (dhd_info_t *)(pub->info);
+ spin_unlock_bh(&dhd->txqlock);
+}
+void
+dhd_os_sdlock_rxq(dhd_pub_t *pub)
+{
+}
+void
+dhd_os_sdunlock_rxq(dhd_pub_t *pub)
+{
+}
+
+void
+dhd_os_sdtxlock(dhd_pub_t *pub)
+{
+ dhd_os_sdlock(pub);
+}
+
+void
+dhd_os_sdtxunlock(dhd_pub_t *pub)
+{
+ dhd_os_sdunlock(pub);
+}
+
+#ifdef DHD_USE_STATIC_BUF
+void * dhd_os_prealloc(int section, unsigned long size)
+{
+#if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC)
+ void *alloc_ptr = NULL;
+ if (wifi_control_data && wifi_control_data->mem_prealloc)
+ {
+ alloc_ptr = wifi_control_data->mem_prealloc(section, size);
+ if (alloc_ptr)
+ {
+ DHD_INFO(("success alloc section %d\n", section));
+ bzero(alloc_ptr, size);
+ return alloc_ptr;
+ }
+ }
+
+ DHD_ERROR(("can't alloc section %d\n", section));
+ return 0;
+#else
+return MALLOC(0, size);
+#endif /* #if defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
+}
+#endif /* DHD_USE_STATIC_BUF */
+#if defined(CONFIG_WIRELESS_EXT)
+struct iw_statistics *
+dhd_get_wireless_stats(struct net_device *dev)
+{
+ int res = 0;
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+ res = wl_iw_get_wireless_stats(dev, &dhd->iw.wstats);
+
+ if (res == 0)
+ return &dhd->iw.wstats;
+ else
+ return NULL;
+}
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+static int
+dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
+ wl_event_msg_t *event, void **data)
+{
+ int bcmerror = 0;
+
+ ASSERT(dhd != NULL);
+
+ bcmerror = wl_host_event(dhd, ifidx, pktdata, event, data);
+ if (bcmerror != BCME_OK)
+ return (bcmerror);
+
+#if defined(CONFIG_WIRELESS_EXT)
+ ASSERT(dhd->iflist[*ifidx] != NULL);
+
+ if (ntoh32(event->event_type) == WLC_E_IF) {
+ DHD_INFO(("<0> interface:%d OP:%d don't pass to wext,"
+ "net_device might not be created yet\n",
+ *ifidx, ntoh32(event->event_type)));
+ return bcmerror;
+ }
+
+ ASSERT(dhd->iflist[*ifidx]->net != NULL);
+
+ if (dhd->iflist[*ifidx]->net)
+ wl_iw_event(dhd->iflist[*ifidx]->net, event, *data);
+#endif /* defined(CONFIG_WIRELESS_EXT) */
+
+ return (bcmerror);
+}
+
+/* send up locally generated event */
+void
+dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data)
+{
+ switch (ntoh32(event->event_type)) {
+ default:
+ break;
+ }
+}
+
+void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+ struct dhd_info *dhdinfo = dhd->info;
+ dhd_os_sdunlock(dhd);
+ wait_event_interruptible_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), HZ * 2);
+ dhd_os_sdlock(dhd);
+#endif
+ return;
+}
+
+void dhd_wait_event_wakeup(dhd_pub_t *dhd)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+ struct dhd_info *dhdinfo = dhd->info;
+ if (waitqueue_active(&dhdinfo->ctrl_wait))
+ wake_up_interruptible(&dhdinfo->ctrl_wait);
+#endif
+ return;
+}
+
+int
+dhd_dev_reset(struct net_device *dev, uint8 flag)
+{
+ int ret;
+
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+ ret = dhd_bus_devreset(&dhd->pub, flag);
+ if (ret) {
+ DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret));
+ return ret;
+ }
+ DHD_ERROR(("%s: WLAN %s DONE\n", __FUNCTION__, flag ? "OFF" : "ON"));
+
+ return ret;
+}
+
+int net_os_set_suspend_disable(struct net_device *dev, int val)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+ int ret = 0;
+
+ if (dhd) {
+ ret = dhd->pub.suspend_disable_flag;
+ dhd->pub.suspend_disable_flag = val;
+ }
+ return ret;
+}
+
+int net_os_set_suspend(struct net_device *dev, int val)
+{
+ int ret = 0;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+ if (dhd) {
+ dhd_os_proto_block(&dhd->pub);
+ ret = dhd_set_suspend(val, &dhd->pub);
+ dhd_os_proto_unblock(&dhd->pub);
+ }
+#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
+ return ret;
+}
+
+int net_os_set_dtim_skip(struct net_device *dev, int val)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+ if (dhd)
+ dhd->pub.dtim_skip = val;
+
+ return 0;
+}
+
+int net_os_set_packet_filter(struct net_device *dev, int val)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+ int ret = 0;
+
+ /* Packet filtering is set only if we still in early-suspend and
+ * we need either to turn it ON or turn it OFF
+ * We can always turn it OFF in case of early-suspend, but we turn it
+ * back ON only if suspend_disable_flag was not set
+ */
+ if (dhd && dhd->pub.up) {
+ dhd_os_proto_block(&dhd->pub);
+ if (dhd->pub.in_suspend) {
+ if (!val || (val && !dhd->pub.suspend_disable_flag))
+ dhd_set_packet_filter(val, &dhd->pub);
+ }
+ dhd_os_proto_unblock(&dhd->pub);
+ }
+ return ret;
+}
+
+
+void
+dhd_dev_init_ioctl(struct net_device *dev)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+ dhd_preinit_ioctls(&dhd->pub);
+}
+
+#ifdef PNO_SUPPORT
+/* Linux wrapper to call common dhd_pno_clean */
+int
+dhd_dev_pno_reset(struct net_device *dev)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+ return (dhd_pno_clean(&dhd->pub));
+}
+
+
+/* Linux wrapper to call common dhd_pno_enable */
+int
+dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+ return (dhd_pno_enable(&dhd->pub, pfn_enabled));
+}
+
+
+/* Linux wrapper to call common dhd_pno_set */
+int
+dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+ return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr));
+}
+
+/* Linux wrapper to get pno status */
+int
+dhd_dev_get_pno_status(struct net_device *dev)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+ return (dhd_pno_get_status(&dhd->pub));
+}
+
+#endif /* PNO_SUPPORT */
+
+int net_os_send_hang_message(struct net_device *dev)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+ int ret = 0;
+
+ if (dhd) {
+ if (!dhd->hang_was_sent) {
+ dhd->hang_was_sent = 1;
+ ret = wl_iw_send_priv_event(dev, "HANG");
+ }
+ }
+ return ret;
+}
+
+void dhd_bus_country_set(struct net_device *dev, char *country_code)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+ if (dhd && dhd->pub.up)
+ strncpy(dhd->pub.country_code, country_code, WLC_CNTRY_BUF_SZ);
+}
+
+char *dhd_bus_country_get(struct net_device *dev)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+ if (dhd && (dhd->pub.country_code[0] != 0))
+ return dhd->pub.country_code;
+ return NULL;
+}
+
+void dhd_os_start_lock(dhd_pub_t *pub)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+ if (dhd)
+ mutex_lock(&dhd->wl_start_lock);
+#endif
+}
+
+void dhd_os_start_unlock(dhd_pub_t *pub)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+ if (dhd)
+ mutex_unlock(&dhd->wl_start_lock);
+#endif
+}
+
+static int
+dhd_get_pend_8021x_cnt(dhd_info_t *dhd)
+{
+ return (atomic_read(&dhd->pend_8021x_cnt));
+}
+
+#define MAX_WAIT_FOR_8021X_TX 10
+
+int
+dhd_wait_pend8021x(struct net_device *dev)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+ int timeout = 10 * HZ / 1000;
+ int ntimes = MAX_WAIT_FOR_8021X_TX;
+ int pend = dhd_get_pend_8021x_cnt(dhd);
+
+ while (ntimes && pend) {
+ if (pend) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(timeout);
+ set_current_state(TASK_RUNNING);
+ ntimes--;
+ }
+ pend = dhd_get_pend_8021x_cnt(dhd);
+ }
+ return pend;
+}
+
+#ifdef DHD_DEBUG
+int
+write_to_file(dhd_pub_t *dhd, uint8 *buf, int size)
+{
+ int ret = 0;
+ struct file *fp;
+ mm_segment_t old_fs;
+ loff_t pos = 0;
+
+ /* change to KERNEL_DS address limit */
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ /* open file to write */
+ fp = filp_open("/tmp/mem_dump", O_WRONLY|O_CREAT, 0640);
+ if (!fp) {
+ printf("%s: open file error\n", __FUNCTION__);
+ ret = -1;
+ goto exit;
+ }
+
+ /* Write buf to file */
+ fp->f_op->write(fp, buf, size, &pos);
+
+exit:
+ /* free buf before return */
+ MFREE(dhd->osh, buf, size);
+ /* close file before return */
+ if (fp)
+ filp_close(fp, current->files);
+ /* restore previous address limit */
+ set_fs(old_fs);
+
+ return ret;
+}
+#endif /* DHD_DEBUG */
+
+int dhd_os_wake_lock_timeout(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+ unsigned long flags;
+ int ret = 0;
+
+ if (dhd) {
+ spin_lock_irqsave(&dhd->wl_lock, flags);
+ ret = dhd->wl_packet;
+#ifdef CONFIG_HAS_WAKELOCK
+ if (dhd->wl_packet)
+ wake_lock_timeout(&dhd->wl_rxwake, HZ);
+#endif
+ dhd->wl_packet = 0;
+ spin_unlock_irqrestore(&dhd->wl_lock, flags);
+ }
+ /* printk("%s: %d\n", __FUNCTION__, ret); */
+ return ret;
+}
+
+int net_os_wake_lock_timeout(struct net_device *dev)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+ int ret = 0;
+
+ if (dhd)
+ ret = dhd_os_wake_lock_timeout(&dhd->pub);
+ return ret;
+}
+
+int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+ unsigned long flags;
+
+ if (dhd) {
+ spin_lock_irqsave(&dhd->wl_lock, flags);
+ dhd->wl_packet = 1;
+ spin_unlock_irqrestore(&dhd->wl_lock, flags);
+ }
+ /* printk("%s\n",__func__); */
+ return 0;
+}
+
+int net_os_wake_lock_timeout_enable(struct net_device *dev)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+ int ret = 0;
+
+ if (dhd)
+ ret = dhd_os_wake_lock_timeout_enable(&dhd->pub);
+ return ret;
+}
+
+int dhd_os_wake_lock(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+ unsigned long flags;
+ int ret = 0;
+
+ if (dhd) {
+ spin_lock_irqsave(&dhd->wl_lock, flags);
+#ifdef CONFIG_HAS_WAKELOCK
+ if (!dhd->wl_count)
+ wake_lock(&dhd->wl_wifi);
+#endif
+ dhd->wl_count++;
+ ret = dhd->wl_count;
+ spin_unlock_irqrestore(&dhd->wl_lock, flags);
+ }
+ /* printk("%s: %d\n", __FUNCTION__, ret); */
+ return ret;
+}
+
+int net_os_wake_lock(struct net_device *dev)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+ int ret = 0;
+
+ if (dhd)
+ ret = dhd_os_wake_lock(&dhd->pub);
+ return ret;
+}
+
+int dhd_os_wake_unlock(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+ unsigned long flags;
+ int ret = 0;
+
+ dhd_os_wake_lock_timeout(pub);
+ if (dhd) {
+ spin_lock_irqsave(&dhd->wl_lock, flags);
+ if (dhd->wl_count) {
+ dhd->wl_count--;
+#ifdef CONFIG_HAS_WAKELOCK
+ if (!dhd->wl_count)
+ wake_unlock(&dhd->wl_wifi);
+#endif
+ ret = dhd->wl_count;
+ }
+ spin_unlock_irqrestore(&dhd->wl_lock, flags);
+ }
+ /* printk("%s: %d\n", __FUNCTION__, ret); */
+ return ret;
+}
+
+int net_os_wake_unlock(struct net_device *dev)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+ int ret = 0;
+
+ if (dhd)
+ ret = dhd_os_wake_unlock(&dhd->pub);
+ return ret;
+}
+
+unsigned long dhd_os_spin_lock(dhd_pub_t *pub)
+{
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+ unsigned long flags = 0;
+
+ if (dhd)
+ spin_lock_irqsave(&dhd->dhd_lock, flags);
+
+ return flags;
+}
+
+void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags)
+{
+ dhd_info_t *dhd = (dhd_info_t *)(pub->info);
+
+ if (dhd)
+ spin_unlock_irqrestore(&dhd->dhd_lock, flags);
+}
diff --git a/drivers/net/wireless/bcm4329/dhd_linux_sched.c b/drivers/net/wireless/bcm4329/dhd_linux_sched.c
new file mode 100644
index 00000000000..480b416657e
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_linux_sched.c
@@ -0,0 +1,38 @@
+/*
+ * Expose some of the kernel scheduler routines
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_linux_sched.c,v 1.1.34.1.6.1 2009/01/16 01:17:40 Exp $
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linuxver.h>
+
+int setScheduler(struct task_struct *p, int policy, struct sched_param *param)
+{
+ int rc = 0;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+ rc = sched_setscheduler(p, policy, param);
+#endif /* LinuxVer */
+ return rc;
+}
diff --git a/drivers/net/wireless/bcm4329/dhd_proto.h b/drivers/net/wireless/bcm4329/dhd_proto.h
new file mode 100644
index 00000000000..7ef6929a5bf
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_proto.h
@@ -0,0 +1,102 @@
+/*
+ * Header file describing the internal (inter-module) DHD interfaces.
+ *
+ * Provides type definitions and function prototypes used to link the
+ * DHD OS, bus, and protocol modules.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_proto.h,v 1.2.82.1.4.1.16.7 2010/05/10 12:54:59 Exp $
+ */
+
+#ifndef _dhd_proto_h_
+#define _dhd_proto_h_
+
+#include <dhdioctl.h>
+#include <wlioctl.h>
+
+#ifndef IOCTL_RESP_TIMEOUT
+#define IOCTL_RESP_TIMEOUT 3000 /* In milli second */
+#endif
+
+#ifndef IOCTL_CHIP_ACTIVE_TIMEOUT
+#define IOCTL_CHIP_ACTIVE_TIMEOUT 10 /* In milli second */
+#endif
+
+/*
+ * Exported from the dhd protocol module (dhd_cdc, dhd_rndis)
+ */
+
+/* Linkage, sets prot link and updates hdrlen in pub */
+extern int dhd_prot_attach(dhd_pub_t *dhdp);
+
+/* Unlink, frees allocated protocol memory (including dhd_prot) */
+extern void dhd_prot_detach(dhd_pub_t *dhdp);
+
+/* Initialize protocol: sync w/dongle state.
+ * Sets dongle media info (iswl, drv_version, mac address).
+ */
+extern int dhd_prot_init(dhd_pub_t *dhdp);
+
+/* Stop protocol: sync w/dongle state. */
+extern void dhd_prot_stop(dhd_pub_t *dhdp);
+
+extern bool dhd_proto_fcinfo(dhd_pub_t *dhd, void *pktbuf, uint8 *fcbits);
+
+/* Add any protocol-specific data header.
+ * Caller must reserve prot_hdrlen prepend space.
+ */
+extern void dhd_prot_hdrpush(dhd_pub_t *, int ifidx, void *txp);
+
+/* Remove any protocol-specific data header. */
+extern int dhd_prot_hdrpull(dhd_pub_t *, int *ifidx, void *rxp);
+
+/* Use protocol to issue ioctl to dongle */
+extern int dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len);
+
+/* Check for and handle local prot-specific iovar commands */
+extern int dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
+ void *params, int plen, void *arg, int len, bool set);
+
+/* Add prot dump output to a buffer */
+extern void dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf);
+
+/* Update local copy of dongle statistics */
+extern void dhd_prot_dstats(dhd_pub_t *dhdp);
+
+extern int dhd_ioctl(dhd_pub_t * dhd_pub, dhd_ioctl_t *ioc, void * buf, uint buflen);
+
+extern int dhd_preinit_ioctls(dhd_pub_t *dhd);
+
+/********************************
+ * For version-string expansion *
+ */
+#if defined(BDC)
+#define DHD_PROTOCOL "bdc"
+#elif defined(CDC)
+#define DHD_PROTOCOL "cdc"
+#elif defined(RNDIS)
+#define DHD_PROTOCOL "rndis"
+#else
+#define DHD_PROTOCOL "unknown"
+#endif /* proto */
+
+#endif /* _dhd_proto_h_ */
diff --git a/drivers/net/wireless/bcm4329/dhd_sdio.c b/drivers/net/wireless/bcm4329/dhd_sdio.c
new file mode 100644
index 00000000000..78dff1c489b
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dhd_sdio.c
@@ -0,0 +1,5883 @@
+/*
+ * DHD Bus Module for SDIO
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhd_sdio.c,v 1.157.2.27.2.33.2.129.4.1 2010/09/02 23:13:16 Exp $
+ */
+
+#include <typedefs.h>
+#include <osl.h>
+#include <bcmsdh.h>
+
+#ifdef BCMEMBEDIMAGE
+#include BCMEMBEDIMAGE
+#endif /* BCMEMBEDIMAGE */
+
+#include <bcmdefs.h>
+#include <bcmutils.h>
+#include <bcmendian.h>
+#include <bcmdevs.h>
+#include <siutils.h>
+#include <hndpmu.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <sbhnddma.h>
+#include <sdio.h>
+#include <sbsdio.h>
+#include <sbsdpcmdev.h>
+#include <bcmsdpcm.h>
+
+#include <proto/ethernet.h>
+#include <proto/802.1d.h>
+#include <proto/802.11.h>
+
+#include <dngl_stats.h>
+#include <dhd.h>
+#include <dhd_bus.h>
+#include <dhd_proto.h>
+#include <dhd_dbg.h>
+#include <dhdioctl.h>
+#include <sdiovar.h>
+
+#ifdef DHD_DEBUG
+#include <hndrte_cons.h>
+#endif /* DHD_DEBUG */
+#ifdef DHD_DEBUG_TRAP
+#include <hndrte_armtrap.h>
+#endif /* DHD_DEBUG_TRAP */
+
+#define QLEN 256 /* bulk rx and tx queue lengths */
+#define FCHI (QLEN - 10)
+#define FCLOW (FCHI / 2)
+#define PRIOMASK 7
+
+#define TXRETRIES 2 /* # of retries for tx frames */
+
+#if defined(CONFIG_MACH_SANDGATE2G)
+#define DHD_RXBOUND 250 /* Default for max rx frames in one scheduling */
+#else
+#define DHD_RXBOUND 50 /* Default for max rx frames in one scheduling */
+#endif /* defined(CONFIG_MACH_SANDGATE2G) */
+
+#define DHD_TXBOUND 20 /* Default for max tx frames in one scheduling */
+
+#define DHD_TXMINMAX 1 /* Max tx frames if rx still pending */
+
+#define MEMBLOCK 2048 /* Block size used for downloading of dongle image */
+#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold biggest possible glom */
+
+/* Packet alignment for most efficient SDIO (can change based on platform) */
+#ifndef DHD_SDALIGN
+#define DHD_SDALIGN 32
+#endif
+#if !ISPOWEROF2(DHD_SDALIGN)
+#error DHD_SDALIGN is not a power of 2!
+#endif
+
+#ifndef DHD_FIRSTREAD
+#define DHD_FIRSTREAD 32
+#endif
+#if !ISPOWEROF2(DHD_FIRSTREAD)
+#error DHD_FIRSTREAD is not a power of 2!
+#endif
+
+/* Total length of frame header for dongle protocol */
+#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
+#ifdef SDTEST
+#define SDPCM_RESERVE (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN)
+#else
+#define SDPCM_RESERVE (SDPCM_HDRLEN + DHD_SDALIGN)
+#endif
+
+/* Space for header read, limit for data packets */
+#ifndef MAX_HDR_READ
+#define MAX_HDR_READ 32
+#endif
+#if !ISPOWEROF2(MAX_HDR_READ)
+#error MAX_HDR_READ is not a power of 2!
+#endif
+
+#define MAX_RX_DATASZ 2048
+
+/* Maximum milliseconds to wait for F2 to come up */
+#define DHD_WAIT_F2RDY 3000
+
+/* Bump up limit on waiting for HT to account for first startup;
+ * if the image is doing a CRC calculation before programming the PMU
+ * for HT availability, it could take a couple hundred ms more, so
+ * max out at a 1 second (1000000us).
+ */
+#if (PMU_MAX_TRANSITION_DLY < 1000000)
+#undef PMU_MAX_TRANSITION_DLY
+#define PMU_MAX_TRANSITION_DLY 1000000
+#endif
+
+/* Value for ChipClockCSR during initial setup */
+#define DHD_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ)
+#define DHD_INIT_CLKCTL2 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP)
+
+/* Flags for SDH calls */
+#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
+
+/* Packet free applicable unconditionally for sdio and sdspi. Conditional if
+ * bufpool was present for gspi bus.
+ */
+#define PKTFREE2() if ((bus->bus != SPI_BUS) || bus->usebufpool) \
+ PKTFREE(bus->dhd->osh, pkt, FALSE);
+DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep);
+extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
+
+extern void bcmsdh_set_irq(int flag);
+
+#ifdef DHD_DEBUG
+/* Device console log buffer state */
+typedef struct dhd_console {
+ uint count; /* Poll interval msec counter */
+ uint log_addr; /* Log struct address (fixed) */
+ hndrte_log_t log; /* Log struct (host copy) */
+ uint bufsize; /* Size of log buffer */
+ uint8 *buf; /* Log buffer (host copy) */
+ uint last; /* Last buffer read index */
+} dhd_console_t;
+#endif /* DHD_DEBUG */
+
+/* Private data for SDIO bus interaction */
+typedef struct dhd_bus {
+ dhd_pub_t *dhd;
+
+ bcmsdh_info_t *sdh; /* Handle for BCMSDH calls */
+ si_t *sih; /* Handle for SI calls */
+ char *vars; /* Variables (from CIS and/or other) */
+ uint varsz; /* Size of variables buffer */
+ uint32 sbaddr; /* Current SB window pointer (-1, invalid) */
+
+ sdpcmd_regs_t *regs; /* Registers for SDIO core */
+ uint sdpcmrev; /* SDIO core revision */
+ uint armrev; /* CPU core revision */
+ uint ramrev; /* SOCRAM core revision */
+ uint32 ramsize; /* Size of RAM in SOCRAM (bytes) */
+ uint32 orig_ramsize; /* Size of RAM in SOCRAM (bytes) */
+
+ uint32 bus; /* gSPI or SDIO bus */
+ uint32 hostintmask; /* Copy of Host Interrupt Mask */
+ uint32 intstatus; /* Intstatus bits (events) pending */
+ bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */
+ bool fcstate; /* State of dongle flow-control */
+
+ uint16 cl_devid; /* cached devid for dhdsdio_probe_attach() */
+ char *fw_path; /* module_param: path to firmware image */
+ char *nv_path; /* module_param: path to nvram vars file */
+ const char *nvram_params; /* user specified nvram params. */
+
+ uint blocksize; /* Block size of SDIO transfers */
+ uint roundup; /* Max roundup limit */
+
+ struct pktq txq; /* Queue length used for flow-control */
+ uint8 flowcontrol; /* per prio flow control bitmask */
+ uint8 tx_seq; /* Transmit sequence number (next) */
+ uint8 tx_max; /* Maximum transmit sequence allowed */
+
+ uint8 hdrbuf[MAX_HDR_READ + DHD_SDALIGN];
+ uint8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
+ uint16 nextlen; /* Next Read Len from last header */
+ uint8 rx_seq; /* Receive sequence number (expected) */
+ bool rxskip; /* Skip receive (awaiting NAK ACK) */
+
+ void *glomd; /* Packet containing glomming descriptor */
+ void *glom; /* Packet chain for glommed superframe */
+ uint glomerr; /* Glom packet read errors */
+
+ uint8 *rxbuf; /* Buffer for receiving control packets */
+ uint rxblen; /* Allocated length of rxbuf */
+ uint8 *rxctl; /* Aligned pointer into rxbuf */
+ uint8 *databuf; /* Buffer for receiving big glom packet */
+ uint8 *dataptr; /* Aligned pointer into databuf */
+ uint rxlen; /* Length of valid data in buffer */
+
+ uint8 sdpcm_ver; /* Bus protocol reported by dongle */
+
+ bool intr; /* Use interrupts */
+ bool poll; /* Use polling */
+ bool ipend; /* Device interrupt is pending */
+ bool intdis; /* Interrupts disabled by isr */
+ uint intrcount; /* Count of device interrupt callbacks */
+ uint lastintrs; /* Count as of last watchdog timer */
+ uint spurious; /* Count of spurious interrupts */
+ uint pollrate; /* Ticks between device polls */
+ uint polltick; /* Tick counter */
+ uint pollcnt; /* Count of active polls */
+
+#ifdef DHD_DEBUG
+ dhd_console_t console; /* Console output polling support */
+ uint console_addr; /* Console address from shared struct */
+#endif /* DHD_DEBUG */
+
+ uint regfails; /* Count of R_REG/W_REG failures */
+
+ uint clkstate; /* State of sd and backplane clock(s) */
+ bool activity; /* Activity flag for clock down */
+ int32 idletime; /* Control for activity timeout */
+ int32 idlecount; /* Activity timeout counter */
+ int32 idleclock; /* How to set bus driver when idle */
+ int32 sd_divisor; /* Speed control to bus driver */
+ int32 sd_mode; /* Mode control to bus driver */
+ int32 sd_rxchain; /* If bcmsdh api accepts PKT chains */
+ bool use_rxchain; /* If dhd should use PKT chains */
+ bool sleeping; /* Is SDIO bus sleeping? */
+ bool rxflow_mode; /* Rx flow control mode */
+ bool rxflow; /* Is rx flow control on */
+ uint prev_rxlim_hit; /* Is prev rx limit exceeded (per dpc schedule) */
+ bool alp_only; /* Don't use HT clock (ALP only) */
+ /* Field to decide if rx of control frames happen in rxbuf or lb-pool */
+ bool usebufpool;
+
+#ifdef SDTEST
+ /* external loopback */
+ bool ext_loop;
+ uint8 loopid;
+
+ /* pktgen configuration */
+ uint pktgen_freq; /* Ticks between bursts */
+ uint pktgen_count; /* Packets to send each burst */
+ uint pktgen_print; /* Bursts between count displays */
+ uint pktgen_total; /* Stop after this many */
+ uint pktgen_minlen; /* Minimum packet data len */
+ uint pktgen_maxlen; /* Maximum packet data len */
+ uint pktgen_mode; /* Configured mode: tx, rx, or echo */
+ uint pktgen_stop; /* Number of tx failures causing stop */
+
+ /* active pktgen fields */
+ uint pktgen_tick; /* Tick counter for bursts */
+ uint pktgen_ptick; /* Burst counter for printing */
+ uint pktgen_sent; /* Number of test packets generated */
+ uint pktgen_rcvd; /* Number of test packets received */
+ uint pktgen_fail; /* Number of failed send attempts */
+ uint16 pktgen_len; /* Length of next packet to send */
+#endif /* SDTEST */
+
+ /* Some additional counters */
+ uint tx_sderrs; /* Count of tx attempts with sd errors */
+ uint fcqueued; /* Tx packets that got queued */
+ uint rxrtx; /* Count of rtx requests (NAK to dongle) */
+ uint rx_toolong; /* Receive frames too long to receive */
+ uint rxc_errors; /* SDIO errors when reading control frames */
+ uint rx_hdrfail; /* SDIO errors on header reads */
+ uint rx_badhdr; /* Bad received headers (roosync?) */
+ uint rx_badseq; /* Mismatched rx sequence number */
+ uint fc_rcvd; /* Number of flow-control events received */
+ uint fc_xoff; /* Number which turned on flow-control */
+ uint fc_xon; /* Number which turned off flow-control */
+ uint rxglomfail; /* Failed deglom attempts */
+ uint rxglomframes; /* Number of glom frames (superframes) */
+ uint rxglompkts; /* Number of packets from glom frames */
+ uint f2rxhdrs; /* Number of header reads */
+ uint f2rxdata; /* Number of frame data reads */
+ uint f2txdata; /* Number of f2 frame writes */
+ uint f1regdata; /* Number of f1 register accesses */
+
+ uint8 *ctrl_frame_buf;
+ uint32 ctrl_frame_len;
+ bool ctrl_frame_stat;
+} dhd_bus_t;
+
+/* clkstate */
+#define CLK_NONE 0
+#define CLK_SDONLY 1
+#define CLK_PENDING 2 /* Not used yet */
+#define CLK_AVAIL 3
+
+#define DHD_NOPMU(dhd) (FALSE)
+
+#ifdef DHD_DEBUG
+static int qcount[NUMPRIO];
+static int tx_packets[NUMPRIO];
+#endif /* DHD_DEBUG */
+
+/* Deferred transmit */
+const uint dhd_deferred_tx = 1;
+
+extern uint dhd_watchdog_ms;
+extern void dhd_os_wd_timer(void *bus, uint wdtick);
+
+/* Tx/Rx bounds */
+uint dhd_txbound;
+uint dhd_rxbound;
+uint dhd_txminmax;
+
+/* override the RAM size if possible */
+#define DONGLE_MIN_MEMSIZE (128 *1024)
+int dhd_dongle_memsize;
+
+static bool dhd_doflow;
+static bool dhd_alignctl;
+
+static bool sd1idle;
+
+static bool retrydata;
+#define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata)
+
+static const uint watermark = 8;
+static const uint firstread = DHD_FIRSTREAD;
+
+#define HDATLEN (firstread - (SDPCM_HDRLEN))
+
+/* Retry count for register access failures */
+static const uint retry_limit = 2;
+
+/* Force even SD lengths (some host controllers mess up on odd bytes) */
+static bool forcealign;
+
+#define ALIGNMENT 4
+
+#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
+extern void bcmsdh_enable_hw_oob_intr(void *sdh, bool enable);
+#endif
+
+#if defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD)
+#error OOB_INTR_ONLY is NOT working with SDIO_ISR_THREAD
+#endif /* defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) */
+#define PKTALIGN(osh, p, len, align) \
+ do { \
+ uint datalign; \
+ datalign = (uintptr)PKTDATA((osh), (p)); \
+ datalign = ROUNDUP(datalign, (align)) - datalign; \
+ ASSERT(datalign < (align)); \
+ ASSERT(PKTLEN((osh), (p)) >= ((len) + datalign)); \
+ if (datalign) \
+ PKTPULL((osh), (p), datalign); \
+ PKTSETLEN((osh), (p), (len)); \
+ } while (0)
+
+/* Limit on rounding up frames */
+static const uint max_roundup = 512;
+
+/* Try doing readahead */
+static bool dhd_readahead;
+
+
+/* To check if there's window offered */
+#define DATAOK(bus) \
+ (((uint8)(bus->tx_max - bus->tx_seq) != 0) && \
+ (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
+
+/* Macros to get register read/write status */
+/* NOTE: these assume a local dhdsdio_bus_t *bus! */
+#define R_SDREG(regvar, regaddr, retryvar) \
+do { \
+ retryvar = 0; \
+ do { \
+ regvar = R_REG(bus->dhd->osh, regaddr); \
+ } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
+ if (retryvar) { \
+ bus->regfails += (retryvar-1); \
+ if (retryvar > retry_limit) { \
+ DHD_ERROR(("%s: FAILED" #regvar "READ, LINE %d\n", \
+ __FUNCTION__, __LINE__)); \
+ regvar = 0; \
+ } \
+ } \
+} while (0)
+
+#define W_SDREG(regval, regaddr, retryvar) \
+do { \
+ retryvar = 0; \
+ do { \
+ W_REG(bus->dhd->osh, regaddr, regval); \
+ } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
+ if (retryvar) { \
+ bus->regfails += (retryvar-1); \
+ if (retryvar > retry_limit) \
+ DHD_ERROR(("%s: FAILED REGISTER WRITE, LINE %d\n", \
+ __FUNCTION__, __LINE__)); \
+ } \
+} while (0)
+
+
+#define DHD_BUS SDIO_BUS
+
+#define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND)
+
+#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
+
+#define GSPI_PR55150_BAILOUT
+
+
+#ifdef SDTEST
+static void dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq);
+static void dhdsdio_sdtest_set(dhd_bus_t *bus, bool start);
+#endif
+
+#ifdef DHD_DEBUG_TRAP
+static int dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size);
+static int dhdsdio_mem_dump(dhd_bus_t *bus);
+#endif /* DHD_DEBUG_TRAP */
+static int dhdsdio_download_state(dhd_bus_t *bus, bool enter);
+
+static void dhdsdio_release(dhd_bus_t *bus, osl_t *osh);
+static void dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh);
+static void dhdsdio_disconnect(void *ptr);
+static bool dhdsdio_chipmatch(uint16 chipid);
+static bool dhdsdio_probe_attach(dhd_bus_t *bus, osl_t *osh, void *sdh,
+ void * regsva, uint16 devid);
+static bool dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh);
+static bool dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh);
+static void dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, int reset_flag);
+
+static uint process_nvram_vars(char *varbuf, uint len);
+
+static void dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size);
+static int dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags,
+ uint8 *buf, uint nbytes,
+ void *pkt, bcmsdh_cmplt_fn_t complete, void *handle);
+static int dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags,
+ uint8 *buf, uint nbytes,
+ void *pkt, bcmsdh_cmplt_fn_t complete, void *handle);
+
+static bool dhdsdio_download_firmware(struct dhd_bus *bus, osl_t *osh, void *sdh);
+static int _dhdsdio_download_firmware(struct dhd_bus *bus);
+
+static int dhdsdio_download_code_file(struct dhd_bus *bus, char *image_path);
+static int dhdsdio_download_nvram(struct dhd_bus *bus);
+#ifdef BCMEMBEDIMAGE
+static int dhdsdio_download_code_array(struct dhd_bus *bus);
+#endif
+
+
+static void
+dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size)
+{
+ int32 min_size = DONGLE_MIN_MEMSIZE;
+ /* Restrict the memsize to user specified limit */
+ DHD_ERROR(("user: Restrict the dongle ram size to %d, min accepted %d\n",
+ dhd_dongle_memsize, min_size));
+ if ((dhd_dongle_memsize > min_size) &&
+ (dhd_dongle_memsize < (int32)bus->orig_ramsize))
+ bus->ramsize = dhd_dongle_memsize;
+}
+
+static int
+dhdsdio_set_siaddr_window(dhd_bus_t *bus, uint32 address)
+{
+ int err = 0;
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
+ (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
+ if (!err)
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID,
+ (address >> 16) & SBSDIO_SBADDRMID_MASK, &err);
+ if (!err)
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH,
+ (address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err);
+ return err;
+}
+
+
+/* Turn backplane clock on or off */
+static int
+dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
+{
+ int err;
+ uint8 clkctl, clkreq, devctl;
+ bcmsdh_info_t *sdh;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+#if defined(OOB_INTR_ONLY)
+ pendok = FALSE;
+#endif
+ clkctl = 0;
+ sdh = bus->sdh;
+
+
+ if (on) {
+ /* Request HT Avail */
+ clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
+
+ if ((bus->sih->chip == BCM4329_CHIP_ID) && (bus->sih->chiprev == 0))
+ clkreq |= SBSDIO_FORCE_ALP;
+
+
+
+
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
+ if (err) {
+ DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err));
+ return BCME_ERROR;
+ }
+
+ if (pendok &&
+ ((bus->sih->buscoretype == PCMCIA_CORE_ID) && (bus->sih->buscorerev == 9))) {
+ uint32 dummy, retries;
+ R_SDREG(dummy, &bus->regs->clockctlstatus, retries);
+ }
+
+ /* Check current status */
+ clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (err) {
+ DHD_ERROR(("%s: HT Avail read error: %d\n", __FUNCTION__, err));
+ return BCME_ERROR;
+ }
+
+ /* Go to pending and await interrupt if appropriate */
+ if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
+ /* Allow only clock-available interrupt */
+ devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
+ if (err) {
+ DHD_ERROR(("%s: Devctl access error setting CA: %d\n",
+ __FUNCTION__, err));
+ return BCME_ERROR;
+ }
+
+ devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
+ DHD_INFO(("CLKCTL: set PENDING\n"));
+ bus->clkstate = CLK_PENDING;
+ return BCME_OK;
+ } else if (bus->clkstate == CLK_PENDING) {
+ /* Cancel CA-only interrupt filter */
+ devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
+ }
+
+ /* Otherwise, wait here (polling) for HT Avail */
+ if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+ SPINWAIT_SLEEP(sdioh_spinwait_sleep,
+ ((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, &err)),
+ !SBSDIO_CLKAV(clkctl, bus->alp_only)), PMU_MAX_TRANSITION_DLY);
+ }
+ if (err) {
+ DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err));
+ return BCME_ERROR;
+ }
+ if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+ DHD_ERROR(("%s: HT Avail timeout (%d): clkctl 0x%02x\n",
+ __FUNCTION__, PMU_MAX_TRANSITION_DLY, clkctl));
+ return BCME_ERROR;
+ }
+
+
+ /* Mark clock available */
+ bus->clkstate = CLK_AVAIL;
+ DHD_INFO(("CLKCTL: turned ON\n"));
+
+#if defined(DHD_DEBUG)
+ if (bus->alp_only == TRUE) {
+#if !defined(BCMLXSDMMC)
+ if (!SBSDIO_ALPONLY(clkctl)) {
+ DHD_ERROR(("%s: HT Clock, when ALP Only\n", __FUNCTION__));
+ }
+#endif /* !defined(BCMLXSDMMC) */
+ } else {
+ if (SBSDIO_ALPONLY(clkctl)) {
+ DHD_ERROR(("%s: HT Clock should be on.\n", __FUNCTION__));
+ }
+ }
+#endif /* defined (DHD_DEBUG) */
+
+ bus->activity = TRUE;
+ } else {
+ clkreq = 0;
+
+ if (bus->clkstate == CLK_PENDING) {
+ /* Cancel CA-only interrupt filter */
+ devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
+ }
+
+ bus->clkstate = CLK_SDONLY;
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
+ DHD_INFO(("CLKCTL: turned OFF\n"));
+ if (err) {
+ DHD_ERROR(("%s: Failed access turning clock off: %d\n",
+ __FUNCTION__, err));
+ return BCME_ERROR;
+ }
+ }
+ return BCME_OK;
+}
+
+/* Change idle/active SD state */
+static int
+dhdsdio_sdclk(dhd_bus_t *bus, bool on)
+{
+ int err;
+ int32 iovalue;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (on) {
+ if (bus->idleclock == DHD_IDLE_STOP) {
+ /* Turn on clock and restore mode */
+ iovalue = 1;
+ err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0,
+ &iovalue, sizeof(iovalue), TRUE);
+ if (err) {
+ DHD_ERROR(("%s: error enabling sd_clock: %d\n",
+ __FUNCTION__, err));
+ return BCME_ERROR;
+ }
+
+ iovalue = bus->sd_mode;
+ err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0,
+ &iovalue, sizeof(iovalue), TRUE);
+ if (err) {
+ DHD_ERROR(("%s: error changing sd_mode: %d\n",
+ __FUNCTION__, err));
+ return BCME_ERROR;
+ }
+ } else if (bus->idleclock != DHD_IDLE_ACTIVE) {
+ /* Restore clock speed */
+ iovalue = bus->sd_divisor;
+ err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0,
+ &iovalue, sizeof(iovalue), TRUE);
+ if (err) {
+ DHD_ERROR(("%s: error restoring sd_divisor: %d\n",
+ __FUNCTION__, err));
+ return BCME_ERROR;
+ }
+ }
+ bus->clkstate = CLK_SDONLY;
+ } else {
+ /* Stop or slow the SD clock itself */
+ if ((bus->sd_divisor == -1) || (bus->sd_mode == -1)) {
+ DHD_TRACE(("%s: can't idle clock, divisor %d mode %d\n",
+ __FUNCTION__, bus->sd_divisor, bus->sd_mode));
+ return BCME_ERROR;
+ }
+ if (bus->idleclock == DHD_IDLE_STOP) {
+ if (sd1idle) {
+ /* Change to SD1 mode and turn off clock */
+ iovalue = 1;
+ err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0,
+ &iovalue, sizeof(iovalue), TRUE);
+ if (err) {
+ DHD_ERROR(("%s: error changing sd_clock: %d\n",
+ __FUNCTION__, err));
+ return BCME_ERROR;
+ }
+ }
+
+ iovalue = 0;
+ err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0,
+ &iovalue, sizeof(iovalue), TRUE);
+ if (err) {
+ DHD_ERROR(("%s: error disabling sd_clock: %d\n",
+ __FUNCTION__, err));
+ return BCME_ERROR;
+ }
+ } else if (bus->idleclock != DHD_IDLE_ACTIVE) {
+ /* Set divisor to idle value */
+ iovalue = bus->idleclock;
+ err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0,
+ &iovalue, sizeof(iovalue), TRUE);
+ if (err) {
+ DHD_ERROR(("%s: error changing sd_divisor: %d\n",
+ __FUNCTION__, err));
+ return BCME_ERROR;
+ }
+ }
+ bus->clkstate = CLK_NONE;
+ }
+
+ return BCME_OK;
+}
+
+/* Transition SD and backplane clock readiness */
+static int
+dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
+{
+ int ret = BCME_OK;
+#ifdef DHD_DEBUG
+ uint oldstate = bus->clkstate;
+#endif /* DHD_DEBUG */
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ /* Early exit if we're already there */
+ if (bus->clkstate == target) {
+ if (target == CLK_AVAIL) {
+ dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
+ bus->activity = TRUE;
+ }
+ return ret;
+ }
+
+ switch (target) {
+ case CLK_AVAIL:
+ /* Make sure SD clock is available */
+ if (bus->clkstate == CLK_NONE)
+ dhdsdio_sdclk(bus, TRUE);
+ /* Now request HT Avail on the backplane */
+ ret = dhdsdio_htclk(bus, TRUE, pendok);
+ if (ret == BCME_OK) {
+ dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
+ bus->activity = TRUE;
+ }
+ break;
+
+ case CLK_SDONLY:
+ /* Remove HT request, or bring up SD clock */
+ if (bus->clkstate == CLK_NONE)
+ ret = dhdsdio_sdclk(bus, TRUE);
+ else if (bus->clkstate == CLK_AVAIL)
+ ret = dhdsdio_htclk(bus, FALSE, FALSE);
+ else
+ DHD_ERROR(("dhdsdio_clkctl: request for %d -> %d\n",
+ bus->clkstate, target));
+ if (ret == BCME_OK)
+ dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
+ break;
+
+ case CLK_NONE:
+ /* Make sure to remove HT request */
+ if (bus->clkstate == CLK_AVAIL)
+ ret = dhdsdio_htclk(bus, FALSE, FALSE);
+ /* Now remove the SD clock */
+ ret = dhdsdio_sdclk(bus, FALSE);
+ dhd_os_wd_timer(bus->dhd, 0);
+ break;
+ }
+#ifdef DHD_DEBUG
+ DHD_INFO(("dhdsdio_clkctl: %d -> %d\n", oldstate, bus->clkstate));
+#endif /* DHD_DEBUG */
+
+ return ret;
+}
+
+int
+dhdsdio_bussleep(dhd_bus_t *bus, bool sleep)
+{
+ bcmsdh_info_t *sdh = bus->sdh;
+ sdpcmd_regs_t *regs = bus->regs;
+ uint retries = 0;
+
+ DHD_INFO(("dhdsdio_bussleep: request %s (currently %s)\n",
+ (sleep ? "SLEEP" : "WAKE"),
+ (bus->sleeping ? "SLEEP" : "WAKE")));
+
+ /* Done if we're already in the requested state */
+ if (sleep == bus->sleeping)
+ return BCME_OK;
+
+ /* Going to sleep: set the alarm and turn off the lights... */
+ if (sleep) {
+ /* Don't sleep if something is pending */
+ if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq))
+ return BCME_BUSY;
+
+
+ /* Disable SDIO interrupts (no longer interested) */
+ bcmsdh_intr_disable(bus->sdh);
+
+ /* Make sure the controller has the bus up */
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+ /* Tell device to start using OOB wakeup */
+ W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
+ if (retries > retry_limit)
+ DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
+
+ /* Turn off our contribution to the HT clock request */
+ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+ SBSDIO_FORCE_HW_CLKREQ_OFF, NULL);
+
+ /* Isolate the bus */
+ if (bus->sih->chip != BCM4329_CHIP_ID && bus->sih->chip != BCM4319_CHIP_ID) {
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
+ SBSDIO_DEVCTL_PADS_ISO, NULL);
+ }
+
+ /* Change state */
+ bus->sleeping = TRUE;
+
+ } else {
+ /* Waking up: bus power up is ok, set local state */
+
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+ 0, NULL);
+
+ /* Force pad isolation off if possible (in case power never toggled) */
+ if ((bus->sih->buscoretype == PCMCIA_CORE_ID) && (bus->sih->buscorerev >= 10))
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, 0, NULL);
+
+
+ /* Make sure the controller has the bus up */
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+ /* Send misc interrupt to indicate OOB not needed */
+ W_SDREG(0, &regs->tosbmailboxdata, retries);
+ if (retries <= retry_limit)
+ W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
+
+ if (retries > retry_limit)
+ DHD_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n"));
+
+ /* Make sure we have SD bus access */
+ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+
+ /* Change state */
+ bus->sleeping = FALSE;
+
+ /* Enable interrupts again */
+ if (bus->intr && (bus->dhd->busstate == DHD_BUS_DATA)) {
+ bus->intdis = FALSE;
+ bcmsdh_intr_enable(bus->sdh);
+ }
+ }
+
+ return BCME_OK;
+}
+#if defined(OOB_INTR_ONLY)
+void
+dhd_enable_oob_intr(struct dhd_bus *bus, bool enable)
+{
+#if defined(HW_OOB)
+ bcmsdh_enable_hw_oob_intr(bus->sdh, enable);
+#else
+ sdpcmd_regs_t *regs = bus->regs;
+ uint retries = 0;
+
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+ if (enable == TRUE) {
+
+ /* Tell device to start using OOB wakeup */
+ W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
+ if (retries > retry_limit)
+ DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
+
+ } else {
+ /* Send misc interrupt to indicate OOB not needed */
+ W_SDREG(0, &regs->tosbmailboxdata, retries);
+ if (retries <= retry_limit)
+ W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
+ }
+
+ /* Turn off our contribution to the HT clock request */
+ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+#endif /* !defined(HW_OOB) */
+}
+#endif /* defined(OOB_INTR_ONLY) */
+
+#define BUS_WAKE(bus) \
+ do { \
+ if ((bus)->sleeping) \
+ dhdsdio_bussleep((bus), FALSE); \
+ } while (0);
+
+
+/* Writes a HW/SW header into the packet and sends it. */
+/* Assumes: (a) header space already there, (b) caller holds lock */
+static int
+dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt)
+{
+ int ret;
+ osl_t *osh;
+ uint8 *frame;
+ uint16 len, pad = 0;
+ uint32 swheader;
+ uint retries = 0;
+ bcmsdh_info_t *sdh;
+ void *new;
+ int i;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ sdh = bus->sdh;
+ osh = bus->dhd->osh;
+
+ if (bus->dhd->dongle_reset) {
+ ret = BCME_NOTREADY;
+ goto done;
+ }
+
+ frame = (uint8*)PKTDATA(osh, pkt);
+
+ /* Add alignment padding, allocate new packet if needed */
+ if ((pad = ((uintptr)frame % DHD_SDALIGN))) {
+ if (PKTHEADROOM(osh, pkt) < pad) {
+ DHD_INFO(("%s: insufficient headroom %d for %d pad\n",
+ __FUNCTION__, (int)PKTHEADROOM(osh, pkt), pad));
+ bus->dhd->tx_realloc++;
+ new = PKTGET(osh, (PKTLEN(osh, pkt) + DHD_SDALIGN), TRUE);
+ if (!new) {
+ DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
+ __FUNCTION__, PKTLEN(osh, pkt) + DHD_SDALIGN));
+ ret = BCME_NOMEM;
+ goto done;
+ }
+
+ PKTALIGN(osh, new, PKTLEN(osh, pkt), DHD_SDALIGN);
+ bcopy(PKTDATA(osh, pkt), PKTDATA(osh, new), PKTLEN(osh, pkt));
+ if (free_pkt)
+ PKTFREE(osh, pkt, TRUE);
+ /* free the pkt if canned one is not used */
+ free_pkt = TRUE;
+ pkt = new;
+ frame = (uint8*)PKTDATA(osh, pkt);
+ ASSERT(((uintptr)frame % DHD_SDALIGN) == 0);
+ pad = 0;
+ } else {
+ PKTPUSH(osh, pkt, pad);
+ frame = (uint8*)PKTDATA(osh, pkt);
+
+ ASSERT((pad + SDPCM_HDRLEN) <= (int) PKTLEN(osh, pkt));
+ bzero(frame, pad + SDPCM_HDRLEN);
+ }
+ }
+ ASSERT(pad < DHD_SDALIGN);
+
+ /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
+ len = (uint16)PKTLEN(osh, pkt);
+ *(uint16*)frame = htol16(len);
+ *(((uint16*)frame) + 1) = htol16(~len);
+
+ /* Software tag: channel, sequence number, data offset */
+ swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
+ (((pad + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
+ htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN);
+ htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+
+#ifdef DHD_DEBUG
+ tx_packets[PKTPRIO(pkt)]++;
+ if (DHD_BYTES_ON() &&
+ (((DHD_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) ||
+ (DHD_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) {
+ prhex("Tx Frame", frame, len);
+ } else if (DHD_HDRS_ON()) {
+ prhex("TxHdr", frame, MIN(len, 16));
+ }
+#endif
+
+ /* Raise len to next SDIO block to eliminate tail command */
+ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
+ uint16 pad = bus->blocksize - (len % bus->blocksize);
+ if ((pad <= bus->roundup) && (pad < bus->blocksize))
+#ifdef NOTUSED
+ if (pad <= PKTTAILROOM(osh, pkt))
+#endif /* NOTUSED */
+ len += pad;
+ } else if (len % DHD_SDALIGN) {
+ len += DHD_SDALIGN - (len % DHD_SDALIGN);
+ }
+
+ /* Some controllers have trouble with odd bytes -- round to even */
+ if (forcealign && (len & (ALIGNMENT - 1))) {
+#ifdef NOTUSED
+ if (PKTTAILROOM(osh, pkt))
+#endif
+ len = ROUNDUP(len, ALIGNMENT);
+#ifdef NOTUSED
+ else
+ DHD_ERROR(("%s: sending unrounded %d-byte packet\n", __FUNCTION__, len));
+#endif
+ }
+
+ do {
+ ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+ frame, len, pkt, NULL, NULL);
+ bus->f2txdata++;
+ ASSERT(ret != BCME_PENDING);
+
+ if (ret < 0) {
+ /* On failure, abort the command and terminate the frame */
+ DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
+ __FUNCTION__, ret));
+ bus->tx_sderrs++;
+
+ bcmsdh_abort(sdh, SDIO_FUNC_2);
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL,
+ SFC_WF_TERM, NULL);
+ bus->f1regdata++;
+
+ for (i = 0; i < 3; i++) {
+ uint8 hi, lo;
+ hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCHI, NULL);
+ lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCLO, NULL);
+ bus->f1regdata += 2;
+ if ((hi == 0) && (lo == 0))
+ break;
+ }
+
+ }
+ if (ret == 0) {
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+ }
+ } while ((ret < 0) && retrydata && retries++ < TXRETRIES);
+
+done:
+ /* restore pkt buffer pointer before calling tx complete routine */
+ PKTPULL(osh, pkt, SDPCM_HDRLEN + pad);
+ dhd_os_sdunlock(bus->dhd);
+ dhd_txcomplete(bus->dhd, pkt, ret != 0);
+ dhd_os_sdlock(bus->dhd);
+
+ if (free_pkt)
+ PKTFREE(osh, pkt, TRUE);
+
+ return ret;
+}
+
+int
+dhd_bus_txdata(struct dhd_bus *bus, void *pkt)
+{
+ int ret = BCME_ERROR;
+ osl_t *osh;
+ uint datalen, prec;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ osh = bus->dhd->osh;
+ datalen = PKTLEN(osh, pkt);
+
+#ifdef SDTEST
+ /* Push the test header if doing loopback */
+ if (bus->ext_loop) {
+ uint8* data;
+ PKTPUSH(osh, pkt, SDPCM_TEST_HDRLEN);
+ data = PKTDATA(osh, pkt);
+ *data++ = SDPCM_TEST_ECHOREQ;
+ *data++ = (uint8)bus->loopid++;
+ *data++ = (datalen >> 0);
+ *data++ = (datalen >> 8);
+ datalen += SDPCM_TEST_HDRLEN;
+ }
+#endif /* SDTEST */
+
+ /* Add space for the header */
+ PKTPUSH(osh, pkt, SDPCM_HDRLEN);
+ ASSERT(ISALIGNED((uintptr)PKTDATA(osh, pkt), 2));
+
+ prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK));
+
+
+ /* Check for existing queue, current flow-control, pending event, or pending clock */
+ if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq) || bus->dpc_sched ||
+ (!DATAOK(bus)) || (bus->flowcontrol & NBITVAL(prec)) ||
+ (bus->clkstate != CLK_AVAIL)) {
+ DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__,
+ pktq_len(&bus->txq)));
+ bus->fcqueued++;
+
+ /* Priority based enq */
+ dhd_os_sdlock_txq(bus->dhd);
+ if (dhd_prec_enq(bus->dhd, &bus->txq, pkt, prec) == FALSE) {
+ PKTPULL(osh, pkt, SDPCM_HDRLEN);
+ dhd_txcomplete(bus->dhd, pkt, FALSE);
+ PKTFREE(osh, pkt, TRUE);
+ DHD_ERROR(("%s: out of bus->txq !!!\n", __FUNCTION__));
+ ret = BCME_NORESOURCE;
+ } else {
+ ret = BCME_OK;
+ }
+ dhd_os_sdunlock_txq(bus->dhd);
+
+ if ((pktq_len(&bus->txq) >= FCHI) && dhd_doflow)
+ dhd_txflowcontrol(bus->dhd, 0, ON);
+
+#ifdef DHD_DEBUG
+ if (pktq_plen(&bus->txq, prec) > qcount[prec])
+ qcount[prec] = pktq_plen(&bus->txq, prec);
+#endif
+ /* Schedule DPC if needed to send queued packet(s) */
+ if (dhd_deferred_tx && !bus->dpc_sched) {
+ bus->dpc_sched = TRUE;
+ dhd_sched_dpc(bus->dhd);
+ }
+ } else {
+ /* Lock: we're about to use shared data/code (and SDIO) */
+ dhd_os_sdlock(bus->dhd);
+
+ /* Otherwise, send it now */
+ BUS_WAKE(bus);
+ /* Make sure back plane ht clk is on, no pending allowed */
+ dhdsdio_clkctl(bus, CLK_AVAIL, TRUE);
+
+#ifndef SDTEST
+ DHD_TRACE(("%s: calling txpkt\n", __FUNCTION__));
+ ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE);
+#else
+ ret = dhdsdio_txpkt(bus, pkt,
+ (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), TRUE);
+#endif
+ if (ret)
+ bus->dhd->tx_errors++;
+ else
+ bus->dhd->dstats.tx_bytes += datalen;
+
+ if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+ bus->activity = FALSE;
+ dhdsdio_clkctl(bus, CLK_NONE, TRUE);
+ }
+
+ dhd_os_sdunlock(bus->dhd);
+ }
+
+
+ return ret;
+}
+
+static uint
+dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes)
+{
+ void *pkt;
+ uint32 intstatus = 0;
+ uint retries = 0;
+ int ret = 0, prec_out;
+ uint cnt = 0;
+ uint datalen;
+ uint8 tx_prec_map;
+
+ dhd_pub_t *dhd = bus->dhd;
+ sdpcmd_regs_t *regs = bus->regs;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ tx_prec_map = ~bus->flowcontrol;
+
+ /* Send frames until the limit or some other event */
+ for (cnt = 0; (cnt < maxframes) && DATAOK(bus); cnt++) {
+ dhd_os_sdlock_txq(bus->dhd);
+ if ((pkt = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out)) == NULL) {
+ dhd_os_sdunlock_txq(bus->dhd);
+ break;
+ }
+ dhd_os_sdunlock_txq(bus->dhd);
+ datalen = PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN;
+
+#ifndef SDTEST
+ ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE);
+#else
+ ret = dhdsdio_txpkt(bus, pkt,
+ (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), TRUE);
+#endif
+ if (ret)
+ bus->dhd->tx_errors++;
+ else
+ bus->dhd->dstats.tx_bytes += datalen;
+
+ /* In poll mode, need to check for other events */
+ if (!bus->intr && cnt)
+ {
+ /* Check device status, signal pending interrupt */
+ R_SDREG(intstatus, &regs->intstatus, retries);
+ bus->f2txdata++;
+ if (bcmsdh_regfail(bus->sdh))
+ break;
+ if (intstatus & bus->hostintmask)
+ bus->ipend = TRUE;
+ }
+ }
+
+ /* Deflow-control stack if needed */
+ if (dhd_doflow && dhd->up && (dhd->busstate == DHD_BUS_DATA) &&
+ dhd->txoff && (pktq_len(&bus->txq) < FCLOW))
+ dhd_txflowcontrol(dhd, 0, OFF);
+
+ return cnt;
+}
+
+int
+dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen)
+{
+ uint8 *frame;
+ uint16 len;
+ uint32 swheader;
+ uint retries = 0;
+ bcmsdh_info_t *sdh = bus->sdh;
+ uint8 doff = 0;
+ int ret = -1;
+ int i;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (bus->dhd->dongle_reset)
+ return -EIO;
+
+ /* Back the pointer to make a room for bus header */
+ frame = msg - SDPCM_HDRLEN;
+ len = (msglen += SDPCM_HDRLEN);
+
+ /* Add alignment padding (optional for ctl frames) */
+ if (dhd_alignctl) {
+ if ((doff = ((uintptr)frame % DHD_SDALIGN))) {
+ frame -= doff;
+ len += doff;
+ msglen += doff;
+ bzero(frame, doff + SDPCM_HDRLEN);
+ }
+ ASSERT(doff < DHD_SDALIGN);
+ }
+ doff += SDPCM_HDRLEN;
+
+ /* Round send length to next SDIO block */
+ if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
+ uint16 pad = bus->blocksize - (len % bus->blocksize);
+ if ((pad <= bus->roundup) && (pad < bus->blocksize))
+ len += pad;
+ } else if (len % DHD_SDALIGN) {
+ len += DHD_SDALIGN - (len % DHD_SDALIGN);
+ }
+
+ /* Satisfy length-alignment requirements */
+ if (forcealign && (len & (ALIGNMENT - 1)))
+ len = ROUNDUP(len, ALIGNMENT);
+
+ ASSERT(ISALIGNED((uintptr)frame, 2));
+
+
+ /* Need to lock here to protect txseq and SDIO tx calls */
+ dhd_os_sdlock(bus->dhd);
+
+ BUS_WAKE(bus);
+
+ /* Make sure backplane clock is on */
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+ /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
+ *(uint16*)frame = htol16((uint16)msglen);
+ *(((uint16*)frame) + 1) = htol16(~msglen);
+
+ /* Software tag: channel, sequence number, data offset */
+ swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK)
+ | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
+ htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN);
+ htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+
+ if (!DATAOK(bus)) {
+ DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n",
+ __FUNCTION__, bus->tx_max, bus->tx_seq));
+ bus->ctrl_frame_stat = TRUE;
+ /* Send from dpc */
+ bus->ctrl_frame_buf = frame;
+ bus->ctrl_frame_len = len;
+
+ dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat);
+
+ if (bus->ctrl_frame_stat == FALSE) {
+ DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__));
+ ret = 0;
+ } else {
+ DHD_INFO(("%s: ctrl_frame_stat == TRUE\n", __FUNCTION__));
+ ret = -1;
+ }
+ }
+
+ if (ret == -1) {
+#ifdef DHD_DEBUG
+ if (DHD_BYTES_ON() && DHD_CTL_ON()) {
+ prhex("Tx Frame", frame, len);
+ } else if (DHD_HDRS_ON()) {
+ prhex("TxHdr", frame, MIN(len, 16));
+ }
+#endif
+
+ do {
+ bus->ctrl_frame_stat = FALSE;
+ ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+ frame, len, NULL, NULL, NULL);
+ ASSERT(ret != BCME_PENDING);
+
+ if (ret < 0) {
+ /* On failure, abort the command and terminate the frame */
+ DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
+ __FUNCTION__, ret));
+ bus->tx_sderrs++;
+
+ bcmsdh_abort(sdh, SDIO_FUNC_2);
+
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL,
+ SFC_WF_TERM, NULL);
+ bus->f1regdata++;
+
+ for (i = 0; i < 3; i++) {
+ uint8 hi, lo;
+ hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCHI, NULL);
+ lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCLO, NULL);
+ bus->f1regdata += 2;
+ if ((hi == 0) && (lo == 0))
+ break;
+ }
+
+ }
+ if (ret == 0) {
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+ }
+ } while ((ret < 0) && retries++ < TXRETRIES);
+ }
+
+ if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+ bus->activity = FALSE;
+ dhdsdio_clkctl(bus, CLK_NONE, TRUE);
+ }
+
+ dhd_os_sdunlock(bus->dhd);
+
+ if (ret)
+ bus->dhd->tx_ctlerrs++;
+ else
+ bus->dhd->tx_ctlpkts++;
+
+ return ret ? -EIO : 0;
+}
+
+int
+dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen)
+{
+ int timeleft;
+ uint rxlen = 0;
+ bool pending;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (bus->dhd->dongle_reset)
+ return -EIO;
+
+ /* Wait until control frame is available */
+ timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, &pending);
+
+ dhd_os_sdlock(bus->dhd);
+ rxlen = bus->rxlen;
+ bcopy(bus->rxctl, msg, MIN(msglen, rxlen));
+ bus->rxlen = 0;
+ dhd_os_sdunlock(bus->dhd);
+
+ if (rxlen) {
+ DHD_CTL(("%s: resumed on rxctl frame, got %d expected %d\n",
+ __FUNCTION__, rxlen, msglen));
+ } else if (timeleft == 0) {
+ DHD_ERROR(("%s: resumed on timeout\n", __FUNCTION__));
+#ifdef DHD_DEBUG_TRAP
+ dhd_os_sdlock(bus->dhd);
+ dhdsdio_checkdied(bus, NULL, 0);
+ dhd_os_sdunlock(bus->dhd);
+#endif /* DHD_DEBUG_TRAP */
+ } else if (pending == TRUE) {
+ DHD_CTL(("%s: cancelled\n", __FUNCTION__));
+ return -ERESTARTSYS;
+ } else {
+ DHD_CTL(("%s: resumed for unknown reason?\n", __FUNCTION__));
+#ifdef DHD_DEBUG_TRAP
+ dhd_os_sdlock(bus->dhd);
+ dhdsdio_checkdied(bus, NULL, 0);
+ dhd_os_sdunlock(bus->dhd);
+#endif /* DHD_DEBUG_TRAP */
+ }
+
+ if (rxlen)
+ bus->dhd->rx_ctlpkts++;
+ else
+ bus->dhd->rx_ctlerrs++;
+
+ return rxlen ? (int)rxlen : -ETIMEDOUT;
+}
+
+/* IOVar table */
+enum {
+ IOV_INTR = 1,
+ IOV_POLLRATE,
+ IOV_SDREG,
+ IOV_SBREG,
+ IOV_SDCIS,
+ IOV_MEMBYTES,
+ IOV_MEMSIZE,
+#ifdef DHD_DEBUG_TRAP
+ IOV_CHECKDIED,
+#endif
+ IOV_DOWNLOAD,
+ IOV_FORCEEVEN,
+ IOV_SDIOD_DRIVE,
+ IOV_READAHEAD,
+ IOV_SDRXCHAIN,
+ IOV_ALIGNCTL,
+ IOV_SDALIGN,
+ IOV_DEVRESET,
+ IOV_CPU,
+#ifdef SDTEST
+ IOV_PKTGEN,
+ IOV_EXTLOOP,
+#endif /* SDTEST */
+ IOV_SPROM,
+ IOV_TXBOUND,
+ IOV_RXBOUND,
+ IOV_TXMINMAX,
+ IOV_IDLETIME,
+ IOV_IDLECLOCK,
+ IOV_SD1IDLE,
+ IOV_SLEEP,
+ IOV_VARS
+};
+
+const bcm_iovar_t dhdsdio_iovars[] = {
+ {"intr", IOV_INTR, 0, IOVT_BOOL, 0 },
+ {"sleep", IOV_SLEEP, 0, IOVT_BOOL, 0 },
+ {"pollrate", IOV_POLLRATE, 0, IOVT_UINT32, 0 },
+ {"idletime", IOV_IDLETIME, 0, IOVT_INT32, 0 },
+ {"idleclock", IOV_IDLECLOCK, 0, IOVT_INT32, 0 },
+ {"sd1idle", IOV_SD1IDLE, 0, IOVT_BOOL, 0 },
+ {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int) },
+ {"memsize", IOV_MEMSIZE, 0, IOVT_UINT32, 0 },
+ {"download", IOV_DOWNLOAD, 0, IOVT_BOOL, 0 },
+ {"vars", IOV_VARS, 0, IOVT_BUFFER, 0 },
+ {"sdiod_drive", IOV_SDIOD_DRIVE, 0, IOVT_UINT32, 0 },
+ {"readahead", IOV_READAHEAD, 0, IOVT_BOOL, 0 },
+ {"sdrxchain", IOV_SDRXCHAIN, 0, IOVT_BOOL, 0 },
+ {"alignctl", IOV_ALIGNCTL, 0, IOVT_BOOL, 0 },
+ {"sdalign", IOV_SDALIGN, 0, IOVT_BOOL, 0 },
+ {"devreset", IOV_DEVRESET, 0, IOVT_BOOL, 0 },
+#ifdef DHD_DEBUG
+ {"sdreg", IOV_SDREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
+ {"sbreg", IOV_SBREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
+ {"sd_cis", IOV_SDCIS, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN },
+ {"forcealign", IOV_FORCEEVEN, 0, IOVT_BOOL, 0 },
+ {"txbound", IOV_TXBOUND, 0, IOVT_UINT32, 0 },
+ {"rxbound", IOV_RXBOUND, 0, IOVT_UINT32, 0 },
+ {"txminmax", IOV_TXMINMAX, 0, IOVT_UINT32, 0 },
+ {"cpu", IOV_CPU, 0, IOVT_BOOL, 0 },
+#endif /* DHD_DEBUG */
+#ifdef DHD_DEBUG_TRAP
+ {"checkdied", IOV_CHECKDIED, 0, IOVT_BUFFER, 0 },
+#endif /* DHD_DEBUG_TRAP */
+#ifdef SDTEST
+ {"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0 },
+ {"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(dhd_pktgen_t) },
+#endif /* SDTEST */
+
+ {NULL, 0, 0, 0, 0 }
+};
+
+static void
+dhd_dump_pct(struct bcmstrbuf *strbuf, char *desc, uint num, uint div)
+{
+ uint q1, q2;
+
+ if (!div) {
+ bcm_bprintf(strbuf, "%s N/A", desc);
+ } else {
+ q1 = num / div;
+ q2 = (100 * (num - (q1 * div))) / div;
+ bcm_bprintf(strbuf, "%s %d.%02d", desc, q1, q2);
+ }
+}
+
+void
+dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
+{
+ dhd_bus_t *bus = dhdp->bus;
+
+ bcm_bprintf(strbuf, "Bus SDIO structure:\n");
+ bcm_bprintf(strbuf, "hostintmask 0x%08x intstatus 0x%08x sdpcm_ver %d\n",
+ bus->hostintmask, bus->intstatus, bus->sdpcm_ver);
+ bcm_bprintf(strbuf, "fcstate %d qlen %d tx_seq %d, max %d, rxskip %d rxlen %d rx_seq %d\n",
+ bus->fcstate, pktq_len(&bus->txq), bus->tx_seq, bus->tx_max, bus->rxskip,
+ bus->rxlen, bus->rx_seq);
+ bcm_bprintf(strbuf, "intr %d intrcount %d lastintrs %d spurious %d\n",
+ bus->intr, bus->intrcount, bus->lastintrs, bus->spurious);
+ bcm_bprintf(strbuf, "pollrate %d pollcnt %d regfails %d\n",
+ bus->pollrate, bus->pollcnt, bus->regfails);
+
+ bcm_bprintf(strbuf, "\nAdditional counters:\n");
+ bcm_bprintf(strbuf, "tx_sderrs %d fcqueued %d rxrtx %d rx_toolong %d rxc_errors %d\n",
+ bus->tx_sderrs, bus->fcqueued, bus->rxrtx, bus->rx_toolong,
+ bus->rxc_errors);
+ bcm_bprintf(strbuf, "rx_hdrfail %d badhdr %d badseq %d\n",
+ bus->rx_hdrfail, bus->rx_badhdr, bus->rx_badseq);
+ bcm_bprintf(strbuf, "fc_rcvd %d, fc_xoff %d, fc_xon %d\n",
+ bus->fc_rcvd, bus->fc_xoff, bus->fc_xon);
+ bcm_bprintf(strbuf, "rxglomfail %d, rxglomframes %d, rxglompkts %d\n",
+ bus->rxglomfail, bus->rxglomframes, bus->rxglompkts);
+ bcm_bprintf(strbuf, "f2rx (hdrs/data) %d (%d/%d), f2tx %d f1regs %d\n",
+ (bus->f2rxhdrs + bus->f2rxdata), bus->f2rxhdrs, bus->f2rxdata,
+ bus->f2txdata, bus->f1regdata);
+ {
+ dhd_dump_pct(strbuf, "\nRx: pkts/f2rd", bus->dhd->rx_packets,
+ (bus->f2rxhdrs + bus->f2rxdata));
+ dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->rx_packets, bus->f1regdata);
+ dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->rx_packets,
+ (bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata));
+ dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->rx_packets, bus->intrcount);
+ bcm_bprintf(strbuf, "\n");
+
+ dhd_dump_pct(strbuf, "Rx: glom pct", (100 * bus->rxglompkts),
+ bus->dhd->rx_packets);
+ dhd_dump_pct(strbuf, ", pkts/glom", bus->rxglompkts, bus->rxglomframes);
+ bcm_bprintf(strbuf, "\n");
+
+ dhd_dump_pct(strbuf, "Tx: pkts/f2wr", bus->dhd->tx_packets, bus->f2txdata);
+ dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->tx_packets, bus->f1regdata);
+ dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->tx_packets,
+ (bus->f2txdata + bus->f1regdata));
+ dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->tx_packets, bus->intrcount);
+ bcm_bprintf(strbuf, "\n");
+
+ dhd_dump_pct(strbuf, "Total: pkts/f2rw",
+ (bus->dhd->tx_packets + bus->dhd->rx_packets),
+ (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata));
+ dhd_dump_pct(strbuf, ", pkts/f1sd",
+ (bus->dhd->tx_packets + bus->dhd->rx_packets), bus->f1regdata);
+ dhd_dump_pct(strbuf, ", pkts/sd",
+ (bus->dhd->tx_packets + bus->dhd->rx_packets),
+ (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata));
+ dhd_dump_pct(strbuf, ", pkts/int",
+ (bus->dhd->tx_packets + bus->dhd->rx_packets), bus->intrcount);
+ bcm_bprintf(strbuf, "\n\n");
+ }
+
+#ifdef SDTEST
+ if (bus->pktgen_count) {
+ bcm_bprintf(strbuf, "pktgen config and count:\n");
+ bcm_bprintf(strbuf, "freq %d count %d print %d total %d min %d len %d\n",
+ bus->pktgen_freq, bus->pktgen_count, bus->pktgen_print,
+ bus->pktgen_total, bus->pktgen_minlen, bus->pktgen_maxlen);
+ bcm_bprintf(strbuf, "send attempts %d rcvd %d fail %d\n",
+ bus->pktgen_sent, bus->pktgen_rcvd, bus->pktgen_fail);
+ }
+#endif /* SDTEST */
+#ifdef DHD_DEBUG
+ bcm_bprintf(strbuf, "dpc_sched %d host interrupt%spending\n",
+ bus->dpc_sched, (bcmsdh_intr_pending(bus->sdh) ? " " : " not "));
+ bcm_bprintf(strbuf, "blocksize %d roundup %d\n", bus->blocksize, bus->roundup);
+#endif /* DHD_DEBUG */
+ bcm_bprintf(strbuf, "clkstate %d activity %d idletime %d idlecount %d sleeping %d\n",
+ bus->clkstate, bus->activity, bus->idletime, bus->idlecount, bus->sleeping);
+}
+
+void
+dhd_bus_clearcounts(dhd_pub_t *dhdp)
+{
+ dhd_bus_t *bus = (dhd_bus_t *)dhdp->bus;
+
+ bus->intrcount = bus->lastintrs = bus->spurious = bus->regfails = 0;
+ bus->rxrtx = bus->rx_toolong = bus->rxc_errors = 0;
+ bus->rx_hdrfail = bus->rx_badhdr = bus->rx_badseq = 0;
+ bus->tx_sderrs = bus->fc_rcvd = bus->fc_xoff = bus->fc_xon = 0;
+ bus->rxglomfail = bus->rxglomframes = bus->rxglompkts = 0;
+ bus->f2rxhdrs = bus->f2rxdata = bus->f2txdata = bus->f1regdata = 0;
+}
+
+#ifdef SDTEST
+static int
+dhdsdio_pktgen_get(dhd_bus_t *bus, uint8 *arg)
+{
+ dhd_pktgen_t pktgen;
+
+ pktgen.version = DHD_PKTGEN_VERSION;
+ pktgen.freq = bus->pktgen_freq;
+ pktgen.count = bus->pktgen_count;
+ pktgen.print = bus->pktgen_print;
+ pktgen.total = bus->pktgen_total;
+ pktgen.minlen = bus->pktgen_minlen;
+ pktgen.maxlen = bus->pktgen_maxlen;
+ pktgen.numsent = bus->pktgen_sent;
+ pktgen.numrcvd = bus->pktgen_rcvd;
+ pktgen.numfail = bus->pktgen_fail;
+ pktgen.mode = bus->pktgen_mode;
+ pktgen.stop = bus->pktgen_stop;
+
+ bcopy(&pktgen, arg, sizeof(pktgen));
+
+ return 0;
+}
+
+static int
+dhdsdio_pktgen_set(dhd_bus_t *bus, uint8 *arg)
+{
+ dhd_pktgen_t pktgen;
+ uint oldcnt, oldmode;
+
+ bcopy(arg, &pktgen, sizeof(pktgen));
+ if (pktgen.version != DHD_PKTGEN_VERSION)
+ return BCME_BADARG;
+
+ oldcnt = bus->pktgen_count;
+ oldmode = bus->pktgen_mode;
+
+ bus->pktgen_freq = pktgen.freq;
+ bus->pktgen_count = pktgen.count;
+ bus->pktgen_print = pktgen.print;
+ bus->pktgen_total = pktgen.total;
+ bus->pktgen_minlen = pktgen.minlen;
+ bus->pktgen_maxlen = pktgen.maxlen;
+ bus->pktgen_mode = pktgen.mode;
+ bus->pktgen_stop = pktgen.stop;
+
+ bus->pktgen_tick = bus->pktgen_ptick = 0;
+ bus->pktgen_len = MAX(bus->pktgen_len, bus->pktgen_minlen);
+ bus->pktgen_len = MIN(bus->pktgen_len, bus->pktgen_maxlen);
+
+ /* Clear counts for a new pktgen (mode change, or was stopped) */
+ if (bus->pktgen_count && (!oldcnt || oldmode != bus->pktgen_mode))
+ bus->pktgen_sent = bus->pktgen_rcvd = bus->pktgen_fail = 0;
+
+ return 0;
+}
+#endif /* SDTEST */
+
+static int
+dhdsdio_membytes(dhd_bus_t *bus, bool write, uint32 address, uint8 *data, uint size)
+{
+ int bcmerror = 0;
+ uint32 sdaddr;
+ uint dsize;
+
+ /* Determine initial transfer parameters */
+ sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
+ if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
+ dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
+ else
+ dsize = size;
+
+ /* Set the backplane window to include the start address */
+ if ((bcmerror = dhdsdio_set_siaddr_window(bus, address))) {
+ DHD_ERROR(("%s: window change failed\n", __FUNCTION__));
+ goto xfer_done;
+ }
+
+ /* Do the transfer(s) */
+ while (size) {
+ DHD_INFO(("%s: %s %d bytes at offset 0x%08x in window 0x%08x\n",
+ __FUNCTION__, (write ? "write" : "read"), dsize, sdaddr,
+ (address & SBSDIO_SBWINDOW_MASK)));
+ if ((bcmerror = bcmsdh_rwdata(bus->sdh, write, sdaddr, data, dsize))) {
+ DHD_ERROR(("%s: membytes transfer failed\n", __FUNCTION__));
+ break;
+ }
+
+ /* Adjust for next transfer (if any) */
+ if ((size -= dsize)) {
+ data += dsize;
+ address += dsize;
+ if ((bcmerror = dhdsdio_set_siaddr_window(bus, address))) {
+ DHD_ERROR(("%s: window change failed\n", __FUNCTION__));
+ break;
+ }
+ sdaddr = 0;
+ dsize = MIN(SBSDIO_SB_OFT_ADDR_LIMIT, size);
+ }
+ }
+
+xfer_done:
+ /* Return the window to backplane enumeration space for core access */
+ if (dhdsdio_set_siaddr_window(bus, bcmsdh_cur_sbwad(bus->sdh))) {
+ DHD_ERROR(("%s: FAILED to set window back to 0x%x\n", __FUNCTION__,
+ bcmsdh_cur_sbwad(bus->sdh)));
+ }
+
+ return bcmerror;
+}
+
+#ifdef DHD_DEBUG_TRAP
+static int
+dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh)
+{
+ uint32 addr;
+ int rv;
+
+ /* Read last word in memory to determine address of sdpcm_shared structure */
+ if ((rv = dhdsdio_membytes(bus, FALSE, bus->ramsize - 4, (uint8 *)&addr, 4)) < 0)
+ return rv;
+
+ addr = ltoh32(addr);
+
+ DHD_INFO(("sdpcm_shared address 0x%08X\n", addr));
+
+ /*
+ * Check if addr is valid.
+ * NVRAM length at the end of memory should have been overwritten.
+ */
+ if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) {
+ DHD_ERROR(("%s: address (0x%08x) of sdpcm_shared invalid\n", __FUNCTION__, addr));
+ return BCME_ERROR;
+ }
+
+ /* Read hndrte_shared structure */
+ if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)sh, sizeof(sdpcm_shared_t))) < 0)
+ return rv;
+
+ /* Endianness */
+ sh->flags = ltoh32(sh->flags);
+ sh->trap_addr = ltoh32(sh->trap_addr);
+ sh->assert_exp_addr = ltoh32(sh->assert_exp_addr);
+ sh->assert_file_addr = ltoh32(sh->assert_file_addr);
+ sh->assert_line = ltoh32(sh->assert_line);
+ sh->console_addr = ltoh32(sh->console_addr);
+ sh->msgtrace_addr = ltoh32(sh->msgtrace_addr);
+
+ if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) {
+ DHD_ERROR(("%s: sdpcm_shared version %d in dhd "
+ "is different than sdpcm_shared version %d in dongle\n",
+ __FUNCTION__, SDPCM_SHARED_VERSION,
+ sh->flags & SDPCM_SHARED_VERSION_MASK));
+ return BCME_ERROR;
+ }
+
+ return BCME_OK;
+}
+
+static int
+dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size)
+{
+ int bcmerror = 0;
+ uint msize = 512;
+ char *mbuffer = NULL;
+ uint maxstrlen = 256;
+ char *str = NULL;
+ trap_t tr;
+ sdpcm_shared_t sdpcm_shared;
+ struct bcmstrbuf strbuf;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (data == NULL) {
+ /*
+ * Called after a rx ctrl timeout. "data" is NULL.
+ * allocate memory to trace the trap or assert.
+ */
+ size = msize;
+ mbuffer = data = MALLOC(bus->dhd->osh, msize);
+ if (mbuffer == NULL) {
+ DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, msize));
+ bcmerror = BCME_NOMEM;
+ goto done;
+ }
+ }
+
+ if ((str = MALLOC(bus->dhd->osh, maxstrlen)) == NULL) {
+ DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, maxstrlen));
+ bcmerror = BCME_NOMEM;
+ goto done;
+ }
+
+ if ((bcmerror = dhdsdio_readshared(bus, &sdpcm_shared)) < 0)
+ goto done;
+
+ bcm_binit(&strbuf, data, size);
+
+ bcm_bprintf(&strbuf, "msgtrace address : 0x%08X\nconsole address : 0x%08X\n",
+ sdpcm_shared.msgtrace_addr, sdpcm_shared.console_addr);
+
+ if ((sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
+ /* NOTE: Misspelled assert is intentional - DO NOT FIX.
+ * (Avoids conflict with real asserts for programmatic parsing of output.)
+ */
+ bcm_bprintf(&strbuf, "Assrt not built in dongle\n");
+ }
+
+ if ((sdpcm_shared.flags & (SDPCM_SHARED_ASSERT|SDPCM_SHARED_TRAP)) == 0) {
+ /* NOTE: Misspelled assert is intentional - DO NOT FIX.
+ * (Avoids conflict with real asserts for programmatic parsing of output.)
+ */
+ bcm_bprintf(&strbuf, "No trap%s in dongle",
+ (sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT)
+ ?"/assrt" :"");
+ } else {
+ if (sdpcm_shared.flags & SDPCM_SHARED_ASSERT) {
+ /* Download assert */
+ bcm_bprintf(&strbuf, "Dongle assert");
+ if (sdpcm_shared.assert_exp_addr != 0) {
+ str[0] = '\0';
+ if ((bcmerror = dhdsdio_membytes(bus, FALSE,
+ sdpcm_shared.assert_exp_addr,
+ (uint8 *)str, maxstrlen)) < 0)
+ goto done;
+
+ str[maxstrlen - 1] = '\0';
+ bcm_bprintf(&strbuf, " expr \"%s\"", str);
+ }
+
+ if (sdpcm_shared.assert_file_addr != 0) {
+ str[0] = '\0';
+ if ((bcmerror = dhdsdio_membytes(bus, FALSE,
+ sdpcm_shared.assert_file_addr,
+ (uint8 *)str, maxstrlen)) < 0)
+ goto done;
+
+ str[maxstrlen - 1] = '\0';
+ bcm_bprintf(&strbuf, " file \"%s\"", str);
+ }
+
+ bcm_bprintf(&strbuf, " line %d ", sdpcm_shared.assert_line);
+ }
+
+ if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
+ if ((bcmerror = dhdsdio_membytes(bus, FALSE,
+ sdpcm_shared.trap_addr,
+ (uint8*)&tr, sizeof(trap_t))) < 0)
+ goto done;
+
+ bcm_bprintf(&strbuf,
+ "Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x,"
+ "lp 0x%x, rpc 0x%x Trap offset 0x%x, "
+ "r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n",
+ tr.type, tr.epc, tr.cpsr, tr.spsr, tr.r13, tr.r14, tr.pc,
+ sdpcm_shared.trap_addr,
+ tr.r0, tr.r1, tr.r2, tr.r3, tr.r4, tr.r5, tr.r6, tr.r7);
+ }
+ }
+
+ if (sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP)) {
+ DHD_ERROR(("%s: %s\n", __FUNCTION__, strbuf.origbuf));
+ }
+
+ if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
+ /* Mem dump to a file on device */
+ dhdsdio_mem_dump(bus);
+ }
+
+done:
+ if (mbuffer)
+ MFREE(bus->dhd->osh, mbuffer, msize);
+ if (str)
+ MFREE(bus->dhd->osh, str, maxstrlen);
+
+ return bcmerror;
+}
+
+static int
+dhdsdio_mem_dump(dhd_bus_t *bus)
+{
+ int ret = 0;
+ int size; /* Full mem size */
+ int start = 0; /* Start address */
+ int read_size = 0; /* Read size of each iteration */
+ uint8 *buf = NULL, *databuf = NULL;
+
+ /* Get full mem size */
+ size = bus->ramsize;
+ buf = MALLOC(bus->dhd->osh, size);
+ if (!buf) {
+ printf("%s: Out of memory (%d bytes)\n", __FUNCTION__, size);
+ return -1;
+ }
+
+ /* Read mem content */
+ printf("Dump dongle memory");
+ databuf = buf;
+ while (size)
+ {
+ read_size = MIN(MEMBLOCK, size);
+ if ((ret = dhdsdio_membytes(bus, FALSE, start, databuf, read_size)))
+ {
+ printf("%s: Error membytes %d\n", __FUNCTION__, ret);
+ if (buf) {
+ MFREE(bus->dhd->osh, buf, size);
+ }
+ return -1;
+ }
+ printf(".");
+
+ /* Decrement size and increment start address */
+ size -= read_size;
+ start += read_size;
+ databuf += read_size;
+ }
+ printf("Done\n");
+
+#ifdef DHD_DEBUG
+ /* free buf before return !!! */
+ if (write_to_file(bus->dhd, buf, bus->ramsize))
+ {
+ printf("%s: Error writing to files\n", __FUNCTION__);
+ return -1;
+ }
+ /* buf free handled in write_to_file, not here */
+#else
+ MFREE(bus->dhd->osh, buf, size);
+#endif
+ return 0;
+}
+#endif /* DHD_DEBUG_TRAP */
+
+#ifdef DHD_DEBUG
+#define CONSOLE_LINE_MAX 192
+
+static int
+dhdsdio_readconsole(dhd_bus_t *bus)
+{
+ dhd_console_t *c = &bus->console;
+ uint8 line[CONSOLE_LINE_MAX], ch;
+ uint32 n, idx, addr;
+ int rv;
+
+ /* Don't do anything until FWREADY updates console address */
+ if (bus->console_addr == 0)
+ return 0;
+
+ /* Read console log struct */
+ addr = bus->console_addr + OFFSETOF(hndrte_cons_t, log);
+ if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&c->log, sizeof(c->log))) < 0)
+ return rv;
+
+ /* Allocate console buffer (one time only) */
+ if (c->buf == NULL) {
+ c->bufsize = ltoh32(c->log.buf_size);
+ if ((c->buf = MALLOC(bus->dhd->osh, c->bufsize)) == NULL)
+ return BCME_NOMEM;
+ }
+
+ idx = ltoh32(c->log.idx);
+
+ /* Protect against corrupt value */
+ if (idx > c->bufsize)
+ return BCME_ERROR;
+
+ /* Skip reading the console buffer if the index pointer has not moved */
+ if (idx == c->last)
+ return BCME_OK;
+
+ /* Read the console buffer */
+ addr = ltoh32(c->log.buf);
+ if ((rv = dhdsdio_membytes(bus, FALSE, addr, c->buf, c->bufsize)) < 0)
+ return rv;
+
+ while (c->last != idx) {
+ for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
+ if (c->last == idx) {
+ /* This would output a partial line. Instead, back up
+ * the buffer pointer and output this line next time around.
+ */
+ if (c->last >= n)
+ c->last -= n;
+ else
+ c->last = c->bufsize - n;
+ goto break2;
+ }
+ ch = c->buf[c->last];
+ c->last = (c->last + 1) % c->bufsize;
+ if (ch == '\n')
+ break;
+ line[n] = ch;
+ }
+
+ if (n > 0) {
+ if (line[n - 1] == '\r')
+ n--;
+ line[n] = 0;
+ printf("CONSOLE: %s\n", line);
+ }
+ }
+break2:
+
+ return BCME_OK;
+}
+#endif /* DHD_DEBUG */
+
+int
+dhdsdio_downloadvars(dhd_bus_t *bus, void *arg, int len)
+{
+ int bcmerror = BCME_OK;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ /* Basic sanity checks */
+ if (bus->dhd->up) {
+ bcmerror = BCME_NOTDOWN;
+ goto err;
+ }
+ if (!len) {
+ bcmerror = BCME_BUFTOOSHORT;
+ goto err;
+ }
+
+ /* Free the old ones and replace with passed variables */
+ if (bus->vars)
+ MFREE(bus->dhd->osh, bus->vars, bus->varsz);
+
+ bus->vars = MALLOC(bus->dhd->osh, len);
+ bus->varsz = bus->vars ? len : 0;
+ if (bus->vars == NULL) {
+ bcmerror = BCME_NOMEM;
+ goto err;
+ }
+
+ /* Copy the passed variables, which should include the terminating double-null */
+ bcopy(arg, bus->vars, bus->varsz);
+err:
+ return bcmerror;
+}
+
+static int
+dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const char *name,
+ void *params, int plen, void *arg, int len, int val_size)
+{
+ int bcmerror = 0;
+ int32 int_val = 0;
+ bool bool_val = 0;
+
+ DHD_TRACE(("%s: Enter, action %d name %s params %p plen %d arg %p len %d val_size %d\n",
+ __FUNCTION__, actionid, name, params, plen, arg, len, val_size));
+
+ if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0)
+ goto exit;
+
+ if (plen >= (int)sizeof(int_val))
+ bcopy(params, &int_val, sizeof(int_val));
+
+ bool_val = (int_val != 0) ? TRUE : FALSE;
+
+
+ /* Some ioctls use the bus */
+ dhd_os_sdlock(bus->dhd);
+
+ /* Check if dongle is in reset. If so, only allow DEVRESET iovars */
+ if (bus->dhd->dongle_reset && !(actionid == IOV_SVAL(IOV_DEVRESET) ||
+ actionid == IOV_GVAL(IOV_DEVRESET))) {
+ bcmerror = BCME_NOTREADY;
+ goto exit;
+ }
+
+ /* Handle sleep stuff before any clock mucking */
+ if (vi->varid == IOV_SLEEP) {
+ if (IOV_ISSET(actionid)) {
+ bcmerror = dhdsdio_bussleep(bus, bool_val);
+ } else {
+ int_val = (int32)bus->sleeping;
+ bcopy(&int_val, arg, val_size);
+ }
+ goto exit;
+ }
+
+ /* Request clock to allow SDIO accesses */
+ if (!bus->dhd->dongle_reset) {
+ BUS_WAKE(bus);
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+ }
+
+ switch (actionid) {
+ case IOV_GVAL(IOV_INTR):
+ int_val = (int32)bus->intr;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_INTR):
+ bus->intr = bool_val;
+ bus->intdis = FALSE;
+ if (bus->dhd->up) {
+ if (bus->intr) {
+ DHD_INTR(("%s: enable SDIO device interrupts\n", __FUNCTION__));
+ bcmsdh_intr_enable(bus->sdh);
+ } else {
+ DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__));
+ bcmsdh_intr_disable(bus->sdh);
+ }
+ }
+ break;
+
+ case IOV_GVAL(IOV_POLLRATE):
+ int_val = (int32)bus->pollrate;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_POLLRATE):
+ bus->pollrate = (uint)int_val;
+ bus->poll = (bus->pollrate != 0);
+ break;
+
+ case IOV_GVAL(IOV_IDLETIME):
+ int_val = bus->idletime;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_IDLETIME):
+ if ((int_val < 0) && (int_val != DHD_IDLE_IMMEDIATE)) {
+ bcmerror = BCME_BADARG;
+ } else {
+ bus->idletime = int_val;
+ }
+ break;
+
+ case IOV_GVAL(IOV_IDLECLOCK):
+ int_val = (int32)bus->idleclock;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_IDLECLOCK):
+ bus->idleclock = int_val;
+ break;
+
+ case IOV_GVAL(IOV_SD1IDLE):
+ int_val = (int32)sd1idle;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_SD1IDLE):
+ sd1idle = bool_val;
+ break;
+
+
+ case IOV_SVAL(IOV_MEMBYTES):
+ case IOV_GVAL(IOV_MEMBYTES):
+ {
+ uint32 address;
+ uint size, dsize;
+ uint8 *data;
+
+ bool set = (actionid == IOV_SVAL(IOV_MEMBYTES));
+
+ ASSERT(plen >= 2*sizeof(int));
+
+ address = (uint32)int_val;
+ bcopy((char *)params + sizeof(int_val), &int_val, sizeof(int_val));
+ size = (uint)int_val;
+
+ /* Do some validation */
+ dsize = set ? plen - (2 * sizeof(int)) : len;
+ if (dsize < size) {
+ DHD_ERROR(("%s: error on %s membytes, addr 0x%08x size %d dsize %d\n",
+ __FUNCTION__, (set ? "set" : "get"), address, size, dsize));
+ bcmerror = BCME_BADARG;
+ break;
+ }
+
+ DHD_INFO(("%s: Request to %s %d bytes at address 0x%08x\n", __FUNCTION__,
+ (set ? "write" : "read"), size, address));
+
+ /* If we know about SOCRAM, check for a fit */
+ if ((bus->orig_ramsize) &&
+ ((address > bus->orig_ramsize) || (address + size > bus->orig_ramsize))) {
+ DHD_ERROR(("%s: ramsize 0x%08x doesn't have %d bytes at 0x%08x\n",
+ __FUNCTION__, bus->orig_ramsize, size, address));
+ bcmerror = BCME_BADARG;
+ break;
+ }
+
+ /* Generate the actual data pointer */
+ data = set ? (uint8*)params + 2 * sizeof(int): (uint8*)arg;
+
+ /* Call to do the transfer */
+ bcmerror = dhdsdio_membytes(bus, set, address, data, size);
+
+ break;
+ }
+
+ case IOV_GVAL(IOV_MEMSIZE):
+ int_val = (int32)bus->ramsize;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_GVAL(IOV_SDIOD_DRIVE):
+ int_val = (int32)dhd_sdiod_drive_strength;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_SDIOD_DRIVE):
+ dhd_sdiod_drive_strength = int_val;
+ si_sdiod_drive_strength_init(bus->sih, bus->dhd->osh, dhd_sdiod_drive_strength);
+ break;
+
+ case IOV_SVAL(IOV_DOWNLOAD):
+ bcmerror = dhdsdio_download_state(bus, bool_val);
+ break;
+
+ case IOV_SVAL(IOV_VARS):
+ bcmerror = dhdsdio_downloadvars(bus, arg, len);
+ break;
+
+ case IOV_GVAL(IOV_READAHEAD):
+ int_val = (int32)dhd_readahead;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_READAHEAD):
+ if (bool_val && !dhd_readahead)
+ bus->nextlen = 0;
+ dhd_readahead = bool_val;
+ break;
+
+ case IOV_GVAL(IOV_SDRXCHAIN):
+ int_val = (int32)bus->use_rxchain;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_SDRXCHAIN):
+ if (bool_val && !bus->sd_rxchain)
+ bcmerror = BCME_UNSUPPORTED;
+ else
+ bus->use_rxchain = bool_val;
+ break;
+ case IOV_GVAL(IOV_ALIGNCTL):
+ int_val = (int32)dhd_alignctl;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_ALIGNCTL):
+ dhd_alignctl = bool_val;
+ break;
+
+ case IOV_GVAL(IOV_SDALIGN):
+ int_val = DHD_SDALIGN;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+#ifdef DHD_DEBUG
+ case IOV_GVAL(IOV_VARS):
+ if (bus->varsz < (uint)len)
+ bcopy(bus->vars, arg, bus->varsz);
+ else
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+#endif /* DHD_DEBUG */
+
+#ifdef DHD_DEBUG
+ case IOV_GVAL(IOV_SDREG):
+ {
+ sdreg_t *sd_ptr;
+ uint32 addr, size;
+
+ sd_ptr = (sdreg_t *)params;
+
+ addr = (uintptr)bus->regs + sd_ptr->offset;
+ size = sd_ptr->func;
+ int_val = (int32)bcmsdh_reg_read(bus->sdh, addr, size);
+ if (bcmsdh_regfail(bus->sdh))
+ bcmerror = BCME_SDIO_ERROR;
+ bcopy(&int_val, arg, sizeof(int32));
+ break;
+ }
+
+ case IOV_SVAL(IOV_SDREG):
+ {
+ sdreg_t *sd_ptr;
+ uint32 addr, size;
+
+ sd_ptr = (sdreg_t *)params;
+
+ addr = (uintptr)bus->regs + sd_ptr->offset;
+ size = sd_ptr->func;
+ bcmsdh_reg_write(bus->sdh, addr, size, sd_ptr->value);
+ if (bcmsdh_regfail(bus->sdh))
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+
+ /* Same as above, but offset is not backplane (not SDIO core) */
+ case IOV_GVAL(IOV_SBREG):
+ {
+ sdreg_t sdreg;
+ uint32 addr, size;
+
+ bcopy(params, &sdreg, sizeof(sdreg));
+
+ addr = SI_ENUM_BASE + sdreg.offset;
+ size = sdreg.func;
+ int_val = (int32)bcmsdh_reg_read(bus->sdh, addr, size);
+ if (bcmsdh_regfail(bus->sdh))
+ bcmerror = BCME_SDIO_ERROR;
+ bcopy(&int_val, arg, sizeof(int32));
+ break;
+ }
+
+ case IOV_SVAL(IOV_SBREG):
+ {
+ sdreg_t sdreg;
+ uint32 addr, size;
+
+ bcopy(params, &sdreg, sizeof(sdreg));
+
+ addr = SI_ENUM_BASE + sdreg.offset;
+ size = sdreg.func;
+ bcmsdh_reg_write(bus->sdh, addr, size, sdreg.value);
+ if (bcmsdh_regfail(bus->sdh))
+ bcmerror = BCME_SDIO_ERROR;
+ break;
+ }
+
+ case IOV_GVAL(IOV_SDCIS):
+ {
+ *(char *)arg = 0;
+
+ bcmstrcat(arg, "\nFunc 0\n");
+ bcmsdh_cis_read(bus->sdh, 0x10, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT);
+ bcmstrcat(arg, "\nFunc 1\n");
+ bcmsdh_cis_read(bus->sdh, 0x11, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT);
+ bcmstrcat(arg, "\nFunc 2\n");
+ bcmsdh_cis_read(bus->sdh, 0x12, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT);
+ break;
+ }
+
+ case IOV_GVAL(IOV_FORCEEVEN):
+ int_val = (int32)forcealign;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_FORCEEVEN):
+ forcealign = bool_val;
+ break;
+
+ case IOV_GVAL(IOV_TXBOUND):
+ int_val = (int32)dhd_txbound;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_TXBOUND):
+ dhd_txbound = (uint)int_val;
+ break;
+
+ case IOV_GVAL(IOV_RXBOUND):
+ int_val = (int32)dhd_rxbound;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_RXBOUND):
+ dhd_rxbound = (uint)int_val;
+ break;
+
+ case IOV_GVAL(IOV_TXMINMAX):
+ int_val = (int32)dhd_txminmax;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_TXMINMAX):
+ dhd_txminmax = (uint)int_val;
+ break;
+
+
+
+#endif /* DHD_DEBUG */
+
+
+#ifdef SDTEST
+ case IOV_GVAL(IOV_EXTLOOP):
+ int_val = (int32)bus->ext_loop;
+ bcopy(&int_val, arg, val_size);
+ break;
+
+ case IOV_SVAL(IOV_EXTLOOP):
+ bus->ext_loop = bool_val;
+ break;
+
+ case IOV_GVAL(IOV_PKTGEN):
+ bcmerror = dhdsdio_pktgen_get(bus, arg);
+ break;
+
+ case IOV_SVAL(IOV_PKTGEN):
+ bcmerror = dhdsdio_pktgen_set(bus, arg);
+ break;
+#endif /* SDTEST */
+
+
+ case IOV_SVAL(IOV_DEVRESET):
+ DHD_TRACE(("%s: Called set IOV_DEVRESET=%d dongle_reset=%d busstate=%d\n",
+ __FUNCTION__, bool_val, bus->dhd->dongle_reset,
+ bus->dhd->busstate));
+
+ ASSERT(bus->dhd->osh);
+ /* ASSERT(bus->cl_devid); */
+
+ dhd_bus_devreset(bus->dhd, (uint8)bool_val);
+
+ break;
+
+ case IOV_GVAL(IOV_DEVRESET):
+ DHD_TRACE(("%s: Called get IOV_DEVRESET\n", __FUNCTION__));
+
+ /* Get its status */
+ int_val = (bool) bus->dhd->dongle_reset;
+ bcopy(&int_val, arg, val_size);
+
+ break;
+
+ default:
+ bcmerror = BCME_UNSUPPORTED;
+ break;
+ }
+
+exit:
+ if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+ bus->activity = FALSE;
+ dhdsdio_clkctl(bus, CLK_NONE, TRUE);
+ }
+
+ dhd_os_sdunlock(bus->dhd);
+
+ if (actionid == IOV_SVAL(IOV_DEVRESET) && bool_val == FALSE)
+ dhd_preinit_ioctls((dhd_pub_t *) bus->dhd);
+
+ return bcmerror;
+}
+
+static int
+dhdsdio_write_vars(dhd_bus_t *bus)
+{
+ int bcmerror = 0;
+ uint32 varsize;
+ uint32 varaddr;
+ uint8 *vbuffer;
+ uint32 varsizew;
+#ifdef DHD_DEBUG
+ char *nvram_ularray;
+#endif /* DHD_DEBUG */
+
+ /* Even if there are no vars are to be written, we still need to set the ramsize. */
+ varsize = bus->varsz ? ROUNDUP(bus->varsz, 4) : 0;
+ varaddr = (bus->ramsize - 4) - varsize;
+
+ if (bus->vars) {
+ vbuffer = (uint8 *)MALLOC(bus->dhd->osh, varsize);
+ if (!vbuffer)
+ return BCME_NOMEM;
+
+ bzero(vbuffer, varsize);
+ bcopy(bus->vars, vbuffer, bus->varsz);
+
+ /* Write the vars list */
+ bcmerror = dhdsdio_membytes(bus, TRUE, varaddr, vbuffer, varsize);
+#ifdef DHD_DEBUG
+ /* Verify NVRAM bytes */
+ DHD_INFO(("Compare NVRAM dl & ul; varsize=%d\n", varsize));
+ nvram_ularray = (char*)MALLOC(bus->dhd->osh, varsize);
+ if (!nvram_ularray)
+ return BCME_NOMEM;
+
+ /* Upload image to verify downloaded contents. */
+ memset(nvram_ularray, 0xaa, varsize);
+
+ /* Read the vars list to temp buffer for comparison */
+ bcmerror = dhdsdio_membytes(bus, FALSE, varaddr, nvram_ularray, varsize);
+ if (bcmerror) {
+ DHD_ERROR(("%s: error %d on reading %d nvram bytes at 0x%08x\n",
+ __FUNCTION__, bcmerror, varsize, varaddr));
+ }
+ /* Compare the org NVRAM with the one read from RAM */
+ if (memcmp(vbuffer, nvram_ularray, varsize)) {
+ DHD_ERROR(("%s: Downloaded NVRAM image is corrupted.\n", __FUNCTION__));
+ } else
+ DHD_ERROR(("%s: Download, Upload and compare of NVRAM succeeded.\n",
+ __FUNCTION__));
+
+ MFREE(bus->dhd->osh, nvram_ularray, varsize);
+#endif /* DHD_DEBUG */
+
+ MFREE(bus->dhd->osh, vbuffer, varsize);
+ }
+
+ /* adjust to the user specified RAM */
+ DHD_INFO(("Physical memory size: %d, usable memory size: %d\n",
+ bus->orig_ramsize, bus->ramsize));
+ DHD_INFO(("Vars are at %d, orig varsize is %d\n",
+ varaddr, varsize));
+ varsize = ((bus->orig_ramsize - 4) - varaddr);
+
+ /*
+ * Determine the length token:
+ * Varsize, converted to words, in lower 16-bits, checksum in upper 16-bits.
+ */
+ if (bcmerror) {
+ varsizew = 0;
+ } else {
+ varsizew = varsize / 4;
+ varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF);
+ varsizew = htol32(varsizew);
+ }
+
+ DHD_INFO(("New varsize is %d, length token=0x%08x\n", varsize, varsizew));
+
+ /* Write the length token to the last word */
+ bcmerror = dhdsdio_membytes(bus, TRUE, (bus->orig_ramsize - 4),
+ (uint8*)&varsizew, 4);
+
+ return bcmerror;
+}
+
+static int
+dhdsdio_download_state(dhd_bus_t *bus, bool enter)
+{
+ uint retries;
+ int bcmerror = 0;
+
+ /* To enter download state, disable ARM and reset SOCRAM.
+ * To exit download state, simply reset ARM (default is RAM boot).
+ */
+ if (enter) {
+
+ bus->alp_only = TRUE;
+
+ if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) &&
+ !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) {
+ DHD_ERROR(("%s: Failed to find ARM core!\n", __FUNCTION__));
+ bcmerror = BCME_ERROR;
+ goto fail;
+ }
+
+ si_core_disable(bus->sih, 0);
+ if (bcmsdh_regfail(bus->sdh)) {
+ bcmerror = BCME_SDIO_ERROR;
+ goto fail;
+ }
+
+ if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) {
+ DHD_ERROR(("%s: Failed to find SOCRAM core!\n", __FUNCTION__));
+ bcmerror = BCME_ERROR;
+ goto fail;
+ }
+
+ si_core_reset(bus->sih, 0, 0);
+ if (bcmsdh_regfail(bus->sdh)) {
+ DHD_ERROR(("%s: Failure trying reset SOCRAM core?\n", __FUNCTION__));
+ bcmerror = BCME_SDIO_ERROR;
+ goto fail;
+ }
+
+ /* Clear the top bit of memory */
+ if (bus->ramsize) {
+ uint32 zeros = 0;
+ dhdsdio_membytes(bus, TRUE, bus->ramsize - 4, (uint8*)&zeros, 4);
+ }
+ } else {
+ if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) {
+ DHD_ERROR(("%s: Failed to find SOCRAM core!\n", __FUNCTION__));
+ bcmerror = BCME_ERROR;
+ goto fail;
+ }
+
+ if (!si_iscoreup(bus->sih)) {
+ DHD_ERROR(("%s: SOCRAM core is down after reset?\n", __FUNCTION__));
+ bcmerror = BCME_ERROR;
+ goto fail;
+ }
+
+ if ((bcmerror = dhdsdio_write_vars(bus))) {
+ DHD_ERROR(("%s: no vars written to RAM\n", __FUNCTION__));
+ bcmerror = 0;
+ }
+
+ if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0) &&
+ !si_setcore(bus->sih, SDIOD_CORE_ID, 0)) {
+ DHD_ERROR(("%s: Can't change back to SDIO core?\n", __FUNCTION__));
+ bcmerror = BCME_ERROR;
+ goto fail;
+ }
+ W_SDREG(0xFFFFFFFF, &bus->regs->intstatus, retries);
+
+
+ if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) &&
+ !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) {
+ DHD_ERROR(("%s: Failed to find ARM core!\n", __FUNCTION__));
+ bcmerror = BCME_ERROR;
+ goto fail;
+ }
+
+ si_core_reset(bus->sih, 0, 0);
+ if (bcmsdh_regfail(bus->sdh)) {
+ DHD_ERROR(("%s: Failure trying to reset ARM core?\n", __FUNCTION__));
+ bcmerror = BCME_SDIO_ERROR;
+ goto fail;
+ }
+
+ /* Allow HT Clock now that the ARM is running. */
+ bus->alp_only = FALSE;
+
+ bus->dhd->busstate = DHD_BUS_LOAD;
+ }
+
+fail:
+ /* Always return to SDIOD core */
+ if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0))
+ si_setcore(bus->sih, SDIOD_CORE_ID, 0);
+
+ return bcmerror;
+}
+
+int
+dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name,
+ void *params, int plen, void *arg, int len, bool set)
+{
+ dhd_bus_t *bus = dhdp->bus;
+ const bcm_iovar_t *vi = NULL;
+ int bcmerror = 0;
+ int val_size;
+ uint32 actionid;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ ASSERT(name);
+ ASSERT(len >= 0);
+
+ /* Get MUST have return space */
+ ASSERT(set || (arg && len));
+
+ /* Set does NOT take qualifiers */
+ ASSERT(!set || (!params && !plen));
+
+ /* Look up var locally; if not found pass to host driver */
+ if ((vi = bcm_iovar_lookup(dhdsdio_iovars, name)) == NULL) {
+ dhd_os_sdlock(bus->dhd);
+
+ BUS_WAKE(bus);
+
+ /* Turn on clock in case SD command needs backplane */
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+ bcmerror = bcmsdh_iovar_op(bus->sdh, name, params, plen, arg, len, set);
+
+ /* Check for bus configuration changes of interest */
+
+ /* If it was divisor change, read the new one */
+ if (set && strcmp(name, "sd_divisor") == 0) {
+ if (bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0,
+ &bus->sd_divisor, sizeof(int32), FALSE) != BCME_OK) {
+ bus->sd_divisor = -1;
+ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, name));
+ } else {
+ DHD_INFO(("%s: noted %s update, value now %d\n",
+ __FUNCTION__, name, bus->sd_divisor));
+ }
+ }
+ /* If it was a mode change, read the new one */
+ if (set && strcmp(name, "sd_mode") == 0) {
+ if (bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0,
+ &bus->sd_mode, sizeof(int32), FALSE) != BCME_OK) {
+ bus->sd_mode = -1;
+ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, name));
+ } else {
+ DHD_INFO(("%s: noted %s update, value now %d\n",
+ __FUNCTION__, name, bus->sd_mode));
+ }
+ }
+ /* Similar check for blocksize change */
+ if (set && strcmp(name, "sd_blocksize") == 0) {
+ int32 fnum = 2;
+ if (bcmsdh_iovar_op(bus->sdh, "sd_blocksize", &fnum, sizeof(int32),
+ &bus->blocksize, sizeof(int32), FALSE) != BCME_OK) {
+ bus->blocksize = 0;
+ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_blocksize"));
+ } else {
+ DHD_INFO(("%s: noted %s update, value now %d\n",
+ __FUNCTION__, "sd_blocksize", bus->blocksize));
+ }
+ }
+ bus->roundup = MIN(max_roundup, bus->blocksize);
+
+ if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+ bus->activity = FALSE;
+ dhdsdio_clkctl(bus, CLK_NONE, TRUE);
+ }
+
+ dhd_os_sdunlock(bus->dhd);
+ goto exit;
+ }
+
+ DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__,
+ name, (set ? "set" : "get"), len, plen));
+
+ /* set up 'params' pointer in case this is a set command so that
+ * the convenience int and bool code can be common to set and get
+ */
+ if (params == NULL) {
+ params = arg;
+ plen = len;
+ }
+
+ if (vi->type == IOVT_VOID)
+ val_size = 0;
+ else if (vi->type == IOVT_BUFFER)
+ val_size = len;
+ else
+ /* all other types are integer sized */
+ val_size = sizeof(int);
+
+ actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+ bcmerror = dhdsdio_doiovar(bus, vi, actionid, name, params, plen, arg, len, val_size);
+
+exit:
+ return bcmerror;
+}
+
+void
+dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex)
+{
+ osl_t *osh = bus->dhd->osh;
+ uint32 local_hostintmask;
+ uint8 saveclk;
+ uint retries;
+ int err;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (enforce_mutex)
+ dhd_os_sdlock(bus->dhd);
+
+ BUS_WAKE(bus);
+
+ /* Change our idea of bus state */
+ bus->dhd->busstate = DHD_BUS_DOWN;
+
+ /* Enable clock for device interrupts */
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+ /* Disable and clear interrupts at the chip level also */
+ W_SDREG(0, &bus->regs->hostintmask, retries);
+ local_hostintmask = bus->hostintmask;
+ bus->hostintmask = 0;
+
+ /* Force clocks on backplane to be sure F2 interrupt propagates */
+ saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (!err) {
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_FORCE_HT), &err);
+ }
+ if (err) {
+ DHD_ERROR(("%s: Failed to force clock for F2: err %d\n", __FUNCTION__, err));
+ }
+
+ /* Turn off the bus (F2), free any pending packets */
+ DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__));
+ bcmsdh_intr_disable(bus->sdh);
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1, NULL);
+
+ /* Clear any pending interrupts now that F2 is disabled */
+ W_SDREG(local_hostintmask, &bus->regs->intstatus, retries);
+
+ /* Turn off the backplane clock (only) */
+ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+
+ /* Clear the data packet queues */
+ pktq_flush(osh, &bus->txq, TRUE);
+
+ /* Clear any held glomming stuff */
+ if (bus->glomd)
+ PKTFREE(osh, bus->glomd, FALSE);
+
+ if (bus->glom)
+ PKTFREE(osh, bus->glom, FALSE);
+
+ bus->glom = bus->glomd = NULL;
+
+ /* Clear rx control and wake any waiters */
+ bus->rxlen = 0;
+ dhd_os_ioctl_resp_wake(bus->dhd);
+
+ /* Reset some F2 state stuff */
+ bus->rxskip = FALSE;
+ bus->tx_seq = bus->rx_seq = 0;
+
+ if (enforce_mutex)
+ dhd_os_sdunlock(bus->dhd);
+}
+
+int
+dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex)
+{
+ dhd_bus_t *bus = dhdp->bus;
+ dhd_timeout_t tmo;
+ uint retries = 0;
+ uint8 ready, enable;
+ int err, ret = BCME_ERROR;
+ uint8 saveclk;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ ASSERT(bus->dhd);
+ if (!bus->dhd)
+ return BCME_OK;
+
+ if (enforce_mutex)
+ dhd_os_sdlock(bus->dhd);
+
+ /* Make sure backplane clock is on, needed to generate F2 interrupt */
+ err = dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+ if ((err != BCME_OK) || (bus->clkstate != CLK_AVAIL)) {
+ DHD_ERROR(("%s: Failed to set backplane clock: err %d\n", __FUNCTION__, err));
+ goto exit;
+ }
+
+ /* Force clocks on backplane to be sure F2 interrupt propagates */
+ saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (!err) {
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+ (saveclk | SBSDIO_FORCE_HT), &err);
+ }
+ if (err) {
+ DHD_ERROR(("%s: Failed to force clock for F2: err %d\n", __FUNCTION__, err));
+ goto exit;
+ }
+
+ /* Enable function 2 (frame transfers) */
+ W_SDREG((SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT),
+ &bus->regs->tosbmailboxdata, retries);
+ enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2);
+
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL);
+
+ /* Give the dongle some time to do its thing and set IOR2 */
+ dhd_timeout_start(&tmo, DHD_WAIT_F2RDY * 1000);
+
+ ready = 0;
+ while (ready != enable && !dhd_timeout_expired(&tmo))
+ ready = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IORDY, NULL);
+
+
+ DHD_INFO(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n",
+ __FUNCTION__, enable, ready, tmo.elapsed));
+
+
+ /* If F2 successfully enabled, set core and enable interrupts */
+ if (ready == enable) {
+ /* Make sure we're talking to the core. */
+ if (!(bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0)))
+ bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0);
+
+ /* Set up the interrupt mask and enable interrupts */
+ bus->hostintmask = HOSTINTMASK;
+ W_SDREG(bus->hostintmask, &bus->regs->hostintmask, retries);
+
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, (uint8)watermark, &err);
+
+ /* Set bus state according to enable result */
+ dhdp->busstate = DHD_BUS_DATA;
+
+ /* bcmsdh_intr_unmask(bus->sdh); */
+
+ bus->intdis = FALSE;
+ if (bus->intr) {
+ DHD_INTR(("%s: enable SDIO device interrupts\n", __FUNCTION__));
+ bcmsdh_intr_enable(bus->sdh);
+ } else {
+ DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__));
+ bcmsdh_intr_disable(bus->sdh);
+ }
+
+ }
+
+
+ else {
+ /* Disable F2 again */
+ enable = SDIO_FUNC_ENABLE_1;
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL);
+ }
+
+ /* Restore previous clock setting */
+ bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err);
+
+
+ /* If we didn't come up, turn off backplane clock */
+ if (dhdp->busstate != DHD_BUS_DATA)
+ dhdsdio_clkctl(bus, CLK_NONE, FALSE);
+
+ ret = BCME_OK;
+exit:
+ if (enforce_mutex)
+ dhd_os_sdunlock(bus->dhd);
+
+ return ret;
+}
+
+static void
+dhdsdio_rxfail(dhd_bus_t *bus, bool abort, bool rtx)
+{
+ bcmsdh_info_t *sdh = bus->sdh;
+ sdpcmd_regs_t *regs = bus->regs;
+ uint retries = 0;
+ uint16 lastrbc;
+ uint8 hi, lo;
+ int err;
+
+ DHD_ERROR(("%s: %sterminate frame%s\n", __FUNCTION__,
+ (abort ? "abort command, " : ""), (rtx ? ", send NAK" : "")));
+
+ if (abort) {
+ bcmsdh_abort(sdh, SDIO_FUNC_2);
+ }
+
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, SFC_RF_TERM, &err);
+ bus->f1regdata++;
+
+ /* Wait until the packet has been flushed (device/FIFO stable) */
+ for (lastrbc = retries = 0xffff; retries > 0; retries--) {
+ hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCHI, NULL);
+ lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCLO, NULL);
+ bus->f1regdata += 2;
+
+ if ((hi == 0) && (lo == 0))
+ break;
+
+ if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
+ DHD_ERROR(("%s: count growing: last 0x%04x now 0x%04x\n",
+ __FUNCTION__, lastrbc, ((hi << 8) + lo)));
+ }
+ lastrbc = (hi << 8) + lo;
+ }
+
+ if (!retries) {
+ DHD_ERROR(("%s: count never zeroed: last 0x%04x\n", __FUNCTION__, lastrbc));
+ } else {
+ DHD_INFO(("%s: flush took %d iterations\n", __FUNCTION__, (0xffff - retries)));
+ }
+
+ if (rtx) {
+ bus->rxrtx++;
+ W_SDREG(SMB_NAK, &regs->tosbmailbox, retries);
+ bus->f1regdata++;
+ if (retries <= retry_limit) {
+ bus->rxskip = TRUE;
+ }
+ }
+
+ /* Clear partial in any case */
+ bus->nextlen = 0;
+
+ /* If we can't reach the device, signal failure */
+ if (err || bcmsdh_regfail(sdh))
+ bus->dhd->busstate = DHD_BUS_DOWN;
+}
+
+static void
+dhdsdio_read_control(dhd_bus_t *bus, uint8 *hdr, uint len, uint doff)
+{
+ bcmsdh_info_t *sdh = bus->sdh;
+ uint rdlen, pad;
+
+ int sdret;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ /* Control data already received in aligned rxctl */
+ if ((bus->bus == SPI_BUS) && (!bus->usebufpool))
+ goto gotpkt;
+
+ ASSERT(bus->rxbuf);
+ /* Set rxctl for frame (w/optional alignment) */
+ bus->rxctl = bus->rxbuf;
+ if (dhd_alignctl) {
+ bus->rxctl += firstread;
+ if ((pad = ((uintptr)bus->rxctl % DHD_SDALIGN)))
+ bus->rxctl += (DHD_SDALIGN - pad);
+ bus->rxctl -= firstread;
+ }
+ ASSERT(bus->rxctl >= bus->rxbuf);
+
+ /* Copy the already-read portion over */
+ bcopy(hdr, bus->rxctl, firstread);
+ if (len <= firstread)
+ goto gotpkt;
+
+ /* Copy the full data pkt in gSPI case and process ioctl. */
+ if (bus->bus == SPI_BUS) {
+ bcopy(hdr, bus->rxctl, len);
+ goto gotpkt;
+ }
+
+ /* Raise rdlen to next SDIO block to avoid tail command */
+ rdlen = len - firstread;
+ if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
+ pad = bus->blocksize - (rdlen % bus->blocksize);
+ if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+ ((len + pad) < bus->dhd->maxctl))
+ rdlen += pad;
+ } else if (rdlen % DHD_SDALIGN) {
+ rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN);
+ }
+
+ /* Satisfy length-alignment requirements */
+ if (forcealign && (rdlen & (ALIGNMENT - 1)))
+ rdlen = ROUNDUP(rdlen, ALIGNMENT);
+
+ /* Drop if the read is too big or it exceeds our maximum */
+ if ((rdlen + firstread) > bus->dhd->maxctl) {
+ DHD_ERROR(("%s: %d-byte control read exceeds %d-byte buffer\n",
+ __FUNCTION__, rdlen, bus->dhd->maxctl));
+ bus->dhd->rx_errors++;
+ dhdsdio_rxfail(bus, FALSE, FALSE);
+ goto done;
+ }
+
+ if ((len - doff) > bus->dhd->maxctl) {
+ DHD_ERROR(("%s: %d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",
+ __FUNCTION__, len, (len - doff), bus->dhd->maxctl));
+ bus->dhd->rx_errors++; bus->rx_toolong++;
+ dhdsdio_rxfail(bus, FALSE, FALSE);
+ goto done;
+ }
+
+
+ /* Read remainder of frame body into the rxctl buffer */
+ sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+ (bus->rxctl + firstread), rdlen, NULL, NULL, NULL);
+ bus->f2rxdata++;
+ ASSERT(sdret != BCME_PENDING);
+
+ /* Control frame failures need retransmission */
+ if (sdret < 0) {
+ DHD_ERROR(("%s: read %d control bytes failed: %d\n", __FUNCTION__, rdlen, sdret));
+ bus->rxc_errors++; /* dhd.rx_ctlerrs is higher level */
+ dhdsdio_rxfail(bus, TRUE, TRUE);
+ goto done;
+ }
+
+gotpkt:
+
+#ifdef DHD_DEBUG
+ if (DHD_BYTES_ON() && DHD_CTL_ON()) {
+ prhex("RxCtrl", bus->rxctl, len);
+ }
+#endif
+
+ /* Point to valid data and indicate its length */
+ bus->rxctl += doff;
+ bus->rxlen = len - doff;
+
+done:
+ /* Awake any waiters */
+ dhd_os_ioctl_resp_wake(bus->dhd);
+}
+
+static uint8
+dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq)
+{
+ uint16 dlen, totlen;
+ uint8 *dptr, num = 0;
+
+ uint16 sublen, check;
+ void *pfirst, *plast, *pnext, *save_pfirst;
+ osl_t *osh = bus->dhd->osh;
+
+ int errcode;
+ uint8 chan, seq, doff, sfdoff;
+ uint8 txmax;
+
+ int ifidx = 0;
+ bool usechain = bus->use_rxchain;
+
+ /* If packets, issue read(s) and send up packet chain */
+ /* Return sequence numbers consumed? */
+
+ DHD_TRACE(("dhdsdio_rxglom: start: glomd %p glom %p\n", bus->glomd, bus->glom));
+
+ /* If there's a descriptor, generate the packet chain */
+ if (bus->glomd) {
+ dhd_os_sdlock_rxq(bus->dhd);
+
+ pfirst = plast = pnext = NULL;
+ dlen = (uint16)PKTLEN(osh, bus->glomd);
+ dptr = PKTDATA(osh, bus->glomd);
+ if (!dlen || (dlen & 1)) {
+ DHD_ERROR(("%s: bad glomd len (%d), ignore descriptor\n",
+ __FUNCTION__, dlen));
+ dlen = 0;
+ }
+
+ for (totlen = num = 0; dlen; num++) {
+ /* Get (and move past) next length */
+ sublen = ltoh16_ua(dptr);
+ dlen -= sizeof(uint16);
+ dptr += sizeof(uint16);
+ if ((sublen < SDPCM_HDRLEN) ||
+ ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
+ DHD_ERROR(("%s: descriptor len %d bad: %d\n",
+ __FUNCTION__, num, sublen));
+ pnext = NULL;
+ break;
+ }
+ if (sublen % DHD_SDALIGN) {
+ DHD_ERROR(("%s: sublen %d not a multiple of %d\n",
+ __FUNCTION__, sublen, DHD_SDALIGN));
+ usechain = FALSE;
+ }
+ totlen += sublen;
+
+ /* For last frame, adjust read len so total is a block multiple */
+ if (!dlen) {
+ sublen += (ROUNDUP(totlen, bus->blocksize) - totlen);
+ totlen = ROUNDUP(totlen, bus->blocksize);
+ }
+
+ /* Allocate/chain packet for next subframe */
+ if ((pnext = PKTGET(osh, sublen + DHD_SDALIGN, FALSE)) == NULL) {
+ DHD_ERROR(("%s: PKTGET failed, num %d len %d\n",
+ __FUNCTION__, num, sublen));
+ break;
+ }
+ ASSERT(!PKTLINK(pnext));
+ if (!pfirst) {
+ ASSERT(!plast);
+ pfirst = plast = pnext;
+ } else {
+ ASSERT(plast);
+ PKTSETNEXT(osh, plast, pnext);
+ plast = pnext;
+ }
+
+ /* Adhere to start alignment requirements */
+ PKTALIGN(osh, pnext, sublen, DHD_SDALIGN);
+ }
+
+ /* If all allocations succeeded, save packet chain in bus structure */
+ if (pnext) {
+ DHD_GLOM(("%s: allocated %d-byte packet chain for %d subframes\n",
+ __FUNCTION__, totlen, num));
+ if (DHD_GLOM_ON() && bus->nextlen) {
+ if (totlen != bus->nextlen) {
+ DHD_GLOM(("%s: glomdesc mismatch: nextlen %d glomdesc %d "
+ "rxseq %d\n", __FUNCTION__, bus->nextlen,
+ totlen, rxseq));
+ }
+ }
+ bus->glom = pfirst;
+ pfirst = pnext = NULL;
+ } else {
+ if (pfirst)
+ PKTFREE(osh, pfirst, FALSE);
+ bus->glom = NULL;
+ num = 0;
+ }
+
+ /* Done with descriptor packet */
+ PKTFREE(osh, bus->glomd, FALSE);
+ bus->glomd = NULL;
+ bus->nextlen = 0;
+
+ dhd_os_sdunlock_rxq(bus->dhd);
+ }
+
+ /* Ok -- either we just generated a packet chain, or had one from before */
+ if (bus->glom) {
+ if (DHD_GLOM_ON()) {
+ DHD_GLOM(("%s: attempt superframe read, packet chain:\n", __FUNCTION__));
+ for (pnext = bus->glom; pnext; pnext = PKTNEXT(osh, pnext)) {
+ DHD_GLOM((" %p: %p len 0x%04x (%d)\n",
+ pnext, (uint8*)PKTDATA(osh, pnext),
+ PKTLEN(osh, pnext), PKTLEN(osh, pnext)));
+ }
+ }
+
+ pfirst = bus->glom;
+ dlen = (uint16)pkttotlen(osh, pfirst);
+
+ /* Do an SDIO read for the superframe. Configurable iovar to
+ * read directly into the chained packet, or allocate a large
+ * packet and and copy into the chain.
+ */
+ if (usechain) {
+ errcode = dhd_bcmsdh_recv_buf(bus,
+ bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2,
+ F2SYNC, (uint8*)PKTDATA(osh, pfirst),
+ dlen, pfirst, NULL, NULL);
+ } else if (bus->dataptr) {
+ errcode = dhd_bcmsdh_recv_buf(bus,
+ bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2,
+ F2SYNC, bus->dataptr,
+ dlen, NULL, NULL, NULL);
+ sublen = (uint16)pktfrombuf(osh, pfirst, 0, dlen, bus->dataptr);
+ if (sublen != dlen) {
+ DHD_ERROR(("%s: FAILED TO COPY, dlen %d sublen %d\n",
+ __FUNCTION__, dlen, sublen));
+ errcode = -1;
+ }
+ pnext = NULL;
+ } else {
+ DHD_ERROR(("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n", dlen));
+ errcode = -1;
+ }
+ bus->f2rxdata++;
+ ASSERT(errcode != BCME_PENDING);
+
+ /* On failure, kill the superframe, allow a couple retries */
+ if (errcode < 0) {
+ DHD_ERROR(("%s: glom read of %d bytes failed: %d\n",
+ __FUNCTION__, dlen, errcode));
+ bus->dhd->rx_errors++;
+
+ if (bus->glomerr++ < 3) {
+ dhdsdio_rxfail(bus, TRUE, TRUE);
+ } else {
+ bus->glomerr = 0;
+ dhdsdio_rxfail(bus, TRUE, FALSE);
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE(osh, bus->glom, FALSE);
+ dhd_os_sdunlock_rxq(bus->dhd);
+ bus->rxglomfail++;
+ bus->glom = NULL;
+ }
+ return 0;
+ }
+
+#ifdef DHD_DEBUG
+ if (DHD_GLOM_ON()) {
+ prhex("SUPERFRAME", PKTDATA(osh, pfirst),
+ MIN(PKTLEN(osh, pfirst), 48));
+ }
+#endif
+
+
+ /* Validate the superframe header */
+ dptr = (uint8 *)PKTDATA(osh, pfirst);
+ sublen = ltoh16_ua(dptr);
+ check = ltoh16_ua(dptr + sizeof(uint16));
+
+ chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+ seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
+ bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+ if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+ DHD_INFO(("%s: got frame w/nextlen too large (%d) seq %d\n",
+ __FUNCTION__, bus->nextlen, seq));
+ bus->nextlen = 0;
+ }
+ doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+ txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+
+ errcode = 0;
+ if ((uint16)~(sublen^check)) {
+ DHD_ERROR(("%s (superframe): HW hdr error: len/check 0x%04x/0x%04x\n",
+ __FUNCTION__, sublen, check));
+ errcode = -1;
+ } else if (ROUNDUP(sublen, bus->blocksize) != dlen) {
+ DHD_ERROR(("%s (superframe): len 0x%04x, rounded 0x%04x, expect 0x%04x\n",
+ __FUNCTION__, sublen, ROUNDUP(sublen, bus->blocksize), dlen));
+ errcode = -1;
+ } else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) != SDPCM_GLOM_CHANNEL) {
+ DHD_ERROR(("%s (superframe): bad channel %d\n", __FUNCTION__,
+ SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN])));
+ errcode = -1;
+ } else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) {
+ DHD_ERROR(("%s (superframe): got second descriptor?\n", __FUNCTION__));
+ errcode = -1;
+ } else if ((doff < SDPCM_HDRLEN) ||
+ (doff > (PKTLEN(osh, pfirst) - SDPCM_HDRLEN))) {
+ DHD_ERROR(("%s (superframe): Bad data offset %d: HW %d pkt %d min %d\n",
+ __FUNCTION__, doff, sublen, PKTLEN(osh, pfirst), SDPCM_HDRLEN));
+ errcode = -1;
+ }
+
+ /* Check sequence number of superframe SW header */
+ if (rxseq != seq) {
+ DHD_INFO(("%s: (superframe) rx_seq %d, expected %d\n",
+ __FUNCTION__, seq, rxseq));
+ bus->rx_badseq++;
+ rxseq = seq;
+ }
+
+ /* Check window for sanity */
+ if ((uint8)(txmax - bus->tx_seq) > 0x40) {
+ DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n",
+ __FUNCTION__, txmax, bus->tx_seq));
+ txmax = bus->tx_seq + 2;
+ }
+ bus->tx_max = txmax;
+
+ /* Remove superframe header, remember offset */
+ PKTPULL(osh, pfirst, doff);
+ sfdoff = doff;
+
+ /* Validate all the subframe headers */
+ for (num = 0, pnext = pfirst; pnext && !errcode;
+ num++, pnext = PKTNEXT(osh, pnext)) {
+ dptr = (uint8 *)PKTDATA(osh, pnext);
+ dlen = (uint16)PKTLEN(osh, pnext);
+ sublen = ltoh16_ua(dptr);
+ check = ltoh16_ua(dptr + sizeof(uint16));
+ chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+ doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+#ifdef DHD_DEBUG
+ if (DHD_GLOM_ON()) {
+ prhex("subframe", dptr, 32);
+ }
+#endif
+
+ if ((uint16)~(sublen^check)) {
+ DHD_ERROR(("%s (subframe %d): HW hdr error: "
+ "len/check 0x%04x/0x%04x\n",
+ __FUNCTION__, num, sublen, check));
+ errcode = -1;
+ } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) {
+ DHD_ERROR(("%s (subframe %d): length mismatch: "
+ "len 0x%04x, expect 0x%04x\n",
+ __FUNCTION__, num, sublen, dlen));
+ errcode = -1;
+ } else if ((chan != SDPCM_DATA_CHANNEL) &&
+ (chan != SDPCM_EVENT_CHANNEL)) {
+ DHD_ERROR(("%s (subframe %d): bad channel %d\n",
+ __FUNCTION__, num, chan));
+ errcode = -1;
+ } else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) {
+ DHD_ERROR(("%s (subframe %d): Bad data offset %d: HW %d min %d\n",
+ __FUNCTION__, num, doff, sublen, SDPCM_HDRLEN));
+ errcode = -1;
+ }
+ }
+
+ if (errcode) {
+ /* Terminate frame on error, request a couple retries */
+ if (bus->glomerr++ < 3) {
+ /* Restore superframe header space */
+ PKTPUSH(osh, pfirst, sfdoff);
+ dhdsdio_rxfail(bus, TRUE, TRUE);
+ } else {
+ bus->glomerr = 0;
+ dhdsdio_rxfail(bus, TRUE, FALSE);
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE(osh, bus->glom, FALSE);
+ dhd_os_sdunlock_rxq(bus->dhd);
+ bus->rxglomfail++;
+ bus->glom = NULL;
+ }
+ bus->nextlen = 0;
+ return 0;
+ }
+
+ /* Basic SD framing looks ok - process each packet (header) */
+ save_pfirst = pfirst;
+ bus->glom = NULL;
+ plast = NULL;
+
+ dhd_os_sdlock_rxq(bus->dhd);
+ for (num = 0; pfirst; rxseq++, pfirst = pnext) {
+ pnext = PKTNEXT(osh, pfirst);
+ PKTSETNEXT(osh, pfirst, NULL);
+
+ dptr = (uint8 *)PKTDATA(osh, pfirst);
+ sublen = ltoh16_ua(dptr);
+ chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+ seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
+ doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+
+ DHD_GLOM(("%s: Get subframe %d, %p(%p/%d), sublen %d chan %d seq %d\n",
+ __FUNCTION__, num, pfirst, PKTDATA(osh, pfirst),
+ PKTLEN(osh, pfirst), sublen, chan, seq));
+
+ ASSERT((chan == SDPCM_DATA_CHANNEL) || (chan == SDPCM_EVENT_CHANNEL));
+
+ if (rxseq != seq) {
+ DHD_GLOM(("%s: rx_seq %d, expected %d\n",
+ __FUNCTION__, seq, rxseq));
+ bus->rx_badseq++;
+ rxseq = seq;
+ }
+
+#ifdef DHD_DEBUG
+ if (DHD_BYTES_ON() && DHD_DATA_ON()) {
+ prhex("Rx Subframe Data", dptr, dlen);
+ }
+#endif
+
+ PKTSETLEN(osh, pfirst, sublen);
+ PKTPULL(osh, pfirst, doff);
+
+ if (PKTLEN(osh, pfirst) == 0) {
+ PKTFREE(bus->dhd->osh, pfirst, FALSE);
+ if (plast) {
+ PKTSETNEXT(osh, plast, pnext);
+ } else {
+ ASSERT(save_pfirst == pfirst);
+ save_pfirst = pnext;
+ }
+ continue;
+ } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pfirst) != 0) {
+ DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__));
+ bus->dhd->rx_errors++;
+ PKTFREE(osh, pfirst, FALSE);
+ if (plast) {
+ PKTSETNEXT(osh, plast, pnext);
+ } else {
+ ASSERT(save_pfirst == pfirst);
+ save_pfirst = pnext;
+ }
+ continue;
+ }
+
+ /* this packet will go up, link back into chain and count it */
+ PKTSETNEXT(osh, pfirst, pnext);
+ plast = pfirst;
+ num++;
+
+#ifdef DHD_DEBUG
+ if (DHD_GLOM_ON()) {
+ DHD_GLOM(("%s subframe %d to stack, %p(%p/%d) nxt/lnk %p/%p\n",
+ __FUNCTION__, num, pfirst,
+ PKTDATA(osh, pfirst), PKTLEN(osh, pfirst),
+ PKTNEXT(osh, pfirst), PKTLINK(pfirst)));
+ prhex("", (uint8 *)PKTDATA(osh, pfirst),
+ MIN(PKTLEN(osh, pfirst), 32));
+ }
+#endif /* DHD_DEBUG */
+ }
+ dhd_os_sdunlock_rxq(bus->dhd);
+ if (num) {
+ dhd_os_sdunlock(bus->dhd);
+ dhd_rx_frame(bus->dhd, ifidx, save_pfirst, num);
+ dhd_os_sdlock(bus->dhd);
+ }
+
+ bus->rxglomframes++;
+ bus->rxglompkts += num;
+ }
+ return num;
+}
+
+/* Return TRUE if there may be more frames to read */
+static uint
+dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished)
+{
+ osl_t *osh = bus->dhd->osh;
+ bcmsdh_info_t *sdh = bus->sdh;
+
+ uint16 len, check; /* Extracted hardware header fields */
+ uint8 chan, seq, doff; /* Extracted software header fields */
+ uint8 fcbits; /* Extracted fcbits from software header */
+ uint8 delta;
+
+ void *pkt; /* Packet for event or data frames */
+ uint16 pad; /* Number of pad bytes to read */
+ uint16 rdlen; /* Total number of bytes to read */
+ uint8 rxseq; /* Next sequence number to expect */
+ uint rxleft = 0; /* Remaining number of frames allowed */
+ int sdret; /* Return code from bcmsdh calls */
+ uint8 txmax; /* Maximum tx sequence offered */
+ bool len_consistent; /* Result of comparing readahead len and len from hw-hdr */
+ uint8 *rxbuf;
+ int ifidx = 0;
+ uint rxcount = 0; /* Total frames read */
+
+#if defined(DHD_DEBUG) || defined(SDTEST)
+ bool sdtest = FALSE; /* To limit message spew from test mode */
+#endif
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ ASSERT(maxframes);
+
+#ifdef SDTEST
+ /* Allow pktgen to override maxframes */
+ if (bus->pktgen_count && (bus->pktgen_mode == DHD_PKTGEN_RECV)) {
+ maxframes = bus->pktgen_count;
+ sdtest = TRUE;
+ }
+#endif
+
+ /* Not finished unless we encounter no more frames indication */
+ *finished = FALSE;
+
+
+ for (rxseq = bus->rx_seq, rxleft = maxframes;
+ !bus->rxskip && rxleft && bus->dhd->busstate != DHD_BUS_DOWN;
+ rxseq++, rxleft--) {
+
+ /* Handle glomming separately */
+ if (bus->glom || bus->glomd) {
+ uint8 cnt;
+ DHD_GLOM(("%s: calling rxglom: glomd %p, glom %p\n",
+ __FUNCTION__, bus->glomd, bus->glom));
+ cnt = dhdsdio_rxglom(bus, rxseq);
+ DHD_GLOM(("%s: rxglom returned %d\n", __FUNCTION__, cnt));
+ rxseq += cnt - 1;
+ rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
+ continue;
+ }
+
+ /* Try doing single read if we can */
+ if (dhd_readahead && bus->nextlen) {
+ uint16 nextlen = bus->nextlen;
+ bus->nextlen = 0;
+
+ if (bus->bus == SPI_BUS) {
+ rdlen = len = nextlen;
+ }
+ else {
+ rdlen = len = nextlen << 4;
+
+ /* Pad read to blocksize for efficiency */
+ if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
+ pad = bus->blocksize - (rdlen % bus->blocksize);
+ if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+ ((rdlen + pad + firstread) < MAX_RX_DATASZ))
+ rdlen += pad;
+ } else if (rdlen % DHD_SDALIGN) {
+ rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN);
+ }
+ }
+
+ /* We use bus->rxctl buffer in WinXP for initial control pkt receives.
+ * Later we use buffer-poll for data as well as control packets.
+ * This is required becuase dhd receives full frame in gSPI unlike SDIO.
+ * After the frame is received we have to distinguish whether it is data
+ * or non-data frame.
+ */
+ /* Allocate a packet buffer */
+ dhd_os_sdlock_rxq(bus->dhd);
+ if (!(pkt = PKTGET(osh, rdlen + DHD_SDALIGN, FALSE))) {
+ if (bus->bus == SPI_BUS) {
+ bus->usebufpool = FALSE;
+ bus->rxctl = bus->rxbuf;
+ if (dhd_alignctl) {
+ bus->rxctl += firstread;
+ if ((pad = ((uintptr)bus->rxctl % DHD_SDALIGN)))
+ bus->rxctl += (DHD_SDALIGN - pad);
+ bus->rxctl -= firstread;
+ }
+ ASSERT(bus->rxctl >= bus->rxbuf);
+ rxbuf = bus->rxctl;
+ /* Read the entire frame */
+ sdret = dhd_bcmsdh_recv_buf(bus,
+ bcmsdh_cur_sbwad(sdh),
+ SDIO_FUNC_2,
+ F2SYNC, rxbuf, rdlen,
+ NULL, NULL, NULL);
+ bus->f2rxdata++;
+ ASSERT(sdret != BCME_PENDING);
+
+
+ /* Control frame failures need retransmission */
+ if (sdret < 0) {
+ DHD_ERROR(("%s: read %d control bytes failed: %d\n",
+ __FUNCTION__, rdlen, sdret));
+ /* dhd.rx_ctlerrs is higher level */
+ bus->rxc_errors++;
+ dhd_os_sdunlock_rxq(bus->dhd);
+ dhdsdio_rxfail(bus, TRUE,
+ (bus->bus == SPI_BUS) ? FALSE : TRUE);
+ continue;
+ }
+ } else {
+ /* Give up on data, request rtx of events */
+ DHD_ERROR(("%s (nextlen): PKTGET failed: len %d rdlen %d "
+ "expected rxseq %d\n",
+ __FUNCTION__, len, rdlen, rxseq));
+ /* Just go try again w/normal header read */
+ dhd_os_sdunlock_rxq(bus->dhd);
+ continue;
+ }
+ } else {
+ if (bus->bus == SPI_BUS)
+ bus->usebufpool = TRUE;
+
+ ASSERT(!PKTLINK(pkt));
+ PKTALIGN(osh, pkt, rdlen, DHD_SDALIGN);
+ rxbuf = (uint8 *)PKTDATA(osh, pkt);
+ /* Read the entire frame */
+ sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh),
+ SDIO_FUNC_2,
+ F2SYNC, rxbuf, rdlen,
+ pkt, NULL, NULL);
+ bus->f2rxdata++;
+ ASSERT(sdret != BCME_PENDING);
+
+ if (sdret < 0) {
+ DHD_ERROR(("%s (nextlen): read %d bytes failed: %d\n",
+ __FUNCTION__, rdlen, sdret));
+ PKTFREE(bus->dhd->osh, pkt, FALSE);
+ bus->dhd->rx_errors++;
+ dhd_os_sdunlock_rxq(bus->dhd);
+ /* Force retry w/normal header read. Don't attemp NAK for
+ * gSPI
+ */
+ dhdsdio_rxfail(bus, TRUE,
+ (bus->bus == SPI_BUS) ? FALSE : TRUE);
+ continue;
+ }
+ }
+ dhd_os_sdunlock_rxq(bus->dhd);
+
+ /* Now check the header */
+ bcopy(rxbuf, bus->rxhdr, SDPCM_HDRLEN);
+
+ /* Extract hardware header fields */
+ len = ltoh16_ua(bus->rxhdr);
+ check = ltoh16_ua(bus->rxhdr + sizeof(uint16));
+
+ /* All zeros means readahead info was bad */
+ if (!(len|check)) {
+ DHD_INFO(("%s (nextlen): read zeros in HW header???\n",
+ __FUNCTION__));
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE2();
+ dhd_os_sdunlock_rxq(bus->dhd);
+ GSPI_PR55150_BAILOUT;
+ continue;
+ }
+
+ /* Validate check bytes */
+ if ((uint16)~(len^check)) {
+ DHD_ERROR(("%s (nextlen): HW hdr error: nextlen/len/check"
+ " 0x%04x/0x%04x/0x%04x\n", __FUNCTION__, nextlen,
+ len, check));
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE2();
+ dhd_os_sdunlock_rxq(bus->dhd);
+ bus->rx_badhdr++;
+ dhdsdio_rxfail(bus, FALSE, FALSE);
+ GSPI_PR55150_BAILOUT;
+ continue;
+ }
+
+ /* Validate frame length */
+ if (len < SDPCM_HDRLEN) {
+ DHD_ERROR(("%s (nextlen): HW hdr length invalid: %d\n",
+ __FUNCTION__, len));
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE2();
+ dhd_os_sdunlock_rxq(bus->dhd);
+ GSPI_PR55150_BAILOUT;
+ continue;
+ }
+
+ /* Check for consistency with readahead info */
+ len_consistent = (nextlen != (ROUNDUP(len, 16) >> 4));
+ if (len_consistent) {
+ /* Mismatch, force retry w/normal header (may be >4K) */
+ DHD_ERROR(("%s (nextlen): mismatch, nextlen %d len %d rnd %d; "
+ "expected rxseq %d\n",
+ __FUNCTION__, nextlen, len, ROUNDUP(len, 16), rxseq));
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE2();
+ dhd_os_sdunlock_rxq(bus->dhd);
+ dhdsdio_rxfail(bus, TRUE, (bus->bus == SPI_BUS) ? FALSE : TRUE);
+ GSPI_PR55150_BAILOUT;
+ continue;
+ }
+
+
+ /* Extract software header fields */
+ chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+ bus->nextlen =
+ bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+ if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+ DHD_INFO(("%s (nextlen): got frame w/nextlen too large"
+ " (%d), seq %d\n", __FUNCTION__, bus->nextlen,
+ seq));
+ bus->nextlen = 0;
+ }
+
+ bus->dhd->rx_readahead_cnt ++;
+ /* Handle Flow Control */
+ fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+ delta = 0;
+ if (~bus->flowcontrol & fcbits) {
+ bus->fc_xoff++;
+ delta = 1;
+ }
+ if (bus->flowcontrol & ~fcbits) {
+ bus->fc_xon++;
+ delta = 1;
+ }
+
+ if (delta) {
+ bus->fc_rcvd++;
+ bus->flowcontrol = fcbits;
+ }
+
+ /* Check and update sequence number */
+ if (rxseq != seq) {
+ DHD_INFO(("%s (nextlen): rx_seq %d, expected %d\n",
+ __FUNCTION__, seq, rxseq));
+ bus->rx_badseq++;
+ rxseq = seq;
+ }
+
+ /* Check window for sanity */
+ if ((uint8)(txmax - bus->tx_seq) > 0x40) {
+ DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n",
+ __FUNCTION__, txmax, bus->tx_seq));
+ txmax = bus->tx_seq + 2;
+ }
+ bus->tx_max = txmax;
+
+#ifdef DHD_DEBUG
+ if (DHD_BYTES_ON() && DHD_DATA_ON()) {
+ prhex("Rx Data", rxbuf, len);
+ } else if (DHD_HDRS_ON()) {
+ prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN);
+ }
+#endif
+
+ if (chan == SDPCM_CONTROL_CHANNEL) {
+ if (bus->bus == SPI_BUS) {
+ dhdsdio_read_control(bus, rxbuf, len, doff);
+ if (bus->usebufpool) {
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE(bus->dhd->osh, pkt, FALSE);
+ dhd_os_sdunlock_rxq(bus->dhd);
+ }
+ continue;
+ } else {
+ DHD_ERROR(("%s (nextlen): readahead on control"
+ " packet %d?\n", __FUNCTION__, seq));
+ /* Force retry w/normal header read */
+ bus->nextlen = 0;
+ dhdsdio_rxfail(bus, FALSE, TRUE);
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE2();
+ dhd_os_sdunlock_rxq(bus->dhd);
+ continue;
+ }
+ }
+
+ if ((bus->bus == SPI_BUS) && !bus->usebufpool) {
+ DHD_ERROR(("Received %d bytes on %d channel. Running out of "
+ "rx pktbuf's or not yet malloced.\n", len, chan));
+ continue;
+ }
+
+ /* Validate data offset */
+ if ((doff < SDPCM_HDRLEN) || (doff > len)) {
+ DHD_ERROR(("%s (nextlen): bad data offset %d: HW len %d min %d\n",
+ __FUNCTION__, doff, len, SDPCM_HDRLEN));
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE2();
+ dhd_os_sdunlock_rxq(bus->dhd);
+ ASSERT(0);
+ dhdsdio_rxfail(bus, FALSE, FALSE);
+ continue;
+ }
+
+ /* All done with this one -- now deliver the packet */
+ goto deliver;
+ }
+ /* gSPI frames should not be handled in fractions */
+ if (bus->bus == SPI_BUS) {
+ break;
+ }
+
+ /* Read frame header (hardware and software) */
+ sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+ bus->rxhdr, firstread, NULL, NULL, NULL);
+ bus->f2rxhdrs++;
+ ASSERT(sdret != BCME_PENDING);
+
+ if (sdret < 0) {
+ DHD_ERROR(("%s: RXHEADER FAILED: %d\n", __FUNCTION__, sdret));
+ bus->rx_hdrfail++;
+ dhdsdio_rxfail(bus, TRUE, TRUE);
+ continue;
+ }
+
+#ifdef DHD_DEBUG
+ if (DHD_BYTES_ON() || DHD_HDRS_ON()) {
+ prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN);
+ }
+#endif
+
+ /* Extract hardware header fields */
+ len = ltoh16_ua(bus->rxhdr);
+ check = ltoh16_ua(bus->rxhdr + sizeof(uint16));
+
+ /* All zeros means no more frames */
+ if (!(len|check)) {
+ *finished = TRUE;
+ break;
+ }
+
+ /* Validate check bytes */
+ if ((uint16)~(len^check)) {
+ DHD_ERROR(("%s: HW hdr error: len/check 0x%04x/0x%04x\n",
+ __FUNCTION__, len, check));
+ bus->rx_badhdr++;
+ dhdsdio_rxfail(bus, FALSE, FALSE);
+ continue;
+ }
+
+ /* Validate frame length */
+ if (len < SDPCM_HDRLEN) {
+ DHD_ERROR(("%s: HW hdr length invalid: %d\n", __FUNCTION__, len));
+ continue;
+ }
+
+ /* Extract software header fields */
+ chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+ txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+ /* Validate data offset */
+ if ((doff < SDPCM_HDRLEN) || (doff > len)) {
+ DHD_ERROR(("%s: Bad data offset %d: HW len %d, min %d seq %d\n",
+ __FUNCTION__, doff, len, SDPCM_HDRLEN, seq));
+ bus->rx_badhdr++;
+ ASSERT(0);
+ dhdsdio_rxfail(bus, FALSE, FALSE);
+ continue;
+ }
+
+ /* Save the readahead length if there is one */
+ bus->nextlen = bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+ if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+ DHD_INFO(("%s (nextlen): got frame w/nextlen too large (%d), seq %d\n",
+ __FUNCTION__, bus->nextlen, seq));
+ bus->nextlen = 0;
+ }
+
+ /* Handle Flow Control */
+ fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+ delta = 0;
+ if (~bus->flowcontrol & fcbits) {
+ bus->fc_xoff++;
+ delta = 1;
+ }
+ if (bus->flowcontrol & ~fcbits) {
+ bus->fc_xon++;
+ delta = 1;
+ }
+
+ if (delta) {
+ bus->fc_rcvd++;
+ bus->flowcontrol = fcbits;
+ }
+
+ /* Check and update sequence number */
+ if (rxseq != seq) {
+ DHD_INFO(("%s: rx_seq %d, expected %d\n", __FUNCTION__, seq, rxseq));
+ bus->rx_badseq++;
+ rxseq = seq;
+ }
+
+ /* Check window for sanity */
+ if ((uint8)(txmax - bus->tx_seq) > 0x40) {
+ DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n",
+ __FUNCTION__, txmax, bus->tx_seq));
+ txmax = bus->tx_seq + 2;
+ }
+ bus->tx_max = txmax;
+
+ /* Call a separate function for control frames */
+ if (chan == SDPCM_CONTROL_CHANNEL) {
+ dhdsdio_read_control(bus, bus->rxhdr, len, doff);
+ continue;
+ }
+
+ ASSERT((chan == SDPCM_DATA_CHANNEL) || (chan == SDPCM_EVENT_CHANNEL) ||
+ (chan == SDPCM_TEST_CHANNEL) || (chan == SDPCM_GLOM_CHANNEL));
+
+ /* Length to read */
+ rdlen = (len > firstread) ? (len - firstread) : 0;
+
+ /* May pad read to blocksize for efficiency */
+ if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
+ pad = bus->blocksize - (rdlen % bus->blocksize);
+ if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+ ((rdlen + pad + firstread) < MAX_RX_DATASZ))
+ rdlen += pad;
+ } else if (rdlen % DHD_SDALIGN) {
+ rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN);
+ }
+
+ /* Satisfy length-alignment requirements */
+ if (forcealign && (rdlen & (ALIGNMENT - 1)))
+ rdlen = ROUNDUP(rdlen, ALIGNMENT);
+
+ if ((rdlen + firstread) > MAX_RX_DATASZ) {
+ /* Too long -- skip this frame */
+ DHD_ERROR(("%s: too long: len %d rdlen %d\n", __FUNCTION__, len, rdlen));
+ bus->dhd->rx_errors++; bus->rx_toolong++;
+ dhdsdio_rxfail(bus, FALSE, FALSE);
+ continue;
+ }
+
+ dhd_os_sdlock_rxq(bus->dhd);
+ if (!(pkt = PKTGET(osh, (rdlen + firstread + DHD_SDALIGN), FALSE))) {
+ /* Give up on data, request rtx of events */
+ DHD_ERROR(("%s: PKTGET failed: rdlen %d chan %d\n",
+ __FUNCTION__, rdlen, chan));
+ bus->dhd->rx_dropped++;
+ dhd_os_sdunlock_rxq(bus->dhd);
+ dhdsdio_rxfail(bus, FALSE, RETRYCHAN(chan));
+ continue;
+ }
+ dhd_os_sdunlock_rxq(bus->dhd);
+
+ ASSERT(!PKTLINK(pkt));
+
+ /* Leave room for what we already read, and align remainder */
+ ASSERT(firstread < (PKTLEN(osh, pkt)));
+ PKTPULL(osh, pkt, firstread);
+ PKTALIGN(osh, pkt, rdlen, DHD_SDALIGN);
+
+ /* Read the remaining frame data */
+ sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+ ((uint8 *)PKTDATA(osh, pkt)), rdlen, pkt, NULL, NULL);
+ bus->f2rxdata++;
+ ASSERT(sdret != BCME_PENDING);
+
+ if (sdret < 0) {
+ DHD_ERROR(("%s: read %d %s bytes failed: %d\n", __FUNCTION__, rdlen,
+ ((chan == SDPCM_EVENT_CHANNEL) ? "event" :
+ ((chan == SDPCM_DATA_CHANNEL) ? "data" : "test")), sdret));
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE(bus->dhd->osh, pkt, FALSE);
+ dhd_os_sdunlock_rxq(bus->dhd);
+ bus->dhd->rx_errors++;
+ dhdsdio_rxfail(bus, TRUE, RETRYCHAN(chan));
+ continue;
+ }
+
+ /* Copy the already-read portion */
+ PKTPUSH(osh, pkt, firstread);
+ bcopy(bus->rxhdr, PKTDATA(osh, pkt), firstread);
+
+#ifdef DHD_DEBUG
+ if (DHD_BYTES_ON() && DHD_DATA_ON()) {
+ prhex("Rx Data", PKTDATA(osh, pkt), len);
+ }
+#endif
+
+deliver:
+ /* Save superframe descriptor and allocate packet frame */
+ if (chan == SDPCM_GLOM_CHANNEL) {
+ if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
+ DHD_GLOM(("%s: got glom descriptor, %d bytes:\n",
+ __FUNCTION__, len));
+#ifdef DHD_DEBUG
+ if (DHD_GLOM_ON()) {
+ prhex("Glom Data", PKTDATA(osh, pkt), len);
+ }
+#endif
+ PKTSETLEN(osh, pkt, len);
+ ASSERT(doff == SDPCM_HDRLEN);
+ PKTPULL(osh, pkt, SDPCM_HDRLEN);
+ bus->glomd = pkt;
+ } else {
+ DHD_ERROR(("%s: glom superframe w/o descriptor!\n", __FUNCTION__));
+ dhdsdio_rxfail(bus, FALSE, FALSE);
+ }
+ continue;
+ }
+
+ /* Fill in packet len and prio, deliver upward */
+ PKTSETLEN(osh, pkt, len);
+ PKTPULL(osh, pkt, doff);
+
+#ifdef SDTEST
+ /* Test channel packets are processed separately */
+ if (chan == SDPCM_TEST_CHANNEL) {
+ dhdsdio_testrcv(bus, pkt, seq);
+ continue;
+ }
+#endif /* SDTEST */
+
+ if (PKTLEN(osh, pkt) == 0) {
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE(bus->dhd->osh, pkt, FALSE);
+ dhd_os_sdunlock_rxq(bus->dhd);
+ continue;
+ } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pkt) != 0) {
+ DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__));
+ dhd_os_sdlock_rxq(bus->dhd);
+ PKTFREE(bus->dhd->osh, pkt, FALSE);
+ dhd_os_sdunlock_rxq(bus->dhd);
+ bus->dhd->rx_errors++;
+ continue;
+ }
+
+
+ /* Unlock during rx call */
+ dhd_os_sdunlock(bus->dhd);
+ dhd_rx_frame(bus->dhd, ifidx, pkt, 1);
+ dhd_os_sdlock(bus->dhd);
+ }
+ rxcount = maxframes - rxleft;
+#ifdef DHD_DEBUG
+ /* Message if we hit the limit */
+ if (!rxleft && !sdtest)
+ DHD_DATA(("%s: hit rx limit of %d frames\n", __FUNCTION__, maxframes));
+ else
+#endif /* DHD_DEBUG */
+ DHD_DATA(("%s: processed %d frames\n", __FUNCTION__, rxcount));
+ /* Back off rxseq if awaiting rtx, update rx_seq */
+ if (bus->rxskip)
+ rxseq--;
+ bus->rx_seq = rxseq;
+
+ return rxcount;
+}
+
+static uint32
+dhdsdio_hostmail(dhd_bus_t *bus)
+{
+ sdpcmd_regs_t *regs = bus->regs;
+ uint32 intstatus = 0;
+ uint32 hmb_data;
+ uint8 fcbits;
+ uint retries = 0;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ /* Read mailbox data and ack that we did so */
+ R_SDREG(hmb_data, &regs->tohostmailboxdata, retries);
+ if (retries <= retry_limit)
+ W_SDREG(SMB_INT_ACK, &regs->tosbmailbox, retries);
+ bus->f1regdata += 2;
+
+ /* Dongle recomposed rx frames, accept them again */
+ if (hmb_data & HMB_DATA_NAKHANDLED) {
+ DHD_INFO(("Dongle reports NAK handled, expect rtx of %d\n", bus->rx_seq));
+ if (!bus->rxskip) {
+ DHD_ERROR(("%s: unexpected NAKHANDLED!\n", __FUNCTION__));
+ }
+ bus->rxskip = FALSE;
+ intstatus |= I_HMB_FRAME_IND;
+ }
+
+ /*
+ * DEVREADY does not occur with gSPI.
+ */
+ if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
+ bus->sdpcm_ver = (hmb_data & HMB_DATA_VERSION_MASK) >> HMB_DATA_VERSION_SHIFT;
+ if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
+ DHD_ERROR(("Version mismatch, dongle reports %d, expecting %d\n",
+ bus->sdpcm_ver, SDPCM_PROT_VERSION));
+ else
+ DHD_INFO(("Dongle ready, protocol version %d\n", bus->sdpcm_ver));
+ }
+
+ /*
+ * Flow Control has been moved into the RX headers and this out of band
+ * method isn't used any more. Leae this here for possibly remaining backward
+ * compatible with older dongles
+ */
+ if (hmb_data & HMB_DATA_FC) {
+ fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >> HMB_DATA_FCDATA_SHIFT;
+
+ if (fcbits & ~bus->flowcontrol)
+ bus->fc_xoff++;
+ if (bus->flowcontrol & ~fcbits)
+ bus->fc_xon++;
+
+ bus->fc_rcvd++;
+ bus->flowcontrol = fcbits;
+ }
+
+ /* Shouldn't be any others */
+ if (hmb_data & ~(HMB_DATA_DEVREADY |
+ HMB_DATA_NAKHANDLED |
+ HMB_DATA_FC |
+ HMB_DATA_FWREADY |
+ HMB_DATA_FCDATA_MASK |
+ HMB_DATA_VERSION_MASK)) {
+ DHD_ERROR(("Unknown mailbox data content: 0x%02x\n", hmb_data));
+ }
+
+ return intstatus;
+}
+
+bool
+dhdsdio_dpc(dhd_bus_t *bus)
+{
+ bcmsdh_info_t *sdh = bus->sdh;
+ sdpcmd_regs_t *regs = bus->regs;
+ uint32 intstatus, newstatus = 0;
+ uint retries = 0;
+ uint rxlimit = dhd_rxbound; /* Rx frames to read before resched */
+ uint txlimit = dhd_txbound; /* Tx frames to send before resched */
+ uint framecnt = 0; /* Temporary counter of tx/rx frames */
+ bool rxdone = TRUE; /* Flag for no more read data */
+ bool resched = FALSE; /* Flag indicating resched wanted */
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ /* Start with leftover status bits */
+ intstatus = bus->intstatus;
+
+ dhd_os_sdlock(bus->dhd);
+
+ /* If waiting for HTAVAIL, check status */
+ if (bus->clkstate == CLK_PENDING) {
+ int err;
+ uint8 clkctl, devctl = 0;
+
+#ifdef DHD_DEBUG
+ /* Check for inconsistent device control */
+ devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
+ if (err) {
+ DHD_ERROR(("%s: error reading DEVCTL: %d\n", __FUNCTION__, err));
+ bus->dhd->busstate = DHD_BUS_DOWN;
+ } else {
+ ASSERT(devctl & SBSDIO_DEVCTL_CA_INT_ONLY);
+ }
+#endif /* DHD_DEBUG */
+
+ /* Read CSR, if clock on switch to AVAIL, else ignore */
+ clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ if (err) {
+ DHD_ERROR(("%s: error reading CSR: %d\n", __FUNCTION__, err));
+ bus->dhd->busstate = DHD_BUS_DOWN;
+ }
+
+ DHD_INFO(("DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl, clkctl));
+
+ if (SBSDIO_HTAV(clkctl)) {
+ devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
+ if (err) {
+ DHD_ERROR(("%s: error reading DEVCTL: %d\n",
+ __FUNCTION__, err));
+ bus->dhd->busstate = DHD_BUS_DOWN;
+ }
+ devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
+ if (err) {
+ DHD_ERROR(("%s: error writing DEVCTL: %d\n",
+ __FUNCTION__, err));
+ bus->dhd->busstate = DHD_BUS_DOWN;
+ }
+ bus->clkstate = CLK_AVAIL;
+ } else {
+ goto clkwait;
+ }
+ }
+
+ BUS_WAKE(bus);
+
+ /* Make sure backplane clock is on */
+ dhdsdio_clkctl(bus, CLK_AVAIL, TRUE);
+ if (bus->clkstate == CLK_PENDING)
+ goto clkwait;
+
+ /* Pending interrupt indicates new device status */
+ if (bus->ipend) {
+ bus->ipend = FALSE;
+ R_SDREG(newstatus, &regs->intstatus, retries);
+ bus->f1regdata++;
+ if (bcmsdh_regfail(bus->sdh))
+ newstatus = 0;
+ newstatus &= bus->hostintmask;
+ bus->fcstate = !!(newstatus & I_HMB_FC_STATE);
+ if (newstatus) {
+ W_SDREG(newstatus, &regs->intstatus, retries);
+ bus->f1regdata++;
+ }
+ }
+
+ /* Merge new bits with previous */
+ intstatus |= newstatus;
+ bus->intstatus = 0;
+
+ /* Handle flow-control change: read new state in case our ack
+ * crossed another change interrupt. If change still set, assume
+ * FC ON for safety, let next loop through do the debounce.
+ */
+ if (intstatus & I_HMB_FC_CHANGE) {
+ intstatus &= ~I_HMB_FC_CHANGE;
+ W_SDREG(I_HMB_FC_CHANGE, &regs->intstatus, retries);
+ R_SDREG(newstatus, &regs->intstatus, retries);
+ bus->f1regdata += 2;
+ bus->fcstate = !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE));
+ intstatus |= (newstatus & bus->hostintmask);
+ }
+
+ /* Handle host mailbox indication */
+ if (intstatus & I_HMB_HOST_INT) {
+ intstatus &= ~I_HMB_HOST_INT;
+ intstatus |= dhdsdio_hostmail(bus);
+ }
+
+ /* Generally don't ask for these, can get CRC errors... */
+ if (intstatus & I_WR_OOSYNC) {
+ DHD_ERROR(("Dongle reports WR_OOSYNC\n"));
+ intstatus &= ~I_WR_OOSYNC;
+ }
+
+ if (intstatus & I_RD_OOSYNC) {
+ DHD_ERROR(("Dongle reports RD_OOSYNC\n"));
+ intstatus &= ~I_RD_OOSYNC;
+ }
+
+ if (intstatus & I_SBINT) {
+ DHD_ERROR(("Dongle reports SBINT\n"));
+ intstatus &= ~I_SBINT;
+ }
+
+ /* Would be active due to wake-wlan in gSPI */
+ if (intstatus & I_CHIPACTIVE) {
+ DHD_INFO(("Dongle reports CHIPACTIVE\n"));
+ intstatus &= ~I_CHIPACTIVE;
+ }
+
+ /* Ignore frame indications if rxskip is set */
+ if (bus->rxskip)
+ intstatus &= ~I_HMB_FRAME_IND;
+
+ /* On frame indication, read available frames */
+ if (PKT_AVAILABLE()) {
+ framecnt = dhdsdio_readframes(bus, rxlimit, &rxdone);
+ if (rxdone || bus->rxskip)
+ intstatus &= ~I_HMB_FRAME_IND;
+ rxlimit -= MIN(framecnt, rxlimit);
+ }
+
+ /* Keep still-pending events for next scheduling */
+ bus->intstatus = intstatus;
+
+clkwait:
+ /* Re-enable interrupts to detect new device events (mailbox, rx frame)
+ * or clock availability. (Allows tx loop to check ipend if desired.)
+ * (Unless register access seems hosed, as we may not be able to ACK...)
+ */
+ if (bus->intr && bus->intdis && !bcmsdh_regfail(sdh)) {
+ DHD_INTR(("%s: enable SDIO interrupts, rxdone %d framecnt %d\n",
+ __FUNCTION__, rxdone, framecnt));
+ bus->intdis = FALSE;
+#if defined(OOB_INTR_ONLY)
+ bcmsdh_oob_intr_set(1);
+#endif /* (OOB_INTR_ONLY) */
+ bcmsdh_intr_enable(sdh);
+ }
+
+ if (DATAOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) {
+ int ret, i;
+
+ ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
+ (uint8 *)bus->ctrl_frame_buf, (uint32)bus->ctrl_frame_len,
+ NULL, NULL, NULL);
+ ASSERT(ret != BCME_PENDING);
+
+ if (ret < 0) {
+ /* On failure, abort the command and terminate the frame */
+ DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
+ __FUNCTION__, ret));
+ bus->tx_sderrs++;
+
+ bcmsdh_abort(sdh, SDIO_FUNC_2);
+
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL,
+ SFC_WF_TERM, NULL);
+ bus->f1regdata++;
+
+ for (i = 0; i < 3; i++) {
+ uint8 hi, lo;
+ hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCHI, NULL);
+ lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_WFRAMEBCLO, NULL);
+ bus->f1regdata += 2;
+ if ((hi == 0) && (lo == 0))
+ break;
+ }
+
+ }
+ if (ret == 0) {
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+ }
+
+ printf("Return_dpc value is : %d\n", ret);
+ bus->ctrl_frame_stat = FALSE;
+ dhd_wait_event_wakeup(bus->dhd);
+ }
+ /* Send queued frames (limit 1 if rx may still be pending) */
+ else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate &&
+ pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && DATAOK(bus)) {
+ framecnt = rxdone ? txlimit : MIN(txlimit, dhd_txminmax);
+ framecnt = dhdsdio_sendfromq(bus, framecnt);
+ txlimit -= framecnt;
+ }
+
+ /* Resched if events or tx frames are pending, else await next interrupt */
+ /* On failed register access, all bets are off: no resched or interrupts */
+ if ((bus->dhd->busstate == DHD_BUS_DOWN) || bcmsdh_regfail(sdh)) {
+ DHD_ERROR(("%s: failed backplane access over SDIO, halting operation %d \n",
+ __FUNCTION__, bcmsdh_regfail(sdh)));
+ bus->dhd->busstate = DHD_BUS_DOWN;
+ bus->intstatus = 0;
+ } else if (bus->clkstate == CLK_PENDING) {
+ DHD_INFO(("%s: rescheduled due to CLK_PENDING awaiting \
+ I_CHIPACTIVE interrupt", __FUNCTION__));
+ resched = TRUE;
+ } else if (bus->intstatus || bus->ipend ||
+ (!bus->fcstate && pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) ||
+ PKT_AVAILABLE()) { /* Read multiple frames */
+ resched = TRUE;
+ }
+
+
+ bus->dpc_sched = resched;
+
+ /* If we're done for now, turn off clock request. */
+ if ((bus->clkstate != CLK_PENDING) && bus->idletime == DHD_IDLE_IMMEDIATE) {
+ bus->activity = FALSE;
+ dhdsdio_clkctl(bus, CLK_NONE, FALSE);
+ }
+
+ dhd_os_sdunlock(bus->dhd);
+
+ return resched;
+}
+
+bool
+dhd_bus_dpc(struct dhd_bus *bus)
+{
+ bool resched;
+
+ /* Call the DPC directly. */
+ DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__));
+ resched = dhdsdio_dpc(bus);
+
+ return resched;
+}
+
+void
+dhdsdio_isr(void *arg)
+{
+ dhd_bus_t *bus = (dhd_bus_t*)arg;
+ bcmsdh_info_t *sdh;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (!bus) {
+ DHD_ERROR(("%s : bus is null pointer , exit \n", __FUNCTION__));
+ return;
+ }
+ sdh = bus->sdh;
+
+ if (bus->dhd->busstate == DHD_BUS_DOWN) {
+ DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
+ return;
+ }
+ /* Count the interrupt call */
+ bus->intrcount++;
+ bus->ipend = TRUE;
+
+ /* Shouldn't get this interrupt if we're sleeping? */
+ if (bus->sleeping) {
+ DHD_ERROR(("INTERRUPT WHILE SLEEPING??\n"));
+ return;
+ }
+
+ /* Disable additional interrupts (is this needed now)? */
+ if (bus->intr) {
+ DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__));
+ } else {
+ DHD_ERROR(("dhdsdio_isr() w/o interrupt configured!\n"));
+ }
+
+ bcmsdh_intr_disable(sdh);
+ bus->intdis = TRUE;
+
+#if defined(SDIO_ISR_THREAD)
+ DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__));
+ dhd_os_wake_lock(bus->dhd);
+ while (dhdsdio_dpc(bus));
+ dhd_os_wake_unlock(bus->dhd);
+#else
+ bus->dpc_sched = TRUE;
+ dhd_sched_dpc(bus->dhd);
+#endif
+
+}
+
+#ifdef SDTEST
+static void
+dhdsdio_pktgen_init(dhd_bus_t *bus)
+{
+ /* Default to specified length, or full range */
+ if (dhd_pktgen_len) {
+ bus->pktgen_maxlen = MIN(dhd_pktgen_len, MAX_PKTGEN_LEN);
+ bus->pktgen_minlen = bus->pktgen_maxlen;
+ } else {
+ bus->pktgen_maxlen = MAX_PKTGEN_LEN;
+ bus->pktgen_minlen = 0;
+ }
+ bus->pktgen_len = (uint16)bus->pktgen_minlen;
+
+ /* Default to per-watchdog burst with 10s print time */
+ bus->pktgen_freq = 1;
+ bus->pktgen_print = 10000 / dhd_watchdog_ms;
+ bus->pktgen_count = (dhd_pktgen * dhd_watchdog_ms + 999) / 1000;
+
+ /* Default to echo mode */
+ bus->pktgen_mode = DHD_PKTGEN_ECHO;
+ bus->pktgen_stop = 1;
+}
+
+static void
+dhdsdio_pktgen(dhd_bus_t *bus)
+{
+ void *pkt;
+ uint8 *data;
+ uint pktcount;
+ uint fillbyte;
+ osl_t *osh = bus->dhd->osh;
+ uint16 len;
+
+ /* Display current count if appropriate */
+ if (bus->pktgen_print && (++bus->pktgen_ptick >= bus->pktgen_print)) {
+ bus->pktgen_ptick = 0;
+ printf("%s: send attempts %d rcvd %d\n",
+ __FUNCTION__, bus->pktgen_sent, bus->pktgen_rcvd);
+ }
+
+ /* For recv mode, just make sure dongle has started sending */
+ if (bus->pktgen_mode == DHD_PKTGEN_RECV) {
+ if (!bus->pktgen_rcvd)
+ dhdsdio_sdtest_set(bus, TRUE);
+ return;
+ }
+
+ /* Otherwise, generate or request the specified number of packets */
+ for (pktcount = 0; pktcount < bus->pktgen_count; pktcount++) {
+ /* Stop if total has been reached */
+ if (bus->pktgen_total && (bus->pktgen_sent >= bus->pktgen_total)) {
+ bus->pktgen_count = 0;
+ break;
+ }
+
+ /* Allocate an appropriate-sized packet */
+ len = bus->pktgen_len;
+ if (!(pkt = PKTGET(osh, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN),
+ TRUE))) {;
+ DHD_ERROR(("%s: PKTGET failed!\n", __FUNCTION__));
+ break;
+ }
+ PKTALIGN(osh, pkt, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), DHD_SDALIGN);
+ data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN;
+
+ /* Write test header cmd and extra based on mode */
+ switch (bus->pktgen_mode) {
+ case DHD_PKTGEN_ECHO:
+ *data++ = SDPCM_TEST_ECHOREQ;
+ *data++ = (uint8)bus->pktgen_sent;
+ break;
+
+ case DHD_PKTGEN_SEND:
+ *data++ = SDPCM_TEST_DISCARD;
+ *data++ = (uint8)bus->pktgen_sent;
+ break;
+
+ case DHD_PKTGEN_RXBURST:
+ *data++ = SDPCM_TEST_BURST;
+ *data++ = (uint8)bus->pktgen_count;
+ break;
+
+ default:
+ DHD_ERROR(("Unrecognized pktgen mode %d\n", bus->pktgen_mode));
+ PKTFREE(osh, pkt, TRUE);
+ bus->pktgen_count = 0;
+ return;
+ }
+
+ /* Write test header length field */
+ *data++ = (len >> 0);
+ *data++ = (len >> 8);
+
+ /* Then fill in the remainder -- N/A for burst, but who cares... */
+ for (fillbyte = 0; fillbyte < len; fillbyte++)
+ *data++ = SDPCM_TEST_FILL(fillbyte, (uint8)bus->pktgen_sent);
+
+#ifdef DHD_DEBUG
+ if (DHD_BYTES_ON() && DHD_DATA_ON()) {
+ data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN;
+ prhex("dhdsdio_pktgen: Tx Data", data, PKTLEN(osh, pkt) - SDPCM_HDRLEN);
+ }
+#endif
+
+ /* Send it */
+ if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE)) {
+ bus->pktgen_fail++;
+ if (bus->pktgen_stop && bus->pktgen_stop == bus->pktgen_fail)
+ bus->pktgen_count = 0;
+ }
+ bus->pktgen_sent++;
+
+ /* Bump length if not fixed, wrap at max */
+ if (++bus->pktgen_len > bus->pktgen_maxlen)
+ bus->pktgen_len = (uint16)bus->pktgen_minlen;
+
+ /* Special case for burst mode: just send one request! */
+ if (bus->pktgen_mode == DHD_PKTGEN_RXBURST)
+ break;
+ }
+}
+
+static void
+dhdsdio_sdtest_set(dhd_bus_t *bus, bool start)
+{
+ void *pkt;
+ uint8 *data;
+ osl_t *osh = bus->dhd->osh;
+
+ /* Allocate the packet */
+ if (!(pkt = PKTGET(osh, SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN, TRUE))) {
+ DHD_ERROR(("%s: PKTGET failed!\n", __FUNCTION__));
+ return;
+ }
+ PKTALIGN(osh, pkt, (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), DHD_SDALIGN);
+ data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN;
+
+ /* Fill in the test header */
+ *data++ = SDPCM_TEST_SEND;
+ *data++ = start;
+ *data++ = (bus->pktgen_maxlen >> 0);
+ *data++ = (bus->pktgen_maxlen >> 8);
+
+ /* Send it */
+ if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE))
+ bus->pktgen_fail++;
+}
+
+
+static void
+dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq)
+{
+ osl_t *osh = bus->dhd->osh;
+ uint8 *data;
+ uint pktlen;
+
+ uint8 cmd;
+ uint8 extra;
+ uint16 len;
+ uint16 offset;
+
+ /* Check for min length */
+ if ((pktlen = PKTLEN(osh, pkt)) < SDPCM_TEST_HDRLEN) {
+ DHD_ERROR(("dhdsdio_restrcv: toss runt frame, pktlen %d\n", pktlen));
+ PKTFREE(osh, pkt, FALSE);
+ return;
+ }
+
+ /* Extract header fields */
+ data = PKTDATA(osh, pkt);
+ cmd = *data++;
+ extra = *data++;
+ len = *data++; len += *data++ << 8;
+
+ /* Check length for relevant commands */
+ if (cmd == SDPCM_TEST_DISCARD || cmd == SDPCM_TEST_ECHOREQ || cmd == SDPCM_TEST_ECHORSP) {
+ if (pktlen != len + SDPCM_TEST_HDRLEN) {
+ DHD_ERROR(("dhdsdio_testrcv: frame length mismatch, pktlen %d seq %d"
+ " cmd %d extra %d len %d\n", pktlen, seq, cmd, extra, len));
+ PKTFREE(osh, pkt, FALSE);
+ return;
+ }
+ }
+
+ /* Process as per command */
+ switch (cmd) {
+ case SDPCM_TEST_ECHOREQ:
+ /* Rx->Tx turnaround ok (even on NDIS w/current implementation) */
+ *(uint8 *)(PKTDATA(osh, pkt)) = SDPCM_TEST_ECHORSP;
+ if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE) == 0) {
+ bus->pktgen_sent++;
+ } else {
+ bus->pktgen_fail++;
+ PKTFREE(osh, pkt, FALSE);
+ }
+ bus->pktgen_rcvd++;
+ break;
+
+ case SDPCM_TEST_ECHORSP:
+ if (bus->ext_loop) {
+ PKTFREE(osh, pkt, FALSE);
+ bus->pktgen_rcvd++;
+ break;
+ }
+
+ for (offset = 0; offset < len; offset++, data++) {
+ if (*data != SDPCM_TEST_FILL(offset, extra)) {
+ DHD_ERROR(("dhdsdio_testrcv: echo data mismatch: "
+ "offset %d (len %d) expect 0x%02x rcvd 0x%02x\n",
+ offset, len, SDPCM_TEST_FILL(offset, extra), *data));
+ break;
+ }
+ }
+ PKTFREE(osh, pkt, FALSE);
+ bus->pktgen_rcvd++;
+ break;
+
+ case SDPCM_TEST_DISCARD:
+ PKTFREE(osh, pkt, FALSE);
+ bus->pktgen_rcvd++;
+ break;
+
+ case SDPCM_TEST_BURST:
+ case SDPCM_TEST_SEND:
+ default:
+ DHD_INFO(("dhdsdio_testrcv: unsupported or unknown command, pktlen %d seq %d"
+ " cmd %d extra %d len %d\n", pktlen, seq, cmd, extra, len));
+ PKTFREE(osh, pkt, FALSE);
+ break;
+ }
+
+ /* For recv mode, stop at limie (and tell dongle to stop sending) */
+ if (bus->pktgen_mode == DHD_PKTGEN_RECV) {
+ if (bus->pktgen_total && (bus->pktgen_rcvd >= bus->pktgen_total)) {
+ bus->pktgen_count = 0;
+ dhdsdio_sdtest_set(bus, FALSE);
+ }
+ }
+}
+#endif /* SDTEST */
+
+extern bool
+dhd_bus_watchdog(dhd_pub_t *dhdp)
+{
+ dhd_bus_t *bus;
+
+ DHD_TIMER(("%s: Enter\n", __FUNCTION__));
+
+ bus = dhdp->bus;
+
+ if (bus->dhd->dongle_reset)
+ return FALSE;
+
+ /* Ignore the timer if simulating bus down */
+ if (bus->sleeping)
+ return FALSE;
+
+ /* Poll period: check device if appropriate. */
+ if (bus->poll && (++bus->polltick >= bus->pollrate)) {
+ uint32 intstatus = 0;
+
+ /* Reset poll tick */
+ bus->polltick = 0;
+
+ /* Check device if no interrupts */
+ if (!bus->intr || (bus->intrcount == bus->lastintrs)) {
+
+ if (!bus->dpc_sched) {
+ uint8 devpend;
+ devpend = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0,
+ SDIOD_CCCR_INTPEND, NULL);
+ intstatus = devpend & (INTR_STATUS_FUNC1 | INTR_STATUS_FUNC2);
+ }
+
+ /* If there is something, make like the ISR and schedule the DPC */
+ if (intstatus) {
+ bus->pollcnt++;
+ bus->ipend = TRUE;
+ if (bus->intr) {
+ bcmsdh_intr_disable(bus->sdh);
+ }
+ bus->dpc_sched = TRUE;
+ dhd_sched_dpc(bus->dhd);
+
+ }
+ }
+
+ /* Update interrupt tracking */
+ bus->lastintrs = bus->intrcount;
+ }
+
+#ifdef DHD_DEBUG
+ /* Poll for console output periodically */
+ if (dhdp->busstate == DHD_BUS_DATA && dhd_console_ms != 0) {
+ bus->console.count += dhd_watchdog_ms;
+ if (bus->console.count >= dhd_console_ms) {
+ bus->console.count -= dhd_console_ms;
+ /* Make sure backplane clock is on */
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+ if (dhdsdio_readconsole(bus) < 0)
+ dhd_console_ms = 0; /* On error, stop trying */
+ }
+ }
+#endif /* DHD_DEBUG */
+
+#ifdef SDTEST
+ /* Generate packets if configured */
+ if (bus->pktgen_count && (++bus->pktgen_tick >= bus->pktgen_freq)) {
+ /* Make sure backplane clock is on */
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+ bus->pktgen_tick = 0;
+ dhdsdio_pktgen(bus);
+ }
+#endif
+
+ /* On idle timeout clear activity flag and/or turn off clock */
+ if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
+ if (++bus->idlecount >= bus->idletime) {
+ bus->idlecount = 0;
+ if (bus->activity) {
+ bus->activity = FALSE;
+ dhdsdio_clkctl(bus, CLK_NONE, FALSE);
+ }
+ }
+ }
+
+ return bus->ipend;
+}
+
+#ifdef DHD_DEBUG
+extern int
+dhd_bus_console_in(dhd_pub_t *dhdp, uchar *msg, uint msglen)
+{
+ dhd_bus_t *bus = dhdp->bus;
+ uint32 addr, val;
+ int rv;
+ void *pkt;
+
+ /* Address could be zero if CONSOLE := 0 in dongle Makefile */
+ if (bus->console_addr == 0)
+ return BCME_UNSUPPORTED;
+
+ /* Exclusive bus access */
+ dhd_os_sdlock(bus->dhd);
+
+ /* Don't allow input if dongle is in reset */
+ if (bus->dhd->dongle_reset) {
+ dhd_os_sdunlock(bus->dhd);
+ return BCME_NOTREADY;
+ }
+
+ /* Request clock to allow SDIO accesses */
+ BUS_WAKE(bus);
+ /* No pend allowed since txpkt is called later, ht clk has to be on */
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+ /* Zero cbuf_index */
+ addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf_idx);
+ val = htol32(0);
+ if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0)
+ goto done;
+
+ /* Write message into cbuf */
+ addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf);
+ if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)msg, msglen)) < 0)
+ goto done;
+
+ /* Write length into vcons_in */
+ addr = bus->console_addr + OFFSETOF(hndrte_cons_t, vcons_in);
+ val = htol32(msglen);
+ if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0)
+ goto done;
+
+ /* Bump dongle by sending an empty event pkt.
+ * sdpcm_sendup (RX) checks for virtual console input.
+ */
+ if (((pkt = PKTGET(bus->dhd->osh, 4 + SDPCM_RESERVE, TRUE)) != NULL) &&
+ bus->clkstate == CLK_AVAIL)
+ dhdsdio_txpkt(bus, pkt, SDPCM_EVENT_CHANNEL, TRUE);
+
+done:
+ if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+ bus->activity = FALSE;
+ dhdsdio_clkctl(bus, CLK_NONE, TRUE);
+ }
+
+ dhd_os_sdunlock(bus->dhd);
+
+ return rv;
+}
+#endif /* DHD_DEBUG */
+
+#ifdef DHD_DEBUG
+static void
+dhd_dump_cis(uint fn, uint8 *cis)
+{
+ uint byte, tag, tdata;
+ DHD_INFO(("Function %d CIS:\n", fn));
+
+ for (tdata = byte = 0; byte < SBSDIO_CIS_SIZE_LIMIT; byte++) {
+ if ((byte % 16) == 0)
+ DHD_INFO((" "));
+ DHD_INFO(("%02x ", cis[byte]));
+ if ((byte % 16) == 15)
+ DHD_INFO(("\n"));
+ if (!tdata--) {
+ tag = cis[byte];
+ if (tag == 0xff)
+ break;
+ else if (!tag)
+ tdata = 0;
+ else if ((byte + 1) < SBSDIO_CIS_SIZE_LIMIT)
+ tdata = cis[byte + 1] + 1;
+ else
+ DHD_INFO(("]"));
+ }
+ }
+ if ((byte % 16) != 15)
+ DHD_INFO(("\n"));
+}
+#endif /* DHD_DEBUG */
+
+static bool
+dhdsdio_chipmatch(uint16 chipid)
+{
+ if (chipid == BCM4325_CHIP_ID)
+ return TRUE;
+ if (chipid == BCM4329_CHIP_ID)
+ return TRUE;
+ if (chipid == BCM4315_CHIP_ID)
+ return TRUE;
+ if (chipid == BCM4319_CHIP_ID)
+ return TRUE;
+ return FALSE;
+}
+
+static void *
+dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot,
+ uint16 func, uint bustype, void *regsva, osl_t * osh, void *sdh)
+{
+ int ret;
+ dhd_bus_t *bus;
+
+ /* Init global variables at run-time, not as part of the declaration.
+ * This is required to support init/de-init of the driver. Initialization
+ * of globals as part of the declaration results in non-deterministic
+ * behavior since the value of the globals may be different on the
+ * first time that the driver is initialized vs subsequent initializations.
+ */
+ dhd_txbound = DHD_TXBOUND;
+ dhd_rxbound = DHD_RXBOUND;
+ dhd_alignctl = TRUE;
+ sd1idle = TRUE;
+ dhd_readahead = TRUE;
+ retrydata = FALSE;
+ dhd_doflow = FALSE;
+ dhd_dongle_memsize = 0;
+ dhd_txminmax = DHD_TXMINMAX;
+
+ forcealign = TRUE;
+
+
+ dhd_common_init();
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+ DHD_INFO(("%s: venid 0x%04x devid 0x%04x\n", __FUNCTION__, venid, devid));
+
+ /* We make assumptions about address window mappings */
+ ASSERT((uintptr)regsva == SI_ENUM_BASE);
+
+ /* BCMSDH passes venid and devid based on CIS parsing -- but low-power start
+ * means early parse could fail, so here we should get either an ID
+ * we recognize OR (-1) indicating we must request power first.
+ */
+ /* Check the Vendor ID */
+ switch (venid) {
+ case 0x0000:
+ case VENDOR_BROADCOM:
+ break;
+ default:
+ DHD_ERROR(("%s: unknown vendor: 0x%04x\n",
+ __FUNCTION__, venid));
+ return NULL;
+ }
+
+ /* Check the Device ID and make sure it's one that we support */
+ switch (devid) {
+ case BCM4325_D11DUAL_ID: /* 4325 802.11a/g id */
+ case BCM4325_D11G_ID: /* 4325 802.11g 2.4Ghz band id */
+ case BCM4325_D11A_ID: /* 4325 802.11a 5Ghz band id */
+ DHD_INFO(("%s: found 4325 Dongle\n", __FUNCTION__));
+ break;
+ case BCM4329_D11NDUAL_ID: /* 4329 802.11n dualband device */
+ case BCM4329_D11N2G_ID: /* 4329 802.11n 2.4G device */
+ case BCM4329_D11N5G_ID: /* 4329 802.11n 5G device */
+ case 0x4329:
+ DHD_INFO(("%s: found 4329 Dongle\n", __FUNCTION__));
+ break;
+ case BCM4315_D11DUAL_ID: /* 4315 802.11a/g id */
+ case BCM4315_D11G_ID: /* 4315 802.11g id */
+ case BCM4315_D11A_ID: /* 4315 802.11a id */
+ DHD_INFO(("%s: found 4315 Dongle\n", __FUNCTION__));
+ break;
+ case BCM4319_D11N_ID: /* 4319 802.11n id */
+ case BCM4319_D11N2G_ID: /* 4319 802.11n2g id */
+ case BCM4319_D11N5G_ID: /* 4319 802.11n5g id */
+ DHD_INFO(("%s: found 4319 Dongle\n", __FUNCTION__));
+ break;
+ case 0:
+ DHD_INFO(("%s: allow device id 0, will check chip internals\n",
+ __FUNCTION__));
+ break;
+
+ default:
+ DHD_ERROR(("%s: skipping 0x%04x/0x%04x, not a dongle\n",
+ __FUNCTION__, venid, devid));
+ return NULL;
+ }
+
+ if (osh == NULL) {
+ /* Ask the OS interface part for an OSL handle */
+ if (!(osh = dhd_osl_attach(sdh, DHD_BUS))) {
+ DHD_ERROR(("%s: osl_attach failed!\n", __FUNCTION__));
+ return NULL;
+ }
+ }
+
+ /* Allocate private bus interface state */
+ if (!(bus = MALLOC(osh, sizeof(dhd_bus_t)))) {
+ DHD_ERROR(("%s: MALLOC of dhd_bus_t failed\n", __FUNCTION__));
+ goto fail;
+ }
+ bzero(bus, sizeof(dhd_bus_t));
+ bus->sdh = sdh;
+ bus->cl_devid = (uint16)devid;
+ bus->bus = DHD_BUS;
+ bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
+ bus->usebufpool = FALSE; /* Use bufpool if allocated, else use locally malloced rxbuf */
+
+ /* attempt to attach to the dongle */
+ if (!(dhdsdio_probe_attach(bus, osh, sdh, regsva, devid))) {
+ DHD_ERROR(("%s: dhdsdio_probe_attach failed\n", __FUNCTION__));
+ goto fail;
+ }
+
+ /* Attach to the dhd/OS/network interface */
+ if (!(bus->dhd = dhd_attach(osh, bus, SDPCM_RESERVE))) {
+ DHD_ERROR(("%s: dhd_attach failed\n", __FUNCTION__));
+ goto fail;
+ }
+
+ /* Allocate buffers */
+ if (!(dhdsdio_probe_malloc(bus, osh, sdh))) {
+ DHD_ERROR(("%s: dhdsdio_probe_malloc failed\n", __FUNCTION__));
+ goto fail;
+ }
+
+ if (!(dhdsdio_probe_init(bus, osh, sdh))) {
+ DHD_ERROR(("%s: dhdsdio_probe_init failed\n", __FUNCTION__));
+ goto fail;
+ }
+
+ /* Register interrupt callback, but mask it (not operational yet). */
+ DHD_INTR(("%s: disable SDIO interrupts (not interested yet)\n", __FUNCTION__));
+ bcmsdh_intr_disable(sdh);
+ if ((ret = bcmsdh_intr_reg(sdh, dhdsdio_isr, bus)) != 0) {
+ DHD_ERROR(("%s: FAILED: bcmsdh_intr_reg returned %d\n",
+ __FUNCTION__, ret));
+ goto fail;
+ }
+ DHD_INTR(("%s: registered SDIO interrupt function ok\n", __FUNCTION__));
+
+ DHD_INFO(("%s: completed!!\n", __FUNCTION__));
+
+
+ /* if firmware path present try to download and bring up bus */
+ if ((ret = dhd_bus_start(bus->dhd)) != 0) {
+#if 1
+ DHD_ERROR(("%s: failed\n", __FUNCTION__));
+ goto fail;
+#else
+ if (ret == BCME_NOTUP) {
+ DHD_ERROR(("%s: dongle is not responding\n", __FUNCTION__));
+ goto fail;
+ }
+#endif
+ }
+ /* Ok, have the per-port tell the stack we're open for business */
+ if (dhd_net_attach(bus->dhd, 0) != 0) {
+ DHD_ERROR(("%s: Net attach failed!!\n", __FUNCTION__));
+ goto fail;
+ }
+
+ return bus;
+
+fail:
+ dhdsdio_release(bus, osh);
+ return NULL;
+}
+
+
+static bool
+dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva,
+ uint16 devid)
+{
+ uint8 clkctl = 0;
+ int err = 0;
+
+ bus->alp_only = TRUE;
+
+ /* Return the window to backplane enumeration space for core access */
+ if (dhdsdio_set_siaddr_window(bus, SI_ENUM_BASE)) {
+ DHD_ERROR(("%s: FAILED to return to SI_ENUM_BASE\n", __FUNCTION__));
+ }
+
+#ifdef DHD_DEBUG
+ printf("F1 signature read @0x18000000=0x%4x\n",
+ bcmsdh_reg_read(bus->sdh, SI_ENUM_BASE, 4));
+
+
+#endif /* DHD_DEBUG */
+
+
+ /* Force PLL off until si_attach() programs PLL control regs */
+
+
+
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, DHD_INIT_CLKCTL1, &err);
+ if (!err)
+ clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+
+ if (err || ((clkctl & ~SBSDIO_AVBITS) != DHD_INIT_CLKCTL1)) {
+ DHD_ERROR(("dhdsdio_probe: ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n",
+ err, DHD_INIT_CLKCTL1, clkctl));
+ goto fail;
+ }
+
+
+#ifdef DHD_DEBUG
+ if (DHD_INFO_ON()) {
+ uint fn, numfn;
+ uint8 *cis[SDIOD_MAX_IOFUNCS];
+ int err = 0;
+
+ numfn = bcmsdh_query_iofnum(sdh);
+ ASSERT(numfn <= SDIOD_MAX_IOFUNCS);
+
+ /* Make sure ALP is available before trying to read CIS */
+ SPINWAIT(((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
+ !SBSDIO_ALPAV(clkctl)), PMU_MAX_TRANSITION_DLY);
+
+ /* Now request ALP be put on the bus */
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+ DHD_INIT_CLKCTL2, &err);
+ OSL_DELAY(65);
+
+ for (fn = 0; fn <= numfn; fn++) {
+ if (!(cis[fn] = MALLOC(osh, SBSDIO_CIS_SIZE_LIMIT))) {
+ DHD_INFO(("dhdsdio_probe: fn %d cis malloc failed\n", fn));
+ break;
+ }
+ bzero(cis[fn], SBSDIO_CIS_SIZE_LIMIT);
+
+ if ((err = bcmsdh_cis_read(sdh, fn, cis[fn], SBSDIO_CIS_SIZE_LIMIT))) {
+ DHD_INFO(("dhdsdio_probe: fn %d cis read err %d\n", fn, err));
+ MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT);
+ break;
+ }
+ dhd_dump_cis(fn, cis[fn]);
+ }
+
+ while (fn-- > 0) {
+ ASSERT(cis[fn]);
+ MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT);
+ }
+
+ if (err) {
+ DHD_ERROR(("dhdsdio_probe: failure reading or parsing CIS\n"));
+ goto fail;
+ }
+ }
+#endif /* DHD_DEBUG */
+
+ /* si_attach() will provide an SI handle and scan the backplane */
+ if (!(bus->sih = si_attach((uint)devid, osh, regsva, DHD_BUS, sdh,
+ &bus->vars, &bus->varsz))) {
+ DHD_ERROR(("%s: si_attach failed!\n", __FUNCTION__));
+ goto fail;
+ }
+
+ bcmsdh_chipinfo(sdh, bus->sih->chip, bus->sih->chiprev);
+
+ if (!dhdsdio_chipmatch((uint16)bus->sih->chip)) {
+ DHD_ERROR(("%s: unsupported chip: 0x%04x\n",
+ __FUNCTION__, bus->sih->chip));
+ goto fail;
+ }
+
+ si_sdiod_drive_strength_init(bus->sih, osh, dhd_sdiod_drive_strength);
+
+
+ /* Get info on the ARM and SOCRAM cores... */
+ if (!DHD_NOPMU(bus)) {
+ if ((si_setcore(bus->sih, ARM7S_CORE_ID, 0)) ||
+ (si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) {
+ bus->armrev = si_corerev(bus->sih);
+ } else {
+ DHD_ERROR(("%s: failed to find ARM core!\n", __FUNCTION__));
+ goto fail;
+ }
+ if (!(bus->orig_ramsize = si_socram_size(bus->sih))) {
+ DHD_ERROR(("%s: failed to find SOCRAM memory!\n", __FUNCTION__));
+ goto fail;
+ }
+ bus->ramsize = bus->orig_ramsize;
+ if (dhd_dongle_memsize)
+ dhd_dongle_setmemsize(bus, dhd_dongle_memsize);
+
+ DHD_ERROR(("DHD: dongle ram size is set to %d(orig %d)\n",
+ bus->ramsize, bus->orig_ramsize));
+ }
+
+ /* ...but normally deal with the SDPCMDEV core */
+ if (!(bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0)) &&
+ !(bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0))) {
+ DHD_ERROR(("%s: failed to find SDIODEV core!\n", __FUNCTION__));
+ goto fail;
+ }
+ bus->sdpcmrev = si_corerev(bus->sih);
+
+ /* Set core control so an SDIO reset does a backplane reset */
+ OR_REG(osh, &bus->regs->corecontrol, CC_BPRESEN);
+
+ pktq_init(&bus->txq, (PRIOMASK + 1), QLEN);
+
+ /* Locate an appropriately-aligned portion of hdrbuf */
+ bus->rxhdr = (uint8 *)ROUNDUP((uintptr)&bus->hdrbuf[0], DHD_SDALIGN);
+
+ /* Set the poll and/or interrupt flags */
+ bus->intr = (bool)dhd_intr;
+ if ((bus->poll = (bool)dhd_poll))
+ bus->pollrate = 1;
+
+ return TRUE;
+
+fail:
+ return FALSE;
+}
+
+static bool
+dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh)
+{
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+#ifndef DHD_USE_STATIC_BUF
+ if (bus->dhd->maxctl) {
+ bus->rxblen = ROUNDUP((bus->dhd->maxctl + SDPCM_HDRLEN), ALIGNMENT) + DHD_SDALIGN;
+ if (!(bus->rxbuf = MALLOC(osh, bus->rxblen))) {
+ DHD_ERROR(("%s: MALLOC of %d-byte rxbuf failed\n",
+ __FUNCTION__, bus->rxblen));
+ goto fail;
+ }
+ }
+
+ /* Allocate buffer to receive glomed packet */
+ if (!(bus->databuf = MALLOC(osh, MAX_DATA_BUF))) {
+ DHD_ERROR(("%s: MALLOC of %d-byte databuf failed\n",
+ __FUNCTION__, MAX_DATA_BUF));
+ /* release rxbuf which was already located as above */
+ if (!bus->rxblen) MFREE(osh, bus->rxbuf, bus->rxblen);
+ goto fail;
+ }
+#else
+ if (bus->dhd->maxctl) {
+ bus->rxblen = ROUNDUP((bus->dhd->maxctl + SDPCM_HDRLEN), ALIGNMENT) + DHD_SDALIGN;
+ if (!(bus->rxbuf = dhd_os_prealloc(DHD_PREALLOC_RXBUF, bus->rxblen))) {
+ DHD_ERROR(("%s: MALLOC of %d-byte rxbuf failed\n",
+ __FUNCTION__, bus->rxblen));
+ goto fail;
+ }
+ }
+ /* Allocate buffer to receive glomed packet */
+ if (!(bus->databuf = dhd_os_prealloc(DHD_PREALLOC_DATABUF, MAX_DATA_BUF))) {
+ DHD_ERROR(("%s: MALLOC of %d-byte databuf failed\n",
+ __FUNCTION__, MAX_DATA_BUF));
+ goto fail;
+ }
+#endif /* DHD_USE_STATIC_BUF */
+
+ /* Align the buffer */
+ if ((uintptr)bus->databuf % DHD_SDALIGN)
+ bus->dataptr = bus->databuf + (DHD_SDALIGN - ((uintptr)bus->databuf % DHD_SDALIGN));
+ else
+ bus->dataptr = bus->databuf;
+
+ return TRUE;
+
+fail:
+ return FALSE;
+}
+
+
+static bool
+dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh)
+{
+ int32 fnum;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+#ifdef SDTEST
+ dhdsdio_pktgen_init(bus);
+#endif /* SDTEST */
+
+ /* Disable F2 to clear any intermediate frame state on the dongle */
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1, NULL);
+
+ bus->dhd->busstate = DHD_BUS_DOWN;
+ bus->sleeping = FALSE;
+ bus->rxflow = FALSE;
+ bus->prev_rxlim_hit = 0;
+
+
+ /* Done with backplane-dependent accesses, can drop clock... */
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
+
+ /* ...and initialize clock/power states */
+ bus->clkstate = CLK_SDONLY;
+ bus->idletime = (int32)dhd_idletime;
+ bus->idleclock = DHD_IDLE_ACTIVE;
+
+ /* Query the SD clock speed */
+ if (bcmsdh_iovar_op(sdh, "sd_divisor", NULL, 0,
+ &bus->sd_divisor, sizeof(int32), FALSE) != BCME_OK) {
+ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_divisor"));
+ bus->sd_divisor = -1;
+ } else {
+ DHD_INFO(("%s: Initial value for %s is %d\n",
+ __FUNCTION__, "sd_divisor", bus->sd_divisor));
+ }
+
+ /* Query the SD bus mode */
+ if (bcmsdh_iovar_op(sdh, "sd_mode", NULL, 0,
+ &bus->sd_mode, sizeof(int32), FALSE) != BCME_OK) {
+ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_mode"));
+ bus->sd_mode = -1;
+ } else {
+ DHD_INFO(("%s: Initial value for %s is %d\n",
+ __FUNCTION__, "sd_mode", bus->sd_mode));
+ }
+
+ /* Query the F2 block size, set roundup accordingly */
+ fnum = 2;
+ if (bcmsdh_iovar_op(sdh, "sd_blocksize", &fnum, sizeof(int32),
+ &bus->blocksize, sizeof(int32), FALSE) != BCME_OK) {
+ bus->blocksize = 0;
+ DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_blocksize"));
+ } else {
+ DHD_INFO(("%s: Initial value for %s is %d\n",
+ __FUNCTION__, "sd_blocksize", bus->blocksize));
+ }
+ bus->roundup = MIN(max_roundup, bus->blocksize);
+
+ /* Query if bus module supports packet chaining, default to use if supported */
+ if (bcmsdh_iovar_op(sdh, "sd_rxchain", NULL, 0,
+ &bus->sd_rxchain, sizeof(int32), FALSE) != BCME_OK) {
+ bus->sd_rxchain = FALSE;
+ } else {
+ DHD_INFO(("%s: bus module (through bcmsdh API) %s chaining\n",
+ __FUNCTION__, (bus->sd_rxchain ? "supports" : "does not support")));
+ }
+ bus->use_rxchain = (bool)bus->sd_rxchain;
+
+ return TRUE;
+}
+
+bool
+dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh,
+ char *fw_path, char *nv_path)
+{
+ bool ret;
+ bus->fw_path = fw_path;
+ bus->nv_path = nv_path;
+
+ ret = dhdsdio_download_firmware(bus, osh, bus->sdh);
+
+ return ret;
+}
+
+static bool
+dhdsdio_download_firmware(struct dhd_bus *bus, osl_t *osh, void *sdh)
+{
+ bool ret;
+
+ /* Download the firmware */
+ dhd_os_wake_lock(bus->dhd);
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+
+ ret = _dhdsdio_download_firmware(bus) == 0;
+
+ dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
+ dhd_os_wake_unlock(bus->dhd);
+ return ret;
+}
+
+/* Detach and free everything */
+static void
+dhdsdio_release(dhd_bus_t *bus, osl_t *osh)
+{
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (bus) {
+ ASSERT(osh);
+
+
+ /* De-register interrupt handler */
+ bcmsdh_intr_disable(bus->sdh);
+ bcmsdh_intr_dereg(bus->sdh);
+
+ if (bus->dhd) {
+
+ dhdsdio_release_dongle(bus, osh, TRUE);
+
+ dhd_detach(bus->dhd);
+ bus->dhd = NULL;
+ }
+
+ dhdsdio_release_malloc(bus, osh);
+
+
+ MFREE(osh, bus, sizeof(dhd_bus_t));
+ }
+
+ if (osh)
+ dhd_osl_detach(osh);
+
+ DHD_TRACE(("%s: Disconnected\n", __FUNCTION__));
+}
+
+static void
+dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh)
+{
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (bus->dhd && bus->dhd->dongle_reset)
+ return;
+
+ if (bus->rxbuf) {
+#ifndef DHD_USE_STATIC_BUF
+ MFREE(osh, bus->rxbuf, bus->rxblen);
+#endif
+ bus->rxctl = bus->rxbuf = NULL;
+ bus->rxlen = 0;
+ }
+
+ if (bus->databuf) {
+#ifndef DHD_USE_STATIC_BUF
+ MFREE(osh, bus->databuf, MAX_DATA_BUF);
+#endif
+ bus->databuf = NULL;
+ }
+}
+
+
+static void
+dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, int reset_flag)
+{
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if ((bus->dhd && bus->dhd->dongle_reset) && reset_flag)
+ return;
+
+ if (bus->sih) {
+ dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+#if !defined(BCMLXSDMMC)
+ si_watchdog(bus->sih, 4);
+#endif /* !defined(BCMLXSDMMC) */
+ dhdsdio_clkctl(bus, CLK_NONE, FALSE);
+ si_detach(bus->sih);
+ if (bus->vars && bus->varsz)
+ MFREE(osh, bus->vars, bus->varsz);
+ bus->vars = NULL;
+ }
+
+ DHD_TRACE(("%s: Disconnected\n", __FUNCTION__));
+}
+
+static void
+dhdsdio_disconnect(void *ptr)
+{
+ dhd_bus_t *bus = (dhd_bus_t *)ptr;
+
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ if (bus) {
+ ASSERT(bus->dhd);
+ dhdsdio_release(bus, bus->dhd->osh);
+ }
+
+ DHD_TRACE(("%s: Disconnected\n", __FUNCTION__));
+}
+
+
+/* Register/Unregister functions are called by the main DHD entry
+ * point (e.g. module insertion) to link with the bus driver, in
+ * order to look for or await the device.
+ */
+
+static bcmsdh_driver_t dhd_sdio = {
+ dhdsdio_probe,
+ dhdsdio_disconnect
+};
+
+int
+dhd_bus_register(void)
+{
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ return bcmsdh_register(&dhd_sdio);
+}
+
+void
+dhd_bus_unregister(void)
+{
+ DHD_TRACE(("%s: Enter\n", __FUNCTION__));
+
+ bcmsdh_unregister();
+}
+
+#ifdef BCMEMBEDIMAGE
+static int
+dhdsdio_download_code_array(struct dhd_bus *bus)
+{
+ int bcmerror = -1;
+ int offset = 0;
+
+ DHD_INFO(("%s: download embedded firmware...\n", __FUNCTION__));
+
+ /* Download image */
+ while ((offset + MEMBLOCK) < sizeof(dlarray)) {
+ bcmerror = dhdsdio_membytes(bus, TRUE, offset, dlarray + offset, MEMBLOCK);
+ if (bcmerror) {
+ DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n",
+ __FUNCTION__, bcmerror, MEMBLOCK, offset));
+ goto err;
+ }
+
+ offset += MEMBLOCK;
+ }
+
+ if (offset < sizeof(dlarray)) {
+ bcmerror = dhdsdio_membytes(bus, TRUE, offset,
+ dlarray + offset, sizeof(dlarray) - offset);
+ if (bcmerror) {
+ DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n",
+ __FUNCTION__, bcmerror, sizeof(dlarray) - offset, offset));
+ goto err;
+ }
+ }
+
+#ifdef DHD_DEBUG
+ /* Upload and compare the downloaded code */
+ {
+ unsigned char *ularray;
+
+ ularray = MALLOC(bus->dhd->osh, bus->ramsize);
+ /* Upload image to verify downloaded contents. */
+ offset = 0;
+ memset(ularray, 0xaa, bus->ramsize);
+ while ((offset + MEMBLOCK) < sizeof(dlarray)) {
+ bcmerror = dhdsdio_membytes(bus, FALSE, offset, ularray + offset, MEMBLOCK);
+ if (bcmerror) {
+ DHD_ERROR(("%s: error %d on reading %d membytes at 0x%08x\n",
+ __FUNCTION__, bcmerror, MEMBLOCK, offset));
+ goto err;
+ }
+
+ offset += MEMBLOCK;
+ }
+
+ if (offset < sizeof(dlarray)) {
+ bcmerror = dhdsdio_membytes(bus, FALSE, offset,
+ ularray + offset, sizeof(dlarray) - offset);
+ if (bcmerror) {
+ DHD_ERROR(("%s: error %d on reading %d membytes at 0x%08x\n",
+ __FUNCTION__, bcmerror, sizeof(dlarray) - offset, offset));
+ goto err;
+ }
+ }
+
+ if (memcmp(dlarray, ularray, sizeof(dlarray))) {
+ DHD_ERROR(("%s: Downloaded image is corrupted.\n", __FUNCTION__));
+ ASSERT(0);
+ goto err;
+ } else
+ DHD_ERROR(("%s: Download, Upload and compare succeeded.\n", __FUNCTION__));
+
+ MFREE(bus->dhd->osh, ularray, bus->ramsize);
+ }
+#endif /* DHD_DEBUG */
+
+err:
+ return bcmerror;
+}
+#endif /* BCMEMBEDIMAGE */
+
+static int
+dhdsdio_download_code_file(struct dhd_bus *bus, char *fw_path)
+{
+ int bcmerror = -1;
+ int offset = 0;
+ uint len;
+ void *image = NULL;
+ uint8 *memblock = NULL, *memptr;
+
+ DHD_INFO(("%s: download firmware %s\n", __FUNCTION__, fw_path));
+
+ image = dhd_os_open_image(fw_path);
+ if (image == NULL)
+ goto err;
+
+ memptr = memblock = MALLOC(bus->dhd->osh, MEMBLOCK + DHD_SDALIGN);
+ if (memblock == NULL) {
+ DHD_ERROR(("%s: Failed to allocate memory %d bytes\n", __FUNCTION__, MEMBLOCK));
+ goto err;
+ }
+ if ((uint32)(uintptr)memblock % DHD_SDALIGN)
+ memptr += (DHD_SDALIGN - ((uint32)(uintptr)memblock % DHD_SDALIGN));
+
+ /* Download image */
+ while ((len = dhd_os_get_image_block((char*)memptr, MEMBLOCK, image))) {
+ bcmerror = dhdsdio_membytes(bus, TRUE, offset, memptr, len);
+ if (bcmerror) {
+ DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n",
+ __FUNCTION__, bcmerror, MEMBLOCK, offset));
+ goto err;
+ }
+
+ offset += MEMBLOCK;
+ }
+
+err:
+ if (memblock)
+ MFREE(bus->dhd->osh, memblock, MEMBLOCK + DHD_SDALIGN);
+
+ if (image)
+ dhd_os_close_image(image);
+
+ return bcmerror;
+}
+
+/*
+ * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file and ending in a NUL.
+ * Removes carriage returns, empty lines, comment lines, and converts newlines to NULs.
+ * Shortens buffer as needed and pads with NULs. End of buffer is marked by two NULs.
+*/
+
+static uint
+process_nvram_vars(char *varbuf, uint len)
+{
+ char *dp;
+ bool findNewline;
+ int column;
+ uint buf_len, n;
+
+ dp = varbuf;
+
+ findNewline = FALSE;
+ column = 0;
+
+ for (n = 0; n < len; n++) {
+ if (varbuf[n] == 0)
+ break;
+ if (varbuf[n] == '\r')
+ continue;
+ if (findNewline && varbuf[n] != '\n')
+ continue;
+ findNewline = FALSE;
+ if (varbuf[n] == '#') {
+ findNewline = TRUE;
+ continue;
+ }
+ if (varbuf[n] == '\n') {
+ if (column == 0)
+ continue;
+ *dp++ = 0;
+ column = 0;
+ continue;
+ }
+ *dp++ = varbuf[n];
+ column++;
+ }
+ buf_len = dp - varbuf;
+
+ while (dp < varbuf + n)
+ *dp++ = 0;
+
+ return buf_len;
+}
+
+/*
+ EXAMPLE: nvram_array
+ nvram_arry format:
+ name=value
+ Use carriage return at the end of each assignment, and an empty string with
+ carriage return at the end of array.
+
+ For example:
+ unsigned char nvram_array[] = {"name1=value1\n", "name2=value2\n", "\n"};
+ Hex values start with 0x, and mac addr format: xx:xx:xx:xx:xx:xx.
+
+ Search "EXAMPLE: nvram_array" to see how the array is activated.
+*/
+
+void
+dhd_bus_set_nvram_params(struct dhd_bus * bus, const char *nvram_params)
+{
+ bus->nvram_params = nvram_params;
+}
+
+static int
+dhdsdio_download_nvram(struct dhd_bus *bus)
+{
+ int bcmerror = -1;
+ uint len;
+ void * image = NULL;
+ char * memblock = NULL;
+ char *bufp;
+ char *nv_path;
+ bool nvram_file_exists;
+
+ nv_path = bus->nv_path;
+
+ nvram_file_exists = ((nv_path != NULL) && (nv_path[0] != '\0'));
+ if (!nvram_file_exists && (bus->nvram_params == NULL))
+ return (0);
+
+ if (nvram_file_exists) {
+ image = dhd_os_open_image(nv_path);
+ if (image == NULL)
+ goto err;
+ }
+
+ memblock = MALLOC(bus->dhd->osh, MEMBLOCK);
+ if (memblock == NULL) {
+ DHD_ERROR(("%s: Failed to allocate memory %d bytes\n",
+ __FUNCTION__, MEMBLOCK));
+ goto err;
+ }
+
+ /* Download variables */
+ if (nvram_file_exists) {
+ len = dhd_os_get_image_block(memblock, MEMBLOCK, image);
+ }
+ else {
+ len = strlen(bus->nvram_params);
+ ASSERT(len <= MEMBLOCK);
+ if (len > MEMBLOCK)
+ len = MEMBLOCK;
+ memcpy(memblock, bus->nvram_params, len);
+ }
+
+ if (len > 0 && len < MEMBLOCK) {
+ bufp = (char *)memblock;
+ bufp[len] = 0;
+ len = process_nvram_vars(bufp, len);
+ bufp += len;
+ *bufp++ = 0;
+ if (len)
+ bcmerror = dhdsdio_downloadvars(bus, memblock, len + 1);
+ if (bcmerror) {
+ DHD_ERROR(("%s: error downloading vars: %d\n",
+ __FUNCTION__, bcmerror));
+ }
+ }
+ else {
+ DHD_ERROR(("%s: error reading nvram file: %d\n",
+ __FUNCTION__, len));
+ bcmerror = BCME_SDIO_ERROR;
+ }
+
+err:
+ if (memblock)
+ MFREE(bus->dhd->osh, memblock, MEMBLOCK);
+
+ if (image)
+ dhd_os_close_image(image);
+
+ return bcmerror;
+}
+
+static int
+_dhdsdio_download_firmware(struct dhd_bus *bus)
+{
+ int bcmerror = -1;
+
+ bool embed = FALSE; /* download embedded firmware */
+ bool dlok = FALSE; /* download firmware succeeded */
+
+ /* Out immediately if no image to download */
+ if ((bus->fw_path == NULL) || (bus->fw_path[0] == '\0')) {
+#ifdef BCMEMBEDIMAGE
+ embed = TRUE;
+#else
+ return bcmerror;
+#endif
+ }
+
+ /* Keep arm in reset */
+ if (dhdsdio_download_state(bus, TRUE)) {
+ DHD_ERROR(("%s: error placing ARM core in reset\n", __FUNCTION__));
+ goto err;
+ }
+
+ /* External image takes precedence if specified */
+ if ((bus->fw_path != NULL) && (bus->fw_path[0] != '\0')) {
+ if (dhdsdio_download_code_file(bus, bus->fw_path)) {
+ DHD_ERROR(("%s: dongle image file download failed\n", __FUNCTION__));
+#ifdef BCMEMBEDIMAGE
+ embed = TRUE;
+#else
+ goto err;
+#endif
+ }
+ else {
+ embed = FALSE;
+ dlok = TRUE;
+ }
+ }
+#ifdef BCMEMBEDIMAGE
+ if (embed) {
+ if (dhdsdio_download_code_array(bus)) {
+ DHD_ERROR(("%s: dongle image array download failed\n", __FUNCTION__));
+ goto err;
+ }
+ else {
+ dlok = TRUE;
+ }
+ }
+#endif
+ if (!dlok) {
+ DHD_ERROR(("%s: dongle image download failed\n", __FUNCTION__));
+ goto err;
+ }
+
+ /* EXAMPLE: nvram_array */
+ /* If a valid nvram_arry is specified as above, it can be passed down to dongle */
+ /* dhd_bus_set_nvram_params(bus, (char *)&nvram_array); */
+
+ /* External nvram takes precedence if specified */
+ if (dhdsdio_download_nvram(bus)) {
+ DHD_ERROR(("%s: dongle nvram file download failed\n", __FUNCTION__));
+ }
+
+ /* Take arm out of reset */
+ if (dhdsdio_download_state(bus, FALSE)) {
+ DHD_ERROR(("%s: error getting out of ARM core reset\n", __FUNCTION__));
+ goto err;
+ }
+
+ bcmerror = 0;
+
+err:
+ return bcmerror;
+}
+
+static int
+dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes,
+ void *pkt, bcmsdh_cmplt_fn_t complete, void *handle)
+{
+ int status;
+
+ /* 4329: GSPI check */
+ status = bcmsdh_recv_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle);
+ return status;
+}
+
+static int
+dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes,
+ void *pkt, bcmsdh_cmplt_fn_t complete, void *handle)
+{
+ return (bcmsdh_send_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle));
+}
+
+uint
+dhd_bus_chip(struct dhd_bus *bus)
+{
+ ASSERT(bus->sih != NULL);
+ return bus->sih->chip;
+}
+
+void *
+dhd_bus_pub(struct dhd_bus *bus)
+{
+ return bus->dhd;
+}
+
+void *
+dhd_bus_txq(struct dhd_bus *bus)
+{
+ return &bus->txq;
+}
+
+uint
+dhd_bus_hdrlen(struct dhd_bus *bus)
+{
+ return SDPCM_HDRLEN;
+}
+
+int
+dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag)
+{
+ int bcmerror = 0;
+ dhd_bus_t *bus;
+
+ bus = dhdp->bus;
+
+ if (flag == TRUE) {
+ if (!bus->dhd->dongle_reset) {
+ dhd_os_sdlock(dhdp);
+ /* Turning off watchdog */
+ dhd_os_wd_timer(dhdp, 0);
+#if !defined(IGNORE_ETH0_DOWN)
+ /* Force flow control as protection when stop come before ifconfig_down */
+ dhd_txflowcontrol(bus->dhd, 0, ON);
+#endif /* !defined(IGNORE_ETH0_DOWN) */
+ /* Expect app to have torn down any connection before calling */
+ /* Stop the bus, disable F2 */
+ dhd_bus_stop(bus, FALSE);
+#if defined(OOB_INTR_ONLY)
+ bcmsdh_set_irq(FALSE);
+#endif /* defined(OOB_INTR_ONLY) */
+ /* Clean tx/rx buffer pointers, detach from the dongle */
+ dhdsdio_release_dongle(bus, bus->dhd->osh, TRUE);
+
+ bus->dhd->dongle_reset = TRUE;
+ bus->dhd->up = FALSE;
+ dhd_os_sdunlock(dhdp);
+
+ DHD_TRACE(("%s: WLAN OFF DONE\n", __FUNCTION__));
+ /* App can now remove power from device */
+ } else
+ bcmerror = BCME_SDIO_ERROR;
+ } else {
+ /* App must have restored power to device before calling */
+
+ DHD_TRACE(("\n\n%s: == WLAN ON ==\n", __FUNCTION__));
+
+ if (bus->dhd->dongle_reset) {
+ /* Turn on WLAN */
+ dhd_os_sdlock(dhdp);
+
+ /* Reset SD client */
+ bcmsdh_reset(bus->sdh);
+
+ /* Attempt to re-attach & download */
+ if (dhdsdio_probe_attach(bus, bus->dhd->osh, bus->sdh,
+ (uint32 *)SI_ENUM_BASE,
+ bus->cl_devid)) {
+ /* Attempt to download binary to the dongle */
+ if (dhdsdio_probe_init(bus, bus->dhd->osh, bus->sdh) &&
+ dhdsdio_download_firmware(bus, bus->dhd->osh, bus->sdh)) {
+
+ /* Re-init bus, enable F2 transfer */
+ bcmerror = dhd_bus_init((dhd_pub_t *) bus->dhd, FALSE);
+ if (bcmerror == BCME_OK) {
+#if defined(OOB_INTR_ONLY)
+ bcmsdh_set_irq(TRUE);
+ dhd_enable_oob_intr(bus, TRUE);
+#endif /* defined(OOB_INTR_ONLY) */
+ bus->dhd->dongle_reset = FALSE;
+ bus->dhd->up = TRUE;
+#if !defined(IGNORE_ETH0_DOWN)
+ /* Restore flow control */
+ dhd_txflowcontrol(bus->dhd, 0, OFF);
+#endif
+ /* Turning on watchdog back */
+ dhd_os_wd_timer(dhdp, dhd_watchdog_ms);
+
+ DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__));
+ } else {
+ dhd_bus_stop(bus, FALSE);
+ dhdsdio_release_dongle(bus, bus->dhd->osh, FALSE);
+ }
+ } else
+ bcmerror = BCME_SDIO_ERROR;
+ } else
+ bcmerror = BCME_SDIO_ERROR;
+
+ dhd_os_sdunlock(dhdp);
+ } else {
+ bcmerror = BCME_NOTDOWN;
+ DHD_ERROR(("%s: Set DEVRESET=FALSE invoked when device is on\n",
+ __FUNCTION__));
+ bcmerror = BCME_SDIO_ERROR;
+ }
+ }
+ return bcmerror;
+}
diff --git a/drivers/net/wireless/bcm4329/dngl_stats.h b/drivers/net/wireless/bcm4329/dngl_stats.h
new file mode 100644
index 00000000000..e5db54e7edf
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/dngl_stats.h
@@ -0,0 +1,43 @@
+/*
+ * Common stats definitions for clients of dongle
+ * ports
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dngl_stats.h,v 1.2.140.3 2008/05/26 16:52:08 Exp $
+ */
+
+#ifndef _dngl_stats_h_
+#define _dngl_stats_h_
+
+typedef struct {
+ unsigned long rx_packets; /* total packets received */
+ unsigned long tx_packets; /* total packets transmitted */
+ unsigned long rx_bytes; /* total bytes received */
+ unsigned long tx_bytes; /* total bytes transmitted */
+ unsigned long rx_errors; /* bad packets received */
+ unsigned long tx_errors; /* packet transmit problems */
+ unsigned long rx_dropped; /* packets dropped by dongle */
+ unsigned long tx_dropped; /* packets dropped by dongle */
+ unsigned long multicast; /* multicast packets received */
+} dngl_stats_t;
+
+#endif /* _dngl_stats_h_ */
diff --git a/drivers/net/wireless/bcm4329/hndpmu.c b/drivers/net/wireless/bcm4329/hndpmu.c
new file mode 100644
index 00000000000..307347a43bd
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/hndpmu.c
@@ -0,0 +1,131 @@
+/*
+ * Misc utility routines for accessing PMU corerev specific features
+ * of the SiliconBackplane-based Broadcom chips.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: hndpmu.c,v 1.95.2.17.4.11.2.63 2010/07/21 13:55:09 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmdevs.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <hndpmu.h>
+
+/* debug/trace */
+#define PMU_ERROR(args)
+
+#define PMU_MSG(args)
+
+
+/* SDIO Pad drive strength to select value mappings */
+typedef struct {
+ uint8 strength; /* Pad Drive Strength in mA */
+ uint8 sel; /* Chip-specific select value */
+} sdiod_drive_str_t;
+
+/* SDIO Drive Strength to sel value table for PMU Rev 1 */
+static const sdiod_drive_str_t sdiod_drive_strength_tab1[] = {
+ {4, 0x2},
+ {2, 0x3},
+ {1, 0x0},
+ {0, 0x0} };
+
+/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */
+static const sdiod_drive_str_t sdiod_drive_strength_tab2[] = {
+ {12, 0x7},
+ {10, 0x6},
+ {8, 0x5},
+ {6, 0x4},
+ {4, 0x2},
+ {2, 0x1},
+ {0, 0x0} };
+
+#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
+
+void
+si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength)
+{
+ chipcregs_t *cc;
+ uint origidx, intr_val = 0;
+ sdiod_drive_str_t *str_tab = NULL;
+ uint32 str_mask = 0;
+ uint32 str_shift = 0;
+
+ if (!(sih->cccaps & CC_CAP_PMU)) {
+ return;
+ }
+
+ /* Remember original core before switch to chipc */
+ cc = (chipcregs_t *) si_switch_core(sih, CC_CORE_ID, &origidx, &intr_val);
+
+ switch (SDIOD_DRVSTR_KEY(sih->chip, sih->pmurev)) {
+ case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 1):
+ str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab1;
+ str_mask = 0x30000000;
+ str_shift = 28;
+ break;
+ case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 2):
+ case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 3):
+ case SDIOD_DRVSTR_KEY(BCM4315_CHIP_ID, 4):
+ str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab2;
+ str_mask = 0x00003800;
+ str_shift = 11;
+ break;
+
+ default:
+ PMU_MSG(("No SDIO Drive strength init done for chip %x rev %d pmurev %d\n",
+ sih->chip, sih->chiprev, sih->pmurev));
+
+ break;
+ }
+
+ if (str_tab != NULL) {
+ uint32 drivestrength_sel = 0;
+ uint32 cc_data_temp;
+ int i;
+
+ for (i = 0; str_tab[i].strength != 0; i ++) {
+ if (drivestrength >= str_tab[i].strength) {
+ drivestrength_sel = str_tab[i].sel;
+ break;
+ }
+ }
+
+ W_REG(osh, &cc->chipcontrol_addr, 1);
+ cc_data_temp = R_REG(osh, &cc->chipcontrol_data);
+ cc_data_temp &= ~str_mask;
+ drivestrength_sel <<= str_shift;
+ cc_data_temp |= drivestrength_sel;
+ W_REG(osh, &cc->chipcontrol_data, cc_data_temp);
+
+ PMU_MSG(("SDIO: %dmA drive strength selected, set to 0x%08x\n",
+ drivestrength, cc_data_temp));
+ }
+
+ /* Return to original core */
+ si_restore_core(sih, origidx, intr_val);
+}
diff --git a/drivers/net/wireless/bcm4329/include/Makefile b/drivers/net/wireless/bcm4329/include/Makefile
new file mode 100644
index 00000000000..439ead14a0e
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/Makefile
@@ -0,0 +1,21 @@
+#
+# include/Makefile
+#
+# Copyright 2005, Broadcom, Inc.
+#
+# $Id: Makefile,v 13.5 2005/02/17 19:11:31 Exp $
+#
+
+SRCBASE = ..
+
+TARGETS = epivers.h
+
+
+all release:
+ bash epivers.sh
+
+clean:
+ rm -rf ${TARGETS} *.prev
+
+
+.PHONY: all release clean
diff --git a/drivers/net/wireless/bcm4329/include/aidmp.h b/drivers/net/wireless/bcm4329/include/aidmp.h
new file mode 100644
index 00000000000..a927e5dae58
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/aidmp.h
@@ -0,0 +1,368 @@
+/*
+ * Broadcom AMBA Interconnect definitions.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: aidmp.h,v 13.2.10.1 2008/05/07 20:32:12 Exp $
+ */
+
+
+#ifndef _AIDMP_H
+#define _AIDMP_H
+
+
+#define MFGID_ARM 0x43b
+#define MFGID_BRCM 0x4bf
+#define MFGID_MIPS 0x4a7
+
+
+#define CC_SIM 0
+#define CC_EROM 1
+#define CC_CORESIGHT 9
+#define CC_VERIF 0xb
+#define CC_OPTIMO 0xd
+#define CC_GEN 0xe
+#define CC_PRIMECELL 0xf
+
+
+#define ER_EROMENTRY 0x000
+#define ER_REMAPCONTROL 0xe00
+#define ER_REMAPSELECT 0xe04
+#define ER_MASTERSELECT 0xe10
+#define ER_ITCR 0xf00
+#define ER_ITIP 0xf04
+
+
+#define ER_TAG 0xe
+#define ER_TAG1 0x6
+#define ER_VALID 1
+#define ER_CI 0
+#define ER_MP 2
+#define ER_ADD 4
+#define ER_END 0xe
+#define ER_BAD 0xffffffff
+
+
+#define CIA_MFG_MASK 0xfff00000
+#define CIA_MFG_SHIFT 20
+#define CIA_CID_MASK 0x000fff00
+#define CIA_CID_SHIFT 8
+#define CIA_CCL_MASK 0x000000f0
+#define CIA_CCL_SHIFT 4
+
+
+#define CIB_REV_MASK 0xff000000
+#define CIB_REV_SHIFT 24
+#define CIB_NSW_MASK 0x00f80000
+#define CIB_NSW_SHIFT 19
+#define CIB_NMW_MASK 0x0007c000
+#define CIB_NMW_SHIFT 14
+#define CIB_NSP_MASK 0x00003e00
+#define CIB_NSP_SHIFT 9
+#define CIB_NMP_MASK 0x000001f0
+#define CIB_NMP_SHIFT 4
+
+
+#define MPD_MUI_MASK 0x0000ff00
+#define MPD_MUI_SHIFT 8
+#define MPD_MP_MASK 0x000000f0
+#define MPD_MP_SHIFT 4
+
+
+#define AD_ADDR_MASK 0xfffff000
+#define AD_SP_MASK 0x00000f00
+#define AD_SP_SHIFT 8
+#define AD_ST_MASK 0x000000c0
+#define AD_ST_SHIFT 6
+#define AD_ST_SLAVE 0x00000000
+#define AD_ST_BRIDGE 0x00000040
+#define AD_ST_SWRAP 0x00000080
+#define AD_ST_MWRAP 0x000000c0
+#define AD_SZ_MASK 0x00000030
+#define AD_SZ_SHIFT 4
+#define AD_SZ_4K 0x00000000
+#define AD_SZ_8K 0x00000010
+#define AD_SZ_16K 0x00000020
+#define AD_SZ_SZD 0x00000030
+#define AD_AG32 0x00000008
+#define AD_ADDR_ALIGN 0x00000fff
+#define AD_SZ_BASE 0x00001000
+
+
+#define SD_SZ_MASK 0xfffff000
+#define SD_SG32 0x00000008
+#define SD_SZ_ALIGN 0x00000fff
+
+
+#ifndef _LANGUAGE_ASSEMBLY
+
+typedef volatile struct _aidmp {
+ uint32 oobselina30;
+ uint32 oobselina74;
+ uint32 PAD[6];
+ uint32 oobselinb30;
+ uint32 oobselinb74;
+ uint32 PAD[6];
+ uint32 oobselinc30;
+ uint32 oobselinc74;
+ uint32 PAD[6];
+ uint32 oobselind30;
+ uint32 oobselind74;
+ uint32 PAD[38];
+ uint32 oobselouta30;
+ uint32 oobselouta74;
+ uint32 PAD[6];
+ uint32 oobseloutb30;
+ uint32 oobseloutb74;
+ uint32 PAD[6];
+ uint32 oobseloutc30;
+ uint32 oobseloutc74;
+ uint32 PAD[6];
+ uint32 oobseloutd30;
+ uint32 oobseloutd74;
+ uint32 PAD[38];
+ uint32 oobsynca;
+ uint32 oobseloutaen;
+ uint32 PAD[6];
+ uint32 oobsyncb;
+ uint32 oobseloutben;
+ uint32 PAD[6];
+ uint32 oobsyncc;
+ uint32 oobseloutcen;
+ uint32 PAD[6];
+ uint32 oobsyncd;
+ uint32 oobseloutden;
+ uint32 PAD[38];
+ uint32 oobaextwidth;
+ uint32 oobainwidth;
+ uint32 oobaoutwidth;
+ uint32 PAD[5];
+ uint32 oobbextwidth;
+ uint32 oobbinwidth;
+ uint32 oobboutwidth;
+ uint32 PAD[5];
+ uint32 oobcextwidth;
+ uint32 oobcinwidth;
+ uint32 oobcoutwidth;
+ uint32 PAD[5];
+ uint32 oobdextwidth;
+ uint32 oobdinwidth;
+ uint32 oobdoutwidth;
+ uint32 PAD[37];
+ uint32 ioctrlset;
+ uint32 ioctrlclear;
+ uint32 ioctrl;
+ uint32 PAD[61];
+ uint32 iostatus;
+ uint32 PAD[127];
+ uint32 ioctrlwidth;
+ uint32 iostatuswidth;
+ uint32 PAD[62];
+ uint32 resetctrl;
+ uint32 resetstatus;
+ uint32 resetreadid;
+ uint32 resetwriteid;
+ uint32 PAD[60];
+ uint32 errlogctrl;
+ uint32 errlogdone;
+ uint32 errlogstatus;
+ uint32 errlogaddrlo;
+ uint32 errlogaddrhi;
+ uint32 errlogid;
+ uint32 errloguser;
+ uint32 errlogflags;
+ uint32 PAD[56];
+ uint32 intstatus;
+ uint32 PAD[127];
+ uint32 config;
+ uint32 PAD[63];
+ uint32 itcr;
+ uint32 PAD[3];
+ uint32 itipooba;
+ uint32 itipoobb;
+ uint32 itipoobc;
+ uint32 itipoobd;
+ uint32 PAD[4];
+ uint32 itipoobaout;
+ uint32 itipoobbout;
+ uint32 itipoobcout;
+ uint32 itipoobdout;
+ uint32 PAD[4];
+ uint32 itopooba;
+ uint32 itopoobb;
+ uint32 itopoobc;
+ uint32 itopoobd;
+ uint32 PAD[4];
+ uint32 itopoobain;
+ uint32 itopoobbin;
+ uint32 itopoobcin;
+ uint32 itopoobdin;
+ uint32 PAD[4];
+ uint32 itopreset;
+ uint32 PAD[15];
+ uint32 peripherialid4;
+ uint32 peripherialid5;
+ uint32 peripherialid6;
+ uint32 peripherialid7;
+ uint32 peripherialid0;
+ uint32 peripherialid1;
+ uint32 peripherialid2;
+ uint32 peripherialid3;
+ uint32 componentid0;
+ uint32 componentid1;
+ uint32 componentid2;
+ uint32 componentid3;
+} aidmp_t;
+
+#endif
+
+
+#define OOB_BUSCONFIG 0x020
+#define OOB_STATUSA 0x100
+#define OOB_STATUSB 0x104
+#define OOB_STATUSC 0x108
+#define OOB_STATUSD 0x10c
+#define OOB_ENABLEA0 0x200
+#define OOB_ENABLEA1 0x204
+#define OOB_ENABLEA2 0x208
+#define OOB_ENABLEA3 0x20c
+#define OOB_ENABLEB0 0x280
+#define OOB_ENABLEB1 0x284
+#define OOB_ENABLEB2 0x288
+#define OOB_ENABLEB3 0x28c
+#define OOB_ENABLEC0 0x300
+#define OOB_ENABLEC1 0x304
+#define OOB_ENABLEC2 0x308
+#define OOB_ENABLEC3 0x30c
+#define OOB_ENABLED0 0x380
+#define OOB_ENABLED1 0x384
+#define OOB_ENABLED2 0x388
+#define OOB_ENABLED3 0x38c
+#define OOB_ITCR 0xf00
+#define OOB_ITIPOOBA 0xf10
+#define OOB_ITIPOOBB 0xf14
+#define OOB_ITIPOOBC 0xf18
+#define OOB_ITIPOOBD 0xf1c
+#define OOB_ITOPOOBA 0xf30
+#define OOB_ITOPOOBB 0xf34
+#define OOB_ITOPOOBC 0xf38
+#define OOB_ITOPOOBD 0xf3c
+
+
+#define AI_OOBSELINA30 0x000
+#define AI_OOBSELINA74 0x004
+#define AI_OOBSELINB30 0x020
+#define AI_OOBSELINB74 0x024
+#define AI_OOBSELINC30 0x040
+#define AI_OOBSELINC74 0x044
+#define AI_OOBSELIND30 0x060
+#define AI_OOBSELIND74 0x064
+#define AI_OOBSELOUTA30 0x100
+#define AI_OOBSELOUTA74 0x104
+#define AI_OOBSELOUTB30 0x120
+#define AI_OOBSELOUTB74 0x124
+#define AI_OOBSELOUTC30 0x140
+#define AI_OOBSELOUTC74 0x144
+#define AI_OOBSELOUTD30 0x160
+#define AI_OOBSELOUTD74 0x164
+#define AI_OOBSYNCA 0x200
+#define AI_OOBSELOUTAEN 0x204
+#define AI_OOBSYNCB 0x220
+#define AI_OOBSELOUTBEN 0x224
+#define AI_OOBSYNCC 0x240
+#define AI_OOBSELOUTCEN 0x244
+#define AI_OOBSYNCD 0x260
+#define AI_OOBSELOUTDEN 0x264
+#define AI_OOBAEXTWIDTH 0x300
+#define AI_OOBAINWIDTH 0x304
+#define AI_OOBAOUTWIDTH 0x308
+#define AI_OOBBEXTWIDTH 0x320
+#define AI_OOBBINWIDTH 0x324
+#define AI_OOBBOUTWIDTH 0x328
+#define AI_OOBCEXTWIDTH 0x340
+#define AI_OOBCINWIDTH 0x344
+#define AI_OOBCOUTWIDTH 0x348
+#define AI_OOBDEXTWIDTH 0x360
+#define AI_OOBDINWIDTH 0x364
+#define AI_OOBDOUTWIDTH 0x368
+#define AI_IOCTRLSET 0x400
+#define AI_IOCTRLCLEAR 0x404
+#define AI_IOCTRL 0x408
+#define AI_IOSTATUS 0x500
+#define AI_IOCTRLWIDTH 0x700
+#define AI_IOSTATUSWIDTH 0x704
+#define AI_RESETCTRL 0x800
+#define AI_RESETSTATUS 0x804
+#define AI_RESETREADID 0x808
+#define AI_RESETWRITEID 0x80c
+#define AI_ERRLOGCTRL 0xa00
+#define AI_ERRLOGDONE 0xa04
+#define AI_ERRLOGSTATUS 0xa08
+#define AI_ERRLOGADDRLO 0xa0c
+#define AI_ERRLOGADDRHI 0xa10
+#define AI_ERRLOGID 0xa14
+#define AI_ERRLOGUSER 0xa18
+#define AI_ERRLOGFLAGS 0xa1c
+#define AI_INTSTATUS 0xa00
+#define AI_CONFIG 0xe00
+#define AI_ITCR 0xf00
+#define AI_ITIPOOBA 0xf10
+#define AI_ITIPOOBB 0xf14
+#define AI_ITIPOOBC 0xf18
+#define AI_ITIPOOBD 0xf1c
+#define AI_ITIPOOBAOUT 0xf30
+#define AI_ITIPOOBBOUT 0xf34
+#define AI_ITIPOOBCOUT 0xf38
+#define AI_ITIPOOBDOUT 0xf3c
+#define AI_ITOPOOBA 0xf50
+#define AI_ITOPOOBB 0xf54
+#define AI_ITOPOOBC 0xf58
+#define AI_ITOPOOBD 0xf5c
+#define AI_ITOPOOBAIN 0xf70
+#define AI_ITOPOOBBIN 0xf74
+#define AI_ITOPOOBCIN 0xf78
+#define AI_ITOPOOBDIN 0xf7c
+#define AI_ITOPRESET 0xf90
+#define AI_PERIPHERIALID4 0xfd0
+#define AI_PERIPHERIALID5 0xfd4
+#define AI_PERIPHERIALID6 0xfd8
+#define AI_PERIPHERIALID7 0xfdc
+#define AI_PERIPHERIALID0 0xfe0
+#define AI_PERIPHERIALID1 0xfe4
+#define AI_PERIPHERIALID2 0xfe8
+#define AI_PERIPHERIALID3 0xfec
+#define AI_COMPONENTID0 0xff0
+#define AI_COMPONENTID1 0xff4
+#define AI_COMPONENTID2 0xff8
+#define AI_COMPONENTID3 0xffc
+
+
+#define AIRC_RESET 1
+
+
+#define AICFG_OOB 0x00000020
+#define AICFG_IOS 0x00000010
+#define AICFG_IOC 0x00000008
+#define AICFG_TO 0x00000004
+#define AICFG_ERRL 0x00000002
+#define AICFG_RST 0x00000001
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/bcmcdc.h b/drivers/net/wireless/bcm4329/include/bcmcdc.h
new file mode 100644
index 00000000000..c2a860beab2
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmcdc.h
@@ -0,0 +1,100 @@
+/*
+ * CDC network driver ioctl/indication encoding
+ * Broadcom 802.11abg Networking Device Driver
+ *
+ * Definitions subject to change without notice.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmcdc.h,v 13.14.16.3.16.4 2009/04/12 16:58:45 Exp $
+ */
+#include <proto/ethernet.h>
+
+typedef struct cdc_ioctl {
+ uint32 cmd; /* ioctl command value */
+ uint32 len; /* lower 16: output buflen; upper 16: input buflen (excludes header) */
+ uint32 flags; /* flag defns given below */
+ uint32 status; /* status code returned from the device */
+} cdc_ioctl_t;
+
+/* Max valid buffer size that can be sent to the dongle */
+#define CDC_MAX_MSG_SIZE ETHER_MAX_LEN
+
+/* len field is divided into input and output buffer lengths */
+#define CDCL_IOC_OUTLEN_MASK 0x0000FFFF /* maximum or expected response length, */
+ /* excluding IOCTL header */
+#define CDCL_IOC_OUTLEN_SHIFT 0
+#define CDCL_IOC_INLEN_MASK 0xFFFF0000 /* input buffer length, excluding IOCTL header */
+#define CDCL_IOC_INLEN_SHIFT 16
+
+/* CDC flag definitions */
+#define CDCF_IOC_ERROR 0x01 /* 0=success, 1=ioctl cmd failed */
+#define CDCF_IOC_SET 0x02 /* 0=get, 1=set cmd */
+#define CDCF_IOC_IF_MASK 0xF000 /* I/F index */
+#define CDCF_IOC_IF_SHIFT 12
+#define CDCF_IOC_ID_MASK 0xFFFF0000 /* used to uniquely id an ioctl req/resp pairing */
+#define CDCF_IOC_ID_SHIFT 16 /* # of bits of shift for ID Mask */
+
+#define CDC_IOC_IF_IDX(flags) (((flags) & CDCF_IOC_IF_MASK) >> CDCF_IOC_IF_SHIFT)
+#define CDC_IOC_ID(flags) (((flags) & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT)
+
+#define CDC_GET_IF_IDX(hdr) \
+ ((int)((((hdr)->flags) & CDCF_IOC_IF_MASK) >> CDCF_IOC_IF_SHIFT))
+#define CDC_SET_IF_IDX(hdr, idx) \
+ ((hdr)->flags = (((hdr)->flags & ~CDCF_IOC_IF_MASK) | ((idx) << CDCF_IOC_IF_SHIFT)))
+
+/*
+ * BDC header
+ *
+ * The BDC header is used on data packets to convey priority across USB.
+ */
+
+#define BDC_HEADER_LEN 4
+
+#define BDC_PROTO_VER 1 /* Protocol version */
+
+#define BDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */
+#define BDC_FLAG_VER_SHIFT 4 /* Protocol version shift */
+
+#define BDC_FLAG__UNUSED 0x03 /* Unassigned */
+#define BDC_FLAG_SUM_GOOD 0x04 /* Dongle has verified good RX checksums */
+#define BDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */
+
+#define BDC_PRIORITY_MASK 0x7
+
+#define BDC_FLAG2_FC_FLAG 0x10 /* flag to indicate if pkt contains */
+ /* FLOW CONTROL info only */
+#define BDC_PRIORITY_FC_SHIFT 4 /* flow control info shift */
+
+#define BDC_FLAG2_IF_MASK 0x0f /* APSTA: interface on which the packet was received */
+#define BDC_FLAG2_IF_SHIFT 0
+
+#define BDC_GET_IF_IDX(hdr) \
+ ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT))
+#define BDC_SET_IF_IDX(hdr, idx) \
+ ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | ((idx) << BDC_FLAG2_IF_SHIFT)))
+
+struct bdc_header {
+ uint8 flags; /* Flags */
+ uint8 priority; /* 802.1d Priority 0:2 bits, 4:7 flow control info for usb */
+ uint8 flags2;
+ uint8 rssi;
+};
diff --git a/drivers/net/wireless/bcm4329/include/bcmdefs.h b/drivers/net/wireless/bcm4329/include/bcmdefs.h
new file mode 100644
index 00000000000..f4e99461971
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmdefs.h
@@ -0,0 +1,114 @@
+/*
+ * Misc system wide definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: bcmdefs.h,v 13.38.4.10.2.7.6.11 2010/02/01 05:51:55 Exp $
+ */
+
+
+#ifndef _bcmdefs_h_
+#define _bcmdefs_h_
+
+#define STATIC static
+
+#define SI_BUS 0
+#define PCI_BUS 1
+#define PCMCIA_BUS 2
+#define SDIO_BUS 3
+#define JTAG_BUS 4
+#define USB_BUS 5
+#define SPI_BUS 6
+
+
+#ifdef BCMBUSTYPE
+#define BUSTYPE(bus) (BCMBUSTYPE)
+#else
+#define BUSTYPE(bus) (bus)
+#endif
+
+
+#ifdef BCMCHIPTYPE
+#define CHIPTYPE(bus) (BCMCHIPTYPE)
+#else
+#define CHIPTYPE(bus) (bus)
+#endif
+
+
+
+#if defined(BCMSPROMBUS)
+#define SPROMBUS (BCMSPROMBUS)
+#elif defined(SI_PCMCIA_SROM)
+#define SPROMBUS (PCMCIA_BUS)
+#else
+#define SPROMBUS (PCI_BUS)
+#endif
+
+
+#ifdef BCMCHIPID
+#define CHIPID(chip) (BCMCHIPID)
+#else
+#define CHIPID(chip) (chip)
+#endif
+
+
+#define DMADDR_MASK_32 0x0
+#define DMADDR_MASK_30 0xc0000000
+#define DMADDR_MASK_0 0xffffffff
+
+#define DMADDRWIDTH_30 30
+#define DMADDRWIDTH_32 32
+#define DMADDRWIDTH_63 63
+#define DMADDRWIDTH_64 64
+
+
+#define BCMEXTRAHDROOM 164
+
+
+#define BCMDONGLEHDRSZ 12
+#define BCMDONGLEPADSZ 16
+
+#define BCMDONGLEOVERHEAD (BCMDONGLEHDRSZ + BCMDONGLEPADSZ)
+
+
+
+#define BITFIELD_MASK(width) \
+ (((unsigned)1 << (width)) - 1)
+#define GFIELD(val, field) \
+ (((val) >> field ## _S) & field ## _M)
+#define SFIELD(val, field, bits) \
+ (((val) & (~(field ## _M << field ## _S))) | \
+ ((unsigned)(bits) << field ## _S))
+
+
+#ifdef BCMSMALL
+#undef BCMSPACE
+#define bcmspace FALSE
+#else
+#define BCMSPACE
+#define bcmspace TRUE
+#endif
+
+
+#define MAXSZ_NVRAM_VARS 4096
+
+#define LOCATOR_EXTERN static
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/bcmdevs.h b/drivers/net/wireless/bcm4329/include/bcmdevs.h
new file mode 100644
index 00000000000..14853f17795
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmdevs.h
@@ -0,0 +1,124 @@
+/*
+ * Broadcom device-specific manifest constants.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmdevs.h,v 13.172.4.5.4.10.2.36 2010/05/25 08:33:44 Exp $
+ */
+
+
+#ifndef _BCMDEVS_H
+#define _BCMDEVS_H
+
+
+#define VENDOR_EPIGRAM 0xfeda
+#define VENDOR_BROADCOM 0x14e4
+#define VENDOR_SI_IMAGE 0x1095
+#define VENDOR_TI 0x104c
+#define VENDOR_RICOH 0x1180
+#define VENDOR_JMICRON 0x197b
+
+
+#define VENDOR_BROADCOM_PCMCIA 0x02d0
+
+
+#define VENDOR_BROADCOM_SDIO 0x00BF
+
+
+#define BCM_DNGL_VID 0xa5c
+#define BCM_DNGL_BL_PID_4320 0xbd11
+#define BCM_DNGL_BL_PID_4328 0xbd12
+#define BCM_DNGL_BL_PID_4322 0xbd13
+#define BCM_DNGL_BL_PID_4325 0xbd14
+#define BCM_DNGL_BL_PID_4315 0xbd15
+#define BCM_DNGL_BL_PID_4319 0xbd16
+#define BCM_DNGL_BDC_PID 0xbdc
+
+#define BCM4325_D11DUAL_ID 0x431b
+#define BCM4325_D11G_ID 0x431c
+#define BCM4325_D11A_ID 0x431d
+#define BCM4329_D11NDUAL_ID 0x432e
+#define BCM4329_D11N2G_ID 0x432f
+#define BCM4329_D11N5G_ID 0x4330
+#define BCM4336_D11N_ID 0x4343
+#define BCM4315_D11DUAL_ID 0x4334
+#define BCM4315_D11G_ID 0x4335
+#define BCM4315_D11A_ID 0x4336
+#define BCM4319_D11N_ID 0x4337
+#define BCM4319_D11N2G_ID 0x4338
+#define BCM4319_D11N5G_ID 0x4339
+
+
+#define SDIOH_FPGA_ID 0x43f2
+#define SPIH_FPGA_ID 0x43f5
+#define BCM4710_DEVICE_ID 0x4710
+#define BCM27XX_SDIOH_ID 0x2702
+#define PCIXX21_FLASHMEDIA0_ID 0x8033
+#define PCIXX21_SDIOH0_ID 0x8034
+#define PCIXX21_FLASHMEDIA_ID 0x803b
+#define PCIXX21_SDIOH_ID 0x803c
+#define R5C822_SDIOH_ID 0x0822
+#define JMICRON_SDIOH_ID 0x2381
+
+
+#define BCM4306_CHIP_ID 0x4306
+#define BCM4311_CHIP_ID 0x4311
+#define BCM4312_CHIP_ID 0x4312
+#define BCM4315_CHIP_ID 0x4315
+#define BCM4318_CHIP_ID 0x4318
+#define BCM4319_CHIP_ID 0x4319
+#define BCM4320_CHIP_ID 0x4320
+#define BCM4321_CHIP_ID 0x4321
+#define BCM4322_CHIP_ID 0x4322
+#define BCM4325_CHIP_ID 0x4325
+#define BCM4328_CHIP_ID 0x4328
+#define BCM4329_CHIP_ID 0x4329
+#define BCM4336_CHIP_ID 0x4336
+#define BCM4402_CHIP_ID 0x4402
+#define BCM4704_CHIP_ID 0x4704
+#define BCM4710_CHIP_ID 0x4710
+#define BCM4712_CHIP_ID 0x4712
+#define BCM4785_CHIP_ID 0x4785
+#define BCM5350_CHIP_ID 0x5350
+#define BCM5352_CHIP_ID 0x5352
+#define BCM5354_CHIP_ID 0x5354
+#define BCM5365_CHIP_ID 0x5365
+
+
+
+#define BCM4303_PKG_ID 2
+#define BCM4309_PKG_ID 1
+#define BCM4712LARGE_PKG_ID 0
+#define BCM4712SMALL_PKG_ID 1
+#define BCM4712MID_PKG_ID 2
+#define BCM4328USBD11G_PKG_ID 2
+#define BCM4328USBDUAL_PKG_ID 3
+#define BCM4328SDIOD11G_PKG_ID 4
+#define BCM4328SDIODUAL_PKG_ID 5
+#define BCM4329_289PIN_PKG_ID 0
+#define BCM4329_182PIN_PKG_ID 1
+#define BCM5354E_PKG_ID 1
+#define HDLSIM5350_PKG_ID 1
+#define HDLSIM_PKG_ID 14
+#define HWSIM_PKG_ID 15
+
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/bcmendian.h b/drivers/net/wireless/bcm4329/include/bcmendian.h
new file mode 100644
index 00000000000..ae468383aa7
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmendian.h
@@ -0,0 +1,205 @@
+/*
+ * Byte order utilities
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmendian.h,v 1.31.302.1.16.1 2009/02/03 18:34:31 Exp $
+ *
+ * This file by default provides proper behavior on little-endian architectures.
+ * On big-endian architectures, IL_BIGENDIAN should be defined.
+ */
+
+
+#ifndef _BCMENDIAN_H_
+#define _BCMENDIAN_H_
+
+#include <typedefs.h>
+
+
+#define BCMSWAP16(val) \
+ ((uint16)((((uint16)(val) & (uint16)0x00ffU) << 8) | \
+ (((uint16)(val) & (uint16)0xff00U) >> 8)))
+
+
+#define BCMSWAP32(val) \
+ ((uint32)((((uint32)(val) & (uint32)0x000000ffU) << 24) | \
+ (((uint32)(val) & (uint32)0x0000ff00U) << 8) | \
+ (((uint32)(val) & (uint32)0x00ff0000U) >> 8) | \
+ (((uint32)(val) & (uint32)0xff000000U) >> 24)))
+
+
+#define BCMSWAP32BY16(val) \
+ ((uint32)((((uint32)(val) & (uint32)0x0000ffffU) << 16) | \
+ (((uint32)(val) & (uint32)0xffff0000U) >> 16)))
+
+
+static INLINE uint16
+bcmswap16(uint16 val)
+{
+ return BCMSWAP16(val);
+}
+
+static INLINE uint32
+bcmswap32(uint32 val)
+{
+ return BCMSWAP32(val);
+}
+
+static INLINE uint32
+bcmswap32by16(uint32 val)
+{
+ return BCMSWAP32BY16(val);
+}
+
+
+
+
+static INLINE void
+bcmswap16_buf(uint16 *buf, uint len)
+{
+ len = len / 2;
+
+ while (len--) {
+ *buf = bcmswap16(*buf);
+ buf++;
+ }
+}
+
+#ifndef hton16
+#ifndef IL_BIGENDIAN
+#define HTON16(i) BCMSWAP16(i)
+#define HTON32(i) BCMSWAP32(i)
+#define hton16(i) bcmswap16(i)
+#define hton32(i) bcmswap32(i)
+#define ntoh16(i) bcmswap16(i)
+#define ntoh32(i) bcmswap32(i)
+#define HTOL16(i) (i)
+#define HTOL32(i) (i)
+#define ltoh16(i) (i)
+#define ltoh32(i) (i)
+#define htol16(i) (i)
+#define htol32(i) (i)
+#else
+#define HTON16(i) (i)
+#define HTON32(i) (i)
+#define hton16(i) (i)
+#define hton32(i) (i)
+#define ntoh16(i) (i)
+#define ntoh32(i) (i)
+#define HTOL16(i) BCMSWAP16(i)
+#define HTOL32(i) BCMSWAP32(i)
+#define ltoh16(i) bcmswap16(i)
+#define ltoh32(i) bcmswap32(i)
+#define htol16(i) bcmswap16(i)
+#define htol32(i) bcmswap32(i)
+#endif
+#endif
+
+#ifndef IL_BIGENDIAN
+#define ltoh16_buf(buf, i)
+#define htol16_buf(buf, i)
+#else
+#define ltoh16_buf(buf, i) bcmswap16_buf((uint16 *)buf, i)
+#define htol16_buf(buf, i) bcmswap16_buf((uint16 *)buf, i)
+#endif
+
+
+static INLINE void
+htol16_ua_store(uint16 val, uint8 *bytes)
+{
+ bytes[0] = val & 0xff;
+ bytes[1] = val >> 8;
+}
+
+
+static INLINE void
+htol32_ua_store(uint32 val, uint8 *bytes)
+{
+ bytes[0] = val & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = (val >> 16) & 0xff;
+ bytes[3] = val >> 24;
+}
+
+
+static INLINE void
+hton16_ua_store(uint16 val, uint8 *bytes)
+{
+ bytes[0] = val >> 8;
+ bytes[1] = val & 0xff;
+}
+
+
+static INLINE void
+hton32_ua_store(uint32 val, uint8 *bytes)
+{
+ bytes[0] = val >> 24;
+ bytes[1] = (val >> 16) & 0xff;
+ bytes[2] = (val >> 8) & 0xff;
+ bytes[3] = val & 0xff;
+}
+
+#define _LTOH16_UA(cp) ((cp)[0] | ((cp)[1] << 8))
+#define _LTOH32_UA(cp) ((cp)[0] | ((cp)[1] << 8) | ((cp)[2] << 16) | ((cp)[3] << 24))
+#define _NTOH16_UA(cp) (((cp)[0] << 8) | (cp)[1])
+#define _NTOH32_UA(cp) (((cp)[0] << 24) | ((cp)[1] << 16) | ((cp)[2] << 8) | (cp)[3])
+
+
+static INLINE uint16
+ltoh16_ua(const void *bytes)
+{
+ return _LTOH16_UA((const uint8 *)bytes);
+}
+
+
+static INLINE uint32
+ltoh32_ua(const void *bytes)
+{
+ return _LTOH32_UA((const uint8 *)bytes);
+}
+
+
+static INLINE uint16
+ntoh16_ua(const void *bytes)
+{
+ return _NTOH16_UA((const uint8 *)bytes);
+}
+
+
+static INLINE uint32
+ntoh32_ua(const void *bytes)
+{
+ return _NTOH32_UA((const uint8 *)bytes);
+}
+
+#define ltoh_ua(ptr) \
+ (sizeof(*(ptr)) == sizeof(uint8) ? *(const uint8 *)ptr : \
+ sizeof(*(ptr)) == sizeof(uint16) ? _LTOH16_UA((const uint8 *)ptr) : \
+ sizeof(*(ptr)) == sizeof(uint32) ? _LTOH32_UA((const uint8 *)ptr) : \
+ 0xfeedf00d)
+
+#define ntoh_ua(ptr) \
+ (sizeof(*(ptr)) == sizeof(uint8) ? *(const uint8 *)ptr : \
+ sizeof(*(ptr)) == sizeof(uint16) ? _NTOH16_UA((const uint8 *)ptr) : \
+ sizeof(*(ptr)) == sizeof(uint32) ? _NTOH32_UA((const uint8 *)ptr) : \
+ 0xfeedf00d)
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/bcmpcispi.h b/drivers/net/wireless/bcm4329/include/bcmpcispi.h
new file mode 100644
index 00000000000..7d98fb7cbdc
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmpcispi.h
@@ -0,0 +1,205 @@
+/*
+ * Broadcom PCI-SPI Host Controller Register Definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmpcispi.h,v 13.11.8.3 2008/07/09 21:23:29 Exp $
+ */
+
+/* cpp contortions to concatenate w/arg prescan */
+#ifndef PAD
+#define _PADLINE(line) pad ## line
+#define _XSTR(line) _PADLINE(line)
+#define PAD _XSTR(__LINE__)
+#endif /* PAD */
+
+/*
++---------------------------------------------------------------------------+
+| |
+| 7 6 5 4 3 2 1 0 |
+| 0x0000 SPI_CTRL SPIE SPE 0 MSTR CPOL CPHA SPR1 SPR0 |
+| 0x0004 SPI_STAT SPIF WCOL ST1 ST0 WFFUL WFEMP RFFUL RFEMP |
+| 0x0008 SPI_DATA Bits 31:0, data to send out on MOSI |
+| 0x000C SPI_EXT ICNT1 ICNT0 BSWAP *HSMODE ESPR1 ESPR0 |
+| 0x0020 GPIO_OE 0=input, 1=output PWR_OE CS_OE |
+| 0x0024 GPIO_DATA CARD:1=missing, 0=present CARD PWR_DAT CS_DAT |
+| 0x0040 INT_EDGE 0=level, 1=edge DEV_E SPI_E |
+| 0x0044 INT_POL 1=active high, 0=active low DEV_P SPI_P |
+| 0x0048 INTMASK DEV SPI |
+| 0x004C INTSTATUS DEV SPI |
+| 0x0060 HEXDISP Reset value: 0x14e443f5. In hexdisp mode, value |
+| shows on the Raggedstone1 4-digit 7-segment display. |
+| 0x0064 CURRENT_MA Low 16 bits indicate card current consumption in mA |
+| 0x006C DISP_SEL Display mode (0=hexdisp, 1=current) DSP |
+| 0x00C0 PLL_CTL bit31=ext_clk, remainder unused. |
+| 0x00C4 PLL_STAT LOCK |
+| 0x00C8 CLK_FREQ |
+| 0x00CC CLK_CNT |
+| |
+| *Notes: HSMODE is not implemented, never set this bit! |
+| BSWAP is available in rev >= 8 |
+| |
++---------------------------------------------------------------------------+
+*/
+
+typedef volatile struct {
+ uint32 spih_ctrl; /* 0x00 SPI Control Register */
+ uint32 spih_stat; /* 0x04 SPI Status Register */
+ uint32 spih_data; /* 0x08 SPI Data Register, 32-bits wide */
+ uint32 spih_ext; /* 0x0C SPI Extension Register */
+ uint32 PAD[4]; /* 0x10-0x1F PADDING */
+
+ uint32 spih_gpio_ctrl; /* 0x20 SPI GPIO Control Register */
+ uint32 spih_gpio_data; /* 0x24 SPI GPIO Data Register */
+ uint32 PAD[6]; /* 0x28-0x3F PADDING */
+
+ uint32 spih_int_edge; /* 0x40 SPI Interrupt Edge Register (0=Level, 1=Edge) */
+ uint32 spih_int_pol; /* 0x44 SPI Interrupt Polarity Register (0=Active Low, */
+ /* 1=Active High) */
+ uint32 spih_int_mask; /* 0x48 SPI Interrupt Mask */
+ uint32 spih_int_status; /* 0x4C SPI Interrupt Status */
+ uint32 PAD[4]; /* 0x50-0x5F PADDING */
+
+ uint32 spih_hex_disp; /* 0x60 SPI 4-digit hex display value */
+ uint32 spih_current_ma; /* 0x64 SPI SD card current consumption in mA */
+ uint32 PAD[1]; /* 0x68 PADDING */
+ uint32 spih_disp_sel; /* 0x6c SPI 4-digit hex display mode select (1=current) */
+ uint32 PAD[4]; /* 0x70-0x7F PADDING */
+ uint32 PAD[8]; /* 0x80-0x9F PADDING */
+ uint32 PAD[8]; /* 0xA0-0xBF PADDING */
+ uint32 spih_pll_ctrl; /* 0xC0 PLL Control Register */
+ uint32 spih_pll_status; /* 0xC4 PLL Status Register */
+ uint32 spih_xtal_freq; /* 0xC8 External Clock Frequency in units of 10000Hz */
+ uint32 spih_clk_count; /* 0xCC External Clock Count Register */
+
+} spih_regs_t;
+
+typedef volatile struct {
+ uint32 cfg_space[0x40]; /* 0x000-0x0FF PCI Configuration Space (Read Only) */
+ uint32 P_IMG_CTRL0; /* 0x100 PCI Image0 Control Register */
+
+ uint32 P_BA0; /* 0x104 32 R/W PCI Image0 Base Address register */
+ uint32 P_AM0; /* 0x108 32 R/W PCI Image0 Address Mask register */
+ uint32 P_TA0; /* 0x10C 32 R/W PCI Image0 Translation Address register */
+ uint32 P_IMG_CTRL1; /* 0x110 32 R/W PCI Image1 Control register */
+ uint32 P_BA1; /* 0x114 32 R/W PCI Image1 Base Address register */
+ uint32 P_AM1; /* 0x118 32 R/W PCI Image1 Address Mask register */
+ uint32 P_TA1; /* 0x11C 32 R/W PCI Image1 Translation Address register */
+ uint32 P_IMG_CTRL2; /* 0x120 32 R/W PCI Image2 Control register */
+ uint32 P_BA2; /* 0x124 32 R/W PCI Image2 Base Address register */
+ uint32 P_AM2; /* 0x128 32 R/W PCI Image2 Address Mask register */
+ uint32 P_TA2; /* 0x12C 32 R/W PCI Image2 Translation Address register */
+ uint32 P_IMG_CTRL3; /* 0x130 32 R/W PCI Image3 Control register */
+ uint32 P_BA3; /* 0x134 32 R/W PCI Image3 Base Address register */
+ uint32 P_AM3; /* 0x138 32 R/W PCI Image3 Address Mask register */
+ uint32 P_TA3; /* 0x13C 32 R/W PCI Image3 Translation Address register */
+ uint32 P_IMG_CTRL4; /* 0x140 32 R/W PCI Image4 Control register */
+ uint32 P_BA4; /* 0x144 32 R/W PCI Image4 Base Address register */
+ uint32 P_AM4; /* 0x148 32 R/W PCI Image4 Address Mask register */
+ uint32 P_TA4; /* 0x14C 32 R/W PCI Image4 Translation Address register */
+ uint32 P_IMG_CTRL5; /* 0x150 32 R/W PCI Image5 Control register */
+ uint32 P_BA5; /* 0x154 32 R/W PCI Image5 Base Address register */
+ uint32 P_AM5; /* 0x158 32 R/W PCI Image5 Address Mask register */
+ uint32 P_TA5; /* 0x15C 32 R/W PCI Image5 Translation Address register */
+ uint32 P_ERR_CS; /* 0x160 32 R/W PCI Error Control and Status register */
+ uint32 P_ERR_ADDR; /* 0x164 32 R PCI Erroneous Address register */
+ uint32 P_ERR_DATA; /* 0x168 32 R PCI Erroneous Data register */
+
+ uint32 PAD[5]; /* 0x16C-0x17F PADDING */
+
+ uint32 WB_CONF_SPC_BAR; /* 0x180 32 R WISHBONE Configuration Space Base Address */
+ uint32 W_IMG_CTRL1; /* 0x184 32 R/W WISHBONE Image1 Control register */
+ uint32 W_BA1; /* 0x188 32 R/W WISHBONE Image1 Base Address register */
+ uint32 W_AM1; /* 0x18C 32 R/W WISHBONE Image1 Address Mask register */
+ uint32 W_TA1; /* 0x190 32 R/W WISHBONE Image1 Translation Address reg */
+ uint32 W_IMG_CTRL2; /* 0x194 32 R/W WISHBONE Image2 Control register */
+ uint32 W_BA2; /* 0x198 32 R/W WISHBONE Image2 Base Address register */
+ uint32 W_AM2; /* 0x19C 32 R/W WISHBONE Image2 Address Mask register */
+ uint32 W_TA2; /* 0x1A0 32 R/W WISHBONE Image2 Translation Address reg */
+ uint32 W_IMG_CTRL3; /* 0x1A4 32 R/W WISHBONE Image3 Control register */
+ uint32 W_BA3; /* 0x1A8 32 R/W WISHBONE Image3 Base Address register */
+ uint32 W_AM3; /* 0x1AC 32 R/W WISHBONE Image3 Address Mask register */
+ uint32 W_TA3; /* 0x1B0 32 R/W WISHBONE Image3 Translation Address reg */
+ uint32 W_IMG_CTRL4; /* 0x1B4 32 R/W WISHBONE Image4 Control register */
+ uint32 W_BA4; /* 0x1B8 32 R/W WISHBONE Image4 Base Address register */
+ uint32 W_AM4; /* 0x1BC 32 R/W WISHBONE Image4 Address Mask register */
+ uint32 W_TA4; /* 0x1C0 32 R/W WISHBONE Image4 Translation Address reg */
+ uint32 W_IMG_CTRL5; /* 0x1C4 32 R/W WISHBONE Image5 Control register */
+ uint32 W_BA5; /* 0x1C8 32 R/W WISHBONE Image5 Base Address register */
+ uint32 W_AM5; /* 0x1CC 32 R/W WISHBONE Image5 Address Mask register */
+ uint32 W_TA5; /* 0x1D0 32 R/W WISHBONE Image5 Translation Address reg */
+ uint32 W_ERR_CS; /* 0x1D4 32 R/W WISHBONE Error Control and Status reg */
+ uint32 W_ERR_ADDR; /* 0x1D8 32 R WISHBONE Erroneous Address register */
+ uint32 W_ERR_DATA; /* 0x1DC 32 R WISHBONE Erroneous Data register */
+ uint32 CNF_ADDR; /* 0x1E0 32 R/W Configuration Cycle register */
+ uint32 CNF_DATA; /* 0x1E4 32 R/W Configuration Cycle Generation Data reg */
+
+ uint32 INT_ACK; /* 0x1E8 32 R Interrupt Acknowledge register */
+ uint32 ICR; /* 0x1EC 32 R/W Interrupt Control register */
+ uint32 ISR; /* 0x1F0 32 R/W Interrupt Status register */
+} spih_pciregs_t;
+
+/*
+ * PCI Core interrupt enable and status bit definitions.
+ */
+
+/* PCI Core ICR Register bit definitions */
+#define PCI_INT_PROP_EN (1 << 0) /* Interrupt Propagation Enable */
+#define PCI_WB_ERR_INT_EN (1 << 1) /* Wishbone Error Interrupt Enable */
+#define PCI_PCI_ERR_INT_EN (1 << 2) /* PCI Error Interrupt Enable */
+#define PCI_PAR_ERR_INT_EN (1 << 3) /* Parity Error Interrupt Enable */
+#define PCI_SYS_ERR_INT_EN (1 << 4) /* System Error Interrupt Enable */
+#define PCI_SOFTWARE_RESET (1U << 31) /* Software reset of the PCI Core. */
+
+
+/* PCI Core ISR Register bit definitions */
+#define PCI_INT_PROP_ST (1 << 0) /* Interrupt Propagation Status */
+#define PCI_WB_ERR_INT_ST (1 << 1) /* Wishbone Error Interrupt Status */
+#define PCI_PCI_ERR_INT_ST (1 << 2) /* PCI Error Interrupt Status */
+#define PCI_PAR_ERR_INT_ST (1 << 3) /* Parity Error Interrupt Status */
+#define PCI_SYS_ERR_INT_ST (1 << 4) /* System Error Interrupt Status */
+
+
+/* Registers on the Wishbone bus */
+#define SPIH_CTLR_INTR (1 << 0) /* SPI Host Controller Core Interrupt */
+#define SPIH_DEV_INTR (1 << 1) /* SPI Device Interrupt */
+#define SPIH_WFIFO_INTR (1 << 2) /* SPI Tx FIFO Empty Intr (FPGA Rev >= 8) */
+
+/* GPIO Bit definitions */
+#define SPIH_CS (1 << 0) /* SPI Chip Select (active low) */
+#define SPIH_SLOT_POWER (1 << 1) /* SD Card Slot Power Enable */
+#define SPIH_CARD_DETECT (1 << 2) /* SD Card Detect */
+
+/* SPI Status Register Bit definitions */
+#define SPIH_STATE_MASK 0x30 /* SPI Transfer State Machine state mask */
+#define SPIH_STATE_SHIFT 4 /* SPI Transfer State Machine state shift */
+#define SPIH_WFFULL (1 << 3) /* SPI Write FIFO Full */
+#define SPIH_WFEMPTY (1 << 2) /* SPI Write FIFO Empty */
+#define SPIH_RFFULL (1 << 1) /* SPI Read FIFO Full */
+#define SPIH_RFEMPTY (1 << 0) /* SPI Read FIFO Empty */
+
+#define SPIH_EXT_CLK (1U << 31) /* Use External Clock as PLL Clock source. */
+
+#define SPIH_PLL_NO_CLK (1 << 1) /* Set to 1 if the PLL's input clock is lost. */
+#define SPIH_PLL_LOCKED (1 << 3) /* Set to 1 when the PLL is locked. */
+
+/* Spin bit loop bound check */
+#define SPI_SPIN_BOUND 0xf4240 /* 1 million */
diff --git a/drivers/net/wireless/bcm4329/include/bcmperf.h b/drivers/net/wireless/bcm4329/include/bcmperf.h
new file mode 100644
index 00000000000..2a78784e85d
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmperf.h
@@ -0,0 +1,36 @@
+/*
+ * Performance counters software interface.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmperf.h,v 13.5 2007/09/14 22:00:59 Exp $
+ */
+/* essai */
+#ifndef _BCMPERF_H_
+#define _BCMPERF_H_
+/* get cache hits and misses */
+#define BCMPERF_ENABLE_INSTRCOUNT()
+#define BCMPERF_ENABLE_ICACHE_MISS()
+#define BCMPERF_ENABLE_ICACHE_HIT()
+#define BCMPERF_GETICACHE_MISS(x) ((x) = 0)
+#define BCMPERF_GETICACHE_HIT(x) ((x) = 0)
+#define BCMPERF_GETINSTRCOUNT(x) ((x) = 0)
+#endif /* _BCMPERF_H_ */
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdbus.h b/drivers/net/wireless/bcm4329/include/bcmsdbus.h
new file mode 100644
index 00000000000..b7b67bc6624
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdbus.h
@@ -0,0 +1,117 @@
+/*
+ * Definitions for API from sdio common code (bcmsdh) to individual
+ * host controller drivers.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdbus.h,v 13.11.14.2.6.6 2009/10/27 17:20:28 Exp $
+ */
+
+#ifndef _sdio_api_h_
+#define _sdio_api_h_
+
+
+#define SDIOH_API_RC_SUCCESS (0x00)
+#define SDIOH_API_RC_FAIL (0x01)
+#define SDIOH_API_SUCCESS(status) (status == 0)
+
+#define SDIOH_READ 0 /* Read request */
+#define SDIOH_WRITE 1 /* Write request */
+
+#define SDIOH_DATA_FIX 0 /* Fixed addressing */
+#define SDIOH_DATA_INC 1 /* Incremental addressing */
+
+#define SDIOH_CMD_TYPE_NORMAL 0 /* Normal command */
+#define SDIOH_CMD_TYPE_APPEND 1 /* Append command */
+#define SDIOH_CMD_TYPE_CUTTHRU 2 /* Cut-through command */
+
+#define SDIOH_DATA_PIO 0 /* PIO mode */
+#define SDIOH_DATA_DMA 1 /* DMA mode */
+
+
+typedef int SDIOH_API_RC;
+
+/* SDio Host structure */
+typedef struct sdioh_info sdioh_info_t;
+
+/* callback function, taking one arg */
+typedef void (*sdioh_cb_fn_t)(void *);
+
+/* attach, return handler on success, NULL if failed.
+ * The handler shall be provided by all subsequent calls. No local cache
+ * cfghdl points to the starting address of pci device mapped memory
+ */
+extern sdioh_info_t * sdioh_attach(osl_t *osh, void *cfghdl, uint irq);
+extern SDIOH_API_RC sdioh_detach(osl_t *osh, sdioh_info_t *si);
+extern SDIOH_API_RC sdioh_interrupt_register(sdioh_info_t *si, sdioh_cb_fn_t fn, void *argh);
+extern SDIOH_API_RC sdioh_interrupt_deregister(sdioh_info_t *si);
+
+/* query whether SD interrupt is enabled or not */
+extern SDIOH_API_RC sdioh_interrupt_query(sdioh_info_t *si, bool *onoff);
+
+/* enable or disable SD interrupt */
+extern SDIOH_API_RC sdioh_interrupt_set(sdioh_info_t *si, bool enable_disable);
+
+#if defined(DHD_DEBUG)
+extern bool sdioh_interrupt_pending(sdioh_info_t *si);
+#endif
+
+/* read or write one byte using cmd52 */
+extern SDIOH_API_RC sdioh_request_byte(sdioh_info_t *si, uint rw, uint fnc, uint addr, uint8 *byte);
+
+/* read or write 2/4 bytes using cmd53 */
+extern SDIOH_API_RC sdioh_request_word(sdioh_info_t *si, uint cmd_type, uint rw, uint fnc,
+ uint addr, uint32 *word, uint nbyte);
+
+/* read or write any buffer using cmd53 */
+extern SDIOH_API_RC sdioh_request_buffer(sdioh_info_t *si, uint pio_dma, uint fix_inc,
+ uint rw, uint fnc_num, uint32 addr, uint regwidth, uint32 buflen, uint8 *buffer,
+ void *pkt);
+
+/* get cis data */
+extern SDIOH_API_RC sdioh_cis_read(sdioh_info_t *si, uint fuc, uint8 *cis, uint32 length);
+
+extern SDIOH_API_RC sdioh_cfg_read(sdioh_info_t *si, uint fuc, uint32 addr, uint8 *data);
+extern SDIOH_API_RC sdioh_cfg_write(sdioh_info_t *si, uint fuc, uint32 addr, uint8 *data);
+
+/* query number of io functions */
+extern uint sdioh_query_iofnum(sdioh_info_t *si);
+
+/* handle iovars */
+extern int sdioh_iovar_op(sdioh_info_t *si, const char *name,
+ void *params, int plen, void *arg, int len, bool set);
+
+/* Issue abort to the specified function and clear controller as needed */
+extern int sdioh_abort(sdioh_info_t *si, uint fnc);
+
+/* Start and Stop SDIO without re-enumerating the SD card. */
+extern int sdioh_start(sdioh_info_t *si, int stage);
+extern int sdioh_stop(sdioh_info_t *si);
+
+/* Reset and re-initialize the device */
+extern int sdioh_sdio_reset(sdioh_info_t *si);
+
+/* Helper function */
+void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh);
+
+
+
+#endif /* _sdio_api_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdh.h b/drivers/net/wireless/bcm4329/include/bcmsdh.h
new file mode 100644
index 00000000000..f5dee5c5844
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdh.h
@@ -0,0 +1,208 @@
+/*
+ * SDIO host client driver interface of Broadcom HNBU
+ * export functions to client drivers
+ * abstract OS and BUS specific details of SDIO
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh.h,v 13.35.14.7.6.8 2009/10/14 04:22:25 Exp $
+ */
+
+#ifndef _bcmsdh_h_
+#define _bcmsdh_h_
+
+#define BCMSDH_ERROR_VAL 0x0001 /* Error */
+#define BCMSDH_INFO_VAL 0x0002 /* Info */
+extern const uint bcmsdh_msglevel;
+
+#define BCMSDH_ERROR(x)
+#define BCMSDH_INFO(x)
+
+/* forward declarations */
+typedef struct bcmsdh_info bcmsdh_info_t;
+typedef void (*bcmsdh_cb_fn_t)(void *);
+
+/* Attach and build an interface to the underlying SD host driver.
+ * - Allocates resources (structs, arrays, mem, OS handles, etc) needed by bcmsdh.
+ * - Returns the bcmsdh handle and virtual address base for register access.
+ * The returned handle should be used in all subsequent calls, but the bcmsh
+ * implementation may maintain a single "default" handle (e.g. the first or
+ * most recent one) to enable single-instance implementations to pass NULL.
+ */
+extern bcmsdh_info_t *bcmsdh_attach(osl_t *osh, void *cfghdl, void **regsva, uint irq);
+
+/* Detach - freeup resources allocated in attach */
+extern int bcmsdh_detach(osl_t *osh, void *sdh);
+
+/* Query if SD device interrupts are enabled */
+extern bool bcmsdh_intr_query(void *sdh);
+
+/* Enable/disable SD interrupt */
+extern int bcmsdh_intr_enable(void *sdh);
+extern int bcmsdh_intr_disable(void *sdh);
+
+/* Register/deregister device interrupt handler. */
+extern int bcmsdh_intr_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh);
+extern int bcmsdh_intr_dereg(void *sdh);
+
+#if defined(DHD_DEBUG)
+/* Query pending interrupt status from the host controller */
+extern bool bcmsdh_intr_pending(void *sdh);
+#endif
+
+#ifdef BCMLXSDMMC
+extern int bcmsdh_claim_host_and_lock(void *sdh);
+extern int bcmsdh_release_host_and_unlock(void *sdh);
+#endif /* BCMLXSDMMC */
+
+/* Register a callback to be called if and when bcmsdh detects
+ * device removal. No-op in the case of non-removable/hardwired devices.
+ */
+extern int bcmsdh_devremove_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh);
+
+/* Access SDIO address space (e.g. CCCR) using CMD52 (single-byte interface).
+ * fn: function number
+ * addr: unmodified SDIO-space address
+ * data: data byte to write
+ * err: pointer to error code (or NULL)
+ */
+extern uint8 bcmsdh_cfg_read(void *sdh, uint func, uint32 addr, int *err);
+extern void bcmsdh_cfg_write(void *sdh, uint func, uint32 addr, uint8 data, int *err);
+
+/* Read/Write 4bytes from/to cfg space */
+extern uint32 bcmsdh_cfg_read_word(void *sdh, uint fnc_num, uint32 addr, int *err);
+extern void bcmsdh_cfg_write_word(void *sdh, uint fnc_num, uint32 addr, uint32 data, int *err);
+
+/* Read CIS content for specified function.
+ * fn: function whose CIS is being requested (0 is common CIS)
+ * cis: pointer to memory location to place results
+ * length: number of bytes to read
+ * Internally, this routine uses the values from the cis base regs (0x9-0xB)
+ * to form an SDIO-space address to read the data from.
+ */
+extern int bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length);
+
+/* Synchronous access to device (client) core registers via CMD53 to F1.
+ * addr: backplane address (i.e. >= regsva from attach)
+ * size: register width in bytes (2 or 4)
+ * data: data for register write
+ */
+extern uint32 bcmsdh_reg_read(void *sdh, uint32 addr, uint size);
+extern uint32 bcmsdh_reg_write(void *sdh, uint32 addr, uint size, uint32 data);
+
+/* Indicate if last reg read/write failed */
+extern bool bcmsdh_regfail(void *sdh);
+
+/* Buffer transfer to/from device (client) core via cmd53.
+ * fn: function number
+ * addr: backplane address (i.e. >= regsva from attach)
+ * flags: backplane width, address increment, sync/async
+ * buf: pointer to memory data buffer
+ * nbytes: number of bytes to transfer to/from buf
+ * pkt: pointer to packet associated with buf (if any)
+ * complete: callback function for command completion (async only)
+ * handle: handle for completion callback (first arg in callback)
+ * Returns 0 or error code.
+ * NOTE: Async operation is not currently supported.
+ */
+typedef void (*bcmsdh_cmplt_fn_t)(void *handle, int status, bool sync_waiting);
+extern int bcmsdh_send_buf(void *sdh, uint32 addr, uint fn, uint flags,
+ uint8 *buf, uint nbytes, void *pkt,
+ bcmsdh_cmplt_fn_t complete, void *handle);
+extern int bcmsdh_recv_buf(void *sdh, uint32 addr, uint fn, uint flags,
+ uint8 *buf, uint nbytes, void *pkt,
+ bcmsdh_cmplt_fn_t complete, void *handle);
+
+/* Flags bits */
+#define SDIO_REQ_4BYTE 0x1 /* Four-byte target (backplane) width (vs. two-byte) */
+#define SDIO_REQ_FIXED 0x2 /* Fixed address (FIFO) (vs. incrementing address) */
+#define SDIO_REQ_ASYNC 0x4 /* Async request (vs. sync request) */
+
+/* Pending (non-error) return code */
+#define BCME_PENDING 1
+
+/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only).
+ * rw: read or write (0/1)
+ * addr: direct SDIO address
+ * buf: pointer to memory data buffer
+ * nbytes: number of bytes to transfer to/from buf
+ * Returns 0 or error code.
+ */
+extern int bcmsdh_rwdata(void *sdh, uint rw, uint32 addr, uint8 *buf, uint nbytes);
+
+/* Issue an abort to the specified function */
+extern int bcmsdh_abort(void *sdh, uint fn);
+
+/* Start SDIO Host Controller communication */
+extern int bcmsdh_start(void *sdh, int stage);
+
+/* Stop SDIO Host Controller communication */
+extern int bcmsdh_stop(void *sdh);
+
+/* Returns the "Device ID" of target device on the SDIO bus. */
+extern int bcmsdh_query_device(void *sdh);
+
+/* Returns the number of IO functions reported by the device */
+extern uint bcmsdh_query_iofnum(void *sdh);
+
+/* Miscellaneous knob tweaker. */
+extern int bcmsdh_iovar_op(void *sdh, const char *name,
+ void *params, int plen, void *arg, int len, bool set);
+
+/* Reset and reinitialize the device */
+extern int bcmsdh_reset(bcmsdh_info_t *sdh);
+
+/* helper functions */
+
+extern void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh);
+
+/* callback functions */
+typedef struct {
+ /* attach to device */
+ void *(*attach)(uint16 vend_id, uint16 dev_id, uint16 bus, uint16 slot,
+ uint16 func, uint bustype, void * regsva, osl_t * osh,
+ void * param);
+ /* detach from device */
+ void (*detach)(void *ch);
+} bcmsdh_driver_t;
+
+/* platform specific/high level functions */
+extern int bcmsdh_register(bcmsdh_driver_t *driver);
+extern void bcmsdh_unregister(void);
+extern bool bcmsdh_chipmatch(uint16 vendor, uint16 device);
+extern void bcmsdh_device_remove(void * sdh);
+
+#if defined(OOB_INTR_ONLY)
+extern int bcmsdh_register_oob_intr(void * dhdp);
+extern void bcmsdh_unregister_oob_intr(void);
+extern void bcmsdh_oob_intr_set(bool enable);
+#endif /* defined(OOB_INTR_ONLY) */
+/* Function to pass device-status bits to DHD. */
+extern uint32 bcmsdh_get_dstatus(void *sdh);
+
+/* Function to return current window addr */
+extern uint32 bcmsdh_cur_sbwad(void *sdh);
+
+/* Function to pass chipid and rev to lower layers for controlling pr's */
+extern void bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev);
+
+
+#endif /* _bcmsdh_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdh_sdmmc.h b/drivers/net/wireless/bcm4329/include/bcmsdh_sdmmc.h
new file mode 100644
index 00000000000..4e6d1b5bd94
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdh_sdmmc.h
@@ -0,0 +1,122 @@
+/*
+ * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdh_sdmmc.h,v 13.1.2.1.8.7 2009/10/27 18:22:52 Exp $
+ */
+
+#ifndef __BCMSDH_SDMMC_H__
+#define __BCMSDH_SDMMC_H__
+
+#define sd_err(x)
+#define sd_trace(x)
+#define sd_info(x)
+#define sd_debug(x)
+#define sd_data(x)
+#define sd_ctrl(x)
+
+#define sd_sync_dma(sd, read, nbytes)
+#define sd_init_dma(sd)
+#define sd_ack_intr(sd)
+#define sd_wakeup(sd);
+
+/* Allocate/init/free per-OS private data */
+extern int sdioh_sdmmc_osinit(sdioh_info_t *sd);
+extern void sdioh_sdmmc_osfree(sdioh_info_t *sd);
+
+#define sd_log(x)
+
+#define SDIOH_ASSERT(exp) \
+ do { if (!(exp)) \
+ printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \
+ } while (0)
+
+#define BLOCK_SIZE_4318 64
+#define BLOCK_SIZE_4328 512
+
+/* internal return code */
+#define SUCCESS 0
+#define ERROR 1
+
+/* private bus modes */
+#define SDIOH_MODE_SD4 2
+#define CLIENT_INTR 0x100 /* Get rid of this! */
+
+struct sdioh_info {
+ osl_t *osh; /* osh handler */
+ bool client_intr_enabled; /* interrupt connnected flag */
+ bool intr_handler_valid; /* client driver interrupt handler valid */
+ sdioh_cb_fn_t intr_handler; /* registered interrupt handler */
+ void *intr_handler_arg; /* argument to call interrupt handler */
+ uint16 intmask; /* Current active interrupts */
+ void *sdos_info; /* Pointer to per-OS private data */
+
+ uint irq; /* Client irq */
+ int intrcount; /* Client interrupts */
+
+ bool sd_use_dma; /* DMA on CMD53 */
+ bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */
+ /* Must be on for sd_multiblock to be effective */
+ bool use_client_ints; /* If this is false, make sure to restore */
+ int sd_mode; /* SD1/SD4/SPI */
+ int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */
+ uint8 num_funcs; /* Supported funcs on client */
+ uint32 com_cis_ptr;
+ uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS];
+ uint max_dma_len;
+ uint max_dma_descriptors; /* DMA Descriptors supported by this controller. */
+// SDDMA_DESCRIPTOR SGList[32]; /* Scatter/Gather DMA List */
+};
+
+/************************************************************
+ * Internal interfaces: per-port references into bcmsdh_sdmmc.c
+ */
+
+/* Global message bits */
+extern uint sd_msglevel;
+
+/* OS-independent interrupt handler */
+extern bool check_client_intr(sdioh_info_t *sd);
+
+/* Core interrupt enable/disable of device interrupts */
+extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd);
+extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd);
+
+
+/**************************************************************
+ * Internal interfaces: bcmsdh_sdmmc.c references to per-port code
+ */
+
+/* Register mapping routines */
+extern uint32 *sdioh_sdmmc_reg_map(osl_t *osh, int32 addr, int size);
+extern void sdioh_sdmmc_reg_unmap(osl_t *osh, int32 addr, int size);
+
+/* Interrupt (de)registration routines */
+extern int sdioh_sdmmc_register_irq(sdioh_info_t *sd, uint irq);
+extern void sdioh_sdmmc_free_irq(uint irq, sdioh_info_t *sd);
+
+typedef struct _BCMSDH_SDMMC_INSTANCE {
+ sdioh_info_t *sd;
+ struct sdio_func *func[SDIOD_MAX_IOFUNCS];
+} BCMSDH_SDMMC_INSTANCE, *PBCMSDH_SDMMC_INSTANCE;
+
+#endif /* __BCMSDH_SDMMC_H__ */
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdpcm.h b/drivers/net/wireless/bcm4329/include/bcmsdpcm.h
new file mode 100644
index 00000000000..77aca4500ad
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdpcm.h
@@ -0,0 +1,263 @@
+/*
+ * Broadcom SDIO/PCMCIA
+ * Software-specific definitions shared between device and host side
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdpcm.h,v 1.1.2.4 2010/07/02 01:15:46 Exp $
+ */
+
+#ifndef _bcmsdpcm_h_
+#define _bcmsdpcm_h_
+
+/*
+ * Software allocation of To SB Mailbox resources
+ */
+
+/* intstatus bits */
+#define I_SMB_NAK I_SMB_SW0 /* To SB Mailbox Frame NAK */
+#define I_SMB_INT_ACK I_SMB_SW1 /* To SB Mailbox Host Interrupt ACK */
+#define I_SMB_USE_OOB I_SMB_SW2 /* To SB Mailbox Use OOB Wakeup */
+#define I_SMB_DEV_INT I_SMB_SW3 /* To SB Mailbox Miscellaneous Interrupt */
+
+/* tosbmailbox bits corresponding to intstatus bits */
+#define SMB_NAK (1 << 0) /* To SB Mailbox Frame NAK */
+#define SMB_INT_ACK (1 << 1) /* To SB Mailbox Host Interrupt ACK */
+#define SMB_USE_OOB (1 << 2) /* To SB Mailbox Use OOB Wakeup */
+#define SMB_DEV_INT (1 << 3) /* To SB Mailbox Miscellaneous Interrupt */
+#define SMB_MASK 0x0000000f /* To SB Mailbox Mask */
+
+/* tosbmailboxdata */
+#define SMB_DATA_VERSION_MASK 0x00ff0000 /* host protocol version (sent with F2 enable) */
+#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version (sent with F2 enable) */
+
+/*
+ * Software allocation of To Host Mailbox resources
+ */
+
+/* intstatus bits */
+#define I_HMB_FC_STATE I_HMB_SW0 /* To Host Mailbox Flow Control State */
+#define I_HMB_FC_CHANGE I_HMB_SW1 /* To Host Mailbox Flow Control State Changed */
+#define I_HMB_FRAME_IND I_HMB_SW2 /* To Host Mailbox Frame Indication */
+#define I_HMB_HOST_INT I_HMB_SW3 /* To Host Mailbox Miscellaneous Interrupt */
+
+/* tohostmailbox bits corresponding to intstatus bits */
+#define HMB_FC_ON (1 << 0) /* To Host Mailbox Flow Control State */
+#define HMB_FC_CHANGE (1 << 1) /* To Host Mailbox Flow Control State Changed */
+#define HMB_FRAME_IND (1 << 2) /* To Host Mailbox Frame Indication */
+#define HMB_HOST_INT (1 << 3) /* To Host Mailbox Miscellaneous Interrupt */
+#define HMB_MASK 0x0000000f /* To Host Mailbox Mask */
+
+/* tohostmailboxdata */
+#define HMB_DATA_NAKHANDLED 1 /* we're ready to retransmit NAK'd frame to host */
+#define HMB_DATA_DEVREADY 2 /* we're ready to to talk to host after enable */
+#define HMB_DATA_FC 4 /* per prio flowcontrol update flag to host */
+#define HMB_DATA_FWREADY 8 /* firmware is ready for protocol activity */
+
+#define HMB_DATA_FCDATA_MASK 0xff000000 /* per prio flowcontrol data */
+#define HMB_DATA_FCDATA_SHIFT 24 /* per prio flowcontrol data */
+
+#define HMB_DATA_VERSION_MASK 0x00ff0000 /* device protocol version (with devready) */
+#define HMB_DATA_VERSION_SHIFT 16 /* device protocol version (with devready) */
+
+/*
+ * Software-defined protocol header
+ */
+
+/* Current protocol version */
+#define SDPCM_PROT_VERSION 4
+
+/* SW frame header */
+#define SDPCM_SEQUENCE_MASK 0x000000ff /* Sequence Number Mask */
+#define SDPCM_PACKET_SEQUENCE(p) (((uint8 *)p)[0] & 0xff) /* p starts w/SW Header */
+
+#define SDPCM_CHANNEL_MASK 0x00000f00 /* Channel Number Mask */
+#define SDPCM_CHANNEL_SHIFT 8 /* Channel Number Shift */
+#define SDPCM_PACKET_CHANNEL(p) (((uint8 *)p)[1] & 0x0f) /* p starts w/SW Header */
+
+#define SDPCM_FLAGS_MASK 0x0000f000 /* Mask of flag bits */
+#define SDPCM_FLAGS_SHIFT 12 /* Flag bits shift */
+#define SDPCM_PACKET_FLAGS(p) ((((uint8 *)p)[1] & 0xf0) >> 4) /* p starts w/SW Header */
+
+/* Next Read Len: lookahead length of next frame, in 16-byte units (rounded up) */
+#define SDPCM_NEXTLEN_MASK 0x00ff0000 /* Next Read Len Mask */
+#define SDPCM_NEXTLEN_SHIFT 16 /* Next Read Len Shift */
+#define SDPCM_NEXTLEN_VALUE(p) ((((uint8 *)p)[2] & 0xff) << 4) /* p starts w/SW Header */
+#define SDPCM_NEXTLEN_OFFSET 2
+
+/* Data Offset from SOF (HW Tag, SW Tag, Pad) */
+#define SDPCM_DOFFSET_OFFSET 3 /* Data Offset */
+#define SDPCM_DOFFSET_VALUE(p) (((uint8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff)
+#define SDPCM_DOFFSET_MASK 0xff000000
+#define SDPCM_DOFFSET_SHIFT 24
+
+#define SDPCM_FCMASK_OFFSET 4 /* Flow control */
+#define SDPCM_FCMASK_VALUE(p) (((uint8 *)p)[SDPCM_FCMASK_OFFSET ] & 0xff)
+#define SDPCM_WINDOW_OFFSET 5 /* Credit based fc */
+#define SDPCM_WINDOW_VALUE(p) (((uint8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff)
+#define SDPCM_VERSION_OFFSET 6 /* Version # */
+#define SDPCM_VERSION_VALUE(p) (((uint8 *)p)[SDPCM_VERSION_OFFSET] & 0xff)
+#define SDPCM_UNUSED_OFFSET 7 /* Spare */
+#define SDPCM_UNUSED_VALUE(p) (((uint8 *)p)[SDPCM_UNUSED_OFFSET] & 0xff)
+
+#define SDPCM_SWHEADER_LEN 8 /* SW header is 64 bits */
+
+/* logical channel numbers */
+#define SDPCM_CONTROL_CHANNEL 0 /* Control Request/Response Channel Id */
+#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication Channel Id */
+#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv Channel Id */
+#define SDPCM_GLOM_CHANNEL 3 /* For coalesced packets (superframes) */
+#define SDPCM_TEST_CHANNEL 15 /* Reserved for test/debug packets */
+#define SDPCM_MAX_CHANNEL 15
+
+#define SDPCM_SEQUENCE_WRAP 256 /* wrap-around val for eight-bit frame seq number */
+
+#define SDPCM_FLAG_RESVD0 0x01
+#define SDPCM_FLAG_RESVD1 0x02
+#define SDPCM_FLAG_GSPI_TXENAB 0x04
+#define SDPCM_FLAG_GLOMDESC 0x08 /* Superframe descriptor mask */
+
+/* For GLOM_CHANNEL frames, use a flag to indicate descriptor frame */
+#define SDPCM_GLOMDESC_FLAG (SDPCM_FLAG_GLOMDESC << SDPCM_FLAGS_SHIFT)
+
+#define SDPCM_GLOMDESC(p) (((uint8 *)p)[1] & 0x80)
+
+/* For TEST_CHANNEL packets, define another 4-byte header */
+#define SDPCM_TEST_HDRLEN 4 /* Generally: Cmd(1), Ext(1), Len(2);
+ * Semantics of Ext byte depend on command.
+ * Len is current or requested frame length, not
+ * including test header; sent little-endian.
+ */
+#define SDPCM_TEST_DISCARD 0x01 /* Receiver discards. Ext is a pattern id. */
+#define SDPCM_TEST_ECHOREQ 0x02 /* Echo request. Ext is a pattern id. */
+#define SDPCM_TEST_ECHORSP 0x03 /* Echo response. Ext is a pattern id. */
+#define SDPCM_TEST_BURST 0x04 /* Receiver to send a burst. Ext is a frame count */
+#define SDPCM_TEST_SEND 0x05 /* Receiver sets send mode. Ext is boolean on/off */
+
+/* Handy macro for filling in datagen packets with a pattern */
+#define SDPCM_TEST_FILL(byteno, id) ((uint8)(id + byteno))
+
+/*
+ * Software counters (first part matches hardware counters)
+ */
+
+typedef volatile struct {
+ uint32 cmd52rd; /* Cmd52RdCount, SDIO: cmd52 reads */
+ uint32 cmd52wr; /* Cmd52WrCount, SDIO: cmd52 writes */
+ uint32 cmd53rd; /* Cmd53RdCount, SDIO: cmd53 reads */
+ uint32 cmd53wr; /* Cmd53WrCount, SDIO: cmd53 writes */
+ uint32 abort; /* AbortCount, SDIO: aborts */
+ uint32 datacrcerror; /* DataCrcErrorCount, SDIO: frames w/CRC error */
+ uint32 rdoutofsync; /* RdOutOfSyncCount, SDIO/PCMCIA: Rd Frm out of sync */
+ uint32 wroutofsync; /* RdOutOfSyncCount, SDIO/PCMCIA: Wr Frm out of sync */
+ uint32 writebusy; /* WriteBusyCount, SDIO: device asserted "busy" */
+ uint32 readwait; /* ReadWaitCount, SDIO: no data ready for a read cmd */
+ uint32 readterm; /* ReadTermCount, SDIO: read frame termination cmds */
+ uint32 writeterm; /* WriteTermCount, SDIO: write frames termination cmds */
+ uint32 rxdescuflo; /* receive descriptor underflows */
+ uint32 rxfifooflo; /* receive fifo overflows */
+ uint32 txfifouflo; /* transmit fifo underflows */
+ uint32 runt; /* runt (too short) frames recv'd from bus */
+ uint32 badlen; /* frame's rxh len does not match its hw tag len */
+ uint32 badcksum; /* frame's hw tag chksum doesn't agree with len value */
+ uint32 seqbreak; /* break in sequence # space from one rx frame to the next */
+ uint32 rxfcrc; /* frame rx header indicates crc error */
+ uint32 rxfwoos; /* frame rx header indicates write out of sync */
+ uint32 rxfwft; /* frame rx header indicates write frame termination */
+ uint32 rxfabort; /* frame rx header indicates frame aborted */
+ uint32 woosint; /* write out of sync interrupt */
+ uint32 roosint; /* read out of sync interrupt */
+ uint32 rftermint; /* read frame terminate interrupt */
+ uint32 wftermint; /* write frame terminate interrupt */
+} sdpcmd_cnt_t;
+
+/*
+ * Register Access Macros
+ */
+
+#define SDIODREV_IS(var, val) ((var) == (val))
+#define SDIODREV_GE(var, val) ((var) >= (val))
+#define SDIODREV_GT(var, val) ((var) > (val))
+#define SDIODREV_LT(var, val) ((var) < (val))
+#define SDIODREV_LE(var, val) ((var) <= (val))
+
+#define SDIODDMAREG32(h, dir, chnl) \
+ ((dir) == DMA_TX ? \
+ (void *)(uintptr)&((h)->regs->dma.sdiod32.dma32regs[chnl].xmt) : \
+ (void *)(uintptr)&((h)->regs->dma.sdiod32.dma32regs[chnl].rcv))
+
+#define SDIODDMAREG64(h, dir, chnl) \
+ ((dir) == DMA_TX ? \
+ (void *)(uintptr)&((h)->regs->dma.sdiod64.dma64regs[chnl].xmt) : \
+ (void *)(uintptr)&((h)->regs->dma.sdiod64.dma64regs[chnl].rcv))
+
+#define SDIODDMAREG(h, dir, chnl) \
+ (SDIODREV_LT((h)->corerev, 1) ? \
+ SDIODDMAREG32((h), (dir), (chnl)) : \
+ SDIODDMAREG64((h), (dir), (chnl)))
+
+#define PCMDDMAREG(h, dir, chnl) \
+ ((dir) == DMA_TX ? \
+ (void *)(uintptr)&((h)->regs->dma.pcm32.dmaregs.xmt) : \
+ (void *)(uintptr)&((h)->regs->dma.pcm32.dmaregs.rcv))
+
+#define SDPCMDMAREG(h, dir, chnl, coreid) \
+ ((coreid) == SDIOD_CORE_ID ? \
+ SDIODDMAREG(h, dir, chnl) : \
+ PCMDDMAREG(h, dir, chnl))
+
+#define SDIODFIFOREG(h, corerev) \
+ (SDIODREV_LT((corerev), 1) ? \
+ ((dma32diag_t *)(uintptr)&((h)->regs->dma.sdiod32.dmafifo)) : \
+ ((dma32diag_t *)(uintptr)&((h)->regs->dma.sdiod64.dmafifo)))
+
+#define PCMDFIFOREG(h) \
+ ((dma32diag_t *)(uintptr)&((h)->regs->dma.pcm32.dmafifo))
+
+#define SDPCMFIFOREG(h, coreid, corerev) \
+ ((coreid) == SDIOD_CORE_ID ? \
+ SDIODFIFOREG(h, corerev) : \
+ PCMDFIFOREG(h))
+
+/*
+ * Shared structure between dongle and the host
+ * The structure contains pointers to trap or assert information shared with the host
+ */
+#define SDPCM_SHARED_VERSION 0x0002
+#define SDPCM_SHARED_VERSION_MASK 0x00FF
+#define SDPCM_SHARED_ASSERT_BUILT 0x0100
+#define SDPCM_SHARED_ASSERT 0x0200
+#define SDPCM_SHARED_TRAP 0x0400
+
+typedef struct {
+ uint32 flags;
+ uint32 trap_addr;
+ uint32 assert_exp_addr;
+ uint32 assert_file_addr;
+ uint32 assert_line;
+ uint32 console_addr; /* Address of hndrte_cons_t */
+ uint32 msgtrace_addr;
+ uint8 tag[32];
+} sdpcm_shared_t;
+
+extern sdpcm_shared_t sdpcm_shared;
+
+#endif /* _bcmsdpcm_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdspi.h b/drivers/net/wireless/bcm4329/include/bcmsdspi.h
new file mode 100644
index 00000000000..eaae10d8bf1
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdspi.h
@@ -0,0 +1,131 @@
+/*
+ * SD-SPI Protocol Conversion - BCMSDH->SPI Translation Layer
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdspi.h,v 13.8.10.2 2008/06/30 21:09:40 Exp $
+ */
+
+/* global msglevel for debug messages - bitvals come from sdiovar.h */
+
+#define sd_err(x)
+#define sd_trace(x)
+#define sd_info(x)
+#define sd_debug(x)
+#define sd_data(x)
+#define sd_ctrl(x)
+
+#define sd_log(x)
+
+#define SDIOH_ASSERT(exp) \
+ do { if (!(exp)) \
+ printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \
+ } while (0)
+
+#define BLOCK_SIZE_4318 64
+#define BLOCK_SIZE_4328 512
+
+/* internal return code */
+#define SUCCESS 0
+#undef ERROR
+#define ERROR 1
+
+/* private bus modes */
+#define SDIOH_MODE_SPI 0
+
+#define USE_BLOCKMODE 0x2 /* Block mode can be single block or multi */
+#define USE_MULTIBLOCK 0x4
+
+struct sdioh_info {
+ uint cfg_bar; /* pci cfg address for bar */
+ uint32 caps; /* cached value of capabilities reg */
+ uint bar0; /* BAR0 for PCI Device */
+ osl_t *osh; /* osh handler */
+ void *controller; /* Pointer to SPI Controller's private data struct */
+
+ uint lockcount; /* nest count of sdspi_lock() calls */
+ bool client_intr_enabled; /* interrupt connnected flag */
+ bool intr_handler_valid; /* client driver interrupt handler valid */
+ sdioh_cb_fn_t intr_handler; /* registered interrupt handler */
+ void *intr_handler_arg; /* argument to call interrupt handler */
+ bool initialized; /* card initialized */
+ uint32 target_dev; /* Target device ID */
+ uint32 intmask; /* Current active interrupts */
+ void *sdos_info; /* Pointer to per-OS private data */
+
+ uint32 controller_type; /* Host controller type */
+ uint8 version; /* Host Controller Spec Compliance Version */
+ uint irq; /* Client irq */
+ uint32 intrcount; /* Client interrupts */
+ uint32 local_intrcount; /* Controller interrupts */
+ bool host_init_done; /* Controller initted */
+ bool card_init_done; /* Client SDIO interface initted */
+ bool polled_mode; /* polling for command completion */
+
+ bool sd_use_dma; /* DMA on CMD53 */
+ bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */
+ /* Must be on for sd_multiblock to be effective */
+ bool use_client_ints; /* If this is false, make sure to restore */
+ bool got_hcint; /* Host Controller interrupt. */
+ /* polling hack in wl_linux.c:wl_timer() */
+ int adapter_slot; /* Maybe dealing with multiple slots/controllers */
+ int sd_mode; /* SD1/SD4/SPI */
+ int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */
+ uint32 data_xfer_count; /* Current register transfer size */
+ uint32 cmd53_wr_data; /* Used to pass CMD53 write data */
+ uint32 card_response; /* Used to pass back response status byte */
+ uint32 card_rsp_data; /* Used to pass back response data word */
+ uint16 card_rca; /* Current Address */
+ uint8 num_funcs; /* Supported funcs on client */
+ uint32 com_cis_ptr;
+ uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS];
+ void *dma_buf;
+ ulong dma_phys;
+ int r_cnt; /* rx count */
+ int t_cnt; /* tx_count */
+};
+
+/************************************************************
+ * Internal interfaces: per-port references into bcmsdspi.c
+ */
+
+/* Global message bits */
+extern uint sd_msglevel;
+
+/**************************************************************
+ * Internal interfaces: bcmsdspi.c references to per-port code
+ */
+
+/* Register mapping routines */
+extern uint32 *spi_reg_map(osl_t *osh, uintptr addr, int size);
+extern void spi_reg_unmap(osl_t *osh, uintptr addr, int size);
+
+/* Interrupt (de)registration routines */
+extern int spi_register_irq(sdioh_info_t *sd, uint irq);
+extern void spi_free_irq(uint irq, sdioh_info_t *sd);
+
+/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */
+extern void spi_lock(sdioh_info_t *sd);
+extern void spi_unlock(sdioh_info_t *sd);
+
+/* Allocate/init/free per-OS private data */
+extern int spi_osinit(sdioh_info_t *sd);
+extern void spi_osfree(sdioh_info_t *sd);
diff --git a/drivers/net/wireless/bcm4329/include/bcmsdstd.h b/drivers/net/wireless/bcm4329/include/bcmsdstd.h
new file mode 100644
index 00000000000..974b3d41698
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmsdstd.h
@@ -0,0 +1,223 @@
+/*
+ * 'Standard' SDIO HOST CONTROLLER driver
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmsdstd.h,v 13.16.18.1.16.3 2009/12/10 01:09:23 Exp $
+ */
+
+/* global msglevel for debug messages - bitvals come from sdiovar.h */
+
+#define sd_err(x) do { if (sd_msglevel & SDH_ERROR_VAL) printf x; } while (0)
+#define sd_trace(x)
+#define sd_info(x)
+#define sd_debug(x)
+#define sd_data(x)
+#define sd_ctrl(x)
+#define sd_dma(x)
+
+#define sd_sync_dma(sd, read, nbytes)
+#define sd_init_dma(sd)
+#define sd_ack_intr(sd)
+#define sd_wakeup(sd);
+/* Allocate/init/free per-OS private data */
+extern int sdstd_osinit(sdioh_info_t *sd);
+extern void sdstd_osfree(sdioh_info_t *sd);
+
+#define sd_log(x)
+
+#define SDIOH_ASSERT(exp) \
+ do { if (!(exp)) \
+ printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \
+ } while (0)
+
+#define BLOCK_SIZE_4318 64
+#define BLOCK_SIZE_4328 512
+
+/* internal return code */
+#define SUCCESS 0
+#define ERROR 1
+
+/* private bus modes */
+#define SDIOH_MODE_SPI 0
+#define SDIOH_MODE_SD1 1
+#define SDIOH_MODE_SD4 2
+
+#define MAX_SLOTS 6 /* For PCI: Only 6 BAR entries => 6 slots */
+#define SDIOH_REG_WINSZ 0x100 /* Number of registers in Standard Host Controller */
+
+#define SDIOH_TYPE_ARASAN_HDK 1
+#define SDIOH_TYPE_BCM27XX 2
+#define SDIOH_TYPE_TI_PCIXX21 4 /* TI PCIxx21 Standard Host Controller */
+#define SDIOH_TYPE_RICOH_R5C822 5 /* Ricoh Co Ltd R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter */
+#define SDIOH_TYPE_JMICRON 6 /* JMicron Standard SDIO Host Controller */
+
+/* For linux, allow yielding for dongle */
+#define BCMSDYIELD
+
+/* Expected card status value for CMD7 */
+#define SDIOH_CMD7_EXP_STATUS 0x00001E00
+
+#define RETRIES_LARGE 100000
+#define RETRIES_SMALL 100
+
+
+#define USE_BLOCKMODE 0x2 /* Block mode can be single block or multi */
+#define USE_MULTIBLOCK 0x4
+
+#define USE_FIFO 0x8 /* Fifo vs non-fifo */
+
+#define CLIENT_INTR 0x100 /* Get rid of this! */
+
+
+struct sdioh_info {
+ uint cfg_bar; /* pci cfg address for bar */
+ uint32 caps; /* cached value of capabilities reg */
+ uint32 curr_caps; /* max current capabilities reg */
+
+ osl_t *osh; /* osh handler */
+ volatile char *mem_space; /* pci device memory va */
+ uint lockcount; /* nest count of sdstd_lock() calls */
+ bool client_intr_enabled; /* interrupt connnected flag */
+ bool intr_handler_valid; /* client driver interrupt handler valid */
+ sdioh_cb_fn_t intr_handler; /* registered interrupt handler */
+ void *intr_handler_arg; /* argument to call interrupt handler */
+ bool initialized; /* card initialized */
+ uint target_dev; /* Target device ID */
+ uint16 intmask; /* Current active interrupts */
+ void *sdos_info; /* Pointer to per-OS private data */
+
+ uint32 controller_type; /* Host controller type */
+ uint8 version; /* Host Controller Spec Compliance Version */
+ uint irq; /* Client irq */
+ int intrcount; /* Client interrupts */
+ int local_intrcount; /* Controller interrupts */
+ bool host_init_done; /* Controller initted */
+ bool card_init_done; /* Client SDIO interface initted */
+ bool polled_mode; /* polling for command completion */
+
+ bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */
+ /* Must be on for sd_multiblock to be effective */
+ bool use_client_ints; /* If this is false, make sure to restore */
+ /* polling hack in wl_linux.c:wl_timer() */
+ int adapter_slot; /* Maybe dealing with multiple slots/controllers */
+ int sd_mode; /* SD1/SD4/SPI */
+ int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */
+ uint32 data_xfer_count; /* Current transfer */
+ uint16 card_rca; /* Current Address */
+ int8 sd_dma_mode; /* DMA Mode (PIO, SDMA, ... ADMA2) on CMD53 */
+ uint8 num_funcs; /* Supported funcs on client */
+ uint32 com_cis_ptr;
+ uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS];
+ void *dma_buf; /* DMA Buffer virtual address */
+ ulong dma_phys; /* DMA Buffer physical address */
+ void *adma2_dscr_buf; /* ADMA2 Descriptor Buffer virtual address */
+ ulong adma2_dscr_phys; /* ADMA2 Descriptor Buffer physical address */
+
+ /* adjustments needed to make the dma align properly */
+ void *dma_start_buf;
+ ulong dma_start_phys;
+ uint alloced_dma_size;
+ void *adma2_dscr_start_buf;
+ ulong adma2_dscr_start_phys;
+ uint alloced_adma2_dscr_size;
+
+ int r_cnt; /* rx count */
+ int t_cnt; /* tx_count */
+ bool got_hcint; /* local interrupt flag */
+ uint16 last_intrstatus; /* to cache intrstatus */
+};
+
+#define DMA_MODE_NONE 0
+#define DMA_MODE_SDMA 1
+#define DMA_MODE_ADMA1 2
+#define DMA_MODE_ADMA2 3
+#define DMA_MODE_ADMA2_64 4
+#define DMA_MODE_AUTO -1
+
+#define USE_DMA(sd) ((bool)((sd->sd_dma_mode > 0) ? TRUE : FALSE))
+
+/* SDIO Host Control Register DMA Mode Definitions */
+#define SDIOH_SDMA_MODE 0
+#define SDIOH_ADMA1_MODE 1
+#define SDIOH_ADMA2_MODE 2
+#define SDIOH_ADMA2_64_MODE 3
+
+#define ADMA2_ATTRIBUTE_VALID (1 << 0) /* ADMA Descriptor line valid */
+#define ADMA2_ATTRIBUTE_END (1 << 1) /* End of Descriptor */
+#define ADMA2_ATTRIBUTE_INT (1 << 2) /* Interrupt when line is done */
+#define ADMA2_ATTRIBUTE_ACT_NOP (0 << 4) /* Skip current line, go to next. */
+#define ADMA2_ATTRIBUTE_ACT_RSV (1 << 4) /* Same as NOP */
+#define ADMA1_ATTRIBUTE_ACT_SET (1 << 4) /* ADMA1 Only - set transfer length */
+#define ADMA2_ATTRIBUTE_ACT_TRAN (2 << 4) /* Transfer Data of one descriptor line. */
+#define ADMA2_ATTRIBUTE_ACT_LINK (3 << 4) /* Link Descriptor */
+
+/* ADMA2 Descriptor Table Entry for 32-bit Address */
+typedef struct adma2_dscr_32b {
+ uint32 len_attr;
+ uint32 phys_addr;
+} adma2_dscr_32b_t;
+
+/* ADMA1 Descriptor Table Entry */
+typedef struct adma1_dscr {
+ uint32 phys_addr_attr;
+} adma1_dscr_t;
+
+/************************************************************
+ * Internal interfaces: per-port references into bcmsdstd.c
+ */
+
+/* Global message bits */
+extern uint sd_msglevel;
+
+/* OS-independent interrupt handler */
+extern bool check_client_intr(sdioh_info_t *sd);
+
+/* Core interrupt enable/disable of device interrupts */
+extern void sdstd_devintr_on(sdioh_info_t *sd);
+extern void sdstd_devintr_off(sdioh_info_t *sd);
+
+/* Enable/disable interrupts for local controller events */
+extern void sdstd_intrs_on(sdioh_info_t *sd, uint16 norm, uint16 err);
+extern void sdstd_intrs_off(sdioh_info_t *sd, uint16 norm, uint16 err);
+
+/* Wait for specified interrupt and error bits to be set */
+extern void sdstd_spinbits(sdioh_info_t *sd, uint16 norm, uint16 err);
+
+
+/**************************************************************
+ * Internal interfaces: bcmsdstd.c references to per-port code
+ */
+
+/* Register mapping routines */
+extern uint32 *sdstd_reg_map(osl_t *osh, int32 addr, int size);
+extern void sdstd_reg_unmap(osl_t *osh, int32 addr, int size);
+
+/* Interrupt (de)registration routines */
+extern int sdstd_register_irq(sdioh_info_t *sd, uint irq);
+extern void sdstd_free_irq(uint irq, sdioh_info_t *sd);
+
+/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */
+extern void sdstd_lock(sdioh_info_t *sd);
+extern void sdstd_unlock(sdioh_info_t *sd);
+
+/* OS-specific wait-for-interrupt-or-status */
+extern uint16 sdstd_waitbits(sdioh_info_t *sd, uint16 norm, uint16 err, bool yield);
diff --git a/drivers/net/wireless/bcm4329/include/bcmspi.h b/drivers/net/wireless/bcm4329/include/bcmspi.h
new file mode 100644
index 00000000000..2e2bc935716
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmspi.h
@@ -0,0 +1,36 @@
+/*
+ * Broadcom SPI Low-Level Hardware Driver API
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmspi.h,v 13.3.10.2 2008/06/30 21:09:40 Exp $
+ */
+
+extern void spi_devintr_off(sdioh_info_t *sd);
+extern void spi_devintr_on(sdioh_info_t *sd);
+extern bool spi_start_clock(sdioh_info_t *sd, uint16 new_sd_divisor);
+extern bool spi_controller_highspeed_mode(sdioh_info_t *sd, bool hsmode);
+extern bool spi_check_client_intr(sdioh_info_t *sd, int *is_dev_intr);
+extern bool spi_hw_attach(sdioh_info_t *sd);
+extern bool spi_hw_detach(sdioh_info_t *sd);
+extern void spi_sendrecv(sdioh_info_t *sd, uint8 *msg_out, uint8 *msg_in, int msglen);
+extern void spi_spinbits(sdioh_info_t *sd);
+extern void spi_waitbits(sdioh_info_t *sd, bool yield);
diff --git a/drivers/net/wireless/bcm4329/include/bcmspibrcm.h b/drivers/net/wireless/bcm4329/include/bcmspibrcm.h
new file mode 100644
index 00000000000..9dce878d11e
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmspibrcm.h
@@ -0,0 +1,134 @@
+/*
+ * SD-SPI Protocol Conversion - BCMSDH->gSPI Translation Layer
+ *
+ * Copyright (C) 2010, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
+ * the contents of this file may not be disclosed to third parties, copied
+ * or duplicated in any form, in whole or in part, without the prior
+ * written permission of Broadcom Corporation.
+ *
+ * $Id: bcmspibrcm.h,v 1.4.4.1.4.3.6.1 2008/09/27 17:03:25 Exp $
+ */
+
+/* global msglevel for debug messages - bitvals come from sdiovar.h */
+
+#define sd_err(x)
+#define sd_trace(x)
+#define sd_info(x)
+#define sd_debug(x)
+#define sd_data(x)
+#define sd_ctrl(x)
+
+#define sd_log(x)
+
+#define SDIOH_ASSERT(exp) \
+ do { if (!(exp)) \
+ printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \
+ } while (0)
+
+#define BLOCK_SIZE_F1 64
+#define BLOCK_SIZE_F2 2048
+#define BLOCK_SIZE_F3 2048
+
+/* internal return code */
+#define SUCCESS 0
+#undef ERROR
+#define ERROR 1
+#define ERROR_UF 2
+#define ERROR_OF 3
+
+/* private bus modes */
+#define SDIOH_MODE_SPI 0
+
+#define USE_BLOCKMODE 0x2 /* Block mode can be single block or multi */
+#define USE_MULTIBLOCK 0x4
+
+struct sdioh_info {
+ uint cfg_bar; /* pci cfg address for bar */
+ uint32 caps; /* cached value of capabilities reg */
+ void *bar0; /* BAR0 for PCI Device */
+ osl_t *osh; /* osh handler */
+ void *controller; /* Pointer to SPI Controller's private data struct */
+
+ uint lockcount; /* nest count of spi_lock() calls */
+ bool client_intr_enabled; /* interrupt connnected flag */
+ bool intr_handler_valid; /* client driver interrupt handler valid */
+ sdioh_cb_fn_t intr_handler; /* registered interrupt handler */
+ void *intr_handler_arg; /* argument to call interrupt handler */
+ bool initialized; /* card initialized */
+ uint32 target_dev; /* Target device ID */
+ uint32 intmask; /* Current active interrupts */
+ void *sdos_info; /* Pointer to per-OS private data */
+
+ uint32 controller_type; /* Host controller type */
+ uint8 version; /* Host Controller Spec Compliance Version */
+ uint irq; /* Client irq */
+ uint32 intrcount; /* Client interrupts */
+ uint32 local_intrcount; /* Controller interrupts */
+ bool host_init_done; /* Controller initted */
+ bool card_init_done; /* Client SDIO interface initted */
+ bool polled_mode; /* polling for command completion */
+
+ bool sd_use_dma; /* DMA on CMD53 */
+ bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */
+ /* Must be on for sd_multiblock to be effective */
+ bool use_client_ints; /* If this is false, make sure to restore */
+ /* polling hack in wl_linux.c:wl_timer() */
+ int adapter_slot; /* Maybe dealing with multiple slots/controllers */
+ int sd_mode; /* SD1/SD4/SPI */
+ int client_block_size[SPI_MAX_IOFUNCS]; /* Blocksize */
+ uint32 data_xfer_count; /* Current transfer */
+ uint16 card_rca; /* Current Address */
+ uint8 num_funcs; /* Supported funcs on client */
+ uint32 card_dstatus; /* 32bit device status */
+ uint32 com_cis_ptr;
+ uint32 func_cis_ptr[SPI_MAX_IOFUNCS];
+ void *dma_buf;
+ ulong dma_phys;
+ int r_cnt; /* rx count */
+ int t_cnt; /* tx_count */
+ uint32 wordlen; /* host processor 16/32bits */
+ uint32 prev_fun;
+ uint32 chip;
+ uint32 chiprev;
+ bool resp_delay_all;
+ bool dwordmode;
+
+ struct spierrstats_t spierrstats;
+};
+
+/************************************************************
+ * Internal interfaces: per-port references into bcmspibrcm.c
+ */
+
+/* Global message bits */
+extern uint sd_msglevel;
+
+/**************************************************************
+ * Internal interfaces: bcmspibrcm.c references to per-port code
+ */
+
+/* Interrupt (de)registration routines */
+extern int spi_register_irq(sdioh_info_t *sd, uint irq);
+extern void spi_free_irq(uint irq, sdioh_info_t *sd);
+
+/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */
+extern void spi_lock(sdioh_info_t *sd);
+extern void spi_unlock(sdioh_info_t *sd);
+
+/* Allocate/init/free per-OS private data */
+extern int spi_osinit(sdioh_info_t *sd);
+extern void spi_osfree(sdioh_info_t *sd);
+
+#define SPI_RW_FLAG_M BITFIELD_MASK(1) /* Bit [31] - R/W Command Bit */
+#define SPI_RW_FLAG_S 31
+#define SPI_ACCESS_M BITFIELD_MASK(1) /* Bit [30] - Fixed/Incr Access */
+#define SPI_ACCESS_S 30
+#define SPI_FUNCTION_M BITFIELD_MASK(2) /* Bit [29:28] - Function Number */
+#define SPI_FUNCTION_S 28
+#define SPI_REG_ADDR_M BITFIELD_MASK(17) /* Bit [27:11] - Address */
+#define SPI_REG_ADDR_S 11
+#define SPI_LEN_M BITFIELD_MASK(11) /* Bit [10:0] - Packet length */
+#define SPI_LEN_S 0
diff --git a/drivers/net/wireless/bcm4329/include/bcmutils.h b/drivers/net/wireless/bcm4329/include/bcmutils.h
new file mode 100644
index 00000000000..f85ed351d66
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmutils.h
@@ -0,0 +1,637 @@
+/*
+ * Misc useful os-independent macros and functions.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: bcmutils.h,v 13.184.4.6.2.1.18.25 2010/04/26 06:05:24 Exp $
+ */
+
+
+#ifndef _bcmutils_h_
+#define _bcmutils_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define _BCM_U 0x01
+#define _BCM_L 0x02
+#define _BCM_D 0x04
+#define _BCM_C 0x08
+#define _BCM_P 0x10
+#define _BCM_S 0x20
+#define _BCM_X 0x40
+#define _BCM_SP 0x80
+
+extern const unsigned char bcm_ctype[];
+#define bcm_ismask(x) (bcm_ctype[(int)(unsigned char)(x)])
+
+#define bcm_isalnum(c) ((bcm_ismask(c)&(_BCM_U|_BCM_L|_BCM_D)) != 0)
+#define bcm_isalpha(c) ((bcm_ismask(c)&(_BCM_U|_BCM_L)) != 0)
+#define bcm_iscntrl(c) ((bcm_ismask(c)&(_BCM_C)) != 0)
+#define bcm_isdigit(c) ((bcm_ismask(c)&(_BCM_D)) != 0)
+#define bcm_isgraph(c) ((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D)) != 0)
+#define bcm_islower(c) ((bcm_ismask(c)&(_BCM_L)) != 0)
+#define bcm_isprint(c) ((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D|_BCM_SP)) != 0)
+#define bcm_ispunct(c) ((bcm_ismask(c)&(_BCM_P)) != 0)
+#define bcm_isspace(c) ((bcm_ismask(c)&(_BCM_S)) != 0)
+#define bcm_isupper(c) ((bcm_ismask(c)&(_BCM_U)) != 0)
+#define bcm_isxdigit(c) ((bcm_ismask(c)&(_BCM_D|_BCM_X)) != 0)
+#define bcm_tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c))
+#define bcm_toupper(c) (bcm_islower((c)) ? ((c) + 'A' - 'a') : (c))
+
+
+
+struct bcmstrbuf {
+ char *buf;
+ unsigned int size;
+ char *origbuf;
+ unsigned int origsize;
+};
+
+
+#ifdef BCMDRIVER
+#include <osl.h>
+
+#define GPIO_PIN_NOTDEFINED 0x20
+
+
+#define SPINWAIT(exp, us) { \
+ uint countdown = (us) + 9; \
+ while ((exp) && (countdown >= 10)) {\
+ OSL_DELAY(10); \
+ countdown -= 10; \
+ } \
+}
+
+
+
+#ifndef PKTQ_LEN_DEFAULT
+#define PKTQ_LEN_DEFAULT 128
+#endif
+#ifndef PKTQ_MAX_PREC
+#define PKTQ_MAX_PREC 16
+#endif
+
+typedef struct pktq_prec {
+ void *head;
+ void *tail;
+ uint16 len;
+ uint16 max;
+} pktq_prec_t;
+
+
+
+struct pktq {
+ uint16 num_prec;
+ uint16 hi_prec;
+ uint16 max;
+ uint16 len;
+
+ struct pktq_prec q[PKTQ_MAX_PREC];
+};
+
+
+struct spktq {
+ uint16 num_prec;
+ uint16 hi_prec;
+ uint16 max;
+ uint16 len;
+
+ struct pktq_prec q[1];
+};
+
+#define PKTQ_PREC_ITER(pq, prec) for (prec = (pq)->num_prec - 1; prec >= 0; prec--)
+
+
+
+
+struct ether_addr;
+
+extern int ether_isbcast(const void *ea);
+extern int ether_isnulladdr(const void *ea);
+
+
+
+#define pktq_psetmax(pq, prec, _max) ((pq)->q[prec].max = (_max))
+#define pktq_plen(pq, prec) ((pq)->q[prec].len)
+#define pktq_pavail(pq, prec) ((pq)->q[prec].max - (pq)->q[prec].len)
+#define pktq_pfull(pq, prec) ((pq)->q[prec].len >= (pq)->q[prec].max)
+#define pktq_pempty(pq, prec) ((pq)->q[prec].len == 0)
+
+#define pktq_ppeek(pq, prec) ((pq)->q[prec].head)
+#define pktq_ppeek_tail(pq, prec) ((pq)->q[prec].tail)
+
+extern void *pktq_penq(struct pktq *pq, int prec, void *p);
+extern void *pktq_penq_head(struct pktq *pq, int prec, void *p);
+extern void *pktq_pdeq(struct pktq *pq, int prec);
+extern void *pktq_pdeq_tail(struct pktq *pq, int prec);
+
+extern bool pktq_pdel(struct pktq *pq, void *p, int prec);
+
+
+extern void pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir);
+
+extern void pktq_flush(osl_t *osh, struct pktq *pq, bool dir);
+
+
+
+extern int pktq_mlen(struct pktq *pq, uint prec_bmp);
+extern void *pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out);
+
+
+
+#define pktq_len(pq) ((int)(pq)->len)
+#define pktq_max(pq) ((int)(pq)->max)
+#define pktq_avail(pq) ((int)((pq)->max - (pq)->len))
+#define pktq_full(pq) ((pq)->len >= (pq)->max)
+#define pktq_empty(pq) ((pq)->len == 0)
+
+
+#define pktenq(pq, p) pktq_penq(((struct pktq *)pq), 0, (p))
+#define pktenq_head(pq, p) pktq_penq_head(((struct pktq *)pq), 0, (p))
+#define pktdeq(pq) pktq_pdeq(((struct pktq *)pq), 0)
+#define pktdeq_tail(pq) pktq_pdeq_tail(((struct pktq *)pq), 0)
+#define pktqinit(pq, len) pktq_init(((struct pktq *)pq), 1, len)
+
+extern void pktq_init(struct pktq *pq, int num_prec, int max_len);
+
+extern void *pktq_deq(struct pktq *pq, int *prec_out);
+extern void *pktq_deq_tail(struct pktq *pq, int *prec_out);
+extern void *pktq_peek(struct pktq *pq, int *prec_out);
+extern void *pktq_peek_tail(struct pktq *pq, int *prec_out);
+
+
+
+extern uint pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf);
+extern uint pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf);
+extern uint pkttotlen(osl_t *osh, void *p);
+extern void *pktlast(osl_t *osh, void *p);
+extern uint pktsegcnt(osl_t *osh, void *p);
+
+
+extern uint pktsetprio(void *pkt, bool update_vtag);
+#define PKTPRIO_VDSCP 0x100
+#define PKTPRIO_VLAN 0x200
+#define PKTPRIO_UPD 0x400
+#define PKTPRIO_DSCP 0x800
+
+
+extern int bcm_atoi(char *s);
+extern ulong bcm_strtoul(char *cp, char **endp, uint base);
+extern char *bcmstrstr(char *haystack, char *needle);
+extern char *bcmstrcat(char *dest, const char *src);
+extern char *bcmstrncat(char *dest, const char *src, uint size);
+extern ulong wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen);
+char* bcmstrtok(char **string, const char *delimiters, char *tokdelim);
+int bcmstricmp(const char *s1, const char *s2);
+int bcmstrnicmp(const char* s1, const char* s2, int cnt);
+
+
+
+extern char *bcm_ether_ntoa(const struct ether_addr *ea, char *buf);
+extern int bcm_ether_atoe(char *p, struct ether_addr *ea);
+
+
+struct ipv4_addr;
+extern char *bcm_ip_ntoa(struct ipv4_addr *ia, char *buf);
+
+
+extern void bcm_mdelay(uint ms);
+
+extern char *getvar(char *vars, const char *name);
+extern int getintvar(char *vars, const char *name);
+extern uint getgpiopin(char *vars, char *pin_name, uint def_pin);
+#define bcm_perf_enable()
+#define bcmstats(fmt)
+#define bcmlog(fmt, a1, a2)
+#define bcmdumplog(buf, size) *buf = '\0'
+#define bcmdumplogent(buf, idx) -1
+
+#define bcmtslog(tstamp, fmt, a1, a2)
+#define bcmprinttslogs()
+#define bcmprinttstamp(us)
+
+
+
+
+typedef struct bcm_iovar {
+ const char *name;
+ uint16 varid;
+ uint16 flags;
+ uint16 type;
+ uint16 minlen;
+} bcm_iovar_t;
+
+
+
+
+#define IOV_GET 0
+#define IOV_SET 1
+
+
+#define IOV_GVAL(id) ((id)*2)
+#define IOV_SVAL(id) (((id)*2)+IOV_SET)
+#define IOV_ISSET(actionid) ((actionid & IOV_SET) == IOV_SET)
+
+
+
+extern const bcm_iovar_t *bcm_iovar_lookup(const bcm_iovar_t *table, const char *name);
+extern int bcm_iovar_lencheck(const bcm_iovar_t *table, void *arg, int len, bool set);
+
+#endif
+
+
+#define IOVT_VOID 0
+#define IOVT_BOOL 1
+#define IOVT_INT8 2
+#define IOVT_UINT8 3
+#define IOVT_INT16 4
+#define IOVT_UINT16 5
+#define IOVT_INT32 6
+#define IOVT_UINT32 7
+#define IOVT_BUFFER 8
+#define BCM_IOVT_VALID(type) (((unsigned int)(type)) <= IOVT_BUFFER)
+
+
+#define BCM_IOV_TYPE_INIT { \
+ "void", \
+ "bool", \
+ "int8", \
+ "uint8", \
+ "int16", \
+ "uint16", \
+ "int32", \
+ "uint32", \
+ "buffer", \
+ "" }
+
+#define BCM_IOVT_IS_INT(type) (\
+ (type == IOVT_BOOL) || \
+ (type == IOVT_INT8) || \
+ (type == IOVT_UINT8) || \
+ (type == IOVT_INT16) || \
+ (type == IOVT_UINT16) || \
+ (type == IOVT_INT32) || \
+ (type == IOVT_UINT32))
+
+
+
+#define BCME_STRLEN 64
+#define VALID_BCMERROR(e) ((e <= 0) && (e >= BCME_LAST))
+
+
+
+
+#define BCME_OK 0
+#define BCME_ERROR -1
+#define BCME_BADARG -2
+#define BCME_BADOPTION -3
+#define BCME_NOTUP -4
+#define BCME_NOTDOWN -5
+#define BCME_NOTAP -6
+#define BCME_NOTSTA -7
+#define BCME_BADKEYIDX -8
+#define BCME_RADIOOFF -9
+#define BCME_NOTBANDLOCKED -10
+#define BCME_NOCLK -11
+#define BCME_BADRATESET -12
+#define BCME_BADBAND -13
+#define BCME_BUFTOOSHORT -14
+#define BCME_BUFTOOLONG -15
+#define BCME_BUSY -16
+#define BCME_NOTASSOCIATED -17
+#define BCME_BADSSIDLEN -18
+#define BCME_OUTOFRANGECHAN -19
+#define BCME_BADCHAN -20
+#define BCME_BADADDR -21
+#define BCME_NORESOURCE -22
+#define BCME_UNSUPPORTED -23
+#define BCME_BADLEN -24
+#define BCME_NOTREADY -25
+#define BCME_EPERM -26
+#define BCME_NOMEM -27
+#define BCME_ASSOCIATED -28
+#define BCME_RANGE -29
+#define BCME_NOTFOUND -30
+#define BCME_WME_NOT_ENABLED -31
+#define BCME_TSPEC_NOTFOUND -32
+#define BCME_ACM_NOTSUPPORTED -33
+#define BCME_NOT_WME_ASSOCIATION -34
+#define BCME_SDIO_ERROR -35
+#define BCME_DONGLE_DOWN -36
+#define BCME_VERSION -37
+#define BCME_TXFAIL -38
+#define BCME_RXFAIL -39
+#define BCME_NODEVICE -40
+#define BCME_UNFINISHED -41
+#define BCME_LAST BCME_UNFINISHED
+
+
+#define BCMERRSTRINGTABLE { \
+ "OK", \
+ "Undefined error", \
+ "Bad Argument", \
+ "Bad Option", \
+ "Not up", \
+ "Not down", \
+ "Not AP", \
+ "Not STA", \
+ "Bad Key Index", \
+ "Radio Off", \
+ "Not band locked", \
+ "No clock", \
+ "Bad Rate valueset", \
+ "Bad Band", \
+ "Buffer too short", \
+ "Buffer too long", \
+ "Busy", \
+ "Not Associated", \
+ "Bad SSID len", \
+ "Out of Range Channel", \
+ "Bad Channel", \
+ "Bad Address", \
+ "Not Enough Resources", \
+ "Unsupported", \
+ "Bad length", \
+ "Not Ready", \
+ "Not Permitted", \
+ "No Memory", \
+ "Associated", \
+ "Not In Range", \
+ "Not Found", \
+ "WME Not Enabled", \
+ "TSPEC Not Found", \
+ "ACM Not Supported", \
+ "Not WME Association", \
+ "SDIO Bus Error", \
+ "Dongle Not Accessible", \
+ "Incorrect version", \
+ "TX Failure", \
+ "RX Failure", \
+ "Device Not Present", \
+ "Command not finished", \
+}
+
+#ifndef ABS
+#define ABS(a) (((a) < 0)?-(a):(a))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b))?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b))?(a):(b))
+#endif
+
+#define CEIL(x, y) (((x) + ((y)-1)) / (y))
+#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
+#define ISALIGNED(a, x) (((a) & ((x)-1)) == 0)
+#define ALIGN_ADDR(addr, boundary) (void *)(((uintptr)(addr) + (boundary) - 1) \
+ & ~((boundary) - 1))
+#define ISPOWEROF2(x) ((((x)-1)&(x)) == 0)
+#define VALID_MASK(mask) !((mask) & ((mask) + 1))
+#ifndef OFFSETOF
+#define OFFSETOF(type, member) ((uint)(uintptr)&((type *)0)->member)
+#endif
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
+#endif
+
+
+#ifndef setbit
+#ifndef NBBY
+#define NBBY 8
+#endif
+#define setbit(a, i) (((uint8 *)a)[(i)/NBBY] |= 1<<((i)%NBBY))
+#define clrbit(a, i) (((uint8 *)a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
+#define isset(a, i) (((const uint8 *)a)[(i)/NBBY] & (1<<((i)%NBBY)))
+#define isclr(a, i) ((((const uint8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+#endif
+
+#define NBITS(type) (sizeof(type) * 8)
+#define NBITVAL(nbits) (1 << (nbits))
+#define MAXBITVAL(nbits) ((1 << (nbits)) - 1)
+#define NBITMASK(nbits) MAXBITVAL(nbits)
+#define MAXNBVAL(nbyte) MAXBITVAL((nbyte) * 8)
+
+
+#define MUX(pred, true, false) ((pred) ? (true) : (false))
+
+
+#define MODDEC(x, bound) MUX((x) == 0, (bound) - 1, (x) - 1)
+#define MODINC(x, bound) MUX((x) == (bound) - 1, 0, (x) + 1)
+
+
+#define MODDEC_POW2(x, bound) (((x) - 1) & ((bound) - 1))
+#define MODINC_POW2(x, bound) (((x) + 1) & ((bound) - 1))
+
+
+#define MODADD(x, y, bound) \
+ MUX((x) + (y) >= (bound), (x) + (y) - (bound), (x) + (y))
+#define MODSUB(x, y, bound) \
+ MUX(((int)(x)) - ((int)(y)) < 0, (x) - (y) + (bound), (x) - (y))
+
+
+#define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1))
+#define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1))
+
+
+#define CRC8_INIT_VALUE 0xff
+#define CRC8_GOOD_VALUE 0x9f
+#define CRC16_INIT_VALUE 0xffff
+#define CRC16_GOOD_VALUE 0xf0b8
+#define CRC32_INIT_VALUE 0xffffffff
+#define CRC32_GOOD_VALUE 0xdebb20e3
+
+
+typedef struct bcm_bit_desc {
+ uint32 bit;
+ const char* name;
+} bcm_bit_desc_t;
+
+
+typedef struct bcm_tlv {
+ uint8 id;
+ uint8 len;
+ uint8 data[1];
+} bcm_tlv_t;
+
+
+#define bcm_valid_tlv(elt, buflen) ((buflen) >= 2 && (int)(buflen) >= (int)(2 + (elt)->len))
+
+
+#define ETHER_ADDR_STR_LEN 18
+
+
+#ifdef IL_BIGENDIAN
+static INLINE uint32
+load32_ua(uint8 *a)
+{
+ return ((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]);
+}
+
+static INLINE void
+store32_ua(uint8 *a, uint32 v)
+{
+ a[0] = (v >> 24) & 0xff;
+ a[1] = (v >> 16) & 0xff;
+ a[2] = (v >> 8) & 0xff;
+ a[3] = v & 0xff;
+}
+
+static INLINE uint16
+load16_ua(uint8 *a)
+{
+ return ((a[0] << 8) | a[1]);
+}
+
+static INLINE void
+store16_ua(uint8 *a, uint16 v)
+{
+ a[0] = (v >> 8) & 0xff;
+ a[1] = v & 0xff;
+}
+
+#else
+
+static INLINE uint32
+load32_ua(uint8 *a)
+{
+ return ((a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]);
+}
+
+static INLINE void
+store32_ua(uint8 *a, uint32 v)
+{
+ a[3] = (v >> 24) & 0xff;
+ a[2] = (v >> 16) & 0xff;
+ a[1] = (v >> 8) & 0xff;
+ a[0] = v & 0xff;
+}
+
+static INLINE uint16
+load16_ua(uint8 *a)
+{
+ return ((a[1] << 8) | a[0]);
+}
+
+static INLINE void
+store16_ua(uint8 *a, uint16 v)
+{
+ a[1] = (v >> 8) & 0xff;
+ a[0] = v & 0xff;
+}
+
+#endif
+
+
+
+static INLINE void
+xor_128bit_block(const uint8 *src1, const uint8 *src2, uint8 *dst)
+{
+ if (
+#ifdef __i386__
+ 1 ||
+#endif
+ (((uintptr)src1 | (uintptr)src2 | (uintptr)dst) & 3) == 0) {
+
+
+ ((uint32 *)dst)[0] = ((uint32 *)src1)[0] ^ ((uint32 *)src2)[0];
+ ((uint32 *)dst)[1] = ((uint32 *)src1)[1] ^ ((uint32 *)src2)[1];
+ ((uint32 *)dst)[2] = ((uint32 *)src1)[2] ^ ((uint32 *)src2)[2];
+ ((uint32 *)dst)[3] = ((uint32 *)src1)[3] ^ ((uint32 *)src2)[3];
+ } else {
+
+ int k;
+ for (k = 0; k < 16; k++)
+ dst[k] = src1[k] ^ src2[k];
+ }
+}
+
+
+
+extern uint8 hndcrc8(uint8 *p, uint nbytes, uint8 crc);
+extern uint16 hndcrc16(uint8 *p, uint nbytes, uint16 crc);
+extern uint32 hndcrc32(uint8 *p, uint nbytes, uint32 crc);
+
+#if defined(DHD_DEBUG) || defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || \
+ defined(WLMSG_ASSOC)
+extern int bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len);
+extern int bcm_format_hex(char *str, const void *bytes, int len);
+extern void prhex(const char *msg, uchar *buf, uint len);
+#endif
+extern char *bcm_brev_str(uint32 brev, char *buf);
+extern void printbig(char *buf);
+
+
+extern bcm_tlv_t *bcm_next_tlv(bcm_tlv_t *elt, int *buflen);
+extern bcm_tlv_t *bcm_parse_tlvs(void *buf, int buflen, uint key);
+extern bcm_tlv_t *bcm_parse_ordered_tlvs(void *buf, int buflen, uint key);
+
+
+extern const char *bcmerrorstr(int bcmerror);
+
+
+typedef uint32 mbool;
+#define mboolset(mb, bit) ((mb) |= (bit))
+#define mboolclr(mb, bit) ((mb) &= ~(bit))
+#define mboolisset(mb, bit) (((mb) & (bit)) != 0)
+#define mboolmaskset(mb, mask, val) ((mb) = (((mb) & ~(mask)) | (val)))
+
+
+extern uint16 bcm_qdbm_to_mw(uint8 qdbm);
+extern uint8 bcm_mw_to_qdbm(uint16 mw);
+
+
+struct fielddesc {
+ const char *nameandfmt;
+ uint32 offset;
+ uint32 len;
+};
+
+extern void bcm_binit(struct bcmstrbuf *b, char *buf, uint size);
+extern int bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...);
+extern void bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount);
+extern int bcm_cmp_bytes(uchar *arg1, uchar *arg2, uint8 nbytes);
+extern void bcm_print_bytes(char *name, const uchar *cdata, int len);
+
+typedef uint32 (*bcmutl_rdreg_rtn)(void *arg0, uint arg1, uint32 offset);
+extern uint bcmdumpfields(bcmutl_rdreg_rtn func_ptr, void *arg0, uint arg1, struct fielddesc *str,
+ char *buf, uint32 bufsize);
+
+extern uint bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint len);
+extern uint bcm_bitcount(uint8 *bitmap, uint bytelength);
+
+#if defined(WLTINYDUMP) || defined(WLMSG_INFORM) || defined(WLMSG_ASSOC) || \
+ defined(WLMSG_PRPKT) || defined(WLMSG_WSEC)
+extern int bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len);
+#endif
+
+
+#define SSID_FMT_BUF_LEN ((4 * DOT11_MAX_SSID_LEN) + 1)
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/bcmwifi.h b/drivers/net/wireless/bcm4329/include/bcmwifi.h
new file mode 100644
index 00000000000..038aedcdb3c
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/bcmwifi.h
@@ -0,0 +1,154 @@
+/*
+ * Misc utility routines for WL and Apps
+ * This header file housing the define and function prototype use by
+ * both the wl driver, tools & Apps.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: bcmwifi.h,v 1.15.30.4 2010/03/10 20:10:52 Exp $
+ */
+
+
+#ifndef _bcmwifi_h_
+#define _bcmwifi_h_
+
+
+
+typedef uint16 chanspec_t;
+
+
+#define CH_UPPER_SB 0x01
+#define CH_LOWER_SB 0x02
+#define CH_EWA_VALID 0x04
+#define CH_20MHZ_APART 4
+#define CH_10MHZ_APART 2
+#define CH_5MHZ_APART 1
+#define CH_MAX_2G_CHANNEL 14
+#define WLC_MAX_2G_CHANNEL CH_MAX_2G_CHANNEL
+#define MAXCHANNEL 224
+
+#define WL_CHANSPEC_CHAN_MASK 0x00ff
+#define WL_CHANSPEC_CHAN_SHIFT 0
+
+#define WL_CHANSPEC_CTL_SB_MASK 0x0300
+#define WL_CHANSPEC_CTL_SB_SHIFT 8
+#define WL_CHANSPEC_CTL_SB_LOWER 0x0100
+#define WL_CHANSPEC_CTL_SB_UPPER 0x0200
+#define WL_CHANSPEC_CTL_SB_NONE 0x0300
+
+#define WL_CHANSPEC_BW_MASK 0x0C00
+#define WL_CHANSPEC_BW_SHIFT 10
+#define WL_CHANSPEC_BW_10 0x0400
+#define WL_CHANSPEC_BW_20 0x0800
+#define WL_CHANSPEC_BW_40 0x0C00
+
+#define WL_CHANSPEC_BAND_MASK 0xf000
+#define WL_CHANSPEC_BAND_SHIFT 12
+#define WL_CHANSPEC_BAND_5G 0x1000
+#define WL_CHANSPEC_BAND_2G 0x2000
+#define INVCHANSPEC 255
+
+
+#define WF_CHAN_FACTOR_2_4_G 4814
+#define WF_CHAN_FACTOR_5_G 10000
+#define WF_CHAN_FACTOR_4_G 8000
+
+
+#define LOWER_20_SB(channel) ((channel > CH_10MHZ_APART) ? (channel - CH_10MHZ_APART) : 0)
+#define UPPER_20_SB(channel) ((channel < (MAXCHANNEL - CH_10MHZ_APART)) ? \
+ (channel + CH_10MHZ_APART) : 0)
+#define CHSPEC_WLCBANDUNIT(chspec) (CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX)
+#define CH20MHZ_CHSPEC(channel) (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_20 | \
+ WL_CHANSPEC_CTL_SB_NONE | (((channel) <= CH_MAX_2G_CHANNEL) ? \
+ WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G))
+#define NEXT_20MHZ_CHAN(channel) ((channel < (MAXCHANNEL - CH_20MHZ_APART)) ? \
+ (channel + CH_20MHZ_APART) : 0)
+#define CH40MHZ_CHSPEC(channel, ctlsb) (chanspec_t) \
+ ((channel) | (ctlsb) | WL_CHANSPEC_BW_40 | \
+ ((channel) <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : \
+ WL_CHANSPEC_BAND_5G))
+#define CHSPEC_CHANNEL(chspec) ((uint8)(chspec & WL_CHANSPEC_CHAN_MASK))
+#define CHSPEC_BAND(chspec) (chspec & WL_CHANSPEC_BAND_MASK)
+
+#ifdef WL20MHZ_ONLY
+
+#define CHSPEC_CTL_SB(chspec) WL_CHANSPEC_CTL_SB_NONE
+#define CHSPEC_BW(chspec) WL_CHANSPEC_BW_20
+#define CHSPEC_IS10(chspec) 0
+#define CHSPEC_IS20(chspec) 1
+#ifndef CHSPEC_IS40
+#define CHSPEC_IS40(chspec) 0
+#endif
+
+#else
+
+#define CHSPEC_CTL_SB(chspec) (chspec & WL_CHANSPEC_CTL_SB_MASK)
+#define CHSPEC_BW(chspec) (chspec & WL_CHANSPEC_BW_MASK)
+#define CHSPEC_IS10(chspec) ((chspec & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10)
+#define CHSPEC_IS20(chspec) ((chspec & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20)
+#ifndef CHSPEC_IS40
+#define CHSPEC_IS40(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40)
+#endif
+
+#endif
+
+#define CHSPEC_IS5G(chspec) ((chspec & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G)
+#define CHSPEC_IS2G(chspec) ((chspec & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G)
+#define CHSPEC_SB_NONE(chspec) ((chspec & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE)
+#define CHSPEC_SB_UPPER(chspec) ((chspec & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER)
+#define CHSPEC_SB_LOWER(chspec) ((chspec & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER)
+#define CHSPEC_CTL_CHAN(chspec) ((CHSPEC_SB_LOWER(chspec)) ? \
+ (LOWER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \
+ (UPPER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK))))
+
+#define CHSPEC2WLC_BAND(chspec) (CHSPEC_IS5G((chspec))? WLC_BAND_5G: WLC_BAND_2G)
+
+#define CHANSPEC_STR_LEN 8
+
+
+#define WLC_MAXRATE 108
+#define WLC_RATE_1M 2
+#define WLC_RATE_2M 4
+#define WLC_RATE_5M5 11
+#define WLC_RATE_11M 22
+#define WLC_RATE_6M 12
+#define WLC_RATE_9M 18
+#define WLC_RATE_12M 24
+#define WLC_RATE_18M 36
+#define WLC_RATE_24M 48
+#define WLC_RATE_36M 72
+#define WLC_RATE_48M 96
+#define WLC_RATE_54M 108
+
+#define WLC_2G_25MHZ_OFFSET 5
+
+
+extern char * wf_chspec_ntoa(chanspec_t chspec, char *buf);
+
+
+extern chanspec_t wf_chspec_aton(char *a);
+
+
+extern int wf_mhz2channel(uint freq, uint start_factor);
+
+
+extern int wf_channel2mhz(uint channel, uint start_factor);
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/dhdioctl.h b/drivers/net/wireless/bcm4329/include/dhdioctl.h
new file mode 100644
index 00000000000..980a1430100
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/dhdioctl.h
@@ -0,0 +1,123 @@
+/*
+ * Definitions for ioctls to access DHD iovars.
+ * Based on wlioctl.h (for Broadcom 802.11abg driver).
+ * (Moves towards generic ioctls for BCM drivers/iovars.)
+ *
+ * Definitions subject to change without notice.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: dhdioctl.h,v 13.7.8.1.4.1.16.5 2010/05/21 21:49:38 Exp $
+ */
+
+#ifndef _dhdioctl_h_
+#define _dhdioctl_h_
+
+#include <typedefs.h>
+
+
+/* require default structure packing */
+#define BWL_DEFAULT_PACKING
+#include <packed_section_start.h>
+
+
+/* Linux network driver ioctl encoding */
+typedef struct dhd_ioctl {
+ uint cmd; /* common ioctl definition */
+ void *buf; /* pointer to user buffer */
+ uint len; /* length of user buffer */
+ bool set; /* get or set request (optional) */
+ uint used; /* bytes read or written (optional) */
+ uint needed; /* bytes needed (optional) */
+ uint driver; /* to identify target driver */
+} dhd_ioctl_t;
+
+/* per-driver magic numbers */
+#define DHD_IOCTL_MAGIC 0x00444944
+
+/* bump this number if you change the ioctl interface */
+#define DHD_IOCTL_VERSION 1
+
+#define DHD_IOCTL_MAXLEN 8192 /* max length ioctl buffer required */
+#define DHD_IOCTL_SMLEN 256 /* "small" length ioctl buffer required */
+
+/* common ioctl definitions */
+#define DHD_GET_MAGIC 0
+#define DHD_GET_VERSION 1
+#define DHD_GET_VAR 2
+#define DHD_SET_VAR 3
+
+/* message levels */
+#define DHD_ERROR_VAL 0x0001
+#define DHD_TRACE_VAL 0x0002
+#define DHD_INFO_VAL 0x0004
+#define DHD_DATA_VAL 0x0008
+#define DHD_CTL_VAL 0x0010
+#define DHD_TIMER_VAL 0x0020
+#define DHD_HDRS_VAL 0x0040
+#define DHD_BYTES_VAL 0x0080
+#define DHD_INTR_VAL 0x0100
+#define DHD_LOG_VAL 0x0200
+#define DHD_GLOM_VAL 0x0400
+#define DHD_EVENT_VAL 0x0800
+#define DHD_BTA_VAL 0x1000
+#define DHD_ISCAN_VAL 0x2000
+
+#ifdef SDTEST
+/* For pktgen iovar */
+typedef struct dhd_pktgen {
+ uint version; /* To allow structure change tracking */
+ uint freq; /* Max ticks between tx/rx attempts */
+ uint count; /* Test packets to send/rcv each attempt */
+ uint print; /* Print counts every <print> attempts */
+ uint total; /* Total packets (or bursts) */
+ uint minlen; /* Minimum length of packets to send */
+ uint maxlen; /* Maximum length of packets to send */
+ uint numsent; /* Count of test packets sent */
+ uint numrcvd; /* Count of test packets received */
+ uint numfail; /* Count of test send failures */
+ uint mode; /* Test mode (type of test packets) */
+ uint stop; /* Stop after this many tx failures */
+} dhd_pktgen_t;
+
+/* Version in case structure changes */
+#define DHD_PKTGEN_VERSION 2
+
+/* Type of test packets to use */
+#define DHD_PKTGEN_ECHO 1 /* Send echo requests */
+#define DHD_PKTGEN_SEND 2 /* Send discard packets */
+#define DHD_PKTGEN_RXBURST 3 /* Request dongle send N packets */
+#define DHD_PKTGEN_RECV 4 /* Continuous rx from continuous tx dongle */
+#endif /* SDTEST */
+
+/* Enter idle immediately (no timeout) */
+#define DHD_IDLE_IMMEDIATE (-1)
+
+/* Values for idleclock iovar: other values are the sd_divisor to use when idle */
+#define DHD_IDLE_ACTIVE 0 /* Do not request any SD clock change when idle */
+#define DHD_IDLE_STOP (-1) /* Request SD clock be stopped (and use SD1 mode) */
+
+
+/* require default structure packing */
+#include <packed_section_end.h>
+
+
+#endif /* _dhdioctl_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/epivers.h b/drivers/net/wireless/bcm4329/include/epivers.h
new file mode 100644
index 00000000000..23ee514a7b6
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/epivers.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: epivers.h.in,v 13.25 2005/10/28 18:35:33 Exp $
+ *
+*/
+
+
+#ifndef _epivers_h_
+#define _epivers_h_
+
+#define EPI_MAJOR_VERSION 4
+
+#define EPI_MINOR_VERSION 218
+
+#define EPI_RC_NUMBER 248
+
+#define EPI_INCREMENTAL_NUMBER 18
+
+#define EPI_BUILD_NUMBER 0
+
+#define EPI_VERSION 4, 218, 248, 18
+
+#define EPI_VERSION_NUM 0x04daf812
+
+
+#define EPI_VERSION_STR "4.218.248.18"
+#define EPI_ROUTER_VERSION_STR "4.219.248.18"
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/hndpmu.h b/drivers/net/wireless/bcm4329/include/hndpmu.h
new file mode 100644
index 00000000000..e829b3df2d0
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/hndpmu.h
@@ -0,0 +1,34 @@
+/*
+ * HND SiliconBackplane PMU support.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: hndpmu.h,v 13.14.4.3.4.3.8.7 2010/04/09 13:20:51 Exp $
+ */
+
+#ifndef _hndpmu_h_
+#define _hndpmu_h_
+
+
+extern void si_pmu_otp_power(si_t *sih, osl_t *osh, bool on);
+extern void si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength);
+
+#endif /* _hndpmu_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/hndrte_armtrap.h b/drivers/net/wireless/bcm4329/include/hndrte_armtrap.h
new file mode 100644
index 00000000000..ca3281b6d90
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/hndrte_armtrap.h
@@ -0,0 +1,88 @@
+/*
+ * HNDRTE arm trap handling.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: hndrte_armtrap.h,v 13.3.196.2 2010/07/15 19:06:11 Exp $
+ */
+
+#ifndef _hndrte_armtrap_h
+#define _hndrte_armtrap_h
+
+
+/* ARM trap handling */
+
+/* Trap types defined by ARM (see arminc.h) */
+
+/* Trap locations in lo memory */
+#define TRAP_STRIDE 4
+#define FIRST_TRAP TR_RST
+#define LAST_TRAP (TR_FIQ * TRAP_STRIDE)
+
+#if defined(__ARM_ARCH_4T__)
+#define MAX_TRAP_TYPE (TR_FIQ + 1)
+#elif defined(__ARM_ARCH_7M__)
+#define MAX_TRAP_TYPE (TR_ISR + ARMCM3_NUMINTS)
+#endif /* __ARM_ARCH_7M__ */
+
+/* The trap structure is defined here as offsets for assembly */
+#define TR_TYPE 0x00
+#define TR_EPC 0x04
+#define TR_CPSR 0x08
+#define TR_SPSR 0x0c
+#define TR_REGS 0x10
+#define TR_REG(n) (TR_REGS + (n) * 4)
+#define TR_SP TR_REG(13)
+#define TR_LR TR_REG(14)
+#define TR_PC TR_REG(15)
+
+#define TRAP_T_SIZE 80
+
+#ifndef _LANGUAGE_ASSEMBLY
+
+#include <typedefs.h>
+
+typedef struct _trap_struct {
+ uint32 type;
+ uint32 epc;
+ uint32 cpsr;
+ uint32 spsr;
+ uint32 r0;
+ uint32 r1;
+ uint32 r2;
+ uint32 r3;
+ uint32 r4;
+ uint32 r5;
+ uint32 r6;
+ uint32 r7;
+ uint32 r8;
+ uint32 r9;
+ uint32 r10;
+ uint32 r11;
+ uint32 r12;
+ uint32 r13;
+ uint32 r14;
+ uint32 pc;
+} trap_t;
+
+#endif /* !_LANGUAGE_ASSEMBLY */
+
+#endif /* _hndrte_armtrap_h */
diff --git a/drivers/net/wireless/bcm4329/include/hndrte_cons.h b/drivers/net/wireless/bcm4329/include/hndrte_cons.h
new file mode 100644
index 00000000000..a42417478a1
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/hndrte_cons.h
@@ -0,0 +1,63 @@
+/*
+ * Console support for hndrte.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: hndrte_cons.h,v 13.1.2.4 2010/07/15 19:06:11 Exp $
+ */
+
+#include <typedefs.h>
+
+#define CBUF_LEN (128)
+
+#define LOG_BUF_LEN 1024
+
+typedef struct {
+ uint32 buf; /* Can't be pointer on (64-bit) hosts */
+ uint buf_size;
+ uint idx;
+ char *_buf_compat; /* Redundant pointer for backward compat. */
+} hndrte_log_t;
+
+typedef struct {
+ /* Virtual UART
+ * When there is no UART (e.g. Quickturn), the host should write a complete
+ * input line directly into cbuf and then write the length into vcons_in.
+ * This may also be used when there is a real UART (at risk of conflicting with
+ * the real UART). vcons_out is currently unused.
+ */
+ volatile uint vcons_in;
+ volatile uint vcons_out;
+
+ /* Output (logging) buffer
+ * Console output is written to a ring buffer log_buf at index log_idx.
+ * The host may read the output when it sees log_idx advance.
+ * Output will be lost if the output wraps around faster than the host polls.
+ */
+ hndrte_log_t log;
+
+ /* Console input line buffer
+ * Characters are read one at a time into cbuf until <CR> is received, then
+ * the buffer is processed as a command line. Also used for virtual UART.
+ */
+ uint cbuf_idx;
+ char cbuf[CBUF_LEN];
+} hndrte_cons_t;
diff --git a/drivers/net/wireless/bcm4329/include/hndsoc.h b/drivers/net/wireless/bcm4329/include/hndsoc.h
new file mode 100644
index 00000000000..35424175f55
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/hndsoc.h
@@ -0,0 +1,195 @@
+/*
+ * Broadcom HND chip & on-chip-interconnect-related definitions.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: hndsoc.h,v 13.3.10.3 2008/08/06 03:43:25 Exp $
+ */
+
+#ifndef _HNDSOC_H
+#define _HNDSOC_H
+
+/* Include the soci specific files */
+#include <sbconfig.h>
+#include <aidmp.h>
+
+/*
+ * SOC Interconnect Address Map.
+ * All regions may not exist on all chips.
+ */
+#define SI_SDRAM_BASE 0x00000000 /* Physical SDRAM */
+#define SI_PCI_MEM 0x08000000 /* Host Mode sb2pcitranslation0 (64 MB) */
+#define SI_PCI_MEM_SZ (64 * 1024 * 1024)
+#define SI_PCI_CFG 0x0c000000 /* Host Mode sb2pcitranslation1 (64 MB) */
+#define SI_SDRAM_SWAPPED 0x10000000 /* Byteswapped Physical SDRAM */
+
+#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */
+#define SI_CORE_SIZE 0x1000 /* each core gets 4Kbytes for registers */
+#ifndef SI_MAXCORES
+#define SI_MAXCORES 16 /* Max cores (this is arbitrary, for software
+ * convenience and could be changed if we
+ * make any larger chips
+ */
+#endif
+
+#define SI_FASTRAM 0x19000000 /* On-chip RAM on chips that also have DDR */
+
+#define SI_FLASH2 0x1c000000 /* Flash Region 2 (region 1 shadowed here) */
+#define SI_FLASH2_SZ 0x02000000 /* Size of Flash Region 2 */
+#define SI_ARMCM3_ROM 0x1e000000 /* ARM Cortex-M3 ROM */
+#define SI_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */
+#define SI_FLASH1_SZ 0x00400000 /* MIPS Size of Flash Region 1 */
+#define SI_ARM7S_ROM 0x20000000 /* ARM7TDMI-S ROM */
+#define SI_ARMCM3_SRAM2 0x60000000 /* ARM Cortex-M3 SRAM Region 2 */
+#define SI_ARM7S_SRAM2 0x80000000 /* ARM7TDMI-S SRAM Region 2 */
+#define SI_ARM_FLASH1 0xffff0000 /* ARM Flash Region 1 */
+#define SI_ARM_FLASH1_SZ 0x00010000 /* ARM Size of Flash Region 1 */
+
+#define SI_PCI_DMA 0x40000000 /* Client Mode sb2pcitranslation2 (1 GB) */
+#define SI_PCI_DMA2 0x80000000 /* Client Mode sb2pcitranslation2 (1 GB) */
+#define SI_PCI_DMA_SZ 0x40000000 /* Client Mode sb2pcitranslation2 size in bytes */
+#define SI_PCIE_DMA_L32 0x00000000 /* PCIE Client Mode sb2pcitranslation2
+ * (2 ZettaBytes), low 32 bits
+ */
+#define SI_PCIE_DMA_H32 0x80000000 /* PCIE Client Mode sb2pcitranslation2
+ * (2 ZettaBytes), high 32 bits
+ */
+
+/* core codes */
+#define NODEV_CORE_ID 0x700 /* Invalid coreid */
+#define CC_CORE_ID 0x800 /* chipcommon core */
+#define ILINE20_CORE_ID 0x801 /* iline20 core */
+#define SRAM_CORE_ID 0x802 /* sram core */
+#define SDRAM_CORE_ID 0x803 /* sdram core */
+#define PCI_CORE_ID 0x804 /* pci core */
+#define MIPS_CORE_ID 0x805 /* mips core */
+#define ENET_CORE_ID 0x806 /* enet mac core */
+#define CODEC_CORE_ID 0x807 /* v90 codec core */
+#define USB_CORE_ID 0x808 /* usb 1.1 host/device core */
+#define ADSL_CORE_ID 0x809 /* ADSL core */
+#define ILINE100_CORE_ID 0x80a /* iline100 core */
+#define IPSEC_CORE_ID 0x80b /* ipsec core */
+#define UTOPIA_CORE_ID 0x80c /* utopia core */
+#define PCMCIA_CORE_ID 0x80d /* pcmcia core */
+#define SOCRAM_CORE_ID 0x80e /* internal memory core */
+#define MEMC_CORE_ID 0x80f /* memc sdram core */
+#define OFDM_CORE_ID 0x810 /* OFDM phy core */
+#define EXTIF_CORE_ID 0x811 /* external interface core */
+#define D11_CORE_ID 0x812 /* 802.11 MAC core */
+#define APHY_CORE_ID 0x813 /* 802.11a phy core */
+#define BPHY_CORE_ID 0x814 /* 802.11b phy core */
+#define GPHY_CORE_ID 0x815 /* 802.11g phy core */
+#define MIPS33_CORE_ID 0x816 /* mips3302 core */
+#define USB11H_CORE_ID 0x817 /* usb 1.1 host core */
+#define USB11D_CORE_ID 0x818 /* usb 1.1 device core */
+#define USB20H_CORE_ID 0x819 /* usb 2.0 host core */
+#define USB20D_CORE_ID 0x81a /* usb 2.0 device core */
+#define SDIOH_CORE_ID 0x81b /* sdio host core */
+#define ROBO_CORE_ID 0x81c /* roboswitch core */
+#define ATA100_CORE_ID 0x81d /* parallel ATA core */
+#define SATAXOR_CORE_ID 0x81e /* serial ATA & XOR DMA core */
+#define GIGETH_CORE_ID 0x81f /* gigabit ethernet core */
+#define PCIE_CORE_ID 0x820 /* pci express core */
+#define NPHY_CORE_ID 0x821 /* 802.11n 2x2 phy core */
+#define SRAMC_CORE_ID 0x822 /* SRAM controller core */
+#define MINIMAC_CORE_ID 0x823 /* MINI MAC/phy core */
+#define ARM11_CORE_ID 0x824 /* ARM 1176 core */
+#define ARM7S_CORE_ID 0x825 /* ARM7tdmi-s core */
+#define LPPHY_CORE_ID 0x826 /* 802.11a/b/g phy core */
+#define PMU_CORE_ID 0x827 /* PMU core */
+#define SSNPHY_CORE_ID 0x828 /* 802.11n single-stream phy core */
+#define SDIOD_CORE_ID 0x829 /* SDIO device core */
+#define ARMCM3_CORE_ID 0x82a /* ARM Cortex M3 core */
+#define QNPHY_CORE_ID 0x82b /* 802.11n 4x4 phy core */
+#define MIPS74K_CORE_ID 0x82c /* mips 74k core */
+#define GMAC_CORE_ID 0x82d /* Gigabit MAC core */
+#define DMEMC_CORE_ID 0x82e /* DDR1/2 memory controller core */
+#define PCIERC_CORE_ID 0x82f /* PCIE Root Complex core */
+#define OCP_CORE_ID 0x830 /* OCP2OCP bridge core */
+#define SC_CORE_ID 0x831 /* shared common core */
+#define AHB_CORE_ID 0x832 /* OCP2AHB bridge core */
+#define SPIH_CORE_ID 0x833 /* SPI host core */
+#define I2S_CORE_ID 0x834 /* I2S core */
+#define OOB_ROUTER_CORE_ID 0x367 /* OOB router core ID */
+#define DEF_AI_COMP 0xfff /* Default component, in ai chips it maps all
+ * unused address ranges
+ */
+
+/* There are TWO constants on all HND chips: SI_ENUM_BASE above,
+ * and chipcommon being the first core:
+ */
+#define SI_CC_IDX 0
+
+/* SOC Interconnect types (aka chip types) */
+#define SOCI_SB 0
+#define SOCI_AI 1
+
+/* Common core control flags */
+#define SICF_BIST_EN 0x8000
+#define SICF_PME_EN 0x4000
+#define SICF_CORE_BITS 0x3ffc
+#define SICF_FGC 0x0002
+#define SICF_CLOCK_EN 0x0001
+
+/* Common core status flags */
+#define SISF_BIST_DONE 0x8000
+#define SISF_BIST_ERROR 0x4000
+#define SISF_GATED_CLK 0x2000
+#define SISF_DMA64 0x1000
+#define SISF_CORE_BITS 0x0fff
+
+/* A register that is common to all cores to
+ * communicate w/PMU regarding clock control.
+ */
+#define SI_CLK_CTL_ST 0x1e0 /* clock control and status */
+
+/* clk_ctl_st register */
+#define CCS_FORCEALP 0x00000001 /* force ALP request */
+#define CCS_FORCEHT 0x00000002 /* force HT request */
+#define CCS_FORCEILP 0x00000004 /* force ILP request */
+#define CCS_ALPAREQ 0x00000008 /* ALP Avail Request */
+#define CCS_HTAREQ 0x00000010 /* HT Avail Request */
+#define CCS_FORCEHWREQOFF 0x00000020 /* Force HW Clock Request Off */
+#define CCS_ALPAVAIL 0x00010000 /* ALP is available */
+#define CCS_HTAVAIL 0x00020000 /* HT is available */
+#define CCS0_HTAVAIL 0x00010000 /* HT avail in chipc and pcmcia on 4328a0 */
+#define CCS0_ALPAVAIL 0x00020000 /* ALP avail in chipc and pcmcia on 4328a0 */
+
+/* Not really related to SOC Interconnect, but a couple of software
+ * conventions for the use the flash space:
+ */
+
+/* Minumum amount of flash we support */
+#define FLASH_MIN 0x00020000 /* Minimum flash size */
+
+/* A boot/binary may have an embedded block that describes its size */
+#define BISZ_OFFSET 0x3e0 /* At this offset into the binary */
+#define BISZ_MAGIC 0x4249535a /* Marked with this value: 'BISZ' */
+#define BISZ_MAGIC_IDX 0 /* Word 0: magic */
+#define BISZ_TXTST_IDX 1 /* 1: text start */
+#define BISZ_TXTEND_IDX 2 /* 2: text end */
+#define BISZ_DATAST_IDX 3 /* 3: data start */
+#define BISZ_DATAEND_IDX 4 /* 4: data end */
+#define BISZ_BSSST_IDX 5 /* 5: bss start */
+#define BISZ_BSSEND_IDX 6 /* 6: bss end */
+#define BISZ_SIZE 7 /* descriptor size in 32-bit intergers */
+
+#endif /* _HNDSOC_H */
diff --git a/drivers/net/wireless/bcm4329/include/linux_osl.h b/drivers/net/wireless/bcm4329/include/linux_osl.h
new file mode 100644
index 00000000000..b059c2adb17
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/linux_osl.h
@@ -0,0 +1,322 @@
+/*
+ * Linux OS Independent Layer
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: linux_osl.h,v 13.131.30.8 2010/04/26 05:42:18 Exp $
+ */
+
+
+#ifndef _linux_osl_h_
+#define _linux_osl_h_
+
+#include <typedefs.h>
+
+
+#include <linuxver.h>
+
+
+#ifdef __GNUC__
+#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#if GCC_VERSION > 30100
+#define ASSERT(exp) do {} while (0)
+#else
+
+#define ASSERT(exp)
+#endif
+#endif
+
+
+#define OSL_DELAY(usec) osl_delay(usec)
+extern void osl_delay(uint usec);
+
+
+
+#define OSL_PCMCIA_READ_ATTR(osh, offset, buf, size) \
+ osl_pcmcia_read_attr((osh), (offset), (buf), (size))
+#define OSL_PCMCIA_WRITE_ATTR(osh, offset, buf, size) \
+ osl_pcmcia_write_attr((osh), (offset), (buf), (size))
+extern void osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size);
+extern void osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size);
+
+
+#define OSL_PCI_READ_CONFIG(osh, offset, size) \
+ osl_pci_read_config((osh), (offset), (size))
+#define OSL_PCI_WRITE_CONFIG(osh, offset, size, val) \
+ osl_pci_write_config((osh), (offset), (size), (val))
+extern uint32 osl_pci_read_config(osl_t *osh, uint offset, uint size);
+extern void osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val);
+
+
+#define OSL_PCI_BUS(osh) osl_pci_bus(osh)
+#define OSL_PCI_SLOT(osh) osl_pci_slot(osh)
+extern uint osl_pci_bus(osl_t *osh);
+extern uint osl_pci_slot(osl_t *osh);
+
+
+typedef struct {
+ bool pkttag;
+ uint pktalloced;
+ bool mmbus;
+ pktfree_cb_fn_t tx_fn;
+ void *tx_ctx;
+} osl_pubinfo_t;
+
+
+extern osl_t *osl_attach(void *pdev, uint bustype, bool pkttag);
+extern void osl_detach(osl_t *osh);
+
+#define PKTFREESETCB(osh, _tx_fn, _tx_ctx) \
+ do { \
+ ((osl_pubinfo_t*)osh)->tx_fn = _tx_fn; \
+ ((osl_pubinfo_t*)osh)->tx_ctx = _tx_ctx; \
+ } while (0)
+
+
+#define BUS_SWAP32(v) (v)
+
+
+#define MALLOC(osh, size) osl_malloc((osh), (size))
+#define MFREE(osh, addr, size) osl_mfree((osh), (addr), (size))
+#define MALLOCED(osh) osl_malloced((osh))
+
+
+#define MALLOC_FAILED(osh) osl_malloc_failed((osh))
+
+extern void *osl_malloc(osl_t *osh, uint size);
+extern void osl_mfree(osl_t *osh, void *addr, uint size);
+extern uint osl_malloced(osl_t *osh);
+extern uint osl_malloc_failed(osl_t *osh);
+
+
+#define DMA_CONSISTENT_ALIGN PAGE_SIZE
+#define DMA_ALLOC_CONSISTENT(osh, size, pap, dmah, alignbits) \
+ osl_dma_alloc_consistent((osh), (size), (pap))
+#define DMA_FREE_CONSISTENT(osh, va, size, pa, dmah) \
+ osl_dma_free_consistent((osh), (void*)(va), (size), (pa))
+extern void *osl_dma_alloc_consistent(osl_t *osh, uint size, ulong *pap);
+extern void osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa);
+
+
+#define DMA_TX 1
+#define DMA_RX 2
+
+
+#define DMA_MAP(osh, va, size, direction, p, dmah) \
+ osl_dma_map((osh), (va), (size), (direction))
+#define DMA_UNMAP(osh, pa, size, direction, p, dmah) \
+ osl_dma_unmap((osh), (pa), (size), (direction))
+extern uint osl_dma_map(osl_t *osh, void *va, uint size, int direction);
+extern void osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction);
+
+
+#define OSL_DMADDRWIDTH(osh, addrwidth) do {} while (0)
+
+
+#include <bcmsdh.h>
+#define OSL_WRITE_REG(osh, r, v) (bcmsdh_reg_write(NULL, (uintptr)(r), sizeof(*(r)), (v)))
+#define OSL_READ_REG(osh, r) (bcmsdh_reg_read(NULL, (uintptr)(r), sizeof(*(r))))
+
+#define SELECT_BUS_WRITE(osh, mmap_op, bus_op) if (((osl_pubinfo_t*)(osh))->mmbus) \
+ mmap_op else bus_op
+#define SELECT_BUS_READ(osh, mmap_op, bus_op) (((osl_pubinfo_t*)(osh))->mmbus) ? \
+ mmap_op : bus_op
+
+
+
+
+#ifndef printf
+#define printf(fmt, args...) printk(fmt, ## args)
+#endif
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+
+#ifndef IL_BIGENDIAN
+#define R_REG(osh, r) (\
+ SELECT_BUS_READ(osh, sizeof(*(r)) == sizeof(uint8) ? readb((volatile uint8*)(r)) : \
+ sizeof(*(r)) == sizeof(uint16) ? readw((volatile uint16*)(r)) : \
+ readl((volatile uint32*)(r)), OSL_READ_REG(osh, r)) \
+)
+#define W_REG(osh, r, v) do { \
+ SELECT_BUS_WRITE(osh, \
+ switch (sizeof(*(r))) { \
+ case sizeof(uint8): writeb((uint8)(v), (volatile uint8*)(r)); break; \
+ case sizeof(uint16): writew((uint16)(v), (volatile uint16*)(r)); break; \
+ case sizeof(uint32): writel((uint32)(v), (volatile uint32*)(r)); break; \
+ }, \
+ (OSL_WRITE_REG(osh, r, v))); \
+ } while (0)
+#else
+#define R_REG(osh, r) (\
+ SELECT_BUS_READ(osh, \
+ ({ \
+ __typeof(*(r)) __osl_v; \
+ switch (sizeof(*(r))) { \
+ case sizeof(uint8): __osl_v = \
+ readb((volatile uint8*)((uintptr)(r)^3)); break; \
+ case sizeof(uint16): __osl_v = \
+ readw((volatile uint16*)((uintptr)(r)^2)); break; \
+ case sizeof(uint32): __osl_v = \
+ readl((volatile uint32*)(r)); break; \
+ } \
+ __osl_v; \
+ }), \
+ OSL_READ_REG(osh, r)) \
+)
+#define W_REG(osh, r, v) do { \
+ SELECT_BUS_WRITE(osh, \
+ switch (sizeof(*(r))) { \
+ case sizeof(uint8): writeb((uint8)(v), \
+ (volatile uint8*)((uintptr)(r)^3)); break; \
+ case sizeof(uint16): writew((uint16)(v), \
+ (volatile uint16*)((uintptr)(r)^2)); break; \
+ case sizeof(uint32): writel((uint32)(v), \
+ (volatile uint32*)(r)); break; \
+ }, \
+ (OSL_WRITE_REG(osh, r, v))); \
+ } while (0)
+#endif
+
+#define AND_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) & (v))
+#define OR_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) | (v))
+
+
+#define bcopy(src, dst, len) memcpy((dst), (src), (len))
+#define bcmp(b1, b2, len) memcmp((b1), (b2), (len))
+#define bzero(b, len) memset((b), '\0', (len))
+
+
+#define OSL_UNCACHED(va) ((void*)va)
+
+
+#if defined(__i386__)
+#define OSL_GETCYCLES(x) rdtscl((x))
+#else
+#define OSL_GETCYCLES(x) ((x) = 0)
+#endif
+
+
+#define BUSPROBE(val, addr) ({ (val) = R_REG(NULL, (addr)); 0; })
+
+
+#if !defined(CONFIG_MMC_MSM7X00A)
+#define REG_MAP(pa, size) ioremap_nocache((unsigned long)(pa), (unsigned long)(size))
+#else
+#define REG_MAP(pa, size) (void *)(0)
+#endif
+#define REG_UNMAP(va) iounmap((va))
+
+
+#define R_SM(r) *(r)
+#define W_SM(r, v) (*(r) = (v))
+#define BZERO_SM(r, len) memset((r), '\0', (len))
+
+
+#define PKTGET(osh, len, send) osl_pktget((osh), (len))
+#define PKTFREE(osh, skb, send) osl_pktfree((osh), (skb), (send))
+#ifdef DHD_USE_STATIC_BUF
+#define PKTGET_STATIC(osh, len, send) osl_pktget_static((osh), (len))
+#define PKTFREE_STATIC(osh, skb, send) osl_pktfree_static((osh), (skb), (send))
+#endif
+#define PKTDATA(osh, skb) (((struct sk_buff*)(skb))->data)
+#define PKTLEN(osh, skb) (((struct sk_buff*)(skb))->len)
+#define PKTHEADROOM(osh, skb) (PKTDATA(osh, skb)-(((struct sk_buff*)(skb))->head))
+#define PKTTAILROOM(osh, skb) ((((struct sk_buff*)(skb))->end)-(((struct sk_buff*)(skb))->tail))
+#define PKTNEXT(osh, skb) (((struct sk_buff*)(skb))->next)
+#define PKTSETNEXT(osh, skb, x) (((struct sk_buff*)(skb))->next = (struct sk_buff*)(x))
+#define PKTSETLEN(osh, skb, len) __skb_trim((struct sk_buff*)(skb), (len))
+#define PKTPUSH(osh, skb, bytes) skb_push((struct sk_buff*)(skb), (bytes))
+#define PKTPULL(osh, skb, bytes) skb_pull((struct sk_buff*)(skb), (bytes))
+#define PKTDUP(osh, skb) osl_pktdup((osh), (skb))
+#define PKTTAG(skb) ((void*)(((struct sk_buff*)(skb))->cb))
+#define PKTALLOCED(osh) ((osl_pubinfo_t *)(osh))->pktalloced
+#define PKTSETPOOL(osh, skb, x, y) do {} while (0)
+#define PKTPOOL(osh, skb) FALSE
+#define PKTPOOLLEN(osh, pktp) (0)
+#define PKTPOOLAVAIL(osh, pktp) (0)
+#define PKTPOOLADD(osh, pktp, p) BCME_ERROR
+#define PKTPOOLGET(osh, pktp) NULL
+#define PKTLIST_DUMP(osh, buf)
+
+extern void *osl_pktget(osl_t *osh, uint len);
+extern void osl_pktfree(osl_t *osh, void *skb, bool send);
+extern void *osl_pktget_static(osl_t *osh, uint len);
+extern void osl_pktfree_static(osl_t *osh, void *skb, bool send);
+extern void *osl_pktdup(osl_t *osh, void *skb);
+
+
+
+static INLINE void *
+osl_pkt_frmnative(osl_pubinfo_t *osh, struct sk_buff *skb)
+{
+ struct sk_buff *nskb;
+
+ if (osh->pkttag)
+ bzero((void*)skb->cb, OSL_PKTTAG_SZ);
+
+
+ for (nskb = skb; nskb; nskb = nskb->next) {
+ osh->pktalloced++;
+ }
+
+ return (void *)skb;
+}
+#define PKTFRMNATIVE(osh, skb) osl_pkt_frmnative(((osl_pubinfo_t *)osh), (struct sk_buff*)(skb))
+
+
+static INLINE struct sk_buff *
+osl_pkt_tonative(osl_pubinfo_t *osh, void *pkt)
+{
+ struct sk_buff *nskb;
+
+ if (osh->pkttag)
+ bzero(((struct sk_buff*)pkt)->cb, OSL_PKTTAG_SZ);
+
+
+ for (nskb = (struct sk_buff *)pkt; nskb; nskb = nskb->next) {
+ osh->pktalloced--;
+ }
+
+ return (struct sk_buff *)pkt;
+}
+#define PKTTONATIVE(osh, pkt) osl_pkt_tonative((osl_pubinfo_t *)(osh), (pkt))
+
+#define PKTLINK(skb) (((struct sk_buff*)(skb))->prev)
+#define PKTSETLINK(skb, x) (((struct sk_buff*)(skb))->prev = (struct sk_buff*)(x))
+#define PKTPRIO(skb) (((struct sk_buff*)(skb))->priority)
+#define PKTSETPRIO(skb, x) (((struct sk_buff*)(skb))->priority = (x))
+#define PKTSUMNEEDED(skb) (((struct sk_buff*)(skb))->ip_summed == CHECKSUM_HW)
+#define PKTSETSUMGOOD(skb, x) (((struct sk_buff*)(skb))->ip_summed = \
+ ((x) ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE))
+
+#define PKTSHARED(skb) (((struct sk_buff*)(skb))->cloned)
+
+
+#define OSL_ERROR(bcmerror) osl_error(bcmerror)
+extern int osl_error(int bcmerror);
+
+
+#define PKTBUFSZ 2048
+
+
+#define OSL_SYSUPTIME() ((uint32)jiffies * (1000 / HZ))
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/linuxver.h b/drivers/net/wireless/bcm4329/include/linuxver.h
new file mode 100644
index 00000000000..6ed22658a72
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/linuxver.h
@@ -0,0 +1,447 @@
+/*
+ * Linux-specific abstractions to gain some independence from linux kernel versions.
+ * Pave over some 2.2 versus 2.4 versus 2.6 kernel differences.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: linuxver.h,v 13.38.8.1.8.6 2010/04/29 05:00:46 Exp $
+ */
+
+
+#ifndef _linuxver_h_
+#define _linuxver_h_
+
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+#include <linux/config.h>
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33))
+#include <linux/autoconf.h>
+#endif
+#include <linux/module.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0))
+
+#ifdef __UNDEF_NO_VERSION__
+#undef __NO_VERSION__
+#else
+#define __NO_VERSION__
+#endif
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+#define module_param(_name_, _type_, _perm_) MODULE_PARM(_name_, "i")
+#define module_param_string(_name_, _string_, _size_, _perm_) \
+ MODULE_PARM(_string_, "c" __MODULE_STRING(_size_))
+#endif
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 9))
+#include <linux/malloc.h>
+#else
+#include <linux/slab.h>
+#endif
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/semaphore.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28))
+#undef IP_TOS
+#endif
+#include <asm/io.h>
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 41))
+#include <linux/workqueue.h>
+#else
+#include <linux/tqueue.h>
+#ifndef work_struct
+#define work_struct tq_struct
+#endif
+#ifndef INIT_WORK
+#define INIT_WORK(_work, _func, _data) INIT_TQUEUE((_work), (_func), (_data))
+#endif
+#ifndef schedule_work
+#define schedule_work(_work) schedule_task((_work))
+#endif
+#ifndef flush_scheduled_work
+#define flush_scheduled_work() flush_scheduled_tasks()
+#endif
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+#define MY_INIT_WORK(_work, _func, _data) INIT_WORK(_work, _func)
+#else
+#define MY_INIT_WORK(_work, _func, _data) INIT_WORK(_work, _func, _data)
+typedef void (*work_func_t)(void *work);
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+
+#ifndef IRQ_NONE
+typedef void irqreturn_t;
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x)
+#endif
+#else
+typedef irqreturn_t(*FN_ISR) (int irq, void *dev_id, struct pt_regs *ptregs);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+#define IRQF_SHARED SA_SHIRQ
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17)
+#ifdef CONFIG_NET_RADIO
+#define CONFIG_WIRELESS_EXT
+#endif
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 67)
+#ifndef SANDGATE2G
+#define MOD_INC_USE_COUNT
+#endif
+#endif
+
+
+#ifndef __exit
+#define __exit
+#endif
+#ifndef __devexit
+#define __devexit
+#endif
+#ifndef __devinit
+#define __devinit __init
+#endif
+#ifndef __devinitdata
+#define __devinitdata
+#endif
+#ifndef __devexit_p
+#define __devexit_p(x) x
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0))
+
+#define pci_get_drvdata(dev) (dev)->sysdata
+#define pci_set_drvdata(dev, value) (dev)->sysdata = (value)
+
+
+
+struct pci_device_id {
+ unsigned int vendor, device;
+ unsigned int subvendor, subdevice;
+ unsigned int class, class_mask;
+ unsigned long driver_data;
+};
+
+struct pci_driver {
+ struct list_head node;
+ char *name;
+ const struct pci_device_id *id_table;
+ int (*probe)(struct pci_dev *dev,
+ const struct pci_device_id *id);
+ void (*remove)(struct pci_dev *dev);
+ void (*suspend)(struct pci_dev *dev);
+ void (*resume)(struct pci_dev *dev);
+};
+
+#define MODULE_DEVICE_TABLE(type, name)
+#define PCI_ANY_ID (~0)
+
+
+#define pci_module_init pci_register_driver
+extern int pci_register_driver(struct pci_driver *drv);
+extern void pci_unregister_driver(struct pci_driver *drv);
+
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18))
+#define pci_module_init pci_register_driver
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18))
+#ifdef MODULE
+#define module_init(x) int init_module(void) { return x(); }
+#define module_exit(x) void cleanup_module(void) { x(); }
+#else
+#define module_init(x) __initcall(x);
+#define module_exit(x) __exitcall(x);
+#endif
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 48))
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 13))
+#define pci_resource_start(dev, bar) ((dev)->base_address[(bar)])
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 44))
+#define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 23))
+#define pci_enable_device(dev) do { } while (0)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 14))
+#define net_device device
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 42))
+
+
+
+#ifndef PCI_DMA_TODEVICE
+#define PCI_DMA_TODEVICE 1
+#define PCI_DMA_FROMDEVICE 2
+#endif
+
+typedef u32 dma_addr_t;
+
+
+static inline int get_order(unsigned long size)
+{
+ int order;
+
+ size = (size-1) >> (PAGE_SHIFT-1);
+ order = -1;
+ do {
+ size >>= 1;
+ order++;
+ } while (size);
+ return order;
+}
+
+static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
+ dma_addr_t *dma_handle)
+{
+ void *ret;
+ int gfp = GFP_ATOMIC | GFP_DMA;
+
+ ret = (void *)__get_free_pages(gfp, get_order(size));
+
+ if (ret != NULL) {
+ memset(ret, 0, size);
+ *dma_handle = virt_to_bus(ret);
+ }
+ return ret;
+}
+static inline void pci_free_consistent(struct pci_dev *hwdev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
+{
+ free_pages((unsigned long)vaddr, get_order(size));
+}
+#define pci_map_single(cookie, address, size, dir) virt_to_bus(address)
+#define pci_unmap_single(cookie, address, size, dir)
+
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 43))
+
+#define dev_kfree_skb_any(a) dev_kfree_skb(a)
+#define netif_down(dev) do { (dev)->start = 0; } while (0)
+
+
+#ifndef _COMPAT_NETDEVICE_H
+
+
+
+#define dev_kfree_skb_irq(a) dev_kfree_skb(a)
+#define netif_wake_queue(dev) \
+ do { clear_bit(0, &(dev)->tbusy); mark_bh(NET_BH); } while (0)
+#define netif_stop_queue(dev) set_bit(0, &(dev)->tbusy)
+
+static inline void netif_start_queue(struct net_device *dev)
+{
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+}
+
+#define netif_queue_stopped(dev) (dev)->tbusy
+#define netif_running(dev) (dev)->start
+
+#endif
+
+#define netif_device_attach(dev) netif_start_queue(dev)
+#define netif_device_detach(dev) netif_stop_queue(dev)
+
+
+#define tasklet_struct tq_struct
+static inline void tasklet_schedule(struct tasklet_struct *tasklet)
+{
+ queue_task(tasklet, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+static inline void tasklet_init(struct tasklet_struct *tasklet,
+ void (*func)(unsigned long),
+ unsigned long data)
+{
+ tasklet->next = NULL;
+ tasklet->sync = 0;
+ tasklet->routine = (void (*)(void *))func;
+ tasklet->data = (void *)data;
+}
+#define tasklet_kill(tasklet) { do {} while (0); }
+
+
+#define del_timer_sync(timer) del_timer(timer)
+
+#else
+
+#define netif_down(dev)
+
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 3))
+
+
+#define PREPARE_TQUEUE(_tq, _routine, _data) \
+ do { \
+ (_tq)->routine = _routine; \
+ (_tq)->data = _data; \
+ } while (0)
+
+
+#define INIT_TQUEUE(_tq, _routine, _data) \
+ do { \
+ INIT_LIST_HEAD(&(_tq)->list); \
+ (_tq)->sync = 0; \
+ PREPARE_TQUEUE((_tq), (_routine), (_data)); \
+ } while (0)
+
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6))
+
+
+
+static inline int
+pci_save_state(struct pci_dev *dev, u32 *buffer)
+{
+ int i;
+ if (buffer) {
+ for (i = 0; i < 16; i++)
+ pci_read_config_dword(dev, i * 4, &buffer[i]);
+ }
+ return 0;
+}
+
+static inline int
+pci_restore_state(struct pci_dev *dev, u32 *buffer)
+{
+ int i;
+
+ if (buffer) {
+ for (i = 0; i < 16; i++)
+ pci_write_config_dword(dev, i * 4, buffer[i]);
+ }
+
+ else {
+ for (i = 0; i < 6; i ++)
+ pci_write_config_dword(dev,
+ PCI_BASE_ADDRESS_0 + (i * 4),
+ pci_resource_start(dev, i));
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+ }
+ return 0;
+}
+
+#endif
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 19))
+#define read_c0_count() read_32bit_cp0_register(CP0_COUNT)
+#endif
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
+#ifndef SET_MODULE_OWNER
+#define SET_MODULE_OWNER(dev) do {} while (0)
+#define OLD_MOD_INC_USE_COUNT MOD_INC_USE_COUNT
+#define OLD_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT
+#else
+#define OLD_MOD_INC_USE_COUNT do {} while (0)
+#define OLD_MOD_DEC_USE_COUNT do {} while (0)
+#endif
+#else
+#ifndef SET_MODULE_OWNER
+#define SET_MODULE_OWNER(dev) do {} while (0)
+#endif
+#ifndef MOD_INC_USE_COUNT
+#define MOD_INC_USE_COUNT do {} while (0)
+#endif
+#ifndef MOD_DEC_USE_COUNT
+#define MOD_DEC_USE_COUNT do {} while (0)
+#endif
+#define OLD_MOD_INC_USE_COUNT MOD_INC_USE_COUNT
+#define OLD_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT
+#endif
+
+#ifndef SET_NETDEV_DEV
+#define SET_NETDEV_DEV(net, pdev) do {} while (0)
+#endif
+
+#ifndef HAVE_FREE_NETDEV
+#define free_netdev(dev) kfree(dev)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+
+#define af_packet_priv data
+#endif
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
+#define DRV_SUSPEND_STATE_TYPE pm_message_t
+#else
+#define DRV_SUSPEND_STATE_TYPE uint32
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+#define CHECKSUM_HW CHECKSUM_PARTIAL
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+#define KILL_PROC(pid, sig) \
+{ \
+ struct task_struct *tsk; \
+ tsk = pid_task(find_vpid(pid), PIDTYPE_PID); \
+ if (tsk) send_sig(sig, tsk, 1); \
+}
+#else
+#define KILL_PROC(pid, sig) \
+{ \
+ kill_proc(pid, sig, 1); \
+}
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+#define netdev_priv(dev) dev->priv
+#endif
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/miniopt.h b/drivers/net/wireless/bcm4329/include/miniopt.h
new file mode 100644
index 00000000000..3667fb1e215
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/miniopt.h
@@ -0,0 +1,77 @@
+/*
+ * Command line options parser.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: miniopt.h,v 1.1.6.2 2009/01/14 23:52:48 Exp $
+ */
+
+
+#ifndef MINI_OPT_H
+#define MINI_OPT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ---- Include Files ---------------------------------------------------- */
+/* ---- Constants and Types ---------------------------------------------- */
+
+#define MINIOPT_MAXKEY 128 /* Max options */
+typedef struct miniopt {
+
+ /* These are persistent after miniopt_init() */
+ const char* name; /* name for prompt in error strings */
+ const char* flags; /* option chars that take no args */
+ bool longflags; /* long options may be flags */
+ bool opt_end; /* at end of options (passed a "--") */
+
+ /* These are per-call to miniopt() */
+
+ int consumed; /* number of argv entries cosumed in
+ * the most recent call to miniopt()
+ */
+ bool positional;
+ bool good_int; /* 'val' member is the result of a sucessful
+ * strtol conversion of the option value
+ */
+ char opt;
+ char key[MINIOPT_MAXKEY];
+ char* valstr; /* positional param, or value for the option,
+ * or null if the option had
+ * no accompanying value
+ */
+ uint uval; /* strtol translation of valstr */
+ int val; /* strtol translation of valstr */
+} miniopt_t;
+
+void miniopt_init(miniopt_t *t, const char* name, const char* flags, bool longflags);
+int miniopt(miniopt_t *t, char **argv);
+
+
+/* ---- Variable Externs ------------------------------------------------- */
+/* ---- Function Prototypes ---------------------------------------------- */
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* MINI_OPT_H */
diff --git a/drivers/net/wireless/bcm4329/include/msgtrace.h b/drivers/net/wireless/bcm4329/include/msgtrace.h
new file mode 100644
index 00000000000..1479086dba3
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/msgtrace.h
@@ -0,0 +1,72 @@
+/*
+ * Trace messages sent over HBUS
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: msgtrace.h,v 1.1.2.4 2009/01/27 04:09:40 Exp $
+ */
+
+#ifndef _MSGTRACE_H
+#define _MSGTRACE_H
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+
+/* This marks the start of a packed structure section. */
+#include <packed_section_start.h>
+
+#define MSGTRACE_VERSION 1
+
+/* Message trace header */
+typedef BWL_PRE_PACKED_STRUCT struct msgtrace_hdr {
+ uint8 version;
+ uint8 spare;
+ uint16 len; /* Len of the trace */
+ uint32 seqnum; /* Sequence number of message. Useful if the messsage has been lost
+ * because of DMA error or a bus reset (ex: SDIO Func2)
+ */
+ uint32 discarded_bytes; /* Number of discarded bytes because of trace overflow */
+ uint32 discarded_printf; /* Number of discarded printf because of trace overflow */
+} BWL_POST_PACKED_STRUCT msgtrace_hdr_t;
+
+#define MSGTRACE_HDRLEN sizeof(msgtrace_hdr_t)
+
+/* The hbus driver generates traces when sending a trace message. This causes endless traces.
+ * This flag must be set to TRUE in any hbus traces. The flag is reset in the function msgtrace_put.
+ * This prevents endless traces but generates hasardous lost of traces only in bus device code.
+ * It is recommendat to set this flag in macro SD_TRACE but not in SD_ERROR for avoiding missing
+ * hbus error traces. hbus error trace should not generates endless traces.
+ */
+extern bool msgtrace_hbus_trace;
+
+typedef void (*msgtrace_func_send_t)(void *hdl1, void *hdl2, uint8 *hdr,
+ uint16 hdrlen, uint8 *buf, uint16 buflen);
+
+extern void msgtrace_sent(void);
+extern void msgtrace_put(char *buf, int count);
+extern void msgtrace_init(void *hdl1, void *hdl2, msgtrace_func_send_t func_send);
+
+/* This marks the end of a packed structure section. */
+#include <packed_section_end.h>
+
+#endif /* _MSGTRACE_H */
diff --git a/drivers/net/wireless/bcm4329/include/osl.h b/drivers/net/wireless/bcm4329/include/osl.h
new file mode 100644
index 00000000000..5599e536eee
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/osl.h
@@ -0,0 +1,55 @@
+/*
+ * OS Abstraction Layer
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: osl.h,v 13.37.32.1 2008/11/20 00:51:15 Exp $
+ */
+
+
+#ifndef _osl_h_
+#define _osl_h_
+
+
+typedef struct osl_info osl_t;
+typedef struct osl_dmainfo osldma_t;
+
+#define OSL_PKTTAG_SZ 32
+
+
+typedef void (*pktfree_cb_fn_t)(void *ctx, void *pkt, unsigned int status);
+
+#include <linux_osl.h>
+
+
+
+
+#define SET_REG(osh, r, mask, val) W_REG((osh), (r), ((R_REG((osh), r) & ~(mask)) | (val)))
+
+#ifndef AND_REG
+#define AND_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) & (v))
+#endif
+
+#ifndef OR_REG
+#define OR_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) | (v))
+#endif
+
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/packed_section_end.h b/drivers/net/wireless/bcm4329/include/packed_section_end.h
new file mode 100644
index 00000000000..5b61c18fcd0
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/packed_section_end.h
@@ -0,0 +1,54 @@
+/*
+ * Declare directives for structure packing. No padding will be provided
+ * between the members of packed structures, and therefore, there is no
+ * guarantee that structure members will be aligned.
+ *
+ * Declaring packed structures is compiler specific. In order to handle all
+ * cases, packed structures should be delared as:
+ *
+ * #include <packed_section_start.h>
+ *
+ * typedef BWL_PRE_PACKED_STRUCT struct foobar_t {
+ * some_struct_members;
+ * } BWL_POST_PACKED_STRUCT foobar_t;
+ *
+ * #include <packed_section_end.h>
+ *
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: packed_section_end.h,v 1.1.6.3 2008/12/10 00:27:54 Exp $
+ */
+
+
+
+
+#ifdef BWL_PACKED_SECTION
+ #undef BWL_PACKED_SECTION
+#else
+ #error "BWL_PACKED_SECTION is NOT defined!"
+#endif
+
+
+
+
+
+#undef BWL_PRE_PACKED_STRUCT
+#undef BWL_POST_PACKED_STRUCT
diff --git a/drivers/net/wireless/bcm4329/include/packed_section_start.h b/drivers/net/wireless/bcm4329/include/packed_section_start.h
new file mode 100644
index 00000000000..cb93aa64079
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/packed_section_start.h
@@ -0,0 +1,61 @@
+/*
+ * Declare directives for structure packing. No padding will be provided
+ * between the members of packed structures, and therefore, there is no
+ * guarantee that structure members will be aligned.
+ *
+ * Declaring packed structures is compiler specific. In order to handle all
+ * cases, packed structures should be delared as:
+ *
+ * #include <packed_section_start.h>
+ *
+ * typedef BWL_PRE_PACKED_STRUCT struct foobar_t {
+ * some_struct_members;
+ * } BWL_POST_PACKED_STRUCT foobar_t;
+ *
+ * #include <packed_section_end.h>
+ *
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: packed_section_start.h,v 1.1.6.3 2008/12/10 00:27:54 Exp $
+ */
+
+
+
+
+#ifdef BWL_PACKED_SECTION
+ #error "BWL_PACKED_SECTION is already defined!"
+#else
+ #define BWL_PACKED_SECTION
+#endif
+
+
+
+
+
+#if defined(__GNUC__)
+ #define BWL_PRE_PACKED_STRUCT
+ #define BWL_POST_PACKED_STRUCT __attribute__((packed))
+#elif defined(__CC_ARM)
+ #define BWL_PRE_PACKED_STRUCT __packed
+ #define BWL_POST_PACKED_STRUCT
+#else
+ #error "Unknown compiler!"
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/pcicfg.h b/drivers/net/wireless/bcm4329/include/pcicfg.h
new file mode 100644
index 00000000000..898962c942a
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/pcicfg.h
@@ -0,0 +1,52 @@
+/*
+ * pcicfg.h: PCI configuration constants and structures.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: pcicfg.h,v 1.41.12.3 2008/06/26 22:49:41 Exp $
+ */
+
+
+#ifndef _h_pcicfg_
+#define _h_pcicfg_
+
+
+#define PCI_CFG_VID 0
+#define PCI_CFG_CMD 4
+#define PCI_CFG_REV 8
+#define PCI_CFG_BAR0 0x10
+#define PCI_CFG_BAR1 0x14
+#define PCI_BAR0_WIN 0x80
+#define PCI_INT_STATUS 0x90
+#define PCI_INT_MASK 0x94
+
+#define PCIE_EXTCFG_OFFSET 0x100
+#define PCI_BAR0_PCIREGS_OFFSET (6 * 1024)
+#define PCI_BAR0_PCISBR_OFFSET (4 * 1024)
+
+#define PCI_BAR0_WINSZ (16 * 1024)
+
+
+#define PCI_16KB0_PCIREGS_OFFSET (8 * 1024)
+#define PCI_16KB0_CCREGS_OFFSET (12 * 1024)
+#define PCI_16KBB0_WINSZ (16 * 1024)
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/proto/802.11.h b/drivers/net/wireless/bcm4329/include/proto/802.11.h
new file mode 100644
index 00000000000..fd26317361d
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/802.11.h
@@ -0,0 +1,1433 @@
+/*
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * Fundamental types and constants relating to 802.11
+ *
+ * $Id: 802.11.h,v 9.219.4.1.4.5.6.11 2010/02/09 13:23:26 Exp $
+ */
+
+
+#ifndef _802_11_H_
+#define _802_11_H_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+#ifndef _NET_ETHERNET_H_
+#include <proto/ethernet.h>
+#endif
+
+#include <proto/wpa.h>
+
+
+#include <packed_section_start.h>
+
+
+#define DOT11_TU_TO_US 1024
+
+
+#define DOT11_A3_HDR_LEN 24
+#define DOT11_A4_HDR_LEN 30
+#define DOT11_MAC_HDR_LEN DOT11_A3_HDR_LEN
+#define DOT11_FCS_LEN 4
+#define DOT11_ICV_LEN 4
+#define DOT11_ICV_AES_LEN 8
+#define DOT11_QOS_LEN 2
+#define DOT11_HTC_LEN 4
+
+#define DOT11_KEY_INDEX_SHIFT 6
+#define DOT11_IV_LEN 4
+#define DOT11_IV_TKIP_LEN 8
+#define DOT11_IV_AES_OCB_LEN 4
+#define DOT11_IV_AES_CCM_LEN 8
+#define DOT11_IV_MAX_LEN 8
+
+
+#define DOT11_MAX_MPDU_BODY_LEN 2304
+
+#define DOT11_MAX_MPDU_LEN (DOT11_A4_HDR_LEN + \
+ DOT11_QOS_LEN + \
+ DOT11_IV_AES_CCM_LEN + \
+ DOT11_MAX_MPDU_BODY_LEN + \
+ DOT11_ICV_LEN + \
+ DOT11_FCS_LEN)
+
+#define DOT11_MAX_SSID_LEN 32
+
+
+#define DOT11_DEFAULT_RTS_LEN 2347
+#define DOT11_MAX_RTS_LEN 2347
+
+
+#define DOT11_MIN_FRAG_LEN 256
+#define DOT11_MAX_FRAG_LEN 2346
+#define DOT11_DEFAULT_FRAG_LEN 2346
+
+
+#define DOT11_MIN_BEACON_PERIOD 1
+#define DOT11_MAX_BEACON_PERIOD 0xFFFF
+
+
+#define DOT11_MIN_DTIM_PERIOD 1
+#define DOT11_MAX_DTIM_PERIOD 0xFF
+
+
+#define DOT11_LLC_SNAP_HDR_LEN 8
+#define DOT11_OUI_LEN 3
+BWL_PRE_PACKED_STRUCT struct dot11_llc_snap_header {
+ uint8 dsap;
+ uint8 ssap;
+ uint8 ctl;
+ uint8 oui[DOT11_OUI_LEN];
+ uint16 type;
+} BWL_POST_PACKED_STRUCT;
+
+
+#define RFC1042_HDR_LEN (ETHER_HDR_LEN + DOT11_LLC_SNAP_HDR_LEN)
+
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_header {
+ uint16 fc;
+ uint16 durid;
+ struct ether_addr a1;
+ struct ether_addr a2;
+ struct ether_addr a3;
+ uint16 seq;
+ struct ether_addr a4;
+} BWL_POST_PACKED_STRUCT;
+
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_rts_frame {
+ uint16 fc;
+ uint16 durid;
+ struct ether_addr ra;
+ struct ether_addr ta;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_RTS_LEN 16
+
+BWL_PRE_PACKED_STRUCT struct dot11_cts_frame {
+ uint16 fc;
+ uint16 durid;
+ struct ether_addr ra;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_CTS_LEN 10
+
+BWL_PRE_PACKED_STRUCT struct dot11_ack_frame {
+ uint16 fc;
+ uint16 durid;
+ struct ether_addr ra;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_ACK_LEN 10
+
+BWL_PRE_PACKED_STRUCT struct dot11_ps_poll_frame {
+ uint16 fc;
+ uint16 durid;
+ struct ether_addr bssid;
+ struct ether_addr ta;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_PS_POLL_LEN 16
+
+BWL_PRE_PACKED_STRUCT struct dot11_cf_end_frame {
+ uint16 fc;
+ uint16 durid;
+ struct ether_addr ra;
+ struct ether_addr bssid;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_CS_END_LEN 16
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_wifi_vendor_specific {
+ uint8 category;
+ uint8 OUI[3];
+ uint8 type;
+ uint8 subtype;
+ uint8 data[1040];
+ struct dot11_action_wifi_vendor_specific* next_node;
+} BWL_POST_PACKED_STRUCT;
+
+typedef struct dot11_action_wifi_vendor_specific dot11_action_wifi_vendor_specific_t;
+
+#define DOT11_BA_CTL_POLICY_NORMAL 0x0000
+#define DOT11_BA_CTL_POLICY_NOACK 0x0001
+#define DOT11_BA_CTL_POLICY_MASK 0x0001
+
+#define DOT11_BA_CTL_MTID 0x0002
+#define DOT11_BA_CTL_COMPRESSED 0x0004
+
+#define DOT11_BA_CTL_NUMMSDU_MASK 0x0FC0
+#define DOT11_BA_CTL_NUMMSDU_SHIFT 6
+
+#define DOT11_BA_CTL_TID_MASK 0xF000
+#define DOT11_BA_CTL_TID_SHIFT 12
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_ctl_header {
+ uint16 fc;
+ uint16 durid;
+ struct ether_addr ra;
+ struct ether_addr ta;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_CTL_HDR_LEN 16
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_bar {
+ uint16 bar_control;
+ uint16 seqnum;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_BAR_LEN 4
+
+#define DOT11_BA_BITMAP_LEN 128
+#define DOT11_BA_CMP_BITMAP_LEN 8
+
+BWL_PRE_PACKED_STRUCT struct dot11_ba {
+ uint16 ba_control;
+ uint16 seqnum;
+ uint8 bitmap[DOT11_BA_BITMAP_LEN];
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_BA_LEN 4
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_management_header {
+ uint16 fc;
+ uint16 durid;
+ struct ether_addr da;
+ struct ether_addr sa;
+ struct ether_addr bssid;
+ uint16 seq;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_MGMT_HDR_LEN 24
+
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_bcn_prb {
+ uint32 timestamp[2];
+ uint16 beacon_interval;
+ uint16 capability;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_BCN_PRB_LEN 12
+
+BWL_PRE_PACKED_STRUCT struct dot11_auth {
+ uint16 alg;
+ uint16 seq;
+ uint16 status;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_AUTH_FIXED_LEN 6
+
+BWL_PRE_PACKED_STRUCT struct dot11_assoc_req {
+ uint16 capability;
+ uint16 listen;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_ASSOC_REQ_FIXED_LEN 4
+
+BWL_PRE_PACKED_STRUCT struct dot11_reassoc_req {
+ uint16 capability;
+ uint16 listen;
+ struct ether_addr ap;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_REASSOC_REQ_FIXED_LEN 10
+
+BWL_PRE_PACKED_STRUCT struct dot11_assoc_resp {
+ uint16 capability;
+ uint16 status;
+ uint16 aid;
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_ASSOC_RESP_FIXED_LEN 6
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_measure {
+ uint8 category;
+ uint8 action;
+ uint8 token;
+ uint8 data[1];
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_ACTION_MEASURE_LEN 3
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_ht_ch_width {
+ uint8 category;
+ uint8 action;
+ uint8 ch_width;
+} BWL_POST_PACKED_STRUCT;
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_ht_mimops {
+ uint8 category;
+ uint8 action;
+ uint8 control;
+} BWL_POST_PACKED_STRUCT;
+
+#define SM_PWRSAVE_ENABLE 1
+#define SM_PWRSAVE_MODE 2
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_power_cnst {
+ uint8 id;
+ uint8 len;
+ uint8 power;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_power_cnst dot11_power_cnst_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_power_cap {
+ uint8 min;
+ uint8 max;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_power_cap dot11_power_cap_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_tpc_rep {
+ uint8 id;
+ uint8 len;
+ uint8 tx_pwr;
+ uint8 margin;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_tpc_rep dot11_tpc_rep_t;
+#define DOT11_MNG_IE_TPC_REPORT_LEN 2
+
+BWL_PRE_PACKED_STRUCT struct dot11_supp_channels {
+ uint8 id;
+ uint8 len;
+ uint8 first_channel;
+ uint8 num_channels;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_supp_channels dot11_supp_channels_t;
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_extch {
+ uint8 id;
+ uint8 len;
+ uint8 extch;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_extch dot11_extch_ie_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_brcm_extch {
+ uint8 id;
+ uint8 len;
+ uint8 oui[3];
+ uint8 type;
+ uint8 extch;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_brcm_extch dot11_brcm_extch_ie_t;
+
+#define BRCM_EXTCH_IE_LEN 5
+#define BRCM_EXTCH_IE_TYPE 53
+#define DOT11_EXTCH_IE_LEN 1
+#define DOT11_EXT_CH_MASK 0x03
+#define DOT11_EXT_CH_UPPER 0x01
+#define DOT11_EXT_CH_LOWER 0x03
+#define DOT11_EXT_CH_NONE 0x00
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_frmhdr {
+ uint8 category;
+ uint8 action;
+ uint8 data[1];
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_ACTION_FRMHDR_LEN 2
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_channel_switch {
+ uint8 id;
+ uint8 len;
+ uint8 mode;
+ uint8 channel;
+ uint8 count;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_channel_switch dot11_chan_switch_ie_t;
+
+#define DOT11_SWITCH_IE_LEN 3
+
+#define DOT11_CSA_MODE_ADVISORY 0
+#define DOT11_CSA_MODE_NO_TX 1
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_switch_channel {
+ uint8 category;
+ uint8 action;
+ dot11_chan_switch_ie_t chan_switch_ie;
+ dot11_brcm_extch_ie_t extch_ie;
+} BWL_POST_PACKED_STRUCT;
+
+BWL_PRE_PACKED_STRUCT struct dot11_csa_body {
+ uint8 mode;
+ uint8 reg;
+ uint8 channel;
+ uint8 count;
+} BWL_POST_PACKED_STRUCT;
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_ext_csa {
+ uint8 id;
+ uint8 len;
+ struct dot11_csa_body b;
+} BWL_POST_PACKED_STRUCT;
+
+BWL_PRE_PACKED_STRUCT struct dot11y_action_ext_csa {
+ uint8 category;
+ uint8 action;
+ struct dot11_csa_body b;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_ext_csa dot11_ext_csa_ie_t;
+#define DOT11_EXT_CSA_IE_LEN 4
+
+BWL_PRE_PACKED_STRUCT struct dot11_action_ext_csa {
+ uint8 category;
+ uint8 action;
+ dot11_ext_csa_ie_t chan_switch_ie;
+} BWL_POST_PACKED_STRUCT;
+
+BWL_PRE_PACKED_STRUCT struct dot11_obss_coex {
+ uint8 id;
+ uint8 len;
+ uint8 info;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_obss_coex dot11_obss_coex_t;
+#define DOT11_OBSS_COEXINFO_LEN 1
+
+#define DOT11_OBSS_COEX_INFO_REQ 0x01
+#define DOT11_OBSS_COEX_40MHZ_INTOLERANT 0x02
+#define DOT11_OBSS_COEX_20MHZ_WIDTH_REQ 0x04
+
+BWL_PRE_PACKED_STRUCT struct dot11_obss_chanlist {
+ uint8 id;
+ uint8 len;
+ uint8 regclass;
+ uint8 chanlist[1];
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_obss_chanlist dot11_obss_chanlist_t;
+#define DOT11_OBSS_CHANLIST_FIXED_LEN 1
+
+BWL_PRE_PACKED_STRUCT struct dot11_extcap_ie {
+ uint8 id;
+ uint8 len;
+ uint8 cap;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_extcap_ie dot11_extcap_ie_t;
+#define DOT11_EXTCAP_LEN 1
+
+
+
+#define DOT11_MEASURE_TYPE_BASIC 0
+#define DOT11_MEASURE_TYPE_CCA 1
+#define DOT11_MEASURE_TYPE_RPI 2
+
+
+#define DOT11_MEASURE_MODE_ENABLE (1<<1)
+#define DOT11_MEASURE_MODE_REQUEST (1<<2)
+#define DOT11_MEASURE_MODE_REPORT (1<<3)
+
+#define DOT11_MEASURE_MODE_LATE (1<<0)
+#define DOT11_MEASURE_MODE_INCAPABLE (1<<1)
+#define DOT11_MEASURE_MODE_REFUSED (1<<2)
+
+#define DOT11_MEASURE_BASIC_MAP_BSS ((uint8)(1<<0))
+#define DOT11_MEASURE_BASIC_MAP_OFDM ((uint8)(1<<1))
+#define DOT11_MEASURE_BASIC_MAP_UKNOWN ((uint8)(1<<2))
+#define DOT11_MEASURE_BASIC_MAP_RADAR ((uint8)(1<<3))
+#define DOT11_MEASURE_BASIC_MAP_UNMEAS ((uint8)(1<<4))
+
+BWL_PRE_PACKED_STRUCT struct dot11_meas_req {
+ uint8 id;
+ uint8 len;
+ uint8 token;
+ uint8 mode;
+ uint8 type;
+ uint8 channel;
+ uint8 start_time[8];
+ uint16 duration;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_meas_req dot11_meas_req_t;
+#define DOT11_MNG_IE_MREQ_LEN 14
+
+#define DOT11_MNG_IE_MREQ_FIXED_LEN 3
+
+BWL_PRE_PACKED_STRUCT struct dot11_meas_rep {
+ uint8 id;
+ uint8 len;
+ uint8 token;
+ uint8 mode;
+ uint8 type;
+ BWL_PRE_PACKED_STRUCT union
+ {
+ BWL_PRE_PACKED_STRUCT struct {
+ uint8 channel;
+ uint8 start_time[8];
+ uint16 duration;
+ uint8 map;
+ } BWL_POST_PACKED_STRUCT basic;
+ uint8 data[1];
+ } BWL_POST_PACKED_STRUCT rep;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_meas_rep dot11_meas_rep_t;
+
+
+#define DOT11_MNG_IE_MREP_FIXED_LEN 3
+
+BWL_PRE_PACKED_STRUCT struct dot11_meas_rep_basic {
+ uint8 channel;
+ uint8 start_time[8];
+ uint16 duration;
+ uint8 map;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_meas_rep_basic dot11_meas_rep_basic_t;
+#define DOT11_MEASURE_BASIC_REP_LEN 12
+
+BWL_PRE_PACKED_STRUCT struct dot11_quiet {
+ uint8 id;
+ uint8 len;
+ uint8 count;
+ uint8 period;
+ uint16 duration;
+ uint16 offset;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_quiet dot11_quiet_t;
+
+BWL_PRE_PACKED_STRUCT struct chan_map_tuple {
+ uint8 channel;
+ uint8 map;
+} BWL_POST_PACKED_STRUCT;
+typedef struct chan_map_tuple chan_map_tuple_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_ibss_dfs {
+ uint8 id;
+ uint8 len;
+ uint8 eaddr[ETHER_ADDR_LEN];
+ uint8 interval;
+ chan_map_tuple_t map[1];
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_ibss_dfs dot11_ibss_dfs_t;
+
+
+#define WME_OUI "\x00\x50\xf2"
+#define WME_VER 1
+#define WME_TYPE 2
+#define WME_SUBTYPE_IE 0
+#define WME_SUBTYPE_PARAM_IE 1
+#define WME_SUBTYPE_TSPEC 2
+
+
+#define AC_BE 0
+#define AC_BK 1
+#define AC_VI 2
+#define AC_VO 3
+#define AC_COUNT 4
+
+typedef uint8 ac_bitmap_t;
+
+#define AC_BITMAP_NONE 0x0
+#define AC_BITMAP_ALL 0xf
+#define AC_BITMAP_TST(ab, ac) (((ab) & (1 << (ac))) != 0)
+#define AC_BITMAP_SET(ab, ac) (((ab) |= (1 << (ac))))
+#define AC_BITMAP_RESET(ab, ac) (((ab) &= ~(1 << (ac))))
+
+
+BWL_PRE_PACKED_STRUCT struct wme_ie {
+ uint8 oui[3];
+ uint8 type;
+ uint8 subtype;
+ uint8 version;
+ uint8 qosinfo;
+} BWL_POST_PACKED_STRUCT;
+typedef struct wme_ie wme_ie_t;
+#define WME_IE_LEN 7
+
+BWL_PRE_PACKED_STRUCT struct edcf_acparam {
+ uint8 ACI;
+ uint8 ECW;
+ uint16 TXOP;
+} BWL_POST_PACKED_STRUCT;
+typedef struct edcf_acparam edcf_acparam_t;
+
+
+BWL_PRE_PACKED_STRUCT struct wme_param_ie {
+ uint8 oui[3];
+ uint8 type;
+ uint8 subtype;
+ uint8 version;
+ uint8 qosinfo;
+ uint8 rsvd;
+ edcf_acparam_t acparam[AC_COUNT];
+} BWL_POST_PACKED_STRUCT;
+typedef struct wme_param_ie wme_param_ie_t;
+#define WME_PARAM_IE_LEN 24
+
+
+#define WME_QI_AP_APSD_MASK 0x80
+#define WME_QI_AP_APSD_SHIFT 7
+#define WME_QI_AP_COUNT_MASK 0x0f
+#define WME_QI_AP_COUNT_SHIFT 0
+
+
+#define WME_QI_STA_MAXSPLEN_MASK 0x60
+#define WME_QI_STA_MAXSPLEN_SHIFT 5
+#define WME_QI_STA_APSD_ALL_MASK 0xf
+#define WME_QI_STA_APSD_ALL_SHIFT 0
+#define WME_QI_STA_APSD_BE_MASK 0x8
+#define WME_QI_STA_APSD_BE_SHIFT 3
+#define WME_QI_STA_APSD_BK_MASK 0x4
+#define WME_QI_STA_APSD_BK_SHIFT 2
+#define WME_QI_STA_APSD_VI_MASK 0x2
+#define WME_QI_STA_APSD_VI_SHIFT 1
+#define WME_QI_STA_APSD_VO_MASK 0x1
+#define WME_QI_STA_APSD_VO_SHIFT 0
+
+
+#define EDCF_AIFSN_MIN 1
+#define EDCF_AIFSN_MAX 15
+#define EDCF_AIFSN_MASK 0x0f
+#define EDCF_ACM_MASK 0x10
+#define EDCF_ACI_MASK 0x60
+#define EDCF_ACI_SHIFT 5
+#define EDCF_AIFSN_SHIFT 12
+
+
+#define EDCF_ECW_MIN 0
+#define EDCF_ECW_MAX 15
+#define EDCF_ECW2CW(exp) ((1 << (exp)) - 1)
+#define EDCF_ECWMIN_MASK 0x0f
+#define EDCF_ECWMAX_MASK 0xf0
+#define EDCF_ECWMAX_SHIFT 4
+
+
+#define EDCF_TXOP_MIN 0
+#define EDCF_TXOP_MAX 65535
+#define EDCF_TXOP2USEC(txop) ((txop) << 5)
+
+
+#define NON_EDCF_AC_BE_ACI_STA 0x02
+
+
+#define EDCF_AC_BE_ACI_STA 0x03
+#define EDCF_AC_BE_ECW_STA 0xA4
+#define EDCF_AC_BE_TXOP_STA 0x0000
+#define EDCF_AC_BK_ACI_STA 0x27
+#define EDCF_AC_BK_ECW_STA 0xA4
+#define EDCF_AC_BK_TXOP_STA 0x0000
+#define EDCF_AC_VI_ACI_STA 0x42
+#define EDCF_AC_VI_ECW_STA 0x43
+#define EDCF_AC_VI_TXOP_STA 0x005e
+#define EDCF_AC_VO_ACI_STA 0x62
+#define EDCF_AC_VO_ECW_STA 0x32
+#define EDCF_AC_VO_TXOP_STA 0x002f
+
+
+#define EDCF_AC_BE_ACI_AP 0x03
+#define EDCF_AC_BE_ECW_AP 0x64
+#define EDCF_AC_BE_TXOP_AP 0x0000
+#define EDCF_AC_BK_ACI_AP 0x27
+#define EDCF_AC_BK_ECW_AP 0xA4
+#define EDCF_AC_BK_TXOP_AP 0x0000
+#define EDCF_AC_VI_ACI_AP 0x41
+#define EDCF_AC_VI_ECW_AP 0x43
+#define EDCF_AC_VI_TXOP_AP 0x005e
+#define EDCF_AC_VO_ACI_AP 0x61
+#define EDCF_AC_VO_ECW_AP 0x32
+#define EDCF_AC_VO_TXOP_AP 0x002f
+
+
+BWL_PRE_PACKED_STRUCT struct edca_param_ie {
+ uint8 qosinfo;
+ uint8 rsvd;
+ edcf_acparam_t acparam[AC_COUNT];
+} BWL_POST_PACKED_STRUCT;
+typedef struct edca_param_ie edca_param_ie_t;
+#define EDCA_PARAM_IE_LEN 18
+
+
+BWL_PRE_PACKED_STRUCT struct qos_cap_ie {
+ uint8 qosinfo;
+} BWL_POST_PACKED_STRUCT;
+typedef struct qos_cap_ie qos_cap_ie_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_qbss_load_ie {
+ uint8 id;
+ uint8 length;
+ uint16 station_count;
+ uint8 channel_utilization;
+ uint16 aac;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_qbss_load_ie dot11_qbss_load_ie_t;
+
+
+#define FIXED_MSDU_SIZE 0x8000
+#define MSDU_SIZE_MASK 0x7fff
+
+
+
+#define INTEGER_SHIFT 13
+#define FRACTION_MASK 0x1FFF
+
+
+BWL_PRE_PACKED_STRUCT struct dot11_management_notification {
+ uint8 category;
+ uint8 action;
+ uint8 token;
+ uint8 status;
+ uint8 data[1];
+} BWL_POST_PACKED_STRUCT;
+#define DOT11_MGMT_NOTIFICATION_LEN 4
+
+
+#define WME_ADDTS_REQUEST 0
+#define WME_ADDTS_RESPONSE 1
+#define WME_DELTS_REQUEST 2
+
+
+#define WME_ADMISSION_ACCEPTED 0
+#define WME_INVALID_PARAMETERS 1
+#define WME_ADMISSION_REFUSED 3
+
+
+#define BCN_PRB_SSID(body) ((char*)(body) + DOT11_BCN_PRB_LEN)
+
+
+#define DOT11_OPEN_SYSTEM 0
+#define DOT11_SHARED_KEY 1
+
+#define DOT11_OPEN_SHARED 2
+#define DOT11_CHALLENGE_LEN 128
+
+
+#define FC_PVER_MASK 0x3
+#define FC_PVER_SHIFT 0
+#define FC_TYPE_MASK 0xC
+#define FC_TYPE_SHIFT 2
+#define FC_SUBTYPE_MASK 0xF0
+#define FC_SUBTYPE_SHIFT 4
+#define FC_TODS 0x100
+#define FC_TODS_SHIFT 8
+#define FC_FROMDS 0x200
+#define FC_FROMDS_SHIFT 9
+#define FC_MOREFRAG 0x400
+#define FC_MOREFRAG_SHIFT 10
+#define FC_RETRY 0x800
+#define FC_RETRY_SHIFT 11
+#define FC_PM 0x1000
+#define FC_PM_SHIFT 12
+#define FC_MOREDATA 0x2000
+#define FC_MOREDATA_SHIFT 13
+#define FC_WEP 0x4000
+#define FC_WEP_SHIFT 14
+#define FC_ORDER 0x8000
+#define FC_ORDER_SHIFT 15
+
+
+#define SEQNUM_SHIFT 4
+#define SEQNUM_MAX 0x1000
+#define FRAGNUM_MASK 0xF
+
+
+
+
+#define FC_TYPE_MNG 0
+#define FC_TYPE_CTL 1
+#define FC_TYPE_DATA 2
+
+
+#define FC_SUBTYPE_ASSOC_REQ 0
+#define FC_SUBTYPE_ASSOC_RESP 1
+#define FC_SUBTYPE_REASSOC_REQ 2
+#define FC_SUBTYPE_REASSOC_RESP 3
+#define FC_SUBTYPE_PROBE_REQ 4
+#define FC_SUBTYPE_PROBE_RESP 5
+#define FC_SUBTYPE_BEACON 8
+#define FC_SUBTYPE_ATIM 9
+#define FC_SUBTYPE_DISASSOC 10
+#define FC_SUBTYPE_AUTH 11
+#define FC_SUBTYPE_DEAUTH 12
+#define FC_SUBTYPE_ACTION 13
+#define FC_SUBTYPE_ACTION_NOACK 14
+
+
+#define FC_SUBTYPE_CTL_WRAPPER 7
+#define FC_SUBTYPE_BLOCKACK_REQ 8
+#define FC_SUBTYPE_BLOCKACK 9
+#define FC_SUBTYPE_PS_POLL 10
+#define FC_SUBTYPE_RTS 11
+#define FC_SUBTYPE_CTS 12
+#define FC_SUBTYPE_ACK 13
+#define FC_SUBTYPE_CF_END 14
+#define FC_SUBTYPE_CF_END_ACK 15
+
+
+#define FC_SUBTYPE_DATA 0
+#define FC_SUBTYPE_DATA_CF_ACK 1
+#define FC_SUBTYPE_DATA_CF_POLL 2
+#define FC_SUBTYPE_DATA_CF_ACK_POLL 3
+#define FC_SUBTYPE_NULL 4
+#define FC_SUBTYPE_CF_ACK 5
+#define FC_SUBTYPE_CF_POLL 6
+#define FC_SUBTYPE_CF_ACK_POLL 7
+#define FC_SUBTYPE_QOS_DATA 8
+#define FC_SUBTYPE_QOS_DATA_CF_ACK 9
+#define FC_SUBTYPE_QOS_DATA_CF_POLL 10
+#define FC_SUBTYPE_QOS_DATA_CF_ACK_POLL 11
+#define FC_SUBTYPE_QOS_NULL 12
+#define FC_SUBTYPE_QOS_CF_POLL 14
+#define FC_SUBTYPE_QOS_CF_ACK_POLL 15
+
+
+#define FC_SUBTYPE_ANY_QOS(s) (((s) & 8) != 0)
+#define FC_SUBTYPE_ANY_NULL(s) (((s) & 4) != 0)
+#define FC_SUBTYPE_ANY_CF_POLL(s) (((s) & 2) != 0)
+#define FC_SUBTYPE_ANY_CF_ACK(s) (((s) & 1) != 0)
+
+
+#define FC_KIND_MASK (FC_TYPE_MASK | FC_SUBTYPE_MASK)
+
+#define FC_KIND(t, s) (((t) << FC_TYPE_SHIFT) | ((s) << FC_SUBTYPE_SHIFT))
+
+#define FC_SUBTYPE(fc) (((fc) & FC_SUBTYPE_MASK) >> FC_SUBTYPE_SHIFT)
+#define FC_TYPE(fc) (((fc) & FC_TYPE_MASK) >> FC_TYPE_SHIFT)
+
+#define FC_ASSOC_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ASSOC_REQ)
+#define FC_ASSOC_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ASSOC_RESP)
+#define FC_REASSOC_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_REASSOC_REQ)
+#define FC_REASSOC_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_REASSOC_RESP)
+#define FC_PROBE_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_PROBE_REQ)
+#define FC_PROBE_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_PROBE_RESP)
+#define FC_BEACON FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_BEACON)
+#define FC_DISASSOC FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_DISASSOC)
+#define FC_AUTH FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_AUTH)
+#define FC_DEAUTH FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_DEAUTH)
+#define FC_ACTION FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ACTION)
+#define FC_ACTION_NOACK FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ACTION_NOACK)
+
+#define FC_CTL_WRAPPER FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CTL_WRAPPER)
+#define FC_BLOCKACK_REQ FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_BLOCKACK_REQ)
+#define FC_BLOCKACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_BLOCKACK)
+#define FC_PS_POLL FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_PS_POLL)
+#define FC_RTS FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_RTS)
+#define FC_CTS FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CTS)
+#define FC_ACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_ACK)
+#define FC_CF_END FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CF_END)
+#define FC_CF_END_ACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CF_END_ACK)
+
+#define FC_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_DATA)
+#define FC_NULL_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_NULL)
+#define FC_DATA_CF_ACK FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_DATA_CF_ACK)
+#define FC_QOS_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_QOS_DATA)
+#define FC_QOS_NULL FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_QOS_NULL)
+
+
+
+
+#define QOS_PRIO_SHIFT 0
+#define QOS_PRIO_MASK 0x0007
+#define QOS_PRIO(qos) (((qos) & QOS_PRIO_MASK) >> QOS_PRIO_SHIFT)
+
+
+#define QOS_TID_SHIFT 0
+#define QOS_TID_MASK 0x000f
+#define QOS_TID(qos) (((qos) & QOS_TID_MASK) >> QOS_TID_SHIFT)
+
+
+#define QOS_EOSP_SHIFT 4
+#define QOS_EOSP_MASK 0x0010
+#define QOS_EOSP(qos) (((qos) & QOS_EOSP_MASK) >> QOS_EOSP_SHIFT)
+
+
+#define QOS_ACK_NORMAL_ACK 0
+#define QOS_ACK_NO_ACK 1
+#define QOS_ACK_NO_EXP_ACK 2
+#define QOS_ACK_BLOCK_ACK 3
+#define QOS_ACK_SHIFT 5
+#define QOS_ACK_MASK 0x0060
+#define QOS_ACK(qos) (((qos) & QOS_ACK_MASK) >> QOS_ACK_SHIFT)
+
+
+#define QOS_AMSDU_SHIFT 7
+#define QOS_AMSDU_MASK 0x0080
+
+
+
+
+
+
+#define DOT11_MNG_AUTH_ALGO_LEN 2
+#define DOT11_MNG_AUTH_SEQ_LEN 2
+#define DOT11_MNG_BEACON_INT_LEN 2
+#define DOT11_MNG_CAP_LEN 2
+#define DOT11_MNG_AP_ADDR_LEN 6
+#define DOT11_MNG_LISTEN_INT_LEN 2
+#define DOT11_MNG_REASON_LEN 2
+#define DOT11_MNG_AID_LEN 2
+#define DOT11_MNG_STATUS_LEN 2
+#define DOT11_MNG_TIMESTAMP_LEN 8
+
+
+#define DOT11_AID_MASK 0x3fff
+
+
+#define DOT11_RC_RESERVED 0
+#define DOT11_RC_UNSPECIFIED 1
+#define DOT11_RC_AUTH_INVAL 2
+#define DOT11_RC_DEAUTH_LEAVING 3
+#define DOT11_RC_INACTIVITY 4
+#define DOT11_RC_BUSY 5
+#define DOT11_RC_INVAL_CLASS_2 6
+#define DOT11_RC_INVAL_CLASS_3 7
+#define DOT11_RC_DISASSOC_LEAVING 8
+#define DOT11_RC_NOT_AUTH 9
+#define DOT11_RC_BAD_PC 10
+#define DOT11_RC_BAD_CHANNELS 11
+
+
+
+#define DOT11_RC_UNSPECIFIED_QOS 32
+#define DOT11_RC_INSUFFCIENT_BW 33
+#define DOT11_RC_EXCESSIVE_FRAMES 34
+#define DOT11_RC_TX_OUTSIDE_TXOP 35
+#define DOT11_RC_LEAVING_QBSS 36
+#define DOT11_RC_BAD_MECHANISM 37
+#define DOT11_RC_SETUP_NEEDED 38
+#define DOT11_RC_TIMEOUT 39
+
+#define DOT11_RC_MAX 23
+
+
+#define DOT11_SC_SUCCESS 0
+#define DOT11_SC_FAILURE 1
+#define DOT11_SC_CAP_MISMATCH 10
+#define DOT11_SC_REASSOC_FAIL 11
+#define DOT11_SC_ASSOC_FAIL 12
+#define DOT11_SC_AUTH_MISMATCH 13
+#define DOT11_SC_AUTH_SEQ 14
+#define DOT11_SC_AUTH_CHALLENGE_FAIL 15
+#define DOT11_SC_AUTH_TIMEOUT 16
+#define DOT11_SC_ASSOC_BUSY_FAIL 17
+#define DOT11_SC_ASSOC_RATE_MISMATCH 18
+#define DOT11_SC_ASSOC_SHORT_REQUIRED 19
+#define DOT11_SC_ASSOC_PBCC_REQUIRED 20
+#define DOT11_SC_ASSOC_AGILITY_REQUIRED 21
+#define DOT11_SC_ASSOC_SPECTRUM_REQUIRED 22
+#define DOT11_SC_ASSOC_BAD_POWER_CAP 23
+#define DOT11_SC_ASSOC_BAD_SUP_CHANNELS 24
+#define DOT11_SC_ASSOC_SHORTSLOT_REQUIRED 25
+#define DOT11_SC_ASSOC_ERPBCC_REQUIRED 26
+#define DOT11_SC_ASSOC_DSSOFDM_REQUIRED 27
+
+#define DOT11_SC_DECLINED 37
+#define DOT11_SC_INVALID_PARAMS 38
+
+
+#define DOT11_MNG_DS_PARAM_LEN 1
+#define DOT11_MNG_IBSS_PARAM_LEN 2
+
+
+#define DOT11_MNG_TIM_FIXED_LEN 3
+#define DOT11_MNG_TIM_DTIM_COUNT 0
+#define DOT11_MNG_TIM_DTIM_PERIOD 1
+#define DOT11_MNG_TIM_BITMAP_CTL 2
+#define DOT11_MNG_TIM_PVB 3
+
+
+#define TLV_TAG_OFF 0
+#define TLV_LEN_OFF 1
+#define TLV_HDR_LEN 2
+#define TLV_BODY_OFF 2
+
+
+#define DOT11_MNG_SSID_ID 0
+#define DOT11_MNG_RATES_ID 1
+#define DOT11_MNG_FH_PARMS_ID 2
+#define DOT11_MNG_DS_PARMS_ID 3
+#define DOT11_MNG_CF_PARMS_ID 4
+#define DOT11_MNG_TIM_ID 5
+#define DOT11_MNG_IBSS_PARMS_ID 6
+#define DOT11_MNG_COUNTRY_ID 7
+#define DOT11_MNG_HOPPING_PARMS_ID 8
+#define DOT11_MNG_HOPPING_TABLE_ID 9
+#define DOT11_MNG_REQUEST_ID 10
+#define DOT11_MNG_QBSS_LOAD_ID 11
+#define DOT11_MNG_EDCA_PARAM_ID 12
+#define DOT11_MNG_CHALLENGE_ID 16
+#define DOT11_MNG_PWR_CONSTRAINT_ID 32
+#define DOT11_MNG_PWR_CAP_ID 33
+#define DOT11_MNG_TPC_REQUEST_ID 34
+#define DOT11_MNG_TPC_REPORT_ID 35
+#define DOT11_MNG_SUPP_CHANNELS_ID 36
+#define DOT11_MNG_CHANNEL_SWITCH_ID 37
+#define DOT11_MNG_MEASURE_REQUEST_ID 38
+#define DOT11_MNG_MEASURE_REPORT_ID 39
+#define DOT11_MNG_QUIET_ID 40
+#define DOT11_MNG_IBSS_DFS_ID 41
+#define DOT11_MNG_ERP_ID 42
+#define DOT11_MNG_TS_DELAY_ID 43
+#define DOT11_MNG_HT_CAP 45
+#define DOT11_MNG_QOS_CAP_ID 46
+#define DOT11_MNG_NONERP_ID 47
+#define DOT11_MNG_RSN_ID 48
+#define DOT11_MNG_EXT_RATES_ID 50
+#define DOT11_MNG_REGCLASS_ID 59
+#define DOT11_MNG_EXT_CSA_ID 60
+#define DOT11_MNG_HT_ADD 61
+#define DOT11_MNG_EXT_CHANNEL_OFFSET 62
+#define DOT11_MNG_WAPI_ID 68
+#define DOT11_MNG_HT_BSS_COEXINFO_ID 72
+#define DOT11_MNG_HT_BSS_CHANNEL_REPORT_ID 73
+#define DOT11_MNG_HT_OBSS_ID 74
+#define DOT11_MNG_EXT_CAP 127
+#define DOT11_MNG_WPA_ID 221
+#define DOT11_MNG_PROPR_ID 221
+
+
+#define DOT11_RATE_BASIC 0x80
+#define DOT11_RATE_MASK 0x7F
+
+
+#define DOT11_MNG_ERP_LEN 1
+#define DOT11_MNG_NONERP_PRESENT 0x01
+#define DOT11_MNG_USE_PROTECTION 0x02
+#define DOT11_MNG_BARKER_PREAMBLE 0x04
+
+#define DOT11_MGN_TS_DELAY_LEN 4
+#define TS_DELAY_FIELD_SIZE 4
+
+
+#define DOT11_CAP_ESS 0x0001
+#define DOT11_CAP_IBSS 0x0002
+#define DOT11_CAP_POLLABLE 0x0004
+#define DOT11_CAP_POLL_RQ 0x0008
+#define DOT11_CAP_PRIVACY 0x0010
+#define DOT11_CAP_SHORT 0x0020
+#define DOT11_CAP_PBCC 0x0040
+#define DOT11_CAP_AGILITY 0x0080
+#define DOT11_CAP_SPECTRUM 0x0100
+#define DOT11_CAP_SHORTSLOT 0x0400
+#define DOT11_CAP_CCK_OFDM 0x2000
+
+
+#define DOT11_OBSS_COEX_MNG_SUPPORT 0x01
+
+
+#define DOT11_ACTION_HDR_LEN 2
+#define DOT11_ACTION_CAT_ERR_MASK 0x80
+#define DOT11_ACTION_CAT_MASK 0x7F
+#define DOT11_ACTION_CAT_SPECT_MNG 0
+#define DOT11_ACTION_CAT_BLOCKACK 3
+#define DOT11_ACTION_CAT_PUBLIC 4
+#define DOT11_ACTION_CAT_HT 7
+#define DOT11_ACTION_CAT_VS 127
+#define DOT11_ACTION_NOTIFICATION 0x11
+
+#define DOT11_ACTION_ID_M_REQ 0
+#define DOT11_ACTION_ID_M_REP 1
+#define DOT11_ACTION_ID_TPC_REQ 2
+#define DOT11_ACTION_ID_TPC_REP 3
+#define DOT11_ACTION_ID_CHANNEL_SWITCH 4
+#define DOT11_ACTION_ID_EXT_CSA 5
+
+
+#define DOT11_ACTION_ID_HT_CH_WIDTH 0
+#define DOT11_ACTION_ID_HT_MIMO_PS 1
+
+
+#define DOT11_PUB_ACTION_BSS_COEX_MNG 0
+#define DOT11_PUB_ACTION_CHANNEL_SWITCH 4
+
+
+#define DOT11_BA_ACTION_ADDBA_REQ 0
+#define DOT11_BA_ACTION_ADDBA_RESP 1
+#define DOT11_BA_ACTION_DELBA 2
+
+
+#define DOT11_ADDBA_PARAM_AMSDU_SUP 0x0001
+#define DOT11_ADDBA_PARAM_POLICY_MASK 0x0002
+#define DOT11_ADDBA_PARAM_POLICY_SHIFT 1
+#define DOT11_ADDBA_PARAM_TID_MASK 0x003c
+#define DOT11_ADDBA_PARAM_TID_SHIFT 2
+#define DOT11_ADDBA_PARAM_BSIZE_MASK 0xffc0
+#define DOT11_ADDBA_PARAM_BSIZE_SHIFT 6
+
+#define DOT11_ADDBA_POLICY_DELAYED 0
+#define DOT11_ADDBA_POLICY_IMMEDIATE 1
+
+BWL_PRE_PACKED_STRUCT struct dot11_addba_req {
+ uint8 category;
+ uint8 action;
+ uint8 token;
+ uint16 addba_param_set;
+ uint16 timeout;
+ uint16 start_seqnum;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_addba_req dot11_addba_req_t;
+#define DOT11_ADDBA_REQ_LEN 9
+
+BWL_PRE_PACKED_STRUCT struct dot11_addba_resp {
+ uint8 category;
+ uint8 action;
+ uint8 token;
+ uint16 status;
+ uint16 addba_param_set;
+ uint16 timeout;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_addba_resp dot11_addba_resp_t;
+#define DOT11_ADDBA_RESP_LEN 9
+
+
+#define DOT11_DELBA_PARAM_INIT_MASK 0x0800
+#define DOT11_DELBA_PARAM_INIT_SHIFT 11
+#define DOT11_DELBA_PARAM_TID_MASK 0xf000
+#define DOT11_DELBA_PARAM_TID_SHIFT 12
+
+BWL_PRE_PACKED_STRUCT struct dot11_delba {
+ uint8 category;
+ uint8 action;
+ uint16 delba_param_set;
+ uint16 reason;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_delba dot11_delba_t;
+#define DOT11_DELBA_LEN 6
+
+
+#define DOT11_BSSTYPE_INFRASTRUCTURE 0
+#define DOT11_BSSTYPE_INDEPENDENT 1
+#define DOT11_BSSTYPE_ANY 2
+#define DOT11_SCANTYPE_ACTIVE 0
+#define DOT11_SCANTYPE_PASSIVE 1
+
+
+#define PREN_PREAMBLE 24
+#define PREN_MM_EXT 8
+#define PREN_PREAMBLE_EXT 4
+
+
+#define NPHY_RIFS_TIME 2
+
+
+#define APHY_SLOT_TIME 9
+#define APHY_SIFS_TIME 16
+#define APHY_DIFS_TIME (APHY_SIFS_TIME + (2 * APHY_SLOT_TIME))
+#define APHY_PREAMBLE_TIME 16
+#define APHY_SIGNAL_TIME 4
+#define APHY_SYMBOL_TIME 4
+#define APHY_SERVICE_NBITS 16
+#define APHY_TAIL_NBITS 6
+#define APHY_CWMIN 15
+
+
+#define BPHY_SLOT_TIME 20
+#define BPHY_SIFS_TIME 10
+#define BPHY_DIFS_TIME 50
+#define BPHY_PLCP_TIME 192
+#define BPHY_PLCP_SHORT_TIME 96
+#define BPHY_CWMIN 31
+
+
+#define DOT11_OFDM_SIGNAL_EXTENSION 6
+
+#define PHY_CWMAX 1023
+
+#define DOT11_MAXNUMFRAGS 16
+
+
+typedef struct d11cnt {
+ uint32 txfrag;
+ uint32 txmulti;
+ uint32 txfail;
+ uint32 txretry;
+ uint32 txretrie;
+ uint32 rxdup;
+ uint32 txrts;
+ uint32 txnocts;
+ uint32 txnoack;
+ uint32 rxfrag;
+ uint32 rxmulti;
+ uint32 rxcrc;
+ uint32 txfrmsnt;
+ uint32 rxundec;
+} d11cnt_t;
+
+
+#define BRCM_PROP_OUI "\x00\x90\x4C"
+
+
+
+
+BWL_PRE_PACKED_STRUCT struct brcm_prop_ie_s {
+ uint8 id;
+ uint8 len;
+ uint8 oui[3];
+ uint8 type;
+ uint16 cap;
+} BWL_POST_PACKED_STRUCT;
+typedef struct brcm_prop_ie_s brcm_prop_ie_t;
+
+#define BRCM_PROP_IE_LEN 6
+
+#define DPT_IE_TYPE 2
+
+
+#define BRCM_OUI "\x00\x10\x18"
+
+
+BWL_PRE_PACKED_STRUCT struct brcm_ie {
+ uint8 id;
+ uint8 len;
+ uint8 oui[3];
+ uint8 ver;
+ uint8 assoc;
+ uint8 flags;
+ uint8 flags1;
+ uint16 amsdu_mtu_pref;
+} BWL_POST_PACKED_STRUCT;
+typedef struct brcm_ie brcm_ie_t;
+#define BRCM_IE_LEN 11
+#define BRCM_IE_VER 2
+#define BRCM_IE_LEGACY_AES_VER 1
+
+
+#ifdef WLAFTERBURNER
+#define BRF_ABCAP 0x1
+#define BRF_ABRQRD 0x2
+#define BRF_ABCOUNTER_MASK 0xf0
+#define BRF_ABCOUNTER_SHIFT 4
+#endif
+#define BRF_LZWDS 0x4
+#define BRF_BLOCKACK 0x8
+
+
+#define BRF1_AMSDU 0x1
+#define BRF1_WMEPS 0x4
+#define BRF1_PSOFIX 0x8
+
+#ifdef WLAFTERBURNER
+#define AB_WDS_TIMEOUT_MAX 15
+#define AB_WDS_TIMEOUT_MIN 1
+#endif
+
+#define AB_GUARDCOUNT 10
+
+#define MCSSET_LEN 16
+#define MAX_MCS_NUM (128)
+
+BWL_PRE_PACKED_STRUCT struct ht_cap_ie {
+ uint16 cap;
+ uint8 params;
+ uint8 supp_mcs[MCSSET_LEN];
+ uint16 ext_htcap;
+ uint32 txbf_cap;
+ uint8 as_cap;
+} BWL_POST_PACKED_STRUCT;
+typedef struct ht_cap_ie ht_cap_ie_t;
+
+
+
+BWL_PRE_PACKED_STRUCT struct ht_prop_cap_ie {
+ uint8 id;
+ uint8 len;
+ uint8 oui[3];
+ uint8 type;
+ ht_cap_ie_t cap_ie;
+} BWL_POST_PACKED_STRUCT;
+typedef struct ht_prop_cap_ie ht_prop_cap_ie_t;
+#define HT_PROP_IE_OVERHEAD 4
+#define HT_CAP_IE_LEN 26
+#define HT_CAP_IE_TYPE 51
+
+#define HT_CAP_LDPC_CODING 0x0001
+#define HT_CAP_40MHZ 0x0002
+#define HT_CAP_MIMO_PS_MASK 0x000C
+#define HT_CAP_MIMO_PS_SHIFT 0x0002
+#define HT_CAP_MIMO_PS_OFF 0x0003
+#define HT_CAP_MIMO_PS_RTS 0x0001
+#define HT_CAP_MIMO_PS_ON 0x0000
+#define HT_CAP_GF 0x0010
+#define HT_CAP_SHORT_GI_20 0x0020
+#define HT_CAP_SHORT_GI_40 0x0040
+#define HT_CAP_TX_STBC 0x0080
+#define HT_CAP_RX_STBC_MASK 0x0300
+#define HT_CAP_RX_STBC_SHIFT 8
+#define HT_CAP_DELAYED_BA 0x0400
+#define HT_CAP_MAX_AMSDU 0x0800
+#define HT_CAP_DSSS_CCK 0x1000
+#define HT_CAP_PSMP 0x2000
+#define HT_CAP_40MHZ_INTOLERANT 0x4000
+#define HT_CAP_LSIG_TXOP 0x8000
+
+#define HT_CAP_RX_STBC_NO 0x0
+#define HT_CAP_RX_STBC_ONE_STREAM 0x1
+#define HT_CAP_RX_STBC_TWO_STREAM 0x2
+#define HT_CAP_RX_STBC_THREE_STREAM 0x3
+
+#define HT_MAX_AMSDU 7935
+#define HT_MIN_AMSDU 3835
+
+#define HT_PARAMS_RX_FACTOR_MASK 0x03
+#define HT_PARAMS_DENSITY_MASK 0x1C
+#define HT_PARAMS_DENSITY_SHIFT 2
+
+
+#define AMPDU_MAX_MPDU_DENSITY 7
+#define AMPDU_RX_FACTOR_64K 3
+#define AMPDU_RX_FACTOR_BASE 8*1024
+#define AMPDU_DELIMITER_LEN 4
+
+#define HT_CAP_EXT_PCO 0x0001
+#define HT_CAP_EXT_PCO_TTIME_MASK 0x0006
+#define HT_CAP_EXT_PCO_TTIME_SHIFT 1
+#define HT_CAP_EXT_MCS_FEEDBACK_MASK 0x0300
+#define HT_CAP_EXT_MCS_FEEDBACK_SHIFT 8
+#define HT_CAP_EXT_HTC 0x0400
+#define HT_CAP_EXT_RD_RESP 0x0800
+
+BWL_PRE_PACKED_STRUCT struct ht_add_ie {
+ uint8 ctl_ch;
+ uint8 byte1;
+ uint16 opmode;
+ uint16 misc_bits;
+ uint8 basic_mcs[MCSSET_LEN];
+} BWL_POST_PACKED_STRUCT;
+typedef struct ht_add_ie ht_add_ie_t;
+
+
+
+BWL_PRE_PACKED_STRUCT struct ht_prop_add_ie {
+ uint8 id;
+ uint8 len;
+ uint8 oui[3];
+ uint8 type;
+ ht_add_ie_t add_ie;
+} BWL_POST_PACKED_STRUCT;
+typedef struct ht_prop_add_ie ht_prop_add_ie_t;
+
+#define HT_ADD_IE_LEN 22
+#define HT_ADD_IE_TYPE 52
+
+
+#define HT_BW_ANY 0x04
+#define HT_RIFS_PERMITTED 0x08
+
+
+#define HT_OPMODE_MASK 0x0003
+#define HT_OPMODE_SHIFT 0
+#define HT_OPMODE_PURE 0x0000
+#define HT_OPMODE_OPTIONAL 0x0001
+#define HT_OPMODE_HT20IN40 0x0002
+#define HT_OPMODE_MIXED 0x0003
+#define HT_OPMODE_NONGF 0x0004
+#define DOT11N_TXBURST 0x0008
+#define DOT11N_OBSS_NONHT 0x0010
+
+
+#define HT_BASIC_STBC_MCS 0x007f
+#define HT_DUAL_STBC_PROT 0x0080
+#define HT_SECOND_BCN 0x0100
+#define HT_LSIG_TXOP 0x0200
+#define HT_PCO_ACTIVE 0x0400
+#define HT_PCO_PHASE 0x0800
+#define HT_DUALCTS_PROTECTION 0x0080
+
+
+#define DOT11N_2G_TXBURST_LIMIT 6160
+#define DOT11N_5G_TXBURST_LIMIT 3080
+
+
+#define GET_HT_OPMODE(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \
+ >> HT_OPMODE_SHIFT)
+#define HT_MIXEDMODE_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \
+ == HT_OPMODE_MIXED)
+#define HT_HT20_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \
+ == HT_OPMODE_HT20IN40)
+#define HT_OPTIONAL_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \
+ == HT_OPMODE_OPTIONAL)
+#define HT_USE_PROTECTION(add_ie) (HT_HT20_PRESENT((add_ie)) || \
+ HT_MIXEDMODE_PRESENT((add_ie)))
+#define HT_NONGF_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_NONGF) \
+ == HT_OPMODE_NONGF)
+#define DOT11N_TXBURST_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & DOT11N_TXBURST) \
+ == DOT11N_TXBURST)
+#define DOT11N_OBSS_NONHT_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & DOT11N_OBSS_NONHT) \
+ == DOT11N_OBSS_NONHT)
+
+BWL_PRE_PACKED_STRUCT struct obss_params {
+ uint16 passive_dwell;
+ uint16 active_dwell;
+ uint16 bss_widthscan_interval;
+ uint16 passive_total;
+ uint16 active_total;
+ uint16 chanwidth_transition_dly;
+ uint16 activity_threshold;
+} BWL_POST_PACKED_STRUCT;
+typedef struct obss_params obss_params_t;
+
+BWL_PRE_PACKED_STRUCT struct dot11_obss_ie {
+ uint8 id;
+ uint8 len;
+ obss_params_t obss_params;
+} BWL_POST_PACKED_STRUCT;
+typedef struct dot11_obss_ie dot11_obss_ie_t;
+#define DOT11_OBSS_SCAN_IE_LEN sizeof(obss_params_t)
+
+
+BWL_PRE_PACKED_STRUCT struct vndr_ie {
+ uchar id;
+ uchar len;
+ uchar oui [3];
+ uchar data [1];
+} BWL_POST_PACKED_STRUCT;
+typedef struct vndr_ie vndr_ie_t;
+
+#define VNDR_IE_HDR_LEN 2
+#define VNDR_IE_MIN_LEN 3
+#define VNDR_IE_MAX_LEN 256
+
+
+#define WPA_VERSION 1
+#define WPA_OUI "\x00\x50\xF2"
+
+#define WPA2_VERSION 1
+#define WPA2_VERSION_LEN 2
+#define WPA2_OUI "\x00\x0F\xAC"
+
+#define WPA_OUI_LEN 3
+
+
+#define RSN_AKM_NONE 0
+#define RSN_AKM_UNSPECIFIED 1
+#define RSN_AKM_PSK 2
+
+
+#define DOT11_MAX_DEFAULT_KEYS 4
+#define DOT11_MAX_KEY_SIZE 32
+#define DOT11_MAX_IV_SIZE 16
+#define DOT11_EXT_IV_FLAG (1<<5)
+#define DOT11_WPA_KEY_RSC_LEN 8
+
+#define WEP1_KEY_SIZE 5
+#define WEP1_KEY_HEX_SIZE 10
+#define WEP128_KEY_SIZE 13
+#define WEP128_KEY_HEX_SIZE 26
+#define TKIP_MIC_SIZE 8
+#define TKIP_EOM_SIZE 7
+#define TKIP_EOM_FLAG 0x5a
+#define TKIP_KEY_SIZE 32
+#define TKIP_MIC_AUTH_TX 16
+#define TKIP_MIC_AUTH_RX 24
+#define TKIP_MIC_SUP_RX TKIP_MIC_AUTH_TX
+#define TKIP_MIC_SUP_TX TKIP_MIC_AUTH_RX
+#define AES_KEY_SIZE 16
+#define AES_MIC_SIZE 8
+
+#define SMS4_KEY_LEN 16
+#define SMS4_WPI_CBC_MAC_LEN 16
+
+
+#include <packed_section_end.h>
+
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/proto/802.11e.h b/drivers/net/wireless/bcm4329/include/proto/802.11e.h
new file mode 100644
index 00000000000..1dd6f45b1ed
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/802.11e.h
@@ -0,0 +1,131 @@
+/*
+ * 802.11e protocol header file
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: 802.11e.h,v 1.5.56.1 2008/11/20 00:51:18 Exp $
+ */
+
+#ifndef _802_11e_H_
+#define _802_11e_H_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+/* This marks the start of a packed structure section. */
+#include <packed_section_start.h>
+
+
+/* WME Traffic Specification (TSPEC) element */
+#define WME_TSPEC_HDR_LEN 2 /* WME TSPEC header length */
+#define WME_TSPEC_BODY_OFF 2 /* WME TSPEC body offset */
+
+#define WME_CATEGORY_CODE_OFFSET 0 /* WME Category code offset */
+#define WME_ACTION_CODE_OFFSET 1 /* WME Action code offset */
+#define WME_TOKEN_CODE_OFFSET 2 /* WME Token code offset */
+#define WME_STATUS_CODE_OFFSET 3 /* WME Status code offset */
+
+BWL_PRE_PACKED_STRUCT struct tsinfo {
+ uint8 octets[3];
+} BWL_POST_PACKED_STRUCT;
+
+typedef struct tsinfo tsinfo_t;
+
+/* 802.11e TSPEC IE */
+typedef BWL_PRE_PACKED_STRUCT struct tspec {
+ uint8 oui[DOT11_OUI_LEN]; /* WME_OUI */
+ uint8 type; /* WME_TYPE */
+ uint8 subtype; /* WME_SUBTYPE_TSPEC */
+ uint8 version; /* WME_VERSION */
+ tsinfo_t tsinfo; /* TS Info bit field */
+ uint16 nom_msdu_size; /* (Nominal or fixed) MSDU Size (bytes) */
+ uint16 max_msdu_size; /* Maximum MSDU Size (bytes) */
+ uint32 min_srv_interval; /* Minimum Service Interval (us) */
+ uint32 max_srv_interval; /* Maximum Service Interval (us) */
+ uint32 inactivity_interval; /* Inactivity Interval (us) */
+ uint32 suspension_interval; /* Suspension Interval (us) */
+ uint32 srv_start_time; /* Service Start Time (us) */
+ uint32 min_data_rate; /* Minimum Data Rate (bps) */
+ uint32 mean_data_rate; /* Mean Data Rate (bps) */
+ uint32 peak_data_rate; /* Peak Data Rate (bps) */
+ uint32 max_burst_size; /* Maximum Burst Size (bytes) */
+ uint32 delay_bound; /* Delay Bound (us) */
+ uint32 min_phy_rate; /* Minimum PHY Rate (bps) */
+ uint16 surplus_bw; /* Surplus Bandwidth Allowance (range 1.0-8.0) */
+ uint16 medium_time; /* Medium Time (32 us/s periods) */
+} BWL_POST_PACKED_STRUCT tspec_t;
+
+#define WME_TSPEC_LEN (sizeof(tspec_t)) /* not including 2-bytes of header */
+
+/* ts_info */
+/* 802.1D priority is duplicated - bits 13-11 AND bits 3-1 */
+#define TS_INFO_TID_SHIFT 1 /* TS info. TID shift */
+#define TS_INFO_TID_MASK (0xf << TS_INFO_TID_SHIFT) /* TS info. TID mask */
+#define TS_INFO_CONTENTION_SHIFT 7 /* TS info. contention shift */
+#define TS_INFO_CONTENTION_MASK (0x1 << TS_INFO_CONTENTION_SHIFT) /* TS info. contention mask */
+#define TS_INFO_DIRECTION_SHIFT 5 /* TS info. direction shift */
+#define TS_INFO_DIRECTION_MASK (0x3 << TS_INFO_DIRECTION_SHIFT) /* TS info. direction mask */
+#define TS_INFO_PSB_SHIFT 2 /* TS info. PSB bit Shift */
+#define TS_INFO_PSB_MASK (1 << TS_INFO_PSB_SHIFT) /* TS info. PSB mask */
+#define TS_INFO_UPLINK (0 << TS_INFO_DIRECTION_SHIFT) /* TS info. uplink */
+#define TS_INFO_DOWNLINK (1 << TS_INFO_DIRECTION_SHIFT) /* TS info. downlink */
+#define TS_INFO_BIDIRECTIONAL (3 << TS_INFO_DIRECTION_SHIFT) /* TS info. bidirectional */
+#define TS_INFO_USER_PRIO_SHIFT 3 /* TS info. user priority shift */
+/* TS info. user priority mask */
+#define TS_INFO_USER_PRIO_MASK (0x7 << TS_INFO_USER_PRIO_SHIFT)
+
+/* Macro to get/set bit(s) field in TSINFO */
+#define WLC_CAC_GET_TID(pt) ((((pt).octets[0]) & TS_INFO_TID_MASK) >> TS_INFO_TID_SHIFT)
+#define WLC_CAC_GET_DIR(pt) ((((pt).octets[0]) & \
+ TS_INFO_DIRECTION_MASK) >> TS_INFO_DIRECTION_SHIFT)
+#define WLC_CAC_GET_PSB(pt) ((((pt).octets[1]) & TS_INFO_PSB_MASK) >> TS_INFO_PSB_SHIFT)
+#define WLC_CAC_GET_USER_PRIO(pt) ((((pt).octets[1]) & \
+ TS_INFO_USER_PRIO_MASK) >> TS_INFO_USER_PRIO_SHIFT)
+
+#define WLC_CAC_SET_TID(pt, id) ((((pt).octets[0]) & (~TS_INFO_TID_MASK)) | \
+ ((id) << TS_INFO_TID_SHIFT))
+#define WLC_CAC_SET_USER_PRIO(pt, prio) ((((pt).octets[0]) & (~TS_INFO_USER_PRIO_MASK)) | \
+ ((prio) << TS_INFO_USER_PRIO_SHIFT))
+
+/* 802.11e QBSS Load IE */
+#define QBSS_LOAD_IE_LEN 5 /* QBSS Load IE length */
+#define QBSS_LOAD_AAC_OFF 3 /* AAC offset in IE */
+
+#define CAC_ADDTS_RESP_TIMEOUT 300 /* default ADDTS response timeout in ms */
+
+/* 802.11e ADDTS status code */
+#define DOT11E_STATUS_ADMISSION_ACCEPTED 0 /* TSPEC Admission accepted status */
+#define DOT11E_STATUS_ADDTS_INVALID_PARAM 1 /* TSPEC invalid parameter status */
+#define DOT11E_STATUS_ADDTS_REFUSED_NSBW 3 /* ADDTS refused (non-sufficient BW) */
+#define DOT11E_STATUS_ADDTS_REFUSED_AWHILE 47 /* ADDTS refused but could retry later */
+
+/* 802.11e DELTS status code */
+#define DOT11E_STATUS_QSTA_LEAVE_QBSS 36 /* STA leave QBSS */
+#define DOT11E_STATUS_END_TS 37 /* END TS */
+#define DOT11E_STATUS_UNKNOWN_TS 38 /* UNKNOWN TS */
+#define DOT11E_STATUS_QSTA_REQ_TIMEOUT 39 /* STA ADDTS request timeout */
+
+
+/* This marks the end of a packed structure section. */
+#include <packed_section_end.h>
+
+#endif /* _802_11e_CAC_H_ */
diff --git a/drivers/net/wireless/bcm4329/include/proto/802.1d.h b/drivers/net/wireless/bcm4329/include/proto/802.1d.h
new file mode 100644
index 00000000000..45c728bc297
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/802.1d.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * Fundamental types and constants relating to 802.1D
+ *
+ * $Id: 802.1d.h,v 9.3 2007/04/10 21:33:06 Exp $
+ */
+
+
+#ifndef _802_1_D_
+#define _802_1_D_
+
+
+#define PRIO_8021D_NONE 2
+#define PRIO_8021D_BK 1
+#define PRIO_8021D_BE 0
+#define PRIO_8021D_EE 3
+#define PRIO_8021D_CL 4
+#define PRIO_8021D_VI 5
+#define PRIO_8021D_VO 6
+#define PRIO_8021D_NC 7
+#define MAXPRIO 7
+#define NUMPRIO (MAXPRIO + 1)
+
+#define ALLPRIO -1
+
+
+#define PRIO2PREC(prio) \
+ (((prio) == PRIO_8021D_NONE || (prio) == PRIO_8021D_BE) ? ((prio^2)) : (prio))
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/proto/bcmeth.h b/drivers/net/wireless/bcm4329/include/proto/bcmeth.h
new file mode 100644
index 00000000000..fdb5a2a5648
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/bcmeth.h
@@ -0,0 +1,83 @@
+/*
+ * Broadcom Ethernettype protocol definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: bcmeth.h,v 9.9.46.1 2008/11/20 00:51:20 Exp $
+ */
+
+
+
+
+#ifndef _BCMETH_H_
+#define _BCMETH_H_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+
+#include <packed_section_start.h>
+
+
+
+
+
+
+
+#define BCMILCP_SUBTYPE_RATE 1
+#define BCMILCP_SUBTYPE_LINK 2
+#define BCMILCP_SUBTYPE_CSA 3
+#define BCMILCP_SUBTYPE_LARQ 4
+#define BCMILCP_SUBTYPE_VENDOR 5
+#define BCMILCP_SUBTYPE_FLH 17
+
+#define BCMILCP_SUBTYPE_VENDOR_LONG 32769
+#define BCMILCP_SUBTYPE_CERT 32770
+#define BCMILCP_SUBTYPE_SES 32771
+
+
+#define BCMILCP_BCM_SUBTYPE_RESERVED 0
+#define BCMILCP_BCM_SUBTYPE_EVENT 1
+#define BCMILCP_BCM_SUBTYPE_SES 2
+
+
+#define BCMILCP_BCM_SUBTYPE_DPT 4
+
+#define BCMILCP_BCM_SUBTYPEHDR_MINLENGTH 8
+#define BCMILCP_BCM_SUBTYPEHDR_VERSION 0
+
+
+typedef BWL_PRE_PACKED_STRUCT struct bcmeth_hdr
+{
+ uint16 subtype;
+ uint16 length;
+ uint8 version;
+ uint8 oui[3];
+
+ uint16 usr_subtype;
+} BWL_POST_PACKED_STRUCT bcmeth_hdr_t;
+
+
+
+#include <packed_section_end.h>
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/proto/bcmevent.h b/drivers/net/wireless/bcm4329/include/proto/bcmevent.h
new file mode 100644
index 00000000000..1f8ecb14d97
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/bcmevent.h
@@ -0,0 +1,212 @@
+/*
+ * Broadcom Event protocol definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ *
+ * Dependencies: proto/bcmeth.h
+ *
+ * $Id: bcmevent.h,v 9.34.4.1.20.16.64.1 2010/11/08 21:57:03 Exp $
+ *
+ */
+
+
+
+
+#ifndef _BCMEVENT_H_
+#define _BCMEVENT_H_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+
+#include <packed_section_start.h>
+
+#define BCM_EVENT_MSG_VERSION 1
+#define BCM_MSG_IFNAME_MAX 16
+
+
+#define WLC_EVENT_MSG_LINK 0x01
+#define WLC_EVENT_MSG_FLUSHTXQ 0x02
+#define WLC_EVENT_MSG_GROUP 0x04
+
+
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+ uint16 version;
+ uint16 flags;
+ uint32 event_type;
+ uint32 status;
+ uint32 reason;
+ uint32 auth_type;
+ uint32 datalen;
+ struct ether_addr addr;
+ char ifname[BCM_MSG_IFNAME_MAX];
+} BWL_POST_PACKED_STRUCT wl_event_msg_t;
+
+
+typedef BWL_PRE_PACKED_STRUCT struct bcm_event {
+ struct ether_header eth;
+ bcmeth_hdr_t bcm_hdr;
+ wl_event_msg_t event;
+
+} BWL_POST_PACKED_STRUCT bcm_event_t;
+
+#define BCM_MSG_LEN (sizeof(bcm_event_t) - sizeof(bcmeth_hdr_t) - sizeof(struct ether_header))
+
+
+#define WLC_E_SET_SSID 0
+#define WLC_E_JOIN 1
+#define WLC_E_START 2
+#define WLC_E_AUTH 3
+#define WLC_E_AUTH_IND 4
+#define WLC_E_DEAUTH 5
+#define WLC_E_DEAUTH_IND 6
+#define WLC_E_ASSOC 7
+#define WLC_E_ASSOC_IND 8
+#define WLC_E_REASSOC 9
+#define WLC_E_REASSOC_IND 10
+#define WLC_E_DISASSOC 11
+#define WLC_E_DISASSOC_IND 12
+#define WLC_E_QUIET_START 13
+#define WLC_E_QUIET_END 14
+#define WLC_E_BEACON_RX 15
+#define WLC_E_LINK 16
+#define WLC_E_MIC_ERROR 17
+#define WLC_E_NDIS_LINK 18
+#define WLC_E_ROAM 19
+#define WLC_E_TXFAIL 20
+#define WLC_E_PMKID_CACHE 21
+#define WLC_E_RETROGRADE_TSF 22
+#define WLC_E_PRUNE 23
+#define WLC_E_AUTOAUTH 24
+#define WLC_E_EAPOL_MSG 25
+#define WLC_E_SCAN_COMPLETE 26
+#define WLC_E_ADDTS_IND 27
+#define WLC_E_DELTS_IND 28
+#define WLC_E_BCNSENT_IND 29
+#define WLC_E_BCNRX_MSG 30
+#define WLC_E_BCNLOST_MSG 31
+#define WLC_E_ROAM_PREP 32
+#define WLC_E_PFN_NET_FOUND 33
+#define WLC_E_PFN_NET_LOST 34
+#define WLC_E_RESET_COMPLETE 35
+#define WLC_E_JOIN_START 36
+#define WLC_E_ROAM_START 37
+#define WLC_E_ASSOC_START 38
+#define WLC_E_IBSS_ASSOC 39
+#define WLC_E_RADIO 40
+#define WLC_E_PSM_WATCHDOG 41
+#define WLC_E_PROBREQ_MSG 44
+#define WLC_E_SCAN_CONFIRM_IND 45
+#define WLC_E_PSK_SUP 46
+#define WLC_E_COUNTRY_CODE_CHANGED 47
+#define WLC_E_EXCEEDED_MEDIUM_TIME 48
+#define WLC_E_ICV_ERROR 49
+#define WLC_E_UNICAST_DECODE_ERROR 50
+#define WLC_E_MULTICAST_DECODE_ERROR 51
+#define WLC_E_TRACE 52
+#define WLC_E_IF 54
+#define WLC_E_RSSI 56
+#define WLC_E_PFN_SCAN_COMPLETE 57
+#define WLC_E_ACTION_FRAME 58
+#define WLC_E_ACTION_FRAME_COMPLETE 59
+
+#define WLC_E_ESCAN_RESULT 69
+#define WLC_E_WAKE_EVENT 70
+#define WLC_E_RELOAD 71
+#define WLC_E_LAST 72
+
+
+
+#define WLC_E_STATUS_SUCCESS 0
+#define WLC_E_STATUS_FAIL 1
+#define WLC_E_STATUS_TIMEOUT 2
+#define WLC_E_STATUS_NO_NETWORKS 3
+#define WLC_E_STATUS_ABORT 4
+#define WLC_E_STATUS_NO_ACK 5
+#define WLC_E_STATUS_UNSOLICITED 6
+#define WLC_E_STATUS_ATTEMPT 7
+#define WLC_E_STATUS_PARTIAL 8
+#define WLC_E_STATUS_NEWSCAN 9
+#define WLC_E_STATUS_NEWASSOC 10
+#define WLC_E_STATUS_11HQUIET 11
+#define WLC_E_STATUS_SUPPRESS 12
+#define WLC_E_STATUS_NOCHANS 13
+#define WLC_E_STATUS_CCXFASTRM 14
+#define WLC_E_STATUS_CS_ABORT 15
+
+
+#define WLC_E_REASON_INITIAL_ASSOC 0
+#define WLC_E_REASON_LOW_RSSI 1
+#define WLC_E_REASON_DEAUTH 2
+#define WLC_E_REASON_DISASSOC 3
+#define WLC_E_REASON_BCNS_LOST 4
+#define WLC_E_REASON_FAST_ROAM_FAILED 5
+#define WLC_E_REASON_DIRECTED_ROAM 6
+#define WLC_E_REASON_TSPEC_REJECTED 7
+#define WLC_E_REASON_BETTER_AP 8
+
+
+#define WLC_E_PRUNE_ENCR_MISMATCH 1
+#define WLC_E_PRUNE_BCAST_BSSID 2
+#define WLC_E_PRUNE_MAC_DENY 3
+#define WLC_E_PRUNE_MAC_NA 4
+#define WLC_E_PRUNE_REG_PASSV 5
+#define WLC_E_PRUNE_SPCT_MGMT 6
+#define WLC_E_PRUNE_RADAR 7
+#define WLC_E_RSN_MISMATCH 8
+#define WLC_E_PRUNE_NO_COMMON_RATES 9
+#define WLC_E_PRUNE_BASIC_RATES 10
+#define WLC_E_PRUNE_CIPHER_NA 12
+#define WLC_E_PRUNE_KNOWN_STA 13
+#define WLC_E_PRUNE_WDS_PEER 15
+#define WLC_E_PRUNE_QBSS_LOAD 16
+#define WLC_E_PRUNE_HOME_AP 17
+
+
+#define WLC_E_SUP_OTHER 0
+#define WLC_E_SUP_DECRYPT_KEY_DATA 1
+#define WLC_E_SUP_BAD_UCAST_WEP128 2
+#define WLC_E_SUP_BAD_UCAST_WEP40 3
+#define WLC_E_SUP_UNSUP_KEY_LEN 4
+#define WLC_E_SUP_PW_KEY_CIPHER 5
+#define WLC_E_SUP_MSG3_TOO_MANY_IE 6
+#define WLC_E_SUP_MSG3_IE_MISMATCH 7
+#define WLC_E_SUP_NO_INSTALL_FLAG 8
+#define WLC_E_SUP_MSG3_NO_GTK 9
+#define WLC_E_SUP_GRP_KEY_CIPHER 10
+#define WLC_E_SUP_GRP_MSG1_NO_GTK 11
+#define WLC_E_SUP_GTK_DECRYPT_FAIL 12
+#define WLC_E_SUP_SEND_FAIL 13
+#define WLC_E_SUP_DEAUTH 14
+#define WLC_E_SUP_WPA_PSK_TMO 15
+
+
+#define WLC_E_IF_ADD 1
+#define WLC_E_IF_DEL 2
+
+#define WLC_E_RELOAD_STATUS1 1
+
+#include <packed_section_end.h>
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/proto/bcmip.h b/drivers/net/wireless/bcm4329/include/proto/bcmip.h
new file mode 100644
index 00000000000..9d2fd6fba48
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/bcmip.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * Fundamental constants relating to IP Protocol
+ *
+ * $Id: bcmip.h,v 9.16.186.4 2009/01/27 04:25:25 Exp $
+ */
+
+
+#ifndef _bcmip_h_
+#define _bcmip_h_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+
+#include <packed_section_start.h>
+
+
+
+#define IP_VER_OFFSET 0x0
+#define IP_VER_MASK 0xf0
+#define IP_VER_SHIFT 4
+#define IP_VER_4 4
+#define IP_VER_6 6
+
+#define IP_VER(ip_body) \
+ ((((uint8 *)(ip_body))[IP_VER_OFFSET] & IP_VER_MASK) >> IP_VER_SHIFT)
+
+#define IP_PROT_ICMP 0x1
+#define IP_PROT_TCP 0x6
+#define IP_PROT_UDP 0x11
+
+
+#define IPV4_VER_HL_OFFSET 0
+#define IPV4_TOS_OFFSET 1
+#define IPV4_PKTLEN_OFFSET 2
+#define IPV4_PKTFLAG_OFFSET 6
+#define IPV4_PROT_OFFSET 9
+#define IPV4_CHKSUM_OFFSET 10
+#define IPV4_SRC_IP_OFFSET 12
+#define IPV4_DEST_IP_OFFSET 16
+#define IPV4_OPTIONS_OFFSET 20
+
+
+#define IPV4_VER_MASK 0xf0
+#define IPV4_VER_SHIFT 4
+
+#define IPV4_HLEN_MASK 0x0f
+#define IPV4_HLEN(ipv4_body) (4 * (((uint8 *)(ipv4_body))[IPV4_VER_HL_OFFSET] & IPV4_HLEN_MASK))
+
+#define IPV4_ADDR_LEN 4
+
+#define IPV4_ADDR_NULL(a) ((((uint8 *)(a))[0] | ((uint8 *)(a))[1] | \
+ ((uint8 *)(a))[2] | ((uint8 *)(a))[3]) == 0)
+
+#define IPV4_ADDR_BCAST(a) ((((uint8 *)(a))[0] & ((uint8 *)(a))[1] & \
+ ((uint8 *)(a))[2] & ((uint8 *)(a))[3]) == 0xff)
+
+#define IPV4_TOS_DSCP_MASK 0xfc
+#define IPV4_TOS_DSCP_SHIFT 2
+
+#define IPV4_TOS(ipv4_body) (((uint8 *)(ipv4_body))[IPV4_TOS_OFFSET])
+
+#define IPV4_TOS_PREC_MASK 0xe0
+#define IPV4_TOS_PREC_SHIFT 5
+
+#define IPV4_TOS_LOWDELAY 0x10
+#define IPV4_TOS_THROUGHPUT 0x8
+#define IPV4_TOS_RELIABILITY 0x4
+
+#define IPV4_PROT(ipv4_body) (((uint8 *)(ipv4_body))[IPV4_PROT_OFFSET])
+
+#define IPV4_FRAG_RESV 0x8000
+#define IPV4_FRAG_DONT 0x4000
+#define IPV4_FRAG_MORE 0x2000
+#define IPV4_FRAG_OFFSET_MASK 0x1fff
+
+#define IPV4_ADDR_STR_LEN 16
+
+
+BWL_PRE_PACKED_STRUCT struct ipv4_addr {
+ uint8 addr[IPV4_ADDR_LEN];
+} BWL_POST_PACKED_STRUCT;
+
+BWL_PRE_PACKED_STRUCT struct ipv4_hdr {
+ uint8 version_ihl;
+ uint8 tos;
+ uint16 tot_len;
+ uint16 id;
+ uint16 frag;
+ uint8 ttl;
+ uint8 prot;
+ uint16 hdr_chksum;
+ uint8 src_ip[IPV4_ADDR_LEN];
+ uint8 dst_ip[IPV4_ADDR_LEN];
+} BWL_POST_PACKED_STRUCT;
+
+
+#define IPV6_PAYLOAD_LEN_OFFSET 4
+#define IPV6_NEXT_HDR_OFFSET 6
+#define IPV6_HOP_LIMIT_OFFSET 7
+#define IPV6_SRC_IP_OFFSET 8
+#define IPV6_DEST_IP_OFFSET 24
+
+
+#define IPV6_TRAFFIC_CLASS(ipv6_body) \
+ (((((uint8 *)(ipv6_body))[0] & 0x0f) << 4) | \
+ ((((uint8 *)(ipv6_body))[1] & 0xf0) >> 4))
+
+#define IPV6_FLOW_LABEL(ipv6_body) \
+ (((((uint8 *)(ipv6_body))[1] & 0x0f) << 16) | \
+ (((uint8 *)(ipv6_body))[2] << 8) | \
+ (((uint8 *)(ipv6_body))[3]))
+
+#define IPV6_PAYLOAD_LEN(ipv6_body) \
+ ((((uint8 *)(ipv6_body))[IPV6_PAYLOAD_LEN_OFFSET + 0] << 8) | \
+ ((uint8 *)(ipv6_body))[IPV6_PAYLOAD_LEN_OFFSET + 1])
+
+#define IPV6_NEXT_HDR(ipv6_body) \
+ (((uint8 *)(ipv6_body))[IPV6_NEXT_HDR_OFFSET])
+
+#define IPV6_PROT(ipv6_body) IPV6_NEXT_HDR(ipv6_body)
+
+#define IPV6_ADDR_LEN 16
+
+
+#ifndef IP_TOS
+#define IP_TOS(ip_body) \
+ (IP_VER(ip_body) == IP_VER_4 ? IPV4_TOS(ip_body) : \
+ IP_VER(ip_body) == IP_VER_6 ? IPV6_TRAFFIC_CLASS(ip_body) : 0)
+#endif
+
+
+
+#include <packed_section_end.h>
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/proto/eapol.h b/drivers/net/wireless/bcm4329/include/proto/eapol.h
new file mode 100644
index 00000000000..95e76ff18c6
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/eapol.h
@@ -0,0 +1,172 @@
+/*
+ * 802.1x EAPOL definitions
+ *
+ * See
+ * IEEE Std 802.1X-2001
+ * IEEE 802.1X RADIUS Usage Guidelines
+ *
+ * Copyright (C) 2002 Broadcom Corporation
+ *
+ * $Id: eapol.h,v 9.18.260.1.2.1.6.6 2009/04/08 05:00:08 Exp $
+ */
+
+#ifndef _eapol_h_
+#define _eapol_h_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+/* This marks the start of a packed structure section. */
+#include <packed_section_start.h>
+
+#define AKW_BLOCK_LEN 8 /* The only def we need here */
+
+/* EAPOL for 802.3/Ethernet */
+typedef struct {
+ struct ether_header eth; /* 802.3/Ethernet header */
+ unsigned char version; /* EAPOL protocol version */
+ unsigned char type; /* EAPOL type */
+ unsigned short length; /* Length of body */
+ unsigned char body[1]; /* Body (optional) */
+} eapol_header_t;
+
+#define EAPOL_HEADER_LEN 18
+
+/* EAPOL version */
+#define WPA2_EAPOL_VERSION 2
+#define WPA_EAPOL_VERSION 1
+#define LEAP_EAPOL_VERSION 1
+#define SES_EAPOL_VERSION 1
+
+/* EAPOL types */
+#define EAP_PACKET 0
+#define EAPOL_START 1
+#define EAPOL_LOGOFF 2
+#define EAPOL_KEY 3
+#define EAPOL_ASF 4
+
+/* EAPOL-Key types */
+#define EAPOL_RC4_KEY 1
+#define EAPOL_WPA2_KEY 2 /* 802.11i/WPA2 */
+#define EAPOL_WPA_KEY 254 /* WPA */
+
+/* RC4 EAPOL-Key header field sizes */
+#define EAPOL_KEY_REPLAY_LEN 8
+#define EAPOL_KEY_IV_LEN 16
+#define EAPOL_KEY_SIG_LEN 16
+
+/* RC4 EAPOL-Key */
+typedef BWL_PRE_PACKED_STRUCT struct {
+ unsigned char type; /* Key Descriptor Type */
+ unsigned short length; /* Key Length (unaligned) */
+ unsigned char replay[EAPOL_KEY_REPLAY_LEN]; /* Replay Counter */
+ unsigned char iv[EAPOL_KEY_IV_LEN]; /* Key IV */
+ unsigned char index; /* Key Flags & Index */
+ unsigned char signature[EAPOL_KEY_SIG_LEN]; /* Key Signature */
+ unsigned char key[1]; /* Key (optional) */
+} BWL_POST_PACKED_STRUCT eapol_key_header_t;
+
+#define EAPOL_KEY_HEADER_LEN 44
+
+/* RC4 EAPOL-Key flags */
+#define EAPOL_KEY_FLAGS_MASK 0x80
+#define EAPOL_KEY_BROADCAST 0
+#define EAPOL_KEY_UNICAST 0x80
+
+/* RC4 EAPOL-Key index */
+#define EAPOL_KEY_INDEX_MASK 0x7f
+
+/* WPA/802.11i/WPA2 EAPOL-Key header field sizes */
+#define EAPOL_WPA_KEY_REPLAY_LEN 8
+#define EAPOL_WPA_KEY_NONCE_LEN 32
+#define EAPOL_WPA_KEY_IV_LEN 16
+#define EAPOL_WPA_KEY_ID_LEN 8
+#define EAPOL_WPA_KEY_RSC_LEN 8
+#define EAPOL_WPA_KEY_MIC_LEN 16
+#define EAPOL_WPA_KEY_DATA_LEN (EAPOL_WPA_MAX_KEY_SIZE + AKW_BLOCK_LEN)
+#define EAPOL_WPA_MAX_KEY_SIZE 32
+
+/* WPA EAPOL-Key */
+typedef BWL_PRE_PACKED_STRUCT struct {
+ unsigned char type; /* Key Descriptor Type */
+ unsigned short key_info; /* Key Information (unaligned) */
+ unsigned short key_len; /* Key Length (unaligned) */
+ unsigned char replay[EAPOL_WPA_KEY_REPLAY_LEN]; /* Replay Counter */
+ unsigned char nonce[EAPOL_WPA_KEY_NONCE_LEN]; /* Nonce */
+ unsigned char iv[EAPOL_WPA_KEY_IV_LEN]; /* Key IV */
+ unsigned char rsc[EAPOL_WPA_KEY_RSC_LEN]; /* Key RSC */
+ unsigned char id[EAPOL_WPA_KEY_ID_LEN]; /* WPA:Key ID, 802.11i/WPA2: Reserved */
+ unsigned char mic[EAPOL_WPA_KEY_MIC_LEN]; /* Key MIC */
+ unsigned short data_len; /* Key Data Length */
+ unsigned char data[EAPOL_WPA_KEY_DATA_LEN]; /* Key data */
+} BWL_POST_PACKED_STRUCT eapol_wpa_key_header_t;
+
+#define EAPOL_WPA_KEY_LEN 95
+
+/* WPA/802.11i/WPA2 KEY KEY_INFO bits */
+#define WPA_KEY_DESC_V1 0x01
+#define WPA_KEY_DESC_V2 0x02
+#define WPA_KEY_PAIRWISE 0x08
+#define WPA_KEY_INSTALL 0x40
+#define WPA_KEY_ACK 0x80
+#define WPA_KEY_MIC 0x100
+#define WPA_KEY_SECURE 0x200
+#define WPA_KEY_ERROR 0x400
+#define WPA_KEY_REQ 0x800
+
+/* WPA-only KEY KEY_INFO bits */
+#define WPA_KEY_INDEX_0 0x00
+#define WPA_KEY_INDEX_1 0x10
+#define WPA_KEY_INDEX_2 0x20
+#define WPA_KEY_INDEX_3 0x30
+#define WPA_KEY_INDEX_MASK 0x30
+#define WPA_KEY_INDEX_SHIFT 0x04
+
+/* 802.11i/WPA2-only KEY KEY_INFO bits */
+#define WPA_KEY_ENCRYPTED_DATA 0x1000
+
+/* Key Data encapsulation */
+typedef BWL_PRE_PACKED_STRUCT struct {
+ uint8 type;
+ uint8 length;
+ uint8 oui[3];
+ uint8 subtype;
+ uint8 data[1];
+} BWL_POST_PACKED_STRUCT eapol_wpa2_encap_data_t;
+
+#define EAPOL_WPA2_ENCAP_DATA_HDR_LEN 6
+
+#define WPA2_KEY_DATA_SUBTYPE_GTK 1
+#define WPA2_KEY_DATA_SUBTYPE_STAKEY 2
+#define WPA2_KEY_DATA_SUBTYPE_MAC 3
+#define WPA2_KEY_DATA_SUBTYPE_PMKID 4
+
+/* GTK encapsulation */
+typedef BWL_PRE_PACKED_STRUCT struct {
+ uint8 flags;
+ uint8 reserved;
+ uint8 gtk[EAPOL_WPA_MAX_KEY_SIZE];
+} BWL_POST_PACKED_STRUCT eapol_wpa2_key_gtk_encap_t;
+
+#define EAPOL_WPA2_KEY_GTK_ENCAP_HDR_LEN 2
+
+#define WPA2_GTK_INDEX_MASK 0x03
+#define WPA2_GTK_INDEX_SHIFT 0x00
+
+#define WPA2_GTK_TRANSMIT 0x04
+
+/* STAKey encapsulation */
+typedef BWL_PRE_PACKED_STRUCT struct {
+ uint8 reserved[2];
+ uint8 mac[ETHER_ADDR_LEN];
+ uint8 stakey[EAPOL_WPA_MAX_KEY_SIZE];
+} BWL_POST_PACKED_STRUCT eapol_wpa2_key_stakey_encap_t;
+
+#define WPA2_KEY_DATA_PAD 0xdd
+
+
+/* This marks the end of a packed structure section. */
+#include <packed_section_end.h>
+
+#endif /* _eapol_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/proto/ethernet.h b/drivers/net/wireless/bcm4329/include/proto/ethernet.h
new file mode 100644
index 00000000000..9ad2ea0c70f
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/ethernet.h
@@ -0,0 +1,148 @@
+/*
+ * From FreeBSD 2.2.7: Fundamental constants relating to ethernet.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: ethernet.h,v 9.45.56.5 2010/02/22 22:04:36 Exp $
+ */
+
+
+#ifndef _NET_ETHERNET_H_
+#define _NET_ETHERNET_H_
+
+#ifndef _TYPEDEFS_H_
+#include "typedefs.h"
+#endif
+
+
+#include <packed_section_start.h>
+
+
+
+#define ETHER_ADDR_LEN 6
+
+
+#define ETHER_TYPE_LEN 2
+
+
+#define ETHER_CRC_LEN 4
+
+
+#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
+
+
+#define ETHER_MIN_LEN 64
+
+
+#define ETHER_MIN_DATA 46
+
+
+#define ETHER_MAX_LEN 1518
+
+
+#define ETHER_MAX_DATA 1500
+
+
+#define ETHER_TYPE_MIN 0x0600
+#define ETHER_TYPE_IP 0x0800
+#define ETHER_TYPE_ARP 0x0806
+#define ETHER_TYPE_8021Q 0x8100
+#define ETHER_TYPE_BRCM 0x886c
+#define ETHER_TYPE_802_1X 0x888e
+#define ETHER_TYPE_WAI 0x88b4
+#ifdef BCMWPA2
+#define ETHER_TYPE_802_1X_PREAUTH 0x88c7
+#endif
+
+
+#define ETHER_BRCM_SUBTYPE_LEN 4
+#define ETHER_BRCM_CRAM 1
+
+
+#define ETHER_DEST_OFFSET (0 * ETHER_ADDR_LEN)
+#define ETHER_SRC_OFFSET (1 * ETHER_ADDR_LEN)
+#define ETHER_TYPE_OFFSET (2 * ETHER_ADDR_LEN)
+
+
+#define ETHER_IS_VALID_LEN(foo) \
+ ((foo) >= ETHER_MIN_LEN && (foo) <= ETHER_MAX_LEN)
+
+
+#ifndef __INCif_etherh
+
+BWL_PRE_PACKED_STRUCT struct ether_header {
+ uint8 ether_dhost[ETHER_ADDR_LEN];
+ uint8 ether_shost[ETHER_ADDR_LEN];
+ uint16 ether_type;
+} BWL_POST_PACKED_STRUCT;
+
+
+BWL_PRE_PACKED_STRUCT struct ether_addr {
+ uint8 octet[ETHER_ADDR_LEN];
+} BWL_POST_PACKED_STRUCT;
+#endif
+
+
+#define ETHER_SET_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] | 2))
+#define ETHER_IS_LOCALADDR(ea) (((uint8 *)(ea))[0] & 2)
+#define ETHER_CLR_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] & 0xd))
+#define ETHER_TOGGLE_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] ^ 2))
+
+
+#define ETHER_SET_UNICAST(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] & ~1))
+
+
+#define ETHER_ISMULTI(ea) (((const uint8 *)(ea))[0] & 1)
+
+
+
+#define ether_cmp(a, b) (!(((short*)a)[0] == ((short*)b)[0]) | \
+ !(((short*)a)[1] == ((short*)b)[1]) | \
+ !(((short*)a)[2] == ((short*)b)[2]))
+
+
+#define ether_copy(s, d) { \
+ ((short*)d)[0] = ((short*)s)[0]; \
+ ((short*)d)[1] = ((short*)s)[1]; \
+ ((short*)d)[2] = ((short*)s)[2]; }
+
+
+static const struct ether_addr ether_bcast = {{255, 255, 255, 255, 255, 255}};
+static const struct ether_addr ether_null = {{0, 0, 0, 0, 0, 0}};
+
+#define ETHER_ISBCAST(ea) ((((uint8 *)(ea))[0] & \
+ ((uint8 *)(ea))[1] & \
+ ((uint8 *)(ea))[2] & \
+ ((uint8 *)(ea))[3] & \
+ ((uint8 *)(ea))[4] & \
+ ((uint8 *)(ea))[5]) == 0xff)
+#define ETHER_ISNULLADDR(ea) ((((uint8 *)(ea))[0] | \
+ ((uint8 *)(ea))[1] | \
+ ((uint8 *)(ea))[2] | \
+ ((uint8 *)(ea))[3] | \
+ ((uint8 *)(ea))[4] | \
+ ((uint8 *)(ea))[5]) == 0)
+
+
+
+#include <packed_section_end.h>
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/proto/sdspi.h b/drivers/net/wireless/bcm4329/include/proto/sdspi.h
new file mode 100644
index 00000000000..7739e68a244
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/sdspi.h
@@ -0,0 +1,71 @@
+/*
+ * SD-SPI Protocol Standard
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sdspi.h,v 9.1.20.1 2008/05/06 22:59:19 Exp $
+ */
+
+#define SPI_START_M BITFIELD_MASK(1) /* Bit [31] - Start Bit */
+#define SPI_START_S 31
+#define SPI_DIR_M BITFIELD_MASK(1) /* Bit [30] - Direction */
+#define SPI_DIR_S 30
+#define SPI_CMD_INDEX_M BITFIELD_MASK(6) /* Bits [29:24] - Command number */
+#define SPI_CMD_INDEX_S 24
+#define SPI_RW_M BITFIELD_MASK(1) /* Bit [23] - Read=0, Write=1 */
+#define SPI_RW_S 23
+#define SPI_FUNC_M BITFIELD_MASK(3) /* Bits [22:20] - Function Number */
+#define SPI_FUNC_S 20
+#define SPI_RAW_M BITFIELD_MASK(1) /* Bit [19] - Read After Wr */
+#define SPI_RAW_S 19
+#define SPI_STUFF_M BITFIELD_MASK(1) /* Bit [18] - Stuff bit */
+#define SPI_STUFF_S 18
+#define SPI_BLKMODE_M BITFIELD_MASK(1) /* Bit [19] - Blockmode 1=blk */
+#define SPI_BLKMODE_S 19
+#define SPI_OPCODE_M BITFIELD_MASK(1) /* Bit [18] - OP Code */
+#define SPI_OPCODE_S 18
+#define SPI_ADDR_M BITFIELD_MASK(17) /* Bits [17:1] - Address */
+#define SPI_ADDR_S 1
+#define SPI_STUFF0_M BITFIELD_MASK(1) /* Bit [0] - Stuff bit */
+#define SPI_STUFF0_S 0
+
+#define SPI_RSP_START_M BITFIELD_MASK(1) /* Bit [7] - Start Bit (always 0) */
+#define SPI_RSP_START_S 7
+#define SPI_RSP_PARAM_ERR_M BITFIELD_MASK(1) /* Bit [6] - Parameter Error */
+#define SPI_RSP_PARAM_ERR_S 6
+#define SPI_RSP_RFU5_M BITFIELD_MASK(1) /* Bit [5] - RFU (Always 0) */
+#define SPI_RSP_RFU5_S 5
+#define SPI_RSP_FUNC_ERR_M BITFIELD_MASK(1) /* Bit [4] - Function number error */
+#define SPI_RSP_FUNC_ERR_S 4
+#define SPI_RSP_CRC_ERR_M BITFIELD_MASK(1) /* Bit [3] - COM CRC Error */
+#define SPI_RSP_CRC_ERR_S 3
+#define SPI_RSP_ILL_CMD_M BITFIELD_MASK(1) /* Bit [2] - Illegal Command error */
+#define SPI_RSP_ILL_CMD_S 2
+#define SPI_RSP_RFU1_M BITFIELD_MASK(1) /* Bit [1] - RFU (Always 0) */
+#define SPI_RSP_RFU1_S 1
+#define SPI_RSP_IDLE_M BITFIELD_MASK(1) /* Bit [0] - In idle state */
+#define SPI_RSP_IDLE_S 0
+
+/* SD-SPI Protocol Definitions */
+#define SDSPI_COMMAND_LEN 6 /* Number of bytes in an SD command */
+#define SDSPI_START_BLOCK 0xFE /* SD Start Block Token */
+#define SDSPI_IDLE_PAD 0xFF /* SD-SPI idle value for MOSI */
+#define SDSPI_START_BIT_MASK 0x80
diff --git a/drivers/net/wireless/bcm4329/include/proto/vlan.h b/drivers/net/wireless/bcm4329/include/proto/vlan.h
new file mode 100644
index 00000000000..670bc44c6bd
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/vlan.h
@@ -0,0 +1,63 @@
+/*
+ * 802.1Q VLAN protocol definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: vlan.h,v 9.4.196.2 2008/12/07 21:19:20 Exp $
+ */
+
+
+#ifndef _vlan_h_
+#define _vlan_h_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+
+
+#include <packed_section_start.h>
+
+#define VLAN_VID_MASK 0xfff
+#define VLAN_CFI_SHIFT 12
+#define VLAN_PRI_SHIFT 13
+
+#define VLAN_PRI_MASK 7
+
+#define VLAN_TAG_LEN 4
+#define VLAN_TAG_OFFSET (2 * ETHER_ADDR_LEN)
+
+#define VLAN_TPID 0x8100
+
+struct ethervlan_header {
+ uint8 ether_dhost[ETHER_ADDR_LEN];
+ uint8 ether_shost[ETHER_ADDR_LEN];
+ uint16 vlan_type;
+ uint16 vlan_tag;
+ uint16 ether_type;
+};
+
+#define ETHERVLAN_HDR_LEN (ETHER_HDR_LEN + VLAN_TAG_LEN)
+
+
+
+#include <packed_section_end.h>
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/proto/wpa.h b/drivers/net/wireless/bcm4329/include/proto/wpa.h
new file mode 100644
index 00000000000..f5d0cd53977
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/proto/wpa.h
@@ -0,0 +1,159 @@
+/*
+ * Fundamental types and constants relating to WPA
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: wpa.h,v 1.16.166.1.20.1 2008/11/20 00:51:31 Exp $
+ */
+
+
+#ifndef _proto_wpa_h_
+#define _proto_wpa_h_
+
+#include <typedefs.h>
+#include <proto/ethernet.h>
+
+
+
+#include <packed_section_start.h>
+
+
+
+
+#define DOT11_RC_INVALID_WPA_IE 13
+#define DOT11_RC_MIC_FAILURE 14
+#define DOT11_RC_4WH_TIMEOUT 15
+#define DOT11_RC_GTK_UPDATE_TIMEOUT 16
+#define DOT11_RC_WPA_IE_MISMATCH 17
+#define DOT11_RC_INVALID_MC_CIPHER 18
+#define DOT11_RC_INVALID_UC_CIPHER 19
+#define DOT11_RC_INVALID_AKMP 20
+#define DOT11_RC_BAD_WPA_VERSION 21
+#define DOT11_RC_INVALID_WPA_CAP 22
+#define DOT11_RC_8021X_AUTH_FAIL 23
+
+#define WPA2_PMKID_LEN 16
+
+
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+ uint8 tag;
+ uint8 length;
+ uint8 oui[3];
+ uint8 oui_type;
+ BWL_PRE_PACKED_STRUCT struct {
+ uint8 low;
+ uint8 high;
+ } BWL_POST_PACKED_STRUCT version;
+} BWL_POST_PACKED_STRUCT wpa_ie_fixed_t;
+#define WPA_IE_OUITYPE_LEN 4
+#define WPA_IE_FIXED_LEN 8
+#define WPA_IE_TAG_FIXED_LEN 6
+
+typedef BWL_PRE_PACKED_STRUCT struct {
+ uint8 tag;
+ uint8 length;
+ BWL_PRE_PACKED_STRUCT struct {
+ uint8 low;
+ uint8 high;
+ } BWL_POST_PACKED_STRUCT version;
+} BWL_POST_PACKED_STRUCT wpa_rsn_ie_fixed_t;
+#define WPA_RSN_IE_FIXED_LEN 4
+#define WPA_RSN_IE_TAG_FIXED_LEN 2
+typedef uint8 wpa_pmkid_t[WPA2_PMKID_LEN];
+
+
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+ uint8 oui[3];
+ uint8 type;
+} BWL_POST_PACKED_STRUCT wpa_suite_t, wpa_suite_mcast_t;
+#define WPA_SUITE_LEN 4
+
+
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+ BWL_PRE_PACKED_STRUCT struct {
+ uint8 low;
+ uint8 high;
+ } BWL_POST_PACKED_STRUCT count;
+ wpa_suite_t list[1];
+} BWL_POST_PACKED_STRUCT wpa_suite_ucast_t, wpa_suite_auth_key_mgmt_t;
+#define WPA_IE_SUITE_COUNT_LEN 2
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+ BWL_PRE_PACKED_STRUCT struct {
+ uint8 low;
+ uint8 high;
+ } BWL_POST_PACKED_STRUCT count;
+ wpa_pmkid_t list[1];
+} BWL_POST_PACKED_STRUCT wpa_pmkid_list_t;
+
+
+#define WPA_CIPHER_NONE 0
+#define WPA_CIPHER_WEP_40 1
+#define WPA_CIPHER_TKIP 2
+#define WPA_CIPHER_AES_OCB 3
+#define WPA_CIPHER_AES_CCM 4
+#define WPA_CIPHER_WEP_104 5
+
+#define IS_WPA_CIPHER(cipher) ((cipher) == WPA_CIPHER_NONE || \
+ (cipher) == WPA_CIPHER_WEP_40 || \
+ (cipher) == WPA_CIPHER_WEP_104 || \
+ (cipher) == WPA_CIPHER_TKIP || \
+ (cipher) == WPA_CIPHER_AES_OCB || \
+ (cipher) == WPA_CIPHER_AES_CCM)
+
+
+#define WPA_TKIP_CM_DETECT 60
+#define WPA_TKIP_CM_BLOCK 60
+
+
+#define RSN_CAP_LEN 2
+
+
+#define RSN_CAP_PREAUTH 0x0001
+#define RSN_CAP_NOPAIRWISE 0x0002
+#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
+#define RSN_CAP_PTK_REPLAY_CNTR_SHIFT 2
+#define RSN_CAP_GTK_REPLAY_CNTR_MASK 0x0030
+#define RSN_CAP_GTK_REPLAY_CNTR_SHIFT 4
+#define RSN_CAP_1_REPLAY_CNTR 0
+#define RSN_CAP_2_REPLAY_CNTRS 1
+#define RSN_CAP_4_REPLAY_CNTRS 2
+#define RSN_CAP_16_REPLAY_CNTRS 3
+
+
+#define WPA_CAP_4_REPLAY_CNTRS RSN_CAP_4_REPLAY_CNTRS
+#define WPA_CAP_16_REPLAY_CNTRS RSN_CAP_16_REPLAY_CNTRS
+#define WPA_CAP_REPLAY_CNTR_SHIFT RSN_CAP_PTK_REPLAY_CNTR_SHIFT
+#define WPA_CAP_REPLAY_CNTR_MASK RSN_CAP_PTK_REPLAY_CNTR_MASK
+
+
+#define WPA_CAP_LEN RSN_CAP_LEN
+
+#define WPA_CAP_WPA2_PREAUTH RSN_CAP_PREAUTH
+
+
+
+#include <packed_section_end.h>
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/sbchipc.h b/drivers/net/wireless/bcm4329/include/sbchipc.h
new file mode 100644
index 00000000000..39e5c8d6aed
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbchipc.h
@@ -0,0 +1,1026 @@
+/*
+ * SiliconBackplane Chipcommon core hardware definitions.
+ *
+ * The chipcommon core provides chip identification, SB control,
+ * jtag, 0/1/2 uarts, clock frequency control, a watchdog interrupt timer,
+ * gpio interface, extbus, and support for serial and parallel flashes.
+ *
+ * $Id: sbchipc.h,v 13.103.2.5.4.5.2.9 2009/07/03 14:23:21 Exp $
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ */
+
+
+#ifndef _SBCHIPC_H
+#define _SBCHIPC_H
+
+#ifndef _LANGUAGE_ASSEMBLY
+
+
+#ifndef PAD
+#define _PADLINE(line) pad ## line
+#define _XSTR(line) _PADLINE(line)
+#define PAD _XSTR(__LINE__)
+#endif
+
+typedef volatile struct {
+ uint32 chipid;
+ uint32 capabilities;
+ uint32 corecontrol;
+ uint32 bist;
+
+
+ uint32 otpstatus;
+ uint32 otpcontrol;
+ uint32 otpprog;
+ uint32 PAD;
+
+
+ uint32 intstatus;
+ uint32 intmask;
+ uint32 chipcontrol;
+ uint32 chipstatus;
+
+
+ uint32 jtagcmd;
+ uint32 jtagir;
+ uint32 jtagdr;
+ uint32 jtagctrl;
+
+
+ uint32 flashcontrol;
+ uint32 flashaddress;
+ uint32 flashdata;
+ uint32 PAD[1];
+
+
+ uint32 broadcastaddress;
+ uint32 broadcastdata;
+
+
+ uint32 gpiopullup;
+ uint32 gpiopulldown;
+ uint32 gpioin;
+ uint32 gpioout;
+ uint32 gpioouten;
+ uint32 gpiocontrol;
+ uint32 gpiointpolarity;
+ uint32 gpiointmask;
+
+
+ uint32 gpioevent;
+ uint32 gpioeventintmask;
+
+
+ uint32 watchdog;
+
+
+ uint32 gpioeventintpolarity;
+
+
+ uint32 gpiotimerval;
+ uint32 gpiotimeroutmask;
+
+
+ uint32 clockcontrol_n;
+ uint32 clockcontrol_sb;
+ uint32 clockcontrol_pci;
+ uint32 clockcontrol_m2;
+ uint32 clockcontrol_m3;
+ uint32 clkdiv;
+ uint32 PAD[2];
+
+
+ uint32 pll_on_delay;
+ uint32 fref_sel_delay;
+ uint32 slow_clk_ctl;
+ uint32 PAD[1];
+
+
+ uint32 system_clk_ctl;
+ uint32 clkstatestretch;
+ uint32 PAD[13];
+
+
+ uint32 eromptr;
+
+
+ uint32 pcmcia_config;
+ uint32 pcmcia_memwait;
+ uint32 pcmcia_attrwait;
+ uint32 pcmcia_iowait;
+ uint32 ide_config;
+ uint32 ide_memwait;
+ uint32 ide_attrwait;
+ uint32 ide_iowait;
+ uint32 prog_config;
+ uint32 prog_waitcount;
+ uint32 flash_config;
+ uint32 flash_waitcount;
+ uint32 PAD[4];
+ uint32 PAD[40];
+
+
+
+ uint32 clk_ctl_st;
+ uint32 hw_war;
+ uint32 PAD[70];
+
+
+ uint8 uart0data;
+ uint8 uart0imr;
+ uint8 uart0fcr;
+ uint8 uart0lcr;
+ uint8 uart0mcr;
+ uint8 uart0lsr;
+ uint8 uart0msr;
+ uint8 uart0scratch;
+ uint8 PAD[248];
+
+ uint8 uart1data;
+ uint8 uart1imr;
+ uint8 uart1fcr;
+ uint8 uart1lcr;
+ uint8 uart1mcr;
+ uint8 uart1lsr;
+ uint8 uart1msr;
+ uint8 uart1scratch;
+ uint32 PAD[126];
+
+
+ uint32 pmucontrol;
+ uint32 pmucapabilities;
+ uint32 pmustatus;
+ uint32 res_state;
+ uint32 res_pending;
+ uint32 pmutimer;
+ uint32 min_res_mask;
+ uint32 max_res_mask;
+ uint32 res_table_sel;
+ uint32 res_dep_mask;
+ uint32 res_updn_timer;
+ uint32 res_timer;
+ uint32 clkstretch;
+ uint32 pmuwatchdog;
+ uint32 gpiosel;
+ uint32 gpioenable;
+ uint32 res_req_timer_sel;
+ uint32 res_req_timer;
+ uint32 res_req_mask;
+ uint32 PAD;
+ uint32 chipcontrol_addr;
+ uint32 chipcontrol_data;
+ uint32 regcontrol_addr;
+ uint32 regcontrol_data;
+ uint32 pllcontrol_addr;
+ uint32 pllcontrol_data;
+ uint32 PAD[102];
+ uint16 otp[768];
+} chipcregs_t;
+
+#endif
+
+#define CC_CHIPID 0
+#define CC_CAPABILITIES 4
+#define CC_OTPST 0x10
+#define CC_CHIPST 0x2c
+#define CC_JTAGCMD 0x30
+#define CC_JTAGIR 0x34
+#define CC_JTAGDR 0x38
+#define CC_JTAGCTRL 0x3c
+#define CC_WATCHDOG 0x80
+#define CC_CLKC_N 0x90
+#define CC_CLKC_M0 0x94
+#define CC_CLKC_M1 0x98
+#define CC_CLKC_M2 0x9c
+#define CC_CLKC_M3 0xa0
+#define CC_CLKDIV 0xa4
+#define CC_SYS_CLK_CTL 0xc0
+#define CC_CLK_CTL_ST SI_CLK_CTL_ST
+#define CC_EROMPTR 0xfc
+#define PMU_CTL 0x600
+#define PMU_CAP 0x604
+#define PMU_ST 0x608
+#define PMU_RES_STATE 0x60c
+#define PMU_TIMER 0x614
+#define PMU_MIN_RES_MASK 0x618
+#define PMU_MAX_RES_MASK 0x61c
+#define PMU_REG_CONTROL_ADDR 0x658
+#define PMU_REG_CONTROL_DATA 0x65C
+#define PMU_PLL_CONTROL_ADDR 0x660
+#define PMU_PLL_CONTROL_DATA 0x664
+#define CC_OTP 0x800
+
+
+#define CID_ID_MASK 0x0000ffff
+#define CID_REV_MASK 0x000f0000
+#define CID_REV_SHIFT 16
+#define CID_PKG_MASK 0x00f00000
+#define CID_PKG_SHIFT 20
+#define CID_CC_MASK 0x0f000000
+#define CID_CC_SHIFT 24
+#define CID_TYPE_MASK 0xf0000000
+#define CID_TYPE_SHIFT 28
+
+
+#define CC_CAP_UARTS_MASK 0x00000003
+#define CC_CAP_MIPSEB 0x00000004
+#define CC_CAP_UCLKSEL 0x00000018
+#define CC_CAP_UINTCLK 0x00000008
+#define CC_CAP_UARTGPIO 0x00000020
+#define CC_CAP_EXTBUS_MASK 0x000000c0
+#define CC_CAP_EXTBUS_NONE 0x00000000
+#define CC_CAP_EXTBUS_FULL 0x00000040
+#define CC_CAP_EXTBUS_PROG 0x00000080
+#define CC_CAP_FLASH_MASK 0x00000700
+#define CC_CAP_PLL_MASK 0x00038000
+#define CC_CAP_PWR_CTL 0x00040000
+#define CC_CAP_OTPSIZE 0x00380000
+#define CC_CAP_OTPSIZE_SHIFT 19
+#define CC_CAP_OTPSIZE_BASE 5
+#define CC_CAP_JTAGP 0x00400000
+#define CC_CAP_ROM 0x00800000
+#define CC_CAP_BKPLN64 0x08000000
+#define CC_CAP_PMU 0x10000000
+#define CC_CAP_ECI 0x20000000
+
+
+#define PLL_NONE 0x00000000
+#define PLL_TYPE1 0x00010000
+#define PLL_TYPE2 0x00020000
+#define PLL_TYPE3 0x00030000
+#define PLL_TYPE4 0x00008000
+#define PLL_TYPE5 0x00018000
+#define PLL_TYPE6 0x00028000
+#define PLL_TYPE7 0x00038000
+
+
+#define ILP_CLOCK 32000
+
+
+#define ALP_CLOCK 20000000
+
+
+#define HT_CLOCK 80000000
+
+
+#define CC_UARTCLKO 0x00000001
+#define CC_SE 0x00000002
+#define CC_UARTCLKEN 0x00000008
+
+
+#define CHIPCTRL_4321A0_DEFAULT 0x3a4
+#define CHIPCTRL_4321A1_DEFAULT 0x0a4
+#define CHIPCTRL_4321_PLL_DOWN 0x800000
+
+
+#define OTPS_OL_MASK 0x000000ff
+#define OTPS_OL_MFG 0x00000001
+#define OTPS_OL_OR1 0x00000002
+#define OTPS_OL_OR2 0x00000004
+#define OTPS_OL_GU 0x00000008
+#define OTPS_GUP_MASK 0x00000f00
+#define OTPS_GUP_SHIFT 8
+#define OTPS_GUP_HW 0x00000100
+#define OTPS_GUP_SW 0x00000200
+#define OTPS_GUP_CI 0x00000400
+#define OTPS_GUP_FUSE 0x00000800
+#define OTPS_READY 0x00001000
+#define OTPS_RV(x) (1 << (16 + (x)))
+#define OTPS_RV_MASK 0x0fff0000
+
+
+#define OTPC_PROGSEL 0x00000001
+#define OTPC_PCOUNT_MASK 0x0000000e
+#define OTPC_PCOUNT_SHIFT 1
+#define OTPC_VSEL_MASK 0x000000f0
+#define OTPC_VSEL_SHIFT 4
+#define OTPC_TMM_MASK 0x00000700
+#define OTPC_TMM_SHIFT 8
+#define OTPC_ODM 0x00000800
+#define OTPC_PROGEN 0x80000000
+
+
+#define OTPP_COL_MASK 0x000000ff
+#define OTPP_COL_SHIFT 0
+#define OTPP_ROW_MASK 0x0000ff00
+#define OTPP_ROW_SHIFT 8
+#define OTPP_OC_MASK 0x0f000000
+#define OTPP_OC_SHIFT 24
+#define OTPP_READERR 0x10000000
+#define OTPP_VALUE_MASK 0x20000000
+#define OTPP_VALUE_SHIFT 29
+#define OTPP_START_BUSY 0x80000000
+
+
+#define OTPPOC_READ 0
+#define OTPPOC_BIT_PROG 1
+#define OTPPOC_VERIFY 3
+#define OTPPOC_INIT 4
+#define OTPPOC_SET 5
+#define OTPPOC_RESET 6
+#define OTPPOC_OCST 7
+#define OTPPOC_ROW_LOCK 8
+#define OTPPOC_PRESCN_TEST 9
+
+
+#define JCMD_START 0x80000000
+#define JCMD_BUSY 0x80000000
+#define JCMD_STATE_MASK 0x60000000
+#define JCMD_STATE_TLR 0x00000000
+#define JCMD_STATE_PIR 0x20000000
+#define JCMD_STATE_PDR 0x40000000
+#define JCMD_STATE_RTI 0x60000000
+#define JCMD0_ACC_MASK 0x0000f000
+#define JCMD0_ACC_IRDR 0x00000000
+#define JCMD0_ACC_DR 0x00001000
+#define JCMD0_ACC_IR 0x00002000
+#define JCMD0_ACC_RESET 0x00003000
+#define JCMD0_ACC_IRPDR 0x00004000
+#define JCMD0_ACC_PDR 0x00005000
+#define JCMD0_IRW_MASK 0x00000f00
+#define JCMD_ACC_MASK 0x000f0000
+#define JCMD_ACC_IRDR 0x00000000
+#define JCMD_ACC_DR 0x00010000
+#define JCMD_ACC_IR 0x00020000
+#define JCMD_ACC_RESET 0x00030000
+#define JCMD_ACC_IRPDR 0x00040000
+#define JCMD_ACC_PDR 0x00050000
+#define JCMD_ACC_PIR 0x00060000
+#define JCMD_ACC_IRDR_I 0x00070000
+#define JCMD_ACC_DR_I 0x00080000
+#define JCMD_IRW_MASK 0x00001f00
+#define JCMD_IRW_SHIFT 8
+#define JCMD_DRW_MASK 0x0000003f
+
+
+#define JCTRL_FORCE_CLK 4
+#define JCTRL_EXT_EN 2
+#define JCTRL_EN 1
+
+
+#define CLKD_SFLASH 0x0f000000
+#define CLKD_SFLASH_SHIFT 24
+#define CLKD_OTP 0x000f0000
+#define CLKD_OTP_SHIFT 16
+#define CLKD_JTAG 0x00000f00
+#define CLKD_JTAG_SHIFT 8
+#define CLKD_UART 0x000000ff
+
+
+#define CI_GPIO 0x00000001
+#define CI_EI 0x00000002
+#define CI_TEMP 0x00000004
+#define CI_SIRQ 0x00000008
+#define CI_ECI 0x00000010
+#define CI_PMU 0x00000020
+#define CI_UART 0x00000040
+#define CI_WDRESET 0x80000000
+
+
+#define SCC_SS_MASK 0x00000007
+#define SCC_SS_LPO 0x00000000
+#define SCC_SS_XTAL 0x00000001
+#define SCC_SS_PCI 0x00000002
+#define SCC_LF 0x00000200
+#define SCC_LP 0x00000400
+#define SCC_FS 0x00000800
+#define SCC_IP 0x00001000
+#define SCC_XC 0x00002000
+#define SCC_XP 0x00004000
+#define SCC_CD_MASK 0xffff0000
+#define SCC_CD_SHIFT 16
+
+
+#define SYCC_IE 0x00000001
+#define SYCC_AE 0x00000002
+#define SYCC_FP 0x00000004
+#define SYCC_AR 0x00000008
+#define SYCC_HR 0x00000010
+#define SYCC_CD_MASK 0xffff0000
+#define SYCC_CD_SHIFT 16
+
+
+#define CF_EN 0x00000001
+#define CF_EM_MASK 0x0000000e
+#define CF_EM_SHIFT 1
+#define CF_EM_FLASH 0
+#define CF_EM_SYNC 2
+#define CF_EM_PCMCIA 4
+#define CF_DS 0x00000010
+#define CF_BS 0x00000020
+#define CF_CD_MASK 0x000000c0
+#define CF_CD_SHIFT 6
+#define CF_CD_DIV2 0x00000000
+#define CF_CD_DIV3 0x00000040
+#define CF_CD_DIV4 0x00000080
+#define CF_CE 0x00000100
+#define CF_SB 0x00000200
+
+
+#define PM_W0_MASK 0x0000003f
+#define PM_W1_MASK 0x00001f00
+#define PM_W1_SHIFT 8
+#define PM_W2_MASK 0x001f0000
+#define PM_W2_SHIFT 16
+#define PM_W3_MASK 0x1f000000
+#define PM_W3_SHIFT 24
+
+
+#define PA_W0_MASK 0x0000003f
+#define PA_W1_MASK 0x00001f00
+#define PA_W1_SHIFT 8
+#define PA_W2_MASK 0x001f0000
+#define PA_W2_SHIFT 16
+#define PA_W3_MASK 0x1f000000
+#define PA_W3_SHIFT 24
+
+
+#define PI_W0_MASK 0x0000003f
+#define PI_W1_MASK 0x00001f00
+#define PI_W1_SHIFT 8
+#define PI_W2_MASK 0x001f0000
+#define PI_W2_SHIFT 16
+#define PI_W3_MASK 0x1f000000
+#define PI_W3_SHIFT 24
+
+
+#define PW_W0_MASK 0x0000001f
+#define PW_W1_MASK 0x00001f00
+#define PW_W1_SHIFT 8
+#define PW_W2_MASK 0x001f0000
+#define PW_W2_SHIFT 16
+#define PW_W3_MASK 0x1f000000
+#define PW_W3_SHIFT 24
+
+#define PW_W0 0x0000000c
+#define PW_W1 0x00000a00
+#define PW_W2 0x00020000
+#define PW_W3 0x01000000
+
+
+#define FW_W0_MASK 0x0000003f
+#define FW_W1_MASK 0x00001f00
+#define FW_W1_SHIFT 8
+#define FW_W2_MASK 0x001f0000
+#define FW_W2_SHIFT 16
+#define FW_W3_MASK 0x1f000000
+#define FW_W3_SHIFT 24
+
+
+#define WATCHDOG_CLOCK 48000000
+#define WATCHDOG_CLOCK_5354 32000
+
+
+#define PCTL_ILP_DIV_MASK 0xffff0000
+#define PCTL_ILP_DIV_SHIFT 16
+#define PCTL_PLL_PLLCTL_UPD 0x00000400
+#define PCTL_NOILP_ON_WAIT 0x00000200
+#define PCTL_HT_REQ_EN 0x00000100
+#define PCTL_ALP_REQ_EN 0x00000080
+#define PCTL_XTALFREQ_MASK 0x0000007c
+#define PCTL_XTALFREQ_SHIFT 2
+#define PCTL_ILP_DIV_EN 0x00000002
+#define PCTL_LPO_SEL 0x00000001
+
+
+#define CSTRETCH_HT 0xffff0000
+#define CSTRETCH_ALP 0x0000ffff
+
+
+#define GPIO_ONTIME_SHIFT 16
+
+
+#define CN_N1_MASK 0x3f
+#define CN_N2_MASK 0x3f00
+#define CN_N2_SHIFT 8
+#define CN_PLLC_MASK 0xf0000
+#define CN_PLLC_SHIFT 16
+
+
+#define CC_M1_MASK 0x3f
+#define CC_M2_MASK 0x3f00
+#define CC_M2_SHIFT 8
+#define CC_M3_MASK 0x3f0000
+#define CC_M3_SHIFT 16
+#define CC_MC_MASK 0x1f000000
+#define CC_MC_SHIFT 24
+
+
+#define CC_F6_2 0x02
+#define CC_F6_3 0x03
+#define CC_F6_4 0x05
+#define CC_F6_5 0x09
+#define CC_F6_6 0x11
+#define CC_F6_7 0x21
+
+#define CC_F5_BIAS 5
+
+#define CC_MC_BYPASS 0x08
+#define CC_MC_M1 0x04
+#define CC_MC_M1M2 0x02
+#define CC_MC_M1M2M3 0x01
+#define CC_MC_M1M3 0x11
+
+
+#define CC_T2_BIAS 2
+#define CC_T2M2_BIAS 3
+
+#define CC_T2MC_M1BYP 1
+#define CC_T2MC_M2BYP 2
+#define CC_T2MC_M3BYP 4
+
+
+#define CC_T6_MMASK 1
+#define CC_T6_M0 120000000
+#define CC_T6_M1 100000000
+#define SB2MIPS_T6(sb) (2 * (sb))
+
+
+#define CC_CLOCK_BASE1 24000000
+#define CC_CLOCK_BASE2 12500000
+
+
+#define CLKC_5350_N 0x0311
+#define CLKC_5350_M 0x04020009
+
+
+#define FLASH_NONE 0x000
+#define SFLASH_ST 0x100
+#define SFLASH_AT 0x200
+#define PFLASH 0x700
+
+
+#define CC_CFG_EN 0x0001
+#define CC_CFG_EM_MASK 0x000e
+#define CC_CFG_EM_ASYNC 0x0000
+#define CC_CFG_EM_SYNC 0x0002
+#define CC_CFG_EM_PCMCIA 0x0004
+#define CC_CFG_EM_IDE 0x0006
+#define CC_CFG_DS 0x0010
+#define CC_CFG_CD_MASK 0x00e0
+#define CC_CFG_CE 0x0100
+#define CC_CFG_SB 0x0200
+#define CC_CFG_IS 0x0400
+
+
+#define CC_EB_BASE 0x1a000000
+#define CC_EB_PCMCIA_MEM 0x1a000000
+#define CC_EB_PCMCIA_IO 0x1a200000
+#define CC_EB_PCMCIA_CFG 0x1a400000
+#define CC_EB_IDE 0x1a800000
+#define CC_EB_PCMCIA1_MEM 0x1a800000
+#define CC_EB_PCMCIA1_IO 0x1aa00000
+#define CC_EB_PCMCIA1_CFG 0x1ac00000
+#define CC_EB_PROGIF 0x1b000000
+
+
+
+#define SFLASH_OPCODE 0x000000ff
+#define SFLASH_ACTION 0x00000700
+#define SFLASH_CS_ACTIVE 0x00001000
+#define SFLASH_START 0x80000000
+#define SFLASH_BUSY SFLASH_START
+
+
+#define SFLASH_ACT_OPONLY 0x0000
+#define SFLASH_ACT_OP1D 0x0100
+#define SFLASH_ACT_OP3A 0x0200
+#define SFLASH_ACT_OP3A1D 0x0300
+#define SFLASH_ACT_OP3A4D 0x0400
+#define SFLASH_ACT_OP3A4X4D 0x0500
+#define SFLASH_ACT_OP3A1X4D 0x0700
+
+
+#define SFLASH_ST_WREN 0x0006
+#define SFLASH_ST_WRDIS 0x0004
+#define SFLASH_ST_RDSR 0x0105
+#define SFLASH_ST_WRSR 0x0101
+#define SFLASH_ST_READ 0x0303
+#define SFLASH_ST_PP 0x0302
+#define SFLASH_ST_SE 0x02d8
+#define SFLASH_ST_BE 0x00c7
+#define SFLASH_ST_DP 0x00b9
+#define SFLASH_ST_RES 0x03ab
+#define SFLASH_ST_CSA 0x1000
+
+
+#define SFLASH_ST_WIP 0x01
+#define SFLASH_ST_WEL 0x02
+#define SFLASH_ST_BP_MASK 0x1c
+#define SFLASH_ST_BP_SHIFT 2
+#define SFLASH_ST_SRWD 0x80
+
+
+#define SFLASH_AT_READ 0x07e8
+#define SFLASH_AT_PAGE_READ 0x07d2
+#define SFLASH_AT_BUF1_READ
+#define SFLASH_AT_BUF2_READ
+#define SFLASH_AT_STATUS 0x01d7
+#define SFLASH_AT_BUF1_WRITE 0x0384
+#define SFLASH_AT_BUF2_WRITE 0x0387
+#define SFLASH_AT_BUF1_ERASE_PROGRAM 0x0283
+#define SFLASH_AT_BUF2_ERASE_PROGRAM 0x0286
+#define SFLASH_AT_BUF1_PROGRAM 0x0288
+#define SFLASH_AT_BUF2_PROGRAM 0x0289
+#define SFLASH_AT_PAGE_ERASE 0x0281
+#define SFLASH_AT_BLOCK_ERASE 0x0250
+#define SFLASH_AT_BUF1_WRITE_ERASE_PROGRAM 0x0382
+#define SFLASH_AT_BUF2_WRITE_ERASE_PROGRAM 0x0385
+#define SFLASH_AT_BUF1_LOAD 0x0253
+#define SFLASH_AT_BUF2_LOAD 0x0255
+#define SFLASH_AT_BUF1_COMPARE 0x0260
+#define SFLASH_AT_BUF2_COMPARE 0x0261
+#define SFLASH_AT_BUF1_REPROGRAM 0x0258
+#define SFLASH_AT_BUF2_REPROGRAM 0x0259
+
+
+#define SFLASH_AT_READY 0x80
+#define SFLASH_AT_MISMATCH 0x40
+#define SFLASH_AT_ID_MASK 0x38
+#define SFLASH_AT_ID_SHIFT 3
+
+
+
+#define UART_RX 0
+#define UART_TX 0
+#define UART_DLL 0
+#define UART_IER 1
+#define UART_DLM 1
+#define UART_IIR 2
+#define UART_FCR 2
+#define UART_LCR 3
+#define UART_MCR 4
+#define UART_LSR 5
+#define UART_MSR 6
+#define UART_SCR 7
+#define UART_LCR_DLAB 0x80
+#define UART_LCR_WLEN8 0x03
+#define UART_MCR_OUT2 0x08
+#define UART_MCR_LOOP 0x10
+#define UART_LSR_RX_FIFO 0x80
+#define UART_LSR_TDHR 0x40
+#define UART_LSR_THRE 0x20
+#define UART_LSR_BREAK 0x10
+#define UART_LSR_FRAMING 0x08
+#define UART_LSR_PARITY 0x04
+#define UART_LSR_OVERRUN 0x02
+#define UART_LSR_RXRDY 0x01
+#define UART_FCR_FIFO_ENABLE 1
+
+
+#define UART_IIR_FIFO_MASK 0xc0
+#define UART_IIR_INT_MASK 0xf
+#define UART_IIR_MDM_CHG 0x0
+#define UART_IIR_NOINT 0x1
+#define UART_IIR_THRE 0x2
+#define UART_IIR_RCVD_DATA 0x4
+#define UART_IIR_RCVR_STATUS 0x6
+#define UART_IIR_CHAR_TIME 0xc
+
+
+#define UART_IER_EDSSI 8
+#define UART_IER_ELSI 4
+#define UART_IER_ETBEI 2
+#define UART_IER_ERBFI 1
+
+
+#define PST_INTPEND 0x0040
+#define PST_SBCLKST 0x0030
+#define PST_SBCLKST_ILP 0x0010
+#define PST_SBCLKST_ALP 0x0020
+#define PST_SBCLKST_HT 0x0030
+#define PST_ALPAVAIL 0x0008
+#define PST_HTAVAIL 0x0004
+#define PST_RESINIT 0x0003
+
+
+#define PCAP_REV_MASK 0x000000ff
+#define PCAP_RC_MASK 0x00001f00
+#define PCAP_RC_SHIFT 8
+#define PCAP_TC_MASK 0x0001e000
+#define PCAP_TC_SHIFT 13
+#define PCAP_PC_MASK 0x001e0000
+#define PCAP_PC_SHIFT 17
+#define PCAP_VC_MASK 0x01e00000
+#define PCAP_VC_SHIFT 21
+#define PCAP_CC_MASK 0x1e000000
+#define PCAP_CC_SHIFT 25
+#define PCAP5_PC_MASK 0x003e0000
+#define PCAP5_PC_SHIFT 17
+#define PCAP5_VC_MASK 0x07c00000
+#define PCAP5_VC_SHIFT 22
+#define PCAP5_CC_MASK 0xf8000000
+#define PCAP5_CC_SHIFT 27
+
+
+
+#define PRRT_TIME_MASK 0x03ff
+#define PRRT_INTEN 0x0400
+#define PRRT_REQ_ACTIVE 0x0800
+#define PRRT_ALP_REQ 0x1000
+#define PRRT_HT_REQ 0x2000
+
+
+#define PMURES_BIT(bit) (1 << (bit))
+
+
+#define PMURES_MAX_RESNUM 30
+
+
+
+
+#define PMU0_PLL0_PLLCTL0 0
+#define PMU0_PLL0_PC0_PDIV_MASK 1
+#define PMU0_PLL0_PC0_PDIV_FREQ 25000
+#define PMU0_PLL0_PC0_DIV_ARM_MASK 0x00000038
+#define PMU0_PLL0_PC0_DIV_ARM_SHIFT 3
+#define PMU0_PLL0_PC0_DIV_ARM_BASE 8
+
+
+#define PMU0_PLL0_PC0_DIV_ARM_110MHZ 0
+#define PMU0_PLL0_PC0_DIV_ARM_97_7MHZ 1
+#define PMU0_PLL0_PC0_DIV_ARM_88MHZ 2
+#define PMU0_PLL0_PC0_DIV_ARM_80MHZ 3
+#define PMU0_PLL0_PC0_DIV_ARM_73_3MHZ 4
+#define PMU0_PLL0_PC0_DIV_ARM_67_7MHZ 5
+#define PMU0_PLL0_PC0_DIV_ARM_62_9MHZ 6
+#define PMU0_PLL0_PC0_DIV_ARM_58_6MHZ 7
+
+
+#define PMU0_PLL0_PLLCTL1 1
+#define PMU0_PLL0_PC1_WILD_INT_MASK 0xf0000000
+#define PMU0_PLL0_PC1_WILD_INT_SHIFT 28
+#define PMU0_PLL0_PC1_WILD_FRAC_MASK 0x0fffff00
+#define PMU0_PLL0_PC1_WILD_FRAC_SHIFT 8
+#define PMU0_PLL0_PC1_STOP_MOD 0x00000040
+
+
+#define PMU0_PLL0_PLLCTL2 2
+#define PMU0_PLL0_PC2_WILD_INT_MASK 0xf
+#define PMU0_PLL0_PC2_WILD_INT_SHIFT 4
+
+
+#define RES4328_EXT_SWITCHER_PWM 0
+#define RES4328_BB_SWITCHER_PWM 1
+#define RES4328_BB_SWITCHER_BURST 2
+#define RES4328_BB_EXT_SWITCHER_BURST 3
+#define RES4328_ILP_REQUEST 4
+#define RES4328_RADIO_SWITCHER_PWM 5
+#define RES4328_RADIO_SWITCHER_BURST 6
+#define RES4328_ROM_SWITCH 7
+#define RES4328_PA_REF_LDO 8
+#define RES4328_RADIO_LDO 9
+#define RES4328_AFE_LDO 10
+#define RES4328_PLL_LDO 11
+#define RES4328_BG_FILTBYP 12
+#define RES4328_TX_FILTBYP 13
+#define RES4328_RX_FILTBYP 14
+#define RES4328_XTAL_PU 15
+#define RES4328_XTAL_EN 16
+#define RES4328_BB_PLL_FILTBYP 17
+#define RES4328_RF_PLL_FILTBYP 18
+#define RES4328_BB_PLL_PU 19
+
+#define RES5354_EXT_SWITCHER_PWM 0
+#define RES5354_BB_SWITCHER_PWM 1
+#define RES5354_BB_SWITCHER_BURST 2
+#define RES5354_BB_EXT_SWITCHER_BURST 3
+#define RES5354_ILP_REQUEST 4
+#define RES5354_RADIO_SWITCHER_PWM 5
+#define RES5354_RADIO_SWITCHER_BURST 6
+#define RES5354_ROM_SWITCH 7
+#define RES5354_PA_REF_LDO 8
+#define RES5354_RADIO_LDO 9
+#define RES5354_AFE_LDO 10
+#define RES5354_PLL_LDO 11
+#define RES5354_BG_FILTBYP 12
+#define RES5354_TX_FILTBYP 13
+#define RES5354_RX_FILTBYP 14
+#define RES5354_XTAL_PU 15
+#define RES5354_XTAL_EN 16
+#define RES5354_BB_PLL_FILTBYP 17
+#define RES5354_RF_PLL_FILTBYP 18
+#define RES5354_BB_PLL_PU 19
+
+
+
+#define DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT 8
+#define DOT11MAC_880MHZ_CLK_DIVISOR_MASK (0xFF << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT)
+#define DOT11MAC_880MHZ_CLK_DIVISOR_VAL (0xE << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT)
+
+
+#define PMU2_PHY_PLL_PLLCTL 4
+#define PMU2_SI_PLL_PLLCTL 10
+
+
+#define RES4325_BUCK_BOOST_BURST 0
+#define RES4325_CBUCK_BURST 1
+#define RES4325_CBUCK_PWM 2
+#define RES4325_CLDO_CBUCK_BURST 3
+#define RES4325_CLDO_CBUCK_PWM 4
+#define RES4325_BUCK_BOOST_PWM 5
+#define RES4325_ILP_REQUEST 6
+#define RES4325_ABUCK_BURST 7
+#define RES4325_ABUCK_PWM 8
+#define RES4325_LNLDO1_PU 9
+#define RES4325_OTP_PU 10
+#define RES4325_LNLDO3_PU 11
+#define RES4325_LNLDO4_PU 12
+#define RES4325_XTAL_PU 13
+#define RES4325_ALP_AVAIL 14
+#define RES4325_RX_PWRSW_PU 15
+#define RES4325_TX_PWRSW_PU 16
+#define RES4325_RFPLL_PWRSW_PU 17
+#define RES4325_LOGEN_PWRSW_PU 18
+#define RES4325_AFE_PWRSW_PU 19
+#define RES4325_BBPLL_PWRSW_PU 20
+#define RES4325_HT_AVAIL 21
+
+
+#define RES4325B0_CBUCK_LPOM 1
+#define RES4325B0_CBUCK_BURST 2
+#define RES4325B0_CBUCK_PWM 3
+#define RES4325B0_CLDO_PU 4
+
+
+#define RES4325C1_OTP_PWRSW_PU 10
+#define RES4325C1_LNLDO2_PU 12
+
+
+#define CST4325_SPROM_OTP_SEL_MASK 0x00000003
+#define CST4325_DEFCIS_SEL 0
+#define CST4325_SPROM_SEL 1
+#define CST4325_OTP_SEL 2
+#define CST4325_OTP_PWRDN 3
+#define CST4325_SDIO_USB_MODE_MASK 0x00000004
+#define CST4325_SDIO_USB_MODE_SHIFT 2
+#define CST4325_RCAL_VALID_MASK 0x00000008
+#define CST4325_RCAL_VALID_SHIFT 3
+#define CST4325_RCAL_VALUE_MASK 0x000001f0
+#define CST4325_RCAL_VALUE_SHIFT 4
+#define CST4325_PMUTOP_2B_MASK 0x00000200
+#define CST4325_PMUTOP_2B_SHIFT 9
+
+#define RES4329_RESERVED0 0
+#define RES4329_CBUCK_LPOM 1
+#define RES4329_CBUCK_BURST 2
+#define RES4329_CBUCK_PWM 3
+#define RES4329_CLDO_PU 4
+#define RES4329_PALDO_PU 5
+#define RES4329_ILP_REQUEST 6
+#define RES4329_RESERVED7 7
+#define RES4329_RESERVED8 8
+#define RES4329_LNLDO1_PU 9
+#define RES4329_OTP_PU 10
+#define RES4329_RESERVED11 11
+#define RES4329_LNLDO2_PU 12
+#define RES4329_XTAL_PU 13
+#define RES4329_ALP_AVAIL 14
+#define RES4329_RX_PWRSW_PU 15
+#define RES4329_TX_PWRSW_PU 16
+#define RES4329_RFPLL_PWRSW_PU 17
+#define RES4329_LOGEN_PWRSW_PU 18
+#define RES4329_AFE_PWRSW_PU 19
+#define RES4329_BBPLL_PWRSW_PU 20
+#define RES4329_HT_AVAIL 21
+
+#define CST4329_SPROM_OTP_SEL_MASK 0x00000003
+#define CST4329_DEFCIS_SEL 0
+#define CST4329_SPROM_SEL 1
+#define CST4329_OTP_SEL 2
+#define CST4329_OTP_PWRDN 3
+#define CST4329_SPI_SDIO_MODE_MASK 0x00000004
+#define CST4329_SPI_SDIO_MODE_SHIFT 2
+
+
+#define RES4312_SWITCHER_BURST 0
+#define RES4312_SWITCHER_PWM 1
+#define RES4312_PA_REF_LDO 2
+#define RES4312_CORE_LDO_BURST 3
+#define RES4312_CORE_LDO_PWM 4
+#define RES4312_RADIO_LDO 5
+#define RES4312_ILP_REQUEST 6
+#define RES4312_BG_FILTBYP 7
+#define RES4312_TX_FILTBYP 8
+#define RES4312_RX_FILTBYP 9
+#define RES4312_XTAL_PU 10
+#define RES4312_ALP_AVAIL 11
+#define RES4312_BB_PLL_FILTBYP 12
+#define RES4312_RF_PLL_FILTBYP 13
+#define RES4312_HT_AVAIL 14
+
+#define RES4322_RF_LDO 0
+#define RES4322_ILP_REQUEST 1
+#define RES4322_XTAL_PU 2
+#define RES4322_ALP_AVAIL 3
+#define RES4322_SI_PLL_ON 4
+#define RES4322_HT_SI_AVAIL 5
+#define RES4322_PHY_PLL_ON 6
+#define RES4322_HT_PHY_AVAIL 7
+#define RES4322_OTP_PU 8
+
+
+#define CST4322_XTAL_FREQ_20_40MHZ 0x00000020
+#define CST4322_SPROM_OTP_SEL_MASK 0x000000c0
+#define CST4322_SPROM_OTP_SEL_SHIFT 6
+#define CST4322_NO_SPROM_OTP 0
+#define CST4322_SPROM_PRESENT 1
+#define CST4322_OTP_PRESENT 2
+#define CST4322_PCI_OR_USB 0x00000100
+#define CST4322_BOOT_MASK 0x00000600
+#define CST4322_BOOT_SHIFT 9
+#define CST4322_BOOT_FROM_SRAM 0
+#define CST4322_BOOT_FROM_ROM 1
+#define CST4322_BOOT_FROM_FLASH 2
+#define CST4322_BOOT_FROM_INVALID 3
+#define CST4322_ILP_DIV_EN 0x00000800
+#define CST4322_FLASH_TYPE_MASK 0x00001000
+#define CST4322_FLASH_TYPE_SHIFT 12
+#define CST4322_FLASH_TYPE_SHIFT_ST 0
+#define CST4322_FLASH_TYPE_SHIFT_ATMEL 1
+#define CST4322_ARM_TAP_SEL 0x00002000
+#define CST4322_RES_INIT_MODE_MASK 0x0000c000
+#define CST4322_RES_INIT_MODE_SHIFT 14
+#define CST4322_RES_INIT_MODE_ILPAVAIL 0
+#define CST4322_RES_INIT_MODE_ILPREQ 1
+#define CST4322_RES_INIT_MODE_ALPAVAIL 2
+#define CST4322_RES_INIT_MODE_HTAVAIL 3
+#define CST4322_PCIPLLCLK_GATING 0x00010000
+#define CST4322_CLK_SWITCH_PCI_TO_ALP 0x00020000
+#define CST4322_PCI_CARDBUS_MODE 0x00040000
+
+#define RES4315_CBUCK_LPOM 1
+#define RES4315_CBUCK_BURST 2
+#define RES4315_CBUCK_PWM 3
+#define RES4315_CLDO_PU 4
+#define RES4315_PALDO_PU 5
+#define RES4315_ILP_REQUEST 6
+#define RES4315_LNLDO1_PU 9
+#define RES4315_OTP_PU 10
+#define RES4315_LNLDO2_PU 12
+#define RES4315_XTAL_PU 13
+#define RES4315_ALP_AVAIL 14
+#define RES4315_RX_PWRSW_PU 15
+#define RES4315_TX_PWRSW_PU 16
+#define RES4315_RFPLL_PWRSW_PU 17
+#define RES4315_LOGEN_PWRSW_PU 18
+#define RES4315_AFE_PWRSW_PU 19
+#define RES4315_BBPLL_PWRSW_PU 20
+#define RES4315_HT_AVAIL 21
+
+#define CST4315_SPROM_OTP_SEL_MASK 0x00000003
+#define CST4315_DEFCIS_SEL 0x00000000
+#define CST4315_SPROM_SEL 0x00000001
+#define CST4315_OTP_SEL 0x00000002
+#define CST4315_OTP_PWRDN 0x00000003
+#define CST4315_SDIO_MODE 0x00000004
+#define CST4315_RCAL_VALID 0x00000008
+#define CST4315_RCAL_VALUE_MASK 0x000001f0
+#define CST4315_RCAL_VALUE_SHIFT 4
+#define CST4315_PALDO_EXTPNP 0x00000200
+#define CST4315_CBUCK_MODE_MASK 0x00000c00
+#define CST4315_CBUCK_MODE_BURST 0x00000400
+#define CST4315_CBUCK_MODE_LPBURST 0x00000c00
+
+#define PMU_MAX_TRANSITION_DLY 15000
+
+
+#define PMURES_UP_TRANSITION 2
+
+
+
+
+
+#define ECI_BW_20 0x0
+#define ECI_BW_25 0x1
+#define ECI_BW_30 0x2
+#define ECI_BW_35 0x3
+#define ECI_BW_40 0x4
+#define ECI_BW_45 0x5
+#define ECI_BW_50 0x6
+#define ECI_BW_ALL 0x7
+
+
+#define WLAN_NUM_ANT1 TXANT_0
+#define WLAN_NUM_ANT2 TXANT_1
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/sbconfig.h b/drivers/net/wireless/bcm4329/include/sbconfig.h
new file mode 100644
index 00000000000..da18ccbe9ab
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbconfig.h
@@ -0,0 +1,276 @@
+/*
+ * Broadcom SiliconBackplane hardware register definitions.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbconfig.h,v 13.67.30.1 2008/05/07 20:17:27 Exp $
+ */
+
+
+#ifndef _SBCONFIG_H
+#define _SBCONFIG_H
+
+
+#ifndef PAD
+#define _PADLINE(line) pad ## line
+#define _XSTR(line) _PADLINE(line)
+#define PAD _XSTR(__LINE__)
+#endif
+
+
+#define SB_BUS_SIZE 0x10000
+#define SB_BUS_BASE(b) (SI_ENUM_BASE + (b) * SB_BUS_SIZE)
+#define SB_BUS_MAXCORES (SB_BUS_SIZE / SI_CORE_SIZE)
+
+
+#define SBCONFIGOFF 0xf00
+#define SBCONFIGSIZE 256
+
+#define SBIPSFLAG 0x08
+#define SBTPSFLAG 0x18
+#define SBTMERRLOGA 0x48
+#define SBTMERRLOG 0x50
+#define SBADMATCH3 0x60
+#define SBADMATCH2 0x68
+#define SBADMATCH1 0x70
+#define SBIMSTATE 0x90
+#define SBINTVEC 0x94
+#define SBTMSTATELOW 0x98
+#define SBTMSTATEHIGH 0x9c
+#define SBBWA0 0xa0
+#define SBIMCONFIGLOW 0xa8
+#define SBIMCONFIGHIGH 0xac
+#define SBADMATCH0 0xb0
+#define SBTMCONFIGLOW 0xb8
+#define SBTMCONFIGHIGH 0xbc
+#define SBBCONFIG 0xc0
+#define SBBSTATE 0xc8
+#define SBACTCNFG 0xd8
+#define SBFLAGST 0xe8
+#define SBIDLOW 0xf8
+#define SBIDHIGH 0xfc
+
+
+
+#define SBIMERRLOGA 0xea8
+#define SBIMERRLOG 0xeb0
+#define SBTMPORTCONNID0 0xed8
+#define SBTMPORTLOCK0 0xef8
+
+#ifndef _LANGUAGE_ASSEMBLY
+
+typedef volatile struct _sbconfig {
+ uint32 PAD[2];
+ uint32 sbipsflag;
+ uint32 PAD[3];
+ uint32 sbtpsflag;
+ uint32 PAD[11];
+ uint32 sbtmerrloga;
+ uint32 PAD;
+ uint32 sbtmerrlog;
+ uint32 PAD[3];
+ uint32 sbadmatch3;
+ uint32 PAD;
+ uint32 sbadmatch2;
+ uint32 PAD;
+ uint32 sbadmatch1;
+ uint32 PAD[7];
+ uint32 sbimstate;
+ uint32 sbintvec;
+ uint32 sbtmstatelow;
+ uint32 sbtmstatehigh;
+ uint32 sbbwa0;
+ uint32 PAD;
+ uint32 sbimconfiglow;
+ uint32 sbimconfighigh;
+ uint32 sbadmatch0;
+ uint32 PAD;
+ uint32 sbtmconfiglow;
+ uint32 sbtmconfighigh;
+ uint32 sbbconfig;
+ uint32 PAD;
+ uint32 sbbstate;
+ uint32 PAD[3];
+ uint32 sbactcnfg;
+ uint32 PAD[3];
+ uint32 sbflagst;
+ uint32 PAD[3];
+ uint32 sbidlow;
+ uint32 sbidhigh;
+} sbconfig_t;
+
+#endif
+
+
+#define SBIPS_INT1_MASK 0x3f
+#define SBIPS_INT1_SHIFT 0
+#define SBIPS_INT2_MASK 0x3f00
+#define SBIPS_INT2_SHIFT 8
+#define SBIPS_INT3_MASK 0x3f0000
+#define SBIPS_INT3_SHIFT 16
+#define SBIPS_INT4_MASK 0x3f000000
+#define SBIPS_INT4_SHIFT 24
+
+
+#define SBTPS_NUM0_MASK 0x3f
+#define SBTPS_F0EN0 0x40
+
+
+#define SBTMEL_CM 0x00000007
+#define SBTMEL_CI 0x0000ff00
+#define SBTMEL_EC 0x0f000000
+#define SBTMEL_ME 0x80000000
+
+
+#define SBIM_PC 0xf
+#define SBIM_AP_MASK 0x30
+#define SBIM_AP_BOTH 0x00
+#define SBIM_AP_TS 0x10
+#define SBIM_AP_TK 0x20
+#define SBIM_AP_RSV 0x30
+#define SBIM_IBE 0x20000
+#define SBIM_TO 0x40000
+#define SBIM_BY 0x01800000
+#define SBIM_RJ 0x02000000
+
+
+#define SBTML_RESET 0x0001
+#define SBTML_REJ_MASK 0x0006
+#define SBTML_REJ 0x0002
+#define SBTML_TMPREJ 0x0004
+
+#define SBTML_SICF_SHIFT 16
+
+
+#define SBTMH_SERR 0x0001
+#define SBTMH_INT 0x0002
+#define SBTMH_BUSY 0x0004
+#define SBTMH_TO 0x0020
+
+#define SBTMH_SISF_SHIFT 16
+
+
+#define SBBWA_TAB0_MASK 0xffff
+#define SBBWA_TAB1_MASK 0xffff
+#define SBBWA_TAB1_SHIFT 16
+
+
+#define SBIMCL_STO_MASK 0x7
+#define SBIMCL_RTO_MASK 0x70
+#define SBIMCL_RTO_SHIFT 4
+#define SBIMCL_CID_MASK 0xff0000
+#define SBIMCL_CID_SHIFT 16
+
+
+#define SBIMCH_IEM_MASK 0xc
+#define SBIMCH_TEM_MASK 0x30
+#define SBIMCH_TEM_SHIFT 4
+#define SBIMCH_BEM_MASK 0xc0
+#define SBIMCH_BEM_SHIFT 6
+
+
+#define SBAM_TYPE_MASK 0x3
+#define SBAM_AD64 0x4
+#define SBAM_ADINT0_MASK 0xf8
+#define SBAM_ADINT0_SHIFT 3
+#define SBAM_ADINT1_MASK 0x1f8
+#define SBAM_ADINT1_SHIFT 3
+#define SBAM_ADINT2_MASK 0x1f8
+#define SBAM_ADINT2_SHIFT 3
+#define SBAM_ADEN 0x400
+#define SBAM_ADNEG 0x800
+#define SBAM_BASE0_MASK 0xffffff00
+#define SBAM_BASE0_SHIFT 8
+#define SBAM_BASE1_MASK 0xfffff000
+#define SBAM_BASE1_SHIFT 12
+#define SBAM_BASE2_MASK 0xffff0000
+#define SBAM_BASE2_SHIFT 16
+
+
+#define SBTMCL_CD_MASK 0xff
+#define SBTMCL_CO_MASK 0xf800
+#define SBTMCL_CO_SHIFT 11
+#define SBTMCL_IF_MASK 0xfc0000
+#define SBTMCL_IF_SHIFT 18
+#define SBTMCL_IM_MASK 0x3000000
+#define SBTMCL_IM_SHIFT 24
+
+
+#define SBTMCH_BM_MASK 0x3
+#define SBTMCH_RM_MASK 0x3
+#define SBTMCH_RM_SHIFT 2
+#define SBTMCH_SM_MASK 0x30
+#define SBTMCH_SM_SHIFT 4
+#define SBTMCH_EM_MASK 0x300
+#define SBTMCH_EM_SHIFT 8
+#define SBTMCH_IM_MASK 0xc00
+#define SBTMCH_IM_SHIFT 10
+
+
+#define SBBC_LAT_MASK 0x3
+#define SBBC_MAX0_MASK 0xf0000
+#define SBBC_MAX0_SHIFT 16
+#define SBBC_MAX1_MASK 0xf00000
+#define SBBC_MAX1_SHIFT 20
+
+
+#define SBBS_SRD 0x1
+#define SBBS_HRD 0x2
+
+
+#define SBIDL_CS_MASK 0x3
+#define SBIDL_AR_MASK 0x38
+#define SBIDL_AR_SHIFT 3
+#define SBIDL_SYNCH 0x40
+#define SBIDL_INIT 0x80
+#define SBIDL_MINLAT_MASK 0xf00
+#define SBIDL_MINLAT_SHIFT 8
+#define SBIDL_MAXLAT 0xf000
+#define SBIDL_MAXLAT_SHIFT 12
+#define SBIDL_FIRST 0x10000
+#define SBIDL_CW_MASK 0xc0000
+#define SBIDL_CW_SHIFT 18
+#define SBIDL_TP_MASK 0xf00000
+#define SBIDL_TP_SHIFT 20
+#define SBIDL_IP_MASK 0xf000000
+#define SBIDL_IP_SHIFT 24
+#define SBIDL_RV_MASK 0xf0000000
+#define SBIDL_RV_SHIFT 28
+#define SBIDL_RV_2_2 0x00000000
+#define SBIDL_RV_2_3 0x10000000
+
+
+#define SBIDH_RC_MASK 0x000f
+#define SBIDH_RCE_MASK 0x7000
+#define SBIDH_RCE_SHIFT 8
+#define SBCOREREV(sbidh) \
+ ((((sbidh) & SBIDH_RCE_MASK) >> SBIDH_RCE_SHIFT) | ((sbidh) & SBIDH_RC_MASK))
+#define SBIDH_CC_MASK 0x8ff0
+#define SBIDH_CC_SHIFT 4
+#define SBIDH_VC_MASK 0xffff0000
+#define SBIDH_VC_SHIFT 16
+
+#define SB_COMMIT 0xfd8
+
+
+#define SB_VEND_BCM 0x4243
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/sbhnddma.h b/drivers/net/wireless/bcm4329/include/sbhnddma.h
new file mode 100644
index 00000000000..7681395f5b3
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbhnddma.h
@@ -0,0 +1,294 @@
+/*
+ * Generic Broadcom Home Networking Division (HND) DMA engine HW interface
+ * This supports the following chips: BCM42xx, 44xx, 47xx .
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbhnddma.h,v 13.11.250.5.16.1 2009/07/21 14:04:51 Exp $
+ */
+
+
+#ifndef _sbhnddma_h_
+#define _sbhnddma_h_
+
+
+
+
+
+
+
+typedef volatile struct {
+ uint32 control;
+ uint32 addr;
+ uint32 ptr;
+ uint32 status;
+} dma32regs_t;
+
+typedef volatile struct {
+ dma32regs_t xmt;
+ dma32regs_t rcv;
+} dma32regp_t;
+
+typedef volatile struct {
+ uint32 fifoaddr;
+ uint32 fifodatalow;
+ uint32 fifodatahigh;
+ uint32 pad;
+} dma32diag_t;
+
+
+typedef volatile struct {
+ uint32 ctrl;
+ uint32 addr;
+} dma32dd_t;
+
+
+#define D32RINGALIGN_BITS 12
+#define D32MAXRINGSZ (1 << D32RINGALIGN_BITS)
+#define D32RINGALIGN (1 << D32RINGALIGN_BITS)
+#define D32MAXDD (D32MAXRINGSZ / sizeof (dma32dd_t))
+
+
+#define XC_XE ((uint32)1 << 0)
+#define XC_SE ((uint32)1 << 1)
+#define XC_LE ((uint32)1 << 2)
+#define XC_FL ((uint32)1 << 4)
+#define XC_PD ((uint32)1 << 11)
+#define XC_AE ((uint32)3 << 16)
+#define XC_AE_SHIFT 16
+
+
+#define XP_LD_MASK 0xfff
+
+
+#define XS_CD_MASK 0x0fff
+#define XS_XS_MASK 0xf000
+#define XS_XS_SHIFT 12
+#define XS_XS_DISABLED 0x0000
+#define XS_XS_ACTIVE 0x1000
+#define XS_XS_IDLE 0x2000
+#define XS_XS_STOPPED 0x3000
+#define XS_XS_SUSP 0x4000
+#define XS_XE_MASK 0xf0000
+#define XS_XE_SHIFT 16
+#define XS_XE_NOERR 0x00000
+#define XS_XE_DPE 0x10000
+#define XS_XE_DFU 0x20000
+#define XS_XE_BEBR 0x30000
+#define XS_XE_BEDA 0x40000
+#define XS_AD_MASK 0xfff00000
+#define XS_AD_SHIFT 20
+
+
+#define RC_RE ((uint32)1 << 0)
+#define RC_RO_MASK 0xfe
+#define RC_RO_SHIFT 1
+#define RC_FM ((uint32)1 << 8)
+#define RC_SH ((uint32)1 << 9)
+#define RC_OC ((uint32)1 << 10)
+#define RC_PD ((uint32)1 << 11)
+#define RC_AE ((uint32)3 << 16)
+#define RC_AE_SHIFT 16
+
+
+#define RP_LD_MASK 0xfff
+
+
+#define RS_CD_MASK 0x0fff
+#define RS_RS_MASK 0xf000
+#define RS_RS_SHIFT 12
+#define RS_RS_DISABLED 0x0000
+#define RS_RS_ACTIVE 0x1000
+#define RS_RS_IDLE 0x2000
+#define RS_RS_STOPPED 0x3000
+#define RS_RE_MASK 0xf0000
+#define RS_RE_SHIFT 16
+#define RS_RE_NOERR 0x00000
+#define RS_RE_DPE 0x10000
+#define RS_RE_DFO 0x20000
+#define RS_RE_BEBW 0x30000
+#define RS_RE_BEDA 0x40000
+#define RS_AD_MASK 0xfff00000
+#define RS_AD_SHIFT 20
+
+
+#define FA_OFF_MASK 0xffff
+#define FA_SEL_MASK 0xf0000
+#define FA_SEL_SHIFT 16
+#define FA_SEL_XDD 0x00000
+#define FA_SEL_XDP 0x10000
+#define FA_SEL_RDD 0x40000
+#define FA_SEL_RDP 0x50000
+#define FA_SEL_XFD 0x80000
+#define FA_SEL_XFP 0x90000
+#define FA_SEL_RFD 0xc0000
+#define FA_SEL_RFP 0xd0000
+#define FA_SEL_RSD 0xe0000
+#define FA_SEL_RSP 0xf0000
+
+
+#define CTRL_BC_MASK 0x1fff
+#define CTRL_AE ((uint32)3 << 16)
+#define CTRL_AE_SHIFT 16
+#define CTRL_EOT ((uint32)1 << 28)
+#define CTRL_IOC ((uint32)1 << 29)
+#define CTRL_EOF ((uint32)1 << 30)
+#define CTRL_SOF ((uint32)1 << 31)
+
+
+#define CTRL_CORE_MASK 0x0ff00000
+
+
+
+
+typedef volatile struct {
+ uint32 control;
+ uint32 ptr;
+ uint32 addrlow;
+ uint32 addrhigh;
+ uint32 status0;
+ uint32 status1;
+} dma64regs_t;
+
+typedef volatile struct {
+ dma64regs_t tx;
+ dma64regs_t rx;
+} dma64regp_t;
+
+typedef volatile struct {
+ uint32 fifoaddr;
+ uint32 fifodatalow;
+ uint32 fifodatahigh;
+ uint32 pad;
+} dma64diag_t;
+
+
+typedef volatile struct {
+ uint32 ctrl1;
+ uint32 ctrl2;
+ uint32 addrlow;
+ uint32 addrhigh;
+} dma64dd_t;
+
+
+#define D64RINGALIGN_BITS 13
+#define D64MAXRINGSZ (1 << D64RINGALIGN_BITS)
+#define D64RINGALIGN (1 << D64RINGALIGN_BITS)
+#define D64MAXDD (D64MAXRINGSZ / sizeof (dma64dd_t))
+
+
+#define D64_XC_XE 0x00000001
+#define D64_XC_SE 0x00000002
+#define D64_XC_LE 0x00000004
+#define D64_XC_FL 0x00000010
+#define D64_XC_PD 0x00000800
+#define D64_XC_AE 0x00030000
+#define D64_XC_AE_SHIFT 16
+
+
+#define D64_XP_LD_MASK 0x00000fff
+
+
+#define D64_XS0_CD_MASK 0x00001fff
+#define D64_XS0_XS_MASK 0xf0000000
+#define D64_XS0_XS_SHIFT 28
+#define D64_XS0_XS_DISABLED 0x00000000
+#define D64_XS0_XS_ACTIVE 0x10000000
+#define D64_XS0_XS_IDLE 0x20000000
+#define D64_XS0_XS_STOPPED 0x30000000
+#define D64_XS0_XS_SUSP 0x40000000
+
+#define D64_XS1_AD_MASK 0x0001ffff
+#define D64_XS1_XE_MASK 0xf0000000
+#define D64_XS1_XE_SHIFT 28
+#define D64_XS1_XE_NOERR 0x00000000
+#define D64_XS1_XE_DPE 0x10000000
+#define D64_XS1_XE_DFU 0x20000000
+#define D64_XS1_XE_DTE 0x30000000
+#define D64_XS1_XE_DESRE 0x40000000
+#define D64_XS1_XE_COREE 0x50000000
+
+
+#define D64_RC_RE 0x00000001
+#define D64_RC_RO_MASK 0x000000fe
+#define D64_RC_RO_SHIFT 1
+#define D64_RC_FM 0x00000100
+#define D64_RC_SH 0x00000200
+#define D64_RC_OC 0x00000400
+#define D64_RC_PD 0x00000800
+#define D64_RC_AE 0x00030000
+#define D64_RC_AE_SHIFT 16
+
+
+#define D64_RP_LD_MASK 0x00000fff
+
+
+#define D64_RS0_CD_MASK 0x00001fff
+#define D64_RS0_RS_MASK 0xf0000000
+#define D64_RS0_RS_SHIFT 28
+#define D64_RS0_RS_DISABLED 0x00000000
+#define D64_RS0_RS_ACTIVE 0x10000000
+#define D64_RS0_RS_IDLE 0x20000000
+#define D64_RS0_RS_STOPPED 0x30000000
+#define D64_RS0_RS_SUSP 0x40000000
+
+#define D64_RS1_AD_MASK 0x0001ffff
+#define D64_RS1_RE_MASK 0xf0000000
+#define D64_RS1_RE_SHIFT 28
+#define D64_RS1_RE_NOERR 0x00000000
+#define D64_RS1_RE_DPO 0x10000000
+#define D64_RS1_RE_DFU 0x20000000
+#define D64_RS1_RE_DTE 0x30000000
+#define D64_RS1_RE_DESRE 0x40000000
+#define D64_RS1_RE_COREE 0x50000000
+
+
+#define D64_FA_OFF_MASK 0xffff
+#define D64_FA_SEL_MASK 0xf0000
+#define D64_FA_SEL_SHIFT 16
+#define D64_FA_SEL_XDD 0x00000
+#define D64_FA_SEL_XDP 0x10000
+#define D64_FA_SEL_RDD 0x40000
+#define D64_FA_SEL_RDP 0x50000
+#define D64_FA_SEL_XFD 0x80000
+#define D64_FA_SEL_XFP 0x90000
+#define D64_FA_SEL_RFD 0xc0000
+#define D64_FA_SEL_RFP 0xd0000
+#define D64_FA_SEL_RSD 0xe0000
+#define D64_FA_SEL_RSP 0xf0000
+
+
+#define D64_CTRL1_EOT ((uint32)1 << 28)
+#define D64_CTRL1_IOC ((uint32)1 << 29)
+#define D64_CTRL1_EOF ((uint32)1 << 30)
+#define D64_CTRL1_SOF ((uint32)1 << 31)
+
+
+#define D64_CTRL2_BC_MASK 0x00007fff
+#define D64_CTRL2_AE 0x00030000
+#define D64_CTRL2_AE_SHIFT 16
+#define D64_CTRL2_PARITY 0x00040000
+
+
+#define D64_CTRL_CORE_MASK 0x0ff00000
+
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/sbpcmcia.h b/drivers/net/wireless/bcm4329/include/sbpcmcia.h
new file mode 100644
index 00000000000..d6d80334258
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbpcmcia.h
@@ -0,0 +1,109 @@
+/*
+ * BCM43XX Sonics SiliconBackplane PCMCIA core hardware definitions.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbpcmcia.h,v 13.31.4.1.2.3.8.7 2009/06/22 05:14:24 Exp $
+ */
+
+
+#ifndef _SBPCMCIA_H
+#define _SBPCMCIA_H
+
+
+
+
+#define PCMCIA_FCR (0x700 / 2)
+
+#define FCR0_OFF 0
+#define FCR1_OFF (0x40 / 2)
+#define FCR2_OFF (0x80 / 2)
+#define FCR3_OFF (0xc0 / 2)
+
+#define PCMCIA_FCR0 (0x700 / 2)
+#define PCMCIA_FCR1 (0x740 / 2)
+#define PCMCIA_FCR2 (0x780 / 2)
+#define PCMCIA_FCR3 (0x7c0 / 2)
+
+
+
+#define PCMCIA_COR 0
+
+#define COR_RST 0x80
+#define COR_LEV 0x40
+#define COR_IRQEN 0x04
+#define COR_BLREN 0x01
+#define COR_FUNEN 0x01
+
+
+#define PCICIA_FCSR (2 / 2)
+#define PCICIA_PRR (4 / 2)
+#define PCICIA_SCR (6 / 2)
+#define PCICIA_ESR (8 / 2)
+
+
+#define PCM_MEMOFF 0x0000
+#define F0_MEMOFF 0x1000
+#define F1_MEMOFF 0x2000
+#define F2_MEMOFF 0x3000
+#define F3_MEMOFF 0x4000
+
+
+#define MEM_ADDR0 (0x728 / 2)
+#define MEM_ADDR1 (0x72a / 2)
+#define MEM_ADDR2 (0x72c / 2)
+
+
+#define PCMCIA_ADDR0 (0x072e / 2)
+#define PCMCIA_ADDR1 (0x0730 / 2)
+#define PCMCIA_ADDR2 (0x0732 / 2)
+
+#define MEM_SEG (0x0734 / 2)
+#define SROM_CS (0x0736 / 2)
+#define SROM_DATAL (0x0738 / 2)
+#define SROM_DATAH (0x073a / 2)
+#define SROM_ADDRL (0x073c / 2)
+#define SROM_ADDRH (0x073e / 2)
+#define SROM_INFO2 (0x0772 / 2)
+#define SROM_INFO (0x07be / 2)
+
+
+#define SROM_IDLE 0
+#define SROM_WRITE 1
+#define SROM_READ 2
+#define SROM_WEN 4
+#define SROM_WDS 7
+#define SROM_DONE 8
+
+
+#define SRI_SZ_MASK 0x03
+#define SRI_BLANK 0x04
+#define SRI_OTP 0x80
+
+
+
+#define SBTML_INT_ACK 0x40000
+#define SBTML_INT_EN 0x20000
+
+
+#define SBTMH_INT_STATUS 0x40000
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/sbsdio.h b/drivers/net/wireless/bcm4329/include/sbsdio.h
new file mode 100644
index 00000000000..75aaf4d88f7
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbsdio.h
@@ -0,0 +1,166 @@
+/*
+ * SDIO device core hardware definitions.
+ * sdio is a portion of the pcmcia core in core rev 3 - rev 8
+ *
+ * SDIO core support 1bit, 4 bit SDIO mode as well as SPI mode.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbsdio.h,v 13.29.4.1.22.3 2009/03/11 20:26:57 Exp $
+ */
+
+#ifndef _SBSDIO_H
+#define _SBSDIO_H
+
+#define SBSDIO_NUM_FUNCTION 3 /* as of sdiod rev 0, supports 3 functions */
+
+/* function 1 miscellaneous registers */
+#define SBSDIO_SPROM_CS 0x10000 /* sprom command and status */
+#define SBSDIO_SPROM_INFO 0x10001 /* sprom info register */
+#define SBSDIO_SPROM_DATA_LOW 0x10002 /* sprom indirect access data byte 0 */
+#define SBSDIO_SPROM_DATA_HIGH 0x10003 /* sprom indirect access data byte 1 */
+#define SBSDIO_SPROM_ADDR_LOW 0x10004 /* sprom indirect access addr byte 0 */
+#define SBSDIO_SPROM_ADDR_HIGH 0x10005 /* sprom indirect access addr byte 0 */
+#define SBSDIO_CHIP_CTRL_DATA 0x10006 /* xtal_pu (gpio) output */
+#define SBSDIO_CHIP_CTRL_EN 0x10007 /* xtal_pu (gpio) enable */
+#define SBSDIO_WATERMARK 0x10008 /* rev < 7, watermark for sdio device */
+#define SBSDIO_DEVICE_CTL 0x10009 /* control busy signal generation */
+
+/* registers introduced in rev 8, some content (mask/bits) defs in sbsdpcmdev.h */
+#define SBSDIO_FUNC1_SBADDRLOW 0x1000A /* SB Address Window Low (b15) */
+#define SBSDIO_FUNC1_SBADDRMID 0x1000B /* SB Address Window Mid (b23:b16) */
+#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C /* SB Address Window High (b31:b24) */
+#define SBSDIO_FUNC1_FRAMECTRL 0x1000D /* Frame Control (frame term/abort) */
+#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E /* ChipClockCSR (ALP/HT ctl/status) */
+#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F /* SdioPullUp (on cmd, d0-d2) */
+#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019 /* Write Frame Byte Count Low */
+#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A /* Write Frame Byte Count High */
+#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B /* Read Frame Byte Count Low */
+#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C /* Read Frame Byte Count High */
+
+#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */
+#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001C /* f1 misc register end */
+
+/* SBSDIO_SPROM_CS */
+#define SBSDIO_SPROM_IDLE 0
+#define SBSDIO_SPROM_WRITE 1
+#define SBSDIO_SPROM_READ 2
+#define SBSDIO_SPROM_WEN 4
+#define SBSDIO_SPROM_WDS 7
+#define SBSDIO_SPROM_DONE 8
+
+/* SBSDIO_SPROM_INFO */
+#define SROM_SZ_MASK 0x03 /* SROM size, 1: 4k, 2: 16k */
+#define SROM_BLANK 0x04 /* depreciated in corerev 6 */
+#define SROM_OTP 0x80 /* OTP present */
+
+/* SBSDIO_CHIP_CTRL */
+#define SBSDIO_CHIP_CTRL_XTAL 0x01 /* or'd with onchip xtal_pu,
+ * 1: power on oscillator
+ * (for 4318 only)
+ */
+/* SBSDIO_WATERMARK */
+#define SBSDIO_WATERMARK_MASK 0x7f /* number of words - 1 for sd device
+ * to wait before sending data to host
+ */
+
+/* SBSDIO_DEVICE_CTL */
+#define SBSDIO_DEVCTL_SETBUSY 0x01 /* 1: device will assert busy signal when
+ * receiving CMD53
+ */
+#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02 /* 1: assertion of sdio interrupt is
+ * synchronous to the sdio clock
+ */
+#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04 /* 1: mask all interrupts to host
+ * except the chipActive (rev 8)
+ */
+#define SBSDIO_DEVCTL_PADS_ISO 0x08 /* 1: isolate internal sdio signals, put
+ * external pads in tri-state; requires
+ * sdio bus power cycle to clear (rev 9)
+ */
+#define SBSDIO_DEVCTL_SB_RST_CTL 0x30 /* Force SD->SB reset mapping (rev 11) */
+#define SBSDIO_DEVCTL_RST_CORECTL 0x00 /* Determined by CoreControl bit */
+#define SBSDIO_DEVCTL_RST_BPRESET 0x10 /* Force backplane reset */
+#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20 /* Force no backplane reset */
+
+
+/* SBSDIO_FUNC1_CHIPCLKCSR */
+#define SBSDIO_FORCE_ALP 0x01 /* Force ALP request to backplane */
+#define SBSDIO_FORCE_HT 0x02 /* Force HT request to backplane */
+#define SBSDIO_FORCE_ILP 0x04 /* Force ILP request to backplane */
+#define SBSDIO_ALP_AVAIL_REQ 0x08 /* Make ALP ready (power up xtal) */
+#define SBSDIO_HT_AVAIL_REQ 0x10 /* Make HT ready (power up PLL) */
+#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 /* Squelch clock requests from HW */
+#define SBSDIO_ALP_AVAIL 0x40 /* Status: ALP is ready */
+#define SBSDIO_HT_AVAIL 0x80 /* Status: HT is ready */
+/* In rev8, actual avail bits followed original docs */
+#define SBSDIO_Rev8_HT_AVAIL 0x40
+#define SBSDIO_Rev8_ALP_AVAIL 0x80
+
+#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
+#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS)
+#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
+#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
+#define SBSDIO_CLKAV(regval, alponly) (SBSDIO_ALPAV(regval) && \
+ (alponly ? 1 : SBSDIO_HTAV(regval)))
+
+/* SBSDIO_FUNC1_SDIOPULLUP */
+#define SBSDIO_PULLUP_D0 0x01 /* Enable D0/MISO pullup */
+#define SBSDIO_PULLUP_D1 0x02 /* Enable D1/INT# pullup */
+#define SBSDIO_PULLUP_D2 0x04 /* Enable D2 pullup */
+#define SBSDIO_PULLUP_CMD 0x08 /* Enable CMD/MOSI pullup */
+#define SBSDIO_PULLUP_ALL 0x0f /* All valid bits */
+
+/* function 1 OCP space */
+#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF /* sb offset addr is <= 15 bits, 32k */
+#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000
+#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000 /* with b15, maps to 32-bit SB access */
+
+/* some duplication with sbsdpcmdev.h here */
+/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
+#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */
+#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */
+#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */
+#define SBSDIO_SBWINDOW_MASK 0xffff8000 /* Address bits from SBADDR regs */
+
+/* direct(mapped) cis space */
+#define SBSDIO_CIS_BASE_COMMON 0x1000 /* MAPPED common CIS address */
+#define SBSDIO_CIS_SIZE_LIMIT 0x200 /* maximum bytes in one CIS */
+#define SBSDIO_OTP_CIS_SIZE_LIMIT 0x078 /* maximum bytes OTP CIS */
+
+#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF /* cis offset addr is < 17 bits */
+
+#define SBSDIO_CIS_MANFID_TUPLE_LEN 6 /* manfid tuple length, include tuple,
+ * link bytes
+ */
+
+/* indirect cis access (in sprom) */
+#define SBSDIO_SPROM_CIS_OFFSET 0x8 /* 8 control bytes first, CIS starts from
+ * 8th byte
+ */
+
+#define SBSDIO_BYTEMODE_DATALEN_MAX 64 /* sdio byte mode: maximum length of one
+ * data comamnd
+ */
+
+#define SBSDIO_CORE_ADDR_MASK 0x1FFFF /* sdio core function one address mask */
+
+#endif /* _SBSDIO_H */
diff --git a/drivers/net/wireless/bcm4329/include/sbsdpcmdev.h b/drivers/net/wireless/bcm4329/include/sbsdpcmdev.h
new file mode 100644
index 00000000000..7c7c7e4de0f
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbsdpcmdev.h
@@ -0,0 +1,288 @@
+/*
+ * Broadcom SiliconBackplane SDIO/PCMCIA hardware-specific device core support
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbsdpcmdev.h,v 13.29.4.1.4.6.6.2 2008/12/31 21:16:51 Exp $
+ */
+
+#ifndef _sbsdpcmdev_h_
+#define _sbsdpcmdev_h_
+
+/* cpp contortions to concatenate w/arg prescan */
+#ifndef PAD
+#define _PADLINE(line) pad ## line
+#define _XSTR(line) _PADLINE(line)
+#define PAD _XSTR(__LINE__)
+#endif /* PAD */
+
+
+typedef volatile struct {
+ dma64regs_t xmt; /* dma tx */
+ uint32 PAD[2];
+ dma64regs_t rcv; /* dma rx */
+ uint32 PAD[2];
+} dma64p_t;
+
+/* dma64 sdiod corerev >= 1 */
+typedef volatile struct {
+ dma64p_t dma64regs[2];
+ dma64diag_t dmafifo; /* DMA Diagnostic Regs, 0x280-0x28c */
+ uint32 PAD[92];
+} sdiodma64_t;
+
+/* dma32 sdiod corerev == 0 */
+typedef volatile struct {
+ dma32regp_t dma32regs[2]; /* dma tx & rx, 0x200-0x23c */
+ dma32diag_t dmafifo; /* DMA Diagnostic Regs, 0x240-0x24c */
+ uint32 PAD[108];
+} sdiodma32_t;
+
+/* dma32 regs for pcmcia core */
+typedef volatile struct {
+ dma32regp_t dmaregs; /* DMA Regs, 0x200-0x21c, rev8 */
+ dma32diag_t dmafifo; /* DMA Diagnostic Regs, 0x220-0x22c */
+ uint32 PAD[116];
+} pcmdma32_t;
+
+/* core registers */
+typedef volatile struct {
+ uint32 corecontrol; /* CoreControl, 0x000, rev8 */
+ uint32 corestatus; /* CoreStatus, 0x004, rev8 */
+ uint32 PAD[1];
+ uint32 biststatus; /* BistStatus, 0x00c, rev8 */
+
+ /* PCMCIA access */
+ uint16 pcmciamesportaladdr; /* PcmciaMesPortalAddr, 0x010, rev8 */
+ uint16 PAD[1];
+ uint16 pcmciamesportalmask; /* PcmciaMesPortalMask, 0x014, rev8 */
+ uint16 PAD[1];
+ uint16 pcmciawrframebc; /* PcmciaWrFrameBC, 0x018, rev8 */
+ uint16 PAD[1];
+ uint16 pcmciaunderflowtimer; /* PcmciaUnderflowTimer, 0x01c, rev8 */
+ uint16 PAD[1];
+
+ /* interrupt */
+ uint32 intstatus; /* IntStatus, 0x020, rev8 */
+ uint32 hostintmask; /* IntHostMask, 0x024, rev8 */
+ uint32 intmask; /* IntSbMask, 0x028, rev8 */
+ uint32 sbintstatus; /* SBIntStatus, 0x02c, rev8 */
+ uint32 sbintmask; /* SBIntMask, 0x030, rev8 */
+ uint32 PAD[3];
+ uint32 tosbmailbox; /* ToSBMailbox, 0x040, rev8 */
+ uint32 tohostmailbox; /* ToHostMailbox, 0x044, rev8 */
+ uint32 tosbmailboxdata; /* ToSbMailboxData, 0x048, rev8 */
+ uint32 tohostmailboxdata; /* ToHostMailboxData, 0x04c, rev8 */
+
+ /* synchronized access to registers in SDIO clock domain */
+ uint32 sdioaccess; /* SdioAccess, 0x050, rev8 */
+ uint32 PAD[3];
+
+ /* PCMCIA frame control */
+ uint8 pcmciaframectrl; /* pcmciaFrameCtrl, 0x060, rev8 */
+ uint8 PAD[3];
+ uint8 pcmciawatermark; /* pcmciaWaterMark, 0x064, rev8 */
+ uint8 PAD[155];
+
+ /* interrupt batching control */
+ uint32 intrcvlazy; /* IntRcvLazy, 0x100, rev8 */
+ uint32 PAD[3];
+
+ /* counters */
+ uint32 cmd52rd; /* Cmd52RdCount, 0x110, rev8, SDIO: cmd52 reads */
+ uint32 cmd52wr; /* Cmd52WrCount, 0x114, rev8, SDIO: cmd52 writes */
+ uint32 cmd53rd; /* Cmd53RdCount, 0x118, rev8, SDIO: cmd53 reads */
+ uint32 cmd53wr; /* Cmd53WrCount, 0x11c, rev8, SDIO: cmd53 writes */
+ uint32 abort; /* AbortCount, 0x120, rev8, SDIO: aborts */
+ uint32 datacrcerror; /* DataCrcErrorCount, 0x124, rev8, SDIO: frames w/bad CRC */
+ uint32 rdoutofsync; /* RdOutOfSyncCount, 0x128, rev8, SDIO/PCMCIA: Rd Frm OOS */
+ uint32 wroutofsync; /* RdOutOfSyncCount, 0x12c, rev8, SDIO/PCMCIA: Wr Frm OOS */
+ uint32 writebusy; /* WriteBusyCount, 0x130, rev8, SDIO: dev asserted "busy" */
+ uint32 readwait; /* ReadWaitCount, 0x134, rev8, SDIO: read: no data avail */
+ uint32 readterm; /* ReadTermCount, 0x138, rev8, SDIO: rd frm terminates */
+ uint32 writeterm; /* WriteTermCount, 0x13c, rev8, SDIO: wr frm terminates */
+ uint32 PAD[40];
+ uint32 clockctlstatus; /* ClockCtlStatus, 0x1e0, rev8 */
+ uint32 PAD[7];
+
+ /* DMA engines */
+ volatile union {
+ pcmdma32_t pcm32;
+ sdiodma32_t sdiod32;
+ sdiodma64_t sdiod64;
+ } dma;
+
+ /* SDIO/PCMCIA CIS region */
+ char cis[512]; /* 512 byte CIS, 0x400-0x5ff, rev6 */
+
+ /* PCMCIA function control registers */
+ char pcmciafcr[256]; /* PCMCIA FCR, 0x600-6ff, rev6 */
+ uint16 PAD[55];
+
+ /* PCMCIA backplane access */
+ uint16 backplanecsr; /* BackplaneCSR, 0x76E, rev6 */
+ uint16 backplaneaddr0; /* BackplaneAddr0, 0x770, rev6 */
+ uint16 backplaneaddr1; /* BackplaneAddr1, 0x772, rev6 */
+ uint16 backplaneaddr2; /* BackplaneAddr2, 0x774, rev6 */
+ uint16 backplaneaddr3; /* BackplaneAddr3, 0x776, rev6 */
+ uint16 backplanedata0; /* BackplaneData0, 0x778, rev6 */
+ uint16 backplanedata1; /* BackplaneData1, 0x77a, rev6 */
+ uint16 backplanedata2; /* BackplaneData2, 0x77c, rev6 */
+ uint16 backplanedata3; /* BackplaneData3, 0x77e, rev6 */
+ uint16 PAD[31];
+
+ /* sprom "size" & "blank" info */
+ uint16 spromstatus; /* SPROMStatus, 0x7BE, rev2 */
+ uint32 PAD[464];
+
+ /* Sonics SiliconBackplane registers */
+ sbconfig_t sbconfig; /* SbConfig Regs, 0xf00-0xfff, rev8 */
+} sdpcmd_regs_t;
+
+/* corecontrol */
+#define CC_CISRDY (1 << 0) /* CIS Ready */
+#define CC_BPRESEN (1 << 1) /* CCCR RES signal causes backplane reset */
+#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */
+#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation bit (rev 11) */
+
+/* corestatus */
+#define CS_PCMCIAMODE (1 << 0) /* Device Mode; 0=SDIO, 1=PCMCIA */
+#define CS_SMARTDEV (1 << 1) /* 1=smartDev enabled */
+#define CS_F2ENABLED (1 << 2) /* 1=host has enabled the device */
+
+#define PCMCIA_MES_PA_MASK 0x7fff /* PCMCIA Message Portal Address Mask */
+#define PCMCIA_MES_PM_MASK 0x7fff /* PCMCIA Message Portal Mask Mask */
+#define PCMCIA_WFBC_MASK 0xffff /* PCMCIA Write Frame Byte Count Mask */
+#define PCMCIA_UT_MASK 0x07ff /* PCMCIA Underflow Timer Mask */
+
+/* intstatus */
+#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */
+#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */
+#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */
+#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */
+#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */
+#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */
+#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */
+#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */
+#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */
+#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */
+#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */
+#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */
+#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */
+#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */
+#define I_PC (1 << 10) /* descriptor error */
+#define I_PD (1 << 11) /* data error */
+#define I_DE (1 << 12) /* Descriptor protocol Error */
+#define I_RU (1 << 13) /* Receive descriptor Underflow */
+#define I_RO (1 << 14) /* Receive fifo Overflow */
+#define I_XU (1 << 15) /* Transmit fifo Underflow */
+#define I_RI (1 << 16) /* Receive Interrupt */
+#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */
+#define I_XI (1 << 24) /* Transmit Interrupt */
+#define I_RF_TERM (1 << 25) /* Read Frame Terminate */
+#define I_WF_TERM (1 << 26) /* Write Frame Terminate */
+#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */
+#define I_SBINT (1 << 28) /* sbintstatus Interrupt */
+#define I_CHIPACTIVE (1 << 29) /* chip transitioned from doze to active state */
+#define I_SRESET (1 << 30) /* CCCR RES interrupt */
+#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */
+#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU) /* DMA Errors */
+#define I_DMA (I_RI | I_XI | I_ERRORS)
+
+/* sbintstatus */
+#define I_SB_SERR (1 << 8) /* Backplane SError (write) */
+#define I_SB_RESPERR (1 << 9) /* Backplane Response Error (read) */
+#define I_SB_SPROMERR (1 << 10) /* Error accessing the sprom */
+
+/* sdioaccess */
+#define SDA_DATA_MASK 0x000000ff /* Read/Write Data Mask */
+#define SDA_ADDR_MASK 0x000fff00 /* Read/Write Address Mask */
+#define SDA_ADDR_SHIFT 8 /* Read/Write Address Shift */
+#define SDA_WRITE 0x01000000 /* Write bit */
+#define SDA_READ 0x00000000 /* Write bit cleared for Read */
+#define SDA_BUSY 0x80000000 /* Busy bit */
+
+/* sdioaccess-accessible register address spaces */
+#define SDA_CCCR_SPACE 0x000 /* sdioAccess CCCR register space */
+#define SDA_F1_FBR_SPACE 0x100 /* sdioAccess F1 FBR register space */
+#define SDA_F2_FBR_SPACE 0x200 /* sdioAccess F2 FBR register space */
+#define SDA_F1_REG_SPACE 0x300 /* sdioAccess F1 core-specific register space */
+
+/* SDA_F1_REG_SPACE sdioaccess-accessible F1 reg space register offsets */
+#define SDA_CHIPCONTROLDATA 0x006 /* ChipControlData */
+#define SDA_CHIPCONTROLENAB 0x007 /* ChipControlEnable */
+#define SDA_F2WATERMARK 0x008 /* Function 2 Watermark */
+#define SDA_DEVICECONTROL 0x009 /* DeviceControl */
+#define SDA_SBADDRLOW 0x00a /* SbAddrLow */
+#define SDA_SBADDRMID 0x00b /* SbAddrMid */
+#define SDA_SBADDRHIGH 0x00c /* SbAddrHigh */
+#define SDA_FRAMECTRL 0x00d /* FrameCtrl */
+#define SDA_CHIPCLOCKCSR 0x00e /* ChipClockCSR */
+#define SDA_SDIOPULLUP 0x00f /* SdioPullUp */
+#define SDA_SDIOWRFRAMEBCLOW 0x019 /* SdioWrFrameBCLow */
+#define SDA_SDIOWRFRAMEBCHIGH 0x01a /* SdioWrFrameBCHigh */
+#define SDA_SDIORDFRAMEBCLOW 0x01b /* SdioRdFrameBCLow */
+#define SDA_SDIORDFRAMEBCHIGH 0x01c /* SdioRdFrameBCHigh */
+
+/* SDA_F2WATERMARK */
+#define SDA_F2WATERMARK_MASK 0x7f /* F2Watermark Mask */
+
+/* SDA_SBADDRLOW */
+#define SDA_SBADDRLOW_MASK 0x80 /* SbAddrLow Mask */
+
+/* SDA_SBADDRMID */
+#define SDA_SBADDRMID_MASK 0xff /* SbAddrMid Mask */
+
+/* SDA_SBADDRHIGH */
+#define SDA_SBADDRHIGH_MASK 0xff /* SbAddrHigh Mask */
+
+/* SDA_FRAMECTRL */
+#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */
+#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */
+#define SFC_CRC4WOOS (1 << 2) /* HW reports CRC error for write out of sync */
+#define SFC_ABORTALL (1 << 3) /* Abort cancels all in-progress frames */
+
+/* pcmciaframectrl */
+#define PFC_RF_TERM (1 << 0) /* Read Frame Terminate */
+#define PFC_WF_TERM (1 << 1) /* Write Frame Terminate */
+
+/* intrcvlazy */
+#define IRL_TO_MASK 0x00ffffff /* timeout */
+#define IRL_FC_MASK 0xff000000 /* frame count */
+#define IRL_FC_SHIFT 24 /* frame count */
+
+/* rx header */
+typedef volatile struct {
+ uint16 len;
+ uint16 flags;
+} sdpcmd_rxh_t;
+
+/* rx header flags */
+#define RXF_CRC 0x0001 /* CRC error detected */
+#define RXF_WOOS 0x0002 /* write frame out of sync */
+#define RXF_WF_TERM 0x0004 /* write frame terminated */
+#define RXF_ABORT 0x0008 /* write frame aborted */
+#define RXF_DISCARD (RXF_CRC | RXF_WOOS | RXF_WF_TERM | RXF_ABORT) /* bad frame */
+
+/* HW frame tag */
+#define SDPCM_FRAMETAG_LEN 4 /* HW frametag: 2 bytes len, 2 bytes check val */
+
+#endif /* _sbsdpcmdev_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/sbsocram.h b/drivers/net/wireless/bcm4329/include/sbsocram.h
new file mode 100644
index 00000000000..5ede0b66d97
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sbsocram.h
@@ -0,0 +1,150 @@
+/*
+ * BCM47XX Sonics SiliconBackplane embedded ram core
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbsocram.h,v 13.9.162.2 2008/12/12 14:13:27 Exp $
+ */
+
+
+#ifndef _SBSOCRAM_H
+#define _SBSOCRAM_H
+
+#ifndef _LANGUAGE_ASSEMBLY
+
+
+#ifndef PAD
+#define _PADLINE(line) pad ## line
+#define _XSTR(line) _PADLINE(line)
+#define PAD _XSTR(__LINE__)
+#endif
+
+
+typedef volatile struct sbsocramregs {
+ uint32 coreinfo;
+ uint32 bwalloc;
+ uint32 extracoreinfo;
+ uint32 biststat;
+ uint32 bankidx;
+ uint32 standbyctrl;
+
+ uint32 errlogstatus;
+ uint32 errlogaddr;
+
+ uint32 cambankidx;
+ uint32 cambankstandbyctrl;
+ uint32 cambankpatchctrl;
+ uint32 cambankpatchtblbaseaddr;
+ uint32 cambankcmdreg;
+ uint32 cambankdatareg;
+ uint32 cambankmaskreg;
+ uint32 PAD[17];
+ uint32 extmemconfig;
+ uint32 extmemparitycsr;
+ uint32 extmemparityerrdata;
+ uint32 extmemparityerrcnt;
+ uint32 extmemwrctrlandsize;
+ uint32 PAD[84];
+ uint32 workaround;
+ uint32 pwrctl;
+} sbsocramregs_t;
+
+#endif
+
+
+#define SR_COREINFO 0x00
+#define SR_BWALLOC 0x04
+#define SR_BISTSTAT 0x0c
+#define SR_BANKINDEX 0x10
+#define SR_BANKSTBYCTL 0x14
+#define SR_PWRCTL 0x1e8
+
+
+#define SRCI_PT_MASK 0x00070000
+#define SRCI_PT_SHIFT 16
+
+#define SRCI_PT_OCP_OCP 0
+#define SRCI_PT_AXI_OCP 1
+#define SRCI_PT_ARM7AHB_OCP 2
+#define SRCI_PT_CM3AHB_OCP 3
+#define SRCI_PT_AXI_AXI 4
+#define SRCI_PT_AHB_AXI 5
+
+#define SRCI_LSS_MASK 0x00f00000
+#define SRCI_LSS_SHIFT 20
+#define SRCI_LRS_MASK 0x0f000000
+#define SRCI_LRS_SHIFT 24
+
+
+#define SRCI_MS0_MASK 0xf
+#define SR_MS0_BASE 16
+
+
+#define SRCI_ROMNB_MASK 0xf000
+#define SRCI_ROMNB_SHIFT 12
+#define SRCI_ROMBSZ_MASK 0xf00
+#define SRCI_ROMBSZ_SHIFT 8
+#define SRCI_SRNB_MASK 0xf0
+#define SRCI_SRNB_SHIFT 4
+#define SRCI_SRBSZ_MASK 0xf
+#define SRCI_SRBSZ_SHIFT 0
+
+#define SR_BSZ_BASE 14
+
+
+#define SRSC_SBYOVR_MASK 0x80000000
+#define SRSC_SBYOVR_SHIFT 31
+#define SRSC_SBYOVRVAL_MASK 0x60000000
+#define SRSC_SBYOVRVAL_SHIFT 29
+#define SRSC_SBYEN_MASK 0x01000000
+#define SRSC_SBYEN_SHIFT 24
+
+
+#define SRPC_PMU_STBYDIS_MASK 0x00000010
+#define SRPC_PMU_STBYDIS_SHIFT 4
+#define SRPC_STBYOVRVAL_MASK 0x00000008
+#define SRPC_STBYOVRVAL_SHIFT 3
+#define SRPC_STBYOVR_MASK 0x00000007
+#define SRPC_STBYOVR_SHIFT 0
+
+
+#define SRECC_NUM_BANKS_MASK 0x000000F0
+#define SRECC_NUM_BANKS_SHIFT 4
+#define SRECC_BANKSIZE_MASK 0x0000000F
+#define SRECC_BANKSIZE_SHIFT 0
+
+#define SRECC_BANKSIZE(value) (1 << (value))
+
+
+#define SRCBPC_PATCHENABLE 0x80000000
+
+#define SRP_ADDRESS 0x0001FFFC
+#define SRP_VALID 0x8000
+
+
+#define SRCMD_WRITE 0x00020000
+#define SRCMD_READ 0x00010000
+#define SRCMD_DONE 0x80000000
+
+#define SRCMD_DONE_DLY 1000
+
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/sdio.h b/drivers/net/wireless/bcm4329/include/sdio.h
new file mode 100644
index 00000000000..280cb845fb0
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sdio.h
@@ -0,0 +1,566 @@
+/*
+ * SDIO spec header file
+ * Protocol and standard (common) device definitions
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sdio.h,v 13.24.4.1.4.1.16.1 2009/08/12 01:08:02 Exp $
+ */
+
+#ifndef _SDIO_H
+#define _SDIO_H
+
+
+/* CCCR structure for function 0 */
+typedef volatile struct {
+ uint8 cccr_sdio_rev; /* RO, cccr and sdio revision */
+ uint8 sd_rev; /* RO, sd spec revision */
+ uint8 io_en; /* I/O enable */
+ uint8 io_rdy; /* I/O ready reg */
+ uint8 intr_ctl; /* Master and per function interrupt enable control */
+ uint8 intr_status; /* RO, interrupt pending status */
+ uint8 io_abort; /* read/write abort or reset all functions */
+ uint8 bus_inter; /* bus interface control */
+ uint8 capability; /* RO, card capability */
+
+ uint8 cis_base_low; /* 0x9 RO, common CIS base address, LSB */
+ uint8 cis_base_mid;
+ uint8 cis_base_high; /* 0xB RO, common CIS base address, MSB */
+
+ /* suspend/resume registers */
+ uint8 bus_suspend; /* 0xC */
+ uint8 func_select; /* 0xD */
+ uint8 exec_flag; /* 0xE */
+ uint8 ready_flag; /* 0xF */
+
+ uint8 fn0_blk_size[2]; /* 0x10(LSB), 0x11(MSB) */
+
+ uint8 power_control; /* 0x12 (SDIO version 1.10) */
+
+ uint8 speed_control; /* 0x13 */
+} sdio_regs_t;
+
+/* SDIO Device CCCR offsets */
+#define SDIOD_CCCR_REV 0x00
+#define SDIOD_CCCR_SDREV 0x01
+#define SDIOD_CCCR_IOEN 0x02
+#define SDIOD_CCCR_IORDY 0x03
+#define SDIOD_CCCR_INTEN 0x04
+#define SDIOD_CCCR_INTPEND 0x05
+#define SDIOD_CCCR_IOABORT 0x06
+#define SDIOD_CCCR_BICTRL 0x07
+#define SDIOD_CCCR_CAPABLITIES 0x08
+#define SDIOD_CCCR_CISPTR_0 0x09
+#define SDIOD_CCCR_CISPTR_1 0x0A
+#define SDIOD_CCCR_CISPTR_2 0x0B
+#define SDIOD_CCCR_BUSSUSP 0x0C
+#define SDIOD_CCCR_FUNCSEL 0x0D
+#define SDIOD_CCCR_EXECFLAGS 0x0E
+#define SDIOD_CCCR_RDYFLAGS 0x0F
+#define SDIOD_CCCR_BLKSIZE_0 0x10
+#define SDIOD_CCCR_BLKSIZE_1 0x11
+#define SDIOD_CCCR_POWER_CONTROL 0x12
+#define SDIOD_CCCR_SPEED_CONTROL 0x13
+
+/* Broadcom extensions (corerev >= 1) */
+#define SDIOD_CCCR_BRCM_SEPINT 0xf2
+
+/* cccr_sdio_rev */
+#define SDIO_REV_SDIOID_MASK 0xf0 /* SDIO spec revision number */
+#define SDIO_REV_CCCRID_MASK 0x0f /* CCCR format version number */
+
+/* sd_rev */
+#define SD_REV_PHY_MASK 0x0f /* SD format version number */
+
+/* io_en */
+#define SDIO_FUNC_ENABLE_1 0x02 /* function 1 I/O enable */
+#define SDIO_FUNC_ENABLE_2 0x04 /* function 2 I/O enable */
+
+/* io_rdys */
+#define SDIO_FUNC_READY_1 0x02 /* function 1 I/O ready */
+#define SDIO_FUNC_READY_2 0x04 /* function 2 I/O ready */
+
+/* intr_ctl */
+#define INTR_CTL_MASTER_EN 0x1 /* interrupt enable master */
+#define INTR_CTL_FUNC1_EN 0x2 /* interrupt enable for function 1 */
+#define INTR_CTL_FUNC2_EN 0x4 /* interrupt enable for function 2 */
+
+/* intr_status */
+#define INTR_STATUS_FUNC1 0x2 /* interrupt pending for function 1 */
+#define INTR_STATUS_FUNC2 0x4 /* interrupt pending for function 2 */
+
+/* io_abort */
+#define IO_ABORT_RESET_ALL 0x08 /* I/O card reset */
+#define IO_ABORT_FUNC_MASK 0x07 /* abort selction: function x */
+
+/* bus_inter */
+#define BUS_CARD_DETECT_DIS 0x80 /* Card Detect disable */
+#define BUS_SPI_CONT_INTR_CAP 0x40 /* support continuous SPI interrupt */
+#define BUS_SPI_CONT_INTR_EN 0x20 /* continuous SPI interrupt enable */
+#define BUS_SD_DATA_WIDTH_MASK 0x03 /* bus width mask */
+#define BUS_SD_DATA_WIDTH_4BIT 0x02 /* bus width 4-bit mode */
+#define BUS_SD_DATA_WIDTH_1BIT 0x00 /* bus width 1-bit mode */
+
+/* capability */
+#define SDIO_CAP_4BLS 0x80 /* 4-bit support for low speed card */
+#define SDIO_CAP_LSC 0x40 /* low speed card */
+#define SDIO_CAP_E4MI 0x20 /* enable interrupt between block of data in 4-bit mode */
+#define SDIO_CAP_S4MI 0x10 /* support interrupt between block of data in 4-bit mode */
+#define SDIO_CAP_SBS 0x08 /* support suspend/resume */
+#define SDIO_CAP_SRW 0x04 /* support read wait */
+#define SDIO_CAP_SMB 0x02 /* support multi-block transfer */
+#define SDIO_CAP_SDC 0x01 /* Support Direct commands during multi-byte transfer */
+
+/* power_control */
+#define SDIO_POWER_SMPC 0x01 /* supports master power control (RO) */
+#define SDIO_POWER_EMPC 0x02 /* enable master power control (allow > 200mA) (RW) */
+
+/* speed_control (control device entry into high-speed clocking mode) */
+#define SDIO_SPEED_SHS 0x01 /* supports high-speed [clocking] mode (RO) */
+#define SDIO_SPEED_EHS 0x02 /* enable high-speed [clocking] mode (RW) */
+
+/* brcm sepint */
+#define SDIO_SEPINT_MASK 0x01 /* route sdpcmdev intr onto separate pad (chip-specific) */
+#define SDIO_SEPINT_OE 0x02 /* 1 asserts output enable for above pad */
+#define SDIO_SEPINT_ACT_HI 0x04 /* use active high interrupt level instead of active low */
+
+/* FBR structure for function 1-7, FBR addresses and register offsets */
+typedef volatile struct {
+ uint8 devctr; /* device interface, CSA control */
+ uint8 ext_dev; /* extended standard I/O device type code */
+ uint8 pwr_sel; /* power selection support */
+ uint8 PAD[6]; /* reserved */
+
+ uint8 cis_low; /* CIS LSB */
+ uint8 cis_mid;
+ uint8 cis_high; /* CIS MSB */
+ uint8 csa_low; /* code storage area, LSB */
+ uint8 csa_mid;
+ uint8 csa_high; /* code storage area, MSB */
+ uint8 csa_dat_win; /* data access window to function */
+
+ uint8 fnx_blk_size[2]; /* block size, little endian */
+} sdio_fbr_t;
+
+/* Maximum number of I/O funcs */
+#define SDIOD_MAX_IOFUNCS 7
+
+/* SDIO Device FBR Start Address */
+#define SDIOD_FBR_STARTADDR 0x100
+
+/* SDIO Device FBR Size */
+#define SDIOD_FBR_SIZE 0x100
+
+/* Macro to calculate FBR register base */
+#define SDIOD_FBR_BASE(n) ((n) * 0x100)
+
+/* Function register offsets */
+#define SDIOD_FBR_DEVCTR 0x00 /* basic info for function */
+#define SDIOD_FBR_EXT_DEV 0x01 /* extended I/O device code */
+#define SDIOD_FBR_PWR_SEL 0x02 /* power selection bits */
+
+/* SDIO Function CIS ptr offset */
+#define SDIOD_FBR_CISPTR_0 0x09
+#define SDIOD_FBR_CISPTR_1 0x0A
+#define SDIOD_FBR_CISPTR_2 0x0B
+
+/* Code Storage Area pointer */
+#define SDIOD_FBR_CSA_ADDR_0 0x0C
+#define SDIOD_FBR_CSA_ADDR_1 0x0D
+#define SDIOD_FBR_CSA_ADDR_2 0x0E
+#define SDIOD_FBR_CSA_DATA 0x0F
+
+/* SDIO Function I/O Block Size */
+#define SDIOD_FBR_BLKSIZE_0 0x10
+#define SDIOD_FBR_BLKSIZE_1 0x11
+
+/* devctr */
+#define SDIOD_FBR_DEVCTR_DIC 0x0f /* device interface code */
+#define SDIOD_FBR_DECVTR_CSA 0x40 /* CSA support flag */
+#define SDIOD_FBR_DEVCTR_CSA_EN 0x80 /* CSA enabled */
+/* interface codes */
+#define SDIOD_DIC_NONE 0 /* SDIO standard interface is not supported */
+#define SDIOD_DIC_UART 1
+#define SDIOD_DIC_BLUETOOTH_A 2
+#define SDIOD_DIC_BLUETOOTH_B 3
+#define SDIOD_DIC_GPS 4
+#define SDIOD_DIC_CAMERA 5
+#define SDIOD_DIC_PHS 6
+#define SDIOD_DIC_WLAN 7
+#define SDIOD_DIC_EXT 0xf /* extended device interface, read ext_dev register */
+
+/* pwr_sel */
+#define SDIOD_PWR_SEL_SPS 0x01 /* supports power selection */
+#define SDIOD_PWR_SEL_EPS 0x02 /* enable power selection (low-current mode) */
+
+/* misc defines */
+#define SDIO_FUNC_0 0
+#define SDIO_FUNC_1 1
+#define SDIO_FUNC_2 2
+#define SDIO_FUNC_3 3
+#define SDIO_FUNC_4 4
+#define SDIO_FUNC_5 5
+#define SDIO_FUNC_6 6
+#define SDIO_FUNC_7 7
+
+#define SD_CARD_TYPE_UNKNOWN 0 /* bad type or unrecognized */
+#define SD_CARD_TYPE_IO 1 /* IO only card */
+#define SD_CARD_TYPE_MEMORY 2 /* memory only card */
+#define SD_CARD_TYPE_COMBO 3 /* IO and memory combo card */
+
+#define SDIO_MAX_BLOCK_SIZE 2048 /* maximum block size for block mode operation */
+#define SDIO_MIN_BLOCK_SIZE 1 /* minimum block size for block mode operation */
+
+/* Card registers: status bit position */
+#define CARDREG_STATUS_BIT_OUTOFRANGE 31
+#define CARDREG_STATUS_BIT_COMCRCERROR 23
+#define CARDREG_STATUS_BIT_ILLEGALCOMMAND 22
+#define CARDREG_STATUS_BIT_ERROR 19
+#define CARDREG_STATUS_BIT_IOCURRENTSTATE3 12
+#define CARDREG_STATUS_BIT_IOCURRENTSTATE2 11
+#define CARDREG_STATUS_BIT_IOCURRENTSTATE1 10
+#define CARDREG_STATUS_BIT_IOCURRENTSTATE0 9
+#define CARDREG_STATUS_BIT_FUN_NUM_ERROR 4
+
+
+
+#define SD_CMD_GO_IDLE_STATE 0 /* mandatory for SDIO */
+#define SD_CMD_SEND_OPCOND 1
+#define SD_CMD_MMC_SET_RCA 3
+#define SD_CMD_IO_SEND_OP_COND 5 /* mandatory for SDIO */
+#define SD_CMD_SELECT_DESELECT_CARD 7
+#define SD_CMD_SEND_CSD 9
+#define SD_CMD_SEND_CID 10
+#define SD_CMD_STOP_TRANSMISSION 12
+#define SD_CMD_SEND_STATUS 13
+#define SD_CMD_GO_INACTIVE_STATE 15
+#define SD_CMD_SET_BLOCKLEN 16
+#define SD_CMD_READ_SINGLE_BLOCK 17
+#define SD_CMD_READ_MULTIPLE_BLOCK 18
+#define SD_CMD_WRITE_BLOCK 24
+#define SD_CMD_WRITE_MULTIPLE_BLOCK 25
+#define SD_CMD_PROGRAM_CSD 27
+#define SD_CMD_SET_WRITE_PROT 28
+#define SD_CMD_CLR_WRITE_PROT 29
+#define SD_CMD_SEND_WRITE_PROT 30
+#define SD_CMD_ERASE_WR_BLK_START 32
+#define SD_CMD_ERASE_WR_BLK_END 33
+#define SD_CMD_ERASE 38
+#define SD_CMD_LOCK_UNLOCK 42
+#define SD_CMD_IO_RW_DIRECT 52 /* mandatory for SDIO */
+#define SD_CMD_IO_RW_EXTENDED 53 /* mandatory for SDIO */
+#define SD_CMD_APP_CMD 55
+#define SD_CMD_GEN_CMD 56
+#define SD_CMD_READ_OCR 58
+#define SD_CMD_CRC_ON_OFF 59 /* mandatory for SDIO */
+#define SD_ACMD_SD_STATUS 13
+#define SD_ACMD_SEND_NUM_WR_BLOCKS 22
+#define SD_ACMD_SET_WR_BLOCK_ERASE_CNT 23
+#define SD_ACMD_SD_SEND_OP_COND 41
+#define SD_ACMD_SET_CLR_CARD_DETECT 42
+#define SD_ACMD_SEND_SCR 51
+
+/* argument for SD_CMD_IO_RW_DIRECT and SD_CMD_IO_RW_EXTENDED */
+#define SD_IO_OP_READ 0 /* Read_Write: Read */
+#define SD_IO_OP_WRITE 1 /* Read_Write: Write */
+#define SD_IO_RW_NORMAL 0 /* no RAW */
+#define SD_IO_RW_RAW 1 /* RAW */
+#define SD_IO_BYTE_MODE 0 /* Byte Mode */
+#define SD_IO_BLOCK_MODE 1 /* BlockMode */
+#define SD_IO_FIXED_ADDRESS 0 /* fix Address */
+#define SD_IO_INCREMENT_ADDRESS 1 /* IncrementAddress */
+
+/* build SD_CMD_IO_RW_DIRECT Argument */
+#define SDIO_IO_RW_DIRECT_ARG(rw, raw, func, addr, data) \
+ ((((rw) & 1) << 31) | (((func) & 0x7) << 28) | (((raw) & 1) << 27) | \
+ (((addr) & 0x1FFFF) << 9) | ((data) & 0xFF))
+
+/* build SD_CMD_IO_RW_EXTENDED Argument */
+#define SDIO_IO_RW_EXTENDED_ARG(rw, blk, func, addr, inc_addr, count) \
+ ((((rw) & 1) << 31) | (((func) & 0x7) << 28) | (((blk) & 1) << 27) | \
+ (((inc_addr) & 1) << 26) | (((addr) & 0x1FFFF) << 9) | ((count) & 0x1FF))
+
+/* SDIO response parameters */
+#define SD_RSP_NO_NONE 0
+#define SD_RSP_NO_1 1
+#define SD_RSP_NO_2 2
+#define SD_RSP_NO_3 3
+#define SD_RSP_NO_4 4
+#define SD_RSP_NO_5 5
+#define SD_RSP_NO_6 6
+
+ /* Modified R6 response (to CMD3) */
+#define SD_RSP_MR6_COM_CRC_ERROR 0x8000
+#define SD_RSP_MR6_ILLEGAL_COMMAND 0x4000
+#define SD_RSP_MR6_ERROR 0x2000
+
+ /* Modified R1 in R4 Response (to CMD5) */
+#define SD_RSP_MR1_SBIT 0x80
+#define SD_RSP_MR1_PARAMETER_ERROR 0x40
+#define SD_RSP_MR1_RFU5 0x20
+#define SD_RSP_MR1_FUNC_NUM_ERROR 0x10
+#define SD_RSP_MR1_COM_CRC_ERROR 0x08
+#define SD_RSP_MR1_ILLEGAL_COMMAND 0x04
+#define SD_RSP_MR1_RFU1 0x02
+#define SD_RSP_MR1_IDLE_STATE 0x01
+
+ /* R5 response (to CMD52 and CMD53) */
+#define SD_RSP_R5_COM_CRC_ERROR 0x80
+#define SD_RSP_R5_ILLEGAL_COMMAND 0x40
+#define SD_RSP_R5_IO_CURRENTSTATE1 0x20
+#define SD_RSP_R5_IO_CURRENTSTATE0 0x10
+#define SD_RSP_R5_ERROR 0x08
+#define SD_RSP_R5_RFU 0x04
+#define SD_RSP_R5_FUNC_NUM_ERROR 0x02
+#define SD_RSP_R5_OUT_OF_RANGE 0x01
+
+#define SD_RSP_R5_ERRBITS 0xCB
+
+
+/* ------------------------------------------------
+ * SDIO Commands and responses
+ *
+ * I/O only commands are:
+ * CMD0, CMD3, CMD5, CMD7, CMD15, CMD52, CMD53
+ * ------------------------------------------------
+ */
+
+/* SDIO Commands */
+#define SDIOH_CMD_0 0
+#define SDIOH_CMD_3 3
+#define SDIOH_CMD_5 5
+#define SDIOH_CMD_7 7
+#define SDIOH_CMD_15 15
+#define SDIOH_CMD_52 52
+#define SDIOH_CMD_53 53
+#define SDIOH_CMD_59 59
+
+/* SDIO Command Responses */
+#define SDIOH_RSP_NONE 0
+#define SDIOH_RSP_R1 1
+#define SDIOH_RSP_R2 2
+#define SDIOH_RSP_R3 3
+#define SDIOH_RSP_R4 4
+#define SDIOH_RSP_R5 5
+#define SDIOH_RSP_R6 6
+
+/*
+ * SDIO Response Error flags
+ */
+#define SDIOH_RSP5_ERROR_FLAGS 0xCB
+
+/* ------------------------------------------------
+ * SDIO Command structures. I/O only commands are:
+ *
+ * CMD0, CMD3, CMD5, CMD7, CMD15, CMD52, CMD53
+ * ------------------------------------------------
+ */
+
+#define CMD5_OCR_M BITFIELD_MASK(24)
+#define CMD5_OCR_S 0
+
+#define CMD7_RCA_M BITFIELD_MASK(16)
+#define CMD7_RCA_S 16
+
+#define CMD_15_RCA_M BITFIELD_MASK(16)
+#define CMD_15_RCA_S 16
+
+#define CMD52_DATA_M BITFIELD_MASK(8) /* Bits [7:0] - Write Data/Stuff bits of CMD52
+ */
+#define CMD52_DATA_S 0
+#define CMD52_REG_ADDR_M BITFIELD_MASK(17) /* Bits [25:9] - register address */
+#define CMD52_REG_ADDR_S 9
+#define CMD52_RAW_M BITFIELD_MASK(1) /* Bit 27 - Read after Write flag */
+#define CMD52_RAW_S 27
+#define CMD52_FUNCTION_M BITFIELD_MASK(3) /* Bits [30:28] - Function number */
+#define CMD52_FUNCTION_S 28
+#define CMD52_RW_FLAG_M BITFIELD_MASK(1) /* Bit 31 - R/W flag */
+#define CMD52_RW_FLAG_S 31
+
+
+#define CMD53_BYTE_BLK_CNT_M BITFIELD_MASK(9) /* Bits [8:0] - Byte/Block Count of CMD53 */
+#define CMD53_BYTE_BLK_CNT_S 0
+#define CMD53_REG_ADDR_M BITFIELD_MASK(17) /* Bits [25:9] - register address */
+#define CMD53_REG_ADDR_S 9
+#define CMD53_OP_CODE_M BITFIELD_MASK(1) /* Bit 26 - R/W Operation Code */
+#define CMD53_OP_CODE_S 26
+#define CMD53_BLK_MODE_M BITFIELD_MASK(1) /* Bit 27 - Block Mode */
+#define CMD53_BLK_MODE_S 27
+#define CMD53_FUNCTION_M BITFIELD_MASK(3) /* Bits [30:28] - Function number */
+#define CMD53_FUNCTION_S 28
+#define CMD53_RW_FLAG_M BITFIELD_MASK(1) /* Bit 31 - R/W flag */
+#define CMD53_RW_FLAG_S 31
+
+/* ------------------------------------------------------
+ * SDIO Command Response structures for SD1 and SD4 modes
+ * -----------------------------------------------------
+ */
+#define RSP4_IO_OCR_M BITFIELD_MASK(24) /* Bits [23:0] - Card's OCR Bits [23:0] */
+#define RSP4_IO_OCR_S 0
+#define RSP4_STUFF_M BITFIELD_MASK(3) /* Bits [26:24] - Stuff bits */
+#define RSP4_STUFF_S 24
+#define RSP4_MEM_PRESENT_M BITFIELD_MASK(1) /* Bit 27 - Memory present */
+#define RSP4_MEM_PRESENT_S 27
+#define RSP4_NUM_FUNCS_M BITFIELD_MASK(3) /* Bits [30:28] - Number of I/O funcs */
+#define RSP4_NUM_FUNCS_S 28
+#define RSP4_CARD_READY_M BITFIELD_MASK(1) /* Bit 31 - SDIO card ready */
+#define RSP4_CARD_READY_S 31
+
+#define RSP6_STATUS_M BITFIELD_MASK(16) /* Bits [15:0] - Card status bits [19,22,23,12:0]
+ */
+#define RSP6_STATUS_S 0
+#define RSP6_IO_RCA_M BITFIELD_MASK(16) /* Bits [31:16] - RCA bits[31-16] */
+#define RSP6_IO_RCA_S 16
+
+#define RSP1_AKE_SEQ_ERROR_M BITFIELD_MASK(1) /* Bit 3 - Authentication seq error */
+#define RSP1_AKE_SEQ_ERROR_S 3
+#define RSP1_APP_CMD_M BITFIELD_MASK(1) /* Bit 5 - Card expects ACMD */
+#define RSP1_APP_CMD_S 5
+#define RSP1_READY_FOR_DATA_M BITFIELD_MASK(1) /* Bit 8 - Ready for data (buff empty) */
+#define RSP1_READY_FOR_DATA_S 8
+#define RSP1_CURR_STATE_M BITFIELD_MASK(4) /* Bits [12:9] - State of card
+ * when Cmd was received
+ */
+#define RSP1_CURR_STATE_S 9
+#define RSP1_EARSE_RESET_M BITFIELD_MASK(1) /* Bit 13 - Erase seq cleared */
+#define RSP1_EARSE_RESET_S 13
+#define RSP1_CARD_ECC_DISABLE_M BITFIELD_MASK(1) /* Bit 14 - Card ECC disabled */
+#define RSP1_CARD_ECC_DISABLE_S 14
+#define RSP1_WP_ERASE_SKIP_M BITFIELD_MASK(1) /* Bit 15 - Partial blocks erased due to W/P */
+#define RSP1_WP_ERASE_SKIP_S 15
+#define RSP1_CID_CSD_OVERW_M BITFIELD_MASK(1) /* Bit 16 - Illegal write to CID or R/O bits
+ * of CSD
+ */
+#define RSP1_CID_CSD_OVERW_S 16
+#define RSP1_ERROR_M BITFIELD_MASK(1) /* Bit 19 - General/Unknown error */
+#define RSP1_ERROR_S 19
+#define RSP1_CC_ERROR_M BITFIELD_MASK(1) /* Bit 20 - Internal Card Control error */
+#define RSP1_CC_ERROR_S 20
+#define RSP1_CARD_ECC_FAILED_M BITFIELD_MASK(1) /* Bit 21 - Card internal ECC failed
+ * to correct data
+ */
+#define RSP1_CARD_ECC_FAILED_S 21
+#define RSP1_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 22 - Cmd not legal for the card state */
+#define RSP1_ILLEGAL_CMD_S 22
+#define RSP1_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 23 - CRC check of previous command failed
+ */
+#define RSP1_COM_CRC_ERROR_S 23
+#define RSP1_LOCK_UNLOCK_FAIL_M BITFIELD_MASK(1) /* Bit 24 - Card lock-unlock Cmd Seq error */
+#define RSP1_LOCK_UNLOCK_FAIL_S 24
+#define RSP1_CARD_LOCKED_M BITFIELD_MASK(1) /* Bit 25 - Card locked by the host */
+#define RSP1_CARD_LOCKED_S 25
+#define RSP1_WP_VIOLATION_M BITFIELD_MASK(1) /* Bit 26 - Attempt to program
+ * write-protected blocks
+ */
+#define RSP1_WP_VIOLATION_S 26
+#define RSP1_ERASE_PARAM_M BITFIELD_MASK(1) /* Bit 27 - Invalid erase blocks */
+#define RSP1_ERASE_PARAM_S 27
+#define RSP1_ERASE_SEQ_ERR_M BITFIELD_MASK(1) /* Bit 28 - Erase Cmd seq error */
+#define RSP1_ERASE_SEQ_ERR_S 28
+#define RSP1_BLK_LEN_ERR_M BITFIELD_MASK(1) /* Bit 29 - Block length error */
+#define RSP1_BLK_LEN_ERR_S 29
+#define RSP1_ADDR_ERR_M BITFIELD_MASK(1) /* Bit 30 - Misaligned address */
+#define RSP1_ADDR_ERR_S 30
+#define RSP1_OUT_OF_RANGE_M BITFIELD_MASK(1) /* Bit 31 - Cmd arg was out of range */
+#define RSP1_OUT_OF_RANGE_S 31
+
+
+#define RSP5_DATA_M BITFIELD_MASK(8) /* Bits [0:7] - data */
+#define RSP5_DATA_S 0
+#define RSP5_FLAGS_M BITFIELD_MASK(8) /* Bit [15:8] - Rsp flags */
+#define RSP5_FLAGS_S 8
+#define RSP5_STUFF_M BITFIELD_MASK(16) /* Bits [31:16] - Stuff bits */
+#define RSP5_STUFF_S 16
+
+/* ----------------------------------------------
+ * SDIO Command Response structures for SPI mode
+ * ----------------------------------------------
+ */
+#define SPIRSP4_IO_OCR_M BITFIELD_MASK(16) /* Bits [15:0] - Card's OCR Bits [23:8] */
+#define SPIRSP4_IO_OCR_S 0
+#define SPIRSP4_STUFF_M BITFIELD_MASK(3) /* Bits [18:16] - Stuff bits */
+#define SPIRSP4_STUFF_S 16
+#define SPIRSP4_MEM_PRESENT_M BITFIELD_MASK(1) /* Bit 19 - Memory present */
+#define SPIRSP4_MEM_PRESENT_S 19
+#define SPIRSP4_NUM_FUNCS_M BITFIELD_MASK(3) /* Bits [22:20] - Number of I/O funcs */
+#define SPIRSP4_NUM_FUNCS_S 20
+#define SPIRSP4_CARD_READY_M BITFIELD_MASK(1) /* Bit 23 - SDIO card ready */
+#define SPIRSP4_CARD_READY_S 23
+#define SPIRSP4_IDLE_STATE_M BITFIELD_MASK(1) /* Bit 24 - idle state */
+#define SPIRSP4_IDLE_STATE_S 24
+#define SPIRSP4_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 26 - Illegal Cmd error */
+#define SPIRSP4_ILLEGAL_CMD_S 26
+#define SPIRSP4_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 27 - COM CRC error */
+#define SPIRSP4_COM_CRC_ERROR_S 27
+#define SPIRSP4_FUNC_NUM_ERROR_M BITFIELD_MASK(1) /* Bit 28 - Function number error
+ */
+#define SPIRSP4_FUNC_NUM_ERROR_S 28
+#define SPIRSP4_PARAM_ERROR_M BITFIELD_MASK(1) /* Bit 30 - Parameter Error Bit */
+#define SPIRSP4_PARAM_ERROR_S 30
+#define SPIRSP4_START_BIT_M BITFIELD_MASK(1) /* Bit 31 - Start Bit */
+#define SPIRSP4_START_BIT_S 31
+
+#define SPIRSP5_DATA_M BITFIELD_MASK(8) /* Bits [23:16] - R/W Data */
+#define SPIRSP5_DATA_S 16
+#define SPIRSP5_IDLE_STATE_M BITFIELD_MASK(1) /* Bit 24 - Idle state */
+#define SPIRSP5_IDLE_STATE_S 24
+#define SPIRSP5_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 26 - Illegal Cmd error */
+#define SPIRSP5_ILLEGAL_CMD_S 26
+#define SPIRSP5_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 27 - COM CRC error */
+#define SPIRSP5_COM_CRC_ERROR_S 27
+#define SPIRSP5_FUNC_NUM_ERROR_M BITFIELD_MASK(1) /* Bit 28 - Function number error
+ */
+#define SPIRSP5_FUNC_NUM_ERROR_S 28
+#define SPIRSP5_PARAM_ERROR_M BITFIELD_MASK(1) /* Bit 30 - Parameter Error Bit */
+#define SPIRSP5_PARAM_ERROR_S 30
+#define SPIRSP5_START_BIT_M BITFIELD_MASK(1) /* Bit 31 - Start Bit */
+#define SPIRSP5_START_BIT_S 31
+
+/* RSP6 card status format; Pg 68 Physical Layer spec v 1.10 */
+#define RSP6STAT_AKE_SEQ_ERROR_M BITFIELD_MASK(1) /* Bit 3 - Authentication seq error
+ */
+#define RSP6STAT_AKE_SEQ_ERROR_S 3
+#define RSP6STAT_APP_CMD_M BITFIELD_MASK(1) /* Bit 5 - Card expects ACMD */
+#define RSP6STAT_APP_CMD_S 5
+#define RSP6STAT_READY_FOR_DATA_M BITFIELD_MASK(1) /* Bit 8 - Ready for data
+ * (buff empty)
+ */
+#define RSP6STAT_READY_FOR_DATA_S 8
+#define RSP6STAT_CURR_STATE_M BITFIELD_MASK(4) /* Bits [12:9] - Card state at
+ * Cmd reception
+ */
+#define RSP6STAT_CURR_STATE_S 9
+#define RSP6STAT_ERROR_M BITFIELD_MASK(1) /* Bit 13 - General/Unknown error Bit 19
+ */
+#define RSP6STAT_ERROR_S 13
+#define RSP6STAT_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 14 - Illegal cmd for
+ * card state Bit 22
+ */
+#define RSP6STAT_ILLEGAL_CMD_S 14
+#define RSP6STAT_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 15 - CRC previous command
+ * failed Bit 23
+ */
+#define RSP6STAT_COM_CRC_ERROR_S 15
+
+#define SDIOH_XFER_TYPE_READ SD_IO_OP_READ
+#define SDIOH_XFER_TYPE_WRITE SD_IO_OP_WRITE
+
+#endif /* _SDIO_H */
diff --git a/drivers/net/wireless/bcm4329/include/sdioh.h b/drivers/net/wireless/bcm4329/include/sdioh.h
new file mode 100644
index 00000000000..8123452eac2
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sdioh.h
@@ -0,0 +1,299 @@
+/*
+ * SDIO Host Controller Spec header file
+ * Register map and definitions for the Standard Host Controller
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sdioh.h,v 13.13.18.1.16.3 2009/12/08 22:34:21 Exp $
+ */
+
+#ifndef _SDIOH_H
+#define _SDIOH_H
+
+#define SD_SysAddr 0x000
+#define SD_BlockSize 0x004
+#define SD_BlockCount 0x006
+#define SD_Arg0 0x008
+#define SD_Arg1 0x00A
+#define SD_TransferMode 0x00C
+#define SD_Command 0x00E
+#define SD_Response0 0x010
+#define SD_Response1 0x012
+#define SD_Response2 0x014
+#define SD_Response3 0x016
+#define SD_Response4 0x018
+#define SD_Response5 0x01A
+#define SD_Response6 0x01C
+#define SD_Response7 0x01E
+#define SD_BufferDataPort0 0x020
+#define SD_BufferDataPort1 0x022
+#define SD_PresentState 0x024
+#define SD_HostCntrl 0x028
+#define SD_PwrCntrl 0x029
+#define SD_BlockGapCntrl 0x02A
+#define SD_WakeupCntrl 0x02B
+#define SD_ClockCntrl 0x02C
+#define SD_TimeoutCntrl 0x02E
+#define SD_SoftwareReset 0x02F
+#define SD_IntrStatus 0x030
+#define SD_ErrorIntrStatus 0x032
+#define SD_IntrStatusEnable 0x034
+#define SD_ErrorIntrStatusEnable 0x036
+#define SD_IntrSignalEnable 0x038
+#define SD_ErrorIntrSignalEnable 0x03A
+#define SD_CMD12ErrorStatus 0x03C
+#define SD_Capabilities 0x040
+#define SD_Capabilities_Reserved 0x044
+#define SD_MaxCurCap 0x048
+#define SD_MaxCurCap_Reserved 0x04C
+#define SD_ADMA_SysAddr 0x58
+#define SD_SlotInterruptStatus 0x0FC
+#define SD_HostControllerVersion 0x0FE
+
+/* SD specific registers in PCI config space */
+#define SD_SlotInfo 0x40
+
+/* SD_Capabilities reg (0x040) */
+#define CAP_TO_CLKFREQ_M BITFIELD_MASK(6)
+#define CAP_TO_CLKFREQ_S 0
+#define CAP_TO_CLKUNIT_M BITFIELD_MASK(1)
+#define CAP_TO_CLKUNIT_S 7
+#define CAP_BASECLK_M BITFIELD_MASK(6)
+#define CAP_BASECLK_S 8
+#define CAP_MAXBLOCK_M BITFIELD_MASK(2)
+#define CAP_MAXBLOCK_S 16
+#define CAP_ADMA2_M BITFIELD_MASK(1)
+#define CAP_ADMA2_S 19
+#define CAP_ADMA1_M BITFIELD_MASK(1)
+#define CAP_ADMA1_S 20
+#define CAP_HIGHSPEED_M BITFIELD_MASK(1)
+#define CAP_HIGHSPEED_S 21
+#define CAP_DMA_M BITFIELD_MASK(1)
+#define CAP_DMA_S 22
+#define CAP_SUSPEND_M BITFIELD_MASK(1)
+#define CAP_SUSPEND_S 23
+#define CAP_VOLT_3_3_M BITFIELD_MASK(1)
+#define CAP_VOLT_3_3_S 24
+#define CAP_VOLT_3_0_M BITFIELD_MASK(1)
+#define CAP_VOLT_3_0_S 25
+#define CAP_VOLT_1_8_M BITFIELD_MASK(1)
+#define CAP_VOLT_1_8_S 26
+#define CAP_64BIT_HOST_M BITFIELD_MASK(1)
+#define CAP_64BIT_HOST_S 28
+
+/* SD_MaxCurCap reg (0x048) */
+#define CAP_CURR_3_3_M BITFIELD_MASK(8)
+#define CAP_CURR_3_3_S 0
+#define CAP_CURR_3_0_M BITFIELD_MASK(8)
+#define CAP_CURR_3_0_S 8
+#define CAP_CURR_1_8_M BITFIELD_MASK(8)
+#define CAP_CURR_1_8_S 16
+
+/* SD_SysAddr: Offset 0x0000, Size 4 bytes */
+
+/* SD_BlockSize: Offset 0x004, Size 2 bytes */
+#define BLKSZ_BLKSZ_M BITFIELD_MASK(12)
+#define BLKSZ_BLKSZ_S 0
+#define BLKSZ_BNDRY_M BITFIELD_MASK(3)
+#define BLKSZ_BNDRY_S 12
+
+/* SD_BlockCount: Offset 0x006, size 2 bytes */
+
+/* SD_Arg0: Offset 0x008, size = 4 bytes */
+/* SD_TransferMode Offset 0x00C, size = 2 bytes */
+#define XFER_DMA_ENABLE_M BITFIELD_MASK(1)
+#define XFER_DMA_ENABLE_S 0
+#define XFER_BLK_COUNT_EN_M BITFIELD_MASK(1)
+#define XFER_BLK_COUNT_EN_S 1
+#define XFER_CMD_12_EN_M BITFIELD_MASK(1)
+#define XFER_CMD_12_EN_S 2
+#define XFER_DATA_DIRECTION_M BITFIELD_MASK(1)
+#define XFER_DATA_DIRECTION_S 4
+#define XFER_MULTI_BLOCK_M BITFIELD_MASK(1)
+#define XFER_MULTI_BLOCK_S 5
+
+/* SD_Command: Offset 0x00E, size = 2 bytes */
+/* resp_type field */
+#define RESP_TYPE_NONE 0
+#define RESP_TYPE_136 1
+#define RESP_TYPE_48 2
+#define RESP_TYPE_48_BUSY 3
+/* type field */
+#define CMD_TYPE_NORMAL 0
+#define CMD_TYPE_SUSPEND 1
+#define CMD_TYPE_RESUME 2
+#define CMD_TYPE_ABORT 3
+
+#define CMD_RESP_TYPE_M BITFIELD_MASK(2) /* Bits [0-1] - Response type */
+#define CMD_RESP_TYPE_S 0
+#define CMD_CRC_EN_M BITFIELD_MASK(1) /* Bit 3 - CRC enable */
+#define CMD_CRC_EN_S 3
+#define CMD_INDEX_EN_M BITFIELD_MASK(1) /* Bit 4 - Enable index checking */
+#define CMD_INDEX_EN_S 4
+#define CMD_DATA_EN_M BITFIELD_MASK(1) /* Bit 5 - Using DAT line */
+#define CMD_DATA_EN_S 5
+#define CMD_TYPE_M BITFIELD_MASK(2) /* Bit [6-7] - Normal, abort, resume, etc
+ */
+#define CMD_TYPE_S 6
+#define CMD_INDEX_M BITFIELD_MASK(6) /* Bits [8-13] - Command number */
+#define CMD_INDEX_S 8
+
+/* SD_BufferDataPort0 : Offset 0x020, size = 2 or 4 bytes */
+/* SD_BufferDataPort1 : Offset 0x022, size = 2 bytes */
+/* SD_PresentState : Offset 0x024, size = 4 bytes */
+#define PRES_CMD_INHIBIT_M BITFIELD_MASK(1) /* Bit 0 May use CMD */
+#define PRES_CMD_INHIBIT_S 0
+#define PRES_DAT_INHIBIT_M BITFIELD_MASK(1) /* Bit 1 May use DAT */
+#define PRES_DAT_INHIBIT_S 1
+#define PRES_DAT_BUSY_M BITFIELD_MASK(1) /* Bit 2 DAT is busy */
+#define PRES_DAT_BUSY_S 2
+#define PRES_PRESENT_RSVD_M BITFIELD_MASK(5) /* Bit [3-7] rsvd */
+#define PRES_PRESENT_RSVD_S 3
+#define PRES_WRITE_ACTIVE_M BITFIELD_MASK(1) /* Bit 8 Write is active */
+#define PRES_WRITE_ACTIVE_S 8
+#define PRES_READ_ACTIVE_M BITFIELD_MASK(1) /* Bit 9 Read is active */
+#define PRES_READ_ACTIVE_S 9
+#define PRES_WRITE_DATA_RDY_M BITFIELD_MASK(1) /* Bit 10 Write buf is avail */
+#define PRES_WRITE_DATA_RDY_S 10
+#define PRES_READ_DATA_RDY_M BITFIELD_MASK(1) /* Bit 11 Read buf data avail */
+#define PRES_READ_DATA_RDY_S 11
+#define PRES_CARD_PRESENT_M BITFIELD_MASK(1) /* Bit 16 Card present - debounced */
+#define PRES_CARD_PRESENT_S 16
+#define PRES_CARD_STABLE_M BITFIELD_MASK(1) /* Bit 17 Debugging */
+#define PRES_CARD_STABLE_S 17
+#define PRES_CARD_PRESENT_RAW_M BITFIELD_MASK(1) /* Bit 18 Not debounced */
+#define PRES_CARD_PRESENT_RAW_S 18
+#define PRES_WRITE_ENABLED_M BITFIELD_MASK(1) /* Bit 19 Write protected? */
+#define PRES_WRITE_ENABLED_S 19
+#define PRES_DAT_SIGNAL_M BITFIELD_MASK(4) /* Bit [20-23] Debugging */
+#define PRES_DAT_SIGNAL_S 20
+#define PRES_CMD_SIGNAL_M BITFIELD_MASK(1) /* Bit 24 Debugging */
+#define PRES_CMD_SIGNAL_S 24
+
+/* SD_HostCntrl: Offset 0x028, size = 1 bytes */
+#define HOST_LED_M BITFIELD_MASK(1) /* Bit 0 LED On/Off */
+#define HOST_LED_S 0
+#define HOST_DATA_WIDTH_M BITFIELD_MASK(1) /* Bit 1 4 bit enable */
+#define HOST_DATA_WIDTH_S 1
+#define HOST_HI_SPEED_EN_M BITFIELD_MASK(1) /* Bit 2 High speed vs low speed */
+#define HOST_DMA_SEL_S 3
+#define HOST_DMA_SEL_M BITFIELD_MASK(2) /* Bit 4:3 DMA Select */
+#define HOST_HI_SPEED_EN_S 2
+
+/* misc defines */
+#define SD1_MODE 0x1 /* SD Host Cntrlr Spec */
+#define SD4_MODE 0x2 /* SD Host Cntrlr Spec */
+
+/* SD_PwrCntrl: Offset 0x029, size = 1 bytes */
+#define PWR_BUS_EN_M BITFIELD_MASK(1) /* Bit 0 Power the bus */
+#define PWR_BUS_EN_S 0
+#define PWR_VOLTS_M BITFIELD_MASK(3) /* Bit [1-3] Voltage Select */
+#define PWR_VOLTS_S 1
+
+/* SD_SoftwareReset: Offset 0x02F, size = 1 byte */
+#define SW_RESET_ALL_M BITFIELD_MASK(1) /* Bit 0 Reset All */
+#define SW_RESET_ALL_S 0
+#define SW_RESET_CMD_M BITFIELD_MASK(1) /* Bit 1 CMD Line Reset */
+#define SW_RESET_CMD_S 1
+#define SW_RESET_DAT_M BITFIELD_MASK(1) /* Bit 2 DAT Line Reset */
+#define SW_RESET_DAT_S 2
+
+/* SD_IntrStatus: Offset 0x030, size = 2 bytes */
+/* Defs also serve SD_IntrStatusEnable and SD_IntrSignalEnable */
+#define INTSTAT_CMD_COMPLETE_M BITFIELD_MASK(1) /* Bit 0 */
+#define INTSTAT_CMD_COMPLETE_S 0
+#define INTSTAT_XFER_COMPLETE_M BITFIELD_MASK(1)
+#define INTSTAT_XFER_COMPLETE_S 1
+#define INTSTAT_BLOCK_GAP_EVENT_M BITFIELD_MASK(1)
+#define INTSTAT_BLOCK_GAP_EVENT_S 2
+#define INTSTAT_DMA_INT_M BITFIELD_MASK(1)
+#define INTSTAT_DMA_INT_S 3
+#define INTSTAT_BUF_WRITE_READY_M BITFIELD_MASK(1)
+#define INTSTAT_BUF_WRITE_READY_S 4
+#define INTSTAT_BUF_READ_READY_M BITFIELD_MASK(1)
+#define INTSTAT_BUF_READ_READY_S 5
+#define INTSTAT_CARD_INSERTION_M BITFIELD_MASK(1)
+#define INTSTAT_CARD_INSERTION_S 6
+#define INTSTAT_CARD_REMOVAL_M BITFIELD_MASK(1)
+#define INTSTAT_CARD_REMOVAL_S 7
+#define INTSTAT_CARD_INT_M BITFIELD_MASK(1)
+#define INTSTAT_CARD_INT_S 8
+#define INTSTAT_ERROR_INT_M BITFIELD_MASK(1) /* Bit 15 */
+#define INTSTAT_ERROR_INT_S 15
+
+/* SD_ErrorIntrStatus: Offset 0x032, size = 2 bytes */
+/* Defs also serve SD_ErrorIntrStatusEnable and SD_ErrorIntrSignalEnable */
+#define ERRINT_CMD_TIMEOUT_M BITFIELD_MASK(1)
+#define ERRINT_CMD_TIMEOUT_S 0
+#define ERRINT_CMD_CRC_M BITFIELD_MASK(1)
+#define ERRINT_CMD_CRC_S 1
+#define ERRINT_CMD_ENDBIT_M BITFIELD_MASK(1)
+#define ERRINT_CMD_ENDBIT_S 2
+#define ERRINT_CMD_INDEX_M BITFIELD_MASK(1)
+#define ERRINT_CMD_INDEX_S 3
+#define ERRINT_DATA_TIMEOUT_M BITFIELD_MASK(1)
+#define ERRINT_DATA_TIMEOUT_S 4
+#define ERRINT_DATA_CRC_M BITFIELD_MASK(1)
+#define ERRINT_DATA_CRC_S 5
+#define ERRINT_DATA_ENDBIT_M BITFIELD_MASK(1)
+#define ERRINT_DATA_ENDBIT_S 6
+#define ERRINT_CURRENT_LIMIT_M BITFIELD_MASK(1)
+#define ERRINT_CURRENT_LIMIT_S 7
+#define ERRINT_AUTO_CMD12_M BITFIELD_MASK(1)
+#define ERRINT_AUTO_CMD12_S 8
+#define ERRINT_VENDOR_M BITFIELD_MASK(4)
+#define ERRINT_VENDOR_S 12
+
+/* Also provide definitions in "normal" form to allow combined masks */
+#define ERRINT_CMD_TIMEOUT_BIT 0x0001
+#define ERRINT_CMD_CRC_BIT 0x0002
+#define ERRINT_CMD_ENDBIT_BIT 0x0004
+#define ERRINT_CMD_INDEX_BIT 0x0008
+#define ERRINT_DATA_TIMEOUT_BIT 0x0010
+#define ERRINT_DATA_CRC_BIT 0x0020
+#define ERRINT_DATA_ENDBIT_BIT 0x0040
+#define ERRINT_CURRENT_LIMIT_BIT 0x0080
+#define ERRINT_AUTO_CMD12_BIT 0x0100
+
+/* Masks to select CMD vs. DATA errors */
+#define ERRINT_CMD_ERRS (ERRINT_CMD_TIMEOUT_BIT | ERRINT_CMD_CRC_BIT |\
+ ERRINT_CMD_ENDBIT_BIT | ERRINT_CMD_INDEX_BIT)
+#define ERRINT_DATA_ERRS (ERRINT_DATA_TIMEOUT_BIT | ERRINT_DATA_CRC_BIT |\
+ ERRINT_DATA_ENDBIT_BIT)
+#define ERRINT_TRANSFER_ERRS (ERRINT_CMD_ERRS | ERRINT_DATA_ERRS)
+
+/* SD_WakeupCntr_BlockGapCntrl : Offset 0x02A , size = bytes */
+/* SD_ClockCntrl : Offset 0x02C , size = bytes */
+/* SD_SoftwareReset_TimeoutCntrl : Offset 0x02E , size = bytes */
+/* SD_IntrStatus : Offset 0x030 , size = bytes */
+/* SD_ErrorIntrStatus : Offset 0x032 , size = bytes */
+/* SD_IntrStatusEnable : Offset 0x034 , size = bytes */
+/* SD_ErrorIntrStatusEnable : Offset 0x036 , size = bytes */
+/* SD_IntrSignalEnable : Offset 0x038 , size = bytes */
+/* SD_ErrorIntrSignalEnable : Offset 0x03A , size = bytes */
+/* SD_CMD12ErrorStatus : Offset 0x03C , size = bytes */
+/* SD_Capabilities : Offset 0x040 , size = bytes */
+/* SD_MaxCurCap : Offset 0x048 , size = bytes */
+/* SD_MaxCurCap_Reserved: Offset 0x04C , size = bytes */
+/* SD_SlotInterruptStatus: Offset 0x0FC , size = bytes */
+/* SD_HostControllerVersion : Offset 0x0FE , size = bytes */
+
+#endif /* _SDIOH_H */
diff --git a/drivers/net/wireless/bcm4329/include/sdiovar.h b/drivers/net/wireless/bcm4329/include/sdiovar.h
new file mode 100644
index 00000000000..0179d4cb96d
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/sdiovar.h
@@ -0,0 +1,58 @@
+/*
+ * Structure used by apps whose drivers access SDIO drivers.
+ * Pulled out separately so dhdu and wlu can both use it.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sdiovar.h,v 13.5.14.2.16.2 2009/12/08 22:34:21 Exp $
+ */
+
+#ifndef _sdiovar_h_
+#define _sdiovar_h_
+
+#include <typedefs.h>
+
+/* require default structure packing */
+#define BWL_DEFAULT_PACKING
+#include <packed_section_start.h>
+
+typedef struct sdreg {
+ int func;
+ int offset;
+ int value;
+} sdreg_t;
+
+/* Common msglevel constants */
+#define SDH_ERROR_VAL 0x0001 /* Error */
+#define SDH_TRACE_VAL 0x0002 /* Trace */
+#define SDH_INFO_VAL 0x0004 /* Info */
+#define SDH_DEBUG_VAL 0x0008 /* Debug */
+#define SDH_DATA_VAL 0x0010 /* Data */
+#define SDH_CTRL_VAL 0x0020 /* Control Regs */
+#define SDH_LOG_VAL 0x0040 /* Enable bcmlog */
+#define SDH_DMA_VAL 0x0080 /* DMA */
+
+#define NUM_PREV_TRANSACTIONS 16
+
+
+#include <packed_section_end.h>
+
+#endif /* _sdiovar_h_ */
diff --git a/drivers/net/wireless/bcm4329/include/siutils.h b/drivers/net/wireless/bcm4329/include/siutils.h
new file mode 100644
index 00000000000..cb9f1407b73
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/siutils.h
@@ -0,0 +1,235 @@
+/*
+ * Misc utility routines for accessing the SOC Interconnects
+ * of Broadcom HNBU chips.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: siutils.h,v 13.197.4.2.4.3.8.16 2010/06/23 21:36:05 Exp $
+ */
+
+
+#ifndef _siutils_h_
+#define _siutils_h_
+
+
+struct si_pub {
+ uint socitype;
+
+ uint bustype;
+ uint buscoretype;
+ uint buscorerev;
+ uint buscoreidx;
+ int ccrev;
+ uint32 cccaps;
+ int pmurev;
+ uint32 pmucaps;
+ uint boardtype;
+ uint boardvendor;
+ uint boardflags;
+ uint chip;
+ uint chiprev;
+ uint chippkg;
+ uint32 chipst;
+ bool issim;
+ uint socirev;
+ bool pci_pr32414;
+};
+
+#if defined(WLC_HIGH) && !defined(WLC_LOW)
+typedef struct si_pub si_t;
+#else
+typedef const struct si_pub si_t;
+#endif
+
+
+#define SI_OSH NULL
+
+
+#define XTAL 0x1
+#define PLL 0x2
+
+
+#define CLK_FAST 0
+#define CLK_DYNAMIC 2
+
+
+#define GPIO_DRV_PRIORITY 0
+#define GPIO_APP_PRIORITY 1
+#define GPIO_HI_PRIORITY 2
+
+
+#define GPIO_PULLUP 0
+#define GPIO_PULLDN 1
+
+
+#define GPIO_REGEVT 0
+#define GPIO_REGEVT_INTMSK 1
+#define GPIO_REGEVT_INTPOL 2
+
+
+#define SI_DEVPATH_BUFSZ 16
+
+
+#define SI_DOATTACH 1
+#define SI_PCIDOWN 2
+#define SI_PCIUP 3
+
+#define ISSIM_ENAB(sih) 0
+
+
+#if defined(BCMPMUCTL)
+#define PMUCTL_ENAB(sih) (BCMPMUCTL)
+#else
+#define PMUCTL_ENAB(sih) ((sih)->cccaps & CC_CAP_PMU)
+#endif
+
+
+#if defined(BCMPMUCTL) && BCMPMUCTL
+#define CCCTL_ENAB(sih) (0)
+#define CCPLL_ENAB(sih) (0)
+#else
+#define CCCTL_ENAB(sih) ((sih)->cccaps & CC_CAP_PWR_CTL)
+#define CCPLL_ENAB(sih) ((sih)->cccaps & CC_CAP_PLL_MASK)
+#endif
+
+typedef void (*gpio_handler_t)(uint32 stat, void *arg);
+
+
+
+extern si_t *si_attach(uint pcidev, osl_t *osh, void *regs, uint bustype,
+ void *sdh, char **vars, uint *varsz);
+extern si_t *si_kattach(osl_t *osh);
+extern void si_detach(si_t *sih);
+extern bool si_pci_war16165(si_t *sih);
+
+extern uint si_corelist(si_t *sih, uint coreid[]);
+extern uint si_coreid(si_t *sih);
+extern uint si_flag(si_t *sih);
+extern uint si_intflag(si_t *sih);
+extern uint si_coreidx(si_t *sih);
+extern uint si_coreunit(si_t *sih);
+extern uint si_corevendor(si_t *sih);
+extern uint si_corerev(si_t *sih);
+extern void *si_osh(si_t *sih);
+extern void si_setosh(si_t *sih, osl_t *osh);
+extern uint si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val);
+extern void *si_coreregs(si_t *sih);
+extern void si_write_wrapperreg(si_t *sih, uint32 offset, uint32 val);
+extern uint32 si_core_cflags(si_t *sih, uint32 mask, uint32 val);
+extern void si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val);
+extern uint32 si_core_sflags(si_t *sih, uint32 mask, uint32 val);
+extern bool si_iscoreup(si_t *sih);
+extern uint si_findcoreidx(si_t *sih, uint coreid, uint coreunit);
+extern void *si_setcoreidx(si_t *sih, uint coreidx);
+extern void *si_setcore(si_t *sih, uint coreid, uint coreunit);
+extern void *si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val);
+extern void si_restore_core(si_t *sih, uint coreid, uint intr_val);
+extern int si_numaddrspaces(si_t *sih);
+extern uint32 si_addrspace(si_t *sih, uint asidx);
+extern uint32 si_addrspacesize(si_t *sih, uint asidx);
+extern int si_corebist(si_t *sih);
+extern void si_core_reset(si_t *sih, uint32 bits, uint32 resetbits);
+extern void si_core_tofixup(si_t *sih);
+extern void si_core_disable(si_t *sih, uint32 bits);
+extern uint32 si_clock_rate(uint32 pll_type, uint32 n, uint32 m);
+extern uint32 si_clock(si_t *sih);
+extern void si_clock_pmu_spuravoid(si_t *sih, bool spuravoid);
+extern uint32 si_alp_clock(si_t *sih);
+extern uint32 si_ilp_clock(si_t *sih);
+extern void si_pci_setup(si_t *sih, uint coremask);
+extern void si_pcmcia_init(si_t *sih);
+extern void si_setint(si_t *sih, int siflag);
+extern bool si_backplane64(si_t *sih);
+extern void si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn,
+ void *intrsenabled_fn, void *intr_arg);
+extern void si_deregister_intr_callback(si_t *sih);
+extern void si_clkctl_init(si_t *sih);
+extern uint16 si_clkctl_fast_pwrup_delay(si_t *sih);
+extern bool si_clkctl_cc(si_t *sih, uint mode);
+extern int si_clkctl_xtal(si_t *sih, uint what, bool on);
+extern uint32 si_gpiotimerval(si_t *sih, uint32 mask, uint32 val);
+extern bool si_backplane64(si_t *sih);
+extern void si_btcgpiowar(si_t *sih);
+extern bool si_deviceremoved(si_t *sih);
+extern uint32 si_socram_size(si_t *sih);
+
+extern void si_watchdog(si_t *sih, uint ticks);
+extern void si_watchdog_ms(si_t *sih, uint32 ms);
+extern void *si_gpiosetcore(si_t *sih);
+extern uint32 si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 priority);
+extern uint32 si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 priority);
+extern uint32 si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 priority);
+extern uint32 si_gpioin(si_t *sih);
+extern uint32 si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority);
+extern uint32 si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority);
+extern uint32 si_gpioled(si_t *sih, uint32 mask, uint32 val);
+extern uint32 si_gpioreserve(si_t *sih, uint32 gpio_num, uint8 priority);
+extern uint32 si_gpiorelease(si_t *sih, uint32 gpio_num, uint8 priority);
+extern uint32 si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val);
+extern uint32 si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val);
+extern uint32 si_gpio_int_enable(si_t *sih, bool enable);
+
+
+extern void *si_gpio_handler_register(si_t *sih, uint32 e, bool lev, gpio_handler_t cb, void *arg);
+extern void si_gpio_handler_unregister(si_t *sih, void* gpioh);
+extern void si_gpio_handler_process(si_t *sih);
+
+
+extern bool si_pci_pmecap(si_t *sih);
+struct osl_info;
+extern bool si_pci_fastpmecap(struct osl_info *osh);
+extern bool si_pci_pmeclr(si_t *sih);
+extern void si_pci_pmeen(si_t *sih);
+extern uint si_pcie_readreg(void *sih, uint addrtype, uint offset);
+
+extern void si_sdio_init(si_t *sih);
+
+extern uint16 si_d11_devid(si_t *sih);
+extern int si_corepciid(si_t *sih, uint func, uint16 *pcivendor, uint16 *pcidevice,
+ uint8 *pciclass, uint8 *pcisubclass, uint8 *pciprogif, uint8 *pciheader);
+
+#define si_eci_init(sih) (0)
+#define si_eci_notify_bt(sih, type, val, interrupt) (0)
+
+
+
+extern int si_devpath(si_t *sih, char *path, int size);
+
+extern char *si_getdevpathvar(si_t *sih, const char *name);
+extern int si_getdevpathintvar(si_t *sih, const char *name);
+
+
+extern uint8 si_pcieclkreq(si_t *sih, uint32 mask, uint32 val);
+extern void si_war42780_clkreq(si_t *sih, bool clkreq);
+extern void si_pci_sleep(si_t *sih);
+extern void si_pci_down(si_t *sih);
+extern void si_pci_up(si_t *sih);
+extern void si_pcie_war_ovr_disable(si_t *sih);
+extern void si_pcie_extendL1timer(si_t *sih, bool extend);
+extern int si_pci_fixcfg(si_t *sih);
+
+
+
+
+
+
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/spid.h b/drivers/net/wireless/bcm4329/include/spid.h
new file mode 100644
index 00000000000..c740296de9a
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/spid.h
@@ -0,0 +1,153 @@
+/*
+ * SPI device spec header file
+ *
+ * Copyright (C) 2010, Broadcom Corporation
+ * All Rights Reserved.
+ *
+ * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
+ * the contents of this file may not be disclosed to third parties, copied
+ * or duplicated in any form, in whole or in part, without the prior
+ * written permission of Broadcom Corporation.
+ *
+ * $Id: spid.h,v 1.7.10.1.16.3 2009/04/09 19:23:14 Exp $
+ */
+
+#ifndef _SPI_H
+#define _SPI_H
+
+/*
+ * Brcm SPI Device Register Map.
+ *
+ */
+
+typedef volatile struct {
+ uint8 config; /* 0x00, len, endian, clock, speed, polarity, wakeup */
+ uint8 response_delay; /* 0x01, read response delay in bytes (corerev < 3) */
+ uint8 status_enable; /* 0x02, status-enable, intr with status, response_delay
+ * function selection, command/data error check
+ */
+ uint8 reset_bp; /* 0x03, reset on wlan/bt backplane reset (corerev >= 1) */
+ uint16 intr_reg; /* 0x04, Intr status register */
+ uint16 intr_en_reg; /* 0x06, Intr mask register */
+ uint32 status_reg; /* 0x08, RO, Status bits of last spi transfer */
+ uint16 f1_info_reg; /* 0x0c, RO, enabled, ready for data transfer, blocksize */
+ uint16 f2_info_reg; /* 0x0e, RO, enabled, ready for data transfer, blocksize */
+ uint16 f3_info_reg; /* 0x10, RO, enabled, ready for data transfer, blocksize */
+ uint32 test_read; /* 0x14, RO 0xfeedbead signature */
+ uint32 test_rw; /* 0x18, RW */
+ uint8 resp_delay_f0; /* 0x1c, read resp delay bytes for F0 (corerev >= 3) */
+ uint8 resp_delay_f1; /* 0x1d, read resp delay bytes for F1 (corerev >= 3) */
+ uint8 resp_delay_f2; /* 0x1e, read resp delay bytes for F2 (corerev >= 3) */
+ uint8 resp_delay_f3; /* 0x1f, read resp delay bytes for F3 (corerev >= 3) */
+} spi_regs_t;
+
+/* SPI device register offsets */
+#define SPID_CONFIG 0x00
+#define SPID_RESPONSE_DELAY 0x01
+#define SPID_STATUS_ENABLE 0x02
+#define SPID_RESET_BP 0x03 /* (corerev >= 1) */
+#define SPID_INTR_REG 0x04 /* 16 bits - Interrupt status */
+#define SPID_INTR_EN_REG 0x06 /* 16 bits - Interrupt mask */
+#define SPID_STATUS_REG 0x08 /* 32 bits */
+#define SPID_F1_INFO_REG 0x0C /* 16 bits */
+#define SPID_F2_INFO_REG 0x0E /* 16 bits */
+#define SPID_F3_INFO_REG 0x10 /* 16 bits */
+#define SPID_TEST_READ 0x14 /* 32 bits */
+#define SPID_TEST_RW 0x18 /* 32 bits */
+#define SPID_RESP_DELAY_F0 0x1c /* 8 bits (corerev >= 3) */
+#define SPID_RESP_DELAY_F1 0x1d /* 8 bits (corerev >= 3) */
+#define SPID_RESP_DELAY_F2 0x1e /* 8 bits (corerev >= 3) */
+#define SPID_RESP_DELAY_F3 0x1f /* 8 bits (corerev >= 3) */
+
+/* Bit masks for SPID_CONFIG device register */
+#define WORD_LENGTH_32 0x1 /* 0/1 16/32 bit word length */
+#define ENDIAN_BIG 0x2 /* 0/1 Little/Big Endian */
+#define CLOCK_PHASE 0x4 /* 0/1 clock phase delay */
+#define CLOCK_POLARITY 0x8 /* 0/1 Idle state clock polarity is low/high */
+#define HIGH_SPEED_MODE 0x10 /* 1/0 High Speed mode / Normal mode */
+#define INTR_POLARITY 0x20 /* 1/0 Interrupt active polarity is high/low */
+#define WAKE_UP 0x80 /* 0/1 Wake-up command from Host to WLAN */
+
+/* Bit mask for SPID_RESPONSE_DELAY device register */
+#define RESPONSE_DELAY_MASK 0xFF /* Configurable rd response delay in multiples of 8 bits */
+
+/* Bit mask for SPID_STATUS_ENABLE device register */
+#define STATUS_ENABLE 0x1 /* 1/0 Status sent/not sent to host after read/write */
+#define INTR_WITH_STATUS 0x2 /* 0/1 Do-not / do-interrupt if status is sent */
+#define RESP_DELAY_ALL 0x4 /* Applicability of resp delay to F1 or all func's read */
+#define DWORD_PKT_LEN_EN 0x8 /* Packet len denoted in dwords instead of bytes */
+#define CMD_ERR_CHK_EN 0x20 /* Command error check enable */
+#define DATA_ERR_CHK_EN 0x40 /* Data error check enable */
+
+/* Bit mask for SPID_RESET_BP device register */
+#define RESET_ON_WLAN_BP_RESET 0x4 /* enable reset for WLAN backplane */
+#define RESET_ON_BT_BP_RESET 0x8 /* enable reset for BT backplane */
+#define RESET_SPI 0x80 /* reset the above enabled logic */
+
+/* Bit mask for SPID_INTR_REG device register */
+#define DATA_UNAVAILABLE 0x0001 /* Requested data not available; Clear by writing a "1" */
+#define F2_F3_FIFO_RD_UNDERFLOW 0x0002
+#define F2_F3_FIFO_WR_OVERFLOW 0x0004
+#define COMMAND_ERROR 0x0008 /* Cleared by writing 1 */
+#define DATA_ERROR 0x0010 /* Cleared by writing 1 */
+#define F2_PACKET_AVAILABLE 0x0020
+#define F3_PACKET_AVAILABLE 0x0040
+#define F1_OVERFLOW 0x0080 /* Due to last write. Bkplane has pending write requests */
+#define MISC_INTR0 0x0100
+#define MISC_INTR1 0x0200
+#define MISC_INTR2 0x0400
+#define MISC_INTR3 0x0800
+#define MISC_INTR4 0x1000
+#define F1_INTR 0x2000
+#define F2_INTR 0x4000
+#define F3_INTR 0x8000
+
+/* Bit mask for 32bit SPID_STATUS_REG device register */
+#define STATUS_DATA_NOT_AVAILABLE 0x00000001
+#define STATUS_UNDERFLOW 0x00000002
+#define STATUS_OVERFLOW 0x00000004
+#define STATUS_F2_INTR 0x00000008
+#define STATUS_F3_INTR 0x00000010
+#define STATUS_F2_RX_READY 0x00000020
+#define STATUS_F3_RX_READY 0x00000040
+#define STATUS_HOST_CMD_DATA_ERR 0x00000080
+#define STATUS_F2_PKT_AVAILABLE 0x00000100
+#define STATUS_F2_PKT_LEN_MASK 0x000FFE00
+#define STATUS_F2_PKT_LEN_SHIFT 9
+#define STATUS_F3_PKT_AVAILABLE 0x00100000
+#define STATUS_F3_PKT_LEN_MASK 0xFFE00000
+#define STATUS_F3_PKT_LEN_SHIFT 21
+
+/* Bit mask for 16 bits SPID_F1_INFO_REG device register */
+#define F1_ENABLED 0x0001
+#define F1_RDY_FOR_DATA_TRANSFER 0x0002
+#define F1_MAX_PKT_SIZE 0x01FC
+
+/* Bit mask for 16 bits SPID_F2_INFO_REG device register */
+#define F2_ENABLED 0x0001
+#define F2_RDY_FOR_DATA_TRANSFER 0x0002
+#define F2_MAX_PKT_SIZE 0x3FFC
+
+/* Bit mask for 16 bits SPID_F3_INFO_REG device register */
+#define F3_ENABLED 0x0001
+#define F3_RDY_FOR_DATA_TRANSFER 0x0002
+#define F3_MAX_PKT_SIZE 0x3FFC
+
+/* Bit mask for 32 bits SPID_TEST_READ device register read in 16bit LE mode */
+#define TEST_RO_DATA_32BIT_LE 0xFEEDBEAD
+
+/* Maximum number of I/O funcs */
+#define SPI_MAX_IOFUNCS 4
+
+#define SPI_MAX_PKT_LEN (2048*4)
+
+/* Misc defines */
+#define SPI_FUNC_0 0
+#define SPI_FUNC_1 1
+#define SPI_FUNC_2 2
+#define SPI_FUNC_3 3
+
+#define WAIT_F2RXFIFORDY 100
+#define WAIT_F2RXFIFORDY_DELAY 20
+
+#endif /* _SPI_H */
diff --git a/drivers/net/wireless/bcm4329/include/trxhdr.h b/drivers/net/wireless/bcm4329/include/trxhdr.h
new file mode 100644
index 00000000000..8f5eed9410e
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/trxhdr.h
@@ -0,0 +1,46 @@
+/*
+ * TRX image file header format.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: trxhdr.h,v 13.11.310.1 2008/08/17 12:58:58 Exp $
+ */
+
+#include <typedefs.h>
+
+#define TRX_MAGIC 0x30524448 /* "HDR0" */
+#define TRX_VERSION 1 /* Version 1 */
+#define TRX_MAX_LEN 0x3A0000 /* Max length */
+#define TRX_NO_HEADER 1 /* Do not write TRX header */
+#define TRX_GZ_FILES 0x2 /* Contains up to TRX_MAX_OFFSET individual gzip files */
+#define TRX_MAX_OFFSET 3 /* Max number of individual files */
+#define TRX_UNCOMP_IMAGE 0x20 /* Trx contains uncompressed rtecdc.bin image */
+
+struct trx_header {
+ uint32 magic; /* "HDR0" */
+ uint32 len; /* Length of file including header */
+ uint32 crc32; /* 32-bit CRC from flag_version to end of file */
+ uint32 flag_version; /* 0:15 flags, 16:31 version */
+ uint32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */
+};
+
+/* Compatibility */
+typedef struct trx_header TRXHDR, *PTRXHDR;
diff --git a/drivers/net/wireless/bcm4329/include/typedefs.h b/drivers/net/wireless/bcm4329/include/typedefs.h
new file mode 100644
index 00000000000..4d9dd761ed6
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/typedefs.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: typedefs.h,v 1.85.34.1.2.5 2009/01/27 04:09:40 Exp $
+ */
+
+
+#ifndef _TYPEDEFS_H_
+#define _TYPEDEFS_H_
+
+#ifdef SITE_TYPEDEFS
+
+
+
+#include "site_typedefs.h"
+
+#else
+
+
+
+#ifdef __cplusplus
+
+#define TYPEDEF_BOOL
+#ifndef FALSE
+#define FALSE false
+#endif
+#ifndef TRUE
+#define TRUE true
+#endif
+
+#else
+
+
+#endif
+
+#if defined(__x86_64__)
+#define TYPEDEF_UINTPTR
+typedef unsigned long long int uintptr;
+#endif
+
+
+
+
+#if defined(TARGETOS_nucleus)
+
+#include <stddef.h>
+
+
+#define TYPEDEF_FLOAT_T
+#endif
+
+#if defined(_NEED_SIZE_T_)
+typedef long unsigned int size_t;
+#endif
+
+#ifdef __DJGPP__
+typedef long unsigned int size_t;
+#endif
+
+
+
+
+
+#define TYPEDEF_UINT
+#ifndef TARGETENV_android
+#define TYPEDEF_USHORT
+#define TYPEDEF_ULONG
+#endif
+#ifdef __KERNEL__
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19))
+#define TYPEDEF_BOOL
+#endif
+#endif
+
+
+
+
+
+#if defined(__GNUC__) && defined(__STRICT_ANSI__)
+#define TYPEDEF_INT64
+#define TYPEDEF_UINT64
+#endif
+
+
+#if defined(__ICL)
+
+#define TYPEDEF_INT64
+
+#if defined(__STDC__)
+#define TYPEDEF_UINT64
+#endif
+
+#endif
+
+#if !defined(__DJGPP__) && !defined(TARGETOS_nucleus)
+
+
+#if defined(__KERNEL__)
+
+#include <linux/types.h>
+
+#else
+
+
+#include <sys/types.h>
+
+#endif
+
+#endif
+
+
+
+
+#define USE_TYPEDEF_DEFAULTS
+
+#endif
+
+
+
+
+#ifdef USE_TYPEDEF_DEFAULTS
+#undef USE_TYPEDEF_DEFAULTS
+
+#ifndef TYPEDEF_BOOL
+typedef unsigned char bool;
+#endif
+
+
+
+#ifndef TYPEDEF_UCHAR
+typedef unsigned char uchar;
+#endif
+
+#ifndef TYPEDEF_USHORT
+typedef unsigned short ushort;
+#endif
+
+#ifndef TYPEDEF_UINT
+typedef unsigned int uint;
+#endif
+
+#ifndef TYPEDEF_ULONG
+typedef unsigned long ulong;
+#endif
+
+
+
+#ifndef TYPEDEF_UINT8
+typedef unsigned char uint8;
+#endif
+
+#ifndef TYPEDEF_UINT16
+typedef unsigned short uint16;
+#endif
+
+#ifndef TYPEDEF_UINT32
+typedef unsigned int uint32;
+#endif
+
+#ifndef TYPEDEF_UINT64
+typedef unsigned long long uint64;
+#endif
+
+#ifndef TYPEDEF_UINTPTR
+typedef unsigned int uintptr;
+#endif
+
+#ifndef TYPEDEF_INT8
+typedef signed char int8;
+#endif
+
+#ifndef TYPEDEF_INT16
+typedef signed short int16;
+#endif
+
+#ifndef TYPEDEF_INT32
+typedef signed int int32;
+#endif
+
+#ifndef TYPEDEF_INT64
+typedef signed long long int64;
+#endif
+
+
+
+#ifndef TYPEDEF_FLOAT32
+typedef float float32;
+#endif
+
+#ifndef TYPEDEF_FLOAT64
+typedef double float64;
+#endif
+
+
+
+#ifndef TYPEDEF_FLOAT_T
+
+#if defined(FLOAT32)
+typedef float32 float_t;
+#else
+typedef float64 float_t;
+#endif
+
+#endif
+
+
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef OFF
+#define OFF 0
+#endif
+
+#ifndef ON
+#define ON 1
+#endif
+
+#define AUTO (-1)
+
+
+
+#ifndef PTRSZ
+#define PTRSZ sizeof(char*)
+#endif
+
+
+
+#if defined(__GNUC__)
+ #define BWL_COMPILER_GNU
+#elif defined(__CC_ARM)
+ #define BWL_COMPILER_ARMCC
+#else
+ #error "Unknown compiler!"
+#endif
+
+
+#ifndef INLINE
+ #if defined(BWL_COMPILER_MICROSOFT)
+ #define INLINE __inline
+ #elif defined(BWL_COMPILER_GNU)
+ #define INLINE __inline__
+ #elif defined(BWL_COMPILER_ARMCC)
+ #define INLINE __inline
+ #else
+ #define INLINE
+ #endif
+#endif
+
+#undef TYPEDEF_BOOL
+#undef TYPEDEF_UCHAR
+#undef TYPEDEF_USHORT
+#undef TYPEDEF_UINT
+#undef TYPEDEF_ULONG
+#undef TYPEDEF_UINT8
+#undef TYPEDEF_UINT16
+#undef TYPEDEF_UINT32
+#undef TYPEDEF_UINT64
+#undef TYPEDEF_UINTPTR
+#undef TYPEDEF_INT8
+#undef TYPEDEF_INT16
+#undef TYPEDEF_INT32
+#undef TYPEDEF_INT64
+#undef TYPEDEF_FLOAT32
+#undef TYPEDEF_FLOAT64
+#undef TYPEDEF_FLOAT_T
+
+#endif
+
+
+#define UNUSED_PARAMETER(x) (void)(x)
+
+
+#include <bcmdefs.h>
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/include/wlioctl.h b/drivers/net/wireless/bcm4329/include/wlioctl.h
new file mode 100644
index 00000000000..cd7725a70db
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/include/wlioctl.h
@@ -0,0 +1,1664 @@
+/*
+ * Custom OID/ioctl definitions for
+ * Broadcom 802.11abg Networking Device Driver
+ *
+ * Definitions subject to change without notice.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: wlioctl.h,v 1.601.4.15.2.14.2.62.4.1 2010/11/17 03:09:28 Exp $
+ */
+
+
+#ifndef _wlioctl_h_
+#define _wlioctl_h_
+
+#include <typedefs.h>
+#include <proto/ethernet.h>
+#include <proto/bcmeth.h>
+#include <proto/bcmevent.h>
+#include <proto/802.11.h>
+#include <bcmwifi.h>
+
+
+
+#define ACTION_FRAME_SIZE 1040
+
+typedef struct wl_action_frame {
+ struct ether_addr da;
+ uint16 len;
+ uint32 packetId;
+ uint8 data[ACTION_FRAME_SIZE];
+} wl_action_frame_t;
+
+#define WL_WIFI_ACTION_FRAME_SIZE sizeof(struct wl_action_frame)
+
+
+#define BWL_DEFAULT_PACKING
+#include <packed_section_start.h>
+
+#define RWL_ACTION_WIFI_CATEGORY 127
+#define RWL_WIFI_OUI_BYTE1 0x90
+#define RWL_WIFI_OUI_BYTE2 0x4C
+#define RWL_WIFI_OUI_BYTE3 0x0F
+#define RWL_WIFI_ACTION_FRAME_SIZE sizeof(struct dot11_action_wifi_vendor_specific)
+#define RWL_WIFI_DEFAULT 0x00
+#define RWL_WIFI_FIND_MY_PEER 0x09
+#define RWL_WIFI_FOUND_PEER 0x0A
+#define RWL_ACTION_WIFI_FRAG_TYPE 0x55
+
+typedef struct ssid_info
+{
+ uint8 ssid_len;
+ uint8 ssid[32];
+} ssid_info_t;
+
+typedef struct cnt_rx
+{
+ uint32 cnt_rxundec;
+ uint32 cnt_rxframe;
+} cnt_rx_t;
+
+
+
+#define RWL_REF_MAC_ADDRESS_OFFSET 17
+#define RWL_DUT_MAC_ADDRESS_OFFSET 23
+#define RWL_WIFI_CLIENT_CHANNEL_OFFSET 50
+#define RWL_WIFI_SERVER_CHANNEL_OFFSET 51
+
+
+
+
+
+#define WL_BSS_INFO_VERSION 108
+
+
+typedef struct wl_bss_info {
+ uint32 version;
+ uint32 length;
+ struct ether_addr BSSID;
+ uint16 beacon_period;
+ uint16 capability;
+ uint8 SSID_len;
+ uint8 SSID[32];
+ struct {
+ uint count;
+ uint8 rates[16];
+ } rateset;
+ chanspec_t chanspec;
+ uint16 atim_window;
+ uint8 dtim_period;
+ int16 RSSI;
+ int8 phy_noise;
+
+ uint8 n_cap;
+ uint32 nbss_cap;
+ uint8 ctl_ch;
+ uint32 reserved32[1];
+ uint8 flags;
+ uint8 reserved[3];
+ uint8 basic_mcs[MCSSET_LEN];
+
+ uint16 ie_offset;
+ uint32 ie_length;
+
+
+} wl_bss_info_t;
+
+typedef struct wlc_ssid {
+ uint32 SSID_len;
+ uchar SSID[32];
+} wlc_ssid_t;
+
+
+#define WL_BSSTYPE_INFRA 1
+#define WL_BSSTYPE_INDEP 0
+#define WL_BSSTYPE_ANY 2
+
+
+#define WL_SCANFLAGS_PASSIVE 0x01
+#define WL_SCANFLAGS_PROHIBITED 0x04
+
+typedef struct wl_scan_params {
+ wlc_ssid_t ssid;
+ struct ether_addr bssid;
+ int8 bss_type;
+ int8 scan_type;
+ int32 nprobes;
+ int32 active_time;
+ int32 passive_time;
+ int32 home_time;
+ int32 channel_num;
+ uint16 channel_list[1];
+} wl_scan_params_t;
+
+#define WL_SCAN_PARAMS_FIXED_SIZE 64
+
+
+#define WL_SCAN_PARAMS_COUNT_MASK 0x0000ffff
+#define WL_SCAN_PARAMS_NSSID_SHIFT 16
+
+#define WL_SCAN_ACTION_START 1
+#define WL_SCAN_ACTION_CONTINUE 2
+#define WL_SCAN_ACTION_ABORT 3
+
+#define ISCAN_REQ_VERSION 1
+
+
+typedef struct wl_iscan_params {
+ uint32 version;
+ uint16 action;
+ uint16 scan_duration;
+ wl_scan_params_t params;
+} wl_iscan_params_t;
+
+#define WL_ISCAN_PARAMS_FIXED_SIZE (OFFSETOF(wl_iscan_params_t, params) + sizeof(wlc_ssid_t))
+
+typedef struct wl_scan_results {
+ uint32 buflen;
+ uint32 version;
+ uint32 count;
+ wl_bss_info_t bss_info[1];
+} wl_scan_results_t;
+
+#define WL_SCAN_RESULTS_FIXED_SIZE 12
+
+
+#define WL_SCAN_RESULTS_SUCCESS 0
+#define WL_SCAN_RESULTS_PARTIAL 1
+#define WL_SCAN_RESULTS_PENDING 2
+#define WL_SCAN_RESULTS_ABORTED 3
+#define WL_SCAN_RESULTS_NO_MEM 4
+
+#define ESCAN_REQ_VERSION 1
+
+typedef struct wl_escan_params {
+ uint32 version;
+ uint16 action;
+ uint16 sync_id;
+ wl_scan_params_t params;
+} wl_escan_params_t;
+
+#define WL_ESCAN_PARAMS_FIXED_SIZE (OFFSETOF(wl_escan_params_t, params) + sizeof(wlc_ssid_t))
+
+typedef struct wl_escan_result {
+ uint32 buflen;
+ uint32 version;
+ uint16 sync_id;
+ uint16 bss_count;
+ wl_bss_info_t bss_info[1];
+} wl_escan_result_t;
+
+#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(wl_escan_result_t) - sizeof(wl_bss_info_t))
+
+
+typedef struct wl_iscan_results {
+ uint32 status;
+ wl_scan_results_t results;
+} wl_iscan_results_t;
+
+#define WL_ISCAN_RESULTS_FIXED_SIZE \
+ (WL_SCAN_RESULTS_FIXED_SIZE + OFFSETOF(wl_iscan_results_t, results))
+
+#define WL_NUMRATES 16
+typedef struct wl_rateset {
+ uint32 count;
+ uint8 rates[WL_NUMRATES];
+} wl_rateset_t;
+
+
+typedef struct wl_uint32_list {
+
+ uint32 count;
+
+ uint32 element[1];
+} wl_uint32_list_t;
+
+
+typedef struct wl_assoc_params {
+ struct ether_addr bssid;
+ uint16 bssid_cnt;
+ int32 chanspec_num;
+ chanspec_t chanspec_list[1];
+} wl_assoc_params_t;
+#define WL_ASSOC_PARAMS_FIXED_SIZE (sizeof(wl_assoc_params_t) - sizeof(chanspec_t))
+
+
+typedef wl_assoc_params_t wl_reassoc_params_t;
+#define WL_REASSOC_PARAMS_FIXED_SIZE WL_ASSOC_PARAMS_FIXED_SIZE
+
+
+typedef struct wl_join_params {
+ wlc_ssid_t ssid;
+ wl_assoc_params_t params;
+} wl_join_params_t;
+#define WL_JOIN_PARAMS_FIXED_SIZE (sizeof(wl_join_params_t) - sizeof(chanspec_t))
+
+#define WLC_CNTRY_BUF_SZ 4
+
+
+typedef enum sup_auth_status {
+
+ WLC_SUP_DISCONNECTED = 0,
+ WLC_SUP_CONNECTING,
+ WLC_SUP_IDREQUIRED,
+ WLC_SUP_AUTHENTICATING,
+ WLC_SUP_AUTHENTICATED,
+ WLC_SUP_KEYXCHANGE,
+ WLC_SUP_KEYED,
+ WLC_SUP_TIMEOUT,
+ WLC_SUP_LAST_BASIC_STATE,
+
+
+ WLC_SUP_KEYXCHANGE_WAIT_M1 = WLC_SUP_AUTHENTICATED,
+
+ WLC_SUP_KEYXCHANGE_PREP_M2 = WLC_SUP_KEYXCHANGE,
+
+ WLC_SUP_KEYXCHANGE_WAIT_M3 = WLC_SUP_LAST_BASIC_STATE,
+
+ WLC_SUP_KEYXCHANGE_PREP_M4,
+ WLC_SUP_KEYXCHANGE_WAIT_G1,
+ WLC_SUP_KEYXCHANGE_PREP_G2
+} sup_auth_status_t;
+
+
+#define CRYPTO_ALGO_OFF 0
+#define CRYPTO_ALGO_WEP1 1
+#define CRYPTO_ALGO_TKIP 2
+#define CRYPTO_ALGO_WEP128 3
+#define CRYPTO_ALGO_AES_CCM 4
+#define CRYPTO_ALGO_AES_OCB_MSDU 5
+#define CRYPTO_ALGO_AES_OCB_MPDU 6
+#define CRYPTO_ALGO_NALG 7
+#define CRYPTO_ALGO_SMS4 11
+
+#define WSEC_GEN_MIC_ERROR 0x0001
+#define WSEC_GEN_REPLAY 0x0002
+#define WSEC_GEN_ICV_ERROR 0x0004
+
+#define WL_SOFT_KEY (1 << 0)
+#define WL_PRIMARY_KEY (1 << 1)
+#define WL_KF_RES_4 (1 << 4)
+#define WL_KF_RES_5 (1 << 5)
+#define WL_IBSS_PEER_GROUP_KEY (1 << 6)
+
+typedef struct wl_wsec_key {
+ uint32 index;
+ uint32 len;
+ uint8 data[DOT11_MAX_KEY_SIZE];
+ uint32 pad_1[18];
+ uint32 algo;
+ uint32 flags;
+ uint32 pad_2[2];
+ int pad_3;
+ int iv_initialized;
+ int pad_4;
+
+ struct {
+ uint32 hi;
+ uint16 lo;
+ } rxiv;
+ uint32 pad_5[2];
+ struct ether_addr ea;
+} wl_wsec_key_t;
+
+#define WSEC_MIN_PSK_LEN 8
+#define WSEC_MAX_PSK_LEN 64
+
+
+#define WSEC_PASSPHRASE (1<<0)
+
+
+typedef struct {
+ ushort key_len;
+ ushort flags;
+ uint8 key[WSEC_MAX_PSK_LEN];
+} wsec_pmk_t;
+
+
+#define WEP_ENABLED 0x0001
+#define TKIP_ENABLED 0x0002
+#define AES_ENABLED 0x0004
+#define WSEC_SWFLAG 0x0008
+#define SES_OW_ENABLED 0x0040
+#define SMS4_ENABLED 0x0100
+
+
+#define WPA_AUTH_DISABLED 0x0000
+#define WPA_AUTH_NONE 0x0001
+#define WPA_AUTH_UNSPECIFIED 0x0002
+#define WPA_AUTH_PSK 0x0004
+
+#define WPA2_AUTH_UNSPECIFIED 0x0040
+#define WPA2_AUTH_PSK 0x0080
+#define BRCM_AUTH_PSK 0x0100
+#define BRCM_AUTH_DPT 0x0200
+#define WPA_AUTH_WAPI 0x0400
+
+#define WPA_AUTH_PFN_ANY 0xffffffff
+
+
+#define MAXPMKID 16
+
+typedef struct _pmkid {
+ struct ether_addr BSSID;
+ uint8 PMKID[WPA2_PMKID_LEN];
+} pmkid_t;
+
+typedef struct _pmkid_list {
+ uint32 npmkid;
+ pmkid_t pmkid[1];
+} pmkid_list_t;
+
+typedef struct _pmkid_cand {
+ struct ether_addr BSSID;
+ uint8 preauth;
+} pmkid_cand_t;
+
+typedef struct _pmkid_cand_list {
+ uint32 npmkid_cand;
+ pmkid_cand_t pmkid_cand[1];
+} pmkid_cand_list_t;
+
+
+
+
+typedef struct {
+ uint32 val;
+ struct ether_addr ea;
+} scb_val_t;
+
+
+
+typedef struct channel_info {
+ int hw_channel;
+ int target_channel;
+ int scan_channel;
+} channel_info_t;
+
+
+struct maclist {
+ uint count;
+ struct ether_addr ea[1];
+};
+
+
+typedef struct get_pktcnt {
+ uint rx_good_pkt;
+ uint rx_bad_pkt;
+ uint tx_good_pkt;
+ uint tx_bad_pkt;
+ uint rx_ocast_good_pkt;
+} get_pktcnt_t;
+
+
+typedef struct wl_ioctl {
+ uint cmd;
+ void *buf;
+ uint len;
+ uint8 set;
+ uint used;
+ uint needed;
+} wl_ioctl_t;
+
+
+
+#define WLC_IOCTL_MAGIC 0x14e46c77
+
+
+#define WLC_IOCTL_VERSION 1
+
+#define WLC_IOCTL_MAXLEN 8192
+#define WLC_IOCTL_SMLEN 256
+#define WLC_IOCTL_MEDLEN 1536
+
+
+
+#define WLC_GET_MAGIC 0
+#define WLC_GET_VERSION 1
+#define WLC_UP 2
+#define WLC_DOWN 3
+#define WLC_GET_LOOP 4
+#define WLC_SET_LOOP 5
+#define WLC_DUMP 6
+#define WLC_GET_MSGLEVEL 7
+#define WLC_SET_MSGLEVEL 8
+#define WLC_GET_PROMISC 9
+#define WLC_SET_PROMISC 10
+
+#define WLC_GET_RATE 12
+
+#define WLC_GET_INSTANCE 14
+
+
+
+
+#define WLC_GET_INFRA 19
+#define WLC_SET_INFRA 20
+#define WLC_GET_AUTH 21
+#define WLC_SET_AUTH 22
+#define WLC_GET_BSSID 23
+#define WLC_SET_BSSID 24
+#define WLC_GET_SSID 25
+#define WLC_SET_SSID 26
+#define WLC_RESTART 27
+
+#define WLC_GET_CHANNEL 29
+#define WLC_SET_CHANNEL 30
+#define WLC_GET_SRL 31
+#define WLC_SET_SRL 32
+#define WLC_GET_LRL 33
+#define WLC_SET_LRL 34
+#define WLC_GET_PLCPHDR 35
+#define WLC_SET_PLCPHDR 36
+#define WLC_GET_RADIO 37
+#define WLC_SET_RADIO 38
+#define WLC_GET_PHYTYPE 39
+#define WLC_DUMP_RATE 40
+#define WLC_SET_RATE_PARAMS 41
+
+
+#define WLC_GET_KEY 44
+#define WLC_SET_KEY 45
+#define WLC_GET_REGULATORY 46
+#define WLC_SET_REGULATORY 47
+#define WLC_GET_PASSIVE_SCAN 48
+#define WLC_SET_PASSIVE_SCAN 49
+#define WLC_SCAN 50
+#define WLC_SCAN_RESULTS 51
+#define WLC_DISASSOC 52
+#define WLC_REASSOC 53
+#define WLC_GET_ROAM_TRIGGER 54
+#define WLC_SET_ROAM_TRIGGER 55
+#define WLC_GET_ROAM_DELTA 56
+#define WLC_SET_ROAM_DELTA 57
+#define WLC_GET_ROAM_SCAN_PERIOD 58
+#define WLC_SET_ROAM_SCAN_PERIOD 59
+#define WLC_EVM 60
+#define WLC_GET_TXANT 61
+#define WLC_SET_TXANT 62
+#define WLC_GET_ANTDIV 63
+#define WLC_SET_ANTDIV 64
+
+
+#define WLC_GET_CLOSED 67
+#define WLC_SET_CLOSED 68
+#define WLC_GET_MACLIST 69
+#define WLC_SET_MACLIST 70
+#define WLC_GET_RATESET 71
+#define WLC_SET_RATESET 72
+
+#define WLC_LONGTRAIN 74
+#define WLC_GET_BCNPRD 75
+#define WLC_SET_BCNPRD 76
+#define WLC_GET_DTIMPRD 77
+#define WLC_SET_DTIMPRD 78
+#define WLC_GET_SROM 79
+#define WLC_SET_SROM 80
+#define WLC_GET_WEP_RESTRICT 81
+#define WLC_SET_WEP_RESTRICT 82
+#define WLC_GET_COUNTRY 83
+#define WLC_SET_COUNTRY 84
+#define WLC_GET_PM 85
+#define WLC_SET_PM 86
+#define WLC_GET_WAKE 87
+#define WLC_SET_WAKE 88
+
+#define WLC_GET_FORCELINK 90
+#define WLC_SET_FORCELINK 91
+#define WLC_FREQ_ACCURACY 92
+#define WLC_CARRIER_SUPPRESS 93
+#define WLC_GET_PHYREG 94
+#define WLC_SET_PHYREG 95
+#define WLC_GET_RADIOREG 96
+#define WLC_SET_RADIOREG 97
+#define WLC_GET_REVINFO 98
+#define WLC_GET_UCANTDIV 99
+#define WLC_SET_UCANTDIV 100
+#define WLC_R_REG 101
+#define WLC_W_REG 102
+
+
+#define WLC_GET_MACMODE 105
+#define WLC_SET_MACMODE 106
+#define WLC_GET_MONITOR 107
+#define WLC_SET_MONITOR 108
+#define WLC_GET_GMODE 109
+#define WLC_SET_GMODE 110
+#define WLC_GET_LEGACY_ERP 111
+#define WLC_SET_LEGACY_ERP 112
+#define WLC_GET_RX_ANT 113
+#define WLC_GET_CURR_RATESET 114
+#define WLC_GET_SCANSUPPRESS 115
+#define WLC_SET_SCANSUPPRESS 116
+#define WLC_GET_AP 117
+#define WLC_SET_AP 118
+#define WLC_GET_EAP_RESTRICT 119
+#define WLC_SET_EAP_RESTRICT 120
+#define WLC_SCB_AUTHORIZE 121
+#define WLC_SCB_DEAUTHORIZE 122
+#define WLC_GET_WDSLIST 123
+#define WLC_SET_WDSLIST 124
+#define WLC_GET_ATIM 125
+#define WLC_SET_ATIM 126
+#define WLC_GET_RSSI 127
+#define WLC_GET_PHYANTDIV 128
+#define WLC_SET_PHYANTDIV 129
+#define WLC_AP_RX_ONLY 130
+#define WLC_GET_TX_PATH_PWR 131
+#define WLC_SET_TX_PATH_PWR 132
+#define WLC_GET_WSEC 133
+#define WLC_SET_WSEC 134
+#define WLC_GET_PHY_NOISE 135
+#define WLC_GET_BSS_INFO 136
+#define WLC_GET_PKTCNTS 137
+#define WLC_GET_LAZYWDS 138
+#define WLC_SET_LAZYWDS 139
+#define WLC_GET_BANDLIST 140
+#define WLC_GET_BAND 141
+#define WLC_SET_BAND 142
+#define WLC_SCB_DEAUTHENTICATE 143
+#define WLC_GET_SHORTSLOT 144
+#define WLC_GET_SHORTSLOT_OVERRIDE 145
+#define WLC_SET_SHORTSLOT_OVERRIDE 146
+#define WLC_GET_SHORTSLOT_RESTRICT 147
+#define WLC_SET_SHORTSLOT_RESTRICT 148
+#define WLC_GET_GMODE_PROTECTION 149
+#define WLC_GET_GMODE_PROTECTION_OVERRIDE 150
+#define WLC_SET_GMODE_PROTECTION_OVERRIDE 151
+#define WLC_UPGRADE 152
+
+
+#define WLC_GET_IGNORE_BCNS 155
+#define WLC_SET_IGNORE_BCNS 156
+#define WLC_GET_SCB_TIMEOUT 157
+#define WLC_SET_SCB_TIMEOUT 158
+#define WLC_GET_ASSOCLIST 159
+#define WLC_GET_CLK 160
+#define WLC_SET_CLK 161
+#define WLC_GET_UP 162
+#define WLC_OUT 163
+#define WLC_GET_WPA_AUTH 164
+#define WLC_SET_WPA_AUTH 165
+#define WLC_GET_UCFLAGS 166
+#define WLC_SET_UCFLAGS 167
+#define WLC_GET_PWRIDX 168
+#define WLC_SET_PWRIDX 169
+#define WLC_GET_TSSI 170
+#define WLC_GET_SUP_RATESET_OVERRIDE 171
+#define WLC_SET_SUP_RATESET_OVERRIDE 172
+
+
+
+
+
+#define WLC_GET_PROTECTION_CONTROL 178
+#define WLC_SET_PROTECTION_CONTROL 179
+#define WLC_GET_PHYLIST 180
+#define WLC_ENCRYPT_STRENGTH 181
+#define WLC_DECRYPT_STATUS 182
+#define WLC_GET_KEY_SEQ 183
+#define WLC_GET_SCAN_CHANNEL_TIME 184
+#define WLC_SET_SCAN_CHANNEL_TIME 185
+#define WLC_GET_SCAN_UNASSOC_TIME 186
+#define WLC_SET_SCAN_UNASSOC_TIME 187
+#define WLC_GET_SCAN_HOME_TIME 188
+#define WLC_SET_SCAN_HOME_TIME 189
+#define WLC_GET_SCAN_NPROBES 190
+#define WLC_SET_SCAN_NPROBES 191
+#define WLC_GET_PRB_RESP_TIMEOUT 192
+#define WLC_SET_PRB_RESP_TIMEOUT 193
+#define WLC_GET_ATTEN 194
+#define WLC_SET_ATTEN 195
+#define WLC_GET_SHMEM 196
+#define WLC_SET_SHMEM 197
+
+
+#define WLC_SET_WSEC_TEST 200
+#define WLC_SCB_DEAUTHENTICATE_FOR_REASON 201
+#define WLC_TKIP_COUNTERMEASURES 202
+#define WLC_GET_PIOMODE 203
+#define WLC_SET_PIOMODE 204
+#define WLC_SET_ASSOC_PREFER 205
+#define WLC_GET_ASSOC_PREFER 206
+#define WLC_SET_ROAM_PREFER 207
+#define WLC_GET_ROAM_PREFER 208
+#define WLC_SET_LED 209
+#define WLC_GET_LED 210
+#define WLC_GET_INTERFERENCE_MODE 211
+#define WLC_SET_INTERFERENCE_MODE 212
+#define WLC_GET_CHANNEL_QA 213
+#define WLC_START_CHANNEL_QA 214
+#define WLC_GET_CHANNEL_SEL 215
+#define WLC_START_CHANNEL_SEL 216
+#define WLC_GET_VALID_CHANNELS 217
+#define WLC_GET_FAKEFRAG 218
+#define WLC_SET_FAKEFRAG 219
+#define WLC_GET_PWROUT_PERCENTAGE 220
+#define WLC_SET_PWROUT_PERCENTAGE 221
+#define WLC_SET_BAD_FRAME_PREEMPT 222
+#define WLC_GET_BAD_FRAME_PREEMPT 223
+#define WLC_SET_LEAP_LIST 224
+#define WLC_GET_LEAP_LIST 225
+#define WLC_GET_CWMIN 226
+#define WLC_SET_CWMIN 227
+#define WLC_GET_CWMAX 228
+#define WLC_SET_CWMAX 229
+#define WLC_GET_WET 230
+#define WLC_SET_WET 231
+#define WLC_GET_PUB 232
+
+
+#define WLC_GET_KEY_PRIMARY 235
+#define WLC_SET_KEY_PRIMARY 236
+
+#define WLC_GET_ACI_ARGS 238
+#define WLC_SET_ACI_ARGS 239
+#define WLC_UNSET_CALLBACK 240
+#define WLC_SET_CALLBACK 241
+#define WLC_GET_RADAR 242
+#define WLC_SET_RADAR 243
+#define WLC_SET_SPECT_MANAGMENT 244
+#define WLC_GET_SPECT_MANAGMENT 245
+#define WLC_WDS_GET_REMOTE_HWADDR 246
+#define WLC_WDS_GET_WPA_SUP 247
+#define WLC_SET_CS_SCAN_TIMER 248
+#define WLC_GET_CS_SCAN_TIMER 249
+#define WLC_MEASURE_REQUEST 250
+#define WLC_INIT 251
+#define WLC_SEND_QUIET 252
+#define WLC_KEEPALIVE 253
+#define WLC_SEND_PWR_CONSTRAINT 254
+#define WLC_UPGRADE_STATUS 255
+#define WLC_CURRENT_PWR 256
+#define WLC_GET_SCAN_PASSIVE_TIME 257
+#define WLC_SET_SCAN_PASSIVE_TIME 258
+#define WLC_LEGACY_LINK_BEHAVIOR 259
+#define WLC_GET_CHANNELS_IN_COUNTRY 260
+#define WLC_GET_COUNTRY_LIST 261
+#define WLC_GET_VAR 262
+#define WLC_SET_VAR 263
+#define WLC_NVRAM_GET 264
+#define WLC_NVRAM_SET 265
+#define WLC_NVRAM_DUMP 266
+#define WLC_REBOOT 267
+#define WLC_SET_WSEC_PMK 268
+#define WLC_GET_AUTH_MODE 269
+#define WLC_SET_AUTH_MODE 270
+#define WLC_GET_WAKEENTRY 271
+#define WLC_SET_WAKEENTRY 272
+#define WLC_NDCONFIG_ITEM 273
+#define WLC_NVOTPW 274
+#define WLC_OTPW 275
+#define WLC_IOV_BLOCK_GET 276
+#define WLC_IOV_MODULES_GET 277
+#define WLC_SOFT_RESET 278
+#define WLC_GET_ALLOW_MODE 279
+#define WLC_SET_ALLOW_MODE 280
+#define WLC_GET_DESIRED_BSSID 281
+#define WLC_SET_DESIRED_BSSID 282
+#define WLC_DISASSOC_MYAP 283
+#define WLC_GET_NBANDS 284
+#define WLC_GET_BANDSTATES 285
+#define WLC_GET_WLC_BSS_INFO 286
+#define WLC_GET_ASSOC_INFO 287
+#define WLC_GET_OID_PHY 288
+#define WLC_SET_OID_PHY 289
+#define WLC_SET_ASSOC_TIME 290
+#define WLC_GET_DESIRED_SSID 291
+#define WLC_GET_CHANSPEC 292
+#define WLC_GET_ASSOC_STATE 293
+#define WLC_SET_PHY_STATE 294
+#define WLC_GET_SCAN_PENDING 295
+#define WLC_GET_SCANREQ_PENDING 296
+#define WLC_GET_PREV_ROAM_REASON 297
+#define WLC_SET_PREV_ROAM_REASON 298
+#define WLC_GET_BANDSTATES_PI 299
+#define WLC_GET_PHY_STATE 300
+#define WLC_GET_BSS_WPA_RSN 301
+#define WLC_GET_BSS_WPA2_RSN 302
+#define WLC_GET_BSS_BCN_TS 303
+#define WLC_GET_INT_DISASSOC 304
+#define WLC_SET_NUM_PEERS 305
+#define WLC_GET_NUM_BSS 306
+#define WLC_LAST 307
+
+
+
+#define WL_RADIO_SW_DISABLE (1<<0)
+#define WL_RADIO_HW_DISABLE (1<<1)
+#define WL_RADIO_MPC_DISABLE (1<<2)
+#define WL_RADIO_COUNTRY_DISABLE (1<<3)
+
+
+#define WL_TXPWR_OVERRIDE (1U<<31)
+
+#define WL_PHY_PAVARS_LEN 6
+
+
+#define WL_DIAG_INTERRUPT 1
+#define WL_DIAG_LOOPBACK 2
+#define WL_DIAG_MEMORY 3
+#define WL_DIAG_LED 4
+#define WL_DIAG_REG 5
+#define WL_DIAG_SROM 6
+#define WL_DIAG_DMA 7
+
+#define WL_DIAGERR_SUCCESS 0
+#define WL_DIAGERR_FAIL_TO_RUN 1
+#define WL_DIAGERR_NOT_SUPPORTED 2
+#define WL_DIAGERR_INTERRUPT_FAIL 3
+#define WL_DIAGERR_LOOPBACK_FAIL 4
+#define WL_DIAGERR_SROM_FAIL 5
+#define WL_DIAGERR_SROM_BADCRC 6
+#define WL_DIAGERR_REG_FAIL 7
+#define WL_DIAGERR_MEMORY_FAIL 8
+#define WL_DIAGERR_NOMEM 9
+#define WL_DIAGERR_DMA_FAIL 10
+
+#define WL_DIAGERR_MEMORY_TIMEOUT 11
+#define WL_DIAGERR_MEMORY_BADPATTERN 12
+
+
+#define WLC_BAND_AUTO 0
+#define WLC_BAND_5G 1
+#define WLC_BAND_2G 2
+#define WLC_BAND_ALL 3
+
+
+#define WL_CHAN_FREQ_RANGE_2G 0
+#define WL_CHAN_FREQ_RANGE_5GL 1
+#define WL_CHAN_FREQ_RANGE_5GM 2
+#define WL_CHAN_FREQ_RANGE_5GH 3
+
+
+#define WLC_PHY_TYPE_A 0
+#define WLC_PHY_TYPE_B 1
+#define WLC_PHY_TYPE_G 2
+#define WLC_PHY_TYPE_N 4
+#define WLC_PHY_TYPE_LP 5
+#define WLC_PHY_TYPE_SSN 6
+#define WLC_PHY_TYPE_NULL 0xf
+
+
+#define WLC_MACMODE_DISABLED 0
+#define WLC_MACMODE_DENY 1
+#define WLC_MACMODE_ALLOW 2
+
+
+#define GMODE_LEGACY_B 0
+#define GMODE_AUTO 1
+#define GMODE_ONLY 2
+#define GMODE_B_DEFERRED 3
+#define GMODE_PERFORMANCE 4
+#define GMODE_LRS 5
+#define GMODE_MAX 6
+
+
+#define WLC_PLCP_AUTO -1
+#define WLC_PLCP_SHORT 0
+#define WLC_PLCP_LONG 1
+
+
+#define WLC_PROTECTION_AUTO -1
+#define WLC_PROTECTION_OFF 0
+#define WLC_PROTECTION_ON 1
+#define WLC_PROTECTION_MMHDR_ONLY 2
+#define WLC_PROTECTION_CTS_ONLY 3
+
+
+#define WLC_PROTECTION_CTL_OFF 0
+#define WLC_PROTECTION_CTL_LOCAL 1
+#define WLC_PROTECTION_CTL_OVERLAP 2
+
+
+#define WLC_N_PROTECTION_OFF 0
+#define WLC_N_PROTECTION_OPTIONAL 1
+#define WLC_N_PROTECTION_20IN40 2
+#define WLC_N_PROTECTION_MIXEDMODE 3
+
+
+#define WLC_N_PREAMBLE_MIXEDMODE 0
+#define WLC_N_PREAMBLE_GF 1
+
+
+#define WLC_N_BW_20ALL 0
+#define WLC_N_BW_40ALL 1
+#define WLC_N_BW_20IN2G_40IN5G 2
+
+
+#define WLC_N_TXRX_CHAIN0 0
+#define WLC_N_TXRX_CHAIN1 1
+
+
+#define WLC_N_SGI_20 0x01
+#define WLC_N_SGI_40 0x02
+
+
+#define PM_OFF 0
+#define PM_MAX 1
+#define PM_FAST 2
+
+#define LISTEN_INTERVAL 20
+
+#define INTERFERE_NONE 0
+#define NON_WLAN 1
+#define WLAN_MANUAL 2
+#define WLAN_AUTO 3
+#define AUTO_ACTIVE (1 << 7)
+
+typedef struct wl_aci_args {
+ int enter_aci_thresh;
+ int exit_aci_thresh;
+ int usec_spin;
+ int glitch_delay;
+ uint16 nphy_adcpwr_enter_thresh;
+ uint16 nphy_adcpwr_exit_thresh;
+ uint16 nphy_repeat_ctr;
+ uint16 nphy_num_samples;
+ uint16 nphy_undetect_window_sz;
+ uint16 nphy_b_energy_lo_aci;
+ uint16 nphy_b_energy_md_aci;
+ uint16 nphy_b_energy_hi_aci;
+} wl_aci_args_t;
+
+#define WL_ACI_ARGS_LEGACY_LENGTH 16
+
+
+
+#define WL_ERROR_VAL 0x00000001
+#define WL_TRACE_VAL 0x00000002
+#define WL_PRHDRS_VAL 0x00000004
+#define WL_PRPKT_VAL 0x00000008
+#define WL_INFORM_VAL 0x00000010
+#define WL_TMP_VAL 0x00000020
+#define WL_OID_VAL 0x00000040
+#define WL_RATE_VAL 0x00000080
+#define WL_ASSOC_VAL 0x00000100
+#define WL_PRUSR_VAL 0x00000200
+#define WL_PS_VAL 0x00000400
+#define WL_TXPWR_VAL 0x00000800
+#define WL_PORT_VAL 0x00001000
+#define WL_DUAL_VAL 0x00002000
+#define WL_WSEC_VAL 0x00004000
+#define WL_WSEC_DUMP_VAL 0x00008000
+#define WL_LOG_VAL 0x00010000
+#define WL_NRSSI_VAL 0x00020000
+#define WL_LOFT_VAL 0x00040000
+#define WL_REGULATORY_VAL 0x00080000
+#define WL_PHYCAL_VAL 0x00100000
+#define WL_RADAR_VAL 0x00200000
+#define WL_MPC_VAL 0x00400000
+#define WL_APSTA_VAL 0x00800000
+#define WL_DFS_VAL 0x01000000
+#define WL_BA_VAL 0x02000000
+#define WL_MBSS_VAL 0x04000000
+#define WL_CAC_VAL 0x08000000
+#define WL_AMSDU_VAL 0x10000000
+#define WL_AMPDU_VAL 0x20000000
+#define WL_FFPLD_VAL 0x40000000
+
+
+#define WL_DPT_VAL 0x00000001
+#define WL_SCAN_VAL 0x00000002
+#define WL_WOWL_VAL 0x00000004
+#define WL_COEX_VAL 0x00000008
+#define WL_RTDC_VAL 0x00000010
+#define WL_BTA_VAL 0x00000040
+
+
+#define WL_LED_NUMGPIO 16
+
+
+#define WL_LED_OFF 0
+#define WL_LED_ON 1
+#define WL_LED_ACTIVITY 2
+#define WL_LED_RADIO 3
+#define WL_LED_ARADIO 4
+#define WL_LED_BRADIO 5
+#define WL_LED_BGMODE 6
+#define WL_LED_WI1 7
+#define WL_LED_WI2 8
+#define WL_LED_WI3 9
+#define WL_LED_ASSOC 10
+#define WL_LED_INACTIVE 11
+#define WL_LED_ASSOCACT 12
+#define WL_LED_NUMBEHAVIOR 13
+
+
+#define WL_LED_BEH_MASK 0x7f
+#define WL_LED_AL_MASK 0x80
+
+
+#define WL_NUMCHANNELS 64
+#define WL_NUMCHANSPECS 100
+
+
+#define WL_WDS_WPA_ROLE_AUTH 0
+#define WL_WDS_WPA_ROLE_SUP 1
+#define WL_WDS_WPA_ROLE_AUTO 255
+
+
+#define WL_EVENTING_MASK_LEN 16
+
+
+#define VNDR_IE_CMD_LEN 4
+
+
+#define VNDR_IE_BEACON_FLAG 0x1
+#define VNDR_IE_PRBRSP_FLAG 0x2
+#define VNDR_IE_ASSOCRSP_FLAG 0x4
+#define VNDR_IE_AUTHRSP_FLAG 0x8
+#define VNDR_IE_PRBREQ_FLAG 0x10
+#define VNDR_IE_ASSOCREQ_FLAG 0x20
+#define VNDR_IE_CUSTOM_FLAG 0x100
+
+#define VNDR_IE_INFO_HDR_LEN (sizeof(uint32))
+
+typedef struct {
+ uint32 pktflag;
+ vndr_ie_t vndr_ie_data;
+} vndr_ie_info_t;
+
+typedef struct {
+ int iecount;
+ vndr_ie_info_t vndr_ie_list[1];
+} vndr_ie_buf_t;
+
+typedef struct {
+ char cmd[VNDR_IE_CMD_LEN];
+ vndr_ie_buf_t vndr_ie_buffer;
+} vndr_ie_setbuf_t;
+
+
+
+
+#define WL_JOIN_PREF_RSSI 1
+#define WL_JOIN_PREF_WPA 2
+#define WL_JOIN_PREF_BAND 3
+
+
+#define WLJP_BAND_ASSOC_PREF 255
+
+
+#define WL_WPA_ACP_MCS_ANY "\x00\x00\x00\x00"
+
+struct tsinfo_arg {
+ uint8 octets[3];
+};
+
+
+#define NFIFO 6
+
+#define WL_CNT_T_VERSION 5
+#define WL_CNT_EXT_T_VERSION 1
+
+typedef struct {
+ uint16 version;
+ uint16 length;
+
+
+ uint32 txframe;
+ uint32 txbyte;
+ uint32 txretrans;
+ uint32 txerror;
+ uint32 txctl;
+ uint32 txprshort;
+ uint32 txserr;
+ uint32 txnobuf;
+ uint32 txnoassoc;
+ uint32 txrunt;
+ uint32 txchit;
+ uint32 txcmiss;
+
+
+ uint32 txuflo;
+ uint32 txphyerr;
+ uint32 txphycrs;
+
+
+ uint32 rxframe;
+ uint32 rxbyte;
+ uint32 rxerror;
+ uint32 rxctl;
+ uint32 rxnobuf;
+ uint32 rxnondata;
+ uint32 rxbadds;
+ uint32 rxbadcm;
+ uint32 rxfragerr;
+ uint32 rxrunt;
+ uint32 rxgiant;
+ uint32 rxnoscb;
+ uint32 rxbadproto;
+ uint32 rxbadsrcmac;
+ uint32 rxbadda;
+ uint32 rxfilter;
+
+
+ uint32 rxoflo;
+ uint32 rxuflo[NFIFO];
+
+ uint32 d11cnt_txrts_off;
+ uint32 d11cnt_rxcrc_off;
+ uint32 d11cnt_txnocts_off;
+
+
+ uint32 dmade;
+ uint32 dmada;
+ uint32 dmape;
+ uint32 reset;
+ uint32 tbtt;
+ uint32 txdmawar;
+ uint32 pkt_callback_reg_fail;
+
+
+ uint32 txallfrm;
+ uint32 txrtsfrm;
+ uint32 txctsfrm;
+ uint32 txackfrm;
+ uint32 txdnlfrm;
+ uint32 txbcnfrm;
+ uint32 txfunfl[8];
+ uint32 txtplunfl;
+ uint32 txphyerror;
+ uint32 rxfrmtoolong;
+ uint32 rxfrmtooshrt;
+ uint32 rxinvmachdr;
+ uint32 rxbadfcs;
+ uint32 rxbadplcp;
+ uint32 rxcrsglitch;
+ uint32 rxstrt;
+ uint32 rxdfrmucastmbss;
+ uint32 rxmfrmucastmbss;
+ uint32 rxcfrmucast;
+ uint32 rxrtsucast;
+ uint32 rxctsucast;
+ uint32 rxackucast;
+ uint32 rxdfrmocast;
+ uint32 rxmfrmocast;
+ uint32 rxcfrmocast;
+ uint32 rxrtsocast;
+ uint32 rxctsocast;
+ uint32 rxdfrmmcast;
+ uint32 rxmfrmmcast;
+ uint32 rxcfrmmcast;
+ uint32 rxbeaconmbss;
+ uint32 rxdfrmucastobss;
+ uint32 rxbeaconobss;
+ uint32 rxrsptmout;
+ uint32 bcntxcancl;
+ uint32 rxf0ovfl;
+ uint32 rxf1ovfl;
+ uint32 rxf2ovfl;
+ uint32 txsfovfl;
+ uint32 pmqovfl;
+ uint32 rxcgprqfrm;
+ uint32 rxcgprsqovfl;
+ uint32 txcgprsfail;
+ uint32 txcgprssuc;
+ uint32 prs_timeout;
+ uint32 rxnack;
+ uint32 frmscons;
+ uint32 txnack;
+ uint32 txglitch_nack;
+ uint32 txburst;
+
+
+ uint32 txfrag;
+ uint32 txmulti;
+ uint32 txfail;
+ uint32 txretry;
+ uint32 txretrie;
+ uint32 rxdup;
+ uint32 txrts;
+ uint32 txnocts;
+ uint32 txnoack;
+ uint32 rxfrag;
+ uint32 rxmulti;
+ uint32 rxcrc;
+ uint32 txfrmsnt;
+ uint32 rxundec;
+
+
+ uint32 tkipmicfaill;
+ uint32 tkipcntrmsr;
+ uint32 tkipreplay;
+ uint32 ccmpfmterr;
+ uint32 ccmpreplay;
+ uint32 ccmpundec;
+ uint32 fourwayfail;
+ uint32 wepundec;
+ uint32 wepicverr;
+ uint32 decsuccess;
+ uint32 tkipicverr;
+ uint32 wepexcluded;
+
+ uint32 txchanrej;
+ uint32 psmwds;
+ uint32 phywatchdog;
+
+
+ uint32 prq_entries_handled;
+ uint32 prq_undirected_entries;
+ uint32 prq_bad_entries;
+ uint32 atim_suppress_count;
+ uint32 bcn_template_not_ready;
+ uint32 bcn_template_not_ready_done;
+ uint32 late_tbtt_dpc;
+
+
+ uint32 rx1mbps;
+ uint32 rx2mbps;
+ uint32 rx5mbps5;
+ uint32 rx6mbps;
+ uint32 rx9mbps;
+ uint32 rx11mbps;
+ uint32 rx12mbps;
+ uint32 rx18mbps;
+ uint32 rx24mbps;
+ uint32 rx36mbps;
+ uint32 rx48mbps;
+ uint32 rx54mbps;
+ uint32 rx108mbps;
+ uint32 rx162mbps;
+ uint32 rx216mbps;
+ uint32 rx270mbps;
+ uint32 rx324mbps;
+ uint32 rx378mbps;
+ uint32 rx432mbps;
+ uint32 rx486mbps;
+ uint32 rx540mbps;
+
+ uint32 pktengrxducast;
+ uint32 pktengrxdmcast;
+} wl_cnt_t;
+
+typedef struct {
+ uint16 version;
+ uint16 length;
+
+ uint32 rxampdu_sgi;
+ uint32 rxampdu_stbc;
+ uint32 rxmpdu_sgi;
+ uint32 rxmpdu_stbc;
+ uint32 rxmcs0_40M;
+ uint32 rxmcs1_40M;
+ uint32 rxmcs2_40M;
+ uint32 rxmcs3_40M;
+ uint32 rxmcs4_40M;
+ uint32 rxmcs5_40M;
+ uint32 rxmcs6_40M;
+ uint32 rxmcs7_40M;
+ uint32 rxmcs32_40M;
+
+ uint32 txfrmsnt_20Mlo;
+ uint32 txfrmsnt_20Mup;
+ uint32 txfrmsnt_40M;
+
+ uint32 rx_20ul;
+} wl_cnt_ext_t;
+
+#define WL_RXDIV_STATS_T_VERSION 1
+typedef struct {
+ uint16 version;
+ uint16 length;
+
+ uint32 rxant[4];
+} wl_rxdiv_stats_t;
+
+#define WL_DELTA_STATS_T_VERSION 1
+
+typedef struct {
+ uint16 version;
+ uint16 length;
+
+
+ uint32 txframe;
+ uint32 txbyte;
+ uint32 txretrans;
+ uint32 txfail;
+
+
+ uint32 rxframe;
+ uint32 rxbyte;
+
+
+ uint32 rx1mbps;
+ uint32 rx2mbps;
+ uint32 rx5mbps5;
+ uint32 rx6mbps;
+ uint32 rx9mbps;
+ uint32 rx11mbps;
+ uint32 rx12mbps;
+ uint32 rx18mbps;
+ uint32 rx24mbps;
+ uint32 rx36mbps;
+ uint32 rx48mbps;
+ uint32 rx54mbps;
+ uint32 rx108mbps;
+ uint32 rx162mbps;
+ uint32 rx216mbps;
+ uint32 rx270mbps;
+ uint32 rx324mbps;
+ uint32 rx378mbps;
+ uint32 rx432mbps;
+ uint32 rx486mbps;
+ uint32 rx540mbps;
+} wl_delta_stats_t;
+
+#define WL_WME_CNT_VERSION 1
+
+typedef struct {
+ uint32 packets;
+ uint32 bytes;
+} wl_traffic_stats_t;
+
+typedef struct {
+ uint16 version;
+ uint16 length;
+
+ wl_traffic_stats_t tx[AC_COUNT];
+ wl_traffic_stats_t tx_failed[AC_COUNT];
+ wl_traffic_stats_t rx[AC_COUNT];
+ wl_traffic_stats_t rx_failed[AC_COUNT];
+
+ wl_traffic_stats_t forward[AC_COUNT];
+
+ wl_traffic_stats_t tx_expired[AC_COUNT];
+
+} wl_wme_cnt_t;
+
+
+
+#define WLC_ROAM_TRIGGER_DEFAULT 0
+#define WLC_ROAM_TRIGGER_BANDWIDTH 1
+#define WLC_ROAM_TRIGGER_DISTANCE 2
+#define WLC_ROAM_TRIGGER_MAX_VALUE 2
+
+
+enum {
+ PFN_LIST_ORDER,
+ PFN_RSSI
+};
+
+enum {
+ DISABLE,
+ ENABLE
+};
+
+#define SORT_CRITERIA_BIT 0
+#define AUTO_NET_SWITCH_BIT 1
+#define ENABLE_BKGRD_SCAN_BIT 2
+#define IMMEDIATE_SCAN_BIT 3
+#define AUTO_CONNECT_BIT 4
+
+#define SORT_CRITERIA_MASK 0x01
+#define AUTO_NET_SWITCH_MASK 0x02
+#define ENABLE_BKGRD_SCAN_MASK 0x04
+#define IMMEDIATE_SCAN_MASK 0x08
+#define AUTO_CONNECT_MASK 0x10
+
+#define PFN_VERSION 1
+
+#define MAX_PFN_LIST_COUNT 16
+
+
+typedef struct wl_pfn_param {
+ int32 version;
+ int32 scan_freq;
+ int32 lost_network_timeout;
+ int16 flags;
+ int16 rssi_margin;
+} wl_pfn_param_t;
+
+typedef struct wl_pfn {
+ wlc_ssid_t ssid;
+ int32 bss_type;
+ int32 infra;
+ int32 auth;
+ uint32 wpa_auth;
+ int32 wsec;
+#ifdef WLPFN_AUTO_CONNECT
+ union {
+ wl_wsec_key_t sec_key;
+ wsec_pmk_t wpa_sec_key;
+ } pfn_security;
+#endif
+} wl_pfn_t;
+
+
+#define TOE_TX_CSUM_OL 0x00000001
+#define TOE_RX_CSUM_OL 0x00000002
+
+
+#define TOE_ERRTEST_TX_CSUM 0x00000001
+#define TOE_ERRTEST_RX_CSUM 0x00000002
+#define TOE_ERRTEST_RX_CSUM2 0x00000004
+
+struct toe_ol_stats_t {
+
+ uint32 tx_summed;
+
+
+ uint32 tx_iph_fill;
+ uint32 tx_tcp_fill;
+ uint32 tx_udp_fill;
+ uint32 tx_icmp_fill;
+
+
+ uint32 rx_iph_good;
+ uint32 rx_iph_bad;
+ uint32 rx_tcp_good;
+ uint32 rx_tcp_bad;
+ uint32 rx_udp_good;
+ uint32 rx_udp_bad;
+ uint32 rx_icmp_good;
+ uint32 rx_icmp_bad;
+
+
+ uint32 tx_tcp_errinj;
+ uint32 tx_udp_errinj;
+ uint32 tx_icmp_errinj;
+
+
+ uint32 rx_tcp_errinj;
+ uint32 rx_udp_errinj;
+ uint32 rx_icmp_errinj;
+};
+
+
+#define ARP_OL_AGENT 0x00000001
+#define ARP_OL_SNOOP 0x00000002
+#define ARP_OL_HOST_AUTO_REPLY 0x00000004
+#define ARP_OL_PEER_AUTO_REPLY 0x00000008
+
+
+#define ARP_ERRTEST_REPLY_PEER 0x1
+#define ARP_ERRTEST_REPLY_HOST 0x2
+
+#define ARP_MULTIHOMING_MAX 8
+
+
+struct arp_ol_stats_t {
+ uint32 host_ip_entries;
+ uint32 host_ip_overflow;
+
+ uint32 arp_table_entries;
+ uint32 arp_table_overflow;
+
+ uint32 host_request;
+ uint32 host_reply;
+ uint32 host_service;
+
+ uint32 peer_request;
+ uint32 peer_request_drop;
+ uint32 peer_reply;
+ uint32 peer_reply_drop;
+ uint32 peer_service;
+};
+
+
+
+
+
+typedef struct wl_keep_alive_pkt {
+ uint32 period_msec;
+ uint16 len_bytes;
+ uint8 data[1];
+} wl_keep_alive_pkt_t;
+
+#define WL_KEEP_ALIVE_FIXED_LEN OFFSETOF(wl_keep_alive_pkt_t, data)
+
+
+
+
+
+typedef enum wl_pkt_filter_type {
+ WL_PKT_FILTER_TYPE_PATTERN_MATCH
+} wl_pkt_filter_type_t;
+
+#define WL_PKT_FILTER_TYPE wl_pkt_filter_type_t
+
+
+typedef struct wl_pkt_filter_pattern {
+ uint32 offset;
+ uint32 size_bytes;
+ uint8 mask_and_pattern[1];
+} wl_pkt_filter_pattern_t;
+
+
+typedef struct wl_pkt_filter {
+ uint32 id;
+ uint32 type;
+ uint32 negate_match;
+ union {
+ wl_pkt_filter_pattern_t pattern;
+ } u;
+} wl_pkt_filter_t;
+
+#define WL_PKT_FILTER_FIXED_LEN OFFSETOF(wl_pkt_filter_t, u)
+#define WL_PKT_FILTER_PATTERN_FIXED_LEN OFFSETOF(wl_pkt_filter_pattern_t, mask_and_pattern)
+
+
+typedef struct wl_pkt_filter_enable {
+ uint32 id;
+ uint32 enable;
+} wl_pkt_filter_enable_t;
+
+
+typedef struct wl_pkt_filter_list {
+ uint32 num;
+ wl_pkt_filter_t filter[1];
+} wl_pkt_filter_list_t;
+
+#define WL_PKT_FILTER_LIST_FIXED_LEN OFFSETOF(wl_pkt_filter_list_t, filter)
+
+
+typedef struct wl_pkt_filter_stats {
+ uint32 num_pkts_matched;
+ uint32 num_pkts_forwarded;
+ uint32 num_pkts_discarded;
+} wl_pkt_filter_stats_t;
+
+
+typedef struct wl_seq_cmd_ioctl {
+ uint32 cmd;
+ uint32 len;
+} wl_seq_cmd_ioctl_t;
+
+#define WL_SEQ_CMD_ALIGN_BYTES 4
+
+
+#define WL_SEQ_CMDS_GET_IOCTL_FILTER(cmd) \
+ (((cmd) == WLC_GET_MAGIC) || \
+ ((cmd) == WLC_GET_VERSION) || \
+ ((cmd) == WLC_GET_AP) || \
+ ((cmd) == WLC_GET_INSTANCE))
+
+
+
+#define WL_PKTENG_PER_TX_START 0x01
+#define WL_PKTENG_PER_TX_STOP 0x02
+#define WL_PKTENG_PER_RX_START 0x04
+#define WL_PKTENG_PER_RX_WITH_ACK_START 0x05
+#define WL_PKTENG_PER_TX_WITH_ACK_START 0x06
+#define WL_PKTENG_PER_RX_STOP 0x08
+#define WL_PKTENG_PER_MASK 0xff
+
+#define WL_PKTENG_SYNCHRONOUS 0x100
+
+typedef struct wl_pkteng {
+ uint32 flags;
+ uint32 delay;
+ uint32 nframes;
+ uint32 length;
+ uint8 seqno;
+ struct ether_addr dest;
+ struct ether_addr src;
+} wl_pkteng_t;
+
+#define NUM_80211b_RATES 4
+#define NUM_80211ag_RATES 8
+#define NUM_80211n_RATES 32
+#define NUM_80211_RATES (NUM_80211b_RATES+NUM_80211ag_RATES+NUM_80211n_RATES)
+typedef struct wl_pkteng_stats {
+ uint32 lostfrmcnt;
+ int32 rssi;
+ int32 snr;
+ uint16 rxpktcnt[NUM_80211_RATES+1];
+} wl_pkteng_stats_t;
+
+#define WL_WOWL_MAGIC (1 << 0)
+#define WL_WOWL_NET (1 << 1)
+#define WL_WOWL_DIS (1 << 2)
+#define WL_WOWL_RETR (1 << 3)
+#define WL_WOWL_BCN (1 << 4)
+#define WL_WOWL_TST (1 << 5)
+#define WL_WOWL_BCAST (1 << 15)
+
+#define MAGIC_PKT_MINLEN 102
+
+typedef struct {
+ uint masksize;
+ uint offset;
+ uint patternoffset;
+ uint patternsize;
+
+
+} wl_wowl_pattern_t;
+
+typedef struct {
+ uint count;
+ wl_wowl_pattern_t pattern[1];
+} wl_wowl_pattern_list_t;
+
+typedef struct {
+ uint8 pci_wakeind;
+ uint16 ucode_wakeind;
+} wl_wowl_wakeind_t;
+
+
+typedef struct wl_txrate_class {
+ uint8 init_rate;
+ uint8 min_rate;
+ uint8 max_rate;
+} wl_txrate_class_t;
+
+
+
+
+#define WLC_OBSS_SCAN_PASSIVE_DWELL_DEFAULT 100
+#define WLC_OBSS_SCAN_PASSIVE_DWELL_MIN 5
+#define WLC_OBSS_SCAN_PASSIVE_DWELL_MAX 1000
+#define WLC_OBSS_SCAN_ACTIVE_DWELL_DEFAULT 20
+#define WLC_OBSS_SCAN_ACTIVE_DWELL_MIN 10
+#define WLC_OBSS_SCAN_ACTIVE_DWELL_MAX 1000
+#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_DEFAULT 300
+#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MIN 10
+#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MAX 900
+#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_DEFAULT 5
+#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MIN 5
+#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MAX 100
+#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_DEFAULT 200
+#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MIN 200
+#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MAX 10000
+#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_DEFAULT 20
+#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MIN 20
+#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MAX 10000
+#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_DEFAULT 25
+#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MIN 0
+#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MAX 100
+
+
+typedef struct wl_obss_scan_arg {
+ int16 passive_dwell;
+ int16 active_dwell;
+ int16 bss_widthscan_interval;
+ int16 passive_total;
+ int16 active_total;
+ int16 chanwidth_transition_delay;
+ int16 activity_threshold;
+} wl_obss_scan_arg_t;
+#define WL_OBSS_SCAN_PARAM_LEN sizeof(wl_obss_scan_arg_t)
+#define WL_MIN_NUM_OBSS_SCAN_ARG 7
+
+#define WL_COEX_INFO_MASK 0x07
+#define WL_COEX_INFO_REQ 0x01
+#define WL_COEX_40MHZ_INTOLERANT 0x02
+#define WL_COEX_WIDTH20 0x04
+
+typedef struct wl_action_obss_coex_req {
+ uint8 info;
+ uint8 num;
+ uint8 ch_list[1];
+} wl_action_obss_coex_req_t;
+
+
+#define MAX_RSSI_LEVELS 8
+
+
+typedef struct wl_rssi_event {
+
+ uint32 rate_limit_msec;
+
+ uint8 num_rssi_levels;
+
+ int8 rssi_levels[MAX_RSSI_LEVELS];
+} wl_rssi_event_t;
+
+
+
+#define WLFEATURE_DISABLE_11N 0x00000001
+#define WLFEATURE_DISABLE_11N_STBC_TX 0x00000002
+#define WLFEATURE_DISABLE_11N_STBC_RX 0x00000004
+#define WLFEATURE_DISABLE_11N_SGI_TX 0x00000008
+#define WLFEATURE_DISABLE_11N_SGI_RX 0x00000010
+#define WLFEATURE_DISABLE_11N_AMPDU_TX 0x00000020
+#define WLFEATURE_DISABLE_11N_AMPDU_RX 0x00000040
+#define WLFEATURE_DISABLE_11N_GF 0x00000080
+
+
+
+#include <packed_section_end.h>
+
+
+#include <packed_section_start.h>
+
+
+typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_hdr {
+ struct ether_addr staAddr;
+ uint16 ieLen;
+} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_hdr_t;
+
+typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_data {
+ sta_prbreq_wps_ie_hdr_t hdr;
+ uint8 ieData[1];
+} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_data_t;
+
+typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_list {
+ uint32 totLen;
+ uint8 ieDataList[1];
+} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_list_t;
+
+
+#include <packed_section_end.h>
+
+#endif
diff --git a/drivers/net/wireless/bcm4329/linux_osl.c b/drivers/net/wireless/bcm4329/linux_osl.c
new file mode 100644
index 00000000000..d00bd1ca291
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/linux_osl.c
@@ -0,0 +1,622 @@
+/*
+ * Linux OS Independent Layer
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: linux_osl.c,v 1.125.12.3.8.7 2010/05/04 21:10:04 Exp $
+ */
+
+
+#define LINUX_OSL
+
+#include <typedefs.h>
+#include <bcmendian.h>
+#include <linuxver.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <linux/delay.h>
+#include <pcicfg.h>
+
+#define PCI_CFG_RETRY 10
+
+#define OS_HANDLE_MAGIC 0x1234abcd
+#define BCM_MEM_FILENAME_LEN 24
+
+#ifdef DHD_USE_STATIC_BUF
+#define MAX_STATIC_BUF_NUM 16
+#define STATIC_BUF_SIZE (PAGE_SIZE*2)
+#define STATIC_BUF_TOTAL_LEN (MAX_STATIC_BUF_NUM*STATIC_BUF_SIZE)
+typedef struct bcm_static_buf {
+ struct semaphore static_sem;
+ unsigned char *buf_ptr;
+ unsigned char buf_use[MAX_STATIC_BUF_NUM];
+} bcm_static_buf_t;
+
+static bcm_static_buf_t *bcm_static_buf = 0;
+
+#define MAX_STATIC_PKT_NUM 8
+typedef struct bcm_static_pkt {
+ struct sk_buff *skb_4k[MAX_STATIC_PKT_NUM];
+ struct sk_buff *skb_8k[MAX_STATIC_PKT_NUM];
+ struct semaphore osl_pkt_sem;
+ unsigned char pkt_use[MAX_STATIC_PKT_NUM*2];
+} bcm_static_pkt_t;
+static bcm_static_pkt_t *bcm_static_skb = 0;
+
+#endif
+typedef struct bcm_mem_link {
+ struct bcm_mem_link *prev;
+ struct bcm_mem_link *next;
+ uint size;
+ int line;
+ char file[BCM_MEM_FILENAME_LEN];
+} bcm_mem_link_t;
+
+struct osl_info {
+ osl_pubinfo_t pub;
+ uint magic;
+ void *pdev;
+ uint malloced;
+ uint failed;
+ uint bustype;
+ bcm_mem_link_t *dbgmem_list;
+};
+
+static int16 linuxbcmerrormap[] =
+{ 0,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -E2BIG,
+ -E2BIG,
+ -EBUSY,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EFAULT,
+ -ENOMEM,
+ -EOPNOTSUPP,
+ -EMSGSIZE,
+ -EINVAL,
+ -EPERM,
+ -ENOMEM,
+ -EINVAL,
+ -ERANGE,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EINVAL,
+ -EIO,
+ -ENODEV,
+ -EINVAL,
+ -EIO,
+ -EIO,
+ -EINVAL,
+ -EINVAL,
+
+
+
+#if BCME_LAST != -41
+#error "You need to add a OS error translation in the linuxbcmerrormap \
+ for new error code defined in bcmutils.h"
+#endif
+};
+
+
+int
+osl_error(int bcmerror)
+{
+ if (bcmerror > 0)
+ bcmerror = 0;
+ else if (bcmerror < BCME_LAST)
+ bcmerror = BCME_ERROR;
+
+
+ return linuxbcmerrormap[-bcmerror];
+}
+
+void * dhd_os_prealloc(int section, unsigned long size);
+osl_t *
+osl_attach(void *pdev, uint bustype, bool pkttag)
+{
+ osl_t *osh;
+ gfp_t flags;
+
+ flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+ osh = kmalloc(sizeof(osl_t), flags);
+ ASSERT(osh);
+
+ bzero(osh, sizeof(osl_t));
+
+
+ ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(linuxbcmerrormap) - 1));
+
+ osh->magic = OS_HANDLE_MAGIC;
+ osh->malloced = 0;
+ osh->failed = 0;
+ osh->dbgmem_list = NULL;
+ osh->pdev = pdev;
+ osh->pub.pkttag = pkttag;
+ osh->bustype = bustype;
+
+ switch (bustype) {
+ case PCI_BUS:
+ case SI_BUS:
+ case PCMCIA_BUS:
+ osh->pub.mmbus = TRUE;
+ break;
+ case JTAG_BUS:
+ case SDIO_BUS:
+ case USB_BUS:
+ case SPI_BUS:
+ osh->pub.mmbus = FALSE;
+ break;
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+
+#ifdef DHD_USE_STATIC_BUF
+
+
+ if (!bcm_static_buf) {
+ if (!(bcm_static_buf = (bcm_static_buf_t *)dhd_os_prealloc(3, STATIC_BUF_SIZE+
+ STATIC_BUF_TOTAL_LEN))) {
+ printk("can not alloc static buf!\n");
+ }
+ else {
+ /* printk("alloc static buf at %x!\n", (unsigned int)bcm_static_buf); */
+ }
+
+ init_MUTEX(&bcm_static_buf->static_sem);
+
+
+ bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE;
+
+ }
+
+ if (!bcm_static_skb)
+ {
+ int i;
+ void *skb_buff_ptr = 0;
+ bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048);
+ skb_buff_ptr = dhd_os_prealloc(4, 0);
+
+ bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *)*16);
+ for (i = 0; i < MAX_STATIC_PKT_NUM*2; i++)
+ bcm_static_skb->pkt_use[i] = 0;
+
+ init_MUTEX(&bcm_static_skb->osl_pkt_sem);
+ }
+#endif
+ return osh;
+}
+
+void
+osl_detach(osl_t *osh)
+{
+ if (osh == NULL)
+ return;
+
+#ifdef DHD_USE_STATIC_BUF
+ if (bcm_static_buf) {
+ bcm_static_buf = 0;
+ }
+ if (bcm_static_skb) {
+ bcm_static_skb = 0;
+ }
+#endif
+ ASSERT(osh->magic == OS_HANDLE_MAGIC);
+ kfree(osh);
+}
+
+
+void*
+osl_pktget(osl_t *osh, uint len)
+{
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(len))) {
+ skb_put(skb, len);
+ skb->priority = 0;
+
+
+ osh->pub.pktalloced++;
+ }
+
+ return ((void*) skb);
+}
+
+
+void
+osl_pktfree(osl_t *osh, void *p, bool send)
+{
+ struct sk_buff *skb, *nskb;
+
+ skb = (struct sk_buff*) p;
+
+ if (send && osh->pub.tx_fn)
+ osh->pub.tx_fn(osh->pub.tx_ctx, p, 0);
+
+
+ while (skb) {
+ nskb = skb->next;
+ skb->next = NULL;
+
+
+ if (skb->destructor) {
+
+ dev_kfree_skb_any(skb);
+ } else {
+
+ dev_kfree_skb(skb);
+ }
+
+ osh->pub.pktalloced--;
+
+ skb = nskb;
+ }
+}
+
+#ifdef DHD_USE_STATIC_BUF
+void*
+osl_pktget_static(osl_t *osh, uint len)
+{
+ int i = 0;
+ struct sk_buff *skb;
+
+
+ if (len > (PAGE_SIZE*2))
+ {
+ printk("Do we really need this big skb??\n");
+ return osl_pktget(osh, len);
+ }
+
+
+ down(&bcm_static_skb->osl_pkt_sem);
+ if (len <= PAGE_SIZE)
+ {
+
+ for (i = 0; i < MAX_STATIC_PKT_NUM; i++)
+ {
+ if (bcm_static_skb->pkt_use[i] == 0)
+ break;
+ }
+
+ if (i != MAX_STATIC_PKT_NUM)
+ {
+ bcm_static_skb->pkt_use[i] = 1;
+ up(&bcm_static_skb->osl_pkt_sem);
+
+ skb = bcm_static_skb->skb_4k[i];
+ skb->tail = skb->data + len;
+ skb->len = len;
+
+ return skb;
+ }
+ }
+
+
+ for (i = 0; i < MAX_STATIC_PKT_NUM; i++)
+ {
+ if (bcm_static_skb->pkt_use[i+MAX_STATIC_PKT_NUM] == 0)
+ break;
+ }
+
+ if (i != MAX_STATIC_PKT_NUM)
+ {
+ bcm_static_skb->pkt_use[i+MAX_STATIC_PKT_NUM] = 1;
+ up(&bcm_static_skb->osl_pkt_sem);
+ skb = bcm_static_skb->skb_8k[i];
+ skb->tail = skb->data + len;
+ skb->len = len;
+
+ return skb;
+ }
+
+
+
+ up(&bcm_static_skb->osl_pkt_sem);
+ printk("all static pkt in use!\n");
+ return osl_pktget(osh, len);
+}
+
+
+void
+osl_pktfree_static(osl_t *osh, void *p, bool send)
+{
+ int i;
+
+ for (i = 0; i < MAX_STATIC_PKT_NUM*2; i++)
+ {
+ if (p == bcm_static_skb->skb_4k[i])
+ {
+ down(&bcm_static_skb->osl_pkt_sem);
+ bcm_static_skb->pkt_use[i] = 0;
+ up(&bcm_static_skb->osl_pkt_sem);
+
+
+ return;
+ }
+ }
+ return osl_pktfree(osh, p, send);
+}
+#endif
+uint32
+osl_pci_read_config(osl_t *osh, uint offset, uint size)
+{
+ uint val = 0;
+ uint retry = PCI_CFG_RETRY;
+
+ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+
+
+ ASSERT(size == 4);
+
+ do {
+ pci_read_config_dword(osh->pdev, offset, &val);
+ if (val != 0xffffffff)
+ break;
+ } while (retry--);
+
+
+ return (val);
+}
+
+void
+osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val)
+{
+ uint retry = PCI_CFG_RETRY;
+
+ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+
+
+ ASSERT(size == 4);
+
+ do {
+ pci_write_config_dword(osh->pdev, offset, val);
+ if (offset != PCI_BAR0_WIN)
+ break;
+ if (osl_pci_read_config(osh, offset, size) == val)
+ break;
+ } while (retry--);
+
+}
+
+
+uint
+osl_pci_bus(osl_t *osh)
+{
+ ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
+
+ return ((struct pci_dev *)osh->pdev)->bus->number;
+}
+
+
+uint
+osl_pci_slot(osl_t *osh)
+{
+ ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
+
+ return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn);
+}
+
+static void
+osl_pcmcia_attr(osl_t *osh, uint offset, char *buf, int size, bool write)
+{
+}
+
+void
+osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size)
+{
+ osl_pcmcia_attr(osh, offset, (char *) buf, size, FALSE);
+}
+
+void
+osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size)
+{
+ osl_pcmcia_attr(osh, offset, (char *) buf, size, TRUE);
+}
+
+
+
+void*
+osl_malloc(osl_t *osh, uint size)
+{
+ void *addr;
+ gfp_t flags;
+
+ if (osh)
+ ASSERT(osh->magic == OS_HANDLE_MAGIC);
+
+#ifdef DHD_USE_STATIC_BUF
+ if (bcm_static_buf)
+ {
+ int i = 0;
+ if ((size >= PAGE_SIZE)&&(size <= STATIC_BUF_SIZE))
+ {
+ down(&bcm_static_buf->static_sem);
+
+ for (i = 0; i < MAX_STATIC_BUF_NUM; i++)
+ {
+ if (bcm_static_buf->buf_use[i] == 0)
+ break;
+ }
+
+ if (i == MAX_STATIC_BUF_NUM)
+ {
+ up(&bcm_static_buf->static_sem);
+ printk("all static buff in use!\n");
+ goto original;
+ }
+
+ bcm_static_buf->buf_use[i] = 1;
+ up(&bcm_static_buf->static_sem);
+
+ bzero(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i, size);
+ if (osh)
+ osh->malloced += size;
+
+ return ((void *)(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i));
+ }
+ }
+original:
+#endif
+ flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+ if ((addr = kmalloc(size, flags)) == NULL) {
+ if (osh)
+ osh->failed++;
+ return (NULL);
+ }
+ if (osh)
+ osh->malloced += size;
+
+ return (addr);
+}
+
+void
+osl_mfree(osl_t *osh, void *addr, uint size)
+{
+#ifdef DHD_USE_STATIC_BUF
+ if (bcm_static_buf)
+ {
+ if ((addr > (void *)bcm_static_buf) && ((unsigned char *)addr
+ <= ((unsigned char *)bcm_static_buf + STATIC_BUF_TOTAL_LEN)))
+ {
+ int buf_idx = 0;
+
+ buf_idx = ((unsigned char *)addr - bcm_static_buf->buf_ptr)/STATIC_BUF_SIZE;
+
+ down(&bcm_static_buf->static_sem);
+ bcm_static_buf->buf_use[buf_idx] = 0;
+ up(&bcm_static_buf->static_sem);
+
+ if (osh) {
+ ASSERT(osh->magic == OS_HANDLE_MAGIC);
+ osh->malloced -= size;
+ }
+ return;
+ }
+ }
+#endif
+ if (osh) {
+ ASSERT(osh->magic == OS_HANDLE_MAGIC);
+ osh->malloced -= size;
+ }
+ kfree(addr);
+}
+
+uint
+osl_malloced(osl_t *osh)
+{
+ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+ return (osh->malloced);
+}
+
+uint
+osl_malloc_failed(osl_t *osh)
+{
+ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+ return (osh->failed);
+}
+
+void*
+osl_dma_alloc_consistent(osl_t *osh, uint size, ulong *pap)
+{
+ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+
+ return (pci_alloc_consistent(osh->pdev, size, (dma_addr_t*)pap));
+}
+
+void
+osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa)
+{
+ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+
+ pci_free_consistent(osh->pdev, size, va, (dma_addr_t)pa);
+}
+
+uint
+osl_dma_map(osl_t *osh, void *va, uint size, int direction)
+{
+ int dir;
+
+ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+ dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
+ return (pci_map_single(osh->pdev, va, size, dir));
+}
+
+void
+osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction)
+{
+ int dir;
+
+ ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
+ dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
+ pci_unmap_single(osh->pdev, (uint32)pa, size, dir);
+}
+
+
+void
+osl_delay(uint usec)
+{
+ uint d;
+
+ while (usec > 0) {
+ d = MIN(usec, 1000);
+ udelay(d);
+ usec -= d;
+ }
+}
+
+
+
+void *
+osl_pktdup(osl_t *osh, void *skb)
+{
+ void * p;
+ gfp_t flags;
+
+ flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+ if ((p = skb_clone((struct sk_buff*)skb, flags)) == NULL)
+ return NULL;
+
+
+ if (osh->pub.pkttag)
+ bzero((void*)((struct sk_buff *)p)->cb, OSL_PKTTAG_SZ);
+
+
+ osh->pub.pktalloced++;
+ return (p);
+}
diff --git a/drivers/net/wireless/bcm4329/miniopt.c b/drivers/net/wireless/bcm4329/miniopt.c
new file mode 100644
index 00000000000..6a184a75f06
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/miniopt.c
@@ -0,0 +1,163 @@
+/*
+ * Description.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ * $Id: miniopt.c,v 1.1.6.4 2009/09/25 00:32:01 Exp $
+ */
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <typedefs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "miniopt.h"
+
+
+/* ---- Public Variables ------------------------------------------------- */
+/* ---- Private Constants and Types -------------------------------------- */
+
+
+
+/* ---- Private Variables ------------------------------------------------ */
+/* ---- Private Function Prototypes -------------------------------------- */
+/* ---- Functions -------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------- */
+void
+miniopt_init(miniopt_t *t, const char* name, const char* flags, bool longflags)
+{
+ static const char *null_flags = "";
+
+ memset(t, 0, sizeof(miniopt_t));
+ t->name = name;
+ if (flags == NULL)
+ t->flags = null_flags;
+ else
+ t->flags = flags;
+ t->longflags = longflags;
+}
+
+
+/* ----------------------------------------------------------------------- */
+int
+miniopt(miniopt_t *t, char **argv)
+{
+ int keylen;
+ char *p, *eq, *valstr, *endptr = NULL;
+ int err = 0;
+
+ t->consumed = 0;
+ t->positional = FALSE;
+ memset(t->key, 0, MINIOPT_MAXKEY);
+ t->opt = '\0';
+ t->valstr = NULL;
+ t->good_int = FALSE;
+ valstr = NULL;
+
+ if (*argv == NULL) {
+ err = -1;
+ goto exit;
+ }
+
+ p = *argv++;
+ t->consumed++;
+
+ if (!t->opt_end && !strcmp(p, "--")) {
+ t->opt_end = TRUE;
+ if (*argv == NULL) {
+ err = -1;
+ goto exit;
+ }
+ p = *argv++;
+ t->consumed++;
+ }
+
+ if (t->opt_end) {
+ t->positional = TRUE;
+ valstr = p;
+ }
+ else if (!strncmp(p, "--", 2)) {
+ eq = strchr(p, '=');
+ if (eq == NULL && !t->longflags) {
+ fprintf(stderr,
+ "%s: missing \" = \" in long param \"%s\"\n", t->name, p);
+ err = 1;
+ goto exit;
+ }
+ keylen = eq ? (eq - (p + 2)) : (int)strlen(p) - 2;
+ if (keylen > 63) keylen = 63;
+ memcpy(t->key, p + 2, keylen);
+
+ if (eq) {
+ valstr = eq + 1;
+ if (*valstr == '\0') {
+ fprintf(stderr,
+ "%s: missing value after \" = \" in long param \"%s\"\n",
+ t->name, p);
+ err = 1;
+ goto exit;
+ }
+ }
+ }
+ else if (!strncmp(p, "-", 1)) {
+ t->opt = p[1];
+ if (strlen(p) > 2) {
+ fprintf(stderr,
+ "%s: only single char options, error on param \"%s\"\n",
+ t->name, p);
+ err = 1;
+ goto exit;
+ }
+ if (strchr(t->flags, t->opt)) {
+ /* this is a flag option, no value expected */
+ valstr = NULL;
+ } else {
+ if (*argv == NULL) {
+ fprintf(stderr,
+ "%s: missing value parameter after \"%s\"\n", t->name, p);
+ err = 1;
+ goto exit;
+ }
+ valstr = *argv;
+ argv++;
+ t->consumed++;
+ }
+ } else {
+ t->positional = TRUE;
+ valstr = p;
+ }
+
+ /* parse valstr as int just in case */
+ if (valstr) {
+ t->uval = (uint)strtoul(valstr, &endptr, 0);
+ t->val = (int)t->uval;
+ t->good_int = (*endptr == '\0');
+ }
+
+ t->valstr = valstr;
+
+exit:
+ if (err == 1)
+ t->opt = '?';
+
+ return err;
+}
diff --git a/drivers/net/wireless/bcm4329/sbutils.c b/drivers/net/wireless/bcm4329/sbutils.c
new file mode 100644
index 00000000000..46cd51010b7
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/sbutils.c
@@ -0,0 +1,1004 @@
+/*
+ * Misc utility routines for accessing chip-specific features
+ * of the SiliconBackplane-based Broadcom chips.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: sbutils.c,v 1.662.4.10.2.7.4.2 2010/04/19 05:48:48 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmdevs.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <pcicfg.h>
+#include <sbpcmcia.h>
+
+#include "siutils_priv.h"
+
+/* local prototypes */
+static uint _sb_coreidx(si_info_t *sii, uint32 sba);
+static uint _sb_scan(si_info_t *sii, uint32 sba, void *regs, uint bus, uint32 sbba,
+ uint ncores);
+static uint32 _sb_coresba(si_info_t *sii);
+static void *_sb_setcoreidx(si_info_t *sii, uint coreidx);
+
+#define SET_SBREG(sii, r, mask, val) \
+ W_SBREG((sii), (r), ((R_SBREG((sii), (r)) & ~(mask)) | (val)))
+#define REGS2SB(va) (sbconfig_t*) ((int8*)(va) + SBCONFIGOFF)
+
+/* sonicsrev */
+#define SONICS_2_2 (SBIDL_RV_2_2 >> SBIDL_RV_SHIFT)
+#define SONICS_2_3 (SBIDL_RV_2_3 >> SBIDL_RV_SHIFT)
+
+#define R_SBREG(sii, sbr) sb_read_sbreg((sii), (sbr))
+#define W_SBREG(sii, sbr, v) sb_write_sbreg((sii), (sbr), (v))
+#define AND_SBREG(sii, sbr, v) W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) & (v)))
+#define OR_SBREG(sii, sbr, v) W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) | (v)))
+
+static uint32
+sb_read_sbreg(si_info_t *sii, volatile uint32 *sbr)
+{
+ uint8 tmp;
+ uint32 val, intr_val = 0;
+
+
+ /*
+ * compact flash only has 11 bits address, while we needs 12 bits address.
+ * MEM_SEG will be OR'd with other 11 bits address in hardware,
+ * so we program MEM_SEG with 12th bit when necessary(access sb regsiters).
+ * For normal PCMCIA bus(CFTable_regwinsz > 2k), do nothing special
+ */
+ if (PCMCIA(sii)) {
+ INTR_OFF(sii, intr_val);
+ tmp = 1;
+ OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1);
+ sbr = (volatile uint32 *)((uintptr)sbr & ~(1 << 11)); /* mask out bit 11 */
+ }
+
+ val = R_REG(sii->osh, sbr);
+
+ if (PCMCIA(sii)) {
+ tmp = 0;
+ OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1);
+ INTR_RESTORE(sii, intr_val);
+ }
+
+ return (val);
+}
+
+static void
+sb_write_sbreg(si_info_t *sii, volatile uint32 *sbr, uint32 v)
+{
+ uint8 tmp;
+ volatile uint32 dummy;
+ uint32 intr_val = 0;
+
+
+ /*
+ * compact flash only has 11 bits address, while we needs 12 bits address.
+ * MEM_SEG will be OR'd with other 11 bits address in hardware,
+ * so we program MEM_SEG with 12th bit when necessary(access sb regsiters).
+ * For normal PCMCIA bus(CFTable_regwinsz > 2k), do nothing special
+ */
+ if (PCMCIA(sii)) {
+ INTR_OFF(sii, intr_val);
+ tmp = 1;
+ OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1);
+ sbr = (volatile uint32 *)((uintptr)sbr & ~(1 << 11)); /* mask out bit 11 */
+ }
+
+ if (BUSTYPE(sii->pub.bustype) == PCMCIA_BUS) {
+#ifdef IL_BIGENDIAN
+ dummy = R_REG(sii->osh, sbr);
+ W_REG(sii->osh, ((volatile uint16 *)sbr + 1), (uint16)((v >> 16) & 0xffff));
+ dummy = R_REG(sii->osh, sbr);
+ W_REG(sii->osh, (volatile uint16 *)sbr, (uint16)(v & 0xffff));
+#else
+ dummy = R_REG(sii->osh, sbr);
+ W_REG(sii->osh, (volatile uint16 *)sbr, (uint16)(v & 0xffff));
+ dummy = R_REG(sii->osh, sbr);
+ W_REG(sii->osh, ((volatile uint16 *)sbr + 1), (uint16)((v >> 16) & 0xffff));
+#endif /* IL_BIGENDIAN */
+ } else
+ W_REG(sii->osh, sbr, v);
+
+ if (PCMCIA(sii)) {
+ tmp = 0;
+ OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1);
+ INTR_RESTORE(sii, intr_val);
+ }
+}
+
+uint
+sb_coreid(si_t *sih)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+
+ sii = SI_INFO(sih);
+ sb = REGS2SB(sii->curmap);
+
+ return ((R_SBREG(sii, &sb->sbidhigh) & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT);
+}
+
+uint
+sb_flag(si_t *sih)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+
+ sii = SI_INFO(sih);
+ sb = REGS2SB(sii->curmap);
+
+ return R_SBREG(sii, &sb->sbtpsflag) & SBTPS_NUM0_MASK;
+}
+
+void
+sb_setint(si_t *sih, int siflag)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+ uint32 vec;
+
+ sii = SI_INFO(sih);
+ sb = REGS2SB(sii->curmap);
+
+ if (siflag == -1)
+ vec = 0;
+ else
+ vec = 1 << siflag;
+ W_SBREG(sii, &sb->sbintvec, vec);
+}
+
+/* return core index of the core with address 'sba' */
+static uint
+_sb_coreidx(si_info_t *sii, uint32 sba)
+{
+ uint i;
+
+ for (i = 0; i < sii->numcores; i ++)
+ if (sba == sii->common_info->coresba[i])
+ return i;
+ return BADIDX;
+}
+
+/* return core address of the current core */
+static uint32
+_sb_coresba(si_info_t *sii)
+{
+ uint32 sbaddr;
+
+
+ switch (BUSTYPE(sii->pub.bustype)) {
+ case SI_BUS: {
+ sbconfig_t *sb = REGS2SB(sii->curmap);
+ sbaddr = sb_base(R_SBREG(sii, &sb->sbadmatch0));
+ break;
+ }
+
+ case PCI_BUS:
+ sbaddr = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32));
+ break;
+
+ case PCMCIA_BUS: {
+ uint8 tmp = 0;
+ OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR0, &tmp, 1);
+ sbaddr = (uint32)tmp << 12;
+ OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR1, &tmp, 1);
+ sbaddr |= (uint32)tmp << 16;
+ OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR2, &tmp, 1);
+ sbaddr |= (uint32)tmp << 24;
+ break;
+ }
+
+ case SPI_BUS:
+ case SDIO_BUS:
+ sbaddr = (uint32)(uintptr)sii->curmap;
+ break;
+
+
+ default:
+ sbaddr = BADCOREADDR;
+ break;
+ }
+
+ return sbaddr;
+}
+
+uint
+sb_corevendor(si_t *sih)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+
+ sii = SI_INFO(sih);
+ sb = REGS2SB(sii->curmap);
+
+ return ((R_SBREG(sii, &sb->sbidhigh) & SBIDH_VC_MASK) >> SBIDH_VC_SHIFT);
+}
+
+uint
+sb_corerev(si_t *sih)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+ uint sbidh;
+
+ sii = SI_INFO(sih);
+ sb = REGS2SB(sii->curmap);
+ sbidh = R_SBREG(sii, &sb->sbidhigh);
+
+ return (SBCOREREV(sbidh));
+}
+
+/* set core-specific control flags */
+void
+sb_core_cflags_wo(si_t *sih, uint32 mask, uint32 val)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+ uint32 w;
+
+ sii = SI_INFO(sih);
+ sb = REGS2SB(sii->curmap);
+
+ ASSERT((val & ~mask) == 0);
+
+ /* mask and set */
+ w = (R_SBREG(sii, &sb->sbtmstatelow) & ~(mask << SBTML_SICF_SHIFT)) |
+ (val << SBTML_SICF_SHIFT);
+ W_SBREG(sii, &sb->sbtmstatelow, w);
+}
+
+/* set/clear core-specific control flags */
+uint32
+sb_core_cflags(si_t *sih, uint32 mask, uint32 val)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+ uint32 w;
+
+ sii = SI_INFO(sih);
+ sb = REGS2SB(sii->curmap);
+
+ ASSERT((val & ~mask) == 0);
+
+ /* mask and set */
+ if (mask || val) {
+ w = (R_SBREG(sii, &sb->sbtmstatelow) & ~(mask << SBTML_SICF_SHIFT)) |
+ (val << SBTML_SICF_SHIFT);
+ W_SBREG(sii, &sb->sbtmstatelow, w);
+ }
+
+ /* return the new value
+ * for write operation, the following readback ensures the completion of write opration.
+ */
+ return (R_SBREG(sii, &sb->sbtmstatelow) >> SBTML_SICF_SHIFT);
+}
+
+/* set/clear core-specific status flags */
+uint32
+sb_core_sflags(si_t *sih, uint32 mask, uint32 val)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+ uint32 w;
+
+ sii = SI_INFO(sih);
+ sb = REGS2SB(sii->curmap);
+
+ ASSERT((val & ~mask) == 0);
+ ASSERT((mask & ~SISF_CORE_BITS) == 0);
+
+ /* mask and set */
+ if (mask || val) {
+ w = (R_SBREG(sii, &sb->sbtmstatehigh) & ~(mask << SBTMH_SISF_SHIFT)) |
+ (val << SBTMH_SISF_SHIFT);
+ W_SBREG(sii, &sb->sbtmstatehigh, w);
+ }
+
+ /* return the new value */
+ return (R_SBREG(sii, &sb->sbtmstatehigh) >> SBTMH_SISF_SHIFT);
+}
+
+bool
+sb_iscoreup(si_t *sih)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+
+ sii = SI_INFO(sih);
+ sb = REGS2SB(sii->curmap);
+
+ return ((R_SBREG(sii, &sb->sbtmstatelow) &
+ (SBTML_RESET | SBTML_REJ_MASK | (SICF_CLOCK_EN << SBTML_SICF_SHIFT))) ==
+ (SICF_CLOCK_EN << SBTML_SICF_SHIFT));
+}
+
+/*
+ * Switch to 'coreidx', issue a single arbitrary 32bit register mask&set operation,
+ * switch back to the original core, and return the new value.
+ *
+ * When using the silicon backplane, no fidleing with interrupts or core switches are needed.
+ *
+ * Also, when using pci/pcie, we can optimize away the core switching for pci registers
+ * and (on newer pci cores) chipcommon registers.
+ */
+uint
+sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val)
+{
+ uint origidx = 0;
+ uint32 *r = NULL;
+ uint w;
+ uint intr_val = 0;
+ bool fast = FALSE;
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ ASSERT(GOODIDX(coreidx));
+ ASSERT(regoff < SI_CORE_SIZE);
+ ASSERT((val & ~mask) == 0);
+
+ if (coreidx >= SI_MAXCORES)
+ return 0;
+
+ if (BUSTYPE(sii->pub.bustype) == SI_BUS) {
+ /* If internal bus, we can always get at everything */
+ fast = TRUE;
+ /* map if does not exist */
+ if (!sii->common_info->regs[coreidx]) {
+ sii->common_info->regs[coreidx] =
+ REG_MAP(sii->common_info->coresba[coreidx], SI_CORE_SIZE);
+ ASSERT(GOODREGS(sii->common_info->regs[coreidx]));
+ }
+ r = (uint32 *)((uchar *)sii->common_info->regs[coreidx] + regoff);
+ } else if (BUSTYPE(sii->pub.bustype) == PCI_BUS) {
+ /* If pci/pcie, we can get at pci/pcie regs and on newer cores to chipc */
+
+ if ((sii->common_info->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) {
+ /* Chipc registers are mapped at 12KB */
+
+ fast = TRUE;
+ r = (uint32 *)((char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff);
+ } else if (sii->pub.buscoreidx == coreidx) {
+ /* pci registers are at either in the last 2KB of an 8KB window
+ * or, in pcie and pci rev 13 at 8KB
+ */
+ fast = TRUE;
+ if (SI_FAST(sii))
+ r = (uint32 *)((char *)sii->curmap +
+ PCI_16KB0_PCIREGS_OFFSET + regoff);
+ else
+ r = (uint32 *)((char *)sii->curmap +
+ ((regoff >= SBCONFIGOFF) ?
+ PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) +
+ regoff);
+ }
+ }
+
+ if (!fast) {
+ INTR_OFF(sii, intr_val);
+
+ /* save current core index */
+ origidx = si_coreidx(&sii->pub);
+
+ /* switch core */
+ r = (uint32*) ((uchar*)sb_setcoreidx(&sii->pub, coreidx) + regoff);
+ }
+ ASSERT(r != NULL);
+
+ /* mask and set */
+ if (mask || val) {
+ if (regoff >= SBCONFIGOFF) {
+ w = (R_SBREG(sii, r) & ~mask) | val;
+ W_SBREG(sii, r, w);
+ } else {
+ w = (R_REG(sii->osh, r) & ~mask) | val;
+ W_REG(sii->osh, r, w);
+ }
+ }
+
+ /* readback */
+ if (regoff >= SBCONFIGOFF)
+ w = R_SBREG(sii, r);
+ else {
+ if ((CHIPID(sii->pub.chip) == BCM5354_CHIP_ID) &&
+ (coreidx == SI_CC_IDX) &&
+ (regoff == OFFSETOF(chipcregs_t, watchdog))) {
+ w = val;
+ } else
+ w = R_REG(sii->osh, r);
+ }
+
+ if (!fast) {
+ /* restore core index */
+ if (origidx != coreidx)
+ sb_setcoreidx(&sii->pub, origidx);
+
+ INTR_RESTORE(sii, intr_val);
+ }
+
+ return (w);
+}
+
+/* Scan the enumeration space to find all cores starting from the given
+ * bus 'sbba'. Append coreid and other info to the lists in 'si'. 'sba'
+ * is the default core address at chip POR time and 'regs' is the virtual
+ * address that the default core is mapped at. 'ncores' is the number of
+ * cores expected on bus 'sbba'. It returns the total number of cores
+ * starting from bus 'sbba', inclusive.
+ */
+#define SB_MAXBUSES 2
+static uint
+_sb_scan(si_info_t *sii, uint32 sba, void *regs, uint bus, uint32 sbba, uint numcores)
+{
+ uint next;
+ uint ncc = 0;
+ uint i;
+
+ if (bus >= SB_MAXBUSES) {
+ SI_ERROR(("_sb_scan: bus 0x%08x at level %d is too deep to scan\n", sbba, bus));
+ return 0;
+ }
+ SI_MSG(("_sb_scan: scan bus 0x%08x assume %u cores\n", sbba, numcores));
+
+ /* Scan all cores on the bus starting from core 0.
+ * Core addresses must be contiguous on each bus.
+ */
+ for (i = 0, next = sii->numcores; i < numcores && next < SB_BUS_MAXCORES; i++, next++) {
+ sii->common_info->coresba[next] = sbba + (i * SI_CORE_SIZE);
+
+ /* keep and reuse the initial register mapping */
+ if ((BUSTYPE(sii->pub.bustype) == SI_BUS) &&
+ (sii->common_info->coresba[next] == sba)) {
+ SI_MSG(("_sb_scan: reuse mapped regs %p for core %u\n", regs, next));
+ sii->common_info->regs[next] = regs;
+ }
+
+ /* change core to 'next' and read its coreid */
+ sii->curmap = _sb_setcoreidx(sii, next);
+ sii->curidx = next;
+
+ sii->common_info->coreid[next] = sb_coreid(&sii->pub);
+
+ /* core specific processing... */
+ /* chipc provides # cores */
+ if (sii->common_info->coreid[next] == CC_CORE_ID) {
+ chipcregs_t *cc = (chipcregs_t *)sii->curmap;
+ uint32 ccrev = sb_corerev(&sii->pub);
+
+ /* determine numcores - this is the total # cores in the chip */
+ if (((ccrev == 4) || (ccrev >= 6)))
+ numcores = (R_REG(sii->osh, &cc->chipid) & CID_CC_MASK) >>
+ CID_CC_SHIFT;
+ else {
+ /* Older chips */
+ uint chip = sii->pub.chip;
+
+ if (chip == BCM4306_CHIP_ID) /* < 4306c0 */
+ numcores = 6;
+ else if (chip == BCM4704_CHIP_ID)
+ numcores = 9;
+ else if (chip == BCM5365_CHIP_ID)
+ numcores = 7;
+ else {
+ SI_ERROR(("sb_chip2numcores: unsupported chip 0x%x\n",
+ chip));
+ ASSERT(0);
+ numcores = 1;
+ }
+ }
+ SI_MSG(("_sb_scan: there are %u cores in the chip %s\n", numcores,
+ sii->pub.issim ? "QT" : ""));
+ }
+ /* scan bridged SB(s) and add results to the end of the list */
+ else if (sii->common_info->coreid[next] == OCP_CORE_ID) {
+ sbconfig_t *sb = REGS2SB(sii->curmap);
+ uint32 nsbba = R_SBREG(sii, &sb->sbadmatch1);
+ uint nsbcc;
+
+ sii->numcores = next + 1;
+
+ if ((nsbba & 0xfff00000) != SI_ENUM_BASE)
+ continue;
+ nsbba &= 0xfffff000;
+ if (_sb_coreidx(sii, nsbba) != BADIDX)
+ continue;
+
+ nsbcc = (R_SBREG(sii, &sb->sbtmstatehigh) & 0x000f0000) >> 16;
+ nsbcc = _sb_scan(sii, sba, regs, bus + 1, nsbba, nsbcc);
+ if (sbba == SI_ENUM_BASE)
+ numcores -= nsbcc;
+ ncc += nsbcc;
+ }
+ }
+
+ SI_MSG(("_sb_scan: found %u cores on bus 0x%08x\n", i, sbba));
+
+ sii->numcores = i + ncc;
+ return sii->numcores;
+}
+
+/* scan the sb enumerated space to identify all cores */
+void
+sb_scan(si_t *sih, void *regs, uint devid)
+{
+ si_info_t *sii;
+ uint32 origsba;
+
+ sii = SI_INFO(sih);
+
+ /* Save the current core info and validate it later till we know
+ * for sure what is good and what is bad.
+ */
+ origsba = _sb_coresba(sii);
+
+ /* scan all SB(s) starting from SI_ENUM_BASE */
+ sii->numcores = _sb_scan(sii, origsba, regs, 0, SI_ENUM_BASE, 1);
+}
+
+/*
+ * This function changes logical "focus" to the indicated core;
+ * must be called with interrupts off.
+ * Moreover, callers should keep interrupts off during switching out of and back to d11 core
+ */
+void *
+sb_setcoreidx(si_t *sih, uint coreidx)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ if (coreidx >= sii->numcores)
+ return (NULL);
+
+ /*
+ * If the user has provided an interrupt mask enabled function,
+ * then assert interrupts are disabled before switching the core.
+ */
+ ASSERT((sii->intrsenabled_fn == NULL) || !(*(sii)->intrsenabled_fn)((sii)->intr_arg));
+
+ sii->curmap = _sb_setcoreidx(sii, coreidx);
+ sii->curidx = coreidx;
+
+ return (sii->curmap);
+}
+
+/* This function changes the logical "focus" to the indicated core.
+ * Return the current core's virtual address.
+ */
+static void *
+_sb_setcoreidx(si_info_t *sii, uint coreidx)
+{
+ uint32 sbaddr = sii->common_info->coresba[coreidx];
+ void *regs;
+
+ switch (BUSTYPE(sii->pub.bustype)) {
+ case SI_BUS:
+ /* map new one */
+ if (!sii->common_info->regs[coreidx]) {
+ sii->common_info->regs[coreidx] = REG_MAP(sbaddr, SI_CORE_SIZE);
+ ASSERT(GOODREGS(sii->common_info->regs[coreidx]));
+ }
+ regs = sii->common_info->regs[coreidx];
+ break;
+
+ case PCI_BUS:
+ /* point bar0 window */
+ OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, sbaddr);
+ regs = sii->curmap;
+ break;
+
+ case PCMCIA_BUS: {
+ uint8 tmp = (sbaddr >> 12) & 0x0f;
+ OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR0, &tmp, 1);
+ tmp = (sbaddr >> 16) & 0xff;
+ OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR1, &tmp, 1);
+ tmp = (sbaddr >> 24) & 0xff;
+ OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR2, &tmp, 1);
+ regs = sii->curmap;
+ break;
+ }
+ case SPI_BUS:
+ case SDIO_BUS:
+ /* map new one */
+ if (!sii->common_info->regs[coreidx]) {
+ sii->common_info->regs[coreidx] = (void *)(uintptr)sbaddr;
+ ASSERT(GOODREGS(sii->common_info->regs[coreidx]));
+ }
+ regs = sii->common_info->regs[coreidx];
+ break;
+
+
+ default:
+ ASSERT(0);
+ regs = NULL;
+ break;
+ }
+
+ return regs;
+}
+
+/* Return the address of sbadmatch0/1/2/3 register */
+static volatile uint32 *
+sb_admatch(si_info_t *sii, uint asidx)
+{
+ sbconfig_t *sb;
+ volatile uint32 *addrm;
+
+ sb = REGS2SB(sii->curmap);
+
+ switch (asidx) {
+ case 0:
+ addrm = &sb->sbadmatch0;
+ break;
+
+ case 1:
+ addrm = &sb->sbadmatch1;
+ break;
+
+ case 2:
+ addrm = &sb->sbadmatch2;
+ break;
+
+ case 3:
+ addrm = &sb->sbadmatch3;
+ break;
+
+ default:
+ SI_ERROR(("%s: Address space index (%d) out of range\n", __FUNCTION__, asidx));
+ return 0;
+ }
+
+ return (addrm);
+}
+
+/* Return the number of address spaces in current core */
+int
+sb_numaddrspaces(si_t *sih)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+
+ sii = SI_INFO(sih);
+ sb = REGS2SB(sii->curmap);
+
+ /* + 1 because of enumeration space */
+ return ((R_SBREG(sii, &sb->sbidlow) & SBIDL_AR_MASK) >> SBIDL_AR_SHIFT) + 1;
+}
+
+/* Return the address of the nth address space in the current core */
+uint32
+sb_addrspace(si_t *sih, uint asidx)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ return (sb_base(R_SBREG(sii, sb_admatch(sii, asidx))));
+}
+
+/* Return the size of the nth address space in the current core */
+uint32
+sb_addrspacesize(si_t *sih, uint asidx)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ return (sb_size(R_SBREG(sii, sb_admatch(sii, asidx))));
+}
+
+
+/* do buffered registers update */
+void
+sb_commit(si_t *sih)
+{
+ si_info_t *sii;
+ uint origidx;
+ uint intr_val = 0;
+
+ sii = SI_INFO(sih);
+
+ origidx = sii->curidx;
+ ASSERT(GOODIDX(origidx));
+
+ INTR_OFF(sii, intr_val);
+
+ /* switch over to chipcommon core if there is one, else use pci */
+ if (sii->pub.ccrev != NOREV) {
+ chipcregs_t *ccregs = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0);
+
+ /* do the buffer registers update */
+ W_REG(sii->osh, &ccregs->broadcastaddress, SB_COMMIT);
+ W_REG(sii->osh, &ccregs->broadcastdata, 0x0);
+ } else
+ ASSERT(0);
+
+ /* restore core index */
+ sb_setcoreidx(sih, origidx);
+ INTR_RESTORE(sii, intr_val);
+}
+
+void
+sb_core_disable(si_t *sih, uint32 bits)
+{
+ si_info_t *sii;
+ volatile uint32 dummy;
+ sbconfig_t *sb;
+
+ sii = SI_INFO(sih);
+
+ ASSERT(GOODREGS(sii->curmap));
+ sb = REGS2SB(sii->curmap);
+
+ /* if core is already in reset, just return */
+ if (R_SBREG(sii, &sb->sbtmstatelow) & SBTML_RESET)
+ return;
+
+ /* if clocks are not enabled, put into reset and return */
+ if ((R_SBREG(sii, &sb->sbtmstatelow) & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) == 0)
+ goto disable;
+
+ /* set target reject and spin until busy is clear (preserve core-specific bits) */
+ OR_SBREG(sii, &sb->sbtmstatelow, SBTML_REJ);
+ dummy = R_SBREG(sii, &sb->sbtmstatelow);
+ OSL_DELAY(1);
+ SPINWAIT((R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY), 100000);
+ if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY)
+ SI_ERROR(("%s: target state still busy\n", __FUNCTION__));
+
+ if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT) {
+ OR_SBREG(sii, &sb->sbimstate, SBIM_RJ);
+ dummy = R_SBREG(sii, &sb->sbimstate);
+ OSL_DELAY(1);
+ SPINWAIT((R_SBREG(sii, &sb->sbimstate) & SBIM_BY), 100000);
+ }
+
+ /* set reset and reject while enabling the clocks */
+ W_SBREG(sii, &sb->sbtmstatelow,
+ (((bits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
+ SBTML_REJ | SBTML_RESET));
+ dummy = R_SBREG(sii, &sb->sbtmstatelow);
+ OSL_DELAY(10);
+
+ /* don't forget to clear the initiator reject bit */
+ if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT)
+ AND_SBREG(sii, &sb->sbimstate, ~SBIM_RJ);
+
+disable:
+ /* leave reset and reject asserted */
+ W_SBREG(sii, &sb->sbtmstatelow, ((bits << SBTML_SICF_SHIFT) | SBTML_REJ | SBTML_RESET));
+ OSL_DELAY(1);
+}
+
+/* reset and re-enable a core
+ * inputs:
+ * bits - core specific bits that are set during and after reset sequence
+ * resetbits - core specific bits that are set only during reset sequence
+ */
+void
+sb_core_reset(si_t *sih, uint32 bits, uint32 resetbits)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+ volatile uint32 dummy;
+
+ sii = SI_INFO(sih);
+ ASSERT(GOODREGS(sii->curmap));
+ sb = REGS2SB(sii->curmap);
+
+ /*
+ * Must do the disable sequence first to work for arbitrary current core state.
+ */
+ sb_core_disable(sih, (bits | resetbits));
+
+ /*
+ * Now do the initialization sequence.
+ */
+
+ /* set reset while enabling the clock and forcing them on throughout the core */
+ W_SBREG(sii, &sb->sbtmstatelow,
+ (((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
+ SBTML_RESET));
+ dummy = R_SBREG(sii, &sb->sbtmstatelow);
+ OSL_DELAY(1);
+
+ if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_SERR) {
+ W_SBREG(sii, &sb->sbtmstatehigh, 0);
+ }
+ if ((dummy = R_SBREG(sii, &sb->sbimstate)) & (SBIM_IBE | SBIM_TO)) {
+ AND_SBREG(sii, &sb->sbimstate, ~(SBIM_IBE | SBIM_TO));
+ }
+
+ /* clear reset and allow it to propagate throughout the core */
+ W_SBREG(sii, &sb->sbtmstatelow,
+ ((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT));
+ dummy = R_SBREG(sii, &sb->sbtmstatelow);
+ OSL_DELAY(1);
+
+ /* leave clock enabled */
+ W_SBREG(sii, &sb->sbtmstatelow, ((bits | SICF_CLOCK_EN) << SBTML_SICF_SHIFT));
+ dummy = R_SBREG(sii, &sb->sbtmstatelow);
+ OSL_DELAY(1);
+}
+
+void
+sb_core_tofixup(si_t *sih)
+{
+ si_info_t *sii;
+ sbconfig_t *sb;
+
+ sii = SI_INFO(sih);
+
+ if ((BUSTYPE(sii->pub.bustype) != PCI_BUS) || PCIE(sii) ||
+ (PCI(sii) && (sii->pub.buscorerev >= 5)))
+ return;
+
+ ASSERT(GOODREGS(sii->curmap));
+ sb = REGS2SB(sii->curmap);
+
+ if (BUSTYPE(sii->pub.bustype) == SI_BUS) {
+ SET_SBREG(sii, &sb->sbimconfiglow,
+ SBIMCL_RTO_MASK | SBIMCL_STO_MASK,
+ (0x5 << SBIMCL_RTO_SHIFT) | 0x3);
+ } else {
+ if (sb_coreid(sih) == PCI_CORE_ID) {
+ SET_SBREG(sii, &sb->sbimconfiglow,
+ SBIMCL_RTO_MASK | SBIMCL_STO_MASK,
+ (0x3 << SBIMCL_RTO_SHIFT) | 0x2);
+ } else {
+ SET_SBREG(sii, &sb->sbimconfiglow, (SBIMCL_RTO_MASK | SBIMCL_STO_MASK), 0);
+ }
+ }
+
+ sb_commit(sih);
+}
+
+/*
+ * Set the initiator timeout for the "master core".
+ * The master core is defined to be the core in control
+ * of the chip and so it issues accesses to non-memory
+ * locations (Because of dma *any* core can access memeory).
+ *
+ * The routine uses the bus to decide who is the master:
+ * SI_BUS => mips
+ * JTAG_BUS => chipc
+ * PCI_BUS => pci or pcie
+ * PCMCIA_BUS => pcmcia
+ * SDIO_BUS => pcmcia
+ *
+ * This routine exists so callers can disable initiator
+ * timeouts so accesses to very slow devices like otp
+ * won't cause an abort. The routine allows arbitrary
+ * settings of the service and request timeouts, though.
+ *
+ * Returns the timeout state before changing it or -1
+ * on error.
+ */
+
+#define TO_MASK (SBIMCL_RTO_MASK | SBIMCL_STO_MASK)
+
+uint32
+sb_set_initiator_to(si_t *sih, uint32 to, uint idx)
+{
+ si_info_t *sii;
+ uint origidx;
+ uint intr_val = 0;
+ uint32 tmp, ret = 0xffffffff;
+ sbconfig_t *sb;
+
+ sii = SI_INFO(sih);
+
+ if ((to & ~TO_MASK) != 0)
+ return ret;
+
+ /* Figure out the master core */
+ if (idx == BADIDX) {
+ switch (BUSTYPE(sii->pub.bustype)) {
+ case PCI_BUS:
+ idx = sii->pub.buscoreidx;
+ break;
+ case JTAG_BUS:
+ idx = SI_CC_IDX;
+ break;
+ case PCMCIA_BUS:
+ case SDIO_BUS:
+ idx = si_findcoreidx(sih, PCMCIA_CORE_ID, 0);
+ break;
+ case SI_BUS:
+ idx = si_findcoreidx(sih, MIPS33_CORE_ID, 0);
+ break;
+ default:
+ ASSERT(0);
+ }
+ if (idx == BADIDX)
+ return ret;
+ }
+
+ INTR_OFF(sii, intr_val);
+ origidx = si_coreidx(sih);
+
+ sb = REGS2SB(sb_setcoreidx(sih, idx));
+
+ tmp = R_SBREG(sii, &sb->sbimconfiglow);
+ ret = tmp & TO_MASK;
+ W_SBREG(sii, &sb->sbimconfiglow, (tmp & ~TO_MASK) | to);
+
+ sb_commit(sih);
+ sb_setcoreidx(sih, origidx);
+ INTR_RESTORE(sii, intr_val);
+ return ret;
+}
+
+uint32
+sb_base(uint32 admatch)
+{
+ uint32 base;
+ uint type;
+
+ type = admatch & SBAM_TYPE_MASK;
+ ASSERT(type < 3);
+
+ base = 0;
+
+ if (type == 0) {
+ base = admatch & SBAM_BASE0_MASK;
+ } else if (type == 1) {
+ ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */
+ base = admatch & SBAM_BASE1_MASK;
+ } else if (type == 2) {
+ ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */
+ base = admatch & SBAM_BASE2_MASK;
+ }
+
+ return (base);
+}
+
+uint32
+sb_size(uint32 admatch)
+{
+ uint32 size;
+ uint type;
+
+ type = admatch & SBAM_TYPE_MASK;
+ ASSERT(type < 3);
+
+ size = 0;
+
+ if (type == 0) {
+ size = 1 << (((admatch & SBAM_ADINT0_MASK) >> SBAM_ADINT0_SHIFT) + 1);
+ } else if (type == 1) {
+ ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */
+ size = 1 << (((admatch & SBAM_ADINT1_MASK) >> SBAM_ADINT1_SHIFT) + 1);
+ } else if (type == 2) {
+ ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */
+ size = 1 << (((admatch & SBAM_ADINT2_MASK) >> SBAM_ADINT2_SHIFT) + 1);
+ }
+
+ return (size);
+}
diff --git a/drivers/net/wireless/bcm4329/siutils.c b/drivers/net/wireless/bcm4329/siutils.c
new file mode 100644
index 00000000000..1814db0f9dd
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/siutils.c
@@ -0,0 +1,1527 @@
+/*
+ * Misc utility routines for accessing chip-specific features
+ * of the SiliconBackplane-based Broadcom chips.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: siutils.c,v 1.662.4.4.4.16.4.28 2010/06/23 21:37:54 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmdevs.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <pcicfg.h>
+#include <sbpcmcia.h>
+#include <sbsocram.h>
+#include <bcmsdh.h>
+#include <sdio.h>
+#include <sbsdio.h>
+#include <sbhnddma.h>
+#include <sbsdpcmdev.h>
+#include <bcmsdpcm.h>
+#include <hndpmu.h>
+
+#include "siutils_priv.h"
+
+/* local prototypes */
+static si_info_t *si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs,
+ uint bustype, void *sdh, char **vars, uint *varsz);
+static bool si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh);
+static bool si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin,
+ uint *origidx, void *regs);
+
+
+/* global variable to indicate reservation/release of gpio's */
+static uint32 si_gpioreservation = 0;
+static void *common_info_alloced = NULL;
+
+/* global flag to prevent shared resources from being initialized multiple times in si_attach() */
+
+/*
+ * Allocate a si handle.
+ * devid - pci device id (used to determine chip#)
+ * osh - opaque OS handle
+ * regs - virtual address of initial core registers
+ * bustype - pci/pcmcia/sb/sdio/etc
+ * vars - pointer to a pointer area for "environment" variables
+ * varsz - pointer to int to return the size of the vars
+ */
+si_t *
+si_attach(uint devid, osl_t *osh, void *regs,
+ uint bustype, void *sdh, char **vars, uint *varsz)
+{
+ si_info_t *sii;
+
+ /* alloc si_info_t */
+ if ((sii = MALLOC(osh, sizeof (si_info_t))) == NULL) {
+ SI_ERROR(("si_attach: malloc failed! malloced %d bytes\n", MALLOCED(osh)));
+ return (NULL);
+ }
+
+ if (si_doattach(sii, devid, osh, regs, bustype, sdh, vars, varsz) == NULL) {
+ if (NULL != sii->common_info)
+ MFREE(osh, sii->common_info, sizeof(si_common_info_t));
+ MFREE(osh, sii, sizeof(si_info_t));
+ return (NULL);
+ }
+ sii->vars = vars ? *vars : NULL;
+ sii->varsz = varsz ? *varsz : 0;
+
+ return (si_t *)sii;
+}
+
+/* global kernel resource */
+static si_info_t ksii;
+
+static uint32 wd_msticks; /* watchdog timer ticks normalized to ms */
+
+/* generic kernel variant of si_attach() */
+si_t *
+si_kattach(osl_t *osh)
+{
+ static bool ksii_attached = FALSE;
+
+ if (!ksii_attached) {
+ void *regs = REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE);
+
+ if (si_doattach(&ksii, BCM4710_DEVICE_ID, osh, regs,
+ SI_BUS, NULL,
+ osh != SI_OSH ? &ksii.vars : NULL,
+ osh != SI_OSH ? &ksii.varsz : NULL) == NULL) {
+ if (NULL != ksii.common_info)
+ MFREE(osh, ksii.common_info, sizeof(si_common_info_t));
+ SI_ERROR(("si_kattach: si_doattach failed\n"));
+ REG_UNMAP(regs);
+ return NULL;
+ }
+ REG_UNMAP(regs);
+
+ /* save ticks normalized to ms for si_watchdog_ms() */
+ if (PMUCTL_ENAB(&ksii.pub)) {
+ /* based on 32KHz ILP clock */
+ wd_msticks = 32;
+ } else {
+ wd_msticks = ALP_CLOCK / 1000;
+ }
+
+ ksii_attached = TRUE;
+ SI_MSG(("si_kattach done. ccrev = %d, wd_msticks = %d\n",
+ ksii.pub.ccrev, wd_msticks));
+ }
+
+ return &ksii.pub;
+}
+
+
+static bool
+si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh)
+{
+ /* need to set memseg flag for CF card first before any sb registers access */
+ if (BUSTYPE(bustype) == PCMCIA_BUS)
+ sii->memseg = TRUE;
+
+
+ if (BUSTYPE(bustype) == SDIO_BUS) {
+ int err;
+ uint8 clkset;
+
+ /* Try forcing SDIO core to do ALPAvail request only */
+ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+ if (!err) {
+ uint8 clkval;
+
+ /* If register supported, wait for ALPAvail and then force ALP */
+ clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, NULL);
+ if ((clkval & ~SBSDIO_AVBITS) == clkset) {
+ SPINWAIT(((clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+ SBSDIO_FUNC1_CHIPCLKCSR, NULL)), !SBSDIO_ALPAV(clkval)),
+ PMU_MAX_TRANSITION_DLY);
+ if (!SBSDIO_ALPAV(clkval)) {
+ SI_ERROR(("timeout on ALPAV wait, clkval 0x%02x\n",
+ clkval));
+ return FALSE;
+ }
+ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+ clkset, &err);
+ OSL_DELAY(65);
+ }
+ }
+
+ /* Also, disable the extra SDIO pull-ups */
+ bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
+ }
+
+
+ return TRUE;
+}
+
+static bool
+si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin,
+ uint *origidx, void *regs)
+{
+ bool pci, pcie;
+ uint i;
+ uint pciidx, pcieidx, pcirev, pcierev;
+
+ cc = si_setcoreidx(&sii->pub, SI_CC_IDX);
+ ASSERT((uintptr)cc);
+
+ /* get chipcommon rev */
+ sii->pub.ccrev = (int)si_corerev(&sii->pub);
+
+ /* get chipcommon chipstatus */
+ if (sii->pub.ccrev >= 11)
+ sii->pub.chipst = R_REG(sii->osh, &cc->chipstatus);
+
+ /* get chipcommon capabilites */
+ sii->pub.cccaps = R_REG(sii->osh, &cc->capabilities);
+
+ /* get pmu rev and caps */
+ if (sii->pub.cccaps & CC_CAP_PMU) {
+ sii->pub.pmucaps = R_REG(sii->osh, &cc->pmucapabilities);
+ sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK;
+ }
+
+ SI_MSG(("Chipc: rev %d, caps 0x%x, chipst 0x%x pmurev %d, pmucaps 0x%x\n",
+ sii->pub.ccrev, sii->pub.cccaps, sii->pub.chipst, sii->pub.pmurev,
+ sii->pub.pmucaps));
+
+ /* figure out bus/orignal core idx */
+ sii->pub.buscoretype = NODEV_CORE_ID;
+ sii->pub.buscorerev = NOREV;
+ sii->pub.buscoreidx = BADIDX;
+
+ pci = pcie = FALSE;
+ pcirev = pcierev = NOREV;
+ pciidx = pcieidx = BADIDX;
+
+ for (i = 0; i < sii->numcores; i++) {
+ uint cid, crev;
+
+ si_setcoreidx(&sii->pub, i);
+ cid = si_coreid(&sii->pub);
+ crev = si_corerev(&sii->pub);
+
+ /* Display cores found */
+ SI_MSG(("CORE[%d]: id 0x%x rev %d base 0x%x regs 0x%p\n",
+ i, cid, crev, sii->common_info->coresba[i], sii->common_info->regs[i]));
+
+ if (BUSTYPE(bustype) == PCI_BUS) {
+ if (cid == PCI_CORE_ID) {
+ pciidx = i;
+ pcirev = crev;
+ pci = TRUE;
+ } else if (cid == PCIE_CORE_ID) {
+ pcieidx = i;
+ pcierev = crev;
+ pcie = TRUE;
+ }
+ } else if ((BUSTYPE(bustype) == PCMCIA_BUS) &&
+ (cid == PCMCIA_CORE_ID)) {
+ sii->pub.buscorerev = crev;
+ sii->pub.buscoretype = cid;
+ sii->pub.buscoreidx = i;
+ }
+ else if (((BUSTYPE(bustype) == SDIO_BUS) ||
+ (BUSTYPE(bustype) == SPI_BUS)) &&
+ ((cid == PCMCIA_CORE_ID) ||
+ (cid == SDIOD_CORE_ID))) {
+ sii->pub.buscorerev = crev;
+ sii->pub.buscoretype = cid;
+ sii->pub.buscoreidx = i;
+ }
+
+ /* find the core idx before entering this func. */
+ if ((savewin && (savewin == sii->common_info->coresba[i])) ||
+ (regs == sii->common_info->regs[i]))
+ *origidx = i;
+ }
+
+
+ SI_MSG(("Buscore id/type/rev %d/0x%x/%d\n", sii->pub.buscoreidx, sii->pub.buscoretype,
+ sii->pub.buscorerev));
+
+ if (BUSTYPE(sii->pub.bustype) == SI_BUS && (CHIPID(sii->pub.chip) == BCM4712_CHIP_ID) &&
+ (sii->pub.chippkg != BCM4712LARGE_PKG_ID) && (sii->pub.chiprev <= 3))
+ OR_REG(sii->osh, &cc->slow_clk_ctl, SCC_SS_XTAL);
+
+
+ /* Make sure any on-chip ARM is off (in case strapping is wrong), or downloaded code was
+ * already running.
+ */
+ if ((BUSTYPE(bustype) == SDIO_BUS) || (BUSTYPE(bustype) == SPI_BUS)) {
+ if (si_setcore(&sii->pub, ARM7S_CORE_ID, 0) ||
+ si_setcore(&sii->pub, ARMCM3_CORE_ID, 0))
+ si_core_disable(&sii->pub, 0);
+ }
+
+ /* return to the original core */
+ si_setcoreidx(&sii->pub, *origidx);
+
+ return TRUE;
+}
+
+
+
+static si_info_t *
+si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs,
+ uint bustype, void *sdh, char **vars, uint *varsz)
+{
+ struct si_pub *sih = &sii->pub;
+ uint32 w, savewin;
+ chipcregs_t *cc;
+ char *pvars = NULL;
+ uint origidx;
+
+ ASSERT(GOODREGS(regs));
+
+ bzero((uchar*)sii, sizeof(si_info_t));
+
+
+ {
+ if (NULL == (common_info_alloced = (void *)MALLOC(osh, sizeof(si_common_info_t)))) {
+ SI_ERROR(("si_doattach: malloc failed! malloced %dbytes\n", MALLOCED(osh)));
+ return (NULL);
+ }
+ bzero((uchar*)(common_info_alloced), sizeof(si_common_info_t));
+ }
+ sii->common_info = (si_common_info_t *)common_info_alloced;
+ sii->common_info->attach_count++;
+
+ savewin = 0;
+
+ sih->buscoreidx = BADIDX;
+
+ sii->curmap = regs;
+ sii->sdh = sdh;
+ sii->osh = osh;
+
+
+ /* find Chipcommon address */
+ if (bustype == PCI_BUS) {
+ savewin = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32));
+ if (!GOODCOREADDR(savewin, SI_ENUM_BASE))
+ savewin = SI_ENUM_BASE;
+ OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, SI_ENUM_BASE);
+ cc = (chipcregs_t *)regs;
+ } else
+ if ((bustype == SDIO_BUS) || (bustype == SPI_BUS)) {
+ cc = (chipcregs_t *)sii->curmap;
+ } else {
+ cc = (chipcregs_t *)REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE);
+ }
+
+ sih->bustype = bustype;
+ if (bustype != BUSTYPE(bustype)) {
+ SI_ERROR(("si_doattach: bus type %d does not match configured bus type %d\n",
+ bustype, BUSTYPE(bustype)));
+ return NULL;
+ }
+
+ /* bus/core/clk setup for register access */
+ if (!si_buscore_prep(sii, bustype, devid, sdh)) {
+ SI_ERROR(("si_doattach: si_core_clk_prep failed %d\n", bustype));
+ return NULL;
+ }
+
+ /* ChipID recognition.
+ * We assume we can read chipid at offset 0 from the regs arg.
+ * If we add other chiptypes (or if we need to support old sdio hosts w/o chipcommon),
+ * some way of recognizing them needs to be added here.
+ */
+ w = R_REG(osh, &cc->chipid);
+ sih->socitype = (w & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
+ /* Might as wll fill in chip id rev & pkg */
+ sih->chip = w & CID_ID_MASK;
+ sih->chiprev = (w & CID_REV_MASK) >> CID_REV_SHIFT;
+ sih->chippkg = (w & CID_PKG_MASK) >> CID_PKG_SHIFT;
+ if ((CHIPID(sih->chip) == BCM4329_CHIP_ID) && (sih->chippkg != BCM4329_289PIN_PKG_ID))
+ sih->chippkg = BCM4329_182PIN_PKG_ID;
+ sih->issim = IS_SIM(sih->chippkg);
+
+ /* scan for cores */
+ if (CHIPTYPE(sii->pub.socitype) == SOCI_SB) {
+ SI_MSG(("Found chip type SB (0x%08x)\n", w));
+ sb_scan(&sii->pub, regs, devid);
+ } else if (CHIPTYPE(sii->pub.socitype) == SOCI_AI) {
+ SI_MSG(("Found chip type AI (0x%08x)\n", w));
+ /* pass chipc address instead of original core base */
+ ai_scan(&sii->pub, (void *)cc, devid);
+ } else {
+ SI_ERROR(("Found chip of unkown type (0x%08x)\n", w));
+ return NULL;
+ }
+ /* no cores found, bail out */
+ if (sii->numcores == 0) {
+ SI_ERROR(("si_doattach: could not find any cores\n"));
+ return NULL;
+ }
+ /* bus/core/clk setup */
+ origidx = SI_CC_IDX;
+ if (!si_buscore_setup(sii, cc, bustype, savewin, &origidx, regs)) {
+ SI_ERROR(("si_doattach: si_buscore_setup failed\n"));
+ return NULL;
+ }
+
+ pvars = NULL;
+
+
+
+ if (sii->pub.ccrev >= 20) {
+ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0);
+ W_REG(osh, &cc->gpiopullup, 0);
+ W_REG(osh, &cc->gpiopulldown, 0);
+ si_setcoreidx(sih, origidx);
+ }
+
+ /* Skip PMU initialization from the Dongle Host.
+ * Firmware will take care of it when it comes up.
+ */
+
+
+
+ return (sii);
+}
+
+/* may be called with core in reset */
+void
+si_detach(si_t *sih)
+{
+ si_info_t *sii;
+ uint idx;
+
+ sii = SI_INFO(sih);
+
+ if (sii == NULL)
+ return;
+
+ if (BUSTYPE(sih->bustype) == SI_BUS)
+ for (idx = 0; idx < SI_MAXCORES; idx++)
+ if (sii->common_info->regs[idx]) {
+ REG_UNMAP(sii->common_info->regs[idx]);
+ sii->common_info->regs[idx] = NULL;
+ }
+
+
+ if (1 == sii->common_info->attach_count--) {
+ MFREE(sii->osh, sii->common_info, sizeof(si_common_info_t));
+ common_info_alloced = NULL;
+ }
+
+#if !defined(BCMBUSTYPE) || (BCMBUSTYPE == SI_BUS)
+ if (sii != &ksii)
+#endif /* !BCMBUSTYPE || (BCMBUSTYPE == SI_BUS) */
+ MFREE(sii->osh, sii, sizeof(si_info_t));
+}
+
+void *
+si_osh(si_t *sih)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+ return sii->osh;
+}
+
+void
+si_setosh(si_t *sih, osl_t *osh)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+ if (sii->osh != NULL) {
+ SI_ERROR(("osh is already set....\n"));
+ ASSERT(!sii->osh);
+ }
+ sii->osh = osh;
+}
+
+/* register driver interrupt disabling and restoring callback functions */
+void
+si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn,
+ void *intrsenabled_fn, void *intr_arg)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+ sii->intr_arg = intr_arg;
+ sii->intrsoff_fn = (si_intrsoff_t)intrsoff_fn;
+ sii->intrsrestore_fn = (si_intrsrestore_t)intrsrestore_fn;
+ sii->intrsenabled_fn = (si_intrsenabled_t)intrsenabled_fn;
+ /* save current core id. when this function called, the current core
+ * must be the core which provides driver functions(il, et, wl, etc.)
+ */
+ sii->dev_coreid = sii->common_info->coreid[sii->curidx];
+}
+
+void
+si_deregister_intr_callback(si_t *sih)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+ sii->intrsoff_fn = NULL;
+}
+
+uint
+si_intflag(si_t *sih)
+{
+ si_info_t *sii = SI_INFO(sih);
+ if (CHIPTYPE(sih->socitype) == SOCI_SB) {
+ sbconfig_t *ccsbr = (sbconfig_t *)((uintptr)((ulong)
+ (sii->common_info->coresba[SI_CC_IDX]) + SBCONFIGOFF));
+ return R_REG(sii->osh, &ccsbr->sbflagst);
+ } else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return R_REG(sii->osh, ((uint32 *)(uintptr)
+ (sii->common_info->oob_router + OOB_STATUSA)));
+ else {
+ ASSERT(0);
+ return 0;
+ }
+}
+
+uint
+si_flag(si_t *sih)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_flag(sih);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_flag(sih);
+ else {
+ ASSERT(0);
+ return 0;
+ }
+}
+
+void
+si_setint(si_t *sih, int siflag)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ sb_setint(sih, siflag);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ ai_setint(sih, siflag);
+ else
+ ASSERT(0);
+}
+
+uint
+si_coreid(si_t *sih)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+ return sii->common_info->coreid[sii->curidx];
+}
+
+uint
+si_coreidx(si_t *sih)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+ return sii->curidx;
+}
+
+/* return the core-type instantiation # of the current core */
+uint
+si_coreunit(si_t *sih)
+{
+ si_info_t *sii;
+ uint idx;
+ uint coreid;
+ uint coreunit;
+ uint i;
+
+ sii = SI_INFO(sih);
+ coreunit = 0;
+
+ idx = sii->curidx;
+
+ ASSERT(GOODREGS(sii->curmap));
+ coreid = si_coreid(sih);
+
+ /* count the cores of our type */
+ for (i = 0; i < idx; i++)
+ if (sii->common_info->coreid[i] == coreid)
+ coreunit++;
+
+ return (coreunit);
+}
+
+uint
+si_corevendor(si_t *sih)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_corevendor(sih);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_corevendor(sih);
+ else {
+ ASSERT(0);
+ return 0;
+ }
+}
+
+bool
+si_backplane64(si_t *sih)
+{
+ return ((sih->cccaps & CC_CAP_BKPLN64) != 0);
+}
+
+uint
+si_corerev(si_t *sih)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_corerev(sih);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_corerev(sih);
+ else {
+ ASSERT(0);
+ return 0;
+ }
+}
+
+/* return index of coreid or BADIDX if not found */
+uint
+si_findcoreidx(si_t *sih, uint coreid, uint coreunit)
+{
+ si_info_t *sii;
+ uint found;
+ uint i;
+
+ sii = SI_INFO(sih);
+
+ found = 0;
+
+ for (i = 0; i < sii->numcores; i++)
+ if (sii->common_info->coreid[i] == coreid) {
+ if (found == coreunit)
+ return (i);
+ found++;
+ }
+
+ return (BADIDX);
+}
+
+/* return list of found cores */
+uint
+si_corelist(si_t *sih, uint coreid[])
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ bcopy((uchar*)sii->common_info->coreid, (uchar*)coreid, (sii->numcores * sizeof(uint)));
+ return (sii->numcores);
+}
+
+/* return current register mapping */
+void *
+si_coreregs(si_t *sih)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+ ASSERT(GOODREGS(sii->curmap));
+
+ return (sii->curmap);
+}
+
+/*
+ * This function changes logical "focus" to the indicated core;
+ * must be called with interrupts off.
+ * Moreover, callers should keep interrupts off during switching out of and back to d11 core
+ */
+void *
+si_setcore(si_t *sih, uint coreid, uint coreunit)
+{
+ uint idx;
+
+ idx = si_findcoreidx(sih, coreid, coreunit);
+ if (!GOODIDX(idx))
+ return (NULL);
+
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_setcoreidx(sih, idx);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_setcoreidx(sih, idx);
+ else {
+ ASSERT(0);
+ return NULL;
+ }
+}
+
+void *
+si_setcoreidx(si_t *sih, uint coreidx)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_setcoreidx(sih, coreidx);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_setcoreidx(sih, coreidx);
+ else {
+ ASSERT(0);
+ return NULL;
+ }
+}
+
+/* Turn off interrupt as required by sb_setcore, before switch core */
+void *si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val)
+{
+ void *cc;
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ INTR_OFF(sii, *intr_val);
+ *origidx = sii->curidx;
+ cc = si_setcore(sih, coreid, 0);
+ ASSERT(cc != NULL);
+
+ return cc;
+}
+
+/* restore coreidx and restore interrupt */
+void si_restore_core(si_t *sih, uint coreid, uint intr_val)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ si_setcoreidx(sih, coreid);
+ INTR_RESTORE(sii, intr_val);
+}
+
+int
+si_numaddrspaces(si_t *sih)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_numaddrspaces(sih);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_numaddrspaces(sih);
+ else {
+ ASSERT(0);
+ return 0;
+ }
+}
+
+uint32
+si_addrspace(si_t *sih, uint asidx)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_addrspace(sih, asidx);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_addrspace(sih, asidx);
+ else {
+ ASSERT(0);
+ return 0;
+ }
+}
+
+uint32
+si_addrspacesize(si_t *sih, uint asidx)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_addrspacesize(sih, asidx);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_addrspacesize(sih, asidx);
+ else {
+ ASSERT(0);
+ return 0;
+ }
+}
+
+uint32
+si_core_cflags(si_t *sih, uint32 mask, uint32 val)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_core_cflags(sih, mask, val);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_core_cflags(sih, mask, val);
+ else {
+ ASSERT(0);
+ return 0;
+ }
+}
+
+void
+si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ sb_core_cflags_wo(sih, mask, val);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ ai_core_cflags_wo(sih, mask, val);
+ else
+ ASSERT(0);
+}
+
+uint32
+si_core_sflags(si_t *sih, uint32 mask, uint32 val)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_core_sflags(sih, mask, val);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_core_sflags(sih, mask, val);
+ else {
+ ASSERT(0);
+ return 0;
+ }
+}
+
+bool
+si_iscoreup(si_t *sih)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_iscoreup(sih);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_iscoreup(sih);
+ else {
+ ASSERT(0);
+ return FALSE;
+ }
+}
+
+void
+si_write_wrapperreg(si_t *sih, uint32 offset, uint32 val)
+{
+ /* only for 4319, no requirement for SOCI_SB */
+ if (CHIPTYPE(sih->socitype) == SOCI_AI) {
+ ai_write_wrap_reg(sih, offset, val);
+ }
+ else
+ return;
+
+ return;
+}
+
+uint
+si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ return sb_corereg(sih, coreidx, regoff, mask, val);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ return ai_corereg(sih, coreidx, regoff, mask, val);
+ else {
+ ASSERT(0);
+ return 0;
+ }
+}
+
+void
+si_core_disable(si_t *sih, uint32 bits)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ sb_core_disable(sih, bits);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ ai_core_disable(sih, bits);
+}
+
+void
+si_core_reset(si_t *sih, uint32 bits, uint32 resetbits)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ sb_core_reset(sih, bits, resetbits);
+ else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+ ai_core_reset(sih, bits, resetbits);
+}
+
+void
+si_core_tofixup(si_t *sih)
+{
+ if (CHIPTYPE(sih->socitype) == SOCI_SB)
+ sb_core_tofixup(sih);
+}
+
+/* Run bist on current core. Caller needs to take care of core-specific bist hazards */
+int
+si_corebist(si_t *sih)
+{
+ uint32 cflags;
+ int result = 0;
+
+ /* Read core control flags */
+ cflags = si_core_cflags(sih, 0, 0);
+
+ /* Set bist & fgc */
+ si_core_cflags(sih, 0, (SICF_BIST_EN | SICF_FGC));
+
+ /* Wait for bist done */
+ SPINWAIT(((si_core_sflags(sih, 0, 0) & SISF_BIST_DONE) == 0), 100000);
+
+ if (si_core_sflags(sih, 0, 0) & SISF_BIST_ERROR)
+ result = BCME_ERROR;
+
+ /* Reset core control flags */
+ si_core_cflags(sih, 0xffff, cflags);
+
+ return result;
+}
+
+static uint32
+factor6(uint32 x)
+{
+ switch (x) {
+ case CC_F6_2: return 2;
+ case CC_F6_3: return 3;
+ case CC_F6_4: return 4;
+ case CC_F6_5: return 5;
+ case CC_F6_6: return 6;
+ case CC_F6_7: return 7;
+ default: return 0;
+ }
+}
+
+/* calculate the speed the SI would run at given a set of clockcontrol values */
+uint32
+si_clock_rate(uint32 pll_type, uint32 n, uint32 m)
+{
+ uint32 n1, n2, clock, m1, m2, m3, mc;
+
+ n1 = n & CN_N1_MASK;
+ n2 = (n & CN_N2_MASK) >> CN_N2_SHIFT;
+
+ if (pll_type == PLL_TYPE6) {
+ if (m & CC_T6_MMASK)
+ return CC_T6_M1;
+ else
+ return CC_T6_M0;
+ } else if ((pll_type == PLL_TYPE1) ||
+ (pll_type == PLL_TYPE3) ||
+ (pll_type == PLL_TYPE4) ||
+ (pll_type == PLL_TYPE7)) {
+ n1 = factor6(n1);
+ n2 += CC_F5_BIAS;
+ } else if (pll_type == PLL_TYPE2) {
+ n1 += CC_T2_BIAS;
+ n2 += CC_T2_BIAS;
+ ASSERT((n1 >= 2) && (n1 <= 7));
+ ASSERT((n2 >= 5) && (n2 <= 23));
+ } else if (pll_type == PLL_TYPE5) {
+ return (100000000);
+ } else
+ ASSERT(0);
+ /* PLL types 3 and 7 use BASE2 (25Mhz) */
+ if ((pll_type == PLL_TYPE3) ||
+ (pll_type == PLL_TYPE7)) {
+ clock = CC_CLOCK_BASE2 * n1 * n2;
+ } else
+ clock = CC_CLOCK_BASE1 * n1 * n2;
+
+ if (clock == 0)
+ return 0;
+
+ m1 = m & CC_M1_MASK;
+ m2 = (m & CC_M2_MASK) >> CC_M2_SHIFT;
+ m3 = (m & CC_M3_MASK) >> CC_M3_SHIFT;
+ mc = (m & CC_MC_MASK) >> CC_MC_SHIFT;
+
+ if ((pll_type == PLL_TYPE1) ||
+ (pll_type == PLL_TYPE3) ||
+ (pll_type == PLL_TYPE4) ||
+ (pll_type == PLL_TYPE7)) {
+ m1 = factor6(m1);
+ if ((pll_type == PLL_TYPE1) || (pll_type == PLL_TYPE3))
+ m2 += CC_F5_BIAS;
+ else
+ m2 = factor6(m2);
+ m3 = factor6(m3);
+
+ switch (mc) {
+ case CC_MC_BYPASS: return (clock);
+ case CC_MC_M1: return (clock / m1);
+ case CC_MC_M1M2: return (clock / (m1 * m2));
+ case CC_MC_M1M2M3: return (clock / (m1 * m2 * m3));
+ case CC_MC_M1M3: return (clock / (m1 * m3));
+ default: return (0);
+ }
+ } else {
+ ASSERT(pll_type == PLL_TYPE2);
+
+ m1 += CC_T2_BIAS;
+ m2 += CC_T2M2_BIAS;
+ m3 += CC_T2_BIAS;
+ ASSERT((m1 >= 2) && (m1 <= 7));
+ ASSERT((m2 >= 3) && (m2 <= 10));
+ ASSERT((m3 >= 2) && (m3 <= 7));
+
+ if ((mc & CC_T2MC_M1BYP) == 0)
+ clock /= m1;
+ if ((mc & CC_T2MC_M2BYP) == 0)
+ clock /= m2;
+ if ((mc & CC_T2MC_M3BYP) == 0)
+ clock /= m3;
+
+ return (clock);
+ }
+}
+
+
+/* set chip watchdog reset timer to fire in 'ticks' */
+void
+si_watchdog(si_t *sih, uint ticks)
+{
+ if (PMUCTL_ENAB(sih)) {
+
+ if ((sih->chip == BCM4319_CHIP_ID) && (sih->chiprev == 0) && (ticks != 0)) {
+ si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, clk_ctl_st), ~0, 0x2);
+ si_setcore(sih, USB20D_CORE_ID, 0);
+ si_core_disable(sih, 1);
+ si_setcore(sih, CC_CORE_ID, 0);
+ }
+
+ if (ticks == 1)
+ ticks = 2;
+ si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, pmuwatchdog), ~0, ticks);
+ } else {
+ /* instant NMI */
+ si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, watchdog), ~0, ticks);
+ }
+}
+
+#if !defined(BCMBUSTYPE) || (BCMBUSTYPE == SI_BUS)
+/* trigger watchdog reset after ms milliseconds */
+void
+si_watchdog_ms(si_t *sih, uint32 ms)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ si_watchdog(sih, wd_msticks * ms);
+}
+#endif
+
+
+
+/* initialize the sdio core */
+void
+si_sdio_init(si_t *sih)
+{
+ si_info_t *sii = SI_INFO(sih);
+
+ if (((sih->buscoretype == PCMCIA_CORE_ID) && (sih->buscorerev >= 8)) ||
+ (sih->buscoretype == SDIOD_CORE_ID)) {
+ uint idx;
+ sdpcmd_regs_t *sdpregs;
+
+ /* get the current core index */
+ idx = sii->curidx;
+ ASSERT(idx == si_findcoreidx(sih, D11_CORE_ID, 0));
+
+ /* switch to sdio core */
+ if (!(sdpregs = (sdpcmd_regs_t *)si_setcore(sih, PCMCIA_CORE_ID, 0)))
+ sdpregs = (sdpcmd_regs_t *)si_setcore(sih, SDIOD_CORE_ID, 0);
+ ASSERT(sdpregs);
+
+ SI_MSG(("si_sdio_init: For PCMCIA/SDIO Corerev %d, enable ints from core %d "
+ "through SD core %d (%p)\n",
+ sih->buscorerev, idx, sii->curidx, sdpregs));
+
+ /* enable backplane error and core interrupts */
+ W_REG(sii->osh, &sdpregs->hostintmask, I_SBINT);
+ W_REG(sii->osh, &sdpregs->sbintmask, (I_SB_SERR | I_SB_RESPERR | (1 << idx)));
+
+ /* switch back to previous core */
+ si_setcoreidx(sih, idx);
+ }
+
+ /* enable interrupts */
+ bcmsdh_intr_enable(sii->sdh);
+
+}
+
+
+/* change logical "focus" to the gpio core for optimized access */
+void *
+si_gpiosetcore(si_t *sih)
+{
+ return (si_setcoreidx(sih, SI_CC_IDX));
+}
+
+/* mask&set gpiocontrol bits */
+uint32
+si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+ uint regoff;
+
+ regoff = 0;
+
+ /* gpios could be shared on router platforms
+ * ignore reservation if it's high priority (e.g., test apps)
+ */
+ if ((priority != GPIO_HI_PRIORITY) &&
+ (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+ mask = priority ? (si_gpioreservation & mask) :
+ ((si_gpioreservation | mask) & ~(si_gpioreservation));
+ val &= mask;
+ }
+
+ regoff = OFFSETOF(chipcregs_t, gpiocontrol);
+ return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* mask&set gpio output enable bits */
+uint32
+si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+ uint regoff;
+
+ regoff = 0;
+
+ /* gpios could be shared on router platforms
+ * ignore reservation if it's high priority (e.g., test apps)
+ */
+ if ((priority != GPIO_HI_PRIORITY) &&
+ (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+ mask = priority ? (si_gpioreservation & mask) :
+ ((si_gpioreservation | mask) & ~(si_gpioreservation));
+ val &= mask;
+ }
+
+ regoff = OFFSETOF(chipcregs_t, gpioouten);
+ return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* mask&set gpio output bits */
+uint32
+si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+ uint regoff;
+
+ regoff = 0;
+
+ /* gpios could be shared on router platforms
+ * ignore reservation if it's high priority (e.g., test apps)
+ */
+ if ((priority != GPIO_HI_PRIORITY) &&
+ (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+ mask = priority ? (si_gpioreservation & mask) :
+ ((si_gpioreservation | mask) & ~(si_gpioreservation));
+ val &= mask;
+ }
+
+ regoff = OFFSETOF(chipcregs_t, gpioout);
+ return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* reserve one gpio */
+uint32
+si_gpioreserve(si_t *sih, uint32 gpio_bitmask, uint8 priority)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ /* only cores on SI_BUS share GPIO's and only applcation users need to
+ * reserve/release GPIO
+ */
+ if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) {
+ ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority));
+ return -1;
+ }
+ /* make sure only one bit is set */
+ if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) {
+ ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1)));
+ return -1;
+ }
+
+ /* already reserved */
+ if (si_gpioreservation & gpio_bitmask)
+ return -1;
+ /* set reservation */
+ si_gpioreservation |= gpio_bitmask;
+
+ return si_gpioreservation;
+}
+
+/* release one gpio */
+/*
+ * releasing the gpio doesn't change the current value on the GPIO last write value
+ * persists till some one overwrites it
+ */
+
+uint32
+si_gpiorelease(si_t *sih, uint32 gpio_bitmask, uint8 priority)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ /* only cores on SI_BUS share GPIO's and only applcation users need to
+ * reserve/release GPIO
+ */
+ if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) {
+ ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority));
+ return -1;
+ }
+ /* make sure only one bit is set */
+ if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) {
+ ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1)));
+ return -1;
+ }
+
+ /* already released */
+ if (!(si_gpioreservation & gpio_bitmask))
+ return -1;
+
+ /* clear reservation */
+ si_gpioreservation &= ~gpio_bitmask;
+
+ return si_gpioreservation;
+}
+
+/* return the current gpioin register value */
+uint32
+si_gpioin(si_t *sih)
+{
+ si_info_t *sii;
+ uint regoff;
+
+ sii = SI_INFO(sih);
+ regoff = 0;
+
+ regoff = OFFSETOF(chipcregs_t, gpioin);
+ return (si_corereg(sih, SI_CC_IDX, regoff, 0, 0));
+}
+
+/* mask&set gpio interrupt polarity bits */
+uint32
+si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+ si_info_t *sii;
+ uint regoff;
+
+ sii = SI_INFO(sih);
+ regoff = 0;
+
+ /* gpios could be shared on router platforms */
+ if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+ mask = priority ? (si_gpioreservation & mask) :
+ ((si_gpioreservation | mask) & ~(si_gpioreservation));
+ val &= mask;
+ }
+
+ regoff = OFFSETOF(chipcregs_t, gpiointpolarity);
+ return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* mask&set gpio interrupt mask bits */
+uint32
+si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+ si_info_t *sii;
+ uint regoff;
+
+ sii = SI_INFO(sih);
+ regoff = 0;
+
+ /* gpios could be shared on router platforms */
+ if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+ mask = priority ? (si_gpioreservation & mask) :
+ ((si_gpioreservation | mask) & ~(si_gpioreservation));
+ val &= mask;
+ }
+
+ regoff = OFFSETOF(chipcregs_t, gpiointmask);
+ return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* assign the gpio to an led */
+uint32
+si_gpioled(si_t *sih, uint32 mask, uint32 val)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+ if (sih->ccrev < 16)
+ return -1;
+
+ /* gpio led powersave reg */
+ return (si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, gpiotimeroutmask), mask, val));
+}
+
+/* mask&set gpio timer val */
+uint32
+si_gpiotimerval(si_t *sih, uint32 mask, uint32 gpiotimerval)
+{
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ if (sih->ccrev < 16)
+ return -1;
+
+ return (si_corereg(sih, SI_CC_IDX,
+ OFFSETOF(chipcregs_t, gpiotimerval), mask, gpiotimerval));
+}
+
+uint32
+si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val)
+{
+ si_info_t *sii;
+ uint offs;
+
+ sii = SI_INFO(sih);
+ if (sih->ccrev < 20)
+ return -1;
+
+ offs = (updown ? OFFSETOF(chipcregs_t, gpiopulldown) : OFFSETOF(chipcregs_t, gpiopullup));
+ return (si_corereg(sih, SI_CC_IDX, offs, mask, val));
+}
+
+uint32
+si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val)
+{
+ si_info_t *sii;
+ uint offs;
+
+ sii = SI_INFO(sih);
+ if (sih->ccrev < 11)
+ return -1;
+
+ if (regtype == GPIO_REGEVT)
+ offs = OFFSETOF(chipcregs_t, gpioevent);
+ else if (regtype == GPIO_REGEVT_INTMSK)
+ offs = OFFSETOF(chipcregs_t, gpioeventintmask);
+ else if (regtype == GPIO_REGEVT_INTPOL)
+ offs = OFFSETOF(chipcregs_t, gpioeventintpolarity);
+ else
+ return -1;
+
+ return (si_corereg(sih, SI_CC_IDX, offs, mask, val));
+}
+
+void *
+si_gpio_handler_register(si_t *sih, uint32 event,
+ bool level, gpio_handler_t cb, void *arg)
+{
+ si_info_t *sii;
+ gpioh_item_t *gi;
+
+ ASSERT(event);
+ ASSERT(cb != NULL);
+
+ sii = SI_INFO(sih);
+ if (sih->ccrev < 11)
+ return NULL;
+
+ if ((gi = MALLOC(sii->osh, sizeof(gpioh_item_t))) == NULL)
+ return NULL;
+
+ bzero(gi, sizeof(gpioh_item_t));
+ gi->event = event;
+ gi->handler = cb;
+ gi->arg = arg;
+ gi->level = level;
+
+ gi->next = sii->gpioh_head;
+ sii->gpioh_head = gi;
+
+ return (void *)(gi);
+}
+
+void
+si_gpio_handler_unregister(si_t *sih, void *gpioh)
+{
+ si_info_t *sii;
+ gpioh_item_t *p, *n;
+
+ sii = SI_INFO(sih);
+ if (sih->ccrev < 11)
+ return;
+
+ ASSERT(sii->gpioh_head != NULL);
+ if ((void*)sii->gpioh_head == gpioh) {
+ sii->gpioh_head = sii->gpioh_head->next;
+ MFREE(sii->osh, gpioh, sizeof(gpioh_item_t));
+ return;
+ } else {
+ p = sii->gpioh_head;
+ n = p->next;
+ while (n) {
+ if ((void*)n == gpioh) {
+ p->next = n->next;
+ MFREE(sii->osh, gpioh, sizeof(gpioh_item_t));
+ return;
+ }
+ p = n;
+ n = n->next;
+ }
+ }
+
+ ASSERT(0); /* Not found in list */
+}
+
+void
+si_gpio_handler_process(si_t *sih)
+{
+ si_info_t *sii;
+ gpioh_item_t *h;
+ uint32 status;
+ uint32 level = si_gpioin(sih);
+ uint32 edge = si_gpioevent(sih, GPIO_REGEVT, 0, 0);
+
+ sii = SI_INFO(sih);
+ for (h = sii->gpioh_head; h != NULL; h = h->next) {
+ if (h->handler) {
+ status = (h->level ? level : edge);
+
+ if (status & h->event)
+ h->handler(status, h->arg);
+ }
+ }
+
+ si_gpioevent(sih, GPIO_REGEVT, edge, edge); /* clear edge-trigger status */
+}
+
+uint32
+si_gpio_int_enable(si_t *sih, bool enable)
+{
+ si_info_t *sii;
+ uint offs;
+
+ sii = SI_INFO(sih);
+ if (sih->ccrev < 11)
+ return -1;
+
+ offs = OFFSETOF(chipcregs_t, intmask);
+ return (si_corereg(sih, SI_CC_IDX, offs, CI_GPIO, (enable ? CI_GPIO : 0)));
+}
+
+
+/* Return the RAM size of the SOCRAM core */
+uint32
+si_socram_size(si_t *sih)
+{
+ si_info_t *sii;
+ uint origidx;
+ uint intr_val = 0;
+
+ sbsocramregs_t *regs;
+ bool wasup;
+ uint corerev;
+ uint32 coreinfo;
+ uint memsize = 0;
+
+ sii = SI_INFO(sih);
+
+ /* Block ints and save current core */
+ INTR_OFF(sii, intr_val);
+ origidx = si_coreidx(sih);
+
+ /* Switch to SOCRAM core */
+ if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0)))
+ goto done;
+
+ /* Get info for determining size */
+ if (!(wasup = si_iscoreup(sih)))
+ si_core_reset(sih, 0, 0);
+ corerev = si_corerev(sih);
+ coreinfo = R_REG(sii->osh, &regs->coreinfo);
+
+ /* Calculate size from coreinfo based on rev */
+ if (corerev == 0)
+ memsize = 1 << (16 + (coreinfo & SRCI_MS0_MASK));
+ else if (corerev < 3) {
+ memsize = 1 << (SR_BSZ_BASE + (coreinfo & SRCI_SRBSZ_MASK));
+ memsize *= (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+ } else {
+ uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+ uint bsz = (coreinfo & SRCI_SRBSZ_MASK);
+ uint lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT;
+ if (lss != 0)
+ nb --;
+ memsize = nb * (1 << (bsz + SR_BSZ_BASE));
+ if (lss != 0)
+ memsize += (1 << ((lss - 1) + SR_BSZ_BASE));
+ }
+
+ /* Return to previous state and core */
+ if (!wasup)
+ si_core_disable(sih, 0);
+ si_setcoreidx(sih, origidx);
+
+done:
+ INTR_RESTORE(sii, intr_val);
+
+ return memsize;
+}
+
+
+void
+si_btcgpiowar(si_t *sih)
+{
+ si_info_t *sii;
+ uint origidx;
+ uint intr_val = 0;
+ chipcregs_t *cc;
+
+ sii = SI_INFO(sih);
+
+ /* Make sure that there is ChipCommon core present &&
+ * UART_TX is strapped to 1
+ */
+ if (!(sih->cccaps & CC_CAP_UARTGPIO))
+ return;
+
+ /* si_corereg cannot be used as we have to guarantee 8-bit read/writes */
+ INTR_OFF(sii, intr_val);
+
+ origidx = si_coreidx(sih);
+
+ cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0);
+ ASSERT(cc != NULL);
+
+ W_REG(sii->osh, &cc->uart0mcr, R_REG(sii->osh, &cc->uart0mcr) | 0x04);
+
+ /* restore the original index */
+ si_setcoreidx(sih, origidx);
+
+ INTR_RESTORE(sii, intr_val);
+}
+
+/* check if the device is removed */
+bool
+si_deviceremoved(si_t *sih)
+{
+ uint32 w;
+ si_info_t *sii;
+
+ sii = SI_INFO(sih);
+
+ switch (BUSTYPE(sih->bustype)) {
+ case PCI_BUS:
+ ASSERT(sii->osh != NULL);
+ w = OSL_PCI_READ_CONFIG(sii->osh, PCI_CFG_VID, sizeof(uint32));
+ if ((w & 0xFFFF) != VENDOR_BROADCOM)
+ return TRUE;
+ else
+ return FALSE;
+ default:
+ return FALSE;
+ }
+ return FALSE;
+}
diff --git a/drivers/net/wireless/bcm4329/siutils_priv.h b/drivers/net/wireless/bcm4329/siutils_priv.h
new file mode 100644
index 00000000000..e8ad7e50958
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/siutils_priv.h
@@ -0,0 +1,213 @@
+/*
+ * Include file private to the SOC Interconnect support files.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: siutils_priv.h,v 1.3.10.5.4.2 2009/09/22 13:28:16 Exp $
+ */
+
+#ifndef _siutils_priv_h_
+#define _siutils_priv_h_
+
+/* debug/trace */
+#define SI_ERROR(args)
+
+#define SI_MSG(args)
+
+#define IS_SIM(chippkg) ((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID))
+
+typedef uint32 (*si_intrsoff_t)(void *intr_arg);
+typedef void (*si_intrsrestore_t)(void *intr_arg, uint32 arg);
+typedef bool (*si_intrsenabled_t)(void *intr_arg);
+
+typedef struct gpioh_item {
+ void *arg;
+ bool level;
+ gpio_handler_t handler;
+ uint32 event;
+ struct gpioh_item *next;
+} gpioh_item_t;
+
+/* misc si info needed by some of the routines */
+typedef struct si_common_info {
+ void *regs[SI_MAXCORES]; /* other regs va */
+ void *regs2[SI_MAXCORES]; /* va of each core second register set (usbh20) */
+ uint coreid[SI_MAXCORES]; /* id of each core */
+ uint32 cia[SI_MAXCORES]; /* erom cia entry for each core */
+ uint32 cib[SI_MAXCORES]; /* erom cia entry for each core */
+ uint32 coresba_size[SI_MAXCORES]; /* backplane address space size */
+ uint32 coresba2_size[SI_MAXCORES]; /* second address space size */
+ uint32 coresba[SI_MAXCORES]; /* backplane address of each core */
+ uint32 coresba2[SI_MAXCORES]; /* address of each core second register set (usbh20) */
+ void *wrappers[SI_MAXCORES]; /* other cores wrapper va */
+ uint32 wrapba[SI_MAXCORES]; /* address of controlling wrapper */
+ uint32 oob_router; /* oob router registers for axi */
+ uint8 attach_count;
+} si_common_info_t;
+
+typedef struct si_info {
+ struct si_pub pub; /* back plane public state (must be first field) */
+
+ void *osh; /* osl os handle */
+ void *sdh; /* bcmsdh handle */
+ void *pch; /* PCI/E core handle */
+ uint dev_coreid; /* the core provides driver functions */
+ void *intr_arg; /* interrupt callback function arg */
+ si_intrsoff_t intrsoff_fn; /* turns chip interrupts off */
+ si_intrsrestore_t intrsrestore_fn; /* restore chip interrupts */
+ si_intrsenabled_t intrsenabled_fn; /* check if interrupts are enabled */
+
+
+ gpioh_item_t *gpioh_head; /* GPIO event handlers list */
+
+ bool memseg; /* flag to toggle MEM_SEG register */
+
+ char *vars;
+ uint varsz;
+
+ void *curmap; /* current regs va */
+
+ uint curidx; /* current core index */
+ uint numcores; /* # discovered cores */
+ void *curwrap; /* current wrapper va */
+ si_common_info_t *common_info; /* Common information for all the cores in a chip */
+} si_info_t;
+
+#define SI_INFO(sih) (si_info_t *)(uintptr)sih
+
+#define GOODCOREADDR(x, b) (((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \
+ ISALIGNED((x), SI_CORE_SIZE))
+#define GOODREGS(regs) ((regs) != NULL && ISALIGNED((uintptr)(regs), SI_CORE_SIZE))
+#define BADCOREADDR 0
+#define GOODIDX(idx) (((uint)idx) < SI_MAXCORES)
+#define BADIDX (SI_MAXCORES + 1)
+#define NOREV -1 /* Invalid rev */
+
+#define PCI(si) ((BUSTYPE((si)->pub.bustype) == PCI_BUS) && \
+ ((si)->pub.buscoretype == PCI_CORE_ID))
+#define PCIE(si) ((BUSTYPE((si)->pub.bustype) == PCI_BUS) && \
+ ((si)->pub.buscoretype == PCIE_CORE_ID))
+#define PCMCIA(si) ((BUSTYPE((si)->pub.bustype) == PCMCIA_BUS) && ((si)->memseg == TRUE))
+
+/* Newer chips can access PCI/PCIE and CC core without requiring to change
+ * PCI BAR0 WIN
+ */
+#define SI_FAST(si) (((si)->pub.buscoretype == PCIE_CORE_ID) || \
+ (((si)->pub.buscoretype == PCI_CORE_ID) && (si)->pub.buscorerev >= 13))
+
+#define PCIEREGS(si) (((char *)((si)->curmap) + PCI_16KB0_PCIREGS_OFFSET))
+#define CCREGS_FAST(si) (((char *)((si)->curmap) + PCI_16KB0_CCREGS_OFFSET))
+
+/*
+ * Macros to disable/restore function core(D11, ENET, ILINE20, etc) interrupts before/
+ * after core switching to avoid invalid register accesss inside ISR.
+ */
+#define INTR_OFF(si, intr_val) \
+ if ((si)->intrsoff_fn && (si)->common_info->coreid[(si)->curidx] == (si)->dev_coreid) { \
+ intr_val = (*(si)->intrsoff_fn)((si)->intr_arg); }
+#define INTR_RESTORE(si, intr_val) \
+ if ((si)->intrsrestore_fn && (si)->common_info->coreid[(si)->curidx] == (si)->dev_coreid) {\
+ (*(si)->intrsrestore_fn)((si)->intr_arg, intr_val); }
+
+/* dynamic clock control defines */
+#define LPOMINFREQ 25000 /* low power oscillator min */
+#define LPOMAXFREQ 43000 /* low power oscillator max */
+#define XTALMINFREQ 19800000 /* 20 MHz - 1% */
+#define XTALMAXFREQ 20200000 /* 20 MHz + 1% */
+#define PCIMINFREQ 25000000 /* 25 MHz */
+#define PCIMAXFREQ 34000000 /* 33 MHz + fudge */
+
+#define ILP_DIV_5MHZ 0 /* ILP = 5 MHz */
+#define ILP_DIV_1MHZ 4 /* ILP = 1 MHz */
+
+#define PCI_FORCEHT(si) \
+ (((PCIE(si)) && (si->pub.chip == BCM4311_CHIP_ID) && ((si->pub.chiprev <= 1))) || \
+ ((PCI(si) || PCIE(si)) && (si->pub.chip == BCM4321_CHIP_ID)))
+
+/* GPIO Based LED powersave defines */
+#define DEFAULT_GPIO_ONTIME 10 /* Default: 10% on */
+#define DEFAULT_GPIO_OFFTIME 90 /* Default: 10% on */
+
+#ifndef DEFAULT_GPIOTIMERVAL
+#define DEFAULT_GPIOTIMERVAL ((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME)
+#endif
+
+/* Silicon Backplane externs */
+extern void sb_scan(si_t *sih, void *regs, uint devid);
+extern uint sb_coreid(si_t *sih);
+extern uint sb_flag(si_t *sih);
+extern void sb_setint(si_t *sih, int siflag);
+extern uint sb_corevendor(si_t *sih);
+extern uint sb_corerev(si_t *sih);
+extern uint sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val);
+extern bool sb_iscoreup(si_t *sih);
+extern void *sb_setcoreidx(si_t *sih, uint coreidx);
+extern uint32 sb_core_cflags(si_t *sih, uint32 mask, uint32 val);
+extern void sb_core_cflags_wo(si_t *sih, uint32 mask, uint32 val);
+extern uint32 sb_core_sflags(si_t *sih, uint32 mask, uint32 val);
+extern void sb_commit(si_t *sih);
+extern uint32 sb_base(uint32 admatch);
+extern uint32 sb_size(uint32 admatch);
+extern void sb_core_reset(si_t *sih, uint32 bits, uint32 resetbits);
+extern void sb_core_tofixup(si_t *sih);
+extern void sb_core_disable(si_t *sih, uint32 bits);
+extern uint32 sb_addrspace(si_t *sih, uint asidx);
+extern uint32 sb_addrspacesize(si_t *sih, uint asidx);
+extern int sb_numaddrspaces(si_t *sih);
+
+extern uint32 sb_set_initiator_to(si_t *sih, uint32 to, uint idx);
+
+
+
+/* Wake-on-wireless-LAN (WOWL) */
+extern bool sb_pci_pmecap(si_t *sih);
+struct osl_info;
+extern bool sb_pci_fastpmecap(struct osl_info *osh);
+extern bool sb_pci_pmeclr(si_t *sih);
+extern void sb_pci_pmeen(si_t *sih);
+extern uint sb_pcie_readreg(void *sih, uint addrtype, uint offset);
+
+/* AMBA Interconnect exported externs */
+extern si_t *ai_attach(uint pcidev, osl_t *osh, void *regs, uint bustype,
+ void *sdh, char **vars, uint *varsz);
+extern si_t *ai_kattach(osl_t *osh);
+extern void ai_scan(si_t *sih, void *regs, uint devid);
+
+extern uint ai_flag(si_t *sih);
+extern void ai_setint(si_t *sih, int siflag);
+extern uint ai_coreidx(si_t *sih);
+extern uint ai_corevendor(si_t *sih);
+extern uint ai_corerev(si_t *sih);
+extern bool ai_iscoreup(si_t *sih);
+extern void *ai_setcoreidx(si_t *sih, uint coreidx);
+extern uint32 ai_core_cflags(si_t *sih, uint32 mask, uint32 val);
+extern void ai_core_cflags_wo(si_t *sih, uint32 mask, uint32 val);
+extern uint32 ai_core_sflags(si_t *sih, uint32 mask, uint32 val);
+extern uint ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val);
+extern void ai_core_reset(si_t *sih, uint32 bits, uint32 resetbits);
+extern void ai_core_disable(si_t *sih, uint32 bits);
+extern int ai_numaddrspaces(si_t *sih);
+extern uint32 ai_addrspace(si_t *sih, uint asidx);
+extern uint32 ai_addrspacesize(si_t *sih, uint asidx);
+extern void ai_write_wrap_reg(si_t *sih, uint32 offset, uint32 val);
+
+
+#endif /* _siutils_priv_h_ */
diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c
new file mode 100644
index 00000000000..8460804c945
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/wl_iw.c
@@ -0,0 +1,8319 @@
+/*
+ * Linux Wireless Extensions support
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: wl_iw.c,v 1.51.4.9.2.6.4.142.4.69 2010/12/21 03:00:08 Exp $
+ */
+
+
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+
+#include <bcmutils.h>
+#include <bcmendian.h>
+#include <proto/ethernet.h>
+
+#include <linux/if_arp.h>
+#include <asm/uaccess.h>
+
+#include <dngl_stats.h>
+#include <dhd.h>
+#include <dhdioctl.h>
+
+typedef void wlc_info_t;
+typedef void wl_info_t;
+typedef const struct si_pub si_t;
+#include <wlioctl.h>
+
+#include <proto/ethernet.h>
+#include <dngl_stats.h>
+#include <dhd.h>
+#define WL_ERROR(x) printf x
+#define WL_TRACE(x)
+#define WL_ASSOC(x)
+#define WL_INFORM(x)
+#define WL_WSEC(x)
+#define WL_SCAN(x)
+#define WL_TRACE_COEX(x)
+
+#include <wl_iw.h>
+
+
+
+#ifndef IW_ENCODE_ALG_SM4
+#define IW_ENCODE_ALG_SM4 0x20
+#endif
+
+#ifndef IW_AUTH_WAPI_ENABLED
+#define IW_AUTH_WAPI_ENABLED 0x20
+#endif
+
+#ifndef IW_AUTH_WAPI_VERSION_1
+#define IW_AUTH_WAPI_VERSION_1 0x00000008
+#endif
+
+#ifndef IW_AUTH_CIPHER_SMS4
+#define IW_AUTH_CIPHER_SMS4 0x00000020
+#endif
+
+#ifndef IW_AUTH_KEY_MGMT_WAPI_PSK
+#define IW_AUTH_KEY_MGMT_WAPI_PSK 4
+#endif
+
+#ifndef IW_AUTH_KEY_MGMT_WAPI_CERT
+#define IW_AUTH_KEY_MGMT_WAPI_CERT 8
+#endif
+
+
+#define IW_WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED | SMS4_ENABLED))
+
+#include <linux/rtnetlink.h>
+#include <linux/mutex.h>
+
+#define WL_IW_USE_ISCAN 1
+#define ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS 1
+
+#if defined(SOFTAP)
+#define WL_SOFTAP(x) printk x
+static struct net_device *priv_dev;
+static bool ap_cfg_running = FALSE;
+bool ap_fw_loaded = FALSE;
+static long ap_cfg_pid = -1;
+struct net_device *ap_net_dev = NULL;
+struct semaphore ap_eth_sema;
+static struct completion ap_cfg_exited;
+static int wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap);
+static int wl_iw_softap_deassoc_stations(struct net_device *dev, u8 *mac);
+#endif
+
+#define WL_IW_IOCTL_CALL(func_call) \
+ do { \
+ func_call; \
+ } while (0)
+
+static int g_onoff = G_WLAN_SET_ON;
+wl_iw_extra_params_t g_wl_iw_params;
+static struct mutex wl_cache_lock;
+
+#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY
+static bool use_non_dfs_channels = true;
+#endif
+
+extern bool wl_iw_conn_status_str(uint32 event_type, uint32 status,
+ uint32 reason, char* stringBuf, uint buflen);
+#include <bcmsdbus.h>
+extern void dhd_customer_gpio_wlan_ctrl(int onoff);
+extern uint dhd_dev_reset(struct net_device *dev, uint8 flag);
+extern void dhd_dev_init_ioctl(struct net_device *dev);
+int dev_iw_write_cfg1_bss_var(struct net_device *dev, int val);
+
+uint wl_msg_level = WL_ERROR_VAL;
+
+#define MAX_WLIW_IOCTL_LEN 1024
+
+
+#if defined(IL_BIGENDIAN)
+#include <bcmendian.h>
+#define htod32(i) (bcmswap32(i))
+#define htod16(i) (bcmswap16(i))
+#define dtoh32(i) (bcmswap32(i))
+#define dtoh16(i) (bcmswap16(i))
+#define htodchanspec(i) htod16(i)
+#define dtohchanspec(i) dtoh16(i)
+#else
+#define htod32(i) i
+#define htod16(i) i
+#define dtoh32(i) i
+#define dtoh16(i) i
+#define htodchanspec(i) i
+#define dtohchanspec(i) i
+#endif
+
+#ifdef CONFIG_WIRELESS_EXT
+
+extern struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
+extern int dhd_wait_pend8021x(struct net_device *dev);
+#endif
+
+#if WIRELESS_EXT < 19
+#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST)
+#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST)
+#endif
+
+static void *g_scan = NULL;
+static volatile uint g_scan_specified_ssid;
+static wlc_ssid_t g_specific_ssid;
+
+static wlc_ssid_t g_ssid;
+
+static wl_iw_ss_cache_ctrl_t g_ss_cache_ctrl;
+#if defined(CONFIG_FIRST_SCAN)
+static volatile uint g_first_broadcast_scan;
+static volatile uint g_first_counter_scans;
+#define MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN 3
+#endif
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
+#define DAEMONIZE(a) daemonize(a); \
+ allow_signal(SIGKILL); \
+ allow_signal(SIGTERM);
+#else
+#define RAISE_RX_SOFTIRQ() \
+ cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
+#define DAEMONIZE(a) daemonize(); \
+ do { if (a) \
+ strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \
+ } while (0);
+#endif
+
+#if defined(WL_IW_USE_ISCAN)
+#if !defined(CSCAN)
+static void wl_iw_free_ss_cache(void);
+static int wl_iw_run_ss_cache_timer(int kick_off);
+#endif
+#if defined(CONFIG_FIRST_SCAN)
+int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag);
+#endif
+static int dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len);
+#define ISCAN_STATE_IDLE 0
+#define ISCAN_STATE_SCANING 1
+
+#define WLC_IW_ISCAN_MAXLEN 2048
+typedef struct iscan_buf {
+ struct iscan_buf * next;
+ char iscan_buf[WLC_IW_ISCAN_MAXLEN];
+} iscan_buf_t;
+
+typedef struct iscan_info {
+ struct net_device *dev;
+ struct timer_list timer;
+ uint32 timer_ms;
+ uint32 timer_on;
+ int iscan_state;
+ iscan_buf_t * list_hdr;
+ iscan_buf_t * list_cur;
+
+
+ long sysioc_pid;
+ struct semaphore sysioc_sem;
+ struct completion sysioc_exited;
+
+ uint32 scan_flag;
+#if defined CSCAN
+ char ioctlbuf[WLC_IOCTL_MEDLEN];
+#else
+ char ioctlbuf[WLC_IOCTL_SMLEN];
+#endif
+ wl_iscan_params_t *iscan_ex_params_p;
+ int iscan_ex_param_size;
+} iscan_info_t;
+#define COEX_DHCP 1
+
+#define BT_DHCP_eSCO_FIX
+#define BT_DHCP_USE_FLAGS
+#define BT_DHCP_OPPORTUNITY_WINDOW_TIME 2500
+#define BT_DHCP_FLAG_FORCE_TIME 5500
+static void wl_iw_bt_flag_set(struct net_device *dev, bool set);
+static void wl_iw_bt_release(void);
+
+typedef enum bt_coex_status {
+ BT_DHCP_IDLE = 0,
+ BT_DHCP_START,
+ BT_DHCP_OPPORTUNITY_WINDOW,
+ BT_DHCP_FLAG_FORCE_TIMEOUT
+} coex_status_t;
+
+typedef struct bt_info {
+ struct net_device *dev;
+ struct timer_list timer;
+ uint32 timer_ms;
+ uint32 timer_on;
+ bool dhcp_done;
+ int bt_state;
+
+ long bt_pid;
+ struct semaphore bt_sem;
+ struct completion bt_exited;
+} bt_info_t;
+
+bt_info_t *g_bt = NULL;
+static void wl_iw_bt_timerfunc(ulong data);
+iscan_info_t *g_iscan = NULL;
+static void wl_iw_timerfunc(ulong data);
+static void wl_iw_set_event_mask(struct net_device *dev);
+static int
+wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action);
+#endif
+static int
+wl_iw_set_scan(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+);
+
+#ifndef CSCAN
+static int
+wl_iw_get_scan(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+);
+
+static uint
+wl_iw_get_scan_prep(
+ wl_scan_results_t *list,
+ struct iw_request_info *info,
+ char *extra,
+ short max_size
+);
+#endif
+
+static void swap_key_from_BE(
+ wl_wsec_key_t *key
+)
+{
+ key->index = htod32(key->index);
+ key->len = htod32(key->len);
+ key->algo = htod32(key->algo);
+ key->flags = htod32(key->flags);
+ key->rxiv.hi = htod32(key->rxiv.hi);
+ key->rxiv.lo = htod16(key->rxiv.lo);
+ key->iv_initialized = htod32(key->iv_initialized);
+}
+
+static void swap_key_to_BE(
+ wl_wsec_key_t *key
+)
+{
+ key->index = dtoh32(key->index);
+ key->len = dtoh32(key->len);
+ key->algo = dtoh32(key->algo);
+ key->flags = dtoh32(key->flags);
+ key->rxiv.hi = dtoh32(key->rxiv.hi);
+ key->rxiv.lo = dtoh16(key->rxiv.lo);
+ key->iv_initialized = dtoh32(key->iv_initialized);
+}
+
+static int
+dev_wlc_ioctl(
+ struct net_device *dev,
+ int cmd,
+ void *arg,
+ int len
+)
+{
+ struct ifreq ifr;
+ wl_ioctl_t ioc;
+ mm_segment_t fs;
+ int ret = -EINVAL;
+
+ if (!dev) {
+ WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+ return ret;
+ }
+
+ net_os_wake_lock(dev);
+
+ WL_INFORM(("\n%s, PID:%x: send Local IOCTL -> dhd: cmd:0x%x, buf:%p, len:%d ,\n",
+ __FUNCTION__, current->pid, cmd, arg, len));
+
+ if (g_onoff == G_WLAN_SET_ON) {
+ memset(&ioc, 0, sizeof(ioc));
+ ioc.cmd = cmd;
+ ioc.buf = arg;
+ ioc.len = len;
+
+ strcpy(ifr.ifr_name, dev->name);
+ ifr.ifr_data = (caddr_t) &ioc;
+
+ ret = dev_open(dev);
+ if (ret) {
+ WL_ERROR(("%s: Error dev_open: %d\n", __func__, ret));
+ net_os_wake_unlock(dev);
+ return ret;
+ }
+
+ fs = get_fs();
+ set_fs(get_ds());
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31))
+ ret = dev->do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
+#else
+ ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
+#endif
+ set_fs(fs);
+ }
+ else {
+ WL_TRACE(("%s: call after driver stop : ignored\n", __FUNCTION__));
+ }
+
+ net_os_wake_unlock(dev);
+
+ return ret;
+}
+
+
+static int
+dev_wlc_intvar_get_reg(
+ struct net_device *dev,
+ char *name,
+ uint reg,
+ int *retval)
+{
+ union {
+ char buf[WLC_IOCTL_SMLEN];
+ int val;
+ } var;
+ int error;
+
+ uint len;
+ len = bcm_mkiovar(name, (char *)(&reg), sizeof(reg), (char *)(&var), sizeof(var.buf));
+ ASSERT(len);
+ error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len);
+
+ *retval = dtoh32(var.val);
+ return (error);
+}
+
+
+static int
+dev_wlc_intvar_set_reg(
+ struct net_device *dev,
+ char *name,
+ char *addr,
+ char * val)
+{
+ char reg_addr[8];
+
+ memset(reg_addr, 0, sizeof(reg_addr));
+ memcpy((char *)&reg_addr[0], (char *)addr, 4);
+ memcpy((char *)&reg_addr[4], (char *)val, 4);
+
+ return (dev_wlc_bufvar_set(dev, name, (char *)&reg_addr[0], sizeof(reg_addr)));
+}
+
+
+static int
+dev_wlc_intvar_set(
+ struct net_device *dev,
+ char *name,
+ int val)
+{
+ char buf[WLC_IOCTL_SMLEN];
+ uint len;
+
+ val = htod32(val);
+ len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf));
+ ASSERT(len);
+
+ return (dev_wlc_ioctl(dev, WLC_SET_VAR, buf, len));
+}
+
+#if defined(WL_IW_USE_ISCAN)
+static int
+dev_iw_iovar_setbuf(
+ struct net_device *dev,
+ char *iovar,
+ void *param,
+ int paramlen,
+ void *bufptr,
+ int buflen)
+{
+ int iolen;
+
+ iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen);
+ ASSERT(iolen);
+
+ if (iolen == 0)
+ return 0;
+
+ return (dev_wlc_ioctl(dev, WLC_SET_VAR, bufptr, iolen));
+}
+
+static int
+dev_iw_iovar_getbuf(
+ struct net_device *dev,
+ char *iovar,
+ void *param,
+ int paramlen,
+ void *bufptr,
+ int buflen)
+{
+ int iolen;
+
+ iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen);
+ ASSERT(iolen);
+
+ return (dev_wlc_ioctl(dev, WLC_GET_VAR, bufptr, buflen));
+}
+#endif
+
+
+#if WIRELESS_EXT > 17
+static int
+dev_wlc_bufvar_set(
+ struct net_device *dev,
+ char *name,
+ char *buf, int len)
+{
+ static char ioctlbuf[MAX_WLIW_IOCTL_LEN];
+ uint buflen;
+
+ buflen = bcm_mkiovar(name, buf, len, ioctlbuf, sizeof(ioctlbuf));
+ ASSERT(buflen);
+
+ return (dev_wlc_ioctl(dev, WLC_SET_VAR, ioctlbuf, buflen));
+}
+#endif
+
+
+static int
+dev_wlc_bufvar_get(
+ struct net_device *dev,
+ char *name,
+ char *buf, int buflen)
+{
+ static char ioctlbuf[MAX_WLIW_IOCTL_LEN];
+ int error;
+ uint len;
+
+ len = bcm_mkiovar(name, NULL, 0, ioctlbuf, sizeof(ioctlbuf));
+ ASSERT(len);
+ error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)ioctlbuf, MAX_WLIW_IOCTL_LEN);
+ if (!error)
+ bcopy(ioctlbuf, buf, buflen);
+
+ return (error);
+}
+
+
+
+static int
+dev_wlc_intvar_get(
+ struct net_device *dev,
+ char *name,
+ int *retval)
+{
+ union {
+ char buf[WLC_IOCTL_SMLEN];
+ int val;
+ } var;
+ int error;
+
+ uint len;
+ uint data_null;
+
+ len = bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), sizeof(var.buf));
+ ASSERT(len);
+ error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len);
+
+ *retval = dtoh32(var.val);
+
+ return (error);
+}
+
+
+#if WIRELESS_EXT > 12
+static int
+wl_iw_set_active_scan(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int as = 0;
+ int error = 0;
+ char *p = extra;
+
+#if defined(WL_IW_USE_ISCAN)
+ if (g_iscan->iscan_state == ISCAN_STATE_IDLE)
+#endif
+ error = dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &as, sizeof(as));
+#if defined(WL_IW_USE_ISCAN)
+ else
+ g_iscan->scan_flag = as;
+#endif
+ p += snprintf(p, MAX_WX_STRING, "OK");
+
+ wrqu->data.length = p - extra + 1;
+ return error;
+}
+
+static int
+wl_iw_set_passive_scan(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int ps = 1;
+ int error = 0;
+ char *p = extra;
+
+#if defined(WL_IW_USE_ISCAN)
+ if (g_iscan->iscan_state == ISCAN_STATE_IDLE) {
+#endif
+
+
+ if (g_scan_specified_ssid == 0) {
+ error = dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &ps, sizeof(ps));
+ }
+#if defined(WL_IW_USE_ISCAN)
+ }
+ else
+ g_iscan->scan_flag = ps;
+#endif
+
+ p += snprintf(p, MAX_WX_STRING, "OK");
+
+ wrqu->data.length = p - extra + 1;
+ return error;
+}
+
+static int
+wl_iw_get_macaddr(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error;
+ char buf[128];
+ struct ether_addr *id;
+ char *p = extra;
+
+
+ strcpy(buf, "cur_etheraddr");
+ error = dev_wlc_ioctl(dev, WLC_GET_VAR, buf, sizeof(buf));
+ id = (struct ether_addr *) buf;
+ p += snprintf(p, MAX_WX_STRING, "Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ id->octet[0], id->octet[1], id->octet[2],
+ id->octet[3], id->octet[4], id->octet[5]);
+ wrqu->data.length = p - extra + 1;
+
+ return error;
+}
+
+static int
+wl_iw_set_country_code(struct net_device *dev, char *ccode)
+{
+ char country_code[WLC_CNTRY_BUF_SZ];
+ int ret = -1;
+
+ WL_TRACE(("%s\n", __FUNCTION__));
+ if (!ccode)
+ ccode = dhd_bus_country_get(dev);
+ strncpy(country_code, ccode, sizeof(country_code));
+ if (ccode && (country_code[0] != 0)) {
+#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY
+ if (use_non_dfs_channels && !strncmp(country_code, "US", 2))
+ strncpy(country_code, "Q2", WLC_CNTRY_BUF_SZ);
+ if (!use_non_dfs_channels && !strncmp(country_code, "Q2", 2))
+ strncpy(country_code, "US", WLC_CNTRY_BUF_SZ);
+#endif
+ ret = dev_wlc_ioctl(dev, WLC_SET_COUNTRY, &country_code, sizeof(country_code));
+ if (ret >= 0) {
+ WL_TRACE(("%s: set country %s OK\n", __FUNCTION__, country_code));
+ dhd_bus_country_set(dev, &country_code[0]);
+ }
+ }
+ return ret;
+}
+
+static int
+wl_iw_set_country(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ char country_code[WLC_CNTRY_BUF_SZ];
+ int error = 0;
+ char *p = extra;
+ int country_offset;
+ int country_code_size;
+
+ WL_TRACE(("%s\n", __FUNCTION__));
+ memset(country_code, 0, sizeof(country_code));
+
+ country_offset = strcspn(extra, " ");
+ country_code_size = strlen(extra) - country_offset;
+
+ if (country_offset != 0) {
+ strncpy(country_code, extra + country_offset + 1,
+ MIN(country_code_size, sizeof(country_code)));
+ error = wl_iw_set_country_code(dev, country_code);
+ if (error >= 0) {
+ p += snprintf(p, MAX_WX_STRING, "OK");
+ WL_TRACE(("%s: set country %s OK\n", __FUNCTION__, country_code));
+ goto exit;
+ }
+ }
+
+ WL_ERROR(("%s: set country %s failed code %d\n", __FUNCTION__, country_code, error));
+ p += snprintf(p, MAX_WX_STRING, "FAIL");
+
+exit:
+ wrqu->data.length = p - extra + 1;
+ return error;
+}
+
+#ifdef CUSTOMER_HW2
+static int
+wl_iw_set_power_mode(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error = 0;
+ char *p = extra;
+ static int pm = PM_FAST;
+ int pm_local = PM_OFF;
+ char powermode_val = 0;
+
+ WL_TRACE_COEX(("%s: DHCP session cmd:%s\n", __FUNCTION__, extra));
+
+ strncpy((char *)&powermode_val, extra + strlen("POWERMODE") + 1, 1);
+
+ if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) {
+
+ dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm));
+ dev_wlc_ioctl(dev, WLC_SET_PM, &pm_local, sizeof(pm_local));
+
+ /* Disable packet filtering if necessary */
+ net_os_set_packet_filter(dev, 0);
+
+ g_bt->dhcp_done = false;
+ WL_TRACE_COEX(("%s: DHCP start, pm:%d changed to pm:%d\n",
+ __FUNCTION__, pm, pm_local));
+
+ } else if (strnicmp((char *)&powermode_val, "0", strlen("0")) == 0) {
+
+ dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm));
+
+ /* Enable packet filtering if was turned off */
+ net_os_set_packet_filter(dev, 1);
+
+ g_bt->dhcp_done = true;
+
+ } else {
+ WL_ERROR(("%s Unkwown yet power setting, ignored\n",
+ __FUNCTION__));
+ }
+
+ p += snprintf(p, MAX_WX_STRING, "OK");
+
+ wrqu->data.length = p - extra + 1;
+
+ return error;
+}
+#endif
+
+
+static bool btcoex_is_sco_active(struct net_device *dev)
+{
+ int ioc_res = 0;
+ bool res = false;
+ int temp = 0;
+
+ ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 4, &temp);
+
+ if (ioc_res == 0) {
+ WL_TRACE_COEX(("%s: read btc_params[4] = %x\n", __FUNCTION__, temp));
+
+ if ((temp > 0xea0) && (temp < 0xed8)) {
+ WL_TRACE_COEX(("%s: BT SCO/eSCO is ACTIVE\n", __FUNCTION__));
+ res = true;
+ } else {
+ WL_TRACE_COEX(("%s: BT SCO/eSCO is NOT detected\n", __FUNCTION__));
+ }
+ } else {
+ WL_ERROR(("%s ioc read btc params error\n", __FUNCTION__));
+ }
+ return res;
+}
+
+#if defined(BT_DHCP_eSCO_FIX)
+
+static int set_btc_esco_params(struct net_device *dev, bool trump_sco)
+{
+ static bool saved_status = false;
+
+ char buf_reg50va_dhcp_on[8] = { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 };
+ char buf_reg51va_dhcp_on[8] = { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
+ char buf_reg64va_dhcp_on[8] = { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
+ char buf_reg65va_dhcp_on[8] = { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
+ char buf_reg71va_dhcp_on[8] = { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 };
+
+ uint32 regaddr;
+ static uint32 saved_reg50;
+ static uint32 saved_reg51;
+ static uint32 saved_reg64;
+ static uint32 saved_reg65;
+ static uint32 saved_reg71;
+
+ if (trump_sco) {
+
+ WL_TRACE_COEX(("Do new SCO/eSCO coex algo {save & override} \n"));
+
+ if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) &&
+ (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) &&
+ (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) &&
+ (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) &&
+ (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) {
+
+ saved_status = TRUE;
+ WL_TRACE_COEX(("%s saved bt_params[50,51,64,65,71]:"
+ " 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ __FUNCTION__, saved_reg50, saved_reg51,
+ saved_reg64, saved_reg65, saved_reg71));
+
+ } else {
+ WL_ERROR((":%s: save btc_params failed\n",
+ __FUNCTION__));
+ saved_status = false;
+ return -1;
+ }
+
+ WL_TRACE_COEX(("override with [50,51,64,65,71]:"
+ " 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ *(u32 *)(buf_reg50va_dhcp_on+4),
+ *(u32 *)(buf_reg51va_dhcp_on+4),
+ *(u32 *)(buf_reg64va_dhcp_on+4),
+ *(u32 *)(buf_reg65va_dhcp_on+4),
+ *(u32 *)(buf_reg71va_dhcp_on+4)));
+
+ dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg50va_dhcp_on[0], 8);
+ dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg51va_dhcp_on[0], 8);
+ dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg64va_dhcp_on[0], 8);
+ dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg65va_dhcp_on[0], 8);
+ dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg71va_dhcp_on[0], 8);
+
+ saved_status = true;
+
+ } else if (saved_status) {
+
+ WL_TRACE_COEX(("Do new SCO/eSCO coex algo {save & override} \n"));
+
+ regaddr = 50;
+ dev_wlc_intvar_set_reg(dev, "btc_params",
+ (char *)&regaddr, (char *)&saved_reg50);
+ regaddr = 51;
+ dev_wlc_intvar_set_reg(dev, "btc_params",
+ (char *)&regaddr, (char *)&saved_reg51);
+ regaddr = 64;
+ dev_wlc_intvar_set_reg(dev, "btc_params",
+ (char *)&regaddr, (char *)&saved_reg64);
+ regaddr = 65;
+ dev_wlc_intvar_set_reg(dev, "btc_params",
+ (char *)&regaddr, (char *)&saved_reg65);
+ regaddr = 71;
+ dev_wlc_intvar_set_reg(dev, "btc_params",
+ (char *)&regaddr, (char *)&saved_reg71);
+
+ WL_TRACE_COEX(("restore bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ saved_reg50, saved_reg51, saved_reg64,
+ saved_reg65, saved_reg71));
+
+ saved_status = false;
+ } else {
+ WL_ERROR((":%s att to restore not saved BTCOEX params\n",
+ __FUNCTION__));
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+static int
+wl_iw_get_power_mode(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error;
+ char *p = extra;
+ int pm_local = PM_FAST;
+
+ error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm_local, sizeof(pm_local));
+ if (!error) {
+ WL_TRACE(("%s: Powermode = %d\n", __func__, pm_local));
+ if (pm_local == PM_OFF)
+ pm_local = 1; /* Active */
+ else
+ pm_local = 0; /* Auto */
+ p += snprintf(p, MAX_WX_STRING, "powermode = %d", pm_local);
+ }
+ else {
+ WL_TRACE(("%s: Error = %d\n", __func__, error));
+ p += snprintf(p, MAX_WX_STRING, "FAIL");
+ }
+ wrqu->data.length = p - extra + 1;
+ return error;
+}
+
+static int
+wl_iw_set_btcoex_dhcp(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error = 0;
+ char *p = extra;
+#ifndef CUSTOMER_HW2
+ static int pm = PM_FAST;
+ int pm_local = PM_OFF;
+#endif
+ char powermode_val = 0;
+ char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 };
+ char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 };
+ char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 };
+
+ uint32 regaddr;
+ static uint32 saved_reg66;
+ static uint32 saved_reg41;
+ static uint32 saved_reg68;
+ static bool saved_status = FALSE;
+
+ char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00};
+
+#ifdef CUSTOMER_HW2
+ strncpy((char *)&powermode_val, extra + strlen("BTCOEXMODE") + 1, 1);
+#else
+ strncpy((char *)&powermode_val, extra + strlen("POWERMODE") + 1, 1);
+#endif
+
+ if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) {
+
+ WL_TRACE_COEX(("%s: DHCP session start, cmd:%s\n", __FUNCTION__, extra));
+
+ if ((saved_status == FALSE) &&
+#ifndef CUSTOMER_HW2
+ (!dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm))) &&
+#endif
+ (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) &&
+ (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) &&
+ (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) {
+ WL_TRACE_COEX(("save regs {66,41,68} ->: 0x%x 0x%x 0x%x\n", \
+ saved_reg66, saved_reg41, saved_reg68));
+
+#ifndef CUSTOMER_HW2
+ dev_wlc_ioctl(dev, WLC_SET_PM, &pm_local, sizeof(pm_local));
+#endif
+
+ if (btcoex_is_sco_active(dev)) {
+
+ dev_wlc_bufvar_set(dev, "btc_params", \
+ (char *)&buf_reg66va_dhcp_on[0], \
+ sizeof(buf_reg66va_dhcp_on));
+
+ dev_wlc_bufvar_set(dev, "btc_params", \
+ (char *)&buf_reg41va_dhcp_on[0], \
+ sizeof(buf_reg41va_dhcp_on));
+
+ dev_wlc_bufvar_set(dev, "btc_params", \
+ (char *)&buf_reg68va_dhcp_on[0], \
+ sizeof(buf_reg68va_dhcp_on));
+ saved_status = TRUE;
+
+ g_bt->bt_state = BT_DHCP_START;
+ g_bt->timer_on = 1;
+ mod_timer(&g_bt->timer, g_bt->timer.expires);
+ WL_TRACE_COEX(("%s enable BT DHCP Timer\n", \
+ __FUNCTION__));
+ }
+ }
+ else if (saved_status == TRUE) {
+ WL_ERROR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__));
+ }
+ }
+#ifdef CUSTOMER_HW2
+ else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) {
+#else
+ else if (strnicmp((char *)&powermode_val, "0", strlen("0")) == 0) {
+#endif
+
+#ifndef CUSTOMER_HW2
+ dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm));
+#endif
+
+ WL_TRACE_COEX(("%s disable BT DHCP Timer\n", __FUNCTION__));
+ if (g_bt->timer_on) {
+ g_bt->timer_on = 0;
+ del_timer_sync(&g_bt->timer);
+
+ if (g_bt->bt_state != BT_DHCP_IDLE) {
+ WL_TRACE_COEX(("%s bt->bt_state:%d\n",
+ __FUNCTION__, g_bt->bt_state));
+
+ up(&g_bt->bt_sem);
+ }
+ }
+
+ if (saved_status == TRUE) {
+ dev_wlc_bufvar_set(dev, "btc_flags", \
+ (char *)&buf_flag7_default[0], sizeof(buf_flag7_default));
+
+ regaddr = 66;
+ dev_wlc_intvar_set_reg(dev, "btc_params", \
+ (char *)&regaddr, (char *)&saved_reg66);
+ regaddr = 41;
+ dev_wlc_intvar_set_reg(dev, "btc_params", \
+ (char *)&regaddr, (char *)&saved_reg41);
+ regaddr = 68;
+ dev_wlc_intvar_set_reg(dev, "btc_params", \
+ (char *)&regaddr, (char *)&saved_reg68);
+
+ WL_TRACE_COEX(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", \
+ saved_reg66, saved_reg41, saved_reg68));
+ }
+ saved_status = FALSE;
+ }
+ else {
+ WL_ERROR(("%s Unkwown yet power setting, ignored\n",
+ __FUNCTION__));
+ }
+
+ p += snprintf(p, MAX_WX_STRING, "OK");
+
+ wrqu->data.length = p - extra + 1;
+
+ return error;
+}
+
+static int
+wl_iw_set_suspend(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int suspend_flag;
+ int ret_now;
+ int ret = 0;
+
+ suspend_flag = *(extra + strlen(SETSUSPEND_CMD) + 1) - '0';
+
+ if (suspend_flag != 0)
+ suspend_flag = 1;
+
+ ret_now = net_os_set_suspend_disable(dev, suspend_flag);
+
+ if (ret_now != suspend_flag) {
+ if (!(ret = net_os_set_suspend(dev, ret_now)))
+ WL_ERROR(("%s: Suspend Flag %d -> %d\n", \
+ __FUNCTION__, ret_now, suspend_flag));
+ else
+ WL_ERROR(("%s: failed %d\n", __FUNCTION__, ret));
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY
+static int
+wl_iw_set_dfs_channels(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ use_non_dfs_channels = *(extra + strlen(SETDFSCHANNELS_CMD) + 1) - '0';
+ use_non_dfs_channels = (use_non_dfs_channels != 0) ? false : true;
+ wl_iw_set_country_code(dev, NULL);
+ return 0;
+}
+#endif
+
+int
+wl_format_ssid(char* ssid_buf, uint8* ssid, int ssid_len)
+{
+ int i, c;
+ char *p = ssid_buf;
+
+ if (ssid_len > 32) ssid_len = 32;
+
+ for (i = 0; i < ssid_len; i++) {
+ c = (int)ssid[i];
+ if (c == '\\') {
+ *p++ = '\\';
+ *p++ = '\\';
+ } else if (isprint((uchar)c)) {
+ *p++ = (char)c;
+ } else {
+ p += sprintf(p, "\\x%02X", c);
+ }
+ }
+ *p = '\0';
+
+ return p - ssid_buf;
+}
+
+static int
+wl_iw_get_link_speed(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error = 0;
+ char *p = extra;
+ static int link_speed;
+
+ net_os_wake_lock(dev);
+ if (g_onoff == G_WLAN_SET_ON) {
+ error = dev_wlc_ioctl(dev, WLC_GET_RATE, &link_speed, sizeof(link_speed));
+ link_speed *= 500000;
+ }
+
+ p += snprintf(p, MAX_WX_STRING, "LinkSpeed %d", link_speed/1000000);
+
+ wrqu->data.length = p - extra + 1;
+
+ net_os_wake_unlock(dev);
+ return error;
+}
+
+
+static int
+wl_iw_get_dtim_skip(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error = -1;
+ char *p = extra;
+ char iovbuf[32];
+
+ net_os_wake_lock(dev);
+ if (g_onoff == G_WLAN_SET_ON) {
+
+ memset(iovbuf, 0, sizeof(iovbuf));
+ strcpy(iovbuf, "bcn_li_dtim");
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_VAR,
+ &iovbuf, sizeof(iovbuf))) >= 0) {
+
+ p += snprintf(p, MAX_WX_STRING, "Dtim_skip %d", iovbuf[0]);
+ WL_TRACE(("%s: get dtim_skip = %d\n", __FUNCTION__, iovbuf[0]));
+ wrqu->data.length = p - extra + 1;
+ }
+ else
+ WL_ERROR(("%s: get dtim_skip failed code %d\n", \
+ __FUNCTION__, error));
+ }
+ net_os_wake_unlock(dev);
+ return error;
+}
+
+
+static int
+wl_iw_set_dtim_skip(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error = -1;
+ char *p = extra;
+ int bcn_li_dtim;
+ char iovbuf[32];
+
+ net_os_wake_lock(dev);
+ if (g_onoff == G_WLAN_SET_ON) {
+
+ bcn_li_dtim = htod32((uint)*(extra + strlen(DTIM_SKIP_SET_CMD) + 1) - '0');
+
+ if ((bcn_li_dtim >= 0) || ((bcn_li_dtim <= 5))) {
+
+ memset(iovbuf, 0, sizeof(iovbuf));
+ bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
+ 4, iovbuf, sizeof(iovbuf));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_VAR,
+ &iovbuf, sizeof(iovbuf))) >= 0) {
+ p += snprintf(p, MAX_WX_STRING, "OK");
+
+ net_os_set_dtim_skip(dev, bcn_li_dtim);
+
+ WL_TRACE(("%s: set dtim_skip %d OK\n", __FUNCTION__, \
+ bcn_li_dtim));
+ goto exit;
+ }
+ else WL_ERROR(("%s: set dtim_skip %d failed code %d\n", \
+ __FUNCTION__, bcn_li_dtim, error));
+ }
+ else WL_ERROR(("%s Incorrect dtim_skip setting %d, ignored\n", \
+ __FUNCTION__, bcn_li_dtim));
+ }
+
+ p += snprintf(p, MAX_WX_STRING, "FAIL");
+
+exit:
+ wrqu->data.length = p - extra + 1;
+ net_os_wake_unlock(dev);
+ return error;
+}
+
+
+static int
+wl_iw_get_band(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error = -1;
+ char *p = extra;
+ static int band;
+
+ net_os_wake_lock(dev);
+
+ if (g_onoff == G_WLAN_SET_ON) {
+ error = dev_wlc_ioctl(dev, WLC_GET_BAND, &band, sizeof(band));
+
+ p += snprintf(p, MAX_WX_STRING, "Band %d", band);
+
+ wrqu->data.length = p - extra + 1;
+ }
+
+ net_os_wake_unlock(dev);
+ return error;
+}
+
+
+static int
+wl_iw_set_band(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error = -1;
+ char *p = extra;
+ uint band;
+
+ net_os_wake_lock(dev);
+
+ if (g_onoff == G_WLAN_SET_ON) {
+
+ band = htod32((uint)*(extra + strlen(BAND_SET_CMD) + 1) - '0');
+
+ if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) {
+
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_BAND,
+ &band, sizeof(band))) >= 0) {
+ p += snprintf(p, MAX_WX_STRING, "OK");
+ WL_TRACE(("%s: set band %d OK\n", __FUNCTION__, band));
+ goto exit;
+ }
+ else WL_ERROR(("%s: set band %d failed code %d\n", __FUNCTION__, \
+ band, error));
+ }
+ else WL_ERROR(("%s Incorrect band setting %d, ignored\n", __FUNCTION__, band));
+ }
+
+ p += snprintf(p, MAX_WX_STRING, "FAIL");
+
+exit:
+ wrqu->data.length = p - extra + 1;
+ net_os_wake_unlock(dev);
+ return error;
+}
+
+#ifdef PNO_SUPPORT
+
+static int
+wl_iw_set_pno_reset(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error = -1;
+ char *p = extra;
+
+ net_os_wake_lock(dev);
+ if ((g_onoff == G_WLAN_SET_ON) && (dev != NULL)) {
+
+ if ((error = dhd_dev_pno_reset(dev)) >= 0) {
+ p += snprintf(p, MAX_WX_STRING, "OK");
+ WL_TRACE(("%s: set OK\n", __FUNCTION__));
+ goto exit;
+ }
+ else WL_ERROR(("%s: failed code %d\n", __FUNCTION__, error));
+ }
+
+ p += snprintf(p, MAX_WX_STRING, "FAIL");
+
+exit:
+ wrqu->data.length = p - extra + 1;
+ net_os_wake_unlock(dev);
+ return error;
+}
+
+
+
+static int
+wl_iw_set_pno_enable(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error = -1;
+ char *p = extra;
+ int pfn_enabled;
+
+ net_os_wake_lock(dev);
+ pfn_enabled = htod32((uint)*(extra + strlen(PNOENABLE_SET_CMD) + 1) - '0');
+
+ if ((g_onoff == G_WLAN_SET_ON) && (dev != NULL)) {
+
+ if ((error = dhd_dev_pno_enable(dev, pfn_enabled)) >= 0) {
+ p += snprintf(p, MAX_WX_STRING, "OK");
+ WL_TRACE(("%s: set OK\n", __FUNCTION__));
+ goto exit;
+ }
+ else WL_ERROR(("%s: failed code %d\n", __FUNCTION__, error));
+ }
+
+ p += snprintf(p, MAX_WX_STRING, "FAIL");
+
+exit:
+ wrqu->data.length = p - extra + 1;
+ net_os_wake_unlock(dev);
+ return error;
+}
+
+
+
+static int
+wl_iw_set_pno_set(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int res = -1;
+ wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT];
+ int nssid = 0;
+ cmd_tlv_t *cmd_tlv_temp;
+ char *str_ptr;
+ char *str_ptr_end;
+ int tlv_size_left;
+ int pno_time;
+
+#ifdef PNO_SET_DEBUG
+ int i;
+ char pno_in_example[] = {'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ', \
+ 'S', '1', '2', '0',
+ 'S',
+ 0x04,
+ 'B', 'R', 'C', 'M',
+ 'S',
+ 0x04,
+ 'G', 'O', 'O', 'G',
+ 'T',
+ '1','E',
+ 0x00
+ };
+#endif
+
+ net_os_wake_lock(dev);
+ WL_ERROR(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n",
+ __FUNCTION__, info->cmd, info->flags,
+ wrqu->data.pointer, wrqu->data.length));
+
+ if (g_onoff == G_WLAN_SET_OFF) {
+ WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__));
+ goto exit_proc;
+ }
+
+ if (wrqu->data.length < (strlen(PNOSETUP_SET_CMD) + sizeof(cmd_tlv_t))) {
+ WL_ERROR(("%s aggument=%d less %d\n", __FUNCTION__, \
+ wrqu->data.length, strlen(PNOSETUP_SET_CMD) + sizeof(cmd_tlv_t)));
+ goto exit_proc;
+ }
+
+#ifdef PNO_SET_DEBUG
+ if (!(extra = kmalloc(sizeof(pno_in_example) +100, GFP_KERNEL))) {
+ res = -ENOMEM;
+ goto exit_proc;
+ }
+ memcpy(extra, pno_in_example, sizeof(pno_in_example));
+ wrqu->data.length = sizeof(pno_in_example);
+ for (i = 0; i < wrqu->data.length; i++)
+ printf("%02X ", extra[i]);
+ printf("\n");
+#endif
+
+ str_ptr = extra;
+#ifdef PNO_SET_DEBUG
+ str_ptr += strlen("PNOSETUP ");
+ tlv_size_left = wrqu->data.length - strlen("PNOSETUP ");
+#else
+ str_ptr += strlen(PNOSETUP_SET_CMD);
+ tlv_size_left = wrqu->data.length - strlen(PNOSETUP_SET_CMD);
+#endif
+
+ cmd_tlv_temp = (cmd_tlv_t *)str_ptr;
+ memset(ssids_local, 0, sizeof(ssids_local));
+
+ if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && \
+ (cmd_tlv_temp->version == PNO_TLV_VERSION) && \
+ (cmd_tlv_temp->subver == PNO_TLV_SUBVERSION))
+ {
+ str_ptr += sizeof(cmd_tlv_t);
+ tlv_size_left -= sizeof(cmd_tlv_t);
+
+ if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, \
+ MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) {
+ WL_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid));
+ goto exit_proc;
+ }
+ else {
+ if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) {
+ WL_ERROR(("%s scan duration corrupted field size %d\n", \
+ __FUNCTION__, tlv_size_left));
+ goto exit_proc;
+ }
+ str_ptr++;
+ pno_time = simple_strtoul(str_ptr, &str_ptr_end, 16);
+ WL_ERROR((" got %d bytes left pno_time %d or %#x\n", \
+ tlv_size_left, pno_time, pno_time));
+ }
+ }
+ else {
+ WL_ERROR(("%s get wrong TLV command\n", __FUNCTION__));
+ goto exit_proc;
+ }
+
+ res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time);
+
+exit_proc:
+ net_os_wake_unlock(dev);
+ return res;
+}
+#endif
+
+static int
+wl_iw_get_rssi(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ static int rssi = 0;
+ static wlc_ssid_t ssid = {0};
+ int error = 0;
+ char *p = extra;
+ static char ssidbuf[SSID_FMT_BUF_LEN];
+ scb_val_t scb_val;
+
+ net_os_wake_lock(dev);
+
+ bzero(&scb_val, sizeof(scb_val_t));
+
+ if (g_onoff == G_WLAN_SET_ON) {
+ error = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t));
+ if (error) {
+ WL_ERROR(("%s: Fails %d\n", __FUNCTION__, error));
+ } else {
+ rssi = dtoh32(scb_val.val);
+
+ error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid));
+ if (!error) {
+ ssid.SSID_len = dtoh32(ssid.SSID_len);
+ wl_format_ssid(ssidbuf, ssid.SSID, dtoh32(ssid.SSID_len));
+ }
+ }
+ }
+
+ WL_ASSOC(("%s ssid_len:%d, rssi:%d\n", __FUNCTION__, ssid.SSID_len, rssi));
+
+ if (error || (ssid.SSID_len == 0)) {
+ p += snprintf(p, MAX_WX_STRING, "FAIL");
+ } else {
+ p += snprintf(p, MAX_WX_STRING, "%s rssi %d ", ssidbuf, rssi);
+ }
+ wrqu->data.length = p - extra + 1;
+
+ net_os_wake_unlock(dev);
+ return error;
+}
+
+int
+wl_iw_send_priv_event(
+ struct net_device *dev,
+ char *flag
+)
+{
+ union iwreq_data wrqu;
+ char extra[IW_CUSTOM_MAX + 1];
+ int cmd;
+
+ cmd = IWEVCUSTOM;
+ memset(&wrqu, 0, sizeof(wrqu));
+ if (strlen(flag) > sizeof(extra))
+ return -1;
+
+ strcpy(extra, flag);
+ wrqu.data.length = strlen(extra);
+ wireless_send_event(dev, cmd, &wrqu, extra);
+ net_os_wake_lock_timeout_enable(dev);
+ WL_TRACE(("Send IWEVCUSTOM Event as %s\n", extra));
+
+ return 0;
+}
+
+
+int
+wl_control_wl_start(struct net_device *dev)
+{
+ int ret = 0;
+ wl_iw_t *iw;
+
+ WL_TRACE(("Enter %s \n", __FUNCTION__));
+
+ if (!dev) {
+ WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+ return -1;
+ }
+
+ iw = *(wl_iw_t **)netdev_priv(dev);
+
+ if (!iw) {
+ WL_ERROR(("%s: wl is null\n", __FUNCTION__));
+ return -1;
+ }
+ dhd_os_start_lock(iw->pub);
+
+ if (g_onoff == G_WLAN_SET_OFF) {
+ dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON);
+
+#if defined(BCMLXSDMMC)
+ sdioh_start(NULL, 0);
+#endif
+
+ ret = dhd_dev_reset(dev, 0);
+
+ if (ret == BCME_OK) {
+#if defined(BCMLXSDMMC)
+ sdioh_start(NULL, 1);
+#endif
+ dhd_dev_init_ioctl(dev);
+ g_onoff = G_WLAN_SET_ON;
+ }
+ }
+ WL_TRACE(("Exited %s \n", __FUNCTION__));
+
+ dhd_os_start_unlock(iw->pub);
+ return ret;
+}
+
+
+static int
+wl_iw_control_wl_off(
+ struct net_device *dev,
+ struct iw_request_info *info
+)
+{
+ int ret = 0;
+ wl_iw_t *iw;
+
+ WL_TRACE(("Enter %s\n", __FUNCTION__));
+
+ if (!dev) {
+ WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+ return -1;
+ }
+
+ iw = *(wl_iw_t **)netdev_priv(dev);
+ if (!iw) {
+ WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+ return -1;
+ }
+ dhd_os_start_lock(iw->pub);
+
+#ifdef SOFTAP
+ ap_cfg_running = FALSE;
+#endif
+
+ if (g_onoff == G_WLAN_SET_ON) {
+ g_onoff = G_WLAN_SET_OFF;
+#if defined(WL_IW_USE_ISCAN)
+ g_iscan->iscan_state = ISCAN_STATE_IDLE;
+#endif
+
+ dhd_dev_reset(dev, 1);
+
+#if defined(WL_IW_USE_ISCAN)
+#if !defined(CSCAN)
+ wl_iw_free_ss_cache();
+ wl_iw_run_ss_cache_timer(0);
+
+ g_ss_cache_ctrl.m_link_down = 1;
+#endif
+ memset(g_scan, 0, G_SCAN_RESULTS);
+ g_scan_specified_ssid = 0;
+#if defined(CONFIG_FIRST_SCAN)
+ g_first_broadcast_scan = BROADCAST_SCAN_FIRST_IDLE;
+ g_first_counter_scans = 0;
+#endif
+#endif
+
+#if defined(BCMLXSDMMC)
+ sdioh_stop(NULL);
+#endif
+
+ net_os_set_dtim_skip(dev, 0);
+
+ dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
+
+ wl_iw_send_priv_event(dev, "STOP");
+ }
+
+ dhd_os_start_unlock(iw->pub);
+
+ WL_TRACE(("Exited %s\n", __FUNCTION__));
+
+ return ret;
+}
+
+static int
+wl_iw_control_wl_on(
+ struct net_device *dev,
+ struct iw_request_info *info
+)
+{
+ int ret = 0;
+
+ WL_TRACE(("Enter %s \n", __FUNCTION__));
+
+ if ((ret = wl_control_wl_start(dev)) != BCME_OK) {
+ WL_ERROR(("%s failed first attemp\n", __FUNCTION__));
+ dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
+ if ((ret = wl_control_wl_start(dev)) != BCME_OK) {
+ WL_ERROR(("%s failed second attemp\n", __FUNCTION__));
+ net_os_send_hang_message(dev);
+ return ret;
+ }
+ }
+
+ wl_iw_send_priv_event(dev, "START");
+
+#ifdef SOFTAP
+ if (!ap_fw_loaded) {
+ wl_iw_iscan_set_scan_broadcast_prep(dev, 0);
+ }
+#else
+ wl_iw_iscan_set_scan_broadcast_prep(dev, 0);
+#endif
+
+ WL_TRACE(("Exited %s \n", __FUNCTION__));
+
+ return ret;
+}
+
+#ifdef SOFTAP
+static struct ap_profile my_ap;
+static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap);
+static int get_assoc_sta_list(struct net_device *dev, char *buf, int len);
+static int set_ap_mac_list(struct net_device *dev, void *buf);
+
+#define PTYPE_STRING 0
+#define PTYPE_INTDEC 1
+#define PTYPE_INTHEX 2
+#define PTYPE_STR_HEX 3
+int get_parmeter_from_string(
+ char **str_ptr, const char *token, int param_type, void *dst, int param_max_len);
+
+#endif
+
+int hex2num(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+
+int hex2byte(const char *hex)
+{
+ int a, b;
+ a = hex2num(*hex++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*hex++);
+ if (b < 0)
+ return -1;
+ return (a << 4) | b;
+}
+
+
+
+int hstr_2_buf(const char *txt, u8 *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ int a, b;
+
+ a = hex2num(*txt++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*txt++);
+ if (b < 0)
+ return -1;
+ *buf++ = (a << 4) | b;
+ }
+
+ return 0;
+}
+
+#ifdef SOFTAP
+int init_ap_profile_from_string(char *param_str, struct ap_profile *ap_cfg)
+{
+ char *str_ptr = param_str;
+ char sub_cmd[16];
+ int ret = 0;
+
+ memset(sub_cmd, 0, sizeof(sub_cmd));
+ memset(ap_cfg, 0, sizeof(struct ap_profile));
+
+ if (get_parmeter_from_string(&str_ptr, "ASCII_CMD=",
+ PTYPE_STRING, sub_cmd, SSID_LEN) != 0) {
+ return -1;
+ }
+ if (strncmp(sub_cmd, "AP_CFG", 6)) {
+ WL_ERROR(("ERROR: sub_cmd:%s != 'AP_CFG'!\n", sub_cmd));
+ return -1;
+ }
+
+ ret = get_parmeter_from_string(&str_ptr, "SSID=", PTYPE_STRING, ap_cfg->ssid, SSID_LEN);
+
+ ret |= get_parmeter_from_string(&str_ptr, "SEC=", PTYPE_STRING, ap_cfg->sec, SEC_LEN);
+
+ ret |= get_parmeter_from_string(&str_ptr, "KEY=", PTYPE_STRING, ap_cfg->key, KEY_LEN);
+
+ ret |= get_parmeter_from_string(&str_ptr, "CHANNEL=", PTYPE_INTDEC, &ap_cfg->channel, 5);
+
+ get_parmeter_from_string(&str_ptr, "PREAMBLE=", PTYPE_INTDEC, &ap_cfg->preamble, 5);
+
+ get_parmeter_from_string(&str_ptr, "MAX_SCB=", PTYPE_INTDEC, &ap_cfg->max_scb, 5);
+
+ get_parmeter_from_string(&str_ptr, "HIDDEN=", PTYPE_INTDEC, &ap_cfg->closednet, 5);
+
+ get_parmeter_from_string(&str_ptr, "COUNTRY=", PTYPE_STRING, &ap_cfg->country_code, 3);
+
+ return ret;
+}
+#endif
+
+
+#ifdef SOFTAP
+static int iwpriv_set_ap_config(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *ext)
+{
+ int res = 0;
+ char *extra = NULL;
+ struct ap_profile *ap_cfg = &my_ap;
+
+ WL_TRACE(("%s: info->cmd:%x, info->flags:%x, u.data:%p, u.len:%d\n",
+ __FUNCTION__,
+ info->cmd, info->flags,
+ wrqu->data.pointer, wrqu->data.length));
+
+ if (wrqu->data.length != 0) {
+
+ char *str_ptr;
+
+ if (!(extra = kmalloc(wrqu->data.length+1, GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) {
+ kfree(extra);
+ return -EFAULT;
+ }
+
+ extra[wrqu->data.length] = 0;
+ WL_SOFTAP((" Got str param in iw_point:\n %s\n", extra));
+
+ memset(ap_cfg, 0, sizeof(struct ap_profile));
+
+ str_ptr = extra;
+
+ if ((res = init_ap_profile_from_string(extra, ap_cfg)) < 0) {
+ WL_ERROR(("%s failed to parse %d\n", __FUNCTION__, res));
+ kfree(extra);
+ return -1;
+ }
+
+ } else {
+ WL_ERROR(("IWPRIV argument len = 0 \n"));
+ return -1;
+ }
+
+ if ((res = set_ap_cfg(dev, ap_cfg)) < 0)
+ WL_ERROR(("%s failed to set_ap_cfg %d\n", __FUNCTION__, res));
+
+ kfree(extra);
+
+ return res;
+}
+#endif
+
+
+#ifdef SOFTAP
+static int iwpriv_get_assoc_list(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *p_iwrq,
+ char *extra)
+{
+ int i, ret = 0;
+ char mac_buf[256];
+ struct maclist *sta_maclist = (struct maclist *)mac_buf;
+
+ char mac_lst[384];
+ char *p_mac_str;
+ char *p_mac_str_end;
+
+ if ((!dev) || (!extra)) {
+ return -EINVAL;
+ }
+
+ net_os_wake_lock(dev);
+
+ WL_TRACE(("\n %s: IWPRIV IOCTL: cmd:%hx, flags:%hx, extra:%p, iwp.len:%d, \
+ iwp.len:%p, iwp.flags:%x \n", __FUNCTION__, info->cmd, info->flags, \
+ extra, p_iwrq->data.length, p_iwrq->data.pointer, p_iwrq->data.flags));
+
+ memset(sta_maclist, 0, sizeof(mac_buf));
+
+ sta_maclist->count = 8;
+
+ WL_SOFTAP(("%s: net device:%s, buf_sz:%d\n",
+ __FUNCTION__, dev->name, sizeof(mac_buf)));
+
+ if ((ret = get_assoc_sta_list(dev, mac_buf, sizeof(mac_buf))) < 0) {
+ WL_ERROR(("%s: sta list ioctl error:%d\n",
+ __FUNCTION__, ret));
+ goto func_exit;
+ }
+
+ WL_SOFTAP(("%s: got %d stations\n", __FUNCTION__,
+ sta_maclist->count));
+
+ memset(mac_lst, 0, sizeof(mac_lst));
+ p_mac_str = mac_lst;
+ p_mac_str_end = &mac_lst[sizeof(mac_lst)-1];
+
+ for (i = 0; i < 8; i++) {
+ struct ether_addr *id = &sta_maclist->ea[i];
+ if (!ETHER_ISNULLADDR(id->octet)) {
+ scb_val_t scb_val;
+ int rssi = 0;
+
+ bzero(&scb_val, sizeof(scb_val_t));
+
+ if ((p_mac_str_end - p_mac_str) <= 36) {
+ WL_ERROR(("%s: mac list buf is < 36 for item[%i] item\n",
+ __FUNCTION__, i));
+ break;
+ }
+
+ p_mac_str += snprintf(p_mac_str, MAX_WX_STRING,
+ "\nMac[%d]=%02X:%02X:%02X:%02X:%02X:%02X,", i,
+ id->octet[0], id->octet[1], id->octet[2],
+ id->octet[3], id->octet[4], id->octet[5]);
+
+ bcopy(id->octet, &scb_val.ea, 6);
+ ret = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t));
+ if (ret < 0) {
+ snprintf(p_mac_str, MAX_WX_STRING, "RSSI:ERR");
+ WL_ERROR(("%s: RSSI ioctl error:%d\n",
+ __FUNCTION__, ret));
+ break;
+ }
+
+ rssi = dtoh32(scb_val.val);
+ p_mac_str += snprintf(p_mac_str, MAX_WX_STRING,
+ "RSSI:%d", rssi);
+ }
+ }
+
+ p_iwrq->data.length = strlen(mac_lst) + 1;
+
+ WL_SOFTAP(("%s: data to user:\n%s\n usr_ptr:%p\n", __FUNCTION__,
+ mac_lst, p_iwrq->data.pointer));
+
+ if (p_iwrq->data.length) {
+ bcopy(mac_lst, extra, p_iwrq->data.length);
+ }
+
+func_exit:
+ net_os_wake_unlock(dev);
+
+ WL_TRACE(("Exited %s \n", __FUNCTION__));
+ return ret;
+}
+#endif
+
+
+#ifdef SOFTAP
+#define MAC_FILT_MAX 8
+static int iwpriv_set_mac_filters(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *ext)
+{
+ int i, ret = -1;
+ char * extra = NULL;
+ int mac_cnt = 0;
+ int mac_mode = 0;
+ struct ether_addr *p_ea;
+ struct mac_list_set mflist_set;
+
+ WL_SOFTAP((">>> Got IWPRIV SET_MAC_FILTER IOCTL: info->cmd:%x, \
+ info->flags:%x, u.data:%p, u.len:%d\n",
+ info->cmd, info->flags,
+ wrqu->data.pointer, wrqu->data.length));
+
+ if (wrqu->data.length != 0) {
+
+ char *str_ptr;
+
+ if (!(extra = kmalloc(wrqu->data.length+1, GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) {
+ kfree(extra);
+ return -EFAULT;
+ }
+
+ extra[wrqu->data.length] = 0;
+ WL_SOFTAP((" Got parameter string in iw_point:\n %s \n", extra));
+
+ memset(&mflist_set, 0, sizeof(mflist_set));
+
+ str_ptr = extra;
+
+ if (get_parmeter_from_string(&str_ptr, "MAC_MODE=",
+ PTYPE_INTDEC, &mac_mode, 4) != 0) {
+ WL_ERROR(("ERROR: 'MAC_MODE=' token is missing\n"));
+ goto exit_proc;
+ }
+
+ p_ea = &mflist_set.mac_list.ea[0];
+
+ if (get_parmeter_from_string(&str_ptr, "MAC_CNT=",
+ PTYPE_INTDEC, &mac_cnt, 4) != 0) {
+ WL_ERROR(("ERROR: 'MAC_CNT=' token param is missing \n"));
+ goto exit_proc;
+ }
+
+ if (mac_cnt > MAC_FILT_MAX) {
+ WL_ERROR(("ERROR: number of MAC filters > MAX\n"));
+ goto exit_proc;
+ }
+
+ for (i=0; i < mac_cnt; i++)
+ if (get_parmeter_from_string(&str_ptr, "MAC=",
+ PTYPE_STR_HEX, &p_ea[i], 12) != 0) {
+ WL_ERROR(("ERROR: MAC_filter[%d] is missing !\n", i));
+ goto exit_proc;
+ }
+
+ WL_SOFTAP(("MAC_MODE=:%d, MAC_CNT=%d, MACs:..\n", mac_mode, mac_cnt));
+ for (i = 0; i < mac_cnt; i++) {
+ WL_SOFTAP(("mac_filt[%d]:", i));
+ print_buf(&p_ea[i], 6, 0);
+ }
+
+ mflist_set.mode = mac_mode;
+ mflist_set.mac_list.count = mac_cnt;
+ set_ap_mac_list(dev, &mflist_set);
+
+ wrqu->data.pointer = NULL;
+ wrqu->data.length = 0;
+ ret = 0;
+
+ } else {
+ WL_ERROR(("IWPRIV argument len is 0\n"));
+ return -1;
+ }
+
+ exit_proc:
+ kfree(extra);
+ return ret;
+}
+#endif
+
+
+#ifdef SOFTAP
+static int iwpriv_set_ap_sta_disassoc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *ext)
+{
+ int res = 0;
+ char sta_mac[6] = {0, 0, 0, 0, 0, 0};
+ char cmd_buf[256];
+ char *str_ptr = cmd_buf;
+
+ WL_SOFTAP((">>%s called\n args: info->cmd:%x,"
+ " info->flags:%x, u.data.p:%p, u.data.len:%d\n",
+ __FUNCTION__, info->cmd, info->flags,
+ wrqu->data.pointer, wrqu->data.length));
+
+ if (wrqu->data.length != 0) {
+
+ if (copy_from_user(cmd_buf, wrqu->data.pointer, wrqu->data.length)) {
+ return -EFAULT;
+ }
+
+ if (get_parmeter_from_string(&str_ptr,
+ "MAC=", PTYPE_STR_HEX, sta_mac, 12) == 0) {
+ res = wl_iw_softap_deassoc_stations(dev, sta_mac);
+ } else {
+ WL_ERROR(("ERROR: STA_MAC= token not found\n"));
+ }
+ }
+
+ return res;
+}
+#endif
+
+#endif
+
+
+#if WIRELESS_EXT < 13
+struct iw_request_info
+{
+ __u16 cmd;
+ __u16 flags;
+};
+
+typedef int (*iw_handler)(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu,
+ char *extra);
+#endif
+
+static int
+wl_iw_config_commit(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ void *zwrq,
+ char *extra
+)
+{
+ wlc_ssid_t ssid;
+ int error;
+ struct sockaddr bssid;
+
+ WL_TRACE(("%s: SIOCSIWCOMMIT\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid))))
+ return error;
+
+ ssid.SSID_len = dtoh32(ssid.SSID_len);
+
+ if (!ssid.SSID_len)
+ return 0;
+
+ bzero(&bssid, sizeof(struct sockaddr));
+ if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, &bssid, ETHER_ADDR_LEN))) {
+ WL_ERROR(("%s: WLC_REASSOC to %s failed \n", __FUNCTION__, ssid.SSID));
+ return error;
+ }
+
+ return 0;
+}
+
+static int
+wl_iw_get_name(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ char *cwrq,
+ char *extra
+)
+{
+ WL_TRACE(("%s: SIOCGIWNAME\n", dev->name));
+
+ strcpy(cwrq, "IEEE 802.11-DS");
+
+ return 0;
+}
+
+static int
+wl_iw_set_freq(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *fwrq,
+ char *extra
+)
+{
+ int error, chan;
+ uint sf = 0;
+
+ WL_TRACE(("%s %s: SIOCSIWFREQ\n", __FUNCTION__, dev->name));
+
+#if defined(SOFTAP)
+ if (ap_cfg_running) {
+ WL_TRACE(("%s:>> not executed, 'SOFT_AP is active' \n", __FUNCTION__));
+ return 0;
+ }
+#endif
+
+
+ if (fwrq->e == 0 && fwrq->m < MAXCHANNEL) {
+ chan = fwrq->m;
+ }
+
+
+ else {
+
+ if (fwrq->e >= 6) {
+ fwrq->e -= 6;
+ while (fwrq->e--)
+ fwrq->m *= 10;
+ } else if (fwrq->e < 6) {
+ while (fwrq->e++ < 6)
+ fwrq->m /= 10;
+ }
+
+ if (fwrq->m > 4000 && fwrq->m < 5000)
+ sf = WF_CHAN_FACTOR_4_G;
+
+ chan = wf_mhz2channel(fwrq->m, sf);
+ }
+ chan = htod32(chan);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan))))
+ return error;
+
+ g_wl_iw_params.target_channel = chan;
+
+ return -EINPROGRESS;
+}
+
+static int
+wl_iw_get_freq(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *fwrq,
+ char *extra
+)
+{
+ channel_info_t ci;
+ int error;
+
+ WL_TRACE(("%s: SIOCGIWFREQ\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci))))
+ return error;
+
+ fwrq->m = dtoh32(ci.hw_channel);
+ fwrq->e = dtoh32(0);
+ return 0;
+}
+
+static int
+wl_iw_set_mode(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *uwrq,
+ char *extra
+)
+{
+ int infra = 0, ap = 0, error = 0;
+
+ WL_TRACE(("%s: SIOCSIWMODE\n", dev->name));
+
+ switch (*uwrq) {
+ case IW_MODE_MASTER:
+ infra = ap = 1;
+ break;
+ case IW_MODE_ADHOC:
+ case IW_MODE_AUTO:
+ break;
+ case IW_MODE_INFRA:
+ infra = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ infra = htod32(infra);
+ ap = htod32(ap);
+
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra))) ||
+ (error = dev_wlc_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap))))
+ return error;
+
+
+ return -EINPROGRESS;
+}
+
+static int
+wl_iw_get_mode(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *uwrq,
+ char *extra
+)
+{
+ int error, infra = 0, ap = 0;
+
+ WL_TRACE(("%s: SIOCGIWMODE\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra))) ||
+ (error = dev_wlc_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap))))
+ return error;
+
+ infra = dtoh32(infra);
+ ap = dtoh32(ap);
+ *uwrq = infra ? ap ? IW_MODE_MASTER : IW_MODE_INFRA : IW_MODE_ADHOC;
+
+ return 0;
+}
+
+static int
+wl_iw_get_range(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ struct iw_range *range = (struct iw_range *) extra;
+ wl_uint32_list_t *list;
+ wl_rateset_t rateset;
+ int8 *channels;
+ int error, i, k;
+ uint sf, ch;
+
+ int phytype;
+ int bw_cap = 0, sgi_tx = 0, nmode = 0;
+ channel_info_t ci;
+ uint8 nrate_list2copy = 0;
+ uint16 nrate_list[4][8] = { {13, 26, 39, 52, 78, 104, 117, 130},
+ {14, 29, 43, 58, 87, 116, 130, 144},
+ {27, 54, 81, 108, 162, 216, 243, 270},
+ {30, 60, 90, 120, 180, 240, 270, 300}};
+
+ WL_TRACE(("%s: SIOCGIWRANGE\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ channels = kmalloc((MAXCHANNEL+1)*4, GFP_KERNEL);
+ if (!channels) {
+ WL_ERROR(("Could not alloc channels\n"));
+ return -ENOMEM;
+ }
+ list = (wl_uint32_list_t *)channels;
+
+ dwrq->length = sizeof(struct iw_range);
+ memset(range, 0, sizeof(range));
+
+ range->min_nwid = range->max_nwid = 0;
+
+ list->count = htod32(MAXCHANNEL);
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_VALID_CHANNELS, channels, (MAXCHANNEL+1)*4))) {
+ kfree(channels);
+ return error;
+ }
+ for (i = 0; i < dtoh32(list->count) && i < IW_MAX_FREQUENCIES; i++) {
+ range->freq[i].i = dtoh32(list->element[i]);
+
+ ch = dtoh32(list->element[i]);
+ if (ch <= CH_MAX_2G_CHANNEL)
+ sf = WF_CHAN_FACTOR_2_4_G;
+ else
+ sf = WF_CHAN_FACTOR_5_G;
+
+ range->freq[i].m = wf_channel2mhz(ch, sf);
+ range->freq[i].e = 6;
+ }
+ range->num_frequency = range->num_channels = i;
+
+ range->max_qual.qual = 5;
+
+ range->max_qual.level = 0x100 - 200;
+
+ range->max_qual.noise = 0x100 - 200;
+
+ range->sensitivity = 65535;
+
+#if WIRELESS_EXT > 11
+
+ range->avg_qual.qual = 3;
+
+ range->avg_qual.level = 0x100 + WL_IW_RSSI_GOOD;
+
+ range->avg_qual.noise = 0x100 - 75;
+#endif
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset)))) {
+ kfree(channels);
+ return error;
+ }
+ rateset.count = dtoh32(rateset.count);
+ range->num_bitrates = rateset.count;
+ for (i = 0; i < rateset.count && i < IW_MAX_BITRATES; i++)
+ range->bitrate[i] = (rateset.rates[i]& 0x7f) * 500000;
+ dev_wlc_intvar_get(dev, "nmode", &nmode);
+ dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
+
+ if (nmode == 1 && phytype == WLC_PHY_TYPE_SSN) {
+ dev_wlc_intvar_get(dev, "mimo_bw_cap", &bw_cap);
+ dev_wlc_intvar_get(dev, "sgi_tx", &sgi_tx);
+ dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t));
+ ci.hw_channel = dtoh32(ci.hw_channel);
+
+ if (bw_cap == 0 ||
+ (bw_cap == 2 && ci.hw_channel <= 14)) {
+ if (sgi_tx == 0)
+ nrate_list2copy = 0;
+ else
+ nrate_list2copy = 1;
+ }
+ if (bw_cap == 1 ||
+ (bw_cap == 2 && ci.hw_channel >= 36)) {
+ if (sgi_tx == 0)
+ nrate_list2copy = 2;
+ else
+ nrate_list2copy = 3;
+ }
+ range->num_bitrates += 8;
+ for (k = 0; i < range->num_bitrates; k++, i++) {
+
+ range->bitrate[i] = (nrate_list[nrate_list2copy][k]) * 500000;
+ }
+ }
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &i, sizeof(i)))) {
+ kfree(channels);
+ return error;
+ }
+ i = dtoh32(i);
+ if (i == WLC_PHY_TYPE_A)
+ range->throughput = 24000000;
+ else
+ range->throughput = 1500000;
+
+ range->min_rts = 0;
+ range->max_rts = 2347;
+ range->min_frag = 256;
+ range->max_frag = 2346;
+
+ range->max_encoding_tokens = DOT11_MAX_DEFAULT_KEYS;
+ range->num_encoding_sizes = 4;
+ range->encoding_size[0] = WEP1_KEY_SIZE;
+ range->encoding_size[1] = WEP128_KEY_SIZE;
+#if WIRELESS_EXT > 17
+ range->encoding_size[2] = TKIP_KEY_SIZE;
+#else
+ range->encoding_size[2] = 0;
+#endif
+ range->encoding_size[3] = AES_KEY_SIZE;
+
+ range->min_pmp = 0;
+ range->max_pmp = 0;
+ range->min_pmt = 0;
+ range->max_pmt = 0;
+ range->pmp_flags = 0;
+ range->pm_capa = 0;
+
+ range->num_txpower = 2;
+ range->txpower[0] = 1;
+ range->txpower[1] = 255;
+ range->txpower_capa = IW_TXPOW_MWATT;
+
+#if WIRELESS_EXT > 10
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 19;
+
+ range->retry_capa = IW_RETRY_LIMIT;
+ range->retry_flags = IW_RETRY_LIMIT;
+ range->r_time_flags = 0;
+
+ range->min_retry = 1;
+ range->max_retry = 255;
+
+ range->min_r_time = 0;
+ range->max_r_time = 0;
+#endif
+
+#if WIRELESS_EXT > 17
+ range->enc_capa = IW_ENC_CAPA_WPA;
+ range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP;
+ range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP;
+#ifdef BCMWPA2
+ range->enc_capa |= IW_ENC_CAPA_WPA2;
+#endif
+
+ IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
+
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
+ IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
+ IW_EVENT_CAPA_SET(range->event_capa, IWEVMICHAELMICFAILURE);
+#ifdef BCMWPA2
+ IW_EVENT_CAPA_SET(range->event_capa, IWEVPMKIDCAND);
+#endif
+#endif
+
+ kfree(channels);
+
+ return 0;
+}
+
+static int
+rssi_to_qual(int rssi)
+{
+ if (rssi <= WL_IW_RSSI_NO_SIGNAL)
+ return 0;
+ else if (rssi <= WL_IW_RSSI_VERY_LOW)
+ return 1;
+ else if (rssi <= WL_IW_RSSI_LOW)
+ return 2;
+ else if (rssi <= WL_IW_RSSI_GOOD)
+ return 3;
+ else if (rssi <= WL_IW_RSSI_VERY_GOOD)
+ return 4;
+ else
+ return 5;
+}
+
+static int
+wl_iw_set_spy(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+ struct sockaddr *addr = (struct sockaddr *) extra;
+ int i;
+
+ WL_TRACE(("%s: SIOCSIWSPY\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ iw->spy_num = MIN(ARRAYSIZE(iw->spy_addr), dwrq->length);
+ for (i = 0; i < iw->spy_num; i++)
+ memcpy(&iw->spy_addr[i], addr[i].sa_data, ETHER_ADDR_LEN);
+ memset(iw->spy_qual, 0, sizeof(iw->spy_qual));
+
+ return 0;
+}
+
+static int
+wl_iw_get_spy(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+ struct sockaddr *addr = (struct sockaddr *) extra;
+ struct iw_quality *qual = (struct iw_quality *) &addr[iw->spy_num];
+ int i;
+
+ WL_TRACE(("%s: SIOCGIWSPY\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ dwrq->length = iw->spy_num;
+ for (i = 0; i < iw->spy_num; i++) {
+ memcpy(addr[i].sa_data, &iw->spy_addr[i], ETHER_ADDR_LEN);
+ addr[i].sa_family = AF_UNIX;
+ memcpy(&qual[i], &iw->spy_qual[i], sizeof(struct iw_quality));
+ iw->spy_qual[i].updated = 0;
+ }
+
+ return 0;
+}
+
+
+static int
+wl_iw_ch_to_chanspec(int ch, wl_join_params_t *join_params, int *join_params_size)
+{
+ chanspec_t chanspec = 0;
+
+ if (ch != 0) {
+
+ join_params->params.chanspec_num = 1;
+ join_params->params.chanspec_list[0] = ch;
+
+ if (join_params->params.chanspec_list[0])
+ chanspec |= WL_CHANSPEC_BAND_2G;
+ else
+ chanspec |= WL_CHANSPEC_BAND_5G;
+
+ chanspec |= WL_CHANSPEC_BW_20;
+ chanspec |= WL_CHANSPEC_CTL_SB_NONE;
+
+ *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE +
+ join_params->params.chanspec_num * sizeof(chanspec_t);
+
+ join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK;
+ join_params->params.chanspec_list[0] |= chanspec;
+ join_params->params.chanspec_list[0] =
+ htodchanspec(join_params->params.chanspec_list[0]);
+
+ join_params->params.chanspec_num = htod32(join_params->params.chanspec_num);
+
+ WL_TRACE(("%s join_params->params.chanspec_list[0]= %X\n", \
+ __FUNCTION__, join_params->params.chanspec_list[0]));
+ }
+ return 1;
+}
+
+static int
+wl_iw_set_wap(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *awrq,
+ char *extra
+)
+{
+ int error = -EINVAL;
+ wl_join_params_t join_params;
+ int join_params_size;
+
+ WL_TRACE(("%s: SIOCSIWAP\n", dev->name));
+
+ if (awrq->sa_family != ARPHRD_ETHER) {
+ WL_ERROR(("Invalid Header...sa_family\n"));
+ return -EINVAL;
+ }
+
+
+ if (ETHER_ISBCAST(awrq->sa_data) || ETHER_ISNULLADDR(awrq->sa_data)) {
+ scb_val_t scbval;
+
+ bzero(&scbval, sizeof(scb_val_t));
+
+ (void) dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t));
+ return 0;
+ }
+
+
+
+ memset(&join_params, 0, sizeof(join_params));
+ join_params_size = sizeof(join_params.ssid);
+
+ memcpy(join_params.ssid.SSID, g_ssid.SSID, g_ssid.SSID_len);
+ join_params.ssid.SSID_len = htod32(g_ssid.SSID_len);
+ memcpy(&join_params.params.bssid, awrq->sa_data, ETHER_ADDR_LEN);
+
+ WL_ASSOC(("%s target_channel=%d\n", __FUNCTION__, g_wl_iw_params.target_channel));
+ wl_iw_ch_to_chanspec(g_wl_iw_params.target_channel, &join_params, &join_params_size);
+
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size))) {
+ WL_ERROR(("%s Invalid ioctl data=%d\n", __FUNCTION__, error));
+ return error;
+ }
+
+ if (g_ssid.SSID_len) {
+ WL_ASSOC(("%s: join SSID=%s BSSID="MACSTR" ch=%d\n", __FUNCTION__, \
+ g_ssid.SSID, MAC2STR((u8 *)awrq->sa_data), \
+ g_wl_iw_params.target_channel));
+ }
+
+
+ memset(&g_ssid, 0, sizeof(g_ssid));
+ return 0;
+}
+
+static int
+wl_iw_get_wap(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *awrq,
+ char *extra
+)
+{
+ WL_TRACE(("%s: SIOCGIWAP\n", dev->name));
+
+ awrq->sa_family = ARPHRD_ETHER;
+ memset(awrq->sa_data, 0, ETHER_ADDR_LEN);
+
+
+ (void) dev_wlc_ioctl(dev, WLC_GET_BSSID, awrq->sa_data, ETHER_ADDR_LEN);
+
+ return 0;
+}
+
+#if WIRELESS_EXT > 17
+static int
+wl_iw_mlme(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *awrq,
+ char *extra
+)
+{
+ struct iw_mlme *mlme;
+ scb_val_t scbval;
+ int error = -EINVAL;
+
+ WL_TRACE(("%s: SIOCSIWMLME DISASSOC/DEAUTH\n", dev->name));
+
+ mlme = (struct iw_mlme *)extra;
+ if (mlme == NULL) {
+ WL_ERROR(("Invalid ioctl data.\n"));
+ return error;
+ }
+
+ scbval.val = mlme->reason_code;
+ bcopy(&mlme->addr.sa_data, &scbval.ea, ETHER_ADDR_LEN);
+
+ if (mlme->cmd == IW_MLME_DISASSOC) {
+ scbval.val = htod32(scbval.val);
+ error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t));
+ }
+ else if (mlme->cmd == IW_MLME_DEAUTH) {
+ scbval.val = htod32(scbval.val);
+ error = dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval,
+ sizeof(scb_val_t));
+ }
+ else {
+ WL_ERROR(("Invalid ioctl data.\n"));
+ return error;
+ }
+
+ return error;
+}
+#endif
+
+#ifndef WL_IW_USE_ISCAN
+static int
+wl_iw_get_aplist(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_scan_results_t *list;
+ struct sockaddr *addr = (struct sockaddr *) extra;
+ struct iw_quality qual[IW_MAX_AP];
+ wl_bss_info_t *bi = NULL;
+ int error, i;
+ uint buflen = dwrq->length;
+
+ WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ list = kmalloc(buflen, GFP_KERNEL);
+ if (!list)
+ return -ENOMEM;
+ memset(list, 0, buflen);
+ list->buflen = htod32(buflen);
+ if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) {
+ WL_ERROR(("%d: Scan results error %d\n", __LINE__, error));
+ kfree(list);
+ return error;
+ }
+ list->buflen = dtoh32(list->buflen);
+ list->version = dtoh32(list->version);
+ list->count = dtoh32(list->count);
+ if (list->version != WL_BSS_INFO_VERSION) {
+ WL_ERROR(("%s: list->version %d != WL_BSS_INFO_VERSION\n", \
+ __FUNCTION__, list->version));
+ kfree(list);
+ return -EINVAL;
+ }
+
+ for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) {
+ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
+
+ if ((dtoh32(bi->length) > buflen) ||
+ (((uintptr)bi + dtoh32(bi->length)) > ((uintptr)list + buflen))) {
+ WL_ERROR(("%s: Scan results out of bounds: %u\n",__FUNCTION__,dtoh32(bi->length)));
+ kfree(list);
+ return -E2BIG;
+ }
+
+ if (!(dtoh16(bi->capability) & DOT11_CAP_ESS))
+ continue;
+
+ memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN);
+ addr[dwrq->length].sa_family = ARPHRD_ETHER;
+ qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI));
+ qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI);
+ qual[dwrq->length].noise = 0x100 + bi->phy_noise;
+
+#if WIRELESS_EXT > 18
+ qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+#else
+ qual[dwrq->length].updated = 7;
+#endif
+
+ dwrq->length++;
+ }
+
+ kfree(list);
+
+ if (dwrq->length) {
+ memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length);
+
+ dwrq->flags = 1;
+ }
+ return 0;
+}
+#endif
+
+#ifdef WL_IW_USE_ISCAN
+static int
+wl_iw_iscan_get_aplist(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_scan_results_t *list;
+ iscan_buf_t * buf;
+ iscan_info_t *iscan = g_iscan;
+
+ struct sockaddr *addr = (struct sockaddr *) extra;
+ struct iw_quality qual[IW_MAX_AP];
+ wl_bss_info_t *bi = NULL;
+ int i;
+
+ WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ if ((!iscan) || (iscan->sysioc_pid < 0)) {
+ WL_ERROR(("%s error\n", __FUNCTION__));
+ return 0;
+ }
+
+ buf = iscan->list_hdr;
+
+ while (buf) {
+ list = &((wl_iscan_results_t*)buf->iscan_buf)->results;
+ if (list->version != WL_BSS_INFO_VERSION) {
+ WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n", \
+ __FUNCTION__, list->version));
+ return -EINVAL;
+ }
+
+ bi = NULL;
+ for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) {
+ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length))
+ : list->bss_info;
+
+ if ((dtoh32(bi->length) > WLC_IW_ISCAN_MAXLEN) ||
+ (((uintptr)bi + dtoh32(bi->length)) > ((uintptr)list + WLC_IW_ISCAN_MAXLEN))) {
+ WL_ERROR(("%s: Scan results out of bounds: %u\n",__FUNCTION__,dtoh32(bi->length)));
+ return -E2BIG;
+ }
+
+ if (!(dtoh16(bi->capability) & DOT11_CAP_ESS))
+ continue;
+
+ memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN);
+ addr[dwrq->length].sa_family = ARPHRD_ETHER;
+ qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI));
+ qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI);
+ qual[dwrq->length].noise = 0x100 + bi->phy_noise;
+
+#if WIRELESS_EXT > 18
+ qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+#else
+ qual[dwrq->length].updated = 7;
+#endif
+
+ dwrq->length++;
+ }
+ buf = buf->next;
+ }
+ if (dwrq->length) {
+ memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length);
+
+ dwrq->flags = 1;
+ }
+ return 0;
+}
+
+static int
+wl_iw_iscan_prep(wl_scan_params_t *params, wlc_ssid_t *ssid)
+{
+ int err = 0;
+
+ memcpy(&params->bssid, &ether_bcast, ETHER_ADDR_LEN);
+ params->bss_type = DOT11_BSSTYPE_ANY;
+ params->scan_type = 0;
+ params->nprobes = -1;
+ params->active_time = -1;
+ params->passive_time = -1;
+ params->home_time = -1;
+ params->channel_num = 0;
+#if defined(CONFIG_FIRST_SCAN)
+ if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED)
+ params->passive_time = 30;
+#endif
+ params->nprobes = htod32(params->nprobes);
+ params->active_time = htod32(params->active_time);
+ params->passive_time = htod32(params->passive_time);
+ params->home_time = htod32(params->home_time);
+ if (ssid && ssid->SSID_len)
+ memcpy(&params->ssid, ssid, sizeof(wlc_ssid_t));
+
+ return err;
+}
+
+static int
+wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action)
+{
+ int err = 0;
+
+ iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION);
+ iscan->iscan_ex_params_p->action = htod16(action);
+ iscan->iscan_ex_params_p->scan_duration = htod16(0);
+
+ WL_SCAN(("%s : nprobes=%d\n", __FUNCTION__, iscan->iscan_ex_params_p->params.nprobes));
+ WL_SCAN(("active_time=%d\n", iscan->iscan_ex_params_p->params.active_time));
+ WL_SCAN(("passive_time=%d\n", iscan->iscan_ex_params_p->params.passive_time));
+ WL_SCAN(("home_time=%d\n", iscan->iscan_ex_params_p->params.home_time));
+ WL_SCAN(("scan_type=%d\n", iscan->iscan_ex_params_p->params.scan_type));
+ WL_SCAN(("bss_type=%d\n", iscan->iscan_ex_params_p->params.bss_type));
+
+ if ((err = dev_iw_iovar_setbuf(iscan->dev, "iscan", iscan->iscan_ex_params_p, \
+ iscan->iscan_ex_param_size, iscan->ioctlbuf, sizeof(iscan->ioctlbuf)))) {
+ WL_ERROR(("Set ISCAN for %s failed with %d\n", __FUNCTION__, err));
+ err = -1;
+ }
+
+ return err;
+}
+
+static void
+wl_iw_timerfunc(ulong data)
+{
+ iscan_info_t *iscan = (iscan_info_t *)data;
+ if (iscan) {
+ iscan->timer_on = 0;
+ if (iscan->iscan_state != ISCAN_STATE_IDLE) {
+ WL_SCAN(("timer trigger\n"));
+ up(&iscan->sysioc_sem);
+ }
+ }
+}
+static void wl_iw_set_event_mask(struct net_device *dev)
+{
+ char eventmask[WL_EVENTING_MASK_LEN];
+ char iovbuf[WL_EVENTING_MASK_LEN + 12];
+
+ dev_iw_iovar_getbuf(dev, "event_msgs", "", 0, iovbuf, sizeof(iovbuf));
+ bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN);
+ setbit(eventmask, WLC_E_SCAN_COMPLETE);
+ dev_iw_iovar_setbuf(dev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN,
+ iovbuf, sizeof(iovbuf));
+}
+
+static uint32
+wl_iw_iscan_get(iscan_info_t *iscan)
+{
+ iscan_buf_t * buf;
+ iscan_buf_t * ptr;
+ wl_iscan_results_t * list_buf;
+ wl_iscan_results_t list;
+ wl_scan_results_t *results;
+ uint32 status;
+ int res;
+
+ mutex_lock(&wl_cache_lock);
+ if (iscan->list_cur) {
+ buf = iscan->list_cur;
+ iscan->list_cur = buf->next;
+ }
+ else {
+ buf = kmalloc(sizeof(iscan_buf_t), GFP_KERNEL);
+ if (!buf) {
+ WL_ERROR(("%s can't alloc iscan_buf_t : going to abort currect iscan\n", \
+ __FUNCTION__));
+ mutex_unlock(&wl_cache_lock);
+ return WL_SCAN_RESULTS_NO_MEM;
+ }
+ buf->next = NULL;
+ if (!iscan->list_hdr)
+ iscan->list_hdr = buf;
+ else {
+ ptr = iscan->list_hdr;
+ while (ptr->next) {
+ ptr = ptr->next;
+ }
+ ptr->next = buf;
+ }
+ }
+ memset(buf->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN);
+ list_buf = (wl_iscan_results_t*)buf->iscan_buf;
+ results = &list_buf->results;
+ results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE;
+ results->version = 0;
+ results->count = 0;
+
+ memset(&list, 0, sizeof(list));
+ list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN);
+ res = dev_iw_iovar_getbuf(
+ iscan->dev,
+ "iscanresults",
+ &list,
+ WL_ISCAN_RESULTS_FIXED_SIZE,
+ buf->iscan_buf,
+ WLC_IW_ISCAN_MAXLEN);
+ if (res == 0) {
+ results->buflen = dtoh32(results->buflen);
+ results->version = dtoh32(results->version);
+ results->count = dtoh32(results->count);
+ WL_SCAN(("results->count = %d\n", results->count));
+
+ WL_SCAN(("results->buflen = %d\n", results->buflen));
+ status = dtoh32(list_buf->status);
+ } else {
+ WL_ERROR(("%s returns error %d\n", __FUNCTION__, res));
+ status = WL_SCAN_RESULTS_NO_MEM;
+ }
+ mutex_unlock(&wl_cache_lock);
+ return status;
+}
+
+static void wl_iw_force_specific_scan(iscan_info_t *iscan)
+{
+ WL_SCAN(("%s force Specific SCAN for %s\n", __FUNCTION__, g_specific_ssid.SSID));
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ rtnl_lock();
+#endif
+ (void) dev_wlc_ioctl(iscan->dev, WLC_SCAN, &g_specific_ssid, sizeof(g_specific_ssid));
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ rtnl_unlock();
+#endif
+}
+
+static void wl_iw_send_scan_complete(iscan_info_t *iscan)
+{
+#ifndef SANDGATE2G
+ union iwreq_data wrqu;
+
+ memset(&wrqu, 0, sizeof(wrqu));
+
+ wireless_send_event(iscan->dev, SIOCGIWSCAN, &wrqu, NULL);
+#if defined(CONFIG_FIRST_SCAN)
+ if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED)
+ g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_READY;
+#endif
+ WL_SCAN(("Send Event ISCAN complete\n"));
+#endif
+}
+
+static int
+_iscan_sysioc_thread(void *data)
+{
+ uint32 status;
+ iscan_info_t *iscan = (iscan_info_t *)data;
+ static bool iscan_pass_abort = FALSE;
+
+ DAEMONIZE("iscan_sysioc");
+
+ status = WL_SCAN_RESULTS_PARTIAL;
+ while (down_interruptible(&iscan->sysioc_sem) == 0) {
+
+ net_os_wake_lock(iscan->dev);
+
+#if defined(SOFTAP)
+ if (ap_cfg_running) {
+ WL_SCAN(("%s skipping SCAN ops in AP mode !!!\n", __FUNCTION__));
+ net_os_wake_unlock(iscan->dev);
+ continue;
+ }
+#endif
+
+ if (iscan->timer_on) {
+ iscan->timer_on = 0;
+ del_timer_sync(&iscan->timer);
+ }
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ rtnl_lock();
+#endif
+ status = wl_iw_iscan_get(iscan);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ rtnl_unlock();
+#endif
+
+ if (g_scan_specified_ssid && (iscan_pass_abort == TRUE)) {
+ WL_SCAN(("%s Get results from specific scan status=%d\n", __FUNCTION__, status));
+ wl_iw_send_scan_complete(iscan);
+ iscan_pass_abort = FALSE;
+ status = -1;
+ }
+
+ switch (status) {
+ case WL_SCAN_RESULTS_PARTIAL:
+ WL_SCAN(("iscanresults incomplete\n"));
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ rtnl_lock();
+#endif
+
+ wl_iw_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ rtnl_unlock();
+#endif
+
+ mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000);
+ iscan->timer_on = 1;
+ break;
+ case WL_SCAN_RESULTS_SUCCESS:
+ WL_SCAN(("iscanresults complete\n"));
+ iscan->iscan_state = ISCAN_STATE_IDLE;
+ wl_iw_send_scan_complete(iscan);
+ break;
+ case WL_SCAN_RESULTS_PENDING:
+ WL_SCAN(("iscanresults pending\n"));
+
+ mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000);
+ iscan->timer_on = 1;
+ break;
+ case WL_SCAN_RESULTS_ABORTED:
+ WL_SCAN(("iscanresults aborted\n"));
+ iscan->iscan_state = ISCAN_STATE_IDLE;
+ if (g_scan_specified_ssid == 0)
+ wl_iw_send_scan_complete(iscan);
+ else {
+ iscan_pass_abort = TRUE;
+ wl_iw_force_specific_scan(iscan);
+ }
+ break;
+ case WL_SCAN_RESULTS_NO_MEM:
+ WL_SCAN(("iscanresults can't alloc memory: skip\n"));
+ iscan->iscan_state = ISCAN_STATE_IDLE;
+ break;
+ default:
+ WL_SCAN(("iscanresults returned unknown status %d\n", status));
+ break;
+ }
+
+ net_os_wake_unlock(iscan->dev);
+ }
+
+ if (iscan->timer_on) {
+ iscan->timer_on = 0;
+ del_timer_sync(&iscan->timer);
+ }
+
+ complete_and_exit(&iscan->sysioc_exited, 0);
+}
+#endif
+
+#if !defined(CSCAN)
+
+static void
+wl_iw_set_ss_cache_timer_flag(void)
+{
+ g_ss_cache_ctrl.m_timer_expired = 1;
+ WL_TRACE(("%s called\n", __FUNCTION__));
+}
+
+static int
+wl_iw_init_ss_cache_ctrl(void)
+{
+ WL_TRACE(("%s :\n", __FUNCTION__));
+ g_ss_cache_ctrl.m_prev_scan_mode = 0;
+ g_ss_cache_ctrl.m_cons_br_scan_cnt = 0;
+ g_ss_cache_ctrl.m_cache_head = NULL;
+ g_ss_cache_ctrl.m_link_down = 0;
+ g_ss_cache_ctrl.m_timer_expired = 0;
+ memset(g_ss_cache_ctrl.m_active_bssid, 0, ETHER_ADDR_LEN);
+
+ g_ss_cache_ctrl.m_timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
+ if (!g_ss_cache_ctrl.m_timer) {
+ return -ENOMEM;
+ }
+ g_ss_cache_ctrl.m_timer->function = (void *)wl_iw_set_ss_cache_timer_flag;
+ init_timer(g_ss_cache_ctrl.m_timer);
+
+ return 0;
+}
+
+
+
+static void
+wl_iw_free_ss_cache(void)
+{
+ wl_iw_ss_cache_t *node, *cur;
+ wl_iw_ss_cache_t **spec_scan_head;
+
+ WL_TRACE(("%s called\n", __FUNCTION__));
+
+ mutex_lock(&wl_cache_lock);
+ spec_scan_head = &g_ss_cache_ctrl.m_cache_head;
+ node = *spec_scan_head;
+
+ for (;node;) {
+ WL_TRACE(("%s : SSID - %s\n", __FUNCTION__, node->bss_info->SSID));
+ cur = node;
+ node = cur->next;
+ kfree(cur);
+ }
+ *spec_scan_head = NULL;
+ mutex_unlock(&wl_cache_lock);
+}
+
+
+
+static int
+wl_iw_run_ss_cache_timer(int kick_off)
+{
+ struct timer_list **timer;
+
+ timer = &g_ss_cache_ctrl.m_timer;
+
+ if (*timer) {
+ if (kick_off) {
+ (*timer)->expires = jiffies + 30000 * HZ / 1000;
+ add_timer(*timer);
+ WL_TRACE(("%s : timer starts \n", __FUNCTION__));
+ } else {
+ del_timer_sync(*timer);
+ WL_TRACE(("%s : timer stops \n", __FUNCTION__));
+ }
+ }
+
+ return 0;
+}
+
+
+void
+wl_iw_release_ss_cache_ctrl(void)
+{
+ WL_TRACE(("%s :\n", __FUNCTION__));
+ wl_iw_free_ss_cache();
+ wl_iw_run_ss_cache_timer(0);
+ if (g_ss_cache_ctrl.m_timer) {
+ kfree(g_ss_cache_ctrl.m_timer);
+ }
+}
+
+
+
+static void
+wl_iw_reset_ss_cache(void)
+{
+ wl_iw_ss_cache_t *node, *prev, *cur;
+ wl_iw_ss_cache_t **spec_scan_head;
+
+ mutex_lock(&wl_cache_lock);
+ spec_scan_head = &g_ss_cache_ctrl.m_cache_head;
+ node = *spec_scan_head;
+ prev = node;
+
+ for (;node;) {
+ WL_TRACE(("%s : node SSID %s \n", __FUNCTION__, node->bss_info->SSID));
+ if (!node->dirty) {
+ cur = node;
+ if (cur == *spec_scan_head) {
+ *spec_scan_head = cur->next;
+ prev = *spec_scan_head;
+ }
+ else {
+ prev->next = cur->next;
+ }
+ node = cur->next;
+
+ WL_TRACE(("%s : Del node : SSID %s\n", __FUNCTION__, cur->bss_info->SSID));
+ kfree(cur);
+ continue;
+ }
+
+ node->dirty = 0;
+ prev = node;
+ node = node->next;
+ }
+ mutex_unlock(&wl_cache_lock);
+}
+
+
+static int
+wl_iw_add_bss_to_ss_cache(wl_scan_results_t *ss_list)
+{
+
+ wl_iw_ss_cache_t *node, *prev, *leaf;
+ wl_iw_ss_cache_t **spec_scan_head;
+ wl_bss_info_t *bi = NULL;
+ int i;
+
+ if (!ss_list->count) {
+ return 0;
+ }
+
+ mutex_lock(&wl_cache_lock);
+ spec_scan_head = &g_ss_cache_ctrl.m_cache_head;
+
+ for (i = 0; i < ss_list->count; i++) {
+
+ node = *spec_scan_head;
+ prev = node;
+
+ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
+
+ WL_TRACE(("%s : find %d with specific SSID %s\n", __FUNCTION__, i, bi->SSID));
+ for (;node;) {
+ if (!memcmp(&node->bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
+
+ WL_TRACE(("dirty marked : SSID %s\n", bi->SSID));
+ node->dirty = 1;
+ break;
+ }
+ prev = node;
+ node = node->next;
+ }
+
+ if (node) {
+ continue;
+ }
+ leaf = kmalloc(bi->length + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN, GFP_KERNEL);
+ if (!leaf) {
+ WL_ERROR(("Memory alloc failure %d\n", \
+ bi->length + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN));
+ mutex_unlock(&wl_cache_lock);
+ return -ENOMEM;
+ }
+
+ memcpy(leaf->bss_info, bi, bi->length);
+ leaf->next = NULL;
+ leaf->dirty = 1;
+ leaf->count = 1;
+ leaf->version = ss_list->version;
+
+ if (!prev) {
+ *spec_scan_head = leaf;
+ }
+ else {
+ prev->next = leaf;
+ }
+ }
+ mutex_unlock(&wl_cache_lock);
+ return 0;
+}
+
+
+static int
+wl_iw_merge_scan_cache(struct iw_request_info *info, char *extra, uint buflen_from_user,
+__u16 *merged_len)
+{
+ wl_iw_ss_cache_t *node;
+ wl_scan_results_t *list_merge;
+
+ mutex_lock(&wl_cache_lock);
+ node = g_ss_cache_ctrl.m_cache_head;
+ for (;node;) {
+ list_merge = (wl_scan_results_t *)&node->buflen;
+ WL_TRACE(("%s: Cached Specific APs list=%d\n", __FUNCTION__, list_merge->count));
+ if (buflen_from_user - *merged_len > 0) {
+ *merged_len += (__u16) wl_iw_get_scan_prep(list_merge, info,
+ extra + *merged_len, buflen_from_user - *merged_len);
+ }
+ else {
+ WL_TRACE(("%s: exit with break\n", __FUNCTION__));
+ break;
+ }
+ node = node->next;
+ }
+ mutex_unlock(&wl_cache_lock);
+ return 0;
+}
+
+
+static int
+wl_iw_delete_bss_from_ss_cache(void *addr)
+{
+
+ wl_iw_ss_cache_t *node, *prev;
+ wl_iw_ss_cache_t **spec_scan_head;
+
+ mutex_lock(&wl_cache_lock);
+ spec_scan_head = &g_ss_cache_ctrl.m_cache_head;
+ node = *spec_scan_head;
+ prev = node;
+ for (;node;) {
+ if (!memcmp(&node->bss_info->BSSID, addr, ETHER_ADDR_LEN)) {
+ if (node == *spec_scan_head) {
+ *spec_scan_head = node->next;
+ }
+ else {
+ prev->next = node->next;
+ }
+
+ WL_TRACE(("%s : Del node : %s\n", __FUNCTION__, node->bss_info->SSID));
+ kfree(node);
+ break;
+ }
+
+ prev = node;
+ node = node->next;
+ }
+
+ memset(addr, 0, ETHER_ADDR_LEN);
+ mutex_unlock(&wl_cache_lock);
+ return 0;
+}
+
+#endif
+
+
+static int
+wl_iw_set_scan(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int error;
+ WL_TRACE(("%s dev:%s: SIOCSIWSCAN : SCAN\n", __FUNCTION__, dev->name));
+
+#if defined(CSCAN)
+ WL_ERROR(("%s: Scan from SIOCGIWSCAN not supported\n", __FUNCTION__));
+ return -EINVAL;
+#endif
+
+#if defined(SOFTAP)
+ if (ap_cfg_running) {
+ WL_TRACE(("\n>%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__));
+ return 0;
+ }
+#endif
+
+ if (g_onoff == G_WLAN_SET_OFF)
+ return 0;
+
+ memset(&g_specific_ssid, 0, sizeof(g_specific_ssid));
+#ifndef WL_IW_USE_ISCAN
+ g_scan_specified_ssid = 0;
+#endif
+
+#if WIRELESS_EXT > 17
+
+ if (wrqu->data.length == sizeof(struct iw_scan_req)) {
+ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+ struct iw_scan_req *req = (struct iw_scan_req *)extra;
+#if defined(CONFIG_FIRST_SCAN)
+ if (g_first_broadcast_scan != BROADCAST_SCAN_FIRST_RESULT_CONSUMED) {
+ WL_ERROR(("%s Ignoring SC %s first BC is not done = %d\n", \
+ __FUNCTION__, req->essid, \
+ g_first_broadcast_scan));
+ return -EBUSY;
+ }
+#endif
+ if (g_scan_specified_ssid) {
+ WL_SCAN(("%s Specific SCAN is not done ignore scan for = %s \n", \
+ __FUNCTION__, req->essid));
+ return -EBUSY;
+ }
+ else {
+ g_specific_ssid.SSID_len = MIN(sizeof(g_specific_ssid.SSID), \
+ req->essid_len);
+ memcpy(g_specific_ssid.SSID, req->essid, g_specific_ssid.SSID_len);
+ g_specific_ssid.SSID_len = htod32(g_specific_ssid.SSID_len);
+ g_scan_specified_ssid = 1;
+ WL_TRACE(("### Specific scan ssid=%s len=%d\n", \
+ g_specific_ssid.SSID, g_specific_ssid.SSID_len));
+ }
+ }
+ }
+#endif
+
+ if ((error = dev_wlc_ioctl(dev, WLC_SCAN, &g_specific_ssid, sizeof(g_specific_ssid)))) {
+ WL_SCAN(("Set SCAN for %s failed with %d\n", g_specific_ssid.SSID, error));
+ g_scan_specified_ssid = 0;
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+#ifdef WL_IW_USE_ISCAN
+int
+wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag)
+{
+ wlc_ssid_t ssid;
+ iscan_info_t *iscan = g_iscan;
+
+#if defined(CONFIG_FIRST_SCAN)
+ if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_IDLE) {
+ g_first_broadcast_scan = BROADCAST_SCAN_FIRST_STARTED;
+ WL_SCAN(("%s: First Brodcast scan was forced\n", __FUNCTION__));
+ }
+ else if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED) {
+ WL_SCAN(("%s: ignore ISCAN request first BS is not done yet\n", __FUNCTION__));
+ return 0;
+ }
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ if (flag)
+ rtnl_lock();
+#endif
+
+ dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &iscan->scan_flag, sizeof(iscan->scan_flag));
+ wl_iw_set_event_mask(dev);
+
+ WL_SCAN(("+++: Set Broadcast ISCAN\n"));
+
+ memset(&ssid, 0, sizeof(ssid));
+
+ iscan->list_cur = iscan->list_hdr;
+ iscan->iscan_state = ISCAN_STATE_SCANING;
+
+ memset(&iscan->iscan_ex_params_p->params, 0, iscan->iscan_ex_param_size);
+ wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, &ssid);
+ wl_iw_iscan(iscan, &ssid, WL_SCAN_ACTION_START);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ if (flag)
+ rtnl_unlock();
+#endif
+
+ mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000);
+
+ iscan->timer_on = 1;
+
+ return 0;
+}
+
+static int
+wl_iw_iscan_set_scan(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ wlc_ssid_t ssid;
+ iscan_info_t *iscan = g_iscan;
+ int ret = 0;
+
+ WL_SCAN(("%s: SIOCSIWSCAN : ISCAN\n", dev->name));
+
+#if defined(CSCAN)
+ WL_ERROR(("%s: Scan from SIOCGIWSCAN not supported\n", __FUNCTION__));
+ return -EINVAL;
+#endif
+
+ net_os_wake_lock(dev);
+
+#if defined(SOFTAP)
+ if (ap_cfg_running) {
+ WL_SCAN(("\n>%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__));
+ goto set_scan_end;
+ }
+#endif
+
+ if (g_onoff == G_WLAN_SET_OFF) {
+ WL_SCAN(("%s: driver is not up yet after START\n", __FUNCTION__));
+ goto set_scan_end;
+ }
+
+#ifdef PNO_SUPPORT
+ if (dhd_dev_get_pno_status(dev)) {
+ WL_SCAN(("%s: Scan called when PNO is active\n", __FUNCTION__));
+ }
+#endif
+
+ if ((!iscan) || (iscan->sysioc_pid < 0)) {
+ WL_ERROR(("%s error\n", __FUNCTION__));
+ goto set_scan_end;
+ }
+
+ if (g_scan_specified_ssid) {
+ WL_SCAN(("%s Specific SCAN already running ignoring BC scan\n", \
+ __FUNCTION__));
+ ret = EBUSY;
+ goto set_scan_end;
+ }
+
+ memset(&ssid, 0, sizeof(ssid));
+
+#if WIRELESS_EXT > 17
+
+ if (wrqu->data.length == sizeof(struct iw_scan_req)) {
+ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+ int as = 0;
+ struct iw_scan_req *req = (struct iw_scan_req *)extra;
+ ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len);
+ memcpy(ssid.SSID, req->essid, ssid.SSID_len);
+ ssid.SSID_len = htod32(ssid.SSID_len);
+ dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &as, sizeof(as));
+ wl_iw_set_event_mask(dev);
+ ret = wl_iw_set_scan(dev, info, wrqu, extra);
+ goto set_scan_end;
+ }
+ else {
+ g_scan_specified_ssid = 0;
+
+ if (iscan->iscan_state == ISCAN_STATE_SCANING) {
+ WL_SCAN(("%s ISCAN already in progress \n", __FUNCTION__));
+ goto set_scan_end;
+ }
+ }
+ }
+#endif
+
+#if defined(CONFIG_FIRST_SCAN) && !defined(CSCAN)
+ if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_CONSUMED) {
+ if (++g_first_counter_scans == MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN) {
+
+ WL_ERROR(("%s Clean up First scan flag which is %d\n", \
+ __FUNCTION__, g_first_broadcast_scan));
+ g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED;
+ }
+ else {
+ WL_ERROR(("%s Ignoring Broadcast Scan:First Scan is not done yet %d\n", \
+ __FUNCTION__, g_first_counter_scans));
+ ret = -EBUSY;
+ goto set_scan_end;
+ }
+ }
+#endif
+
+ wl_iw_iscan_set_scan_broadcast_prep(dev, 0);
+
+set_scan_end:
+ net_os_wake_unlock(dev);
+ return ret;
+}
+#endif
+
+#if WIRELESS_EXT > 17
+static bool
+ie_is_wpa_ie(uint8 **wpaie, uint8 **tlvs, int *tlvs_len)
+{
+ uint8 *ie = *wpaie;
+
+ if ((ie[1] >= 6) &&
+ !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x01"), 4)) {
+ return TRUE;
+ }
+
+ ie += ie[1] + 2;
+
+ *tlvs_len -= (int)(ie - *tlvs);
+
+ *tlvs = ie;
+ return FALSE;
+}
+
+static bool
+ie_is_wps_ie(uint8 **wpsie, uint8 **tlvs, int *tlvs_len)
+{
+ uint8 *ie = *wpsie;
+
+ if ((ie[1] >= 4) &&
+ !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x04"), 4)) {
+ return TRUE;
+ }
+
+ ie += ie[1] + 2;
+
+ *tlvs_len -= (int)(ie - *tlvs);
+
+ *tlvs = ie;
+ return FALSE;
+}
+#endif
+
+static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
+ size_t len, int uppercase)
+{
+ size_t i;
+ char *pos = buf, *end = buf + buf_size;
+ int ret;
+ if (buf_size == 0)
+ return 0;
+ for (i = 0; i < len; i++) {
+ ret = snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
+ data[i]);
+ if (ret < 0 || ret >= end - pos) {
+ end[-1] = '\0';
+ return pos - buf;
+ }
+ pos += ret;
+ }
+ end[-1] = '\0';
+ return pos - buf;
+}
+
+
+int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len)
+{
+ return _wpa_snprintf_hex(buf, buf_size, data, len, 0);
+}
+
+static int
+wl_iw_handle_scanresults_ies(char **event_p, char *end,
+ struct iw_request_info *info, wl_bss_info_t *bi)
+{
+#if WIRELESS_EXT > 17
+ struct iw_event iwe;
+ char *event;
+ char *buf;
+ int custom_event_len;
+
+ event = *event_p;
+ if (bi->ie_length) {
+
+ bcm_tlv_t *ie;
+ uint8 *ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
+ int ptr_len = bi->ie_length;
+
+#ifdef BCMWPA2
+ if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_RSN_ID))) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie->len + 2;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
+ }
+ ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
+#endif
+
+ while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
+
+ if (ie_is_wps_ie(((uint8 **)&ie), &ptr, &ptr_len)) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie->len + 2;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
+ break;
+ }
+ }
+
+ ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
+ ptr_len = bi->ie_length;
+ while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
+ if (ie_is_wpa_ie(((uint8 **)&ie), &ptr, &ptr_len)) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie->len + 2;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
+ break;
+ }
+ }
+
+ ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
+ ptr_len = bi->ie_length;
+
+ while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WAPI_ID))) {
+ WL_TRACE(("%s: found a WAPI IE...\n", __FUNCTION__));
+#ifdef WAPI_IE_USE_GENIE
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie->len + 2;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
+#else
+ iwe.cmd = IWEVCUSTOM;
+ custom_event_len = strlen("wapi_ie=") + 2*(ie->len + 2);
+ iwe.u.data.length = custom_event_len;
+
+ buf = kmalloc(custom_event_len+1, GFP_KERNEL);
+ if (buf == NULL)
+ {
+ WL_ERROR(("malloc(%d) returned NULL...\n", custom_event_len));
+ break;
+ }
+
+ memcpy(buf, "wapi_ie=", 8);
+ wpa_snprintf_hex(buf + 8, 2+1, &(ie->id), 1);
+ wpa_snprintf_hex(buf + 10, 2+1, &(ie->len), 1);
+ wpa_snprintf_hex(buf + 12, 2*ie->len+1, ie->data, ie->len);
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, buf);
+#endif
+ break;
+ }
+ *event_p = event;
+ }
+#endif
+
+ return 0;
+}
+
+#ifndef CSCAN
+static uint
+wl_iw_get_scan_prep(
+ wl_scan_results_t *list,
+ struct iw_request_info *info,
+ char *extra,
+ short max_size)
+{
+ int i, j;
+ struct iw_event iwe;
+ wl_bss_info_t *bi = NULL;
+ char *event = extra, *end = extra + max_size - WE_ADD_EVENT_FIX, *value;
+ int ret = 0;
+ int channel;
+
+ if (!list) {
+ WL_ERROR(("%s: Null list pointer",__FUNCTION__));
+ return ret;
+ }
+
+ for (i = 0; i < list->count && i < IW_MAX_AP; i++)
+ {
+ if (list->version != WL_BSS_INFO_VERSION) {
+ WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n", \
+ __FUNCTION__, list->version));
+ return ret;
+ }
+
+ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
+
+ WL_TRACE(("%s : %s\n", __FUNCTION__, bi->SSID));
+
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN);
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN);
+
+ iwe.u.data.length = dtoh32(bi->SSID_len);
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
+
+ if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
+ iwe.cmd = SIOCGIWMODE;
+ if (dtoh16(bi->capability) & DOT11_CAP_ESS)
+ iwe.u.mode = IW_MODE_INFRA;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN);
+ }
+
+ iwe.cmd = SIOCGIWFREQ;
+ channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch;
+ iwe.u.freq.m = wf_channel2mhz(channel,
+ channel <= CH_MAX_2G_CHANNEL ?
+ WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
+ iwe.u.freq.e = 6;
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN);
+
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI));
+ iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI);
+ iwe.u.qual.noise = 0x100 + bi->phy_noise;
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN);
+
+ wl_iw_handle_scanresults_ies(&event, end, info, bi);
+
+ iwe.cmd = SIOCGIWENCODE;
+ if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
+
+ if (bi->rateset.count) {
+ if (((event -extra) + IW_EV_LCP_LEN) <= (uintptr)end) {
+ value = event + IW_EV_LCP_LEN;
+ iwe.cmd = SIOCGIWRATE;
+
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+ for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) {
+ iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000;
+ value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe,
+ IW_EV_PARAM_LEN);
+ }
+ event = value;
+ }
+ }
+ }
+
+ if ((ret = (event - extra)) < 0) {
+ WL_ERROR(("==> Wrong size\n"));
+ ret = 0;
+ }
+ WL_TRACE(("%s: size=%d bytes prepared \n", __FUNCTION__, (unsigned int)(event - extra)));
+ return (uint)ret;
+}
+
+static int
+wl_iw_get_scan(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ channel_info_t ci;
+ wl_scan_results_t *list_merge;
+ wl_scan_results_t *list = (wl_scan_results_t *) g_scan;
+ int error;
+ uint buflen_from_user = dwrq->length;
+ uint len = G_SCAN_RESULTS;
+ __u16 len_ret = 0;
+#if !defined(CSCAN)
+ __u16 merged_len = 0;
+#endif
+#if defined(WL_IW_USE_ISCAN)
+ iscan_info_t *iscan = g_iscan;
+ iscan_buf_t * p_buf;
+#if !defined(CSCAN)
+ uint32 counter = 0;
+#endif
+#endif
+ WL_TRACE(("%s: buflen_from_user %d: \n", dev->name, buflen_from_user));
+
+ if (!extra) {
+ WL_TRACE(("%s: wl_iw_get_scan return -EINVAL\n", dev->name));
+ return -EINVAL;
+ }
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci))))
+ return error;
+ ci.scan_channel = dtoh32(ci.scan_channel);
+ if (ci.scan_channel)
+ return -EAGAIN;
+
+#if !defined(CSCAN)
+ if (g_ss_cache_ctrl.m_timer_expired) {
+ wl_iw_free_ss_cache();
+ g_ss_cache_ctrl.m_timer_expired ^= 1;
+ }
+ if ((!g_scan_specified_ssid && g_ss_cache_ctrl.m_prev_scan_mode) ||
+ g_ss_cache_ctrl.m_cons_br_scan_cnt > 4) {
+ g_ss_cache_ctrl.m_cons_br_scan_cnt = 0;
+
+ wl_iw_reset_ss_cache();
+ }
+ g_ss_cache_ctrl.m_prev_scan_mode = g_scan_specified_ssid;
+ if (g_scan_specified_ssid) {
+ g_ss_cache_ctrl.m_cons_br_scan_cnt = 0;
+ }
+ else {
+ g_ss_cache_ctrl.m_cons_br_scan_cnt++;
+ }
+#endif
+
+ if (g_scan_specified_ssid) {
+
+ list = kmalloc(len, GFP_KERNEL);
+ if (!list) {
+ WL_TRACE(("%s: wl_iw_get_scan return -ENOMEM\n", dev->name));
+ g_scan_specified_ssid = 0;
+ return -ENOMEM;
+ }
+ }
+
+ memset(list, 0, len);
+ list->buflen = htod32(len);
+ if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, len))) {
+ WL_ERROR(("%s: %s : Scan_results ERROR %d\n", dev->name, __FUNCTION__, error));
+ dwrq->length = len;
+ if (g_scan_specified_ssid) {
+ g_scan_specified_ssid = 0;
+ kfree(list);
+ }
+ return 0;
+ }
+ list->buflen = dtoh32(list->buflen);
+ list->version = dtoh32(list->version);
+ list->count = dtoh32(list->count);
+
+ if (list->version != WL_BSS_INFO_VERSION) {
+ WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n",
+ __FUNCTION__, list->version));
+ if (g_scan_specified_ssid) {
+ g_scan_specified_ssid = 0;
+ kfree(list);
+ }
+ return -EINVAL;
+ }
+
+#if !defined(CSCAN)
+ if (g_scan_specified_ssid) {
+
+ wl_iw_add_bss_to_ss_cache(list);
+ kfree(list);
+ }
+
+ mutex_lock(&wl_cache_lock);
+#if defined(WL_IW_USE_ISCAN)
+ if (g_scan_specified_ssid)
+ WL_TRACE(("%s: Specified scan APs from scan=%d\n", __FUNCTION__, list->count));
+ p_buf = iscan->list_hdr;
+
+ while (p_buf != iscan->list_cur) {
+ list_merge = &((wl_iscan_results_t*)p_buf->iscan_buf)->results;
+ WL_TRACE(("%s: Bcast APs list=%d\n", __FUNCTION__, list_merge->count));
+ counter += list_merge->count;
+ if (list_merge->count > 0)
+ len_ret += (__u16) wl_iw_get_scan_prep(list_merge, info,
+ extra+len_ret, buflen_from_user -len_ret);
+ p_buf = p_buf->next;
+ }
+ WL_TRACE(("%s merged with total Bcast APs=%d\n", __FUNCTION__, counter));
+#else
+ list_merge = (wl_scan_results_t *) g_scan;
+ len_ret = (__u16) wl_iw_get_scan_prep(list_merge, info, extra, buflen_from_user);
+#endif
+ mutex_unlock(&wl_cache_lock);
+ if (g_ss_cache_ctrl.m_link_down) {
+ wl_iw_delete_bss_from_ss_cache(g_ss_cache_ctrl.m_active_bssid);
+ }
+
+ wl_iw_merge_scan_cache(info, extra+len_ret, buflen_from_user-len_ret, &merged_len);
+ len_ret += merged_len;
+ wl_iw_run_ss_cache_timer(0);
+ wl_iw_run_ss_cache_timer(1);
+#else
+
+ if (g_scan_specified_ssid) {
+ WL_TRACE(("%s: Specified scan APs in the list =%d\n", __FUNCTION__, list->count));
+ len_ret = (__u16) wl_iw_get_scan_prep(list, info, extra, buflen_from_user);
+ kfree(list);
+
+#if defined(WL_IW_USE_ISCAN)
+ p_buf = iscan->list_hdr;
+
+ while (p_buf != iscan->list_cur) {
+ list_merge = &((wl_iscan_results_t*)p_buf->iscan_buf)->results;
+ WL_TRACE(("%s: Bcast APs list=%d\n", __FUNCTION__, list_merge->count));
+ if (list_merge->count > 0)
+ len_ret += (__u16) wl_iw_get_scan_prep(list_merge, info,
+ extra+len_ret, buflen_from_user -len_ret);
+ p_buf = p_buf->next;
+ }
+#else
+ list_merge = (wl_scan_results_t *) g_scan;
+ WL_TRACE(("%s: Bcast APs list=%d\n", __FUNCTION__, list_merge->count));
+ if (list_merge->count > 0)
+ len_ret += (__u16) wl_iw_get_scan_prep(list_merge, info, extra+len_ret,
+ buflen_from_user -len_ret);
+#endif
+ }
+ else {
+ list = (wl_scan_results_t *) g_scan;
+ len_ret = (__u16) wl_iw_get_scan_prep(list, info, extra, buflen_from_user);
+ }
+#endif
+
+#if defined(WL_IW_USE_ISCAN)
+
+ g_scan_specified_ssid = 0;
+#endif
+
+ if ((len_ret + WE_ADD_EVENT_FIX) < buflen_from_user)
+ len = len_ret;
+
+ dwrq->length = len;
+ dwrq->flags = 0;
+
+ WL_TRACE(("%s return to WE %d bytes APs=%d\n", __FUNCTION__, dwrq->length, list->count));
+ return 0;
+}
+#endif
+
+#if defined(WL_IW_USE_ISCAN)
+static int
+wl_iw_iscan_get_scan(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_scan_results_t *list;
+ struct iw_event iwe;
+ wl_bss_info_t *bi = NULL;
+ int ii, j;
+ int apcnt;
+ char *event = extra, *end = extra + dwrq->length, *value;
+ iscan_info_t *iscan = g_iscan;
+ iscan_buf_t * p_buf;
+ uint32 counter = 0;
+ uint8 channel;
+#if !defined(CSCAN)
+ __u16 merged_len = 0;
+ uint buflen_from_user = dwrq->length;
+#endif
+
+ WL_SCAN(("%s %s buflen_from_user %d:\n", dev->name, __FUNCTION__, dwrq->length));
+
+#if defined(SOFTAP)
+ if (ap_cfg_running) {
+ WL_TRACE(("%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__));
+ return -EINVAL;
+ }
+#endif
+
+ if (!extra) {
+ WL_TRACE(("%s: INVALID SIOCGIWSCAN GET bad parameter\n", dev->name));
+ return -EINVAL;
+ }
+
+#if defined(CONFIG_FIRST_SCAN)
+ if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_READY) {
+ WL_TRACE(("%s %s: first ISCAN results are NOT ready yet \n", \
+ dev->name, __FUNCTION__));
+ return -EAGAIN;
+ }
+#endif
+
+ if ((!iscan) || (iscan->sysioc_pid < 0)) {
+ WL_ERROR(("%ssysioc_pid\n", __FUNCTION__));
+ return -EAGAIN;
+ }
+
+#if !defined(CSCAN)
+ if (g_ss_cache_ctrl.m_timer_expired) {
+ wl_iw_free_ss_cache();
+ g_ss_cache_ctrl.m_timer_expired ^= 1;
+ }
+ if (g_scan_specified_ssid) {
+ return wl_iw_get_scan(dev, info, dwrq, extra);
+ }
+ else {
+ if (g_ss_cache_ctrl.m_link_down) {
+ wl_iw_delete_bss_from_ss_cache(g_ss_cache_ctrl.m_active_bssid);
+ }
+ if (g_ss_cache_ctrl.m_prev_scan_mode || g_ss_cache_ctrl.m_cons_br_scan_cnt > 4) {
+ g_ss_cache_ctrl.m_cons_br_scan_cnt = 0;
+
+ wl_iw_reset_ss_cache();
+ }
+ g_ss_cache_ctrl.m_prev_scan_mode = g_scan_specified_ssid;
+ g_ss_cache_ctrl.m_cons_br_scan_cnt++;
+ }
+#endif
+
+ WL_TRACE(("%s: SIOCGIWSCAN GET broadcast results\n", dev->name));
+ apcnt = 0;
+ p_buf = iscan->list_hdr;
+
+ while (p_buf != iscan->list_cur) {
+ list = &((wl_iscan_results_t*)p_buf->iscan_buf)->results;
+
+ counter += list->count;
+
+ if (list->version != WL_BSS_INFO_VERSION) {
+ WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n",
+ __FUNCTION__, list->version));
+ return -EINVAL;
+ }
+
+ bi = NULL;
+ for (ii = 0; ii < list->count && apcnt < IW_MAX_AP; apcnt++, ii++) {
+ bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
+
+ if ((dtoh32(bi->length) > WLC_IW_ISCAN_MAXLEN) ||
+ (((uintptr)bi + dtoh32(bi->length)) > ((uintptr)list + WLC_IW_ISCAN_MAXLEN))) {
+ WL_ERROR(("%s: Scan results out of bounds: %u\n",__FUNCTION__,dtoh32(bi->length)));
+ return -E2BIG;
+ }
+
+ if (event + ETHER_ADDR_LEN + bi->SSID_len + IW_EV_UINT_LEN + IW_EV_FREQ_LEN +
+ IW_EV_QUAL_LEN >= end)
+ return -E2BIG;
+
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN);
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN);
+
+ iwe.u.data.length = dtoh32(bi->SSID_len);
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
+
+ if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
+ iwe.cmd = SIOCGIWMODE;
+ if (dtoh16(bi->capability) & DOT11_CAP_ESS)
+ iwe.u.mode = IW_MODE_INFRA;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN);
+ }
+
+ iwe.cmd = SIOCGIWFREQ;
+ channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch;
+ iwe.u.freq.m = wf_channel2mhz(channel,
+ channel <= CH_MAX_2G_CHANNEL ?
+ WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
+ iwe.u.freq.e = 6;
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN);
+
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI));
+ iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI);
+ iwe.u.qual.noise = 0x100 + bi->phy_noise;
+ event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN);
+
+ wl_iw_handle_scanresults_ies(&event, end, info, bi);
+
+ iwe.cmd = SIOCGIWENCODE;
+ if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
+
+ if (bi->rateset.count) {
+ if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end)
+ return -E2BIG;
+
+ value = event + IW_EV_LCP_LEN;
+ iwe.cmd = SIOCGIWRATE;
+
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+ for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) {
+ iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000;
+ value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe,
+ IW_EV_PARAM_LEN);
+ }
+ event = value;
+ }
+ }
+ p_buf = p_buf->next;
+ }
+
+ dwrq->length = event - extra;
+ dwrq->flags = 0;
+
+#if !defined(CSCAN)
+ wl_iw_merge_scan_cache(info, event, buflen_from_user - dwrq->length, &merged_len);
+ dwrq->length += merged_len;
+ wl_iw_run_ss_cache_timer(0);
+ wl_iw_run_ss_cache_timer(1);
+#endif /* CSCAN */
+#if defined(CONFIG_FIRST_SCAN)
+ g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED;
+#endif
+
+ WL_TRACE(("%s return to WE %d bytes APs=%d\n", __FUNCTION__, dwrq->length, counter));
+
+ return 0;
+}
+#endif
+
+static int
+wl_iw_set_essid(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ int error;
+ wl_join_params_t join_params;
+ int join_params_size;
+
+ WL_TRACE(("%s: SIOCSIWESSID\n", dev->name));
+
+
+ memset(&g_ssid, 0, sizeof(g_ssid));
+
+ CHECK_EXTRA_FOR_NULL(extra);
+
+ if (dwrq->length && extra) {
+#if WIRELESS_EXT > 20
+ g_ssid.SSID_len = MIN(sizeof(g_ssid.SSID), dwrq->length);
+#else
+ g_ssid.SSID_len = MIN(sizeof(g_ssid.SSID), dwrq->length-1);
+#endif
+ memcpy(g_ssid.SSID, extra, g_ssid.SSID_len);
+ } else {
+
+ g_ssid.SSID_len = 0;
+ }
+ g_ssid.SSID_len = htod32(g_ssid.SSID_len);
+
+ memset(&join_params, 0, sizeof(join_params));
+ join_params_size = sizeof(join_params.ssid);
+
+ memcpy(&join_params.ssid.SSID, g_ssid.SSID, g_ssid.SSID_len);
+ join_params.ssid.SSID_len = htod32(g_ssid.SSID_len);
+ memcpy(&join_params.params.bssid, &ether_bcast, ETHER_ADDR_LEN);
+
+ wl_iw_ch_to_chanspec(g_wl_iw_params.target_channel, &join_params, &join_params_size);
+
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size))) {
+ WL_ERROR(("Invalid ioctl data=%d\n", error));
+ return error;
+ }
+
+ if (g_ssid.SSID_len) {
+ WL_TRACE(("%s: join SSID=%s ch=%d\n", __FUNCTION__, \
+ g_ssid.SSID, g_wl_iw_params.target_channel));
+ }
+ return 0;
+}
+
+static int
+wl_iw_get_essid(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wlc_ssid_t ssid;
+ int error;
+
+ WL_TRACE(("%s: SIOCGIWESSID\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) {
+ WL_ERROR(("Error getting the SSID\n"));
+ return error;
+ }
+
+ ssid.SSID_len = dtoh32(ssid.SSID_len);
+
+ memcpy(extra, ssid.SSID, ssid.SSID_len);
+
+ dwrq->length = ssid.SSID_len;
+
+ dwrq->flags = 1;
+
+ return 0;
+}
+
+static int
+wl_iw_set_nick(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+
+ WL_TRACE(("%s: SIOCSIWNICKN\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ if (dwrq->length > sizeof(iw->nickname))
+ return -E2BIG;
+
+ memcpy(iw->nickname, extra, dwrq->length);
+ iw->nickname[dwrq->length - 1] = '\0';
+
+ return 0;
+}
+
+static int
+wl_iw_get_nick(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+
+ WL_TRACE(("%s: SIOCGIWNICKN\n", dev->name));
+
+ if (!extra)
+ return -EINVAL;
+
+ strcpy(extra, iw->nickname);
+ dwrq->length = strlen(extra) + 1;
+
+ return 0;
+}
+
+static int wl_iw_set_rate(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ wl_rateset_t rateset;
+ int error, rate, i, error_bg, error_a;
+
+ WL_TRACE(("%s: SIOCSIWRATE\n", dev->name));
+
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset))))
+ return error;
+
+ rateset.count = dtoh32(rateset.count);
+
+ if (vwrq->value < 0) {
+
+ rate = rateset.rates[rateset.count - 1] & 0x7f;
+ } else if (vwrq->value < rateset.count) {
+
+ rate = rateset.rates[vwrq->value] & 0x7f;
+ } else {
+
+ rate = vwrq->value / 500000;
+ }
+
+ if (vwrq->fixed) {
+
+ error_bg = dev_wlc_intvar_set(dev, "bg_rate", rate);
+ error_a = dev_wlc_intvar_set(dev, "a_rate", rate);
+
+ if (error_bg && error_a)
+ return (error_bg | error_a);
+ } else {
+
+
+ error_bg = dev_wlc_intvar_set(dev, "bg_rate", 0);
+
+ error_a = dev_wlc_intvar_set(dev, "a_rate", 0);
+
+ if (error_bg && error_a)
+ return (error_bg | error_a);
+
+
+ for (i = 0; i < rateset.count; i++)
+ if ((rateset.rates[i] & 0x7f) > rate)
+ break;
+ rateset.count = htod32(i);
+
+
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_RATESET, &rateset, sizeof(rateset))))
+ return error;
+ }
+
+ return 0;
+}
+
+static int wl_iw_get_rate(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, rate;
+
+ WL_TRACE(("%s: SIOCGIWRATE\n", dev->name));
+
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate))))
+ return error;
+ rate = dtoh32(rate);
+ vwrq->value = rate * 500000;
+
+ return 0;
+}
+
+static int
+wl_iw_set_rts(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, rts;
+
+ WL_TRACE(("%s: SIOCSIWRTS\n", dev->name));
+
+ if (vwrq->disabled)
+ rts = DOT11_DEFAULT_RTS_LEN;
+ else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_RTS_LEN)
+ return -EINVAL;
+ else
+ rts = vwrq->value;
+
+ if ((error = dev_wlc_intvar_set(dev, "rtsthresh", rts)))
+ return error;
+
+ return 0;
+}
+
+static int
+wl_iw_get_rts(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, rts;
+
+ WL_TRACE(("%s: SIOCGIWRTS\n", dev->name));
+
+ if ((error = dev_wlc_intvar_get(dev, "rtsthresh", &rts)))
+ return error;
+
+ vwrq->value = rts;
+ vwrq->disabled = (rts >= DOT11_DEFAULT_RTS_LEN);
+ vwrq->fixed = 1;
+
+ return 0;
+}
+
+static int
+wl_iw_set_frag(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, frag;
+
+ WL_TRACE(("%s: SIOCSIWFRAG\n", dev->name));
+
+ if (vwrq->disabled)
+ frag = DOT11_DEFAULT_FRAG_LEN;
+ else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_FRAG_LEN)
+ return -EINVAL;
+ else
+ frag = vwrq->value;
+
+ if ((error = dev_wlc_intvar_set(dev, "fragthresh", frag)))
+ return error;
+
+ return 0;
+}
+
+static int
+wl_iw_get_frag(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, fragthreshold;
+
+ WL_TRACE(("%s: SIOCGIWFRAG\n", dev->name));
+
+ if ((error = dev_wlc_intvar_get(dev, "fragthresh", &fragthreshold)))
+ return error;
+
+ vwrq->value = fragthreshold;
+ vwrq->disabled = (fragthreshold >= DOT11_DEFAULT_FRAG_LEN);
+ vwrq->fixed = 1;
+
+ return 0;
+}
+
+static int
+wl_iw_set_txpow(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, disable;
+ uint16 txpwrmw;
+ WL_TRACE(("%s: SIOCSIWTXPOW\n", dev->name));
+
+
+ disable = vwrq->disabled ? WL_RADIO_SW_DISABLE : 0;
+ disable += WL_RADIO_SW_DISABLE << 16;
+
+ disable = htod32(disable);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_RADIO, &disable, sizeof(disable))))
+ return error;
+
+
+ if (disable & WL_RADIO_SW_DISABLE)
+ return 0;
+
+
+ if (!(vwrq->flags & IW_TXPOW_MWATT))
+ return -EINVAL;
+
+
+ if (vwrq->value < 0)
+ return 0;
+
+ if (vwrq->value > 0xffff) txpwrmw = 0xffff;
+ else txpwrmw = (uint16)vwrq->value;
+
+
+ error = dev_wlc_intvar_set(dev, "qtxpower", (int)(bcm_mw_to_qdbm(txpwrmw)));
+ return error;
+}
+
+static int
+wl_iw_get_txpow(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, disable, txpwrdbm;
+ uint8 result;
+
+ WL_TRACE(("%s: SIOCGIWTXPOW\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_RADIO, &disable, sizeof(disable))) ||
+ (error = dev_wlc_intvar_get(dev, "qtxpower", &txpwrdbm)))
+ return error;
+
+ disable = dtoh32(disable);
+ result = (uint8)(txpwrdbm & ~WL_TXPWR_OVERRIDE);
+ vwrq->value = (int32)bcm_qdbm_to_mw(result);
+ vwrq->fixed = 0;
+ vwrq->disabled = (disable & (WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE)) ? 1 : 0;
+ vwrq->flags = IW_TXPOW_MWATT;
+
+ return 0;
+}
+
+#if WIRELESS_EXT > 10
+static int
+wl_iw_set_retry(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, lrl, srl;
+
+ WL_TRACE(("%s: SIOCSIWRETRY\n", dev->name));
+
+
+ if (vwrq->disabled || (vwrq->flags & IW_RETRY_LIFETIME))
+ return -EINVAL;
+
+
+ if (vwrq->flags & IW_RETRY_LIMIT) {
+
+
+#if WIRELESS_EXT > 20
+ if ((vwrq->flags & IW_RETRY_LONG) ||(vwrq->flags & IW_RETRY_MAX) ||
+ !((vwrq->flags & IW_RETRY_SHORT) || (vwrq->flags & IW_RETRY_MIN))) {
+#else
+ if ((vwrq->flags & IW_RETRY_MAX) || !(vwrq->flags & IW_RETRY_MIN)) {
+#endif
+ lrl = htod32(vwrq->value);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_LRL, &lrl, sizeof(lrl))))
+ return error;
+ }
+
+
+#if WIRELESS_EXT > 20
+ if ((vwrq->flags & IW_RETRY_SHORT) ||(vwrq->flags & IW_RETRY_MIN) ||
+ !((vwrq->flags & IW_RETRY_LONG) || (vwrq->flags & IW_RETRY_MAX))) {
+#else
+ if ((vwrq->flags & IW_RETRY_MIN) || !(vwrq->flags & IW_RETRY_MAX)) {
+#endif
+ srl = htod32(vwrq->value);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_SRL, &srl, sizeof(srl))))
+ return error;
+ }
+ }
+ return 0;
+}
+
+static int
+wl_iw_get_retry(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, lrl, srl;
+
+ WL_TRACE(("%s: SIOCGIWRETRY\n", dev->name));
+
+ vwrq->disabled = 0;
+
+
+ if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
+ return -EINVAL;
+
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_LRL, &lrl, sizeof(lrl))) ||
+ (error = dev_wlc_ioctl(dev, WLC_GET_SRL, &srl, sizeof(srl))))
+ return error;
+
+ lrl = dtoh32(lrl);
+ srl = dtoh32(srl);
+
+
+ if (vwrq->flags & IW_RETRY_MAX) {
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ vwrq->value = lrl;
+ } else {
+ vwrq->flags = IW_RETRY_LIMIT;
+ vwrq->value = srl;
+ if (srl != lrl)
+ vwrq->flags |= IW_RETRY_MIN;
+ }
+
+ return 0;
+}
+#endif
+
+static int
+wl_iw_set_encode(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_wsec_key_t key;
+ int error, val, wsec;
+
+ WL_TRACE(("%s: SIOCSIWENCODE\n", dev->name));
+
+ memset(&key, 0, sizeof(key));
+
+ if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
+
+ for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) {
+ val = htod32(key.index);
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val))))
+ return error;
+ val = dtoh32(val);
+ if (val)
+ break;
+ }
+
+ if (key.index == DOT11_MAX_DEFAULT_KEYS)
+ key.index = 0;
+ } else {
+ key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+ if (key.index >= DOT11_MAX_DEFAULT_KEYS)
+ return -EINVAL;
+ }
+
+
+ if (!extra || !dwrq->length || (dwrq->flags & IW_ENCODE_NOKEY)) {
+
+ val = htod32(key.index);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, &val, sizeof(val))))
+ return error;
+ } else {
+ key.len = dwrq->length;
+
+ if (dwrq->length > sizeof(key.data))
+ return -EINVAL;
+
+ memcpy(key.data, extra, dwrq->length);
+
+ key.flags = WL_PRIMARY_KEY;
+ switch (key.len) {
+ case WEP1_KEY_SIZE:
+ key.algo = CRYPTO_ALGO_WEP1;
+ break;
+ case WEP128_KEY_SIZE:
+ key.algo = CRYPTO_ALGO_WEP128;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
+ case TKIP_KEY_SIZE:
+ key.algo = CRYPTO_ALGO_TKIP;
+ break;
+#endif
+ case AES_KEY_SIZE:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ swap_key_from_BE(&key);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key))))
+ return error;
+ }
+
+
+ val = (dwrq->flags & IW_ENCODE_DISABLED) ? 0 : WEP_ENABLED;
+
+ if ((error = dev_wlc_intvar_get(dev, "wsec", &wsec)))
+ return error;
+
+ wsec &= ~(WEP_ENABLED);
+ wsec |= val;
+
+ if ((error = dev_wlc_intvar_set(dev, "wsec", wsec)))
+ return error;
+
+
+ val = (dwrq->flags & IW_ENCODE_RESTRICTED) ? 1 : 0;
+ val = htod32(val);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val))))
+ return error;
+
+ return 0;
+}
+
+static int
+wl_iw_get_encode(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_wsec_key_t key;
+ int error, val, wsec, auth;
+
+ WL_TRACE(("%s: SIOCGIWENCODE\n", dev->name));
+
+
+ bzero(&key, sizeof(wl_wsec_key_t));
+
+ if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
+
+ for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) {
+ val = key.index;
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val))))
+ return error;
+ val = dtoh32(val);
+ if (val)
+ break;
+ }
+ } else
+ key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+ if (key.index >= DOT11_MAX_DEFAULT_KEYS)
+ key.index = 0;
+
+
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec))) ||
+ (error = dev_wlc_ioctl(dev, WLC_GET_AUTH, &auth, sizeof(auth))))
+ return error;
+
+ swap_key_to_BE(&key);
+
+ wsec = dtoh32(wsec);
+ auth = dtoh32(auth);
+
+ dwrq->length = MIN(DOT11_MAX_KEY_SIZE, key.len);
+
+
+ dwrq->flags = key.index + 1;
+ if (!(wsec & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED))) {
+
+ dwrq->flags |= IW_ENCODE_DISABLED;
+ }
+ if (auth) {
+
+ dwrq->flags |= IW_ENCODE_RESTRICTED;
+ }
+
+
+ if (dwrq->length && extra)
+ memcpy(extra, key.data, dwrq->length);
+
+ return 0;
+}
+
+static int
+wl_iw_set_power(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, pm;
+
+ WL_TRACE(("%s: SIOCSIWPOWER\n", dev->name));
+
+ pm = vwrq->disabled ? PM_OFF : PM_MAX;
+
+ pm = htod32(pm);
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm))))
+ return error;
+
+ return 0;
+}
+
+static int
+wl_iw_get_power(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error, pm;
+
+ WL_TRACE(("%s: SIOCGIWPOWER\n", dev->name));
+
+ if ((error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm))))
+ return error;
+
+ pm = dtoh32(pm);
+ vwrq->disabled = pm ? 0 : 1;
+ vwrq->flags = IW_POWER_ALL_R;
+
+ return 0;
+}
+
+#if WIRELESS_EXT > 17
+static int
+wl_iw_set_wpaie(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *iwp,
+ char *extra
+)
+{
+ uchar buf[WLC_IOCTL_SMLEN] = {0};
+ uchar *p = buf;
+ int wapi_ie_size;
+
+ WL_TRACE(("%s: SIOCSIWGENIE\n", dev->name));
+
+ CHECK_EXTRA_FOR_NULL(extra);
+
+ if (extra[0] == DOT11_MNG_WAPI_ID)
+ {
+ wapi_ie_size = iwp->length;
+ memcpy(p, extra, iwp->length);
+ dev_wlc_bufvar_set(dev, "wapiie", buf, wapi_ie_size);
+ }
+ else
+ dev_wlc_bufvar_set(dev, "wpaie", extra, iwp->length);
+
+ return 0;
+}
+
+static int
+wl_iw_get_wpaie(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *iwp,
+ char *extra
+)
+{
+ WL_TRACE(("%s: SIOCGIWGENIE\n", dev->name));
+ iwp->length = 64;
+ dev_wlc_bufvar_get(dev, "wpaie", extra, iwp->length);
+ return 0;
+}
+
+static int
+wl_iw_set_encodeext(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *extra
+)
+{
+ wl_wsec_key_t key;
+ int error;
+ struct iw_encode_ext *iwe;
+
+ WL_WSEC(("%s: SIOCSIWENCODEEXT\n", dev->name));
+
+ CHECK_EXTRA_FOR_NULL(extra);
+
+ memset(&key, 0, sizeof(key));
+ iwe = (struct iw_encode_ext *)extra;
+
+
+ if (dwrq->flags & IW_ENCODE_DISABLED) {
+
+ }
+
+
+ key.index = 0;
+ if (dwrq->flags & IW_ENCODE_INDEX)
+ key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+ key.len = iwe->key_len;
+
+
+ if (!ETHER_ISMULTI(iwe->addr.sa_data))
+ bcopy((void *)&iwe->addr.sa_data, (char *)&key.ea, ETHER_ADDR_LEN);
+
+
+ if (key.len == 0) {
+ if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ WL_WSEC(("Changing the the primary Key to %d\n", key.index));
+
+ key.index = htod32(key.index);
+ error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY,
+ &key.index, sizeof(key.index));
+ if (error)
+ return error;
+ }
+
+ else {
+ swap_key_from_BE(&key);
+ dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+ }
+ }
+ else {
+ if (iwe->key_len > sizeof(key.data))
+ return -EINVAL;
+
+ WL_WSEC(("Setting the key index %d\n", key.index));
+ if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ WL_WSEC(("key is a Primary Key\n"));
+ key.flags = WL_PRIMARY_KEY;
+ }
+
+ bcopy((void *)iwe->key, key.data, iwe->key_len);
+
+ if (iwe->alg == IW_ENCODE_ALG_TKIP) {
+ uint8 keybuf[8];
+ bcopy(&key.data[24], keybuf, sizeof(keybuf));
+ bcopy(&key.data[16], &key.data[24], sizeof(keybuf));
+ bcopy(keybuf, &key.data[16], sizeof(keybuf));
+ }
+
+
+ if (iwe->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
+ uchar *ivptr;
+ ivptr = (uchar *)iwe->rx_seq;
+ key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
+ (ivptr[3] << 8) | ivptr[2];
+ key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
+ key.iv_initialized = TRUE;
+ }
+
+ switch (iwe->alg) {
+ case IW_ENCODE_ALG_NONE:
+ key.algo = CRYPTO_ALGO_OFF;
+ break;
+ case IW_ENCODE_ALG_WEP:
+ if (iwe->key_len == WEP1_KEY_SIZE)
+ key.algo = CRYPTO_ALGO_WEP1;
+ else
+ key.algo = CRYPTO_ALGO_WEP128;
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ key.algo = CRYPTO_ALGO_TKIP;
+ break;
+ case IW_ENCODE_ALG_CCMP:
+ key.algo = CRYPTO_ALGO_AES_CCM;
+ break;
+ case IW_ENCODE_ALG_SM4:
+ key.algo = CRYPTO_ALGO_SMS4;
+ if (iwe->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+ key.flags &= ~WL_PRIMARY_KEY;
+ }
+ break;
+ default:
+ break;
+ }
+ swap_key_from_BE(&key);
+
+ dhd_wait_pend8021x(dev);
+
+ error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+ if (error)
+ return error;
+ }
+ return 0;
+}
+
+#if WIRELESS_EXT > 17
+#ifdef BCMWPA2
+struct {
+ pmkid_list_t pmkids;
+ pmkid_t foo[MAXPMKID-1];
+} pmkid_list;
+
+static int
+wl_iw_set_pmksa(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ struct iw_pmksa *iwpmksa;
+ uint i;
+ int ret = 0;
+ char eabuf[ETHER_ADDR_STR_LEN];
+
+ WL_WSEC(("%s: SIOCSIWPMKSA\n", dev->name));
+ CHECK_EXTRA_FOR_NULL(extra);
+
+ iwpmksa = (struct iw_pmksa *)extra;
+ bzero((char *)eabuf, ETHER_ADDR_STR_LEN);
+
+ if (iwpmksa->cmd == IW_PMKSA_FLUSH) {
+ WL_WSEC(("wl_iw_set_pmksa - IW_PMKSA_FLUSH\n"));
+ bzero((char *)&pmkid_list, sizeof(pmkid_list));
+ }
+
+ else if (iwpmksa->cmd == IW_PMKSA_REMOVE) {
+ {
+ pmkid_list_t pmkid, *pmkidptr;
+ uint j;
+ pmkidptr = &pmkid;
+
+ bcopy(&iwpmksa->bssid.sa_data[0], &pmkidptr->pmkid[0].BSSID, ETHER_ADDR_LEN);
+ bcopy(&iwpmksa->pmkid[0], &pmkidptr->pmkid[0].PMKID, WPA2_PMKID_LEN);
+
+ WL_WSEC(("wl_iw_set_pmksa,IW_PMKSA_REMOVE - PMKID: %s = ",
+ bcm_ether_ntoa(&pmkidptr->pmkid[0].BSSID,
+ eabuf)));
+ for (j = 0; j < WPA2_PMKID_LEN; j++)
+ WL_WSEC(("%02x ", pmkidptr->pmkid[0].PMKID[j]));
+ WL_WSEC(("\n"));
+ }
+
+ for (i = 0; i < pmkid_list.pmkids.npmkid; i++)
+ if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_list.pmkids.pmkid[i].BSSID,
+ ETHER_ADDR_LEN))
+ break;
+
+ if ((pmkid_list.pmkids.npmkid > 0) && (i < pmkid_list.pmkids.npmkid)) {
+ bzero(&pmkid_list.pmkids.pmkid[i], sizeof(pmkid_t));
+ for (; i < (pmkid_list.pmkids.npmkid - 1); i++) {
+ bcopy(&pmkid_list.pmkids.pmkid[i+1].BSSID,
+ &pmkid_list.pmkids.pmkid[i].BSSID,
+ ETHER_ADDR_LEN);
+ bcopy(&pmkid_list.pmkids.pmkid[i+1].PMKID,
+ &pmkid_list.pmkids.pmkid[i].PMKID,
+ WPA2_PMKID_LEN);
+ }
+ pmkid_list.pmkids.npmkid--;
+ }
+ else
+ ret = -EINVAL;
+ }
+
+ else if (iwpmksa->cmd == IW_PMKSA_ADD) {
+ for (i = 0; i < pmkid_list.pmkids.npmkid; i++)
+ if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_list.pmkids.pmkid[i].BSSID,
+ ETHER_ADDR_LEN))
+ break;
+ if (i < MAXPMKID) {
+ bcopy(&iwpmksa->bssid.sa_data[0],
+ &pmkid_list.pmkids.pmkid[i].BSSID,
+ ETHER_ADDR_LEN);
+ bcopy(&iwpmksa->pmkid[0], &pmkid_list.pmkids.pmkid[i].PMKID,
+ WPA2_PMKID_LEN);
+ if (i == pmkid_list.pmkids.npmkid)
+ pmkid_list.pmkids.npmkid++;
+ }
+ else
+ ret = -EINVAL;
+
+ {
+ uint j;
+ uint k;
+ k = pmkid_list.pmkids.npmkid;
+ WL_WSEC(("wl_iw_set_pmksa,IW_PMKSA_ADD - PMKID: %s = ",
+ bcm_ether_ntoa(&pmkid_list.pmkids.pmkid[k].BSSID,
+ eabuf)));
+ for (j = 0; j < WPA2_PMKID_LEN; j++)
+ WL_WSEC(("%02x ", pmkid_list.pmkids.pmkid[k].PMKID[j]));
+ WL_WSEC(("\n"));
+ }
+ }
+ WL_WSEC(("PRINTING pmkid LIST - No of elements %d, ret = %d\n", pmkid_list.pmkids.npmkid, ret));
+ for (i = 0; i < pmkid_list.pmkids.npmkid; i++) {
+ uint j;
+ WL_WSEC(("PMKID[%d]: %s = ", i,
+ bcm_ether_ntoa(&pmkid_list.pmkids.pmkid[i].BSSID,
+ eabuf)));
+ for (j = 0; j < WPA2_PMKID_LEN; j++)
+ WL_WSEC(("%02x ", pmkid_list.pmkids.pmkid[i].PMKID[j]));
+ WL_WSEC(("\n"));
+ }
+ WL_WSEC(("\n"));
+
+ if (!ret)
+ ret = dev_wlc_bufvar_set(dev, "pmkid_info", (char *)&pmkid_list, sizeof(pmkid_list));
+ return ret;
+}
+#endif
+#endif
+
+static int
+wl_iw_get_encodeext(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ WL_WSEC(("%s: SIOCGIWENCODEEXT\n", dev->name));
+ return 0;
+}
+
+static int
+wl_iw_set_wpaauth(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error = 0;
+ int paramid;
+ int paramval;
+ int val = 0;
+ wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+
+ WL_WSEC(("%s: SIOCSIWAUTH\n", dev->name));
+
+#if defined(SOFTAP)
+ if (ap_cfg_running) {
+ WL_TRACE(("%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__));
+ return 0;
+ }
+#endif
+
+ paramid = vwrq->flags & IW_AUTH_INDEX;
+ paramval = vwrq->value;
+
+ WL_WSEC(("%s: SIOCSIWAUTH, paramid = 0x%0x, paramval = 0x%0x\n",
+ dev->name, paramid, paramval));
+
+ switch (paramid) {
+ case IW_AUTH_WPA_VERSION:
+
+ if (paramval & IW_AUTH_WPA_VERSION_DISABLED)
+ val = WPA_AUTH_DISABLED;
+ else if (paramval & (IW_AUTH_WPA_VERSION_WPA))
+ val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
+#ifdef BCMWPA2
+ else if (paramval & IW_AUTH_WPA_VERSION_WPA2)
+ val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
+#endif
+ else if (paramval & IW_AUTH_WAPI_VERSION_1)
+ val = WPA_AUTH_WAPI;
+ WL_WSEC(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val));
+ if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val)))
+ return error;
+ break;
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+
+
+ if (paramval & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
+ val = WEP_ENABLED;
+ if (paramval & IW_AUTH_CIPHER_TKIP)
+ val = TKIP_ENABLED;
+ if (paramval & IW_AUTH_CIPHER_CCMP)
+ val = AES_ENABLED;
+ if (paramval & IW_AUTH_CIPHER_SMS4)
+ val = SMS4_ENABLED;
+
+ if (paramid == IW_AUTH_CIPHER_PAIRWISE) {
+ iw->pwsec = val;
+ val |= iw->gwsec;
+ }
+ else {
+ iw->gwsec = val;
+ val |= iw->pwsec;
+ }
+
+ if (iw->privacy_invoked && !val) {
+ WL_WSEC(("%s: %s: 'Privacy invoked' TRUE but clearing wsec, assuming "
+ "we're a WPS enrollee\n", dev->name, __FUNCTION__));
+ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) {
+ WL_ERROR(("Failed to set iovar is_WPS_enrollee\n"));
+ return error;
+ }
+ } else if (val) {
+ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
+ WL_ERROR(("Failed to clear iovar is_WPS_enrollee\n"));
+ return error;
+ }
+ }
+
+ if ((error = dev_wlc_intvar_set(dev, "wsec", val))) {
+ WL_ERROR(("Failed to set 'wsec'iovar\n"));
+ return error;
+ }
+
+ break;
+
+ case IW_AUTH_KEY_MGMT:
+ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) {
+ WL_ERROR(("Failed to get 'wpa_auth'iovar\n"));
+ return error;
+ }
+
+ if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
+ if (paramval & IW_AUTH_KEY_MGMT_PSK)
+ val = WPA_AUTH_PSK;
+ else
+ val = WPA_AUTH_UNSPECIFIED;
+ }
+#ifdef BCMWPA2
+ else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+ if (paramval & IW_AUTH_KEY_MGMT_PSK)
+ val = WPA2_AUTH_PSK;
+ else
+ val = WPA2_AUTH_UNSPECIFIED;
+ }
+#endif
+ if (paramval & (IW_AUTH_KEY_MGMT_WAPI_PSK | IW_AUTH_KEY_MGMT_WAPI_CERT))
+ val = WPA_AUTH_WAPI;
+ WL_WSEC(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val));
+ if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) {
+ WL_ERROR(("Failed to set 'wpa_auth'iovar\n"));
+ return error;
+ }
+
+ break;
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ if ((error = dev_wlc_bufvar_set(dev, "tkip_countermeasures", \
+ (char *)&paramval, sizeof(paramval))))
+ WL_WSEC(("%s: tkip_countermeasures failed %d\n", __FUNCTION__, error));
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+
+ WL_WSEC(("Setting the D11auth %d\n", paramval));
+ if (paramval == IW_AUTH_ALG_OPEN_SYSTEM)
+ val = 0;
+ else if (paramval == IW_AUTH_ALG_SHARED_KEY)
+ val = 1;
+ else if (paramval == (IW_AUTH_ALG_OPEN_SYSTEM | IW_AUTH_ALG_SHARED_KEY))
+ val = 2;
+ else
+ error = 1;
+ if (!error && (error = dev_wlc_intvar_set(dev, "auth", val)))
+ return error;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ if (paramval == 0) {
+ iw->pwsec = 0;
+ iw->gwsec = 0;
+ if ((error = dev_wlc_intvar_get(dev, "wsec", &val))) {
+ WL_ERROR(("Failed to get 'wsec'iovar\n"));
+ return error;
+ }
+ if (val & (TKIP_ENABLED | AES_ENABLED)) {
+ val &= ~(TKIP_ENABLED | AES_ENABLED);
+ dev_wlc_intvar_set(dev, "wsec", val);
+ }
+ val = 0;
+
+ WL_INFORM(("%s: %d: setting wpa_auth to %d\n",
+ __FUNCTION__, __LINE__, val));
+ error = dev_wlc_intvar_set(dev, "wpa_auth", 0);
+ if (error)
+ WL_ERROR(("Failed to set 'wpa_auth'iovar\n"));
+ return error;
+ }
+
+
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ error = dev_wlc_bufvar_set(dev, "wsec_restrict", \
+ (char *)&paramval, sizeof(paramval));
+ if (error)
+ WL_ERROR(("%s: wsec_restrict %d\n", __FUNCTION__, error));
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ error = dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol", \
+ (char *)&paramval, sizeof(paramval));
+ if (error)
+ WL_WSEC(("%s: rx_unencrypted_eapol %d\n", __FUNCTION__, error));
+ break;
+
+#if WIRELESS_EXT > 17
+ case IW_AUTH_ROAMING_CONTROL:
+ WL_INFORM(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__));
+
+ break;
+ case IW_AUTH_PRIVACY_INVOKED: {
+ int wsec;
+
+ if (paramval == 0) {
+ iw->privacy_invoked = FALSE;
+ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
+ WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
+ return error;
+ }
+ } else {
+ iw->privacy_invoked = TRUE;
+ if ((error = dev_wlc_intvar_get(dev, "wsec", &wsec)))
+ return error;
+
+ if (!(IW_WSEC_ENABLED(wsec))) {
+
+ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) {
+ WL_WSEC(("Failed to set iovar is_WPS_enrollee\n"));
+ return error;
+ }
+ } else {
+ if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
+ WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
+ return error;
+ }
+ }
+ }
+ break;
+ }
+#endif
+ case IW_AUTH_WAPI_ENABLED:
+ if ((error = dev_wlc_intvar_get(dev, "wsec", &val)))
+ return error;
+ if (paramval) {
+ val |= SMS4_ENABLED;
+ if ((error = dev_wlc_intvar_set(dev, "wsec", val))) {
+ WL_ERROR(("%s: setting wsec to 0x%0x returned error %d\n",
+ __FUNCTION__, val, error));
+ return error;
+ }
+ if ((error = dev_wlc_intvar_set(dev, "wpa_auth", WPA_AUTH_WAPI))) {
+ WL_ERROR(("%s: setting wpa_auth(WPA_AUTH_WAPI) returned %d\n",
+ __FUNCTION__, error));
+ return error;
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+#ifdef BCMWPA2
+#define VAL_PSK(_val) (((_val) & WPA_AUTH_PSK) || ((_val) & WPA2_AUTH_PSK))
+#else
+#define VAL_PSK(_val) (((_val) & WPA_AUTH_PSK))
+#endif
+
+static int
+wl_iw_get_wpaauth(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *vwrq,
+ char *extra
+)
+{
+ int error;
+ int paramid;
+ int paramval = 0;
+ int val;
+ wl_iw_t *iw = *(wl_iw_t **)netdev_priv(dev);
+
+ WL_TRACE(("%s: SIOCGIWAUTH\n", dev->name));
+
+ paramid = vwrq->flags & IW_AUTH_INDEX;
+
+ switch (paramid) {
+ case IW_AUTH_WPA_VERSION:
+
+ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
+ return error;
+ if (val & (WPA_AUTH_NONE | WPA_AUTH_DISABLED))
+ paramval = IW_AUTH_WPA_VERSION_DISABLED;
+ else if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED))
+ paramval = IW_AUTH_WPA_VERSION_WPA;
+#ifdef BCMWPA2
+ else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED))
+ paramval = IW_AUTH_WPA_VERSION_WPA2;
+#endif
+ break;
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ if (paramid == IW_AUTH_CIPHER_PAIRWISE)
+ val = iw->pwsec;
+ else
+ val = iw->gwsec;
+
+ paramval = 0;
+ if (val) {
+ if (val & WEP_ENABLED)
+ paramval |= (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104);
+ if (val & TKIP_ENABLED)
+ paramval |= (IW_AUTH_CIPHER_TKIP);
+ if (val & AES_ENABLED)
+ paramval |= (IW_AUTH_CIPHER_CCMP);
+ }
+ else
+ paramval = IW_AUTH_CIPHER_NONE;
+ break;
+ case IW_AUTH_KEY_MGMT:
+
+ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
+ return error;
+ if (VAL_PSK(val))
+ paramval = IW_AUTH_KEY_MGMT_PSK;
+ else
+ paramval = IW_AUTH_KEY_MGMT_802_1X;
+
+ break;
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ error = dev_wlc_bufvar_get(dev, "tkip_countermeasures", \
+ (char *)&paramval, sizeof(paramval));
+ if (error)
+ WL_ERROR(("%s get tkip_countermeasures %d\n", __FUNCTION__, error));
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ error = dev_wlc_bufvar_get(dev, "wsec_restrict", \
+ (char *)&paramval, sizeof(paramval));
+ if (error)
+ WL_ERROR(("%s get wsec_restrict %d\n", __FUNCTION__, error));
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ error = dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol", \
+ (char *)&paramval, sizeof(paramval));
+ if (error)
+ WL_ERROR(("%s get rx_unencrypted_eapol %d\n", __FUNCTION__, error));
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+
+ if ((error = dev_wlc_intvar_get(dev, "auth", &val)))
+ return error;
+ if (!val)
+ paramval = IW_AUTH_ALG_OPEN_SYSTEM;
+ else
+ paramval = IW_AUTH_ALG_SHARED_KEY;
+ break;
+ case IW_AUTH_WPA_ENABLED:
+ if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
+ return error;
+ if (val)
+ paramval = TRUE;
+ else
+ paramval = FALSE;
+ break;
+#if WIRELESS_EXT > 17
+ case IW_AUTH_ROAMING_CONTROL:
+ WL_ERROR(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__));
+
+ break;
+ case IW_AUTH_PRIVACY_INVOKED:
+ paramval = iw->privacy_invoked;
+ break;
+#endif
+ }
+ vwrq->value = paramval;
+ return 0;
+}
+#endif
+
+
+#ifdef SOFTAP
+
+static int ap_macmode = MACLIST_MODE_DISABLED;
+static struct mflist ap_black_list;
+static int
+wl_iw_parse_wep(char *keystr, wl_wsec_key_t *key)
+{
+ char hex[] = "XX";
+ unsigned char *data = key->data;
+
+ switch (strlen(keystr)) {
+ case 5:
+ case 13:
+ case 16:
+ key->len = strlen(keystr);
+ memcpy(data, keystr, key->len + 1);
+ break;
+ case 12:
+ case 28:
+ case 34:
+ case 66:
+ if (!strnicmp(keystr, "0x", 2))
+ keystr += 2;
+ else
+ return -1;
+ case 10:
+ case 26:
+ case 32:
+ case 64:
+ key->len = strlen(keystr) / 2;
+ while (*keystr) {
+ strncpy(hex, keystr, 2);
+ *data++ = (char) bcm_strtoul(hex, NULL, 16);
+ keystr += 2;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ switch (key->len) {
+ case 5:
+ key->algo = CRYPTO_ALGO_WEP1;
+ break;
+ case 13:
+ key->algo = CRYPTO_ALGO_WEP128;
+ break;
+ case 16:
+ key->algo = CRYPTO_ALGO_AES_CCM;
+ break;
+ case 32:
+ key->algo = CRYPTO_ALGO_TKIP;
+ break;
+ default:
+ return -1;
+ }
+
+ key->flags |= WL_PRIMARY_KEY;
+
+ return 0;
+}
+
+#ifdef EXT_WPA_CRYPTO
+#define SHA1HashSize 20
+extern void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen);
+
+#else
+
+#define SHA1HashSize 20
+int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen)
+{
+ WL_ERROR(("WARNING: %s is not implemented !!!\n", __FUNCTION__));
+ return -1;
+}
+
+#endif
+
+
+int dev_iw_write_cfg1_bss_var(struct net_device *dev, int val)
+{
+ struct {
+ int cfg;
+ int val;
+ } bss_setbuf;
+
+ int bss_set_res;
+ char smbuf[WLC_IOCTL_SMLEN];
+ memset(smbuf, 0, sizeof(smbuf));
+
+ bss_setbuf.cfg = 1;
+ bss_setbuf.val = val;
+
+ bss_set_res = dev_iw_iovar_setbuf(dev, "bss",
+ &bss_setbuf, sizeof(bss_setbuf), smbuf, sizeof(smbuf));
+ WL_TRACE(("%s: bss_set_result:%d set with %d\n", __FUNCTION__, bss_set_res, val));
+
+ return bss_set_res;
+}
+
+
+int dev_iw_read_cfg1_bss_var(struct net_device *dev, int *val)
+{
+ int bsscfg_idx = 1;
+ int bss_set_res;
+ char smbuf[WLC_IOCTL_SMLEN];
+ memset(smbuf, 0, sizeof(smbuf));
+
+ bss_set_res = dev_iw_iovar_getbuf(dev, "bss", \
+ &bsscfg_idx, sizeof(bsscfg_idx), smbuf, sizeof(smbuf));
+ *val = *(int*)smbuf;
+ *val = dtoh32(*val);
+ WL_TRACE(("%s: status=%d bss_get_result=%d\n", __FUNCTION__, bss_set_res, *val));
+ return bss_set_res;
+}
+
+
+#ifndef AP_ONLY
+static int wl_bssiovar_mkbuf(
+ const char *iovar,
+ int bssidx,
+ void *param,
+ int paramlen,
+ void *bufptr,
+ int buflen,
+ int *perr)
+{
+ const char *prefix = "bsscfg:";
+ int8 *p;
+ uint prefixlen;
+ uint namelen;
+ uint iolen;
+
+ prefixlen = strlen(prefix);
+ namelen = strlen(iovar) + 1;
+ iolen = prefixlen + namelen + sizeof(int) + paramlen;
+
+ if (buflen < 0 || iolen > (uint)buflen) {
+ *perr = BCME_BUFTOOSHORT;
+ return 0;
+ }
+
+ p = (int8 *)bufptr;
+
+ memcpy(p, prefix, prefixlen);
+ p += prefixlen;
+
+ memcpy(p, iovar, namelen);
+ p += namelen;
+
+ bssidx = htod32(bssidx);
+ memcpy(p, &bssidx, sizeof(int32));
+ p += sizeof(int32);
+
+ if (paramlen)
+ memcpy(p, param, paramlen);
+
+ *perr = 0;
+ return iolen;
+}
+#endif
+
+
+int get_user_params(char *user_params, struct iw_point *dwrq)
+{
+ int ret = 0;
+
+ if (copy_from_user(user_params, dwrq->pointer, dwrq->length)) {
+ WL_ERROR(("\n%s: no user params: uptr:%p, ulen:%d\n",
+ __FUNCTION__, dwrq->pointer, dwrq->length));
+ return -EFAULT;
+ }
+
+ WL_TRACE(("\n%s: iwpriv user params:%s\n", __FUNCTION__, user_params));
+
+ return ret;
+}
+
+
+#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
+
+#if defined(CSCAN)
+
+static int
+wl_iw_combined_scan_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, int nchan)
+{
+ int params_size = WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
+ int err = 0;
+ char *p;
+ int i;
+ iscan_info_t *iscan = g_iscan;
+
+ WL_SCAN(("%s nssid=%d nchan=%d\n", __FUNCTION__, nssid, nchan));
+
+ if ((!dev) && (!g_iscan) && (!iscan->iscan_ex_params_p)) {
+ WL_ERROR(("%s error exit\n", __FUNCTION__));
+ err = -1;
+ goto exit;
+ }
+
+#ifdef PNO_SUPPORT
+ if (dhd_dev_get_pno_status(dev)) {
+ WL_ERROR(("%s: Scan called when PNO is active\n", __FUNCTION__));
+ }
+#endif
+
+ params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
+
+ if (nssid > 0) {
+ i = OFFSETOF(wl_scan_params_t, channel_list) + nchan * sizeof(uint16);
+ i = ROUNDUP(i, sizeof(uint32));
+ if (i + nssid * sizeof(wlc_ssid_t) > params_size) {
+ printf("additional ssids exceed params_size\n");
+ err = -1;
+ goto exit;
+ }
+
+ p = ((char*)&iscan->iscan_ex_params_p->params) + i;
+ memcpy(p, ssids_local, nssid * sizeof(wlc_ssid_t));
+ p += nssid * sizeof(wlc_ssid_t);
+ } else {
+ p = (char*)iscan->iscan_ex_params_p->params.channel_list + nchan * sizeof(uint16);
+ }
+
+ iscan->iscan_ex_params_p->params.channel_num = \
+ htod32((nssid << WL_SCAN_PARAMS_NSSID_SHIFT) | \
+ (nchan & WL_SCAN_PARAMS_COUNT_MASK));
+
+ nssid = \
+ (uint)((iscan->iscan_ex_params_p->params.channel_num >> WL_SCAN_PARAMS_NSSID_SHIFT) & \
+ WL_SCAN_PARAMS_COUNT_MASK);
+
+ params_size = (int) (p - (char*)iscan->iscan_ex_params_p + nssid * sizeof(wlc_ssid_t));
+ iscan->iscan_ex_param_size = params_size;
+
+ iscan->list_cur = iscan->list_hdr;
+ iscan->iscan_state = ISCAN_STATE_SCANING;
+ wl_iw_set_event_mask(dev);
+ mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000);
+
+ iscan->timer_on = 1;
+
+#ifdef SCAN_DUMP
+ {
+ int i;
+ WL_SCAN(("\n### List of SSIDs to scan ###\n"));
+ for (i = 0; i < nssid; i++) {
+ if (!ssids_local[i].SSID_len)
+ WL_SCAN(("%d: Broadcast scan\n", i));
+ else
+ WL_SCAN(("%d: scan for %s size =%d\n", i, \
+ ssids_local[i].SSID, ssids_local[i].SSID_len));
+ }
+ WL_SCAN(("### List of channels to scan ###\n"));
+ for (i = 0; i < nchan; i++)
+ {
+ WL_SCAN(("%d ", iscan->iscan_ex_params_p->params.channel_list[i]));
+ }
+ WL_SCAN(("\nnprobes=%d\n", iscan->iscan_ex_params_p->params.nprobes));
+ WL_SCAN(("active_time=%d\n", iscan->iscan_ex_params_p->params.active_time));
+ WL_SCAN(("passive_time=%d\n", iscan->iscan_ex_params_p->params.passive_time));
+ WL_SCAN(("home_time=%d\n", iscan->iscan_ex_params_p->params.home_time));
+ WL_SCAN(("scan_type=%d\n", iscan->iscan_ex_params_p->params.scan_type));
+ WL_SCAN(("\n###################\n"));
+ }
+#endif
+
+ if (params_size > WLC_IOCTL_MEDLEN) {
+ WL_ERROR(("Set ISCAN for %s due to params_size=%d \n", \
+ __FUNCTION__, params_size));
+ err = -1;
+ }
+
+ if ((err = dev_iw_iovar_setbuf(dev, "iscan", iscan->iscan_ex_params_p, \
+ iscan->iscan_ex_param_size, \
+ iscan->ioctlbuf, sizeof(iscan->ioctlbuf)))) {
+ WL_ERROR(("Set ISCAN for %s failed with %d\n", __FUNCTION__, err));
+ err = -1;
+ }
+
+exit:
+
+ return err;
+}
+
+
+static int iwpriv_set_cscan(struct net_device *dev, struct iw_request_info *info, \
+ union iwreq_data *wrqu, char *ext)
+{
+ int res = 0;
+ char *extra = NULL;
+ iscan_info_t *iscan = g_iscan;
+ wlc_ssid_t ssids_local[WL_SCAN_PARAMS_SSID_MAX];
+ int nssid = 0;
+ int nchan = 0;
+
+ WL_TRACE(("\%s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n",
+ __FUNCTION__, info->cmd, info->flags,
+ wrqu->data.pointer, wrqu->data.length));
+
+ if (g_onoff == G_WLAN_SET_OFF) {
+ WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__));
+ return -1;
+ }
+
+ if (wrqu->data.length != 0) {
+
+ char *str_ptr;
+
+ if (!iscan->iscan_ex_params_p) {
+ return -EFAULT;
+ }
+
+ if (!(extra = kmalloc(wrqu->data.length+1, GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) {
+ kfree(extra);
+ return -EFAULT;
+ }
+
+ extra[wrqu->data.length] = 0;
+ WL_ERROR(("Got str param in iw_point:\n %s\n", extra));
+
+ str_ptr = extra;
+
+ if (strncmp(str_ptr, GET_SSID, strlen(GET_SSID))) {
+ WL_ERROR(("%s Error: extracting SSID='' string\n", __FUNCTION__));
+ goto exit_proc;
+ }
+ str_ptr += strlen(GET_SSID);
+ nssid = wl_iw_parse_ssid_list(&str_ptr, ssids_local, nssid, \
+ WL_SCAN_PARAMS_SSID_MAX);
+ if (nssid == -1) {
+ WL_ERROR(("%s wrong ssid list", __FUNCTION__));
+ return -1;
+ }
+
+ if (iscan->iscan_ex_param_size > WLC_IOCTL_MAXLEN) {
+ WL_ERROR(("%s wrong ex_param_size %d", \
+ __FUNCTION__, iscan->iscan_ex_param_size));
+ return -1;
+ }
+ memset(iscan->iscan_ex_params_p, 0, iscan->iscan_ex_param_size);
+
+
+ wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, NULL);
+ iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION);
+ iscan->iscan_ex_params_p->action = htod16(WL_SCAN_ACTION_START);
+ iscan->iscan_ex_params_p->scan_duration = htod16(0);
+
+
+ if ((nchan = wl_iw_parse_channel_list(&str_ptr, \
+ &iscan->iscan_ex_params_p->params.channel_list[0], \
+ WL_NUMCHANNELS)) == -1) {
+ WL_ERROR(("%s missing channel list\n", __FUNCTION__));
+ return -1;
+ }
+
+
+ get_parmeter_from_string(&str_ptr, \
+ GET_NPROBE, PTYPE_INTDEC, \
+ &iscan->iscan_ex_params_p->params.nprobes, 2);
+
+ get_parmeter_from_string(&str_ptr, GET_ACTIVE_ASSOC_DWELL, PTYPE_INTDEC, \
+ &iscan->iscan_ex_params_p->params.active_time, 4);
+
+ get_parmeter_from_string(&str_ptr, GET_PASSIVE_ASSOC_DWELL, PTYPE_INTDEC, \
+ &iscan->iscan_ex_params_p->params.passive_time, 4);
+
+ get_parmeter_from_string(&str_ptr, GET_HOME_DWELL, PTYPE_INTDEC, \
+ &iscan->iscan_ex_params_p->params.home_time, 4);
+
+ get_parmeter_from_string(&str_ptr, GET_SCAN_TYPE, PTYPE_INTDEC, \
+ &iscan->iscan_ex_params_p->params.scan_type, 1);
+
+ res = wl_iw_combined_scan_set(dev, ssids_local, nssid, nchan);
+
+ } else {
+ WL_ERROR(("IWPRIV argument len = 0 \n"));
+ return -1;
+ }
+
+exit_proc:
+
+ kfree(extra);
+
+ return res;
+}
+
+
+static int
+wl_iw_set_cscan(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra
+)
+{
+ int res = -1;
+ iscan_info_t *iscan = g_iscan;
+ wlc_ssid_t ssids_local[WL_SCAN_PARAMS_SSID_MAX];
+ int nssid = 0;
+ int nchan = 0;
+ cscan_tlv_t *cscan_tlv_temp;
+ char type;
+ char *str_ptr;
+ int tlv_size_left;
+#ifdef TLV_DEBUG
+ int i;
+ char tlv_in_example[] = { 'C', 'S', 'C', 'A', 'N', ' ', \
+ 0x53, 0x01, 0x00, 0x00,
+ 'S',
+ 0x00,
+ 'S',
+ 0x04,
+ 'B', 'R', 'C', 'M',
+ 'C',
+ 0x06,
+ 'P',
+ 0x94,
+ 0x11,
+ 'T',
+ 0x01
+ };
+#endif
+
+ WL_TRACE(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n",
+ __FUNCTION__, info->cmd, info->flags,
+ wrqu->data.pointer, wrqu->data.length));
+
+ net_os_wake_lock(dev);
+
+ if (g_onoff == G_WLAN_SET_OFF) {
+ WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__));
+ goto exit_proc;
+ }
+
+
+ if (wrqu->data.length < (strlen(CSCAN_COMMAND) + sizeof(cscan_tlv_t))) {
+ WL_ERROR(("%s aggument=%d less %d\n", __FUNCTION__, \
+ wrqu->data.length, strlen(CSCAN_COMMAND) + sizeof(cscan_tlv_t)));
+ goto exit_proc;
+ }
+
+#ifdef TLV_DEBUG
+ memcpy(extra, tlv_in_example, sizeof(tlv_in_example));
+ wrqu->data.length = sizeof(tlv_in_example);
+ for (i = 0; i < wrqu->data.length; i++)
+ printf("%02X ", extra[i]);
+ printf("\n");
+#endif
+
+ str_ptr = extra;
+ str_ptr += strlen(CSCAN_COMMAND);
+ tlv_size_left = wrqu->data.length - strlen(CSCAN_COMMAND);
+
+ cscan_tlv_temp = (cscan_tlv_t *)str_ptr;
+ memset(ssids_local, 0, sizeof(ssids_local));
+
+ if ((cscan_tlv_temp->prefix == CSCAN_TLV_PREFIX) && \
+ (cscan_tlv_temp->version == CSCAN_TLV_VERSION) && \
+ (cscan_tlv_temp->subver == CSCAN_TLV_SUBVERSION))
+ {
+ str_ptr += sizeof(cscan_tlv_t);
+ tlv_size_left -= sizeof(cscan_tlv_t);
+
+
+ if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, \
+ WL_SCAN_PARAMS_SSID_MAX, &tlv_size_left)) <= 0) {
+ WL_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid));
+ goto exit_proc;
+ }
+ else {
+
+ memset(iscan->iscan_ex_params_p, 0, iscan->iscan_ex_param_size);
+
+
+ wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, NULL);
+ iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION);
+ iscan->iscan_ex_params_p->action = htod16(WL_SCAN_ACTION_START);
+ iscan->iscan_ex_params_p->scan_duration = htod16(0);
+
+
+ while (tlv_size_left > 0)
+ {
+ type = str_ptr[0];
+ switch (type) {
+ case CSCAN_TLV_TYPE_CHANNEL_IE:
+
+ if ((nchan = wl_iw_parse_channel_list_tlv(&str_ptr, \
+ &iscan->iscan_ex_params_p->params.channel_list[0], \
+ WL_NUMCHANNELS, &tlv_size_left)) == -1) {
+ WL_ERROR(("%s missing channel list\n", \
+ __FUNCTION__));
+ goto exit_proc;
+ }
+ break;
+ case CSCAN_TLV_TYPE_NPROBE_IE:
+ if ((res = wl_iw_parse_data_tlv(&str_ptr, \
+ &iscan->iscan_ex_params_p->params.nprobes, \
+ sizeof(iscan->iscan_ex_params_p->params.nprobes), \
+ type, sizeof(char), &tlv_size_left)) == -1) {
+ WL_ERROR(("%s return %d\n", \
+ __FUNCTION__, res));
+ goto exit_proc;
+ }
+ break;
+ case CSCAN_TLV_TYPE_ACTIVE_IE:
+ if ((res = wl_iw_parse_data_tlv(&str_ptr, \
+ &iscan->iscan_ex_params_p->params.active_time, \
+ sizeof(iscan->iscan_ex_params_p->params.active_time), \
+ type, sizeof(short), &tlv_size_left)) == -1) {
+ WL_ERROR(("%s return %d\n", \
+ __FUNCTION__, res));
+ goto exit_proc;
+ }
+ break;
+ case CSCAN_TLV_TYPE_PASSIVE_IE:
+ if ((res = wl_iw_parse_data_tlv(&str_ptr, \
+ &iscan->iscan_ex_params_p->params.passive_time, \
+ sizeof(iscan->iscan_ex_params_p->params.passive_time), \
+ type, sizeof(short), &tlv_size_left)) == -1) {
+ WL_ERROR(("%s return %d\n", \
+ __FUNCTION__, res));
+ goto exit_proc;
+ }
+ break;
+ case CSCAN_TLV_TYPE_HOME_IE:
+ if ((res = wl_iw_parse_data_tlv(&str_ptr, \
+ &iscan->iscan_ex_params_p->params.home_time, \
+ sizeof(iscan->iscan_ex_params_p->params.home_time), \
+ type, sizeof(short), &tlv_size_left)) == -1) {
+ WL_ERROR(("%s return %d\n", \
+ __FUNCTION__, res));
+ goto exit_proc;
+ }
+ break;
+ case CSCAN_TLV_TYPE_STYPE_IE:
+ if ((res = wl_iw_parse_data_tlv(&str_ptr, \
+ &iscan->iscan_ex_params_p->params.scan_type, \
+ sizeof(iscan->iscan_ex_params_p->params.scan_type), \
+ type, sizeof(char), &tlv_size_left)) == -1) {
+ WL_ERROR(("%s return %d\n", \
+ __FUNCTION__, res));
+ goto exit_proc;
+ }
+ break;
+
+ default :
+ WL_ERROR(("%s get unkwown type %X\n", \
+ __FUNCTION__, type));
+ goto exit_proc;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ WL_ERROR(("%s get wrong TLV command\n", __FUNCTION__));
+ goto exit_proc;
+ }
+
+#if defined(CONFIG_FIRST_SCAN)
+ if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_CONSUMED) {
+ if (++g_first_counter_scans == MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN) {
+
+ WL_ERROR(("%s Clean up First scan flag which is %d\n", \
+ __FUNCTION__, g_first_broadcast_scan));
+ g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED;
+ }
+ else {
+ WL_ERROR(("%s Ignoring CSCAN : First Scan is not done yet %d\n", \
+ __FUNCTION__, g_first_counter_scans));
+ res = -EBUSY;
+ goto exit_proc;
+ }
+ }
+#endif
+
+ res = wl_iw_combined_scan_set(dev, ssids_local, nssid, nchan);
+
+exit_proc:
+ net_os_wake_unlock(dev);
+ return res;
+}
+
+#endif
+
+#ifdef SOFTAP
+#ifndef AP_ONLY
+
+static int thr_wait_for_2nd_eth_dev(void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ wl_iw_t *iw;
+ int ret = 0;
+ unsigned long flags;
+
+ net_os_wake_lock(dev);
+
+ DAEMONIZE("wl0_eth_wthread");
+
+ WL_TRACE(("\n>%s thread started:, PID:%x\n", __FUNCTION__, current->pid));
+ iw = *(wl_iw_t **)netdev_priv(dev);
+ if (!iw) {
+ WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+ ret = -1;
+ goto fail;
+ }
+
+#ifndef BCMSDIOH_STD
+ if (down_timeout(&ap_eth_sema, msecs_to_jiffies(5000)) != 0) {
+ WL_ERROR(("\n%s: sap_eth_sema timeout \n", __FUNCTION__));
+ ret = -1;
+ goto fail;
+ }
+#endif
+
+ flags = dhd_os_spin_lock(iw->pub);
+ if (!ap_net_dev) {
+ WL_ERROR((" ap_net_dev is null !!!"));
+ ret = -1;
+ dhd_os_spin_unlock(iw->pub, flags);
+ goto fail;
+ }
+
+ WL_TRACE(("\n>%s: Thread:'softap ethdev IF:%s is detected !!!'\n\n",
+ __FUNCTION__, ap_net_dev->name));
+
+ ap_cfg_running = TRUE;
+
+ dhd_os_spin_unlock(iw->pub, flags);
+
+ bcm_mdelay(500);
+
+ wl_iw_send_priv_event(priv_dev, "AP_SET_CFG_OK");
+
+fail:
+ WL_TRACE(("\n>%s, thread completed\n", __FUNCTION__));
+
+ net_os_wake_unlock(dev);
+
+ complete_and_exit(&ap_cfg_exited, 0);
+ return ret;
+}
+#endif
+#ifndef AP_ONLY
+static int last_auto_channel = 6;
+#endif
+static int get_softap_auto_channel(struct net_device *dev, struct ap_profile *ap)
+{
+ int chosen = 0;
+ wl_uint32_list_t request;
+ int rescan = 0;
+ int retry = 0;
+ int updown = 0;
+ int ret = 0;
+ wlc_ssid_t null_ssid;
+ int res = 0;
+#ifndef AP_ONLY
+ int iolen = 0;
+ int mkvar_err = 0;
+ int bsscfg_index = 1;
+ char buf[WLC_IOCTL_SMLEN];
+#endif
+ WL_SOFTAP(("Enter %s\n", __FUNCTION__));
+
+#ifndef AP_ONLY
+ if (ap_cfg_running) {
+ ap->channel = last_auto_channel;
+ return res;
+ }
+#endif
+ memset(&null_ssid, 0, sizeof(wlc_ssid_t));
+ res |= dev_wlc_ioctl(dev, WLC_UP, &updown, sizeof(updown));
+#ifdef AP_ONLY
+ res |= dev_wlc_ioctl(dev, WLC_SET_SSID, &null_ssid, sizeof(null_ssid));
+#else
+ iolen = wl_bssiovar_mkbuf("ssid", bsscfg_index, (char *)(&null_ssid), \
+ null_ssid.SSID_len+4, buf, sizeof(buf), &mkvar_err);
+ ASSERT(iolen);
+ res |= dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen);
+#endif
+ auto_channel_retry:
+ request.count = htod32(0);
+ ret = dev_wlc_ioctl(dev, WLC_START_CHANNEL_SEL, &request, sizeof(request));
+ if (ret < 0) {
+ WL_ERROR(("can't start auto channel scan\n"));
+ goto fail;
+ }
+
+ get_channel_retry:
+ bcm_mdelay(500);
+
+ ret = dev_wlc_ioctl(dev, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen));
+ if (ret < 0 || dtoh32(chosen) == 0) {
+ if (retry++ < 3)
+ goto get_channel_retry;
+ else {
+ WL_ERROR(("can't get auto channel sel, err = %d, \
+ chosen = %d\n", ret, chosen));
+ goto fail;
+ }
+ }
+ if ((chosen == 1) && (!rescan++))
+ goto auto_channel_retry;
+ WL_SOFTAP(("Set auto channel = %d\n", chosen));
+ ap->channel = chosen;
+ if ((res = dev_wlc_ioctl(dev, WLC_DOWN, &updown, sizeof(updown))) < 0) {
+ WL_ERROR(("%s fail to set up err =%d\n", __FUNCTION__, res));
+ goto fail;
+ }
+#ifndef AP_ONLY
+ if (!res)
+ last_auto_channel = ap->channel;
+#endif
+
+fail :
+ return res;
+}
+
+
+static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap)
+{
+ int updown = 0;
+ int channel = 0;
+
+ wlc_ssid_t ap_ssid;
+ int max_assoc = 8;
+
+ int res = 0;
+ int apsta_var = 0;
+#ifndef AP_ONLY
+ int mpc = 0;
+ int iolen = 0;
+ int mkvar_err = 0;
+ int bsscfg_index = 1;
+ char buf[WLC_IOCTL_SMLEN];
+#endif
+
+ if (!dev) {
+ WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+ return -1;
+ }
+
+ net_os_wake_lock(dev);
+
+ WL_SOFTAP(("wl_iw: set ap profile:\n"));
+ WL_SOFTAP((" ssid = '%s'\n", ap->ssid));
+ WL_SOFTAP((" security = '%s'\n", ap->sec));
+ if (ap->key[0] != '\0')
+ WL_SOFTAP((" key = '%s'\n", ap->key));
+ WL_SOFTAP((" channel = %d\n", ap->channel));
+ WL_SOFTAP((" max scb = %d\n", ap->max_scb));
+
+#ifdef AP_ONLY
+ if (ap_cfg_running) {
+ wl_iw_softap_deassoc_stations(dev, NULL);
+ ap_cfg_running = FALSE;
+ }
+#endif
+
+ if (ap_cfg_running == FALSE) {
+
+#ifndef AP_ONLY
+ sema_init(&ap_eth_sema, 0);
+
+ mpc = 0;
+ if ((res = dev_wlc_intvar_set(dev, "mpc", mpc))) {
+ WL_ERROR(("%s fail to set mpc\n", __FUNCTION__));
+ goto fail;
+ }
+#endif
+
+ updown = 0;
+ if ((res = dev_wlc_ioctl(dev, WLC_DOWN, &updown, sizeof(updown)))) {
+ WL_ERROR(("%s fail to set updown\n", __FUNCTION__));
+ goto fail;
+ }
+
+#ifdef AP_ONLY
+ apsta_var = 0;
+ if ((res = dev_wlc_ioctl(dev, WLC_SET_AP, &apsta_var, sizeof(apsta_var)))) {
+ WL_ERROR(("%s fail to set apsta_var 0\n", __FUNCTION__));
+ goto fail;
+ }
+ apsta_var = 1;
+ if ((res = dev_wlc_ioctl(dev, WLC_SET_AP, &apsta_var, sizeof(apsta_var)))) {
+ WL_ERROR(("%s fail to set apsta_var 1\n", __FUNCTION__));
+ goto fail;
+ }
+ res = dev_wlc_ioctl(dev, WLC_GET_AP, &apsta_var, sizeof(apsta_var));
+#else
+ apsta_var = 1;
+ iolen = wl_bssiovar_mkbuf("apsta",
+ bsscfg_index, &apsta_var, sizeof(apsta_var)+4,
+ buf, sizeof(buf), &mkvar_err);
+
+ if (iolen <= 0)
+ goto fail;
+
+ if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) < 0) {
+ WL_ERROR(("%s fail to set apsta \n", __FUNCTION__));
+ goto fail;
+ }
+ WL_TRACE(("\n>in %s: apsta set result: %d \n", __FUNCTION__, res));
+#endif
+
+ updown = 1;
+ if ((res = dev_wlc_ioctl(dev, WLC_UP, &updown, sizeof(updown))) < 0) {
+ WL_ERROR(("%s fail to set apsta \n", __FUNCTION__));
+ goto fail;
+ }
+
+ } else {
+
+ if (!ap_net_dev) {
+ WL_ERROR(("%s: ap_net_dev is null\n", __FUNCTION__));
+ goto fail;
+ }
+
+ res = wl_iw_softap_deassoc_stations(ap_net_dev, NULL);
+
+
+ if ((res = dev_iw_write_cfg1_bss_var(dev, 0)) < 0) {
+ WL_ERROR(("%s fail to set bss down\n", __FUNCTION__));
+ goto fail;
+ }
+ }
+
+ if (strlen(ap->country_code)) {
+ int error = 0;
+ if ((error = dev_wlc_ioctl(dev, WLC_SET_COUNTRY,
+ ap->country_code, sizeof(ap->country_code))) >= 0) {
+ WL_SOFTAP(("%s: set country %s OK\n",
+ __FUNCTION__, ap->country_code));
+ dhd_bus_country_set(dev, &ap->country_code[0]);
+ } else {
+ WL_ERROR(("%s: ERROR:%d setting country %s\n",
+ __FUNCTION__, error, ap->country_code));
+ }
+ } else {
+ WL_SOFTAP(("%s: Country code is not specified,"
+ " will use Radio's default\n",
+ __FUNCTION__));
+ }
+
+ iolen = wl_bssiovar_mkbuf("closednet",
+ bsscfg_index, &ap->closednet, sizeof(ap->closednet)+4,
+ buf, sizeof(buf), &mkvar_err);
+ ASSERT(iolen);
+ if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) < 0) {
+ WL_ERROR(("%s failed to set 'closednet'for apsta \n", __FUNCTION__));
+ goto fail;
+ }
+
+
+ if ((ap->channel == 0) && (get_softap_auto_channel(dev, ap) < 0)) {
+ ap->channel = 1;
+ WL_ERROR(("%s auto channel failed, pick up channel=%d\n", \
+ __FUNCTION__, ap->channel));
+ }
+
+ channel = ap->channel;
+ if ((res = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &channel, sizeof(channel)))) {
+ WL_ERROR(("%s fail to set channel\n", __FUNCTION__));
+ goto fail;
+ }
+
+ if (ap_cfg_running == FALSE) {
+ updown = 0;
+ if ((res = dev_wlc_ioctl(dev, WLC_UP, &updown, sizeof(updown)))) {
+ WL_ERROR(("%s fail to set up\n", __FUNCTION__));
+ goto fail;
+ }
+ }
+
+ max_assoc = ap->max_scb;
+ if ((res = dev_wlc_intvar_set(dev, "maxassoc", max_assoc))) {
+ WL_ERROR(("%s fail to set maxassoc\n", __FUNCTION__));
+ goto fail;
+ }
+
+ ap_ssid.SSID_len = strlen(ap->ssid);
+ strncpy(ap_ssid.SSID, ap->ssid, ap_ssid.SSID_len);
+
+#ifdef AP_ONLY
+ if ((res = wl_iw_set_ap_security(dev, &my_ap)) != 0) {
+ WL_ERROR(("ERROR:%d in:%s, wl_iw_set_ap_security is skipped\n", \
+ res, __FUNCTION__));
+ goto fail;
+ }
+ wl_iw_send_priv_event(dev, "ASCII_CMD=AP_BSS_START");
+ ap_cfg_running = TRUE;
+#else
+ iolen = wl_bssiovar_mkbuf("ssid", bsscfg_index, (char *)(&ap_ssid),
+ ap_ssid.SSID_len+4, buf, sizeof(buf), &mkvar_err);
+ ASSERT(iolen);
+ if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) != 0) {
+ WL_ERROR(("ERROR:%d in:%s, Security & BSS reconfiguration is skipped\n", \
+ res, __FUNCTION__));
+ goto fail;
+ }
+ if (ap_cfg_running == FALSE) {
+ init_completion(&ap_cfg_exited);
+ ap_cfg_pid = kernel_thread(thr_wait_for_2nd_eth_dev, dev, 0);
+ } else {
+ ap_cfg_pid = -1;
+ if (ap_net_dev == NULL) {
+ WL_ERROR(("%s ERROR: ap_net_dev is NULL !!!\n", __FUNCTION__));
+ goto fail;
+ }
+
+ WL_ERROR(("%s: %s Configure security & restart AP bss \n", \
+ __FUNCTION__, ap_net_dev->name));
+
+ if ((res = wl_iw_set_ap_security(ap_net_dev, &my_ap)) < 0) {
+ WL_ERROR(("%s fail to set security : %d\n", __FUNCTION__, res));
+ goto fail;
+ }
+
+ if ((res = dev_iw_write_cfg1_bss_var(dev, 1)) < 0) {
+ WL_ERROR(("%s fail to set bss up\n", __FUNCTION__));
+ goto fail;
+ }
+ }
+#endif
+fail:
+ WL_SOFTAP(("%s exit with %d\n", __FUNCTION__, res));
+
+ net_os_wake_unlock(dev);
+
+ return res;
+}
+
+
+static int wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap)
+{
+ int wsec = 0;
+ int wpa_auth = 0;
+ int res = 0;
+ int i;
+ char *ptr;
+#ifdef AP_ONLY
+ int mpc = 0;
+ wlc_ssid_t ap_ssid;
+#endif
+ wl_wsec_key_t key;
+
+ WL_SOFTAP(("\nsetting SOFTAP security mode:\n"));
+ WL_SOFTAP(("wl_iw: set ap profile:\n"));
+ WL_SOFTAP((" ssid = '%s'\n", ap->ssid));
+ WL_SOFTAP((" security = '%s'\n", ap->sec));
+ if (ap->key[0] != '\0') {
+ WL_SOFTAP((" key = '%s'\n", ap->key));
+ }
+ WL_SOFTAP((" channel = %d\n", ap->channel));
+ WL_SOFTAP((" max scb = %d\n", ap->max_scb));
+
+ if (strnicmp(ap->sec, "open", strlen("open")) == 0) {
+ wsec = 0;
+ res = dev_wlc_intvar_set(dev, "wsec", wsec);
+ wpa_auth = WPA_AUTH_DISABLED;
+ res |= dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth);
+
+ WL_SOFTAP(("=====================\n"));
+ WL_SOFTAP((" wsec & wpa_auth set 'OPEN', result:&d %d\n", res));
+ WL_SOFTAP(("=====================\n"));
+
+ } else if (strnicmp(ap->sec, "wep", strlen("wep")) == 0) {
+
+ memset(&key, 0, sizeof(key));
+
+ wsec = WEP_ENABLED;
+ res = dev_wlc_intvar_set(dev, "wsec", wsec);
+
+ key.index = 0;
+ if (wl_iw_parse_wep(ap->key, &key)) {
+ WL_SOFTAP(("wep key parse err!\n"));
+ return -1;
+ }
+
+ key.index = htod32(key.index);
+ key.len = htod32(key.len);
+ key.algo = htod32(key.algo);
+ key.flags = htod32(key.flags);
+
+ res |= dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+
+ wpa_auth = WPA_AUTH_DISABLED;
+ res |= dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth);
+
+ WL_SOFTAP(("=====================\n"));
+ WL_SOFTAP((" wsec & auth set 'WEP', result:&d %d\n", res));
+ WL_SOFTAP(("=====================\n"));
+
+ } else if (strnicmp(ap->sec, "wpa2-psk", strlen("wpa2-psk")) == 0) {
+ wsec_pmk_t psk;
+ size_t key_len;
+
+ wsec = AES_ENABLED;
+ dev_wlc_intvar_set(dev, "wsec", wsec);
+
+ key_len = strlen(ap->key);
+ if (key_len < WSEC_MIN_PSK_LEN || key_len > WSEC_MAX_PSK_LEN) {
+ WL_SOFTAP(("passphrase must be between %d and %d characters long\n",
+ WSEC_MIN_PSK_LEN, WSEC_MAX_PSK_LEN));
+ return -1;
+ }
+
+ if (key_len < WSEC_MAX_PSK_LEN) {
+ unsigned char output[2*SHA1HashSize];
+ char key_str_buf[WSEC_MAX_PSK_LEN+1];
+
+ memset(output, 0, sizeof(output));
+ pbkdf2_sha1(ap->key, ap->ssid, strlen(ap->ssid), 4096, output, 32);
+
+ ptr = key_str_buf;
+ for (i = 0; i < (WSEC_MAX_PSK_LEN/8); i++) {
+ sprintf(ptr, "%02x%02x%02x%02x", (uint)output[i*4], \
+ (uint)output[i*4+1], (uint)output[i*4+2], \
+ (uint)output[i*4+3]);
+ ptr += 8;
+ }
+ WL_SOFTAP(("%s: passphase = %s\n", __FUNCTION__, key_str_buf));
+
+ psk.key_len = htod16((ushort)WSEC_MAX_PSK_LEN);
+ memcpy(psk.key, key_str_buf, psk.key_len);
+ } else {
+ psk.key_len = htod16((ushort) key_len);
+ memcpy(psk.key, ap->key, key_len);
+ }
+ psk.flags = htod16(WSEC_PASSPHRASE);
+ dev_wlc_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk));
+
+ wpa_auth = WPA2_AUTH_PSK;
+ dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth);
+
+ } else if (strnicmp(ap->sec, "wpa-psk", strlen("wpa-psk")) == 0) {
+
+ wsec_pmk_t psk;
+ size_t key_len;
+
+ wsec = TKIP_ENABLED;
+ res = dev_wlc_intvar_set(dev, "wsec", wsec);
+
+ key_len = strlen(ap->key);
+ if (key_len < WSEC_MIN_PSK_LEN || key_len > WSEC_MAX_PSK_LEN) {
+ WL_SOFTAP(("passphrase must be between %d and %d characters long\n",
+ WSEC_MIN_PSK_LEN, WSEC_MAX_PSK_LEN));
+ return -1;
+ }
+
+ if (key_len < WSEC_MAX_PSK_LEN) {
+ unsigned char output[2*SHA1HashSize];
+ char key_str_buf[WSEC_MAX_PSK_LEN+1];
+ bzero(output, 2*SHA1HashSize);
+
+ WL_SOFTAP(("%s: do passhash...\n", __FUNCTION__));
+
+ pbkdf2_sha1(ap->key, ap->ssid, strlen(ap->ssid), 4096, output, 32);
+
+ ptr = key_str_buf;
+ for (i = 0; i < (WSEC_MAX_PSK_LEN/8); i++) {
+ WL_SOFTAP(("[%02d]: %08x\n", i, *((unsigned int *)&output[i*4])));
+
+ sprintf(ptr, "%02x%02x%02x%02x", (uint)output[i*4],
+ (uint)output[i*4+1], (uint)output[i*4+2],
+ (uint)output[i*4+3]);
+ ptr += 8;
+ }
+ WL_SOFTAP(("%s: passphase = %s\n", __FUNCTION__, key_str_buf));
+
+ psk.key_len = htod16((ushort)WSEC_MAX_PSK_LEN);
+ memcpy(psk.key, key_str_buf, psk.key_len);
+ } else {
+ psk.key_len = htod16((ushort) key_len);
+ memcpy(psk.key, ap->key, key_len);
+ }
+
+ psk.flags = htod16(WSEC_PASSPHRASE);
+ res |= dev_wlc_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk));
+
+ wpa_auth = WPA_AUTH_PSK;
+ res |= dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth);
+
+ WL_SOFTAP((" wsec & auth set 'wpa-psk' (TKIP), result:&d %d\n", res));
+ }
+
+#ifdef AP_ONLY
+ ap_ssid.SSID_len = strlen(ap->ssid);
+ strncpy(ap_ssid.SSID, ap->ssid, ap_ssid.SSID_len);
+ res |= dev_wlc_ioctl(dev, WLC_SET_SSID, &ap_ssid, sizeof(ap_ssid));
+ mpc = 0;
+ res |= dev_wlc_intvar_set(dev, "mpc", mpc);
+ if (strnicmp(ap->sec, "wep", strlen("wep")) == 0) {
+ res |= dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
+ }
+#endif
+ return res;
+}
+
+
+
+int get_parmeter_from_string(
+ char **str_ptr, const char *token,
+ int param_type, void *dst, int param_max_len)
+{
+ char int_str[7] = "0";
+ int parm_str_len;
+ char *param_str_begin;
+ char *param_str_end;
+
+ if ((*str_ptr) && !strncmp(*str_ptr, token, strlen(token))) {
+
+ strsep(str_ptr, "=,");
+ param_str_begin = *str_ptr;
+ strsep(str_ptr, "=,");
+
+ if (*str_ptr == NULL) {
+ parm_str_len = strlen(param_str_begin);
+ } else {
+ param_str_end = *str_ptr-1;
+ parm_str_len = param_str_end - param_str_begin;
+ }
+
+ WL_TRACE((" 'token:%s', len:%d, ", token, parm_str_len));
+
+ if (parm_str_len > param_max_len) {
+ WL_TRACE((" WARNING: extracted param len:%d is > MAX:%d\n",
+ parm_str_len, param_max_len));
+
+ parm_str_len = param_max_len;
+ }
+
+ switch (param_type) {
+
+ case PTYPE_INTDEC: {
+ int *pdst_int = dst;
+ char *eptr;
+
+ if (parm_str_len > sizeof(int_str))
+ parm_str_len = sizeof(int_str);
+
+ memcpy(int_str, param_str_begin, parm_str_len);
+
+ *pdst_int = simple_strtoul(int_str, &eptr, 10);
+
+ WL_TRACE((" written as integer:%d\n", *pdst_int));
+ }
+ break;
+ case PTYPE_STR_HEX: {
+ u8 *buf = dst;
+
+ param_max_len = param_max_len >> 1;
+ hstr_2_buf(param_str_begin, buf, param_max_len);
+ print_buf(buf, param_max_len, 0);
+ }
+ break;
+ default:
+ memcpy(dst, param_str_begin, parm_str_len);
+ *((char *)dst + parm_str_len) = 0;
+ WL_TRACE((" written as a string:%s\n", (char *)dst));
+ break;
+ }
+
+ return 0;
+ } else {
+ WL_ERROR(("\n %s: No token:%s in str:%s\n",
+ __FUNCTION__, token, *str_ptr));
+
+ return -1;
+ }
+}
+
+static int wl_iw_softap_deassoc_stations(struct net_device *dev, u8 *mac)
+{
+ int i;
+ int res = 0;
+ char mac_buf[128] = {0};
+ char z_mac[6] = {0, 0, 0, 0, 0, 0};
+ char *sta_mac;
+ struct maclist *assoc_maclist = (struct maclist *) mac_buf;
+ bool deauth_all = false;
+
+ if (mac == NULL) {
+ deauth_all = true;
+ sta_mac = z_mac;
+ } else {
+ sta_mac = mac;
+ }
+
+ memset(assoc_maclist, 0, sizeof(mac_buf));
+ assoc_maclist->count = 8;
+
+ res = dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, 128);
+ if (res != 0) {
+ WL_SOFTAP(("%s: Error:%d Couldn't get ASSOC List\n", __FUNCTION__, res));
+ return res;
+ }
+
+ if (assoc_maclist->count) {
+ for (i = 0; i < assoc_maclist->count; i++) {
+ scb_val_t scbval;
+
+ scbval.val = htod32(1);
+ bcopy(&assoc_maclist->ea[i], &scbval.ea, ETHER_ADDR_LEN);
+
+ if (deauth_all || (memcmp(&scbval.ea, sta_mac, ETHER_ADDR_LEN) == 0)) {
+ WL_SOFTAP(("%s, deauth STA:%d \n", __FUNCTION__, i));
+ res |= dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON,
+ &scbval, sizeof(scb_val_t));
+ }
+ }
+ } else {
+ WL_SOFTAP((" STA ASSOC list is empty\n"));
+ }
+
+ if (res != 0) {
+ WL_ERROR(("%s: Error:%d\n", __FUNCTION__, res));
+ } else if (assoc_maclist->count) {
+ bcm_mdelay(200);
+ }
+ return res;
+}
+
+
+static int iwpriv_softap_stop(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *ext)
+{
+ int res = 0;
+
+ WL_SOFTAP(("got iwpriv AP_BSS_STOP\n"));
+
+ if ((!dev) && (!ap_net_dev)) {
+ WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+ return res;
+ }
+
+ net_os_wake_lock(dev);
+
+ if ((ap_cfg_running == TRUE)) {
+#ifdef AP_ONLY
+ wl_iw_softap_deassoc_stations(dev, NULL);
+#else
+ wl_iw_softap_deassoc_stations(ap_net_dev, NULL);
+
+ if ((res = dev_iw_write_cfg1_bss_var(dev, 2)) < 0)
+ WL_ERROR(("%s failed to del BSS err = %d", __FUNCTION__, res));
+#endif
+
+ bcm_mdelay(100);
+
+ wrqu->data.length = 0;
+ ap_cfg_running = FALSE;
+ }
+ else
+ WL_ERROR(("%s: was called when SoftAP is OFF : move on\n", __FUNCTION__));
+
+ WL_SOFTAP(("%s Done with %d\n", __FUNCTION__, res));
+
+ net_os_wake_unlock(dev);
+
+ return res;
+}
+
+
+static int iwpriv_fw_reload(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *ext)
+{
+ int ret = -1;
+ char extra[256];
+ char *fwstr = fw_path;
+
+ WL_SOFTAP(("current firmware_path[]=%s\n", fwstr));
+
+ WL_TRACE((">Got FW_RELOAD cmd:"
+ "info->cmd:%x, info->flags:%x, u.data:%p, u.len:%d, \
+ fw_path:%p, len:%d \n",
+ info->cmd, info->flags,
+ wrqu->data.pointer, wrqu->data.length, fwstr, strlen(fwstr)));
+
+ if ((wrqu->data.length > 4) && (wrqu->data.length < sizeof(extra))) {
+
+ char *str_ptr;
+
+ if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) {
+ ret = -EFAULT;
+ goto exit_proc;
+ }
+
+ extra[wrqu->data.length] = 8;
+ str_ptr = extra;
+
+ if (get_parmeter_from_string(&str_ptr, "FW_PATH=", PTYPE_STRING, fwstr, 255) != 0) {
+ WL_ERROR(("Error: extracting FW_PATH='' string\n"));
+ goto exit_proc;
+ }
+
+ if (strstr(fwstr, "apsta") != NULL) {
+ WL_SOFTAP(("GOT APSTA FIRMWARE\n"));
+ ap_fw_loaded = TRUE;
+ } else {
+ WL_SOFTAP(("GOT STA FIRMWARE\n"));
+ ap_fw_loaded = FALSE;
+ }
+
+ WL_SOFTAP(("SET firmware_path[]=%s , str_p:%p\n", fwstr, fwstr));
+ ret = 0;
+ } else {
+ WL_ERROR(("Error: ivalid param len:%d\n", wrqu->data.length));
+ }
+
+exit_proc:
+ return ret;
+}
+#endif
+
+#ifdef SOFTAP
+static int iwpriv_wpasupp_loop_tst(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *ext)
+{
+ int res = 0;
+ char *params = NULL;
+
+ WL_TRACE((">Got IWPRIV wp_supp loopback cmd test:"
+ "info->cmd:%x, info->flags:%x, u.data:%p, u.len:%d\n",
+ info->cmd, info->flags,
+ wrqu->data.pointer, wrqu->data.length));
+
+ if (wrqu->data.length != 0) {
+
+ if (!(params = kmalloc(wrqu->data.length+1, GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (copy_from_user(params, wrqu->data.pointer, wrqu->data.length)) {
+ kfree(params);
+ return -EFAULT;
+ }
+
+ params[wrqu->data.length] = 0;
+ WL_SOFTAP(("\n>> copied from user:\n %s\n", params));
+ } else {
+ WL_ERROR(("ERROR param length is 0\n"));
+ return -EFAULT;
+ }
+
+ res = wl_iw_send_priv_event(dev, params);
+ kfree(params);
+
+ return res;
+}
+#endif
+
+
+static int
+iwpriv_en_ap_bss(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu,
+ char *extra)
+{
+ int res = 0;
+
+ if (!dev) {
+ WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+ return -1;
+ }
+
+ net_os_wake_lock(dev);
+
+ WL_SOFTAP(("%s: rcvd IWPRIV IOCTL: for dev:%s\n", __FUNCTION__, dev->name));
+
+#ifndef AP_ONLY
+ if (ap_cfg_pid >= 0) {
+ wait_for_completion(&ap_cfg_exited);
+ ap_cfg_pid = -1;
+ }
+
+ if ((res = wl_iw_set_ap_security(dev, &my_ap)) != 0) {
+ WL_ERROR((" %s ERROR setting SOFTAP security in :%d\n", __FUNCTION__, res));
+ }
+ else {
+ if ((res = dev_iw_write_cfg1_bss_var(dev, 1)) < 0)
+ WL_ERROR(("%s fail to set bss up err=%d\n", __FUNCTION__, res));
+ else
+ bcm_mdelay(100);
+ }
+
+#endif
+ WL_SOFTAP(("%s done with res %d \n", __FUNCTION__, res));
+
+ net_os_wake_unlock(dev);
+
+ return res;
+}
+
+static int
+get_assoc_sta_list(struct net_device *dev, char *buf, int len)
+{
+ WL_TRACE(("%s: dev_wlc_ioctl(dev:%p, cmd:%d, buf:%p, len:%d)\n",
+ __FUNCTION__, dev, WLC_GET_ASSOCLIST, buf, len));
+
+ return dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, buf, len);
+
+}
+
+
+void check_error(int res, const char *msg, const char *func, int line)
+{
+ if (res != 0)
+ WL_ERROR(("%s, %d function:%s, line:%d\n", msg, res, func, line));
+}
+
+static int
+set_ap_mac_list(struct net_device *dev, void *buf)
+{
+ struct mac_list_set *mac_list_set = (struct mac_list_set *)buf;
+ struct maclist *maclist = (struct maclist *)&mac_list_set->mac_list;
+ int length;
+ int i;
+ int mac_mode = mac_list_set->mode;
+ int ioc_res = 0;
+ ap_macmode = mac_list_set->mode;
+
+ bzero(&ap_black_list, sizeof(struct mflist));
+
+ if (mac_mode == MACLIST_MODE_DISABLED) {
+
+ ioc_res = dev_wlc_ioctl(dev, WLC_SET_MACMODE, &mac_mode, sizeof(mac_mode));
+ check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__);
+ WL_SOFTAP(("%s: MAC filtering disabled\n", __FUNCTION__));
+ } else {
+
+ scb_val_t scbval;
+ char mac_buf[256] = {0};
+ struct maclist *assoc_maclist = (struct maclist *) mac_buf;
+
+ bcopy(maclist, &ap_black_list, sizeof(ap_black_list));
+
+ ioc_res = dev_wlc_ioctl(dev, WLC_SET_MACMODE, &mac_mode, sizeof(mac_mode));
+ check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__);
+
+ length = sizeof(maclist->count) + maclist->count*ETHER_ADDR_LEN;
+ dev_wlc_ioctl(dev, WLC_SET_MACLIST, maclist, length);
+
+ WL_SOFTAP(("%s: applied MAC List, mode:%d, length %d:\n",
+ __FUNCTION__, mac_mode, length));
+ for (i = 0; i < maclist->count; i++)
+ WL_SOFTAP(("mac %d: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ i, maclist->ea[i].octet[0], maclist->ea[i].octet[1], \
+ maclist->ea[i].octet[2], \
+ maclist->ea[i].octet[3], maclist->ea[i].octet[4], \
+ maclist->ea[i].octet[5]));
+
+ assoc_maclist->count = 8;
+ ioc_res = dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, 256);
+ check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__);
+ WL_SOFTAP((" Cur assoc clients:%d\n", assoc_maclist->count));
+
+ if (assoc_maclist->count)
+ for (i = 0; i < assoc_maclist->count; i++) {
+ int j;
+ bool assoc_mac_matched = false;
+
+ WL_SOFTAP(("\n Cheking assoc STA: "));
+ print_buf(&assoc_maclist->ea[i], 6, 7);
+ WL_SOFTAP(("with the b/w list:"));
+
+ for (j = 0; j < maclist->count; j++)
+ if (!bcmp(&assoc_maclist->ea[i], &maclist->ea[j],
+ ETHER_ADDR_LEN)) {
+
+ assoc_mac_matched = true;
+ break;
+ }
+
+ if (((mac_mode == MACLIST_MODE_ALLOW) && !assoc_mac_matched) ||
+ ((mac_mode == MACLIST_MODE_DENY) && assoc_mac_matched)) {
+
+ WL_SOFTAP(("b-match or w-mismatch,"
+ " do deauth/disassoc \n"));
+ scbval.val = htod32(1);
+ bcopy(&assoc_maclist->ea[i], &scbval.ea, \
+ ETHER_ADDR_LEN);
+ ioc_res = dev_wlc_ioctl(dev,
+ WLC_SCB_DEAUTHENTICATE_FOR_REASON,
+ &scbval, sizeof(scb_val_t));
+ check_error(ioc_res,
+ "ioctl ERROR:",
+ __FUNCTION__, __LINE__);
+
+ } else {
+ WL_SOFTAP((" no b/w list hits, let it be\n"));
+ }
+ } else {
+ WL_SOFTAP(("No ASSOC CLIENTS\n"));
+ }
+ }
+
+ WL_SOFTAP(("%s iocres:%d\n", __FUNCTION__, ioc_res));
+ return ioc_res;
+}
+#endif
+
+
+#ifdef SOFTAP
+int set_macfilt_from_string(struct mflist *pmflist, char **param_str)
+{
+ return 0;
+}
+#endif
+
+
+#ifdef SOFTAP
+#define PARAM_OFFSET PROFILE_OFFSET
+
+int wl_iw_process_private_ascii_cmd(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *dwrq,
+ char *cmd_str)
+{
+ int ret = 0;
+ char *sub_cmd = cmd_str + PROFILE_OFFSET + strlen("ASCII_CMD=");
+
+ WL_SOFTAP(("\n %s: ASCII_CMD: offs_0:%s, offset_32:\n'%s'\n",
+ __FUNCTION__, cmd_str, cmd_str + PROFILE_OFFSET));
+
+ if (strnicmp(sub_cmd, "AP_CFG", strlen("AP_CFG")) == 0) {
+
+ WL_SOFTAP((" AP_CFG \n"));
+
+
+ if (init_ap_profile_from_string(cmd_str+PROFILE_OFFSET, &my_ap) != 0) {
+ WL_ERROR(("ERROR: SoftAP CFG prams !\n"));
+ ret = -1;
+ } else {
+ ret = set_ap_cfg(dev, &my_ap);
+ }
+
+ } else if (strnicmp(sub_cmd, "AP_BSS_START", strlen("AP_BSS_START")) == 0) {
+
+ WL_SOFTAP(("\n SOFTAP - ENABLE BSS \n"));
+
+ WL_SOFTAP(("\n!!! got 'WL_AP_EN_BSS' from WPA supplicant, dev:%s\n", dev->name));
+
+#ifndef AP_ONLY
+ if (ap_net_dev == NULL) {
+ printf("\n ERROR: SOFTAP net_dev* is NULL !!!\n");
+ } else {
+ if ((ret = iwpriv_en_ap_bss(ap_net_dev, info, dwrq, cmd_str)) < 0)
+ WL_ERROR(("%s line %d fail to set bss up\n", \
+ __FUNCTION__, __LINE__));
+ }
+#else
+ if ((ret = iwpriv_en_ap_bss(dev, info, dwrq, cmd_str)) < 0)
+ WL_ERROR(("%s line %d fail to set bss up\n", \
+ __FUNCTION__, __LINE__));
+#endif
+ } else if (strnicmp(sub_cmd, "ASSOC_LST", strlen("ASSOC_LST")) == 0) {
+ /* no code yet */
+ } else if (strnicmp(sub_cmd, "AP_BSS_STOP", strlen("AP_BSS_STOP")) == 0) {
+ WL_SOFTAP((" \n temp DOWN SOFTAP\n"));
+#ifndef AP_ONLY
+ if ((ret = dev_iw_write_cfg1_bss_var(dev, 0)) < 0) {
+ WL_ERROR(("%s line %d fail to set bss down\n", \
+ __FUNCTION__, __LINE__));
+ }
+#endif
+ }
+
+ return ret;
+}
+#endif
+
+static int wl_iw_set_priv(
+ struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *dwrq,
+ char *ext
+)
+{
+ int ret = 0;
+ char * extra;
+
+ if (!(extra = kmalloc(dwrq->length, GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (copy_from_user(extra, dwrq->pointer, dwrq->length)) {
+ kfree(extra);
+ return -EFAULT;
+ }
+
+ WL_TRACE(("%s: SIOCSIWPRIV request %s, info->cmd:%x, info->flags:%d\n dwrq->length:%d",
+ dev->name, extra, info->cmd, info->flags, dwrq->length));
+
+ net_os_wake_lock(dev);
+
+ if (dwrq->length && extra) {
+ if (strnicmp(extra, "START", strlen("START")) == 0) {
+ wl_iw_control_wl_on(dev, info);
+ WL_TRACE(("%s, Received regular START command\n", __FUNCTION__));
+ }
+
+ if (g_onoff == G_WLAN_SET_OFF) {
+ WL_TRACE(("%s, missing START, Fail\n", __FUNCTION__));
+ kfree(extra);
+ net_os_wake_unlock(dev);
+ return -EFAULT;
+ }
+
+ if (strnicmp(extra, "SCAN-ACTIVE", strlen("SCAN-ACTIVE")) == 0) {
+#ifdef ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS
+ WL_TRACE(("%s: active scan setting suppressed\n", dev->name));
+#else
+ ret = wl_iw_set_active_scan(dev, info, (union iwreq_data *)dwrq, extra);
+#endif
+ } else if (strnicmp(extra, "SCAN-PASSIVE", strlen("SCAN-PASSIVE")) == 0)
+#ifdef ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS
+ WL_TRACE(("%s: passive scan setting suppressed\n", dev->name));
+#else
+ ret = wl_iw_set_passive_scan(dev, info, (union iwreq_data *)dwrq, extra);
+#endif
+ else if (strnicmp(extra, "RSSI", strlen("RSSI")) == 0)
+ ret = wl_iw_get_rssi(dev, info, (union iwreq_data *)dwrq, extra);
+ else if (strnicmp(extra, "LINKSPEED", strlen("LINKSPEED")) == 0)
+ ret = wl_iw_get_link_speed(dev, info, (union iwreq_data *)dwrq, extra);
+ else if (strnicmp(extra, "MACADDR", strlen("MACADDR")) == 0)
+ ret = wl_iw_get_macaddr(dev, info, (union iwreq_data *)dwrq, extra);
+ else if (strnicmp(extra, "COUNTRY", strlen("COUNTRY")) == 0)
+ ret = wl_iw_set_country(dev, info, (union iwreq_data *)dwrq, extra);
+ else if (strnicmp(extra, "STOP", strlen("STOP")) == 0)
+ ret = wl_iw_control_wl_off(dev, info);
+ else if (strnicmp(extra, BAND_GET_CMD, strlen(BAND_GET_CMD)) == 0)
+ ret = wl_iw_get_band(dev, info, (union iwreq_data *)dwrq, extra);
+ else if (strnicmp(extra, BAND_SET_CMD, strlen(BAND_SET_CMD)) == 0)
+ ret = wl_iw_set_band(dev, info, (union iwreq_data *)dwrq, extra);
+ else if (strnicmp(extra, DTIM_SKIP_GET_CMD, strlen(DTIM_SKIP_GET_CMD)) == 0)
+ ret = wl_iw_get_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra);
+ else if (strnicmp(extra, DTIM_SKIP_SET_CMD, strlen(DTIM_SKIP_SET_CMD)) == 0)
+ ret = wl_iw_set_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra);
+ else if (strnicmp(extra, SETSUSPEND_CMD, strlen(SETSUSPEND_CMD)) == 0)
+ ret = wl_iw_set_suspend(dev, info, (union iwreq_data *)dwrq, extra);
+#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY
+ else if (strnicmp(extra, SETDFSCHANNELS_CMD, strlen(SETDFSCHANNELS_CMD)) == 0)
+ ret = wl_iw_set_dfs_channels(dev, info, (union iwreq_data *)dwrq, extra);
+#endif
+#if defined(PNO_SUPPORT)
+ else if (strnicmp(extra, PNOSSIDCLR_SET_CMD, strlen(PNOSSIDCLR_SET_CMD)) == 0)
+ ret = wl_iw_set_pno_reset(dev, info, (union iwreq_data *)dwrq, extra);
+ else if (strnicmp(extra, PNOSETUP_SET_CMD, strlen(PNOSETUP_SET_CMD)) == 0)
+ ret = wl_iw_set_pno_set(dev, info, (union iwreq_data *)dwrq, extra);
+ else if (strnicmp(extra, PNOENABLE_SET_CMD, strlen(PNOENABLE_SET_CMD)) == 0)
+ ret = wl_iw_set_pno_enable(dev, info, (union iwreq_data *)dwrq, extra);
+#endif
+#if defined(CSCAN)
+ else if (strnicmp(extra, CSCAN_COMMAND, strlen(CSCAN_COMMAND)) == 0)
+ ret = wl_iw_set_cscan(dev, info, (union iwreq_data *)dwrq, extra);
+#endif
+#ifdef CUSTOMER_HW2
+ else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0)
+ ret = wl_iw_set_power_mode(dev, info, (union iwreq_data *)dwrq, extra);
+ else if (strnicmp(extra, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0)
+ ret = wl_iw_set_btcoex_dhcp(dev, info, (union iwreq_data *)dwrq, extra);
+#else
+ else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0)
+ ret = wl_iw_set_btcoex_dhcp(dev, info, (union iwreq_data *)dwrq, extra);
+#endif
+ else if (strnicmp(extra, "GETPOWER", strlen("GETPOWER")) == 0)
+ ret = wl_iw_get_power_mode(dev, info, (union iwreq_data *)dwrq, extra);
+#ifdef SOFTAP
+ else if (strnicmp(extra, "ASCII_CMD", strlen("ASCII_CMD")) == 0) {
+ wl_iw_process_private_ascii_cmd(dev, info, (union iwreq_data *)dwrq, extra);
+ } else if (strnicmp(extra, "AP_MAC_LIST_SET", strlen("AP_MAC_LIST_SET")) == 0) {
+ WL_SOFTAP(("penguin, set AP_MAC_LIST_SET\n"));
+ set_ap_mac_list(dev, (extra + PROFILE_OFFSET));
+ }
+#endif
+ else {
+ WL_TRACE(("Unknown PRIVATE command: %s: ignored\n", extra));
+ snprintf(extra, MAX_WX_STRING, "OK");
+ dwrq->length = strlen("OK") + 1;
+ }
+ }
+
+ net_os_wake_unlock(dev);
+
+ if (extra) {
+ if (copy_to_user(dwrq->pointer, extra, dwrq->length)) {
+ kfree(extra);
+ return -EFAULT;
+ }
+
+ kfree(extra);
+ }
+
+ return ret;
+}
+
+static const iw_handler wl_iw_handler[] =
+{
+ (iw_handler) wl_iw_config_commit,
+ (iw_handler) wl_iw_get_name,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_set_freq,
+ (iw_handler) wl_iw_get_freq,
+ (iw_handler) wl_iw_set_mode,
+ (iw_handler) wl_iw_get_mode,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_get_range,
+ (iw_handler) wl_iw_set_priv,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_set_spy,
+ (iw_handler) wl_iw_get_spy,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_set_wap,
+ (iw_handler) wl_iw_get_wap,
+#if WIRELESS_EXT > 17
+ (iw_handler) wl_iw_mlme,
+#else
+ (iw_handler) NULL,
+#endif
+#if defined(WL_IW_USE_ISCAN)
+ (iw_handler) wl_iw_iscan_get_aplist,
+#else
+ (iw_handler) wl_iw_get_aplist,
+#endif
+#if WIRELESS_EXT > 13
+#if defined(WL_IW_USE_ISCAN)
+ (iw_handler) wl_iw_iscan_set_scan,
+ (iw_handler) wl_iw_iscan_get_scan,
+#else
+ (iw_handler) wl_iw_set_scan,
+ (iw_handler) wl_iw_get_scan,
+#endif
+#else
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+#endif
+ (iw_handler) wl_iw_set_essid,
+ (iw_handler) wl_iw_get_essid,
+ (iw_handler) wl_iw_set_nick,
+ (iw_handler) wl_iw_get_nick,
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_set_rate,
+ (iw_handler) wl_iw_get_rate,
+ (iw_handler) wl_iw_set_rts,
+ (iw_handler) wl_iw_get_rts,
+ (iw_handler) wl_iw_set_frag,
+ (iw_handler) wl_iw_get_frag,
+ (iw_handler) wl_iw_set_txpow,
+ (iw_handler) wl_iw_get_txpow,
+#if WIRELESS_EXT > 10
+ (iw_handler) wl_iw_set_retry,
+ (iw_handler) wl_iw_get_retry,
+#endif
+ (iw_handler) wl_iw_set_encode,
+ (iw_handler) wl_iw_get_encode,
+ (iw_handler) wl_iw_set_power,
+ (iw_handler) wl_iw_get_power,
+#if WIRELESS_EXT > 17
+ (iw_handler) NULL,
+ (iw_handler) NULL,
+ (iw_handler) wl_iw_set_wpaie,
+ (iw_handler) wl_iw_get_wpaie,
+ (iw_handler) wl_iw_set_wpaauth,
+ (iw_handler) wl_iw_get_wpaauth,
+ (iw_handler) wl_iw_set_encodeext,
+ (iw_handler) wl_iw_get_encodeext,
+#ifdef BCMWPA2
+ (iw_handler) wl_iw_set_pmksa,
+#endif
+#endif
+};
+
+#if WIRELESS_EXT > 12
+static const iw_handler wl_iw_priv_handler[] = {
+ NULL,
+ (iw_handler)wl_iw_set_active_scan,
+ NULL,
+ (iw_handler)wl_iw_get_rssi,
+ NULL,
+ (iw_handler)wl_iw_set_passive_scan,
+ NULL,
+ (iw_handler)wl_iw_get_link_speed,
+ NULL,
+ (iw_handler)wl_iw_get_macaddr,
+ NULL,
+ (iw_handler)wl_iw_control_wl_off,
+ NULL,
+ (iw_handler)wl_iw_control_wl_on,
+#ifdef SOFTAP
+ NULL,
+ (iw_handler)iwpriv_set_ap_config,
+
+ NULL,
+ (iw_handler)iwpriv_get_assoc_list,
+
+ NULL,
+ (iw_handler)iwpriv_set_mac_filters,
+
+ NULL,
+ (iw_handler)iwpriv_en_ap_bss,
+
+ NULL,
+ (iw_handler)iwpriv_wpasupp_loop_tst,
+
+ NULL,
+ (iw_handler)iwpriv_softap_stop,
+
+ NULL,
+ (iw_handler)iwpriv_fw_reload,
+
+ NULL,
+ (iw_handler)iwpriv_set_ap_sta_disassoc,
+#endif
+#if defined(CSCAN)
+
+ NULL,
+ (iw_handler)iwpriv_set_cscan
+#endif
+};
+
+static const struct iw_priv_args wl_iw_priv_args[] = {
+ {
+ WL_IW_SET_ACTIVE_SCAN,
+ 0,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ "SCAN-ACTIVE"
+ },
+ {
+ WL_IW_GET_RSSI,
+ 0,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ "RSSI"
+ },
+ {
+ WL_IW_SET_PASSIVE_SCAN,
+ 0,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ "SCAN-PASSIVE"
+ },
+ {
+ WL_IW_GET_LINK_SPEED,
+ 0,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ "LINKSPEED"
+ },
+ {
+ WL_IW_GET_CURR_MACADDR,
+ 0,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ "Macaddr"
+ },
+ {
+ WL_IW_SET_STOP,
+ 0,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ "STOP"
+ },
+ {
+ WL_IW_SET_START,
+ 0,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ "START"
+ },
+
+#ifdef SOFTAP
+ {
+ WL_SET_AP_CFG,
+ IW_PRIV_TYPE_CHAR | 256,
+ 0,
+ "AP_SET_CFG"
+ },
+
+ {
+ WL_AP_STA_LIST,
+ IW_PRIV_TYPE_CHAR | 0,
+ IW_PRIV_TYPE_CHAR | 1024,
+ "AP_GET_STA_LIST"
+ },
+
+ {
+ WL_AP_MAC_FLTR,
+ IW_PRIV_TYPE_CHAR | 256,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0,
+ "AP_SET_MAC_FLTR"
+ },
+
+ {
+ WL_AP_BSS_START,
+ 0,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ "AP_BSS_START"
+ },
+
+ {
+ AP_LPB_CMD,
+ IW_PRIV_TYPE_CHAR | 256,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0,
+ "AP_LPB_CMD"
+ },
+
+ {
+ WL_AP_STOP,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0,
+ "AP_BSS_STOP"
+ },
+
+ {
+ WL_FW_RELOAD,
+ IW_PRIV_TYPE_CHAR | 256,
+ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0,
+ "WL_FW_RELOAD"
+ },
+
+ {
+ WL_AP_STA_DISASSOC,
+ IW_PRIV_TYPE_CHAR | 256,
+ IW_PRIV_TYPE_CHAR | 0,
+ "AP_STA_DISASSOC"
+ },
+#endif
+#if defined(CSCAN)
+ {
+ WL_COMBO_SCAN,
+ IW_PRIV_TYPE_CHAR | 1024,
+ 0,
+ "CSCAN"
+ },
+#endif
+};
+
+const struct iw_handler_def wl_iw_handler_def =
+{
+ .num_standard = ARRAYSIZE(wl_iw_handler),
+ .standard = (iw_handler *) wl_iw_handler,
+ .num_private = ARRAYSIZE(wl_iw_priv_handler),
+ .num_private_args = ARRAY_SIZE(wl_iw_priv_args),
+ .private = (iw_handler *)wl_iw_priv_handler,
+ .private_args = (void *) wl_iw_priv_args,
+
+#if WIRELESS_EXT >= 19
+ get_wireless_stats: dhd_get_wireless_stats,
+#endif
+};
+#endif
+
+
+int wl_iw_ioctl(
+ struct net_device *dev,
+ struct ifreq *rq,
+ int cmd
+)
+{
+ struct iwreq *wrq = (struct iwreq *) rq;
+ struct iw_request_info info;
+ iw_handler handler;
+ char *extra = NULL;
+ int token_size = 1, max_tokens = 0, ret = 0;
+
+ net_os_wake_lock(dev);
+
+ WL_TRACE(("%s: cmd:%x alled via dhd->do_ioctl()entry point\n", __FUNCTION__, cmd));
+ if (cmd < SIOCIWFIRST ||
+ IW_IOCTL_IDX(cmd) >= ARRAYSIZE(wl_iw_handler) ||
+ !(handler = wl_iw_handler[IW_IOCTL_IDX(cmd)])) {
+ WL_ERROR(("%s: error in cmd=%x : not supported\n", __FUNCTION__, cmd));
+ net_os_wake_unlock(dev);
+ return -EOPNOTSUPP;
+ }
+
+ switch (cmd) {
+
+ case SIOCSIWESSID:
+ case SIOCGIWESSID:
+ case SIOCSIWNICKN:
+ case SIOCGIWNICKN:
+ max_tokens = IW_ESSID_MAX_SIZE + 1;
+ break;
+
+ case SIOCSIWENCODE:
+ case SIOCGIWENCODE:
+#if WIRELESS_EXT > 17
+ case SIOCSIWENCODEEXT:
+ case SIOCGIWENCODEEXT:
+#endif
+ max_tokens = wrq->u.data.length;
+ break;
+
+ case SIOCGIWRANGE:
+ max_tokens = sizeof(struct iw_range) + 500;
+ break;
+
+ case SIOCGIWAPLIST:
+ token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality);
+ max_tokens = IW_MAX_AP;
+ break;
+
+#if WIRELESS_EXT > 13
+ case SIOCGIWSCAN:
+#if defined(WL_IW_USE_ISCAN)
+ if (g_iscan)
+ max_tokens = wrq->u.data.length;
+ else
+#endif
+ max_tokens = IW_SCAN_MAX_DATA;
+ break;
+#endif
+
+ case SIOCSIWSPY:
+ token_size = sizeof(struct sockaddr);
+ max_tokens = IW_MAX_SPY;
+ break;
+
+ case SIOCGIWSPY:
+ token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality);
+ max_tokens = IW_MAX_SPY;
+ break;
+
+#if WIRELESS_EXT > 17
+ case SIOCSIWPMKSA:
+ case SIOCSIWGENIE:
+#endif
+ case SIOCSIWPRIV:
+ max_tokens = wrq->u.data.length;
+ break;
+ }
+
+ if (max_tokens && wrq->u.data.pointer) {
+ if (wrq->u.data.length > max_tokens) {
+ WL_ERROR(("%s: error in cmd=%x wrq->u.data.length=%d > max_tokens=%d\n", \
+ __FUNCTION__, cmd, wrq->u.data.length, max_tokens));
+ ret = -E2BIG;
+ goto wl_iw_ioctl_done;
+ }
+ if (!(extra = kmalloc(max_tokens * token_size, GFP_KERNEL))) {
+ ret = -ENOMEM;
+ goto wl_iw_ioctl_done;
+ }
+
+ if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length * token_size)) {
+ kfree(extra);
+ ret = -EFAULT;
+ goto wl_iw_ioctl_done;
+ }
+ }
+
+ info.cmd = cmd;
+ info.flags = 0;
+
+ ret = handler(dev, &info, &wrq->u, extra);
+
+ if (extra) {
+ if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length * token_size)) {
+ kfree(extra);
+ ret = -EFAULT;
+ goto wl_iw_ioctl_done;
+ }
+
+ kfree(extra);
+ }
+
+wl_iw_ioctl_done:
+
+ net_os_wake_unlock(dev);
+
+ return ret;
+}
+
+
+bool
+wl_iw_conn_status_str(uint32 event_type, uint32 status, uint32 reason,
+ char* stringBuf, uint buflen)
+{
+ typedef struct conn_fail_event_map_t {
+ uint32 inEvent;
+ uint32 inStatus;
+ uint32 inReason;
+ const char* outName;
+ const char* outCause;
+ } conn_fail_event_map_t;
+
+
+# define WL_IW_DONT_CARE 9999
+ const conn_fail_event_map_t event_map [] = {
+
+
+ {WLC_E_SET_SSID, WLC_E_STATUS_SUCCESS, WL_IW_DONT_CARE,
+ "Conn", "Success"},
+ {WLC_E_SET_SSID, WLC_E_STATUS_NO_NETWORKS, WL_IW_DONT_CARE,
+ "Conn", "NoNetworks"},
+ {WLC_E_SET_SSID, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
+ "Conn", "ConfigMismatch"},
+ {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_PRUNE_ENCR_MISMATCH,
+ "Conn", "EncrypMismatch"},
+ {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_RSN_MISMATCH,
+ "Conn", "RsnMismatch"},
+ {WLC_E_AUTH, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE,
+ "Conn", "AuthTimeout"},
+ {WLC_E_AUTH, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
+ "Conn", "AuthFail"},
+ {WLC_E_AUTH, WLC_E_STATUS_NO_ACK, WL_IW_DONT_CARE,
+ "Conn", "AuthNoAck"},
+ {WLC_E_REASSOC, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
+ "Conn", "ReassocFail"},
+ {WLC_E_REASSOC, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE,
+ "Conn", "ReassocTimeout"},
+ {WLC_E_REASSOC, WLC_E_STATUS_ABORT, WL_IW_DONT_CARE,
+ "Conn", "ReassocAbort"},
+ {WLC_E_PSK_SUP, WLC_SUP_KEYED, WL_IW_DONT_CARE,
+ "Sup", "ConnSuccess"},
+ {WLC_E_PSK_SUP, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
+ "Sup", "WpaHandshakeFail"},
+ {WLC_E_DEAUTH_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
+ "Conn", "Deauth"},
+ {WLC_E_DISASSOC_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
+ "Conn", "DisassocInd"},
+ {WLC_E_DISASSOC, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
+ "Conn", "Disassoc"}
+ };
+
+ const char* name = "";
+ const char* cause = NULL;
+ int i;
+
+
+ for (i = 0; i < sizeof(event_map)/sizeof(event_map[0]); i++) {
+ const conn_fail_event_map_t* row = &event_map[i];
+ if (row->inEvent == event_type &&
+ (row->inStatus == status || row->inStatus == WL_IW_DONT_CARE) &&
+ (row->inReason == reason || row->inReason == WL_IW_DONT_CARE)) {
+ name = row->outName;
+ cause = row->outCause;
+ break;
+ }
+ }
+
+
+ if (cause) {
+ memset(stringBuf, 0, buflen);
+ snprintf(stringBuf, buflen, "%s %s %02d %02d",
+ name, cause, status, reason);
+ WL_INFORM(("Connection status: %s\n", stringBuf));
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+#if WIRELESS_EXT > 14
+
+static bool
+wl_iw_check_conn_fail(wl_event_msg_t *e, char* stringBuf, uint buflen)
+{
+ uint32 event = ntoh32(e->event_type);
+ uint32 status = ntoh32(e->status);
+ uint32 reason = ntoh32(e->reason);
+
+ if (wl_iw_conn_status_str(event, status, reason, stringBuf, buflen)) {
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+#endif
+
+#ifndef IW_CUSTOM_MAX
+#define IW_CUSTOM_MAX 256
+#endif
+
+void
+wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data)
+{
+#if WIRELESS_EXT > 13
+ union iwreq_data wrqu;
+ char extra[IW_CUSTOM_MAX + 1];
+ int cmd = 0;
+ uint32 event_type = ntoh32(e->event_type);
+ uint16 flags = ntoh16(e->flags);
+ uint32 datalen = ntoh32(e->datalen);
+ uint32 status = ntoh32(e->status);
+ uint32 toto;
+ static uint32 roam_no_success = 0;
+ static bool roam_no_success_send = FALSE;
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ memset(extra, 0, sizeof(extra));
+
+ if (!dev) {
+ WL_ERROR(("%s: dev is null\n", __FUNCTION__));
+ return;
+ }
+
+ net_os_wake_lock(dev);
+
+ WL_TRACE(("%s: dev=%s event=%d \n", __FUNCTION__, dev->name, event_type));
+
+ switch (event_type) {
+
+ case WLC_E_RELOAD:
+ WL_ERROR(("%s: Firmware ERROR %d\n", __FUNCTION__, status));
+ net_os_send_hang_message(dev);
+ goto wl_iw_event_end;
+
+#if defined(SOFTAP)
+ case WLC_E_PRUNE:
+ if (ap_cfg_running) {
+ char *macaddr = (char *)&e->addr;
+ WL_SOFTAP(("PRUNE received, %02X:%02X:%02X:%02X:%02X:%02X!\n",
+ macaddr[0], macaddr[1], macaddr[2], macaddr[3], \
+ macaddr[4], macaddr[5]));
+
+ if (ap_macmode) {
+ int i;
+ for (i = 0; i < ap_black_list.count; i++) {
+ if (!bcmp(macaddr, &ap_black_list.ea[i], \
+ sizeof(struct ether_addr))) {
+ WL_SOFTAP(("mac in black list, ignore it\n"));
+ break;
+ }
+ }
+
+ if (i == ap_black_list.count) {
+ char mac_buf[32] = {0};
+ sprintf(mac_buf, "STA_BLOCK %02X:%02X:%02X:%02X:%02X:%02X",
+ macaddr[0], macaddr[1], macaddr[2],
+ macaddr[3], macaddr[4], macaddr[5]);
+ wl_iw_send_priv_event(priv_dev, mac_buf);
+ }
+ }
+ }
+ break;
+#endif
+ case WLC_E_TXFAIL:
+ cmd = IWEVTXDROP;
+ memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+ break;
+#if WIRELESS_EXT > 14
+ case WLC_E_JOIN:
+ case WLC_E_ASSOC_IND:
+ case WLC_E_REASSOC_IND:
+#if defined(SOFTAP)
+ WL_SOFTAP(("STA connect received %d\n", event_type));
+ if (ap_cfg_running) {
+ wl_iw_send_priv_event(priv_dev, "STA_JOIN");
+ goto wl_iw_event_end;
+ }
+#endif
+ memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+ cmd = IWEVREGISTERED;
+ break;
+ case WLC_E_ROAM:
+ if (status == WLC_E_STATUS_SUCCESS) {
+ memcpy(wrqu.addr.sa_data, &e->addr.octet, ETHER_ADDR_LEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+ cmd = SIOCGIWAP;
+ }
+ else if (status == WLC_E_STATUS_NO_NETWORKS) {
+ roam_no_success++;
+ if ((roam_no_success == 5) && (roam_no_success_send == FALSE)) {
+ roam_no_success_send = TRUE;
+ bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
+ bzero(&extra, ETHER_ADDR_LEN);
+ cmd = SIOCGIWAP;
+ WL_ERROR(("%s ROAMING did not succeeded , send Link Down\n", \
+ __FUNCTION__));
+ } else {
+ WL_TRACE(("##### ROAMING did not succeeded %d\n", roam_no_success));
+ goto wl_iw_event_end;
+ }
+ }
+ break;
+ case WLC_E_DEAUTH_IND:
+ case WLC_E_DISASSOC_IND:
+#if defined(SOFTAP)
+ WL_SOFTAP(("STA disconnect received %d\n", event_type));
+ if (ap_cfg_running) {
+ wl_iw_send_priv_event(priv_dev, "STA_LEAVE");
+ goto wl_iw_event_end;
+ }
+#endif
+ cmd = SIOCGIWAP;
+ bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+ bzero(&extra, ETHER_ADDR_LEN);
+ break;
+ case WLC_E_LINK:
+ case WLC_E_NDIS_LINK:
+ cmd = SIOCGIWAP;
+ if (!(flags & WLC_EVENT_MSG_LINK)) {
+#ifdef SOFTAP
+#ifdef AP_ONLY
+ if (ap_cfg_running) {
+#else
+ if (ap_cfg_running && !strncmp(dev->name, "wl0.1", 5)) {
+#endif
+ WL_SOFTAP(("AP DOWN %d\n", event_type));
+ wl_iw_send_priv_event(priv_dev, "AP_DOWN");
+ } else {
+ WL_TRACE(("STA_Link Down\n"));
+ g_ss_cache_ctrl.m_link_down = 1;
+ }
+#else
+ g_ss_cache_ctrl.m_link_down = 1;
+#endif
+ WL_TRACE(("Link Down\n"));
+
+ bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
+ bzero(&extra, ETHER_ADDR_LEN);
+ }
+ else {
+ memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN);
+ g_ss_cache_ctrl.m_link_down = 0;
+
+ memcpy(g_ss_cache_ctrl.m_active_bssid, &e->addr, ETHER_ADDR_LEN);
+
+#ifdef SOFTAP
+#ifdef AP_ONLY
+ if (ap_cfg_running) {
+#else
+ if (ap_cfg_running && !strncmp(dev->name, "wl0.1", 5)) {
+#endif
+ WL_SOFTAP(("AP UP %d\n", event_type));
+ wl_iw_send_priv_event(priv_dev, "AP_UP");
+ } else {
+ WL_TRACE(("STA_LINK_UP\n"));
+ roam_no_success_send = FALSE;
+ roam_no_success = 0;
+ }
+#endif
+ WL_TRACE(("Link UP\n"));
+
+ }
+ net_os_wake_lock_timeout_enable(dev);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+ break;
+ case WLC_E_ACTION_FRAME:
+ cmd = IWEVCUSTOM;
+ if (datalen + 1 <= sizeof(extra)) {
+ wrqu.data.length = datalen + 1;
+ extra[0] = WLC_E_ACTION_FRAME;
+ memcpy(&extra[1], data, datalen);
+ WL_TRACE(("WLC_E_ACTION_FRAME len %d \n", wrqu.data.length));
+ }
+ break;
+
+ case WLC_E_ACTION_FRAME_COMPLETE:
+ cmd = IWEVCUSTOM;
+ memcpy(&toto, data, 4);
+ if (sizeof(status) + 1 <= sizeof(extra)) {
+ wrqu.data.length = sizeof(status) + 1;
+ extra[0] = WLC_E_ACTION_FRAME_COMPLETE;
+ memcpy(&extra[1], &status, sizeof(status));
+ printf("wl_iw_event status %d PacketId %d \n", status, toto);
+ printf("WLC_E_ACTION_FRAME_COMPLETE len %d \n", wrqu.data.length);
+ }
+ break;
+#endif
+#if WIRELESS_EXT > 17
+ case WLC_E_MIC_ERROR: {
+ struct iw_michaelmicfailure *micerrevt = (struct iw_michaelmicfailure *)&extra;
+ cmd = IWEVMICHAELMICFAILURE;
+ wrqu.data.length = sizeof(struct iw_michaelmicfailure);
+ if (flags & WLC_EVENT_MSG_GROUP)
+ micerrevt->flags |= IW_MICFAILURE_GROUP;
+ else
+ micerrevt->flags |= IW_MICFAILURE_PAIRWISE;
+ memcpy(micerrevt->src_addr.sa_data, &e->addr, ETHER_ADDR_LEN);
+ micerrevt->src_addr.sa_family = ARPHRD_ETHER;
+
+ break;
+ }
+#ifdef BCMWPA2
+ case WLC_E_PMKID_CACHE: {
+ if (data)
+ {
+ struct iw_pmkid_cand *iwpmkidcand = (struct iw_pmkid_cand *)&extra;
+ pmkid_cand_list_t *pmkcandlist;
+ pmkid_cand_t *pmkidcand;
+ int count;
+
+ cmd = IWEVPMKIDCAND;
+ pmkcandlist = data;
+ count = ntoh32_ua((uint8 *)&pmkcandlist->npmkid_cand);
+ ASSERT(count >= 0);
+ wrqu.data.length = sizeof(struct iw_pmkid_cand);
+ pmkidcand = pmkcandlist->pmkid_cand;
+ while (count) {
+ bzero(iwpmkidcand, sizeof(struct iw_pmkid_cand));
+ if (pmkidcand->preauth)
+ iwpmkidcand->flags |= IW_PMKID_CAND_PREAUTH;
+ bcopy(&pmkidcand->BSSID, &iwpmkidcand->bssid.sa_data,
+ ETHER_ADDR_LEN);
+#ifndef SANDGATE2G
+ wireless_send_event(dev, cmd, &wrqu, extra);
+#endif
+ pmkidcand++;
+ count--;
+ }
+ }
+ goto wl_iw_event_end;
+ }
+#endif
+#endif
+
+ case WLC_E_SCAN_COMPLETE:
+#if defined(WL_IW_USE_ISCAN)
+ if ((g_iscan) && (g_iscan->sysioc_pid >= 0) &&
+ (g_iscan->iscan_state != ISCAN_STATE_IDLE))
+ {
+ up(&g_iscan->sysioc_sem);
+ } else {
+ cmd = SIOCGIWSCAN;
+ wrqu.data.length = strlen(extra);
+ WL_TRACE(("Event WLC_E_SCAN_COMPLETE from specific scan %d\n", \
+ g_iscan->iscan_state));
+ }
+#else
+ cmd = SIOCGIWSCAN;
+ wrqu.data.length = strlen(extra);
+ WL_TRACE(("Event WLC_E_SCAN_COMPLETE\n"));
+#endif
+ break;
+
+ case WLC_E_PFN_NET_FOUND:
+ {
+ wlc_ssid_t * ssid;
+ ssid = (wlc_ssid_t *)data;
+ WL_TRACE(("%s Event WLC_E_PFN_NET_FOUND, send %s up : find %s len=%d\n", \
+ __FUNCTION__, PNO_EVENT_UP, ssid->SSID, ssid->SSID_len));
+ net_os_wake_lock_timeout_enable(dev);
+ cmd = IWEVCUSTOM;
+ memset(&wrqu, 0, sizeof(wrqu));
+ strcpy(extra, PNO_EVENT_UP);
+ wrqu.data.length = strlen(extra);
+ }
+ break;
+
+ default:
+
+ WL_TRACE(("Unknown Event %d: ignoring\n", event_type));
+ break;
+ }
+#ifndef SANDGATE2G
+ if (cmd) {
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31))
+ if (cmd == SIOCGIWSCAN)
+ wireless_send_event(dev, cmd, &wrqu, NULL);
+ else
+#endif
+ wireless_send_event(dev, cmd, &wrqu, extra);
+ }
+#endif
+
+#if WIRELESS_EXT > 14
+
+ memset(extra, 0, sizeof(extra));
+ if (wl_iw_check_conn_fail(e, extra, sizeof(extra))) {
+ cmd = IWEVCUSTOM;
+ wrqu.data.length = strlen(extra);
+#ifndef SANDGATE2G
+ wireless_send_event(dev, cmd, &wrqu, extra);
+#endif
+ }
+#endif
+wl_iw_event_end:
+ net_os_wake_unlock(dev);
+#endif
+}
+
+int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats)
+{
+ int res = 0;
+ wl_cnt_t cnt;
+ int phy_noise;
+ int rssi;
+ scb_val_t scb_val;
+
+ phy_noise = 0;
+ if ((res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise))))
+ goto done;
+
+ phy_noise = dtoh32(phy_noise);
+ WL_TRACE(("wl_iw_get_wireless_stats phy noise=%d\n", phy_noise));
+
+ bzero(&scb_val, sizeof(scb_val_t));
+ if ((res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t))))
+ goto done;
+
+ rssi = dtoh32(scb_val.val);
+ WL_TRACE(("wl_iw_get_wireless_stats rssi=%d\n", rssi));
+ if (rssi <= WL_IW_RSSI_NO_SIGNAL)
+ wstats->qual.qual = 0;
+ else if (rssi <= WL_IW_RSSI_VERY_LOW)
+ wstats->qual.qual = 1;
+ else if (rssi <= WL_IW_RSSI_LOW)
+ wstats->qual.qual = 2;
+ else if (rssi <= WL_IW_RSSI_GOOD)
+ wstats->qual.qual = 3;
+ else if (rssi <= WL_IW_RSSI_VERY_GOOD)
+ wstats->qual.qual = 4;
+ else
+ wstats->qual.qual = 5;
+
+
+ wstats->qual.level = 0x100 + rssi;
+ wstats->qual.noise = 0x100 + phy_noise;
+#if WIRELESS_EXT > 18
+ wstats->qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM);
+#else
+ wstats->qual.updated |= 7;
+#endif
+
+#if WIRELESS_EXT > 11
+ WL_TRACE(("wl_iw_get_wireless_stats counters=%d\n", (int)sizeof(wl_cnt_t)));
+
+ memset(&cnt, 0, sizeof(wl_cnt_t));
+ res = dev_wlc_bufvar_get(dev, "counters", (char *)&cnt, sizeof(wl_cnt_t));
+ if (res)
+ {
+ WL_ERROR(("wl_iw_get_wireless_stats counters failed error=%d\n", res));
+ goto done;
+ }
+
+ cnt.version = dtoh16(cnt.version);
+ if (cnt.version != WL_CNT_T_VERSION) {
+ WL_TRACE(("\tIncorrect version of counters struct: expected %d; got %d\n",
+ WL_CNT_T_VERSION, cnt.version));
+ goto done;
+ }
+
+ wstats->discard.nwid = 0;
+ wstats->discard.code = dtoh32(cnt.rxundec);
+ wstats->discard.fragment = dtoh32(cnt.rxfragerr);
+ wstats->discard.retries = dtoh32(cnt.txfail);
+ wstats->discard.misc = dtoh32(cnt.rxrunt) + dtoh32(cnt.rxgiant);
+ wstats->miss.beacon = 0;
+
+ WL_TRACE(("wl_iw_get_wireless_stats counters txframe=%d txbyte=%d\n",
+ dtoh32(cnt.txframe), dtoh32(cnt.txbyte)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxfrmtoolong=%d\n", dtoh32(cnt.rxfrmtoolong)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxbadplcp=%d\n", dtoh32(cnt.rxbadplcp)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxundec=%d\n", dtoh32(cnt.rxundec)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxfragerr=%d\n", dtoh32(cnt.rxfragerr)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters txfail=%d\n", dtoh32(cnt.txfail)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxrunt=%d\n", dtoh32(cnt.rxrunt)));
+ WL_TRACE(("wl_iw_get_wireless_stats counters rxgiant=%d\n", dtoh32(cnt.rxgiant)));
+
+#endif
+
+done:
+ return res;
+}
+static void
+wl_iw_bt_flag_set(
+ struct net_device *dev,
+ bool set)
+{
+#if defined(BT_DHCP_USE_FLAGS)
+ char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 };
+ char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00};
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ rtnl_lock();
+#endif
+
+#if defined(BT_DHCP_eSCO_FIX)
+ set_btc_esco_params(dev, set);
+#endif
+
+#if defined(BT_DHCP_USE_FLAGS)
+ WL_TRACE_COEX(("WI-FI priority boost via bt flags, set:%d\n", set));
+ if (set == TRUE) {
+ dev_wlc_bufvar_set(dev, "btc_flags",
+ (char *)&buf_flag7_dhcp_on[0], sizeof(buf_flag7_dhcp_on));
+ }
+ else {
+ dev_wlc_bufvar_set(dev, "btc_flags",
+ (char *)&buf_flag7_default[0], sizeof(buf_flag7_default));
+ }
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
+ rtnl_unlock();
+#endif
+}
+
+static void
+wl_iw_bt_timerfunc(ulong data)
+{
+ bt_info_t *bt_local = (bt_info_t *)data;
+ bt_local->timer_on = 0;
+ WL_TRACE(("%s\n", __FUNCTION__));
+
+ up(&bt_local->bt_sem);
+}
+
+static int
+_bt_dhcp_sysioc_thread(void *data)
+{
+ DAEMONIZE("dhcp_sysioc");
+
+ while (down_interruptible(&g_bt->bt_sem) == 0) {
+
+ net_os_wake_lock(g_bt->dev);
+
+ if (g_bt->timer_on) {
+ g_bt->timer_on = 0;
+ del_timer_sync(&g_bt->timer);
+ }
+
+ switch (g_bt->bt_state) {
+ case BT_DHCP_START:
+ WL_TRACE_COEX(("%s bt_dhcp stm: started \n", __FUNCTION__));
+ g_bt->bt_state = BT_DHCP_OPPORTUNITY_WINDOW;
+ mod_timer(&g_bt->timer, jiffies + BT_DHCP_OPPORTUNITY_WINDOW_TIME*HZ/1000);
+ g_bt->timer_on = 1;
+ break;
+
+ case BT_DHCP_OPPORTUNITY_WINDOW:
+ if (g_bt->dhcp_done) {
+ WL_TRACE_COEX(("%s DHCP Done before T1 expiration\n", \
+ __FUNCTION__));
+ g_bt->bt_state = BT_DHCP_IDLE;
+ g_bt->timer_on = 0;
+ break;
+ }
+
+ WL_TRACE_COEX(("%s DHCP T1:%d expired\n", \
+ __FUNCTION__, BT_DHCP_OPPORTUNITY_WINDOW_TIME));
+ if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, TRUE);
+ g_bt->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT;
+ mod_timer(&g_bt->timer, jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000);
+ g_bt->timer_on = 1;
+ break;
+
+ case BT_DHCP_FLAG_FORCE_TIMEOUT:
+ if (g_bt->dhcp_done) {
+ WL_TRACE_COEX(("%s DHCP Done before T2 expiration\n", \
+ __FUNCTION__));
+ } else {
+ WL_TRACE_COEX(("%s DHCP wait interval T2:%d msec expired\n",
+ __FUNCTION__, BT_DHCP_FLAG_FORCE_TIME));
+ }
+
+ if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, FALSE);
+ g_bt->bt_state = BT_DHCP_IDLE;
+ g_bt->timer_on = 0;
+ break;
+
+ default:
+ WL_ERROR(("%s error g_status=%d !!!\n", __FUNCTION__, \
+ g_bt->bt_state));
+ if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, FALSE);
+ g_bt->bt_state = BT_DHCP_IDLE;
+ g_bt->timer_on = 0;
+ break;
+ }
+
+ net_os_wake_unlock(g_bt->dev);
+ }
+
+ if (g_bt->timer_on) {
+ g_bt->timer_on = 0;
+ del_timer_sync(&g_bt->timer);
+ }
+
+ complete_and_exit(&g_bt->bt_exited, 0);
+}
+
+static void
+wl_iw_bt_release(void)
+{
+ bt_info_t *bt_local = g_bt;
+
+ if (!bt_local) {
+ return;
+ }
+
+ if (bt_local->bt_pid >= 0) {
+ KILL_PROC(bt_local->bt_pid, SIGTERM);
+ wait_for_completion(&bt_local->bt_exited);
+ }
+ kfree(bt_local);
+ g_bt = NULL;
+}
+
+static int
+wl_iw_bt_init(struct net_device *dev)
+{
+ bt_info_t *bt_dhcp = NULL;
+
+ bt_dhcp = kmalloc(sizeof(bt_info_t), GFP_KERNEL);
+ if (!bt_dhcp)
+ return -ENOMEM;
+
+ memset(bt_dhcp, 0, sizeof(bt_info_t));
+ bt_dhcp->bt_pid = -1;
+ g_bt = bt_dhcp;
+ bt_dhcp->dev = dev;
+ bt_dhcp->bt_state = BT_DHCP_IDLE;
+
+
+ bt_dhcp->timer_ms = 10;
+ init_timer(&bt_dhcp->timer);
+ bt_dhcp->timer.data = (ulong)bt_dhcp;
+ bt_dhcp->timer.function = wl_iw_bt_timerfunc;
+
+ sema_init(&bt_dhcp->bt_sem, 0);
+ init_completion(&bt_dhcp->bt_exited);
+ bt_dhcp->bt_pid = kernel_thread(_bt_dhcp_sysioc_thread, bt_dhcp, 0);
+ if (bt_dhcp->bt_pid < 0) {
+ WL_ERROR(("Failed in %s\n", __FUNCTION__));
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int wl_iw_attach(struct net_device *dev, void *dhdp)
+{
+ int params_size;
+ wl_iw_t *iw;
+#if defined(WL_IW_USE_ISCAN)
+ iscan_info_t *iscan = NULL;
+#endif
+
+ mutex_init(&wl_cache_lock);
+
+#if defined(WL_IW_USE_ISCAN)
+ if (!dev)
+ return 0;
+
+ memset(&g_wl_iw_params, 0, sizeof(wl_iw_extra_params_t));
+
+#ifdef CSCAN
+ params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params)) +
+ (WL_NUMCHANNELS * sizeof(uint16)) + WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
+#else
+ params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params));
+#endif
+ iscan = kmalloc(sizeof(iscan_info_t), GFP_KERNEL);
+ if (!iscan)
+ return -ENOMEM;
+ memset(iscan, 0, sizeof(iscan_info_t));
+
+ iscan->iscan_ex_params_p = (wl_iscan_params_t*)kmalloc(params_size, GFP_KERNEL);
+ if (!iscan->iscan_ex_params_p)
+ return -ENOMEM;
+ iscan->iscan_ex_param_size = params_size;
+ iscan->sysioc_pid = -1;
+
+ g_iscan = iscan;
+ iscan->dev = dev;
+ iscan->iscan_state = ISCAN_STATE_IDLE;
+#if defined(CONFIG_FIRST_SCAN)
+ g_first_broadcast_scan = BROADCAST_SCAN_FIRST_IDLE;
+ g_first_counter_scans = 0;
+ g_iscan->scan_flag = 0;
+#endif
+
+ iscan->timer_ms = 8000;
+ init_timer(&iscan->timer);
+ iscan->timer.data = (ulong)iscan;
+ iscan->timer.function = wl_iw_timerfunc;
+
+ sema_init(&iscan->sysioc_sem, 0);
+ init_completion(&iscan->sysioc_exited);
+ iscan->sysioc_pid = kernel_thread(_iscan_sysioc_thread, iscan, 0);
+ if (iscan->sysioc_pid < 0)
+ return -ENOMEM;
+#endif
+
+ iw = *(wl_iw_t **)netdev_priv(dev);
+ iw->pub = (dhd_pub_t *)dhdp;
+#ifdef SOFTAP
+ priv_dev = dev;
+#endif
+ g_scan = NULL;
+
+ g_scan = (void *)kmalloc(G_SCAN_RESULTS, GFP_KERNEL);
+ if (!g_scan)
+ return -ENOMEM;
+
+ memset(g_scan, 0, G_SCAN_RESULTS);
+ g_scan_specified_ssid = 0;
+
+#if !defined(CSCAN)
+ wl_iw_init_ss_cache_ctrl();
+#endif
+
+ wl_iw_bt_init(dev);
+
+ return 0;
+}
+
+void wl_iw_detach(void)
+{
+#if defined(WL_IW_USE_ISCAN)
+ iscan_buf_t *buf;
+ iscan_info_t *iscan = g_iscan;
+
+ if (!iscan)
+ return;
+ if (iscan->sysioc_pid >= 0) {
+ KILL_PROC(iscan->sysioc_pid, SIGTERM);
+ wait_for_completion(&iscan->sysioc_exited);
+ }
+ mutex_lock(&wl_cache_lock);
+ while (iscan->list_hdr) {
+ buf = iscan->list_hdr->next;
+ kfree(iscan->list_hdr);
+ iscan->list_hdr = buf;
+ }
+ kfree(iscan->iscan_ex_params_p);
+ kfree(iscan);
+ g_iscan = NULL;
+ mutex_unlock(&wl_cache_lock);
+#endif
+
+ if (g_scan)
+ kfree(g_scan);
+
+ g_scan = NULL;
+#if !defined(CSCAN)
+ wl_iw_release_ss_cache_ctrl();
+#endif
+ wl_iw_bt_release();
+#ifdef SOFTAP
+ if (ap_cfg_running) {
+ WL_TRACE(("\n%s AP is going down\n", __FUNCTION__));
+ wl_iw_send_priv_event(priv_dev, "AP_DOWN");
+ }
+#endif
+}
diff --git a/drivers/net/wireless/bcm4329/wl_iw.h b/drivers/net/wireless/bcm4329/wl_iw.h
new file mode 100644
index 00000000000..928291fe589
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/wl_iw.h
@@ -0,0 +1,284 @@
+/*
+ * Linux Wireless Extensions support
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ * As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module. An independent module is a module which is not
+ * derived from this software. The special exception does not apply to any
+ * modifications of the software.
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: wl_iw.h,v 1.5.34.1.6.36.4.15 2010/11/17 03:13:51 Exp $
+ */
+
+
+#ifndef _wl_iw_h_
+#define _wl_iw_h_
+
+#include <linux/wireless.h>
+
+#include <typedefs.h>
+#include <proto/ethernet.h>
+#include <wlioctl.h>
+
+#define WL_SCAN_PARAMS_SSID_MAX 10
+#define GET_SSID "SSID="
+#define GET_CHANNEL "CH="
+#define GET_NPROBE "NPROBE="
+#define GET_ACTIVE_ASSOC_DWELL "ACTIVE="
+#define GET_PASSIVE_ASSOC_DWELL "PASSIVE="
+#define GET_HOME_DWELL "HOME="
+#define GET_SCAN_TYPE "TYPE="
+
+#define BAND_GET_CMD "GETBAND"
+#define BAND_SET_CMD "SETBAND"
+#define DTIM_SKIP_GET_CMD "DTIMSKIPGET"
+#define DTIM_SKIP_SET_CMD "DTIMSKIPSET"
+#define SETSUSPEND_CMD "SETSUSPENDOPT"
+#define PNOSSIDCLR_SET_CMD "PNOSSIDCLR"
+#define PNOSETUP_SET_CMD "PNOSETUP "
+#define PNOENABLE_SET_CMD "PNOFORCE"
+#define PNODEBUG_SET_CMD "PNODEBUG"
+#define SETDFSCHANNELS_CMD "SETDFSCHANNELS"
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+
+typedef struct wl_iw_extra_params {
+ int target_channel;
+} wl_iw_extra_params_t;
+
+#define WL_IW_RSSI_MINVAL -200
+#define WL_IW_RSSI_NO_SIGNAL -91
+#define WL_IW_RSSI_VERY_LOW -80
+#define WL_IW_RSSI_LOW -70
+#define WL_IW_RSSI_GOOD -68
+#define WL_IW_RSSI_VERY_GOOD -58
+#define WL_IW_RSSI_EXCELLENT -57
+#define WL_IW_RSSI_INVALID 0
+#define MAX_WX_STRING 80
+#define isprint(c) bcm_isprint(c)
+#define WL_IW_SET_ACTIVE_SCAN (SIOCIWFIRSTPRIV+1)
+#define WL_IW_GET_RSSI (SIOCIWFIRSTPRIV+3)
+#define WL_IW_SET_PASSIVE_SCAN (SIOCIWFIRSTPRIV+5)
+#define WL_IW_GET_LINK_SPEED (SIOCIWFIRSTPRIV+7)
+#define WL_IW_GET_CURR_MACADDR (SIOCIWFIRSTPRIV+9)
+#define WL_IW_SET_STOP (SIOCIWFIRSTPRIV+11)
+#define WL_IW_SET_START (SIOCIWFIRSTPRIV+13)
+
+
+#define WL_SET_AP_CFG (SIOCIWFIRSTPRIV+15)
+#define WL_AP_STA_LIST (SIOCIWFIRSTPRIV+17)
+#define WL_AP_MAC_FLTR (SIOCIWFIRSTPRIV+19)
+#define WL_AP_BSS_START (SIOCIWFIRSTPRIV+21)
+#define AP_LPB_CMD (SIOCIWFIRSTPRIV+23)
+#define WL_AP_STOP (SIOCIWFIRSTPRIV+25)
+#define WL_FW_RELOAD (SIOCIWFIRSTPRIV+27)
+#define WL_AP_STA_DISASSOC (SIOCIWFIRSTPRIV+29)
+#define WL_COMBO_SCAN (SIOCIWFIRSTPRIV+31)
+
+#define G_SCAN_RESULTS (8*1024)
+#define WE_ADD_EVENT_FIX 0x80
+#define G_WLAN_SET_ON 0
+#define G_WLAN_SET_OFF 1
+
+#define CHECK_EXTRA_FOR_NULL(extra) \
+if (!extra) { \
+ WL_ERROR(("%s: error : extra is null pointer\n", __FUNCTION__)); \
+ return -EINVAL; \
+}
+
+typedef struct wl_iw {
+ char nickname[IW_ESSID_MAX_SIZE];
+
+ struct iw_statistics wstats;
+
+ int spy_num;
+ uint32 pwsec;
+ uint32 gwsec;
+ bool privacy_invoked;
+
+ struct ether_addr spy_addr[IW_MAX_SPY];
+ struct iw_quality spy_qual[IW_MAX_SPY];
+ void *wlinfo;
+ dhd_pub_t * pub;
+} wl_iw_t;
+
+#define WLC_IW_SS_CACHE_MAXLEN 2048
+#define WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN 32
+#define WLC_IW_BSS_INFO_MAXLEN \
+ (WLC_IW_SS_CACHE_MAXLEN - WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN)
+
+typedef struct wl_iw_ss_cache {
+ struct wl_iw_ss_cache *next;
+ int dirty;
+ uint32 buflen;
+ uint32 version;
+ uint32 count;
+ wl_bss_info_t bss_info[1];
+} wl_iw_ss_cache_t;
+
+typedef struct wl_iw_ss_cache_ctrl {
+ wl_iw_ss_cache_t *m_cache_head;
+ int m_link_down;
+ int m_timer_expired;
+ char m_active_bssid[ETHER_ADDR_LEN];
+ uint m_prev_scan_mode;
+ uint m_cons_br_scan_cnt;
+ struct timer_list *m_timer;
+} wl_iw_ss_cache_ctrl_t;
+
+typedef enum broadcast_first_scan {
+ BROADCAST_SCAN_FIRST_IDLE = 0,
+ BROADCAST_SCAN_FIRST_STARTED,
+ BROADCAST_SCAN_FIRST_RESULT_READY,
+ BROADCAST_SCAN_FIRST_RESULT_CONSUMED
+} broadcast_first_scan_t;
+#ifdef SOFTAP
+#define SSID_LEN 33
+#define SEC_LEN 16
+#define KEY_LEN 65
+#define PROFILE_OFFSET 32
+struct ap_profile {
+ uint8 ssid[SSID_LEN];
+ uint8 sec[SEC_LEN];
+ uint8 key[KEY_LEN];
+ uint32 channel;
+ uint32 preamble;
+ uint32 max_scb;
+ uint32 closednet;
+ char country_code[WLC_CNTRY_BUF_SZ];
+};
+
+
+#define MACLIST_MODE_DISABLED 0
+#define MACLIST_MODE_DENY 1
+#define MACLIST_MODE_ALLOW 2
+struct mflist {
+ uint count;
+ struct ether_addr ea[16];
+};
+
+struct mac_list_set {
+ uint32 mode;
+ struct mflist mac_list;
+};
+#endif
+
+#if WIRELESS_EXT > 12
+#include <net/iw_handler.h>
+extern const struct iw_handler_def wl_iw_handler_def;
+#endif
+
+extern int wl_iw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+extern void wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data);
+extern int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats);
+int wl_iw_attach(struct net_device *dev, void * dhdp);
+void wl_iw_detach(void);
+int wl_control_wl_start(struct net_device *dev);
+
+extern int net_os_wake_lock(struct net_device *dev);
+extern int net_os_wake_unlock(struct net_device *dev);
+extern int net_os_wake_lock_timeout(struct net_device *dev);
+extern int net_os_wake_lock_timeout_enable(struct net_device *dev);
+extern int net_os_set_suspend_disable(struct net_device *dev, int val);
+extern int net_os_set_suspend(struct net_device *dev, int val);
+extern int net_os_set_dtim_skip(struct net_device *dev, int val);
+extern int net_os_set_packet_filter(struct net_device *dev, int val);
+extern void dhd_bus_country_set(struct net_device *dev, char *country_code);
+extern char *dhd_bus_country_get(struct net_device *dev);
+extern int dhd_get_dtim_skip(dhd_pub_t *dhd);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
+#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \
+ iwe_stream_add_event(info, stream, ends, iwe, extra)
+#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \
+ iwe_stream_add_value(info, event, value, ends, iwe, event_len)
+#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \
+ iwe_stream_add_point(info, stream, ends, iwe, extra)
+#else
+#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \
+ iwe_stream_add_event(stream, ends, iwe, extra)
+#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \
+ iwe_stream_add_value(event, value, ends, iwe, event_len)
+#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \
+ iwe_stream_add_point(stream, ends, iwe, extra)
+#endif
+
+extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled);
+extern int dhd_pno_clean(dhd_pub_t *dhd);
+extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr);
+extern int dhd_pno_get_status(dhd_pub_t *dhd);
+extern int dhd_dev_pno_reset(struct net_device *dev);
+extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, \
+ int nssid, ushort scan_fr);
+extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled);
+extern int dhd_dev_get_pno_status(struct net_device *dev);
+
+#define PNO_TLV_PREFIX 'S'
+#define PNO_TLV_VERSION '1'
+#define PNO_TLV_SUBVERSION '2'
+#define PNO_TLV_RESERVED '0'
+#define PNO_TLV_TYPE_SSID_IE 'S'
+#define PNO_TLV_TYPE_TIME 'T'
+#define PNO_EVENT_UP "PNO_EVENT"
+#define PNO_SCAN_MAX_FW 508
+
+typedef struct cmd_tlv {
+ char prefix;
+ char version;
+ char subver;
+ char reserved;
+} cmd_tlv_t;
+
+#if defined(CSCAN)
+
+typedef struct cscan_tlv {
+ char prefix;
+ char version;
+ char subver;
+ char reserved;
+} cscan_tlv_t;
+
+#define CSCAN_COMMAND "CSCAN "
+#define CSCAN_TLV_PREFIX 'S'
+#define CSCAN_TLV_VERSION 1
+#define CSCAN_TLV_SUBVERSION 0
+#define CSCAN_TLV_TYPE_SSID_IE 'S'
+#define CSCAN_TLV_TYPE_CHANNEL_IE 'C'
+#define CSCAN_TLV_TYPE_NPROBE_IE 'N'
+#define CSCAN_TLV_TYPE_ACTIVE_IE 'A'
+#define CSCAN_TLV_TYPE_PASSIVE_IE 'P'
+#define CSCAN_TLV_TYPE_HOME_IE 'H'
+#define CSCAN_TLV_TYPE_STYPE_IE 'T'
+
+extern int wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list, \
+ int channel_num, int *bytes_left);
+
+extern int wl_iw_parse_data_tlv(char** list_str, void *dst, int dst_size, \
+ const char token, int input_size, int *bytes_left);
+
+extern int wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, \
+ int max, int *bytes_left);
+
+extern int wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int max);
+
+extern int wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num);
+
+#endif
+
+#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index c7f56b4aeb8..2a7af4e787b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -3341,7 +3341,7 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
iwl_send_lq_cmd(priv, &sta_priv->lq_sta.lq,
CMD_ASYNC, false);
}
- break;
+ break;
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return 0;
else
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 8e9ba177d81..8530dcab2e0 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -142,4 +142,16 @@ config CHARGER_PCF50633
help
Say Y to include support for NXP PCF50633 Main Battery Charger.
+config AB8500_BM
+ bool "AB8500 Battery Management Driver"
+ depends on AB8500_CORE && AB8500_GPADC && ARCH_U8500
+ help
+ Say Y to include support for AB8500 battery management.
+
+config AB8500_BATTERY_THERM_ON_BATCTRL
+ bool "Thermistor connected on BATCTRL ADC"
+ depends on AB8500_BM
+ help
+ Say Y to enable battery temperature measurements using
+ thermistor connected on BATCTRL ADC.
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 00050809a6c..1cccfef28b6 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -34,3 +34,4 @@ obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
+obj-$(CONFIG_AB8500_BM) += ab8500_charger.o ab8500_btemp.o ab8500_fg.o ab8500_chargalg.o
diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
new file mode 100644
index 00000000000..ac6b4a2e19f
--- /dev/null
+++ b/drivers/power/ab8500_btemp.c
@@ -0,0 +1,1080 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Battery temperature driver for AB8500
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Johan Palsson <johan.palsson@stericsson.com>
+ * Author: Karl Komierowski <karl.komierowski@stericsson.com>
+ * Author: Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500/ab8500-bm.h>
+#include <linux/mfd/ab8500/ab8500-gpadc.h>
+
+#define VTVOUT_V 1800
+
+#define BTEMP_THERMAL_LOW_LIMIT -10
+#define BTEMP_THERMAL_MED_LIMIT 0
+#define BTEMP_THERMAL_HIGH_LIMIT_52 52
+#define BTEMP_THERMAL_HIGH_LIMIT_57 57
+#define BTEMP_THERMAL_HIGH_LIMIT_62 62
+
+#define BTEMP_BATCTRL_CURR_SRC_7UA 7
+#define BTEMP_BATCTRL_CURR_SRC_20UA 20
+
+#define to_ab8500_btemp_device_info(x) container_of((x), \
+ struct ab8500_btemp, btemp_psy);
+
+/**
+ * struct ab8500_btemp_interrupts - ab8500 interrupts
+ * @name: name of the interrupt
+ * @isr function pointer to the isr
+ */
+struct ab8500_btemp_interrupts {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct ab8500_btemp_events {
+ bool batt_rem;
+ bool btemp_high;
+ bool btemp_medhigh;
+ bool btemp_lowmed;
+ bool btemp_low;
+ bool ac_conn;
+ bool usb_conn;
+};
+
+struct ab8500_btemp_ranges {
+ int btemp_high_limit;
+ int btemp_med_limit;
+ int btemp_low_limit;
+};
+
+/**
+ * struct ab8500_btemp - ab8500 BTEMP device information
+ * @dev: Pointer to the structure device
+ * @chip_id: Chip-Id of the AB8500
+ * @curr_source: What current source we use, in uA
+ * @bat_temp: Battery temperature in degree Celcius
+ * @prev_bat_temp Last dispatched battery temperature
+ * @parent: Pointer to the struct ab8500
+ * @gpadc: Pointer to the struct gpadc
+ * @pdata: Pointer to the ab8500_btemp platform data
+ * @bat: Pointer to the ab8500_bm platform data
+ * @btemp_psy: Structure for BTEMP specific battery properties
+ * @events: Structure for information about events triggered
+ * @btemp_ranges: Battery temperature range structure
+ * @btemp_wq: Work queue for measuring the temperature periodically
+ * @btemp_periodic_work: Work for measuring the temperature periodically
+ */
+struct ab8500_btemp {
+ struct device *dev;
+ u8 chip_id;
+ int curr_source;
+ int bat_temp;
+ int prev_bat_temp;
+ struct ab8500 *parent;
+ struct ab8500_gpadc *gpadc;
+ struct ab8500_btemp_platform_data *pdata;
+ struct ab8500_bm_data *bat;
+ struct power_supply btemp_psy;
+ struct ab8500_btemp_events events;
+ struct ab8500_btemp_ranges btemp_ranges;
+ struct workqueue_struct *btemp_wq;
+ struct delayed_work btemp_periodic_work;
+};
+
+/* BTEMP power supply properties */
+static enum power_supply_property ab8500_btemp_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_TEMP,
+};
+
+/**
+ * ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance
+ * @di: pointer to the ab8500_btemp structure
+ * @v_batctrl: measured batctrl voltage
+ *
+ * This function returns the battery resistance that is
+ * derived from the BATCTRL voltage.
+ * Returns value in Ohms.
+ */
+static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
+ int v_batctrl)
+{
+ int rbs;
+
+ switch (di->chip_id) {
+ case AB8500_CUT1P0:
+ case AB8500_CUT1P1:
+ /*
+ * For ABB cut1.0 and 1.1 BAT_CTRL is internally
+ * connected to 1.8V through a 450k resistor
+ */
+ rbs = (450000 * (v_batctrl)) / (1800 - v_batctrl);
+ break;
+ default:
+ if (di->bat->adc_therm == ADC_THERM_BATCTRL) {
+ /*
+ * If the battery has internal NTC, we use the current
+ * source to calculate the resistance, 7uA or 20uA
+ */
+ rbs = v_batctrl * 1000 / di->curr_source;
+ } else {
+ /*
+ * BAT_CTRL is internally
+ * connected to 1.8V through a 80k resistor
+ */
+ rbs = (80000 * (v_batctrl)) / (1800 - v_batctrl);
+ }
+ break;
+ }
+
+ return rbs;
+}
+
+/**
+ * ab8500_btemp_read_batctrl_voltage() - measure batctrl voltage
+ * @di: pointer to the ab8500_btemp structure
+ *
+ * This function returns the voltage on BATCTRL. Returns value in mV.
+ */
+static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di)
+{
+ int vbtemp;
+ static int prev;
+
+ vbtemp = ab8500_gpadc_convert(di->gpadc, BAT_CTRL);
+ if (vbtemp < 0) {
+ dev_err(di->dev,
+ "%s gpadc conversion failed, using previous value",
+ __func__);
+ return prev;
+ }
+ prev = vbtemp;
+ return vbtemp;
+}
+
+/**
+ * ab8500_btemp_curr_source_enable() - enable/disable batctrl current source
+ * @di: pointer to the ab8500_btemp structure
+ * @enable: enable or disable the current source
+ *
+ * Enable or disable the current sources for the BatCtrl AD channel
+ */
+static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
+ bool enable)
+{
+ int curr;
+ int ret = 0;
+
+ /*
+ * BATCTRL current sources are included on AB8500 cut2.0
+ * and future versions
+ */
+ if (di->chip_id == AB8500_CUT1P0 || di->chip_id == AB8500_CUT1P1)
+ return 0;
+
+ /* Only do this for batteries with internal NTC */
+ if (di->bat->adc_therm == ADC_THERM_BATCTRL && enable) {
+ if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
+ curr = BAT_CTRL_7U_ENA;
+ else
+ curr = BAT_CTRL_20U_ENA;
+
+ dev_dbg(di->dev, "Set BATCTRL %duA\n", di->curr_source);
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ FORCE_BAT_CTRL_CMP_HIGH, FORCE_BAT_CTRL_CMP_HIGH);
+ if (ret) {
+ dev_err(di->dev, "%s failed setting cmp_force\n",
+ __func__);
+ return ret;
+ }
+
+ /*
+ * We have to wait one 32kHz cycle before enabling
+ * the current source, since ForceBatCtrlCmpHigh needs
+ * to be written in a separate cycle
+ */
+ udelay(32);
+
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ FORCE_BAT_CTRL_CMP_HIGH | curr);
+ if (ret) {
+ dev_err(di->dev, "%s failed enabling current source\n",
+ __func__);
+ goto disable_curr_source;
+ }
+ } else if (di->bat->adc_therm == ADC_THERM_BATCTRL && !enable) {
+ dev_dbg(di->dev, "Disable BATCTRL curr source\n");
+
+ /* Write 0 to the curr bits */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
+ ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+ if (ret) {
+ dev_err(di->dev, "%s failed disabling current source\n",
+ __func__);
+ goto disable_curr_source;
+ }
+
+ /* Enable Pull-Up and comparator */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA,
+ BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA);
+ if (ret) {
+ dev_err(di->dev, "%s failed enabling PU and comp\n",
+ __func__);
+ goto enable_pu_comp;
+ }
+
+ /*
+ * We have to wait one 32kHz cycle before disabling
+ * ForceBatCtrlCmpHigh since this needs to be written
+ * in a separate cycle
+ */
+ udelay(32);
+
+ /* Disable 'force comparator' */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH);
+ if (ret) {
+ dev_err(di->dev, "%s failed disabling force comp\n",
+ __func__);
+ goto disable_force_comp;
+ }
+ }
+ return ret;
+
+ /*
+ * We have to try unsetting FORCE_BAT_CTRL_CMP_HIGH one more time
+ * if we got an error above
+ */
+disable_curr_source:
+ /* Write 0 to the curr bits */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
+ ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+ if (ret) {
+ dev_err(di->dev, "%s failed disabling current source\n",
+ __func__);
+ return ret;
+ }
+enable_pu_comp:
+ /* Enable Pull-Up and comparator */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA,
+ BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA);
+ if (ret) {
+ dev_err(di->dev, "%s failed enabling PU and comp\n",
+ __func__);
+ return ret;
+ }
+
+disable_force_comp:
+ /*
+ * We have to wait one 32kHz cycle before disabling
+ * ForceBatCtrlCmpHigh since this needs to be written
+ * in a separate cycle
+ */
+ udelay(32);
+
+ /* Disable 'force comparator' */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH);
+ if (ret) {
+ dev_err(di->dev, "%s failed disabling force comp\n",
+ __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_btemp_get_batctrl_res() - get battery resistance
+ * @di: pointer to the ab8500_btemp structure
+ *
+ * This function returns the battery pack identification resistance.
+ * Returns value in Ohms.
+ */
+static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
+{
+ int ret;
+ int batctrl;
+ int res;
+
+ /*
+ * BATCTRL current sources are included on AB8500 cut2.0
+ * and future versions
+ */
+ ret = ab8500_btemp_curr_source_enable(di, true);
+ if (ret) {
+ dev_err(di->dev, "%s curr source enabled failed\n", __func__);
+ return ret;
+ }
+
+ batctrl = ab8500_btemp_read_batctrl_voltage(di);
+ res = ab8500_btemp_batctrl_volt_to_res(di, batctrl);
+
+ ret = ab8500_btemp_curr_source_enable(di, false);
+ if (ret) {
+ dev_err(di->dev, "%s curr source disable failed\n", __func__);
+ return ret;
+ }
+
+ dev_dbg(di->dev, "%s batctrl: %d res: %d ",
+ __func__, batctrl, res);
+
+ return res;
+}
+
+/**
+ * ab8500_btemp_res_to_temp() - resistance to temperature
+ * @di: pointer to the ab8500_btemp structure
+ * @tbl: pointer to the resiatance to temperature table
+ * @tbl_size: size of the resistance to temperature table
+ * @res: resistance to calculate the temperature from
+ *
+ * This function returns the battery temperature in degrees Celcius
+ * based on the NTC resistance.
+ */
+static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
+ const struct res_to_temp *tbl, int tbl_size, int res)
+{
+ int i, temp;
+ /*
+ * Calculate the formula for the straight line
+ * Simple interpolation if we are within
+ * the resistance table limits, extrapolate
+ * if resistance is outside the limits.
+ */
+ if (res > tbl[0].resist)
+ i = 0;
+ else if (res <= tbl[tbl_size - 1].resist)
+ i = tbl_size - 2;
+ else {
+ i = 0;
+ while (!(res <= tbl[i].resist &&
+ res > tbl[i + 1].resist))
+ i++;
+ }
+
+ temp = tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
+ (res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
+ return temp;
+}
+
+/**
+ * ab8500_btemp_measure_temp() - measure battery temperature
+ * @di: pointer to the ab8500_btemp structure
+ *
+ * Returns battery temperature (on success) else the previous temperature
+ */
+static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
+{
+ int temp;
+ static int prev;
+ int rbat, rntc, vntc;
+ u8 id;
+
+ id = di->bat->batt_id;
+
+ if (di->bat->adc_therm == ADC_THERM_BATCTRL &&
+ id != BATTERY_UNKNOWN) {
+
+ rbat = ab8500_btemp_get_batctrl_res(di);
+ if (rbat < 0) {
+ dev_err(di->dev, "%s get batctrl res failed\n",
+ __func__);
+ /*
+ * Return out-of-range temperature so that
+ * charging is stopped
+ */
+ return BTEMP_THERMAL_LOW_LIMIT;
+ }
+
+ temp = ab8500_btemp_res_to_temp(di,
+ di->bat->bat_type[id].r_to_t_tbl,
+ di->bat->bat_type[id].n_temp_tbl_elements, rbat);
+ } else {
+ vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL);
+ if (vntc < 0) {
+ dev_err(di->dev,
+ "%s gpadc conversion failed,"
+ " using previous value\n", __func__);
+ return prev;
+ }
+ /*
+ * The PCB NTC is sourced from VTVOUT via a 230kOhm
+ * resistor.
+ */
+ rntc = 230000 * vntc / (VTVOUT_V - vntc);
+
+ temp = ab8500_btemp_res_to_temp(di,
+ di->bat->bat_type[id].r_to_t_tbl,
+ di->bat->bat_type[id].n_temp_tbl_elements, rntc);
+ prev = temp;
+ }
+ dev_dbg(di->dev, "Battery temperature is %d\n", temp);
+ return temp;
+}
+
+/**
+ * ab8500_btemp_id() - Identify the connected battery
+ * @di: pointer to the ab8500_btemp structure
+ *
+ * This function will try to identify the battery by reading the ID
+ * resistor. Some brands use a combined ID resistor with a NTC resistor to
+ * both be able to identify and to read the temperature of it.
+ */
+static int ab8500_btemp_id(struct ab8500_btemp *di)
+{
+ int res;
+ u8 i;
+
+ di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
+ di->bat->batt_id = BATTERY_UNKNOWN;
+
+ res = ab8500_btemp_get_batctrl_res(di);
+ if (res < 0) {
+ dev_err(di->dev, "%s get batctrl res failed\n", __func__);
+ return -ENXIO;
+ }
+
+ /* BATTERY_UNKNOWN is defined on position 0, skip it! */
+ for (i = BATTERY_UNKNOWN + 1; i < di->bat->n_btypes; i++) {
+ if ((res <= di->bat->bat_type[i].resis_high) &&
+ (res >= di->bat->bat_type[i].resis_low)) {
+ dev_dbg(di->dev, "Battery detected on %s"
+ " low %d < res %d < high: %d"
+ " index: %d\n",
+ di->bat->adc_therm == ADC_THERM_BATCTRL ?
+ "BATCTRL" : "BATTEMP",
+ di->bat->bat_type[i].resis_low, res,
+ di->bat->bat_type[i].resis_high, i);
+
+ di->bat->batt_id = i;
+ break;
+ }
+ }
+
+ if (di->bat->batt_id == BATTERY_UNKNOWN) {
+ dev_warn(di->dev, "Battery identified as unknown"
+ ", resistance %d Ohm\n", res);
+ return -ENXIO;
+ }
+
+ /*
+ * We only have to change current source if the
+ * detected type is Type 1, else we use the 7uA source
+ */
+ if (di->bat->adc_therm == ADC_THERM_BATCTRL && di->bat->batt_id == 1) {
+ dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
+ di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
+ }
+
+ return di->bat->batt_id;
+}
+
+/**
+ * ab8500_btemp_periodic_work() - Measuring the temperature periodically
+ * @work: pointer to the work_struct structure
+ *
+ * Work function for measuring the temperature periodically
+ */
+static void ab8500_btemp_periodic_work(struct work_struct *work)
+{
+ struct ab8500_btemp *di = container_of(work,
+ struct ab8500_btemp, btemp_periodic_work.work);
+
+ di->bat_temp = ab8500_btemp_measure_temp(di);
+
+ if (di->bat_temp != di->prev_bat_temp) {
+ di->prev_bat_temp = di->bat_temp;
+ power_supply_changed(&di->btemp_psy);
+ }
+
+ /* Schedule a new measurement */
+ queue_delayed_work(di->btemp_wq,
+ &di->btemp_periodic_work,
+ round_jiffies(20 * HZ));
+}
+
+/**
+ * ab8500_btemp_batctrlindb_handler() - battery removal detected
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_batctrlindb_handler(int irq, void *_di)
+{
+ struct ab8500_btemp *di = _di;
+ dev_err(di->dev, "Battery removal detected!\n");
+
+ di->events.batt_rem = true;
+ power_supply_changed(&di->btemp_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_templow_handler() - battery temp lower than 10 degrees
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
+{
+ struct ab8500_btemp *di = _di;
+ switch (di->chip_id) {
+ case AB8500_CUT1P0:
+ case AB8500_CUT1P1:
+ case AB8500_CUT2P0:
+ dev_dbg(di->dev, "Ignore false btemp low irq"
+ " for ABB cut 1.0, 1.1 and 2.0\n");
+
+ break;
+ default:
+ dev_crit(di->dev, "Battery temperature lower than -10deg c\n");
+
+ di->events.btemp_low = true;
+ di->events.btemp_high = false;
+ di->events.btemp_medhigh = false;
+ di->events.btemp_lowmed = false;
+ power_supply_changed(&di->btemp_psy);
+
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_temphigh_handler() - battery temp higher than max temp
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_temphigh_handler(int irq, void *_di)
+{
+ struct ab8500_btemp *di = _di;
+
+ dev_crit(di->dev, "Battery temperature is higher than MAX temp\n");
+
+ di->events.btemp_high = true;
+ di->events.btemp_medhigh = false;
+ di->events.btemp_lowmed = false;
+ di->events.btemp_low = false;
+ power_supply_changed(&di->btemp_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_lowmed_handler() - battery temp between low and medium
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_lowmed_handler(int irq, void *_di)
+{
+ struct ab8500_btemp *di = _di;
+
+ dev_dbg(di->dev, "Battery temperature is between low and medium\n");
+
+ di->events.btemp_lowmed = true;
+ di->events.btemp_medhigh = false;
+ di->events.btemp_high = false;
+ di->events.btemp_low = false;
+ power_supply_changed(&di->btemp_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_medhigh_handler() - battery temp between medium and high
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_medhigh_handler(int irq, void *_di)
+{
+ struct ab8500_btemp *di = _di;
+
+ dev_dbg(di->dev, "Battery temperature is between medium and high\n");
+
+ di->events.btemp_medhigh = true;
+ di->events.btemp_lowmed = false;
+ di->events.btemp_high = false;
+ di->events.btemp_low = false;
+ power_supply_changed(&di->btemp_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_periodic() - Periodic temperature measurements
+ * @di: pointer to the ab8500_btemp structure
+ * @enable: enable or disable periodic temperature measurements
+ *
+ * Starts of stops periodic temperature measurements. Periodic measurements
+ * should only be done when a charger is connected.
+ */
+static void ab8500_btemp_periodic(struct ab8500_btemp *di,
+ bool enable)
+{
+ dev_dbg(di->dev, "Enable periodic temperature measurements: %d\n",
+ enable);
+
+ if (enable)
+ queue_delayed_work(di->btemp_wq, &di->btemp_periodic_work, 0);
+ else
+ cancel_delayed_work_sync(&di->btemp_periodic_work);
+}
+
+/**
+ * ab8500_btemp_get_temp() - get battery temperature
+ * @di: pointer to the ab8500_btemp structure
+ *
+ * Returns battery temperature
+ */
+static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
+{
+ int temp = 0;
+
+ /*
+ * The BTEMP events are not reliabe on AB8500 cut2.0
+ * and prior versions
+ */
+ switch (di->chip_id) {
+ case AB8500_CUT1P0:
+ case AB8500_CUT1P1:
+ case AB8500_CUT2P0:
+ temp = di->bat_temp * 10;
+
+ break;
+ default:
+ if (di->events.btemp_low) {
+ if (temp > di->btemp_ranges.btemp_low_limit)
+ temp = di->btemp_ranges.btemp_low_limit;
+ else
+ temp = di->bat_temp * 10;
+ } else if (di->events.btemp_high) {
+ if (temp < di->btemp_ranges.btemp_high_limit)
+ temp = di->btemp_ranges.btemp_high_limit;
+ else
+ temp = di->bat_temp * 10;
+ } else if (di->events.btemp_lowmed) {
+ if (temp > di->btemp_ranges.btemp_med_limit)
+ temp = di->btemp_ranges.btemp_med_limit;
+ else
+ temp = di->bat_temp * 10;
+ } else if (di->events.btemp_medhigh) {
+ if (temp < di->btemp_ranges.btemp_med_limit)
+ temp = di->btemp_ranges.btemp_med_limit;
+ else
+ temp = di->bat_temp * 10;
+ } else
+ temp = di->bat_temp * 10;
+
+ break;
+ }
+ return temp;
+}
+
+/**
+ * ab8500_btemp_get_property() - get the btemp properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the btemp
+ * properties by reading the sysfs files.
+ * online: presence of the battery
+ * present: presence of the battery
+ * technology: battery technology
+ * temp: battery temperature
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_btemp_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab8500_btemp *di;
+
+ di = to_ab8500_btemp_device_info(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (di->events.batt_rem)
+ val->intval = 0;
+ else
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = di->bat->bat_type[di->bat->batt_id].name;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = ab8500_btemp_get_temp(di);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)
+{
+ struct power_supply *psy;
+ struct power_supply *ext;
+ struct ab8500_btemp *di;
+ union power_supply_propval ret;
+ int i, j;
+ bool psy_found = false;
+
+ psy = (struct power_supply *)data;
+ ext = dev_get_drvdata(dev);
+ di = to_ab8500_btemp_device_info(psy);
+
+ /*
+ * For all psy where the name of your driver
+ * appears in any supplied_to
+ */
+ for (i = 0; i < ext->num_supplicants; i++) {
+ if (!strcmp(ext->supplied_to[i], psy->name))
+ psy_found = true;
+ }
+
+ if (!psy_found)
+ return 0;
+
+ /* Go through all properties for the psy */
+ for (j = 0; j < ext->num_properties; j++) {
+ enum power_supply_property prop;
+ prop = ext->properties[j];
+
+ if (ext->get_property(ext, prop, &ret))
+ continue;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_MAINS:
+ /* AC disconnected */
+ if (!ret.intval && di->events.ac_conn) {
+ di->events.ac_conn = false;
+ if (!di->events.usb_conn)
+ ab8500_btemp_periodic(di,
+ false);
+ }
+ /* AC connected */
+ else if (ret.intval && !di->events.ac_conn) {
+ di->events.ac_conn = true;
+ if (!di->events.usb_conn)
+ ab8500_btemp_periodic(di, true);
+ }
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ /* USB disconnected */
+ if (!ret.intval && di->events.usb_conn) {
+ di->events.usb_conn = false;
+ if (!di->events.ac_conn)
+ ab8500_btemp_periodic(di,
+ false);
+ }
+ /* USB connected */
+ else if (ret.intval && !di->events.usb_conn) {
+ di->events.usb_conn = true;
+ if (!di->events.ac_conn)
+ ab8500_btemp_periodic(di, true);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ab8500_btemp_external_power_changed() - callback for power supply changes
+ * @psy: pointer to the structure power_supply
+ *
+ * This function is pointing to the function pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in the external power
+ * supply to the btemp.
+ */
+static void ab8500_btemp_external_power_changed(struct power_supply *psy)
+{
+ struct ab8500_btemp *di = to_ab8500_btemp_device_info(psy);
+
+ class_for_each_device(power_supply_class, NULL,
+ &di->btemp_psy, ab8500_btemp_get_ext_psy_data);
+}
+
+/* ab8500 btemp driver interrupts and their respective isr */
+static struct ab8500_btemp_interrupts ab8500_btemp_irq[] = {
+ {"BAT_CTRL_INDB", ab8500_btemp_batctrlindb_handler},
+ {"BTEMP_LOW", ab8500_btemp_templow_handler},
+ {"BTEMP_HIGH", ab8500_btemp_temphigh_handler},
+ {"BTEMP_LOW_MEDIUM", ab8500_btemp_lowmed_handler},
+ {"BTEMP_MEDIUM_HIGH", ab8500_btemp_medhigh_handler},
+};
+
+#if defined(CONFIG_PM)
+static int ab8500_btemp_resume(struct platform_device *pdev)
+{
+ struct ab8500_btemp *di = platform_get_drvdata(pdev);
+
+ if (di->events.ac_conn || di->events.usb_conn)
+ ab8500_btemp_periodic(di, true);
+
+ return 0;
+}
+
+static int ab8500_btemp_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab8500_btemp *di = platform_get_drvdata(pdev);
+
+ if (di->events.ac_conn || di->events.usb_conn)
+ ab8500_btemp_periodic(di, false);
+
+ return 0;
+}
+#else
+#define ab8500_btemp_suspend NULL
+#define ab8500_btemp_resume NULL
+#endif
+
+static int __devexit ab8500_btemp_remove(struct platform_device *pdev)
+{
+ struct ab8500_btemp *di = platform_get_drvdata(pdev);
+ int i, irq;
+
+ /* Disable interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+ free_irq(irq, di);
+ }
+
+ /* Delete the work queue */
+ destroy_workqueue(di->btemp_wq);
+
+ flush_scheduled_work();
+ power_supply_unregister(&di->btemp_psy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di);
+
+ return 0;
+}
+
+static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
+{
+ int irq, i, ret = 0;
+ u8 val;
+ struct ab8500_platform_data *plat;
+
+ struct ab8500_btemp *di =
+ kzalloc(sizeof(struct ab8500_btemp), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ /* get parent data */
+ di->dev = &pdev->dev;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
+ di->gpadc = ab8500_gpadc_get();
+
+ plat = dev_get_platdata(di->parent->dev);
+
+ /* get btemp specific platform data */
+ if (!plat->btemp) {
+ dev_err(di->dev, "no btemp platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+ di->pdata = plat->btemp;
+
+ /* get battery specific platform data */
+ if (!plat->battery) {
+ dev_err(di->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+ di->bat = plat->battery;
+
+ /* BTEMP supply */
+ di->btemp_psy.name = "ab8500_btemp";
+ di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->btemp_psy.properties = ab8500_btemp_props;
+ di->btemp_psy.num_properties = ARRAY_SIZE(ab8500_btemp_props);
+ di->btemp_psy.get_property = ab8500_btemp_get_property;
+ di->btemp_psy.supplied_to = di->pdata->supplied_to;
+ di->btemp_psy.num_supplicants = di->pdata->num_supplicants;
+ di->btemp_psy.external_power_changed =
+ ab8500_btemp_external_power_changed;
+
+
+ /* Create a work queue for the btemp */
+ di->btemp_wq =
+ create_singlethread_workqueue("ab8500_btemp_wq");
+ if (di->btemp_wq == NULL) {
+ dev_err(di->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for measuring temperature periodically */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->btemp_periodic_work,
+ ab8500_btemp_periodic_work);
+
+ /* Get Chip ID of the ABB ASIC */
+ ret = abx500_get_chip_id(di->dev);
+ if (ret < 0) {
+ dev_err(di->dev, "failed to get chip ID\n");
+ goto free_btemp_wq;
+ }
+ di->chip_id = ret;
+ dev_dbg(di->dev, "AB8500 CID is: 0x%02x\n",
+ di->chip_id);
+
+ /* Identify the battery */
+ if (ab8500_btemp_id(di) < 0)
+ dev_warn(di->dev, "failed to identify the battery\n");
+
+ /* Set BTEMP thermal limits. Low and Med are fixed */
+ di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
+ di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_BTEMP_HIGH_TH, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ goto free_btemp_wq;
+ }
+ switch (val) {
+ case BTEMP_HIGH_TH_57_0:
+ case BTEMP_HIGH_TH_57_1:
+ di->btemp_ranges.btemp_high_limit =
+ BTEMP_THERMAL_HIGH_LIMIT_57;
+ break;
+ case BTEMP_HIGH_TH_52:
+ di->btemp_ranges.btemp_high_limit =
+ BTEMP_THERMAL_HIGH_LIMIT_52;
+ break;
+ case BTEMP_HIGH_TH_62:
+ di->btemp_ranges.btemp_high_limit =
+ BTEMP_THERMAL_HIGH_LIMIT_62;
+ break;
+ }
+
+ /* Measure temperature once initially */
+ di->bat_temp = ab8500_btemp_measure_temp(di);
+
+ /* Register BTEMP power supply class */
+ ret = power_supply_register(di->dev, &di->btemp_psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register BTEMP psy\n");
+ goto free_btemp_wq;
+ }
+
+ /* Register interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+ ret = request_threaded_irq(irq, NULL, ab8500_btemp_irq[i].isr,
+ IRQF_SHARED | IRQF_NO_SUSPEND,
+ ab8500_btemp_irq[i].name, di);
+
+ if (ret) {
+ dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+ , ab8500_btemp_irq[i].name, irq, ret);
+ goto free_irq;
+ }
+ dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+ ab8500_btemp_irq[i].name, irq, ret);
+ }
+
+ platform_set_drvdata(pdev, di);
+
+ return ret;
+
+free_irq:
+ power_supply_unregister(&di->btemp_psy);
+
+ /* We also have to free all successfully registered irqs */
+ for (i = i - 1; i >= 0; i--) {
+ irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+ free_irq(irq, di);
+ }
+free_btemp_wq:
+ destroy_workqueue(di->btemp_wq);
+free_device_info:
+ kfree(di);
+
+ return ret;
+}
+
+static struct platform_driver ab8500_btemp_driver = {
+ .probe = ab8500_btemp_probe,
+ .remove = __devexit_p(ab8500_btemp_remove),
+ .suspend = ab8500_btemp_suspend,
+ .resume = ab8500_btemp_resume,
+ .driver = {
+ .name = "ab8500-btemp",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab8500_btemp_init(void)
+{
+ return platform_driver_register(&ab8500_btemp_driver);
+}
+
+static void __exit ab8500_btemp_exit(void)
+{
+ platform_driver_unregister(&ab8500_btemp_driver);
+}
+
+subsys_initcall_sync(ab8500_btemp_init);
+module_exit(ab8500_btemp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
+MODULE_ALIAS("platform:ab8500-btemp");
+MODULE_DESCRIPTION("AB8500 battery temperature driver");
diff --git a/drivers/power/ab8500_chargalg.c b/drivers/power/ab8500_chargalg.c
new file mode 100644
index 00000000000..fde04969ea5
--- /dev/null
+++ b/drivers/power/ab8500_chargalg.c
@@ -0,0 +1,1938 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Charging algorithm driver for AB8500
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Johan Palsson <johan.palsson@stericsson.com>
+ * Author: Karl Komierowski <karl.komierowski@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/ab8500/ux500_chargalg.h>
+#include <linux/mfd/ab8500/ab8500-bm.h>
+#include <linux/mfd/ab8500/ab8500-gpadc.h>
+
+/* Watchdog kick interval */
+#define CHG_WD_INTERVAL (60 * HZ)
+
+/* End-of-charge criteria counter */
+#define EOC_COND_CNT 10
+
+/* Recharge criteria counter */
+#define RCH_COND_CNT 3
+
+#define to_ab8500_chargalg_device_info(x) container_of((x), \
+ struct ab8500_chargalg, chargalg_psy);
+
+enum ab8500_chargers {
+ NO_CHG,
+ AC_CHG,
+ USB_CHG,
+};
+
+struct ab8500_chargalg_charger_info {
+ enum ab8500_chargers conn_chg;
+ enum ab8500_chargers prev_conn_chg;
+ enum ab8500_chargers online_chg;
+ enum ab8500_chargers prev_online_chg;
+ enum ab8500_chargers charger_type;
+ bool usb_chg_ok;
+ bool ac_chg_ok;
+ int usb_volt;
+ int usb_curr;
+ int ac_volt;
+ int ac_curr;
+ int usb_vset;
+ int usb_iset;
+ int ac_vset;
+ int ac_iset;
+};
+
+struct ab8500_chargalg_suspension_status {
+ bool suspended_change;
+ bool ac_suspended;
+ bool usb_suspended;
+};
+
+struct ab8500_chargalg_battery_data {
+ int temp;
+ int volt;
+ int avg_curr;
+ int inst_curr;
+ int percent;
+};
+
+enum ab8500_chargalg_states {
+ STATE_HANDHELD_INIT,
+ STATE_HANDHELD,
+ STATE_CHG_NOT_OK_INIT,
+ STATE_CHG_NOT_OK,
+ STATE_HW_TEMP_PROTECT_INIT,
+ STATE_HW_TEMP_PROTECT,
+ STATE_NORMAL_INIT,
+ STATE_NORMAL,
+ STATE_WAIT_FOR_RECHARGE_INIT,
+ STATE_WAIT_FOR_RECHARGE,
+ STATE_MAINTENANCE_A_INIT,
+ STATE_MAINTENANCE_A,
+ STATE_MAINTENANCE_B_INIT,
+ STATE_MAINTENANCE_B,
+ STATE_TEMP_UNDEROVER_INIT,
+ STATE_TEMP_UNDEROVER,
+ STATE_TEMP_LOWHIGH_INIT,
+ STATE_TEMP_LOWHIGH,
+ STATE_SUSPENDED_INIT,
+ STATE_SUSPENDED,
+ STATE_OVV_PROTECT_INIT,
+ STATE_OVV_PROTECT,
+ STATE_SAFETY_TIMER_EXPIRED_INIT,
+ STATE_SAFETY_TIMER_EXPIRED,
+ STATE_BATT_REMOVED_INIT,
+ STATE_BATT_REMOVED,
+ STATE_WD_EXPIRED_INIT,
+ STATE_WD_EXPIRED,
+};
+
+static const char *states[] = {
+ "HANDHELD_INIT",
+ "HANDHELD",
+ "CHG_NOT_OK_INIT",
+ "CHG_NOT_OK",
+ "HW_TEMP_PROTECT_INIT",
+ "HW_TEMP_PROTECT",
+ "NORMAL_INIT",
+ "NORMAL",
+ "WAIT_FOR_RECHARGE_INIT",
+ "WAIT_FOR_RECHARGE",
+ "MAINTENANCE_A_INIT",
+ "MAINTENANCE_A",
+ "MAINTENANCE_B_INIT",
+ "MAINTENANCE_B",
+ "TEMP_UNDEROVER_INIT",
+ "TEMP_UNDEROVER",
+ "TEMP_LOWHIGH_INIT",
+ "TEMP_LOWHIGH",
+ "SUSPENDED_INIT",
+ "SUSPENDED",
+ "OVV_PROTECT_INIT",
+ "OVV_PROTECT",
+ "SAFETY_TIMER_EXPIRED_INIT",
+ "SAFETY_TIMER_EXPIRED",
+ "BATT_REMOVED_INIT",
+ "BATT_REMOVED",
+ "WD_EXPIRED_INIT",
+ "WD_EXPIRED",
+};
+
+struct ab8500_chargalg_events {
+ bool batt_unknown;
+ bool mainextchnotok;
+ bool batt_ovv;
+ bool batt_rem;
+ bool btemp_underover;
+ bool btemp_lowhigh;
+ bool main_thermal_prot;
+ bool usb_thermal_prot;
+ bool main_ovv;
+ bool vbus_ovv;
+ bool usbchargernotok;
+ bool safety_timer_expired;
+ bool maintenance_timer_expired;
+ bool ac_wd_expired;
+ bool usb_wd_expired;
+ bool ac_cv_active;
+ bool usb_cv_active;
+ bool vbus_collapsed;
+};
+
+/**
+ * struct ab8500_charge_curr_maximization - Charger maximization parameters
+ * @original_iset: the non optimized/maximised charger current
+ * @current_iset: the charging current used at this moment
+ * @test_delta_i: the delta between the current we want to charge and the
+ current that is really going into the battery
+ * @condition_cnt: number of iterations needed before a new charger current
+ is set
+ * @max_current: maximum charger current
+ * @wait_cnt: to avoid too fast current step down in case of charger
+ * voltage collapse, we insert this delay between step
+ * down
+ * @level: tells in how many steps the charging current has been
+ increased
+ */
+struct ab8500_charge_curr_maximization {
+ int original_iset;
+ int current_iset;
+ int test_delta_i;
+ int condition_cnt;
+ int max_current;
+ int wait_cnt;
+ u8 level;
+};
+
+enum maxim_ret {
+ MAXIM_RET_NOACTION,
+ MAXIM_RET_CHANGE,
+ MAXIM_RET_IBAT_TOO_HIGH,
+};
+
+/**
+ * struct ab8500_chargalg - ab8500 Charging algorithm device information
+ * @dev: pointer to the structure device
+ * @charge_status: battery operating status
+ * @eoc_cnt: counter used to determine end-of_charge
+ * @rch_cnt: counter used to determine start of recharge
+ * @maintenance_chg: indicate if maintenance charge is active
+ * @t_hyst_norm temperature hysteresis when the temperature has been
+ * over or under normal limits
+ * @t_hyst_lowhigh temperature hysteresis when the temperature has been
+ * over or under the high or low limits
+ * @charge_state: current state of the charging algorithm
+ * @ccm charging current maximization parameters
+ * @chg_info: information about connected charger types
+ * @batt_data: data of the battery
+ * @susp_status: current charger suspension status
+ * @parent: pointer to the struct ab8500
+ * @pdata: pointer to the ab8500_chargalg platform data
+ * @bat: pointer to the ab8500_bm platform data
+ * @chargalg_psy: structure that holds the battery properties exposed by
+ * the charging algorithm
+ * @events: structure for information about events triggered
+ * @chargalg_wq: work queue for running the charging algorithm
+ * @chargalg_periodic_work: work to run the charging algorithm periodically
+ * @chargalg_wd_work: work to kick the charger watchdog periodically
+ * @chargalg_work: work to run the charging algorithm instantly
+ * @safety_timer: charging safety timer
+ * @maintenance_timer: maintenance charging timer
+ * @chargalg_kobject: structure of type kobject
+ */
+struct ab8500_chargalg {
+ struct device *dev;
+ int charge_status;
+ int eoc_cnt;
+ int rch_cnt;
+ bool maintenance_chg;
+ int t_hyst_norm;
+ int t_hyst_lowhigh;
+ enum ab8500_chargalg_states charge_state;
+ struct ab8500_charge_curr_maximization ccm;
+ struct ab8500_chargalg_charger_info chg_info;
+ struct ab8500_chargalg_battery_data batt_data;
+ struct ab8500_chargalg_suspension_status susp_status;
+ struct ab8500 *parent;
+ struct ab8500_chargalg_platform_data *pdata;
+ struct ab8500_bm_data *bat;
+ struct power_supply chargalg_psy;
+ struct ux500_charger *ac_chg;
+ struct ux500_charger *usb_chg;
+ struct ab8500_chargalg_events events;
+ struct workqueue_struct *chargalg_wq;
+ struct delayed_work chargalg_periodic_work;
+ struct delayed_work chargalg_wd_work;
+ struct work_struct chargalg_work;
+ struct timer_list safety_timer;
+ struct timer_list maintenance_timer;
+ struct kobject chargalg_kobject;
+};
+
+/* Main battery properties */
+static enum power_supply_property ab8500_chargalg_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+};
+
+/**
+ * ab8500_chargalg_safety_timer_expired() - Expiration of the safety timer
+ * @data: pointer to the ab8500_chargalg structure
+ *
+ * This function gets called when the safety timer for the charger
+ * expires
+ */
+static void ab8500_chargalg_safety_timer_expired(unsigned long data)
+{
+ struct ab8500_chargalg *di = (struct ab8500_chargalg *) data;
+ dev_err(di->dev, "Safety timer expired\n");
+ di->events.safety_timer_expired = true;
+
+ /* Trigger execution of the algorithm instantly */
+ queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * ab8500_chargalg_maintenance_timer_expired() - Expiration of
+ * the maintenance timer
+ * @i: pointer to the ab8500_chargalg structure
+ *
+ * This function gets called when the maintenence timer
+ * expires
+ */
+static void ab8500_chargalg_maintenance_timer_expired(unsigned long data)
+{
+
+ struct ab8500_chargalg *di = (struct ab8500_chargalg *) data;
+ dev_dbg(di->dev, "Maintenance timer expired\n");
+ di->events.maintenance_timer_expired = true;
+
+ /* Trigger execution of the algorithm instantly */
+ queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * ab8500_chargalg_state_to() - Change charge state
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * This function gets called when a charge state change should occur
+ */
+static void ab8500_chargalg_state_to(struct ab8500_chargalg *di,
+ enum ab8500_chargalg_states state)
+{
+ dev_dbg(di->dev,
+ "State changed: %s (From state: [%d] %s =to=> [%d] %s )\n",
+ di->charge_state == state ? "NO" : "YES",
+ di->charge_state,
+ states[di->charge_state],
+ state,
+ states[state]);
+
+ di->charge_state = state;
+}
+
+/**
+ * ab8500_chargalg_check_charger_connection() - Check charger connection change
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * This function will check if there is a change in the charger connection
+ * and change charge state accordingly. AC has precedence over USB.
+ */
+static int ab8500_chargalg_check_charger_connection(struct ab8500_chargalg *di)
+{
+ if (di->chg_info.conn_chg != di->chg_info.prev_conn_chg ||
+ di->susp_status.suspended_change) {
+ /*
+ * Charger state changed or suspension
+ * has changed since last update
+ */
+ if ((di->chg_info.conn_chg & AC_CHG) &&
+ !di->susp_status.ac_suspended) {
+ dev_dbg(di->dev, "Charging source is AC\n");
+ if (di->chg_info.charger_type != AC_CHG) {
+ di->chg_info.charger_type = AC_CHG;
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ }
+ } else if ((di->chg_info.conn_chg & USB_CHG) &&
+ !di->susp_status.usb_suspended) {
+ dev_dbg(di->dev, "Charging source is USB\n");
+ di->chg_info.charger_type = USB_CHG;
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ } else if (di->chg_info.conn_chg &&
+ (di->susp_status.ac_suspended ||
+ di->susp_status.usb_suspended)) {
+ dev_dbg(di->dev, "Charging is suspended\n");
+ di->chg_info.charger_type = NO_CHG;
+ ab8500_chargalg_state_to(di, STATE_SUSPENDED_INIT);
+ } else {
+ dev_dbg(di->dev, "Charging source is OFF\n");
+ di->chg_info.charger_type = NO_CHG;
+ ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT);
+ }
+ di->chg_info.prev_conn_chg = di->chg_info.conn_chg;
+ di->susp_status.suspended_change = false;
+ }
+ return di->chg_info.conn_chg;
+}
+
+/**
+ * ab8500_chargalg_start_safety_timer() - Start charging safety timer
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * The safety timer is used to avoid overcharging of old or bad batteries.
+ * There are different timers for AC and USB
+ */
+static void ab8500_chargalg_start_safety_timer(struct ab8500_chargalg *di)
+{
+ unsigned long timer_expiration = 0;
+
+ switch (di->chg_info.charger_type) {
+ case AC_CHG:
+ timer_expiration =
+ round_jiffies(jiffies +
+ (di->bat->main_safety_tmr_h * 3600 * HZ));
+ break;
+
+ case USB_CHG:
+ timer_expiration =
+ round_jiffies(jiffies +
+ (di->bat->usb_safety_tmr_h * 3600 * HZ));
+ break;
+
+ default:
+ dev_err(di->dev, "Unknown charger to charge from\n");
+ break;
+ }
+
+ di->events.safety_timer_expired = false;
+ di->safety_timer.expires = timer_expiration;
+ if (!timer_pending(&di->safety_timer))
+ add_timer(&di->safety_timer);
+ else
+ mod_timer(&di->safety_timer, timer_expiration);
+}
+
+/**
+ * ab8500_chargalg_stop_safety_timer() - Stop charging safety timer
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * The safety timer is stopped whenever the NORMAL state is exited
+ */
+static void ab8500_chargalg_stop_safety_timer(struct ab8500_chargalg *di)
+{
+ di->events.safety_timer_expired = false;
+ del_timer(&di->safety_timer);
+}
+
+/**
+ * ab8500_chargalg_start_maintenance_timer() - Start charging maintenance timer
+ * @di: pointer to the ab8500_chargalg structure
+ * @duration: duration of ther maintenance timer in hours
+ *
+ * The maintenance timer is used to maintain the charge in the battery once
+ * the battery is considered full. These timers are chosen to match the
+ * discharge curve of the battery
+ */
+static void ab8500_chargalg_start_maintenance_timer(struct ab8500_chargalg *di,
+ int duration)
+{
+ unsigned long timer_expiration;
+
+ /* Convert from hours to jiffies */
+ timer_expiration = round_jiffies(jiffies + (duration * 3600 * HZ));
+
+ di->events.maintenance_timer_expired = false;
+ di->maintenance_timer.expires = timer_expiration;
+ if (!timer_pending(&di->maintenance_timer))
+ add_timer(&di->maintenance_timer);
+ else
+ mod_timer(&di->maintenance_timer, timer_expiration);
+}
+
+/**
+ * ab8500_chargalg_stop_maintenance_timer() - Stop maintenance timer
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * The maintenance timer is stopped whenever maintenance ends or when another
+ * state is entered
+ */
+static void ab8500_chargalg_stop_maintenance_timer(struct ab8500_chargalg *di)
+{
+ di->events.maintenance_timer_expired = false;
+ del_timer(&di->maintenance_timer);
+}
+
+/**
+ * ab8500_chargalg_kick_watchdog() - Kick charger watchdog
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * The charger watchdog have to be kicked periodically whenever the charger is
+ * on, else the ABB will reset the system
+ */
+static int ab8500_chargalg_kick_watchdog(struct ab8500_chargalg *di)
+{
+ /* Check if charger exists and kick watchdog if charging */
+ if (di->ac_chg && di->ac_chg->ops.kick_wd &&
+ di->chg_info.online_chg & AC_CHG)
+ return di->ac_chg->ops.kick_wd(di->ac_chg);
+ else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
+ di->chg_info.online_chg & USB_CHG)
+ return di->usb_chg->ops.kick_wd(di->usb_chg);
+
+ return -ENXIO;
+}
+
+/**
+ * ab8500_chargalg_ac_en() - Turn on/off the AC charger
+ * @di: pointer to the ab8500_chargalg structure
+ * @enable: charger on/off
+ * @vset: requested charger output voltage
+ * @iset: requested charger output current
+ *
+ * The AC charger will be turned on/off with the requested charge voltage and
+ * current
+ */
+static int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable,
+ int vset, int iset)
+{
+ if (!di->ac_chg || !di->ac_chg->ops.enable)
+ return -ENXIO;
+
+ /* Select maximum of what both the charger and the battery supports */
+ if (di->ac_chg->max_out_volt)
+ vset = min(vset, di->ac_chg->max_out_volt);
+ if (di->ac_chg->max_out_curr)
+ iset = min(iset, di->ac_chg->max_out_curr);
+
+ di->chg_info.ac_iset = iset;
+ di->chg_info.ac_vset = vset;
+
+ return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
+}
+
+/**
+ * ab8500_chargalg_usb_en() - Turn on/off the USB charger
+ * @di: pointer to the ab8500_chargalg structure
+ * @enable: charger on/off
+ * @vset: requested charger output voltage
+ * @iset: requested charger output current
+ *
+ * The USB charger will be turned on/off with the requested charge voltage and
+ * current
+ */
+static int ab8500_chargalg_usb_en(struct ab8500_chargalg *di, int enable,
+ int vset, int iset)
+{
+ if (!di->usb_chg || !di->usb_chg->ops.enable)
+ return -ENXIO;
+
+ /* Select maximum of what both the charger and the battery supports */
+ if (di->usb_chg->max_out_volt)
+ vset = min(vset, di->usb_chg->max_out_volt);
+ if (di->usb_chg->max_out_curr)
+ iset = min(iset, di->usb_chg->max_out_curr);
+
+ di->chg_info.usb_iset = iset;
+ di->chg_info.usb_vset = vset;
+
+ return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset);
+}
+
+/**
+ * ab8500_chargalg_update_chg_curr() - Update charger current
+ * @di: pointer to the ab8500_chargalg structure
+ * @iset: requested charger output current
+ *
+ * The charger output current will be updated for the charger
+ * that is currently in use
+ */
+static int ab8500_chargalg_update_chg_curr(struct ab8500_chargalg *di,
+ int iset)
+{
+ /* Check if charger exists and update current if charging */
+ if (di->ac_chg && di->ac_chg->ops.update_curr &&
+ di->chg_info.charger_type & AC_CHG) {
+ /*
+ * Select maximum of what both the charger
+ * and the battery supports
+ */
+ if (di->ac_chg->max_out_curr)
+ iset = min(iset, di->ac_chg->max_out_curr);
+
+ di->chg_info.ac_iset = iset;
+
+ return di->ac_chg->ops.update_curr(di->ac_chg, iset);
+ } else if (di->usb_chg && di->usb_chg->ops.update_curr &&
+ di->chg_info.charger_type & USB_CHG) {
+ /*
+ * Select maximum of what both the charger
+ * and the battery supports
+ */
+ if (di->usb_chg->max_out_curr)
+ iset = min(iset, di->usb_chg->max_out_curr);
+
+ di->chg_info.usb_iset = iset;
+
+ return di->usb_chg->ops.update_curr(di->usb_chg, iset);
+ }
+
+ return -ENXIO;
+}
+
+/**
+ * ab8500_chargalg_stop_charging() - Stop charging
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * This function is called from any state where charging should be stopped.
+ * All charging is disabled and all status parameters and timers are changed
+ * accordingly
+ */
+static void ab8500_chargalg_stop_charging(struct ab8500_chargalg *di)
+{
+ ab8500_chargalg_ac_en(di, false, 0, 0);
+ ab8500_chargalg_usb_en(di, false, 0, 0);
+ ab8500_chargalg_stop_safety_timer(di);
+ ab8500_chargalg_stop_maintenance_timer(di);
+ di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ di->maintenance_chg = false;
+ cancel_delayed_work(&di->chargalg_wd_work);
+ power_supply_changed(&di->chargalg_psy);
+}
+
+/**
+ * ab8500_chargalg_hold_charging() - Pauses charging
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * This function is called in the case where maintenance charging has been
+ * disabled and instead a battery voltage mode is entered to check when the
+ * battery voltage has reached a certain recharge voltage
+ */
+static void ab8500_chargalg_hold_charging(struct ab8500_chargalg *di)
+{
+ ab8500_chargalg_ac_en(di, false, 0, 0);
+ ab8500_chargalg_usb_en(di, false, 0, 0);
+ ab8500_chargalg_stop_safety_timer(di);
+ ab8500_chargalg_stop_maintenance_timer(di);
+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+ di->maintenance_chg = false;
+ cancel_delayed_work(&di->chargalg_wd_work);
+ power_supply_changed(&di->chargalg_psy);
+}
+
+/**
+ * ab8500_chargalg_start_charging() - Start the charger
+ * @di: pointer to the ab8500_chargalg structure
+ * @vset: requested charger output voltage
+ * @iset: requested charger output current
+ *
+ * A charger will be enabled depending on the requested charger type that was
+ * detected previously.
+ */
+static void ab8500_chargalg_start_charging(struct ab8500_chargalg *di,
+ int vset, int iset)
+{
+ switch (di->chg_info.charger_type) {
+ case AC_CHG:
+ dev_dbg(di->dev,
+ "AC parameters: Vset %d, Ich %d\n", vset, iset);
+ ab8500_chargalg_usb_en(di, false, 0, 0);
+ ab8500_chargalg_ac_en(di, true, vset, iset);
+ break;
+
+ case USB_CHG:
+ dev_dbg(di->dev,
+ "USB parameters: Vset %d, Ich %d\n", vset, iset);
+ ab8500_chargalg_ac_en(di, false, 0, 0);
+ ab8500_chargalg_usb_en(di, true, vset, iset);
+ break;
+
+ default:
+ dev_err(di->dev, "Unknown charger to charge from\n");
+ break;
+ }
+}
+
+/**
+ * ab8500_chargalg_check_temp() - Check battery temperature ranges
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * The battery temperature is checked against the predefined limits and the
+ * charge state is changed accordingly
+ */
+static void ab8500_chargalg_check_temp(struct ab8500_chargalg *di)
+{
+ if (di->batt_data.temp > (di->bat->temp_low + di->t_hyst_norm) &&
+ di->batt_data.temp < (di->bat->temp_high - di->t_hyst_norm)) {
+ /* Temp OK! */
+ di->events.btemp_underover = false;
+ di->events.btemp_lowhigh = false;
+ di->t_hyst_norm = 0;
+ di->t_hyst_lowhigh = 0;
+ } else {
+ if (((di->batt_data.temp >= di->bat->temp_high) &&
+ (di->batt_data.temp <
+ (di->bat->temp_over - di->t_hyst_lowhigh))) ||
+ ((di->batt_data.temp >
+ (di->bat->temp_under + di->t_hyst_lowhigh)) &&
+ (di->batt_data.temp <= di->bat->temp_low))) {
+ /* TEMP minor!!!!! */
+ di->events.btemp_underover = false;
+ di->events.btemp_lowhigh = true;
+ di->t_hyst_norm = di->bat->temp_hysteresis;
+ di->t_hyst_lowhigh = 0;
+ } else if (di->batt_data.temp <= di->bat->temp_under ||
+ di->batt_data.temp >= di->bat->temp_over) {
+ /* TEMP major!!!!! */
+ di->events.btemp_underover = true;
+ di->events.btemp_lowhigh = false;
+ di->t_hyst_norm = 0;
+ di->t_hyst_lowhigh = di->bat->temp_hysteresis;
+ } else {
+ /* Within hysteresis */
+ dev_dbg(di->dev, "Within hysteresis limit temp: %d "
+ "hyst_lowhigh %d, hyst normal %d\n",
+ di->batt_data.temp, di->t_hyst_lowhigh,
+ di->t_hyst_norm);
+ }
+ }
+}
+
+/**
+ * ab8500_chargalg_check_charger_voltage() - Check charger voltage
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * Charger voltage is checked against maximum limit
+ */
+static void ab8500_chargalg_check_charger_voltage(struct ab8500_chargalg *di)
+{
+ if (di->chg_info.usb_volt > di->bat->chg_params->usb_volt_max)
+ di->chg_info.usb_chg_ok = false;
+ else
+ di->chg_info.usb_chg_ok = true;
+
+ if (di->chg_info.ac_volt > di->bat->chg_params->ac_volt_max)
+ di->chg_info.ac_chg_ok = false;
+ else
+ di->chg_info.ac_chg_ok = true;
+
+}
+
+/**
+ * ab8500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * End-of-charge criteria is fulfilled when the battery voltage is above a
+ * certain limit and the battery current is below a certain limit for a
+ * predefined number of consecutive seconds. If true, the battery is full
+ */
+static void ab8500_chargalg_end_of_charge(struct ab8500_chargalg *di)
+{
+ if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
+ di->charge_state == STATE_NORMAL &&
+ !di->maintenance_chg && (di->batt_data.volt >=
+ di->bat->bat_type[di->bat->batt_id].termination_vol ||
+ di->events.usb_cv_active || di->events.ac_cv_active) &&
+ di->batt_data.avg_curr <
+ di->bat->bat_type[di->bat->batt_id].termination_curr &&
+ di->batt_data.avg_curr > 0) {
+ if (++di->eoc_cnt >= EOC_COND_CNT) {
+ di->eoc_cnt = 0;
+ di->charge_status = POWER_SUPPLY_STATUS_FULL;
+ di->maintenance_chg = true;
+ dev_dbg(di->dev, "EOC reached!\n");
+ power_supply_changed(&di->chargalg_psy);
+ } else {
+ dev_dbg(di->dev,
+ " EOC limit reached for the %d"
+ " time, out of %d before EOC\n",
+ di->eoc_cnt,
+ EOC_COND_CNT);
+ }
+ } else {
+ di->eoc_cnt = 0;
+ }
+}
+
+static void init_maxim_chg_curr(struct ab8500_chargalg *di)
+{
+ di->ccm.original_iset =
+ di->bat->bat_type[di->bat->batt_id].normal_cur_lvl;
+ di->ccm.current_iset =
+ di->bat->bat_type[di->bat->batt_id].normal_cur_lvl;
+ di->ccm.test_delta_i = di->bat->maxi->charger_curr_step;
+ di->ccm.max_current = di->bat->maxi->chg_curr;
+ di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+ di->ccm.level = 0;
+}
+
+/**
+ * ab8500_chargalg_chg_curr_maxim - increases the charger current to
+ * compensate for the system load
+ * @di pointer to the ab8500_chargalg structure
+ *
+ * This maximization function is used to raise the charger current to get the
+ * battery current as close to the optimal value as possible. The battery
+ * current during charging is affected by the system load
+ */
+static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di)
+{
+ int delta_i;
+
+ if (!di->bat->maxi->ena_maxi)
+ return MAXIM_RET_NOACTION;
+
+ delta_i = di->ccm.original_iset - di->batt_data.inst_curr;
+
+ if (di->events.vbus_collapsed) {
+ dev_dbg(di->dev, "Charger voltage has collapsed %d\n",
+ di->ccm.wait_cnt);
+ if (di->ccm.wait_cnt == 0) {
+ dev_dbg(di->dev, "lowering current\n");
+ di->ccm.wait_cnt++;
+ di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+ di->ccm.max_current =
+ di->ccm.current_iset - di->ccm.test_delta_i;
+ di->ccm.current_iset = di->ccm.max_current;
+ di->ccm.level--;
+ return MAXIM_RET_CHANGE;
+ } else {
+ dev_dbg(di->dev, "waiting\n");
+ /* Let's go in here twice before lowering curr again */
+ di->ccm.wait_cnt = (di->ccm.wait_cnt + 1) % 3;
+ return MAXIM_RET_NOACTION;
+ }
+ }
+
+ di->ccm.wait_cnt = 0;
+
+ if ((di->batt_data.inst_curr > di->ccm.original_iset)) {
+ dev_dbg(di->dev, " Maximization Ibat (%dmA) too high"
+ " (limit %dmA) (current iset: %dmA)!\n",
+ di->batt_data.inst_curr, di->ccm.original_iset,
+ di->ccm.current_iset);
+
+ if (di->ccm.current_iset == di->ccm.original_iset)
+ return MAXIM_RET_NOACTION;
+
+ di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+ di->ccm.current_iset = di->ccm.original_iset;
+ di->ccm.level = 0;
+
+ return MAXIM_RET_IBAT_TOO_HIGH;
+ }
+
+ if (delta_i > di->ccm.test_delta_i &&
+ (di->ccm.current_iset + di->ccm.test_delta_i) <
+ di->ccm.max_current) {
+ if (di->ccm.condition_cnt-- == 0) {
+ /* Increse the iset with cco.test_delta_i */
+ di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+ di->ccm.current_iset += di->ccm.test_delta_i;
+ di->ccm.level++;
+ dev_dbg(di->dev, " Maximization needed, increase"
+ " with %d mA to %dmA (Optimal ibat: %d)"
+ " Level %d\n",
+ di->ccm.test_delta_i,
+ di->ccm.current_iset,
+ di->ccm.original_iset,
+ di->ccm.level);
+ return MAXIM_RET_CHANGE;
+ } else {
+ return MAXIM_RET_NOACTION;
+ }
+ } else {
+ di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+ return MAXIM_RET_NOACTION;
+ }
+}
+
+static void handle_maxim_chg_curr(struct ab8500_chargalg *di)
+{
+ enum maxim_ret ret;
+ int result;
+
+ ret = ab8500_chargalg_chg_curr_maxim(di);
+ switch (ret) {
+ case MAXIM_RET_CHANGE:
+ result = ab8500_chargalg_update_chg_curr(di,
+ di->ccm.current_iset);
+ if (result)
+ dev_err(di->dev, "failed to set chg curr\n");
+ break;
+ case MAXIM_RET_IBAT_TOO_HIGH:
+ result = ab8500_chargalg_update_chg_curr(di,
+ di->bat->bat_type[di->bat->batt_id].normal_cur_lvl);
+ if (result)
+ dev_err(di->dev, "failed to set chg curr\n");
+ break;
+
+ case MAXIM_RET_NOACTION:
+ default:
+ /* Do nothing..*/
+ break;
+ }
+}
+
+static int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data)
+{
+ struct power_supply *psy;
+ struct power_supply *ext;
+ struct ab8500_chargalg *di;
+ union power_supply_propval ret;
+ int i, j;
+ bool psy_found = false;
+
+ psy = (struct power_supply *)data;
+ ext = dev_get_drvdata(dev);
+ di = to_ab8500_chargalg_device_info(psy);
+
+ /* For all psy where the driver name appears in any supplied_to */
+ for (i = 0; i < ext->num_supplicants; i++) {
+ if (!strcmp(ext->supplied_to[i], psy->name))
+ psy_found = true;
+ }
+
+ if (!psy_found)
+ return 0;
+
+ /* Go through all properties for the psy */
+ for (j = 0; j < ext->num_properties; j++) {
+ enum power_supply_property prop;
+ prop = ext->properties[j];
+
+ /* Initialize chargers if not already done */
+ if (!di->ac_chg &&
+ ext->type == POWER_SUPPLY_TYPE_MAINS)
+ di->ac_chg = psy_to_ux500_charger(ext);
+ else if (!di->usb_chg &&
+ ext->type == POWER_SUPPLY_TYPE_USB)
+ di->usb_chg = psy_to_ux500_charger(ext);
+
+ if (ext->get_property(ext, prop, &ret))
+ continue;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ /* Battery present */
+ if (ret.intval)
+ di->events.batt_rem = false;
+ /* Battery removed */
+ else
+ di->events.batt_rem = true;
+ break;
+ case POWER_SUPPLY_TYPE_MAINS:
+ /* AC disconnected */
+ if (!ret.intval &&
+ (di->chg_info.conn_chg & AC_CHG)) {
+ di->chg_info.prev_conn_chg =
+ di->chg_info.conn_chg;
+ di->chg_info.conn_chg &= ~AC_CHG;
+ }
+ /* AC connected */
+ else if (ret.intval &&
+ !(di->chg_info.conn_chg & AC_CHG)) {
+ di->chg_info.prev_conn_chg =
+ di->chg_info.conn_chg;
+ di->chg_info.conn_chg |= AC_CHG;
+ }
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ /* USB disconnected */
+ if (!ret.intval &&
+ (di->chg_info.conn_chg & USB_CHG)) {
+ di->chg_info.prev_conn_chg =
+ di->chg_info.conn_chg;
+ di->chg_info.conn_chg &= ~USB_CHG;
+ }
+ /* USB connected */
+ else if (ret.intval &&
+ !(di->chg_info.conn_chg & USB_CHG)) {
+ di->chg_info.prev_conn_chg =
+ di->chg_info.conn_chg;
+ di->chg_info.conn_chg |= USB_CHG;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_ONLINE:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ break;
+ case POWER_SUPPLY_TYPE_MAINS:
+ /* AC offline */
+ if (!ret.intval &&
+ (di->chg_info.online_chg & AC_CHG)) {
+ di->chg_info.prev_online_chg =
+ di->chg_info.online_chg;
+ di->chg_info.online_chg &= ~AC_CHG;
+ }
+ /* AC online */
+ else if (ret.intval &&
+ !(di->chg_info.online_chg & AC_CHG)) {
+ di->chg_info.prev_online_chg =
+ di->chg_info.online_chg;
+ di->chg_info.online_chg |= AC_CHG;
+ queue_delayed_work(di->chargalg_wq,
+ &di->chargalg_wd_work, 0);
+ }
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ /* USB offline */
+ if (!ret.intval &&
+ (di->chg_info.online_chg & USB_CHG)) {
+ di->chg_info.prev_online_chg =
+ di->chg_info.online_chg;
+ di->chg_info.online_chg &= ~USB_CHG;
+ }
+ /* USB online */
+ else if (ret.intval &&
+ !(di->chg_info.online_chg & USB_CHG)) {
+ di->chg_info.prev_online_chg =
+ di->chg_info.online_chg;
+ di->chg_info.online_chg |= USB_CHG;
+ queue_delayed_work(di->chargalg_wq,
+ &di->chargalg_wd_work, 0);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_HEALTH:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ break;
+ case POWER_SUPPLY_TYPE_MAINS:
+ switch (ret.intval) {
+ case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
+ di->events.mainextchnotok = true;
+ di->events.main_thermal_prot = false;
+ di->events.main_ovv = false;
+ di->events.ac_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_DEAD:
+ di->events.ac_wd_expired = true;
+ di->events.mainextchnotok = false;
+ di->events.main_ovv = false;
+ di->events.main_thermal_prot = false;
+ break;
+ case POWER_SUPPLY_HEALTH_COLD:
+ case POWER_SUPPLY_HEALTH_OVERHEAT:
+ di->events.main_thermal_prot = true;
+ di->events.mainextchnotok = false;
+ di->events.main_ovv = false;
+ di->events.ac_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+ di->events.main_ovv = true;
+ di->events.mainextchnotok = false;
+ di->events.main_thermal_prot = false;
+ di->events.ac_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_GOOD:
+ di->events.main_thermal_prot = false;
+ di->events.mainextchnotok = false;
+ di->events.main_ovv = false;
+ di->events.ac_wd_expired = false;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_TYPE_USB:
+ switch (ret.intval) {
+ case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
+ di->events.usbchargernotok = true;
+ di->events.usb_thermal_prot = false;
+ di->events.vbus_ovv = false;
+ di->events.usb_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_DEAD:
+ di->events.usb_wd_expired = true;
+ di->events.usbchargernotok = false;
+ di->events.usb_thermal_prot = false;
+ di->events.vbus_ovv = false;
+ break;
+ case POWER_SUPPLY_HEALTH_COLD:
+ case POWER_SUPPLY_HEALTH_OVERHEAT:
+ di->events.usb_thermal_prot = true;
+ di->events.usbchargernotok = false;
+ di->events.vbus_ovv = false;
+ di->events.usb_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+ di->events.vbus_ovv = true;
+ di->events.usbchargernotok = false;
+ di->events.usb_thermal_prot = false;
+ di->events.usb_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_GOOD:
+ di->events.usbchargernotok = false;
+ di->events.usb_thermal_prot = false;
+ di->events.vbus_ovv = false;
+ di->events.usb_wd_expired = false;
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ di->batt_data.volt = ret.intval / 1000;
+ break;
+ case POWER_SUPPLY_TYPE_MAINS:
+ di->chg_info.ac_volt = ret.intval / 1000;
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ di->chg_info.usb_volt = ret.intval / 1000;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_MAINS:
+ /* AVG is used to indicate when we are
+ * in CV mode */
+ if (ret.intval)
+ di->events.ac_cv_active = true;
+ else
+ di->events.ac_cv_active = false;
+
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ /* AVG is used to indicate when we are
+ * in CV mode */
+ if (ret.intval)
+ di->events.usb_cv_active = true;
+ else
+ di->events.usb_cv_active = false;
+
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ if (ret.intval)
+ di->events.batt_unknown = false;
+ else
+ di->events.batt_unknown = true;
+
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_TEMP:
+ di->batt_data.temp = ret.intval / 10;
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_MAINS:
+ di->chg_info.ac_curr =
+ ret.intval / 1000;
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ di->chg_info.usb_curr =
+ ret.intval / 1000;
+ break;
+ case POWER_SUPPLY_TYPE_BATTERY:
+ di->batt_data.inst_curr = ret.intval / 1000;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ di->batt_data.avg_curr = ret.intval / 1000;
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ if (ret.intval)
+ di->events.vbus_collapsed = true;
+ else
+ di->events.vbus_collapsed = false;
+ break;
+ default:
+ break;
+ }
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ di->batt_data.percent = ret.intval;
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ab8500_chargalg_external_power_changed() - callback for power supply changes
+ * @psy: pointer to the structure power_supply
+ *
+ * This function is the entry point of the pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in any external power
+ * supply that this driver needs to be notified of.
+ */
+static void ab8500_chargalg_external_power_changed(struct power_supply *psy)
+{
+ struct ab8500_chargalg *di = to_ab8500_chargalg_device_info(psy);
+
+ /*
+ * Trigger execution of the algorithm instantly and read
+ * all power_supply properties there instead
+ */
+ queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * ab8500_chargalg_algorithm() - Main function for the algorithm
+ * @di: pointer to the ab8500_chargalg structure
+ *
+ * This is the main control function for the charging algorithm.
+ * It is called periodically or when something happens that will
+ * trigger a state change
+ */
+static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
+{
+ int charger_status;
+
+ /* Collect data from all power_supply class devices */
+ class_for_each_device(power_supply_class, NULL,
+ &di->chargalg_psy, ab8500_chargalg_get_ext_psy_data);
+
+ ab8500_chargalg_end_of_charge(di);
+ ab8500_chargalg_check_temp(di);
+ ab8500_chargalg_check_charger_voltage(di);
+ charger_status = ab8500_chargalg_check_charger_connection(di);
+
+ /*
+ * First check if we have a charger connected.
+ * Also we don't allow charging of unknown batteries if configured
+ * this way
+ */
+ if (!charger_status ||
+ (di->events.batt_unknown && !di->bat->chg_unknown_bat)) {
+ if (di->charge_state != STATE_HANDHELD) {
+ di->events.safety_timer_expired = false;
+ ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT);
+ }
+ }
+
+ /* If suspended, we should not continue checking the flags */
+ else if (di->charge_state == STATE_SUSPENDED_INIT ||
+ di->charge_state == STATE_SUSPENDED) {
+ /* We don't do anything here, just don,t continue */
+ }
+
+ /* Safety timer expiration */
+ else if (di->events.safety_timer_expired) {
+ if (di->charge_state != STATE_SAFETY_TIMER_EXPIRED)
+ ab8500_chargalg_state_to(di,
+ STATE_SAFETY_TIMER_EXPIRED_INIT);
+ }
+ /*
+ * Check if any interrupts has occured
+ * that will prevent us from charging
+ */
+
+ /* Battery removed */
+ else if (di->events.batt_rem) {
+ if (di->charge_state != STATE_BATT_REMOVED)
+ ab8500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT);
+ }
+ /* Main or USB charger not ok. */
+ else if (di->events.mainextchnotok || di->events.usbchargernotok) {
+ /*
+ * If vbus_collapsed is set, we have to lower the charger
+ * current, which is done in the normal state below
+ */
+ if (di->charge_state != STATE_CHG_NOT_OK &&
+ !di->events.vbus_collapsed)
+ ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT);
+ }
+ /* VBUS, Main or VBAT OVV. */
+ else if (di->events.vbus_ovv ||
+ di->events.main_ovv ||
+ di->events.batt_ovv ||
+ !di->chg_info.usb_chg_ok ||
+ !di->chg_info.ac_chg_ok) {
+ if (di->charge_state != STATE_OVV_PROTECT)
+ ab8500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT);
+ }
+ /* USB Thermal, stop charging */
+ else if (di->events.main_thermal_prot ||
+ di->events.usb_thermal_prot) {
+ if (di->charge_state != STATE_HW_TEMP_PROTECT)
+ ab8500_chargalg_state_to(di,
+ STATE_HW_TEMP_PROTECT_INIT);
+ }
+ /* Battery temp over/under */
+ else if (di->events.btemp_underover) {
+ if (di->charge_state != STATE_TEMP_UNDEROVER)
+ ab8500_chargalg_state_to(di,
+ STATE_TEMP_UNDEROVER_INIT);
+ }
+ /* Watchdog expired */
+ else if (di->events.ac_wd_expired ||
+ di->events.usb_wd_expired) {
+ if (di->charge_state != STATE_WD_EXPIRED)
+ ab8500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT);
+ }
+ /* Battery temp high/low */
+ else if (di->events.btemp_lowhigh) {
+ if (di->charge_state != STATE_TEMP_LOWHIGH)
+ ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT);
+ }
+
+ dev_dbg(di->dev,
+ "[CHARGALG] Vb %d Ib_avg %d Ib_inst %d Tb %d Cap %d Maint %d "
+ "State %s Active_chg %d Chg_status %d AC %d USB %d "
+ "AC_online %d USB_online %d AC_CV %d USB_CV %d AC_I %d "
+ "USB_I %d AC_Vset %d AC_Iset %d USB_Vset %d USB_Iset %d\n",
+ di->batt_data.volt,
+ di->batt_data.avg_curr,
+ di->batt_data.inst_curr,
+ di->batt_data.temp,
+ di->batt_data.percent,
+ di->maintenance_chg,
+ states[di->charge_state],
+ di->chg_info.charger_type,
+ di->charge_status,
+ di->chg_info.conn_chg & AC_CHG,
+ di->chg_info.conn_chg & USB_CHG,
+ di->chg_info.online_chg & AC_CHG,
+ di->chg_info.online_chg & USB_CHG,
+ di->events.ac_cv_active,
+ di->events.usb_cv_active,
+ di->chg_info.ac_curr,
+ di->chg_info.usb_curr,
+ di->chg_info.ac_vset,
+ di->chg_info.ac_iset,
+ di->chg_info.usb_vset,
+ di->chg_info.usb_iset);
+
+ switch (di->charge_state) {
+ case STATE_HANDHELD_INIT:
+ ab8500_chargalg_stop_charging(di);
+ di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ ab8500_chargalg_state_to(di, STATE_HANDHELD);
+ /* Intentional fallthrough */
+
+ case STATE_HANDHELD:
+ break;
+
+ case STATE_SUSPENDED_INIT:
+ if (di->susp_status.ac_suspended)
+ ab8500_chargalg_ac_en(di, false, 0, 0);
+ if (di->susp_status.usb_suspended)
+ ab8500_chargalg_usb_en(di, false, 0, 0);
+ ab8500_chargalg_stop_safety_timer(di);
+ ab8500_chargalg_stop_maintenance_timer(di);
+ di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ di->maintenance_chg = false;
+ ab8500_chargalg_state_to(di, STATE_SUSPENDED);
+ power_supply_changed(&di->chargalg_psy);
+ /* Intentional fallthrough */
+
+ case STATE_SUSPENDED:
+ /* CHARGING is suspended */
+ break;
+
+ case STATE_BATT_REMOVED_INIT:
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_BATT_REMOVED);
+ /* Intentional fallthrough */
+
+ case STATE_BATT_REMOVED:
+ if (!di->events.batt_rem)
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_HW_TEMP_PROTECT_INIT:
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT);
+ /* Intentional fallthrough */
+
+ case STATE_HW_TEMP_PROTECT:
+ if (!di->events.main_thermal_prot &&
+ !di->events.usb_thermal_prot)
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_OVV_PROTECT_INIT:
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_OVV_PROTECT);
+ /* Intentional fallthrough */
+
+ case STATE_OVV_PROTECT:
+ if (!di->events.vbus_ovv &&
+ !di->events.main_ovv &&
+ !di->events.batt_ovv &&
+ di->chg_info.usb_chg_ok &&
+ di->chg_info.ac_chg_ok)
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_CHG_NOT_OK_INIT:
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK);
+ /* Intentional fallthrough */
+
+ case STATE_CHG_NOT_OK:
+ if (!di->events.mainextchnotok &&
+ !di->events.usbchargernotok)
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_SAFETY_TIMER_EXPIRED_INIT:
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED);
+ /* Intentional fallthrough */
+
+ case STATE_SAFETY_TIMER_EXPIRED:
+ /* We exit this state when charger is removed */
+ break;
+
+ case STATE_NORMAL_INIT:
+ ab8500_chargalg_start_charging(di,
+ di->bat->bat_type[di->bat->batt_id].normal_vol_lvl,
+ di->bat->bat_type[di->bat->batt_id].normal_cur_lvl);
+ ab8500_chargalg_state_to(di, STATE_NORMAL);
+ ab8500_chargalg_start_safety_timer(di);
+ ab8500_chargalg_stop_maintenance_timer(di);
+ init_maxim_chg_curr(di);
+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+ di->eoc_cnt = 0;
+ di->maintenance_chg = false;
+ power_supply_changed(&di->chargalg_psy);
+
+ break;
+
+ case STATE_NORMAL:
+ handle_maxim_chg_curr(di);
+ if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
+ di->maintenance_chg) {
+ if (di->bat->no_maintenance)
+ ab8500_chargalg_state_to(di,
+ STATE_WAIT_FOR_RECHARGE_INIT);
+ else
+ ab8500_chargalg_state_to(di,
+ STATE_MAINTENANCE_A_INIT);
+ }
+ break;
+
+ /* This state will be used when the maintenance state is disabled */
+ case STATE_WAIT_FOR_RECHARGE_INIT:
+ ab8500_chargalg_hold_charging(di);
+ ab8500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
+ di->rch_cnt = RCH_COND_CNT;
+ /* Intentional fallthrough */
+
+ case STATE_WAIT_FOR_RECHARGE:
+ if (di->batt_data.volt <=
+ di->bat->bat_type[di->bat->batt_id].recharge_vol) {
+ if (di->rch_cnt-- == 0)
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ } else
+ di->rch_cnt = RCH_COND_CNT;
+ break;
+
+ case STATE_MAINTENANCE_A_INIT:
+ ab8500_chargalg_stop_safety_timer(di);
+ ab8500_chargalg_start_maintenance_timer(di,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_a_chg_timer_h);
+ ab8500_chargalg_start_charging(di,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_a_vol_lvl,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_a_cur_lvl);
+ ab8500_chargalg_state_to(di, STATE_MAINTENANCE_A);
+ power_supply_changed(&di->chargalg_psy);
+ /* Intentional fallthrough*/
+
+ case STATE_MAINTENANCE_A:
+ if (di->events.maintenance_timer_expired) {
+ ab8500_chargalg_stop_maintenance_timer(di);
+ ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT);
+ }
+ break;
+
+ case STATE_MAINTENANCE_B_INIT:
+ ab8500_chargalg_start_maintenance_timer(di,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_b_chg_timer_h);
+ ab8500_chargalg_start_charging(di,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_b_vol_lvl,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_b_cur_lvl);
+ ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B);
+ power_supply_changed(&di->chargalg_psy);
+ /* Intentional fallthrough*/
+
+ case STATE_MAINTENANCE_B:
+ if (di->events.maintenance_timer_expired) {
+ ab8500_chargalg_stop_maintenance_timer(di);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ }
+ break;
+
+ case STATE_TEMP_LOWHIGH_INIT:
+ ab8500_chargalg_start_charging(di,
+ di->bat->bat_type[
+ di->bat->batt_id].low_high_vol_lvl,
+ di->bat->bat_type[
+ di->bat->batt_id].low_high_cur_lvl);
+ ab8500_chargalg_stop_maintenance_timer(di);
+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+ ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
+ power_supply_changed(&di->chargalg_psy);
+ /* Intentional fallthrough */
+
+ case STATE_TEMP_LOWHIGH:
+ if (!di->events.btemp_lowhigh)
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_WD_EXPIRED_INIT:
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_WD_EXPIRED);
+ /* Intentional fallthrough */
+
+ case STATE_WD_EXPIRED:
+ if (!di->events.ac_wd_expired &&
+ !di->events.usb_wd_expired)
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_TEMP_UNDEROVER_INIT:
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_TEMP_UNDEROVER);
+ /* Intentional fallthrough */
+
+ case STATE_TEMP_UNDEROVER:
+ if (!di->events.btemp_underover)
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+ }
+
+ /* Start charging directly if the new state is a charge state */
+ if (di->charge_state == STATE_NORMAL_INIT ||
+ di->charge_state == STATE_MAINTENANCE_A_INIT ||
+ di->charge_state == STATE_MAINTENANCE_B_INIT)
+ queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * ab8500_chargalg_periodic_work() - Periodic work for the algorithm
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for the charging algorithm
+ */
+static void ab8500_chargalg_periodic_work(struct work_struct *work)
+{
+ struct ab8500_chargalg *di = container_of(work,
+ struct ab8500_chargalg, chargalg_periodic_work.work);
+
+ ab8500_chargalg_algorithm(di);
+
+ /*
+ * If a charger is connected then the battery has to be monitored
+ * frequently, else the work can be delayed.
+ */
+ if (di->chg_info.conn_chg)
+ queue_delayed_work(di->chargalg_wq,
+ &di->chargalg_periodic_work,
+ di->bat->interval_charging * HZ);
+ else
+ queue_delayed_work(di->chargalg_wq,
+ &di->chargalg_periodic_work,
+ di->bat->interval_not_charging * HZ);
+}
+
+/**
+ * ab8500_chargalg_wd_work() - periodic work to kick the charger watchdog
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for kicking the charger watchdog
+ */
+static void ab8500_chargalg_wd_work(struct work_struct *work)
+{
+ int ret;
+ struct ab8500_chargalg *di = container_of(work,
+ struct ab8500_chargalg, chargalg_wd_work.work);
+
+ dev_dbg(di->dev, "ab8500_chargalg_wd_work\n");
+
+ ret = ab8500_chargalg_kick_watchdog(di);
+ if (ret < 0)
+ dev_err(di->dev, "failed to kick watchdog\n");
+
+ queue_delayed_work(di->chargalg_wq,
+ &di->chargalg_wd_work, CHG_WD_INTERVAL);
+}
+
+/**
+ * ab8500_chargalg_work() - Work to run the charging algorithm instantly
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for calling the charging algorithm
+ */
+static void ab8500_chargalg_work(struct work_struct *work)
+{
+ struct ab8500_chargalg *di = container_of(work,
+ struct ab8500_chargalg, chargalg_work);
+
+ ab8500_chargalg_algorithm(di);
+}
+
+/**
+ * ab8500_chargalg_get_property() - get the chargalg properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the
+ * chargalg properties by reading the sysfs files.
+ * status: charging/discharging/full/unknown
+ * health: health of the battery
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_chargalg_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab8500_chargalg *di;
+
+ di = to_ab8500_chargalg_device_info(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = di->charge_status;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (di->events.batt_ovv) {
+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ } else if (di->events.btemp_underover) {
+ if (di->batt_data.temp <= di->bat->temp_under)
+ val->intval = POWER_SUPPLY_HEALTH_COLD;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ } else {
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Exposure to the sysfs interface */
+
+/**
+ * ab8500_chargalg_sysfs_charger() - sysfs store operations
+ * @kobj: pointer to the struct kobject
+ * @attr: pointer to the struct attribute
+ * @buf: buffer that holds the parameter passed from userspace
+ * @length: length of the parameter passed
+ *
+ * Returns length of the buffer(input taken from user space) on success
+ * else error code on failure
+ * The operation to be performed on passing the parameters from the user space.
+ */
+static ssize_t ab8500_chargalg_sysfs_charger(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t length)
+{
+ struct ab8500_chargalg *di = container_of(kobj,
+ struct ab8500_chargalg, chargalg_kobject);
+ long int param;
+ int ac_usb;
+ int ret;
+ char entry = *attr->name;
+
+ switch (entry) {
+ case 'c':
+ ret = strict_strtol(buf, 10, &param);
+ if (ret < 0)
+ return ret;
+
+ ac_usb = param;
+ switch (ac_usb) {
+ case 0:
+ /* Disable charging */
+ di->susp_status.ac_suspended = true;
+ di->susp_status.usb_suspended = true;
+ di->susp_status.suspended_change = true;
+ /* Trigger a state change */
+ queue_work(di->chargalg_wq,
+ &di->chargalg_work);
+ break;
+ case 1:
+ /* Enable AC Charging */
+ di->susp_status.ac_suspended = false;
+ di->susp_status.suspended_change = true;
+ /* Trigger a state change */
+ queue_work(di->chargalg_wq,
+ &di->chargalg_work);
+ break;
+ case 2:
+ /* Enable USB charging */
+ di->susp_status.usb_suspended = false;
+ di->susp_status.suspended_change = true;
+ /* Trigger a state change */
+ queue_work(di->chargalg_wq,
+ &di->chargalg_work);
+ break;
+ default:
+ dev_info(di->dev, "Wrong input\n"
+ "Enter 0. Disable AC/USB Charging\n"
+ "1. Enable AC charging\n"
+ "2. Enable USB Charging\n");
+ };
+ break;
+ };
+ return strlen(buf);
+}
+
+static struct attribute ab8500_chargalg_en_charger = \
+{
+ .name = "chargalg",
+ .mode = S_IWUGO,
+};
+
+static struct attribute *ab8500_chargalg_chg[] = {
+ &ab8500_chargalg_en_charger,
+ NULL
+};
+
+const struct sysfs_ops ab8500_chargalg_sysfs_ops = {
+ .store = ab8500_chargalg_sysfs_charger,
+};
+
+static struct kobj_type ab8500_chargalg_ktype = {
+ .sysfs_ops = &ab8500_chargalg_sysfs_ops,
+ .default_attrs = ab8500_chargalg_chg,
+};
+
+/**
+ * ab8500_chargalg_sysfs_exit() - de-init of sysfs entry
+ * @di: pointer to the struct ab8500_chargalg
+ *
+ * This function removes the entry in sysfs.
+ */
+static void ab8500_chargalg_sysfs_exit(struct ab8500_chargalg *di)
+{
+ kobject_del(&di->chargalg_kobject);
+}
+
+/**
+ * ab8500_chargalg_sysfs_init() - init of sysfs entry
+ * @di: pointer to the struct ab8500_chargalg
+ *
+ * This function adds an entry in sysfs.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_chargalg_sysfs_init(struct ab8500_chargalg *di)
+{
+ int ret = 0;
+
+ ret = kobject_init_and_add(&di->chargalg_kobject,
+ &ab8500_chargalg_ktype,
+ NULL, "ab8500_chargalg");
+ if (ret < 0)
+ dev_err(di->dev, "failed to create sysfs entry\n");
+
+ return ret;
+}
+/* Exposure to the sysfs interface <<END>> */
+
+#if defined(CONFIG_PM)
+static int ab8500_chargalg_resume(struct platform_device *pdev)
+{
+ struct ab8500_chargalg *di = platform_get_drvdata(pdev);
+
+ /* Kick charger watchdog if charging (any charger online) */
+ if (di->chg_info.online_chg)
+ queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0);
+
+ /*
+ * Run the charging algorithm directly to be sure we don't
+ * do it too seldom
+ */
+ queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
+
+ return 0;
+}
+
+static int ab8500_chargalg_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab8500_chargalg *di = platform_get_drvdata(pdev);
+
+ if (di->chg_info.online_chg)
+ cancel_delayed_work_sync(&di->chargalg_wd_work);
+
+ cancel_delayed_work_sync(&di->chargalg_periodic_work);
+
+ return 0;
+}
+#else
+#define ab8500_chargalg_suspend NULL
+#define ab8500_chargalg_resume NULL
+#endif
+
+static int __devexit ab8500_chargalg_remove(struct platform_device *pdev)
+{
+ struct ab8500_chargalg *di = platform_get_drvdata(pdev);
+
+ /* sysfs interface to enable/disbale charging from user space */
+ ab8500_chargalg_sysfs_exit(di);
+
+ /* Delete the work queue */
+ destroy_workqueue(di->chargalg_wq);
+
+ flush_scheduled_work();
+ power_supply_unregister(&di->chargalg_psy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di);
+
+ return 0;
+}
+
+static int __devinit ab8500_chargalg_probe(struct platform_device *pdev)
+{
+ struct ab8500_platform_data *plat;
+ int ret = 0;
+
+ struct ab8500_chargalg *di =
+ kzalloc(sizeof(struct ab8500_chargalg), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ /* get parent data */
+ di->dev = &pdev->dev;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
+
+ plat = dev_get_platdata(di->parent->dev);
+
+ /* get chargalg specific platform data */
+ if (!plat->chargalg) {
+ dev_err(di->dev, "no chargalg platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+ di->pdata = plat->chargalg;
+
+ /* get battery specific platform data */
+ if (!plat->battery) {
+ dev_err(di->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+ di->bat = plat->battery;
+
+ /* chargalg supply */
+ di->chargalg_psy.name = "ab8500_chargalg";
+ di->chargalg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->chargalg_psy.properties = ab8500_chargalg_props;
+ di->chargalg_psy.num_properties = ARRAY_SIZE(ab8500_chargalg_props);
+ di->chargalg_psy.get_property = ab8500_chargalg_get_property;
+ di->chargalg_psy.supplied_to = di->pdata->supplied_to;
+ di->chargalg_psy.num_supplicants = di->pdata->num_supplicants;
+ di->chargalg_psy.external_power_changed =
+ ab8500_chargalg_external_power_changed;
+
+ /* Initilialize safety timer */
+ init_timer(&di->safety_timer);
+ di->safety_timer.function = ab8500_chargalg_safety_timer_expired;
+ di->safety_timer.data = (unsigned long) di;
+
+ /* Initilialize maintenance timer */
+ init_timer(&di->maintenance_timer);
+ di->maintenance_timer.function =
+ ab8500_chargalg_maintenance_timer_expired;
+ di->maintenance_timer.data = (unsigned long) di;
+
+ /* Create a work queue for the chargalg */
+ di->chargalg_wq =
+ create_singlethread_workqueue("ab8500_chargalg_wq");
+ if (di->chargalg_wq == NULL) {
+ dev_err(di->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for chargalg */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->chargalg_periodic_work,
+ ab8500_chargalg_periodic_work);
+ INIT_DELAYED_WORK_DEFERRABLE(&di->chargalg_wd_work,
+ ab8500_chargalg_wd_work);
+
+ /* Init work for chargalg */
+ INIT_WORK(&di->chargalg_work, ab8500_chargalg_work);
+
+ /* To detect charger at startup */
+ di->chg_info.prev_conn_chg = -1;
+
+ /* Register chargalg power supply class */
+ ret = power_supply_register(di->dev, &di->chargalg_psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register chargalg psy\n");
+ goto free_chargalg_wq;
+ }
+
+ platform_set_drvdata(pdev, di);
+
+ /* sysfs interface to enable/disable charging from user space */
+ ret = ab8500_chargalg_sysfs_init(di);
+ if (ret) {
+ dev_err(di->dev, "failed to create sysfs entry\n");
+ goto free_psy;
+ }
+
+ /* Run the charging algorithm */
+ queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
+ return ret;
+
+free_psy:
+ power_supply_unregister(&di->chargalg_psy);
+free_chargalg_wq:
+ destroy_workqueue(di->chargalg_wq);
+free_device_info:
+ kfree(di);
+
+ return ret;
+}
+
+static struct platform_driver ab8500_chargalg_driver = {
+ .probe = ab8500_chargalg_probe,
+ .remove = __devexit_p(ab8500_chargalg_remove),
+ .suspend = ab8500_chargalg_suspend,
+ .resume = ab8500_chargalg_resume,
+ .driver = {
+ .name = "ab8500-chargalg",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab8500_chargalg_init(void)
+{
+ return platform_driver_register(&ab8500_chargalg_driver);
+}
+
+static void __exit ab8500_chargalg_exit(void)
+{
+ platform_driver_unregister(&ab8500_chargalg_driver);
+}
+
+module_init(ab8500_chargalg_init);
+module_exit(ab8500_chargalg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
+MODULE_ALIAS("platform:ab8500-chargalg");
+MODULE_DESCRIPTION("AB8500 battery temperature driver");
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
new file mode 100644
index 00000000000..2a755852288
--- /dev/null
+++ b/drivers/power/ab8500_charger.c
@@ -0,0 +1,2541 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Charger driver for AB8500
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Johan Palsson <johan.palsson@stericsson.com>
+ * Author: Karl Komierowski <karl.komierowski@stericsson.com>
+ * Author: Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500/ab8500-bm.h>
+#include <linux/mfd/ab8500/ab8500-gpadc.h>
+#include <linux/mfd/ab8500/ux500_chargalg.h>
+
+/* Charger constants */
+#define NO_PW_CONN 0
+#define AC_PW_CONN 1
+#define USB_PW_CONN 2
+
+#define MAIN_WDOG_ENA 0x01
+#define MAIN_WDOG_KICK 0x02
+#define MAIN_WDOG_DIS 0x00
+#define CHARG_WD_KICK 0x01
+#define MAIN_CH_ENA 0x01
+#define MAIN_CH_NO_OVERSHOOT_ENA_N 0x02
+#define USB_CH_ENA 0x01
+#define USB_CHG_NO_OVERSHOOT_ENA_N 0x02
+#define MAIN_CH_DET 0x01
+#define MAIN_CH_CV_ON 0x04
+#define USB_CH_CV_ON 0x08
+#define VBUS_DET_DBNC100 0x02
+#define VBUS_DET_DBNC1 0x01
+#define OTP_ENABLE_WD 0x01
+
+#define MAIN_CH_INPUT_CURR_SHIFT 4
+#define VBUS_IN_CURR_LIM_SHIFT 4
+
+#define LED_INDICATOR_PWM_ENA 0x01
+#define LED_INDICATOR_PWM_DIS 0x00
+#define LED_IND_CUR_5MA 0x04
+#define LED_INDICATOR_PWM_DUTY_252_256 0xBF
+
+/* HW failure constants */
+#define MAIN_CH_TH_PROT 0x02
+#define VBUS_CH_NOK 0x08
+#define USB_CH_TH_PROT 0x02
+#define VBUS_OVV_TH 0x01
+#define MAIN_CH_NOK 0x01
+#define VBUS_DET 0x80
+
+/* UsbLineStatus register bit masks */
+#define AB8500_USB_LINK_STATUS 0x78
+#define AB8500_STD_HOST_SUSP 0x18
+
+/* Watchdog timeout constant */
+#define WD_TIMER 0x30 /* 4min */
+#define WD_KICK_INTERVAL (60 * HZ)
+
+/* Lowest charger voltage is 3.39V -> 0x4E */
+#define LOW_VOLT_REG 0x4E
+
+/* UsbLineStatus register - usb types */
+enum ab8500_charger_link_status {
+ USB_STAT_NOT_CONFIGURED,
+ USB_STAT_STD_HOST_NC,
+ USB_STAT_STD_HOST_C_NS,
+ USB_STAT_STD_HOST_C_S,
+ USB_STAT_HOST_CHG_NM,
+ USB_STAT_HOST_CHG_HS,
+ USB_STAT_HOST_CHG_HS_CHIRP,
+ USB_STAT_DEDICATED_CHG,
+ USB_STAT_ACA_RID_A,
+ USB_STAT_ACA_RID_B,
+ USB_STAT_ACA_RID_C_NM,
+ USB_STAT_ACA_RID_C_HS,
+ USB_STAT_ACA_RID_C_HS_CHIRP,
+ USB_STAT_HM_IDGND,
+ USB_STAT_RESERVED,
+ USB_STAT_NOT_VALID_LINK,
+};
+
+enum ab8500_usb_state {
+ AB8500_BM_USB_STATE_RESET_HS, /* HighSpeed Reset */
+ AB8500_BM_USB_STATE_RESET_FS, /* FullSpeed/LowSpeed Reset */
+ AB8500_BM_USB_STATE_CONFIGURED,
+ AB8500_BM_USB_STATE_SUSPEND,
+ AB8500_BM_USB_STATE_RESUME,
+ AB8500_BM_USB_STATE_MAX,
+};
+
+/* VBUS input current limits supported in AB8500 in mA */
+#define USB_CH_IP_CUR_LVL_0P05 50
+#define USB_CH_IP_CUR_LVL_0P09 98
+#define USB_CH_IP_CUR_LVL_0P19 193
+#define USB_CH_IP_CUR_LVL_0P29 290
+#define USB_CH_IP_CUR_LVL_0P38 380
+#define USB_CH_IP_CUR_LVL_0P45 450
+#define USB_CH_IP_CUR_LVL_0P5 500
+#define USB_CH_IP_CUR_LVL_0P6 600
+#define USB_CH_IP_CUR_LVL_0P7 700
+#define USB_CH_IP_CUR_LVL_0P8 800
+#define USB_CH_IP_CUR_LVL_0P9 900
+#define USB_CH_IP_CUR_LVL_1P0 1000
+#define USB_CH_IP_CUR_LVL_1P1 1100
+#define USB_CH_IP_CUR_LVL_1P3 1300
+#define USB_CH_IP_CUR_LVL_1P4 1400
+#define USB_CH_IP_CUR_LVL_1P5 1500
+
+#define to_ab8500_charger_usb_device_info(x) container_of((x), \
+ struct ab8500_charger, usb_chg)
+#define to_ab8500_charger_ac_device_info(x) container_of((x), \
+ struct ab8500_charger, ac_chg)
+
+/**
+ * struct ab8500_charger_interrupts - ab8500 interupts
+ * @name: name of the interrupt
+ * @isr function pointer to the isr
+ */
+struct ab8500_charger_interrupts {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct ab8500_charger_info {
+ int charger_connected;
+ int charger_online;
+ int charger_voltage;
+ int cv_active;
+ bool wd_expired;
+};
+
+struct ab8500_charger_event_flags {
+ bool mainextchnotok;
+ bool main_thermal_prot;
+ bool usb_thermal_prot;
+ bool vbus_ovv;
+ bool usbchargernotok;
+ bool chgwdexp;
+ bool vbus_collapse;
+};
+
+struct ab8500_charger_usb_state {
+ bool usb_changed;
+ int usb_current;
+ enum ab8500_usb_state state;
+ spinlock_t usb_lock;
+};
+
+/**
+ * struct ab8500_charger - ab8500 Charger device information
+ * @dev: Pointer to the structure device
+ * @chip_id: Chip-Id of the AB8500
+ * @max_usb_in_curr: Max USB charger input current
+ * @vbus_detected: VBUS detected
+ * @vbus_detected_start:
+ * VBUS detected during startup
+ * @ac_conn: This will be true when the AC charger has been plugged
+ * @vddadc_en: Indicate if VDD ADC supply is enabled from this driver
+ * @parent: Pointer to the struct ab8500
+ * @gpadc: Pointer to the struct gpadc
+ * @pdata: Pointer to the ab8500_charger platform data
+ * @bat: Pointer to the ab8500_bm platform data
+ * @flags: Structure for information about events triggered
+ * @usb_state: Structure for usb stack information
+ * @ac_chg: AC charger power supply
+ * @usb_chg: USB charger power supply
+ * @ac: Structure that holds the AC charger properties
+ * @usb: Structure that holds the USB charger properties
+ * @regu: Pointer to the struct regulator
+ * @charger_wq: Work queue for the IRQs and checking HW state
+ * @check_hw_failure_work: Work for checking HW state
+ * @check_usbchgnotok_work: Work for checking USB charger not ok status
+ * @kick_wd_work: Work for kicking the charger watchdog in case
+ * of ABB rev 1.* due to the watchog logic bug
+ * @ac_work: Work for checking AC charger connection
+ * @detect_usb_type_work: Work for detecting the USB type connected
+ * @usb_link_status_work: Work for checking the new USB link status
+ * @usb_state_changed_work: Work for checking USB state
+ * @check_main_thermal_prot_work:
+ * Work for checking Main thermal status
+ * @check_usb_thermal_prot_work:
+ * Work for checking USB thermal status
+ */
+struct ab8500_charger {
+ struct device *dev;
+ u8 chip_id;
+ int max_usb_in_curr;
+ bool vbus_detected;
+ bool vbus_detected_start;
+ bool ac_conn;
+ bool vddadc_en;
+ struct ab8500 *parent;
+ struct ab8500_gpadc *gpadc;
+ struct ab8500_charger_platform_data *pdata;
+ struct ab8500_bm_data *bat;
+ struct ab8500_charger_event_flags flags;
+ struct ab8500_charger_usb_state usb_state;
+ struct ux500_charger ac_chg;
+ struct ux500_charger usb_chg;
+ struct ab8500_charger_info ac;
+ struct ab8500_charger_info usb;
+ struct regulator *regu;
+ struct workqueue_struct *charger_wq;
+ struct delayed_work check_hw_failure_work;
+ struct delayed_work check_usbchgnotok_work;
+ struct delayed_work kick_wd_work;
+ struct work_struct ac_work;
+ struct work_struct detect_usb_type_work;
+ struct work_struct usb_link_status_work;
+ struct work_struct usb_state_changed_work;
+ struct work_struct check_main_thermal_prot_work;
+ struct work_struct check_usb_thermal_prot_work;
+};
+
+/*
+ * TODO: This variable is static in order to get information
+ * about maximum current and USB state from the USB driver
+ * This should be solved in a better way
+ */
+static struct ab8500_charger *static_di;
+
+/* AC properties */
+static enum power_supply_property ab8500_charger_ac_props[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+/* USB properties */
+static enum power_supply_property ab8500_charger_usb_props[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+/**
+ * ab8500_charger_get_ac_voltage() - get ac charger voltage
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Returns ac charger voltage (on success)
+ */
+static int ab8500_charger_get_ac_voltage(struct ab8500_charger *di)
+{
+ int vch;
+
+ /* Only measure voltage if the charger is connected */
+ if (di->ac.charger_connected) {
+ vch = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_V);
+ if (vch < 0)
+ dev_err(di->dev, "%s gpadc conv failed,\n", __func__);
+ } else {
+ vch = 0;
+ }
+ return vch;
+}
+
+/**
+ * ab8500_charger_ac_cv() - check if the main charger is in CV mode
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Returns ac charger CV mode (on success) else error code
+ */
+static int ab8500_charger_ac_cv(struct ab8500_charger *di)
+{
+ u8 val;
+ int ret = 0;
+
+ /* Only check CV mode if the charger is online */
+ if (di->ac.charger_online) {
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_STATUS1_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return 0;
+ }
+
+ if (val & MAIN_CH_CV_ON)
+ ret = 1;
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_get_vbus_voltage() - get vbus voltage
+ * @di: pointer to the ab8500_charger structure
+ *
+ * This function returns the vbus voltage.
+ * Returns vbus voltage (on success)
+ */
+static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di)
+{
+ int vch;
+
+ /* Only measure voltage if the charger is connected */
+ if (di->usb.charger_connected) {
+ vch = ab8500_gpadc_convert(di->gpadc, VBUS_V);
+ if (vch < 0)
+ dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+ } else {
+ vch = 0;
+ }
+ return vch;
+}
+
+/**
+ * ab8500_charger_get_usb_current() - get usb charger current
+ * @di: pointer to the ab8500_charger structure
+ *
+ * This function returns the usb charger current.
+ * Returns usb current (on success) and error code on failure
+ */
+static int ab8500_charger_get_usb_current(struct ab8500_charger *di)
+{
+ int ich;
+
+ /* Only measure current if the charger is online */
+ if (di->usb.charger_online) {
+ ich = ab8500_gpadc_convert(di->gpadc, USB_CHARGER_C);
+ if (ich < 0)
+ dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+ } else {
+ ich = 0;
+ }
+ return ich;
+}
+
+/**
+ * ab8500_charger_get_ac_current() - get ac charger current
+ * @di: pointer to the ab8500_charger structure
+ *
+ * This function returns the ac charger current.
+ * Returns ac current (on success) and error code on failure.
+ */
+static int ab8500_charger_get_ac_current(struct ab8500_charger *di)
+{
+ int ich;
+
+ /* Only measure current if the charger is online */
+ if (di->ac.charger_online) {
+ ich = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_C);
+ if (ich < 0)
+ dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+ } else {
+ ich = 0;
+ }
+ return ich;
+}
+
+/**
+ * ab8500_charger_usb_cv() - check if the usb charger is in CV mode
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Returns ac charger CV mode (on success) else error code
+ */
+static int ab8500_charger_usb_cv(struct ab8500_charger *di)
+{
+ int ret;
+ u8 val;
+
+ /* Only check CV mode if the charger is online */
+ if (di->usb.charger_online) {
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_USBCH_STAT1_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return 0;
+ }
+
+ if (val & USB_CH_CV_ON)
+ ret = 1;
+ else
+ ret = 0;
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_detect_chargers() - Detect the connected chargers
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Returns the type of charger connected.
+ * For USB it will not mean we can actually charge from it
+ * but that there is a USB cable connected that we have to
+ * identify. This is used during startup when we don't get
+ * interrupts of the charger detection
+ *
+ * Returns an integer value, that means,
+ * NO_PW_CONN no power supply is connected
+ * AC_PW_CONN if the AC power supply is connected
+ * USB_PW_CONN if the USB power supply is connected
+ * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
+ */
+static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
+{
+ int result = NO_PW_CONN;
+ int ret;
+ u8 val;
+
+ /* Check for AC charger */
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_STATUS1_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ goto out;
+ }
+
+ if (val & MAIN_CH_DET)
+ result = AC_PW_CONN;
+
+ /* Check for USB charger */
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_USBCH_STAT1_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ goto out;
+ }
+
+ if (val & (VBUS_DET_DBNC100 | VBUS_DET_DBNC1))
+ result |= USB_PW_CONN;
+
+ /*
+ * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
+ * will be triggered everytime we enable the VDD ADC supply.
+ * This will turn off charging for a short while.
+ * It can be avoided by having the supply on when
+ * there is a charger connected. Normally the VDD ADC supply
+ * is enabled everytime a GPADC conversion is triggered. We will
+ * force it to be enabled from this driver to have
+ * the GPADC module independant of the AB8500 chargers
+ */
+ if (result == NO_PW_CONN && di->vddadc_en) {
+ regulator_disable(di->regu);
+ di->vddadc_en = false;
+ } else if ((result & AC_PW_CONN || result & USB_PW_CONN) &&
+ !di->vddadc_en) {
+ regulator_enable(di->regu);
+ di->vddadc_en = true;
+ }
+
+ return result;
+
+out:
+ if (di->vddadc_en) {
+ regulator_disable(di->regu);
+ di->vddadc_en = false;
+ }
+ return ret;
+}
+
+/**
+ * ab8500_charger_max_usb_curr() - get the max curr for the USB type
+ * @di: pointer to the ab8500_charger structure
+ * @link_status: the identified USB type
+ *
+ * Get the maximum current that is allowed to be drawn from the host
+ * based on the USB type.
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
+ enum ab8500_charger_link_status link_status)
+{
+ int ret = 0;
+
+ switch (link_status) {
+ case USB_STAT_STD_HOST_NC:
+ case USB_STAT_STD_HOST_C_NS:
+ case USB_STAT_STD_HOST_C_S:
+ dev_dbg(di->dev, "USB Type - Standard host is "
+ "detected through USB driver\n");
+ ret = -1;
+ break;
+ case USB_STAT_HOST_CHG_HS_CHIRP:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ break;
+ case USB_STAT_HOST_CHG_HS:
+ case USB_STAT_ACA_RID_C_HS:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
+ break;
+ case USB_STAT_ACA_RID_A:
+ /*
+ * Dedicated charger level minus maximum current accessory
+ * can consume (300mA). Closest level is 1100mA
+ */
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P1;
+ break;
+ case USB_STAT_ACA_RID_B:
+ /*
+ * Dedicated charger level minus 120mA (20mA for ACA and
+ * 100mA for potential accessory). Closest level is 1300mA
+ */
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
+ break;
+ case USB_STAT_DEDICATED_CHG:
+ case USB_STAT_HOST_CHG_NM:
+ case USB_STAT_ACA_RID_C_HS_CHIRP:
+ case USB_STAT_ACA_RID_C_NM:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
+ break;
+ case USB_STAT_RESERVED:
+ /*
+ * This state is used to indicate that VBUS has dropped below
+ * the detection level 4 times in a row. This is due to the
+ * charger output current is set to high making the charger
+ * voltage collapse. This have to be propagated through to
+ * chargalg. This is done using the property
+ * POWER_SUPPLY_PROP_CURRENT_AVG = 1
+ */
+ di->flags.vbus_collapse = true;
+ dev_dbg(di->dev, "USB Type - USB_STAT_RESERVED "
+ "VBUS has collapsed\n");
+ ret = -1;
+ break;
+ case USB_STAT_HM_IDGND:
+ case USB_STAT_NOT_CONFIGURED:
+ case USB_STAT_NOT_VALID_LINK:
+ dev_err(di->dev, "USB Type - Charging not allowed\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ ret = -ENXIO;
+ break;
+ default:
+ dev_err(di->dev, "USB Type - Unknown\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ ret = -ENXIO;
+ break;
+ };
+
+ dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
+ link_status, di->max_usb_in_curr);
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_read_usb_type() - read the type of usb connected
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Detect the type of the plugged USB
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
+{
+ int ret;
+ u8 val;
+
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return ret;
+ }
+ ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+ AB8500_USB_LINE_STAT_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return ret;
+ }
+
+ /* get the USB type */
+ val = (val & AB8500_USB_LINK_STATUS) >> 3;
+ ret = ab8500_charger_max_usb_curr(di,
+ (enum ab8500_charger_link_status) val);
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_detect_usb_type() - get the type of usb connected
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Detect the type of the plugged USB
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
+{
+ int i, ret;
+ u8 val;
+
+ /*
+ * On getting the VBUS rising edge detect interrupt there
+ * is a 250ms delay after which the register UsbLineStatus
+ * is filled with valid data.
+ */
+ for (i = 0; i < 10; i++) {
+ msleep(250);
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG,
+ &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return ret;
+ }
+ ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+ AB8500_USB_LINE_STAT_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return ret;
+ }
+ /*
+ * Until the IT source register is read the UsbLineStatus
+ * register is not updated, hence doing the same
+ * Revisit this:
+ */
+
+ /* get the USB type */
+ val = (val & AB8500_USB_LINK_STATUS) >> 3;
+ if (val)
+ break;
+ }
+ ret = ab8500_charger_max_usb_curr(di,
+ (enum ab8500_charger_link_status) val);
+
+ return ret;
+}
+
+/*
+ * This array maps the raw hex value to charger voltage used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_charger_voltage_map[] = {
+ 3500 ,
+ 3525 ,
+ 3550 ,
+ 3575 ,
+ 3600 ,
+ 3625 ,
+ 3650 ,
+ 3675 ,
+ 3700 ,
+ 3725 ,
+ 3750 ,
+ 3775 ,
+ 3800 ,
+ 3825 ,
+ 3850 ,
+ 3875 ,
+ 3900 ,
+ 3925 ,
+ 3950 ,
+ 3975 ,
+ 4000 ,
+ 4025 ,
+ 4050 ,
+ 4060 ,
+ 4070 ,
+ 4080 ,
+ 4090 ,
+ 4100 ,
+ 4110 ,
+ 4120 ,
+ 4130 ,
+ 4140 ,
+ 4150 ,
+ 4160 ,
+ 4170 ,
+ 4180 ,
+ 4190 ,
+ 4200 ,
+ 4210 ,
+ 4220 ,
+ 4230 ,
+ 4240 ,
+ 4250 ,
+ 4260 ,
+ 4270 ,
+ 4280 ,
+ 4290 ,
+ 4300 ,
+ 4310 ,
+ 4320 ,
+ 4330 ,
+ 4340 ,
+ 4350 ,
+ 4360 ,
+ 4370 ,
+ 4380 ,
+ 4390 ,
+ 4400 ,
+ 4410 ,
+ 4420 ,
+ 4430 ,
+ 4440 ,
+ 4450 ,
+ 4460 ,
+ 4470 ,
+ 4480 ,
+ 4490 ,
+ 4500 ,
+ 4510 ,
+ 4520 ,
+ 4530 ,
+ 4540 ,
+ 4550 ,
+ 4560 ,
+ 4570 ,
+ 4580 ,
+ 4590 ,
+ 4600 ,
+};
+
+/*
+ * This array maps the raw hex value to charger current used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_charger_current_map[] = {
+ 100 ,
+ 200 ,
+ 300 ,
+ 400 ,
+ 500 ,
+ 600 ,
+ 700 ,
+ 800 ,
+ 900 ,
+ 1000 ,
+ 1100 ,
+ 1200 ,
+ 1300 ,
+ 1400 ,
+ 1500 ,
+};
+
+/*
+ * This array maps the raw hex value to VBUS input current used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_charger_vbus_in_curr_map[] = {
+ USB_CH_IP_CUR_LVL_0P05,
+ USB_CH_IP_CUR_LVL_0P09,
+ USB_CH_IP_CUR_LVL_0P19,
+ USB_CH_IP_CUR_LVL_0P29,
+ USB_CH_IP_CUR_LVL_0P38,
+ USB_CH_IP_CUR_LVL_0P45,
+ USB_CH_IP_CUR_LVL_0P5,
+ USB_CH_IP_CUR_LVL_0P6,
+ USB_CH_IP_CUR_LVL_0P7,
+ USB_CH_IP_CUR_LVL_0P8,
+ USB_CH_IP_CUR_LVL_0P9,
+ USB_CH_IP_CUR_LVL_1P0,
+ USB_CH_IP_CUR_LVL_1P1,
+ USB_CH_IP_CUR_LVL_1P3,
+ USB_CH_IP_CUR_LVL_1P4,
+ USB_CH_IP_CUR_LVL_1P5,
+};
+
+static int ab8500_voltage_to_regval(int voltage)
+{
+ int i;
+
+ /* Special case for voltage below 3.5V */
+ if (voltage < ab8500_charger_voltage_map[0])
+ return LOW_VOLT_REG;
+
+ for (i = 1; i < ARRAY_SIZE(ab8500_charger_voltage_map); i++) {
+ if (voltage < ab8500_charger_voltage_map[i])
+ return i - 1;
+ }
+
+ /* If not last element, return error */
+ i = ARRAY_SIZE(ab8500_charger_voltage_map) - 1;
+ if (voltage == ab8500_charger_voltage_map[i])
+ return i;
+ else
+ return -1;
+}
+
+static int ab8500_current_to_regval(int curr)
+{
+ int i;
+
+ if (curr < ab8500_charger_current_map[0])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_charger_current_map); i++) {
+ if (curr < ab8500_charger_current_map[i])
+ return i - 1;
+ }
+
+ /* If not last element, return error */
+ i = ARRAY_SIZE(ab8500_charger_current_map) - 1;
+ if (curr == ab8500_charger_current_map[i])
+ return i;
+ else
+ return -1;
+}
+
+static int ab8500_vbus_in_curr_to_regval(int curr)
+{
+ int i;
+
+ if (curr < ab8500_charger_vbus_in_curr_map[0])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_charger_vbus_in_curr_map); i++) {
+ if (curr < ab8500_charger_vbus_in_curr_map[i])
+ return i - 1;
+ }
+
+ /* If not last element, return error */
+ i = ARRAY_SIZE(ab8500_charger_vbus_in_curr_map) - 1;
+ if (curr == ab8500_charger_vbus_in_curr_map[i])
+ return i;
+ else
+ return -1;
+}
+
+/**
+ * ab8500_charger_get_usb_cur() - get usb current
+ * @di: pointer to the ab8500_charger structre
+ *
+ * The usb stack provides the maximum current that can be drawn from
+ * the standard usb host. This will be in mA.
+ * This function converts current in mA to a value that can be written
+ * to the register. Returns -1 if charging is not allowed
+ */
+static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
+{
+ switch (di->usb_state.usb_current) {
+ case 100:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+ break;
+ case 200:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P19;
+ break;
+ case 300:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P29;
+ break;
+ case 400:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P38;
+ break;
+ case 500:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ break;
+ default:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ return -1;
+ break;
+ };
+ return 0;
+}
+
+/**
+ * ab8500_charger_set_vbus_in_curr() - set VBUS input current limit
+ * @di: pointer to the ab8500_charger structure
+ * @ich_in: charger input current limit
+ *
+ * Sets the current that can be drawn from the USB host
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
+ int ich_in)
+{
+ int ret;
+ int input_curr_index;
+ int min_value;
+
+ /* We should always use to lowest current limit */
+ min_value = min(di->bat->chg_params->usb_curr_max, ich_in);
+
+ input_curr_index = ab8500_vbus_in_curr_to_regval(min_value);
+ if (input_curr_index < 0) {
+ dev_err(di->dev, "VBUS input current limit too high\n");
+ return -ENXIO;
+ }
+
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_USBCH_IPT_CRNTLVL_REG,
+ input_curr_index << VBUS_IN_CURR_LIM_SHIFT);
+ if (ret)
+ dev_err(di->dev, "%s write failed\n", __func__);
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_led_en() - turn on/off chargign led
+ * @di: pointer to the ab8500_charger structure
+ * @on: flag to turn on/off the chargign led
+ *
+ * Power ON/OFF charging LED indication
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_led_en(struct ab8500_charger *di, int on)
+{
+ int ret;
+
+ if (on) {
+ /* Power ON charging LED indicator, set LED current to 5mA */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_LED_INDICATOR_PWM_CTRL,
+ (LED_IND_CUR_5MA | LED_INDICATOR_PWM_ENA));
+ if (ret) {
+ dev_err(di->dev, "Power ON LED failed\n");
+ return ret;
+ }
+ /* LED indicator PWM duty cycle 252/256 */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_LED_INDICATOR_PWM_DUTY,
+ LED_INDICATOR_PWM_DUTY_252_256);
+ if (ret) {
+ dev_err(di->dev, "Set LED PWM duty cycle failed\n");
+ return ret;
+ }
+ } else {
+ /* Power off charging LED indicator */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_LED_INDICATOR_PWM_CTRL,
+ LED_INDICATOR_PWM_DIS);
+ if (ret) {
+ dev_err(di->dev, "Power-off LED failed\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_ac_en() - enable or disable ac charging
+ * @di: pointer to the ab8500_charger structure
+ * @enable: enable/disable flag
+ * @vset: charging voltage
+ * @iset: charging current
+ *
+ * Enable/Disable AC/Mains charging and turns on/off the charging led
+ * respectively.
+ **/
+static int ab8500_charger_ac_en(struct ux500_charger *charger,
+ int enable, int vset, int iset)
+{
+ int ret;
+ int volt_index;
+ int curr_index;
+ int input_curr_index;
+ u8 overshoot = 0;
+
+ struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger);
+
+ if (enable) {
+ /* Check if AC is connected */
+ if (!di->ac.charger_connected) {
+ dev_err(di->dev, "AC charger not connected\n");
+ return -ENXIO;
+ }
+
+ /* Enable AC charging */
+ dev_dbg(di->dev, "Enable AC: %dmV %dmA\n", vset, iset);
+
+ /* Check if the requested voltage or current is valid */
+ volt_index = ab8500_voltage_to_regval(vset);
+ curr_index = ab8500_current_to_regval(iset);
+ input_curr_index = ab8500_current_to_regval(
+ di->bat->chg_params->ac_curr_max);
+ if (volt_index < 0 || curr_index < 0 || input_curr_index < 0) {
+ dev_err(di->dev,
+ "Charger voltage or current too high, "
+ "charging not started\n");
+ return -ENXIO;
+ }
+
+ /* ChVoltLevel: maximum battery charging voltage */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+ /* MainChInputCurr: current that can be drawn from the charger*/
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_MCH_IPT_CURLVL_REG,
+ input_curr_index << MAIN_CH_INPUT_CURR_SHIFT);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+ /* ChOutputCurentLevel: protected output current */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ /* Check if VBAT overshoot control should be enabled */
+ if (!di->bat->enable_overshoot)
+ overshoot = MAIN_CH_NO_OVERSHOOT_ENA_N;
+
+ /* Enable Main Charger */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_MCH_CTRL1, MAIN_CH_ENA | overshoot);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ /* Power on charging LED indication */
+ ret = ab8500_charger_led_en(di, true);
+ if (ret < 0)
+ dev_err(di->dev, "failed to enable LED\n");
+
+ di->ac.charger_online = 1;
+ } else {
+ /* Disable AC charging */
+
+ switch (di->chip_id) {
+ case AB8500_CUT1P0:
+ case AB8500_CUT1P1:
+ /*
+ * For ABB revision 1.0 and 1.1 there is a bug in the
+ * watchdog logic. That means we have to continously
+ * kick the charger watchdog even when no charger is
+ * connected. This is only valid once the AC charger
+ * has been enabled. This is a bug that is not handled
+ * by the algorithm and the watchdog have to be kicked
+ * by the charger driver when the AC charger
+ * is disabled
+ */
+ if (di->ac_conn) {
+ queue_delayed_work(di->charger_wq,
+ &di->kick_wd_work,
+ round_jiffies(WD_KICK_INTERVAL));
+ }
+
+ /*
+ * We can't turn off charging completely
+ * due to a bug in AB8500 cut1.
+ * If we do, charging will not start again.
+ * That is why we set the lowest voltage
+ * and current possible
+ */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CH_VOLT_LVL_REG, CH_VOL_LVL_3P5);
+ if (ret) {
+ dev_err(di->dev,
+ "%s write failed\n", __func__);
+ return ret;
+ }
+
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CH_OPT_CRNTLVL_REG, CH_OP_CUR_LVL_0P1);
+ if (ret) {
+ dev_err(di->dev,
+ "%s write failed\n", __func__);
+ return ret;
+ }
+ break;
+
+ case AB8500_CUT2P0:
+ default:
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_MCH_CTRL1, 0);
+ if (ret) {
+ dev_err(di->dev,
+ "%s write failed\n", __func__);
+ return ret;
+ }
+ break;
+ }
+
+ ret = ab8500_charger_led_en(di, false);
+ if (ret < 0)
+ dev_err(di->dev, "failed to disable LED\n");
+
+ di->ac.charger_online = 0;
+ di->ac.wd_expired = false;
+ dev_dbg(di->dev, "%s Disabled AC charging\n", __func__);
+ }
+ power_supply_changed(&di->ac_chg.psy);
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_usb_en() - enable usb charging
+ * @di: pointer to the ab8500_charger structure
+ * @enable: enable/disable flag
+ * @vset: charging voltage
+ * @ich_out: charger output current
+ *
+ * Enable/Disable USB charging and turns on/off the charging led respectively.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_usb_en(struct ux500_charger *charger,
+ int enable, int vset, int ich_out)
+{
+ int ret;
+ int volt_index;
+ int curr_index;
+ u8 overshoot = 0;
+
+ struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
+
+ if (enable) {
+ /* Check if USB is connected */
+ if (!di->usb.charger_connected) {
+ dev_err(di->dev, "USB charger not connected\n");
+ return -ENXIO;
+ }
+
+ /* Enable USB charging */
+ dev_dbg(di->dev, "Enable USB: %dmV %dmA\n", vset, ich_out);
+
+ /* Check if the requested voltage or current is valid */
+ volt_index = ab8500_voltage_to_regval(vset);
+ curr_index = ab8500_current_to_regval(ich_out);
+ if (volt_index < 0 || curr_index < 0) {
+ dev_err(di->dev,
+ "Charger voltage or current too high, "
+ "charging not started\n");
+ return -ENXIO;
+ }
+
+ /* ChVoltLevel: max voltage upto which battery can be charged */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+ /* USBChInputCurr: current that can be drawn from the usb */
+ ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+ if (ret) {
+ dev_err(di->dev, "setting USBChInputCurr failed\n");
+ return ret;
+ }
+ /* ChOutputCurentLevel: protected output current */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+ /* Check if VBAT overshoot control should be enabled */
+ if (!di->bat->enable_overshoot)
+ overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
+
+ /* Enable USB Charger */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ /* If success power on charging LED indication */
+ ret = ab8500_charger_led_en(di, true);
+ if (ret < 0)
+ dev_err(di->dev, "failed to enable LED\n");
+
+ di->usb.charger_online = 1;
+ } else {
+ /* Disable USB charging */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_USBCH_CTRL1_REG, 0);
+ if (ret) {
+ dev_err(di->dev,
+ "%s write failed\n", __func__);
+ return ret;
+ }
+
+ ret = ab8500_charger_led_en(di, false);
+ if (ret < 0)
+ dev_err(di->dev, "failed to disable LED\n");
+
+ di->usb.charger_online = 0;
+ di->usb.wd_expired = false;
+ dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
+ }
+ power_supply_changed(&di->usb_chg.psy);
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_watchdog_kick() - kick charger watchdog
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Kick charger watchdog
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_watchdog_kick(struct ux500_charger *charger)
+{
+ int ret;
+ struct ab8500_charger *di;
+
+ if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
+ di = to_ab8500_charger_ac_device_info(charger);
+ else if (charger->psy.type == POWER_SUPPLY_TYPE_USB)
+ di = to_ab8500_charger_usb_device_info(charger);
+ else
+ return -ENXIO;
+
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+ if (ret)
+ dev_err(di->dev, "Failed to kick WD!\n");
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_update_charger_current() - update charger current
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Update the charger output current for the specified charger
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
+ int ich_out)
+{
+ int ret;
+ int curr_index;
+ struct ab8500_charger *di;
+
+ if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
+ di = to_ab8500_charger_ac_device_info(charger);
+ else if (charger->psy.type == POWER_SUPPLY_TYPE_USB)
+ di = to_ab8500_charger_usb_device_info(charger);
+ else
+ return -ENXIO;
+
+ curr_index = ab8500_current_to_regval(ich_out);
+ if (curr_index < 0) {
+ dev_err(di->dev,
+ "Charger current too high, "
+ "charging not started\n");
+ return -ENXIO;
+ }
+
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ /* Reset the main and usb drop input current measurement counter */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CHARGER_CTRL,
+ 0x1);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_check_hw_failure_work() - check main charger failure
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the main charger status
+ */
+static void ab8500_charger_check_hw_failure_work(struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, check_hw_failure_work.work);
+
+ /* Check if the status bits for HW failure is still active */
+ if (di->flags.mainextchnotok) {
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return;
+ }
+ if (!(reg_value & MAIN_CH_NOK)) {
+ di->flags.mainextchnotok = false;
+ power_supply_changed(&di->ac_chg.psy);
+ }
+ }
+ if (di->flags.vbus_ovv) {
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG,
+ &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return;
+ }
+ if (!(reg_value & VBUS_OVV_TH)) {
+ di->flags.vbus_ovv = false;
+ power_supply_changed(&di->usb_chg.psy);
+ }
+ }
+ /* If we still have a failure, schedule a new check */
+ if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
+ queue_delayed_work(di->charger_wq,
+ &di->check_hw_failure_work, round_jiffies(HZ));
+ }
+}
+
+/**
+ * ab8500_charger_kick_watchdog_work() - kick the watchdog
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for kicking the charger watchdog.
+ *
+ * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+ * logic. That means we have to continously kick the charger
+ * watchdog even when no charger is connected. This is only
+ * valid once the AC charger has been enabled. This is
+ * a bug that is not handled by the algorithm and the
+ * watchdog have to be kicked by the charger driver
+ * when the AC charger is disabled
+ */
+static void ab8500_charger_kick_watchdog_work(struct work_struct *work)
+{
+ int ret;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, kick_wd_work.work);
+
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+ if (ret)
+ dev_err(di->dev, "Failed to kick WD!\n");
+
+ /* Schedule a new watchdog kick */
+ queue_delayed_work(di->charger_wq,
+ &di->kick_wd_work, round_jiffies(WD_KICK_INTERVAL));
+}
+
+/**
+ * ab8500_charger_ac_work() - work to get and set main charger status
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the main charger status
+ */
+static void ab8500_charger_ac_work(struct work_struct *work)
+{
+ int ret;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, ac_work);
+
+ /*
+ * Since we can't be sure that the events are received
+ * synchronously, we have the check if the main charger is
+ * connected by reading the status register
+ */
+ ret = ab8500_charger_detect_chargers(di);
+ if (ret < 0)
+ return;
+
+ if (ret & AC_PW_CONN) {
+ di->ac.charger_connected = 1;
+ di->ac_conn = true;
+ } else {
+ di->ac.charger_connected = 0;
+ }
+
+ power_supply_changed(&di->ac_chg.psy);
+}
+
+/**
+ * ab8500_charger_detect_usb_type_work() - work to detect USB type
+ * @work: Pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+void ab8500_charger_detect_usb_type_work(struct work_struct *work)
+{
+ int ret;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, detect_usb_type_work);
+
+ /*
+ * Since we can't be sure that the events are received
+ * synchronously, we have the check if is
+ * connected by reading the status register
+ */
+ ret = ab8500_charger_detect_chargers(di);
+ if (ret < 0)
+ return;
+
+ if (!(ret & USB_PW_CONN)) {
+ di->vbus_detected = 0;
+ di->usb.charger_connected = 0;
+ power_supply_changed(&di->usb_chg.psy);
+ } else {
+ di->vbus_detected = 1;
+
+ switch (di->chip_id) {
+ case AB8500_CUT1P0:
+ case AB8500_CUT1P1:
+ ret = ab8500_charger_detect_usb_type(di);
+ if (!ret) {
+ di->usb.charger_connected = 1;
+ power_supply_changed(&di->usb_chg.psy);
+ }
+ break;
+
+ case AB8500_CUT2P0:
+ default:
+ /* For ABB cut2.0 and onwards we have an IRQ,
+ * USB_LINK_STATUS that will be triggered when the USB
+ * link status changes. The exception is USB connected
+ * during startup. Then we don't get a
+ * USB_LINK_STATUS IRQ
+ */
+ if (di->vbus_detected_start) {
+ di->vbus_detected_start = false;
+ ret = ab8500_charger_detect_usb_type(di);
+ if (!ret) {
+ di->usb.charger_connected = 1;
+ power_supply_changed(&di->usb_chg.psy);
+ }
+ }
+ break;
+ }
+ }
+}
+
+/**
+ * ab8500_charger_usb_link_status_work() - work to detect USB type
+ * @work: pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+static void ab8500_charger_usb_link_status_work(struct work_struct *work)
+{
+ int ret;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, usb_link_status_work);
+
+ /*
+ * Since we can't be sure that the events are received
+ * synchronously, we have the check if is
+ * connected by reading the status register
+ */
+ ret = ab8500_charger_detect_chargers(di);
+ if (ret < 0)
+ return;
+
+ if (!(ret & USB_PW_CONN)) {
+ di->vbus_detected = 0;
+ di->usb.charger_connected = 0;
+ power_supply_changed(&di->usb_chg.psy);
+ } else {
+ di->vbus_detected = 1;
+ ret = ab8500_charger_read_usb_type(di);
+ if (!ret) {
+ /* Update maximum input current */
+ ret = ab8500_charger_set_vbus_in_curr(di,
+ di->max_usb_in_curr);
+ if (ret)
+ return;
+
+ di->usb.charger_connected = 1;
+ power_supply_changed(&di->usb_chg.psy);
+ } else if (ret == -ENXIO) {
+ /* No valid charger type detected */
+ di->usb.charger_connected = 0;
+ power_supply_changed(&di->usb_chg.psy);
+ }
+ }
+}
+
+static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
+{
+ int ret;
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, usb_state_changed_work);
+
+ if (!di->vbus_detected)
+ return;
+
+ spin_lock(&di->usb_state.usb_lock);
+ di->usb_state.usb_changed = false;
+ spin_unlock(&di->usb_state.usb_lock);
+
+ /*
+ * wait for some time until you get updates from the usb stack
+ * and negotiations are completed
+ */
+ msleep(250);
+
+ if (di->usb_state.usb_changed)
+ return;
+
+ dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
+ __func__, di->usb_state.state, di->usb_state.usb_current);
+
+ switch (di->usb_state.state) {
+ case AB8500_BM_USB_STATE_RESET_HS:
+ case AB8500_BM_USB_STATE_RESET_FS:
+ case AB8500_BM_USB_STATE_SUSPEND:
+ case AB8500_BM_USB_STATE_MAX:
+ di->usb.charger_connected = 0;
+ power_supply_changed(&di->usb_chg.psy);
+ break;
+
+ case AB8500_BM_USB_STATE_RESUME:
+ /*
+ * when suspend->resume there should be delay
+ * of 1sec for enabling charging
+ */
+ msleep(1000);
+ /* Intentional fall through */
+ case AB8500_BM_USB_STATE_CONFIGURED:
+ /*
+ * USB is configured, enable charging with the charging
+ * input current obtained from USB driver
+ */
+ if (!ab8500_charger_get_usb_cur(di)) {
+ /* Update maximum input current */
+ ret = ab8500_charger_set_vbus_in_curr(di,
+ di->max_usb_in_curr);
+ if (ret)
+ return;
+
+ di->usb.charger_connected = 1;
+ power_supply_changed(&di->usb_chg.psy);
+ }
+ break;
+
+ default:
+ break;
+ };
+}
+
+/**
+ * ab8500_charger_check_usbchargernotok_work() - check USB chg not ok status
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the USB charger Not OK status
+ */
+static void ab8500_charger_check_usbchargernotok_work(struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+ bool prev_status;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, check_usbchgnotok_work.work);
+
+ /* Check if the status bit for usbchargernotok is still active */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return;
+ }
+ prev_status = di->flags.usbchargernotok;
+
+ if (reg_value & VBUS_CH_NOK) {
+ di->flags.usbchargernotok = true;
+ /* Check again in 1sec */
+ queue_delayed_work(di->charger_wq,
+ &di->check_usbchgnotok_work, HZ);
+ } else {
+ di->flags.usbchargernotok = false;
+ di->flags.vbus_collapse = false;
+ }
+
+ if (prev_status != di->flags.usbchargernotok)
+ power_supply_changed(&di->usb_chg.psy);
+}
+
+/**
+ * ab8500_charger_check_main_thermal_prot_work() - check main thermal status
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the Main thermal prot status
+ */
+static void ab8500_charger_check_main_thermal_prot_work(
+ struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, check_main_thermal_prot_work);
+
+ /* Check if the status bit for main_thermal_prot is still active */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return;
+ }
+ if (reg_value & MAIN_CH_TH_PROT)
+ di->flags.main_thermal_prot = true;
+ else
+ di->flags.main_thermal_prot = false;
+
+ power_supply_changed(&di->ac_chg.psy);
+}
+
+/**
+ * ab8500_charger_check_usb_thermal_prot_work() - check usb thermal status
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the USB thermal prot status
+ */
+static void ab8500_charger_check_usb_thermal_prot_work(
+ struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, check_usb_thermal_prot_work);
+
+ /* Check if the status bit for usb_thermal_prot is still active */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return;
+ }
+ if (reg_value & USB_CH_TH_PROT)
+ di->flags.usb_thermal_prot = true;
+ else
+ di->flags.usb_thermal_prot = false;
+
+ power_supply_changed(&di->usb_chg.psy);
+}
+
+/**
+ * ab8500_charger_mainchunplugdet_handler() - main charger unplugged
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchunplugdet_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "Main charger unplugged\n");
+ queue_work(di->charger_wq, &di->ac_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchplugdet_handler() - main charger plugged
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchplugdet_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "Main charger plugged\n");
+ queue_work(di->charger_wq, &di->ac_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainextchnotok_handler() - main charger not ok
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainextchnotok_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "Main charger not ok\n");
+ di->flags.mainextchnotok = true;
+ power_supply_changed(&di->ac_chg.psy);
+
+ /* Schedule a new HW failure check */
+ queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchthprotr_handler() - Die temp is above main charger
+ * thermal protection threshold
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchthprotr_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev,
+ "Die temp above Main charger thermal protection threshold\n");
+ queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchthprotf_handler() - Die temp is below main charger
+ * thermal protection threshold
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchthprotf_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev,
+ "Die temp ok for Main charger thermal protection threshold\n");
+ queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbusdetf_handler() - VBUS falling detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusdetf_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "VBUS falling detected\n");
+ queue_work(di->charger_wq, &di->detect_usb_type_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbusdetr_handler() - VBUS rising detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusdetr_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ di->vbus_detected = true;
+ dev_dbg(di->dev, "VBUS rising detected\n");
+ queue_work(di->charger_wq, &di->detect_usb_type_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usblinkstatus_handler() - USB link status has changed
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usblinkstatus_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "USB link status changed\n");
+
+ queue_work(di->charger_wq, &di->usb_link_status_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchthprotr_handler() - Die temp is above usb charger
+ * thermal protection threshold
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchthprotr_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev,
+ "Die temp above USB charger thermal protection threshold\n");
+ queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchthprotf_handler() - Die temp is below usb charger
+ * thermal protection threshold
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchthprotf_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev,
+ "Die temp ok for USB charger thermal protection threshold\n");
+ queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchargernotokr_handler() - USB charger not ok detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchargernotokr_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "Not allowed USB charger detected\n");
+ queue_delayed_work(di->charger_wq, &di->check_usbchgnotok_work, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_chwdexp_handler() - Charger watchdog expired
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "Charger watchdog expired\n");
+
+ /*
+ * The charger that was online when the watchdog expired
+ * needs to be restarted for charging to start again
+ */
+ if (di->ac.charger_online) {
+ di->ac.wd_expired = true;
+ power_supply_changed(&di->ac_chg.psy);
+ }
+ if (di->usb.charger_online) {
+ di->usb.wd_expired = true;
+ power_supply_changed(&di->usb_chg.psy);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbusovv_handler() - VBUS overvoltage detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusovv_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "VBUS overvoltage detected\n");
+ di->flags.vbus_ovv = true;
+ power_supply_changed(&di->usb_chg.psy);
+
+ /* Schedule a new HW failure check */
+ queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_ac_get_property() - get the ac/mains properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the ac/mains
+ * properties by reading the sysfs files.
+ * AC/Mains properties are online, present and voltage.
+ * online: ac/mains charging is in progress or not
+ * present: presence of the ac/mains
+ * voltage: AC/Mains voltage
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_ac_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab8500_charger *di;
+
+ di = to_ab8500_charger_ac_device_info(psy_to_ux500_charger(psy));
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (di->flags.mainextchnotok)
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ else if (di->ac.wd_expired || di->usb.wd_expired)
+ val->intval = POWER_SUPPLY_HEALTH_DEAD;
+ else if (di->flags.main_thermal_prot)
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = di->ac.charger_online;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = di->ac.charger_connected;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ di->ac.charger_voltage = ab8500_charger_get_ac_voltage(di);
+ val->intval = di->ac.charger_voltage * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ /*
+ * This property is used to indicate when CV mode is entered
+ * for the AC charger
+ */
+ di->ac.cv_active = ab8500_charger_ac_cv(di);
+ val->intval = di->ac.cv_active;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = ab8500_charger_get_ac_current(di) * 1000;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * ab8500_charger_usb_get_property() - get the usb properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the usb
+ * properties by reading the sysfs files.
+ * USB properties are online, present and voltage.
+ * online: usb charging is in progress or not
+ * present: presence of the usb
+ * voltage: vbus voltage
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab8500_charger *di;
+
+ di = to_ab8500_charger_usb_device_info(psy_to_ux500_charger(psy));
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (di->flags.usbchargernotok)
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ else if (di->ac.wd_expired || di->usb.wd_expired)
+ val->intval = POWER_SUPPLY_HEALTH_DEAD;
+ else if (di->flags.usb_thermal_prot)
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (di->flags.vbus_ovv)
+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = di->usb.charger_online;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = di->usb.charger_connected;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ di->usb.charger_voltage = ab8500_charger_get_vbus_voltage(di);
+ val->intval = di->usb.charger_voltage * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ /*
+ * This property is used to indicate when CV mode is entered
+ * for the USB charger
+ */
+ di->usb.cv_active = ab8500_charger_usb_cv(di);
+ val->intval = di->usb.cv_active;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = ab8500_charger_get_usb_current(di) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ /*
+ * This property is used to indicate when VBUS has collapsed
+ * due to too high output current from the USB charger
+ */
+ if (di->flags.vbus_collapse)
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * ab8500_charger_init_hw_registers() - Set up charger related registers
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Set up charger OVV, watchdog and maximum voltage registers as well as
+ * charging of the backup battery
+ */
+static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
+{
+ int ret = 0;
+
+ /* Setup maximum charger current and voltage for ABB cut2.0 */
+ switch (di->chip_id) {
+ case AB8500_CUT1P0:
+ case AB8500_CUT1P1:
+ break;
+ case AB8500_CUT2P0:
+ default:
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CH_VOLT_LVL_MAX_REG, CH_VOL_LVL_4P6);
+ if (ret) {
+ dev_err(di->dev,
+ "failed to set CH_VOLT_LVL_MAX_REG\n");
+ goto out;
+ }
+
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CH_OPT_CRNTLVL_MAX_REG, CH_OP_CUR_LVL_1P6);
+ if (ret) {
+ dev_err(di->dev,
+ "failed to set CH_OPT_CRNTLVL_MAX_REG\n");
+ goto out;
+ }
+
+ break;
+ }
+
+ /* VBUS OVV set to 6.3V and enable automatic current limitiation */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_USBCH_CTRL2_REG,
+ VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
+ if (ret) {
+ dev_err(di->dev, "failed to set VBUS OVV\n");
+ goto out;
+ }
+
+ /* Enable main watchdog in OTP */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_OTP_EMUL, AB8500_OTP_CONF_15, OTP_ENABLE_WD);
+ if (ret) {
+ dev_err(di->dev, "failed to enable main WD in OTP\n");
+ goto out;
+ }
+
+ /* Enable main watchdog */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_SYS_CTRL2_BLOCK,
+ AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_ENA);
+ if (ret) {
+ dev_err(di->dev, "faile to enable main watchdog\n");
+ goto out;
+ }
+
+ /*
+ * Due to internal synchronisation, Enable and Kick watchdog bits
+ * cannot be enabled in a single write.
+ * A minimum delay of 2*32 kHz period (62.5µs) must be inserted
+ * between writing Enable then Kick bits.
+ */
+ udelay(63);
+
+ /* Kick main watchdog */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_SYS_CTRL2_BLOCK,
+ AB8500_MAIN_WDOG_CTRL_REG,
+ (MAIN_WDOG_ENA | MAIN_WDOG_KICK));
+ if (ret) {
+ dev_err(di->dev, "failed to kick main watchdog\n");
+ goto out;
+ }
+
+ /* Disable main watchdog */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_SYS_CTRL2_BLOCK,
+ AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_DIS);
+ if (ret) {
+ dev_err(di->dev, "failed to disable main watchdog\n");
+ goto out;
+ }
+
+ /* Set watchdog timeout */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_WD_TIMER_REG, WD_TIMER);
+ if (ret) {
+ dev_err(di->dev, "failed to set charger watchdog timeout\n");
+ goto out;
+ }
+
+ /* Backup battery voltage and current */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_RTC,
+ AB8500_RTC_BACKUP_CHG_REG,
+ di->bat->bkup_bat_v |
+ di->bat->bkup_bat_i);
+ if (ret) {
+ dev_err(di->dev, "failed to setup backup battery charging\n");
+ goto out;
+ }
+
+ /* Enable backup battery charging */
+ abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_RTC, AB8500_RTC_CTRL_REG,
+ RTC_BUP_CH_ENA, RTC_BUP_CH_ENA);
+ if (ret < 0)
+ dev_err(di->dev, "%s mask and set failed\n", __func__);
+
+out:
+ return ret;
+}
+
+/*
+ * ab8500 charger driver interrupts and their respective isr
+ */
+static struct ab8500_charger_interrupts ab8500_charger_irq[] = {
+ {"MAIN_CH_UNPLUG_DET", ab8500_charger_mainchunplugdet_handler},
+ {"MAIN_CHARGE_PLUG_DET", ab8500_charger_mainchplugdet_handler},
+ {"MAIN_EXT_CH_NOT_OK", ab8500_charger_mainextchnotok_handler},
+ {"MAIN_CH_TH_PROT_R", ab8500_charger_mainchthprotr_handler},
+ {"MAIN_CH_TH_PROT_F", ab8500_charger_mainchthprotf_handler},
+ {"VBUS_DET_F", ab8500_charger_vbusdetf_handler},
+ {"VBUS_DET_R", ab8500_charger_vbusdetr_handler},
+ {"USB_LINK_STATUS", ab8500_charger_usblinkstatus_handler},
+ {"USB_CH_TH_PROT_R", ab8500_charger_usbchthprotr_handler},
+ {"USB_CH_TH_PROT_F", ab8500_charger_usbchthprotf_handler},
+ {"USB_CHARGER_NOT_OKR", ab8500_charger_usbchargernotokr_handler},
+ {"VBUS_OVV", ab8500_charger_vbusovv_handler},
+ {"CH_WD_EXP", ab8500_charger_chwdexp_handler},
+};
+
+void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA)
+{
+ struct ab8500_charger *di = static_di;
+
+ dev_dbg(di->dev, "%s usb_state: 0x%02x mA: %d\n",
+ __func__, bm_usb_state, mA);
+
+ spin_lock(&di->usb_state.usb_lock);
+ di->usb_state.usb_changed = true;
+ spin_unlock(&di->usb_state.usb_lock);
+
+ di->usb_state.state = bm_usb_state;
+ di->usb_state.usb_current = mA;
+
+ queue_work(di->charger_wq, &di->usb_state_changed_work);
+
+ return;
+}
+EXPORT_SYMBOL(ab8500_charger_usb_state_changed);
+
+#if defined(CONFIG_PM)
+static int ab8500_charger_resume(struct platform_device *pdev)
+{
+ int ret;
+ struct ab8500_charger *di = platform_get_drvdata(pdev);
+
+ /*
+ * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+ * logic. That means we have to continously kick the charger
+ * watchdog even when no charger is connected. This is only
+ * valid once the AC charger has been enabled. This is
+ * a bug that is not handled by the algorithm and the
+ * watchdog have to be kicked by the charger driver
+ * when the AC charger is disabled
+ */
+ if (di->ac_conn && (di->chip_id == AB8500_CUT1P0 ||
+ di->chip_id == AB8500_CUT1P1)) {
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+ if (ret)
+ dev_err(di->dev, "Failed to kick WD!\n");
+
+ /* If not already pending start a new timer */
+ if (!delayed_work_pending(
+ &di->kick_wd_work)) {
+ queue_delayed_work(di->charger_wq, &di->kick_wd_work,
+ round_jiffies(WD_KICK_INTERVAL));
+ }
+ }
+
+ /* If we still have a HW failure, schedule a new check */
+ if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
+ queue_delayed_work(di->charger_wq,
+ &di->check_hw_failure_work, 0);
+ }
+
+ return 0;
+}
+
+static int ab8500_charger_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab8500_charger *di = platform_get_drvdata(pdev);
+
+ /* Cancel any pending HW failure check */
+ if (delayed_work_pending(&di->check_hw_failure_work))
+ cancel_delayed_work(&di->check_hw_failure_work);
+
+ return 0;
+}
+#else
+#define ab8500_charger_suspend NULL
+#define ab8500_charger_resume NULL
+#endif
+
+static int __devexit ab8500_charger_remove(struct platform_device *pdev)
+{
+ struct ab8500_charger *di = platform_get_drvdata(pdev);
+ int i, irq, ret;
+
+ /* Disable AC charging */
+ ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
+
+ /* Disable USB charging */
+ ab8500_charger_usb_en(&di->usb_chg, false, 0, 0);
+
+ /* Disable interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+ free_irq(irq, di);
+ }
+
+ /* disable the regulator */
+ regulator_put(di->regu);
+
+ /* Backup battery voltage and current disable */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, 0);
+ if (ret < 0)
+ dev_err(di->dev, "%s mask and set failed\n", __func__);
+
+ /* Delete the work queue */
+ destroy_workqueue(di->charger_wq);
+
+ flush_scheduled_work();
+ power_supply_unregister(&di->usb_chg.psy);
+ power_supply_unregister(&di->ac_chg.psy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di);
+
+ return 0;
+}
+
+static int __devinit ab8500_charger_probe(struct platform_device *pdev)
+{
+ int irq, i, charger_status, ret = 0;
+ struct ab8500_platform_data *plat;
+
+ struct ab8500_charger *di =
+ kzalloc(sizeof(struct ab8500_charger), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ static_di = di;
+
+ /* get parent data */
+ di->dev = &pdev->dev;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
+ di->gpadc = ab8500_gpadc_get();
+
+ /* initialize lock */
+ spin_lock_init(&di->usb_state.usb_lock);
+
+ plat = dev_get_platdata(di->parent->dev);
+
+ /* get charger specific platform data */
+ if (!plat->charger) {
+ dev_err(di->dev, "no charger platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+ di->pdata = plat->charger;
+
+ /* get battery specific platform data */
+ if (!plat->battery) {
+ dev_err(di->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+ di->bat = plat->battery;
+
+ /* AC supply */
+ /* power_supply base class */
+ di->ac_chg.psy.name = "ab8500_ac";
+ di->ac_chg.psy.type = POWER_SUPPLY_TYPE_MAINS;
+ di->ac_chg.psy.properties = ab8500_charger_ac_props;
+ di->ac_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_ac_props);
+ di->ac_chg.psy.get_property = ab8500_charger_ac_get_property;
+ di->ac_chg.psy.supplied_to = di->pdata->supplied_to;
+ di->ac_chg.psy.num_supplicants = di->pdata->num_supplicants;
+ /* ux500_charger sub-class */
+ di->ac_chg.ops.enable = &ab8500_charger_ac_en;
+ di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
+ di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current;
+ di->ac_chg.max_out_volt = ab8500_charger_voltage_map[
+ ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
+ di->ac_chg.max_out_curr = ab8500_charger_current_map[
+ ARRAY_SIZE(ab8500_charger_current_map) - 1];
+
+ /* USB supply */
+ /* power_supply base class */
+ di->usb_chg.psy.name = "ab8500_usb";
+ di->usb_chg.psy.type = POWER_SUPPLY_TYPE_USB;
+ di->usb_chg.psy.properties = ab8500_charger_usb_props;
+ di->usb_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_usb_props);
+ di->usb_chg.psy.get_property = ab8500_charger_usb_get_property;
+ di->usb_chg.psy.supplied_to = di->pdata->supplied_to;
+ di->usb_chg.psy.num_supplicants = di->pdata->num_supplicants;
+ /* ux500_charger sub-class */
+ di->usb_chg.ops.enable = &ab8500_charger_usb_en;
+ di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
+ di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current;
+ di->usb_chg.max_out_volt = ab8500_charger_voltage_map[
+ ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
+ di->usb_chg.max_out_curr = ab8500_charger_current_map[
+ ARRAY_SIZE(ab8500_charger_current_map) - 1];
+
+
+ /* Create a work queue for the charger */
+ di->charger_wq =
+ create_singlethread_workqueue("ab8500_charger_wq");
+ if (di->charger_wq == NULL) {
+ dev_err(di->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for HW failure check */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->check_hw_failure_work,
+ ab8500_charger_check_hw_failure_work);
+ INIT_DELAYED_WORK_DEFERRABLE(&di->check_usbchgnotok_work,
+ ab8500_charger_check_usbchargernotok_work);
+
+
+ /*
+ * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+ * logic. That means we have to continously kick the charger
+ * watchdog even when no charger is connected. This is only
+ * valid once the AC charger has been enabled. This is
+ * a bug that is not handled by the algorithm and the
+ * watchdog have to be kicked by the charger driver
+ * when the AC charger is disabled
+ */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->kick_wd_work,
+ ab8500_charger_kick_watchdog_work);
+
+ /* Init work for charger detection */
+ INIT_WORK(&di->usb_link_status_work,
+ ab8500_charger_usb_link_status_work);
+ INIT_WORK(&di->ac_work, ab8500_charger_ac_work);
+ INIT_WORK(&di->detect_usb_type_work,
+ ab8500_charger_detect_usb_type_work);
+
+ INIT_WORK(&di->usb_state_changed_work,
+ ab8500_charger_usb_state_changed_work);
+
+ /* Init work for checking HW status */
+ INIT_WORK(&di->check_main_thermal_prot_work,
+ ab8500_charger_check_main_thermal_prot_work);
+ INIT_WORK(&di->check_usb_thermal_prot_work,
+ ab8500_charger_check_usb_thermal_prot_work);
+
+ /* Get Chip ID of the ABB ASIC */
+ ret = abx500_get_chip_id(di->dev);
+ if (ret < 0) {
+ dev_err(di->dev, "failed to get chip ID\n");
+ goto free_charger_wq;
+ }
+ di->chip_id = ret;
+ dev_dbg(di->dev, "AB8500 CID is: 0x%02x\n", di->chip_id);
+
+ /*
+ * VDD ADC supply needs to be enabled from this driver when there
+ * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
+ * interrupts during charging
+ */
+ di->regu = regulator_get(di->dev, "vddadc");
+ if (IS_ERR(di->regu)) {
+ ret = PTR_ERR(di->regu);
+ dev_err(di->dev, "failed to get vddadc regulator\n");
+ goto free_charger_wq;
+ }
+
+
+ /* Initialize OVV, and other registers */
+ ret = ab8500_charger_init_hw_registers(di);
+ if (ret) {
+ dev_err(di->dev, "failed to initialize ABB registers\n");
+ goto free_regulator;
+ }
+
+ /* Register AC charger class */
+ ret = power_supply_register(di->dev, &di->ac_chg.psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register AC charger\n");
+ goto free_regulator;
+ }
+
+ /* Register USB charger class */
+ ret = power_supply_register(di->dev, &di->usb_chg.psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register USB charger\n");
+ goto free_ac;
+ }
+
+ /* Identify the connected charger types during startup */
+ charger_status = ab8500_charger_detect_chargers(di);
+ if (charger_status & AC_PW_CONN) {
+ di->ac.charger_connected = 1;
+ di->ac_conn = true;
+ power_supply_changed(&di->ac_chg.psy);
+ }
+
+ if (charger_status & USB_PW_CONN) {
+ dev_dbg(di->dev, "VBUS Detect during startup\n");
+ di->vbus_detected = true;
+ di->vbus_detected_start = true;
+ queue_work(di->charger_wq,
+ &di->detect_usb_type_work);
+ }
+
+ /* Register interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+ ret = request_threaded_irq(irq, NULL, ab8500_charger_irq[i].isr,
+ IRQF_SHARED | IRQF_NO_SUSPEND,
+ ab8500_charger_irq[i].name, di);
+
+ if (ret != 0) {
+ dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+ , ab8500_charger_irq[i].name, irq, ret);
+ goto free_irq;
+ }
+ dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+ ab8500_charger_irq[i].name, irq, ret);
+ }
+
+ platform_set_drvdata(pdev, di);
+ di->parent->charger = di;
+
+ return ret;
+
+free_irq:
+ power_supply_unregister(&di->usb_chg.psy);
+
+ /* We also have to free all successfully registered irqs */
+ for (i = i - 1; i >= 0; i--) {
+ irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+ free_irq(irq, di);
+ }
+free_ac:
+ power_supply_unregister(&di->ac_chg.psy);
+free_regulator:
+ regulator_put(di->regu);
+free_charger_wq:
+ destroy_workqueue(di->charger_wq);
+free_device_info:
+ kfree(di);
+
+ return ret;
+}
+
+static struct platform_driver ab8500_charger_driver = {
+ .probe = ab8500_charger_probe,
+ .remove = __devexit_p(ab8500_charger_remove),
+ .suspend = ab8500_charger_suspend,
+ .resume = ab8500_charger_resume,
+ .driver = {
+ .name = "ab8500-charger",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab8500_charger_init(void)
+{
+ return platform_driver_register(&ab8500_charger_driver);
+}
+
+static void __exit ab8500_charger_exit(void)
+{
+ platform_driver_unregister(&ab8500_charger_driver);
+}
+
+subsys_initcall_sync(ab8500_charger_init);
+module_exit(ab8500_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
+MODULE_ALIAS("platform:ab8500-charger");
+MODULE_DESCRIPTION("AB8500 charger management driver");
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
new file mode 100644
index 00000000000..bb90c79f19c
--- /dev/null
+++ b/drivers/power/ab8500_fg.c
@@ -0,0 +1,1938 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * Main and Back-up battery management driver.
+ *
+ * Note: Backup battery management is required in case of Li-Ion battery and not
+ * for capacitive battery. HREF boards have capacitive battery and hence backup
+ * battery management is not used and the supported code is available in this
+ * driver.
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Johan Palsson <johan.palsson@stericsson.com>
+ * Author: Karl Komierowski <karl.komierowski@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/slab.h>
+#include <linux/mfd/ab8500/ab8500-bm.h>
+#include <linux/delay.h>
+#include <linux/mfd/ab8500/ab8500-gpadc.h>
+#include <linux/mfd/abx500.h>
+#include <linux/time.h>
+
+#define MILLI_TO_MICRO 1000
+#define FG_LSB_IN_MA 1627
+#define QLSB_NANO_AMP_HOURS_X10 1129
+
+#define SEC_TO_SAMPLE(S) (S * 4)
+
+#define NBR_AVG_SAMPLES 20
+
+#define LOW_BAT_CHECK_INTERVAL (2 * HZ)
+
+#define VALID_CAPACITY_SEC (45 * 60) /* 45 minutes */
+
+#define interpolate(x, x1, y1, x2, y2) \
+ ((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
+
+#define to_ab8500_fg_device_info(x) container_of((x), \
+ struct ab8500_fg, fg_psy);
+
+/**
+ * struct ab8500_fg_interrupts - ab8500 fg interupts
+ * @name: name of the interrupt
+ * @isr function pointer to the isr
+ */
+struct ab8500_fg_interrupts {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+enum ab8500_fg_discharge_state {
+ AB8500_FG_DISCHARGE_INIT,
+ AB8500_FG_DISCHARGE_INITMEASURING,
+ AB8500_FG_DISCHARGE_INIT_RECOVERY,
+ AB8500_FG_DISCHARGE_RECOVERY,
+ AB8500_FG_DISCHARGE_READOUT,
+ AB8500_FG_DISCHARGE_WAKEUP,
+};
+
+static char *discharge_state[] = {
+ "DISCHARGE_INIT",
+ "DISCHARGE_INITMEASURING",
+ "DISCHARGE_INIT_RECOVERY",
+ "DISCHARGE_RECOVERY",
+ "DISCHARGE_READOUT",
+ "DISCHARGE_WAKEUP",
+};
+
+enum ab8500_fg_charge_state {
+ AB8500_FG_CHARGE_INIT,
+ AB8500_FG_CHARGE_READOUT,
+};
+
+static char *charge_state[] = {
+ "CHARGE_INIT",
+ "CHARGE_READOUT",
+};
+
+enum ab8500_fg_calibration_state {
+ AB8500_FG_CALIB_INIT,
+ AB8500_FG_CALIB_WAIT,
+ AB8500_FG_CALIB_END,
+};
+
+struct ab8500_fg_avg_cap {
+ int avg;
+ int samples[NBR_AVG_SAMPLES];
+ __kernel_time_t time_stamps[NBR_AVG_SAMPLES];
+ int pos;
+ int nbr_samples;
+ int sum;
+};
+
+struct ab8500_fg_battery_capacity {
+ int max_mah_design;
+ int max_mah;
+ int mah;
+ int permille;
+ int level;
+ int prev_mah;
+ int prev_percent;
+ int prev_level;
+};
+
+struct ab8500_fg_flags {
+ bool fg_enabled;
+ bool conv_done;
+ bool charging;
+ bool fully_charged;
+ bool low_bat_delay;
+ bool low_bat;
+ bool bat_ovv;
+ bool batt_unknown;
+ bool calibrate;
+};
+
+/**
+ * struct ab8500_fg - ab8500 FG device information
+ * @dev: Pointer to the structure device
+ * @vbat: Battery voltage in mV
+ * @vbat_nom: Nominal battery voltage in mV
+ * @inst_curr: Instantenous battery current in mA
+ * @avg_curr: Average battery current in mA
+ * @fg_samples: Number of samples used in the FG accumulation
+ * @accu_charge: Accumulated charge from the last conversion
+ * @recovery_cnt: Counter for recovery mode
+ * @high_curr_cnt: Counter for high current mode
+ * @init_cnt: Counter for init mode
+ * @recovery_needed: Indicate if recovery is needed
+ * @high_curr_mode: Indicate if we're in high current mode
+ * @init_capacity: Indicate if initial capacity measuring should be done
+ * @calib_state State during offset calibration
+ * @discharge_state: Current discharge state
+ * @charge_state: Current charge state
+ * @flags: Structure for information about events triggered
+ * @bat_cap: Structure for battery capacity specific parameters
+ * @avg_cap: Average capacity filter
+ * @parent: Pointer to the struct ab8500
+ * @gpadc: Pointer to the struct gpadc
+ * @pdata: Pointer to the ab8500_fg platform data
+ * @bat: Pointer to the ab8500_bm platform data
+ * @fg_psy: Structure that holds the FG specific battery properties
+ * @fg_wq: Work queue for running the FG algorithm
+ * @fg_periodic_work: Work to run the FG algorithm periodically
+ * @fg_low_bat_work: Work to check low bat condition
+ * @fg_work: Work to run the FG algorithm instantly
+ * @fg_acc_cur_work: Work to read the FG accumulator
+ * @cc_lock: Mutex for locking the CC
+ */
+struct ab8500_fg {
+ struct device *dev;
+ int vbat;
+ int vbat_nom;
+ int inst_curr;
+ int avg_curr;
+ int fg_samples;
+ int accu_charge;
+ int recovery_cnt;
+ int high_curr_cnt;
+ int init_cnt;
+ bool recovery_needed;
+ bool high_curr_mode;
+ bool init_capacity;
+ enum ab8500_fg_calibration_state calib_state;
+ enum ab8500_fg_discharge_state discharge_state;
+ enum ab8500_fg_charge_state charge_state;
+ struct ab8500_fg_flags flags;
+ struct ab8500_fg_battery_capacity bat_cap;
+ struct ab8500_fg_avg_cap avg_cap;
+ struct ab8500 *parent;
+ struct ab8500_gpadc *gpadc;
+ struct ab8500_fg_platform_data *pdata;
+ struct ab8500_bm_data *bat;
+ struct power_supply fg_psy;
+ struct workqueue_struct *fg_wq;
+ struct delayed_work fg_periodic_work;
+ struct delayed_work fg_low_bat_work;
+ struct work_struct fg_work;
+ struct work_struct fg_acc_cur_work;
+ struct mutex cc_lock;
+};
+
+/* Main battery properties */
+static enum power_supply_property ab8500_fg_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_FULL,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+};
+
+/*
+ * This array maps the raw hex value to lowbat voltage used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_fg_lowbat_voltage_map[] = {
+ 2300 ,
+ 2325 ,
+ 2350 ,
+ 2375 ,
+ 2400 ,
+ 2425 ,
+ 2450 ,
+ 2475 ,
+ 2500 ,
+ 2525 ,
+ 2550 ,
+ 2575 ,
+ 2600 ,
+ 2625 ,
+ 2650 ,
+ 2675 ,
+ 2700 ,
+ 2725 ,
+ 2750 ,
+ 2775 ,
+ 2800 ,
+ 2825 ,
+ 2850 ,
+ 2875 ,
+ 2900 ,
+ 2925 ,
+ 2950 ,
+ 2975 ,
+ 3000 ,
+ 3025 ,
+ 3050 ,
+ 3075 ,
+ 3100 ,
+ 3125 ,
+ 3150 ,
+ 3175 ,
+ 3200 ,
+ 3225 ,
+ 3250 ,
+ 3275 ,
+ 3300 ,
+ 3325 ,
+ 3350 ,
+ 3375 ,
+ 3400 ,
+ 3425 ,
+ 3450 ,
+ 3475 ,
+ 3500 ,
+ 3525 ,
+ 3550 ,
+ 3575 ,
+ 3600 ,
+ 3625 ,
+ 3650 ,
+ 3675 ,
+ 3700 ,
+ 3725 ,
+ 3750 ,
+ 3775 ,
+ 3800 ,
+ 3825 ,
+ 3850 ,
+ 3850 ,
+};
+
+static u8 ab8500_volt_to_regval(int voltage)
+{
+ int i;
+
+ if (voltage < ab8500_fg_lowbat_voltage_map[0])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_fg_lowbat_voltage_map); i++) {
+ if (voltage < ab8500_fg_lowbat_voltage_map[i])
+ return (u8) i - 1;
+ }
+
+ /* If not captured above, return index of last element */
+ return (u8) ARRAY_SIZE(ab8500_fg_lowbat_voltage_map) - 1;
+}
+
+/**
+ * ab8500_fg_is_low_curr() - Low or high current mode
+ * @di: pointer to the ab8500_fg structure
+ * @curr: the current to base or our decision on
+ *
+ * Low current mode if the current consumption is below a certain threshold
+ */
+static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr)
+{
+ /*
+ * We want to know if we're in low current mode
+ */
+ if (curr > -di->bat->fg_params->high_curr_threshold)
+ return true;
+ else
+ return false;
+}
+
+/**
+ * ab8500_fg_add_cap_sample() - Add capacity to average filter
+ * @di: pointer to the ab8500_fg structure
+ * @sample: the capacity in mAh to add to the filter
+ *
+ * A capacity is added to the filter and a new mean capacity is calculated and
+ * returned
+ */
+static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample)
+{
+ struct timespec ts;
+ struct ab8500_fg_avg_cap *avg = &di->avg_cap;
+
+ getnstimeofday(&ts);
+
+ do {
+ avg->sum += sample - avg->samples[avg->pos];
+ avg->samples[avg->pos] = sample;
+ avg->time_stamps[avg->pos] = ts.tv_sec;
+ avg->pos++;
+
+ if (avg->pos == NBR_AVG_SAMPLES)
+ avg->pos = 0;
+
+ if (avg->nbr_samples < NBR_AVG_SAMPLES)
+ avg->nbr_samples++;
+
+ /*
+ * Check the time stamp for each sample. If too old,
+ * replace with latest sample
+ */
+ } while (ts.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
+
+ avg->avg = avg->sum / avg->nbr_samples;
+
+ return avg->avg;
+}
+
+/**
+ * ab8500_fg_fill_cap_sample() - Fill average filter
+ * @di: pointer to the ab8500_fg structure
+ * @sample: the capacity in mAh to fill the filter with
+ *
+ * The capacity filter is filled with a capacity in mAh
+ */
+static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample)
+{
+ int i;
+ struct timespec ts;
+ struct ab8500_fg_avg_cap *avg = &di->avg_cap;
+
+ getnstimeofday(&ts);
+
+ for (i = 0; i < NBR_AVG_SAMPLES; i++) {
+ avg->samples[i] = sample;
+ avg->time_stamps[i] = ts.tv_sec;
+ }
+
+ avg->pos = 0;
+ avg->nbr_samples = NBR_AVG_SAMPLES;
+ avg->sum = sample * NBR_AVG_SAMPLES;
+ avg->avg = sample;
+}
+
+/**
+ * ab8500_fg_coulomb_counter() - enable coulomb counter
+ * @di: pointer to the ab8500_fg structure
+ * @enable: enable/disable
+ *
+ * Enable/Disable coulomb counter.
+ * On failure returns negative value.
+ */
+static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
+{
+ int ret = 0;
+ mutex_lock(&di->cc_lock);
+ if (enable) {
+ /* To be able to reprogram the number of samples, we have to
+ * first stop the CC and then enable it again */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG, 0x00);
+ if (ret)
+ goto cc_err;
+
+ /* Program the samples */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
+ di->fg_samples);
+ if (ret)
+ goto cc_err;
+
+ /* Start the CC */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG,
+ (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
+ if (ret)
+ goto cc_err;
+
+ di->flags.fg_enabled = true;
+ } else {
+ /* Clear any pending read requests */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
+ if (ret)
+ goto cc_err;
+
+ /* Stop the CC */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG, 0);
+ if (ret)
+ goto cc_err;
+
+ di->flags.fg_enabled = false;
+
+ }
+ dev_dbg(di->dev, " CC enabled: %d Samples: %d\n",
+ enable, di->fg_samples);
+
+ mutex_unlock(&di->cc_lock);
+
+ return ret;
+cc_err:
+ dev_err(di->dev, "%s Enabling coulomb counter failed\n", __func__);
+ mutex_unlock(&di->cc_lock);
+ return ret;
+}
+
+/**
+ * ab8500_fg_inst_curr() - battery instantaneous current
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Returns battery instantenous current(on success) else error code
+ */
+static int ab8500_fg_inst_curr(struct ab8500_fg *di)
+{
+ u8 low, high, reg_val;
+ static int val;
+ int ret = 0;
+ bool fg_off = false;
+
+ mutex_lock(&di->cc_lock);
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG, &reg_val);
+ if (ret < 0)
+ goto inst_curr_err;
+
+ if (!(reg_val & CC_PWR_UP_ENA)) {
+ dev_dbg(di->dev, "%s Enable FG\n", __func__);
+ fg_off = true;
+
+ /* Program the samples */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
+ SEC_TO_SAMPLE(10));
+ if (ret)
+ goto inst_curr_err;
+
+ /* Start the CC */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG,
+ (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
+ if (ret)
+ goto inst_curr_err;
+ }
+
+ /* Reset counter and Read request */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_CTRL_REG, (RESET_ACCU | READ_REQ));
+ if (ret)
+ goto inst_curr_err;
+
+ /*
+ * Since there is no interrupt for this, just wait for 250ms
+ * 250ms is one sample conversion time with 32.768 Khz RTC clock
+ */
+ msleep(250);
+
+ /* Read CC Sample conversion value Low and high */
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_SMPL_CNVL_REG, &low);
+ if (ret < 0)
+ goto inst_curr_err;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_SMPL_CNVH_REG, &high);
+ if (ret < 0)
+ goto inst_curr_err;
+
+ /*
+ * negative value for Discharging
+ * convert 2's compliment into decimal
+ */
+ if (high & 0x10)
+ val = (low | (high << 8) | 0xFFFFE000);
+ else
+ val = (low | (high << 8));
+
+ /*
+ * Convert to unit value in mA
+ * Full scale input voltage is
+ * 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA
+ * resistance is in mOhm
+ */
+ val = ((val * 66660) / (4096 * di->bat->fg_res));
+
+ if (fg_off) {
+ dev_dbg(di->dev, "%s Disable FG\n", __func__);
+
+ /* Clear any pending read requests */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
+ if (ret)
+ goto inst_curr_err;
+
+ /* Stop the CC */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG, 0);
+ if (ret)
+ goto inst_curr_err;
+ }
+
+ mutex_unlock(&di->cc_lock);
+
+ return val;
+
+inst_curr_err:
+ dev_err(di->dev, "%s Get instanst current failed\n", __func__);
+ mutex_unlock(&di->cc_lock);
+ return ret;
+}
+
+/**
+ * ab8500_fg_acc_cur_work() - average battery current
+ * @work: pointer to the work_struct structure
+ *
+ * Updated the average battery current obtained from the
+ * coulomb counter.
+ */
+static void ab8500_fg_acc_cur_work(struct work_struct *work)
+{
+ int val;
+ int ret;
+ u8 low, med, high;
+
+ struct ab8500_fg *di = container_of(work,
+ struct ab8500_fg, fg_acc_cur_work);
+
+ mutex_lock(&di->cc_lock);
+ ret = abx500_set_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_CTRL, RD_NCONV_ACCU_REQ);
+ if (ret)
+ goto exit;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_LOW, &low);
+ if (ret < 0)
+ goto exit;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_MED, &med);
+ if (ret < 0)
+ goto exit;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_HIGH, &high);
+ if (ret < 0)
+ goto exit;
+
+ /* Check for sign bit in case of negative value, 2's compliment */
+ if (high & 0x10)
+ val = (low | (med << 8) | (high << 16) | 0xFFE00000);
+ else
+ val = (low | (med << 8) | (high << 16));
+
+ di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X10)/10000;
+
+ di->avg_curr = (val * FG_LSB_IN_MA) / (di->fg_samples * 1000);
+ di->flags.conv_done = true;
+
+ mutex_unlock(&di->cc_lock);
+
+ queue_work(di->fg_wq, &di->fg_work);
+
+ return;
+exit:
+ dev_err(di->dev,
+ "Failed to read or write gas gauge registers\n");
+ mutex_unlock(&di->cc_lock);
+ queue_work(di->fg_wq,
+ &di->fg_work);
+}
+
+/**
+ * ab8500_fg_bat_voltage() - get battery voltage
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Returns battery voltage(on success) else error code
+ */
+static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
+{
+ int vbat;
+ static int prev;
+
+ vbat = ab8500_gpadc_convert(di->gpadc, MAIN_BAT_V);
+ if (vbat < 0) {
+ dev_err(di->dev,
+ "%s gpadc conversion failed, using previous value\n",
+ __func__);
+ return prev;
+ }
+
+ prev = vbat;
+ return vbat;
+}
+
+/**
+ * ab8500_fg_volt_to_capacity() - Voltage based capacity
+ * @di: pointer to the ab8500_fg structure
+ * @voltage: The voltage to convert to a capacity
+ *
+ * Returns battery capacity in per mille based on voltage
+ */
+static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
+{
+ int i, tbl_size;
+ struct v_to_cap *tbl;
+ int cap = 0;
+
+ tbl = di->bat->bat_type[di->bat->batt_id].v_to_cap_tbl,
+ tbl_size = di->bat->bat_type[di->bat->batt_id].n_v_cap_tbl_elements;
+
+ for (i = 0; i < tbl_size; ++i) {
+ if (voltage > tbl[i].voltage)
+ break;
+ }
+
+ if ((i > 0) && (i < tbl_size)) {
+ cap = interpolate(voltage,
+ tbl[i].voltage,
+ tbl[i].capacity * 10,
+ tbl[i-1].voltage,
+ tbl[i-1].capacity * 10);
+ } else if (i == 0) {
+ cap = 1000;
+ } else {
+ cap = 0;
+ }
+
+ dev_dbg(di->dev, "%s Vbat: %d, Cap: %d per mille",
+ __func__, voltage, cap);
+
+ return cap;
+}
+
+/**
+ * ab8500_fg_uncomp_volt_to_capacity() - Uncompensated voltage based capacity
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Returns battery capacity based on battery voltage that is not compensated
+ * for the voltage drop due to the load
+ */
+static int ab8500_fg_uncomp_volt_to_capacity(struct ab8500_fg *di)
+{
+ di->vbat = ab8500_fg_bat_voltage(di);
+ return ab8500_fg_volt_to_capacity(di, di->vbat);
+}
+
+/**
+ * ab8500_fg_load_comp_volt_to_capacity() - Load compensated voltage based capacity
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Returns battery capacity based on battery voltage that is load compensated
+ * for the voltage drop
+ */
+static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di)
+{
+ int vbat_comp;
+
+ di->inst_curr = ab8500_fg_inst_curr(di);
+ di->vbat = ab8500_fg_bat_voltage(di);
+
+ /* Use Ohms law to get the load compensated voltage */
+ vbat_comp = di->vbat - (di->inst_curr *
+ di->bat->bat_type[di->bat->batt_id].battery_resistance) / 1000;
+
+ dev_dbg(di->dev, "%s Measured Vbat: %dmV,Compensated Vbat %dmV, "
+ "R: %dmOhm, Current: %dmA\n",
+ __func__,
+ di->vbat,
+ vbat_comp,
+ di->bat->bat_type[di->bat->batt_id].battery_resistance,
+ di->inst_curr);
+
+ return ab8500_fg_volt_to_capacity(di, vbat_comp);
+}
+
+/**
+ * ab8500_fg_convert_mah_to_permille() - Capacity in mAh to permille
+ * @di: pointer to the ab8500_fg structure
+ * @cap_mah: capacity in mAh
+ *
+ * Converts capacity in mAh to capacity in permille
+ */
+static int ab8500_fg_convert_mah_to_permille(struct ab8500_fg *di, int cap_mah)
+{
+ return (cap_mah * 1000) / di->bat_cap.max_mah_design;
+}
+
+/**
+ * ab8500_fg_convert_permille_to_mah() - Capacity in permille to mAh
+ * @di: pointer to the ab8500_fg structure
+ * @cap_pm: capacity in permille
+ *
+ * Converts capacity in permille to capacity in mAh
+ */
+static int ab8500_fg_convert_permille_to_mah(struct ab8500_fg *di, int cap_pm)
+{
+ return cap_pm * di->bat_cap.max_mah_design / 1000;
+}
+
+/**
+ * ab8500_fg_convert_mah_to_uwh() - Capacity in mAh to uWh
+ * @di: pointer to the ab8500_fg structure
+ * @cap_mah: capacity in mAh
+ *
+ * Converts capacity in mAh to capacity in uWh
+ */
+static int ab8500_fg_convert_mah_to_uwh(struct ab8500_fg *di, int cap_mah)
+{
+ u64 div_res;
+ u32 div_rem;
+
+ div_res = ((u64) cap_mah) * ((u64) di->vbat_nom);
+ div_rem = do_div(div_res, 1000);
+
+ /* Make sure to round upwards if necessary */
+ if (div_rem >= 1000 / 2)
+ div_res++;
+
+ return (int) div_res;
+}
+
+/**
+ * ab8500_fg_calc_cap_charging() - Calculate remaining capacity while charging
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return the capacity in mAh based on previous calculated capcity and the FG
+ * accumulator register value. The filter is filled with this capacity
+ */
+static int ab8500_fg_calc_cap_charging(struct ab8500_fg *di)
+{
+ dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
+ __func__,
+ di->bat_cap.mah,
+ di->accu_charge);
+
+ /* Capacity should not be less than 0 */
+ if (di->bat_cap.mah + di->accu_charge > 0)
+ di->bat_cap.mah += di->accu_charge;
+ else
+ di->bat_cap.mah = 0;
+
+ /*
+ * We force capacity to 100% as long as the algorithm
+ * reports that it's full.
+ */
+ if (di->bat_cap.mah >= di->bat_cap.max_mah_design ||
+ di->flags.fully_charged)
+ di->bat_cap.mah = di->bat_cap.max_mah_design;
+
+ ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+ di->bat_cap.permille =
+ ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+
+ /* We need to update battery voltage and inst current when charging */
+ di->vbat = ab8500_fg_bat_voltage(di);
+ di->inst_curr = ab8500_fg_inst_curr(di);
+
+ return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_calc_cap_discharge_voltage() - Capacity in discharge with voltage
+ * @di: pointer to the ab8500_fg structure
+ * @comp: if voltage should be load compensated before capacity calc
+ *
+ * Return the capacity in mAh based on the battery voltage. The voltage can
+ * either be load compensated or not. This value is added to the filter and a
+ * new mean value is calculated and returned.
+ */
+static int ab8500_fg_calc_cap_discharge_voltage(struct ab8500_fg *di, bool comp)
+{
+ int permille, mah;
+
+ if (comp)
+ permille = ab8500_fg_load_comp_volt_to_capacity(di);
+ else
+ permille = ab8500_fg_uncomp_volt_to_capacity(di);
+
+ mah = ab8500_fg_convert_permille_to_mah(di, permille);
+
+ di->bat_cap.mah = ab8500_fg_add_cap_sample(di, mah);
+ di->bat_cap.permille =
+ ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+
+ return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_calc_cap_discharge_fg() - Capacity in discharge with FG
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return the capacity in mAh based on previous calculated capcity and the FG
+ * accumulator register value. This value is added to the filter and a
+ * new mean value is calculated and returned.
+ */
+static int ab8500_fg_calc_cap_discharge_fg(struct ab8500_fg *di)
+{
+ int permille_volt, permille;
+
+ dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
+ __func__,
+ di->bat_cap.mah,
+ di->accu_charge);
+
+ /* Capacity should not be less than 0 */
+ if (di->bat_cap.mah + di->accu_charge > 0)
+ di->bat_cap.mah += di->accu_charge;
+ else
+ di->bat_cap.mah = 0;
+
+ if (di->bat_cap.mah >= di->bat_cap.max_mah_design)
+ di->bat_cap.mah = di->bat_cap.max_mah_design;
+
+ /*
+ * Check against voltage based capacity. It can not be lower
+ * than what the uncompensated voltage says
+ */
+ permille = ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+ permille_volt = ab8500_fg_uncomp_volt_to_capacity(di);
+
+ if (permille < permille_volt) {
+ di->bat_cap.permille = permille_volt;
+ di->bat_cap.mah = ab8500_fg_convert_permille_to_mah(di,
+ di->bat_cap.permille);
+
+ dev_dbg(di->dev, "%s voltage based: perm %d perm_volt %d\n",
+ __func__,
+ permille,
+ permille_volt);
+
+ ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+ } else {
+ ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+ di->bat_cap.permille =
+ ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+ }
+
+ return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_capacity_level() - Get the battery capacity level
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Get the battery capacity level based on the capacity in percent
+ */
+static int ab8500_fg_capacity_level(struct ab8500_fg *di)
+{
+ int ret, percent;
+
+ percent = di->bat_cap.permille / 10;
+
+ if (percent <= di->bat->cap_levels->critical ||
+ di->flags.low_bat)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+ else if (percent <= di->bat->cap_levels->low)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+ else if (percent <= di->bat->cap_levels->normal)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+ else if (percent <= di->bat->cap_levels->high)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+ else
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_check_capacity_limits() - Check if capacity has changed
+ * @di: pointer to the ab8500_fg structure
+ * @init: capacity is allowed to go up in init mode
+ *
+ * Check if capacity or capacity limit has changed and notify the system
+ * about it using the power_supply framework
+ */
+static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
+{
+ bool changed = false;
+
+ di->bat_cap.level = ab8500_fg_capacity_level(di);
+
+ if (di->bat_cap.level != di->bat_cap.prev_level) {
+ /*
+ * We do not allow reported capacity level to go up
+ * unless we're charging or if we're in init
+ */
+ if (!(!di->flags.charging && di->bat_cap.level >
+ di->bat_cap.prev_level) || init) {
+ dev_dbg(di->dev, "level changed from %d to %d\n",
+ di->bat_cap.prev_level,
+ di->bat_cap.level);
+ di->bat_cap.prev_level = di->bat_cap.level;
+ changed = true;
+ } else {
+ dev_dbg(di->dev, "level not allowed to go up "
+ "since no charger is connected: %d to %d\n",
+ di->bat_cap.prev_level,
+ di->bat_cap.level);
+ }
+ }
+
+ /*
+ * If we have received the LOW_BAT IRQ, set capacity to 0 to initiate
+ * shutdown
+ */
+ if (di->flags.low_bat) {
+ dev_dbg(di->dev, "Battery low, set capacity to 0\n");
+ di->bat_cap.prev_percent = 0;
+ di->bat_cap.permille = 0;
+ di->bat_cap.prev_mah = 0;
+ di->bat_cap.mah = 0;
+ changed = true;
+ } else if (di->bat_cap.prev_percent != di->bat_cap.permille / 10) {
+ if (di->bat_cap.permille / 10 == 0) {
+ /*
+ * We will not report 0% unless we've got
+ * the LOW_BAT IRQ, no matter what the FG
+ * algorithm says.
+ */
+ di->bat_cap.prev_percent = 1;
+ di->bat_cap.permille = 1;
+ di->bat_cap.prev_mah = 1;
+ di->bat_cap.mah = 1;
+
+ changed = true;
+ } else if (!(!di->flags.charging &&
+ (di->bat_cap.permille / 10) >
+ di->bat_cap.prev_percent) || init) {
+ /*
+ * We do not allow reported capacity to go up
+ * unless we're charging or if we're in init
+ */
+ dev_dbg(di->dev,
+ "capacity changed from %d to %d (%d)\n",
+ di->bat_cap.prev_percent,
+ di->bat_cap.permille / 10,
+ di->bat_cap.permille);
+ di->bat_cap.prev_percent = di->bat_cap.permille / 10;
+ di->bat_cap.prev_mah = di->bat_cap.mah;
+
+ changed = true;
+ } else {
+ dev_dbg(di->dev, "capacity not allowed to go up since "
+ "no charger is connected: %d to %d (%d)\n",
+ di->bat_cap.prev_percent,
+ di->bat_cap.permille / 10,
+ di->bat_cap.permille);
+ }
+ }
+
+ if (changed)
+ power_supply_changed(&di->fg_psy);
+
+}
+
+static void ab8500_fg_charge_state_to(struct ab8500_fg *di,
+ enum ab8500_fg_charge_state new_state)
+{
+ dev_dbg(di->dev, "Charge state from %d [%s] to %d [%s]\n",
+ di->charge_state,
+ charge_state[di->charge_state],
+ new_state,
+ charge_state[new_state]);
+
+ di->charge_state = new_state;
+}
+
+static void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
+ enum ab8500_fg_charge_state new_state)
+{
+ dev_dbg(di->dev, "Disharge state from %d [%s] to %d [%s]\n",
+ di->discharge_state,
+ discharge_state[di->discharge_state],
+ new_state,
+ discharge_state[new_state]);
+
+ di->discharge_state = new_state;
+}
+
+/**
+ * ab8500_fg_algorithm_charging() - FG algorithm for when charging
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Battery capacity calculation state machine for when we're charging
+ */
+static void ab8500_fg_algorithm_charging(struct ab8500_fg *di)
+{
+ /*
+ * If we change to discharge mode
+ * we should start with recovery
+ */
+ if (di->discharge_state != AB8500_FG_DISCHARGE_INIT_RECOVERY)
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_INIT_RECOVERY);
+
+ switch (di->charge_state) {
+ case AB8500_FG_CHARGE_INIT:
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_charging);
+
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_READOUT);
+
+ break;
+
+ case AB8500_FG_CHARGE_READOUT:
+ /*
+ * Read the FG and calculate the new capacity
+ */
+ mutex_lock(&di->cc_lock);
+ if (!di->flags.conv_done) {
+ /* Wasn't the CC IRQ that got us here */
+ mutex_unlock(&di->cc_lock);
+ dev_dbg(di->dev, "%s CC conv not done\n",
+ __func__);
+
+ break;
+ }
+ di->flags.conv_done = false;
+ mutex_unlock(&di->cc_lock);
+
+ ab8500_fg_calc_cap_charging(di);
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* Check capacity limits */
+ ab8500_fg_check_capacity_limits(di, false);
+}
+
+/**
+ * ab8500_fg_algorithm_discharging() - FG algorithm for when discharging
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Battery capacity calculation state machine for when we're discharging
+ */
+static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
+{
+ int sleep_time;
+
+ /* If we change to charge mode we should start with init */
+ if (di->charge_state != AB8500_FG_CHARGE_INIT)
+ ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+
+ switch (di->discharge_state) {
+ case AB8500_FG_DISCHARGE_INIT:
+ /* We use the FG IRQ to work on */
+ di->init_cnt = 0;
+ di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_INITMEASURING);
+
+ /* Intentional fallthrough */
+ case AB8500_FG_DISCHARGE_INITMEASURING:
+ /*
+ * Discard a number of samples during startup.
+ * After that, use compensated voltage for a few
+ * samples to get an initial capacity.
+ * Then go to READOUT
+ */
+ sleep_time = di->bat->fg_params->init_timer;
+
+ /* Discard the first [x] seconds */
+ if (di->init_cnt >
+ di->bat->fg_params->init_discard_time) {
+
+ ab8500_fg_calc_cap_discharge_voltage(di, true);
+
+ ab8500_fg_check_capacity_limits(di, true);
+ }
+
+ di->init_cnt += sleep_time;
+ if (di->init_cnt >
+ di->bat->fg_params->init_total_time) {
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT);
+ }
+
+ break;
+
+ case AB8500_FG_DISCHARGE_INIT_RECOVERY:
+ di->recovery_cnt = 0;
+ di->recovery_needed = true;
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_RECOVERY);
+
+ /* Intentional fallthrough */
+
+ case AB8500_FG_DISCHARGE_RECOVERY:
+ sleep_time = di->bat->fg_params->recovery_sleep_timer;
+
+ /*
+ * We should check the power consumption
+ * If low, go to READOUT (after x min) or
+ * RECOVERY_SLEEP if time left.
+ * If high, go to READOUT
+ */
+ di->inst_curr = ab8500_fg_inst_curr(di);
+
+ if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
+ if (di->recovery_cnt >
+ di->bat->fg_params->recovery_total_time) {
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT);
+ di->recovery_needed = false;
+ } else {
+ queue_delayed_work(di->fg_wq,
+ &di->fg_periodic_work,
+ sleep_time * HZ);
+ }
+ di->recovery_cnt += sleep_time;
+ } else {
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT);
+ }
+
+ break;
+
+ case AB8500_FG_DISCHARGE_READOUT:
+ di->inst_curr = ab8500_fg_inst_curr(di);
+
+ if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
+ /* Detect mode change */
+ if (di->high_curr_mode) {
+ di->high_curr_mode = false;
+ di->high_curr_cnt = 0;
+ }
+
+ if (di->recovery_needed) {
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_RECOVERY);
+
+ queue_delayed_work(di->fg_wq,
+ &di->fg_periodic_work,
+ 0);
+
+ break;
+ }
+
+ ab8500_fg_calc_cap_discharge_voltage(di, true);
+ } else {
+ mutex_lock(&di->cc_lock);
+ if (!di->flags.conv_done) {
+ /* Wasn't the CC IRQ that got us here */
+ mutex_unlock(&di->cc_lock);
+ dev_dbg(di->dev, "%s CC conv not done\n",
+ __func__);
+
+ break;
+ }
+ di->flags.conv_done = false;
+ mutex_unlock(&di->cc_lock);
+
+ /* Detect mode change */
+ if (!di->high_curr_mode) {
+ di->high_curr_mode = true;
+ di->high_curr_cnt = 0;
+ }
+
+ di->high_curr_cnt +=
+ di->bat->fg_params->accu_high_curr;
+ if (di->high_curr_cnt >
+ di->bat->fg_params->high_curr_time)
+ di->recovery_needed = true;
+
+ ab8500_fg_calc_cap_discharge_fg(di);
+ }
+
+ ab8500_fg_check_capacity_limits(di, false);
+
+ break;
+
+ case AB8500_FG_DISCHARGE_WAKEUP:
+ ab8500_fg_coulomb_counter(di, true);
+ di->inst_curr = ab8500_fg_inst_curr(di);
+
+ ab8500_fg_calc_cap_discharge_voltage(di, true);
+
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+ /* Re-program number of samples set above */
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_READOUT);
+
+ ab8500_fg_check_capacity_limits(di, false);
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * ab8500_fg_algorithm_calibrate() - Internal columb counter offset calibration
+ * @di: pointer to the ab8500_fg structure
+ *
+ */
+static void ab8500_fg_algorithm_calibrate(struct ab8500_fg *di)
+{
+ int ret;
+
+ switch (di->calib_state) {
+ case AB8500_FG_CALIB_INIT:
+ dev_dbg(di->dev, "Calibration ongoing...\n");
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_INT_CAL_N_AVG_MASK, CC_INT_CAL_SAMPLES_8);
+ if (ret < 0)
+ goto err;
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_INTAVGOFFSET_ENA, CC_INTAVGOFFSET_ENA);
+ if (ret < 0)
+ goto err;
+ di->calib_state = AB8500_FG_CALIB_WAIT;
+ break;
+ case AB8500_FG_CALIB_END:
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_MUXOFFSET, CC_MUXOFFSET);
+ if (ret < 0)
+ goto err;
+ di->flags.calibrate = false;
+ dev_dbg(di->dev, "Calibration done...\n");
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ break;
+ case AB8500_FG_CALIB_WAIT:
+ dev_dbg(di->dev, "Calibration WFI\n");
+ default:
+ break;
+ }
+ return;
+err:
+ /* Something went wrong, don't calibrate then */
+ dev_err(di->dev, "failed to calibrate the CC\n");
+ di->flags.calibrate = false;
+ di->calib_state = AB8500_FG_CALIB_INIT;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+}
+
+/**
+ * ab8500_fg_algorithm() - Entry point for the FG algorithm
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Entry point for the battery capacity calculation state machine
+ */
+static void ab8500_fg_algorithm(struct ab8500_fg *di)
+{
+ if (di->flags.calibrate)
+ ab8500_fg_algorithm_calibrate(di);
+ else {
+ if (di->flags.charging)
+ ab8500_fg_algorithm_charging(di);
+ else
+ ab8500_fg_algorithm_discharging(di);
+ }
+
+ dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d\n",
+ di->bat_cap.max_mah_design,
+ di->bat_cap.mah,
+ di->bat_cap.permille,
+ di->bat_cap.level,
+ di->bat_cap.prev_mah,
+ di->bat_cap.prev_percent,
+ di->bat_cap.prev_level,
+ di->vbat,
+ di->inst_curr,
+ di->avg_curr,
+ di->accu_charge,
+ di->flags.charging,
+ di->charge_state,
+ di->discharge_state,
+ di->high_curr_mode,
+ di->recovery_needed);
+}
+
+/**
+ * ab8500_fg_periodic_work() - Run the FG state machine periodically
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for periodic work
+ */
+static void ab8500_fg_periodic_work(struct work_struct *work)
+{
+ struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+ fg_periodic_work.work);
+
+ if (di->init_capacity) {
+ /* A dummy read that will return 0 */
+ di->inst_curr = ab8500_fg_inst_curr(di);
+ /* Get an initial capacity calculation */
+ ab8500_fg_calc_cap_discharge_voltage(di, true);
+ ab8500_fg_check_capacity_limits(di, true);
+ di->init_capacity = false;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ } else
+ ab8500_fg_algorithm(di);
+}
+
+/**
+ * ab8500_fg_low_bat_work() - Check LOW_BAT condition
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the LOW_BAT condition
+ */
+static void ab8500_fg_low_bat_work(struct work_struct *work)
+{
+ int vbat;
+
+ struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+ fg_low_bat_work.work);
+
+ vbat = ab8500_fg_bat_voltage(di);
+
+ /* Check if LOW_BAT still fulfilled */
+ if (vbat < di->bat->fg_params->lowbat_threshold) {
+ di->flags.low_bat = true;
+ dev_warn(di->dev, "Battery voltage still LOW\n");
+
+ /*
+ * We need to re-schedule this check to be able to detect
+ * if the voltage increases again during charging
+ */
+ queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+ round_jiffies(LOW_BAT_CHECK_INTERVAL));
+ } else {
+ di->flags.low_bat = false;
+ dev_warn(di->dev, "Battery voltage OK again\n");
+ }
+
+ /* This is needed to dispatch LOW_BAT */
+ ab8500_fg_check_capacity_limits(di, false);
+
+ /* Set this flag to check if LOW_BAT IRQ still occurs */
+ di->flags.low_bat_delay = false;
+}
+
+/**
+ * ab8500_fg_instant_work() - Run the FG state machine instantly
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for instant work
+ */
+static void ab8500_fg_instant_work(struct work_struct *work)
+{
+ struct ab8500_fg *di = container_of(work, struct ab8500_fg, fg_work);
+
+ ab8500_fg_algorithm(di);
+}
+
+/**
+ * ab8500_fg_cc_convend_handler() - isr to get battery avg current.
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_cc_int_calib_handler(int irq, void *_di)
+{
+ struct ab8500_fg *di = _di;
+ di->calib_state = AB8500_FG_CALIB_END;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_cc_convend_handler() - isr to get battery avg current.
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_cc_convend_handler(int irq, void *_di)
+{
+ struct ab8500_fg *di = _di;
+
+ queue_work(di->fg_wq, &di->fg_acc_cur_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_batt_ovv_handler() - Battery OVV occured
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di)
+{
+ struct ab8500_fg *di = _di;
+
+ dev_dbg(di->dev, "Battery OVV\n");
+ di->flags.bat_ovv = true;
+
+ power_supply_changed(&di->fg_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_lowbatf_handler() - Battery voltage is below LOW threshold
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_lowbatf_handler(int irq, void *_di)
+{
+ struct ab8500_fg *di = _di;
+
+ if (!di->flags.low_bat_delay) {
+ dev_warn(di->dev, "Battery voltage is below LOW threshold\n");
+ di->flags.low_bat_delay = true;
+ /*
+ * Start a timer to check LOW_BAT again after some time
+ * This is done to avoid shutdown on single voltage dips
+ */
+ queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+ round_jiffies(LOW_BAT_CHECK_INTERVAL));
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_get_property() - get the fg properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the
+ * fg properties by reading the sysfs files.
+ * voltage_now: battery voltage
+ * current_now: battery instant current
+ * current_avg: battery average current
+ * charge_full_design: capacity where battery is considered full
+ * charge_now: battery capacity in nAh
+ * capacity: capacity in percent
+ * capacity_level: capacity level
+ *
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_fg_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab8500_fg *di;
+
+ di = to_ab8500_fg_device_info(psy);
+
+ /*
+ * If battery is identified as unknown and charging of unknown
+ * batteries is disabled, we always report 100% capacity and
+ * capacity level UNKNOWN, since we can't calculate
+ * remaining capacity
+ */
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (di->flags.bat_ovv)
+ val->intval = 47500000;
+ else
+ val->intval = di->vbat * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = di->inst_curr * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = di->avg_curr * 1000;
+ break;
+ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+ val->intval = ab8500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.max_mah_design);
+ break;
+ case POWER_SUPPLY_PROP_ENERGY_FULL:
+ val->intval = ab8500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.max_mah);
+ break;
+ case POWER_SUPPLY_PROP_ENERGY_NOW:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat)
+ val->intval = ab8500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.max_mah);
+ else
+ val->intval = ab8500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.prev_mah);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = di->bat_cap.max_mah_design;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = di->bat_cap.max_mah;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat)
+ val->intval = di->bat_cap.max_mah;
+ else
+ val->intval = di->bat_cap.prev_mah;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat)
+ val->intval = 100;
+ else
+ val->intval = di->bat_cap.prev_percent;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+ else
+ val->intval = di->bat_cap.prev_level;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
+{
+ struct power_supply *psy;
+ struct power_supply *ext;
+ struct ab8500_fg *di;
+ union power_supply_propval ret;
+ int i, j;
+ bool psy_found = false;
+
+ psy = (struct power_supply *)data;
+ ext = dev_get_drvdata(dev);
+ di = to_ab8500_fg_device_info(psy);
+
+ /*
+ * For all psy where the name of your driver
+ * appears in any supplied_to
+ */
+ for (i = 0; i < ext->num_supplicants; i++) {
+ if (!strcmp(ext->supplied_to[i], psy->name))
+ psy_found = true;
+ }
+
+ if (!psy_found)
+ return 0;
+
+ /* Go through all properties for the psy */
+ for (j = 0; j < ext->num_properties; j++) {
+ enum power_supply_property prop;
+ prop = ext->properties[j];
+
+ if (ext->get_property(ext, prop, &ret))
+ continue;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_STATUS:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ switch (ret.intval) {
+ case POWER_SUPPLY_STATUS_UNKNOWN:
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ case POWER_SUPPLY_STATUS_NOT_CHARGING:
+ if (!di->flags.charging)
+ break;
+ di->flags.charging = false;
+ di->flags.fully_charged = false;
+ queue_work(di->fg_wq, &di->fg_work);
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ if (di->flags.fully_charged)
+ break;
+ di->flags.fully_charged = true;
+ /* Save current capacity as maximum */
+ di->bat_cap.max_mah = di->bat_cap.mah;
+ queue_work(di->fg_wq, &di->fg_work);
+ break;
+ case POWER_SUPPLY_STATUS_CHARGING:
+ if (di->flags.charging)
+ break;
+ di->flags.charging = true;
+ di->flags.fully_charged = false;
+ queue_work(di->fg_wq, &di->fg_work);
+ break;
+ };
+ default:
+ break;
+ };
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ if (ret.intval)
+ di->flags.batt_unknown = false;
+ else
+ di->flags.batt_unknown = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ab8500_fg_init_hw_registers() - Set up FG related registers
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Set up battery OVV, low battery voltage registers
+ */
+static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
+{
+ int ret;
+
+ /* Set up VBAT OVV register */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_BATT_OVV,
+ (BATT_OVV_ENA | BATT_OVV_TH_4P75));
+ if (ret) {
+ dev_err(di->dev, "failed to set BATT_OVV\n");
+ goto out;
+ }
+
+ /* Low Battery Voltage */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_SYS_CTRL2_BLOCK,
+ AB8500_LOW_BAT_REG,
+ ab8500_volt_to_regval(
+ di->bat->fg_params->lowbat_threshold) << 1 |
+ LOW_BAT_ENABLE);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * ab8500_fg_external_power_changed() - callback for power supply changes
+ * @psy: pointer to the structure power_supply
+ *
+ * This function is the entry point of the pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in any external power
+ * supply that this driver needs to be notified of.
+ */
+static void ab8500_fg_external_power_changed(struct power_supply *psy)
+{
+ struct ab8500_fg *di = to_ab8500_fg_device_info(psy);
+
+ class_for_each_device(power_supply_class, NULL,
+ &di->fg_psy, ab8500_fg_get_ext_psy_data);
+}
+
+#if defined(CONFIG_PM)
+static int ab8500_fg_resume(struct platform_device *pdev)
+{
+ struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+ /*
+ * Change state if we're not charging. If we're charging we will wake
+ * up on the FG IRQ
+ */
+ if (!di->flags.charging) {
+ ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_WAKEUP);
+ queue_work(di->fg_wq, &di->fg_work);
+ }
+
+ return 0;
+}
+
+static int ab8500_fg_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+ flush_delayed_work(&di->fg_periodic_work);
+
+ /*
+ * If the FG is enabled we will disable it before going to suspend
+ * only if we're not charging
+ */
+ if (di->flags.fg_enabled && !di->flags.charging)
+ ab8500_fg_coulomb_counter(di, false);
+
+ return 0;
+}
+#else
+#define ab8500_fg_suspend NULL
+#define ab8500_fg_resume NULL
+#endif
+
+static int __devexit ab8500_fg_remove(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+ /* Disable coulomb counter */
+ ret = ab8500_fg_coulomb_counter(di, false);
+ if (ret)
+ dev_err(di->dev, "failed to disable coulomb counter\n");
+
+ destroy_workqueue(di->fg_wq);
+
+ flush_scheduled_work();
+ power_supply_unregister(&di->fg_psy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di);
+ return ret;
+}
+
+/* ab8500 fg driver interrupts and their respective isr */
+static struct ab8500_fg_interrupts ab8500_fg_irq[] = {
+ {"NCONV_ACCU", ab8500_fg_cc_convend_handler},
+ {"BATT_OVV", ab8500_fg_batt_ovv_handler},
+ {"LOW_BAT_F", ab8500_fg_lowbatf_handler},
+ {"CC_INT_CALIB", ab8500_fg_cc_int_calib_handler},
+};
+
+static int __devinit ab8500_fg_probe(struct platform_device *pdev)
+{
+ int i, irq;
+ struct ab8500_platform_data *plat;
+ int ret = 0;
+
+ struct ab8500_fg *di =
+ kzalloc(sizeof(struct ab8500_fg), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ mutex_init(&di->cc_lock);
+
+ /* get parent data */
+ di->dev = &pdev->dev;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
+ di->gpadc = ab8500_gpadc_get();
+
+ plat = dev_get_platdata(di->parent->dev);
+
+ /* get fg specific platform data */
+ if (!plat->fg) {
+ dev_err(di->dev, "no fg platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+ di->pdata = plat->fg;
+
+ /* get battery specific platform data */
+ if (!plat->battery) {
+ dev_err(di->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+ di->bat = plat->battery;
+
+ di->fg_psy.name = "ab8500_fg";
+ di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->fg_psy.properties = ab8500_fg_props;
+ di->fg_psy.num_properties = ARRAY_SIZE(ab8500_fg_props);
+ di->fg_psy.get_property = ab8500_fg_get_property;
+ di->fg_psy.supplied_to = di->pdata->supplied_to;
+ di->fg_psy.num_supplicants = di->pdata->num_supplicants;
+ di->fg_psy.external_power_changed = ab8500_fg_external_power_changed;
+
+ di->bat_cap.max_mah_design = MILLI_TO_MICRO *
+ di->bat->bat_type[di->bat->batt_id].charge_full_design;
+
+ di->bat_cap.max_mah = di->bat_cap.max_mah_design;
+
+ di->vbat_nom = di->bat->bat_type[di->bat->batt_id].nominal_voltage;
+
+ di->init_capacity = true;
+
+ ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+ ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
+
+ /* Create a work queue for running the FG algorithm */
+ di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq");
+ if (di->fg_wq == NULL) {
+ dev_err(di->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for running the fg algorithm instantly */
+ INIT_WORK(&di->fg_work, ab8500_fg_instant_work);
+
+ /* Init work for getting the battery accumulated current */
+ INIT_WORK(&di->fg_acc_cur_work, ab8500_fg_acc_cur_work);
+
+ /* Work delayed Queue to run the state machine */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->fg_periodic_work,
+ ab8500_fg_periodic_work);
+
+ /* Work to check low battery condition */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->fg_low_bat_work,
+ ab8500_fg_low_bat_work);
+
+ /* Initialize OVV, and other registers */
+ ret = ab8500_fg_init_hw_registers(di);
+ if (ret) {
+ dev_err(di->dev, "failed to initialize registers\n");
+ goto free_fg_wq;
+ }
+
+ /* Consider battery unknown until we're informed otherwise */
+ di->flags.batt_unknown = true;
+
+ /* Register FG power supply class */
+ ret = power_supply_register(di->dev, &di->fg_psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register FG psy\n");
+ goto free_fg_wq;
+ }
+
+ di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
+ ab8500_fg_coulomb_counter(di, true);
+
+ /* Register interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name);
+ ret = request_threaded_irq(irq, NULL, ab8500_fg_irq[i].isr,
+ IRQF_SHARED | IRQF_NO_SUSPEND,
+ ab8500_fg_irq[i].name, di);
+
+ if (ret != 0) {
+ dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+ , ab8500_fg_irq[i].name, irq, ret);
+ goto free_irq;
+ }
+ dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+ ab8500_fg_irq[i].name, irq, ret);
+ }
+
+ platform_set_drvdata(pdev, di);
+
+ /* Calibrate the fg first time */
+ di->flags.calibrate = true;
+ di->calib_state = AB8500_FG_CALIB_INIT;
+ /* Run the FG algorithm */
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+
+ return ret;
+
+free_irq:
+ power_supply_unregister(&di->fg_psy);
+
+ /* We also have to free all successfully registered irqs */
+ for (i = i - 1; i >= 0; i--) {
+ irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name);
+ free_irq(irq, di);
+ }
+free_fg_wq:
+ destroy_workqueue(di->fg_wq);
+free_device_info:
+ kfree(di);
+
+ return ret;
+}
+
+static struct platform_driver ab8500_fg_driver = {
+ .probe = ab8500_fg_probe,
+ .remove = __devexit_p(ab8500_fg_remove),
+ .suspend = ab8500_fg_suspend,
+ .resume = ab8500_fg_resume,
+ .driver = {
+ .name = "ab8500-fg",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab8500_fg_init(void)
+{
+ return platform_driver_register(&ab8500_fg_driver);
+}
+
+static void __exit ab8500_fg_exit(void)
+{
+ platform_driver_unregister(&ab8500_fg_driver);
+}
+
+subsys_initcall_sync(ab8500_fg_init);
+module_exit(ab8500_fg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
+MODULE_ALIAS("platform:ab8500-fg");
+MODULE_DESCRIPTION("AB8500 Fuel Gauge driver");
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 91606bb5531..12761719562 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -41,23 +41,40 @@ static int __power_supply_changed_work(struct device *dev, void *data)
static void power_supply_changed_work(struct work_struct *work)
{
+ unsigned long flags;
struct power_supply *psy = container_of(work, struct power_supply,
changed_work);
dev_dbg(psy->dev, "%s\n", __func__);
- class_for_each_device(power_supply_class, NULL, psy,
- __power_supply_changed_work);
+ spin_lock_irqsave(&psy->changed_lock, flags);
+ if (psy->changed) {
+ psy->changed = false;
+ spin_unlock_irqrestore(&psy->changed_lock, flags);
- power_supply_update_leds(psy);
+ class_for_each_device(power_supply_class, NULL, psy,
+ __power_supply_changed_work);
- kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
+ power_supply_update_leds(psy);
+
+ kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
+ spin_lock_irqsave(&psy->changed_lock, flags);
+ }
+ if (!psy->changed)
+ wake_unlock(&psy->work_wake_lock);
+ spin_unlock_irqrestore(&psy->changed_lock, flags);
}
void power_supply_changed(struct power_supply *psy)
{
+ unsigned long flags;
+
dev_dbg(psy->dev, "%s\n", __func__);
+ spin_lock_irqsave(&psy->changed_lock, flags);
+ psy->changed = true;
+ wake_lock(&psy->work_wake_lock);
+ spin_unlock_irqrestore(&psy->changed_lock, flags);
schedule_work(&psy->changed_work);
}
EXPORT_SYMBOL_GPL(power_supply_changed);
@@ -180,6 +197,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
goto device_add_failed;
INIT_WORK(&psy->changed_work, power_supply_changed_work);
+ spin_lock_init(&psy->changed_lock);
+ wake_lock_init(&psy->work_wake_lock, WAKE_LOCK_SUSPEND, "power-supply");
rc = power_supply_create_triggers(psy);
if (rc)
@@ -190,6 +209,7 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
goto success;
create_triggers_failed:
+ wake_lock_destroy(&psy->work_wake_lock);
device_unregister(psy->dev);
kobject_set_name_failed:
device_add_failed:
@@ -203,6 +223,7 @@ void power_supply_unregister(struct power_supply *psy)
{
flush_scheduled_work();
power_supply_remove_triggers(psy);
+ wake_lock_destroy(&psy->work_wake_lock);
device_unregister(psy->dev);
}
EXPORT_SYMBOL_GPL(power_supply_unregister);
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 04f2e085116..c39731ce554 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -64,6 +64,29 @@ config REGULATOR_USERSPACE_CONSUMER
If unsure, say no.
+config REGULATOR_AB5500
+ bool "ST-Ericsson AB5500 Power Regulators"
+ depends on AB5500_CORE
+ help
+ This driver supports the regulators found on the ST-Ericsson mixed
+ signal AB5500 PMIC
+
+config REGULATOR_AB8500
+ bool "ST-Ericsson AB8500 Power Regulators"
+ depends on AB8500_CORE
+ default y if AB8500_CORE
+ help
+ This driver supports the regulators found on the ST-Ericsson mixed
+ signal AB8500 PMIC
+
+config REGULATOR_AB8500_DEBUG
+ bool "AB8500 regulator debug"
+ depends on REGULATOR_AB8500
+ help
+ Say Y here to add debug functionality for ST-Ericsson
+ ab8500 regulators. This is a module that exposes a
+ number of settings and debug output in debugfs.
+
config REGULATOR_BQ24022
tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC"
help
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 4e7feece22d..ce1a332dfbe 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -8,6 +8,9 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
+obj-$(CONFIG_REGULATOR_AB5500) += ab5500.o
+obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
+obj-$(CONFIG_REGULATOR_AB8500_DEBUG) += ab8500-debug.o
obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
obj-$(CONFIG_REGULATOR_DUMMY) += dummy.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c
index 11790990277..b349266a43d 100644
--- a/drivers/regulator/ab3100.c
+++ b/drivers/regulator/ab3100.c
@@ -634,12 +634,9 @@ static int __devinit ab3100_regulators_probe(struct platform_device *pdev)
"%s: failed to register regulator %s err %d\n",
__func__, ab3100_regulator_desc[i].name,
err);
- i--;
/* remove the already registered regulators */
- while (i > 0) {
+ while (--i >= 0)
regulator_unregister(ab3100_regulators[i].rdev);
- i--;
- }
return err;
}
diff --git a/drivers/regulator/ab5500.c b/drivers/regulator/ab5500.c
new file mode 100644
index 00000000000..e9aa505c35a
--- /dev/null
+++ b/drivers/regulator/ab5500.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Based on ab3100.c.
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/regulator/ab5500.h>
+
+#define AB5500_LDO_VDIGMIC_ST 0x50
+
+#define AB5500_LDO_S_ST 0x66
+#define AB5500_LDO_S_PWR1 0x67
+#define AB5500_LDO_S_PWR0 0x68
+
+#define AB5500_LDO_D_ST 0x72
+#define AB5500_LDO_D_PWR1 0x73
+#define AB5500_LDO_D_PWR0 0x74
+
+#define AB5500_LDO_G_ST 0x78
+#define AB5500_LDO_G_PWR1 0x79
+#define AB5500_LDO_G_PWR0 0x7a
+
+#define AB5500_LDO_H_ST 0x7b
+#define AB5500_LDO_H_PWR1 0x7c
+#define AB5500_LDO_H_PWR0 0x7d
+
+#define AB5500_LDO_K_ST 0x7e
+#define AB5500_LDO_K_PWR1 0x7f
+#define AB5500_LDO_K_PWR0 0x80
+
+#define AB5500_LDO_L_ST 0x81
+#define AB5500_LDO_L_PWR1 0x82
+#define AB5500_LDO_L_PWR0 0x83
+
+/* In SIM bank */
+#define AB5500_SIM_SUP 0x14
+
+#define AB5500_LDO_MODE_MASK (0x3 << 4)
+#define AB5500_LDO_MODE_FULLPOWER (0x3 << 4)
+#define AB5500_LDO_MODE_PWRCTRL (0x2 << 4)
+#define AB5500_LDO_MODE_LOWPOWER (0x1 << 4)
+#define AB5500_LDO_MODE_OFF (0x0 << 4)
+#define AB5500_LDO_VOLT_MASK 0x07
+
+struct ab5500_regulator {
+ struct regulator_desc desc;
+ const int *voltages;
+ int num_holes;
+ bool pwrctrl;
+ int enable_time;
+ u8 bank;
+ u8 reg;
+};
+
+struct ab5500_regulators {
+ struct device *dev;
+ struct ab5500_regulator *regulator[AB5500_NUM_REGULATORS];
+ struct regulator_dev *rdev[AB5500_NUM_REGULATORS];
+};
+
+static int ab5500_regulator_enable_time(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+
+ return r->enable_time; /* microseconds */
+}
+
+static int ab5500_regulator_enable(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ u8 regval = AB5500_LDO_MODE_FULLPOWER;
+
+ return abx500_mask_and_set_register_interruptible(
+ ab5500->dev, r->bank, r->reg,
+ AB5500_LDO_MODE_MASK, regval);
+}
+
+static int ab5500_regulator_disable(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ u8 regval = r->pwrctrl ? AB5500_LDO_MODE_PWRCTRL : AB5500_LDO_MODE_OFF;
+
+ return abx500_mask_and_set_register_interruptible(
+ ab5500->dev, r->bank, r->reg,
+ AB5500_LDO_MODE_MASK, regval);
+}
+
+static int ab5500_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ u8 regval;
+ int err;
+
+ err = abx500_get_register_interruptible(ab5500->dev,
+ r->bank, r->reg, &regval);
+ if (err) {
+ dev_err(rdev_get_dev(rdev), "unable to get register 0x%x\n",
+ r->reg);
+ return err;
+ }
+
+ return (regval & AB5500_LDO_MODE_MASK) == AB5500_LDO_MODE_FULLPOWER;
+}
+
+static int
+ab5500_regulator_list_voltage(struct regulator_dev *rdev, unsigned selector)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ unsigned n_voltages = r->desc.n_voltages;
+ int selindex;
+ int i;
+
+ for (i = 0, selindex = 0; selindex < n_voltages; i++) {
+ int voltage = r->voltages[i];
+
+ if (!voltage)
+ continue;
+
+ if (selindex == selector)
+ return voltage;
+
+ selindex++;
+ }
+
+ return -EINVAL;
+}
+
+static int ab5500_regulator_fixed_get_voltage(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+
+ return r->voltages[0];
+}
+
+static int ab5500_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ u8 regval;
+ int ret;
+
+ ret = abx500_get_register_interruptible(ab5500->dev,
+ r->bank, r->reg, &regval);
+ if (ret) {
+ dev_warn(rdev_get_dev(rdev),
+ "failed to get regulator value in register "
+ "%02x\n", r->reg);
+ return ret;
+ }
+
+ regval &= AB5500_LDO_VOLT_MASK;
+ if (regval >= r->desc.n_voltages + r->num_holes)
+ return -EINVAL;
+
+ if (!r->voltages[regval])
+ return -EINVAL;
+
+ return r->voltages[regval];
+}
+
+static int ab5500_get_best_voltage_index(struct ab5500_regulator *r,
+ int min_uV, int max_uV)
+{
+ unsigned n_voltages = r->desc.n_voltages;
+ int bestmatch = INT_MAX;
+ int bestindex = -EINVAL;
+ int selindex;
+ int i;
+
+ /*
+ * Locate the minimum voltage fitting the criteria on
+ * this regulator. The switchable voltages are not
+ * in strict falling order so we need to check them
+ * all for the best match.
+ */
+ for (i = 0, selindex = 0; selindex < n_voltages; i++) {
+ int voltage = r->voltages[i];
+
+ if (!voltage)
+ continue;
+
+ if (voltage <= max_uV &&
+ voltage >= min_uV &&
+ voltage < bestmatch) {
+ bestmatch = voltage;
+ bestindex = i;
+ }
+
+ selindex++;
+ }
+
+ return bestindex;
+}
+
+static int ab5500_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ int bestindex;
+
+ bestindex = ab5500_get_best_voltage_index(r, min_uV, max_uV);
+ if (bestindex < 0) {
+ dev_warn(rdev_get_dev(rdev),
+ "requested %d<=x<=%d uV, out of range!\n",
+ min_uV, max_uV);
+ return bestindex;
+ }
+
+ return abx500_mask_and_set_register_interruptible(ab5500->dev,
+ r->bank, r->reg, AB5500_LDO_VOLT_MASK, bestindex);
+
+}
+
+static struct regulator_ops ab5500_regulator_variable_ops = {
+ .enable = ab5500_regulator_enable,
+ .disable = ab5500_regulator_disable,
+ .is_enabled = ab5500_regulator_is_enabled,
+ .enable_time = ab5500_regulator_enable_time,
+ .get_voltage = ab5500_regulator_get_voltage,
+ .set_voltage = ab5500_regulator_set_voltage,
+ .list_voltage = ab5500_regulator_list_voltage,
+};
+
+static struct regulator_ops ab5500_regulator_fixed_ops = {
+ .enable = ab5500_regulator_enable,
+ .disable = ab5500_regulator_disable,
+ .is_enabled = ab5500_regulator_is_enabled,
+ .enable_time = ab5500_regulator_enable_time,
+ .get_voltage = ab5500_regulator_fixed_get_voltage,
+ .list_voltage = ab5500_regulator_list_voltage,
+};
+
+static const int ab5500_ldo_s_voltages[] = {
+ [0x00] = 1200000,
+ [0x01] = 1050000,
+ [0x02] = 1100000,
+ [0x03] = 1500000,
+ [0x04] = 1800000,
+ [0x05] = 2200000,
+ [0x06] = 2500000,
+ [0x07] = 2790000,
+};
+
+static const int ab5500_ldo_d_voltages[] = {
+ [0x00] = 2100000,
+};
+
+static const int ab5500_ldo_lg_voltages[] = {
+ [0x00] = 1200000,
+ [0x01] = 0, /* not used */
+ [0x02] = 1500000,
+ [0x03] = 1800000,
+ [0x04] = 0, /* not used */
+ [0x05] = 2500000,
+ [0x06] = 2730000,
+ [0x07] = 2910000,
+};
+
+static const int ab5500_ldo_kh_voltages[] = {
+ [0x00] = 1200000,
+ [0x01] = 1500000,
+ [0x02] = 1800000,
+ [0x03] = 2100000,
+ [0x04] = 2500000,
+ [0x05] = 2750000,
+ [0x06] = 2790000,
+ [0x07] = 2910000,
+};
+
+static const int ab5500_ldo_vdigmic_voltages[] = {
+ [0x00] = 2100000,
+};
+
+static const int ab5500_ldo_sim_voltages[] = {
+ [0x00] = 1875000,
+ [0x01] = 2800000,
+ [0x02] = 2900000,
+};
+
+static struct ab5500_regulator ab5500_regulators[] = {
+ [AB5500_LDO_S] = {
+ .desc = {
+ .name = "LDO_S",
+ .id = AB5500_LDO_S,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_s_voltages),
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_S_ST,
+ .voltages = ab5500_ldo_s_voltages,
+ .enable_time = 400,
+ .pwrctrl = true,
+ },
+ [AB5500_LDO_D] = {
+ .desc = {
+ .name = "LDO_D",
+ .id = AB5500_LDO_D,
+ .ops = &ab5500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_d_voltages),
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_D_ST,
+ .voltages = ab5500_ldo_d_voltages,
+ .enable_time = 400,
+ .pwrctrl = true,
+ },
+ [AB5500_LDO_L] = {
+ .desc = {
+ .name = "LDO_L",
+ .id = AB5500_LDO_L,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_lg_voltages) - 2,
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_L_ST,
+ .voltages = ab5500_ldo_lg_voltages,
+ .num_holes = 2, /* 2 register values unused */
+ .enable_time = 400,
+ },
+ [AB5500_LDO_G] = {
+ .desc = {
+ .name = "LDO_G",
+ .id = AB5500_LDO_G,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_lg_voltages) - 2,
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_G_ST,
+ .voltages = ab5500_ldo_lg_voltages,
+ .num_holes = 2, /* 2 register values unused */
+ .enable_time = 400,
+ },
+ [AB5500_LDO_K] = {
+ .desc = {
+ .name = "LDO_K",
+ .id = AB5500_LDO_K,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_kh_voltages),
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_K_ST,
+ .voltages = ab5500_ldo_kh_voltages,
+ .enable_time = 400,
+ },
+ [AB5500_LDO_H] = {
+ .desc = {
+ .name = "LDO_H",
+ .id = AB5500_LDO_H,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_kh_voltages),
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_H_ST,
+ .voltages = ab5500_ldo_kh_voltages,
+ .enable_time = 400,
+ },
+ [AB5500_LDO_VDIGMIC] = {
+ .desc = {
+ .name = "LDO_VDIGMIC",
+ .id = AB5500_LDO_VDIGMIC,
+ .ops = &ab5500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_vdigmic_voltages),
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_VDIGMIC_ST,
+ .voltages = ab5500_ldo_vdigmic_voltages,
+ .enable_time = 450,
+ },
+ [AB5500_LDO_SIM] = {
+ .desc = {
+ .name = "LDO_SIM",
+ .id = AB5500_LDO_SIM,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_sim_voltages),
+ },
+ .bank = AB5500_BANK_SIM_USBSIM,
+ .reg = AB5500_SIM_SUP,
+ .voltages = ab5500_ldo_sim_voltages,
+ .enable_time = 1000,
+ },
+};
+
+
+static int __devinit ab5500_regulator_probe(struct platform_device *pdev)
+{
+ struct ab5500_platform_data *ppdata = pdev->dev.parent->platform_data;
+ struct ab5500_regulator_platform_data *pdata = ppdata->regulator;
+ struct ab5500_regulators *ab5500;
+ int err = 0;
+ int i;
+
+ if (!pdata || !pdata->regulator)
+ return -EINVAL;
+
+ ab5500 = kzalloc(sizeof(*ab5500), GFP_KERNEL);
+ if (!ab5500)
+ return -ENOMEM;
+
+ ab5500->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, ab5500);
+
+ for (i = 0; i < AB5500_NUM_REGULATORS; i++) {
+ struct ab5500_regulator *regulator = &ab5500_regulators[i];
+ struct regulator_dev *rdev;
+
+ ab5500->regulator[i] = regulator;
+
+ rdev = regulator_register(&regulator->desc, &pdev->dev,
+ &pdata->regulator[i], ab5500);
+ if (IS_ERR(rdev)) {
+ err = PTR_ERR(rdev);
+ dev_err(&pdev->dev, "failed to register regulator %s err %d\n",
+ regulator->desc.name, err);
+ goto err_unregister;
+ }
+
+ ab5500->rdev[i] = rdev;
+ }
+
+ return 0;
+
+err_unregister:
+ /* remove the already registered regulators */
+ while (--i >= 0)
+ regulator_unregister(ab5500->rdev[i]);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(ab5500);
+
+ return err;
+}
+
+static int __devexit ab5500_regulators_remove(struct platform_device *pdev)
+{
+ struct ab5500_regulators *ab5500 = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < AB5500_NUM_REGULATORS; i++)
+ regulator_unregister(ab5500->rdev[i]);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(ab5500);
+
+ return 0;
+}
+
+static struct platform_driver ab5500_regulator_driver = {
+ .driver = {
+ .name = "ab5500-regulator",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab5500_regulator_probe,
+ .remove = __devexit_p(ab5500_regulators_remove),
+};
+
+static __init int ab5500_regulator_init(void)
+{
+ return platform_driver_register(&ab5500_regulator_driver);
+}
+
+static __exit void ab5500_regulator_exit(void)
+{
+ platform_driver_unregister(&ab5500_regulator_driver);
+}
+
+subsys_initcall(ab5500_regulator_init);
+module_exit(ab5500_regulator_exit);
+
+MODULE_DESCRIPTION("AB5500 Regulator Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ab5500-regulator");
diff --git a/drivers/regulator/ab8500-debug.c b/drivers/regulator/ab8500-debug.c
new file mode 100644
index 00000000000..b03f7b1b1fa
--- /dev/null
+++ b/drivers/regulator/ab8500-debug.c
@@ -0,0 +1,1834 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson.
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/mfd/abx500.h>
+#include <linux/regulator/ab8500-debug.h>
+#include <linux/io.h>
+#include <mach/db8500-regs.h> /* U8500_BACKUPRAM1_BASE */
+
+/* board profile address - to determine if suspend-force is default */
+#define BOOT_INFO_BACKUPRAM1 (U8500_BACKUPRAM1_BASE + 0xffc)
+#define BOARD_PROFILE_BACKUPRAM1 (0x3)
+
+/* board profile option */
+#define OPTION_BOARD_VERSION_V5X 50
+
+/* for error prints */
+struct device *dev;
+struct platform_device *pdev;
+
+/* setting for suspend force (disabled by default) */
+static bool setting_suspend_force;
+
+/*
+ * regulator states
+ */
+enum ab8500_regulator_state_id {
+ AB8500_REGULATOR_STATE_INIT,
+ AB8500_REGULATOR_STATE_SUSPEND,
+ AB8500_REGULATOR_STATE_SUSPEND_CORE,
+ AB8500_REGULATOR_STATE_RESUME_CORE,
+ AB8500_REGULATOR_STATE_RESUME,
+ AB8500_REGULATOR_STATE_CURRENT,
+ NUM_REGULATOR_STATE
+};
+
+static const char *regulator_state_name[NUM_REGULATOR_STATE] = {
+ [AB8500_REGULATOR_STATE_INIT] = "init",
+ [AB8500_REGULATOR_STATE_SUSPEND] = "suspend",
+ [AB8500_REGULATOR_STATE_SUSPEND_CORE] = "suspend-core",
+ [AB8500_REGULATOR_STATE_RESUME_CORE] = "resume-core",
+ [AB8500_REGULATOR_STATE_RESUME] = "resume",
+ [AB8500_REGULATOR_STATE_CURRENT] = "current",
+};
+
+/*
+ * regulator register definitions
+ */
+enum ab8500_register_id {
+ AB8500_REGU_NOUSE, /* if not defined */
+ AB8500_REGU_REQUEST_CTRL1,
+ AB8500_REGU_REQUEST_CTRL2,
+ AB8500_REGU_REQUEST_CTRL3,
+ AB8500_REGU_REQUEST_CTRL4,
+ AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ AB8500_REGU_HW_HP_REQ1_VALID1,
+ AB8500_REGU_HW_HP_REQ1_VALID2,
+ AB8500_REGU_HW_HP_REQ2_VALID1,
+ AB8500_REGU_HW_HP_REQ2_VALID2,
+ AB8500_REGU_SW_HP_REQ_VALID1,
+ AB8500_REGU_SW_HP_REQ_VALID2,
+ AB8500_REGU_SYSCLK_REQ1_VALID,
+ AB8500_REGU_SYSCLK_REQ2_VALID,
+ AB8500_REGU_MISC1,
+ AB8500_REGU_OTG_SUPPLY_CTRL,
+ AB8500_REGU_VUSB_CTRL,
+ AB8500_REGU_VAUDIO_SUPPLY,
+ AB8500_REGU_CTRL1_VAMIC,
+ AB8500_REGU_ARM_REGU1,
+ AB8500_REGU_ARM_REGU2,
+ AB8500_REGU_VAPE_REGU,
+ AB8500_REGU_VSMPS1_REGU,
+ AB8500_REGU_VSMPS2_REGU,
+ AB8500_REGU_VSMPS3_REGU,
+ AB8500_REGU_VPLL_VANA_REGU,
+ AB8500_REGU_VREF_DDR,
+ AB8500_REGU_EXT_SUPPLY_REGU,
+ AB8500_REGU_VAUX12_REGU,
+ AB8500_REGU_VRF1_VAUX3_REGU,
+ AB8500_REGU_VARM_SEL1,
+ AB8500_REGU_VARM_SEL2,
+ AB8500_REGU_VARM_SEL3,
+ AB8500_REGU_VAPE_SEL1,
+ AB8500_REGU_VAPE_SEL2,
+ AB8500_REGU_VAPE_SEL3,
+ AB8500_REGU_VBB_SEL1,
+ AB8500_REGU_VBB_SEL2,
+ AB8500_REGU_VSMPS1_SEL1,
+ AB8500_REGU_VSMPS1_SEL2,
+ AB8500_REGU_VSMPS1_SEL3,
+ AB8500_REGU_VSMPS2_SEL1,
+ AB8500_REGU_VSMPS2_SEL2,
+ AB8500_REGU_VSMPS2_SEL3,
+ AB8500_REGU_VSMPS3_SEL1,
+ AB8500_REGU_VSMPS3_SEL2,
+ AB8500_REGU_VSMPS3_SEL3,
+ AB8500_REGU_VAUX1_SEL,
+ AB8500_REGU_VAUX2_SEL,
+ AB8500_REGU_VRF1_VAUX3_SEL,
+ AB8500_REGU_CTRL_EXT_SUP,
+ AB8500_REGU_VMOD_REGU,
+ AB8500_REGU_VMOD_SEL1,
+ AB8500_REGU_VMOD_SEL2,
+ AB8500_REGU_CTRL_DISCH,
+ AB8500_REGU_CTRL_DISCH2,
+ AB8500_OTHER_SYSCLK_CTRL, /* Other */
+ AB8500_OTHER_VSIM_SYSCLK_CTRL, /* Other */
+ AB8500_OTHER_SYSULPCLK_CTRL1, /* Other */
+ AB8500_OTHER_TVOUT_CTRL, /* Other */
+ NUM_AB8500_REGISTER
+};
+
+struct ab8500_register {
+ const char *name;
+ u8 bank;
+ u8 addr;
+};
+
+static struct ab8500_register
+ ab8500_register[NUM_AB8500_REGISTER] = {
+ [AB8500_REGU_REQUEST_CTRL1] = {
+ .name = "ReguRequestCtrl1",
+ .bank = 0x03,
+ .addr = 0x03,
+ },
+ [AB8500_REGU_REQUEST_CTRL2] = {
+ .name = "ReguRequestCtrl2",
+ .bank = 0x03,
+ .addr = 0x04,
+ },
+ [AB8500_REGU_REQUEST_CTRL3] = {
+ .name = "ReguRequestCtrl3",
+ .bank = 0x03,
+ .addr = 0x05,
+ },
+ [AB8500_REGU_REQUEST_CTRL4] = {
+ .name = "ReguRequestCtrl4",
+ .bank = 0x03,
+ .addr = 0x06,
+ },
+ [AB8500_REGU_SYSCLK_REQ1_HP_VALID1] = {
+ .name = "ReguSysClkReq1HPValid",
+ .bank = 0x03,
+ .addr = 0x07,
+ },
+ [AB8500_REGU_SYSCLK_REQ1_HP_VALID2] = {
+ .name = "ReguSysClkReq1HPValid2",
+ .bank = 0x03,
+ .addr = 0x08,
+ },
+ [AB8500_REGU_HW_HP_REQ1_VALID1] = {
+ .name = "ReguHwHPReq1Valid1",
+ .bank = 0x03,
+ .addr = 0x09,
+ },
+ [AB8500_REGU_HW_HP_REQ1_VALID2] = {
+ .name = "ReguHwHPReq1Valid2",
+ .bank = 0x03,
+ .addr = 0x0a,
+ },
+ [AB8500_REGU_HW_HP_REQ2_VALID1] = {
+ .name = "ReguHwHPReq2Valid1",
+ .bank = 0x03,
+ .addr = 0x0b,
+ },
+ [AB8500_REGU_HW_HP_REQ2_VALID2] = {
+ .name = "ReguHwHPReq2Valid2",
+ .bank = 0x03,
+ .addr = 0x0c,
+ },
+ [AB8500_REGU_SW_HP_REQ_VALID1] = {
+ .name = "ReguSwHPReqValid1",
+ .bank = 0x03,
+ .addr = 0x0d,
+ },
+ [AB8500_REGU_SW_HP_REQ_VALID2] = {
+ .name = "ReguSwHPReqValid2",
+ .bank = 0x03,
+ .addr = 0x0e,
+ },
+ [AB8500_REGU_SYSCLK_REQ1_VALID] = {
+ .name = "ReguSysClkReqValid1",
+ .bank = 0x03,
+ .addr = 0x0f,
+ },
+ [AB8500_REGU_SYSCLK_REQ2_VALID] = {
+ .name = "ReguSysClkReqValid2",
+ .bank = 0x03,
+ .addr = 0x10,
+ },
+ [AB8500_REGU_MISC1] = {
+ .name = "ReguMisc1",
+ .bank = 0x03,
+ .addr = 0x80,
+ },
+ [AB8500_REGU_OTG_SUPPLY_CTRL] = {
+ .name = "OTGSupplyCtrl",
+ .bank = 0x03,
+ .addr = 0x81,
+ },
+ [AB8500_REGU_VUSB_CTRL] = {
+ .name = "VusbCtrl",
+ .bank = 0x03,
+ .addr = 0x82,
+ },
+ [AB8500_REGU_VAUDIO_SUPPLY] = {
+ .name = "VaudioSupply",
+ .bank = 0x03,
+ .addr = 0x83,
+ },
+ [AB8500_REGU_CTRL1_VAMIC] = {
+ .name = "ReguCtrl1VAmic",
+ .bank = 0x03,
+ .addr = 0x84,
+ },
+ [AB8500_REGU_ARM_REGU1] = {
+ .name = "ArmRegu1",
+ .bank = 0x04,
+ .addr = 0x00,
+ },
+ [AB8500_REGU_ARM_REGU2] = {
+ .name = "ArmRegu2",
+ .bank = 0x04,
+ .addr = 0x01,
+ },
+ [AB8500_REGU_VAPE_REGU] = {
+ .name = "VapeRegu",
+ .bank = 0x04,
+ .addr = 0x02,
+ },
+ [AB8500_REGU_VSMPS1_REGU] = {
+ .name = "Vsmps1Regu",
+ .bank = 0x04,
+ .addr = 0x03,
+ },
+ [AB8500_REGU_VSMPS2_REGU] = {
+ .name = "Vsmps2Regu",
+ .bank = 0x04,
+ .addr = 0x04,
+ },
+ [AB8500_REGU_VSMPS3_REGU] = {
+ .name = "Vsmps3Regu",
+ .bank = 0x04,
+ .addr = 0x05,
+ },
+ [AB8500_REGU_VPLL_VANA_REGU] = {
+ .name = "VpllVanaRegu",
+ .bank = 0x04,
+ .addr = 0x06,
+ },
+ [AB8500_REGU_VREF_DDR] = {
+ .name = "VrefDDR",
+ .bank = 0x04,
+ .addr = 0x07,
+ },
+ [AB8500_REGU_EXT_SUPPLY_REGU] = {
+ .name = "ExtSupplyRegu",
+ .bank = 0x04,
+ .addr = 0x08,
+ },
+ [AB8500_REGU_VAUX12_REGU] = {
+ .name = "Vaux12Regu",
+ .bank = 0x04,
+ .addr = 0x09,
+ },
+ [AB8500_REGU_VRF1_VAUX3_REGU] = {
+ .name = "VRF1Vaux3Regu",
+ .bank = 0x04,
+ .addr = 0x0a,
+ },
+ [AB8500_REGU_VARM_SEL1] = {
+ .name = "VarmSel1",
+ .bank = 0x04,
+ .addr = 0x0b,
+ },
+ [AB8500_REGU_VARM_SEL2] = {
+ .name = "VarmSel2",
+ .bank = 0x04,
+ .addr = 0x0c,
+ },
+ [AB8500_REGU_VARM_SEL3] = {
+ .name = "VarmSel3",
+ .bank = 0x04,
+ .addr = 0x0d,
+ },
+ [AB8500_REGU_VAPE_SEL1] = {
+ .name = "VapeSel1",
+ .bank = 0x04,
+ .addr = 0x0e,
+ },
+ [AB8500_REGU_VAPE_SEL2] = {
+ .name = "VapeSel2",
+ .bank = 0x04,
+ .addr = 0x0f,
+ },
+ [AB8500_REGU_VAPE_SEL3] = {
+ .name = "VapeSel3",
+ .bank = 0x04,
+ .addr = 0x10,
+ },
+ [AB8500_REGU_VBB_SEL1] = {
+ .name = "VBBSel1",
+ .bank = 0x04,
+ .addr = 0x11,
+ },
+ [AB8500_REGU_VBB_SEL2] = {
+ .name = "VBBSel2",
+ .bank = 0x04,
+ .addr = 0x12,
+ },
+ [AB8500_REGU_VSMPS1_SEL1] = {
+ .name = "Vsmps1Sel1",
+ .bank = 0x04,
+ .addr = 0x13,
+ },
+ [AB8500_REGU_VSMPS1_SEL2] = {
+ .name = "Vsmps1Sel2",
+ .bank = 0x04,
+ .addr = 0x14,
+ },
+ [AB8500_REGU_VSMPS1_SEL3] = {
+ .name = "Vsmps1Sel3",
+ .bank = 0x04,
+ .addr = 0x15,
+ },
+ [AB8500_REGU_VSMPS2_SEL1] = {
+ .name = "Vsmps2Sel1",
+ .bank = 0x04,
+ .addr = 0x17,
+ },
+ [AB8500_REGU_VSMPS2_SEL2] = {
+ .name = "Vsmps2Sel2",
+ .bank = 0x04,
+ .addr = 0x18,
+ },
+ [AB8500_REGU_VSMPS2_SEL3] = {
+ .name = "Vsmps2Sel3",
+ .bank = 0x04,
+ .addr = 0x19,
+ },
+ [AB8500_REGU_VSMPS3_SEL1] = {
+ .name = "Vsmps3Sel1",
+ .bank = 0x04,
+ .addr = 0x1b,
+ },
+ [AB8500_REGU_VSMPS3_SEL2] = {
+ .name = "Vsmps3Sel2",
+ .bank = 0x04,
+ .addr = 0x1c,
+ },
+ [AB8500_REGU_VSMPS3_SEL3] = {
+ .name = "Vsmps3Sel3",
+ .bank = 0x04,
+ .addr = 0x1d,
+ },
+ [AB8500_REGU_VAUX1_SEL] = {
+ .name = "Vaux1Sel",
+ .bank = 0x04,
+ .addr = 0x1f,
+ },
+ [AB8500_REGU_VAUX2_SEL] = {
+ .name = "Vaux2Sel",
+ .bank = 0x04,
+ .addr = 0x20,
+ },
+ [AB8500_REGU_VRF1_VAUX3_SEL] = {
+ .name = "VRF1Vaux3Sel",
+ .bank = 0x04,
+ .addr = 0x21,
+ },
+ [AB8500_REGU_CTRL_EXT_SUP] = {
+ .name = "ReguCtrlExtSup",
+ .bank = 0x04,
+ .addr = 0x22,
+ },
+ [AB8500_REGU_VMOD_REGU] = {
+ .name = "VmodRegu",
+ .bank = 0x04,
+ .addr = 0x40,
+ },
+ [AB8500_REGU_VMOD_SEL1] = {
+ .name = "VmodSel1",
+ .bank = 0x04,
+ .addr = 0x41,
+ },
+ [AB8500_REGU_VMOD_SEL2] = {
+ .name = "VmodSel2",
+ .bank = 0x04,
+ .addr = 0x42,
+ },
+ [AB8500_REGU_CTRL_DISCH] = {
+ .name = "ReguCtrlDisch",
+ .bank = 0x04,
+ .addr = 0x43,
+ },
+ [AB8500_REGU_CTRL_DISCH2] = {
+ .name = "ReguCtrlDisch2",
+ .bank = 0x04,
+ .addr = 0x44,
+ },
+ /* Outside regulator banks */
+ [AB8500_OTHER_SYSCLK_CTRL] = {
+ .name = "SysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x0c,
+ },
+ [AB8500_OTHER_VSIM_SYSCLK_CTRL] = {
+ .name = "VsimSysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x33,
+ },
+ [AB8500_OTHER_SYSULPCLK_CTRL1] = {
+ .name = "SysUlpClkCtrl1",
+ .bank = 0x02,
+ .addr = 0x0b,
+ },
+ [AB8500_OTHER_TVOUT_CTRL] = {
+ .name = "TVoutCtrl",
+ .bank = 0x06,
+ .addr = 0x80,
+ },
+};
+
+static u8 ab8500_register_state[NUM_REGULATOR_STATE][NUM_AB8500_REGISTER];
+static bool ab8500_register_state_saved[NUM_REGULATOR_STATE];
+static bool ab8500_register_state_save = true;
+
+static int ab8500_regulator_record_state(int state)
+{
+ u8 val;
+ int i;
+ int ret;
+
+ /* check arguments */
+ if ((state > NUM_REGULATOR_STATE) || (state < 0)) {
+ dev_err(dev, "Wrong state specified\n");
+ return -EINVAL;
+ }
+
+ /* record */
+ if (!ab8500_register_state_save)
+ goto exit;
+
+ ab8500_register_state_saved[state] = true;
+
+ for (i = 1; i < NUM_AB8500_REGISTER; i++) {
+ ret = abx500_get_register_interruptible(dev,
+ ab8500_register[i].bank,
+ ab8500_register[i].addr,
+ &val);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+
+ ab8500_register_state[state][i] = val;
+ }
+exit:
+ return 0;
+}
+
+/*
+ * regulator register dump
+ */
+static int ab8500_regulator_dump_print(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ int state, reg_id, i;
+ int err;
+
+ /* record current state */
+ ab8500_regulator_record_state(AB8500_REGULATOR_STATE_CURRENT);
+
+ /* print dump header */
+ err = seq_printf(s, "ab8500-regulator dump:\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow\n");
+
+ /* print states */
+ for (state = NUM_REGULATOR_STATE - 1; state >= 0; state--) {
+ if (ab8500_register_state_saved[state])
+ err = seq_printf(s, "%16s saved -------",
+ regulator_state_name[state]);
+ else
+ err = seq_printf(s, "%12s not saved -------",
+ regulator_state_name[state]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+ for (i = 0; i < NUM_REGULATOR_STATE; i++) {
+ if (i < state)
+ err = seq_printf(s, "-----");
+ else if (i == state)
+ err = seq_printf(s, "----+");
+ else
+ err = seq_printf(s, " |");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n",
+ __LINE__);
+ }
+ err = seq_printf(s, "\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ }
+
+ /* print labels */
+ err = seq_printf(s, "\n addr\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+ /* dump registers */
+ for (reg_id = 1; reg_id < NUM_AB8500_REGISTER; reg_id++) {
+ err = seq_printf(s, "%22s 0x%02x%02x:",
+ ab8500_register[reg_id].name,
+ ab8500_register[reg_id].bank,
+ ab8500_register[reg_id].addr);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ reg_id, __LINE__);
+
+ for (state = 0; state < NUM_REGULATOR_STATE; state++) {
+ err = seq_printf(s, " 0x%02x",
+ ab8500_register_state[state][reg_id]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ reg_id, __LINE__);
+ }
+
+ err = seq_printf(s, "\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ reg_id, __LINE__);
+ }
+
+ return 0;
+}
+
+static int ab8500_regulator_dump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_regulator_dump_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_regulator_dump_fops = {
+ .open = ab8500_regulator_dump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * regulator status print
+ */
+enum ab8500_regulator_id {
+ AB8500_VARM,
+ AB8500_VBBP,
+ AB8500_VBBN,
+ AB8500_VAPE,
+ AB8500_VSMPS1,
+ AB8500_VSMPS2,
+ AB8500_VSMPS3,
+ AB8500_VPLL,
+ AB8500_VREFDDR,
+ AB8500_VMOD,
+ AB8500_VEXTSUPPLY1,
+ AB8500_VEXTSUPPLY2,
+ AB8500_VEXTSUPPLY3,
+ AB8500_VRF1,
+ AB8500_VANA,
+ AB8500_VAUX1,
+ AB8500_VAUX2,
+ AB8500_VAUX3,
+ AB8500_VINTCORE,
+ AB8500_VTVOUT,
+ AB8500_VAUDIO,
+ AB8500_VANAMIC1,
+ AB8500_VANAMIC2,
+ AB8500_VDMIC,
+ AB8500_VUSB,
+ AB8500_VOTG,
+ AB8500_VBUSBIS,
+ AB8500_NUM_REGULATORS,
+};
+
+/*
+ * regulator_voltage
+ */
+struct regulator_volt {
+ u8 value;
+ int volt;
+};
+
+struct regulator_volt_range {
+ struct regulator_volt start;
+ struct regulator_volt step;
+ struct regulator_volt end;
+};
+
+/*
+ * ab8500_regulator
+ * @name
+ * @update_regid
+ * @update_mask
+ * @update_val[4] {off, on, hw, lp}
+ * @hw_mode_regid
+ * @hw_mode_mask
+ * @hw_mode_val[4] {hp/lp, hp/off, hp, hp}
+ * @hw_valid_regid[4] {sysclkreq1, hw1, hw2, sw}
+ * @hw_valid_mask[4] {sysclkreq1, hw1, hw2, sw}
+ * @vsel_sel_regid
+ * @vsel_sel_mask
+ * @vsel_val[333] {sel1, sel2, sel3, sel3}
+ * @vsel_regid
+ * @vsel_mask
+ * @vsel_range
+ * @vsel_range_len
+ */
+struct ab8500_regulator {
+ const char *name;
+ int update_regid;
+ u8 update_mask;
+ u8 update_val[4];
+ int hw_mode_regid;
+ u8 hw_mode_mask;
+ u8 hw_mode_val[4];
+ int hw_valid_regid[4];
+ u8 hw_valid_mask[4];
+ int vsel_sel_regid;
+ u8 vsel_sel_mask;
+ u8 vsel_sel_val[4];
+ int vsel_regid[3];
+ u8 vsel_mask[3];
+ struct regulator_volt_range const *vsel_range[3];
+ int vsel_range_len[3];
+};
+
+static const char *update_val_name[] = {
+ "off",
+ "on ",
+ "hw ",
+ "lp ",
+ " - " /* undefined value */
+};
+
+static const char *hw_mode_val_name[] = {
+ "hp/lp ",
+ "hp/off",
+ "hp ",
+ "hp ",
+ "-/- ", /* undefined value */
+};
+
+/* voltage selection */
+static const struct regulator_volt_range varm_vape_vmod_vsel[] = {
+ { {0x00, 700000}, {0x01, 12500}, {0x35, 1362500} },
+ { {0x36, 1362500}, {0x01, 0}, {0x3f, 1362500} },
+};
+
+static const struct regulator_volt_range vbbp_vsel[] = {
+ { {0x00, 0}, {0x10, 100000}, {0x40, 400000} },
+ { {0x50, 400000}, {0x10, 0}, {0x70, 400000} },
+ { {0x80, -400000}, {0x10, 0}, {0xb0, -400000} },
+ { {0xc0, -400000}, {0x10, 100000}, {0xf0, -100000} },
+};
+
+static const struct regulator_volt_range vbbn_vsel[] = {
+ { {0x00, 0}, {0x01, -100000}, {0x04, -400000} },
+ { {0x05, -400000}, {0x01, 0}, {0x07, -400000} },
+ { {0x08, 0}, {0x01, 100000}, {0x0c, 400000} },
+ { {0x0d, 400000}, {0x01, 0}, {0x0f, 400000} },
+};
+
+static const struct regulator_volt_range vsmps1_vsel[] = {
+ { {0x00, 1100000}, {0x01, 0}, {0x1f, 1100000} },
+ { {0x20, 1100000}, {0x01, 12500}, {0x30, 1300000} },
+ { {0x31, 1300000}, {0x01, 0}, {0x3f, 1300000} },
+};
+
+static const struct regulator_volt_range vsmps2_vsel[] = {
+ { {0x00, 1800000}, {0x01, 0}, {0x38, 1800000} },
+ { {0x39, 1800000}, {0x01, 12500}, {0x7f, 1875000} },
+};
+
+static const struct regulator_volt_range vsmps3_vsel[] = {
+ { {0x00, 700000}, {0x01, 12500}, {0x35, 1363500} },
+ { {0x36, 1363500}, {0x01, 0}, {0x7f, 1363500} },
+};
+
+static const struct regulator_volt_range vaux1_vaux2_vsel[] = {
+ { {0x00, 1100000}, {0x01, 100000}, {0x04, 1500000} },
+ { {0x05, 1800000}, {0x01, 50000}, {0x07, 1900000} },
+ { {0x08, 2500000}, {0x01, 0}, {0x08, 2500000} },
+ { {0x09, 2650000}, {0x01, 50000}, {0x0c, 2800000} },
+ { {0x0d, 2900000}, {0x01, 100000}, {0x0e, 3000000} },
+ { {0x0f, 3300000}, {0x01, 0}, {0x0f, 3300000} },
+};
+
+static const struct regulator_volt_range vaux3_vsel[] = {
+ { {0x00, 1200000}, {0x01, 300000}, {0x03, 2100000} },
+ { {0x04, 2500000}, {0x01, 250000}, {0x05, 2750000} },
+ { {0x06, 2790000}, {0x01, 0}, {0x06, 2790000} },
+ { {0x07, 2910000}, {0x01, 0}, {0x07, 2910000} },
+};
+
+static const struct regulator_volt_range vrf1_vsel[] = {
+ { {0x00, 1800000}, {0x10, 200000}, {0x10, 2000000} },
+ { {0x20, 2150000}, {0x10, 0}, {0x20, 2150000} },
+ { {0x30, 2500000}, {0x10, 0}, {0x30, 2500000} },
+};
+
+static const struct regulator_volt_range vintcore12_vsel[] = {
+ { {0x00, 1200000}, {0x08, 25000}, {0x30, 1350000} },
+ { {0x38, 1350000}, {0x01, 0}, {0x38, 1350000} },
+};
+
+/* regulators */
+static struct ab8500_regulator ab8500_regulator[AB8500_NUM_REGULATORS] = {
+ [AB8500_VARM] = {
+ .name = "Varm",
+ .update_regid = AB8500_REGU_ARM_REGU1,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x02,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x02,
+ .vsel_sel_regid = AB8500_REGU_ARM_REGU1,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VARM_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = varm_vape_vmod_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(varm_vape_vmod_vsel),
+ .vsel_regid[1] = AB8500_REGU_VARM_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = varm_vape_vmod_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(varm_vape_vmod_vsel),
+ .vsel_regid[2] = AB8500_REGU_VARM_SEL3,
+ .vsel_mask[2] = 0x3f,
+ .vsel_range[2] = varm_vape_vmod_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(varm_vape_vmod_vsel),
+ },
+ [AB8500_VBBP] = {
+ .name = "Vbbp",
+ .update_regid = AB8500_REGU_ARM_REGU2,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x00},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x04,
+ .vsel_sel_regid = AB8500_REGU_ARM_REGU1,
+ .vsel_sel_mask = 0x10,
+ .vsel_sel_val = {0x00, 0x10, 0x00, 0x00},
+ .vsel_regid[0] = AB8500_REGU_VBB_SEL1,
+ .vsel_mask[0] = 0xf0,
+ .vsel_range[0] = vbbp_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vbbp_vsel),
+ .vsel_regid[1] = AB8500_REGU_VBB_SEL2,
+ .vsel_mask[1] = 0xf0,
+ .vsel_range[1] = vbbp_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vbbp_vsel),
+ },
+ [AB8500_VBBN] = {
+ .name = "Vbbn",
+ .update_regid = AB8500_REGU_ARM_REGU2,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x00},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x04,
+ .vsel_sel_regid = AB8500_REGU_ARM_REGU1,
+ .vsel_sel_mask = 0x20,
+ .vsel_sel_val = {0x00, 0x20, 0x00, 0x00},
+ .vsel_regid[0] = AB8500_REGU_VBB_SEL1,
+ .vsel_mask[0] = 0x0f,
+ .vsel_range[0] = vbbn_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vbbn_vsel),
+ .vsel_regid[1] = AB8500_REGU_VBB_SEL2,
+ .vsel_mask[1] = 0x0f,
+ .vsel_range[1] = vbbn_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vbbn_vsel),
+ },
+ [AB8500_VAPE] = {
+ .name = "Vape",
+ .update_regid = AB8500_REGU_VAPE_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0x0c,
+ .hw_mode_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x01,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x01,
+ .vsel_sel_regid = AB8500_REGU_VAPE_REGU,
+ .vsel_sel_mask = 0x24,
+ .vsel_sel_val = {0x00, 0x04, 0x20, 0x24},
+ .vsel_regid[0] = AB8500_REGU_VAPE_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = varm_vape_vmod_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(varm_vape_vmod_vsel),
+ .vsel_regid[1] = AB8500_REGU_VAPE_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = varm_vape_vmod_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(varm_vape_vmod_vsel),
+ .vsel_regid[2] = AB8500_REGU_VAPE_SEL3,
+ .vsel_mask[2] = 0x3f,
+ .vsel_range[2] = varm_vape_vmod_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(varm_vape_vmod_vsel),
+ },
+ [AB8500_VSMPS1] = {
+ .name = "Vsmps1",
+ .update_regid = AB8500_REGU_VSMPS1_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0x30,
+ .hw_mode_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x01,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x01,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x01,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x04,
+ .vsel_sel_regid = AB8500_REGU_VSMPS1_REGU,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VSMPS1_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = vsmps1_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vsmps1_vsel),
+ .vsel_regid[1] = AB8500_REGU_VSMPS1_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = vsmps1_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vsmps1_vsel),
+ .vsel_regid[2] = AB8500_REGU_VSMPS1_SEL3,
+ .vsel_mask[2] = 0x3f,
+ .vsel_range[2] = vsmps1_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(vsmps1_vsel),
+ },
+ [AB8500_VSMPS2] = {
+ .name = "Vsmps2",
+ .update_regid = AB8500_REGU_VSMPS2_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x02,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x02,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x02,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x08,
+ .vsel_sel_regid = AB8500_REGU_VSMPS2_REGU,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VSMPS2_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = vsmps2_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vsmps2_vsel),
+ .vsel_regid[1] = AB8500_REGU_VSMPS2_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = vsmps2_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vsmps2_vsel),
+ .vsel_regid[2] = AB8500_REGU_VSMPS2_SEL3,
+ .vsel_mask[2] = 0x3f,
+ .vsel_range[2] = vsmps2_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(vsmps2_vsel),
+ },
+ [AB8500_VSMPS3] = {
+ .name = "Vsmps3",
+ .update_regid = AB8500_REGU_VSMPS3_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x04,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x04,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x04,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x10,
+ .vsel_sel_regid = AB8500_REGU_VSMPS3_REGU,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VSMPS3_SEL1,
+ .vsel_mask[0] = 0x7f,
+ .vsel_range[0] = vsmps3_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vsmps3_vsel),
+ .vsel_regid[1] = AB8500_REGU_VSMPS3_SEL2,
+ .vsel_mask[1] = 0x7f,
+ .vsel_range[1] = vsmps3_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vsmps3_vsel),
+ .vsel_regid[2] = AB8500_REGU_VSMPS3_SEL3,
+ .vsel_mask[2] = 0x7f,
+ .vsel_range[2] = vsmps3_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(vsmps3_vsel),
+ },
+ [AB8500_VPLL] = {
+ .name = "Vpll",
+ .update_regid = AB8500_REGU_VPLL_VANA_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0x0c,
+ .hw_mode_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x10,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x10,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x10,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x40,
+ },
+ [AB8500_VREFDDR] = {
+ .name = "VrefDDR",
+ .update_regid = AB8500_REGU_VREF_DDR,
+ .update_mask = 0x01,
+ .update_val = {0x00, 0x01, 0x00, 0x00},
+ },
+ [AB8500_VMOD] = {
+ .name = "Vmod",
+ .update_regid = AB8500_REGU_VMOD_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_VMOD_REGU,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x08,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x08,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x08,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x20,
+ .vsel_sel_regid = AB8500_REGU_VMOD_REGU,
+ .vsel_sel_mask = 0x04,
+ .vsel_sel_val = {0x00, 0x04, 0x00, 0x00},
+ .vsel_regid[0] = AB8500_REGU_VMOD_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = varm_vape_vmod_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(varm_vape_vmod_vsel),
+ .vsel_regid[1] = AB8500_REGU_VMOD_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = varm_vape_vmod_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(varm_vape_vmod_vsel),
+ },
+ [AB8500_VEXTSUPPLY1] = {
+ .name = "Vextsupply1",
+ .update_regid = AB8500_REGU_EXT_SUPPLY_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x10,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x01,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x01,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x04,
+ },
+ [AB8500_VEXTSUPPLY2] = {
+ .name = "VextSupply2",
+ .update_regid = AB8500_REGU_EXT_SUPPLY_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x20,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x02,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x02,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x08,
+ },
+ [AB8500_VEXTSUPPLY3] = {
+ .name = "VextSupply3",
+ .update_regid = AB8500_REGU_EXT_SUPPLY_REGU,
+ .update_mask = 0x30,
+ .update_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0x0c,
+ .hw_mode_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x40,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x04,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x04,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x10,
+ },
+ [AB8500_VRF1] = {
+ .name = "Vrf1",
+ .update_regid = AB8500_REGU_VRF1_VAUX3_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VRF1_VAUX3_SEL,
+ .vsel_mask[0] = 0x30,
+ .vsel_range[0] = vrf1_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vrf1_vsel),
+ },
+ [AB8500_VANA] = {
+ .name = "Vana",
+ .update_regid = AB8500_REGU_VPLL_VANA_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0x30,
+ .hw_mode_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x08,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x08,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x08,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x20,
+ },
+ [AB8500_VAUX1] = {
+ .name = "Vaux1",
+ .update_regid = AB8500_REGU_VAUX12_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0x30,
+ .hw_mode_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x20,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x20,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x20,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x80,
+ .vsel_regid[0] = AB8500_REGU_VAUX1_SEL,
+ .vsel_mask[0] = 0x0f,
+ .vsel_range[0] = vaux1_vaux2_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vaux1_vaux2_vsel),
+ },
+ [AB8500_VAUX2] = {
+ .name = "Vaux2",
+ .update_regid = AB8500_REGU_VAUX12_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x40,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x40,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x40,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x01,
+ .vsel_regid[0] = AB8500_REGU_VAUX2_SEL,
+ .vsel_mask[0] = 0x0f,
+ .vsel_range[0] = vaux1_vaux2_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vaux1_vaux2_vsel),
+ },
+ [AB8500_VAUX3] = {
+ .name = "Vaux3",
+ .update_regid = AB8500_REGU_VRF1_VAUX3_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL4,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x80,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x80,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x80,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x02,
+ .vsel_regid[0] = AB8500_REGU_VRF1_VAUX3_SEL,
+ .vsel_mask[0] = 0x07,
+ .vsel_range[0] = vaux3_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vaux3_vsel),
+ },
+ [AB8500_VINTCORE] = {
+ .name = "VintCore12",
+ .update_regid = AB8500_REGU_MISC1,
+ .update_mask = 0x44,
+ .update_val = {0x00, 0x04, 0x00, 0x44},
+ .vsel_regid[0] = AB8500_REGU_MISC1,
+ .vsel_mask[0] = 0x38,
+ .vsel_range[0] = vintcore12_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vintcore12_vsel),
+ },
+ [AB8500_VTVOUT] = {
+ .name = "VTVout",
+ .update_regid = AB8500_REGU_MISC1,
+ .update_mask = 0x82,
+ .update_val = {0x00, 0x02, 0x00, 0x82},
+ },
+ [AB8500_VAUDIO] = {
+ .name = "Vaudio",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x02,
+ .update_val = {0x00, 0x02, 0x00, 0x00},
+ },
+ [AB8500_VANAMIC1] = {
+ .name = "Vanamic1",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x08,
+ .update_val = {0x00, 0x08, 0x00, 0x00},
+ },
+ [AB8500_VANAMIC2] = {
+ .name = "Vanamic2",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x10,
+ .update_val = {0x00, 0x10, 0x00, 0x00},
+ },
+ [AB8500_VDMIC] = {
+ .name = "Vdmic",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x04,
+ .update_val = {0x00, 0x04, 0x00, 0x00},
+ },
+ [AB8500_VUSB] = {
+ .name = "Vusb",
+ .update_regid = AB8500_REGU_VUSB_CTRL,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x00, 0x03},
+ },
+ [AB8500_VOTG] = {
+ .name = "VOTG",
+ .update_regid = AB8500_REGU_OTG_SUPPLY_CTRL,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x00, 0x03},
+ },
+ [AB8500_VBUSBIS] = {
+ .name = "Vbusbis",
+ .update_regid = AB8500_REGU_OTG_SUPPLY_CTRL,
+ .update_mask = 0x08,
+ .update_val = {0x00, 0x08, 0x00, 0x00},
+ },
+};
+
+static int status_state;
+
+static int _get_voltage(struct regulator_volt_range const *volt_range,
+ u8 value, int *volt)
+{
+ u8 start = volt_range->start.value;
+ u8 end = volt_range->end.value;
+ u8 step = volt_range->step.value;
+
+ /* Check if witin range */
+ if (step == 0) {
+ if (value == start) {
+ *volt = volt_range->start.volt;
+ return 1;
+ }
+ } else {
+ if ((start <= value) && (value <= end)) {
+ if ((value - start)%step != 0)
+ return -EINVAL; /* invalid setting */
+ *volt = volt_range->start.volt
+ + volt_range->step.volt
+ *((value - start)/step);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int get_voltage(struct regulator_volt_range const *volt_range,
+ int volt_range_len,
+ u8 value)
+{
+ int volt;
+ int i, ret;
+
+ for (i = 0; i < volt_range_len; i++) {
+ ret = _get_voltage(&volt_range[i], value, &volt);
+ if (ret < 0)
+ break; /* invalid setting */
+ if (ret == 1)
+ return volt; /* successful */
+ }
+
+ return -EINVAL;
+}
+
+static int ab8500_regulator_status_print(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ int id, regid;
+ int i;
+ u8 val;
+ int err;
+
+ /* record current state */
+ ab8500_regulator_record_state(AB8500_REGULATOR_STATE_CURRENT);
+
+ /* check if chosen state is recorded */
+ if (!ab8500_register_state_saved[status_state]) {
+ seq_printf(s, "ab8500-regulator status is not recorded.\n");
+ goto exit;
+ }
+
+ /* print dump header */
+ err = seq_printf(s, "ab8500-regulator status:\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow\n");
+
+ /* print state */
+ err = seq_printf(s, "%12s\n",
+ regulator_state_name[status_state]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow\n");
+
+ /* print labels */
+ err = seq_printf(s,
+ "+-----------+----+--------------+-------------------------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "| name|man |auto |voltage |\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "+-----------+----+--------------+ +-----------------------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "| |mode|mode |0|1|2|3| | 1 | 2 | 3 |\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "+-----------+----+------+-+-+-+-+-+-------+-------+-------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+ /* dump registers */
+ for (id = 0; id < AB8500_NUM_REGULATORS; id++) {
+ /* print name */
+ err = seq_printf(s, "|%11s|",
+ ab8500_regulator[id].name);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ id, __LINE__);
+
+ /* print manual mode */
+ regid = ab8500_regulator[id].update_regid;
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].update_mask;
+ for (i = 0; i < 4; i++) {
+ if (val == ab8500_regulator[id].update_val[i])
+ break;
+ }
+ err = seq_printf(s, "%4s|",
+ update_val_name[i]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ id, __LINE__);
+
+ /* print auto mode */
+ regid = ab8500_regulator[id].hw_mode_regid;
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].hw_mode_mask;
+ for (i = 0; i < 4; i++) {
+ if (val == ab8500_regulator[id].hw_mode_val[i])
+ break;
+ }
+ err = seq_printf(s, "%6s|",
+ hw_mode_val_name[i]);
+ } else {
+ err = seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ id, __LINE__);
+
+ /* print valid bits */
+ for (i = 0; i < 4; i++) {
+ regid = ab8500_regulator[id].hw_valid_regid[i];
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].hw_valid_mask[i];
+ if (val)
+ err = seq_printf(s, "1|");
+ else
+ err = seq_printf(s, "0|");
+ } else {
+ err = seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+ }
+
+ /* print voltage selection */
+ regid = ab8500_regulator[id].vsel_sel_regid;
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].vsel_sel_mask;
+ for (i = 0; i < 3; i++) {
+ if (val == ab8500_regulator[id].vsel_sel_val[i])
+ break;
+ }
+ if (i < 3)
+ seq_printf(s, "%i|", i + 1);
+ else
+ seq_printf(s, "-|");
+ } else {
+ seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+
+ for (i = 0; i < 3; i++) {
+ int volt;
+
+ regid = ab8500_regulator[id].vsel_regid[i];
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].vsel_mask[i];
+ volt = get_voltage(
+ ab8500_regulator[id].vsel_range[i],
+ ab8500_regulator[id].vsel_range_len[i],
+ val);
+ seq_printf(s, "%7i|", volt);
+ } else {
+ seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+ }
+
+ err = seq_printf(s, "\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+
+ }
+ err = seq_printf(s,
+ "+-----------+----+------+-+-+-+-+-+-------+-------+-------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "Note! In HW mode, voltage selection is controlled by HW.\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+
+exit:
+ return 0;
+}
+
+static int ab8500_regulator_status_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+
+ /* copy user data */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* convert */
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+
+ /* set suspend force setting */
+ if (user_val > NUM_REGULATOR_STATE) {
+ dev_err(dev, "debugfs error input > number of states\n");
+ return -EINVAL;
+ }
+
+ status_state = user_val;
+
+ return buf_size;
+}
+
+
+static int ab8500_regulator_status_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_regulator_status_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_regulator_status_fops = {
+ .open = ab8500_regulator_status_open,
+ .write = ab8500_regulator_status_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+#ifdef CONFIG_PM
+
+struct ab8500_force_reg {
+ char *name;
+ u8 bank;
+ u8 addr;
+ u8 mask;
+ u8 val;
+ bool restore;
+ u8 restore_val;
+};
+
+static struct ab8500_force_reg ab8500_force_reg[] = {
+ {
+ /*
+ * SysClkCtrl
+ * OTP: 0x00, HSI: 0x06, suspend: 0x00/0x07 (value/mask)
+ * [ 2] USBClkEna = disable SysClk path to USB block
+ * [ 1] TVoutClkEna = disable 27Mhz clock to TVout block
+ * [ 0] TVoutPllEna = disable TVout pll
+ * (generate 27Mhz from SysClk)
+ */
+ .name = "SysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x0c,
+ .mask = 0x07,
+ .val = 0x00,
+ },
+ {
+ /*
+ * ReguSysClkReq1HPValid2
+ * OTP: 0x03, HSI: 0x40, suspend: 0x60/0x70 (value/mask)
+ * [ 5] VextSupply2SysClkReq1HPValid = Vext2 set by SysClkReq1
+ */
+ .name = "ReguSysClkReq1HPValid2",
+ .bank = 0x03,
+ .addr = 0x08,
+ .mask = 0x20, /* test and compare with 0x7f */
+ .val = 0x20,
+ },
+ {
+ /*
+ * ReguRequestCtrl3
+ * OTP: 0x00, HSI: 0x00, suspend: 0x05/0x0f (value/mask)
+ * [1:0] VExtSupply2RequestCtrl[1:0] = VExt2 set in HP/OFF mode
+ */
+ .name = "ReguRequestCtrl3",
+ .bank = 0x03,
+ .addr = 0x05,
+ .mask = 0x03, /* test and compare with 0xff */
+ .val = 0x01,
+ },
+ {
+ /*
+ * VsimSysClkCtrl
+ * OTP: 0x01, HSI: 0x21, suspend: 0x01/0xff (value/mask)
+ * [ 7] VsimSysClkReq8Valid = no connection
+ * [ 6] VsimSysClkReq7Valid = no connection
+ * [ 5] VsimSysClkReq6Valid = no connection
+ * [ 4] VsimSysClkReq5Valid = no connection
+ * [ 3] VsimSysClkReq4Valid = no connection
+ * [ 2] VsimSysClkReq3Valid = no connection
+ * [ 1] VsimSysClkReq2Valid = no connection
+ * [ 0] VsimSysClkReq1Valid = Vsim set by SysClkReq1
+ */
+ .name = "VsimSysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x33,
+ .mask = 0xff,
+ .val = 0x01,
+ },
+ {
+ /*
+ * SysUlpClkCtrl1
+ * OTP: 0x00, HSI: 0x00, suspend: 0x00/0x0f (value/mask)
+ * [ 3] 4500SysClkReq = inactive
+ * [ 2] UlpClkReq = inactive
+ * [1:0] SysUlpClkIntSel[1:0] = no internal clock switching.
+ * Internal clock is SysClk.
+ */
+ .name = "SysUlpClkCtrl1",
+ .bank = 0x02,
+ .addr = 0x0b,
+ .mask = 0x0f,
+ .val = 0x00,
+ },
+ {
+ /*
+ * ExtSupplyRegu (HSI: 0x2a on v2-v40?)
+ * OTP: 0x15, HSI: 0x28, suspend: 0x28/0x3f (value/mask)
+ * [5:4] VExtSupply3Regu[1:0] = 10 = Vext3 off
+ * [3:2] VExtSupply2Regu[1:0] = 10 = Vext2 in HW control
+ * [1:0] VExtSupply1Regu[1:0] = 00 = Vext1 off
+ */
+ .name = "ExtSupplyRegu",
+ .bank = 0x04,
+ .addr = 0x08,
+ .mask = 0x3f,
+ .val = 0x08,
+ },
+ {
+ /*
+ * TVoutCtrl
+ * OTP: N/A, HSI: N/A, suspend: 0x00/0x03 (value/mask)
+ * [ 2] PlugTvOn = plug/unplug detection disabled
+ * [1:0] TvoutDacCtrl[1:0] = "0" forced on DAC input (test)
+ */
+ .name = "TVoutCtrl",
+ .bank = 0x06,
+ .addr = 0x80,
+ .mask = 0x03,
+ .val = 0x00,
+ },
+};
+
+void ab8500_regulator_debug_force(void)
+{
+ int ret, i;
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_SUSPEND);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record suspend state.\n");
+
+ /* check if registers should be forced */
+ if (!setting_suspend_force)
+ goto exit;
+
+ /*
+ * Optimize href v2_v50_pwr board for ApSleep/ApDeepSleep
+ * power consumption measurements
+ */
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_force_reg); i++) {
+ dev_vdbg(&pdev->dev, "Save and set %s: "
+ "0x%02x, 0x%02x, 0x%02x, 0x%02x.\n",
+ ab8500_force_reg[i].name,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].val);
+
+ /* assume that register should be restored */
+ ab8500_force_reg[i].restore = true;
+
+ /* get register value before forcing it */
+ ret = abx500_get_register_interruptible(&pdev->dev,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ &ab8500_force_reg[i].restore_val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read %s.\n",
+ ab8500_force_reg[i].name);
+ ab8500_force_reg[i].restore = false;
+ break;
+ }
+
+ /* force register value */
+ ret = abx500_mask_and_set_register_interruptible(&pdev->dev,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].val);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to write %s.\n",
+ ab8500_force_reg[i].name);
+ ab8500_force_reg[i].restore = false;
+ }
+ }
+
+exit:
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(
+ AB8500_REGULATOR_STATE_SUSPEND_CORE);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record suspend state.\n");
+
+ return;
+}
+
+void ab8500_regulator_debug_restore(void)
+{
+ int ret, i;
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_RESUME_CORE);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record resume state.\n");
+ for (i = ARRAY_SIZE(ab8500_force_reg) - 1; i >= 0; i--) {
+ /* restore register value */
+ if (ab8500_force_reg[i].restore) {
+ ret = abx500_mask_and_set_register_interruptible(
+ &pdev->dev,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].restore_val);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to restore %s.\n",
+ ab8500_force_reg[i].name);
+ dev_vdbg(&pdev->dev, "Restore %s: "
+ "0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ ab8500_force_reg[i].name,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].restore_val);
+ }
+ }
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_RESUME);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record resume state.\n");
+
+ return;
+}
+
+#endif
+
+static int ab8500_regulator_suspend_force_show(struct seq_file *s, void *p)
+{
+ /* print suspend standby status */
+ if (setting_suspend_force)
+ return seq_printf(s, "suspend force enabled\n");
+ else
+ return seq_printf(s, "no suspend force\n");
+}
+
+static int ab8500_regulator_suspend_force_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+
+ /* copy user data */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* convert */
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+
+ /* set suspend force setting */
+ if (user_val > 1) {
+ dev_err(dev, "debugfs error input > 1\n");
+ return -EINVAL;
+ }
+
+ if (user_val)
+ setting_suspend_force = true;
+ else
+ setting_suspend_force = false;
+
+ return buf_size;
+}
+
+static int ab8500_regulator_suspend_force_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_regulator_suspend_force_show,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_regulator_suspend_force_fops = {
+ .open = ab8500_regulator_suspend_force_open,
+ .write = ab8500_regulator_suspend_force_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static struct dentry *ab8500_regulator_dir;
+static struct dentry *ab8500_regulator_dump_file;
+static struct dentry *ab8500_regulator_status_file;
+static struct dentry *ab8500_regulator_suspend_force_file;
+
+static int __devinit ab8500_regulator_debug_probe(struct platform_device *plf)
+{
+ void __iomem *boot_info_backupram;
+ int ret;
+
+ /* setup dev pointers */
+ dev = &plf->dev;
+ pdev = plf;
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_INIT);
+ if (ret < 0)
+ dev_err(&plf->dev, "Failed to record init state.\n");
+
+ /* make suspend-force default if board profile is v5x-power */
+ boot_info_backupram = ioremap(BOOT_INFO_BACKUPRAM1, 0x4);
+
+ if (boot_info_backupram) {
+ u8 board_profile;
+ board_profile = readb(
+ boot_info_backupram + BOARD_PROFILE_BACKUPRAM1);
+ dev_dbg(dev, "Board profile is 0x%02x\n", board_profile);
+
+ if (board_profile >= OPTION_BOARD_VERSION_V5X)
+ setting_suspend_force = true;
+
+ iounmap(boot_info_backupram);
+ } else {
+ dev_err(dev, "Failed to read backupram.\n");
+ }
+
+ /* create directory */
+ ab8500_regulator_dir = debugfs_create_dir("ab8500-regulator", NULL);
+ if (!ab8500_regulator_dir)
+ goto exit_no_debugfs;
+
+ /* create "dump" file */
+ ab8500_regulator_dump_file = debugfs_create_file("dump",
+ S_IRUGO, ab8500_regulator_dir, &plf->dev,
+ &ab8500_regulator_dump_fops);
+ if (!ab8500_regulator_dump_file)
+ goto exit_destroy_dir;
+
+ /* create "status" file */
+ ab8500_regulator_status_file = debugfs_create_file("status",
+ S_IRUGO, ab8500_regulator_dir, &plf->dev,
+ &ab8500_regulator_status_fops);
+ if (!ab8500_regulator_status_file)
+ goto exit_destroy_dump_file;
+
+ /*
+ * create "suspend-force-v5x" file. As indicated by the name, this is
+ * only applicable for v2_v5x hardware versions.
+ */
+ ab8500_regulator_suspend_force_file = debugfs_create_file(
+ "suspend-force-v5x",
+ S_IRUGO, ab8500_regulator_dir, &plf->dev,
+ &ab8500_regulator_suspend_force_fops);
+ if (!ab8500_regulator_suspend_force_file)
+ goto exit_destroy_status_file;
+
+ return 0;
+
+exit_destroy_status_file:
+ debugfs_remove(ab8500_regulator_status_file);
+exit_destroy_dump_file:
+ debugfs_remove(ab8500_regulator_dump_file);
+exit_destroy_dir:
+ debugfs_remove(ab8500_regulator_dir);
+exit_no_debugfs:
+ dev_err(&plf->dev, "failed to create debugfs entries.\n");
+ return -ENOMEM;
+}
+
+static int __devexit ab8500_regulator_debug_remove(struct platform_device *plf)
+{
+ debugfs_remove(ab8500_regulator_suspend_force_file);
+ debugfs_remove(ab8500_regulator_status_file);
+ debugfs_remove(ab8500_regulator_dump_file);
+ debugfs_remove(ab8500_regulator_dir);
+
+ return 0;
+}
+
+static struct platform_driver ab8500_regulator_debug_driver = {
+ .driver = {
+ .name = "ab8500-regulator-debug",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab8500_regulator_debug_probe,
+ .remove = __devexit_p(ab8500_regulator_debug_remove),
+};
+
+static int __init ab8500_regulator_debug_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&ab8500_regulator_debug_driver);
+ if (ret)
+ pr_err("Failed to register ab8500 regulator: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(ab8500_regulator_debug_init);
+
+static void __exit ab8500_regulator_debug_exit(void)
+{
+ platform_driver_unregister(&ab8500_regulator_debug_driver);
+}
+module_exit(ab8500_regulator_debug_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com");
+MODULE_DESCRIPTION("AB8500 Regulator Debug");
+MODULE_ALIAS("platform:ab8500-regulator-debug");
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
new file mode 100644
index 00000000000..e281c117adb
--- /dev/null
+++ b/drivers/regulator/ab8500.c
@@ -0,0 +1,1172 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ * Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ *
+ * AB8500 peripheral regulators
+ *
+ * AB8500 supports the following regulators:
+ * VAUX1/2/3, VINTCORE, VTVOUT, VUSB, VAUDIO, VAMIC1/2, VDMIC, VANA
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/ab8500.h>
+#include <linux/mfd/ab8500/ab8500-gpio.h> /* for sysclkreq pins */
+#include <mach/gpio.h> /* for sysclkreq pins */
+
+/**
+ * struct ab8500_regulator_info - ab8500 regulator information
+ * @dev: device pointer
+ * @desc: regulator description
+ * @regulator_dev: regulator device
+ * @is_enabled: status of regulator (on/off)
+ * @max_uV: maximum voltage (for variable voltage supplies)
+ * @min_uV: minimum voltage (for variable voltage supplies)
+ * @fixed_uV: typical voltage (for fixed voltage supplies)
+ * @load_lp_uA: maximum load in idle (low power) mode
+ * @update_bank: bank to control on/off
+ * @update_reg: register to control on/off
+ * @update_mask: mask to enable/disable and set mode of regulator
+ * @update_val: bits holding the regulator current mode
+ * @update_val_idle: bits to enable the regulator in idle (low power) mode
+ * @update_val_normal: bits to enable the regulator in normal (high power) mode
+ * @voltage_bank: bank to control regulator voltage
+ * @voltage_reg: register to control regulator voltage
+ * @voltage_mask: mask to control regulator voltage
+ * @voltages: supported voltage table
+ * @voltages_len: number of supported voltages for the regulator
+ * @delay: startup delay in ms
+ * @gpio_pin: gpio pin number (for sysclkreq regulator only)
+ */
+struct ab8500_regulator_info {
+ struct device *dev;
+ struct regulator_desc desc;
+ struct regulator_dev *regulator;
+ bool is_enabled;
+ int max_uV;
+ int min_uV;
+ int fixed_uV;
+ int load_lp_uA;
+ u8 update_bank;
+ u8 update_reg;
+ u8 update_mask;
+ u8 update_val;
+ u8 update_val_idle;
+ u8 update_val_normal;
+ u8 voltage_bank;
+ u8 voltage_reg;
+ u8 voltage_mask;
+ int const *voltages;
+ int voltages_len;
+ unsigned int delay;
+ unsigned int gpio_pin;
+};
+
+/* voltage tables for the vauxn/vintcore supplies */
+static const int ldo_vauxn_voltages[] = {
+ 1100000,
+ 1200000,
+ 1300000,
+ 1400000,
+ 1500000,
+ 1800000,
+ 1850000,
+ 1900000,
+ 2500000,
+ 2650000,
+ 2700000,
+ 2750000,
+ 2800000,
+ 2900000,
+ 3000000,
+ 3300000,
+};
+
+static const int ldo_vaux3_voltages[] = {
+ 1200000,
+ 1500000,
+ 1800000,
+ 2100000,
+ 2500000,
+ 2750000,
+ 2790000,
+ 2910000,
+};
+
+static const int ldo_vintcore_voltages[] = {
+ 1200000,
+ 1225000,
+ 1250000,
+ 1275000,
+ 1300000,
+ 1325000,
+ 1350000,
+};
+
+static int ab8500_regulator_enable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->update_bank, info->update_reg,
+ info->update_mask, info->update_val);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set enable bits for regulator\n");
+
+ msleep(info->delay);
+
+ info->is_enabled = true;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-enable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, info->update_val);
+
+ return ret;
+}
+
+static int ab8500_regulator_disable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->update_bank, info->update_reg,
+ info->update_mask, 0x0);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set disable bits for regulator\n");
+
+ msleep(info->delay);
+
+ info->is_enabled = false;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-disable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, 0x0);
+
+ return ret;
+}
+
+static unsigned int ab8500_regulator_get_optimum_mode(
+ struct regulator_dev *rdev, int input_uV,
+ int output_uV, int load_uA)
+{
+ unsigned int mode;
+
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ if (load_uA <= info->load_lp_uA)
+ mode = REGULATOR_MODE_IDLE;
+ else
+ mode = REGULATOR_MODE_NORMAL;
+
+ return mode;
+}
+
+static int ab8500_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int ret = 0;
+
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ info->update_val = info->update_val_normal;
+ break;
+ case REGULATOR_MODE_IDLE:
+ info->update_val = info->update_val_idle;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (info->is_enabled) {
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->update_bank, info->update_reg,
+ info->update_mask, info->update_val);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set regulator mode\n");
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-set_mode (bank, reg, mask, value): "
+ "0x%x, 0x%x, 0x%x, 0x%x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, info->update_val);
+ }
+
+ return ret;
+}
+
+static unsigned int ab8500_regulator_get_mode(struct regulator_dev *rdev)
+{
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ if (info->update_val == info->update_val_normal)
+ ret = REGULATOR_MODE_NORMAL;
+ else if (info->update_val == info->update_val_idle)
+ ret = REGULATOR_MODE_IDLE;
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ u8 regval;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = abx500_get_register_interruptible(info->dev,
+ info->update_bank, info->update_reg, &regval);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read 0x%x register\n", info->update_reg);
+ return ret;
+ }
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-is_enabled (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
+ " 0x%x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, regval);
+
+ if (regval & info->update_mask)
+ info->is_enabled = true;
+ else
+ info->is_enabled = false;
+
+ return info->is_enabled;
+}
+
+static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector)
+{
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ /* return the uV for the fixed regulators */
+ if (info->fixed_uV)
+ return info->fixed_uV;
+
+ if (selector >= info->voltages_len)
+ return -EINVAL;
+
+ return info->voltages[selector];
+}
+
+static int ab8500_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ int ret, val;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ u8 regval;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = abx500_get_register_interruptible(info->dev,
+ info->voltage_bank, info->voltage_reg, &regval);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read voltage reg for regulator\n");
+ return ret;
+ }
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-get_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
+ " 0x%x\n",
+ info->desc.name, info->voltage_bank, info->voltage_reg,
+ info->voltage_mask, regval);
+
+ /* vintcore has a different layout */
+ val = regval & info->voltage_mask;
+ if (info->desc.id == AB8500_LDO_INTCORE)
+ ret = info->voltages[val >> 0x3];
+ else
+ ret = info->voltages[val];
+
+ return ret;
+}
+
+static int ab8500_get_best_voltage_index(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ int i;
+
+ /* check the supported voltage */
+ for (i = 0; i < info->voltages_len; i++) {
+ if ((info->voltages[i] >= min_uV) &&
+ (info->voltages[i] <= max_uV))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int ab8500_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ u8 regval;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ /* get the appropriate voltages within the range */
+ ret = ab8500_get_best_voltage_index(rdev, min_uV, max_uV);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't get best voltage for regulator\n");
+ return ret;
+ }
+
+ /* vintcore register has a different layout */
+ if (info->desc.id == AB8500_LDO_INTCORE)
+ regval = ((u8)ret) << 3;
+ else
+ regval = (u8)ret;
+
+ /* set the registers for the request */
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->voltage_bank, info->voltage_reg,
+ info->voltage_mask, regval);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set voltage reg for regulator\n");
+
+ msleep(info->delay);
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-set_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
+ " 0x%x\n",
+ info->desc.name, info->voltage_bank, info->voltage_reg,
+ info->voltage_mask, regval);
+
+ return ret;
+}
+
+static struct regulator_ops ab8500_regulator_volt_mode_ops = {
+ .enable = ab8500_regulator_enable,
+ .disable = ab8500_regulator_disable,
+ .is_enabled = ab8500_regulator_is_enabled,
+ .get_optimum_mode = ab8500_regulator_get_optimum_mode,
+ .set_mode = ab8500_regulator_set_mode,
+ .get_mode = ab8500_regulator_get_mode,
+ .get_voltage = ab8500_regulator_get_voltage,
+ .set_voltage = ab8500_regulator_set_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+static int ab8500_fixed_get_voltage(struct regulator_dev *rdev)
+{
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ return info->fixed_uV;
+}
+
+static struct regulator_ops ab8500_regulator_mode_ops = {
+ .enable = ab8500_regulator_enable,
+ .disable = ab8500_regulator_disable,
+ .is_enabled = ab8500_regulator_is_enabled,
+ .get_optimum_mode = ab8500_regulator_get_optimum_mode,
+ .set_mode = ab8500_regulator_set_mode,
+ .get_mode = ab8500_regulator_get_mode,
+ .get_voltage = ab8500_fixed_get_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+static struct regulator_ops ab8500_regulator_ops = {
+ .enable = ab8500_regulator_enable,
+ .disable = ab8500_regulator_disable,
+ .is_enabled = ab8500_regulator_is_enabled,
+ .get_voltage = ab8500_fixed_get_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+static int ab8500_sysclkreq_enable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = ab8500_gpio_config_select(info->dev, info->gpio_pin, false);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set sysclkreq pin selection\n");
+ return ret;
+ }
+
+ info->is_enabled = true;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-enable (gpio_pin, gpio_select): %i, false\n",
+ info->desc.name, info->gpio_pin);
+
+ return ret;
+}
+
+static int ab8500_sysclkreq_disable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = ab8500_gpio_config_select(info->dev, info->gpio_pin, true);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set gpio pin selection\n");
+ return ret;
+ }
+
+ info->is_enabled = false;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-disable (gpio_pin, gpio_select): %i, true\n",
+ info->desc.name, info->gpio_pin);
+
+ return ret;
+}
+
+static int ab8500_sysclkreq_is_enabled(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ bool gpio_select;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = ab8500_gpio_config_get_select(info->dev, info->gpio_pin,
+ &gpio_select);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read gpio pin selection\n");
+ return ret;
+ }
+
+ info->is_enabled = !gpio_select;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-is_enabled (gpio_pin, is_enabled): %i, %i\n",
+ info->desc.name, info->gpio_pin, !gpio_select);
+
+ return info->is_enabled;
+}
+
+static struct regulator_ops ab8500_sysclkreq_ops = {
+ .enable = ab8500_sysclkreq_enable,
+ .disable = ab8500_sysclkreq_disable,
+ .is_enabled = ab8500_sysclkreq_is_enabled,
+ .get_voltage = ab8500_fixed_get_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+static struct ab8500_regulator_info
+ ab8500_regulator_info[AB8500_NUM_REGULATORS] = {
+ /*
+ * Regulators with variable voltage and normal/idle modes
+ */
+ [AB8500_LDO_AUX1] = {
+ .desc = {
+ .name = "LDO-AUX1",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX1,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x04,
+ .update_reg = 0x09,
+ .update_mask = 0x03,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x1f,
+ .voltage_mask = 0x0f,
+ .voltages = ldo_vauxn_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ [AB8500_LDO_AUX2] = {
+ .desc = {
+ .name = "LDO-AUX2",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX2,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x04,
+ .update_reg = 0x09,
+ .update_mask = 0x0c,
+ .update_val = 0x04,
+ .update_val_idle = 0x0c,
+ .update_val_normal = 0x04,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x20,
+ .voltage_mask = 0x0f,
+ .voltages = ldo_vauxn_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ [AB8500_LDO_AUX3] = {
+ .desc = {
+ .name = "LDO-AUX3",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX3,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x04,
+ .update_reg = 0x0a,
+ .update_mask = 0x03,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x21,
+ .voltage_mask = 0x07,
+ .voltages = ldo_vaux3_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vaux3_voltages),
+ },
+ [AB8500_LDO_INTCORE] = {
+ .desc = {
+ .name = "LDO-INTCORE",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_INTCORE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x03,
+ .update_reg = 0x80,
+ .update_mask = 0x44,
+ .update_val = 0x04,
+ .update_val_idle = 0x44,
+ .update_val_normal = 0x04,
+ .voltage_bank = 0x03,
+ .voltage_reg = 0x80,
+ .voltage_mask = 0x38,
+ .voltages = ldo_vintcore_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vintcore_voltages),
+ },
+
+ /*
+ * Regulators with fixed voltage and normal/idle modes
+ */
+ [AB8500_LDO_TVOUT] = {
+ .desc = {
+ .name = "LDO-TVOUT",
+ .ops = &ab8500_regulator_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_TVOUT,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .delay = 10,
+ .fixed_uV = 2000000,
+ .load_lp_uA = 1000,
+ .update_bank = 0x03,
+ .update_reg = 0x80,
+ .update_mask = 0x82,
+ .update_val = 0x02,
+ .update_val_idle = 0x82,
+ .update_val_normal = 0x02,
+ },
+
+ /*
+ * Regulators with fixed voltage and normal mode
+ */
+ [AB8500_LDO_AUDIO] = {
+ .desc = {
+ .name = "LDO-AUDIO",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUDIO,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2000000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x02,
+ .update_val = 0x02,
+ },
+ [AB8500_LDO_ANAMIC1] = {
+ .desc = {
+ .name = "LDO-ANAMIC1",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANAMIC1,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2050000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x08,
+ .update_val = 0x08,
+ },
+ [AB8500_LDO_ANAMIC2] = {
+ .desc = {
+ .name = "LDO-ANAMIC2",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANAMIC2,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2050000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x10,
+ .update_val = 0x10,
+ },
+ [AB8500_LDO_DMIC] = {
+ .desc = {
+ .name = "LDO-DMIC",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_DMIC,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1800000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x04,
+ .update_val = 0x04,
+ },
+
+ /*
+ * Regulators with fixed voltage and normal/idle modes
+ */
+ [AB8500_LDO_ANA] = {
+ .desc = {
+ .name = "LDO-ANA",
+ .ops = &ab8500_regulator_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANA,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1200000,
+ .load_lp_uA = 1000,
+ .update_bank = 0x04,
+ .update_reg = 0x06,
+ .update_mask = 0x0c,
+ .update_val = 0x04,
+ .update_val_idle = 0x0c,
+ .update_val_normal = 0x04,
+ },
+
+ /*
+ * SysClkReq regulators
+ */
+ [AB8500_SYSCLKREQ_2] = {
+ .desc = {
+ .name = "SYSCLKREQ-2",
+ .ops = &ab8500_sysclkreq_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_SYSCLKREQ_2,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1, /* bogus value */
+ .gpio_pin = AB8500_PIN_GPIO1,
+ },
+ [AB8500_SYSCLKREQ_4] = {
+ .desc = {
+ .name = "SYSCLKREQ-4",
+ .ops = &ab8500_sysclkreq_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_SYSCLKREQ_4,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1, /* bogus value */
+ .gpio_pin = AB8500_PIN_GPIO3,
+ },
+};
+
+struct ab8500_reg_init {
+ u8 bank;
+ u8 addr;
+ u8 mask;
+};
+
+#define REG_INIT(_id, _bank, _addr, _mask) \
+ [_id] = { \
+ .bank = _bank, \
+ .addr = _addr, \
+ .mask = _mask, \
+ }
+
+static struct ab8500_reg_init ab8500_reg_init[] = {
+ /*
+ * 0x30, VanaRequestCtrl
+ * 0x0c, VpllRequestCtrl
+ * 0xc0, VextSupply1RequestCtrl
+ */
+ REG_INIT(AB8500_REGUREQUESTCTRL2, 0x03, 0x04, 0xfc),
+ /*
+ * 0x03, VextSupply2RequestCtrl
+ * 0x0c, VextSupply3RequestCtrl
+ * 0x30, Vaux1RequestCtrl
+ * 0xc0, Vaux2RequestCtrl
+ */
+ REG_INIT(AB8500_REGUREQUESTCTRL3, 0x03, 0x05, 0xff),
+ /*
+ * 0x03, Vaux3RequestCtrl
+ * 0x04, SwHPReq
+ */
+ REG_INIT(AB8500_REGUREQUESTCTRL4, 0x03, 0x06, 0x07),
+ /*
+ * 0x01, Vsmps1SysClkReq1HPValid
+ * 0x02, Vsmps2SysClkReq1HPValid
+ * 0x04, Vsmps3SysClkReq1HPValid
+ * 0x08, VanaSysClkReq1HPValid
+ * 0x10, VpllSysClkReq1HPValid
+ * 0x20, Vaux1SysClkReq1HPValid
+ * 0x40, Vaux2SysClkReq1HPValid
+ * 0x80, Vaux3SysClkReq1HPValid
+ */
+ REG_INIT(AB8500_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xff),
+ /*
+ * 0x10, VextSupply1SysClkReq1HPValid
+ * 0x20, VextSupply2SysClkReq1HPValid
+ * 0x40, VextSupply3SysClkReq1HPValid
+ */
+ REG_INIT(AB8500_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x70),
+ /*
+ * 0x08, VanaHwHPReq1Valid
+ * 0x20, Vaux1HwHPReq1Valid
+ * 0x40, Vaux2HwHPReq1Valid
+ * 0x80, Vaux3HwHPReq1Valid
+ */
+ REG_INIT(AB8500_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xe8),
+ /*
+ * 0x01, VextSupply1HwHPReq1Valid
+ * 0x02, VextSupply2HwHPReq1Valid
+ * 0x04, VextSupply3HwHPReq1Valid
+ */
+ REG_INIT(AB8500_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x07),
+ /*
+ * 0x08, VanaHwHPReq2Valid
+ * 0x20, Vaux1HwHPReq2Valid
+ * 0x40, Vaux2HwHPReq2Valid
+ * 0x80, Vaux3HwHPReq2Valid
+ */
+ REG_INIT(AB8500_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xe8),
+ /*
+ * 0x01, VextSupply1HwHPReq2Valid
+ * 0x02, VextSupply2HwHPReq2Valid
+ * 0x04, VextSupply3HwHPReq2Valid
+ */
+ REG_INIT(AB8500_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x07),
+ /*
+ * 0x20, VanaSwHPReqValid
+ * 0x80, Vaux1SwHPReqValid
+ */
+ REG_INIT(AB8500_REGUSWHPREQVALID1, 0x03, 0x0d, 0xa0),
+ /*
+ * 0x01, Vaux2SwHPReqValid
+ * 0x02, Vaux3SwHPReqValid
+ * 0x04, VextSupply1SwHPReqValid
+ * 0x08, VextSupply2SwHPReqValid
+ * 0x10, VextSupply3SwHPReqValid
+ */
+ REG_INIT(AB8500_REGUSWHPREQVALID2, 0x03, 0x0e, 0x1f),
+ /*
+ * 0x02, SysClkReq2Valid1
+ * ...
+ * 0x80, SysClkReq8Valid1
+ */
+ REG_INIT(AB8500_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xfe),
+ /*
+ * 0x02, SysClkReq2Valid2
+ * ...
+ * 0x80, SysClkReq8Valid2
+ */
+ REG_INIT(AB8500_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xfe),
+ /*
+ * 0x02, VTVoutEna
+ * 0x04, Vintcore12Ena
+ * 0x38, Vintcore12Sel
+ * 0x40, Vintcore12LP
+ * 0x80, VTVoutLP
+ */
+ REG_INIT(AB8500_REGUMISC1, 0x03, 0x80, 0xfe),
+ /*
+ * 0x02, VaudioEna
+ * 0x04, VdmicEna
+ * 0x08, Vamic1Ena
+ * 0x10, Vamic2Ena
+ */
+ REG_INIT(AB8500_VAUDIOSUPPLY, 0x03, 0x83, 0x1e),
+ /*
+ * 0x01, Vamic1_dzout
+ * 0x02, Vamic2_dzout
+ */
+ REG_INIT(AB8500_REGUCTRL1VAMIC, 0x03, 0x84, 0x03),
+ /*
+ * 0x0c, VBBNRegu
+ * 0x03, VBBPRegu
+ * NOTE! PRCMU register
+ */
+ REG_INIT(AB8500_ARMREGU2, 0x04, 0x01, 0x0f),
+ /*
+ * 0x0c, VBBPSel1
+ * 0x03, VBBNSel1
+ * NOTE! PRCMU register
+ */
+ REG_INIT(AB8500_VBBSEL1, 0x04, 0x11, 0x0f),
+ /*
+ * 0x0c, VBBNSel2
+ * 0x03, VBBPSel2
+ * NOTE! PRCMU register
+ */
+ REG_INIT(AB8500_VBBSEL2, 0x04, 0x12, 0x0f),
+ /*
+ * 0x03, Vsmps1Regu
+ * 0x0c, Vsmps1SelCtrl
+ */
+ REG_INIT(AB8500_VSMPS1REGU, 0x04, 0x03, 0x0f),
+ /*
+ * 0x03, Vsmps2Regu
+ * 0x0c, Vsmps2SelCtrl
+ */
+ REG_INIT(AB8500_VSMPS2REGU, 0x04, 0x04, 0x0f),
+ /*
+ * 0x03, Vsmps3Regu
+ * 0x0c, Vsmps3SelCtrl
+ * NOTE! PRCMU register
+ */
+ REG_INIT(AB8500_VSMPS3REGU, 0x04, 0x05, 0x0f),
+ /*
+ * 0x0c, VanaRegu
+ * 0x03, VpllRegu
+ */
+ REG_INIT(AB8500_VPLLVANAREGU, 0x04, 0x06, 0x0f),
+ /*
+ * 0x01, VrefDDREna
+ * 0x02, VrefDDRSleepMode
+ */
+ REG_INIT(AB8500_VREFDDR, 0x04, 0x07, 0x03),
+ /*
+ * 0x03, VextSupply1Regu
+ * 0x0c, VextSupply2Regu
+ * 0x30, VextSupply3Regu
+ * 0x40, ExtSupply2Bypass
+ * 0x80, ExtSupply3Bypass
+ */
+ REG_INIT(AB8500_EXTSUPPLYREGU, 0x04, 0x08, 0xff),
+ /*
+ * 0x03, Vaux1Regu
+ * 0x0c, Vaux2Regu
+ */
+ REG_INIT(AB8500_VAUX12REGU, 0x04, 0x09, 0x0f),
+ /*
+ * 0x0c, Vrf1Regu
+ * 0x03, Vaux3Regu
+ */
+ REG_INIT(AB8500_VRF1VAUX3REGU, 0x04, 0x0a, 0x0f),
+ /*
+ * 0x3f, Vsmps1Sel1
+ */
+ REG_INIT(AB8500_VSMPS1SEL1, 0x04, 0x13, 0x3f),
+ /*
+ * 0x7f, Vsmps3Sel1
+ */
+ REG_INIT(AB8500_VSMPS3SEL1, 0x04, 0x1b, 0x7f),
+ /*
+ * 0x7f, Vsmps3Sel2
+ */
+ REG_INIT(AB8500_VSMPS3SEL2, 0x04, 0x1c, 0x7f),
+ /*
+ * 0x0f, Vaux1Sel
+ */
+ REG_INIT(AB8500_VAUX1SEL, 0x04, 0x1f, 0x0f),
+ /*
+ * 0x0f, Vaux2Sel
+ */
+ REG_INIT(AB8500_VAUX2SEL, 0x04, 0x20, 0x0f),
+ /*
+ * 0x07, Vaux3Sel
+ */
+ REG_INIT(AB8500_VRF1VAUX3SEL, 0x04, 0x21, 0x07),
+ /*
+ * 0x01, VextSupply12LP
+ */
+ REG_INIT(AB8500_REGUCTRL2SPARE, 0x04, 0x22, 0x01),
+ /*
+ * 0x04, Vaux1Disch
+ * 0x08, Vaux2Disch
+ * 0x10, Vaux3Disch
+ * 0x20, Vintcore12Disch
+ * 0x40, VTVoutDisch
+ * 0x80, VaudioDisch
+ */
+ REG_INIT(AB8500_REGUCTRLDISCH, 0x04, 0x43, 0xfc),
+ /*
+ * 0x02, VanaDisch
+ * 0x04, VdmicPullDownEna
+ * 0x10, VdmicDisch
+ */
+ REG_INIT(AB8500_REGUCTRLDISCH2, 0x04, 0x44, 0x16),
+};
+
+static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *pdata;
+ int i, err;
+
+ if (!ab8500) {
+ dev_err(&pdev->dev, "null mfd parent\n");
+ return -EINVAL;
+ }
+ pdata = dev_get_platdata(ab8500->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "null pdata\n");
+ return -EINVAL;
+ }
+
+ /* make sure the platform data has the correct size */
+ if (pdata->num_regulator != ARRAY_SIZE(ab8500_regulator_info)) {
+ dev_err(&pdev->dev, "Configuration error: size mismatch.\n");
+ return -EINVAL;
+ }
+
+ /* initialize registers */
+ for (i = 0; i < pdata->num_regulator_reg_init; i++) {
+ int id;
+ u8 value;
+
+ id = pdata->regulator_reg_init[i].id;
+ value = pdata->regulator_reg_init[i].value;
+
+ /* check for configuration errors */
+ if (id >= AB8500_NUM_REGULATOR_REGISTERS) {
+ dev_err(&pdev->dev,
+ "Configuration error: id outside range.\n");
+ return -EINVAL;
+ }
+ if (value & ~ab8500_reg_init[id].mask) {
+ dev_err(&pdev->dev,
+ "Configuration error: value outside mask.\n");
+ return -EINVAL;
+ }
+
+ /* initialize register */
+ err = abx500_mask_and_set_register_interruptible(&pdev->dev,
+ ab8500_reg_init[id].bank,
+ ab8500_reg_init[id].addr,
+ ab8500_reg_init[id].mask,
+ value);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "Failed to initialize 0x%02x, 0x%02x.\n",
+ ab8500_reg_init[id].bank,
+ ab8500_reg_init[id].addr);
+ return err;
+ }
+ dev_vdbg(&pdev->dev,
+ " init: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ ab8500_reg_init[id].bank,
+ ab8500_reg_init[id].addr,
+ ab8500_reg_init[id].mask,
+ value);
+ }
+
+ /*
+ * Remove below when ab8500v2.0 is no longer important.
+ * Below only affects power consumption and is depending on the
+ * HREF OTP configuration.
+ * It changes the default setting for VextSupply3Regu to Low Power.
+ * Active high or low is depending on OTP which is changed from ab8500v3.0.
+ */
+ if (abx500_get_chip_id(&pdev->dev) < 0x30) {
+ err = abx500_mask_and_set_register_interruptible(&pdev->dev,
+ AB8500_REGU_CTRL2, 0x08, 0x30, 0x30);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "Failed to override 0x%02x, 0x%02x.\n",
+ AB8500_REGU_CTRL2, 0x08);
+ return err;
+ }
+ }
+
+ /* register all regulators */
+ for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ struct ab8500_regulator_info *info = NULL;
+
+ /* assign per-regulator data */
+ info = &ab8500_regulator_info[i];
+ info->dev = &pdev->dev;
+
+ /* fix for hardware before ab8500v2.0 */
+ if (abx500_get_chip_id(info->dev) < 0x20) {
+ if (info->desc.id == AB8500_LDO_AUX3) {
+ info->desc.n_voltages =
+ ARRAY_SIZE(ldo_vauxn_voltages);
+ info->voltages = ldo_vauxn_voltages;
+ info->voltages_len =
+ ARRAY_SIZE(ldo_vauxn_voltages);
+ info->voltage_mask = 0xf;
+ }
+ }
+
+ /* register regulator with framework */
+ info->regulator = regulator_register(&info->desc, &pdev->dev,
+ &pdata->regulator[i], info);
+ if (IS_ERR(info->regulator)) {
+ err = PTR_ERR(info->regulator);
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ info->desc.name);
+ /* when we fail, un-register all earlier regulators */
+ while (--i >= 0) {
+ info = &ab8500_regulator_info[i];
+ regulator_unregister(info->regulator);
+ }
+ return err;
+ }
+
+ dev_vdbg(rdev_get_dev(info->regulator),
+ "%s-probed\n", info->desc.name);
+ }
+
+ return 0;
+}
+
+static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ struct ab8500_regulator_info *info = NULL;
+ info = &ab8500_regulator_info[i];
+
+ dev_vdbg(rdev_get_dev(info->regulator),
+ "%s-remove\n", info->desc.name);
+
+ regulator_unregister(info->regulator);
+ }
+
+ return 0;
+}
+
+static struct platform_driver ab8500_regulator_driver = {
+ .probe = ab8500_regulator_probe,
+ .remove = __devexit_p(ab8500_regulator_remove),
+ .driver = {
+ .name = "ab8500-regulator",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab8500_regulator_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&ab8500_regulator_driver);
+ if (ret != 0)
+ pr_err("Failed to register ab8500 regulator: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(ab8500_regulator_init);
+
+static void __exit ab8500_regulator_exit(void)
+{
+ platform_driver_unregister(&ab8500_regulator_driver);
+}
+module_exit(ab8500_regulator_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("Regulator Driver for ST-Ericsson AB8500 Mixed-Sig PMIC");
+MODULE_ALIAS("platform:ab8500-regulator");
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 2248087b9be..0d78dfa7476 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -60,15 +60,17 @@ struct regulator {
char *supply_name;
struct device_attribute dev_attr;
struct regulator_dev *rdev;
+ int use;
};
static int _regulator_is_enabled(struct regulator_dev *rdev);
-static int _regulator_disable(struct regulator_dev *rdev);
+static int _regulator_disable(struct regulator_dev *rdev,
+ struct regulator_dev **supply_rdev_ptr);
static int _regulator_get_voltage(struct regulator_dev *rdev);
static int _regulator_get_current_limit(struct regulator_dev *rdev);
static unsigned int _regulator_get_mode(struct regulator_dev *rdev);
static void _notifier_call_chain(struct regulator_dev *rdev,
- unsigned long event, void *data);
+ unsigned long event, void *data, int lock_sublevel);
static const char *rdev_get_name(struct regulator_dev *rdev)
{
@@ -517,6 +519,32 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev,
static DEVICE_ATTR(suspend_standby_state, 0444,
regulator_suspend_standby_state_show, NULL);
+static ssize_t regulator_use_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
+ struct regulator *reg;
+ size_t size = 0;
+
+ if (rdev->use_count == 0)
+ return sprintf(buf, "no users\n");
+
+ list_for_each_entry(reg, &rdev->consumer_list, list) {
+ if (!reg->use)
+ continue;
+
+ if (reg->dev != NULL)
+ size += sprintf((buf + size), "%s (%d) ",
+ dev_name(reg->dev), reg->use);
+ else
+ size += sprintf((buf + size), "unknown (%d) ",
+ reg->use);
+ }
+ size += sprintf((buf + size), "\n");
+
+ return size;
+}
+static DEVICE_ATTR(use, 0444, regulator_use_show, NULL);
/*
* These are the only attributes are present for all regulators.
@@ -1262,13 +1290,15 @@ static int _regulator_enable(struct regulator_dev *rdev)
{
int ret, delay;
- /* do we need to enable the supply regulator first */
- if (rdev->supply) {
- ret = _regulator_enable(rdev->supply);
- if (ret < 0) {
- printk(KERN_ERR "%s: failed to enable %s: %d\n",
- __func__, rdev_get_name(rdev), ret);
- return ret;
+ if (rdev->use_count == 0) {
+ /* do we need to enable the supply regulator first */
+ if (rdev->supply) {
+ ret = _regulator_enable(rdev->supply);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: failed to enable %s: %d\n",
+ __func__, rdev_get_name(rdev), ret);
+ return ret;
+ }
}
}
@@ -1344,14 +1374,19 @@ int regulator_enable(struct regulator *regulator)
mutex_lock(&rdev->mutex);
ret = _regulator_enable(rdev);
mutex_unlock(&rdev->mutex);
+ if (ret == 0)
+ regulator->use++;
+
return ret;
}
EXPORT_SYMBOL_GPL(regulator_enable);
/* locks held by regulator_disable() */
-static int _regulator_disable(struct regulator_dev *rdev)
+static int _regulator_disable(struct regulator_dev *rdev,
+ struct regulator_dev **supply_rdev_ptr)
{
int ret = 0;
+ *supply_rdev_ptr = NULL;
if (WARN(rdev->use_count <= 0,
"unbalanced disables for %s\n",
@@ -1373,12 +1408,11 @@ static int _regulator_disable(struct regulator_dev *rdev)
}
_notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE,
- NULL);
+ NULL, 0);
}
/* decrease our supplies ref count and disable if required */
- if (rdev->supply)
- _regulator_disable(rdev->supply);
+ *supply_rdev_ptr = rdev->supply;
rdev->use_count = 0;
} else if (rdev->use_count > 1) {
@@ -1408,17 +1442,32 @@ static int _regulator_disable(struct regulator_dev *rdev)
int regulator_disable(struct regulator *regulator)
{
struct regulator_dev *rdev = regulator->rdev;
+ struct regulator_dev *supply_rdev = NULL;
int ret = 0;
mutex_lock(&rdev->mutex);
- ret = _regulator_disable(rdev);
+ ret = _regulator_disable(rdev, &supply_rdev);
mutex_unlock(&rdev->mutex);
+
+ /* decrease our supplies ref count and disable if required */
+ while (supply_rdev != NULL) {
+ rdev = supply_rdev;
+
+ mutex_lock(&rdev->mutex);
+ _regulator_disable(rdev, &supply_rdev);
+ mutex_unlock(&rdev->mutex);
+ }
+
+ if (ret == 0)
+ regulator->use--;
+
return ret;
}
EXPORT_SYMBOL_GPL(regulator_disable);
/* locks held by regulator_force_disable() */
-static int _regulator_force_disable(struct regulator_dev *rdev)
+static int _regulator_force_disable(struct regulator_dev *rdev,
+ struct regulator_dev **supply_rdev_ptr)
{
int ret = 0;
@@ -1433,12 +1482,11 @@ static int _regulator_force_disable(struct regulator_dev *rdev)
}
/* notify other consumers that power has been forced off */
_notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE |
- REGULATOR_EVENT_DISABLE, NULL);
+ REGULATOR_EVENT_DISABLE, NULL, 0);
}
/* decrease our supplies ref count and disable if required */
- if (rdev->supply)
- _regulator_disable(rdev->supply);
+ *supply_rdev_ptr = rdev->supply;
rdev->use_count = 0;
return ret;
@@ -1455,12 +1503,17 @@ static int _regulator_force_disable(struct regulator_dev *rdev)
*/
int regulator_force_disable(struct regulator *regulator)
{
+ struct regulator_dev *supply_rdev = NULL;
int ret;
mutex_lock(&regulator->rdev->mutex);
regulator->uA_load = 0;
- ret = _regulator_force_disable(regulator->rdev);
+ ret = _regulator_force_disable(regulator->rdev, &supply_rdev);
mutex_unlock(&regulator->rdev->mutex);
+
+ if (supply_rdev)
+ regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev)));
+
return ret;
}
EXPORT_SYMBOL_GPL(regulator_force_disable);
@@ -1617,7 +1670,7 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)
ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV);
out:
- _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE, NULL);
+ _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE, NULL, 0);
mutex_unlock(&rdev->mutex);
return ret;
}
@@ -1930,19 +1983,23 @@ EXPORT_SYMBOL_GPL(regulator_unregister_notifier);
/* notify regulator consumers and downstream regulator consumers.
* Note mutex must be held by caller.
+ * lock_sublevel should always be 0, only used for recursive calls.
*/
static void _notifier_call_chain(struct regulator_dev *rdev,
- unsigned long event, void *data)
+ unsigned long event, void *data, int lock_sublevel)
{
struct regulator_dev *_rdev;
/* call rdev chain first */
blocking_notifier_call_chain(&rdev->notifier, event, NULL);
+ /* increase sublevel before stepping into nested regulators */
+ lock_sublevel++;
+
/* now notify regulator we supply */
list_for_each_entry(_rdev, &rdev->supply_list, slist) {
- mutex_lock(&_rdev->mutex);
- _notifier_call_chain(_rdev, event, data);
+ mutex_lock_nested(&_rdev->mutex, lock_sublevel);
+ _notifier_call_chain(_rdev, event, data, lock_sublevel);
mutex_unlock(&_rdev->mutex);
}
}
@@ -2097,7 +2154,7 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free);
int regulator_notifier_call_chain(struct regulator_dev *rdev,
unsigned long event, void *data)
{
- _notifier_call_chain(rdev, event, data);
+ _notifier_call_chain(rdev, event, data, 0);
return NOTIFY_DONE;
}
@@ -2137,6 +2194,10 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
struct regulator_ops *ops = rdev->desc->ops;
int status = 0;
+ status = device_create_file(dev, &dev_attr_use);
+ if (status < 0)
+ dev_warn(dev, "Create sysfs file \"use\" failed");
+
/* some attributes need specific methods to be displayed */
if (ops->get_voltage) {
status = device_create_file(dev, &dev_attr_microvolts);
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 10ba12c8c5e..aa8e968de51 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -109,6 +109,24 @@ config RTC_INTF_DEV_UIE_EMUL
clock several times per second, please enable this option
only if you know that you really need it.
+config RTC_INTF_ALARM
+ bool "Android alarm driver"
+ depends on RTC_CLASS
+ default y
+ help
+ Provides non-wakeup and rtc backed wakeup alarms based on rtc or
+ elapsed realtime, and a non-wakeup alarm on the monotonic clock.
+ Also provides an interface to set the wall time which must be used
+ for elapsed realtime to work.
+
+config RTC_INTF_ALARM_DEV
+ bool "Android alarm device"
+ depends on RTC_INTF_ALARM
+ default y
+ help
+ Exports the alarm interface to user-space.
+
+
config RTC_DRV_TEST
tristate "Test driver/device"
help
@@ -603,6 +621,13 @@ config RTC_DRV_PCF50633
If you say yes here you get support for the RTC subsystem of the
NXP PCF50633 used in embedded systems.
+config RTC_DRV_AB
+ tristate "ST-Ericsson AB5500 RTC"
+ depends on AB5500_CORE
+ help
+ Select this to enable the ST-Ericsson AB5500 Mixed Signal IC RTC
+ support. This chip contains a battery- and capacitor-backed RTC.
+
config RTC_DRV_AB3100
tristate "ST-Ericsson AB3100 RTC"
depends on AB3100_CORE
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 5adbba7cf89..77eca98d9f7 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -11,12 +11,15 @@ obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o
obj-$(CONFIG_RTC_CLASS) += rtc-core.o
rtc-core-y := class.o interface.o
+obj-$(CONFIG_RTC_INTF_ALARM) += alarm.o
+obj-$(CONFIG_RTC_INTF_ALARM_DEV) += alarm-dev.o
rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
# Keep the list ordered.
+obj-$(CONFIG_RTC_DRV_AB) += rtc-ab.o
obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
diff --git a/drivers/rtc/alarm-dev.c b/drivers/rtc/alarm-dev.c
new file mode 100644
index 00000000000..686e6f7ed48
--- /dev/null
+++ b/drivers/rtc/alarm-dev.c
@@ -0,0 +1,286 @@
+/* drivers/rtc/alarm-dev.c
+ *
+ * Copyright (C) 2007-2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/mach/time.h>
+#include <linux/android_alarm.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/sysdev.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+
+#define ANDROID_ALARM_PRINT_INFO (1U << 0)
+#define ANDROID_ALARM_PRINT_IO (1U << 1)
+#define ANDROID_ALARM_PRINT_INT (1U << 2)
+
+static int debug_mask = ANDROID_ALARM_PRINT_INFO;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define pr_alarm(debug_level_mask, args...) \
+ do { \
+ if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \
+ pr_info(args); \
+ } \
+ } while (0)
+
+#define ANDROID_ALARM_WAKEUP_MASK ( \
+ ANDROID_ALARM_RTC_WAKEUP_MASK | \
+ ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
+
+/* support old usespace code */
+#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */
+#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t)
+
+static int alarm_opened;
+static DEFINE_SPINLOCK(alarm_slock);
+static struct wake_lock alarm_wake_lock;
+static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue);
+static uint32_t alarm_pending;
+static uint32_t alarm_enabled;
+static uint32_t wait_pending;
+
+static struct alarm alarms[ANDROID_ALARM_TYPE_COUNT];
+
+static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int rv = 0;
+ unsigned long flags;
+ struct timespec new_alarm_time;
+ struct timespec new_rtc_time;
+ struct timespec tmp_time;
+ enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd);
+ uint32_t alarm_type_mask = 1U << alarm_type;
+
+ if (alarm_type >= ANDROID_ALARM_TYPE_COUNT)
+ return -EINVAL;
+
+ if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) {
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+ return -EPERM;
+ if (file->private_data == NULL &&
+ cmd != ANDROID_ALARM_SET_RTC) {
+ spin_lock_irqsave(&alarm_slock, flags);
+ if (alarm_opened) {
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ return -EBUSY;
+ }
+ alarm_opened = 1;
+ file->private_data = (void *)1;
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ }
+ }
+
+ switch (ANDROID_ALARM_BASE_CMD(cmd)) {
+ case ANDROID_ALARM_CLEAR(0):
+ spin_lock_irqsave(&alarm_slock, flags);
+ pr_alarm(IO, "alarm %d clear\n", alarm_type);
+ alarm_try_to_cancel(&alarms[alarm_type]);
+ if (alarm_pending) {
+ alarm_pending &= ~alarm_type_mask;
+ if (!alarm_pending && !wait_pending)
+ wake_unlock(&alarm_wake_lock);
+ }
+ alarm_enabled &= ~alarm_type_mask;
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ break;
+
+ case ANDROID_ALARM_SET_OLD:
+ case ANDROID_ALARM_SET_AND_WAIT_OLD:
+ if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) {
+ rv = -EFAULT;
+ goto err1;
+ }
+ new_alarm_time.tv_nsec = 0;
+ goto from_old_alarm_set;
+
+ case ANDROID_ALARM_SET_AND_WAIT(0):
+ case ANDROID_ALARM_SET(0):
+ if (copy_from_user(&new_alarm_time, (void __user *)arg,
+ sizeof(new_alarm_time))) {
+ rv = -EFAULT;
+ goto err1;
+ }
+from_old_alarm_set:
+ spin_lock_irqsave(&alarm_slock, flags);
+ pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type,
+ new_alarm_time.tv_sec, new_alarm_time.tv_nsec);
+ alarm_enabled |= alarm_type_mask;
+ alarm_start_range(&alarms[alarm_type],
+ timespec_to_ktime(new_alarm_time),
+ timespec_to_ktime(new_alarm_time));
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0)
+ && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD)
+ break;
+ /* fall though */
+ case ANDROID_ALARM_WAIT:
+ spin_lock_irqsave(&alarm_slock, flags);
+ pr_alarm(IO, "alarm wait\n");
+ if (!alarm_pending && wait_pending) {
+ wake_unlock(&alarm_wake_lock);
+ wait_pending = 0;
+ }
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
+ if (rv)
+ goto err1;
+ spin_lock_irqsave(&alarm_slock, flags);
+ rv = alarm_pending;
+ wait_pending = 1;
+ alarm_pending = 0;
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ break;
+ case ANDROID_ALARM_SET_RTC:
+ if (copy_from_user(&new_rtc_time, (void __user *)arg,
+ sizeof(new_rtc_time))) {
+ rv = -EFAULT;
+ goto err1;
+ }
+ rv = alarm_set_rtc(new_rtc_time);
+ spin_lock_irqsave(&alarm_slock, flags);
+ alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;
+ wake_up(&alarm_wait_queue);
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ if (rv < 0)
+ goto err1;
+ break;
+ case ANDROID_ALARM_GET_TIME(0):
+ switch (alarm_type) {
+ case ANDROID_ALARM_RTC_WAKEUP:
+ case ANDROID_ALARM_RTC:
+ getnstimeofday(&tmp_time);
+ break;
+ case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
+ case ANDROID_ALARM_ELAPSED_REALTIME:
+ tmp_time =
+ ktime_to_timespec(alarm_get_elapsed_realtime());
+ break;
+ case ANDROID_ALARM_TYPE_COUNT:
+ case ANDROID_ALARM_SYSTEMTIME:
+ ktime_get_ts(&tmp_time);
+ break;
+ }
+ if (copy_to_user((void __user *)arg, &tmp_time,
+ sizeof(tmp_time))) {
+ rv = -EFAULT;
+ goto err1;
+ }
+ break;
+
+ default:
+ rv = -EINVAL;
+ goto err1;
+ }
+err1:
+ return rv;
+}
+
+static int alarm_open(struct inode *inode, struct file *file)
+{
+ file->private_data = NULL;
+ return 0;
+}
+
+static int alarm_release(struct inode *inode, struct file *file)
+{
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&alarm_slock, flags);
+ if (file->private_data != 0) {
+ for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) {
+ uint32_t alarm_type_mask = 1U << i;
+ if (alarm_enabled & alarm_type_mask) {
+ pr_alarm(INFO, "alarm_release: clear alarm, "
+ "pending %d\n",
+ !!(alarm_pending & alarm_type_mask));
+ alarm_enabled &= ~alarm_type_mask;
+ }
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ alarm_cancel(&alarms[i]);
+ spin_lock_irqsave(&alarm_slock, flags);
+ }
+ if (alarm_pending | wait_pending) {
+ if (alarm_pending)
+ pr_alarm(INFO, "alarm_release: clear "
+ "pending alarms %x\n", alarm_pending);
+ wake_unlock(&alarm_wake_lock);
+ wait_pending = 0;
+ alarm_pending = 0;
+ }
+ alarm_opened = 0;
+ }
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ return 0;
+}
+
+static void alarm_triggered(struct alarm *alarm)
+{
+ unsigned long flags;
+ uint32_t alarm_type_mask = 1U << alarm->type;
+
+ pr_alarm(INT, "alarm_triggered type %d\n", alarm->type);
+ spin_lock_irqsave(&alarm_slock, flags);
+ if (alarm_enabled & alarm_type_mask) {
+ wake_lock_timeout(&alarm_wake_lock, 5 * HZ);
+ alarm_enabled &= ~alarm_type_mask;
+ alarm_pending |= alarm_type_mask;
+ wake_up(&alarm_wait_queue);
+ }
+ spin_unlock_irqrestore(&alarm_slock, flags);
+}
+
+static const struct file_operations alarm_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = alarm_ioctl,
+ .open = alarm_open,
+ .release = alarm_release,
+};
+
+static struct miscdevice alarm_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "alarm",
+ .fops = &alarm_fops,
+};
+
+static int __init alarm_dev_init(void)
+{
+ int err;
+ int i;
+
+ err = misc_register(&alarm_device);
+ if (err)
+ return err;
+
+ for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++)
+ alarm_init(&alarms[i], i, alarm_triggered);
+ wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm");
+
+ return 0;
+}
+
+static void __exit alarm_dev_exit(void)
+{
+ misc_deregister(&alarm_device);
+ wake_lock_destroy(&alarm_wake_lock);
+}
+
+module_init(alarm_dev_init);
+module_exit(alarm_dev_exit);
+
diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c
new file mode 100644
index 00000000000..b445a867585
--- /dev/null
+++ b/drivers/rtc/alarm.c
@@ -0,0 +1,590 @@
+/* drivers/rtc/alarm.c
+ *
+ * Copyright (C) 2007-2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/mach/time.h>
+#include <linux/android_alarm.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/sysdev.h>
+#include <linux/wakelock.h>
+
+#define ANDROID_ALARM_PRINT_ERROR (1U << 0)
+#define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1)
+#define ANDROID_ALARM_PRINT_TSET (1U << 2)
+#define ANDROID_ALARM_PRINT_CALL (1U << 3)
+#define ANDROID_ALARM_PRINT_SUSPEND (1U << 4)
+#define ANDROID_ALARM_PRINT_INT (1U << 5)
+#define ANDROID_ALARM_PRINT_FLOW (1U << 6)
+
+static int debug_mask = ANDROID_ALARM_PRINT_ERROR | \
+ ANDROID_ALARM_PRINT_INIT_STATUS;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define pr_alarm(debug_level_mask, args...) \
+ do { \
+ if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \
+ pr_info(args); \
+ } \
+ } while (0)
+
+#define ANDROID_ALARM_WAKEUP_MASK ( \
+ ANDROID_ALARM_RTC_WAKEUP_MASK | \
+ ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
+
+/* support old usespace code */
+#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */
+#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t)
+
+struct alarm_queue {
+ struct rb_root alarms;
+ struct rb_node *first;
+ struct hrtimer timer;
+ ktime_t delta;
+ bool stopped;
+ ktime_t stopped_time;
+};
+
+static struct rtc_device *alarm_rtc_dev;
+static DEFINE_SPINLOCK(alarm_slock);
+static DEFINE_MUTEX(alarm_setrtc_mutex);
+static struct wake_lock alarm_rtc_wake_lock;
+static struct platform_device *alarm_platform_dev;
+struct alarm_queue alarms[ANDROID_ALARM_TYPE_COUNT];
+static bool suspended;
+
+static void update_timer_locked(struct alarm_queue *base, bool head_removed)
+{
+ struct alarm *alarm;
+ bool is_wakeup = base == &alarms[ANDROID_ALARM_RTC_WAKEUP] ||
+ base == &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP];
+
+ if (base->stopped) {
+ pr_alarm(FLOW, "changed alarm while setting the wall time\n");
+ return;
+ }
+
+ if (is_wakeup && !suspended && head_removed)
+ wake_unlock(&alarm_rtc_wake_lock);
+
+ if (!base->first)
+ return;
+
+ alarm = container_of(base->first, struct alarm, node);
+
+ pr_alarm(FLOW, "selected alarm, type %d, func %pF at %lld\n",
+ alarm->type, alarm->function, ktime_to_ns(alarm->expires));
+
+ if (is_wakeup && suspended) {
+ pr_alarm(FLOW, "changed alarm while suspened\n");
+ wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ);
+ return;
+ }
+
+ hrtimer_try_to_cancel(&base->timer);
+ base->timer._expires = ktime_add(base->delta, alarm->expires);
+ base->timer._softexpires = ktime_add(base->delta, alarm->softexpires);
+ hrtimer_start_expires(&base->timer, HRTIMER_MODE_ABS);
+}
+
+static void alarm_enqueue_locked(struct alarm *alarm)
+{
+ struct alarm_queue *base = &alarms[alarm->type];
+ struct rb_node **link = &base->alarms.rb_node;
+ struct rb_node *parent = NULL;
+ struct alarm *entry;
+ int leftmost = 1;
+ bool was_first = false;
+
+ pr_alarm(FLOW, "added alarm, type %d, func %pF at %lld\n",
+ alarm->type, alarm->function, ktime_to_ns(alarm->expires));
+
+ if (base->first == &alarm->node) {
+ base->first = rb_next(&alarm->node);
+ was_first = true;
+ }
+ if (!RB_EMPTY_NODE(&alarm->node)) {
+ rb_erase(&alarm->node, &base->alarms);
+ RB_CLEAR_NODE(&alarm->node);
+ }
+
+ while (*link) {
+ parent = *link;
+ entry = rb_entry(parent, struct alarm, node);
+ /*
+ * We dont care about collisions. Nodes with
+ * the same expiry time stay together.
+ */
+ if (alarm->expires.tv64 < entry->expires.tv64) {
+ link = &(*link)->rb_left;
+ } else {
+ link = &(*link)->rb_right;
+ leftmost = 0;
+ }
+ }
+ if (leftmost)
+ base->first = &alarm->node;
+ if (leftmost || was_first)
+ update_timer_locked(base, was_first);
+
+ rb_link_node(&alarm->node, parent, link);
+ rb_insert_color(&alarm->node, &base->alarms);
+}
+
+/**
+ * alarm_init - initialize an alarm
+ * @alarm: the alarm to be initialized
+ * @type: the alarm type to be used
+ * @function: alarm callback function
+ */
+void alarm_init(struct alarm *alarm,
+ enum android_alarm_type type, void (*function)(struct alarm *))
+{
+ RB_CLEAR_NODE(&alarm->node);
+ alarm->type = type;
+ alarm->function = function;
+
+ pr_alarm(FLOW, "created alarm, type %d, func %pF\n", type, function);
+}
+
+
+/**
+ * alarm_start_range - (re)start an alarm
+ * @alarm: the alarm to be added
+ * @start: earliest expiry time
+ * @end: expiry time
+ */
+void alarm_start_range(struct alarm *alarm, ktime_t start, ktime_t end)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&alarm_slock, flags);
+ alarm->softexpires = start;
+ alarm->expires = end;
+ alarm_enqueue_locked(alarm);
+ spin_unlock_irqrestore(&alarm_slock, flags);
+}
+
+/**
+ * alarm_try_to_cancel - try to deactivate an alarm
+ * @alarm: alarm to stop
+ *
+ * Returns:
+ * 0 when the alarm was not active
+ * 1 when the alarm was active
+ * -1 when the alarm may currently be excuting the callback function and
+ * cannot be stopped (it may also be inactive)
+ */
+int alarm_try_to_cancel(struct alarm *alarm)
+{
+ struct alarm_queue *base = &alarms[alarm->type];
+ unsigned long flags;
+ bool first = false;
+ int ret = 0;
+
+ spin_lock_irqsave(&alarm_slock, flags);
+ if (!RB_EMPTY_NODE(&alarm->node)) {
+ pr_alarm(FLOW, "canceled alarm, type %d, func %pF at %lld\n",
+ alarm->type, alarm->function,
+ ktime_to_ns(alarm->expires));
+ ret = 1;
+ if (base->first == &alarm->node) {
+ base->first = rb_next(&alarm->node);
+ first = true;
+ }
+ rb_erase(&alarm->node, &base->alarms);
+ RB_CLEAR_NODE(&alarm->node);
+ if (first)
+ update_timer_locked(base, true);
+ } else
+ pr_alarm(FLOW, "tried to cancel alarm, type %d, func %pF\n",
+ alarm->type, alarm->function);
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ if (!ret && hrtimer_callback_running(&base->timer))
+ ret = -1;
+ return ret;
+}
+
+/**
+ * alarm_cancel - cancel an alarm and wait for the handler to finish.
+ * @alarm: the alarm to be cancelled
+ *
+ * Returns:
+ * 0 when the alarm was not active
+ * 1 when the alarm was active
+ */
+int alarm_cancel(struct alarm *alarm)
+{
+ for (;;) {
+ int ret = alarm_try_to_cancel(alarm);
+ if (ret >= 0)
+ return ret;
+ cpu_relax();
+ }
+}
+
+/**
+ * alarm_set_rtc - set the kernel and rtc walltime
+ * @new_time: timespec value containing the new time
+ */
+int alarm_set_rtc(struct timespec new_time)
+{
+ int i;
+ int ret;
+ unsigned long flags;
+ struct rtc_time rtc_new_rtc_time;
+ struct timespec tmp_time;
+
+ rtc_time_to_tm(new_time.tv_sec, &rtc_new_rtc_time);
+
+ pr_alarm(TSET, "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n",
+ new_time.tv_sec, new_time.tv_nsec,
+ rtc_new_rtc_time.tm_hour, rtc_new_rtc_time.tm_min,
+ rtc_new_rtc_time.tm_sec, rtc_new_rtc_time.tm_mon + 1,
+ rtc_new_rtc_time.tm_mday,
+ rtc_new_rtc_time.tm_year + 1900);
+
+ mutex_lock(&alarm_setrtc_mutex);
+ spin_lock_irqsave(&alarm_slock, flags);
+ wake_lock(&alarm_rtc_wake_lock);
+ getnstimeofday(&tmp_time);
+ for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) {
+ hrtimer_try_to_cancel(&alarms[i].timer);
+ alarms[i].stopped = true;
+ alarms[i].stopped_time = timespec_to_ktime(tmp_time);
+ }
+ alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta =
+ alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta =
+ ktime_sub(alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta,
+ timespec_to_ktime(timespec_sub(tmp_time, new_time)));
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ ret = do_settimeofday(&new_time);
+ spin_lock_irqsave(&alarm_slock, flags);
+ for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) {
+ alarms[i].stopped = false;
+ update_timer_locked(&alarms[i], false);
+ }
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ if (ret < 0) {
+ pr_alarm(ERROR, "alarm_set_rtc: Failed to set time\n");
+ goto err;
+ }
+ if (!alarm_rtc_dev) {
+ pr_alarm(ERROR,
+ "alarm_set_rtc: no RTC, time will be lost on reboot\n");
+ goto err;
+ }
+ ret = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time);
+ if (ret < 0)
+ pr_alarm(ERROR, "alarm_set_rtc: "
+ "Failed to set RTC, time will be lost on reboot\n");
+err:
+ wake_unlock(&alarm_rtc_wake_lock);
+ mutex_unlock(&alarm_setrtc_mutex);
+ return ret;
+}
+
+/**
+ * alarm_get_elapsed_realtime - get the elapsed real time in ktime_t format
+ *
+ * returns the time in ktime_t format
+ */
+ktime_t alarm_get_elapsed_realtime(void)
+{
+ ktime_t now;
+ unsigned long flags;
+ struct alarm_queue *base = &alarms[ANDROID_ALARM_ELAPSED_REALTIME];
+
+ spin_lock_irqsave(&alarm_slock, flags);
+ now = base->stopped ? base->stopped_time : ktime_get_real();
+ now = ktime_sub(now, base->delta);
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ return now;
+}
+
+static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer)
+{
+ struct alarm_queue *base;
+ struct alarm *alarm;
+ unsigned long flags;
+ ktime_t now;
+
+ spin_lock_irqsave(&alarm_slock, flags);
+
+ base = container_of(timer, struct alarm_queue, timer);
+ now = base->stopped ? base->stopped_time : hrtimer_cb_get_time(timer);
+ now = ktime_sub(now, base->delta);
+
+ pr_alarm(INT, "alarm_timer_triggered type %d at %lld\n",
+ base - alarms, ktime_to_ns(now));
+
+ while (base->first) {
+ alarm = container_of(base->first, struct alarm, node);
+ if (alarm->softexpires.tv64 > now.tv64) {
+ pr_alarm(FLOW, "don't call alarm, %pF, %lld (s %lld)\n",
+ alarm->function, ktime_to_ns(alarm->expires),
+ ktime_to_ns(alarm->softexpires));
+ break;
+ }
+ base->first = rb_next(&alarm->node);
+ rb_erase(&alarm->node, &base->alarms);
+ RB_CLEAR_NODE(&alarm->node);
+ pr_alarm(CALL, "call alarm, type %d, func %pF, %lld (s %lld)\n",
+ alarm->type, alarm->function,
+ ktime_to_ns(alarm->expires),
+ ktime_to_ns(alarm->softexpires));
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ alarm->function(alarm);
+ spin_lock_irqsave(&alarm_slock, flags);
+ }
+ if (!base->first)
+ pr_alarm(FLOW, "no more alarms of type %d\n", base - alarms);
+ update_timer_locked(base, true);
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ return HRTIMER_NORESTART;
+}
+
+static void alarm_triggered_func(void *p)
+{
+ struct rtc_device *rtc = alarm_rtc_dev;
+ if (!(rtc->irq_data & RTC_AF))
+ return;
+ pr_alarm(INT, "rtc alarm triggered\n");
+ wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ);
+}
+
+static int alarm_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int err = 0;
+ unsigned long flags;
+ struct rtc_wkalrm rtc_alarm;
+ struct rtc_time rtc_current_rtc_time;
+ unsigned long rtc_current_time;
+ unsigned long rtc_alarm_time;
+ struct timespec rtc_delta;
+ struct timespec wall_time;
+ struct alarm_queue *wakeup_queue = NULL;
+ struct alarm_queue *tmp_queue = NULL;
+
+ pr_alarm(SUSPEND, "alarm_suspend(%p, %d)\n", pdev, state.event);
+
+ spin_lock_irqsave(&alarm_slock, flags);
+ suspended = true;
+ spin_unlock_irqrestore(&alarm_slock, flags);
+
+ hrtimer_cancel(&alarms[ANDROID_ALARM_RTC_WAKEUP].timer);
+ hrtimer_cancel(&alarms[
+ ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK].timer);
+
+ tmp_queue = &alarms[ANDROID_ALARM_RTC_WAKEUP];
+ if (tmp_queue->first)
+ wakeup_queue = tmp_queue;
+ tmp_queue = &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP];
+ if (tmp_queue->first && (!wakeup_queue ||
+ hrtimer_get_expires(&tmp_queue->timer).tv64 <
+ hrtimer_get_expires(&wakeup_queue->timer).tv64))
+ wakeup_queue = tmp_queue;
+ if (wakeup_queue) {
+ rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time);
+ getnstimeofday(&wall_time);
+ rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time);
+ set_normalized_timespec(&rtc_delta,
+ wall_time.tv_sec - rtc_current_time,
+ wall_time.tv_nsec);
+
+ rtc_alarm_time = timespec_sub(ktime_to_timespec(
+ hrtimer_get_expires(&wakeup_queue->timer)),
+ rtc_delta).tv_sec;
+
+ rtc_time_to_tm(rtc_alarm_time, &rtc_alarm.time);
+ rtc_alarm.enabled = 1;
+ rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);
+ rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time);
+ rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time);
+ pr_alarm(SUSPEND,
+ "rtc alarm set at %ld, now %ld, rtc delta %ld.%09ld\n",
+ rtc_alarm_time, rtc_current_time,
+ rtc_delta.tv_sec, rtc_delta.tv_nsec);
+ if (rtc_current_time + 1 >= rtc_alarm_time) {
+ pr_alarm(SUSPEND, "alarm about to go off\n");
+ memset(&rtc_alarm, 0, sizeof(rtc_alarm));
+ rtc_alarm.enabled = 0;
+ rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);
+
+ spin_lock_irqsave(&alarm_slock, flags);
+ suspended = false;
+ wake_lock_timeout(&alarm_rtc_wake_lock, 2 * HZ);
+ update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP],
+ false);
+ update_timer_locked(&alarms[
+ ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP], false);
+ err = -EBUSY;
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ }
+ }
+ return err;
+}
+
+static int alarm_resume(struct platform_device *pdev)
+{
+ struct rtc_wkalrm alarm;
+ unsigned long flags;
+
+ pr_alarm(SUSPEND, "alarm_resume(%p)\n", pdev);
+
+ memset(&alarm, 0, sizeof(alarm));
+ alarm.enabled = 0;
+ rtc_set_alarm(alarm_rtc_dev, &alarm);
+
+ spin_lock_irqsave(&alarm_slock, flags);
+ suspended = false;
+ update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP], false);
+ update_timer_locked(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP],
+ false);
+ spin_unlock_irqrestore(&alarm_slock, flags);
+
+ return 0;
+}
+
+static struct rtc_task alarm_rtc_task = {
+ .func = alarm_triggered_func
+};
+
+static int rtc_alarm_add_device(struct device *dev,
+ struct class_interface *class_intf)
+{
+ int err;
+ struct rtc_device *rtc = to_rtc_device(dev);
+
+ mutex_lock(&alarm_setrtc_mutex);
+
+ if (alarm_rtc_dev) {
+ err = -EBUSY;
+ goto err1;
+ }
+
+ alarm_platform_dev =
+ platform_device_register_simple("alarm", -1, NULL, 0);
+ if (IS_ERR(alarm_platform_dev)) {
+ err = PTR_ERR(alarm_platform_dev);
+ goto err2;
+ }
+ err = rtc_irq_register(rtc, &alarm_rtc_task);
+ if (err)
+ goto err3;
+ alarm_rtc_dev = rtc;
+ pr_alarm(INIT_STATUS, "using rtc device, %s, for alarms", rtc->name);
+ mutex_unlock(&alarm_setrtc_mutex);
+
+ return 0;
+
+err3:
+ platform_device_unregister(alarm_platform_dev);
+err2:
+err1:
+ mutex_unlock(&alarm_setrtc_mutex);
+ return err;
+}
+
+static void rtc_alarm_remove_device(struct device *dev,
+ struct class_interface *class_intf)
+{
+ if (dev == &alarm_rtc_dev->dev) {
+ pr_alarm(INIT_STATUS, "lost rtc device for alarms");
+ rtc_irq_unregister(alarm_rtc_dev, &alarm_rtc_task);
+ platform_device_unregister(alarm_platform_dev);
+ alarm_rtc_dev = NULL;
+ }
+}
+
+static struct class_interface rtc_alarm_interface = {
+ .add_dev = &rtc_alarm_add_device,
+ .remove_dev = &rtc_alarm_remove_device,
+};
+
+static struct platform_driver alarm_driver = {
+ .suspend = alarm_suspend,
+ .resume = alarm_resume,
+ .driver = {
+ .name = "alarm"
+ }
+};
+
+static int __init alarm_late_init(void)
+{
+ unsigned long flags;
+ struct timespec tmp_time, system_time;
+
+ /* this needs to run after the rtc is read at boot */
+ spin_lock_irqsave(&alarm_slock, flags);
+ /* We read the current rtc and system time so we can later calulate
+ * elasped realtime to be (boot_systemtime + rtc - boot_rtc) ==
+ * (rtc - (boot_rtc - boot_systemtime))
+ */
+ getnstimeofday(&tmp_time);
+ ktime_get_ts(&system_time);
+ alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta =
+ alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta =
+ timespec_to_ktime(timespec_sub(tmp_time, system_time));
+
+ spin_unlock_irqrestore(&alarm_slock, flags);
+ return 0;
+}
+
+static int __init alarm_driver_init(void)
+{
+ int err;
+ int i;
+
+ for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) {
+ hrtimer_init(&alarms[i].timer,
+ CLOCK_REALTIME, HRTIMER_MODE_ABS);
+ alarms[i].timer.function = alarm_timer_triggered;
+ }
+ hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].timer,
+ CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ alarms[ANDROID_ALARM_SYSTEMTIME].timer.function = alarm_timer_triggered;
+ err = platform_driver_register(&alarm_driver);
+ if (err < 0)
+ goto err1;
+ wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc");
+ rtc_alarm_interface.class = rtc_class;
+ err = class_interface_register(&rtc_alarm_interface);
+ if (err < 0)
+ goto err2;
+
+ return 0;
+
+err2:
+ wake_lock_destroy(&alarm_rtc_wake_lock);
+ platform_driver_unregister(&alarm_driver);
+err1:
+ return err;
+}
+
+static void __exit alarm_exit(void)
+{
+ class_interface_unregister(&rtc_alarm_interface);
+ wake_lock_destroy(&alarm_rtc_wake_lock);
+ platform_driver_unregister(&alarm_driver);
+}
+
+late_initcall(alarm_late_init);
+module_init(alarm_driver_init);
+module_exit(alarm_exit);
+
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 565562ba6ac..11d7ab90a67 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -41,25 +41,32 @@ static void rtc_device_release(struct device *dev)
*/
static struct timespec delta;
+static struct timespec delta_delta;
static time_t oldtime;
static int rtc_suspend(struct device *dev, pm_message_t mesg)
{
struct rtc_device *rtc = to_rtc_device(dev);
struct rtc_time tm;
- struct timespec ts = current_kernel_time();
+ struct timespec ts;
+ struct timespec new_delta;
if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
return 0;
+ getnstimeofday(&ts);
rtc_read_time(rtc, &tm);
rtc_tm_to_time(&tm, &oldtime);
/* RTC precision is 1 second; adjust delta for avg 1/2 sec err */
- set_normalized_timespec(&delta,
+ set_normalized_timespec(&new_delta,
ts.tv_sec - oldtime,
ts.tv_nsec - (NSEC_PER_SEC >> 1));
+ /* prevent 1/2 sec errors from accumulating */
+ delta_delta = timespec_sub(new_delta, delta);
+ if (delta_delta.tv_sec < -2 || delta_delta.tv_sec >= 2)
+ delta = new_delta;
return 0;
}
@@ -79,6 +86,8 @@ static int rtc_resume(struct device *dev)
return 0;
}
rtc_tm_to_time(&tm, &newtime);
+ if (delta_delta.tv_sec < -1)
+ newtime++;
if (newtime <= oldtime) {
if (newtime < oldtime)
pr_debug("%s: time travel!\n", dev_name(&rtc->dev));
diff --git a/drivers/rtc/rtc-ab.c b/drivers/rtc/rtc-ab.c
new file mode 100644
index 00000000000..db1992632fa
--- /dev/null
+++ b/drivers/rtc/rtc-ab.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+
+#define AB5500_RTC_CLOCK_RATE 32768
+#define AB5500_RTC 0x00
+#define AB5500_RTC_ALARM (1 << 1)
+#define AB5500_READREQ 0x01
+#define AB5500_READREQ_REQ 0x01
+#define AB5500_AL0 0x02
+#define AB5500_TI0 0x06
+
+/**
+ * struct ab_rtc - variant specific data
+ * @irqname: optional name for the alarm interrupt resource
+ * @epoch: epoch to adjust year to
+ * @bank: AB bank where this block is present
+ * @rtc: address of the "RTC" (control) register
+ * @rtc_alarmon: mask of the alarm enable bit in the above register
+ * @ti0: address of the TI0 register. The rest of the TI
+ * registers are assumed to contiguously follow this one.
+ * @nr_ti: number of TI* registers
+ * @al0: address of the AL0 register. The rest of the
+ * AL registers are assumed to contiguously follow this one.
+ * @nr_al: number of AL* registers
+ * @startup: optional function to initialize the RTC
+ * @alarm_to_regs: function to convert alarm time in seconds
+ * to a list of AL register values
+ * @time_to_regs: function to convert alarm time in seconds
+ * to a list of TI register values
+ * @regs_to_alarm: function to convert a list of AL register
+ * values to the alarm time in seconds
+ * @regs_to_time: function to convert a list of TI register
+ * values to the alarm time in seconds
+ * @request_read: optional function to request a read from the TI* registers
+ * @request_write: optional function to request a write to the TI* registers
+ */
+struct ab_rtc {
+ const char *irqname;
+ unsigned int epoch;
+
+ u8 bank;
+ u8 rtc;
+ u8 rtc_alarmon;
+ u8 ti0;
+ int nr_ti;
+ u8 al0;
+ int nr_al;
+
+ int (*startup)(struct device *dev);
+ void (*alarm_to_regs)(struct device *dev, unsigned long secs, u8 *regs);
+ void (*time_to_regs)(struct device *dev, unsigned long secs, u8 *regs);
+ unsigned long (*regs_to_alarm)(struct device *dev, u8 *regs);
+ unsigned long (*regs_to_time)(struct device *dev, u8 *regs);
+ int (*request_read)(struct device *dev);
+ int (*request_write)(struct device *dev);
+};
+
+static const struct ab_rtc *to_ab_rtc(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ return (struct ab_rtc *)pdev->id_entry->driver_data;
+}
+
+/* Calculate the number of seconds since year, for epoch adjustment */
+static unsigned long ab_rtc_get_elapsed_seconds(unsigned int year)
+{
+ unsigned long secs;
+ struct rtc_time tm = {
+ .tm_year = year - 1900,
+ .tm_mday = 1,
+ };
+
+ rtc_tm_to_time(&tm, &secs);
+
+ return secs;
+}
+
+static int ab5500_rtc_request_read(struct device *dev)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ unsigned long timeout;
+ int err;
+
+ err = abx500_set_register_interruptible(dev, variant->bank,
+ AB5500_READREQ,
+ AB5500_READREQ_REQ);
+ if (err < 0)
+ return err;
+
+ timeout = jiffies + HZ;
+ while (time_before(jiffies, timeout)) {
+ u8 value;
+
+ err = abx500_get_register_interruptible(dev, variant->bank,
+ AB5500_READREQ, &value);
+ if (err < 0)
+ return err;
+
+ if (!(value & AB5500_READREQ_REQ))
+ return 0;
+
+ msleep(1);
+ }
+
+ return -EIO;
+}
+
+static void
+ab5500_rtc_time_to_regs(struct device *dev, unsigned long secs, u8 *regs)
+{
+ unsigned long mins = secs / 60;
+ u64 fat_time;
+
+ secs %= 60;
+
+ fat_time = secs * AB5500_RTC_CLOCK_RATE;
+ fat_time |= (u64)mins << 21;
+
+ regs[0] = (fat_time) & 0xFF;
+ regs[1] = (fat_time >> 8) & 0xFF;
+ regs[2] = (fat_time >> 16) & 0xFF;
+ regs[3] = (fat_time >> 24) & 0xFF;
+ regs[4] = (fat_time >> 32) & 0xFF;
+ regs[5] = (fat_time >> 40) & 0xFF;
+}
+
+static unsigned long
+ab5500_rtc_regs_to_time(struct device *dev, u8 *regs)
+{
+ u64 fat_time = ((u64)regs[5] << 40) | ((u64)regs[4] << 32) |
+ ((u64)regs[3] << 24) | ((u64)regs[2] << 16) |
+ ((u64)regs[1] << 8) | regs[0];
+ unsigned long secs = (fat_time & 0x1fffff) / AB5500_RTC_CLOCK_RATE;
+ unsigned long mins = fat_time >> 21;
+
+ return mins * 60 + secs;
+}
+
+static void
+ab5500_rtc_alarm_to_regs(struct device *dev, unsigned long secs, u8 *regs)
+{
+ unsigned long mins = secs / 60;
+
+#ifdef CONFIG_ANDROID
+ /*
+ * Needed because Android believes all hw have a wake-up resolution in
+ * seconds.
+ */
+ mins++;
+#endif
+
+ regs[0] = mins & 0xFF;
+ regs[1] = (mins >> 8) & 0xFF;
+ regs[2] = (mins >> 16) & 0xFF;
+}
+
+static unsigned long
+ab5500_rtc_regs_to_alarm(struct device *dev, u8 *regs)
+{
+ unsigned long mins = ((unsigned long)regs[2] << 16) |
+ ((unsigned long)regs[1] << 8) |
+ regs[0];
+ unsigned long secs = mins * 60;
+
+ return secs;
+}
+
+static const struct ab_rtc ab5500_rtc = {
+ .irqname = "RTC_Alarm",
+ .bank = AB5500_BANK_RTC,
+ .rtc = AB5500_RTC,
+ .rtc_alarmon = AB5500_RTC_ALARM,
+ .ti0 = AB5500_TI0,
+ .nr_ti = 6,
+ .al0 = AB5500_AL0,
+ .nr_al = 3,
+ .epoch = 2000,
+ .time_to_regs = ab5500_rtc_time_to_regs,
+ .regs_to_time = ab5500_rtc_regs_to_time,
+ .alarm_to_regs = ab5500_rtc_alarm_to_regs,
+ .regs_to_alarm = ab5500_rtc_regs_to_alarm,
+ .request_read = ab5500_rtc_request_read,
+};
+
+static int ab_rtc_request_read(struct device *dev)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+
+ if (!variant->request_read)
+ return 0;
+
+ return variant->request_read(dev);
+}
+
+static int ab_rtc_request_write(struct device *dev)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+
+ if (!variant->request_write)
+ return 0;
+
+ return variant->request_write(dev);
+}
+
+static bool ab_rtc_valid_time(struct device *dev, struct rtc_time *time)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+
+ if (!variant->epoch)
+ return true;
+
+ return time->tm_year >= variant->epoch - 1900;
+}
+
+static int
+ab_rtc_tm_to_time(struct device *dev, struct rtc_time *tm, unsigned long *secs)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+
+ rtc_tm_to_time(tm, secs);
+
+ if (variant->epoch)
+ *secs -= ab_rtc_get_elapsed_seconds(variant->epoch);
+
+ return 0;
+}
+
+static int
+ab_rtc_time_to_tm(struct device *dev, unsigned long secs, struct rtc_time *tm)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+
+ if (variant->epoch)
+ secs += ab_rtc_get_elapsed_seconds(variant->epoch);
+
+ rtc_time_to_tm(secs, tm);
+
+ return 0;
+}
+
+static int ab_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ unsigned char buf[variant->nr_ti];
+ unsigned long secs;
+ int err;
+
+ err = ab_rtc_request_read(dev);
+ if (err)
+ return err;
+
+ err = abx500_get_register_page_interruptible(dev, variant->bank,
+ variant->ti0,
+ buf, variant->nr_ti);
+ if (err)
+ return err;
+
+ secs = variant->regs_to_time(dev, buf);
+ ab_rtc_time_to_tm(dev, secs, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int ab_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ unsigned char buf[variant->nr_ti];
+ unsigned long secs;
+ u8 reg = variant->ti0;
+ int err;
+ int i;
+
+ if (!ab_rtc_valid_time(dev, tm))
+ return -EINVAL;
+
+ ab_rtc_tm_to_time(dev, tm, &secs);
+ variant->time_to_regs(dev, secs, buf);
+
+ for (i = 0; i < variant->nr_ti; i++, reg++) {
+ err = abx500_set_register_interruptible(dev, variant->bank,
+ reg, buf[i]);
+ if (err)
+ return err;
+ }
+
+ return ab_rtc_request_write(dev);
+}
+
+static int ab_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ unsigned long secs;
+ u8 buf[variant->nr_al];
+ u8 rtcval;
+ int err;
+
+ err = abx500_get_register_interruptible(dev, variant->bank,
+ variant->rtc, &rtcval);
+ if (err)
+ return err;
+
+ alarm->enabled = !!(rtcval & variant->rtc_alarmon);
+ alarm->pending = 0;
+
+ err = abx500_get_register_page_interruptible(dev, variant->bank,
+ variant->al0, buf,
+ variant->nr_al);
+ if (err)
+ return err;
+
+ secs = variant->regs_to_alarm(dev, buf);
+ ab_rtc_time_to_tm(dev, secs, &alarm->time);
+
+ return rtc_valid_tm(&alarm->time);
+}
+
+static int ab_rtc_alarm_enable(struct device *dev, unsigned int enabled)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ u8 mask = variant->rtc_alarmon;
+ u8 value = enabled ? mask : 0;
+
+ return abx500_mask_and_set_register_interruptible(dev, variant->bank,
+ variant->rtc, mask,
+ value);
+}
+
+static int ab_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ unsigned char buf[variant->nr_al];
+ unsigned long secs;
+ u8 reg = variant->al0;
+ int err;
+ int i;
+
+ if (!ab_rtc_valid_time(dev, &alarm->time))
+ return -EINVAL;
+
+ ab_rtc_tm_to_time(dev, &alarm->time, &secs);
+ variant->alarm_to_regs(dev, secs, buf);
+
+ /*
+ * Disable alarm first. Otherwise the RTC may not detect an alarm
+ * reprogrammed for the same time without disabling the alarm in
+ * between the programmings.
+ */
+ err = ab_rtc_alarm_enable(dev, false);
+ if (err)
+ return err;
+
+ for (i = 0; i < variant->nr_al; i++, reg++) {
+ err = abx500_set_register_interruptible(dev, variant->bank,
+ reg, buf[i]);
+ if (err)
+ return err;
+ }
+
+ return ab_rtc_alarm_enable(dev, true);
+}
+
+static const struct rtc_class_ops ab_rtc_ops = {
+ .read_time = ab_rtc_read_time,
+ .set_time = ab_rtc_set_time,
+ .read_alarm = ab_rtc_read_alarm,
+ .set_alarm = ab_rtc_set_alarm,
+ .alarm_irq_enable = ab_rtc_alarm_enable,
+};
+
+static irqreturn_t ab_rtc_irq(int irq, void *dev_id)
+{
+ unsigned long events = RTC_IRQF | RTC_AF;
+ struct rtc_device *rtc = dev_id;
+
+ rtc_update_irq(rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit ab_rtc_probe(struct platform_device *pdev)
+{
+ const struct ab_rtc *variant = to_ab_rtc(&pdev->dev);
+ int err;
+ struct rtc_device *rtc;
+ int irq = -ENXIO;
+
+ if (variant->irqname) {
+ irq = platform_get_irq_byname(pdev, variant->irqname);
+ if (irq < 0)
+ return irq;
+ }
+
+ if (variant->startup) {
+ err = variant->startup(&pdev->dev);
+ if (err)
+ return err;
+ }
+
+ rtc = rtc_device_register("ab8500-rtc", &pdev->dev, &ab_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "Registration failed\n");
+ err = PTR_ERR(rtc);
+ return err;
+ }
+
+ if (irq >= 0) {
+ err = request_any_context_irq(irq, ab_rtc_irq,
+ IRQF_NO_SUSPEND,
+ pdev->id_entry->name,
+ rtc);
+ if (err < 0) {
+ dev_err(&pdev->dev, "could not get irq: %d\n", err);
+ goto out_unregister;
+ }
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ return 0;
+
+out_unregister:
+ rtc_device_unregister(rtc);
+ return err;
+}
+
+static int __devexit ab_rtc_remove(struct platform_device *pdev)
+{
+ const struct ab_rtc *variant = to_ab_rtc(&pdev->dev);
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ int irq = platform_get_irq_byname(pdev, variant->irqname);
+
+ if (irq >= 0)
+ free_irq(irq, rtc);
+ rtc_device_unregister(rtc);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_device_id ab_rtc_id_table[] = {
+ { "ab5500-rtc", (kernel_ulong_t)&ab5500_rtc, },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, ab_rtc_id_table);
+
+static struct platform_driver ab_rtc_driver = {
+ .driver.name = "ab-rtc",
+ .driver.owner = THIS_MODULE,
+ .id_table = ab_rtc_id_table,
+ .probe = ab_rtc_probe,
+ .remove = __devexit_p(ab_rtc_remove),
+};
+
+static int __init ab_rtc_init(void)
+{
+ return platform_driver_register(&ab_rtc_driver);
+}
+module_init(ab_rtc_init);
+
+static void __exit ab_rtc_exit(void)
+{
+ platform_driver_unregister(&ab_rtc_driver);
+}
+module_exit(ab_rtc_exit);
+
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
+MODULE_DESCRIPTION("AB5500 RTC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c
index d26780ea254..261a07e0fb2 100644
--- a/drivers/rtc/rtc-ab3100.c
+++ b/drivers/rtc/rtc-ab3100.c
@@ -235,6 +235,7 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev)
err = PTR_ERR(rtc);
return err;
}
+ platform_set_drvdata(pdev, rtc);
return 0;
}
@@ -244,6 +245,7 @@ static int __exit ab3100_rtc_remove(struct platform_device *pdev)
struct rtc_device *rtc = platform_get_drvdata(pdev);
rtc_device_unregister(rtc);
+ platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
index 2fda03125e5..89e282292d3 100644
--- a/drivers/rtc/rtc-ab8500.c
+++ b/drivers/rtc/rtc-ab8500.c
@@ -14,26 +14,26 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
+#include <linux/mfd/abx500.h>
#include <linux/mfd/ab8500.h>
#include <linux/delay.h>
-#define AB8500_RTC_SOFF_STAT_REG 0x0F00
-#define AB8500_RTC_CC_CONF_REG 0x0F01
-#define AB8500_RTC_READ_REQ_REG 0x0F02
-#define AB8500_RTC_WATCH_TSECMID_REG 0x0F03
-#define AB8500_RTC_WATCH_TSECHI_REG 0x0F04
-#define AB8500_RTC_WATCH_TMIN_LOW_REG 0x0F05
-#define AB8500_RTC_WATCH_TMIN_MID_REG 0x0F06
-#define AB8500_RTC_WATCH_TMIN_HI_REG 0x0F07
-#define AB8500_RTC_ALRM_MIN_LOW_REG 0x0F08
-#define AB8500_RTC_ALRM_MIN_MID_REG 0x0F09
-#define AB8500_RTC_ALRM_MIN_HI_REG 0x0F0A
-#define AB8500_RTC_STAT_REG 0x0F0B
-#define AB8500_RTC_BKUP_CHG_REG 0x0F0C
-#define AB8500_RTC_FORCE_BKUP_REG 0x0F0D
-#define AB8500_RTC_CALIB_REG 0x0F0E
-#define AB8500_RTC_SWITCH_STAT_REG 0x0F0F
-#define AB8500_REV_REG 0x1080
+#define AB8500_RTC_SOFF_STAT_REG 0x00
+#define AB8500_RTC_CC_CONF_REG 0x01
+#define AB8500_RTC_READ_REQ_REG 0x02
+#define AB8500_RTC_WATCH_TSECMID_REG 0x03
+#define AB8500_RTC_WATCH_TSECHI_REG 0x04
+#define AB8500_RTC_WATCH_TMIN_LOW_REG 0x05
+#define AB8500_RTC_WATCH_TMIN_MID_REG 0x06
+#define AB8500_RTC_WATCH_TMIN_HI_REG 0x07
+#define AB8500_RTC_ALRM_MIN_LOW_REG 0x08
+#define AB8500_RTC_ALRM_MIN_MID_REG 0x09
+#define AB8500_RTC_ALRM_MIN_HI_REG 0x0A
+#define AB8500_RTC_STAT_REG 0x0B
+#define AB8500_RTC_BKUP_CHG_REG 0x0C
+#define AB8500_RTC_FORCE_BKUP_REG 0x0D
+#define AB8500_RTC_CALIB_REG 0x0E
+#define AB8500_RTC_SWITCH_STAT_REG 0x0F
/* RtcReadRequest bits */
#define RTC_READ_REQUEST 0x01
@@ -46,13 +46,13 @@
#define COUNTS_PER_SEC (0xF000 / 60)
#define AB8500_RTC_EPOCH 2000
-static const unsigned long ab8500_rtc_time_regs[] = {
+static const u8 ab8500_rtc_time_regs[] = {
AB8500_RTC_WATCH_TMIN_HI_REG, AB8500_RTC_WATCH_TMIN_MID_REG,
AB8500_RTC_WATCH_TMIN_LOW_REG, AB8500_RTC_WATCH_TSECHI_REG,
AB8500_RTC_WATCH_TSECMID_REG
};
-static const unsigned long ab8500_rtc_alarm_regs[] = {
+static const u8 ab8500_rtc_alarm_regs[] = {
AB8500_RTC_ALRM_MIN_HI_REG, AB8500_RTC_ALRM_MIN_MID_REG,
AB8500_RTC_ALRM_MIN_LOW_REG
};
@@ -76,29 +76,30 @@ static unsigned long get_elapsed_seconds(int year)
static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
unsigned long timeout = jiffies + HZ;
int retval, i;
unsigned long mins, secs;
unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)];
+ u8 value;
/* Request a data read */
- retval = ab8500_write(ab8500, AB8500_RTC_READ_REQ_REG,
- RTC_READ_REQUEST);
+ retval = abx500_set_register_interruptible(dev,
+ AB8500_RTC, AB8500_RTC_READ_REQ_REG, RTC_READ_REQUEST);
if (retval < 0)
return retval;
/* Early AB8500 chips will not clear the rtc read request bit */
- if (ab8500->revision == 0) {
+ if (abx500_get_chip_id(dev) == 0) {
msleep(1);
} else {
/* Wait for some cycles after enabling the rtc read in ab8500 */
while (time_before(jiffies, timeout)) {
- retval = ab8500_read(ab8500, AB8500_RTC_READ_REQ_REG);
+ retval = abx500_get_register_interruptible(dev,
+ AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value);
if (retval < 0)
return retval;
- if (!(retval & RTC_READ_REQUEST))
+ if (!(value & RTC_READ_REQUEST))
break;
msleep(1);
@@ -107,10 +108,11 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
/* Read the Watchtime registers */
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) {
- retval = ab8500_read(ab8500, ab8500_rtc_time_regs[i]);
+ retval = abx500_get_register_interruptible(dev,
+ AB8500_RTC, ab8500_rtc_time_regs[i], &value);
if (retval < 0)
return retval;
- buf[i] = retval;
+ buf[i] = value;
}
mins = (buf[0] << 16) | (buf[1] << 8) | buf[2];
@@ -128,7 +130,6 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
int retval, i;
unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)];
unsigned long no_secs, no_mins, secs = 0;
@@ -162,27 +163,29 @@ static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
buf[0] = (no_mins >> 16) & 0xFF;
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) {
- retval = ab8500_write(ab8500, ab8500_rtc_time_regs[i], buf[i]);
+ retval = abx500_set_register_interruptible(dev, AB8500_RTC,
+ ab8500_rtc_time_regs[i], buf[i]);
if (retval < 0)
return retval;
}
/* Request a data write */
- return ab8500_write(ab8500, AB8500_RTC_READ_REQ_REG, RTC_WRITE_REQUEST);
+ return abx500_set_register_interruptible(dev, AB8500_RTC,
+ AB8500_RTC_READ_REQ_REG, RTC_WRITE_REQUEST);
}
static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
- struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
int retval, i;
- int rtc_ctrl;
+ u8 rtc_ctrl, value;
unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
unsigned long secs, mins;
/* Check if the alarm is enabled or not */
- rtc_ctrl = ab8500_read(ab8500, AB8500_RTC_STAT_REG);
- if (rtc_ctrl < 0)
- return rtc_ctrl;
+ retval = abx500_get_register_interruptible(dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, &rtc_ctrl);
+ if (retval < 0)
+ return retval;
if (rtc_ctrl & RTC_ALARM_ENA)
alarm->enabled = 1;
@@ -192,10 +195,11 @@ static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
alarm->pending = 0;
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) {
- retval = ab8500_read(ab8500, ab8500_rtc_alarm_regs[i]);
+ retval = abx500_get_register_interruptible(dev, AB8500_RTC,
+ ab8500_rtc_alarm_regs[i], &value);
if (retval < 0)
return retval;
- buf[i] = retval;
+ buf[i] = value;
}
mins = (buf[0] << 16) | (buf[1] << 8) | (buf[2]);
@@ -211,15 +215,13 @@ static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
static int ab8500_rtc_irq_enable(struct device *dev, unsigned int enabled)
{
- struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
-
- return ab8500_set_bits(ab8500, AB8500_RTC_STAT_REG, RTC_ALARM_ENA,
- enabled ? RTC_ALARM_ENA : 0);
+ return abx500_mask_and_set_register_interruptible(dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, RTC_ALARM_ENA,
+ enabled ? RTC_ALARM_ENA : 0);
}
static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
- struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
int retval, i;
unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
unsigned long mins, secs = 0;
@@ -239,15 +241,27 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
*/
secs -= get_elapsed_seconds(AB8500_RTC_EPOCH);
+#ifndef CONFIG_ANDROID
+ secs += 30; /* Round to nearest minute */
+#endif
+
mins = secs / 60;
+#ifdef CONFIG_ANDROID
+ /*
+ * Needed due to Android believes all hw have a wake-up resolution
+ * in seconds.
+ */
+ mins++;
+#endif
buf[2] = mins & 0xFF;
buf[1] = (mins >> 8) & 0xFF;
buf[0] = (mins >> 16) & 0xFF;
/* Set the alarm time */
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) {
- retval = ab8500_write(ab8500, ab8500_rtc_alarm_regs[i], buf[i]);
+ retval = abx500_set_register_interruptible(dev, AB8500_RTC,
+ ab8500_rtc_alarm_regs[i], buf[i]);
if (retval < 0)
return retval;
}
@@ -276,10 +290,9 @@ static const struct rtc_class_ops ab8500_rtc_ops = {
static int __devinit ab8500_rtc_probe(struct platform_device *pdev)
{
- struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
int err;
struct rtc_device *rtc;
- int rtc_ctrl;
+ u8 rtc_ctrl;
int irq;
irq = platform_get_irq_byname(pdev, "ALARM");
@@ -287,17 +300,18 @@ static int __devinit ab8500_rtc_probe(struct platform_device *pdev)
return irq;
/* For RTC supply test */
- err = ab8500_set_bits(ab8500, AB8500_RTC_STAT_REG, RTC_STATUS_DATA,
- RTC_STATUS_DATA);
+ err = abx500_mask_and_set_register_interruptible(&pdev->dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, RTC_STATUS_DATA, RTC_STATUS_DATA);
if (err < 0)
return err;
/* Wait for reset by the PorRtc */
msleep(1);
- rtc_ctrl = ab8500_read(ab8500, AB8500_RTC_STAT_REG);
- if (rtc_ctrl < 0)
- return rtc_ctrl;
+ err = abx500_get_register_interruptible(&pdev->dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, &rtc_ctrl);
+ if (err < 0)
+ return err;
/* Check if the RTC Supply fails */
if (!(rtc_ctrl & RTC_STATUS_DATA)) {
@@ -313,8 +327,8 @@ static int __devinit ab8500_rtc_probe(struct platform_device *pdev)
return err;
}
- err = request_threaded_irq(irq, NULL, rtc_alarm_handler, 0,
- "ab8500-rtc", rtc);
+ err = request_threaded_irq(irq, NULL, rtc_alarm_handler,
+ IRQF_NO_SUSPEND, "ab8500-rtc", rtc);
if (err < 0) {
rtc_device_unregister(rtc);
return err;
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 3587d9922f2..4e43619adb6 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -404,7 +404,7 @@ static int pl031_probe(struct amba_device *adev, struct amba_id *id)
}
if (request_irq(adev->irq[0], pl031_interrupt,
- IRQF_DISABLED | IRQF_SHARED, "rtc-pl031", ldata)) {
+ IRQF_DISABLED, "rtc-pl031", ldata)) {
ret = -EIO;
goto out_no_irq;
}
@@ -456,7 +456,7 @@ static struct rtc_class_ops stv2_pl031_ops = {
.irq_set_freq = pl031_irq_set_freq,
};
-static struct amba_id pl031_ids[] __initdata = {
+static struct amba_id pl031_ids[] = {
{
.id = 0x00041031,
.mask = 0x000fffff,
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 891e1dd65f2..42a7a4b1001 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -300,6 +300,13 @@ static const struct serial8250_config uart_config[] = {
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
.flags = UART_CAP_FIFO | UART_CAP_AFE,
},
+ [PORT_U6_16550A] = {
+ .name = "U6_16550A",
+ .fifo_size = 64,
+ .tx_loadsz = 64,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+ .flags = UART_CAP_FIFO | UART_CAP_AFE,
+ },
};
#if defined (CONFIG_SERIAL_8250_AU1X00)
@@ -1075,6 +1082,15 @@ static void autoconfig_16550a(struct uart_8250_port *up)
DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 ");
}
serial_outp(up, UART_IER, iersave);
+
+ /*
+ * We distinguish between 16550A and U6 16550A by counting
+ * how many bytes are in the FIFO.
+ */
+ if (up->port.type == PORT_16550A && size_fifo(up) == 64) {
+ up->port.type = PORT_U6_16550A;
+ up->capabilities |= UART_CAP_AFE;
+ }
}
/*
@@ -2229,9 +2245,9 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int
return quot;
}
-static void
-serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
+void
+serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned char cval, fcr = 0;
@@ -2407,6 +2423,17 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
}
+EXPORT_SYMBOL(serial8250_do_set_termios);
+
+static void
+serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ if (port->set_termios)
+ port->set_termios(port, termios, old);
+ else
+ serial8250_do_set_termios(port, termios, old);
+}
static void
serial8250_set_ldisc(struct uart_port *port)
@@ -2994,6 +3021,7 @@ static int __devinit serial8250_probe(struct platform_device *dev)
port.type = p->type;
port.serial_in = p->serial_in;
port.serial_out = p->serial_out;
+ port.set_termios = p->set_termios;
port.dev = &dev->dev;
port.irqflags |= irqflag;
ret = serial8250_register_port(&port);
@@ -3157,6 +3185,9 @@ int serial8250_register_port(struct uart_port *port)
uart->port.serial_in = port->serial_in;
if (port->serial_out)
uart->port.serial_out = port->serial_out;
+ /* Possibly override set_termios call */
+ if (port->set_termios)
+ uart->port.set_termios = port->set_termios;
ret = uart_add_one_port(&serial8250_reg, &uart->port);
if (ret == 0)
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 8b23165bc5d..0ad5acd044d 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -331,6 +331,15 @@ config SERIAL_AMBA_PL011_CONSOLE
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
+config SERIAL_AMBA_PL011_CLOCK_CONTROL
+ bool "Support for clock control on AMBA serial port"
+ depends on SERIAL_AMBA_PL011
+ select CONSOLE_POLL
+ ---help---
+ Say Y here if you wish to use amba set_termios function to control
+ the pl011 clock. Any positive baudrate passed enables clock,
+ otherwise disables (baudrate = B0 -> hangup)
+
config SERIAL_SB1250_DUART
tristate "BCM1xxx on-chip DUART serial support"
depends on SIBYTE_SB1xxx_SOC=y
diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c
index b09a638d051..50441ffe8e3 100644
--- a/drivers/serial/amba-pl010.c
+++ b/drivers/serial/amba-pl010.c
@@ -782,7 +782,7 @@ static int pl010_resume(struct amba_device *dev)
return 0;
}
-static struct amba_id pl010_ids[] __initdata = {
+static struct amba_id pl010_ids[] = {
{
.id = 0x00041010,
.mask = 0x000fffff,
diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c
index eb4cb480b93..1e01a838792 100644
--- a/drivers/serial/amba-pl011.c
+++ b/drivers/serial/amba-pl011.c
@@ -48,6 +48,9 @@
#include <linux/amba/serial.h>
#include <linux/clk.h>
#include <linux/slab.h>
+#include <mach/uart.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include <asm/sizes.h>
@@ -64,33 +67,415 @@
#define UART_DUMMY_DR_RX (1 << 16)
/*
+ * The console UART is handled differently for power management (it doesn't
+ * take the regulator, in order to allow the system to go to sleep even if the
+ * console is open). This should be removed once cable detect is in place.
+ */
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+#define uart_console(port) ((port)->cons \
+ && (port)->cons->index == (port)->line)
+#else
+#define uart_console(port) (0)
+#endif
+
+/* Available amba pl011 port clock states */
+enum pl011_clk_states {
+ PL011_CLK_OFF = 0, /* clock disabled */
+ PL011_CLK_REQUEST_OFF, /* disable after TX flushed */
+ PL011_CLK_ON, /* clock enabled */
+ PL011_PORT_OFF, /* port disabled */
+};
+
+/*
+ * Backup registers to be used during regulator startup/shutdown
+ */
+static const u32 backup_regs[] = {
+ UART011_IBRD,
+ UART011_FBRD,
+ ST_UART011_LCRH_RX,
+ ST_UART011_LCRH_TX,
+ UART011_CR,
+ UART011_IMSC,
+};
+
+#define UART_WA_SAVE_NR 14
+
+static void pl011_lockup_wa(unsigned long data);
+static const u32 uart_wa_reg[UART_WA_SAVE_NR] = {
+ ST_UART011_DMAWM,
+ ST_UART011_TIMEOUT,
+ ST_UART011_LCRH_RX,
+ UART011_IBRD,
+ UART011_FBRD,
+ ST_UART011_LCRH_TX,
+ UART011_IFLS,
+ ST_UART011_XFCR,
+ ST_UART011_XON1,
+ ST_UART011_XON2,
+ ST_UART011_XOFF1,
+ ST_UART011_XOFF2,
+ UART011_CR,
+ UART011_IMSC
+};
+
+static u32 uart_wa_regdata[UART_WA_SAVE_NR];
+static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0);
+
+/*
* We wrap our port structure around the generic uart_port.
*/
struct uart_amba_port {
struct uart_port port;
struct clk *clk;
- unsigned int im; /* interrupt mask */
+ unsigned int im; /* interrupt mask */
unsigned int old_status;
- unsigned int ifls; /* vendor-specific */
+ unsigned int ifls; /* vendor-specific */
+ unsigned int lcrh_tx; /* vendor-specific */
+ unsigned int lcrh_rx; /* vendor-specific */
+ bool oversampling; /* vendor-specific */
+ bool interrupt_may_hang; /* vendor-specific */
bool autorts;
+#ifdef CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL
+ enum pl011_clk_states clk_state; /* actual clock state */
+ struct delayed_work clk_off_work; /* work used for clock off */
+ unsigned int clk_off_delay; /* clock off delay */
+#endif
+ struct regulator *regulator;
+ u32 backup[ARRAY_SIZE(backup_regs)];
};
/* There is by now at least one vendor with differing details, so handle it */
struct vendor_data {
unsigned int ifls;
unsigned int fifosize;
+ unsigned int lcrh_tx;
+ unsigned int lcrh_rx;
+ bool oversampling;
+ bool interrupt_may_hang;
};
static struct vendor_data vendor_arm = {
.ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
.fifosize = 16,
+ .lcrh_tx = UART011_LCRH,
+ .lcrh_rx = UART011_LCRH,
+ .oversampling = false,
};
static struct vendor_data vendor_st = {
.ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF,
.fifosize = 64,
+ .lcrh_tx = ST_UART011_LCRH_TX,
+ .lcrh_rx = ST_UART011_LCRH_RX,
+ .oversampling = true,
+ .interrupt_may_hang = true,
};
+static struct uart_amba_port *amba_ports[UART_NR];
+
+static void __pl011_startup(struct uart_amba_port *uap)
+{
+ unsigned int cr;
+
+ writew(uap->ifls, uap->port.membase + UART011_IFLS);
+
+ /*
+ * Provoke TX FIFO interrupt into asserting.
+ */
+ cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE;
+ writew(cr, uap->port.membase + UART011_CR);
+ writew(0, uap->port.membase + UART011_FBRD);
+ writew(1, uap->port.membase + UART011_IBRD);
+ writew(0, uap->port.membase + uap->lcrh_rx);
+ if (uap->lcrh_tx != uap->lcrh_rx) {
+ int i;
+ /*
+ * Wait 10 PCLKs before writing LCRH_TX register,
+ * to get this delay write read only register 10 times
+ */
+ for (i = 0; i < 10; ++i)
+ writew(0xff, uap->port.membase + UART011_MIS);
+ writew(0, uap->port.membase + uap->lcrh_tx);
+ }
+ writew(0, uap->port.membase + UART01x_DR);
+ while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
+ barrier();
+}
+
+/* Backup the registers during regulator startup/shutdown */
+#ifdef CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL
+static int pl011_backup(struct uart_amba_port *uap, bool suspend)
+{
+ int i, cnt;
+
+ if (!suspend) {
+ __pl011_startup(uap);
+ writew(0, uap->port.membase + UART011_CR);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(backup_regs); i++) {
+ if (suspend)
+ uap->backup[i] = readw(uap->port.membase +
+ backup_regs[i]);
+ else {
+ if (backup_regs[i] == ST_UART011_LCRH_TX) {
+ /*
+ * Wait 10 PCLKs before writing LCRH_TX
+ * register, to get this delay write read
+ * only register 10 times
+ */
+ for (cnt = 0; cnt < 10; ++cnt)
+ writew(0xff, uap->port.membase +
+ UART011_MIS);
+ }
+
+ writew(uap->backup[i],
+ uap->port.membase + backup_regs[i]);
+ }
+ }
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL
+/* Turn clock off if TX buffer is empty, otherwise reschedule */
+static void pl011_clock_off(struct work_struct *work)
+{
+ struct uart_amba_port *uap = container_of(work, struct uart_amba_port,
+ clk_off_work.work);
+ struct uart_port *port = &uap->port;
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned long flags;
+ bool disable_regulator = false;
+ unsigned int busy, interrupt_status;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ interrupt_status = readw(uap->port.membase + UART011_MIS);
+ busy = readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY;
+
+ if (uap->clk_state == PL011_CLK_REQUEST_OFF) {
+ if (uart_circ_empty(xmit) && !interrupt_status && !busy) {
+ if (!uart_console(&uap->port) && uap->regulator) {
+ pl011_backup(uap, true);
+ disable_regulator = true;
+ }
+ uap->clk_state = PL011_CLK_OFF;
+ clk_disable(uap->clk);
+ } else
+ schedule_delayed_work(&uap->clk_off_work,
+ uap->clk_off_delay);
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ if (disable_regulator)
+ regulator_disable(uap->regulator);
+}
+
+/* Request to turn off uart clock once pending TX is flushed */
+static void pl011_clock_request_off(struct uart_port *port)
+{
+ unsigned long flags;
+ struct uart_amba_port *uap = (struct uart_amba_port *)(port);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (uap->clk_state == PL011_CLK_ON) {
+ uap->clk_state = PL011_CLK_REQUEST_OFF;
+ /* Turn off later */
+ schedule_delayed_work(&uap->clk_off_work,
+ uap->clk_off_delay);
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* Request to immediately turn on uart clock */
+static void pl011_clock_on(struct uart_port *port)
+{
+ unsigned long flags;
+ struct uart_amba_port *uap = (struct uart_amba_port *)(port);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ switch (uap->clk_state) {
+ case PL011_CLK_OFF:
+ clk_enable(uap->clk);
+ if (!uart_console(&uap->port) && uap->regulator) {
+ spin_unlock_irqrestore(&port->lock, flags);
+ regulator_enable(uap->regulator);
+ spin_lock_irqsave(&port->lock, flags);
+ pl011_backup(uap, false);
+ }
+ /* fallthrough */
+ case PL011_CLK_REQUEST_OFF:
+ cancel_delayed_work(&uap->clk_off_work);
+ uap->clk_state = PL011_CLK_ON;
+ break;
+ default:
+ break;
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void pl011_clock_check(struct uart_amba_port *uap)
+{
+ /* Reshedule work during off request */
+ if (uap->clk_state == PL011_CLK_REQUEST_OFF)
+ /* New TX - restart work */
+ if (cancel_delayed_work(&uap->clk_off_work))
+ schedule_delayed_work(&uap->clk_off_work,
+ uap->clk_off_delay);
+}
+
+static int pl011_power_startup(struct uart_amba_port *uap)
+{
+ int retval = 0;
+
+ if (uap->clk_state == PL011_PORT_OFF) {
+ if (!uart_console(&uap->port) && uap->regulator)
+ regulator_enable(uap->regulator);
+ retval = clk_enable(uap->clk);
+ if (!retval)
+ uap->clk_state = PL011_CLK_ON;
+ else
+ uap->clk_state = PL011_PORT_OFF;
+ }
+
+ return retval;
+}
+
+static void pl011_power_shutdown(struct uart_amba_port *uap)
+{
+ bool disable_regulator = false;
+
+ cancel_delayed_work_sync(&uap->clk_off_work);
+
+ spin_lock_irq(&uap->port.lock);
+ if (uap->clk_state == PL011_CLK_ON ||
+ uap->clk_state == PL011_CLK_REQUEST_OFF) {
+ clk_disable(uap->clk);
+ if (!uart_console(&uap->port) && uap->regulator)
+ disable_regulator = true;
+ }
+ uap->clk_state = PL011_PORT_OFF;
+ spin_unlock_irq(&uap->port.lock);
+
+ if (disable_regulator)
+ regulator_disable(uap->regulator);
+}
+
+static void
+pl011_clock_control(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ speed_t new_baud = tty_termios_baud_rate(termios);
+
+ if (new_baud == 0)
+ pl011_clock_request_off(port);
+ else
+ pl011_clock_on(port);
+}
+
+static void pl011_clock_control_init(struct uart_amba_port *uap)
+{
+ uap->clk_state = PL011_PORT_OFF;
+ INIT_DELAYED_WORK(&uap->clk_off_work, pl011_clock_off);
+ uap->clk_off_delay = HZ / 10; /* 100 ms */
+}
+
+#else
+/* Blank functions for clock control */
+static inline void pl011_clock_check(struct uart_amba_port *uap)
+{
+}
+
+static inline int pl011_power_startup(struct uart_amba_port *uap)
+{
+ return clk_enable(uap->clk);
+}
+
+static inline void pl011_power_shutdown(struct uart_amba_port *uap)
+{
+ clk_disable(uap->clk);
+}
+
+static inline void
+pl011_clock_control(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+}
+
+static inline void pl011_clock_control_init(struct uart_amba_port *uap)
+{
+}
+#endif
+
+/*
+ * pl011_lockup_wa
+ * This workaround aims to break the deadlock situation
+ * when after long transfer over uart in hardware flow
+ * control, uart interrupt registers cannot be cleared.
+ * Hence uart transfer gets blocked.
+ *
+ * It is seen that during such deadlock condition ICR
+ * don't get cleared even on multiple write. This leads
+ * pass_counter to decrease and finally reach zero. This
+ * can be taken as trigger point to run this UART_BT_WA.
+ *
+ */
+static void pl011_lockup_wa(unsigned long data)
+{
+ struct uart_amba_port *uap = amba_ports[0];
+ void __iomem *base = uap->port.membase;
+ struct circ_buf *xmit = &uap->port.state->xmit;
+ struct tty_struct *tty = uap->port.state->port.tty;
+ int buf_empty_retries = 200;
+ int loop;
+
+ /* Stop HCI layer from submitting data for tx */
+ tty->hw_stopped = 1;
+
+ while (!uart_circ_empty(xmit)) {
+ if (buf_empty_retries-- == 0)
+ break;
+ udelay(100);
+ }
+
+ /* Backup registers */
+ for (loop = 0; loop < UART_WA_SAVE_NR; loop++)
+ uart_wa_regdata[loop] = readl(base + uart_wa_reg[loop]);
+
+ /* Disable UART so that FIFO data is flushed out */
+ writew(0x00, uap->port.membase + UART011_CR);
+
+ /* Soft reset UART module */
+ if (uap->port.dev->platform_data) {
+ struct uart_amba_plat_data *plat;
+
+ plat = uap->port.dev->platform_data;
+ if (plat->reset)
+ plat->reset();
+ }
+
+ /* Restore registers */
+ for (loop = 0; loop < UART_WA_SAVE_NR; loop++)
+ writew(uart_wa_regdata[loop] ,
+ uap->port.membase + uart_wa_reg[loop]);
+
+ /* Initialise the old status of the modem signals */
+ uap->old_status = readw(uap->port.membase + UART01x_FR) &
+ UART01x_FR_MODEM_ANY;
+
+ if (readl(base + UART011_MIS) & 0x2)
+ printk(KERN_EMERG "UART_BT_WA: ***FAILED***\n");
+
+ /* Start Tx/Rx */
+ tty->hw_stopped = 0;
+}
+
static void pl011_stop_tx(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
@@ -200,6 +585,9 @@ static void pl011_tx_chars(struct uart_amba_port *uap)
break;
} while (--count > 0);
+ if (count)
+ pl011_clock_check(uap);
+
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);
@@ -244,7 +632,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
do {
writew(status & ~(UART011_TXIS|UART011_RTIS|
UART011_RXIS),
- uap->port.membase + UART011_ICR);
+ uap->port.membase + UART011_ICR);
if (status & (UART011_RTIS|UART011_RXIS))
pl011_rx_chars(uap);
@@ -254,8 +642,11 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
if (status & UART011_TXIS)
pl011_tx_chars(uap);
- if (pass_counter-- == 0)
+ if (pass_counter-- == 0) {
+ if (uap->interrupt_may_hang)
+ tasklet_schedule(&pl011_lockup_tlet);
break;
+ }
status = readw(uap->port.membase + UART011_MIS);
} while (status != 0);
@@ -327,12 +718,12 @@ static void pl011_break_ctl(struct uart_port *port, int break_state)
unsigned int lcr_h;
spin_lock_irqsave(&uap->port.lock, flags);
- lcr_h = readw(uap->port.membase + UART011_LCRH);
+ lcr_h = readw(uap->port.membase + uap->lcrh_tx);
if (break_state == -1)
lcr_h |= UART01x_LCRH_BRK;
else
lcr_h &= ~UART01x_LCRH_BRK;
- writew(lcr_h, uap->port.membase + UART011_LCRH);
+ writew(lcr_h, uap->port.membase + uap->lcrh_tx);
spin_unlock_irqrestore(&uap->port.lock, flags);
}
@@ -369,9 +760,9 @@ static int pl011_startup(struct uart_port *port)
int retval;
/*
- * Try to enable the clock producer.
+ * Try to enable the clock producer and the regulator.
*/
- retval = clk_enable(uap->clk);
+ retval = pl011_power_startup(uap);
if (retval)
goto out;
@@ -384,19 +775,7 @@ static int pl011_startup(struct uart_port *port)
if (retval)
goto clk_dis;
- writew(uap->ifls, uap->port.membase + UART011_IFLS);
-
- /*
- * Provoke TX FIFO interrupt into asserting.
- */
- cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE;
- writew(cr, uap->port.membase + UART011_CR);
- writew(0, uap->port.membase + UART011_FBRD);
- writew(1, uap->port.membase + UART011_IBRD);
- writew(0, uap->port.membase + UART011_LCRH);
- writew(0, uap->port.membase + UART01x_DR);
- while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
- barrier();
+ __pl011_startup(uap);
cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
writew(cr, uap->port.membase + UART011_CR);
@@ -414,18 +793,35 @@ static int pl011_startup(struct uart_port *port)
writew(uap->im, uap->port.membase + UART011_IMSC);
spin_unlock_irq(&uap->port.lock);
+ if (uap->port.dev->platform_data) {
+ struct uart_amba_plat_data *plat;
+
+ plat = uap->port.dev->platform_data;
+ if (plat->init)
+ plat->init();
+ }
+
return 0;
clk_dis:
- clk_disable(uap->clk);
+ pl011_power_shutdown(uap);
out:
return retval;
}
+static void pl011_shutdown_channel(struct uart_amba_port *uap,
+ unsigned int lcrh)
+{
+ unsigned long val;
+
+ val = readw(uap->port.membase + lcrh);
+ val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN);
+ writew(val, uap->port.membase + lcrh);
+}
+
static void pl011_shutdown(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
- unsigned long val;
/*
* disable all interrupts
@@ -450,14 +846,48 @@ static void pl011_shutdown(struct uart_port *port)
/*
* disable break condition and fifos
*/
- val = readw(uap->port.membase + UART011_LCRH);
- val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN);
- writew(val, uap->port.membase + UART011_LCRH);
+ pl011_shutdown_channel(uap, uap->lcrh_rx);
+ if (uap->lcrh_rx != uap->lcrh_tx)
+ pl011_shutdown_channel(uap, uap->lcrh_tx);
/*
- * Shut down the clock producer
+ * Shut down the clock producer and the regulator
*/
- clk_disable(uap->clk);
+ pl011_power_shutdown(uap);
+
+ if (uap->port.dev->platform_data) {
+ struct uart_amba_plat_data *plat;
+
+ plat = uap->port.dev->platform_data;
+ if (plat->exit)
+ plat->exit();
+ }
+}
+
+/* Power/Clock management. */
+static void pl011_serial_pm(struct uart_port *port, unsigned int state,
+unsigned int oldstate)
+{
+ struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+ switch (state) {
+ case 0: /*fully on */
+ /*
+ * Enable the peripheral clock for this serial port.
+ * This is called on uart_open() or a resume event.
+ */
+ pl011_power_startup(uap);
+ break;
+ case 3: /* powered down */
+ /*
+ * Disable the peripheral clock for this serial port.
+ * This is called on uart_close() or a suspend event.
+ */
+ pl011_power_shutdown(uap);
+ break;
+ default:
+ printk(KERN_ERR "pl011_serial: unknown pm %d\n", state);
+ }
}
static void
@@ -470,10 +900,22 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
unsigned int baud, quot;
/*
+ * Must be before uart_get_baud_rate() call, because
+ * this function changes baudrate to default in case of 0
+ * B0 hangup !!!
+ */
+ pl011_clock_control(port, termios, old);
+
+ /*
* Ask the core to calculate the divisor for us.
*/
- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
- quot = port->uartclk * 4 / baud;
+ baud = uart_get_baud_rate(port, termios, old, 0,
+ port->uartclk/(uap->oversampling ? 8 : 16));
+
+ if (baud > port->uartclk/16)
+ quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud);
+ else
+ quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud);
switch (termios->c_cflag & CSIZE) {
case CS5:
@@ -552,6 +994,13 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
uap->autorts = false;
}
+ if (uap->oversampling) {
+ if (baud > port->uartclk/16)
+ old_cr |= ST_UART011_CR_OVSFACT;
+ else
+ old_cr &= ~ST_UART011_CR_OVSFACT;
+ }
+
/* Set baud rate */
writew(quot & 0x3f, port->membase + UART011_FBRD);
writew(quot >> 6, port->membase + UART011_IBRD);
@@ -561,7 +1010,17 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
* NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
* ----------^----------^----------^----------^-----
*/
- writew(lcr_h, port->membase + UART011_LCRH);
+ writew(lcr_h, port->membase + uap->lcrh_rx);
+ if (uap->lcrh_rx != uap->lcrh_tx) {
+ int i;
+ /*
+ * Wait 10 PCLKs before writing LCRH_TX register,
+ * to get this delay write read only register 10 times
+ */
+ for (i = 0; i < 10; ++i)
+ writew(0xff, uap->port.membase + UART011_MIS);
+ writew(lcr_h, port->membase + uap->lcrh_tx);
+ }
writew(old_cr, port->membase + UART011_CR);
spin_unlock_irqrestore(&port->lock, flags);
@@ -632,14 +1091,13 @@ static struct uart_ops amba_pl011_pops = {
.request_port = pl010_request_port,
.config_port = pl010_config_port,
.verify_port = pl010_verify_port,
+ .pm = pl011_serial_pm,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = pl010_get_poll_char,
.poll_put_char = pl010_put_poll_char,
#endif
};
-static struct uart_amba_port *amba_ports[UART_NR];
-
#ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE
static void pl011_console_putchar(struct uart_port *port, int ch)
@@ -688,7 +1146,7 @@ pl011_console_get_options(struct uart_amba_port *uap, int *baud,
if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) {
unsigned int lcr_h, ibrd, fbrd;
- lcr_h = readw(uap->port.membase + UART011_LCRH);
+ lcr_h = readw(uap->port.membase + uap->lcrh_tx);
*parity = 'n';
if (lcr_h & UART01x_LCRH_PEN) {
@@ -707,6 +1165,12 @@ pl011_console_get_options(struct uart_amba_port *uap, int *baud,
fbrd = readw(uap->port.membase + UART011_FBRD);
*baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd);
+
+ if (uap->oversampling) {
+ if (readw(uap->port.membase + UART011_CR)
+ & ST_UART011_CR_OVSFACT)
+ *baud *= 2;
+ }
}
}
@@ -729,6 +1193,14 @@ static int __init pl011_console_setup(struct console *co, char *options)
if (!uap)
return -ENODEV;
+ if (uap->port.dev->platform_data) {
+ struct uart_amba_plat_data *plat;
+
+ plat = uap->port.dev->platform_data;
+ if (plat->init)
+ plat->init();
+ }
+
uap->port.uartclk = clk_get_rate(uap->clk);
if (options)
@@ -793,6 +1265,12 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id)
goto free;
}
+ uap->regulator = regulator_get(&dev->dev, "v-uart");
+ if (IS_ERR(uap->regulator)) {
+ dev_warn(&dev->dev, "could not get uart regulator\n");
+ uap->regulator = NULL;
+ }
+
uap->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(uap->clk)) {
ret = PTR_ERR(uap->clk);
@@ -800,6 +1278,10 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id)
}
uap->ifls = vendor->ifls;
+ uap->lcrh_rx = vendor->lcrh_rx;
+ uap->lcrh_tx = vendor->lcrh_tx;
+ uap->oversampling = vendor->oversampling;
+ uap->interrupt_may_hang = vendor->interrupt_may_hang;
uap->port.dev = &dev->dev;
uap->port.mapbase = dev->res.start;
uap->port.membase = base;
@@ -813,12 +1295,17 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id)
amba_ports[i] = uap;
amba_set_drvdata(dev, uap);
+
+ pl011_clock_control_init(uap);
+
ret = uart_add_one_port(&amba_reg, &uap->port);
if (ret) {
amba_set_drvdata(dev, NULL);
amba_ports[i] = NULL;
clk_put(uap->clk);
unmap:
+ if (uap->regulator)
+ regulator_put(uap->regulator);
iounmap(base);
free:
kfree(uap);
@@ -841,6 +1328,8 @@ static int pl011_remove(struct amba_device *dev)
amba_ports[i] = NULL;
iounmap(uap->port.membase);
+ if (uap->regulator)
+ regulator_put(uap->regulator);
clk_put(uap->clk);
kfree(uap);
return 0;
@@ -854,6 +1343,13 @@ static int pl011_suspend(struct amba_device *dev, pm_message_t state)
if (!uap)
return -EINVAL;
+#ifdef CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL
+ cancel_delayed_work_sync(&uap->clk_off_work);
+
+ if (uap->clk_state == PL011_CLK_OFF)
+ return 0;
+#endif
+
return uart_suspend_port(&amba_reg, &uap->port);
}
@@ -864,11 +1360,16 @@ static int pl011_resume(struct amba_device *dev)
if (!uap)
return -EINVAL;
+#ifdef CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL
+ if (uap->clk_state == PL011_CLK_OFF)
+ return 0;
+#endif
+
return uart_resume_port(&amba_reg, &uap->port);
}
#endif
-static struct amba_id pl011_ids[] __initdata = {
+static struct amba_id pl011_ids[] = {
{
.id = 0x00041011,
.mask = 0x000fffff,
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index 7f283070951..a97e530ea75 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -94,6 +94,9 @@ static void __uart_start(struct tty_struct *tty)
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
+ if (port->ops->wake_peer)
+ port->ops->wake_peer(port);
+
if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&
!tty->stopped && !tty->hw_stopped)
port->ops->start_tx(port);
@@ -2090,7 +2093,19 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
/*
* Re-enable the console device after suspending.
*/
- if (uart_console(uport)) {
+ if (console_suspend_enabled && uart_console(uport)) {
+ /*
+ * First try to use the console cflag setting.
+ */
+ memset(&termios, 0, sizeof(struct ktermios));
+ termios.c_cflag = uport->cons->cflag;
+
+ /*
+ * If that's unset, use the tty termios setting.
+ */
+ if (port->tty && port->tty->termios && termios.c_cflag == 0)
+ termios = *(port->tty->termios);
+
uart_change_pm(state, 0);
uport->ops->set_termios(uport, &termios, NULL);
console_start(uport->cons);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 91c2f4f3af1..4c71e7a9086 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -90,6 +90,20 @@ config SPI_BITBANG
need it. You only need to select this explicitly to support driver
modules that aren't part of this kernel tree.
+config STM_MSP_SPI
+ tristate "STM MSP CONTROLLER (SPI master)"
+ default y
+ help
+ This enables using the STM MSP controller in master
+ mode.
+
+config SPI_WORKQUEUE
+ bool "SPI_WORKQUEUE"
+ depends on STM_MSP_SPI
+ default n
+ help
+ This feature allow SPI works to be deferred in MSP driver.
+
config SPI_BUTTERFLY
tristate "Parallel port adapter for AVR Butterfly (DEVELOPMENT)"
depends on PARPORT
@@ -216,8 +230,8 @@ config SPI_ORION
This enables using the SPI master controller on the Orion chips.
config SPI_PL022
- tristate "ARM AMBA PL022 SSP controller (EXPERIMENTAL)"
- depends on ARM_AMBA && EXPERIMENTAL
+ tristate "ARM AMBA PL022 SSP controller"
+ depends on ARM_AMBA
default y if MACH_U300
default y if ARCH_REALVIEW
default y if INTEGRATOR_IMPD1
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e9cbd18217a..e1771805521 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o
obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o
+obj-$(CONFIG_STM_MSP_SPI) += stm_msp.o
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c
index f0a1418ce66..2e20f60f027 100644
--- a/drivers/spi/amba-pl022.c
+++ b/drivers/spi/amba-pl022.c
@@ -27,7 +27,6 @@
/*
* TODO:
* - add timeout on polled transfers
- * - add generic DMA framework support
*/
#include <linux/init.h>
@@ -45,6 +44,10 @@
#include <linux/amba/pl022.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/pm_runtime.h>
/*
* This macro is used to define some register default values.
@@ -320,6 +323,7 @@ enum ssp_writing {
* @extended_cr: 32 bit wide control register 0 with extra
* features and extra features in CR1 as found in the ST variants
* @pl023: supports a subset of the ST extensions called "PL023"
+ * @loopback: supports loopback mode
*/
struct vendor_data {
int fifodepth;
@@ -327,6 +331,7 @@ struct vendor_data {
bool unidir;
bool extended_cr;
bool pl023;
+ bool loopback;
};
/**
@@ -335,12 +340,13 @@ struct vendor_data {
* @vendor: Vendor data for the IP block
* @phybase: The physical memory where the SSP device resides
* @virtbase: The virtual memory where the SSP is mapped
+ * @clk: Pointer to the SPI clock structure.
* @master: SPI framework hookup
* @master_info: controller-specific data from machine setup
- * @regs: SSP controller register's virtual address
- * @pump_messages: Work struct for scheduling work to the workqueue
- * @lock: spinlock to syncronise access to driver data
* @workqueue: a workqueue on which any spi_message request is queued
+ * @pump_messages: Work struct for scheduling work to the workqueue
+ * @queue_lock: spinlock to synchronise access to driver data
+ * @queue: list of spi_message to be serviced
* @busy: workqueue is busy
* @run: workqueue is running
* @pump_transfers: Tasklet used in Interrupt Transfer mode
@@ -351,8 +357,18 @@ struct vendor_data {
* @tx_end: end position in TX buffer to be read
* @rx: current position in RX buffer to be written
* @rx_end: end position in RX buffer to be written
- * @readingtype: the type of read currently going on
- * @writingtype: the type or write currently going on
+ * @read: data width used for rx
+ * @write: data width used for tx
+ * @exp_fifo_level: counter used to keep a track of amount data written to and
+ * read from FIFO.
+ * @dma_rx_channel: Rx DMA channel descriptor
+ * @dma_tx_channel: Tx DMA channel descriptor
+ * @sgt_rx: Pointer to the rx buffer sg table.
+ * @sgt_tx: Pointer to the tx buffer sg table.
+ * @dummypage: Page used when no tx/rx buffer is supplied for transmission.
+ * @regulator: Pointer to the SPI regulator structure.
+ * @rx_lev_trig: RX FIFO Trigger Level
+ * @tx_lev_trig: TX FIFO Trigger Level
*/
struct pl022 {
struct amba_device *adev;
@@ -381,6 +397,16 @@ struct pl022 {
enum ssp_reading read;
enum ssp_writing write;
u32 exp_fifo_level;
+ /* DMA settings */
+#ifdef CONFIG_DMA_ENGINE
+ struct dma_chan *dma_rx_channel;
+ struct dma_chan *dma_tx_channel;
+ struct sg_table sgt_rx;
+ struct sg_table sgt_tx;
+ char *dummypage;
+#endif
+ enum ssp_rx_level_trig rx_lev_trig;
+ enum ssp_tx_level_trig tx_lev_trig;
};
/**
@@ -406,7 +432,7 @@ struct chip_data {
u16 dmacr;
u16 cpsr;
u8 n_bytes;
- u8 enable_dma:1;
+ bool enable_dma;
enum ssp_reading read;
enum ssp_writing write;
void (*cs_control) (u32 command);
@@ -503,8 +529,14 @@ static void giveback(struct pl022 *pl022)
msg->state = NULL;
if (msg->complete)
msg->complete(msg->context);
+
+ /* disable the SPI/SSP operation */
+ writew((readw(SSP_CR1(pl022->virtbase)) &
+ (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
+
/* This message is completed, so let's turn off the clock! */
clk_disable(pl022->clk);
+ pm_runtime_put(&pl022->adev->dev);
}
/**
@@ -762,6 +794,375 @@ static void *next_transfer(struct pl022 *pl022)
}
return STATE_DONE;
}
+
+/*
+ * This DMA functionality is only compiled in if we have
+ * access to the generic DMA devices/DMA engine.
+ */
+#ifdef CONFIG_DMA_ENGINE
+static void unmap_free_dma_scatter(struct pl022 *pl022)
+{
+ /* Unmap and free the SG tables */
+ dma_unmap_sg(&pl022->adev->dev, pl022->sgt_tx.sgl,
+ pl022->sgt_tx.nents, DMA_TO_DEVICE);
+ dma_unmap_sg(&pl022->adev->dev, pl022->sgt_rx.sgl,
+ pl022->sgt_rx.nents, DMA_FROM_DEVICE);
+ sg_free_table(&pl022->sgt_rx);
+ sg_free_table(&pl022->sgt_tx);
+}
+
+static void dma_callback(void *data)
+{
+ struct pl022 *pl022 = data;
+ struct spi_message *msg = pl022->cur_msg;
+
+ BUG_ON(!pl022->sgt_rx.sgl);
+
+#ifdef VERBOSE_DEBUG
+ /*
+ * Optionally dump out buffers to inspect contents, this is
+ * good if you want to convince yourself that the loopback
+ * read/write contents are the same, when adopting to a new
+ * DMA engine.
+ */
+ {
+ struct scatterlist *sg;
+ unsigned int i;
+
+ dma_sync_sg_for_cpu(&pl022->adev->dev,
+ pl022->sgt_rx.sgl,
+ pl022->sgt_rx.nents,
+ DMA_FROM_DEVICE);
+
+ for_each_sg(pl022->sgt_rx.sgl, sg, pl022->sgt_rx.nents, i) {
+ dev_dbg(&pl022->adev->dev, "SPI RX SG ENTRY: %d", i);
+ print_hex_dump(KERN_ERR, "SPI RX: ",
+ DUMP_PREFIX_OFFSET,
+ 16,
+ 1,
+ sg_virt(sg),
+ sg_dma_len(sg),
+ 1);
+ }
+ for_each_sg(pl022->sgt_tx.sgl, sg, pl022->sgt_tx.nents, i) {
+ dev_dbg(&pl022->adev->dev, "SPI TX SG ENTRY: %d", i);
+ print_hex_dump(KERN_ERR, "SPI TX: ",
+ DUMP_PREFIX_OFFSET,
+ 16,
+ 1,
+ sg_virt(sg),
+ sg_dma_len(sg),
+ 1);
+ }
+ }
+#endif
+
+ unmap_free_dma_scatter(pl022);
+
+ /* Update total bytes transfered */
+ msg->actual_length += pl022->cur_transfer->len;
+ if (pl022->cur_transfer->cs_change)
+ pl022->cur_chip->
+ cs_control(SSP_CHIP_DESELECT);
+
+ /* Move to next transfer */
+ msg->state = next_transfer(pl022);
+ tasklet_schedule(&pl022->pump_transfers);
+}
+
+static void setup_dma_scatter(struct pl022 *pl022,
+ void *buffer,
+ unsigned int length,
+ struct sg_table *sgtab)
+{
+ struct scatterlist *sg;
+ int bytesleft = length;
+ void *bufp = buffer;
+ int mapbytes;
+ int i;
+
+ if (buffer) {
+ for_each_sg(sgtab->sgl, sg, sgtab->nents, i) {
+ /*
+ * If there are less bytes left than what fits
+ * in the current page (plus page alignment offset)
+ * we just feed in this, else we stuff in as much
+ * as we can.
+ */
+ if (bytesleft < (PAGE_SIZE - offset_in_page(bufp)))
+ mapbytes = bytesleft;
+ else
+ mapbytes = PAGE_SIZE - offset_in_page(bufp);
+ sg_set_page(sg, virt_to_page(bufp),
+ mapbytes, offset_in_page(bufp));
+ bufp += mapbytes;
+ bytesleft -= mapbytes;
+ dev_dbg(&pl022->adev->dev,
+ "set RX/TX target page @ %p, %d bytes, %d left\n",
+ bufp, mapbytes, bytesleft);
+ }
+ } else {
+ /* Map the dummy buffer on every page */
+ for_each_sg(sgtab->sgl, sg, sgtab->nents, i) {
+ if (bytesleft < PAGE_SIZE)
+ mapbytes = bytesleft;
+ else
+ mapbytes = PAGE_SIZE;
+ sg_set_page(sg, virt_to_page(pl022->dummypage),
+ mapbytes, 0);
+ bytesleft -= mapbytes;
+ dev_dbg(&pl022->adev->dev,
+ "set RX/TX to dummy page %d bytes, %d left\n",
+ mapbytes, bytesleft);
+
+ }
+ }
+ BUG_ON(bytesleft);
+}
+
+/**
+ * configure_dma - configures the channels for the next transfer
+ * @pl022: SSP driver's private data structure
+ */
+static int configure_dma(struct pl022 *pl022)
+{
+ struct dma_slave_config rx_conf = {
+ .src_addr = SSP_DR(pl022->phybase),
+ .direction = DMA_FROM_DEVICE,
+ };
+ struct dma_slave_config tx_conf = {
+ .dst_addr = SSP_DR(pl022->phybase),
+ .direction = DMA_TO_DEVICE,
+ };
+ unsigned int pages;
+ int ret;
+ int sglen;
+ struct dma_chan *rxchan = pl022->dma_rx_channel;
+ struct dma_chan *txchan = pl022->dma_tx_channel;
+ struct dma_async_tx_descriptor *rxdesc;
+ struct dma_async_tx_descriptor *txdesc;
+ dma_cookie_t cookie;
+
+ /* DMA burstsize should be same as the FIFO trigger level */
+ rx_conf.src_maxburst = pl022->rx_lev_trig ? 1 <<
+ (pl022->rx_lev_trig + 1) : pl022->rx_lev_trig;
+ tx_conf.dst_maxburst = pl022->tx_lev_trig ? 1 <<
+ (pl022->tx_lev_trig + 1) : pl022->tx_lev_trig;
+
+ /* Check that the channels are available */
+ if (!rxchan || !txchan)
+ return -ENODEV;
+
+ switch (pl022->read) {
+ case READING_NULL:
+ /* Use the same as for writing */
+ rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+ break;
+ case READING_U8:
+ rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ break;
+ case READING_U16:
+ rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case READING_U32:
+ rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ }
+
+ switch (pl022->write) {
+ case WRITING_NULL:
+ /* Use the same as for reading */
+ tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+ break;
+ case WRITING_U8:
+ tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ break;
+ case WRITING_U16:
+ tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case WRITING_U32:
+ tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;;
+ break;
+ }
+
+ /* SPI pecularity: we need to read and write the same width */
+ if (rx_conf.src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ rx_conf.src_addr_width = tx_conf.dst_addr_width;
+ if (tx_conf.dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ tx_conf.dst_addr_width = rx_conf.src_addr_width;
+ BUG_ON(rx_conf.src_addr_width != tx_conf.dst_addr_width);
+
+ rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG,
+ (unsigned long) &rx_conf);
+ txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
+ (unsigned long) &tx_conf);
+
+ /* Create sglists for the transfers */
+ pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1;
+ dev_dbg(&pl022->adev->dev, "using %d pages for transfer\n", pages);
+
+ ret = sg_alloc_table(&pl022->sgt_rx, pages, GFP_KERNEL);
+ if (ret)
+ goto err_alloc_rx_sg;
+
+ ret = sg_alloc_table(&pl022->sgt_tx, pages, GFP_KERNEL);
+ if (ret)
+ goto err_alloc_tx_sg;
+
+ /* Fill in the scatterlists for the RX+TX buffers */
+ setup_dma_scatter(pl022, pl022->rx,
+ pl022->cur_transfer->len, &pl022->sgt_rx);
+ setup_dma_scatter(pl022, pl022->tx,
+ pl022->cur_transfer->len, &pl022->sgt_tx);
+
+ /* Map DMA buffers */
+ sglen = dma_map_sg(&pl022->adev->dev, pl022->sgt_rx.sgl,
+ pl022->sgt_rx.nents, DMA_FROM_DEVICE);
+ if (!sglen)
+ goto err_rx_sgmap;
+
+ sglen = dma_map_sg(&pl022->adev->dev, pl022->sgt_tx.sgl,
+ pl022->sgt_tx.nents, DMA_TO_DEVICE);
+ if (!sglen)
+ goto err_tx_sgmap;
+
+ /* Send both scatterlists */
+ rxdesc = rxchan->device->device_prep_slave_sg(rxchan,
+ pl022->sgt_rx.sgl,
+ pl022->sgt_rx.nents,
+ DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!rxdesc)
+ goto err_rxdesc;
+
+ txdesc = txchan->device->device_prep_slave_sg(txchan,
+ pl022->sgt_tx.sgl,
+ pl022->sgt_tx.nents,
+ DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!txdesc)
+ goto err_txdesc;
+
+ /* Put the callback on the RX transfer only, that should finish last */
+ rxdesc->callback = dma_callback;
+ rxdesc->callback_param = pl022;
+
+ /* Submit and fire RX and TX with TX last so we're ready to read! */
+ cookie = rxdesc->tx_submit(rxdesc);
+ if (dma_submit_error(cookie))
+ goto err_submit_rx;
+ cookie = txdesc->tx_submit(txdesc);
+ if (dma_submit_error(cookie))
+ goto err_submit_tx;
+ rxchan->device->device_issue_pending(rxchan);
+ txchan->device->device_issue_pending(txchan);
+
+ return 0;
+
+err_submit_tx:
+err_submit_rx:
+err_txdesc:
+ txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0);
+err_rxdesc:
+ rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0);
+ dma_unmap_sg(&pl022->adev->dev, pl022->sgt_tx.sgl,
+ pl022->sgt_tx.nents, DMA_TO_DEVICE);
+err_tx_sgmap:
+ dma_unmap_sg(&pl022->adev->dev, pl022->sgt_rx.sgl,
+ pl022->sgt_tx.nents, DMA_FROM_DEVICE);
+err_rx_sgmap:
+ sg_free_table(&pl022->sgt_tx);
+err_alloc_tx_sg:
+ sg_free_table(&pl022->sgt_rx);
+err_alloc_rx_sg:
+ return -ENOMEM;
+}
+
+static int __init pl022_dma_probe(struct pl022 *pl022)
+{
+ dma_cap_mask_t mask;
+
+ /* Try to acquire a generic DMA engine slave channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ /*
+ * We need both RX and TX channels to do DMA, else do none
+ * of them.
+ */
+ pl022->dma_rx_channel = dma_request_channel(mask,
+ pl022->master_info->dma_filter,
+ pl022->master_info->dma_rx_param);
+ if (!pl022->dma_rx_channel) {
+ dev_err(&pl022->adev->dev, "no RX DMA channel!\n");
+ goto err_no_rxchan;
+ }
+
+ pl022->dma_tx_channel = dma_request_channel(mask,
+ pl022->master_info->dma_filter,
+ pl022->master_info->dma_tx_param);
+ if (!pl022->dma_tx_channel) {
+ dev_err(&pl022->adev->dev, "no TX DMA channel!\n");
+ goto err_no_txchan;
+ }
+
+ pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!pl022->dummypage) {
+ dev_err(&pl022->adev->dev, "no DMA dummypage!\n");
+ goto err_no_dummypage;
+ }
+
+ dev_info(&pl022->adev->dev, "setup for DMA on RX %s, TX %s\n",
+ dma_chan_name(pl022->dma_rx_channel),
+ dma_chan_name(pl022->dma_tx_channel));
+
+ return 0;
+
+err_no_dummypage:
+ dma_release_channel(pl022->dma_tx_channel);
+err_no_txchan:
+ dma_release_channel(pl022->dma_rx_channel);
+ pl022->dma_rx_channel = NULL;
+err_no_rxchan:
+ return -ENODEV;
+}
+
+static void terminate_dma(struct pl022 *pl022)
+{
+ struct dma_chan *rxchan = pl022->dma_rx_channel;
+ struct dma_chan *txchan = pl022->dma_tx_channel;
+
+ rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0);
+ txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0);
+ unmap_free_dma_scatter(pl022);
+}
+
+static void pl022_dma_remove(struct pl022 *pl022)
+{
+ if (pl022->busy)
+ terminate_dma(pl022);
+ if (pl022->dma_tx_channel)
+ dma_release_channel(pl022->dma_tx_channel);
+ if (pl022->dma_rx_channel)
+ dma_release_channel(pl022->dma_rx_channel);
+ kfree(pl022->dummypage);
+}
+
+#else
+static inline int configure_dma(struct pl022 *pl022)
+{
+ return -ENODEV;
+}
+
+static inline int pl022_dma_probe(struct pl022 *pl022)
+{
+ return 0;
+}
+
+static inline void pl022_dma_remove(struct pl022 *pl022)
+{
+}
+#endif
+
/**
* pl022_interrupt_handler - Interrupt handler for SSP controller
*
@@ -793,14 +1194,17 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
if (unlikely(!irq_status))
return IRQ_NONE;
- /* This handles the error code interrupts */
+ /*
+ * This handles the FIFO interrupts, the timeout
+ * interrupts are flatly ignored, they cannot be
+ * trusted.
+ */
if (unlikely(irq_status & SSP_MIS_MASK_RORMIS)) {
/*
* Overrun interrupt - bail out since our Data has been
* corrupted
*/
- dev_err(&pl022->adev->dev,
- "FIFO overrun\n");
+ dev_err(&pl022->adev->dev, "FIFO overrun\n");
if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RFF)
dev_err(&pl022->adev->dev,
"RXFIFO is full\n");
@@ -895,8 +1299,8 @@ static int set_up_next_transfer(struct pl022 *pl022,
}
/**
- * pump_transfers - Tasklet function which schedules next interrupt transfer
- * when running in interrupt transfer mode.
+ * pump_transfers - Tasklet function which schedules next transfer
+ * when running in interrupt or DMA transfer mode.
* @data: SSP driver private data structure
*
*/
@@ -953,65 +1357,23 @@ static void pump_transfers(unsigned long data)
}
/* Flush the FIFOs and let's go! */
flush(pl022);
- writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
-}
-
-/**
- * NOT IMPLEMENTED
- * configure_dma - It configures the DMA pipes for DMA transfers
- * @data: SSP driver's private data structure
- *
- */
-static int configure_dma(void *data)
-{
- struct pl022 *pl022 = data;
- dev_dbg(&pl022->adev->dev, "configure DMA\n");
- return -ENOTSUPP;
-}
-/**
- * do_dma_transfer - It handles transfers of the current message
- * if it is DMA xfer.
- * NOT FULLY IMPLEMENTED
- * @data: SSP driver's private data structure
- */
-static void do_dma_transfer(void *data)
-{
- struct pl022 *pl022 = data;
-
- if (configure_dma(data)) {
- dev_dbg(&pl022->adev->dev, "configuration of DMA Failed!\n");
- goto err_config_dma;
- }
-
- /* TODO: Implememt DMA setup of pipes here */
-
- /* Enable target chip, set up transfer */
- pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
- if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
- /* Error path */
- pl022->cur_msg->state = STATE_ERROR;
- pl022->cur_msg->status = -EIO;
- giveback(pl022);
+ if (pl022->cur_chip->enable_dma) {
+ if (configure_dma(pl022)) {
+ dev_dbg(&pl022->adev->dev,
+ "configuration of DMA failed, fall back to interrupt mode\n");
+ goto err_config_dma;
+ }
return;
}
- /* Enable SSP */
- writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE),
- SSP_CR1(pl022->virtbase));
-
- /* TODO: Enable the DMA transfer here */
- return;
- err_config_dma:
- pl022->cur_msg->state = STATE_ERROR;
- pl022->cur_msg->status = -EIO;
- giveback(pl022);
- return;
+err_config_dma:
+ writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
}
-static void do_interrupt_transfer(void *data)
+static void do_interrupt_dma_transfer(struct pl022 *pl022)
{
- struct pl022 *pl022 = data;
+ u32 irqflags = ENABLE_ALL_INTERRUPTS;
/* Enable target chip */
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
@@ -1022,15 +1384,26 @@ static void do_interrupt_transfer(void *data)
giveback(pl022);
return;
}
+ /* If we're using DMA, set up DMA here */
+ if (pl022->cur_chip->enable_dma) {
+ /* Configure DMA transfer */
+ if (configure_dma(pl022)) {
+ dev_dbg(&pl022->adev->dev,
+ "configuration of DMA failed, fall back to interrupt mode\n");
+ goto err_config_dma;
+ }
+ /* Disable interrupts in DMA mode, IRQ from DMA controller */
+ irqflags = DISABLE_ALL_INTERRUPTS;
+ }
+err_config_dma:
/* Enable SSP, turn on interrupts */
writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE),
SSP_CR1(pl022->virtbase));
- writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
+ writew(irqflags, SSP_IMSC(pl022->virtbase));
}
-static void do_polling_transfer(void *data)
+static void do_polling_transfer(struct pl022 *pl022)
{
- struct pl022 *pl022 = data;
struct spi_message *message = NULL;
struct spi_transfer *transfer = NULL;
struct spi_transfer *previous = NULL;
@@ -1100,7 +1473,7 @@ static void do_polling_transfer(void *data)
*
* This function checks if there is any spi message in the queue that
* needs processing and delegate control to appropriate function
- * do_polling_transfer()/do_interrupt_transfer()/do_dma_transfer()
+ * do_polling_transfer()/do_interrupt_dma_transfer()
* based on the kind of the transfer
*
*/
@@ -1142,16 +1515,15 @@ static void pump_messages(struct work_struct *work)
* We enable the clock here, then the clock will be disabled when
* giveback() is called in each method (poll/interrupt/DMA)
*/
+ pm_runtime_get_sync(&pl022->adev->dev);
clk_enable(pl022->clk);
restore_state(pl022);
flush(pl022);
if (pl022->cur_chip->xfer_type == POLLING_TRANSFER)
do_polling_transfer(pl022);
- else if (pl022->cur_chip->xfer_type == INTERRUPT_TRANSFER)
- do_interrupt_transfer(pl022);
else
- do_dma_transfer(pl022);
+ do_interrupt_dma_transfer(pl022);
}
@@ -1246,100 +1618,56 @@ static int destroy_queue(struct pl022 *pl022)
}
static int verify_controller_parameters(struct pl022 *pl022,
- struct pl022_config_chip *chip_info)
+ struct pl022_config_chip const *chip_info)
{
- if ((chip_info->lbm != LOOPBACK_ENABLED)
- && (chip_info->lbm != LOOPBACK_DISABLED)) {
- dev_err(chip_info->dev,
- "loopback Mode is configured incorrectly\n");
- return -EINVAL;
- }
if ((chip_info->iface < SSP_INTERFACE_MOTOROLA_SPI)
|| (chip_info->iface > SSP_INTERFACE_UNIDIRECTIONAL)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"interface is configured incorrectly\n");
return -EINVAL;
}
if ((chip_info->iface == SSP_INTERFACE_UNIDIRECTIONAL) &&
(!pl022->vendor->unidir)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"unidirectional mode not supported in this "
"hardware version\n");
return -EINVAL;
}
if ((chip_info->hierarchy != SSP_MASTER)
&& (chip_info->hierarchy != SSP_SLAVE)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"hierarchy is configured incorrectly\n");
return -EINVAL;
}
- if (((chip_info->clk_freq).cpsdvsr < CPSDVR_MIN)
- || ((chip_info->clk_freq).cpsdvsr > CPSDVR_MAX)) {
- dev_err(chip_info->dev,
- "cpsdvsr is configured incorrectly\n");
- return -EINVAL;
- }
- if ((chip_info->endian_rx != SSP_RX_MSB)
- && (chip_info->endian_rx != SSP_RX_LSB)) {
- dev_err(chip_info->dev,
- "RX FIFO endianess is configured incorrectly\n");
- return -EINVAL;
- }
- if ((chip_info->endian_tx != SSP_TX_MSB)
- && (chip_info->endian_tx != SSP_TX_LSB)) {
- dev_err(chip_info->dev,
- "TX FIFO endianess is configured incorrectly\n");
- return -EINVAL;
- }
- if ((chip_info->data_size < SSP_DATA_BITS_4)
- || (chip_info->data_size > SSP_DATA_BITS_32)) {
- dev_err(chip_info->dev,
- "DATA Size is configured incorrectly\n");
- return -EINVAL;
- }
if ((chip_info->com_mode != INTERRUPT_TRANSFER)
&& (chip_info->com_mode != DMA_TRANSFER)
&& (chip_info->com_mode != POLLING_TRANSFER)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"Communication mode is configured incorrectly\n");
return -EINVAL;
}
if ((chip_info->rx_lev_trig < SSP_RX_1_OR_MORE_ELEM)
|| (chip_info->rx_lev_trig > SSP_RX_32_OR_MORE_ELEM)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"RX FIFO Trigger Level is configured incorrectly\n");
return -EINVAL;
}
if ((chip_info->tx_lev_trig < SSP_TX_1_OR_MORE_EMPTY_LOC)
|| (chip_info->tx_lev_trig > SSP_TX_32_OR_MORE_EMPTY_LOC)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"TX FIFO Trigger Level is configured incorrectly\n");
return -EINVAL;
}
- if (chip_info->iface == SSP_INTERFACE_MOTOROLA_SPI) {
- if ((chip_info->clk_phase != SSP_CLK_FIRST_EDGE)
- && (chip_info->clk_phase != SSP_CLK_SECOND_EDGE)) {
- dev_err(chip_info->dev,
- "Clock Phase is configured incorrectly\n");
- return -EINVAL;
- }
- if ((chip_info->clk_pol != SSP_CLK_POL_IDLE_LOW)
- && (chip_info->clk_pol != SSP_CLK_POL_IDLE_HIGH)) {
- dev_err(chip_info->dev,
- "Clock Polarity is configured incorrectly\n");
- return -EINVAL;
- }
- }
if (chip_info->iface == SSP_INTERFACE_NATIONAL_MICROWIRE) {
if ((chip_info->ctrl_len < SSP_BITS_4)
|| (chip_info->ctrl_len > SSP_BITS_32)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"CTRL LEN is configured incorrectly\n");
return -EINVAL;
}
if ((chip_info->wait_state != SSP_MWIRE_WAIT_ZERO)
&& (chip_info->wait_state != SSP_MWIRE_WAIT_ONE)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"Wait State is configured incorrectly\n");
return -EINVAL;
}
@@ -1349,23 +1677,18 @@ static int verify_controller_parameters(struct pl022 *pl022,
SSP_MICROWIRE_CHANNEL_FULL_DUPLEX)
&& (chip_info->duplex !=
SSP_MICROWIRE_CHANNEL_HALF_DUPLEX))
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"Microwire duplex mode is configured incorrectly\n");
return -EINVAL;
} else {
if (chip_info->duplex != SSP_MICROWIRE_CHANNEL_FULL_DUPLEX)
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"Microwire half duplex mode requested,"
" but this is only available in the"
" ST version of PL022\n");
return -EINVAL;
}
}
- if (chip_info->cs_control == NULL) {
- dev_warn(chip_info->dev,
- "Chip Select Function is NULL for this chip\n");
- chip_info->cs_control = null_cs_control;
- }
return 0;
}
@@ -1465,22 +1788,24 @@ static int calculate_effective_freq(struct pl022 *pl022,
return 0;
}
-/**
- * NOT IMPLEMENTED
- * process_dma_info - Processes the DMA info provided by client drivers
- * @chip_info: chip info provided by client device
- * @chip: Runtime state maintained by the SSP controller for each spi device
- *
- * This function processes and stores DMA config provided by client driver
- * into the runtime state maintained by the SSP controller driver
+
+/*
+ * A piece of default chip info unless the platform
+ * supplies it.
*/
-static int process_dma_info(struct pl022_config_chip *chip_info,
- struct chip_data *chip)
-{
- dev_err(chip_info->dev,
- "cannot process DMA info, DMA not implemented!\n");
- return -ENOTSUPP;
-}
+static const struct pl022_config_chip pl022_default_chip_info = {
+ .com_mode = POLLING_TRANSFER,
+ .iface = SSP_INTERFACE_MOTOROLA_SPI,
+ .hierarchy = SSP_SLAVE,
+ .slave_tx_disable = DO_NOT_DRIVE_TX,
+ .rx_lev_trig = SSP_RX_1_OR_MORE_ELEM,
+ .tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC,
+ .ctrl_len = SSP_BITS_8,
+ .wait_state = SSP_MWIRE_WAIT_ZERO,
+ .duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
+ .cs_control = null_cs_control,
+};
+
/**
* pl022_setup - setup function registered to SPI master framework
@@ -1494,23 +1819,15 @@ static int process_dma_info(struct pl022_config_chip *chip_info,
* controller hardware here, that is not done until the actual transfer
* commence.
*/
-
-/* FIXME: JUST GUESSING the spi->mode bits understood by this driver */
-#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
- | SPI_LSB_FIRST | SPI_LOOP)
-
static int pl022_setup(struct spi_device *spi)
{
- struct pl022_config_chip *chip_info;
+ struct pl022_config_chip const *chip_info;
struct chip_data *chip;
+ struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0};
int status = 0;
struct pl022 *pl022 = spi_master_get_devdata(spi->master);
-
- if (spi->mode & ~MODEBITS) {
- dev_dbg(&spi->dev, "unsupported mode bits %x\n",
- spi->mode & ~MODEBITS);
- return -EINVAL;
- }
+ unsigned int bits = spi->bits_per_word;
+ u32 tmp;
if (!spi->max_speed_hz)
return -EINVAL;
@@ -1533,48 +1850,13 @@ static int pl022_setup(struct spi_device *spi)
chip_info = spi->controller_data;
if (chip_info == NULL) {
+ chip_info = &pl022_default_chip_info;
/* spi_board_info.controller_data not is supplied */
dev_dbg(&spi->dev,
"using default controller_data settings\n");
-
- chip_info =
- kzalloc(sizeof(struct pl022_config_chip), GFP_KERNEL);
-
- if (!chip_info) {
- dev_err(&spi->dev,
- "cannot allocate controller data\n");
- status = -ENOMEM;
- goto err_first_setup;
- }
-
- dev_dbg(&spi->dev, "allocated memory for controller data\n");
-
- /* Pointer back to the SPI device */
- chip_info->dev = &spi->dev;
- /*
- * Set controller data default values:
- * Polling is supported by default
- */
- chip_info->lbm = LOOPBACK_DISABLED;
- chip_info->com_mode = POLLING_TRANSFER;
- chip_info->iface = SSP_INTERFACE_MOTOROLA_SPI;
- chip_info->hierarchy = SSP_SLAVE;
- chip_info->slave_tx_disable = DO_NOT_DRIVE_TX;
- chip_info->endian_tx = SSP_TX_LSB;
- chip_info->endian_rx = SSP_RX_LSB;
- chip_info->data_size = SSP_DATA_BITS_12;
- chip_info->rx_lev_trig = SSP_RX_1_OR_MORE_ELEM;
- chip_info->tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC;
- chip_info->clk_phase = SSP_CLK_SECOND_EDGE;
- chip_info->clk_pol = SSP_CLK_POL_IDLE_LOW;
- chip_info->ctrl_len = SSP_BITS_8;
- chip_info->wait_state = SSP_MWIRE_WAIT_ZERO;
- chip_info->duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX;
- chip_info->cs_control = null_cs_control;
- } else {
+ } else
dev_dbg(&spi->dev,
"using user supplied controller_data settings\n");
- }
/*
* We can override with custom divisors, else we use the board
@@ -1584,29 +1866,51 @@ static int pl022_setup(struct spi_device *spi)
&& (0 == chip_info->clk_freq.scr)) {
status = calculate_effective_freq(pl022,
spi->max_speed_hz,
- &chip_info->clk_freq);
+ &clk_freq);
if (status < 0)
goto err_config_params;
} else {
- if ((chip_info->clk_freq.cpsdvsr % 2) != 0)
- chip_info->clk_freq.cpsdvsr =
- chip_info->clk_freq.cpsdvsr - 1;
+ memcpy(&clk_freq, &chip_info->clk_freq, sizeof(clk_freq));
+ if ((clk_freq.cpsdvsr % 2) != 0)
+ clk_freq.cpsdvsr =
+ clk_freq.cpsdvsr - 1;
}
+ if ((clk_freq.cpsdvsr < CPSDVR_MIN)
+ || (clk_freq.cpsdvsr > CPSDVR_MAX)) {
+ dev_err(&spi->dev,
+ "cpsdvsr is configured incorrectly\n");
+ goto err_config_params;
+ }
+
+
status = verify_controller_parameters(pl022, chip_info);
if (status) {
dev_err(&spi->dev, "controller data is incorrect");
goto err_config_params;
}
+
+ pl022->rx_lev_trig = chip_info->rx_lev_trig;
+ pl022->tx_lev_trig = chip_info->tx_lev_trig;
+
/* Now set controller state based on controller data */
chip->xfer_type = chip_info->com_mode;
- chip->cs_control = chip_info->cs_control;
-
- if (chip_info->data_size <= 8) {
- dev_dbg(&spi->dev, "1 <= n <=8 bits per word\n");
+ if (!chip_info->cs_control) {
+ chip->cs_control = null_cs_control;
+ dev_warn(&spi->dev,
+ "chip select function is NULL for this chip\n");
+ } else
+ chip->cs_control = chip_info->cs_control;
+
+ if (bits <= 3) {
+ /* PL022 doesn't support less than 4-bits */
+ status = -ENOTSUPP;
+ goto err_config_params;
+ } else if (bits <= 8) {
+ dev_dbg(&spi->dev, "4 <= n <=8 bits per word\n");
chip->n_bytes = 1;
chip->read = READING_U8;
chip->write = WRITING_U8;
- } else if (chip_info->data_size <= 16) {
+ } else if (bits <= 16) {
dev_dbg(&spi->dev, "9 <= n <= 16 bits per word\n");
chip->n_bytes = 2;
chip->read = READING_U16;
@@ -1623,6 +1927,7 @@ static int pl022_setup(struct spi_device *spi)
dev_err(&spi->dev,
"a standard pl022 can only handle "
"1 <= n <= 16 bit words\n");
+ status = -ENOTSUPP;
goto err_config_params;
}
}
@@ -1634,17 +1939,14 @@ static int pl022_setup(struct spi_device *spi)
chip->cpsr = 0;
if ((chip_info->com_mode == DMA_TRANSFER)
&& ((pl022->master_info)->enable_dma)) {
- chip->enable_dma = 1;
+ chip->enable_dma = true;
dev_dbg(&spi->dev, "DMA mode set in controller state\n");
- status = process_dma_info(chip_info, chip);
- if (status < 0)
- goto err_config_params;
SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
SSP_DMACR_MASK_RXDMAE, 0);
SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
SSP_DMACR_MASK_TXDMAE, 1);
} else {
- chip->enable_dma = 0;
+ chip->enable_dma = false;
dev_dbg(&spi->dev, "DMA mode NOT set in controller state\n");
SSP_WRITE_BITS(chip->dmacr, SSP_DMA_DISABLED,
SSP_DMACR_MASK_RXDMAE, 0);
@@ -1652,10 +1954,12 @@ static int pl022_setup(struct spi_device *spi)
SSP_DMACR_MASK_TXDMAE, 1);
}
- chip->cpsr = chip_info->clk_freq.cpsdvsr;
+ chip->cpsr = clk_freq.cpsdvsr;
/* Special setup for the ST micro extended control registers */
if (pl022->vendor->extended_cr) {
+ u32 etx;
+
if (pl022->vendor->pl023) {
/* These bits are only in the PL023 */
SSP_WRITE_BITS(chip->cr1, chip_info->clkdelay,
@@ -1671,29 +1975,51 @@ static int pl022_setup(struct spi_device *spi)
SSP_WRITE_BITS(chip->cr1, chip_info->wait_state,
SSP_CR1_MASK_MWAIT_ST, 6);
}
- SSP_WRITE_BITS(chip->cr0, chip_info->data_size,
+ SSP_WRITE_BITS(chip->cr0, bits - 1,
SSP_CR0_MASK_DSS_ST, 0);
- SSP_WRITE_BITS(chip->cr1, chip_info->endian_rx,
- SSP_CR1_MASK_RENDN_ST, 4);
- SSP_WRITE_BITS(chip->cr1, chip_info->endian_tx,
- SSP_CR1_MASK_TENDN_ST, 5);
+
+ if (spi->mode & SPI_LSB_FIRST) {
+ tmp = SSP_RX_LSB;
+ etx = SSP_TX_LSB;
+ } else {
+ tmp = SSP_RX_MSB;
+ etx = SSP_TX_MSB;
+ }
+ SSP_WRITE_BITS(chip->cr1, tmp, SSP_CR1_MASK_RENDN_ST, 4);
+ SSP_WRITE_BITS(chip->cr1, etx, SSP_CR1_MASK_TENDN_ST, 5);
SSP_WRITE_BITS(chip->cr1, chip_info->rx_lev_trig,
SSP_CR1_MASK_RXIFLSEL_ST, 7);
SSP_WRITE_BITS(chip->cr1, chip_info->tx_lev_trig,
SSP_CR1_MASK_TXIFLSEL_ST, 10);
} else {
- SSP_WRITE_BITS(chip->cr0, chip_info->data_size,
+ SSP_WRITE_BITS(chip->cr0, bits - 1,
SSP_CR0_MASK_DSS, 0);
SSP_WRITE_BITS(chip->cr0, chip_info->iface,
SSP_CR0_MASK_FRF, 4);
}
+
/* Stuff that is common for all versions */
- SSP_WRITE_BITS(chip->cr0, chip_info->clk_pol, SSP_CR0_MASK_SPO, 6);
- SSP_WRITE_BITS(chip->cr0, chip_info->clk_phase, SSP_CR0_MASK_SPH, 7);
- SSP_WRITE_BITS(chip->cr0, chip_info->clk_freq.scr, SSP_CR0_MASK_SCR, 8);
+ if (spi->mode & SPI_CPOL)
+ tmp = SSP_CLK_POL_IDLE_HIGH;
+ else
+ tmp = SSP_CLK_POL_IDLE_LOW;
+ SSP_WRITE_BITS(chip->cr0, tmp, SSP_CR0_MASK_SPO, 6);
+
+ if (spi->mode & SPI_CPHA)
+ tmp = SSP_CLK_SECOND_EDGE;
+ else
+ tmp = SSP_CLK_FIRST_EDGE;
+ SSP_WRITE_BITS(chip->cr0, tmp, SSP_CR0_MASK_SPH, 7);
+
+ SSP_WRITE_BITS(chip->cr0, clk_freq.scr, SSP_CR0_MASK_SCR, 8);
/* Loopback is available on all versions except PL023 */
- if (!pl022->vendor->pl023)
- SSP_WRITE_BITS(chip->cr1, chip_info->lbm, SSP_CR1_MASK_LBM, 0);
+ if (pl022->vendor->loopback) {
+ if (spi->mode & SPI_LOOP)
+ tmp = LOOPBACK_ENABLED;
+ else
+ tmp = LOOPBACK_DISABLED;
+ SSP_WRITE_BITS(chip->cr1, tmp, SSP_CR1_MASK_LBM, 0);
+ }
SSP_WRITE_BITS(chip->cr1, SSP_DISABLED, SSP_CR1_MASK_SSE, 1);
SSP_WRITE_BITS(chip->cr1, chip_info->hierarchy, SSP_CR1_MASK_MS, 2);
SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD, 3);
@@ -1702,7 +2028,7 @@ static int pl022_setup(struct spi_device *spi)
spi_set_ctldata(spi, chip);
return status;
err_config_params:
- err_first_setup:
+ spi_set_ctldata(spi, NULL);
kfree(chip);
return status;
}
@@ -1723,7 +2049,7 @@ static void pl022_cleanup(struct spi_device *spi)
}
-static int __init
+static int __devinit
pl022_probe(struct amba_device *adev, struct amba_id *id)
{
struct device *dev = &adev->dev;
@@ -1764,12 +2090,21 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
master->setup = pl022_setup;
master->transfer = pl022_transfer;
+ /*
+ * Supports mode 0-3, loopback, and active low CS. Transfers are
+ * always MS bit first on the original pl022.
+ */
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
+ if (pl022->vendor->extended_cr)
+ master->mode_bits |= SPI_LSB_FIRST;
+
dev_dbg(&adev->dev, "BUSNO: %d\n", master->bus_num);
status = amba_request_regions(adev, NULL);
if (status)
goto err_no_ioregion;
+ pl022->phybase = adev->res.start;
pl022->virtbase = ioremap(adev->res.start, resource_size(&adev->res));
if (pl022->virtbase == NULL) {
status = -ENOMEM;
@@ -1778,6 +2113,9 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
adev->res.start, pl022->virtbase);
+ pm_runtime_enable(dev);
+ pm_runtime_resume(dev);
+
pl022->clk = clk_get(&adev->dev, NULL);
if (IS_ERR(pl022->clk)) {
status = PTR_ERR(pl022->clk);
@@ -1798,6 +2136,14 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
goto err_no_irq;
}
+
+ /* Get DMA channels */
+ if (platform_info->enable_dma) {
+ status = pl022_dma_probe(pl022);
+ if (status != 0)
+ goto err_no_dma;
+ }
+
/* Initialize and start queue */
status = init_queue(pl022);
if (status != 0) {
@@ -1824,6 +2170,8 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
err_start_queue:
err_init_queue:
destroy_queue(pl022);
+ pl022_dma_remove(pl022);
+ err_no_dma:
free_irq(adev->irq[0], pl022);
err_no_irq:
clk_put(pl022->clk);
@@ -1838,7 +2186,7 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
return status;
}
-static int __exit
+static int __devexit
pl022_remove(struct amba_device *adev)
{
struct pl022 *pl022 = amba_get_drvdata(adev);
@@ -1854,7 +2202,9 @@ pl022_remove(struct amba_device *adev)
return status;
}
load_ssp_default_config(pl022);
+ pl022_dma_remove(pl022);
free_irq(adev->irq[0], pl022);
+ pm_runtime_disable(&adev->dev);
clk_disable(pl022->clk);
clk_put(pl022->clk);
iounmap(pl022->virtbase);
@@ -1879,9 +2229,6 @@ static int pl022_suspend(struct amba_device *adev, pm_message_t state)
return status;
}
- clk_enable(pl022->clk);
- load_ssp_default_config(pl022);
- clk_disable(pl022->clk);
dev_dbg(&adev->dev, "suspended\n");
return 0;
}
@@ -1911,6 +2258,7 @@ static struct vendor_data vendor_arm = {
.unidir = false,
.extended_cr = false,
.pl023 = false,
+ .loopback = true,
};
@@ -1920,6 +2268,7 @@ static struct vendor_data vendor_st = {
.unidir = false,
.extended_cr = true,
.pl023 = false,
+ .loopback = true,
};
static struct vendor_data vendor_st_pl023 = {
@@ -1928,6 +2277,16 @@ static struct vendor_data vendor_st_pl023 = {
.unidir = false,
.extended_cr = true,
.pl023 = true,
+ .loopback = false,
+};
+
+static struct vendor_data vendor_db5500_pl023 = {
+ .fifodepth = 32,
+ .max_bpw = 32,
+ .unidir = false,
+ .extended_cr = true,
+ .pl023 = true,
+ .loopback = true,
};
static struct amba_id pl022_ids[] = {
@@ -1961,6 +2320,12 @@ static struct amba_id pl022_ids[] = {
.mask = 0xffffffff,
.data = &vendor_st_pl023,
},
+ {
+ .id = 0x00080023,
+ .mask = 0xffffffff,
+ .data = &vendor_db5500_pl023,
+ .name = "db5500-spi",
+ },
{ 0, 0 },
};
@@ -1970,7 +2335,7 @@ static struct amba_driver pl022_driver = {
},
.id_table = pl022_ids,
.probe = pl022_probe,
- .remove = __exit_p(pl022_remove),
+ .remove = __devexit_p(pl022_remove),
.suspend = pl022_suspend,
.resume = pl022_resume,
};
@@ -1981,7 +2346,7 @@ static int __init pl022_init(void)
return amba_driver_register(&pl022_driver);
}
-module_init(pl022_init);
+subsys_initcall(pl022_init);
static void __exit pl022_exit(void)
{
diff --git a/drivers/spi/stm_msp.c b/drivers/spi/stm_msp.c
new file mode 100644
index 00000000000..6b9d7bb75e5
--- /dev/null
+++ b/drivers/spi/stm_msp.c
@@ -0,0 +1,1929 @@
+/*
+ * drivers/spi/stm_msp.c
+ *
+ * Copyright (C) 2010 STMicroelectronics Pvt. Ltd.
+ *
+ * Author: Sachin Verma <sachin.verma@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/amba/bus.h>
+#include <linux/spi/stm_msp.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+
+/**
+ * MSP Controller Register Offsets
+ */
+#define MSP_DR(r) (r + 0x000)
+#define MSP_GCR(r) (r + 0x004)
+#define MSP_TCF(r) (r + 0x008)
+#define MSP_RCF(r) (r + 0x00C)
+#define MSP_SRG(r) (r + 0x010)
+#define MSP_FLR(r) (r + 0x014)
+#define MSP_DMACR(r) (r + 0x018)
+#define MSP_IMSC(r) (r + 0x020)
+#define MSP_RIS(r) (r + 0x024)
+#define MSP_MIS(r) (r + 0x028)
+#define MSP_ICR(r) (r + 0x02C)
+#define MSP_MCR(r) (r + 0x030)
+#define MSP_RCV(r) (r + 0x034)
+#define MSP_RCM(r) (r + 0x038)
+#define MSP_TCE0(r) (r + 0x040)
+#define MSP_TCE1(r) (r + 0x044)
+#define MSP_TCE2(r) (r + 0x048)
+#define MSP_TCE3(r) (r + 0x04C)
+#define MSP_RCE0(r) (r + 0x060)
+#define MSP_RCE1(r) (r + 0x064)
+#define MSP_RCE2(r) (r + 0x068)
+#define MSP_RCE3(r) (r + 0x06C)
+#define MSP_PID0(r) (r + 0xFE0)
+#define MSP_PID1(r) (r + 0xFE4)
+#define MSP_PID2(r) (r + 0xFE8)
+#define MSP_PID3(r) (r + 0xFEC)
+
+/**
+ * MSP Global Configuration Register - MSP_GCR
+ */
+#define MSP_GCR_MASK_RXEN ((u32)(0x1UL << 0))
+#define MSP_GCR_MASK_RFFEN ((u32)(0x1UL << 1))
+#define MSP_GCR_MASK_RFSPOL ((u32)(0x1UL << 2))
+#define MSP_GCR_MASK_DCM ((u32)(0x1UL << 3))
+#define MSP_GCR_MASK_RFSSEL ((u32)(0x1UL << 4))
+#define MSP_GCR_MASK_RCKPOL ((u32)(0x1UL << 5))
+#define MSP_GCR_MASK_RCKSEL ((u32)(0x1UL << 6))
+#define MSP_GCR_MASK_LBM ((u32)(0x1UL << 7))
+#define MSP_GCR_MASK_TXEN ((u32)(0x1UL << 8))
+#define MSP_GCR_MASK_TFFEN ((u32)(0x1UL << 9))
+#define MSP_GCR_MASK_TFSPOL ((u32)(0x1UL << 10))
+#define MSP_GCR_MASK_TFSSEL ((u32)(0x3UL << 11))
+#define MSP_GCR_MASK_TCKPOL ((u32)(0x1UL << 13))
+#define MSP_GCR_MASK_TCKSEL ((u32)(0x1UL << 14))
+#define MSP_GCR_MASK_TXDDL ((u32)(0x1UL << 15))
+#define MSP_GCR_MASK_SGEN ((u32)(0x1UL << 16))
+#define MSP_GCR_MASK_SCKPOL ((u32)(0x1UL << 17))
+#define MSP_GCR_MASK_SCKSEL ((u32)(0x3UL << 18))
+#define MSP_GCR_MASK_FGEN ((u32)(0x1UL << 20))
+#define MSP_GCR_MASK_SPICKM ((u32)(0x3UL << 21))
+#define MSP_GCR_MASK_SPIBME ((u32)(0x1UL << 23))
+
+/**
+ * MSP Transmit Configuration Register - MSP_TCF
+ */
+#define MSP_TCF_MASK_TP1ELEN ((u32)(0x7UL << 0))
+#define MSP_TCF_MASK_TP1FLEN ((u32)(0x7FUL << 3))
+#define MSP_TCF_MASK_TDTYP ((u32)(0x3UL << 10))
+#define MSP_TCF_MASK_TENDN ((u32)(0x1UL << 12))
+#define MSP_TCF_MASK_TDDLY ((u32)(0x3UL << 13))
+#define MSP_TCF_MASK_TFSIG ((u32)(0x1UL << 15))
+#define MSP_TCF_MASK_TP2ELEN ((u32)(0x7UL << 16))
+#define MSP_TCF_MASK_TP2FLEN ((u32)(0x7FUL << 19))
+#define MSP_TCF_MASK_TP2SM ((u32)(0x1UL << 26))
+#define MSP_TCF_MASK_TP2EN ((u32)(0x1UL << 27))
+#define MSP_TCF_MASK_TBSWAP ((u32)(0x3UL << 28))
+
+/**
+ * MSP Receive Configuration Register - MSP_RCF
+ */
+#define MSP_RCF_MASK_RP1ELEN ((u32)(0x7UL << 0))
+#define MSP_RCF_MASK_RP1FLEN ((u32)(0x7FUL << 3))
+#define MSP_RCF_MASK_RDTYP ((u32)(0x3UL << 10))
+#define MSP_RCF_MASK_RENDN ((u32)(0x1UL << 12))
+#define MSP_RCF_MASK_RDDLY ((u32)(0x3UL << 13))
+#define MSP_RCF_MASK_RFSIG ((u32)(0x1UL << 15))
+#define MSP_RCF_MASK_RP2ELEN ((u32)(0x7UL << 16))
+#define MSP_RCF_MASK_RP2FLEN ((u32)(0x7FUL << 19))
+#define MSP_RCF_MASK_RP2SM ((u32)(0x1UL << 26))
+#define MSP_RCF_MASK_RP2EN ((u32)(0x1UL << 27))
+#define MSP_RCF_MASK_RBSWAP ((u32)(0x3UL << 28))
+
+/**
+ * MSP Sample Rate Generator Register - MSP_SRG
+ */
+#define MSP_SRG_MASK_SCKDIV ((u32)(0x3FFUL << 0))
+#define MSP_SRG_MASK_FRWID ((u32)(0x3FUL << 10))
+#define MSP_SRG_MASK_FRPER ((u32)(0x1FFFUL << 16))
+
+/**
+ * MSP Flag Register - MSP_FLR
+ */
+#define MSP_FLR_MASK_RBUSY ((u32)(0x1UL << 0))
+#define MSP_FLR_MASK_RFE ((u32)(0x1UL << 1))
+#define MSP_FLR_MASK_RFU ((u32)(0x1UL << 2))
+#define MSP_FLR_MASK_TBUSY ((u32)(0x1UL << 3))
+#define MSP_FLR_MASK_TFE ((u32)(0x1UL << 4))
+#define MSP_FLR_MASK_TFU ((u32)(0x1UL << 5))
+
+/**
+ * MSP DMA Control Register - MSP_DMACR
+ */
+#define MSP_DMACR_MASK_RDMAE ((u32)(0x1UL << 0))
+#define MSP_DMACR_MASK_TDMAE ((u32)(0x1UL << 1))
+
+/**
+ * MSP Interrupt Mask Set/Clear Register - MSP_IMSC
+ */
+#define MSP_IMSC_MASK_RXIM ((u32)(0x1UL << 0))
+#define MSP_IMSC_MASK_ROEIM ((u32)(0x1UL << 1))
+#define MSP_IMSC_MASK_RSEIM ((u32)(0x1UL << 2))
+#define MSP_IMSC_MASK_RFSIM ((u32)(0x1UL << 3))
+#define MSP_IMSC_MASK_TXIM ((u32)(0x1UL << 4))
+#define MSP_IMSC_MASK_TUEIM ((u32)(0x1UL << 5))
+#define MSP_IMSC_MASK_TSEIM ((u32)(0x1UL << 6))
+#define MSP_IMSC_MASK_TFSIM ((u32)(0x1UL << 7))
+#define MSP_IMSC_MASK_RFOIM ((u32)(0x1UL << 8))
+#define MSP_IMSC_MASK_TFOIM ((u32)(0x1UL << 9))
+
+/**
+ * MSP Raw Interrupt status Register - MSP_RIS
+ */
+#define MSP_RIS_MASK_RXRIS ((u32)(0x1UL << 0))
+#define MSP_RIS_MASK_ROERIS ((u32)(0x1UL << 1))
+#define MSP_RIS_MASK_RSERIS ((u32)(0x1UL << 2))
+#define MSP_RIS_MASK_RFSRIS ((u32)(0x1UL << 3))
+#define MSP_RIS_MASK_TXRIS ((u32)(0x1UL << 4))
+#define MSP_RIS_MASK_TUERIS ((u32)(0x1UL << 5))
+#define MSP_RIS_MASK_TSERIS ((u32)(0x1UL << 6))
+#define MSP_RIS_MASK_TFSRIS ((u32)(0x1UL << 7))
+#define MSP_RIS_MASK_RFORIS ((u32)(0x1UL << 8))
+#define MSP_RIS_MASK_TFORIS ((u32)(0x1UL << 9))
+
+/**
+ * MSP Masked Interrupt status Register - MSP_MIS
+ */
+#define MSP_MIS_MASK_RXMIS ((u32)(0x1UL << 0))
+#define MSP_MIS_MASK_ROEMIS ((u32)(0x1UL << 1))
+#define MSP_MIS_MASK_RSEMIS ((u32)(0x1UL << 2))
+#define MSP_MIS_MASK_RFSMIS ((u32)(0x1UL << 3))
+#define MSP_MIS_MASK_TXMIS ((u32)(0x1UL << 4))
+#define MSP_MIS_MASK_TUEMIS ((u32)(0x1UL << 5))
+#define MSP_MIS_MASK_TSEMIS ((u32)(0x1UL << 6))
+#define MSP_MIS_MASK_TFSMIS ((u32)(0x1UL << 7))
+#define MSP_MIS_MASK_RFOMIS ((u32)(0x1UL << 8))
+#define MSP_MIS_MASK_TFOMIS ((u32)(0x1UL << 9))
+
+/**
+ * MSP Interrupt Clear Register - MSP_ICR
+ */
+#define MSP_ICR_MASK_ROEIC ((u32)(0x1UL << 1))
+#define MSP_ICR_MASK_RSEIC ((u32)(0x1UL << 2))
+#define MSP_ICR_MASK_RFSIC ((u32)(0x1UL << 3))
+#define MSP_ICR_MASK_TUEIC ((u32)(0x1UL << 5))
+#define MSP_ICR_MASK_TSEIC ((u32)(0x1UL << 6))
+#define MSP_ICR_MASK_TFSIC ((u32)(0x1UL << 7))
+
+#define GEN_MASK_BITS(val, mask, sb) ((u32)((((u32)val) << (sb)) & (mask)))
+#define MSP_WBITS(reg, val, mask, sb) ((reg) = (((reg) & ~(mask)) |\
+ (((val) << (sb)) & (mask))))
+#define DEFAULT_MSP_REG_DMACR 0x00000000
+#define DEFAULT_MSP_REG_SRG 0x1FFF0000
+
+#define DEFAULT_MSP_REG_GCR ( \
+ GEN_MASK_BITS(MSP_RECEIVER_DISABLED, MSP_GCR_MASK_RXEN, 0) |\
+ GEN_MASK_BITS(MSP_RX_FIFO_ENABLED, MSP_GCR_MASK_RFFEN, 1) |\
+ GEN_MASK_BITS(MSP_LOOPBACK_DISABLED, MSP_GCR_MASK_LBM, 7) |\
+ GEN_MASK_BITS(MSP_TRANSMITTER_DISABLED, MSP_GCR_MASK_TXEN, 8) |\
+ GEN_MASK_BITS(MSP_TX_FIFO_ENABLED, MSP_GCR_MASK_TFFEN, 9) |\
+ GEN_MASK_BITS(MSP_TX_FRAME_SYNC_POL_LOW, MSP_GCR_MASK_TFSPOL, 10)|\
+ GEN_MASK_BITS(MSP_TX_FRAME_SYNC_INT, MSP_GCR_MASK_TFSSEL, 11) |\
+ GEN_MASK_BITS(MSP_TX_CLOCK_POL_HIGH, MSP_GCR_MASK_TCKPOL, 13) |\
+ GEN_MASK_BITS(MSP_IS_SPI_MASTER, MSP_GCR_MASK_TCKSEL, 14) |\
+ GEN_MASK_BITS(MSP_TRANSMIT_DATA_WITHOUT_DELAY, MSP_GCR_MASK_TXDDL, 15)|\
+ GEN_MASK_BITS(MSP_SAMPLE_RATE_GEN_ENABLE, MSP_GCR_MASK_SGEN, 16)|\
+ GEN_MASK_BITS(MSP_CLOCK_INTERNAL, MSP_GCR_MASK_SCKSEL, 18) |\
+ GEN_MASK_BITS(MSP_FRAME_GEN_ENABLE, MSP_GCR_MASK_FGEN, 20) |\
+ GEN_MASK_BITS(MSP_SPI_PHASE_ZERO_CYCLE_DELAY, MSP_GCR_MASK_SPICKM, 21)|\
+ GEN_MASK_BITS(SPI_BURST_MODE_DISABLE, MSP_GCR_MASK_SPIBME, 23)\
+ )
+#define DEFAULT_MSP_REG_RCF ( \
+ GEN_MASK_BITS(MSP_DATA_BITS_32, MSP_RCF_MASK_RP1ELEN, 0) | \
+ GEN_MASK_BITS(MSP_IGNORE_RX_FRAME_SYNC_PULSE, MSP_RCF_MASK_RFSIG, 15) |\
+ GEN_MASK_BITS(MSP_RX_1BIT_DATA_DELAY, MSP_RCF_MASK_RDDLY, 13) | \
+ GEN_MASK_BITS(MSP_RX_ENDIANESS_LSB, MSP_RCF_MASK_RENDN, 12) \
+ )
+
+#define DEFAULT_MSP_REG_TCF ( \
+ GEN_MASK_BITS(MSP_DATA_BITS_32, MSP_TCF_MASK_TP1ELEN, 0) | \
+ GEN_MASK_BITS(MSP_IGNORE_TX_FRAME_SYNC_PULSE, MSP_TCF_MASK_TFSIG, 15) |\
+ GEN_MASK_BITS(MSP_TX_1BIT_DATA_DELAY, MSP_TCF_MASK_TDDLY, 13) | \
+ GEN_MASK_BITS(MSP_TX_ENDIANESS_LSB, MSP_TCF_MASK_TENDN, 12) \
+ )
+
+/**
+ * MSP Receiver/Transmitter states (enabled or disabled)
+ */
+#define MSP_RECEIVER_DISABLED 0
+#define MSP_RECEIVER_ENABLED 1
+#define MSP_TRANSMITTER_DISABLED 0
+#define MSP_TRANSMITTER_ENABLED 1
+
+/**
+ * MSP Receiver/Transmitter FIFO constants
+ */
+#define MSP_LOOPBACK_DISABLED 0
+#define MSP_LOOPBACK_ENABLED 1
+
+#define MSP_TX_FIFO_DISABLED 0
+#define MSP_TX_FIFO_ENABLED 1
+#define MSP_TX_ENDIANESS_MSB 0
+#define MSP_TX_ENDIANESS_LSB 1
+
+#define MSP_RX_FIFO_DISABLED 0
+#define MSP_RX_FIFO_ENABLED 1
+#define MSP_RX_ENDIANESS_MSB 0
+#define MSP_RX_ENDIANESS_LSB 1
+
+#define MSP_TX_FRAME_SYNC_EXT 0x0
+#define MSP_TX_FRAME_SYNC_INT 0x2
+#define MSP_TX_FRAME_SYNC_INT_CFG 0x3
+
+#define MSP_TX_FRAME_SYNC_POL_HIGH 0
+#define MSP_TX_FRAME_SYNC_POL_LOW 1
+
+#define MSP_HANDLE_RX_FRAME_SYNC_PULSE 0
+#define MSP_IGNORE_RX_FRAME_SYNC_PULSE 1
+
+#define MSP_RX_NO_DATA_DELAY 0x0
+#define MSP_RX_1BIT_DATA_DELAY 0x1
+#define MSP_RX_2BIT_DATA_DELAY 0x2
+#define MSP_RX_3BIT_DATA_DELAY 0x3
+
+#define MSP_HANDLE_TX_FRAME_SYNC_PULSE 0
+#define MSP_IGNORE_TX_FRAME_SYNC_PULSE 1
+
+#define MSP_TX_NO_DATA_DELAY 0x0
+#define MSP_TX_1BIT_DATA_DELAY 0x1
+#define MSP_TX_2BIT_DATA_DELAY 0x2
+#define MSP_TX_3BIT_DATA_DELAY 0x3
+
+#define MSP_TX_CLOCK_POL_LOW 0
+#define MSP_TX_CLOCK_POL_HIGH 1
+
+#define MSP_SPI_PHASE_ZERO_CYCLE_DELAY 0x2
+#define MSP_SPI_PHASE_HALF_CYCLE_DELAY 0x3
+
+#define MSP_IS_SPI_SLAVE 0
+#define MSP_IS_SPI_MASTER 1
+
+#define MSP_FRAME_GEN_DISABLE 0
+#define MSP_FRAME_GEN_ENABLE 1
+
+#define MSP_SAMPLE_RATE_GEN_DISABLE 0
+#define MSP_SAMPLE_RATE_GEN_ENABLE 1
+
+#define SPI_BURST_MODE_DISABLE 0
+#define SPI_BURST_MODE_ENABLE 1
+
+#define MSP_TRANSMIT_DATA_WITHOUT_DELAY 0
+#define MSP_TRANSMIT_DATA_WITH_DELAY 1
+
+#define MSP_CLOCK_INTERNAL 0x0 /* 48 MHz */
+
+/* SRG is derived from MSPSCK pin but is resynchronized on MSPRFS
+ * (Receive Frame Sync signal) */
+#define MSP_CLOCK_EXTERNAL 0x2
+#define MSP_CLOCK_EXTERNAL_RESYNC 0x3
+
+#define DISABLE_ALL_MSP_INTERRUPTS (0x0)
+#define ENABLE_ALL_MSP_INTERRUPTS (0x333)
+#define CLEAR_ALL_MSP_INTERRUPTS (0xEE)
+#define DEFAULT_MSP_CLK (48000000)
+#define MAX_SCKDIV (1023)
+
+#define MSP_FIFO_DEPTH 8
+
+/**
+ * Queue State
+ */
+#define QUEUE_RUNNING (0)
+#define QUEUE_STOPPED (1)
+
+#define START_STATE ((void *)0)
+#define RUNNING_STATE ((void *)1)
+#define DONE_STATE ((void *)2)
+#define ERROR_STATE ((void *)-1)
+
+/* Default values */
+#define SPI_DEFAULT_MAX_SPEED_HZ 48000
+#define SPI_TRANSFER_TIMEOUT_MS 5000
+
+/* CONTROLLER COMMANDS */
+enum cntlr_commands {
+ DISABLE_CONTROLLER = 0,
+ ENABLE_CONTROLLER ,
+ DISABLE_ALL_INTERRUPT ,
+ ENABLE_ALL_INTERRUPT ,
+ FLUSH_FIFO ,
+ RESTORE_STATE ,
+ LOAD_DEFAULT_CONFIG ,
+ CLEAR_ALL_INTERRUPT,
+};
+
+struct stm_msp {
+ struct amba_device *adev;
+ struct spi_master *master;
+ struct stm_msp_controller *master_info;
+ void __iomem *regs;
+ struct clk *clk;
+#ifdef CONFIG_SPI_WORKQUEUE
+ struct workqueue_struct *workqueue;
+#endif
+ struct work_struct spi_work;
+ spinlock_t lock;
+ struct list_head queue;
+ int busy;
+ int run;
+ struct tasklet_struct pump_transfers;
+ struct timer_list spi_notify_timer;
+ int spi_io_error;
+ struct spi_message *cur_msg;
+ struct spi_transfer *cur_transfer;
+ struct chip_data *cur_chip;
+ void *tx;
+ void *tx_end;
+ void *rx;
+ void *rx_end;
+ void (*write)(struct stm_msp *stm_msp);
+ void (*read)(struct stm_msp *stm_msp);
+ void (*delay)(struct stm_msp *stm_msp);
+};
+
+/**
+ * struct chip_data - To maintain runtime state of SPICntlr for each client chip
+ * @ctr_regs: void pointer which is assigned a struct having regs of the cntlr.
+ * @chip_id: Chip Id assigned to this client to identify it.
+ * @n_bytes: how many bytes(power of 2) reqd for a given data width of client
+ * @write: function to be used to write when doing xfer for this chip
+ * @null_write: function to be used for dummy write for receiving data.
+ * @read: function to be used to read when doing xfer for this chip
+ * @null_read: function to be used to for dummy read while writting data.
+ * @cs_control: chip select callback provided by chip
+ * @xfer_type: polling/interrupt
+ *
+ * Runtime state of the SPI controller, maintained per chip,
+ * This would be set according to the current message that would be served
+ */
+struct chip_data {
+ void *ctr_regs;
+ u32 chip_id;
+ u8 n_bytes;
+ void (*write) (struct stm_msp *stm_msp);
+ void (*null_write) (struct stm_msp *stm_msp);
+ void (*read) (struct stm_msp *stm_msp);
+ void (*null_read) (struct stm_msp *stm_msp);
+ void (*delay) (struct stm_msp *stm_msp);
+ void (*cs_control) (u32 command);
+ int xfer_type;
+};
+
+/**
+ * struct msp_regs - Used to store MSP controller registry values
+ * used by the driver.
+ * @gcr: global configuration register
+ * @tcf: transmit configuration register
+ * @rcf: receive configuration register
+ * @srg: sample rate generator register
+ * @dmacr: DMA configuration register
+ */
+struct msp_regs {
+ u32 gcr;
+ u32 tcf;
+ u32 rcf;
+ u32 srg;
+ u32 dmacr;
+};
+
+/**
+ * stm_msp_controller_cmd - To execute controller commands for MSP
+ * @stm_msp: SPI driver private data structure
+ * @cmd: Command which is to be executed on the controller
+ */
+static int stm_msp_controller_cmd(struct stm_msp *stm_msp, int cmd)
+{
+ int retval = 0;
+ struct msp_regs *msp_regs = NULL;
+
+ switch (cmd) {
+ case DISABLE_CONTROLLER: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Disabling MSP controller...\n");
+ writel((readl(MSP_GCR(stm_msp->regs)) &
+ (~(MSP_GCR_MASK_TXEN | MSP_GCR_MASK_RXEN))),
+ MSP_GCR(stm_msp->regs));
+ break;
+ }
+ case ENABLE_CONTROLLER: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Enabling MSP controller...\n");
+ writel((readl(MSP_GCR(stm_msp->regs)) |
+ (MSP_GCR_MASK_TXEN | MSP_GCR_MASK_RXEN)),
+ MSP_GCR(stm_msp->regs));
+ break;
+ }
+ case DISABLE_ALL_INTERRUPT: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Disabling all MSP interrupts...\n");
+ writel(DISABLE_ALL_MSP_INTERRUPTS,
+ MSP_IMSC(stm_msp->regs));
+ break;
+ }
+ case ENABLE_ALL_INTERRUPT: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Enabling all MSP interrupts...\n");
+ writel(ENABLE_ALL_MSP_INTERRUPTS,
+ MSP_IMSC(stm_msp->regs));
+ break;
+ }
+ case CLEAR_ALL_INTERRUPT: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Clearing all MSP interrupts...\n");
+ writel(CLEAR_ALL_MSP_INTERRUPTS,
+ MSP_ICR(stm_msp->regs));
+ break;
+ }
+ case FLUSH_FIFO: {
+ unsigned long limit = loops_per_jiffy << 1;
+
+ dev_dbg(&stm_msp->adev->dev, "MSP FIFO flushed\n");
+
+ do {
+ while (!(readl(MSP_FLR(stm_msp->regs)) &
+ MSP_FLR_MASK_RFE)) {
+ readl(MSP_DR(stm_msp->regs));
+ }
+ } while ((readl(MSP_FLR(stm_msp->regs)) &
+ (MSP_FLR_MASK_TBUSY | MSP_FLR_MASK_RBUSY)) &&
+ limit--);
+
+ retval = limit;
+ break;
+ }
+ case RESTORE_STATE: {
+ msp_regs =
+ (struct msp_regs *)stm_msp->cur_chip->ctr_regs;
+
+ dev_dbg(&stm_msp->adev->dev,
+ "Restoring MSP state...\n");
+
+ writel(msp_regs->gcr, MSP_GCR(stm_msp->regs));
+ writel(msp_regs->tcf, MSP_TCF(stm_msp->regs));
+ writel(msp_regs->rcf, MSP_RCF(stm_msp->regs));
+ writel(msp_regs->srg, MSP_SRG(stm_msp->regs));
+ writel(msp_regs->dmacr, MSP_DMACR(stm_msp->regs));
+ writel(DISABLE_ALL_MSP_INTERRUPTS,
+ MSP_IMSC(stm_msp->regs));
+ writel(CLEAR_ALL_MSP_INTERRUPTS,
+ MSP_ICR(stm_msp->regs));
+ break;
+ }
+ case LOAD_DEFAULT_CONFIG: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Loading default MSP config...\n");
+
+ writel(DEFAULT_MSP_REG_GCR, MSP_GCR(stm_msp->regs));
+ writel(DEFAULT_MSP_REG_TCF, MSP_TCF(stm_msp->regs));
+ writel(DEFAULT_MSP_REG_RCF, MSP_RCF(stm_msp->regs));
+ writel(DEFAULT_MSP_REG_SRG, MSP_SRG(stm_msp->regs));
+ writel(DEFAULT_MSP_REG_DMACR, MSP_DMACR(stm_msp->regs));
+ writel(DISABLE_ALL_MSP_INTERRUPTS,
+ MSP_IMSC(stm_msp->regs));
+ writel(CLEAR_ALL_MSP_INTERRUPTS,
+ MSP_ICR(stm_msp->regs));
+ break;
+ }
+ default:
+ dev_dbg(&stm_msp->adev->dev, "Unknown command\n");
+ retval = -1;
+ break;
+ }
+
+ return retval;
+}
+
+/**
+ * giveback - current spi_message is over, schedule next spi_message
+ * @message: current SPI message
+ * @stm_msp: spi driver private data structure
+ *
+ * current spi_message is over, schedule next spi_message and call
+ * callback of this msg.
+ */
+static void giveback(struct spi_message *message, struct stm_msp *stm_msp)
+{
+ struct spi_transfer *last_transfer;
+ unsigned long flags;
+ struct spi_message *msg;
+ void (*curr_cs_control)(u32 command);
+
+ spin_lock_irqsave(&stm_msp->lock, flags);
+ msg = stm_msp->cur_msg;
+
+ curr_cs_control = stm_msp->cur_chip->cs_control;
+
+ stm_msp->cur_msg = NULL;
+ stm_msp->cur_transfer = NULL;
+ stm_msp->cur_chip = NULL;
+#ifdef CONFIG_SPI_WORKQUEUE
+ queue_work(stm_msp->workqueue, &stm_msp->spi_work);
+#else
+ schedule_work(&stm_msp->spi_work);
+#endif
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+
+ last_transfer = list_entry(msg->transfers.prev,
+ struct spi_transfer, transfer_list);
+
+ if (!last_transfer->cs_change)
+ curr_cs_control(SPI_CHIP_DESELECT);
+
+ msg->state = NULL;
+
+ if (msg->complete)
+ msg->complete(msg->context);
+
+ stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER);
+ clk_disable(stm_msp->clk);
+}
+
+/**
+ * spi_notify - Handles Polling hang issue over spi bus.
+ * @data: main driver data
+ * Context: Process.
+ *
+ * This is used to handle error condition in transfer and receive function used
+ * in polling mode.
+ * Sometimes due to passing wrong protocol desc , polling transfer may hang.
+ * To prevent this, timer is added.
+ *
+ * Returns void.
+ */
+static void spi_notify(unsigned long data)
+{
+ struct stm_msp *stm_msp = (struct stm_msp *)data;
+ stm_msp->spi_io_error = 1;
+
+ dev_err(&stm_msp->adev->dev,
+ "Polling is taking time, maybe device not responding\n");
+
+ del_timer(&stm_msp->spi_notify_timer);
+}
+
+/**
+ * stm_msp_transfer - transfer function registered to SPI master framework
+ * @spi: spi device which is requesting transfer
+ * @msg: spi message which is to handled is queued to driver queue
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. It will queue the spi_message in the queue of driver if
+ * the queue is not stopped and return.
+ */
+static int stm_msp_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct stm_msp *stm_msp = spi_master_get_devdata(spi->master);
+ unsigned long flags;
+
+ spin_lock_irqsave(&stm_msp->lock, flags);
+
+ if (stm_msp->run == QUEUE_STOPPED) {
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return -ESHUTDOWN;
+ }
+ dev_err(&spi->dev, "Regular request (No infinite DMA ongoing)\n");
+
+ msg->actual_length = 0;
+ msg->status = -EINPROGRESS;
+ msg->state = START_STATE;
+
+ list_add_tail(&msg->queue, &stm_msp->queue);
+
+ if ((stm_msp->run == QUEUE_RUNNING) && (!stm_msp->busy))
+#ifdef CONFIG_SPI_WORKQUEUE
+ queue_work(stm_msp->workqueue, &stm_msp->spi_work);
+#else
+ schedule_work(&stm_msp->spi_work);
+#endif
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return 0;
+}
+
+/**
+ * next_transfer - Move to the Next transfer in the current spi message
+ * @stm_msp: spi driver private data structure
+ *
+ * This function moves though the linked list of spi transfers in the
+ * current spi message and returns with the state of current spi
+ * message i.e whether its last transfer is done(DONE_STATE) or
+ * Next transfer is ready(RUNNING_STATE)
+ */
+static void *next_transfer(struct stm_msp *stm_msp)
+{
+ struct spi_message *msg = stm_msp->cur_msg;
+ struct spi_transfer *trans = stm_msp->cur_transfer;
+
+ /* Move to next transfer */
+ if (trans->transfer_list.next != &msg->transfers) {
+ stm_msp->cur_transfer = list_entry(trans->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ return RUNNING_STATE;
+ }
+ return DONE_STATE;
+}
+
+static void do_interrupt_transfer(void *data)
+{
+ struct stm_msp *stm_msp = (struct stm_msp *)data;
+
+ stm_msp->tx = (void *)stm_msp->cur_transfer->tx_buf;
+ stm_msp->tx_end = stm_msp->tx + stm_msp->cur_transfer->len;
+
+ stm_msp->rx = (void *)stm_msp->cur_transfer->rx_buf;
+ stm_msp->rx_end = stm_msp->rx + stm_msp->cur_transfer->len;
+
+ stm_msp->write = stm_msp->tx ?
+ stm_msp->cur_chip->write : stm_msp->cur_chip->null_write;
+ stm_msp->read = stm_msp->rx ?
+ stm_msp->cur_chip->read : stm_msp->cur_chip->null_read;
+
+ stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT);
+
+ stm_msp_controller_cmd(stm_msp, ENABLE_ALL_INTERRUPT);
+ stm_msp_controller_cmd(stm_msp, ENABLE_CONTROLLER);
+}
+
+static void do_polling_transfer(void *data)
+{
+ struct stm_msp *stm_msp = (struct stm_msp *)data;
+ struct spi_message *message = NULL;
+ struct spi_transfer *transfer = NULL;
+ struct spi_transfer *previous = NULL;
+ struct chip_data *chip;
+ unsigned long limit = 0;
+ u32 timer_expire = 0;
+
+ chip = stm_msp->cur_chip;
+ message = stm_msp->cur_msg;
+
+ while (message->state != DONE_STATE) {
+ /* Handle for abort */
+ if (message->state == ERROR_STATE)
+ break;
+
+ transfer = stm_msp->cur_transfer;
+
+ /* Delay if requested at end of transfer */
+ if (message->state == RUNNING_STATE) {
+ previous = list_entry(transfer->transfer_list.prev,
+ struct spi_transfer,
+ transfer_list);
+
+ if (previous->delay_usecs)
+ udelay(previous->delay_usecs);
+
+ if (previous->cs_change)
+ stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT);
+ } else {
+ /* START_STATE */
+ message->state = RUNNING_STATE;
+ stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT);
+ }
+
+ /* Configuration Changing Per Transfer */
+ stm_msp->tx = (void *)transfer->tx_buf;
+ stm_msp->tx_end = stm_msp->tx + stm_msp->cur_transfer->len;
+ stm_msp->rx = (void *)transfer->rx_buf;
+ stm_msp->rx_end = stm_msp->rx + stm_msp->cur_transfer->len;
+
+ stm_msp->write = stm_msp->tx ?
+ stm_msp->cur_chip->write :
+ stm_msp->cur_chip->null_write;
+ stm_msp->read = stm_msp->rx ?
+ stm_msp->cur_chip->read :
+ stm_msp->cur_chip->null_read;
+ stm_msp->delay = stm_msp->cur_chip->delay;
+
+ stm_msp_controller_cmd(stm_msp, FLUSH_FIFO);
+ stm_msp_controller_cmd(stm_msp, ENABLE_CONTROLLER);
+
+ timer_expire = stm_msp->cur_transfer->len / 1024;
+
+ if (!timer_expire)
+ timer_expire = SPI_TRANSFER_TIMEOUT_MS;
+ else
+ timer_expire =
+ (stm_msp->cur_transfer->len / 1024) *
+ SPI_TRANSFER_TIMEOUT_MS;
+
+ stm_msp->spi_notify_timer.expires =
+ jiffies + msecs_to_jiffies(timer_expire);
+
+ add_timer(&stm_msp->spi_notify_timer);
+
+ dev_dbg(&stm_msp->adev->dev, "Polling transfer ongoing...\n");
+
+ while (stm_msp->tx < stm_msp->tx_end) {
+
+ stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER);
+ stm_msp->read(stm_msp);
+ stm_msp->write(stm_msp);
+
+ stm_msp_controller_cmd(stm_msp, ENABLE_CONTROLLER);
+
+ if (stm_msp->delay)
+ stm_msp->delay(stm_msp);
+
+ if (stm_msp->spi_io_error == 1)
+ break;
+ }
+
+ del_timer(&stm_msp->spi_notify_timer);
+
+ if (stm_msp->spi_io_error == 1)
+ goto out;
+
+ limit = loops_per_jiffy << 1;
+
+ while ((stm_msp->rx < stm_msp->rx_end) && (limit--))
+ stm_msp->read(stm_msp);
+
+ /* Update total byte transfered */
+ message->actual_length += stm_msp->cur_transfer->len;
+
+ if (stm_msp->cur_transfer->cs_change)
+ stm_msp->cur_chip->cs_control(SPI_CHIP_DESELECT);
+
+ stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER);
+
+ /* Move to next transfer */
+ message->state = next_transfer(stm_msp);
+ }
+out:
+ /* Handle end of message */
+ if (message->state == DONE_STATE)
+ message->status = 0;
+ else
+ message->status = -EIO;
+
+ giveback(message, stm_msp);
+
+ stm_msp->spi_io_error = 0; /* Reset state for further transfers */
+
+ return;
+}
+
+/**
+ * pump_messages - Workqueue function which processes spi message queue
+ * @work: pointer to work
+ *
+ * This function checks if there is any spi message in the queue that
+ * needs processing and delegate control to appropriate function
+ * do_polling_transfer()/do_interrupt_transfer()/do_dma_transfer()
+ * based on the kind of the transfer
+ *
+ */
+static void pump_messages(struct work_struct *work)
+{
+ struct stm_msp *stm_msp = container_of(work, struct stm_msp, spi_work);
+ unsigned long flags;
+
+ /* Lock queue and check for queue work */
+ spin_lock_irqsave(&stm_msp->lock, flags);
+
+ if (list_empty(&stm_msp->queue) || stm_msp->run == QUEUE_STOPPED) {
+ dev_dbg(&stm_msp->adev->dev, "work_queue: Queue Empty\n");
+ stm_msp->busy = 0;
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return;
+ }
+ /* Make sure we are not already running a message */
+ if (stm_msp->cur_msg) {
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return;
+ }
+
+ clk_enable(stm_msp->clk);
+
+ /* Extract head of queue */
+ stm_msp->cur_msg = list_entry(stm_msp->queue.next,
+ struct spi_message,
+ queue);
+
+ list_del_init(&stm_msp->cur_msg->queue);
+ stm_msp->busy = 1;
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+
+ /* Initial message state */
+ stm_msp->cur_msg->state = START_STATE;
+ stm_msp->cur_transfer = list_entry(stm_msp->cur_msg->transfers.next,
+ struct spi_transfer,
+ transfer_list);
+
+ /* Setup the SPI using the per chip configuration */
+ stm_msp->cur_chip = spi_get_ctldata(stm_msp->cur_msg->spi);
+ stm_msp_controller_cmd(stm_msp, RESTORE_STATE);
+ stm_msp_controller_cmd(stm_msp, FLUSH_FIFO);
+
+ if (stm_msp->cur_chip->xfer_type == SPI_POLLING_TRANSFER)
+ do_polling_transfer(stm_msp);
+ else if (stm_msp->cur_chip->xfer_type == SPI_INTERRUPT_TRANSFER)
+ do_interrupt_transfer(stm_msp);
+}
+
+/**
+ * pump_transfers - Tasklet function which schedules next interrupt xfer
+ * @data: spi driver private data structure
+ */
+static void pump_transfers(unsigned long data)
+{
+ struct stm_msp *stm_msp = (struct stm_msp *)data;
+ struct spi_message *message = NULL;
+ struct spi_transfer *transfer = NULL;
+ struct spi_transfer *previous = NULL;
+
+ message = stm_msp->cur_msg;
+
+ /* Handle for abort */
+ if (message->state == ERROR_STATE) {
+ message->status = -EIO;
+ giveback(message, stm_msp);
+ return;
+ }
+
+ /* Handle end of message */
+ if (message->state == DONE_STATE) {
+ message->status = 0;
+ giveback(message, stm_msp);
+ return;
+ }
+ transfer = stm_msp->cur_transfer;
+
+ /* Delay if requested at end of transfer */
+ if (message->state == RUNNING_STATE) {
+ previous = list_entry(transfer->transfer_list.prev,
+ struct spi_transfer, transfer_list);
+ if (previous->delay_usecs)
+ udelay(previous->delay_usecs);
+ if (previous->cs_change)
+ stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT);
+ } else {
+ /* START_STATE */
+ message->state = RUNNING_STATE;
+ }
+ stm_msp->tx = (void *)transfer->tx_buf;
+ stm_msp->tx_end = stm_msp->tx + stm_msp->cur_transfer->len;
+ stm_msp->rx = (void *)transfer->rx_buf;
+ stm_msp->rx_end = stm_msp->rx + stm_msp->cur_transfer->len;
+
+ stm_msp->write = stm_msp->tx ?
+ stm_msp->cur_chip->write : stm_msp->cur_chip->null_write;
+ stm_msp->read = stm_msp->rx ?
+ stm_msp->cur_chip->read : stm_msp->cur_chip->null_read;
+
+ stm_msp_controller_cmd(stm_msp, FLUSH_FIFO);
+ stm_msp_controller_cmd(stm_msp, ENABLE_ALL_INTERRUPT);
+}
+
+static int init_queue(struct stm_msp *stm_msp)
+{
+ INIT_LIST_HEAD(&stm_msp->queue);
+ spin_lock_init(&stm_msp->lock);
+
+ stm_msp->run = QUEUE_STOPPED;
+ stm_msp->busy = 0;
+
+ tasklet_init(&stm_msp->pump_transfers, pump_transfers,
+ (unsigned long)stm_msp);
+ INIT_WORK(&stm_msp->spi_work, pump_messages);
+
+#ifdef CONFIG_SPI_WORKQUEUE
+ stm_msp->workqueue = create_singlethread_workqueue(
+ dev_name(&stm_msp->master->dev));
+
+ if (stm_msp->workqueue == NULL)
+ return -EBUSY;
+#endif /* CONFIG_SPI_WORKQUEUE */
+
+ init_timer(&stm_msp->spi_notify_timer);
+
+ stm_msp->spi_notify_timer.expires = jiffies + msecs_to_jiffies(1000);
+ stm_msp->spi_notify_timer.function = spi_notify;
+ stm_msp->spi_notify_timer.data = (unsigned long)stm_msp;
+
+ return 0;
+}
+
+static int start_queue(struct stm_msp *stm_msp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&stm_msp->lock, flags);
+
+ if (stm_msp->run == QUEUE_RUNNING || stm_msp->busy) {
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return -EBUSY;
+ }
+
+ stm_msp->run = QUEUE_RUNNING;
+ stm_msp->cur_msg = NULL;
+ stm_msp->cur_transfer = NULL;
+ stm_msp->cur_chip = NULL;
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return 0;
+}
+
+static int stop_queue(struct stm_msp *stm_msp)
+{
+ unsigned long flags;
+ unsigned limit = 500;
+ int status = 0;
+
+ spin_lock_irqsave(&stm_msp->lock, flags);
+
+ /* This is a bit lame, but is optimized for the common execution path.
+ * A wait_queue on the stm_msp->busy could be used, but then the common
+ * execution path (pump_messages) would be required to call wake_up or
+ * friends on every SPI message. Do this instead */
+
+ stm_msp->run = QUEUE_STOPPED;
+
+ while (!list_empty(&stm_msp->queue) && stm_msp->busy && limit--) {
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ msleep(10);
+ spin_lock_irqsave(&stm_msp->lock, flags);
+ }
+
+ if (!list_empty(&stm_msp->queue) || stm_msp->busy)
+ status = -EBUSY;
+
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+
+ return status;
+}
+
+static int destroy_queue(struct stm_msp *stm_msp)
+{
+ int status;
+
+ status = stop_queue(stm_msp);
+
+ if (status != 0)
+ return status;
+#ifdef CONFIG_SPI_WORKQUEUE
+ destroy_workqueue(stm_msp->workqueue);
+#endif
+ del_timer_sync(&stm_msp->spi_notify_timer);
+
+ return 0;
+}
+
+/**
+ * stm_msp_null_writer - To Write Dummy Data in Data register
+ * @stm_msp: spi driver private data structure
+ *
+ * This function is set as a write function for transfer which have
+ * Tx transfer buffer as NULL. It simply writes '0' in the Data
+ * register
+ */
+static void stm_msp_null_writer(struct stm_msp *stm_msp)
+{
+ u32 cur_write = 0;
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_TFU) ||
+ (stm_msp->tx >= stm_msp->tx_end))
+ return;
+
+ writel(0x0, MSP_DR(stm_msp->regs));
+ stm_msp->tx += (stm_msp->cur_chip->n_bytes);
+ cur_write++;
+
+ if (cur_write == 8)
+ return;
+ }
+}
+
+/**
+ * stm_msp_null_reader - To read data from Data register and discard it
+ * @stm_msp: spi driver private data structure
+ *
+ * This function is set as a reader function for transfer which have
+ * Rx Transfer buffer as null. Read Data is rejected
+ */
+static void stm_msp_null_reader(struct stm_msp *stm_msp)
+{
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_RFE) ||
+ (stm_msp->rx >= stm_msp->rx_end))
+ return;
+
+ readl(MSP_DR(stm_msp->regs));
+ stm_msp->rx += (stm_msp->cur_chip->n_bytes);
+ }
+}
+
+/**
+ * stm_msp_u8_writer - Write FIFO data in Data register as a 8 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function writes data in Tx FIFO till it is not full
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary write ptr tx in stm_msp which maintains
+ * current write position in transfer buffer. we do not write data more than
+ * FIFO depth
+ */
+void stm_msp_u8_writer(struct stm_msp *stm_msp)
+{
+ u32 cur_write = 0;
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_TFU) ||
+ (stm_msp->tx >= stm_msp->tx_end))
+ return;
+
+ writel((u32)(*(u8 *)(stm_msp->tx)), MSP_DR(stm_msp->regs));
+ stm_msp->tx += (stm_msp->cur_chip->n_bytes);
+ cur_write++;
+
+ if (cur_write == MSP_FIFO_DEPTH)
+ return;
+ }
+}
+
+/**
+ * stm_msp_u8_reader - Read FIFO data in Data register as a 8 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function reads data in Rx FIFO till it is not empty
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary Read ptr rx in stm_msp which maintains
+ * current read position in transfer buffer
+ */
+void stm_msp_u8_reader(struct stm_msp *stm_msp)
+{
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_RFE) ||
+ (stm_msp->rx >= stm_msp->rx_end))
+ return;
+
+ *(u8 *)(stm_msp->rx) = (u8)readl(MSP_DR(stm_msp->regs));
+ stm_msp->rx += (stm_msp->cur_chip->n_bytes);
+ }
+}
+
+/**
+ * stm_msp_u16_writer - Write FIFO data in Data register as a 16 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function writes data in Tx FIFO till it is not full
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary write ptr tx in stm_msp which maintains
+ * current write position in transfer buffer. we do not write data more than
+ * FIFO depth
+ */
+void stm_msp_u16_writer(struct stm_msp *stm_msp)
+{
+ u32 cur_write = 0;
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_TFU) ||
+ (stm_msp->tx >= stm_msp->tx_end))
+ return;
+
+ writel((u32)(*(u16 *)(stm_msp->tx)), MSP_DR(stm_msp->regs));
+ stm_msp->tx += (stm_msp->cur_chip->n_bytes);
+ cur_write++;
+
+ if (cur_write == MSP_FIFO_DEPTH)
+ return;
+ }
+}
+
+/**
+ * stm_msp_u16_reader - Read FIFO data in Data register as a 16 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function reads data in Rx FIFO till it is not empty
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary Read ptr rx in stm_msp which maintains
+ * current read position in transfer buffer
+ */
+void stm_msp_u16_reader(struct stm_msp *stm_msp)
+{
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_RFE) ||
+ (stm_msp->rx >= stm_msp->rx_end))
+ return;
+
+ *(u16 *)(stm_msp->rx) = (u16)readl(MSP_DR(stm_msp->regs));
+ stm_msp->rx += (stm_msp->cur_chip->n_bytes);
+ }
+}
+
+/**
+ * stm_msp_u32_writer - Write FIFO data in Data register as a 32 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function writes data in Tx FIFO till it is not full
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary write ptr tx in stm_msp which maintains
+ * current write position in transfer buffer. we do not write data more than
+ * FIFO depth
+ */
+void stm_msp_u32_writer(struct stm_msp *stm_msp)
+{
+ u32 cur_write = 0;
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_TFU) ||
+ (stm_msp->tx >= stm_msp->tx_end))
+ return;
+
+ /* Write Data to Data Register */
+ writel(*(u32 *)(stm_msp->tx), MSP_DR(stm_msp->regs));
+ stm_msp->tx += (stm_msp->cur_chip->n_bytes);
+ cur_write++;
+
+ if (cur_write == MSP_FIFO_DEPTH)
+ return;
+ }
+}
+
+/**
+ * stm_msp_u32_reader - Read FIFO data in Data register as a 32 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function reads data in Rx FIFO till it is not empty
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary Read ptr rx in stm_msp which maintains
+ * current read position in transfer buffer
+ */
+void stm_msp_u32_reader(struct stm_msp *stm_msp)
+{
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_RFE) ||
+ (stm_msp->rx >= stm_msp->rx_end))
+ return;
+
+ *(u32 *)(stm_msp->rx) = readl(MSP_DR(stm_msp->regs));
+ stm_msp->rx += (stm_msp->cur_chip->n_bytes);
+ }
+}
+
+/**
+ * stm_msp_interrupt_handler - Interrupt hanlder function
+ */
+static irqreturn_t stm_msp_interrupt_handler(int irq, void *dev_id)
+{
+ struct stm_msp *stm_msp = (struct stm_msp *)dev_id;
+ struct spi_message *msg = stm_msp->cur_msg;
+ u32 irq_status = 0;
+ u32 flag = 0;
+
+ if (!msg) {
+ dev_err(&stm_msp->adev->dev,
+ "Bad message state in interrupt handler");
+ /* Never fail */
+ return IRQ_HANDLED;
+ }
+
+ /* Read the Interrupt Status Register */
+ irq_status = readl(MSP_MIS(stm_msp->regs));
+
+ if (irq_status) {
+ if (irq_status & MSP_MIS_MASK_ROEMIS) { /* Overrun interrupt */
+ /* Bail out our Data has been corrupted */
+ dev_dbg(&stm_msp->adev->dev,
+ "Received ROR interrupt\n");
+
+ stm_msp_controller_cmd(stm_msp, DISABLE_ALL_INTERRUPT);
+ stm_msp_controller_cmd(stm_msp, CLEAR_ALL_INTERRUPT);
+ stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER);
+ msg->state = ERROR_STATE;
+ tasklet_schedule(&stm_msp->pump_transfers);
+ return IRQ_HANDLED;
+ }
+
+ stm_msp->read(stm_msp);
+ stm_msp->write(stm_msp);
+
+ if ((stm_msp->tx == stm_msp->tx_end) && (flag == 0)) {
+ flag = 1;
+ /* Disable Transmit interrupt */
+ writel(readl(MSP_IMSC(stm_msp->regs)) &
+ (~MSP_IMSC_MASK_TXIM) & (~MSP_IMSC_MASK_TFOIM),
+ (stm_msp->regs + 0x14));
+ }
+
+ /* Clearing any Xmit underrun error. Overrun already handled */
+ stm_msp_controller_cmd(stm_msp, CLEAR_ALL_INTERRUPT);
+
+ if (stm_msp->rx == stm_msp->rx_end) {
+ stm_msp_controller_cmd(stm_msp, DISABLE_ALL_INTERRUPT);
+ stm_msp_controller_cmd(stm_msp, CLEAR_ALL_INTERRUPT);
+
+ dev_dbg(&stm_msp->adev->dev,
+ "Interrupt transfer completed.\n");
+
+ /* Update total bytes transfered */
+ msg->actual_length += stm_msp->cur_transfer->len;
+
+ if (stm_msp->cur_transfer->cs_change)
+ stm_msp->cur_chip->cs_control(
+ SPI_CHIP_DESELECT);
+
+ /* Move to next transfer */
+ msg->state = next_transfer(stm_msp);
+ tasklet_schedule(&stm_msp->pump_transfers);
+ return IRQ_HANDLED;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * stm_msp_cleanup - cleanup function registered to SPI master framework
+ * @spi: spi device which is requesting cleanup
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. It will free the runtime state of chip.
+ */
+static void stm_msp_cleanup(struct spi_device *spi)
+{
+ struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi);
+ struct stm_msp *stm_msp = spi_master_get_devdata(spi->master);
+ struct spi_master *master;
+ master = stm_msp->master;
+
+ if (chip) {
+ kfree(chip->ctr_regs);
+ kfree(chip);
+ spi_set_ctldata(spi, NULL);
+ }
+}
+
+/**
+ * null_cs_control - Dummy chip select function
+ * @command: select/delect the chip
+ *
+ * If no chip select function is provided by client this is used as dummy
+ * chip select
+ */
+static void null_cs_control(u32 command)
+{
+ /* Nothing to do */
+ (void)command;
+}
+
+static int verify_msp_controller_parameters(struct stm_msp_config_chip
+ *chip_info)
+{
+
+ /* FIXME: check clock params */
+ if ((chip_info->lbm != SPI_LOOPBACK_ENABLED) &&
+ (chip_info->lbm != SPI_LOOPBACK_DISABLED)) {
+ dev_dbg(chip_info->dev,
+ "Loopback Mode is configured incorrectly\n");
+ return -1;
+ }
+ if ((chip_info->hierarchy != SPI_MASTER) &&
+ (chip_info->hierarchy != SPI_SLAVE)) {
+ dev_dbg(chip_info->dev,
+ "hierarchy is configured incorrectly\n");
+ return -1;
+ }
+ if ((chip_info->endian_rx != SPI_FIFO_MSB) &&
+ (chip_info->endian_rx != SPI_FIFO_LSB)) {
+ dev_dbg(chip_info->dev,
+ "Rx FIFO endianess is configured incorrectly\n");
+ return -1;
+ }
+ if ((chip_info->endian_tx != SPI_FIFO_MSB) &&
+ (chip_info->endian_tx != SPI_FIFO_LSB)) {
+ dev_dbg(chip_info->dev,
+ "Tx FIFO endianess is configured incorrectly\n");
+ return -1;
+ }
+ if ((chip_info->data_size < MSP_DATA_BITS_8) ||
+ (chip_info->data_size > MSP_DATA_BITS_32)) {
+ dev_dbg(chip_info->dev,
+ "MSP DATA Size is configured incorrectly\n");
+ return -1;
+ }
+ if ((chip_info->com_mode != SPI_INTERRUPT_TRANSFER) &&
+ (chip_info->com_mode != SPI_POLLING_TRANSFER)) {
+ dev_dbg(chip_info->dev,
+ "Communication mode is configured incorrectly\n");
+ return -1;
+ }
+ if (((chip_info->proto_params).clk_phase !=
+ SPI_CLK_ZERO_CYCLE_DELAY) &&
+ ((chip_info->proto_params).clk_phase !=
+ SPI_CLK_HALF_CYCLE_DELAY)) {
+ dev_dbg(chip_info->dev,
+ "Clock Phase is configured incorrectly\n");
+ return -1;
+ }
+ if (((chip_info->proto_params).clk_pol !=
+ SPI_CLK_POL_IDLE_LOW) &&
+ ((chip_info->proto_params).clk_pol !=
+ SPI_CLK_POL_IDLE_HIGH)) {
+ dev_dbg(chip_info->dev,
+ "Clk Polarity configured incorrectly\n");
+ return -1;
+ }
+ if (chip_info->cs_control == NULL) {
+ dev_dbg(chip_info->dev,
+ "Chip Select Function is NULL for this chip\n");
+ chip_info->cs_control = null_cs_control;
+ }
+ return 0;
+}
+
+static struct stm_msp_config_chip *allocate_default_msp_chip_cfg(
+ struct spi_device *spi)
+{
+ struct stm_msp_config_chip *chip_info;
+
+ chip_info = kzalloc(sizeof(struct stm_msp_config_chip), GFP_KERNEL);
+
+ if (!chip_info) {
+ dev_err(&spi->dev, "setup - cannot allocate controller data");
+ return NULL;
+ }
+ dev_dbg(&spi->dev, "Allocated Memory for controller data\n");
+
+ chip_info->lbm = SPI_LOOPBACK_DISABLED;
+ chip_info->com_mode = SPI_POLLING_TRANSFER;
+ chip_info->hierarchy = SPI_MASTER;
+ chip_info->endian_tx = SPI_FIFO_LSB;
+ chip_info->endian_rx = SPI_FIFO_LSB;
+ chip_info->data_size = MSP_DATA_BITS_32;
+
+ if (spi->max_speed_hz != 0)
+ chip_info->freq = spi->max_speed_hz;
+ else
+ chip_info->freq = SPI_DEFAULT_MAX_SPEED_HZ;
+
+ chip_info->proto_params.clk_phase = SPI_CLK_HALF_CYCLE_DELAY;
+ chip_info->proto_params.clk_pol = SPI_CLK_POL_IDLE_LOW;
+ chip_info->cs_control = null_cs_control;
+
+ return chip_info;
+}
+
+static void stm_msp_delay(struct stm_msp *stm_msp)
+{
+ udelay(15);
+
+ while (readl(MSP_FLR(stm_msp->regs)) &
+ (MSP_FLR_MASK_RBUSY | MSP_FLR_MASK_TBUSY))
+ udelay(1);
+}
+
+/**
+ * stm_msp_setup - setup function registered to SPI master framework
+ * @spi: spi device which is requesting setup
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. If it is the first time when setup is called by this device,
+ * this function will initialize the runtime state for this chip and save
+ * the same in the device structure. Else it will update the runtime info
+ * with the updated chip info.
+ */
+static int stm_msp_setup(struct spi_device *spi)
+{
+ struct stm_msp_config_chip *chip_info;
+ struct chip_data *curr_cfg;
+ struct spi_master *master;
+ int status = 0;
+ u16 sckdiv = 0;
+ s16 bus_num = 0;
+ struct stm_msp *stm_msp = spi_master_get_devdata(spi->master);
+ struct msp_regs *msp_regs;
+ master = stm_msp->master;
+ bus_num = master->bus_num - 1;
+
+ /* Get controller data */
+ chip_info = spi->controller_data;
+ /* Get controller_state */
+ curr_cfg = spi_get_ctldata(spi);
+
+ if (curr_cfg == NULL) {
+ curr_cfg = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
+
+ if (!curr_cfg) {
+ dev_err(&stm_msp->adev->dev,
+ "setup - cannot allocate controller state");
+ return -ENOMEM;
+ }
+
+ curr_cfg->chip_id = spi->chip_select;
+ curr_cfg->ctr_regs = kzalloc(sizeof(struct msp_regs),
+ GFP_KERNEL);
+
+ if (curr_cfg->ctr_regs == NULL) {
+ dev_err(&stm_msp->adev->dev,
+ "setup - cannot allocate mem for regs");
+ goto err_first_setup;
+ }
+
+ dev_err(&stm_msp->adev->dev,
+ "chip Id = %d\n", curr_cfg->chip_id);
+
+ if (chip_info == NULL) {
+ chip_info = allocate_default_msp_chip_cfg(spi);
+
+ if (!chip_info) {
+ dev_err(&stm_msp->adev->dev,
+ "setup - cannot allocate cntlr data");
+ status = -ENOMEM;
+ goto err_first_setup;
+ }
+
+ spi->controller_data = chip_info;
+ }
+ }
+
+ /* Pointer back to the SPI device */
+ chip_info->dev = &spi->dev;
+
+ if (chip_info->freq == 0) {
+ /* Calculate Specific Freq. */
+ if ((MSP_INTERNAL_CLK == chip_info->clk_freq.clk_src) ||
+ (MSP_EXTERNAL_CLK == chip_info->clk_freq.clk_src)) {
+ sckdiv = chip_info->clk_freq.sckdiv;
+ } else {
+ status = -1;
+ dev_err(&stm_msp->adev->dev,
+ "setup - controller clock data is incorrect");
+ goto err_config_params;
+ }
+ } else {
+ /* Calculate Effective Freq. */
+ sckdiv = (DEFAULT_MSP_CLK / (chip_info->freq)) - 1;
+
+ if (sckdiv > MAX_SCKDIV) {
+ dev_dbg(&stm_msp->adev->dev,
+ "SPI: Cannot set frequency less than 48Khz,"
+ "setting lowest(48 Khz)\n");
+ sckdiv = MAX_SCKDIV;
+ }
+ }
+
+ status = verify_msp_controller_parameters(chip_info);
+
+ if (status) {
+ dev_err(&stm_msp->adev->dev,
+ "setup - controller data is incorrect");
+ goto err_config_params;
+ }
+
+ /* Now set controller state based on controller data */
+ curr_cfg->xfer_type = chip_info->com_mode;
+ curr_cfg->cs_control = chip_info->cs_control;
+ curr_cfg->delay = stm_msp_delay;
+
+ curr_cfg->null_write = stm_msp_null_writer;
+ curr_cfg->null_read = stm_msp_null_reader;
+
+ if (chip_info->data_size <= MSP_DATA_BITS_8) {
+ dev_dbg(&stm_msp->adev->dev, "Less than 8 bits per word...\n");
+
+ curr_cfg->n_bytes = 1;
+ curr_cfg->read = stm_msp_u8_reader;
+ curr_cfg->write = stm_msp_u8_writer;
+ } else if (chip_info->data_size <= MSP_DATA_BITS_16) {
+ dev_dbg(&stm_msp->adev->dev, "Less than 16 bits per word...\n");
+
+ curr_cfg->n_bytes = 2;
+ curr_cfg->read = stm_msp_u16_reader;
+ curr_cfg->write = stm_msp_u16_writer;
+ } else {
+ dev_dbg(&stm_msp->adev->dev, "Less than 32 bits per word...\n");
+
+ curr_cfg->n_bytes = 4;
+ curr_cfg->read = stm_msp_u32_reader;
+ curr_cfg->write = stm_msp_u32_writer;
+ }
+
+ /* Now initialize all register settings reqd. for this chip */
+
+ msp_regs = (struct msp_regs *)(curr_cfg->ctr_regs);
+ msp_regs->gcr = 0x0;
+ msp_regs->tcf = 0x0;
+ msp_regs->rcf = 0x0;
+ msp_regs->srg = 0x0;
+ msp_regs->dmacr = 0x0;
+
+ MSP_WBITS(msp_regs->dmacr, 0x0, MSP_DMACR_MASK_RDMAE, 0);
+ MSP_WBITS(msp_regs->dmacr, 0x0, MSP_DMACR_MASK_TDMAE, 1);
+
+ /* GCR Reg Config */
+
+ MSP_WBITS(msp_regs->gcr,
+ MSP_RECEIVER_DISABLED, MSP_GCR_MASK_RXEN, 0);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_RX_FIFO_ENABLED, MSP_GCR_MASK_RFFEN, 1);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TRANSMITTER_DISABLED, MSP_GCR_MASK_TXEN, 8);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TX_FIFO_ENABLED, MSP_GCR_MASK_TFFEN, 9);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TX_FRAME_SYNC_POL_LOW, MSP_GCR_MASK_TFSPOL, 10);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TX_FRAME_SYNC_INT, MSP_GCR_MASK_TFSSEL, 11);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TRANSMIT_DATA_WITH_DELAY, MSP_GCR_MASK_TXDDL, 15);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_SAMPLE_RATE_GEN_ENABLE, MSP_GCR_MASK_SGEN, 16);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_CLOCK_INTERNAL, MSP_GCR_MASK_SCKSEL, 18);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_FRAME_GEN_ENABLE, MSP_GCR_MASK_FGEN, 20);
+ MSP_WBITS(msp_regs->gcr,
+ SPI_BURST_MODE_DISABLE, MSP_GCR_MASK_SPIBME, 23);
+
+ if (chip_info->lbm == SPI_LOOPBACK_ENABLED)
+ MSP_WBITS(msp_regs->gcr,
+ MSP_LOOPBACK_ENABLED, MSP_GCR_MASK_LBM, 7);
+ else
+ MSP_WBITS(msp_regs->gcr,
+ MSP_LOOPBACK_DISABLED, MSP_GCR_MASK_LBM, 7);
+
+ if (chip_info->hierarchy == SPI_MASTER)
+ MSP_WBITS(msp_regs->gcr,
+ MSP_IS_SPI_MASTER, MSP_GCR_MASK_TCKSEL, 14);
+ else
+ MSP_WBITS(msp_regs->gcr,
+ MSP_IS_SPI_SLAVE, MSP_GCR_MASK_TCKSEL, 14);
+
+ if (chip_info->proto_params.clk_phase == SPI_CLK_ZERO_CYCLE_DELAY)
+ MSP_WBITS(msp_regs->gcr,
+ MSP_SPI_PHASE_ZERO_CYCLE_DELAY,
+ MSP_GCR_MASK_SPICKM, 21);
+ else
+ MSP_WBITS(msp_regs->gcr,
+ MSP_SPI_PHASE_HALF_CYCLE_DELAY,
+ MSP_GCR_MASK_SPICKM, 21);
+
+ if (chip_info->proto_params.clk_pol == SPI_CLK_POL_IDLE_HIGH)
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TX_CLOCK_POL_HIGH, MSP_GCR_MASK_TCKPOL, 13);
+ else
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TX_CLOCK_POL_LOW, MSP_GCR_MASK_TCKPOL, 13);
+
+ /* RCF Reg Config */
+ MSP_WBITS(msp_regs->rcf,
+ MSP_IGNORE_RX_FRAME_SYNC_PULSE, MSP_RCF_MASK_RFSIG, 15);
+ MSP_WBITS(msp_regs->rcf,
+ MSP_RX_1BIT_DATA_DELAY, MSP_RCF_MASK_RDDLY, 13);
+
+ if (chip_info->endian_rx == SPI_FIFO_LSB)
+ MSP_WBITS(msp_regs->rcf,
+ MSP_RX_ENDIANESS_LSB, MSP_RCF_MASK_RENDN, 12);
+ else
+ MSP_WBITS(msp_regs->rcf,
+ MSP_RX_ENDIANESS_MSB, MSP_RCF_MASK_RENDN, 12);
+
+ MSP_WBITS(msp_regs->rcf, chip_info->data_size, MSP_RCF_MASK_RP1ELEN, 0);
+
+ /* TCF Reg Config */
+
+ MSP_WBITS(msp_regs->tcf,
+ MSP_IGNORE_TX_FRAME_SYNC_PULSE, MSP_TCF_MASK_TFSIG, 15);
+ MSP_WBITS(msp_regs->tcf,
+ MSP_TX_1BIT_DATA_DELAY, MSP_TCF_MASK_TDDLY, 13);
+
+ if (chip_info->endian_rx == SPI_FIFO_LSB)
+ MSP_WBITS(msp_regs->tcf,
+ MSP_TX_ENDIANESS_LSB, MSP_TCF_MASK_TENDN, 12);
+ else
+ MSP_WBITS(msp_regs->tcf,
+ MSP_TX_ENDIANESS_MSB, MSP_TCF_MASK_TENDN, 12);
+ MSP_WBITS(msp_regs->tcf, chip_info->data_size, MSP_TCF_MASK_TP1ELEN, 0);
+
+ /* SRG Reg Config */
+
+ MSP_WBITS(msp_regs->srg, sckdiv, MSP_SRG_MASK_SCKDIV, 0);
+
+ /* Save controller_state */
+ spi_set_ctldata(spi, curr_cfg);
+
+ return status;
+
+err_config_params:
+err_first_setup:
+
+ kfree(curr_cfg);
+ return status;
+}
+
+static int __init stm_msp_probe(struct amba_device *adev, struct amba_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct stm_msp_controller *platform_info = adev->dev.platform_data;
+ struct spi_master *master;
+ struct stm_msp *stm_msp = NULL; /* Data for this driver */
+ int irq, status = 0;
+
+ dev_info(dev, "STM MSP driver, device ID: 0x%08x\n", adev->periphid);
+
+ if (platform_info == NULL) {
+ dev_err(dev, "probe - no platform data supplied\n");
+ status = -ENODEV;
+ goto err_no_pdata;
+ }
+
+ /* Allocate master with space for data */
+ master = spi_alloc_master(dev, sizeof(struct stm_msp));
+
+ if (master == NULL) {
+ dev_err(dev, "probe - cannot alloc spi_master\n");
+ status = -ENOMEM;
+ goto err_no_mem;
+ }
+
+ stm_msp = spi_master_get_devdata(master);
+ stm_msp->master = master;
+ stm_msp->master_info = platform_info;
+ stm_msp->adev = adev;
+
+ stm_msp->clk = clk_get(&adev->dev, NULL);
+
+ if (IS_ERR(stm_msp->clk)) {
+ dev_err(dev, "probe - cannot find clock\n");
+ status = PTR_ERR(stm_msp->clk);
+ goto free_master;
+ }
+
+ /* Fetch the Resources, using platform data */
+ status = amba_request_regions(adev, NULL);
+
+ if (status) {
+ status = -ENODEV;
+ goto disable_clk;
+ }
+
+ /* Get Hold of Device Register Area... */
+ stm_msp->regs = ioremap(adev->res.start, resource_size(&adev->res));
+
+ if (stm_msp->regs == NULL) {
+ status = -ENODEV;
+ goto disable_clk;
+ }
+
+ irq = adev->irq[0];
+
+ if (irq <= 0) {
+ status = -ENODEV;
+ goto err_no_iores;
+ }
+
+ stm_msp_controller_cmd(stm_msp, LOAD_DEFAULT_CONFIG);
+
+ /* Required Info for an SPI controller */
+ /* Bus Number Which Assigned to this SPI controller on this board */
+ master->bus_num = (u16) platform_info->id;
+ master->num_chipselect = platform_info->num_chipselect;
+ master->setup = stm_msp_setup;
+ master->cleanup = (void *)stm_msp_cleanup;
+ master->transfer = stm_msp_transfer;
+
+ dev_dbg(dev, "BUSNO: %d\n", master->bus_num);
+
+ /* Initialize and start queue */
+ status = init_queue(stm_msp);
+
+ if (status != 0) {
+ dev_err(dev, "probe - problem initializing queue\n");
+ goto err_init_queue;
+ }
+
+ status = start_queue(stm_msp);
+
+ if (status != 0) {
+ dev_err(dev, "probe - problem starting queue\n");
+ goto err_start_queue;
+ }
+
+ amba_set_drvdata(adev, stm_msp);
+
+ dev_dbg(dev, "probe succeded\n");
+ dev_dbg(dev, "Bus No = %d, IRQ Line = %d, Virtual Addr: %x\n",
+ master->bus_num, irq, (u32)(stm_msp->regs));
+
+ status = request_irq(stm_msp->adev->irq[0],
+ stm_msp_interrupt_handler,
+ 0, stm_msp->master_info->device_name,
+ stm_msp);
+
+ if (status < 0) {
+ dev_err(dev, "probe - cannot get IRQ (%d)\n", status);
+ goto err_irq;
+ }
+
+ /* Register with the SPI framework */
+ status = spi_register_master(master);
+
+ if (status != 0) {
+ dev_err(dev, "probe - problem registering spi master\n");
+ goto err_spi_register;
+ }
+
+ return 0;
+
+err_spi_register:
+ free_irq(stm_msp->adev->irq[0], stm_msp);
+err_irq:
+err_init_queue:
+err_start_queue:
+ destroy_queue(stm_msp);
+err_no_iores:
+ iounmap(stm_msp->regs);
+disable_clk:
+ clk_put(stm_msp->clk);
+free_master:
+ spi_master_put(master);
+err_no_mem:
+err_no_pdata:
+ return status;
+}
+
+static int __exit stm_msp_remove(struct amba_device *adev)
+{
+ struct stm_msp *stm_msp = amba_get_drvdata(adev);
+ int status = 0;
+
+ if (!stm_msp)
+ return 0;
+
+ /* Remove the queue */
+ status = destroy_queue(stm_msp);
+
+ if (status != 0) {
+ dev_err(&adev->dev, "queue remove failed (%d)\n", status);
+ return status;
+ }
+
+ stm_msp_controller_cmd(stm_msp, LOAD_DEFAULT_CONFIG);
+
+ /* Release map resources */
+ iounmap(stm_msp->regs);
+ amba_release_regions(adev);
+ tasklet_disable(&stm_msp->pump_transfers);
+ free_irq(stm_msp->adev->irq[0], stm_msp);
+
+ /* Disconnect from the SPI framework */
+ spi_unregister_master(stm_msp->master);
+
+ clk_put(stm_msp->clk);
+
+ /* Prevent double remove */
+ amba_set_drvdata(adev, NULL);
+ dev_dbg(&adev->dev, "remove succeded\n");
+ return status;
+}
+
+#ifdef CONFIG_PM
+
+/**
+ * stm_msp_suspend - MSP suspend function registered with PM framework.
+ * @dev: Reference to amba device structure of the device
+ * @state: power mgmt state.
+ *
+ * This function is invoked when the system is going into sleep, called
+ * by the power management framework of the linux kernel.
+ */
+static int stm_msp_suspend(struct amba_device *adev, pm_message_t state)
+{
+ struct stm_msp *stm_msp = amba_get_drvdata(adev);
+ int status = 0;
+
+ status = stop_queue(stm_msp);
+
+ if (status != 0) {
+ dev_warn(&adev->dev, "suspend cannot stop queue\n");
+ return status;
+ }
+
+ dev_dbg(&adev->dev, "suspended\n");
+ return 0;
+}
+
+/**
+ * stm_msp_resume - MSP Resume function registered with PM framework.
+ * @dev: Reference to amba device structure of the device
+ *
+ * This function is invoked when the system is coming out of sleep, called
+ * by the power management framework of the linux kernel.
+ */
+static int stm_msp_resume(struct amba_device *adev)
+{
+ struct stm_msp *stm_msp = amba_get_drvdata(adev);
+ int status = 0;
+
+ /* Start the queue running */
+ status = start_queue(stm_msp);
+
+ if (status)
+ dev_err(&adev->dev, "problem starting queue (%d)\n", status);
+ else
+ dev_dbg(&adev->dev, "resumed\n");
+
+ return status;
+}
+
+#else
+#define stm_msp_suspend NULL
+#define stm_msp_resume NULL
+#endif /* CONFIG_PM */
+
+static struct amba_id stm_msp_ids[] = {
+ {
+ .id = MSP_PER_ID,
+ .mask = MSP_PER_MASK,
+ },
+ {
+ 0,
+ 0,
+ },
+};
+
+static struct amba_driver stm_msp_driver = {
+ .drv = {
+ .name = "MSP",
+ },
+ .id_table = stm_msp_ids,
+ .probe = stm_msp_probe,
+ .remove = __exit_p(stm_msp_remove),
+ .resume = stm_msp_resume,
+ .suspend = stm_msp_suspend,
+};
+
+static int __init stm_msp_init(void)
+{
+ return amba_driver_register(&stm_msp_driver);
+}
+
+static void __exit stm_msp_exit(void)
+{
+ amba_driver_unregister(&stm_msp_driver);
+}
+
+module_init(stm_msp_init);
+module_exit(stm_msp_exit);
+
+MODULE_AUTHOR("Sachin Verma <sachin.verma@st.com>");
+MODULE_DESCRIPTION("STM MSP (SPI protocol) Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 984a7544071..cf08e8f00c2 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -24,6 +24,29 @@ menuconfig STAGING
if STAGING
+config U8500_MMIO
+ bool "ST-Ericsson MMIO (Camera) Driver"
+ depends on ARCH_U8500
+ default n
+
+config U8500_FLASH
+ bool "ST-Ericsson Flash (Camera) Driver"
+ depends on ARCH_U8500
+ default n
+
+config U8500_CM
+ tristate "U8500 Component Manager driver"
+ depends on UX500_SOC_DB8500
+ default n
+ help
+ This is the Component Manager driver. It is part of the
+ Nomadik Multiprocessing Framework.
+
+ Note: This option allows the kernel developers to build
+ the driver in kernel to ease there life. By default, this driver
+ must be built outside this kernel source tree.
+
+
config STAGING_EXCLUDE_BUILD
bool "Exclude Staging drivers from being built" if STAGING
default y
@@ -81,6 +104,8 @@ source "drivers/staging/rtl8192e/Kconfig"
source "drivers/staging/frontier/Kconfig"
+source "drivers/staging/android/Kconfig"
+
source "drivers/staging/dream/Kconfig"
source "drivers/staging/pohmelfs/Kconfig"
@@ -147,5 +172,7 @@ source "drivers/staging/mrst-touchscreen/Kconfig"
source "drivers/staging/msm/Kconfig"
+source "drivers/staging/ste_rmi4/Kconfig"
+
endif # !STAGING_EXCLUDE_BUILD
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 9fa25133874..7628789a61e 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_RTL8192SU) += rtl8192su/
obj-$(CONFIG_RTL8192U) += rtl8192u/
obj-$(CONFIG_RTL8192E) += rtl8192e/
obj-$(CONFIG_TRANZPORT) += frontier/
+obj-$(CONFIG_ANDROID) += android/
obj-$(CONFIG_DREAM) += dream/
obj-$(CONFIG_POHMELFS) += pohmelfs/
obj-$(CONFIG_IDE_PHISON) += phison/
@@ -54,3 +55,13 @@ obj-$(CONFIG_ADIS16255) += adis16255/
obj-$(CONFIG_FB_XGI) += xgifb/
obj-$(CONFIG_TOUCHSCREEN_MRSTOUCH) += mrst-touchscreen/
obj-$(CONFIG_MSM_STAGING) += msm/
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
+ifeq ($(CONFIG_U8500_MMIO), y)
+ obj-y += mmio/
+endif
+ifeq ($(CONFIG_U8500_FLASH), y)
+ obj-y += camera_flash/
+endif
+ifeq ($(CONFIG_U8500_CM), y)
+ obj-y += nmf-cm/
+endif
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
new file mode 100644
index 00000000000..6f54dabfff8
--- /dev/null
+++ b/drivers/staging/android/Kconfig
@@ -0,0 +1,103 @@
+menu "Android"
+
+config ANDROID
+ bool "Android Drivers"
+ default N
+ ---help---
+ Enable support for various drivers needed on the Android platform
+
+if ANDROID
+
+config ANDROID_BINDER_IPC
+ bool "Android Binder IPC Driver"
+ default n
+
+config ANDROID_LOGGER
+ tristate "Android log driver"
+ default n
+
+config ANDROID_RAM_CONSOLE
+ bool "Android RAM buffer console"
+ default n
+
+config ANDROID_RAM_CONSOLE_ENABLE_VERBOSE
+ bool "Enable verbose console messages on Android RAM console"
+ default y
+ depends on ANDROID_RAM_CONSOLE
+
+menuconfig ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+ bool "Android RAM Console Enable error correction"
+ default n
+ depends on ANDROID_RAM_CONSOLE
+ depends on !ANDROID_RAM_CONSOLE_EARLY_INIT
+ select REED_SOLOMON
+ select REED_SOLOMON_ENC8
+ select REED_SOLOMON_DEC8
+
+if ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+
+config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE
+ int "Android RAM Console Data data size"
+ default 128
+ help
+ Must be a power of 2.
+
+config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE
+ int "Android RAM Console ECC size"
+ default 16
+
+config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE
+ int "Android RAM Console Symbol size"
+ default 8
+
+config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL
+ hex "Android RAM Console Polynomial"
+ default 0x19 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 4)
+ default 0x29 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 5)
+ default 0x61 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 6)
+ default 0x89 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 7)
+ default 0x11d if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 8)
+
+endif # ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+
+config ANDROID_RAM_CONSOLE_EARLY_INIT
+ bool "Start Android RAM console early"
+ default n
+ depends on ANDROID_RAM_CONSOLE
+
+config ANDROID_RAM_CONSOLE_EARLY_ADDR
+ hex "Android RAM console virtual address"
+ default 0
+ depends on ANDROID_RAM_CONSOLE_EARLY_INIT
+
+config ANDROID_RAM_CONSOLE_EARLY_SIZE
+ hex "Android RAM console buffer size"
+ default 0
+ depends on ANDROID_RAM_CONSOLE_EARLY_INIT
+
+config ANDROID_TIMED_OUTPUT
+ bool "Timed output class driver"
+ default y
+
+config ANDROID_TIMED_GPIO
+ tristate "Android timed gpio driver"
+ depends on GENERIC_GPIO && ANDROID_TIMED_OUTPUT
+ default n
+
+config ANDROID_LOW_MEMORY_KILLER
+ bool "Android Low Memory Killer"
+ default N
+ ---help---
+ Register processes to be killed when memory is low
+
+config ANDROID_STE_TIMED_VIBRA
+ bool "ST-Ericsson Timed Output Vibrator"
+ depends on SND_SOC_AB8500
+ depends on ANDROID_TIMED_OUTPUT
+ default y
+ help
+ ST-Ericsson's vibrator driver using timed output class device
+
+endif # if ANDROID
+
+endmenu
diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile
new file mode 100644
index 00000000000..7aee0cb4e85
--- /dev/null
+++ b/drivers/staging/android/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o
+obj-$(CONFIG_ANDROID_LOGGER) += logger.o
+obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o
+obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
+obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
+obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
+obj-$(CONFIG_ANDROID_STE_TIMED_VIBRA) += ste_timed_vibra.o
diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c
new file mode 100644
index 00000000000..426bb62e712
--- /dev/null
+++ b/drivers/staging/android/binder.c
@@ -0,0 +1,3601 @@
+/* binder.c
+ *
+ * Android IPC Subsystem
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/fdtable.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/nsproxy.h>
+#include <linux/poll.h>
+#include <linux/debugfs.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+
+#include "binder.h"
+
+static DEFINE_MUTEX(binder_lock);
+static DEFINE_MUTEX(binder_deferred_lock);
+
+static HLIST_HEAD(binder_procs);
+static HLIST_HEAD(binder_deferred_list);
+static HLIST_HEAD(binder_dead_nodes);
+
+static struct dentry *binder_debugfs_dir_entry_root;
+static struct dentry *binder_debugfs_dir_entry_proc;
+static struct binder_node *binder_context_mgr_node;
+static uid_t binder_context_mgr_uid = -1;
+static int binder_last_id;
+static struct workqueue_struct *binder_deferred_workqueue;
+
+#define BINDER_DEBUG_ENTRY(name) \
+static int binder_##name##_open(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, binder_##name##_show, inode->i_private); \
+} \
+\
+static const struct file_operations binder_##name##_fops = { \
+ .owner = THIS_MODULE, \
+ .open = binder_##name##_open, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = single_release, \
+}
+
+static int binder_proc_show(struct seq_file *m, void *unused);
+BINDER_DEBUG_ENTRY(proc);
+
+/* This is only defined in include/asm-arm/sizes.h */
+#ifndef SZ_1K
+#define SZ_1K 0x400
+#endif
+
+#ifndef SZ_4M
+#define SZ_4M 0x400000
+#endif
+
+#define FORBIDDEN_MMAP_FLAGS (VM_WRITE)
+
+#define BINDER_SMALL_BUF_SIZE (PAGE_SIZE * 64)
+
+enum {
+ BINDER_DEBUG_USER_ERROR = 1U << 0,
+ BINDER_DEBUG_FAILED_TRANSACTION = 1U << 1,
+ BINDER_DEBUG_DEAD_TRANSACTION = 1U << 2,
+ BINDER_DEBUG_OPEN_CLOSE = 1U << 3,
+ BINDER_DEBUG_DEAD_BINDER = 1U << 4,
+ BINDER_DEBUG_DEATH_NOTIFICATION = 1U << 5,
+ BINDER_DEBUG_READ_WRITE = 1U << 6,
+ BINDER_DEBUG_USER_REFS = 1U << 7,
+ BINDER_DEBUG_THREADS = 1U << 8,
+ BINDER_DEBUG_TRANSACTION = 1U << 9,
+ BINDER_DEBUG_TRANSACTION_COMPLETE = 1U << 10,
+ BINDER_DEBUG_FREE_BUFFER = 1U << 11,
+ BINDER_DEBUG_INTERNAL_REFS = 1U << 12,
+ BINDER_DEBUG_BUFFER_ALLOC = 1U << 13,
+ BINDER_DEBUG_PRIORITY_CAP = 1U << 14,
+ BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 15,
+};
+static uint32_t binder_debug_mask = BINDER_DEBUG_USER_ERROR;
+
+module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO);
+
+static int binder_debug_no_lock;
+module_param_named(proc_no_lock, binder_debug_no_lock, bool, S_IWUSR | S_IRUGO);
+
+static DECLARE_WAIT_QUEUE_HEAD(binder_user_error_wait);
+static int binder_stop_on_user_error;
+
+static int binder_set_stop_on_user_error(const char *val,
+ struct kernel_param *kp)
+{
+ int ret;
+ ret = param_set_int(val, kp);
+ if (binder_stop_on_user_error < 2)
+ wake_up(&binder_user_error_wait);
+ return ret;
+}
+module_param_call(stop_on_user_error, binder_set_stop_on_user_error,
+ param_get_int, &binder_stop_on_user_error, S_IWUSR | S_IRUGO);
+
+#define binder_debug(mask, x...) \
+ do { \
+ if (binder_debug_mask & mask) \
+ printk(KERN_INFO x); \
+ } while (0)
+
+#define binder_user_error(x...) \
+ do { \
+ if (binder_debug_mask & BINDER_DEBUG_USER_ERROR) \
+ printk(KERN_INFO x); \
+ if (binder_stop_on_user_error) \
+ binder_stop_on_user_error = 2; \
+ } while (0)
+
+enum binder_stat_types {
+ BINDER_STAT_PROC,
+ BINDER_STAT_THREAD,
+ BINDER_STAT_NODE,
+ BINDER_STAT_REF,
+ BINDER_STAT_DEATH,
+ BINDER_STAT_TRANSACTION,
+ BINDER_STAT_TRANSACTION_COMPLETE,
+ BINDER_STAT_COUNT
+};
+
+struct binder_stats {
+ int br[_IOC_NR(BR_FAILED_REPLY) + 1];
+ int bc[_IOC_NR(BC_DEAD_BINDER_DONE) + 1];
+ int obj_created[BINDER_STAT_COUNT];
+ int obj_deleted[BINDER_STAT_COUNT];
+};
+
+static struct binder_stats binder_stats;
+
+static inline void binder_stats_deleted(enum binder_stat_types type)
+{
+ binder_stats.obj_deleted[type]++;
+}
+
+static inline void binder_stats_created(enum binder_stat_types type)
+{
+ binder_stats.obj_created[type]++;
+}
+
+struct binder_transaction_log_entry {
+ int debug_id;
+ int call_type;
+ int from_proc;
+ int from_thread;
+ int target_handle;
+ int to_proc;
+ int to_thread;
+ int to_node;
+ int data_size;
+ int offsets_size;
+};
+struct binder_transaction_log {
+ int next;
+ int full;
+ struct binder_transaction_log_entry entry[32];
+};
+static struct binder_transaction_log binder_transaction_log;
+static struct binder_transaction_log binder_transaction_log_failed;
+
+static struct binder_transaction_log_entry *binder_transaction_log_add(
+ struct binder_transaction_log *log)
+{
+ struct binder_transaction_log_entry *e;
+ e = &log->entry[log->next];
+ memset(e, 0, sizeof(*e));
+ log->next++;
+ if (log->next == ARRAY_SIZE(log->entry)) {
+ log->next = 0;
+ log->full = 1;
+ }
+ return e;
+}
+
+struct binder_work {
+ struct list_head entry;
+ enum {
+ BINDER_WORK_TRANSACTION = 1,
+ BINDER_WORK_TRANSACTION_COMPLETE,
+ BINDER_WORK_NODE,
+ BINDER_WORK_DEAD_BINDER,
+ BINDER_WORK_DEAD_BINDER_AND_CLEAR,
+ BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
+ } type;
+};
+
+struct binder_node {
+ int debug_id;
+ struct binder_work work;
+ union {
+ struct rb_node rb_node;
+ struct hlist_node dead_node;
+ };
+ struct binder_proc *proc;
+ struct hlist_head refs;
+ int internal_strong_refs;
+ int local_weak_refs;
+ int local_strong_refs;
+ void __user *ptr;
+ void __user *cookie;
+ unsigned has_strong_ref:1;
+ unsigned pending_strong_ref:1;
+ unsigned has_weak_ref:1;
+ unsigned pending_weak_ref:1;
+ unsigned has_async_transaction:1;
+ unsigned accept_fds:1;
+ unsigned min_priority:8;
+ struct list_head async_todo;
+};
+
+struct binder_ref_death {
+ struct binder_work work;
+ void __user *cookie;
+};
+
+struct binder_ref {
+ /* Lookups needed: */
+ /* node + proc => ref (transaction) */
+ /* desc + proc => ref (transaction, inc/dec ref) */
+ /* node => refs + procs (proc exit) */
+ int debug_id;
+ struct rb_node rb_node_desc;
+ struct rb_node rb_node_node;
+ struct hlist_node node_entry;
+ struct binder_proc *proc;
+ struct binder_node *node;
+ uint32_t desc;
+ int strong;
+ int weak;
+ struct binder_ref_death *death;
+};
+
+struct binder_buffer {
+ struct list_head entry; /* free and allocated entries by addesss */
+ struct rb_node rb_node; /* free entry by size or allocated entry */
+ /* by address */
+ unsigned free:1;
+ unsigned allow_user_free:1;
+ unsigned async_transaction:1;
+ unsigned debug_id:29;
+
+ struct binder_transaction *transaction;
+
+ struct binder_node *target_node;
+ size_t data_size;
+ size_t offsets_size;
+ uint8_t data[0];
+};
+
+enum binder_deferred_state {
+ BINDER_DEFERRED_PUT_FILES = 0x01,
+ BINDER_DEFERRED_FLUSH = 0x02,
+ BINDER_DEFERRED_RELEASE = 0x04,
+};
+
+struct binder_proc {
+ struct hlist_node proc_node;
+ struct rb_root threads;
+ struct rb_root nodes;
+ struct rb_root refs_by_desc;
+ struct rb_root refs_by_node;
+ int pid;
+ struct vm_area_struct *vma;
+ struct task_struct *tsk;
+ struct files_struct *files;
+ struct hlist_node deferred_work_node;
+ int deferred_work;
+ void *buffer;
+ ptrdiff_t user_buffer_offset;
+
+ struct list_head buffers;
+ struct rb_root free_buffers;
+ struct rb_root allocated_buffers;
+ size_t free_async_space;
+
+ struct page **pages;
+ size_t buffer_size;
+ uint32_t buffer_free;
+ struct list_head todo;
+ wait_queue_head_t wait;
+ struct binder_stats stats;
+ struct list_head delivered_death;
+ int max_threads;
+ int requested_threads;
+ int requested_threads_started;
+ int ready_threads;
+ long default_priority;
+ struct dentry *debugfs_entry;
+};
+
+enum {
+ BINDER_LOOPER_STATE_REGISTERED = 0x01,
+ BINDER_LOOPER_STATE_ENTERED = 0x02,
+ BINDER_LOOPER_STATE_EXITED = 0x04,
+ BINDER_LOOPER_STATE_INVALID = 0x08,
+ BINDER_LOOPER_STATE_WAITING = 0x10,
+ BINDER_LOOPER_STATE_NEED_RETURN = 0x20
+};
+
+struct binder_thread {
+ struct binder_proc *proc;
+ struct rb_node rb_node;
+ int pid;
+ int looper;
+ struct binder_transaction *transaction_stack;
+ struct list_head todo;
+ uint32_t return_error; /* Write failed, return error code in read buf */
+ uint32_t return_error2; /* Write failed, return error code in read */
+ /* buffer. Used when sending a reply to a dead process that */
+ /* we are also waiting on */
+ wait_queue_head_t wait;
+ struct binder_stats stats;
+};
+
+struct binder_transaction {
+ int debug_id;
+ struct binder_work work;
+ struct binder_thread *from;
+ struct binder_transaction *from_parent;
+ struct binder_proc *to_proc;
+ struct binder_thread *to_thread;
+ struct binder_transaction *to_parent;
+ unsigned need_reply:1;
+ /* unsigned is_dead:1; */ /* not used at the moment */
+
+ struct binder_buffer *buffer;
+ unsigned int code;
+ unsigned int flags;
+ long priority;
+ long saved_priority;
+ uid_t sender_euid;
+};
+
+static void
+binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer);
+
+/*
+ * copied from get_unused_fd_flags
+ */
+int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
+{
+ struct files_struct *files = proc->files;
+ int fd, error;
+ struct fdtable *fdt;
+ unsigned long rlim_cur;
+ unsigned long irqs;
+
+ if (files == NULL)
+ return -ESRCH;
+
+ error = -EMFILE;
+ spin_lock(&files->file_lock);
+
+repeat:
+ fdt = files_fdtable(files);
+ fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds,
+ files->next_fd);
+
+ /*
+ * N.B. For clone tasks sharing a files structure, this test
+ * will limit the total number of files that can be opened.
+ */
+ rlim_cur = 0;
+ if (lock_task_sighand(proc->tsk, &irqs)) {
+ rlim_cur = proc->tsk->signal->rlim[RLIMIT_NOFILE].rlim_cur;
+ unlock_task_sighand(proc->tsk, &irqs);
+ }
+ if (fd >= rlim_cur)
+ goto out;
+
+ /* Do we need to expand the fd array or fd set? */
+ error = expand_files(files, fd);
+ if (error < 0)
+ goto out;
+
+ if (error) {
+ /*
+ * If we needed to expand the fs array we
+ * might have blocked - try again.
+ */
+ error = -EMFILE;
+ goto repeat;
+ }
+
+ FD_SET(fd, fdt->open_fds);
+ if (flags & O_CLOEXEC)
+ FD_SET(fd, fdt->close_on_exec);
+ else
+ FD_CLR(fd, fdt->close_on_exec);
+ files->next_fd = fd + 1;
+#if 1
+ /* Sanity check */
+ if (fdt->fd[fd] != NULL) {
+ printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd);
+ fdt->fd[fd] = NULL;
+ }
+#endif
+ error = fd;
+
+out:
+ spin_unlock(&files->file_lock);
+ return error;
+}
+
+/*
+ * copied from fd_install
+ */
+static void task_fd_install(
+ struct binder_proc *proc, unsigned int fd, struct file *file)
+{
+ struct files_struct *files = proc->files;
+ struct fdtable *fdt;
+
+ if (files == NULL)
+ return;
+
+ spin_lock(&files->file_lock);
+ fdt = files_fdtable(files);
+ BUG_ON(fdt->fd[fd] != NULL);
+ rcu_assign_pointer(fdt->fd[fd], file);
+ spin_unlock(&files->file_lock);
+}
+
+/*
+ * copied from __put_unused_fd in open.c
+ */
+static void __put_unused_fd(struct files_struct *files, unsigned int fd)
+{
+ struct fdtable *fdt = files_fdtable(files);
+ __FD_CLR(fd, fdt->open_fds);
+ if (fd < files->next_fd)
+ files->next_fd = fd;
+}
+
+/*
+ * copied from sys_close
+ */
+static long task_close_fd(struct binder_proc *proc, unsigned int fd)
+{
+ struct file *filp;
+ struct files_struct *files = proc->files;
+ struct fdtable *fdt;
+ int retval;
+
+ if (files == NULL)
+ return -ESRCH;
+
+ spin_lock(&files->file_lock);
+ fdt = files_fdtable(files);
+ if (fd >= fdt->max_fds)
+ goto out_unlock;
+ filp = fdt->fd[fd];
+ if (!filp)
+ goto out_unlock;
+ rcu_assign_pointer(fdt->fd[fd], NULL);
+ FD_CLR(fd, fdt->close_on_exec);
+ __put_unused_fd(files, fd);
+ spin_unlock(&files->file_lock);
+ retval = filp_close(filp, files);
+
+ /* can't restart close syscall because file table entry was cleared */
+ if (unlikely(retval == -ERESTARTSYS ||
+ retval == -ERESTARTNOINTR ||
+ retval == -ERESTARTNOHAND ||
+ retval == -ERESTART_RESTARTBLOCK))
+ retval = -EINTR;
+
+ return retval;
+
+out_unlock:
+ spin_unlock(&files->file_lock);
+ return -EBADF;
+}
+
+static void binder_set_nice(long nice)
+{
+ long min_nice;
+ if (can_nice(current, nice)) {
+ set_user_nice(current, nice);
+ return;
+ }
+ min_nice = 20 - current->signal->rlim[RLIMIT_NICE].rlim_cur;
+ binder_debug(BINDER_DEBUG_PRIORITY_CAP,
+ "binder: %d: nice value %ld not allowed use "
+ "%ld instead\n", current->pid, nice, min_nice);
+ set_user_nice(current, min_nice);
+ if (min_nice < 20)
+ return;
+ binder_user_error("binder: %d RLIMIT_NICE not set\n", current->pid);
+}
+
+static size_t binder_buffer_size(struct binder_proc *proc,
+ struct binder_buffer *buffer)
+{
+ if (list_is_last(&buffer->entry, &proc->buffers))
+ return proc->buffer + proc->buffer_size - (void *)buffer->data;
+ else
+ return (size_t)list_entry(buffer->entry.next,
+ struct binder_buffer, entry) - (size_t)buffer->data;
+}
+
+static void binder_insert_free_buffer(struct binder_proc *proc,
+ struct binder_buffer *new_buffer)
+{
+ struct rb_node **p = &proc->free_buffers.rb_node;
+ struct rb_node *parent = NULL;
+ struct binder_buffer *buffer;
+ size_t buffer_size;
+ size_t new_buffer_size;
+
+ BUG_ON(!new_buffer->free);
+
+ new_buffer_size = binder_buffer_size(proc, new_buffer);
+
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "binder: %d: add free buffer, size %zd, "
+ "at %p\n", proc->pid, new_buffer_size, new_buffer);
+
+ while (*p) {
+ parent = *p;
+ buffer = rb_entry(parent, struct binder_buffer, rb_node);
+ BUG_ON(!buffer->free);
+
+ buffer_size = binder_buffer_size(proc, buffer);
+
+ if (new_buffer_size < buffer_size)
+ p = &parent->rb_left;
+ else
+ p = &parent->rb_right;
+ }
+ rb_link_node(&new_buffer->rb_node, parent, p);
+ rb_insert_color(&new_buffer->rb_node, &proc->free_buffers);
+}
+
+static void binder_insert_allocated_buffer(struct binder_proc *proc,
+ struct binder_buffer *new_buffer)
+{
+ struct rb_node **p = &proc->allocated_buffers.rb_node;
+ struct rb_node *parent = NULL;
+ struct binder_buffer *buffer;
+
+ BUG_ON(new_buffer->free);
+
+ while (*p) {
+ parent = *p;
+ buffer = rb_entry(parent, struct binder_buffer, rb_node);
+ BUG_ON(buffer->free);
+
+ if (new_buffer < buffer)
+ p = &parent->rb_left;
+ else if (new_buffer > buffer)
+ p = &parent->rb_right;
+ else
+ BUG();
+ }
+ rb_link_node(&new_buffer->rb_node, parent, p);
+ rb_insert_color(&new_buffer->rb_node, &proc->allocated_buffers);
+}
+
+static struct binder_buffer *binder_buffer_lookup(struct binder_proc *proc,
+ void __user *user_ptr)
+{
+ struct rb_node *n = proc->allocated_buffers.rb_node;
+ struct binder_buffer *buffer;
+ struct binder_buffer *kern_ptr;
+
+ kern_ptr = user_ptr - proc->user_buffer_offset
+ - offsetof(struct binder_buffer, data);
+
+ while (n) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ BUG_ON(buffer->free);
+
+ if (kern_ptr < buffer)
+ n = n->rb_left;
+ else if (kern_ptr > buffer)
+ n = n->rb_right;
+ else
+ return buffer;
+ }
+ return NULL;
+}
+
+static int binder_update_page_range(struct binder_proc *proc, int allocate,
+ void *start, void *end,
+ struct vm_area_struct *vma)
+{
+ void *page_addr;
+ unsigned long user_page_addr;
+ struct vm_struct tmp_area;
+ struct page **page;
+ struct mm_struct *mm;
+
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "binder: %d: %s pages %p-%p\n", proc->pid,
+ allocate ? "allocate" : "free", start, end);
+
+ if (end <= start)
+ return 0;
+
+ if (vma)
+ mm = NULL;
+ else
+ mm = get_task_mm(proc->tsk);
+
+ if (mm) {
+ down_write(&mm->mmap_sem);
+ vma = proc->vma;
+ }
+
+ if (allocate == 0)
+ goto free_range;
+
+ if (vma == NULL) {
+ printk(KERN_ERR "binder: %d: binder_alloc_buf failed to "
+ "map pages in userspace, no vma\n", proc->pid);
+ goto err_no_vma;
+ }
+
+ for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
+ int ret;
+ struct page **page_array_ptr;
+ page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
+
+ BUG_ON(*page);
+ *page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (*page == NULL) {
+ printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
+ "for page at %p\n", proc->pid, page_addr);
+ goto err_alloc_page_failed;
+ }
+ tmp_area.addr = page_addr;
+ tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
+ page_array_ptr = page;
+ ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);
+ if (ret) {
+ printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
+ "to map page at %p in kernel\n",
+ proc->pid, page_addr);
+ goto err_map_kernel_failed;
+ }
+ user_page_addr =
+ (uintptr_t)page_addr + proc->user_buffer_offset;
+ ret = vm_insert_page(vma, user_page_addr, page[0]);
+ if (ret) {
+ printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
+ "to map page at %lx in userspace\n",
+ proc->pid, user_page_addr);
+ goto err_vm_insert_page_failed;
+ }
+ /* vm_insert_page does not seem to increment the refcount */
+ }
+ if (mm) {
+ up_write(&mm->mmap_sem);
+ mmput(mm);
+ }
+ return 0;
+
+free_range:
+ for (page_addr = end - PAGE_SIZE; page_addr >= start;
+ page_addr -= PAGE_SIZE) {
+ page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
+ if (vma)
+ zap_page_range(vma, (uintptr_t)page_addr +
+ proc->user_buffer_offset, PAGE_SIZE, NULL);
+err_vm_insert_page_failed:
+ unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
+err_map_kernel_failed:
+ __free_page(*page);
+ *page = NULL;
+err_alloc_page_failed:
+ ;
+ }
+err_no_vma:
+ if (mm) {
+ up_write(&mm->mmap_sem);
+ mmput(mm);
+ }
+ return -ENOMEM;
+}
+
+static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc,
+ size_t data_size,
+ size_t offsets_size, int is_async)
+{
+ struct rb_node *n = proc->free_buffers.rb_node;
+ struct binder_buffer *buffer;
+ size_t buffer_size;
+ struct rb_node *best_fit = NULL;
+ void *has_page_addr;
+ void *end_page_addr;
+ size_t size;
+
+ if (proc->vma == NULL) {
+ printk(KERN_ERR "binder: %d: binder_alloc_buf, no vma\n",
+ proc->pid);
+ return NULL;
+ }
+
+ size = ALIGN(data_size, sizeof(void *)) +
+ ALIGN(offsets_size, sizeof(void *));
+
+ if (size < data_size || size < offsets_size) {
+ binder_user_error("binder: %d: got transaction with invalid "
+ "size %zd-%zd\n", proc->pid, data_size, offsets_size);
+ return NULL;
+ }
+
+ if (is_async &&
+ proc->free_async_space < size + sizeof(struct binder_buffer)) {
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "binder: %d: binder_alloc_buf size %zd"
+ "failed, no async space left\n", proc->pid, size);
+ return NULL;
+ }
+
+ while (n) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ BUG_ON(!buffer->free);
+ buffer_size = binder_buffer_size(proc, buffer);
+
+ if (size < buffer_size) {
+ best_fit = n;
+ n = n->rb_left;
+ } else if (size > buffer_size)
+ n = n->rb_right;
+ else {
+ best_fit = n;
+ break;
+ }
+ }
+ if (best_fit == NULL) {
+ printk(KERN_ERR "binder: %d: binder_alloc_buf size %zd failed, "
+ "no address space\n", proc->pid, size);
+ return NULL;
+ }
+ if (n == NULL) {
+ buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
+ buffer_size = binder_buffer_size(proc, buffer);
+ }
+
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "binder: %d: binder_alloc_buf size %zd got buff"
+ "er %p size %zd\n", proc->pid, size, buffer, buffer_size);
+
+ has_page_addr =
+ (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK);
+ if (n == NULL) {
+ if (size + sizeof(struct binder_buffer) + 4 >= buffer_size)
+ buffer_size = size; /* no room for other buffers */
+ else
+ buffer_size = size + sizeof(struct binder_buffer);
+ }
+ end_page_addr =
+ (void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size);
+ if (end_page_addr > has_page_addr)
+ end_page_addr = has_page_addr;
+ if (binder_update_page_range(proc, 1,
+ (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL))
+ return NULL;
+
+ rb_erase(best_fit, &proc->free_buffers);
+ buffer->free = 0;
+ binder_insert_allocated_buffer(proc, buffer);
+ if (buffer_size != size) {
+ struct binder_buffer *new_buffer = (void *)buffer->data + size;
+ list_add(&new_buffer->entry, &buffer->entry);
+ new_buffer->free = 1;
+ binder_insert_free_buffer(proc, new_buffer);
+ }
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "binder: %d: binder_alloc_buf size %zd got "
+ "%p\n", proc->pid, size, buffer);
+ buffer->data_size = data_size;
+ buffer->offsets_size = offsets_size;
+ buffer->async_transaction = is_async;
+ if (is_async) {
+ proc->free_async_space -= size + sizeof(struct binder_buffer);
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
+ "binder: %d: binder_alloc_buf size %zd "
+ "async free %zd\n", proc->pid, size,
+ proc->free_async_space);
+ }
+
+ return buffer;
+}
+
+static void *buffer_start_page(struct binder_buffer *buffer)
+{
+ return (void *)((uintptr_t)buffer & PAGE_MASK);
+}
+
+static void *buffer_end_page(struct binder_buffer *buffer)
+{
+ return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK);
+}
+
+static void binder_delete_free_buffer(struct binder_proc *proc,
+ struct binder_buffer *buffer)
+{
+ struct binder_buffer *prev, *next = NULL;
+ int free_page_end = 1;
+ int free_page_start = 1;
+
+ BUG_ON(proc->buffers.next == &buffer->entry);
+ prev = list_entry(buffer->entry.prev, struct binder_buffer, entry);
+ BUG_ON(!prev->free);
+ if (buffer_end_page(prev) == buffer_start_page(buffer)) {
+ free_page_start = 0;
+ if (buffer_end_page(prev) == buffer_end_page(buffer))
+ free_page_end = 0;
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "binder: %d: merge free, buffer %p "
+ "share page with %p\n", proc->pid, buffer, prev);
+ }
+
+ if (!list_is_last(&buffer->entry, &proc->buffers)) {
+ next = list_entry(buffer->entry.next,
+ struct binder_buffer, entry);
+ if (buffer_start_page(next) == buffer_end_page(buffer)) {
+ free_page_end = 0;
+ if (buffer_start_page(next) ==
+ buffer_start_page(buffer))
+ free_page_start = 0;
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "binder: %d: merge free, buffer"
+ " %p share page with %p\n", proc->pid,
+ buffer, prev);
+ }
+ }
+ list_del(&buffer->entry);
+ if (free_page_start || free_page_end) {
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "binder: %d: merge free, buffer %p do "
+ "not share page%s%s with with %p or %p\n",
+ proc->pid, buffer, free_page_start ? "" : " end",
+ free_page_end ? "" : " start", prev, next);
+ binder_update_page_range(proc, 0, free_page_start ?
+ buffer_start_page(buffer) : buffer_end_page(buffer),
+ (free_page_end ? buffer_end_page(buffer) :
+ buffer_start_page(buffer)) + PAGE_SIZE, NULL);
+ }
+}
+
+static void binder_free_buf(struct binder_proc *proc,
+ struct binder_buffer *buffer)
+{
+ size_t size, buffer_size;
+
+ buffer_size = binder_buffer_size(proc, buffer);
+
+ size = ALIGN(buffer->data_size, sizeof(void *)) +
+ ALIGN(buffer->offsets_size, sizeof(void *));
+
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "binder: %d: binder_free_buf %p size %zd buffer"
+ "_size %zd\n", proc->pid, buffer, size, buffer_size);
+
+ BUG_ON(buffer->free);
+ BUG_ON(size > buffer_size);
+ BUG_ON(buffer->transaction != NULL);
+ BUG_ON((void *)buffer < proc->buffer);
+ BUG_ON((void *)buffer > proc->buffer + proc->buffer_size);
+
+ if (buffer->async_transaction) {
+ proc->free_async_space += size + sizeof(struct binder_buffer);
+
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
+ "binder: %d: binder_free_buf size %zd "
+ "async free %zd\n", proc->pid, size,
+ proc->free_async_space);
+ }
+
+ binder_update_page_range(proc, 0,
+ (void *)PAGE_ALIGN((uintptr_t)buffer->data),
+ (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK),
+ NULL);
+ rb_erase(&buffer->rb_node, &proc->allocated_buffers);
+ buffer->free = 1;
+ if (!list_is_last(&buffer->entry, &proc->buffers)) {
+ struct binder_buffer *next = list_entry(buffer->entry.next,
+ struct binder_buffer, entry);
+ if (next->free) {
+ rb_erase(&next->rb_node, &proc->free_buffers);
+ binder_delete_free_buffer(proc, next);
+ }
+ }
+ if (proc->buffers.next != &buffer->entry) {
+ struct binder_buffer *prev = list_entry(buffer->entry.prev,
+ struct binder_buffer, entry);
+ if (prev->free) {
+ binder_delete_free_buffer(proc, buffer);
+ rb_erase(&prev->rb_node, &proc->free_buffers);
+ buffer = prev;
+ }
+ }
+ binder_insert_free_buffer(proc, buffer);
+}
+
+static struct binder_node *binder_get_node(struct binder_proc *proc,
+ void __user *ptr)
+{
+ struct rb_node *n = proc->nodes.rb_node;
+ struct binder_node *node;
+
+ while (n) {
+ node = rb_entry(n, struct binder_node, rb_node);
+
+ if (ptr < node->ptr)
+ n = n->rb_left;
+ else if (ptr > node->ptr)
+ n = n->rb_right;
+ else
+ return node;
+ }
+ return NULL;
+}
+
+static struct binder_node *binder_new_node(struct binder_proc *proc,
+ void __user *ptr,
+ void __user *cookie)
+{
+ struct rb_node **p = &proc->nodes.rb_node;
+ struct rb_node *parent = NULL;
+ struct binder_node *node;
+
+ while (*p) {
+ parent = *p;
+ node = rb_entry(parent, struct binder_node, rb_node);
+
+ if (ptr < node->ptr)
+ p = &(*p)->rb_left;
+ else if (ptr > node->ptr)
+ p = &(*p)->rb_right;
+ else
+ return NULL;
+ }
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (node == NULL)
+ return NULL;
+ binder_stats_created(BINDER_STAT_NODE);
+ rb_link_node(&node->rb_node, parent, p);
+ rb_insert_color(&node->rb_node, &proc->nodes);
+ node->debug_id = ++binder_last_id;
+ node->proc = proc;
+ node->ptr = ptr;
+ node->cookie = cookie;
+ node->work.type = BINDER_WORK_NODE;
+ INIT_LIST_HEAD(&node->work.entry);
+ INIT_LIST_HEAD(&node->async_todo);
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "binder: %d:%d node %d u%p c%p created\n",
+ proc->pid, current->pid, node->debug_id,
+ node->ptr, node->cookie);
+ return node;
+}
+
+static int binder_inc_node(struct binder_node *node, int strong, int internal,
+ struct list_head *target_list)
+{
+ if (strong) {
+ if (internal) {
+ if (target_list == NULL &&
+ node->internal_strong_refs == 0 &&
+ !(node == binder_context_mgr_node &&
+ node->has_strong_ref)) {
+ printk(KERN_ERR "binder: invalid inc strong "
+ "node for %d\n", node->debug_id);
+ return -EINVAL;
+ }
+ node->internal_strong_refs++;
+ } else
+ node->local_strong_refs++;
+ if (!node->has_strong_ref && target_list) {
+ list_del_init(&node->work.entry);
+ list_add_tail(&node->work.entry, target_list);
+ }
+ } else {
+ if (!internal)
+ node->local_weak_refs++;
+ if (!node->has_weak_ref && list_empty(&node->work.entry)) {
+ if (target_list == NULL) {
+ printk(KERN_ERR "binder: invalid inc weak node "
+ "for %d\n", node->debug_id);
+ return -EINVAL;
+ }
+ list_add_tail(&node->work.entry, target_list);
+ }
+ }
+ return 0;
+}
+
+static int binder_dec_node(struct binder_node *node, int strong, int internal)
+{
+ if (strong) {
+ if (internal)
+ node->internal_strong_refs--;
+ else
+ node->local_strong_refs--;
+ if (node->local_strong_refs || node->internal_strong_refs)
+ return 0;
+ } else {
+ if (!internal)
+ node->local_weak_refs--;
+ if (node->local_weak_refs || !hlist_empty(&node->refs))
+ return 0;
+ }
+ if (node->proc && (node->has_strong_ref || node->has_weak_ref)) {
+ if (list_empty(&node->work.entry)) {
+ list_add_tail(&node->work.entry, &node->proc->todo);
+ wake_up_interruptible(&node->proc->wait);
+ }
+ } else {
+ if (hlist_empty(&node->refs) && !node->local_strong_refs &&
+ !node->local_weak_refs) {
+ list_del_init(&node->work.entry);
+ if (node->proc) {
+ rb_erase(&node->rb_node, &node->proc->nodes);
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "binder: refless node %d deleted\n",
+ node->debug_id);
+ } else {
+ hlist_del(&node->dead_node);
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "binder: dead node %d deleted\n",
+ node->debug_id);
+ }
+ kfree(node);
+ binder_stats_deleted(BINDER_STAT_NODE);
+ }
+ }
+
+ return 0;
+}
+
+
+static struct binder_ref *binder_get_ref(struct binder_proc *proc,
+ uint32_t desc)
+{
+ struct rb_node *n = proc->refs_by_desc.rb_node;
+ struct binder_ref *ref;
+
+ while (n) {
+ ref = rb_entry(n, struct binder_ref, rb_node_desc);
+
+ if (desc < ref->desc)
+ n = n->rb_left;
+ else if (desc > ref->desc)
+ n = n->rb_right;
+ else
+ return ref;
+ }
+ return NULL;
+}
+
+static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
+ struct binder_node *node)
+{
+ struct rb_node *n;
+ struct rb_node **p = &proc->refs_by_node.rb_node;
+ struct rb_node *parent = NULL;
+ struct binder_ref *ref, *new_ref;
+
+ while (*p) {
+ parent = *p;
+ ref = rb_entry(parent, struct binder_ref, rb_node_node);
+
+ if (node < ref->node)
+ p = &(*p)->rb_left;
+ else if (node > ref->node)
+ p = &(*p)->rb_right;
+ else
+ return ref;
+ }
+ new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+ if (new_ref == NULL)
+ return NULL;
+ binder_stats_created(BINDER_STAT_REF);
+ new_ref->debug_id = ++binder_last_id;
+ new_ref->proc = proc;
+ new_ref->node = node;
+ rb_link_node(&new_ref->rb_node_node, parent, p);
+ rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
+
+ new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1;
+ for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
+ ref = rb_entry(n, struct binder_ref, rb_node_desc);
+ if (ref->desc > new_ref->desc)
+ break;
+ new_ref->desc = ref->desc + 1;
+ }
+
+ p = &proc->refs_by_desc.rb_node;
+ while (*p) {
+ parent = *p;
+ ref = rb_entry(parent, struct binder_ref, rb_node_desc);
+
+ if (new_ref->desc < ref->desc)
+ p = &(*p)->rb_left;
+ else if (new_ref->desc > ref->desc)
+ p = &(*p)->rb_right;
+ else
+ BUG();
+ }
+ rb_link_node(&new_ref->rb_node_desc, parent, p);
+ rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);
+ if (node) {
+ hlist_add_head(&new_ref->node_entry, &node->refs);
+
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "binder: %d new ref %d desc %d for "
+ "node %d\n", proc->pid, new_ref->debug_id,
+ new_ref->desc, node->debug_id);
+ } else {
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "binder: %d new ref %d desc %d for "
+ "dead node\n", proc->pid, new_ref->debug_id,
+ new_ref->desc);
+ }
+ return new_ref;
+}
+
+static void binder_delete_ref(struct binder_ref *ref)
+{
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "binder: %d delete ref %d desc %d for "
+ "node %d\n", ref->proc->pid, ref->debug_id,
+ ref->desc, ref->node->debug_id);
+
+ rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
+ rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
+ if (ref->strong)
+ binder_dec_node(ref->node, 1, 1);
+ hlist_del(&ref->node_entry);
+ binder_dec_node(ref->node, 0, 1);
+ if (ref->death) {
+ binder_debug(BINDER_DEBUG_DEAD_BINDER,
+ "binder: %d delete ref %d desc %d "
+ "has death notification\n", ref->proc->pid,
+ ref->debug_id, ref->desc);
+ list_del(&ref->death->work.entry);
+ kfree(ref->death);
+ binder_stats_deleted(BINDER_STAT_DEATH);
+ }
+ kfree(ref);
+ binder_stats_deleted(BINDER_STAT_REF);
+}
+
+static int binder_inc_ref(struct binder_ref *ref, int strong,
+ struct list_head *target_list)
+{
+ int ret;
+ if (strong) {
+ if (ref->strong == 0) {
+ ret = binder_inc_node(ref->node, 1, 1, target_list);
+ if (ret)
+ return ret;
+ }
+ ref->strong++;
+ } else {
+ if (ref->weak == 0) {
+ ret = binder_inc_node(ref->node, 0, 1, target_list);
+ if (ret)
+ return ret;
+ }
+ ref->weak++;
+ }
+ return 0;
+}
+
+
+static int binder_dec_ref(struct binder_ref *ref, int strong)
+{
+ if (strong) {
+ if (ref->strong == 0) {
+ binder_user_error("binder: %d invalid dec strong, "
+ "ref %d desc %d s %d w %d\n",
+ ref->proc->pid, ref->debug_id,
+ ref->desc, ref->strong, ref->weak);
+ return -EINVAL;
+ }
+ ref->strong--;
+ if (ref->strong == 0) {
+ int ret;
+ ret = binder_dec_node(ref->node, strong, 1);
+ if (ret)
+ return ret;
+ }
+ } else {
+ if (ref->weak == 0) {
+ binder_user_error("binder: %d invalid dec weak, "
+ "ref %d desc %d s %d w %d\n",
+ ref->proc->pid, ref->debug_id,
+ ref->desc, ref->strong, ref->weak);
+ return -EINVAL;
+ }
+ ref->weak--;
+ }
+ if (ref->strong == 0 && ref->weak == 0)
+ binder_delete_ref(ref);
+ return 0;
+}
+
+static void binder_pop_transaction(struct binder_thread *target_thread,
+ struct binder_transaction *t)
+{
+ if (target_thread) {
+ BUG_ON(target_thread->transaction_stack != t);
+ BUG_ON(target_thread->transaction_stack->from != target_thread);
+ target_thread->transaction_stack =
+ target_thread->transaction_stack->from_parent;
+ t->from = NULL;
+ }
+ t->need_reply = 0;
+ if (t->buffer)
+ t->buffer->transaction = NULL;
+ kfree(t);
+ binder_stats_deleted(BINDER_STAT_TRANSACTION);
+}
+
+static void binder_send_failed_reply(struct binder_transaction *t,
+ uint32_t error_code)
+{
+ struct binder_thread *target_thread;
+ BUG_ON(t->flags & TF_ONE_WAY);
+ while (1) {
+ target_thread = t->from;
+ if (target_thread) {
+ if (target_thread->return_error != BR_OK &&
+ target_thread->return_error2 == BR_OK) {
+ target_thread->return_error2 =
+ target_thread->return_error;
+ target_thread->return_error = BR_OK;
+ }
+ if (target_thread->return_error == BR_OK) {
+ binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
+ "binder: send failed reply for "
+ "transaction %d to %d:%d\n",
+ t->debug_id, target_thread->proc->pid,
+ target_thread->pid);
+
+ binder_pop_transaction(target_thread, t);
+ target_thread->return_error = error_code;
+ wake_up_interruptible(&target_thread->wait);
+ } else {
+ printk(KERN_ERR "binder: reply failed, target "
+ "thread, %d:%d, has error code %d "
+ "already\n", target_thread->proc->pid,
+ target_thread->pid,
+ target_thread->return_error);
+ }
+ return;
+ } else {
+ struct binder_transaction *next = t->from_parent;
+
+ binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
+ "binder: send failed reply "
+ "for transaction %d, target dead\n",
+ t->debug_id);
+
+ binder_pop_transaction(target_thread, t);
+ if (next == NULL) {
+ binder_debug(BINDER_DEBUG_DEAD_BINDER,
+ "binder: reply failed,"
+ " no target thread at root\n");
+ return;
+ }
+ t = next;
+ binder_debug(BINDER_DEBUG_DEAD_BINDER,
+ "binder: reply failed, no target "
+ "thread -- retry %d\n", t->debug_id);
+ }
+ }
+}
+
+static void binder_transaction_buffer_release(struct binder_proc *proc,
+ struct binder_buffer *buffer,
+ size_t *failed_at)
+{
+ size_t *offp, *off_end;
+ int debug_id = buffer->debug_id;
+
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ "binder: %d buffer release %d, size %zd-%zd, failed at %p\n",
+ proc->pid, buffer->debug_id,
+ buffer->data_size, buffer->offsets_size, failed_at);
+
+ if (buffer->target_node)
+ binder_dec_node(buffer->target_node, 1, 0);
+
+ offp = (size_t *)(buffer->data + ALIGN(buffer->data_size, sizeof(void *)));
+ if (failed_at)
+ off_end = failed_at;
+ else
+ off_end = (void *)offp + buffer->offsets_size;
+ for (; offp < off_end; offp++) {
+ struct flat_binder_object *fp;
+ if (*offp > buffer->data_size - sizeof(*fp) ||
+ buffer->data_size < sizeof(*fp) ||
+ !IS_ALIGNED(*offp, sizeof(void *))) {
+ printk(KERN_ERR "binder: transaction release %d bad"
+ "offset %zd, size %zd\n", debug_id,
+ *offp, buffer->data_size);
+ continue;
+ }
+ fp = (struct flat_binder_object *)(buffer->data + *offp);
+ switch (fp->type) {
+ case BINDER_TYPE_BINDER:
+ case BINDER_TYPE_WEAK_BINDER: {
+ struct binder_node *node = binder_get_node(proc, fp->binder);
+ if (node == NULL) {
+ printk(KERN_ERR "binder: transaction release %d"
+ " bad node %p\n", debug_id, fp->binder);
+ break;
+ }
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " node %d u%p\n",
+ node->debug_id, node->ptr);
+ binder_dec_node(node, fp->type == BINDER_TYPE_BINDER, 0);
+ } break;
+ case BINDER_TYPE_HANDLE:
+ case BINDER_TYPE_WEAK_HANDLE: {
+ struct binder_ref *ref = binder_get_ref(proc, fp->handle);
+ if (ref == NULL) {
+ printk(KERN_ERR "binder: transaction release %d"
+ " bad handle %ld\n", debug_id,
+ fp->handle);
+ break;
+ }
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " ref %d desc %d (node %d)\n",
+ ref->debug_id, ref->desc, ref->node->debug_id);
+ binder_dec_ref(ref, fp->type == BINDER_TYPE_HANDLE);
+ } break;
+
+ case BINDER_TYPE_FD:
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " fd %ld\n", fp->handle);
+ if (failed_at)
+ task_close_fd(proc, fp->handle);
+ break;
+
+ default:
+ printk(KERN_ERR "binder: transaction release %d bad "
+ "object type %lx\n", debug_id, fp->type);
+ break;
+ }
+ }
+}
+
+static void binder_transaction(struct binder_proc *proc,
+ struct binder_thread *thread,
+ struct binder_transaction_data *tr, int reply)
+{
+ struct binder_transaction *t;
+ struct binder_work *tcomplete;
+ size_t *offp, *off_end;
+ struct binder_proc *target_proc;
+ struct binder_thread *target_thread = NULL;
+ struct binder_node *target_node = NULL;
+ struct list_head *target_list;
+ wait_queue_head_t *target_wait;
+ struct binder_transaction *in_reply_to = NULL;
+ struct binder_transaction_log_entry *e;
+ uint32_t return_error;
+
+ e = binder_transaction_log_add(&binder_transaction_log);
+ e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
+ e->from_proc = proc->pid;
+ e->from_thread = thread->pid;
+ e->target_handle = tr->target.handle;
+ e->data_size = tr->data_size;
+ e->offsets_size = tr->offsets_size;
+
+ if (reply) {
+ in_reply_to = thread->transaction_stack;
+ if (in_reply_to == NULL) {
+ binder_user_error("binder: %d:%d got reply transaction "
+ "with no transaction stack\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_empty_call_stack;
+ }
+ binder_set_nice(in_reply_to->saved_priority);
+ if (in_reply_to->to_thread != thread) {
+ binder_user_error("binder: %d:%d got reply transaction "
+ "with bad transaction stack,"
+ " transaction %d has target %d:%d\n",
+ proc->pid, thread->pid, in_reply_to->debug_id,
+ in_reply_to->to_proc ?
+ in_reply_to->to_proc->pid : 0,
+ in_reply_to->to_thread ?
+ in_reply_to->to_thread->pid : 0);
+ return_error = BR_FAILED_REPLY;
+ in_reply_to = NULL;
+ goto err_bad_call_stack;
+ }
+ thread->transaction_stack = in_reply_to->to_parent;
+ target_thread = in_reply_to->from;
+ if (target_thread == NULL) {
+ return_error = BR_DEAD_REPLY;
+ goto err_dead_binder;
+ }
+ if (target_thread->transaction_stack != in_reply_to) {
+ binder_user_error("binder: %d:%d got reply transaction "
+ "with bad target transaction stack %d, "
+ "expected %d\n",
+ proc->pid, thread->pid,
+ target_thread->transaction_stack ?
+ target_thread->transaction_stack->debug_id : 0,
+ in_reply_to->debug_id);
+ return_error = BR_FAILED_REPLY;
+ in_reply_to = NULL;
+ target_thread = NULL;
+ goto err_dead_binder;
+ }
+ target_proc = target_thread->proc;
+ } else {
+ if (tr->target.handle) {
+ struct binder_ref *ref;
+ ref = binder_get_ref(proc, tr->target.handle);
+ if (ref == NULL) {
+ binder_user_error("binder: %d:%d got "
+ "transaction to invalid handle\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_invalid_target_handle;
+ }
+ target_node = ref->node;
+ } else {
+ target_node = binder_context_mgr_node;
+ if (target_node == NULL) {
+ return_error = BR_DEAD_REPLY;
+ goto err_no_context_mgr_node;
+ }
+ }
+ e->to_node = target_node->debug_id;
+ target_proc = target_node->proc;
+ if (target_proc == NULL) {
+ return_error = BR_DEAD_REPLY;
+ goto err_dead_binder;
+ }
+ if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
+ struct binder_transaction *tmp;
+ tmp = thread->transaction_stack;
+ if (tmp->to_thread != thread) {
+ binder_user_error("binder: %d:%d got new "
+ "transaction with bad transaction stack"
+ ", transaction %d has target %d:%d\n",
+ proc->pid, thread->pid, tmp->debug_id,
+ tmp->to_proc ? tmp->to_proc->pid : 0,
+ tmp->to_thread ?
+ tmp->to_thread->pid : 0);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_call_stack;
+ }
+ while (tmp) {
+ if (tmp->from && tmp->from->proc == target_proc)
+ target_thread = tmp->from;
+ tmp = tmp->from_parent;
+ }
+ }
+ }
+ if (target_thread) {
+ e->to_thread = target_thread->pid;
+ target_list = &target_thread->todo;
+ target_wait = &target_thread->wait;
+ } else {
+ target_list = &target_proc->todo;
+ target_wait = &target_proc->wait;
+ }
+ e->to_proc = target_proc->pid;
+
+ /* TODO: reuse incoming transaction for reply */
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL) {
+ return_error = BR_FAILED_REPLY;
+ goto err_alloc_t_failed;
+ }
+ binder_stats_created(BINDER_STAT_TRANSACTION);
+
+ tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
+ if (tcomplete == NULL) {
+ return_error = BR_FAILED_REPLY;
+ goto err_alloc_tcomplete_failed;
+ }
+ binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
+
+ t->debug_id = ++binder_last_id;
+ e->debug_id = t->debug_id;
+
+ if (reply)
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ "binder: %d:%d BC_REPLY %d -> %d:%d, "
+ "data %p-%p size %zd-%zd\n",
+ proc->pid, thread->pid, t->debug_id,
+ target_proc->pid, target_thread->pid,
+ tr->data.ptr.buffer, tr->data.ptr.offsets,
+ tr->data_size, tr->offsets_size);
+ else
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ "binder: %d:%d BC_TRANSACTION %d -> "
+ "%d - node %d, data %p-%p size %zd-%zd\n",
+ proc->pid, thread->pid, t->debug_id,
+ target_proc->pid, target_node->debug_id,
+ tr->data.ptr.buffer, tr->data.ptr.offsets,
+ tr->data_size, tr->offsets_size);
+
+ if (!reply && !(tr->flags & TF_ONE_WAY))
+ t->from = thread;
+ else
+ t->from = NULL;
+ t->sender_euid = proc->tsk->cred->euid;
+ t->to_proc = target_proc;
+ t->to_thread = target_thread;
+ t->code = tr->code;
+ t->flags = tr->flags;
+ t->priority = task_nice(current);
+ t->buffer = binder_alloc_buf(target_proc, tr->data_size,
+ tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
+ if (t->buffer == NULL) {
+ return_error = BR_FAILED_REPLY;
+ goto err_binder_alloc_buf_failed;
+ }
+ t->buffer->allow_user_free = 0;
+ t->buffer->debug_id = t->debug_id;
+ t->buffer->transaction = t;
+ t->buffer->target_node = target_node;
+ if (target_node)
+ binder_inc_node(target_node, 1, 0, NULL);
+
+ offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
+
+ if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
+ binder_user_error("binder: %d:%d got transaction with invalid "
+ "data ptr\n", proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_copy_data_failed;
+ }
+ if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
+ binder_user_error("binder: %d:%d got transaction with invalid "
+ "offsets ptr\n", proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_copy_data_failed;
+ }
+ if (!IS_ALIGNED(tr->offsets_size, sizeof(size_t))) {
+ binder_user_error("binder: %d:%d got transaction with "
+ "invalid offsets size, %zd\n",
+ proc->pid, thread->pid, tr->offsets_size);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_offset;
+ }
+ off_end = (void *)offp + tr->offsets_size;
+ for (; offp < off_end; offp++) {
+ struct flat_binder_object *fp;
+ if (*offp > t->buffer->data_size - sizeof(*fp) ||
+ t->buffer->data_size < sizeof(*fp) ||
+ !IS_ALIGNED(*offp, sizeof(void *))) {
+ binder_user_error("binder: %d:%d got transaction with "
+ "invalid offset, %zd\n",
+ proc->pid, thread->pid, *offp);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_offset;
+ }
+ fp = (struct flat_binder_object *)(t->buffer->data + *offp);
+ switch (fp->type) {
+ case BINDER_TYPE_BINDER:
+ case BINDER_TYPE_WEAK_BINDER: {
+ struct binder_ref *ref;
+ struct binder_node *node = binder_get_node(proc, fp->binder);
+ if (node == NULL) {
+ node = binder_new_node(proc, fp->binder, fp->cookie);
+ if (node == NULL) {
+ return_error = BR_FAILED_REPLY;
+ goto err_binder_new_node_failed;
+ }
+ node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
+ node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
+ }
+ if (fp->cookie != node->cookie) {
+ binder_user_error("binder: %d:%d sending u%p "
+ "node %d, cookie mismatch %p != %p\n",
+ proc->pid, thread->pid,
+ fp->binder, node->debug_id,
+ fp->cookie, node->cookie);
+ return_error = BR_FAILED_REPLY;
+ goto err_binder_get_ref_for_node_failed;
+ }
+ ref = binder_get_ref_for_node(target_proc, node);
+ if (ref == NULL) {
+ return_error = BR_FAILED_REPLY;
+ goto err_binder_get_ref_for_node_failed;
+ }
+ if (fp->type == BINDER_TYPE_BINDER)
+ fp->type = BINDER_TYPE_HANDLE;
+ else
+ fp->type = BINDER_TYPE_WEAK_HANDLE;
+ fp->handle = ref->desc;
+ binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
+ &thread->todo);
+
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " node %d u%p -> ref %d desc %d\n",
+ node->debug_id, node->ptr, ref->debug_id,
+ ref->desc);
+ } break;
+ case BINDER_TYPE_HANDLE:
+ case BINDER_TYPE_WEAK_HANDLE: {
+ struct binder_ref *ref = binder_get_ref(proc, fp->handle);
+ if (ref == NULL) {
+ binder_user_error("binder: %d:%d got "
+ "transaction with invalid "
+ "handle, %ld\n", proc->pid,
+ thread->pid, fp->handle);
+ return_error = BR_FAILED_REPLY;
+ goto err_binder_get_ref_failed;
+ }
+ if (ref->node->proc == target_proc) {
+ if (fp->type == BINDER_TYPE_HANDLE)
+ fp->type = BINDER_TYPE_BINDER;
+ else
+ fp->type = BINDER_TYPE_WEAK_BINDER;
+ fp->binder = ref->node->ptr;
+ fp->cookie = ref->node->cookie;
+ binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " ref %d desc %d -> node %d u%p\n",
+ ref->debug_id, ref->desc, ref->node->debug_id,
+ ref->node->ptr);
+ } else {
+ struct binder_ref *new_ref;
+ new_ref = binder_get_ref_for_node(target_proc, ref->node);
+ if (new_ref == NULL) {
+ return_error = BR_FAILED_REPLY;
+ goto err_binder_get_ref_for_node_failed;
+ }
+ fp->handle = new_ref->desc;
+ binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " ref %d desc %d -> ref %d desc %d (node %d)\n",
+ ref->debug_id, ref->desc, new_ref->debug_id,
+ new_ref->desc, ref->node->debug_id);
+ }
+ } break;
+
+ case BINDER_TYPE_FD: {
+ int target_fd;
+ struct file *file;
+
+ if (reply) {
+ if (!(in_reply_to->flags & TF_ACCEPT_FDS)) {
+ binder_user_error("binder: %d:%d got reply with fd, %ld, but target does not allow fds\n",
+ proc->pid, thread->pid, fp->handle);
+ return_error = BR_FAILED_REPLY;
+ goto err_fd_not_allowed;
+ }
+ } else if (!target_node->accept_fds) {
+ binder_user_error("binder: %d:%d got transaction with fd, %ld, but target does not allow fds\n",
+ proc->pid, thread->pid, fp->handle);
+ return_error = BR_FAILED_REPLY;
+ goto err_fd_not_allowed;
+ }
+
+ file = fget(fp->handle);
+ if (file == NULL) {
+ binder_user_error("binder: %d:%d got transaction with invalid fd, %ld\n",
+ proc->pid, thread->pid, fp->handle);
+ return_error = BR_FAILED_REPLY;
+ goto err_fget_failed;
+ }
+ target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
+ if (target_fd < 0) {
+ fput(file);
+ return_error = BR_FAILED_REPLY;
+ goto err_get_unused_fd_failed;
+ }
+ task_fd_install(target_proc, target_fd, file);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " fd %ld -> %d\n", fp->handle, target_fd);
+ /* TODO: fput? */
+ fp->handle = target_fd;
+ } break;
+
+ default:
+ binder_user_error("binder: %d:%d got transactio"
+ "n with invalid object type, %lx\n",
+ proc->pid, thread->pid, fp->type);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_object_type;
+ }
+ }
+ if (reply) {
+ BUG_ON(t->buffer->async_transaction != 0);
+ binder_pop_transaction(target_thread, in_reply_to);
+ } else if (!(t->flags & TF_ONE_WAY)) {
+ BUG_ON(t->buffer->async_transaction != 0);
+ t->need_reply = 1;
+ t->from_parent = thread->transaction_stack;
+ thread->transaction_stack = t;
+ } else {
+ BUG_ON(target_node == NULL);
+ BUG_ON(t->buffer->async_transaction != 1);
+ if (target_node->has_async_transaction) {
+ target_list = &target_node->async_todo;
+ target_wait = NULL;
+ } else
+ target_node->has_async_transaction = 1;
+ }
+ t->work.type = BINDER_WORK_TRANSACTION;
+ list_add_tail(&t->work.entry, target_list);
+ tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
+ list_add_tail(&tcomplete->entry, &thread->todo);
+ if (target_wait)
+ wake_up_interruptible(target_wait);
+ return;
+
+err_get_unused_fd_failed:
+err_fget_failed:
+err_fd_not_allowed:
+err_binder_get_ref_for_node_failed:
+err_binder_get_ref_failed:
+err_binder_new_node_failed:
+err_bad_object_type:
+err_bad_offset:
+err_copy_data_failed:
+ binder_transaction_buffer_release(target_proc, t->buffer, offp);
+ t->buffer->transaction = NULL;
+ binder_free_buf(target_proc, t->buffer);
+err_binder_alloc_buf_failed:
+ kfree(tcomplete);
+ binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
+err_alloc_tcomplete_failed:
+ kfree(t);
+ binder_stats_deleted(BINDER_STAT_TRANSACTION);
+err_alloc_t_failed:
+err_bad_call_stack:
+err_empty_call_stack:
+err_dead_binder:
+err_invalid_target_handle:
+err_no_context_mgr_node:
+ binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
+ "binder: %d:%d transaction failed %d, size %zd-%zd\n",
+ proc->pid, thread->pid, return_error,
+ tr->data_size, tr->offsets_size);
+
+ {
+ struct binder_transaction_log_entry *fe;
+ fe = binder_transaction_log_add(&binder_transaction_log_failed);
+ *fe = *e;
+ }
+
+ BUG_ON(thread->return_error != BR_OK);
+ if (in_reply_to) {
+ thread->return_error = BR_TRANSACTION_COMPLETE;
+ binder_send_failed_reply(in_reply_to, return_error);
+ } else
+ thread->return_error = return_error;
+}
+
+int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
+ void __user *buffer, int size, signed long *consumed)
+{
+ uint32_t cmd;
+ void __user *ptr = buffer + *consumed;
+ void __user *end = buffer + size;
+
+ while (ptr < end && thread->return_error == BR_OK) {
+ if (get_user(cmd, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
+ binder_stats.bc[_IOC_NR(cmd)]++;
+ proc->stats.bc[_IOC_NR(cmd)]++;
+ thread->stats.bc[_IOC_NR(cmd)]++;
+ }
+ switch (cmd) {
+ case BC_INCREFS:
+ case BC_ACQUIRE:
+ case BC_RELEASE:
+ case BC_DECREFS: {
+ uint32_t target;
+ struct binder_ref *ref;
+ const char *debug_string;
+
+ if (get_user(target, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ if (target == 0 && binder_context_mgr_node &&
+ (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {
+ ref = binder_get_ref_for_node(proc,
+ binder_context_mgr_node);
+ if (ref->desc != target) {
+ binder_user_error("binder: %d:"
+ "%d tried to acquire "
+ "reference to desc 0, "
+ "got %d instead\n",
+ proc->pid, thread->pid,
+ ref->desc);
+ }
+ } else
+ ref = binder_get_ref(proc, target);
+ if (ref == NULL) {
+ binder_user_error("binder: %d:%d refcou"
+ "nt change on invalid ref %d\n",
+ proc->pid, thread->pid, target);
+ break;
+ }
+ switch (cmd) {
+ case BC_INCREFS:
+ debug_string = "IncRefs";
+ binder_inc_ref(ref, 0, NULL);
+ break;
+ case BC_ACQUIRE:
+ debug_string = "Acquire";
+ binder_inc_ref(ref, 1, NULL);
+ break;
+ case BC_RELEASE:
+ debug_string = "Release";
+ binder_dec_ref(ref, 1);
+ break;
+ case BC_DECREFS:
+ default:
+ debug_string = "DecRefs";
+ binder_dec_ref(ref, 0);
+ break;
+ }
+ binder_debug(BINDER_DEBUG_USER_REFS,
+ "binder: %d:%d %s ref %d desc %d s %d w %d for node %d\n",
+ proc->pid, thread->pid, debug_string, ref->debug_id,
+ ref->desc, ref->strong, ref->weak, ref->node->debug_id);
+ break;
+ }
+ case BC_INCREFS_DONE:
+ case BC_ACQUIRE_DONE: {
+ void __user *node_ptr;
+ void *cookie;
+ struct binder_node *node;
+
+ if (get_user(node_ptr, (void * __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(void *);
+ if (get_user(cookie, (void * __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(void *);
+ node = binder_get_node(proc, node_ptr);
+ if (node == NULL) {
+ binder_user_error("binder: %d:%d "
+ "%s u%p no match\n",
+ proc->pid, thread->pid,
+ cmd == BC_INCREFS_DONE ?
+ "BC_INCREFS_DONE" :
+ "BC_ACQUIRE_DONE",
+ node_ptr);
+ break;
+ }
+ if (cookie != node->cookie) {
+ binder_user_error("binder: %d:%d %s u%p node %d"
+ " cookie mismatch %p != %p\n",
+ proc->pid, thread->pid,
+ cmd == BC_INCREFS_DONE ?
+ "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
+ node_ptr, node->debug_id,
+ cookie, node->cookie);
+ break;
+ }
+ if (cmd == BC_ACQUIRE_DONE) {
+ if (node->pending_strong_ref == 0) {
+ binder_user_error("binder: %d:%d "
+ "BC_ACQUIRE_DONE node %d has "
+ "no pending acquire request\n",
+ proc->pid, thread->pid,
+ node->debug_id);
+ break;
+ }
+ node->pending_strong_ref = 0;
+ } else {
+ if (node->pending_weak_ref == 0) {
+ binder_user_error("binder: %d:%d "
+ "BC_INCREFS_DONE node %d has "
+ "no pending increfs request\n",
+ proc->pid, thread->pid,
+ node->debug_id);
+ break;
+ }
+ node->pending_weak_ref = 0;
+ }
+ binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0);
+ binder_debug(BINDER_DEBUG_USER_REFS,
+ "binder: %d:%d %s node %d ls %d lw %d\n",
+ proc->pid, thread->pid,
+ cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
+ node->debug_id, node->local_strong_refs, node->local_weak_refs);
+ break;
+ }
+ case BC_ATTEMPT_ACQUIRE:
+ printk(KERN_ERR "binder: BC_ATTEMPT_ACQUIRE not supported\n");
+ return -EINVAL;
+ case BC_ACQUIRE_RESULT:
+ printk(KERN_ERR "binder: BC_ACQUIRE_RESULT not supported\n");
+ return -EINVAL;
+
+ case BC_FREE_BUFFER: {
+ void __user *data_ptr;
+ struct binder_buffer *buffer;
+
+ if (get_user(data_ptr, (void * __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(void *);
+
+ buffer = binder_buffer_lookup(proc, data_ptr);
+ if (buffer == NULL) {
+ binder_user_error("binder: %d:%d "
+ "BC_FREE_BUFFER u%p no match\n",
+ proc->pid, thread->pid, data_ptr);
+ break;
+ }
+ if (!buffer->allow_user_free) {
+ binder_user_error("binder: %d:%d "
+ "BC_FREE_BUFFER u%p matched "
+ "unreturned buffer\n",
+ proc->pid, thread->pid, data_ptr);
+ break;
+ }
+ binder_debug(BINDER_DEBUG_FREE_BUFFER,
+ "binder: %d:%d BC_FREE_BUFFER u%p found buffer %d for %s transaction\n",
+ proc->pid, thread->pid, data_ptr, buffer->debug_id,
+ buffer->transaction ? "active" : "finished");
+
+ if (buffer->transaction) {
+ buffer->transaction->buffer = NULL;
+ buffer->transaction = NULL;
+ }
+ if (buffer->async_transaction && buffer->target_node) {
+ BUG_ON(!buffer->target_node->has_async_transaction);
+ if (list_empty(&buffer->target_node->async_todo))
+ buffer->target_node->has_async_transaction = 0;
+ else
+ list_move_tail(buffer->target_node->async_todo.next, &thread->todo);
+ }
+ binder_transaction_buffer_release(proc, buffer, NULL);
+ binder_free_buf(proc, buffer);
+ break;
+ }
+
+ case BC_TRANSACTION:
+ case BC_REPLY: {
+ struct binder_transaction_data tr;
+
+ if (copy_from_user(&tr, ptr, sizeof(tr)))
+ return -EFAULT;
+ ptr += sizeof(tr);
+ binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
+ break;
+ }
+
+ case BC_REGISTER_LOOPER:
+ binder_debug(BINDER_DEBUG_THREADS,
+ "binder: %d:%d BC_REGISTER_LOOPER\n",
+ proc->pid, thread->pid);
+ if (thread->looper & BINDER_LOOPER_STATE_ENTERED) {
+ thread->looper |= BINDER_LOOPER_STATE_INVALID;
+ binder_user_error("binder: %d:%d ERROR:"
+ " BC_REGISTER_LOOPER called "
+ "after BC_ENTER_LOOPER\n",
+ proc->pid, thread->pid);
+ } else if (proc->requested_threads == 0) {
+ thread->looper |= BINDER_LOOPER_STATE_INVALID;
+ binder_user_error("binder: %d:%d ERROR:"
+ " BC_REGISTER_LOOPER called "
+ "without request\n",
+ proc->pid, thread->pid);
+ } else {
+ proc->requested_threads--;
+ proc->requested_threads_started++;
+ }
+ thread->looper |= BINDER_LOOPER_STATE_REGISTERED;
+ break;
+ case BC_ENTER_LOOPER:
+ binder_debug(BINDER_DEBUG_THREADS,
+ "binder: %d:%d BC_ENTER_LOOPER\n",
+ proc->pid, thread->pid);
+ if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
+ thread->looper |= BINDER_LOOPER_STATE_INVALID;
+ binder_user_error("binder: %d:%d ERROR:"
+ " BC_ENTER_LOOPER called after "
+ "BC_REGISTER_LOOPER\n",
+ proc->pid, thread->pid);
+ }
+ thread->looper |= BINDER_LOOPER_STATE_ENTERED;
+ break;
+ case BC_EXIT_LOOPER:
+ binder_debug(BINDER_DEBUG_THREADS,
+ "binder: %d:%d BC_EXIT_LOOPER\n",
+ proc->pid, thread->pid);
+ thread->looper |= BINDER_LOOPER_STATE_EXITED;
+ break;
+
+ case BC_REQUEST_DEATH_NOTIFICATION:
+ case BC_CLEAR_DEATH_NOTIFICATION: {
+ uint32_t target;
+ void __user *cookie;
+ struct binder_ref *ref;
+ struct binder_ref_death *death;
+
+ if (get_user(target, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ if (get_user(cookie, (void __user * __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(void *);
+ ref = binder_get_ref(proc, target);
+ if (ref == NULL) {
+ binder_user_error("binder: %d:%d %s "
+ "invalid ref %d\n",
+ proc->pid, thread->pid,
+ cmd == BC_REQUEST_DEATH_NOTIFICATION ?
+ "BC_REQUEST_DEATH_NOTIFICATION" :
+ "BC_CLEAR_DEATH_NOTIFICATION",
+ target);
+ break;
+ }
+
+ binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
+ "binder: %d:%d %s %p ref %d desc %d s %d w %d for node %d\n",
+ proc->pid, thread->pid,
+ cmd == BC_REQUEST_DEATH_NOTIFICATION ?
+ "BC_REQUEST_DEATH_NOTIFICATION" :
+ "BC_CLEAR_DEATH_NOTIFICATION",
+ cookie, ref->debug_id, ref->desc,
+ ref->strong, ref->weak, ref->node->debug_id);
+
+ if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
+ if (ref->death) {
+ binder_user_error("binder: %d:%"
+ "d BC_REQUEST_DEATH_NOTI"
+ "FICATION death notific"
+ "ation already set\n",
+ proc->pid, thread->pid);
+ break;
+ }
+ death = kzalloc(sizeof(*death), GFP_KERNEL);
+ if (death == NULL) {
+ thread->return_error = BR_ERROR;
+ binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
+ "binder: %d:%d "
+ "BC_REQUEST_DEATH_NOTIFICATION failed\n",
+ proc->pid, thread->pid);
+ break;
+ }
+ binder_stats_created(BINDER_STAT_DEATH);
+ INIT_LIST_HEAD(&death->work.entry);
+ death->cookie = cookie;
+ ref->death = death;
+ if (ref->node->proc == NULL) {
+ ref->death->work.type = BINDER_WORK_DEAD_BINDER;
+ if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
+ list_add_tail(&ref->death->work.entry, &thread->todo);
+ } else {
+ list_add_tail(&ref->death->work.entry, &proc->todo);
+ wake_up_interruptible(&proc->wait);
+ }
+ }
+ } else {
+ if (ref->death == NULL) {
+ binder_user_error("binder: %d:%"
+ "d BC_CLEAR_DEATH_NOTIFI"
+ "CATION death notificat"
+ "ion not active\n",
+ proc->pid, thread->pid);
+ break;
+ }
+ death = ref->death;
+ if (death->cookie != cookie) {
+ binder_user_error("binder: %d:%"
+ "d BC_CLEAR_DEATH_NOTIFI"
+ "CATION death notificat"
+ "ion cookie mismatch "
+ "%p != %p\n",
+ proc->pid, thread->pid,
+ death->cookie, cookie);
+ break;
+ }
+ ref->death = NULL;
+ if (list_empty(&death->work.entry)) {
+ death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
+ if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
+ list_add_tail(&death->work.entry, &thread->todo);
+ } else {
+ list_add_tail(&death->work.entry, &proc->todo);
+ wake_up_interruptible(&proc->wait);
+ }
+ } else {
+ BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER);
+ death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR;
+ }
+ }
+ } break;
+ case BC_DEAD_BINDER_DONE: {
+ struct binder_work *w;
+ void __user *cookie;
+ struct binder_ref_death *death = NULL;
+ if (get_user(cookie, (void __user * __user *)ptr))
+ return -EFAULT;
+
+ ptr += sizeof(void *);
+ list_for_each_entry(w, &proc->delivered_death, entry) {
+ struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work);
+ if (tmp_death->cookie == cookie) {
+ death = tmp_death;
+ break;
+ }
+ }
+ binder_debug(BINDER_DEBUG_DEAD_BINDER,
+ "binder: %d:%d BC_DEAD_BINDER_DONE %p found %p\n",
+ proc->pid, thread->pid, cookie, death);
+ if (death == NULL) {
+ binder_user_error("binder: %d:%d BC_DEAD"
+ "_BINDER_DONE %p not found\n",
+ proc->pid, thread->pid, cookie);
+ break;
+ }
+
+ list_del_init(&death->work.entry);
+ if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) {
+ death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
+ if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
+ list_add_tail(&death->work.entry, &thread->todo);
+ } else {
+ list_add_tail(&death->work.entry, &proc->todo);
+ wake_up_interruptible(&proc->wait);
+ }
+ }
+ } break;
+
+ default:
+ printk(KERN_ERR "binder: %d:%d unknown command %d\n",
+ proc->pid, thread->pid, cmd);
+ return -EINVAL;
+ }
+ *consumed = ptr - buffer;
+ }
+ return 0;
+}
+
+void binder_stat_br(struct binder_proc *proc, struct binder_thread *thread,
+ uint32_t cmd)
+{
+ if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.br)) {
+ binder_stats.br[_IOC_NR(cmd)]++;
+ proc->stats.br[_IOC_NR(cmd)]++;
+ thread->stats.br[_IOC_NR(cmd)]++;
+ }
+}
+
+static int binder_has_proc_work(struct binder_proc *proc,
+ struct binder_thread *thread)
+{
+ return !list_empty(&proc->todo) ||
+ (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
+}
+
+static int binder_has_thread_work(struct binder_thread *thread)
+{
+ return !list_empty(&thread->todo) || thread->return_error != BR_OK ||
+ (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
+}
+
+static int binder_thread_read(struct binder_proc *proc,
+ struct binder_thread *thread,
+ void __user *buffer, int size,
+ signed long *consumed, int non_block)
+{
+ void __user *ptr = buffer + *consumed;
+ void __user *end = buffer + size;
+
+ int ret = 0;
+ int wait_for_proc_work;
+
+ if (*consumed == 0) {
+ if (put_user(BR_NOOP, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ }
+
+retry:
+ wait_for_proc_work = thread->transaction_stack == NULL &&
+ list_empty(&thread->todo);
+
+ if (thread->return_error != BR_OK && ptr < end) {
+ if (thread->return_error2 != BR_OK) {
+ if (put_user(thread->return_error2, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ if (ptr == end)
+ goto done;
+ thread->return_error2 = BR_OK;
+ }
+ if (put_user(thread->return_error, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ thread->return_error = BR_OK;
+ goto done;
+ }
+
+
+ thread->looper |= BINDER_LOOPER_STATE_WAITING;
+ if (wait_for_proc_work)
+ proc->ready_threads++;
+ mutex_unlock(&binder_lock);
+ if (wait_for_proc_work) {
+ if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
+ BINDER_LOOPER_STATE_ENTERED))) {
+ binder_user_error("binder: %d:%d ERROR: Thread waiting "
+ "for process work before calling BC_REGISTER_"
+ "LOOPER or BC_ENTER_LOOPER (state %x)\n",
+ proc->pid, thread->pid, thread->looper);
+ wait_event_interruptible(binder_user_error_wait,
+ binder_stop_on_user_error < 2);
+ }
+ binder_set_nice(proc->default_priority);
+ if (non_block) {
+ if (!binder_has_proc_work(proc, thread))
+ ret = -EAGAIN;
+ } else
+ ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));
+ } else {
+ if (non_block) {
+ if (!binder_has_thread_work(thread))
+ ret = -EAGAIN;
+ } else
+ ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));
+ }
+ mutex_lock(&binder_lock);
+ if (wait_for_proc_work)
+ proc->ready_threads--;
+ thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
+
+ if (ret)
+ return ret;
+
+ while (1) {
+ uint32_t cmd;
+ struct binder_transaction_data tr;
+ struct binder_work *w;
+ struct binder_transaction *t = NULL;
+
+ if (!list_empty(&thread->todo))
+ w = list_first_entry(&thread->todo, struct binder_work, entry);
+ else if (!list_empty(&proc->todo) && wait_for_proc_work)
+ w = list_first_entry(&proc->todo, struct binder_work, entry);
+ else {
+ if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */
+ goto retry;
+ break;
+ }
+
+ if (end - ptr < sizeof(tr) + 4)
+ break;
+
+ switch (w->type) {
+ case BINDER_WORK_TRANSACTION: {
+ t = container_of(w, struct binder_transaction, work);
+ } break;
+ case BINDER_WORK_TRANSACTION_COMPLETE: {
+ cmd = BR_TRANSACTION_COMPLETE;
+ if (put_user(cmd, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+
+ binder_stat_br(proc, thread, cmd);
+ binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE,
+ "binder: %d:%d BR_TRANSACTION_COMPLETE\n",
+ proc->pid, thread->pid);
+
+ list_del(&w->entry);
+ kfree(w);
+ binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
+ } break;
+ case BINDER_WORK_NODE: {
+ struct binder_node *node = container_of(w, struct binder_node, work);
+ uint32_t cmd = BR_NOOP;
+ const char *cmd_name;
+ int strong = node->internal_strong_refs || node->local_strong_refs;
+ int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong;
+ if (weak && !node->has_weak_ref) {
+ cmd = BR_INCREFS;
+ cmd_name = "BR_INCREFS";
+ node->has_weak_ref = 1;
+ node->pending_weak_ref = 1;
+ node->local_weak_refs++;
+ } else if (strong && !node->has_strong_ref) {
+ cmd = BR_ACQUIRE;
+ cmd_name = "BR_ACQUIRE";
+ node->has_strong_ref = 1;
+ node->pending_strong_ref = 1;
+ node->local_strong_refs++;
+ } else if (!strong && node->has_strong_ref) {
+ cmd = BR_RELEASE;
+ cmd_name = "BR_RELEASE";
+ node->has_strong_ref = 0;
+ } else if (!weak && node->has_weak_ref) {
+ cmd = BR_DECREFS;
+ cmd_name = "BR_DECREFS";
+ node->has_weak_ref = 0;
+ }
+ if (cmd != BR_NOOP) {
+ if (put_user(cmd, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ if (put_user(node->ptr, (void * __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(void *);
+ if (put_user(node->cookie, (void * __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(void *);
+
+ binder_stat_br(proc, thread, cmd);
+ binder_debug(BINDER_DEBUG_USER_REFS,
+ "binder: %d:%d %s %d u%p c%p\n",
+ proc->pid, thread->pid, cmd_name, node->debug_id, node->ptr, node->cookie);
+ } else {
+ list_del_init(&w->entry);
+ if (!weak && !strong) {
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "binder: %d:%d node %d u%p c%p deleted\n",
+ proc->pid, thread->pid, node->debug_id,
+ node->ptr, node->cookie);
+ rb_erase(&node->rb_node, &proc->nodes);
+ kfree(node);
+ binder_stats_deleted(BINDER_STAT_NODE);
+ } else {
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "binder: %d:%d node %d u%p c%p state unchanged\n",
+ proc->pid, thread->pid, node->debug_id, node->ptr,
+ node->cookie);
+ }
+ }
+ } break;
+ case BINDER_WORK_DEAD_BINDER:
+ case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
+ case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
+ struct binder_ref_death *death;
+ uint32_t cmd;
+
+ death = container_of(w, struct binder_ref_death, work);
+ if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
+ cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
+ else
+ cmd = BR_DEAD_BINDER;
+ if (put_user(cmd, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ if (put_user(death->cookie, (void * __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(void *);
+ binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
+ "binder: %d:%d %s %p\n",
+ proc->pid, thread->pid,
+ cmd == BR_DEAD_BINDER ?
+ "BR_DEAD_BINDER" :
+ "BR_CLEAR_DEATH_NOTIFICATION_DONE",
+ death->cookie);
+
+ if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
+ list_del(&w->entry);
+ kfree(death);
+ binder_stats_deleted(BINDER_STAT_DEATH);
+ } else
+ list_move(&w->entry, &proc->delivered_death);
+ if (cmd == BR_DEAD_BINDER)
+ goto done; /* DEAD_BINDER notifications can cause transactions */
+ } break;
+ }
+
+ if (!t)
+ continue;
+
+ BUG_ON(t->buffer == NULL);
+ if (t->buffer->target_node) {
+ struct binder_node *target_node = t->buffer->target_node;
+ tr.target.ptr = target_node->ptr;
+ tr.cookie = target_node->cookie;
+ t->saved_priority = task_nice(current);
+ if (t->priority < target_node->min_priority &&
+ !(t->flags & TF_ONE_WAY))
+ binder_set_nice(t->priority);
+ else if (!(t->flags & TF_ONE_WAY) ||
+ t->saved_priority > target_node->min_priority)
+ binder_set_nice(target_node->min_priority);
+ cmd = BR_TRANSACTION;
+ } else {
+ tr.target.ptr = NULL;
+ tr.cookie = NULL;
+ cmd = BR_REPLY;
+ }
+ tr.code = t->code;
+ tr.flags = t->flags;
+ tr.sender_euid = t->sender_euid;
+
+ if (t->from) {
+ struct task_struct *sender = t->from->proc->tsk;
+ tr.sender_pid = task_tgid_nr_ns(sender,
+ current->nsproxy->pid_ns);
+ } else {
+ tr.sender_pid = 0;
+ }
+
+ tr.data_size = t->buffer->data_size;
+ tr.offsets_size = t->buffer->offsets_size;
+ tr.data.ptr.buffer = (void *)t->buffer->data +
+ proc->user_buffer_offset;
+ tr.data.ptr.offsets = tr.data.ptr.buffer +
+ ALIGN(t->buffer->data_size,
+ sizeof(void *));
+
+ if (put_user(cmd, (uint32_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(uint32_t);
+ if (copy_to_user(ptr, &tr, sizeof(tr)))
+ return -EFAULT;
+ ptr += sizeof(tr);
+
+ binder_stat_br(proc, thread, cmd);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ "binder: %d:%d %s %d %d:%d, cmd %d"
+ "size %zd-%zd ptr %p-%p\n",
+ proc->pid, thread->pid,
+ (cmd == BR_TRANSACTION) ? "BR_TRANSACTION" :
+ "BR_REPLY",
+ t->debug_id, t->from ? t->from->proc->pid : 0,
+ t->from ? t->from->pid : 0, cmd,
+ t->buffer->data_size, t->buffer->offsets_size,
+ tr.data.ptr.buffer, tr.data.ptr.offsets);
+
+ list_del(&t->work.entry);
+ t->buffer->allow_user_free = 1;
+ if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
+ t->to_parent = thread->transaction_stack;
+ t->to_thread = thread;
+ thread->transaction_stack = t;
+ } else {
+ t->buffer->transaction = NULL;
+ kfree(t);
+ binder_stats_deleted(BINDER_STAT_TRANSACTION);
+ }
+ break;
+ }
+
+done:
+
+ *consumed = ptr - buffer;
+ if (proc->requested_threads + proc->ready_threads == 0 &&
+ proc->requested_threads_started < proc->max_threads &&
+ (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
+ BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
+ /*spawn a new thread if we leave this out */) {
+ proc->requested_threads++;
+ binder_debug(BINDER_DEBUG_THREADS,
+ "binder: %d:%d BR_SPAWN_LOOPER\n",
+ proc->pid, thread->pid);
+ if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static void binder_release_work(struct list_head *list)
+{
+ struct binder_work *w;
+ while (!list_empty(list)) {
+ w = list_first_entry(list, struct binder_work, entry);
+ list_del_init(&w->entry);
+ switch (w->type) {
+ case BINDER_WORK_TRANSACTION: {
+ struct binder_transaction *t;
+
+ t = container_of(w, struct binder_transaction, work);
+ if (t->buffer->target_node && !(t->flags & TF_ONE_WAY))
+ binder_send_failed_reply(t, BR_DEAD_REPLY);
+ } break;
+ case BINDER_WORK_TRANSACTION_COMPLETE: {
+ kfree(w);
+ binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
+ } break;
+ default:
+ break;
+ }
+ }
+
+}
+
+static struct binder_thread *binder_get_thread(struct binder_proc *proc)
+{
+ struct binder_thread *thread = NULL;
+ struct rb_node *parent = NULL;
+ struct rb_node **p = &proc->threads.rb_node;
+
+ while (*p) {
+ parent = *p;
+ thread = rb_entry(parent, struct binder_thread, rb_node);
+
+ if (current->pid < thread->pid)
+ p = &(*p)->rb_left;
+ else if (current->pid > thread->pid)
+ p = &(*p)->rb_right;
+ else
+ break;
+ }
+ if (*p == NULL) {
+ thread = kzalloc(sizeof(*thread), GFP_KERNEL);
+ if (thread == NULL)
+ return NULL;
+ binder_stats_created(BINDER_STAT_THREAD);
+ thread->proc = proc;
+ thread->pid = current->pid;
+ init_waitqueue_head(&thread->wait);
+ INIT_LIST_HEAD(&thread->todo);
+ rb_link_node(&thread->rb_node, parent, p);
+ rb_insert_color(&thread->rb_node, &proc->threads);
+ thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
+ thread->return_error = BR_OK;
+ thread->return_error2 = BR_OK;
+ }
+ return thread;
+}
+
+static int binder_free_thread(struct binder_proc *proc,
+ struct binder_thread *thread)
+{
+ struct binder_transaction *t;
+ struct binder_transaction *send_reply = NULL;
+ int active_transactions = 0;
+
+ rb_erase(&thread->rb_node, &proc->threads);
+ t = thread->transaction_stack;
+ if (t && t->to_thread == thread)
+ send_reply = t;
+ while (t) {
+ active_transactions++;
+ binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
+ "binder: release %d:%d transaction %d "
+ "%s, still active\n", proc->pid, thread->pid,
+ t->debug_id,
+ (t->to_thread == thread) ? "in" : "out");
+
+ if (t->to_thread == thread) {
+ t->to_proc = NULL;
+ t->to_thread = NULL;
+ if (t->buffer) {
+ t->buffer->transaction = NULL;
+ t->buffer = NULL;
+ }
+ t = t->to_parent;
+ } else if (t->from == thread) {
+ t->from = NULL;
+ t = t->from_parent;
+ } else
+ BUG();
+ }
+ if (send_reply)
+ binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
+ binder_release_work(&thread->todo);
+ kfree(thread);
+ binder_stats_deleted(BINDER_STAT_THREAD);
+ return active_transactions;
+}
+
+static unsigned int binder_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct binder_proc *proc = filp->private_data;
+ struct binder_thread *thread = NULL;
+ int wait_for_proc_work;
+
+ mutex_lock(&binder_lock);
+ thread = binder_get_thread(proc);
+
+ wait_for_proc_work = thread->transaction_stack == NULL &&
+ list_empty(&thread->todo) && thread->return_error == BR_OK;
+ mutex_unlock(&binder_lock);
+
+ if (wait_for_proc_work) {
+ if (binder_has_proc_work(proc, thread))
+ return POLLIN;
+ poll_wait(filp, &proc->wait, wait);
+ if (binder_has_proc_work(proc, thread))
+ return POLLIN;
+ } else {
+ if (binder_has_thread_work(thread))
+ return POLLIN;
+ poll_wait(filp, &thread->wait, wait);
+ if (binder_has_thread_work(thread))
+ return POLLIN;
+ }
+ return 0;
+}
+
+static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int ret;
+ struct binder_proc *proc = filp->private_data;
+ struct binder_thread *thread;
+ unsigned int size = _IOC_SIZE(cmd);
+ void __user *ubuf = (void __user *)arg;
+
+ /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/
+
+ ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
+ if (ret)
+ return ret;
+
+ mutex_lock(&binder_lock);
+ thread = binder_get_thread(proc);
+ if (thread == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ switch (cmd) {
+ case BINDER_WRITE_READ: {
+ struct binder_write_read bwr;
+ if (size != sizeof(struct binder_write_read)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
+ ret = -EFAULT;
+ goto err;
+ }
+ binder_debug(BINDER_DEBUG_READ_WRITE,
+ "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",
+ proc->pid, thread->pid, bwr.write_size, bwr.write_buffer,
+ bwr.read_size, bwr.read_buffer);
+
+ if (bwr.write_size > 0) {
+ ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
+ if (ret < 0) {
+ bwr.read_consumed = 0;
+ if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
+ ret = -EFAULT;
+ goto err;
+ }
+ }
+ if (bwr.read_size > 0) {
+ ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
+ if (!list_empty(&proc->todo))
+ wake_up_interruptible(&proc->wait);
+ if (ret < 0) {
+ if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
+ ret = -EFAULT;
+ goto err;
+ }
+ }
+ binder_debug(BINDER_DEBUG_READ_WRITE,
+ "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",
+ proc->pid, thread->pid, bwr.write_consumed, bwr.write_size,
+ bwr.read_consumed, bwr.read_size);
+ if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
+ ret = -EFAULT;
+ goto err;
+ }
+ break;
+ }
+ case BINDER_SET_MAX_THREADS:
+ if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ case BINDER_SET_CONTEXT_MGR:
+ if (binder_context_mgr_node != NULL) {
+ printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
+ ret = -EBUSY;
+ goto err;
+ }
+ if (binder_context_mgr_uid != -1) {
+ if (binder_context_mgr_uid != current->cred->euid) {
+ printk(KERN_ERR "binder: BINDER_SET_"
+ "CONTEXT_MGR bad uid %d != %d\n",
+ current->cred->euid,
+ binder_context_mgr_uid);
+ ret = -EPERM;
+ goto err;
+ }
+ } else
+ binder_context_mgr_uid = current->cred->euid;
+ binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
+ if (binder_context_mgr_node == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ binder_context_mgr_node->local_weak_refs++;
+ binder_context_mgr_node->local_strong_refs++;
+ binder_context_mgr_node->has_strong_ref = 1;
+ binder_context_mgr_node->has_weak_ref = 1;
+ break;
+ case BINDER_THREAD_EXIT:
+ binder_debug(BINDER_DEBUG_THREADS, "binder: %d:%d exit\n",
+ proc->pid, thread->pid);
+ binder_free_thread(proc, thread);
+ thread = NULL;
+ break;
+ case BINDER_VERSION:
+ if (size != sizeof(struct binder_version)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &((struct binder_version *)ubuf)->protocol_version)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ ret = 0;
+err:
+ if (thread)
+ thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
+ mutex_unlock(&binder_lock);
+ wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
+ if (ret && ret != -ERESTARTSYS)
+ printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
+ return ret;
+}
+
+static void binder_vma_open(struct vm_area_struct *vma)
+{
+ struct binder_proc *proc = vma->vm_private_data;
+ binder_debug(BINDER_DEBUG_OPEN_CLOSE,
+ "binder: %d open vm area %lx-%lx (%ld K) vma %lx pagep %lx\n",
+ proc->pid, vma->vm_start, vma->vm_end,
+ (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
+ (unsigned long)pgprot_val(vma->vm_page_prot));
+ dump_stack();
+}
+
+static void binder_vma_close(struct vm_area_struct *vma)
+{
+ struct binder_proc *proc = vma->vm_private_data;
+ binder_debug(BINDER_DEBUG_OPEN_CLOSE,
+ "binder: %d close vm area %lx-%lx (%ld K) vma %lx pagep %lx\n",
+ proc->pid, vma->vm_start, vma->vm_end,
+ (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
+ (unsigned long)pgprot_val(vma->vm_page_prot));
+ proc->vma = NULL;
+ binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES);
+}
+
+static struct vm_operations_struct binder_vm_ops = {
+ .open = binder_vma_open,
+ .close = binder_vma_close,
+};
+
+static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ int ret;
+ struct vm_struct *area;
+ struct binder_proc *proc = filp->private_data;
+ const char *failure_string;
+ struct binder_buffer *buffer;
+
+ if ((vma->vm_end - vma->vm_start) > SZ_4M)
+ vma->vm_end = vma->vm_start + SZ_4M;
+
+ binder_debug(BINDER_DEBUG_OPEN_CLOSE,
+ "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
+ proc->pid, vma->vm_start, vma->vm_end,
+ (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
+ (unsigned long)pgprot_val(vma->vm_page_prot));
+
+ if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
+ ret = -EPERM;
+ failure_string = "bad vm_flags";
+ goto err_bad_arg;
+ }
+ vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
+
+ if (proc->buffer) {
+ ret = -EBUSY;
+ failure_string = "already mapped";
+ goto err_already_mapped;
+ }
+
+ area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
+ if (area == NULL) {
+ ret = -ENOMEM;
+ failure_string = "get_vm_area";
+ goto err_get_vm_area_failed;
+ }
+ proc->buffer = area->addr;
+ proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
+
+#ifdef CONFIG_CPU_CACHE_VIPT
+ if (cache_is_vipt_aliasing()) {
+ while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
+ printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
+ vma->vm_start += PAGE_SIZE;
+ }
+ }
+#endif
+ proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
+ if (proc->pages == NULL) {
+ ret = -ENOMEM;
+ failure_string = "alloc page array";
+ goto err_alloc_pages_failed;
+ }
+ proc->buffer_size = vma->vm_end - vma->vm_start;
+
+ vma->vm_ops = &binder_vm_ops;
+ vma->vm_private_data = proc;
+
+ if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
+ ret = -ENOMEM;
+ failure_string = "alloc small buf";
+ goto err_alloc_small_buf_failed;
+ }
+ buffer = proc->buffer;
+ INIT_LIST_HEAD(&proc->buffers);
+ list_add(&buffer->entry, &proc->buffers);
+ buffer->free = 1;
+ binder_insert_free_buffer(proc, buffer);
+ proc->free_async_space = proc->buffer_size / 2;
+ barrier();
+ proc->files = get_files_struct(current);
+ proc->vma = vma;
+
+ /*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n",
+ proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
+ return 0;
+
+err_alloc_small_buf_failed:
+ kfree(proc->pages);
+ proc->pages = NULL;
+err_alloc_pages_failed:
+ vfree(proc->buffer);
+ proc->buffer = NULL;
+err_get_vm_area_failed:
+err_already_mapped:
+err_bad_arg:
+ printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n",
+ proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
+ return ret;
+}
+
+static int binder_open(struct inode *nodp, struct file *filp)
+{
+ struct binder_proc *proc;
+
+ binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
+ current->group_leader->pid, current->pid);
+
+ proc = kzalloc(sizeof(*proc), GFP_KERNEL);
+ if (proc == NULL)
+ return -ENOMEM;
+ get_task_struct(current);
+ proc->tsk = current;
+ INIT_LIST_HEAD(&proc->todo);
+ init_waitqueue_head(&proc->wait);
+ proc->default_priority = task_nice(current);
+ mutex_lock(&binder_lock);
+ binder_stats_created(BINDER_STAT_PROC);
+ hlist_add_head(&proc->proc_node, &binder_procs);
+ proc->pid = current->group_leader->pid;
+ INIT_LIST_HEAD(&proc->delivered_death);
+ filp->private_data = proc;
+ mutex_unlock(&binder_lock);
+
+ if (binder_debugfs_dir_entry_proc) {
+ char strbuf[11];
+ snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
+ proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
+ binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
+ }
+
+ return 0;
+}
+
+static int binder_flush(struct file *filp, fl_owner_t id)
+{
+ struct binder_proc *proc = filp->private_data;
+
+ binder_defer_work(proc, BINDER_DEFERRED_FLUSH);
+
+ return 0;
+}
+
+static void binder_deferred_flush(struct binder_proc *proc)
+{
+ struct rb_node *n;
+ int wake_count = 0;
+ for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) {
+ struct binder_thread *thread = rb_entry(n, struct binder_thread, rb_node);
+ thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
+ if (thread->looper & BINDER_LOOPER_STATE_WAITING) {
+ wake_up_interruptible(&thread->wait);
+ wake_count++;
+ }
+ }
+ wake_up_interruptible_all(&proc->wait);
+
+ binder_debug(BINDER_DEBUG_OPEN_CLOSE,
+ "binder_flush: %d woke %d threads\n", proc->pid,
+ wake_count);
+}
+
+static int binder_release(struct inode *nodp, struct file *filp)
+{
+ struct binder_proc *proc = filp->private_data;
+ debugfs_remove(proc->debugfs_entry);
+ binder_defer_work(proc, BINDER_DEFERRED_RELEASE);
+
+ return 0;
+}
+
+static void binder_deferred_release(struct binder_proc *proc)
+{
+ struct hlist_node *pos;
+ struct binder_transaction *t;
+ struct rb_node *n;
+ int threads, nodes, incoming_refs, outgoing_refs, buffers, active_transactions, page_count;
+
+ BUG_ON(proc->vma);
+ BUG_ON(proc->files);
+
+ hlist_del(&proc->proc_node);
+ if (binder_context_mgr_node && binder_context_mgr_node->proc == proc) {
+ binder_debug(BINDER_DEBUG_DEAD_BINDER,
+ "binder_release: %d context_mgr_node gone\n",
+ proc->pid);
+ binder_context_mgr_node = NULL;
+ }
+
+ threads = 0;
+ active_transactions = 0;
+ while ((n = rb_first(&proc->threads))) {
+ struct binder_thread *thread = rb_entry(n, struct binder_thread, rb_node);
+ threads++;
+ active_transactions += binder_free_thread(proc, thread);
+ }
+ nodes = 0;
+ incoming_refs = 0;
+ while ((n = rb_first(&proc->nodes))) {
+ struct binder_node *node = rb_entry(n, struct binder_node, rb_node);
+
+ nodes++;
+ rb_erase(&node->rb_node, &proc->nodes);
+ list_del_init(&node->work.entry);
+ if (hlist_empty(&node->refs)) {
+ kfree(node);
+ binder_stats_deleted(BINDER_STAT_NODE);
+ } else {
+ struct binder_ref *ref;
+ int death = 0;
+
+ node->proc = NULL;
+ node->local_strong_refs = 0;
+ node->local_weak_refs = 0;
+ hlist_add_head(&node->dead_node, &binder_dead_nodes);
+
+ hlist_for_each_entry(ref, pos, &node->refs, node_entry) {
+ incoming_refs++;
+ if (ref->death) {
+ death++;
+ if (list_empty(&ref->death->work.entry)) {
+ ref->death->work.type = BINDER_WORK_DEAD_BINDER;
+ list_add_tail(&ref->death->work.entry, &ref->proc->todo);
+ wake_up_interruptible(&ref->proc->wait);
+ } else
+ BUG();
+ }
+ }
+ binder_debug(BINDER_DEBUG_DEAD_BINDER,
+ "binder: node %d now dead, "
+ "refs %d, death %d\n", node->debug_id,
+ incoming_refs, death);
+ }
+ }
+ outgoing_refs = 0;
+ while ((n = rb_first(&proc->refs_by_desc))) {
+ struct binder_ref *ref = rb_entry(n, struct binder_ref,
+ rb_node_desc);
+ outgoing_refs++;
+ binder_delete_ref(ref);
+ }
+ binder_release_work(&proc->todo);
+ buffers = 0;
+
+ while ((n = rb_first(&proc->allocated_buffers))) {
+ struct binder_buffer *buffer = rb_entry(n, struct binder_buffer,
+ rb_node);
+ t = buffer->transaction;
+ if (t) {
+ t->buffer = NULL;
+ buffer->transaction = NULL;
+ printk(KERN_ERR "binder: release proc %d, "
+ "transaction %d, not freed\n",
+ proc->pid, t->debug_id);
+ /*BUG();*/
+ }
+ binder_free_buf(proc, buffer);
+ buffers++;
+ }
+
+ binder_stats_deleted(BINDER_STAT_PROC);
+
+ page_count = 0;
+ if (proc->pages) {
+ int i;
+ for (i = 0; i < proc->buffer_size / PAGE_SIZE; i++) {
+ if (proc->pages[i]) {
+ void *page_addr = proc->buffer + i * PAGE_SIZE;
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "binder_release: %d: "
+ "page %d at %p not freed\n",
+ proc->pid, i,
+ page_addr);
+ unmap_kernel_range((unsigned long)page_addr,
+ PAGE_SIZE);
+ __free_page(proc->pages[i]);
+ page_count++;
+ }
+ }
+ kfree(proc->pages);
+ vfree(proc->buffer);
+ }
+
+ put_task_struct(proc->tsk);
+
+ binder_debug(BINDER_DEBUG_OPEN_CLOSE,
+ "binder_release: %d threads %d, nodes %d (ref %d), "
+ "refs %d, active transactions %d, buffers %d, "
+ "pages %d\n",
+ proc->pid, threads, nodes, incoming_refs, outgoing_refs,
+ active_transactions, buffers, page_count);
+
+ kfree(proc);
+}
+
+static void binder_deferred_func(struct work_struct *work)
+{
+ struct binder_proc *proc;
+ struct files_struct *files;
+
+ int defer;
+ do {
+ mutex_lock(&binder_lock);
+ mutex_lock(&binder_deferred_lock);
+ if (!hlist_empty(&binder_deferred_list)) {
+ proc = hlist_entry(binder_deferred_list.first,
+ struct binder_proc, deferred_work_node);
+ hlist_del_init(&proc->deferred_work_node);
+ defer = proc->deferred_work;
+ proc->deferred_work = 0;
+ } else {
+ proc = NULL;
+ defer = 0;
+ }
+ mutex_unlock(&binder_deferred_lock);
+
+ files = NULL;
+ if (defer & BINDER_DEFERRED_PUT_FILES) {
+ files = proc->files;
+ if (files)
+ proc->files = NULL;
+ }
+
+ if (defer & BINDER_DEFERRED_FLUSH)
+ binder_deferred_flush(proc);
+
+ if (defer & BINDER_DEFERRED_RELEASE)
+ binder_deferred_release(proc); /* frees proc */
+
+ mutex_unlock(&binder_lock);
+ if (files)
+ put_files_struct(files);
+ } while (proc);
+}
+static DECLARE_WORK(binder_deferred_work, binder_deferred_func);
+
+static void
+binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer)
+{
+ mutex_lock(&binder_deferred_lock);
+ proc->deferred_work |= defer;
+ if (hlist_unhashed(&proc->deferred_work_node)) {
+ hlist_add_head(&proc->deferred_work_node,
+ &binder_deferred_list);
+ queue_work(binder_deferred_workqueue, &binder_deferred_work);
+ }
+ mutex_unlock(&binder_deferred_lock);
+}
+
+static void print_binder_transaction(struct seq_file *m, const char *prefix,
+ struct binder_transaction *t)
+{
+ seq_printf(m,
+ "%s %d: %p from %d:%d to %d:%d code %x flags %x pri %ld r%d",
+ prefix, t->debug_id, t,
+ t->from ? t->from->proc->pid : 0,
+ t->from ? t->from->pid : 0,
+ t->to_proc ? t->to_proc->pid : 0,
+ t->to_thread ? t->to_thread->pid : 0,
+ t->code, t->flags, t->priority, t->need_reply);
+ if (t->buffer == NULL) {
+ seq_puts(m, " buffer free\n");
+ return;
+ }
+ if (t->buffer->target_node)
+ seq_printf(m, " node %d",
+ t->buffer->target_node->debug_id);
+ seq_printf(m, " size %zd:%zd data %p\n",
+ t->buffer->data_size, t->buffer->offsets_size,
+ t->buffer->data);
+}
+
+static void print_binder_buffer(struct seq_file *m, const char *prefix,
+ struct binder_buffer *buffer)
+{
+ seq_printf(m, "%s %d: %p size %zd:%zd %s\n",
+ prefix, buffer->debug_id, buffer->data,
+ buffer->data_size, buffer->offsets_size,
+ buffer->transaction ? "active" : "delivered");
+}
+
+static void print_binder_work(struct seq_file *m, const char *prefix,
+ const char *transaction_prefix,
+ struct binder_work *w)
+{
+ struct binder_node *node;
+ struct binder_transaction *t;
+
+ switch (w->type) {
+ case BINDER_WORK_TRANSACTION:
+ t = container_of(w, struct binder_transaction, work);
+ print_binder_transaction(m, transaction_prefix, t);
+ break;
+ case BINDER_WORK_TRANSACTION_COMPLETE:
+ seq_printf(m, "%stransaction complete\n", prefix);
+ break;
+ case BINDER_WORK_NODE:
+ node = container_of(w, struct binder_node, work);
+ seq_printf(m, "%snode work %d: u%p c%p\n",
+ prefix, node->debug_id, node->ptr, node->cookie);
+ break;
+ case BINDER_WORK_DEAD_BINDER:
+ seq_printf(m, "%shas dead binder\n", prefix);
+ break;
+ case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
+ seq_printf(m, "%shas cleared dead binder\n", prefix);
+ break;
+ case BINDER_WORK_CLEAR_DEATH_NOTIFICATION:
+ seq_printf(m, "%shas cleared death notification\n", prefix);
+ break;
+ default:
+ seq_printf(m, "%sunknown work: type %d\n", prefix, w->type);
+ break;
+ }
+}
+
+static void print_binder_thread(struct seq_file *m,
+ struct binder_thread *thread,
+ int print_always)
+{
+ struct binder_transaction *t;
+ struct binder_work *w;
+ size_t start_pos = m->count;
+ size_t header_pos;
+
+ seq_printf(m, " thread %d: l %02x\n", thread->pid, thread->looper);
+ header_pos = m->count;
+ t = thread->transaction_stack;
+ while (t) {
+ if (t->from == thread) {
+ print_binder_transaction(m,
+ " outgoing transaction", t);
+ t = t->from_parent;
+ } else if (t->to_thread == thread) {
+ print_binder_transaction(m,
+ " incoming transaction", t);
+ t = t->to_parent;
+ } else {
+ print_binder_transaction(m, " bad transaction", t);
+ t = NULL;
+ }
+ }
+ list_for_each_entry(w, &thread->todo, entry) {
+ print_binder_work(m, " ", " pending transaction", w);
+ }
+ if (!print_always && m->count == header_pos)
+ m->count = start_pos;
+}
+
+static void print_binder_node(struct seq_file *m, struct binder_node *node)
+{
+ struct binder_ref *ref;
+ struct hlist_node *pos;
+ struct binder_work *w;
+ int count;
+
+ count = 0;
+ hlist_for_each_entry(ref, pos, &node->refs, node_entry)
+ count++;
+
+ seq_printf(m, " node %d: u%p c%p hs %d hw %d ls %d lw %d is %d iw %d",
+ node->debug_id, node->ptr, node->cookie,
+ node->has_strong_ref, node->has_weak_ref,
+ node->local_strong_refs, node->local_weak_refs,
+ node->internal_strong_refs, count);
+ if (count) {
+ seq_puts(m, " proc");
+ hlist_for_each_entry(ref, pos, &node->refs, node_entry)
+ seq_printf(m, " %d", ref->proc->pid);
+ }
+ seq_puts(m, "\n");
+ list_for_each_entry(w, &node->async_todo, entry)
+ print_binder_work(m, " ",
+ " pending async transaction", w);
+}
+
+static void print_binder_ref(struct seq_file *m, struct binder_ref *ref)
+{
+ seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %p\n",
+ ref->debug_id, ref->desc, ref->node->proc ? "" : "dead ",
+ ref->node->debug_id, ref->strong, ref->weak, ref->death);
+}
+
+static void print_binder_proc(struct seq_file *m,
+ struct binder_proc *proc, int print_all)
+{
+ struct binder_work *w;
+ struct rb_node *n;
+ size_t start_pos = m->count;
+ size_t header_pos;
+
+ seq_printf(m, "proc %d\n", proc->pid);
+ header_pos = m->count;
+
+ for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
+ print_binder_thread(m, rb_entry(n, struct binder_thread,
+ rb_node), print_all);
+ for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) {
+ struct binder_node *node = rb_entry(n, struct binder_node,
+ rb_node);
+ if (print_all || node->has_async_transaction)
+ print_binder_node(m, node);
+ }
+ if (print_all) {
+ for (n = rb_first(&proc->refs_by_desc);
+ n != NULL;
+ n = rb_next(n))
+ print_binder_ref(m, rb_entry(n, struct binder_ref,
+ rb_node_desc));
+ }
+ for (n = rb_first(&proc->allocated_buffers); n != NULL; n = rb_next(n))
+ print_binder_buffer(m, " buffer",
+ rb_entry(n, struct binder_buffer, rb_node));
+ list_for_each_entry(w, &proc->todo, entry)
+ print_binder_work(m, " ", " pending transaction", w);
+ list_for_each_entry(w, &proc->delivered_death, entry) {
+ seq_puts(m, " has delivered dead binder\n");
+ break;
+ }
+ if (!print_all && m->count == header_pos)
+ m->count = start_pos;
+}
+
+static const char *binder_return_strings[] = {
+ "BR_ERROR",
+ "BR_OK",
+ "BR_TRANSACTION",
+ "BR_REPLY",
+ "BR_ACQUIRE_RESULT",
+ "BR_DEAD_REPLY",
+ "BR_TRANSACTION_COMPLETE",
+ "BR_INCREFS",
+ "BR_ACQUIRE",
+ "BR_RELEASE",
+ "BR_DECREFS",
+ "BR_ATTEMPT_ACQUIRE",
+ "BR_NOOP",
+ "BR_SPAWN_LOOPER",
+ "BR_FINISHED",
+ "BR_DEAD_BINDER",
+ "BR_CLEAR_DEATH_NOTIFICATION_DONE",
+ "BR_FAILED_REPLY"
+};
+
+static const char *binder_command_strings[] = {
+ "BC_TRANSACTION",
+ "BC_REPLY",
+ "BC_ACQUIRE_RESULT",
+ "BC_FREE_BUFFER",
+ "BC_INCREFS",
+ "BC_ACQUIRE",
+ "BC_RELEASE",
+ "BC_DECREFS",
+ "BC_INCREFS_DONE",
+ "BC_ACQUIRE_DONE",
+ "BC_ATTEMPT_ACQUIRE",
+ "BC_REGISTER_LOOPER",
+ "BC_ENTER_LOOPER",
+ "BC_EXIT_LOOPER",
+ "BC_REQUEST_DEATH_NOTIFICATION",
+ "BC_CLEAR_DEATH_NOTIFICATION",
+ "BC_DEAD_BINDER_DONE"
+};
+
+static const char *binder_objstat_strings[] = {
+ "proc",
+ "thread",
+ "node",
+ "ref",
+ "death",
+ "transaction",
+ "transaction_complete"
+};
+
+static void print_binder_stats(struct seq_file *m, const char *prefix,
+ struct binder_stats *stats)
+{
+ int i;
+
+ BUILD_BUG_ON(ARRAY_SIZE(stats->bc) !=
+ ARRAY_SIZE(binder_command_strings));
+ for (i = 0; i < ARRAY_SIZE(stats->bc); i++) {
+ if (stats->bc[i])
+ seq_printf(m, "%s%s: %d\n", prefix,
+ binder_command_strings[i], stats->bc[i]);
+ }
+
+ BUILD_BUG_ON(ARRAY_SIZE(stats->br) !=
+ ARRAY_SIZE(binder_return_strings));
+ for (i = 0; i < ARRAY_SIZE(stats->br); i++) {
+ if (stats->br[i])
+ seq_printf(m, "%s%s: %d\n", prefix,
+ binder_return_strings[i], stats->br[i]);
+ }
+
+ BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) !=
+ ARRAY_SIZE(binder_objstat_strings));
+ BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) !=
+ ARRAY_SIZE(stats->obj_deleted));
+ for (i = 0; i < ARRAY_SIZE(stats->obj_created); i++) {
+ if (stats->obj_created[i] || stats->obj_deleted[i])
+ seq_printf(m, "%s%s: active %d total %d\n", prefix,
+ binder_objstat_strings[i],
+ stats->obj_created[i] - stats->obj_deleted[i],
+ stats->obj_created[i]);
+ }
+}
+
+static void print_binder_proc_stats(struct seq_file *m,
+ struct binder_proc *proc)
+{
+ struct binder_work *w;
+ struct rb_node *n;
+ int count, strong, weak;
+
+ seq_printf(m, "proc %d\n", proc->pid);
+ count = 0;
+ for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
+ count++;
+ seq_printf(m, " threads: %d\n", count);
+ seq_printf(m, " requested threads: %d+%d/%d\n"
+ " ready threads %d\n"
+ " free async space %zd\n", proc->requested_threads,
+ proc->requested_threads_started, proc->max_threads,
+ proc->ready_threads, proc->free_async_space);
+ count = 0;
+ for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n))
+ count++;
+ seq_printf(m, " nodes: %d\n", count);
+ count = 0;
+ strong = 0;
+ weak = 0;
+ for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
+ struct binder_ref *ref = rb_entry(n, struct binder_ref,
+ rb_node_desc);
+ count++;
+ strong += ref->strong;
+ weak += ref->weak;
+ }
+ seq_printf(m, " refs: %d s %d w %d\n", count, strong, weak);
+
+ count = 0;
+ for (n = rb_first(&proc->allocated_buffers); n != NULL; n = rb_next(n))
+ count++;
+ seq_printf(m, " buffers: %d\n", count);
+
+ count = 0;
+ list_for_each_entry(w, &proc->todo, entry) {
+ switch (w->type) {
+ case BINDER_WORK_TRANSACTION:
+ count++;
+ break;
+ default:
+ break;
+ }
+ }
+ seq_printf(m, " pending transactions: %d\n", count);
+
+ print_binder_stats(m, " ", &proc->stats);
+}
+
+
+static int binder_state_show(struct seq_file *m, void *unused)
+{
+ struct binder_proc *proc;
+ struct hlist_node *pos;
+ struct binder_node *node;
+ int do_lock = !binder_debug_no_lock;
+
+ if (do_lock)
+ mutex_lock(&binder_lock);
+
+ seq_puts(m, "binder state:\n");
+
+ if (!hlist_empty(&binder_dead_nodes))
+ seq_puts(m, "dead nodes:\n");
+ hlist_for_each_entry(node, pos, &binder_dead_nodes, dead_node)
+ print_binder_node(m, node);
+
+ hlist_for_each_entry(proc, pos, &binder_procs, proc_node)
+ print_binder_proc(m, proc, 1);
+ if (do_lock)
+ mutex_unlock(&binder_lock);
+ return 0;
+}
+
+static int binder_stats_show(struct seq_file *m, void *unused)
+{
+ struct binder_proc *proc;
+ struct hlist_node *pos;
+ int do_lock = !binder_debug_no_lock;
+
+ if (do_lock)
+ mutex_lock(&binder_lock);
+
+ seq_puts(m, "binder stats:\n");
+
+ print_binder_stats(m, "", &binder_stats);
+
+ hlist_for_each_entry(proc, pos, &binder_procs, proc_node)
+ print_binder_proc_stats(m, proc);
+ if (do_lock)
+ mutex_unlock(&binder_lock);
+ return 0;
+}
+
+static int binder_transactions_show(struct seq_file *m, void *unused)
+{
+ struct binder_proc *proc;
+ struct hlist_node *pos;
+ int do_lock = !binder_debug_no_lock;
+
+ if (do_lock)
+ mutex_lock(&binder_lock);
+
+ seq_puts(m, "binder transactions:\n");
+ hlist_for_each_entry(proc, pos, &binder_procs, proc_node)
+ print_binder_proc(m, proc, 0);
+ if (do_lock)
+ mutex_unlock(&binder_lock);
+ return 0;
+}
+
+static int binder_proc_show(struct seq_file *m, void *unused)
+{
+ struct binder_proc *proc = m->private;
+ int do_lock = !binder_debug_no_lock;
+
+ if (do_lock)
+ mutex_lock(&binder_lock);
+ seq_puts(m, "binder proc state:\n");
+ print_binder_proc(m, proc, 1);
+ if (do_lock)
+ mutex_unlock(&binder_lock);
+ return 0;
+}
+
+static void print_binder_transaction_log_entry(struct seq_file *m,
+ struct binder_transaction_log_entry *e)
+{
+ seq_printf(m,
+ "%d: %s from %d:%d to %d:%d node %d handle %d size %d:%d\n",
+ e->debug_id, (e->call_type == 2) ? "reply" :
+ ((e->call_type == 1) ? "async" : "call "), e->from_proc,
+ e->from_thread, e->to_proc, e->to_thread, e->to_node,
+ e->target_handle, e->data_size, e->offsets_size);
+}
+
+static int binder_transaction_log_show(struct seq_file *m, void *unused)
+{
+ struct binder_transaction_log *log = m->private;
+ int i;
+
+ if (log->full) {
+ for (i = log->next; i < ARRAY_SIZE(log->entry); i++)
+ print_binder_transaction_log_entry(m, &log->entry[i]);
+ }
+ for (i = 0; i < log->next; i++)
+ print_binder_transaction_log_entry(m, &log->entry[i]);
+ return 0;
+}
+
+static const struct file_operations binder_fops = {
+ .owner = THIS_MODULE,
+ .poll = binder_poll,
+ .unlocked_ioctl = binder_ioctl,
+ .mmap = binder_mmap,
+ .open = binder_open,
+ .flush = binder_flush,
+ .release = binder_release,
+};
+
+static struct miscdevice binder_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "binder",
+ .fops = &binder_fops
+};
+
+BINDER_DEBUG_ENTRY(state);
+BINDER_DEBUG_ENTRY(stats);
+BINDER_DEBUG_ENTRY(transactions);
+BINDER_DEBUG_ENTRY(transaction_log);
+
+static int __init binder_init(void)
+{
+ int ret;
+
+ binder_deferred_workqueue = create_singlethread_workqueue("binder");
+ if (!binder_deferred_workqueue)
+ return -ENOMEM;
+
+ binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
+ if (binder_debugfs_dir_entry_root)
+ binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
+ binder_debugfs_dir_entry_root);
+ ret = misc_register(&binder_miscdev);
+ if (binder_debugfs_dir_entry_root) {
+ debugfs_create_file("state",
+ S_IRUGO,
+ binder_debugfs_dir_entry_root,
+ NULL,
+ &binder_state_fops);
+ debugfs_create_file("stats",
+ S_IRUGO,
+ binder_debugfs_dir_entry_root,
+ NULL,
+ &binder_stats_fops);
+ debugfs_create_file("transactions",
+ S_IRUGO,
+ binder_debugfs_dir_entry_root,
+ NULL,
+ &binder_transactions_fops);
+ debugfs_create_file("transaction_log",
+ S_IRUGO,
+ binder_debugfs_dir_entry_root,
+ &binder_transaction_log,
+ &binder_transaction_log_fops);
+ debugfs_create_file("failed_transaction_log",
+ S_IRUGO,
+ binder_debugfs_dir_entry_root,
+ &binder_transaction_log_failed,
+ &binder_transaction_log_fops);
+ }
+ return ret;
+}
+
+device_initcall(binder_init);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/android/binder.h b/drivers/staging/android/binder.h
new file mode 100644
index 00000000000..863ae1ad5d5
--- /dev/null
+++ b/drivers/staging/android/binder.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * Based on, but no longer compatible with, the original
+ * OpenBinder.org binder driver interface, which is:
+ *
+ * Copyright (c) 2005 Palmsource, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_BINDER_H
+#define _LINUX_BINDER_H
+
+#include <linux/ioctl.h>
+
+#define B_PACK_CHARS(c1, c2, c3, c4) \
+ ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
+#define B_TYPE_LARGE 0x85
+
+enum {
+ BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
+ BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
+ BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
+ BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
+ BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
+};
+
+enum {
+ FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff,
+ FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100,
+};
+
+/*
+ * This is the flattened representation of a Binder object for transfer
+ * between processes. The 'offsets' supplied as part of a binder transaction
+ * contains offsets into the data where these structures occur. The Binder
+ * driver takes care of re-writing the structure type and data as it moves
+ * between processes.
+ */
+struct flat_binder_object {
+ /* 8 bytes for large_flat_header. */
+ unsigned long type;
+ unsigned long flags;
+
+ /* 8 bytes of data. */
+ union {
+ void *binder; /* local object */
+ signed long handle; /* remote object */
+ };
+
+ /* extra data associated with local object */
+ void *cookie;
+};
+
+/*
+ * On 64-bit platforms where user code may run in 32-bits the driver must
+ * translate the buffer (and local binder) addresses apropriately.
+ */
+
+struct binder_write_read {
+ signed long write_size; /* bytes to write */
+ signed long write_consumed; /* bytes consumed by driver */
+ unsigned long write_buffer;
+ signed long read_size; /* bytes to read */
+ signed long read_consumed; /* bytes consumed by driver */
+ unsigned long read_buffer;
+};
+
+/* Use with BINDER_VERSION, driver fills in fields. */
+struct binder_version {
+ /* driver protocol version -- increment with incompatible change */
+ signed long protocol_version;
+};
+
+/* This is the current protocol version. */
+#define BINDER_CURRENT_PROTOCOL_VERSION 7
+
+#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
+#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t)
+#define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t)
+#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int)
+#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)
+#define BINDER_THREAD_EXIT _IOW('b', 8, int)
+#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
+
+/*
+ * NOTE: Two special error codes you should check for when calling
+ * in to the driver are:
+ *
+ * EINTR -- The operation has been interupted. This should be
+ * handled by retrying the ioctl() until a different error code
+ * is returned.
+ *
+ * ECONNREFUSED -- The driver is no longer accepting operations
+ * from your process. That is, the process is being destroyed.
+ * You should handle this by exiting from your process. Note
+ * that once this error code is returned, all further calls to
+ * the driver from any thread will return this same code.
+ */
+
+enum transaction_flags {
+ TF_ONE_WAY = 0x01, /* this is a one-way call: async, no return */
+ TF_ROOT_OBJECT = 0x04, /* contents are the component's root object */
+ TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */
+ TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */
+};
+
+struct binder_transaction_data {
+ /* The first two are only used for bcTRANSACTION and brTRANSACTION,
+ * identifying the target and contents of the transaction.
+ */
+ union {
+ size_t handle; /* target descriptor of command transaction */
+ void *ptr; /* target descriptor of return transaction */
+ } target;
+ void *cookie; /* target object cookie */
+ unsigned int code; /* transaction command */
+
+ /* General information about the transaction. */
+ unsigned int flags;
+ pid_t sender_pid;
+ uid_t sender_euid;
+ size_t data_size; /* number of bytes of data */
+ size_t offsets_size; /* number of bytes of offsets */
+
+ /* If this transaction is inline, the data immediately
+ * follows here; otherwise, it ends with a pointer to
+ * the data buffer.
+ */
+ union {
+ struct {
+ /* transaction data */
+ const void *buffer;
+ /* offsets from buffer to flat_binder_object structs */
+ const void *offsets;
+ } ptr;
+ uint8_t buf[8];
+ } data;
+};
+
+struct binder_ptr_cookie {
+ void *ptr;
+ void *cookie;
+};
+
+struct binder_pri_desc {
+ int priority;
+ int desc;
+};
+
+struct binder_pri_ptr_cookie {
+ int priority;
+ void *ptr;
+ void *cookie;
+};
+
+enum BinderDriverReturnProtocol {
+ BR_ERROR = _IOR('r', 0, int),
+ /*
+ * int: error code
+ */
+
+ BR_OK = _IO('r', 1),
+ /* No parameters! */
+
+ BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data),
+ BR_REPLY = _IOR('r', 3, struct binder_transaction_data),
+ /*
+ * binder_transaction_data: the received command.
+ */
+
+ BR_ACQUIRE_RESULT = _IOR('r', 4, int),
+ /*
+ * not currently supported
+ * int: 0 if the last bcATTEMPT_ACQUIRE was not successful.
+ * Else the remote object has acquired a primary reference.
+ */
+
+ BR_DEAD_REPLY = _IO('r', 5),
+ /*
+ * The target of the last transaction (either a bcTRANSACTION or
+ * a bcATTEMPT_ACQUIRE) is no longer with us. No parameters.
+ */
+
+ BR_TRANSACTION_COMPLETE = _IO('r', 6),
+ /*
+ * No parameters... always refers to the last transaction requested
+ * (including replies). Note that this will be sent even for
+ * asynchronous transactions.
+ */
+
+ BR_INCREFS = _IOR('r', 7, struct binder_ptr_cookie),
+ BR_ACQUIRE = _IOR('r', 8, struct binder_ptr_cookie),
+ BR_RELEASE = _IOR('r', 9, struct binder_ptr_cookie),
+ BR_DECREFS = _IOR('r', 10, struct binder_ptr_cookie),
+ /*
+ * void *: ptr to binder
+ * void *: cookie for binder
+ */
+
+ BR_ATTEMPT_ACQUIRE = _IOR('r', 11, struct binder_pri_ptr_cookie),
+ /*
+ * not currently supported
+ * int: priority
+ * void *: ptr to binder
+ * void *: cookie for binder
+ */
+
+ BR_NOOP = _IO('r', 12),
+ /*
+ * No parameters. Do nothing and examine the next command. It exists
+ * primarily so that we can replace it with a BR_SPAWN_LOOPER command.
+ */
+
+ BR_SPAWN_LOOPER = _IO('r', 13),
+ /*
+ * No parameters. The driver has determined that a process has no
+ * threads waiting to service incomming transactions. When a process
+ * receives this command, it must spawn a new service thread and
+ * register it via bcENTER_LOOPER.
+ */
+
+ BR_FINISHED = _IO('r', 14),
+ /*
+ * not currently supported
+ * stop threadpool thread
+ */
+
+ BR_DEAD_BINDER = _IOR('r', 15, void *),
+ /*
+ * void *: cookie
+ */
+ BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, void *),
+ /*
+ * void *: cookie
+ */
+
+ BR_FAILED_REPLY = _IO('r', 17),
+ /*
+ * The the last transaction (either a bcTRANSACTION or
+ * a bcATTEMPT_ACQUIRE) failed (e.g. out of memory). No parameters.
+ */
+};
+
+enum BinderDriverCommandProtocol {
+ BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
+ BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
+ /*
+ * binder_transaction_data: the sent command.
+ */
+
+ BC_ACQUIRE_RESULT = _IOW('c', 2, int),
+ /*
+ * not currently supported
+ * int: 0 if the last BR_ATTEMPT_ACQUIRE was not successful.
+ * Else you have acquired a primary reference on the object.
+ */
+
+ BC_FREE_BUFFER = _IOW('c', 3, int),
+ /*
+ * void *: ptr to transaction data received on a read
+ */
+
+ BC_INCREFS = _IOW('c', 4, int),
+ BC_ACQUIRE = _IOW('c', 5, int),
+ BC_RELEASE = _IOW('c', 6, int),
+ BC_DECREFS = _IOW('c', 7, int),
+ /*
+ * int: descriptor
+ */
+
+ BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie),
+ BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie),
+ /*
+ * void *: ptr to binder
+ * void *: cookie for binder
+ */
+
+ BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc),
+ /*
+ * not currently supported
+ * int: priority
+ * int: descriptor
+ */
+
+ BC_REGISTER_LOOPER = _IO('c', 11),
+ /*
+ * No parameters.
+ * Register a spawned looper thread with the device.
+ */
+
+ BC_ENTER_LOOPER = _IO('c', 12),
+ BC_EXIT_LOOPER = _IO('c', 13),
+ /*
+ * No parameters.
+ * These two commands are sent as an application-level thread
+ * enters and exits the binder loop, respectively. They are
+ * used so the binder can have an accurate count of the number
+ * of looping threads it has available.
+ */
+
+ BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_ptr_cookie),
+ /*
+ * void *: ptr to binder
+ * void *: cookie
+ */
+
+ BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_ptr_cookie),
+ /*
+ * void *: ptr to binder
+ * void *: cookie
+ */
+
+ BC_DEAD_BINDER_DONE = _IOW('c', 16, void *),
+ /*
+ * void *: cookie
+ */
+};
+
+#endif /* _LINUX_BINDER_H */
+
diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c
new file mode 100644
index 00000000000..531bdbeede8
--- /dev/null
+++ b/drivers/staging/android/logger.c
@@ -0,0 +1,616 @@
+/*
+ * drivers/misc/logger.c
+ *
+ * A Logging Subsystem
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * Robert Love <rlove@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include "logger.h"
+
+#include <asm/ioctls.h>
+
+/*
+ * struct logger_log - represents a specific log, such as 'main' or 'radio'
+ *
+ * This structure lives from module insertion until module removal, so it does
+ * not need additional reference counting. The structure is protected by the
+ * mutex 'mutex'.
+ */
+struct logger_log {
+ unsigned char *buffer;/* the ring buffer itself */
+ struct miscdevice misc; /* misc device representing the log */
+ wait_queue_head_t wq; /* wait queue for readers */
+ struct list_head readers; /* this log's readers */
+ struct mutex mutex; /* mutex protecting buffer */
+ size_t w_off; /* current write head offset */
+ size_t head; /* new readers start here */
+ size_t size; /* size of the log */
+};
+
+/*
+ * struct logger_reader - a logging device open for reading
+ *
+ * This object lives from open to release, so we don't need additional
+ * reference counting. The structure is protected by log->mutex.
+ */
+struct logger_reader {
+ struct logger_log *log; /* associated log */
+ struct list_head list; /* entry in logger_log's list */
+ size_t r_off; /* current read head offset */
+};
+
+/* logger_offset - returns index 'n' into the log via (optimized) modulus */
+#define logger_offset(n) ((n) & (log->size - 1))
+
+/*
+ * file_get_log - Given a file structure, return the associated log
+ *
+ * This isn't aesthetic. We have several goals:
+ *
+ * 1) Need to quickly obtain the associated log during an I/O operation
+ * 2) Readers need to maintain state (logger_reader)
+ * 3) Writers need to be very fast (open() should be a near no-op)
+ *
+ * In the reader case, we can trivially go file->logger_reader->logger_log.
+ * For a writer, we don't want to maintain a logger_reader, so we just go
+ * file->logger_log. Thus what file->private_data points at depends on whether
+ * or not the file was opened for reading. This function hides that dirtiness.
+ */
+static inline struct logger_log *file_get_log(struct file *file)
+{
+ if (file->f_mode & FMODE_READ) {
+ struct logger_reader *reader = file->private_data;
+ return reader->log;
+ } else
+ return file->private_data;
+}
+
+/*
+ * get_entry_len - Grabs the length of the payload of the next entry starting
+ * from 'off'.
+ *
+ * Caller needs to hold log->mutex.
+ */
+static __u32 get_entry_len(struct logger_log *log, size_t off)
+{
+ __u16 val;
+
+ switch (log->size - off) {
+ case 1:
+ memcpy(&val, log->buffer + off, 1);
+ memcpy(((char *) &val) + 1, log->buffer, 1);
+ break;
+ default:
+ memcpy(&val, log->buffer + off, 2);
+ }
+
+ return sizeof(struct logger_entry) + val;
+}
+
+/*
+ * do_read_log_to_user - reads exactly 'count' bytes from 'log' into the
+ * user-space buffer 'buf'. Returns 'count' on success.
+ *
+ * Caller must hold log->mutex.
+ */
+static ssize_t do_read_log_to_user(struct logger_log *log,
+ struct logger_reader *reader,
+ char __user *buf,
+ size_t count)
+{
+ size_t len;
+
+ /*
+ * We read from the log in two disjoint operations. First, we read from
+ * the current read head offset up to 'count' bytes or to the end of
+ * the log, whichever comes first.
+ */
+ len = min(count, log->size - reader->r_off);
+ if (copy_to_user(buf, log->buffer + reader->r_off, len))
+ return -EFAULT;
+
+ /*
+ * Second, we read any remaining bytes, starting back at the head of
+ * the log.
+ */
+ if (count != len)
+ if (copy_to_user(buf + len, log->buffer, count - len))
+ return -EFAULT;
+
+ reader->r_off = logger_offset(reader->r_off + count);
+
+ return count;
+}
+
+/*
+ * logger_read - our log's read() method
+ *
+ * Behavior:
+ *
+ * - O_NONBLOCK works
+ * - If there are no log entries to read, blocks until log is written to
+ * - Atomically reads exactly one log entry
+ *
+ * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read
+ * buffer is insufficient to hold next entry.
+ */
+static ssize_t logger_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct logger_reader *reader = file->private_data;
+ struct logger_log *log = reader->log;
+ ssize_t ret;
+ DEFINE_WAIT(wait);
+
+start:
+ while (1) {
+ prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);
+
+ mutex_lock(&log->mutex);
+ ret = (log->w_off == reader->r_off);
+ mutex_unlock(&log->mutex);
+ if (!ret)
+ break;
+
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+
+ schedule();
+ }
+
+ finish_wait(&log->wq, &wait);
+ if (ret)
+ return ret;
+
+ mutex_lock(&log->mutex);
+
+ /* is there still something to read or did we race? */
+ if (unlikely(log->w_off == reader->r_off)) {
+ mutex_unlock(&log->mutex);
+ goto start;
+ }
+
+ /* get the size of the next entry */
+ ret = get_entry_len(log, reader->r_off);
+ if (count < ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* get exactly one entry from the log */
+ ret = do_read_log_to_user(log, reader, buf, ret);
+
+out:
+ mutex_unlock(&log->mutex);
+
+ return ret;
+}
+
+/*
+ * get_next_entry - return the offset of the first valid entry at least 'len'
+ * bytes after 'off'.
+ *
+ * Caller must hold log->mutex.
+ */
+static size_t get_next_entry(struct logger_log *log, size_t off, size_t len)
+{
+ size_t count = 0;
+
+ do {
+ size_t nr = get_entry_len(log, off);
+ off = logger_offset(off + nr);
+ count += nr;
+ } while (count < len);
+
+ return off;
+}
+
+/*
+ * clock_interval - is a < c < b in mod-space? Put another way, does the line
+ * from a to b cross c?
+ */
+static inline int clock_interval(size_t a, size_t b, size_t c)
+{
+ if (b < a) {
+ if (a < c || b >= c)
+ return 1;
+ } else {
+ if (a < c && b >= c)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * fix_up_readers - walk the list of all readers and "fix up" any who were
+ * lapped by the writer; also do the same for the default "start head".
+ * We do this by "pulling forward" the readers and start head to the first
+ * entry after the new write head.
+ *
+ * The caller needs to hold log->mutex.
+ */
+static void fix_up_readers(struct logger_log *log, size_t len)
+{
+ size_t old = log->w_off;
+ size_t new = logger_offset(old + len);
+ struct logger_reader *reader;
+
+ if (clock_interval(old, new, log->head))
+ log->head = get_next_entry(log, log->head, len);
+
+ list_for_each_entry(reader, &log->readers, list)
+ if (clock_interval(old, new, reader->r_off))
+ reader->r_off = get_next_entry(log, reader->r_off, len);
+}
+
+/*
+ * do_write_log - writes 'len' bytes from 'buf' to 'log'
+ *
+ * The caller needs to hold log->mutex.
+ */
+static void do_write_log(struct logger_log *log, const void *buf, size_t count)
+{
+ size_t len;
+
+ len = min(count, log->size - log->w_off);
+ memcpy(log->buffer + log->w_off, buf, len);
+
+ if (count != len)
+ memcpy(log->buffer, buf + len, count - len);
+
+ log->w_off = logger_offset(log->w_off + count);
+
+}
+
+/*
+ * do_write_log_user - writes 'len' bytes from the user-space buffer 'buf' to
+ * the log 'log'
+ *
+ * The caller needs to hold log->mutex.
+ *
+ * Returns 'count' on success, negative error code on failure.
+ */
+static ssize_t do_write_log_from_user(struct logger_log *log,
+ const void __user *buf, size_t count)
+{
+ size_t len;
+
+ len = min(count, log->size - log->w_off);
+ if (len && copy_from_user(log->buffer + log->w_off, buf, len))
+ return -EFAULT;
+
+ if (count != len)
+ if (copy_from_user(log->buffer, buf + len, count - len))
+ return -EFAULT;
+
+ log->w_off = logger_offset(log->w_off + count);
+
+ return count;
+}
+
+/*
+ * logger_aio_write - our write method, implementing support for write(),
+ * writev(), and aio_write(). Writes are our fast path, and we try to optimize
+ * them above all else.
+ */
+ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t ppos)
+{
+ struct logger_log *log = file_get_log(iocb->ki_filp);
+ size_t orig = log->w_off;
+ struct logger_entry header;
+ struct timespec now;
+ ssize_t ret = 0;
+
+ now = current_kernel_time();
+
+ header.pid = current->tgid;
+ header.tid = current->pid;
+ header.sec = now.tv_sec;
+ header.nsec = now.tv_nsec;
+ header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);
+
+ /* null writes succeed, return zero */
+ if (unlikely(!header.len))
+ return 0;
+
+ mutex_lock(&log->mutex);
+
+ /*
+ * Fix up any readers, pulling them forward to the first readable
+ * entry after (what will be) the new write offset. We do this now
+ * because if we partially fail, we can end up with clobbered log
+ * entries that encroach on readable buffer.
+ */
+ fix_up_readers(log, sizeof(struct logger_entry) + header.len);
+
+ do_write_log(log, &header, sizeof(struct logger_entry));
+
+ while (nr_segs-- > 0) {
+ size_t len;
+ ssize_t nr;
+
+ /* figure out how much of this vector we can keep */
+ len = min_t(size_t, iov->iov_len, header.len - ret);
+
+ /* write out this segment's payload */
+ nr = do_write_log_from_user(log, iov->iov_base, len);
+ if (unlikely(nr < 0)) {
+ log->w_off = orig;
+ mutex_unlock(&log->mutex);
+ return nr;
+ }
+
+ iov++;
+ ret += nr;
+ }
+
+ mutex_unlock(&log->mutex);
+
+ /* wake up any blocked readers */
+ wake_up_interruptible(&log->wq);
+
+ return ret;
+}
+
+static struct logger_log *get_log_from_minor(int);
+
+/*
+ * logger_open - the log's open() file operation
+ *
+ * Note how near a no-op this is in the write-only case. Keep it that way!
+ */
+static int logger_open(struct inode *inode, struct file *file)
+{
+ struct logger_log *log;
+ int ret;
+
+ ret = nonseekable_open(inode, file);
+ if (ret)
+ return ret;
+
+ log = get_log_from_minor(MINOR(inode->i_rdev));
+ if (!log)
+ return -ENODEV;
+
+ if (file->f_mode & FMODE_READ) {
+ struct logger_reader *reader;
+
+ reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL);
+ if (!reader)
+ return -ENOMEM;
+
+ reader->log = log;
+ INIT_LIST_HEAD(&reader->list);
+
+ mutex_lock(&log->mutex);
+ reader->r_off = log->head;
+ list_add_tail(&reader->list, &log->readers);
+ mutex_unlock(&log->mutex);
+
+ file->private_data = reader;
+ } else
+ file->private_data = log;
+
+ return 0;
+}
+
+/*
+ * logger_release - the log's release file operation
+ *
+ * Note this is a total no-op in the write-only case. Keep it that way!
+ */
+static int logger_release(struct inode *ignored, struct file *file)
+{
+ if (file->f_mode & FMODE_READ) {
+ struct logger_reader *reader = file->private_data;
+ list_del(&reader->list);
+ kfree(reader);
+ }
+
+ return 0;
+}
+
+/*
+ * logger_poll - the log's poll file operation, for poll/select/epoll
+ *
+ * Note we always return POLLOUT, because you can always write() to the log.
+ * Note also that, strictly speaking, a return value of POLLIN does not
+ * guarantee that the log is readable without blocking, as there is a small
+ * chance that the writer can lap the reader in the interim between poll()
+ * returning and the read() request.
+ */
+static unsigned int logger_poll(struct file *file, poll_table *wait)
+{
+ struct logger_reader *reader;
+ struct logger_log *log;
+ unsigned int ret = POLLOUT | POLLWRNORM;
+
+ if (!(file->f_mode & FMODE_READ))
+ return ret;
+
+ reader = file->private_data;
+ log = reader->log;
+
+ poll_wait(file, &log->wq, wait);
+
+ mutex_lock(&log->mutex);
+ if (log->w_off != reader->r_off)
+ ret |= POLLIN | POLLRDNORM;
+ mutex_unlock(&log->mutex);
+
+ return ret;
+}
+
+static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct logger_log *log = file_get_log(file);
+ struct logger_reader *reader;
+ long ret = -ENOTTY;
+
+ mutex_lock(&log->mutex);
+
+ switch (cmd) {
+ case LOGGER_GET_LOG_BUF_SIZE:
+ ret = log->size;
+ break;
+ case LOGGER_GET_LOG_LEN:
+ if (!(file->f_mode & FMODE_READ)) {
+ ret = -EBADF;
+ break;
+ }
+ reader = file->private_data;
+ if (log->w_off >= reader->r_off)
+ ret = log->w_off - reader->r_off;
+ else
+ ret = (log->size - reader->r_off) + log->w_off;
+ break;
+ case LOGGER_GET_NEXT_ENTRY_LEN:
+ if (!(file->f_mode & FMODE_READ)) {
+ ret = -EBADF;
+ break;
+ }
+ reader = file->private_data;
+ if (log->w_off != reader->r_off)
+ ret = get_entry_len(log, reader->r_off);
+ else
+ ret = 0;
+ break;
+ case LOGGER_FLUSH_LOG:
+ if (!(file->f_mode & FMODE_WRITE)) {
+ ret = -EBADF;
+ break;
+ }
+ list_for_each_entry(reader, &log->readers, list)
+ reader->r_off = log->w_off;
+ log->head = log->w_off;
+ ret = 0;
+ break;
+ }
+
+ mutex_unlock(&log->mutex);
+
+ return ret;
+}
+
+static const struct file_operations logger_fops = {
+ .owner = THIS_MODULE,
+ .read = logger_read,
+ .aio_write = logger_aio_write,
+ .poll = logger_poll,
+ .unlocked_ioctl = logger_ioctl,
+ .compat_ioctl = logger_ioctl,
+ .open = logger_open,
+ .release = logger_release,
+};
+
+/*
+ * Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which
+ * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than
+ * LONG_MAX minus LOGGER_ENTRY_MAX_LEN.
+ */
+#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \
+static unsigned char _buf_ ## VAR[SIZE]; \
+static struct logger_log VAR = { \
+ .buffer = _buf_ ## VAR, \
+ .misc = { \
+ .minor = MISC_DYNAMIC_MINOR, \
+ .name = NAME, \
+ .fops = &logger_fops, \
+ .parent = NULL, \
+ }, \
+ .wq = __WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), \
+ .readers = LIST_HEAD_INIT(VAR .readers), \
+ .mutex = __MUTEX_INITIALIZER(VAR .mutex), \
+ .w_off = 0, \
+ .head = 0, \
+ .size = SIZE, \
+};
+
+DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024)
+DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 256*1024)
+DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024)
+DEFINE_LOGGER_DEVICE(log_system, LOGGER_LOG_SYSTEM, 64*1024)
+
+static struct logger_log *get_log_from_minor(int minor)
+{
+ if (log_main.misc.minor == minor)
+ return &log_main;
+ if (log_events.misc.minor == minor)
+ return &log_events;
+ if (log_radio.misc.minor == minor)
+ return &log_radio;
+ if (log_system.misc.minor == minor)
+ return &log_system;
+ return NULL;
+}
+
+static int __init init_log(struct logger_log *log)
+{
+ int ret;
+
+ ret = misc_register(&log->misc);
+ if (unlikely(ret)) {
+ printk(KERN_ERR "logger: failed to register misc "
+ "device for log '%s'!\n", log->misc.name);
+ return ret;
+ }
+
+ printk(KERN_INFO "logger: created %luK log '%s'\n",
+ (unsigned long) log->size >> 10, log->misc.name);
+
+ return 0;
+}
+
+static int __init logger_init(void)
+{
+ int ret;
+
+ ret = init_log(&log_main);
+ if (unlikely(ret))
+ goto out;
+
+ ret = init_log(&log_events);
+ if (unlikely(ret))
+ goto out;
+
+ ret = init_log(&log_radio);
+ if (unlikely(ret))
+ goto out;
+
+ ret = init_log(&log_system);
+ if (unlikely(ret))
+ goto out;
+
+out:
+ return ret;
+}
+device_initcall(logger_init);
diff --git a/drivers/staging/android/logger.h b/drivers/staging/android/logger.h
new file mode 100644
index 00000000000..2cb06e9d8f9
--- /dev/null
+++ b/drivers/staging/android/logger.h
@@ -0,0 +1,49 @@
+/* include/linux/logger.h
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ * Author: Robert Love <rlove@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_LOGGER_H
+#define _LINUX_LOGGER_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+struct logger_entry {
+ __u16 len; /* length of the payload */
+ __u16 __pad; /* no matter what, we get 2 bytes of padding */
+ __s32 pid; /* generating process's pid */
+ __s32 tid; /* generating process's tid */
+ __s32 sec; /* seconds since Epoch */
+ __s32 nsec; /* nanoseconds */
+ char msg[0]; /* the entry's payload */
+};
+
+#define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */
+#define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */
+#define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */
+#define LOGGER_LOG_MAIN "log_main" /* everything else */
+
+#define LOGGER_ENTRY_MAX_LEN (4*1024)
+#define LOGGER_ENTRY_MAX_PAYLOAD \
+ (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))
+
+#define __LOGGERIO 0xAE
+
+#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */
+#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */
+#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */
+#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */
+
+#endif /* _LINUX_LOGGER_H */
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
new file mode 100644
index 00000000000..897bc3ab966
--- /dev/null
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -0,0 +1,213 @@
+/* drivers/misc/lowmemorykiller.c
+ *
+ * The lowmemorykiller driver lets user-space specify a set of memory thresholds
+ * where processes with a range of oom_adj values will get killed. Specify the
+ * minimum oom_adj values in /sys/module/lowmemorykiller/parameters/adj and the
+ * number of free pages in /sys/module/lowmemorykiller/parameters/minfree. Both
+ * files take a comma separated list of numbers in ascending order.
+ *
+ * For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and
+ * "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill processes
+ * with a oom_adj value of 8 or higher when the free memory drops below 4096 pages
+ * and kill processes with a oom_adj value of 0 or higher when the free memory
+ * drops below 1024 pages.
+ *
+ * The driver considers memory used for caches to be free, but if a large
+ * percentage of the cached memory is locked this can be very inaccurate
+ * and processes may not get killed until the normal oom killer is triggered.
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/oom.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+
+static uint32_t lowmem_debug_level = 2;
+static int lowmem_adj[6] = {
+ 0,
+ 1,
+ 6,
+ 12,
+};
+static int lowmem_adj_size = 4;
+static size_t lowmem_minfree[6] = {
+ 3 * 512, /* 6MB */
+ 2 * 1024, /* 8MB */
+ 4 * 1024, /* 16MB */
+ 16 * 1024, /* 64MB */
+};
+static int lowmem_minfree_size = 4;
+
+static struct task_struct *lowmem_deathpending;
+static unsigned long lowmem_deathpending_timeout;
+
+#define lowmem_print(level, x...) \
+ do { \
+ if (lowmem_debug_level >= (level)) \
+ printk(x); \
+ } while (0)
+
+static int
+task_notify_func(struct notifier_block *self, unsigned long val, void *data);
+
+static struct notifier_block task_nb = {
+ .notifier_call = task_notify_func,
+};
+
+static int
+task_notify_func(struct notifier_block *self, unsigned long val, void *data)
+{
+ struct task_struct *task = data;
+
+ if (task == lowmem_deathpending)
+ lowmem_deathpending = NULL;
+
+ return NOTIFY_OK;
+}
+
+static int lowmem_shrink(struct shrinker *s, int nr_to_scan, gfp_t gfp_mask)
+{
+ struct task_struct *p;
+ struct task_struct *selected = NULL;
+ int rem = 0;
+ int tasksize;
+ int i;
+ int min_adj = OOM_ADJUST_MAX + 1;
+ int selected_tasksize = 0;
+ int selected_oom_adj;
+ int array_size = ARRAY_SIZE(lowmem_adj);
+ int other_free = global_page_state(NR_FREE_PAGES);
+ int other_file = global_page_state(NR_FILE_PAGES) -
+ global_page_state(NR_SHMEM);
+
+ /*
+ * If we already have a death outstanding, then
+ * bail out right away; indicating to vmscan
+ * that we have nothing further to offer on
+ * this pass.
+ *
+ */
+ if (lowmem_deathpending &&
+ time_before_eq(jiffies, lowmem_deathpending_timeout))
+ return 0;
+
+ if (lowmem_adj_size < array_size)
+ array_size = lowmem_adj_size;
+ if (lowmem_minfree_size < array_size)
+ array_size = lowmem_minfree_size;
+ for (i = 0; i < array_size; i++) {
+ if (other_free < lowmem_minfree[i] &&
+ other_file < lowmem_minfree[i]) {
+ min_adj = lowmem_adj[i];
+ break;
+ }
+ }
+ if (nr_to_scan > 0)
+ lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n",
+ nr_to_scan, gfp_mask, other_free, other_file,
+ min_adj);
+ rem = global_page_state(NR_ACTIVE_ANON) +
+ global_page_state(NR_ACTIVE_FILE) +
+ global_page_state(NR_INACTIVE_ANON) +
+ global_page_state(NR_INACTIVE_FILE);
+ if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
+ lowmem_print(5, "lowmem_shrink %d, %x, return %d\n",
+ nr_to_scan, gfp_mask, rem);
+ return rem;
+ }
+ selected_oom_adj = min_adj;
+
+ read_lock(&tasklist_lock);
+ for_each_process(p) {
+ struct mm_struct *mm;
+ struct signal_struct *sig;
+ int oom_adj;
+
+ task_lock(p);
+ mm = p->mm;
+ sig = p->signal;
+ if (!mm || !sig) {
+ task_unlock(p);
+ continue;
+ }
+ oom_adj = sig->oom_adj;
+ if (oom_adj < min_adj) {
+ task_unlock(p);
+ continue;
+ }
+ tasksize = get_mm_rss(mm);
+ task_unlock(p);
+ if (tasksize <= 0)
+ continue;
+ if (selected) {
+ if (oom_adj < selected_oom_adj)
+ continue;
+ if (oom_adj == selected_oom_adj &&
+ tasksize <= selected_tasksize)
+ continue;
+ }
+ selected = p;
+ selected_tasksize = tasksize;
+ selected_oom_adj = oom_adj;
+ lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
+ p->pid, p->comm, oom_adj, tasksize);
+ }
+ if (selected) {
+ lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
+ selected->pid, selected->comm,
+ selected_oom_adj, selected_tasksize);
+ lowmem_deathpending = selected;
+ lowmem_deathpending_timeout = jiffies + HZ;
+ force_sig(SIGKILL, selected);
+ rem -= selected_tasksize;
+ }
+ lowmem_print(4, "lowmem_shrink %d, %x, return %d\n",
+ nr_to_scan, gfp_mask, rem);
+ read_unlock(&tasklist_lock);
+ return rem;
+}
+
+static struct shrinker lowmem_shrinker = {
+ .shrink = lowmem_shrink,
+ .seeks = DEFAULT_SEEKS * 16
+};
+
+static int __init lowmem_init(void)
+{
+ task_free_register(&task_nb);
+ register_shrinker(&lowmem_shrinker);
+ return 0;
+}
+
+static void __exit lowmem_exit(void)
+{
+ unregister_shrinker(&lowmem_shrinker);
+ task_free_unregister(&task_nb);
+}
+
+module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR);
+module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size,
+ S_IRUGO | S_IWUSR);
+module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size,
+ S_IRUGO | S_IWUSR);
+module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
+
+module_init(lowmem_init);
+module_exit(lowmem_exit);
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/staging/android/ram_console.c b/drivers/staging/android/ram_console.c
new file mode 100644
index 00000000000..53f736b0ec8
--- /dev/null
+++ b/drivers/staging/android/ram_console.c
@@ -0,0 +1,418 @@
+/* drivers/android/ram_console.c
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+#include <linux/rslib.h>
+#endif
+
+struct ram_console_buffer {
+ uint32_t sig;
+ uint32_t start;
+ uint32_t size;
+ uint8_t data[0];
+};
+
+#define RAM_CONSOLE_SIG (0x43474244) /* DBGC */
+
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT
+static char __initdata
+ ram_console_old_log_init_buffer[CONFIG_ANDROID_RAM_CONSOLE_EARLY_SIZE];
+#endif
+static char *ram_console_old_log;
+static size_t ram_console_old_log_size;
+
+static struct ram_console_buffer *ram_console_buffer;
+static size_t ram_console_buffer_size;
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+static char *ram_console_par_buffer;
+static struct rs_control *ram_console_rs_decoder;
+static int ram_console_corrected_bytes;
+static int ram_console_bad_blocks;
+#define ECC_BLOCK_SIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE
+#define ECC_SIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE
+#define ECC_SYMSIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE
+#define ECC_POLY CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL
+#endif
+
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+static void ram_console_encode_rs8(uint8_t *data, size_t len, uint8_t *ecc)
+{
+ int i;
+ uint16_t par[ECC_SIZE];
+ /* Initialize the parity buffer */
+ memset(par, 0, sizeof(par));
+ encode_rs8(ram_console_rs_decoder, data, len, par, 0);
+ for (i = 0; i < ECC_SIZE; i++)
+ ecc[i] = par[i];
+}
+
+static int ram_console_decode_rs8(void *data, size_t len, uint8_t *ecc)
+{
+ int i;
+ uint16_t par[ECC_SIZE];
+ for (i = 0; i < ECC_SIZE; i++)
+ par[i] = ecc[i];
+ return decode_rs8(ram_console_rs_decoder, data, par, len,
+ NULL, 0, NULL, 0, NULL);
+}
+#endif
+
+static void ram_console_update(const char *s, unsigned int count)
+{
+ struct ram_console_buffer *buffer = ram_console_buffer;
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+ uint8_t *buffer_end = buffer->data + ram_console_buffer_size;
+ uint8_t *block;
+ uint8_t *par;
+ int size = ECC_BLOCK_SIZE;
+#endif
+ memcpy(buffer->data + buffer->start, s, count);
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+ block = buffer->data + (buffer->start & ~(ECC_BLOCK_SIZE - 1));
+ par = ram_console_par_buffer +
+ (buffer->start / ECC_BLOCK_SIZE) * ECC_SIZE;
+ do {
+ if (block + ECC_BLOCK_SIZE > buffer_end)
+ size = buffer_end - block;
+ ram_console_encode_rs8(block, size, par);
+ block += ECC_BLOCK_SIZE;
+ par += ECC_SIZE;
+ } while (block < buffer->data + buffer->start + count);
+#endif
+}
+
+static void ram_console_update_header(void)
+{
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+ struct ram_console_buffer *buffer = ram_console_buffer;
+ uint8_t *par;
+ par = ram_console_par_buffer +
+ DIV_ROUND_UP(ram_console_buffer_size, ECC_BLOCK_SIZE) * ECC_SIZE;
+ ram_console_encode_rs8((uint8_t *)buffer, sizeof(*buffer), par);
+#endif
+}
+
+static void
+ram_console_write(struct console *console, const char *s, unsigned int count)
+{
+ int rem;
+ struct ram_console_buffer *buffer = ram_console_buffer;
+
+ if (count > ram_console_buffer_size) {
+ s += count - ram_console_buffer_size;
+ count = ram_console_buffer_size;
+ }
+ rem = ram_console_buffer_size - buffer->start;
+ if (rem < count) {
+ ram_console_update(s, rem);
+ s += rem;
+ count -= rem;
+ buffer->start = 0;
+ buffer->size = ram_console_buffer_size;
+ }
+ ram_console_update(s, count);
+
+ buffer->start += count;
+ if (buffer->size < ram_console_buffer_size)
+ buffer->size += count;
+ ram_console_update_header();
+}
+
+static struct console ram_console = {
+ .name = "ram",
+ .write = ram_console_write,
+ .flags = CON_PRINTBUFFER | CON_ENABLED,
+ .index = -1,
+};
+
+void ram_console_enable_console(int enabled)
+{
+ if (enabled)
+ ram_console.flags |= CON_ENABLED;
+ else
+ ram_console.flags &= ~CON_ENABLED;
+}
+
+static void __init
+ram_console_save_old(struct ram_console_buffer *buffer, char *dest)
+{
+ size_t old_log_size = buffer->size;
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+ uint8_t *block;
+ uint8_t *par;
+ char strbuf[80];
+ int strbuf_len;
+
+ block = buffer->data;
+ par = ram_console_par_buffer;
+ while (block < buffer->data + buffer->size) {
+ int numerr;
+ int size = ECC_BLOCK_SIZE;
+ if (block + size > buffer->data + ram_console_buffer_size)
+ size = buffer->data + ram_console_buffer_size - block;
+ numerr = ram_console_decode_rs8(block, size, par);
+ if (numerr > 0) {
+#if 0
+ printk(KERN_INFO "ram_console: error in block %p, %d\n",
+ block, numerr);
+#endif
+ ram_console_corrected_bytes += numerr;
+ } else if (numerr < 0) {
+#if 0
+ printk(KERN_INFO "ram_console: uncorrectable error in "
+ "block %p\n", block);
+#endif
+ ram_console_bad_blocks++;
+ }
+ block += ECC_BLOCK_SIZE;
+ par += ECC_SIZE;
+ }
+ if (ram_console_corrected_bytes || ram_console_bad_blocks)
+ strbuf_len = snprintf(strbuf, sizeof(strbuf),
+ "\n%d Corrected bytes, %d unrecoverable blocks\n",
+ ram_console_corrected_bytes, ram_console_bad_blocks);
+ else
+ strbuf_len = snprintf(strbuf, sizeof(strbuf),
+ "\nNo errors detected\n");
+ if (strbuf_len >= sizeof(strbuf))
+ strbuf_len = sizeof(strbuf) - 1;
+ old_log_size += strbuf_len;
+#endif
+
+ if (dest == NULL) {
+ dest = kmalloc(old_log_size, GFP_KERNEL);
+ if (dest == NULL) {
+ printk(KERN_ERR
+ "ram_console: failed to allocate buffer\n");
+ return;
+ }
+ }
+
+ ram_console_old_log = dest;
+ ram_console_old_log_size = old_log_size;
+ memcpy(ram_console_old_log,
+ &buffer->data[buffer->start], buffer->size - buffer->start);
+ memcpy(ram_console_old_log + buffer->size - buffer->start,
+ &buffer->data[0], buffer->start);
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+ memcpy(ram_console_old_log + old_log_size - strbuf_len,
+ strbuf, strbuf_len);
+#endif
+}
+
+static int __init ram_console_init(struct ram_console_buffer *buffer,
+ size_t buffer_size, char *old_buf)
+{
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+ int numerr;
+ uint8_t *par;
+#endif
+ ram_console_buffer = buffer;
+ ram_console_buffer_size =
+ buffer_size - sizeof(struct ram_console_buffer);
+
+ if (ram_console_buffer_size > buffer_size) {
+ pr_err("ram_console: buffer %p, invalid size %zu, "
+ "datasize %zu\n", buffer, buffer_size,
+ ram_console_buffer_size);
+ return 0;
+ }
+
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
+ ram_console_buffer_size -= (DIV_ROUND_UP(ram_console_buffer_size,
+ ECC_BLOCK_SIZE) + 1) * ECC_SIZE;
+
+ if (ram_console_buffer_size > buffer_size) {
+ pr_err("ram_console: buffer %p, invalid size %zu, "
+ "non-ecc datasize %zu\n",
+ buffer, buffer_size, ram_console_buffer_size);
+ return 0;
+ }
+
+ ram_console_par_buffer = buffer->data + ram_console_buffer_size;
+
+
+ /* first consecutive root is 0
+ * primitive element to generate roots = 1
+ */
+ ram_console_rs_decoder = init_rs(ECC_SYMSIZE, ECC_POLY, 0, 1, ECC_SIZE);
+ if (ram_console_rs_decoder == NULL) {
+ printk(KERN_INFO "ram_console: init_rs failed\n");
+ return 0;
+ }
+
+ ram_console_corrected_bytes = 0;
+ ram_console_bad_blocks = 0;
+
+ par = ram_console_par_buffer +
+ DIV_ROUND_UP(ram_console_buffer_size, ECC_BLOCK_SIZE) * ECC_SIZE;
+
+ numerr = ram_console_decode_rs8(buffer, sizeof(*buffer), par);
+ if (numerr > 0) {
+ printk(KERN_INFO "ram_console: error in header, %d\n", numerr);
+ ram_console_corrected_bytes += numerr;
+ } else if (numerr < 0) {
+ printk(KERN_INFO
+ "ram_console: uncorrectable error in header\n");
+ ram_console_bad_blocks++;
+ }
+#endif
+
+ if (buffer->sig == RAM_CONSOLE_SIG) {
+ if (buffer->size > ram_console_buffer_size
+ || buffer->start > buffer->size)
+ printk(KERN_INFO "ram_console: found existing invalid "
+ "buffer, size %d, start %d\n",
+ buffer->size, buffer->start);
+ else {
+ printk(KERN_INFO "ram_console: found existing buffer, "
+ "size %d, start %d\n",
+ buffer->size, buffer->start);
+ ram_console_save_old(buffer, old_buf);
+ }
+ } else {
+ printk(KERN_INFO "ram_console: no valid data in buffer "
+ "(sig = 0x%08x)\n", buffer->sig);
+ }
+
+ buffer->sig = RAM_CONSOLE_SIG;
+ buffer->start = 0;
+ buffer->size = 0;
+
+ register_console(&ram_console);
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE
+ console_verbose();
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT
+static int __init ram_console_early_init(void)
+{
+ return ram_console_init((struct ram_console_buffer *)
+ CONFIG_ANDROID_RAM_CONSOLE_EARLY_ADDR,
+ CONFIG_ANDROID_RAM_CONSOLE_EARLY_SIZE,
+ ram_console_old_log_init_buffer);
+}
+#else
+static int ram_console_driver_probe(struct platform_device *pdev)
+{
+ struct resource *res = pdev->resource;
+ size_t start;
+ size_t buffer_size;
+ void *buffer;
+
+ if (res == NULL || pdev->num_resources != 1 ||
+ !(res->flags & IORESOURCE_MEM)) {
+ printk(KERN_ERR "ram_console: invalid resource, %p %d flags "
+ "%lx\n", res, pdev->num_resources, res ? res->flags : 0);
+ return -ENXIO;
+ }
+ buffer_size = res->end - res->start + 1;
+ start = res->start;
+ printk(KERN_INFO "ram_console: got buffer at %zx, size %zx\n",
+ start, buffer_size);
+ buffer = ioremap(res->start, buffer_size);
+ if (buffer == NULL) {
+ printk(KERN_ERR "ram_console: failed to map memory\n");
+ return -ENOMEM;
+ }
+
+ return ram_console_init(buffer, buffer_size, NULL/* allocate */);
+}
+
+static struct platform_driver ram_console_driver = {
+ .probe = ram_console_driver_probe,
+ .driver = {
+ .name = "ram_console",
+ },
+};
+
+static int __init ram_console_module_init(void)
+{
+ int err;
+ err = platform_driver_register(&ram_console_driver);
+ return err;
+}
+#endif
+
+static ssize_t ram_console_read_old(struct file *file, char __user *buf,
+ size_t len, loff_t *offset)
+{
+ loff_t pos = *offset;
+ ssize_t count;
+
+ if (pos >= ram_console_old_log_size)
+ return 0;
+
+ count = min(len, (size_t)(ram_console_old_log_size - pos));
+ if (copy_to_user(buf, ram_console_old_log + pos, count))
+ return -EFAULT;
+
+ *offset += count;
+ return count;
+}
+
+static const struct file_operations ram_console_file_ops = {
+ .owner = THIS_MODULE,
+ .read = ram_console_read_old,
+};
+
+static int __init ram_console_late_init(void)
+{
+ struct proc_dir_entry *entry;
+
+ if (ram_console_old_log == NULL)
+ return 0;
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT
+ ram_console_old_log = kmalloc(ram_console_old_log_size, GFP_KERNEL);
+ if (ram_console_old_log == NULL) {
+ printk(KERN_ERR
+ "ram_console: failed to allocate buffer for old log\n");
+ ram_console_old_log_size = 0;
+ return 0;
+ }
+ memcpy(ram_console_old_log,
+ ram_console_old_log_init_buffer, ram_console_old_log_size);
+#endif
+ entry = create_proc_entry("last_kmsg", S_IFREG | S_IRUGO, NULL);
+ if (!entry) {
+ printk(KERN_ERR "ram_console: failed to create proc entry\n");
+ kfree(ram_console_old_log);
+ ram_console_old_log = NULL;
+ return 0;
+ }
+
+ entry->proc_fops = &ram_console_file_ops;
+ entry->size = ram_console_old_log_size;
+ return 0;
+}
+
+#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT
+console_initcall(ram_console_early_init);
+#else
+postcore_initcall(ram_console_module_init);
+#endif
+late_initcall(ram_console_late_init);
+
diff --git a/drivers/staging/android/ste_timed_vibra.c b/drivers/staging/android/ste_timed_vibra.c
new file mode 100644
index 00000000000..4cb84eb25e7
--- /dev/null
+++ b/drivers/staging/android/ste_timed_vibra.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com>
+ * for ST-Ericsson
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/kernel.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/ste_timed_vibra.h>
+
+#include "timed_output.h"
+
+/**
+ * struct vibra_info - Vibrator information structure
+ * @tdev: Pointer to timed output device structure
+ * @vibra_workqueue: Pointer to vibrator workqueue structure
+ * @vibra_work: Vibrator work
+ * @vibra_lock: Vibrator lock
+ * @vibra_timer: Vibrator high resolution timer
+ * @vibra_state: Actual vibrator state
+ * @state_force: Indicates if oppositive state is requested
+ * @timeout: Indicates how long time the vibrator will be enabled
+ * @time_passed: Total time passed in states
+ * @pdata: Local pointer to platform data with vibrator parameters
+ *
+ * Structure vibra_info holds vibrator information
+ **/
+struct vibra_info {
+ struct timed_output_dev tdev;
+ struct workqueue_struct *vibra_workqueue;
+ struct work_struct vibra_work;
+ spinlock_t vibra_lock;
+ struct hrtimer vibra_timer;
+ enum ste_timed_vibra_states vibra_state;
+ bool state_force;
+ unsigned int timeout;
+ unsigned int time_passed;
+ struct ste_timed_vibra_platform_data *pdata;
+};
+
+/**
+ * vibra_control_work() - Vibrator work, turns on/off vibrator
+ * @work: Pointer to work structure
+ *
+ * This function is called from workqueue, turns on/off vibrator
+ **/
+static void vibra_control_work(struct work_struct *work)
+{
+ struct vibra_info *vinfo =
+ container_of(work, struct vibra_info, vibra_work);
+ unsigned val = 0;
+ unsigned char speed_pos = 0, speed_neg = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vinfo->vibra_lock, flags);
+ /* Should be already expired */
+ hrtimer_cancel(&vinfo->vibra_timer);
+
+ switch (vinfo->vibra_state) {
+ case STE_VIBRA_BOOST:
+ /* Turn on both vibrators with boost speed */
+ speed_pos = vinfo->pdata->boost_level;
+ val = vinfo->pdata->boost_time;
+ break;
+ case STE_VIBRA_ON:
+ /* Turn on both vibrators with speed */
+ speed_pos = vinfo->pdata->on_level;
+ val = vinfo->timeout - vinfo->pdata->boost_time;
+ break;
+ case STE_VIBRA_OFF:
+ /* Turn on both vibrators with reversed speed */
+ speed_neg = vinfo->pdata->off_level;
+ val = vinfo->pdata->off_time;
+ break;
+ case STE_VIBRA_IDLE:
+ vinfo->time_passed = 0;
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&vinfo->vibra_lock, flags);
+
+ /* Send new settings */
+ vinfo->pdata->timed_vibra_control(
+ speed_pos, speed_neg,
+ speed_pos, speed_neg);
+
+ /* Start timer if it's not in IDLE state */
+ if (vinfo->vibra_state != STE_VIBRA_IDLE) {
+ hrtimer_start(&vinfo->vibra_timer,
+ ktime_set(val / 1000, (val % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ }
+}
+
+/**
+ * vibra_enable() - Enables vibrator
+ * @tdev: Pointer to timed output device structure
+ * @timeout: Time indicating how long vibrator will be enabled
+ *
+ * This function enables vibrator
+ **/
+static void vibra_enable(struct timed_output_dev *tdev, int timeout)
+{
+ struct vibra_info *vinfo = dev_get_drvdata(tdev->dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vinfo->vibra_lock, flags);
+ switch (vinfo->vibra_state) {
+ case STE_VIBRA_IDLE:
+ if (timeout)
+ vinfo->vibra_state = STE_VIBRA_BOOST;
+ else /* Already disabled */
+ break;
+
+ vinfo->state_force = false;
+ /* Trim timeout */
+ timeout = timeout < vinfo->pdata->boost_time ?
+ vinfo->pdata->boost_time : timeout;
+ /* Remember timeout value */
+ vinfo->timeout = timeout;
+ queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work);
+ break;
+ case STE_VIBRA_BOOST:
+ /* Force only when user requested OFF while BOOST */
+ if (!timeout)
+ vinfo->state_force = true;
+ break;
+ case STE_VIBRA_ON:
+ /* If user requested OFF */
+ if (!timeout) {
+ hrtimer_cancel(&vinfo->vibra_timer);
+ vinfo->vibra_state = STE_VIBRA_OFF;
+ queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work);
+ }
+ break;
+ case STE_VIBRA_OFF:
+ /* Force only when user requested ON while OFF */
+ if (timeout)
+ vinfo->state_force = true;
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&vinfo->vibra_lock, flags);
+}
+
+/**
+ * vibra_timer_expired() - Handles vibrator machine state
+ * @hrtimer: Pointer to high resolution timer structure
+ *
+ * This function handles vibrator machine state
+ *
+ * Returns:
+ * Returns value which indicates wether hrtimer should be restarted
+ **/
+static enum hrtimer_restart vibra_timer_expired(struct hrtimer *hrtimer)
+{
+ struct vibra_info *vinfo =
+ container_of(hrtimer, struct vibra_info, vibra_timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vinfo->vibra_lock, flags);
+ switch (vinfo->vibra_state) {
+ case STE_VIBRA_BOOST:
+ /* If BOOST finished and force, go to OFF */
+ if (vinfo->state_force)
+ vinfo->vibra_state = STE_VIBRA_OFF;
+ else
+ vinfo->vibra_state = STE_VIBRA_ON;
+ vinfo->time_passed = vinfo->pdata->boost_time;
+ break;
+ case STE_VIBRA_ON:
+ vinfo->vibra_state = STE_VIBRA_OFF;
+ vinfo->time_passed = vinfo->timeout;
+ break;
+ case STE_VIBRA_OFF:
+ /* If OFF finished and force, go to ON */
+ if (vinfo->state_force)
+ vinfo->vibra_state = STE_VIBRA_ON;
+ else
+ vinfo->vibra_state = STE_VIBRA_IDLE;
+ vinfo->time_passed += vinfo->pdata->off_time;
+ break;
+ case STE_VIBRA_IDLE:
+ break;
+ default:
+ break;
+ }
+ vinfo->state_force = false;
+ spin_unlock_irqrestore(&vinfo->vibra_lock, flags);
+
+ queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work);
+
+ return HRTIMER_NORESTART;
+}
+
+/**
+ * vibra_get_time() - Returns remaining time to disabling vibration
+ * @tdev: Pointer to timed output device structure
+ *
+ * This function returns time remaining to disabling vibration
+ *
+ * Returns:
+ * Returns remaining time to disabling vibration
+ **/
+static int vibra_get_time(struct timed_output_dev *tdev)
+{
+ struct vibra_info *vinfo = dev_get_drvdata(tdev->dev);
+ u32 ms;
+
+ if (hrtimer_active(&vinfo->vibra_timer)) {
+ ktime_t remain = hrtimer_get_remaining(&vinfo->vibra_timer);
+ ms = (u32) ktime_to_ms(remain);
+ return ms + vinfo->time_passed;
+ } else
+ return 0;
+}
+
+static int __devinit ste_timed_vibra_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct vibra_info *vinfo;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -ENODEV;
+ }
+
+ vinfo = kmalloc(sizeof *vinfo, GFP_KERNEL);
+ if (!vinfo) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ vinfo->tdev.name = "vibrator";
+ vinfo->tdev.enable = vibra_enable;
+ vinfo->tdev.get_time = vibra_get_time;
+ vinfo->time_passed = 0;
+ vinfo->vibra_state = STE_VIBRA_IDLE;
+ vinfo->state_force = false;
+ vinfo->pdata = pdev->dev.platform_data;
+
+ ret = timed_output_dev_register(&vinfo->tdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register timed output device\n");
+ goto exit_free_vinfo;
+ }
+
+
+ vinfo->tdev.dev->parent = pdev->dev.parent;
+ dev_set_drvdata(vinfo->tdev.dev, vinfo);
+
+ /* Create workqueue just for timed output vibrator */
+ vinfo->vibra_workqueue =
+ create_singlethread_workqueue("ste-timed-output-vibra");
+ if (!vinfo->vibra_workqueue) {
+ dev_err(&pdev->dev, "failed to allocate workqueue\n");
+ ret = -ENOMEM;
+ goto exit_timed_output_unregister;
+ }
+
+ INIT_WORK(&vinfo->vibra_work, vibra_control_work);
+ spin_lock_init(&vinfo->vibra_lock);
+ hrtimer_init(&vinfo->vibra_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ vinfo->vibra_timer.function = vibra_timer_expired;
+ platform_set_drvdata(pdev, vinfo);
+ return 0;
+
+exit_timed_output_unregister:
+ timed_output_dev_unregister(&vinfo->tdev);
+exit_free_vinfo:
+ kfree(vinfo);
+ return ret;
+}
+
+static int __devexit ste_timed_vibra_remove(struct platform_device *pdev)
+{
+ struct vibra_info *vinfo = platform_get_drvdata(pdev);
+
+ timed_output_dev_unregister(&vinfo->tdev);
+ destroy_workqueue(vinfo->vibra_workqueue);
+ kfree(vinfo);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver ste_timed_vibra_driver = {
+ .driver = {
+ .name = "ste_timed_output_vibra",
+ .owner = THIS_MODULE,
+ },
+ .probe = ste_timed_vibra_probe,
+ .remove = __devexit_p(ste_timed_vibra_remove)
+};
+
+static int __init ste_timed_vibra_init(void)
+{
+ return platform_driver_register(&ste_timed_vibra_driver);
+}
+module_init(ste_timed_vibra_init);
+
+static void __exit ste_timed_vibra_exit(void)
+{
+ platform_driver_unregister(&ste_timed_vibra_driver);
+}
+module_exit(ste_timed_vibra_exit);
+
+MODULE_AUTHOR("Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com>");
+MODULE_DESCRIPTION("STE Timed Output Vibrator");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c
new file mode 100644
index 00000000000..a64481c3e86
--- /dev/null
+++ b/drivers/staging/android/timed_gpio.c
@@ -0,0 +1,176 @@
+/* drivers/misc/timed_gpio.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/hrtimer.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+
+#include "timed_output.h"
+#include "timed_gpio.h"
+
+
+struct timed_gpio_data {
+ struct timed_output_dev dev;
+ struct hrtimer timer;
+ spinlock_t lock;
+ unsigned gpio;
+ int max_timeout;
+ u8 active_low;
+};
+
+static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer)
+{
+ struct timed_gpio_data *data =
+ container_of(timer, struct timed_gpio_data, timer);
+
+ gpio_direction_output(data->gpio, data->active_low ? 1 : 0);
+ return HRTIMER_NORESTART;
+}
+
+static int gpio_get_time(struct timed_output_dev *dev)
+{
+ struct timed_gpio_data *data =
+ container_of(dev, struct timed_gpio_data, dev);
+
+ if (hrtimer_active(&data->timer)) {
+ ktime_t r = hrtimer_get_remaining(&data->timer);
+ struct timeval t = ktime_to_timeval(r);
+ return t.tv_sec * 1000 + t.tv_usec / 1000;
+ } else
+ return 0;
+}
+
+static void gpio_enable(struct timed_output_dev *dev, int value)
+{
+ struct timed_gpio_data *data =
+ container_of(dev, struct timed_gpio_data, dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* cancel previous timer and set GPIO according to value */
+ hrtimer_cancel(&data->timer);
+ gpio_direction_output(data->gpio, data->active_low ? !value : !!value);
+
+ if (value > 0) {
+ if (value > data->max_timeout)
+ value = data->max_timeout;
+
+ hrtimer_start(&data->timer,
+ ktime_set(value / 1000, (value % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ }
+
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static int timed_gpio_probe(struct platform_device *pdev)
+{
+ struct timed_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct timed_gpio *cur_gpio;
+ struct timed_gpio_data *gpio_data, *gpio_dat;
+ int i, j, ret = 0;
+
+ if (!pdata)
+ return -EBUSY;
+
+ gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios,
+ GFP_KERNEL);
+ if (!gpio_data)
+ return -ENOMEM;
+
+ for (i = 0; i < pdata->num_gpios; i++) {
+ cur_gpio = &pdata->gpios[i];
+ gpio_dat = &gpio_data[i];
+
+ hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ gpio_dat->timer.function = gpio_timer_func;
+ spin_lock_init(&gpio_dat->lock);
+
+ gpio_dat->dev.name = cur_gpio->name;
+ gpio_dat->dev.get_time = gpio_get_time;
+ gpio_dat->dev.enable = gpio_enable;
+ ret = gpio_request(cur_gpio->gpio, cur_gpio->name);
+ if (ret >= 0) {
+ ret = timed_output_dev_register(&gpio_dat->dev);
+ if (ret < 0)
+ gpio_free(cur_gpio->gpio);
+ }
+ if (ret < 0) {
+ for (j = 0; j < i; j++) {
+ timed_output_dev_unregister(&gpio_data[i].dev);
+ gpio_free(gpio_data[i].gpio);
+ }
+ kfree(gpio_data);
+ return ret;
+ }
+
+ gpio_dat->gpio = cur_gpio->gpio;
+ gpio_dat->max_timeout = cur_gpio->max_timeout;
+ gpio_dat->active_low = cur_gpio->active_low;
+ gpio_direction_output(gpio_dat->gpio, gpio_dat->active_low);
+ }
+
+ platform_set_drvdata(pdev, gpio_data);
+
+ return 0;
+}
+
+static int timed_gpio_remove(struct platform_device *pdev)
+{
+ struct timed_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct timed_gpio_data *gpio_data = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < pdata->num_gpios; i++) {
+ timed_output_dev_unregister(&gpio_data[i].dev);
+ gpio_free(gpio_data[i].gpio);
+ }
+
+ kfree(gpio_data);
+
+ return 0;
+}
+
+static struct platform_driver timed_gpio_driver = {
+ .probe = timed_gpio_probe,
+ .remove = timed_gpio_remove,
+ .driver = {
+ .name = TIMED_GPIO_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init timed_gpio_init(void)
+{
+ return platform_driver_register(&timed_gpio_driver);
+}
+
+static void __exit timed_gpio_exit(void)
+{
+ platform_driver_unregister(&timed_gpio_driver);
+}
+
+module_init(timed_gpio_init);
+module_exit(timed_gpio_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("timed gpio driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/android/timed_gpio.h b/drivers/staging/android/timed_gpio.h
new file mode 100644
index 00000000000..a0e15f8be3f
--- /dev/null
+++ b/drivers/staging/android/timed_gpio.h
@@ -0,0 +1,33 @@
+/* include/linux/timed_gpio.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+*/
+
+#ifndef _LINUX_TIMED_GPIO_H
+#define _LINUX_TIMED_GPIO_H
+
+#define TIMED_GPIO_NAME "timed-gpio"
+
+struct timed_gpio {
+ const char *name;
+ unsigned gpio;
+ int max_timeout;
+ u8 active_low;
+};
+
+struct timed_gpio_platform_data {
+ int num_gpios;
+ struct timed_gpio *gpios;
+};
+
+#endif
diff --git a/drivers/staging/android/timed_output.c b/drivers/staging/android/timed_output.c
new file mode 100644
index 00000000000..f373422308e
--- /dev/null
+++ b/drivers/staging/android/timed_output.c
@@ -0,0 +1,123 @@
+/* drivers/misc/timed_output.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+
+#include "timed_output.h"
+
+static struct class *timed_output_class;
+static atomic_t device_count;
+
+static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct timed_output_dev *tdev = dev_get_drvdata(dev);
+ int remaining = tdev->get_time(tdev);
+
+ return sprintf(buf, "%d\n", remaining);
+}
+
+static ssize_t enable_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct timed_output_dev *tdev = dev_get_drvdata(dev);
+ int value;
+
+ if (sscanf(buf, "%d", &value) != 1)
+ return -EINVAL;
+
+ tdev->enable(tdev, value);
+
+ return size;
+}
+
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
+
+static int create_timed_output_class(void)
+{
+ if (!timed_output_class) {
+ timed_output_class = class_create(THIS_MODULE, "timed_output");
+ if (IS_ERR(timed_output_class))
+ return PTR_ERR(timed_output_class);
+ atomic_set(&device_count, 0);
+ }
+
+ return 0;
+}
+
+int timed_output_dev_register(struct timed_output_dev *tdev)
+{
+ int ret;
+
+ if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time)
+ return -EINVAL;
+
+ ret = create_timed_output_class();
+ if (ret < 0)
+ return ret;
+
+ tdev->index = atomic_inc_return(&device_count);
+ tdev->dev = device_create(timed_output_class, NULL,
+ MKDEV(0, tdev->index), NULL, tdev->name);
+ if (IS_ERR(tdev->dev))
+ return PTR_ERR(tdev->dev);
+
+ ret = device_create_file(tdev->dev, &dev_attr_enable);
+ if (ret < 0)
+ goto err_create_file;
+
+ dev_set_drvdata(tdev->dev, tdev);
+ tdev->state = 0;
+ return 0;
+
+err_create_file:
+ device_destroy(timed_output_class, MKDEV(0, tdev->index));
+ printk(KERN_ERR "timed_output: Failed to register driver %s\n",
+ tdev->name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(timed_output_dev_register);
+
+void timed_output_dev_unregister(struct timed_output_dev *tdev)
+{
+ device_remove_file(tdev->dev, &dev_attr_enable);
+ device_destroy(timed_output_class, MKDEV(0, tdev->index));
+ dev_set_drvdata(tdev->dev, NULL);
+}
+EXPORT_SYMBOL_GPL(timed_output_dev_unregister);
+
+static int __init timed_output_init(void)
+{
+ return create_timed_output_class();
+}
+
+static void __exit timed_output_exit(void)
+{
+ class_destroy(timed_output_class);
+}
+
+module_init(timed_output_init);
+module_exit(timed_output_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("timed output class driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/android/timed_output.h b/drivers/staging/android/timed_output.h
new file mode 100644
index 00000000000..ec907ab2ff5
--- /dev/null
+++ b/drivers/staging/android/timed_output.h
@@ -0,0 +1,37 @@
+/* include/linux/timed_output.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+*/
+
+#ifndef _LINUX_TIMED_OUTPUT_H
+#define _LINUX_TIMED_OUTPUT_H
+
+struct timed_output_dev {
+ const char *name;
+
+ /* enable the output and set the timer */
+ void (*enable)(struct timed_output_dev *sdev, int timeout);
+
+ /* returns the current number of milliseconds remaining on the timer */
+ int (*get_time)(struct timed_output_dev *sdev);
+
+ /* private data */
+ struct device *dev;
+ int index;
+ int state;
+};
+
+extern int timed_output_dev_register(struct timed_output_dev *dev);
+extern void timed_output_dev_unregister(struct timed_output_dev *dev);
+
+#endif
diff --git a/drivers/staging/camera_flash/Makefile b/drivers/staging/camera_flash/Makefile
new file mode 100644
index 00000000000..ecc4d2fb927
--- /dev/null
+++ b/drivers/staging/camera_flash/Makefile
@@ -0,0 +1,5 @@
+export ADP1653_SUPPORT
+EXTRA_CFLAGS += -DADP1653_SUPPORT
+obj-$(CONFIG_U8500_FLASH) := camera_flash.o
+camera_flash-y := flash_common.o
+camera_flash-y += flash_chips/adp1653.o \ No newline at end of file
diff --git a/drivers/staging/camera_flash/flash_chips/adp1653.c b/drivers/staging/camera_flash/flash_chips/adp1653.c
new file mode 100644
index 00000000000..1ff503d19b9
--- /dev/null
+++ b/drivers/staging/camera_flash/flash_chips/adp1653.c
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * adp1653: Driver Adp1653 HPLED flash driver chip. This driver
+ * currently support I2C interface, 2bit interface is not supported.
+ * Author: Pankaj Chauhan/pankaj.chauhan@stericsson.com for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/camera_flash.h>
+#include "flash_common.h"
+#include "adp1653.h"
+
+/* This data is platform specific for 8500 href-v1 platform,
+ * Ideally this should be supplied from platform code
+ */
+//#define ADAPTER_I2C2 (2)
+#define STR_GPIO (6)
+
+static int strobe_gpio = 0; //TODO: set to 0 when it works
+static int adapter_i2c2 = 2;
+static int flash_irq = 7;
+static int enable_gpio = 272;
+static int flash_position = 0;
+module_param(strobe_gpio, int, S_IRUGO);
+MODULE_PARM_DESC(strobe_gpio, "use GPIO 6 to strobe, 0 means that someone else (sensor?) will strobe for us");
+module_param(adapter_i2c2, int, S_IRUGO);
+MODULE_PARM_DESC(adapter_i2c2, "use the given I2C adaptater to communicate with the chip");
+module_param(flash_irq, int, S_IRUGO);
+MODULE_PARM_DESC(flash_irq, "the GPIO number associated to the i2c irq line");
+module_param(enable_gpio, int, S_IRUGO);
+MODULE_PARM_DESC(enable_gpio, "use the given GPIO line to enable the chip");
+module_param(flash_position, int, S_IRUGO);
+MODULE_PARM_DESC(flash_position, "the position of the flash chip (0=PRIMARY, 1=SECONDARY)");
+
+
+int __flash_gpio_to_irq(int gpio)
+{
+
+ return GPIO_TO_IRQ(gpio);
+}
+
+#define DEBUG_LOG(...) printk(KERN_DEBUG "Adp1653 flash driver: " __VA_ARGS__)
+
+#define ADP1653_SUPPORTED_MODES (FLASH_MODE_VIDEO_LED | FLASH_MODE_STILL_LED | \
+ FLASH_MODE_STILL_LED_EXTERNAL_STROBE | \
+ FLASH_MODE_AF_ASSISTANT | FLASH_MODE_INDICATOR)
+
+#define ADP1653_SELFTEST_SUPPORTED_MODES (FLASH_SELFTEST_CONNECTION | FLASH_SELFTEST_FLASH_WITH_STROBE | \
+ FLASH_SELFTEST_VIDEO_LIGHT | FLASH_SELFTEST_AF_LIGHT | FLASH_SELFTEST_INDICATOR | FLASH_SELFTEST_TORCH_LIGHT)
+
+static int adp1653_trigger_strobe(void *priv_data, int enable);
+
+static int adp1653_get_modes(void *priv_data,unsigned long *modes)
+{
+ int err;
+ struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data;
+ err = i2c_smbus_read_byte_data(priv_p->i2c_client, FAULT_STATUS_REG);
+ if (err)
+ *modes = 0x0;
+ else
+ *modes = ADP1653_SUPPORTED_MODES;
+ return 0;
+}
+
+static int adp1653_get_mode_details(void *priv_data, unsigned long mode,
+struct flash_mode_details *details_p)
+{
+ int err = 0;
+ memset(details_p,0,sizeof(struct flash_mode_details));
+
+ details_p->led_type = 2;
+
+ /* Still LED settings*/
+ details_p->nbFaultRegisters = 1;
+ if(mode & (FLASH_MODE_STILL_LED | FLASH_MODE_STILL_LED_EXTERNAL_STROBE)){
+ details_p->max_intensity_uAmp = FLASH_MAX_INTENSITY;
+ details_p->min_intensity_uAmp = FLASH_MIN_INTENSITY;
+ details_p->max_strobe_duration_uSecs = FLASH_MAX_STROBE_DURATION;
+ details_p->feature_bitmap = INTENSITY_PROGRAMMABLE | DURATION_PROGRAMMABLE;
+ goto out;
+ }
+ /*Video LED settings*/
+ if(mode & FLASH_MODE_VIDEO_LED){
+ details_p->max_intensity_uAmp = TORCH_MAX_INTENSITY;
+ details_p->min_intensity_uAmp = TORCH_MIN_INTENSITY;
+ details_p->max_strobe_duration_uSecs = 0;
+ details_p->feature_bitmap = INTENSITY_PROGRAMMABLE;
+ goto out;
+ }
+ /*Privacy Indicator settings */
+ if(mode & FLASH_MODE_INDICATOR){
+ details_p->max_intensity_uAmp = ILED_MAX_INTENSITY;
+ details_p->min_intensity_uAmp = ILED_MIN_INTENSITY;
+ details_p->max_strobe_duration_uSecs = 0;
+ details_p->feature_bitmap = INTENSITY_PROGRAMMABLE;
+ goto out;
+ }
+ DEBUG_LOG("Mode %lx, not supported\n",mode);
+ err = EINVAL;
+out:
+ return err;
+}
+
+static int adp1653_enable_flash_mode(void *priv_data,
+ unsigned long mode, int enable)
+{
+ int err = 0;
+ struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data;
+
+ if(enable){
+
+ if(!(mode & ADP1653_SUPPORTED_MODES)){
+ DEBUG_LOG("Unsupported mode %lx\n",mode);
+ err = -EINVAL;
+ goto out;
+ }
+ /*Nothing to be done in enabling, just set current mode and return*/
+ /*May be enable disable can be done here but why not enable in
+ *probe and keep it on always
+ */
+ adp1653_trigger_strobe(priv_p,0);
+ priv_p->curr_mode = mode;
+ }else{
+ adp1653_trigger_strobe(priv_p,0);
+ priv_p->curr_mode =0;
+ }
+out:
+ return err;
+}
+
+static int adp1653_configure_flash_mode(void *priv,unsigned long mode,
+struct flash_mode_params *params_p)
+{
+ int err = 0;
+ unsigned char intensity_code;
+ struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv;
+
+ if(!(mode & ADP1653_SUPPORTED_MODES)){
+ DEBUG_LOG("Mode %lx not supported\n",mode);
+ err = -EINVAL;
+ goto out;
+ }
+ switch(mode){
+ case FLASH_MODE_STILL_LED:
+ case FLASH_MODE_STILL_LED_EXTERNAL_STROBE:
+ {
+ FLASH_UAMP_TO_CODE(intensity_code,params_p->intensity_uAmp);
+ if(params_p->duration_uSecs){
+ DURATION_USEC_TO_CODE(priv_p->flash_duration,
+ params_p->duration_uSecs);
+ DEBUG_LOG("Duration %lu, code 0x%x\n",params_p->duration_uSecs,
+ priv_p->flash_duration);
+ priv_p->flash_duration |= TIMER_ENABLE;
+ }else{
+ priv_p->flash_duration = 0;
+ }
+ priv_p->flash_intensity = intensity_code << 3;
+ }
+ break;
+ case FLASH_MODE_VIDEO_LED:
+ {
+ TORCH_UAMP_TO_CODE(intensity_code,params_p->intensity_uAmp);
+ DEBUG_LOG("Torch mode setting intensity 0x%x, current(uA) %lu\n",
+ intensity_code,params_p->intensity_uAmp);
+ priv_p->torch_intensity = intensity_code << 3;
+ }
+ break;
+ case FLASH_MODE_INDICATOR:
+ {
+ ILED_UAMP_TO_CODE(intensity_code,params_p->intensity_uAmp);
+ DEBUG_LOG("ILED setting intensity 0x%x, current(uA) %lu\n",
+ intensity_code,params_p->intensity_uAmp);
+ priv_p->indicator_intensity = intensity_code;
+ }
+ break;
+ default:
+ err = -EINVAL;
+ DEBUG_LOG("Unsupported mode %lx\n",mode);
+ break;
+ }
+
+ if((mode == FLASH_MODE_STILL_LED_EXTERNAL_STROBE) || (mode == FLASH_MODE_STILL_LED))
+ {
+ adp1653_trigger_strobe(priv_p,0);
+ DEBUG_LOG("CONFIG_TIMER_REG : 0x%x\n",priv_p->flash_duration);
+ DEBUG_LOG("OUTPUT_SEL_REG : 0x%x\n",priv_p->flash_intensity);
+
+ /*TimeOut Must be programmed before Intensity*/
+ err = i2c_smbus_write_byte_data(priv_p->i2c_client,CONFIG_TIMER_REG,
+ priv_p->flash_duration);
+ if(err){
+ DEBUG_LOG("I2C: Unsable to write timer config, err %d\n",err);
+ goto out;
+ }
+ err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,
+ priv_p->flash_intensity);
+ if(err){
+ DEBUG_LOG("I2C: Unable to write OUTPUT_SEL_REG , err %d\n",err);
+ goto out;
+ }
+ }
+out:
+ return err;
+}
+
+static int adp1653_set_intensity(struct adp1653_priv_data *priv_p, uint8_t intensity)
+{
+ return i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,intensity);
+}
+
+static int adp1653_strobe_still_led(struct adp1653_priv_data *priv_p,int enable)
+{
+ int err=0,gpio_val;
+ uint8_t intensity,duration;
+
+ if(enable){
+ intensity = priv_p->flash_intensity;
+ duration = priv_p->flash_duration;
+ gpio_val = GPIO_HIGH;
+ }else{
+ intensity = 0;
+ duration = 0;
+ gpio_val = GPIO_LOW;
+ }
+
+ err = adp1653_set_intensity(priv_p,intensity);
+ if(err){
+ DEBUG_LOG("I2C: Unable to write OUTPUT_SEL_REG reg, err %d\n",err);
+ goto out;
+ }
+
+ /*TimeOut Must be programmed before Intensity*/
+ err = i2c_smbus_write_byte_data(priv_p->i2c_client,CONFIG_TIMER_REG,
+ priv_p->flash_duration);
+ if(err){
+ DEBUG_LOG("I2C: Unsable to write timer config, err %d\n",err);
+ goto out;
+ }
+ err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,intensity);
+ if(err){
+ DEBUG_LOG("I2C: Unable to write OUTPUT_SEL_REG, err %d\n",err);
+ goto out;
+ }
+
+ if (strobe_gpio != 0 )
+ {
+ gpio_set_value(priv_p->strobe_gpio,gpio_val);
+ }
+
+out:
+ return err;
+}
+
+static int adp1653_trigger_strobe(void *priv, int enable)
+{
+ int err = 0;
+ uint8_t intensity;
+ struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv;
+
+ switch(priv_p->curr_mode){
+ case FLASH_MODE_STILL_LED:
+ case FLASH_MODE_STILL_LED_EXTERNAL_STROBE:
+ err = adp1653_strobe_still_led(priv_p,enable);
+ break;
+ case FLASH_MODE_VIDEO_LED:
+ {
+ if(enable)
+ intensity = priv_p->torch_intensity;
+ else
+ intensity = 0;
+ err = adp1653_set_intensity(priv_p,intensity);
+ }
+ break;
+ case FLASH_MODE_INDICATOR:
+ {
+ if(enable)
+ intensity = priv_p->indicator_intensity;
+ else
+ intensity =0;
+ err = adp1653_set_intensity(priv_p,intensity);
+ }
+ break;
+ default:
+ DEBUG_LOG("Unsupported mode %lx\n",priv_p->curr_mode);
+ goto out;
+ }
+ if(err){
+ DEBUG_LOG("Unable to enable/disable %d, strobe. Mode %lx, err %d\n",enable,
+ priv_p->curr_mode,err);
+ goto out;
+ }
+ disable_irq(priv_p->i2c_client->irq);
+ if(enable)
+ SET_FLASH_STATUS(priv_p->status,FLASH_STATUS_LIT);
+ else
+ CLR_FLASH_STATUS(priv_p->status,FLASH_STATUS_LIT);
+
+ enable_irq(priv_p->i2c_client->irq);
+
+out:
+ return err;
+}
+#define FLASH_ERR_ALL (FLASH_ERR_OVER_CHARGE |FLASH_ERR_OVER_HEAT | \
+ FLASH_ERR_SHORT_CIRCUIT | FLASH_ERR_TIMEOUT | \
+ FLASH_ERR_OVER_VOLTAGE)
+int adp1653_get_status(void *priv_data,unsigned long *status)
+{
+ struct adp1653_priv_data *priv_p= (struct adp1653_priv_data *)priv_data;
+ disable_irq(priv_p->i2c_client->irq);
+ if(priv_p->fault){
+ if(priv_p->fault & OVER_VOLTAGE_FAULT)
+ SET_FLASH_ERROR(priv_p->status,FLASH_ERR_OVER_VOLTAGE);
+ if(priv_p->fault & TIMEOUT_FAULT)
+ SET_FLASH_ERROR(priv_p->status,FLASH_ERR_TIMEOUT);
+ if(priv_p->fault & OVER_TEMPERATURE_FAULT)
+ SET_FLASH_ERROR(priv_p->status,FLASH_ERR_OVER_HEAT);
+ if(priv_p->fault & SHORT_CIRCUIT_FAULT){
+ CLR_FLASH_STATUS(priv_p->status,FLASH_STATUS_READY);
+ SET_FLASH_STATUS(priv_p->status,FLASH_STATUS_BROKEN);
+ SET_FLASH_ERROR(priv_p->status,FLASH_ERR_SHORT_CIRCUIT);
+ }
+ priv_p->fault =0;
+ }else{
+ CLR_FLASH_ERROR(priv_p->status,FLASH_ERR_ALL);
+ }
+ enable_irq(priv_p->i2c_client->irq);
+ *status = priv_p->status;
+ return 0;
+}
+
+int adp1653_get_selftest_modes(void *priv_data, unsigned long *modes)
+{
+ int err;
+ struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data;
+ err = i2c_smbus_read_byte_data(priv_p->i2c_client, FAULT_STATUS_REG);
+ if (err) *modes = 0x0;
+ else *modes = ADP1653_SELFTEST_SUPPORTED_MODES;
+ return 0;
+}
+
+int adp1653_get_fault_registers(void *priv_data, unsigned long mode, unsigned long *status)
+{
+ int err = 0;
+ struct adp1653_priv_data *priv_p = (struct adp1653_priv_data *)priv_data;
+
+ *status = i2c_smbus_read_byte_data(priv_p->i2c_client, FAULT_STATUS_REG);
+
+ /* clear fault register */
+ err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,0);
+ if(0 != err)
+ {
+ DEBUG_LOG("Unable to write OUTPUT_SEL_REG, err %d\n",err);
+ }
+ return err;
+}
+
+struct flash_chip_ops adp1653_ops = {
+ .get_modes = adp1653_get_modes,
+ .get_mode_details = adp1653_get_mode_details,
+ .get_status = adp1653_get_status,
+ .enable_flash_mode = adp1653_enable_flash_mode,
+ .configure_flash_mode = adp1653_configure_flash_mode,
+ .trigger_strobe = adp1653_trigger_strobe,
+ .get_selftest_modes = adp1653_get_selftest_modes,
+ .get_fault_registers = adp1653_get_fault_registers
+};
+
+static irqreturn_t adp1653_irq_hdlr(int irq_no,void *data)
+{
+ int err;
+ struct adp1653_priv_data *priv_p= (struct adp1653_priv_data *)data;
+
+ priv_p->fault = i2c_smbus_read_byte_data(priv_p->i2c_client,
+ FAULT_STATUS_REG);
+ DEBUG_LOG("Got Fault, status 0x%x\n",priv_p->fault);
+ /*Writing 0 to OUTPUT_SEL_REG clears the interrtup
+ *and FAULT_STATUS_REG register
+ */
+ err = i2c_smbus_write_byte_data(priv_p->i2c_client,OUTPUT_SEL_REG,0);
+ if(err)
+ DEBUG_LOG("Unable to write OUTPUT_SEL_REG to clr intr, err %d\n",err);
+ /*TBD: send even to user process*/
+ return IRQ_HANDLED;
+}
+static int __devinit adp1653_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err=0;
+ struct flash_chip *flash_chip_p=NULL;
+ struct adp1653_priv_data *priv_p=NULL;
+
+ DEBUG_LOG("> adp1653_probe\n");
+
+ priv_p = kzalloc(sizeof(struct adp1653_priv_data),GFP_KERNEL);
+ if(!priv_p){
+ DEBUG_LOG("Kmalloc failed for priv data\n");
+ err = ENOMEM;
+ goto out;
+ }
+ priv_p->i2c_client = client;
+ flash_chip_p = kzalloc(sizeof(struct flash_chip),GFP_KERNEL);
+ if(!flash_chip_p){
+ DEBUG_LOG("Kmalloc failed for flash_chip_p");
+ err = ENOMEM;
+ goto out;
+ }
+ flash_chip_p->priv_data = priv_p;
+ flash_chip_p->ops = &adp1653_ops;
+ SET_FLASHCHIP_TYPE(flash_chip_p,FLASH_TYPE_HPLED);
+ SET_FLASHCHIP_ID(flash_chip_p,ADP1653_ID);
+
+ strncpy(flash_chip_p->name,"Adp1653",FLASH_NAME_SIZE);
+
+ i2c_set_clientdata(client,priv_p);
+ /*Request GPIO and Register IRQ if supported by platform and flash chip*/
+
+ if(machine_is_hrefv60())
+ enable_gpio = 21;
+ else
+ enable_gpio = 272;
+
+ if(enable_gpio){
+ err = gpio_request(enable_gpio,"Camera LED flash Enable");
+ if(err){
+ DEBUG_LOG("Unable to get GPIO %d, for enable\n",enable_gpio);
+ goto out;
+ }
+ priv_p->enable_gpio = enable_gpio;
+
+ err = gpio_direction_output(priv_p->enable_gpio,GPIO_HIGH);
+ if(err){
+ DEBUG_LOG("Unable to set GPIO %lu in output mode, err %d\n",priv_p->enable_gpio,err);
+ gpio_free(priv_p->enable_gpio);
+ goto out;
+ }
+ gpio_set_value(priv_p->enable_gpio,GPIO_HIGH);
+ }
+
+ if (strobe_gpio != 0)
+ {
+ err = gpio_request(STR_GPIO,"Camera flash strobe\n");
+ if(err){
+ DEBUG_LOG("Unable to request strobe GPIO\n");
+ // somebody else requested this gpio ...
+ //goto out;
+ }
+
+ err = gpio_direction_output(STR_GPIO,GPIO_LOW);
+ if(err){
+ DEBUG_LOG("Unable to set GPIO %d in output mode, err %d\n",STR_GPIO,err);
+ goto out;
+ }
+ }
+ else
+ {
+ err = gpio_request(STR_GPIO,"Camera flash strobe\n");
+ if(err){
+ DEBUG_LOG("Unable to request strobe GPIO\n");
+ // somebody else requested this gpio ...
+ //goto out;
+ }
+
+ err = gpio_direction_input(STR_GPIO);
+ if(err){
+ DEBUG_LOG("Unable to set GPIO %d in input mode, err %d\n",STR_GPIO,err);
+ goto out;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+ err = nmk_gpio_set_pull(STR_GPIO, NMK_GPIO_PULL_DOWN);
+#endif
+ if(err){
+ DEBUG_LOG("Unable to set pull down on GPIO %d\n",STR_GPIO);
+ goto out;
+ }
+
+ }
+
+ priv_p->strobe_gpio = STR_GPIO;
+
+ if(client->irq){
+ err = request_threaded_irq(client->irq,NULL,adp1653_irq_hdlr,
+ IRQF_ONESHOT|IRQF_TRIGGER_FALLING,
+ "Adp1653 flash",priv_p);
+ if(err){
+ DEBUG_LOG("Unable to register flash IRQ handler, irq %d, err %d\n",
+ client->irq,err);
+ goto out;
+ }
+ }
+ err = register_flash_chip(flash_position,flash_chip_p);
+ if(err){
+ DEBUG_LOG("Failed to register Adp1653 as flash for %s camera\n",
+ (flash_position?"Primary":"Secondary"));
+ goto out;
+ }
+ SET_FLASH_STATUS(priv_p->status,FLASH_STATUS_READY);
+ DEBUG_LOG("< adp1653_probe ok\n");
+ return err;
+out:
+ if(priv_p->irq_no)
+ free_irq(priv_p->irq_no,NULL);
+ if(priv_p->enable_gpio)
+ gpio_free(priv_p->enable_gpio);
+ if(priv_p)
+ kfree(priv_p);
+ if(flash_chip_p)
+ kfree(flash_chip_p);
+ DEBUG_LOG("< adp1653_probe (%d)\n", err);
+ return err;
+}
+
+static int __devexit adp1653_remove(struct i2c_client *client)
+{
+ int err=0;
+ /*Nothing here yet, implement it later.*/
+ return err;
+}
+static const struct i2c_device_id adp1653_id[] = {
+ { "adp1653", 0},
+ {}
+};
+static struct i2c_driver adp1653_i2c_driver = {
+ .driver = {
+ .name = "adp1653",
+ .owner = THIS_MODULE,
+ },
+ .probe = adp1653_probe,
+ .remove = __devexit_p(adp1653_remove),
+ .id_table = adp1653_id,
+};
+
+int adp1653_init(void){
+ int err=0;
+ struct i2c_adapter *adap_p;
+ struct i2c_board_info info;
+
+ /*Registration of I2C flash device is platform specific code
+ *Ideally it should be done from kernel (arch/arm/mach-XXX).
+ *Do it locally till the time it gets into platform code
+ *OR This portion (registration of device) and flash chip init
+ *Routine can be moved to Flash chip module init.
+ */
+ DEBUG_LOG("getting I2C adaptor %d\n",adapter_i2c2);
+ adap_p = i2c_get_adapter(adapter_i2c2);
+ if(!adap_p){
+ DEBUG_LOG("Unable to get I2C adaptor\n");
+ goto out;
+ }
+ memset(&info,0,sizeof( struct i2c_board_info));
+
+ info.irq = __flash_gpio_to_irq(flash_irq);
+
+ strcpy(&info.type[0],"adp1653");
+ DEBUG_LOG("trying to register %s at position %d, GPIO enable %d, GPIO IRQ line %d\n",
+ info.type,
+ flash_position,
+ enable_gpio,
+ flash_irq);
+
+ /* I2C framework expects least significant 7 bits as address, not complete
+ * 8 bits with bit 0 (read/write bit)
+ */
+ info.addr = 0x60 >> 1;
+
+
+ err = i2c_add_driver(&adp1653_i2c_driver);
+ if(err)
+ {
+ DEBUG_LOG("Failed to register i2c driver\n");
+ goto out;
+ }
+
+
+ DEBUG_LOG("Initialized adp1653\n");
+ if(!i2c_new_device(adap_p,&info)){
+ DEBUG_LOG("Unable to add i2c dev: %s (err=%d)\n",info.type, err);
+ goto out;
+ }
+
+out:
+ return err;
+}
+
+/*
+MODULE_DEPEND
+*/
diff --git a/drivers/staging/camera_flash/flash_chips/adp1653.h b/drivers/staging/camera_flash/flash_chips/adp1653.h
new file mode 100755
index 00000000000..3035ab56d99
--- /dev/null
+++ b/drivers/staging/camera_flash/flash_chips/adp1653.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+#ifndef __ADP1653_H__
+#define __ADP1653_H__
+
+#include <linux/types.h>
+#define ADP1653_ID (0) /*chip does not give any id :) so be it zero!*/
+
+#define OUTPUT_SEL_REG (0x00)
+#define CONFIG_TIMER_REG (0x01)
+#define SW_STROBE_REG (0x02)
+#define FAULT_STATUS_REG (0x03)
+
+/* Fault codes, FALUT_STATUS_REG bits */
+#define OVER_VOLTAGE_FAULT (0x01)
+#define TIMEOUT_FAULT (0x02)
+#define OVER_TEMPERATURE_FAULT (0x04)
+#define SHORT_CIRCUIT_FAULT (0x08)
+
+/*CONFIG_TIMER_REG bits*/
+#define TIMER_ENABLE (0x10)
+
+struct adp1653_priv_data{
+ struct i2c_client *i2c_client;
+ unsigned long curr_mode;
+ unsigned long enable_gpio;
+ unsigned long strobe_gpio;
+ unsigned long irq_no;
+ unsigned long status;
+ uint8_t fault;
+ uint8_t flash_intensity;
+ uint8_t flash_duration;
+ uint8_t torch_intensity;
+ uint8_t indicator_intensity;
+};
+
+/*Intensity current limits in Micro Amps*/
+/* over 250mA flash current is reduced */
+/* do not know why, neither really care about */
+//#define FLASH_MAX_INTENSITY (500000) /*code - 31*/
+#define FLASH_MAX_INTENSITY (250000)
+#define FLASH_MIN_INTENSITY (215000) /*code - 12*/
+#define TORCH_MAX_INTENSITY (200000) /*code - 11*/
+#define TORCH_MIN_INTENSITY (50000) /*code - 1*/
+#define ILED_MAX_INTENSITY (17500) /*Code - 7*/
+#define ILED_MIN_INTENSITY (2500) /*code - 1*/
+
+#define FLASH_MAX_STROBE_DURATION (820000) /*820 uSec*/
+
+#define DURATION_USEC_TO_CODE(_code,_duration) do{ \
+ if(_duration > FLASH_MAX_STROBE_DURATION) \
+ _duration = FLASH_MAX_STROBE_DURATION; \
+ _code = (FLASH_MAX_STROBE_DURATION - _duration) / 54600;\
+}while(0);
+
+#define HPLED_UAMP_TO_CODE(_current) ((_current - 35000) / 15000)
+
+#define FLASH_UAMP_TO_CODE(_code,_current){ \
+ if(_current > FLASH_MAX_INTENSITY) \
+ _current = FLASH_MAX_INTENSITY; \
+ if(_current < FLASH_MIN_INTENSITY) \
+ _current = FLASH_MIN_INTENSITY; \
+ _code = HPLED_UAMP_TO_CODE(_current); \
+}while(0)
+
+#define TORCH_UAMP_TO_CODE(_code,_current){ \
+ if(_current > TORCH_MAX_INTENSITY) \
+ _current = TORCH_MAX_INTENSITY; \
+ if(_current < TORCH_MIN_INTENSITY) \
+ _current = TORCH_MIN_INTENSITY; \
+ _code = HPLED_UAMP_TO_CODE(_current); \
+}while(0)
+
+#define ILED_UAMP_TO_CODE(_code,_current) do { \
+ if(_current > ILED_MAX_INTENSITY) \
+ _current = ILED_MAX_INTENSITY; \
+ _code = _current / ILED_MIN_INTENSITY; /* Min current: 2.5mA/2500uA*/ \
+}while(0)
+
+#endif
diff --git a/drivers/staging/camera_flash/flash_common.c b/drivers/staging/camera_flash/flash_common.c
new file mode 100644
index 00000000000..089bd8a8b68
--- /dev/null
+++ b/drivers/staging/camera_flash/flash_common.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * camera flash: Flash driver to export camera flash to user space application.
+ * It supports two flashes, one for primary and one for secondary camera
+ * Author: Pankaj Chauhan/pankaj.chauhan@stericsson.com for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/kthread.h>
+#include <linux/jiffies.h>
+#include <linux/miscdevice.h>
+#include <linux/camera_flash.h>
+#include "flash_common.h"
+#define DEBUG_LOG(...) printk(KERN_DEBUG "Camera Flash driver: " __VA_ARGS__)
+
+#define PRIMARY_CAMERA (0)
+#define SECONDARY_CAMERA (1)
+static struct miscdevice misc_dev;
+struct flash_chip *flash_chips[2];
+struct fasync_struct * async_queue;
+struct task_struct* ptaskStruct;
+wait_queue_head_t waitQueue;
+int waitCondition = 0;
+struct flash_ioctl_args_t flashArg;
+
+#define COPY_ARG_FROM_USER(_to,_from_usr) do{ \
+ memset((_to),0,sizeof(struct flash_ioctl_args_t)); \
+ if (copy_from_user((_to), (struct flash_ioctl_args_t*) (_from_usr), sizeof(struct flash_ioctl_args_t))) { \
+ DEBUG_LOG("Could not copy data from userspace successfully\n"); \
+ break; \
+ } \
+}while(0)
+
+#define COPY_ARG_TO_USER(_to_usr,_from) do{ \
+ if (copy_to_user((struct flash_ioctl_args_t *)(_to_usr), (_from), sizeof(struct flash_ioctl_args_t))) { \
+ DEBUG_LOG("Could not copy data from userspace successfully\n"); \
+ break; \
+ } \
+}while(0)
+
+
+static int flash_ioctl(struct inode *node, struct file *file_p, unsigned int cmd, unsigned long arg)
+{
+ int err=0;
+ struct flash_chip *flash_p = NULL;
+ struct flash_chip_ops *ops = NULL;
+ char *my_name=NULL;
+ struct flash_ioctl_args_t flash_arg;
+
+ if (_IOC_TYPE(cmd) != FLASH_MAGIC_NUMBER) {
+ printk(KERN_ALERT "Flash driver: Not an ioctl for this module\n");
+ err = -EINVAL;
+ }
+
+ COPY_ARG_FROM_USER(&flash_arg,arg);
+
+ if(flash_arg.cam == SECONDARY_CAMERA || flash_arg.cam == PRIMARY_CAMERA)
+ flash_p = flash_chips[flash_arg.cam];
+ else{
+ DEBUG_LOG("unsupported cam %lu\n",flash_arg.cam);
+ err = -ENODEV;
+ goto out;
+ }
+ my_name = flash_arg.cam ?"Secondary":"Primary";
+
+ if (flash_arg.cam == PRIMARY_CAMERA)
+ {
+ ops = flash_p->ops;
+ }
+
+ switch(cmd){
+ case FLASH_GET_MODES:
+ {
+ if (flash_arg.cam == PRIMARY_CAMERA)
+ {
+ err = ops->get_modes(flash_p->priv_data,&flash_arg.flash_mode);
+ if(!err){
+ DEBUG_LOG("Supported flash modes for %s camera: %lx\n",
+ my_name,flash_arg.flash_mode);
+ COPY_ARG_TO_USER(arg,&flash_arg);
+ }else{
+ DEBUG_LOG("unable to get supported modes for %s camera\n",my_name);
+ }
+ }
+ else
+ {
+ flash_arg.flash_mode = FLASH_MODE_NONE;
+ COPY_ARG_TO_USER(arg,&flash_arg);
+ }
+ }
+ break;
+ case FLASH_GET_MODE_DETAILS:
+ {
+ err = ops->get_mode_details(flash_p->priv_data,flash_arg.flash_mode,
+ &flash_arg.mode_arg.details);
+ if(!err){
+ COPY_ARG_TO_USER(arg,&flash_arg);
+ }else{
+ DEBUG_LOG("Unable to get mode details for %s camera, flash mode %lx\n",
+ my_name,flash_arg.flash_mode);
+ }
+ }
+ break;
+ case FLASH_ENABLE_MODE:
+ case FLASH_DISABLE_MODE:
+ {
+ int enable=0;
+ if(cmd == FLASH_ENABLE_MODE){
+ enable = 1;
+ }
+ err = ops->enable_flash_mode(flash_p->priv_data,flash_arg.flash_mode,enable);
+ if(err){
+ DEBUG_LOG("Unable to %s: %s camera, flash mode %lx\n",
+ (enable ?"Enable":"Disable"), my_name,flash_arg.flash_mode);
+ }
+ }
+ break;
+ case FLASH_CONFIGURE_MODE:
+ err = ops->configure_flash_mode(flash_p->priv_data,flash_arg.flash_mode,
+ &flash_arg.mode_arg.params);
+ if(err){
+ DEBUG_LOG("Unable to configure %s camera, flash mode %lx\n",
+ my_name,flash_arg.flash_mode);
+ }
+ break;
+ case FLASH_TRIGGER_STROBE:
+ err = ops->trigger_strobe(flash_p->priv_data,flash_arg.mode_arg.strobe_enable);
+ if(err){
+ DEBUG_LOG("Unable to %s: %s camera strobe trigger, mode %lx\n",
+ (arg ?"Enable":"Disable"), my_name,flash_arg.flash_mode);
+ }
+ break;
+ case FLASH_GET_STATUS:
+ err = ops->get_status(flash_p->priv_data,&flash_arg.status);
+ if(!err){
+ COPY_ARG_TO_USER(arg,&flash_arg);
+ }else{
+ DEBUG_LOG("Unable to get status details for %s camera, flash mode %lx\n",
+ my_name,flash_arg.flash_mode);
+ }
+ break;
+ case FLASH_GET_LIFE_COUNTER:
+ DEBUG_LOG("Not Implemented\n");
+ break;
+ case FLASH_SELF_TEST:
+ flashArg = flash_arg;
+ if (0 != (flashArg.cam & (FLASH_SELFTEST_FLASH | FLASH_SELFTEST_FLASH_WITH_STROBE)))
+ {
+ err = ENODEV;
+ }
+ else
+ {
+ /* wake up worker thread */
+ waitCondition = 1;
+ wake_up_interruptible(&waitQueue);
+ }
+ break;
+ case FLASH_GET_SELF_TEST_MODES:
+ {
+ if (flash_arg.cam == PRIMARY_CAMERA)
+ {
+ err = ops->get_selftest_modes(flash_p->priv_data,&flash_arg.flash_mode);
+ if(!err){
+ COPY_ARG_TO_USER(arg,&flash_arg);
+ }else{
+ DEBUG_LOG("unable to get supported modes for %s camera\n",my_name);
+ }
+ }
+ else
+ {
+ flash_arg.flash_mode = FLASH_SELFTEST_NONE;
+ COPY_ARG_TO_USER(arg,&flash_arg);
+ }
+ break;
+ }
+ case FLASH_GET_FAULT_REGISTERS:
+ {
+ err = ops->get_fault_registers(flash_p->priv_data,flash_arg.flash_mode,&flash_arg.status);
+ if(!err){
+ COPY_ARG_TO_USER(arg,&flash_arg);
+ }else{
+ DEBUG_LOG("unable to get supported modes for %s camera\n",my_name);
+ }
+
+ break;
+ }
+ case FLASH_GET_SELF_TEST_RESULT:
+ {
+ COPY_ARG_TO_USER(arg,&flashArg);
+ DEBUG_LOG("FLASH_GET_SELF_TEST_RESULT arg : 0x%lx\n", flashArg.status);
+ break;
+ }
+ default:
+ DEBUG_LOG("Unknown command %x\n",cmd);
+
+ }
+out:
+ return err;
+}
+
+int worker_thread (void* data)
+{
+ int err = 0;
+ struct flash_chip *flash_p=NULL;
+ struct flash_chip_ops *ops=NULL;
+ struct flash_mode_params params;
+ struct flash_mode_details details;
+
+ while (1)
+ {
+ /* waiting for some job to do */
+ wait_event_interruptible(waitQueue, (waitCondition != 0));
+ waitCondition = 0;
+
+ DEBUG_LOG("worker_thread wakes up\n");
+ /* do we need to stop ? */
+ err = kthread_should_stop();
+ if (0 != err)
+ {
+ DEBUG_LOG("worker_thread stops\n");
+ break;
+ }
+
+ /* do the job */
+ flash_p = flash_chips[flashArg.cam];
+ ops = flash_p->ops;
+
+ /* clear fault registers */
+ err = ops->get_fault_registers(flash_p->priv_data, FLASH_MODE_INDICATOR, &flashArg.status);
+ if (0 != err)
+ {
+ flashArg.status = flashArg.flash_mode;
+ flashArg.flash_mode = 0;
+ }
+ flashArg.status = 0;
+
+ /* do all selftests */
+ while (flashArg.flash_mode != FLASH_SELFTEST_NONE)
+ {
+ if (0 != (flashArg.flash_mode & FLASH_SELFTEST_CONNECTION))
+ {
+ err = ops->get_mode_details(flash_p->priv_data, FLASH_MODE_INDICATOR, &details);
+ if (0 != err)
+ {
+ DEBUG_LOG("not able to get mode FLASH_MODE_INDICATOR details\n");
+ flashArg.status |= FLASH_SELFTEST_CONNECTION;
+ }
+ flashArg.flash_mode &= ~FLASH_SELFTEST_CONNECTION;
+ }
+ else if (0 != (flashArg.flash_mode & (FLASH_SELFTEST_FLASH | FLASH_SELFTEST_FLASH_WITH_STROBE)))
+ {
+ if (0 != (flashArg.flash_mode & FLASH_SELFTEST_FLASH))
+ {
+ flashArg.status |= FLASH_SELFTEST_FLASH;
+ flashArg.flash_mode &= ~FLASH_SELFTEST_FLASH;
+ }
+ else
+ {
+ flashArg.status |= FLASH_SELFTEST_FLASH_WITH_STROBE;
+ flashArg.flash_mode &= ~FLASH_SELFTEST_FLASH_WITH_STROBE;
+ }
+ }
+ /* FLASH_SELFTEST_VIDEO_LIGHT | FLASH_SELFTEST_AF_LIGHT | FLASH_SELFTEST_INDICATOR | FLASH_SELFTEST_TORCH_LIGHT */
+ else
+ {
+ unsigned long currentSelftest = FLASH_SELFTEST_NONE;
+ unsigned long currentFlashMode = FLASH_MODE_NONE;
+
+ if (0 != (flashArg.flash_mode & FLASH_SELFTEST_VIDEO_LIGHT))
+ {
+ currentSelftest = FLASH_SELFTEST_VIDEO_LIGHT;
+ currentFlashMode = FLASH_MODE_VIDEO_LED;
+ }
+ else if (0 != (flashArg.flash_mode & FLASH_SELFTEST_AF_LIGHT))
+ {
+ currentSelftest = FLASH_SELFTEST_AF_LIGHT;
+ currentFlashMode = FLASH_MODE_AF_ASSISTANT;
+ }
+ else if (0 != (flashArg.flash_mode & FLASH_SELFTEST_INDICATOR))
+ {
+ currentSelftest = FLASH_SELFTEST_INDICATOR;
+ currentFlashMode = FLASH_MODE_INDICATOR;
+ }
+ else
+ {
+ currentSelftest = FLASH_SELFTEST_TORCH_LIGHT;
+ currentFlashMode = FLASH_MODE_VIDEO_LED;
+ }
+
+ err = ops->get_mode_details(flash_p->priv_data, currentFlashMode, &details);
+ if (0 != err)
+ {
+ DEBUG_LOG("not able to get mode 0x%lx details\n",currentFlashMode);
+ flashArg.status |= currentSelftest;
+ flashArg.flash_mode &= ~currentSelftest;
+ continue;
+ }
+
+ err = ops->enable_flash_mode(flash_p->priv_data, currentFlashMode, 1);
+ if (0 != err)
+ {
+ DEBUG_LOG("not able to enable flash mode 0x%lx\n",currentFlashMode);
+ flashArg.status |= currentSelftest;
+ flashArg.flash_mode &= ~currentSelftest;
+ continue;
+ }
+
+ params.duration_uSecs = 0;
+ params.intensity_uAmp = details.max_intensity_uAmp;
+ params.timeout_uSecs = 0;
+ err = ops->configure_flash_mode(flash_p->priv_data, currentFlashMode, &params);
+ if (0 != err)
+ {
+ DEBUG_LOG("not able to configure flash mode 0x%lx\n",currentFlashMode);
+ flashArg.status |= currentSelftest;
+ flashArg.flash_mode &= ~currentSelftest;
+ continue;
+ }
+
+ err = ops->trigger_strobe(flash_p->priv_data,1);
+ if (0 != err)
+ {
+ DEBUG_LOG("not able to strobe, mode : 0x%lx\n",currentFlashMode);
+ flashArg.status |= currentSelftest;
+ flashArg.flash_mode &= ~currentSelftest;
+ continue;
+ }
+
+ wait_event_timeout(waitQueue, 0, msecs_to_jiffies(1000));
+
+ err = ops->trigger_strobe(flash_p->priv_data,0);
+ if (0 != err)
+ {
+ DEBUG_LOG("not able to strobe, mode : 0x%lx\n",currentFlashMode);
+ flashArg.status |= currentSelftest;
+ flashArg.flash_mode &= ~currentSelftest;
+ continue;
+ }
+ flashArg.flash_mode &= ~currentSelftest;
+ }
+ }
+
+ /* job's done ! */
+ flash_async_notify();
+ }
+ return 0;
+}
+
+int flash_open(struct inode *node, struct file *file_p)
+{
+ // init sleep queue
+ init_waitqueue_head(&waitQueue);
+
+ // start worker thread
+ ptaskStruct = kthread_run (&worker_thread, NULL, "flashDriverWorker");
+
+ return 0;
+}
+
+int register_flash_chip(unsigned int cam, struct flash_chip *flash_p)
+{
+ int err =0;
+ DEBUG_LOG("Registering cam %d\n", cam);
+ DEBUG_LOG("flash_p: name=%s\n", flash_p->name);
+ if(cam > 1 || !flash_p){
+ DEBUG_LOG("Registration: something is wrong! cam %d, flash_p %x \n",cam,(int)flash_p);
+ err = EINVAL;
+ goto out;
+ }
+ if(!flash_chips[cam]){
+ flash_chips[cam] = flash_p;
+ DEBUG_LOG("Registered flash: id %lx, %s for camera %d\n",
+ flash_p->id,flash_p->name,cam);
+ }else{
+ DEBUG_LOG("%s flash already registered for camera %d, ignore flash %s\n",
+ flash_chips[cam]->name,cam, flash_p->name);
+ }
+out:
+ return err;
+}
+
+int flash_async_notify ()
+{
+ kill_fasync(&async_queue, SIGIO, POLL_IN);
+ return 0;
+}
+
+static int flash_fasync(int fd, struct file *filp, int mode)
+{
+ DEBUG_LOG("registered async notification on %d fd\n",fd);
+ return fasync_helper(fd, filp, mode, &async_queue);
+}
+
+static int flash_release(struct inode *node, struct file *file_p)
+{
+ int err = 0;
+
+ fasync_helper(-1, file_p, 0, &async_queue);
+
+ // stop worker thread
+ waitCondition = 1;
+ err = kthread_stop(ptaskStruct);
+ return err;
+}
+
+static struct file_operations flash_fops = {
+ owner:THIS_MODULE,
+ ioctl:flash_ioctl,
+ open:flash_open,
+ release:flash_release,
+ fasync:flash_fasync,
+};
+
+int major_device_number;
+
+/*Temporary here (adp_init)*/
+extern int adp1653_init(void);
+static int __init flash_init(void)
+{
+ int err = 0;
+ err = adp1653_init();
+ if(err){
+ DEBUG_LOG("Unable to initialize adp1653, err %d\n",err);
+ goto out;
+ }
+ /* Register misc device */
+ misc_dev.minor = MISC_DYNAMIC_MINOR;
+ misc_dev.name = "camera_flash";
+ misc_dev.fops = &flash_fops;
+ err = misc_register(&misc_dev);
+ if (err < 0) {
+ printk(KERN_INFO "camera_flash driver misc_register failed (%d)\n", err);
+ return err;
+ } else {
+ major_device_number = err;
+ printk(KERN_INFO "camera_flash driver initialized with minor=%d\n", misc_dev.minor);
+ }
+out:
+ return err;
+}
+
+static void __exit flash_exit(void)
+{
+ misc_deregister(&misc_dev);
+ printk(KERN_INFO"Camera flash driver unregistered\n");
+}
+
+module_init(flash_init);
+module_exit(flash_exit);
+MODULE_LICENSE("GPL");
+EXPORT_SYMBOL(register_flash_chip);
+EXPORT_SYMBOL(flash_async_notify);
diff --git a/drivers/staging/camera_flash/flash_common.h b/drivers/staging/camera_flash/flash_common.h
new file mode 100755
index 00000000000..a73c2279d19
--- /dev/null
+++ b/drivers/staging/camera_flash/flash_common.h
@@ -0,0 +1,56 @@
+#ifndef __FLASH_COMMON_H__
+#define __FLASH_COMMON_H__
+
+#include <linux/camera_flash_bitfields.h>
+
+struct flash_chip_ops{
+ int (*get_modes)( void *priv_data, unsigned long *modes);
+ int (*get_mode_details)(void *priv_data,unsigned long mode,
+ struct flash_mode_details *details_p);
+ int (*enable_flash_mode) (void *priv_data,unsigned long mode,
+ int enable);
+ int (*configure_flash_mode) (void *priv_data, unsigned long mode,
+ struct flash_mode_params *params_p);
+ int (*trigger_strobe) (void *priv_data, int enable);
+ int (*get_life_counter) (void *priv_data);
+ int (*get_status) (void *priv_data, unsigned long *status);
+ int (*get_selftest_modes) (void *priv_data,
+ unsigned long *modes);
+ int (*get_fault_registers) (void *priv_data, unsigned long mode,
+ unsigned long *status);
+};
+
+#define FLASH_TYPE_XENON (0x1)
+#define FLASH_TYPE_HPLED (0x2)
+
+#define SET_FLASHCHIP_TYPE(flash_chip_p,_TYPE) ((flash_chip_p)->id |= _TYPE)
+#define GET_FLASHHIP_TYPE(flash_chip_p) ((flash_chip_p)->id & 0xffff)
+#define GET_FLASHCHIP_ID(flash_chip_p) ((flash_chip_p)->id >> 16)
+#define SET_FLASHCHIP_ID(flash_chip_p,_ID) ((flash_chip_p)->id |= (_ID << 16))
+
+struct flash_chip {
+ unsigned long id;
+ struct flash_chip_ops *ops;
+ void *priv_data;
+ unsigned char name[FLASH_NAME_SIZE];
+};
+
+/**
+ * struct flash_platform_data:
+ * platform specific data For flash chip driver
+ * @cam : 0 - primary, 1 - secondary
+ * @strobe_gpio: GPIO used as strobe
+ * @enable_gpio: GPIO used for enable/reset input
+ */
+struct flash_platform_data{
+ unsigned long cam;
+ unsigned long strobe_gpio;
+ unsigned long strobe_gpio_alt_func;
+ unsigned long enable_gpio;
+ unsigned long enable_gpio_alt_func;
+};
+
+extern int register_flash_chip(unsigned int cam, struct flash_chip *flash_p);
+extern int flash_async_notify (void );
+
+#endif
diff --git a/drivers/staging/mmio/Makefile b/drivers/staging/mmio/Makefile
new file mode 100644
index 00000000000..bec2a6efe63
--- /dev/null
+++ b/drivers/staging/mmio/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_U8500_MMIO) := st_mmio.o
diff --git a/drivers/staging/mmio/st_mmio.c b/drivers/staging/mmio/st_mmio.c
new file mode 100644
index 00000000000..15a13e16211
--- /dev/null
+++ b/drivers/staging/mmio/st_mmio.c
@@ -0,0 +1,1246 @@
+/*
+* Copyright (C) ST-Ericsson SA 2010
+* Author: Pankaj Chauhan/pankaj.chauhan@stericsson.com for ST-Ericsson.
+* License terms: GNU General Public License (GPL), version 2.
+*/
+
+#include <linux/init.h> /* Initiliasation support */
+#include <linux/module.h> /* Module support */
+#include <linux/kernel.h> /* Kernel support */
+#include <linux/version.h> /* Kernel version */
+#include <linux/fs.h> /* File operations (fops) defines */
+#include <linux/cdev.h> /* Charactor device support */
+#include <linux/ioport.h> /* Memory/device locking macros */
+#include <linux/errno.h> /* Defines standard err codes */
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/mmio.h>
+#include <linux/workqueue.h>
+#include <plat/pincfg.h>
+#include <mach/prcmu-fw-api.h>
+#include <mach/hardware.h>
+#include <mach/prcmu-regs.h>
+#define ISP_REGION_IO (0xE0000000)
+#define SIA_SIA_BASE_ADDR (0x54000)
+#define SIA_ISP_MEM (0x56000)
+#define SIA_ISP_REG (0x52000)
+#define SIA_TIMER_ITC (0x5BC00)
+#define SIA_ISP_MCU_SYS_ADDR0_OFFSET (SIA_SIA_BASE_ADDR + 0x40)
+#define SIA_ISP_MCU_SYS_SIZE0_OFFSET (SIA_SIA_BASE_ADDR + 0x42)
+#define SIA_ISP_MCU_SYS_ADDR1_OFFSET (SIA_ISP_MCU_SYS_ADDR0_OFFSET + 0x04)
+#define SIA_ISP_MCU_SYS_SIZE1_OFFSET (SIA_ISP_MCU_SYS_SIZE0_OFFSET + 0x04)
+#define SIA_ISP_MCU_IO_ADDR_0_OFFSET (SIA_SIA_BASE_ADDR + 0x60)
+
+/* HTimer enable in CR register */
+#define CR_REG0_HTIMEN (1 << 26)
+
+#define pictor_in_XP70_L2_MEM_BASE_ADDR (0x40000)
+#define pictor_in_XP70_TCDM_MEM_BASE_ADDR (0x60000)
+
+#define write16(addr , value) ((*(volatile unsigned short *) (addr)) = (value))
+#define write32(_addr, _value) writel(_value, _addr)
+#define read32(_addr) readl(_addr)
+#define clrbits32(_addr, _clear) \
+ write32(_addr, read32(_addr) & ~(u32)(_clear))
+#define setbits32(_addr, _set) \
+ write32(_addr, read32(_addr) | (u32)(_set))
+
+#define XP70_BLOCK_SIZE 124
+#define XP70_NB_BLOCK 50
+#define XP70_TIMEOUT_MSEC 300
+
+struct trace_block {
+ u32 msg_id;
+ char data[XP70_BLOCK_SIZE];
+};
+
+struct mmio_trace {
+ u32 nb_block;
+ u32 block_size;
+ u32 block_id;
+ u32 overwrite_count;
+ struct trace_block block[XP70_NB_BLOCK];
+};
+
+struct trace_buffer_status {
+ u32 prev_overwrite_count;
+ u32 prev_block_id;
+};
+
+
+
+struct mmio_info {
+ struct mmio_platform_data *pdata; /* Config from board */
+ struct device *dev; /* My device */
+ /* Runtime variables */
+ struct miscdevice misc_dev;
+ void __iomem *siabase;
+ void __iomem *crbase;
+ /* States */
+ int xshutdown_enabled;
+ /* tracing */
+ struct trace_buffer_status trace_status;
+ struct mmio_trace *trace_buffer;
+ struct delayed_work trace_work;
+ int trace_allowed;
+ int xshutdown_is_active_high;
+};
+/* The one and only private data holder. Default inited to NULL.
+* Declare it here so no code above can use it directly.
+*/
+static struct mmio_info *info;
+
+static int mmio_cam_pwr_sensor(struct mmio_info *info, int on)
+{
+ int err = 0;
+
+ if (on) {
+ err = info->pdata->power_enable(info->pdata);
+ if (err)
+ dev_err(info->dev,
+ "Encountered error while powering on sensor,"
+ "err = %d\n", err);
+ } else
+ info->pdata->power_disable(info->pdata);
+ return err;
+}
+
+static int mmio_cam_set_ext_clk(struct mmio_info *info, enum MMIO_BOOL powerOn)
+{
+ int err = 0;
+ if (powerOn) {
+ err = info->pdata->clock_enable(info->pdata);
+ if (err)
+ dev_err(info->dev,
+ "Encountered error while enabling clock, err = %d\n",
+ err);
+ } else
+ info->pdata->clock_disable(info->pdata);
+ return err;
+}
+
+static int mmio_cam_set_pri_hwif(struct mmio_info *info)
+{
+ if (info->xshutdown_enabled) {
+ info->pdata->set_xshutdown(info->pdata);
+ dev_dbg(info->dev, "Primary cam interface set\n");
+ } else
+ dev_dbg(info->dev, "XSHUTDOWN is not enabled\n");
+ return MMIO_OK;
+}
+
+static int mmio_cam_set_sec_hwif(struct mmio_info *info)
+{
+ if (info->xshutdown_enabled) {
+ info->pdata->set_xshutdown(info->pdata);
+ dev_dbg(info->dev, "Secondary cam interface set\n");
+ } else
+ dev_dbg(info->dev, "XSHUTDOWN is not enabled\n");
+ return MMIO_OK;
+}
+
+static int mmio_cam_init_mmdsp_timer(struct mmio_info *info)
+{
+ /* Disabling Accelerators timers */
+ clrbits32(info->crbase, CR_REG0_HTIMEN);
+
+ /* Write MMDSPTimer */
+ write32(info->siabase + SIA_TIMER_ITC, 0);
+
+ /* Enabling Accelerators timers */
+ setbits32(info->crbase, CR_REG0_HTIMEN);
+ dev_dbg(info->dev, " mmdsp timer set successfully\n");
+ return MMIO_OK;
+}
+
+static u32 t1_to_arm(u32 t1_addr, void __iomem *smia_base_address,
+ u16 *p_mem_page)
+{
+ u16 mem_page_update = 0;
+ mem_page_update = (t1_addr >> 13) & 0x3F;
+
+ if (mem_page_update != *p_mem_page) {
+ /*Update sia_mem_page register */
+ dev_dbg(info->dev, "mem_page_update=0x%x, mem_page=0x%x\n",
+ mem_page_update, *p_mem_page);
+ write16(smia_base_address + 0x54070, mem_page_update);
+ *p_mem_page = mem_page_update;
+ }
+ return SIA_ISP_MEM + (t1_addr & 0x00001fff);
+}
+
+#define VMALLOC_TEMP_AREA(_buf, _src, _size) \
+do { \
+ _buf = vmalloc(_size); \
+ if (!_buf) \
+ goto nomem; \
+ if (copy_from_user(_buf, _src, _size)) { \
+ dev_dbg(info->dev, "Copy from user failed,addr %x, size %x\n",\
+ (unsigned int) _src, (unsigned int) _size); \
+ vfree(_buf); \
+ goto err_exit; \
+ } \
+} while (0)
+
+static int mmio_loadXP70FW(struct mmio_info *info, struct xp70_fw_t *xp70_fw)
+{
+ u32 i = 0, offset = 0;
+ u32 itval = 0;
+ u16 mem_page = 0;
+ void *addrSplit = NULL, *addrData = NULL;
+
+ if (xp70_fw->sizeSplit != 0) {
+
+ dev_dbg(info->dev,
+ "Transferring XP70 firmware split into L2\n");
+ VMALLOC_TEMP_AREA(addrSplit, xp70_fw->addrSplit,
+ xp70_fw->sizeSplit);
+
+ write32(info->siabase + 0x521E4, 0x0);
+
+ /* Put the low 64k IRP firmware in ISP MCU L2 PSRAM */
+ for (i = pictor_in_XP70_L2_MEM_BASE_ADDR;
+ i < (pictor_in_XP70_L2_MEM_BASE_ADDR + 0x10000);
+ i = i + 2) {
+ /* size is hardcoded as L2 cache is 64 kB */
+ itval = t1_to_arm(i, info->siabase, &mem_page);
+
+ itval = ((u32) info->siabase) + 0x00000 + itval;
+ /*Copy fw in L2 */
+ write16(itval, (*((u16 *) addrSplit +
+ offset++)));
+ }
+ vfree(addrSplit);
+ }
+
+ if (xp70_fw->sizeData != 0) {
+ dev_dbg(info->dev,
+ "Transferring XP70 firmware data into TCDM\n");
+ mem_page = 0;
+ offset = 0;
+ VMALLOC_TEMP_AREA(addrData, xp70_fw->addrData,
+ xp70_fw->sizeData);
+
+ write32(info->siabase + 0x521E4, 0x0);
+
+ for (i = pictor_in_XP70_TCDM_MEM_BASE_ADDR;
+ i < (pictor_in_XP70_TCDM_MEM_BASE_ADDR +
+ (xp70_fw->sizeData));
+ i = i + 2) {
+ itval = t1_to_arm(i, info->siabase, &mem_page);
+
+ itval = ((u32) info->siabase) + 0x00000 + itval;
+ /* Copy fw data in TCDM */
+ write16(itval, (*((u16 *) addrData + offset++)));
+ }
+ dev_dbg(info->dev,
+ "Done: Transferring XP70 firmware data into TCDM\n");
+ vfree(addrData);
+ }
+
+ if (xp70_fw->sizeEsramExt != 0) {
+ /* ISP_MCU_SYS_ADDRx XP70 register (@ of ESRAM where the
+ external code has been loaded */
+ write16(info->siabase + SIA_ISP_MCU_SYS_ADDR0_OFFSET,
+ ((u32) xp70_fw->addrEsramExt >> 16));
+ /* ISP_MCU_SYS_SIZEx XP70 register (size of the code =64KB) */
+ write16(info->siabase + SIA_ISP_MCU_SYS_SIZE0_OFFSET, 0x0);
+ dev_dbg(info->dev,
+ "Done: Transferring XP70 firmware ext in eSRAM\n");
+ }
+
+ if (xp70_fw->sizeSdramExt != 0) {
+ /* ISP_MCU_SYS_ADDRx XP70 register (@ of SDRAM where the
+ external code has been loaded */
+ write16(info->siabase + SIA_ISP_MCU_SYS_ADDR1_OFFSET,
+ ((u32) xp70_fw->addrSdramExt >> 16));
+ /* ISP_MCU_SYS_SIZEx XP70 register (size of the code =64KB) */
+ write16(info->siabase + SIA_ISP_MCU_SYS_SIZE1_OFFSET, 0x0);
+
+ dev_dbg(info->dev,
+ "Done: Transferring XP70 firmware ext in SDRAM "
+ "(addr %p)\n", xp70_fw->addrSdramExt);
+ }
+ dev_dbg(info->dev, "Loaded XP70 fw successfully\n");
+ return MMIO_OK;
+nomem:
+ dev_err(info->dev, "Unable to vmalloc temp buffer for f/w load\n");
+ return MMIO_INTERNAL_ERROR;
+err_exit:
+ dev_err(info->dev, "Unable Load XP70 fw\n");
+ return MMIO_INTERNAL_ERROR;
+}
+
+static int mmio_mapStatisticsMemArea(struct mmio_info *info, u32 * addrToMap)
+{
+ u16 value;
+ BUG_ON(addrToMap == NULL);
+
+ value = ((*addrToMap) >> 24) << 8; /* 16 Mbyte aligned page */
+ write16(info->siabase + SIA_ISP_MCU_IO_ADDR_0_OFFSET, value);
+ /* Return the address in the XP70 address space */
+ *addrToMap = ((*addrToMap) & 0x00FFFFFF) | ISP_REGION_IO;
+ dev_dbg(info->dev, "Mapped statistics Mem area to XP70 addr space.\n");
+ return MMIO_OK;
+}
+
+static int mmio_activate_i2c2(struct mmio_info *info, unsigned long enable)
+{
+ int err = MMIO_OK;
+
+ switch (enable) {
+ case MMIO_ACTIVATE_I2C_HOST: {
+ /* Select I2C-2 */
+ err = info->pdata->config_i2c_pins(info->pdata, MMIO_ACTIVATE_I2C_HOST);
+ if (err) {
+ dev_err(info->dev, "Failed to Enable I2C-2, err %d\n",
+ err);
+ err = MMIO_INTERNAL_ERROR;
+ goto out;
+ }
+ dev_dbg(info->dev, "Activated I2C-2 successfully\n");
+ }
+ break;
+ case MMIO_ACTIVATE_IPI2C2: {
+ /* Select IPI2C */
+ err = info->pdata->config_i2c_pins(info->pdata, MMIO_ACTIVATE_IPI2C2);
+ if (err) {
+ dev_err(info->dev, "Failed to Enable IPI2C, err %d\n",
+ err);
+ err = MMIO_INTERNAL_ERROR;
+ goto out;
+ }
+ dev_dbg(info->dev, "Activated IPI2C successfully\n");
+ }
+ break;
+ case MMIO_DEACTIVATE_I2C: {
+ info->pdata->config_i2c_pins(info->pdata, MMIO_DEACTIVATE_I2C);
+ dev_dbg(info->dev, "Deactivated I2C successfully\n");
+ }
+ break;
+ default:
+ dev_dbg(info->dev, "Unknown value passed to"
+ "mmio_activate_i2c2\n");
+ }
+out:
+ return err;
+
+}
+
+static int mmio_enable_xshutdown_from_host(struct mmio_info *info,
+ unsigned long enable)
+{
+ info->xshutdown_is_active_high = enable & MMIO_XSHUTDOWN_ACTIVE_HIGH;
+
+ if (enable & MMIO_XSHUTDOWN_ENABLE) {
+ info->pdata->config_xshutdown_pins(info->pdata,
+ MMIO_ENABLE_XSHUTDOWN_HOST, enable &
+ MMIO_XSHUTDOWN_ACTIVE_HIGH);
+ } else {
+ info->pdata->config_xshutdown_pins(info->pdata,
+ MMIO_ENABLE_XSHUTDOWN_FW, -1);
+ /*XShutdown is controlled by firmware, initial output value is
+ provided by firmware*/
+ }
+ info->xshutdown_enabled = enable & MMIO_XSHUTDOWN_ENABLE;
+
+ dev_dbg(info->dev,
+ "Success in Enable/Disable Xshutdown from host done,"
+ "enable %d\n", info->xshutdown_enabled);
+ return MMIO_OK; /* Always success */
+
+}
+
+static int mmio_cam_initboard(struct mmio_info *info)
+{
+ int err = MMIO_OK;
+ err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP , MMIO_NAME, 100);
+ if (err) {
+ dev_err(info->dev, "Error adding PRCMU QoS requirement %d\n",
+ err);
+ goto out;
+ }
+ /* Configure xshutdown to be disabled by default */
+ mmio_enable_xshutdown_from_host(info, 0);
+
+ /* Enable IPI2C */
+ err = mmio_activate_i2c2(info, MMIO_ACTIVATE_IPI2C2);
+ if (err) {
+ dev_err(info->dev, "Error activating i2c2 %d\n", err);
+ goto out;
+ }
+ dev_dbg(info->dev, "Board initialised successfully\n");
+ return err;
+out:
+ if (err)
+ dev_err(info->dev, "Unable to initialise the board\n");
+ return err;
+}
+int mmio_cam_desinitboard(struct mmio_info *info)
+{
+ dev_dbg(info->dev, "Desinitboard : disable bml & ipi2c clocks\n");
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP , MMIO_NAME);
+ return MMIO_OK;
+}
+static int mmio_isp_write(struct mmio_info *info,
+ struct isp_write_t *isp_write_p)
+{
+ int err = MMIO_OK, i;
+ unsigned long *data = NULL, *addr;
+ unsigned short mem_page = 0;
+ if (isp_write_p->count) {
+ data = kmalloc(isp_write_p->count * 4, GFP_KERNEL);
+ if (!data) {
+ dev_err(info->dev, "kmalloc() failed\n");
+ err = MMIO_INTERNAL_ERROR;
+ goto out;
+ }
+ if (copy_from_user
+ (data, isp_write_p->data, isp_write_p->count * 4)) {
+ dev_err(info->dev, "%s Unable to copy from user\n",
+ __func__);
+ err = MMIO_INTERNAL_ERROR;
+ goto out;
+ }
+ for (i = 0; i < isp_write_p->count; i++) {
+ addr = (unsigned long *)(info->siabase +
+ t1_to_arm(isp_write_p->
+ t1_dest + 4 * i,
+ info->siabase,
+ &mem_page));
+
+ *addr = data[i];
+ }
+ }
+out:
+ if (err) {
+ kfree(data);
+ dev_err(info->dev, "ISP write Failed, %d\n", err);
+ } else
+ dev_dbg(info->dev, "ISP write done\n");
+ return err;
+}
+static int mmio_set_trace_buffer(struct mmio_info *info,
+ struct trace_buf_t *buf)
+{
+ u32 i;
+ int ret = MMIO_INTERNAL_ERROR;
+
+ if (info->trace_allowed != 1) {
+ dev_info(info->dev, "trace disabled in kernel");
+ goto out;
+ }
+
+ if (!buf->size || !buf->address
+ || buf->size < sizeof(struct mmio_trace)) {
+ dev_info(info->dev, "invalid xp70 trace buffer");
+ goto out;
+ }
+
+ if (info->trace_buffer) {
+ dev_info(info->dev, "unmap old buffer");
+ iounmap(info->trace_buffer);
+ info->trace_buffer = NULL;
+ }
+
+ info->trace_buffer = ioremap((unsigned int)buf->address, buf->size);
+ if (!info->trace_buffer) {
+ dev_info(info->dev, "failed to map trace buffer");
+ goto out;
+ }
+ ret = MMIO_OK;
+
+ dev_info(info->dev, "xp70 trace buffer stats(1): overwrite_count=%d (0x%x) block_id=%d (0x%x)",
+ info->trace_buffer->overwrite_count, info->trace_buffer->overwrite_count,
+ info->trace_buffer->block_id, info->trace_buffer->block_id);
+#ifndef CAM_SHARED_MEM_DEBUG
+ /* Reset the allocated buffer contents */
+ for (i = 0; i < XP70_NB_BLOCK; i++) {
+ info->trace_buffer->block[i].msg_id = 0xcdcdcdcd;
+ }
+#endif /* CAM_SHARED_MEMORY_DEBUG */
+
+ dev_info(info->dev, "xp70 trace buffer stats(2): overwrite_count=%d (0x%x) block_id=%d (0x%x)",
+ info->trace_buffer->overwrite_count, info->trace_buffer->overwrite_count,
+ info->trace_buffer->block_id, info->trace_buffer->block_id);
+
+ info->trace_status.prev_overwrite_count = 0;
+ info->trace_status.prev_block_id = 0;
+ /* schedule work */
+ if (!schedule_delayed_work(&info->trace_work,
+ msecs_to_jiffies(XP70_TIMEOUT_MSEC)))
+ dev_info(info->dev, "failed to schedule work");
+out:
+ return ret;
+}
+
+static int mmio_ioctl(struct inode *node, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct MMIO_InputOutput data;
+ int no_of_bytes, err = 0, enable;
+ enum mmio_error error;
+ struct mmio_info *info = (struct mmio_info *)filp->private_data;
+ BUG_ON(info == NULL);
+
+ if (_IOC_TYPE(cmd) != MMIO_MAGIC_NUMBER) {
+ dev_err(info->dev,
+ "MMIO driver: Not an ioctl for this module\n");
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case MMIO_CAM_INITBOARD:
+ no_of_bytes = sizeof(struct MMIO_InputOutput);
+ if (copy_from_user(&data, (struct MMIO_InputOutput *)arg,
+ no_of_bytes)) {
+ dev_dbg(info->dev,
+ "Copy from userspace failed\n");
+ break;
+ }
+ info->pdata->camera_slot = data.mmio_arg.camera_slot;
+
+ error = mmio_cam_initboard(info);
+ err = (int)error;
+ if (MMIO_OK != error)
+ dev_err(info->dev,
+ "Unable to initialise the board\n");
+ else
+ dev_dbg(info->dev,
+ "Board initialised successfully\n");
+
+ if (copy_to_user((int *)arg, &err, sizeof(int))) {
+ dev_dbg(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+ break;
+ case MMIO_CAM_DESINITBOARD:
+ no_of_bytes = sizeof(int);
+ error = mmio_cam_desinitboard(info);
+ err = (int)error;
+ if (MMIO_OK != error)
+ dev_err(info->dev , "Desinitboard failed\n");
+ else
+ dev_dbg(info->dev,
+ "Board desinitialised successfully\n");
+ if (copy_to_user((int *)arg, &err, no_of_bytes)) {
+ dev_dbg(info->dev ,
+ "Copy to userspace failed\n");
+ break;
+ }
+ break;
+ case MMIO_CAM_PWR_SENSOR:
+ no_of_bytes = sizeof(struct MMIO_InputOutput);
+
+ if (copy_from_user
+ (&data, (struct MMIO_InputOutput *)arg,
+ no_of_bytes)) {
+ dev_dbg(info->dev,
+ "Copy from userspace failed\n");
+ break;
+ }
+
+ error = mmio_cam_pwr_sensor(info, data.mmio_arg.powerOn);
+
+ data.error = error;
+
+ if (MMIO_OK != error)
+ dev_err(info->dev,
+ "Unable to power the sensor\n");
+ else
+ dev_dbg(info->dev,
+ "Powered the sensor successfully\n");
+
+ if (copy_to_user
+ ((struct MMIO_InputOutput *)arg, &data,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+ break;
+ case MMIO_CAM_SET_EXT_CLK:
+ no_of_bytes = sizeof(struct MMIO_InputOutput);
+
+ if (copy_from_user
+ (&data, (struct MMIO_InputOutput *)arg,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy from userspace failed\n");
+ break;
+ }
+
+ error =
+ mmio_cam_set_ext_clk(info, data.mmio_arg.powerOn);
+
+ data.error = error;
+
+ if (MMIO_OK != error)
+ dev_err(info->dev,
+ "Unable to set external clock\n");
+ else
+ dev_dbg(info->dev,
+ "External clock set successfully\n");
+
+ if (copy_to_user
+ ((struct MMIO_InputOutput *)arg, &data,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+ break;
+ case MMIO_CAM_LOAD_XP70_FW:
+
+ no_of_bytes = sizeof(struct MMIO_InputOutput);
+ memset(&data, 0, sizeof(struct MMIO_InputOutput));
+ if (copy_from_user
+ (&data, (struct MMIO_InputOutput *)arg,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy from userspace failed\n");
+ break;
+ }
+
+ error = mmio_loadXP70FW(info, &data.mmio_arg.xp70_fw);
+
+ if (MMIO_OK != error)
+ dev_err(info->dev, "Unable Load XP70 fw\n");
+ else
+ dev_dbg(info->dev,
+ "Loaded XP70 fw successfully\n");
+
+ data.error = error;
+ if (copy_to_user
+ ((struct MMIO_InputOutput *)arg, &data,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+ break;
+ case MMIO_CAM_MAP_STATS_AREA:
+
+ no_of_bytes = sizeof(struct MMIO_InputOutput);
+ memset(&data, 0, sizeof(struct MMIO_InputOutput));
+ if (copy_from_user
+ (&data, (struct MMIO_InputOutput *)arg,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy from userspace failed\n");
+ break;
+ }
+
+ error =
+ mmio_mapStatisticsMemArea(info,
+ &data.mmio_arg.addrToMap);
+
+ if (MMIO_OK != error)
+ dev_err(info->dev,
+ "Unable map Statistics Mem area\n");
+ else
+ dev_dbg(info->dev,
+ "Mapped statistics Mem area to XP70 addr space.\n");
+
+ data.error = error;
+ if (copy_to_user
+ ((struct MMIO_InputOutput *)arg, &data,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+ break;
+ case MMIO_CAM_SET_PRI_HWIF:
+ no_of_bytes = sizeof(int);
+
+ error = mmio_cam_set_pri_hwif(info);
+
+ err = (int)error;
+
+ if (MMIO_OK != error)
+ dev_err(info->dev,
+ "Unable to set primary hardware interface\n");
+ else
+ dev_dbg(info->dev,
+ "Primary hardware interface set successfully\n");
+
+ if (copy_to_user((int *)arg, &err, no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+ break;
+ case MMIO_CAM_SET_SEC_HWIF:
+ no_of_bytes = sizeof(int);
+
+ error = mmio_cam_set_sec_hwif(info);
+
+ err = (int)error;
+
+ if (MMIO_OK != error)
+ dev_err(info->dev,
+ "Unable to set secondary hardware interface\n");
+ else
+ dev_dbg(info->dev,
+ "Secondary hardware interface set successfully\n");
+
+ if (copy_to_user((int *)arg, &err, no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+ break;
+ case MMIO_CAM_INITMMDSPTIMER:
+
+ error = mmio_cam_init_mmdsp_timer(info);
+ err = (int)error;
+
+ if (MMIO_OK != error)
+ dev_err(info->dev,
+ "Unable to initialise mmdsp timer\n");
+ else
+ dev_dbg(info->dev,
+ " mmdsp timer set successfully\n");
+
+ break;
+ case MMIO_CAM_ISP_WRITE:
+ {
+ no_of_bytes = sizeof(struct MMIO_InputOutput);
+ memset(&data, 0,
+ sizeof(struct MMIO_InputOutput));
+ if (copy_from_user
+ (&data, (struct MMIO_InputOutput *)arg,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy from userspace failed\n");
+ break;
+ }
+ error =
+ mmio_isp_write(info,
+ &data.mmio_arg.isp_write);
+ err = (int)error;
+ if (MMIO_OK != error)
+ dev_err(info->dev,
+ "ISP write Failed, %d\n", err);
+ else
+ dev_dbg(info->dev, "ISP write done\n");
+ data.error = error;
+ if (copy_to_user
+ ((struct MMIO_InputOutput *)arg, &data,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+ }
+ break;
+ case MMIO_ACTIVATE_I2C2:
+ {
+ if (copy_from_user
+ (&enable, (int *)arg, sizeof(enable))) {
+ dev_err(info->dev,
+ "Copy from userspace failed\n");
+ break;
+ }
+ error = mmio_activate_i2c2(info, enable);
+ err = (int)error;
+ if (MMIO_OK != error)
+ dev_err(info->dev,
+ "activate_i2c2 Failed, Activate %d\n",
+ enable);
+ else
+ dev_dbg(info->dev,
+ "activate_i2c2 Done, Activate %d\n",
+ enable);
+
+ if (copy_to_user((int *)arg, &err, sizeof(err))) {
+ dev_err(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+
+ }
+ break;
+ case MMIO_ENABLE_XSHUTDOWN_FROM_HOST:
+ {
+ if (copy_from_user
+ (&enable, (int *)arg, sizeof(enable))) {
+ dev_err(info->dev,
+ "Copy from userspace failed\n");
+ break;
+ }
+ error =
+ mmio_enable_xshutdown_from_host(info,
+ enable);
+ err = (int)error;
+ if (MMIO_OK != error)
+ dev_err(info->dev,
+ "Config Xshutdown from host failed , enable %d\n",
+ enable);
+ else
+ dev_dbg(info->dev,
+ "Config Xshutdown from host done , enable %d\n",
+ enable);
+
+ if (copy_to_user((int *)arg, &err, sizeof(err))) {
+ dev_err(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+
+ }
+ break;
+ case MMIO_CAM_GET_IP_GPIO:
+ {
+ error = MMIO_OK;
+ no_of_bytes = sizeof(struct MMIO_InputOutput);
+ memset(&data, 0,
+ sizeof(struct MMIO_InputOutput));
+ if (copy_from_user
+ (&data, (struct MMIO_InputOutput *)arg,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy from userspace failed\n");
+ break;
+ }
+ data.mmio_arg.xshutdown_info.ip_gpio =
+ info->pdata->reset_ipgpio
+ [data.mmio_arg.xshutdown_info.camera_function];
+
+ err = (int)error;
+ if (copy_to_user((int *)arg, &err, sizeof(err))) {
+ dev_err(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+ }
+ break;
+ case MMIO_CAM_SET_TRACE_BUFFER:
+ {
+ no_of_bytes = sizeof(struct MMIO_InputOutput);
+ memset(&data, 0, sizeof(struct MMIO_InputOutput));
+ if (copy_from_user
+ (&data, (struct MMIO_InputOutput *) arg,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy from userspace failed\n");
+ break;
+ }
+ error = mmio_set_trace_buffer(info, &data.mmio_arg.trace_buf);
+
+ if (MMIO_OK != error)
+ dev_dbg(info->dev , "Error in setTraceBuffer\n");
+ else
+ dev_dbg(info->dev , "setTraceBuffer successful\n");
+
+ data.error = error;
+ if (copy_to_user
+ ((struct MMIO_InputOutput *) arg, &data,
+ no_of_bytes)) {
+ dev_err(info->dev,
+ "Copy to userspace failed\n");
+ break;
+ }
+ }
+ break;
+ default:
+ dev_err(info->dev,
+ "MMIO driver: Invalid ioctl command\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mmio_release(struct inode *node, struct file *filp)
+{
+ struct mmio_info *info = filp->private_data;
+
+ BUG_ON(info == NULL);
+ mmio_activate_i2c2(info, MMIO_DEACTIVATE_I2C);
+ info->pdata->config_xshutdown_pins(info->pdata, MMIO_DISABLE_XSHUTDOWN,
+ -1);
+
+ if (info->trace_buffer) {
+ iounmap(info->trace_buffer);
+ info->trace_buffer = NULL;
+ }
+
+ flush_delayed_work(&info->trace_work);
+ dev_dbg(info->dev, "Closed successfully\n");
+ return 0;
+}
+
+static int mmio_open(struct inode *node, struct file *filp)
+{
+ filp->private_data = info; /* Hook our mmio info */
+ dev_dbg(info->dev, "Opened successfully\n");
+ return 0;
+}
+
+static const struct file_operations mmio_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = mmio_ioctl,
+ .open = mmio_open,
+ .release = mmio_release,
+};
+
+
+static ssize_t xp70_data_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ int i, len, size = 0, count = 0, first_index;
+
+ first_index = info->trace_status.prev_block_id + 1;
+ if (!info->trace_buffer || info->trace_buffer->block_id == 0xFFFFFFFF)
+ goto out_unlock;
+
+ if (info->trace_allowed != 1) {
+ dev_info(info->dev, "xp70 trace disabled in kernel\n");
+ size = sprintf(buf, "xp70 trace disabled in kernel, "
+ "use sysfs to enable\n");
+ goto out_unlock;
+ }
+
+ count = info->trace_buffer->block_id - info->trace_status.prev_block_id;
+
+ if ((info->trace_buffer->overwrite_count -
+ info->trace_status.prev_overwrite_count) * XP70_NB_BLOCK
+ + (info->trace_buffer->block_id - info->trace_status.prev_block_id)
+ >= XP70_NB_BLOCK) {
+ /* overflow case */
+ info->trace_status.prev_block_id =
+ info->trace_buffer->block_id - XP70_NB_BLOCK;
+ first_index = info->trace_buffer->block_id + 1;
+ count = XP70_NB_BLOCK;
+ len = sprintf(buf, "XP70 trace overflow\n");
+ size += len;
+ buf += len;
+ }
+
+ for (i = first_index; count; count--) {
+ int msglen;
+ if (i < 0 || i >= XP70_NB_BLOCK || count > XP70_NB_BLOCK) {
+ dev_info(info->dev, "trace index out-of-bounds\n");
+ goto out_unlock;
+ }
+ msglen = strnlen(info->trace_buffer->block[i].data,
+ XP70_BLOCK_SIZE);
+ if (msglen > 0) {
+ /* zero terminate full length message */
+ if (msglen == XP70_BLOCK_SIZE)
+ info->trace_buffer->block[i].data[
+ XP70_BLOCK_SIZE - 1] = '\0';
+ len = snprintf(buf, PAGE_SIZE - size, "%d %s\n",
+ info->trace_buffer->block[i].msg_id,
+ info->trace_buffer->block[i].data);
+ if (len > PAGE_SIZE - size) {
+ dev_info(info->dev, "sysfs buffer overflow\n");
+ size = PAGE_SIZE;
+ goto out_unlock;
+ }
+ size += len;
+ buf += len;
+ }
+ i = (i+1)%(XP70_NB_BLOCK);
+ }
+out_unlock:
+ return size;
+}
+
+
+static ssize_t xp70_trace_allowed_show(struct device *device,
+ struct device_attribute *attr,
+ char * buf)
+{
+ int len;
+ len = sprintf(buf, "%d\n", info->trace_allowed);
+ return len;
+}
+
+static ssize_t xp70_trace_allowed_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ if (count <= 0) {
+ dev_info(info->dev, " empty buffer to store\n");
+ return 0;
+ }
+ if (buf[0] == '1')
+ info->trace_allowed = 1;
+ else if (buf[0] == '0')
+ info->trace_allowed = 0;
+ else
+ dev_info(info->dev, "illegal trace_allowed val %c\n",
+ buf[0]);
+ return count;
+}
+
+
+static struct device_attribute xp70_device_attrs[] = {
+ __ATTR_RO(xp70_data),
+ __ATTR(trace_allowed, S_IRUGO | S_IWUSR, xp70_trace_allowed_show,
+ xp70_trace_allowed_store),
+ __ATTR_NULL
+};
+
+void xp70_buffer_wqtask(struct work_struct *data)
+{
+ int i, first_index = info->trace_status.prev_block_id + 1, count;
+
+ if (!info->trace_buffer)
+ goto out_err;
+
+ printk(KERN_INFO "xp70 trace buffer stats: overwrite_count=%d (0x%x) block_id=%d (0x%x)",
+ info->trace_buffer->overwrite_count, info->trace_buffer->overwrite_count,
+ info->trace_buffer->block_id, info->trace_buffer->block_id);
+
+ /* check if trace already started */
+ if (info->trace_buffer->block_id == 0xFFFFFFFF ||
+ info->trace_buffer->block_id == 0xcdcdcdcd ||
+ info->trace_buffer->overwrite_count == 0xcdcdcdcd)
+ goto out;
+
+ if ((info->trace_buffer->overwrite_count -
+ info->trace_status.prev_overwrite_count) * XP70_NB_BLOCK
+ + (info->trace_buffer->block_id -
+ info->trace_status.prev_block_id)
+ >= XP70_NB_BLOCK) {
+ /* overflow case */
+ info->trace_status.prev_block_id =
+ info->trace_buffer->block_id - XP70_NB_BLOCK;
+ first_index = info->trace_buffer->block_id + 1;
+ count = XP70_NB_BLOCK;
+ if (printk_ratelimit())
+ dev_info(info->dev, " XP70 trace overflow\n");
+ } else if (info->trace_buffer->block_id
+ > info->trace_status.prev_block_id) {
+ count = info->trace_buffer->block_id -
+ info->trace_status.prev_block_id;
+ } else {
+ u64 block_id, prev_block_id, diff;
+ block_id = (u64)(info->trace_buffer->block_id);
+ prev_block_id = (u64)(info->trace_status.prev_block_id);
+ diff = (block_id + 0x100000000) - prev_block_id;
+ count = (u32)diff;
+ }
+
+ for (i = first_index; count; count--) {
+ if (i < 0 || i >= XP70_NB_BLOCK || count > XP70_NB_BLOCK) {
+ if (printk_ratelimit())
+ printk(KERN_INFO MMIO_NAME
+ "trace index out-of-bounds"
+ "i=%d count=%d XP70_NB_BLOCK=%d\n",
+ i, count, XP70_NB_BLOCK);
+ break;
+ }
+ if (info->trace_buffer->block[i].msg_id != 0xcdcdcdcd) {
+ int msglen = strnlen(info->trace_buffer->block[i].data,
+ XP70_BLOCK_SIZE);
+ /* zero terminate full length message */
+ if (msglen > 0 && msglen == XP70_BLOCK_SIZE) {
+ info->trace_buffer->block[i].data[
+ XP70_BLOCK_SIZE - 1] = '\0';
+ dev_info(info->dev, " %d %s\n",
+ info->trace_buffer->block[i].msg_id,
+ info->trace_buffer->block[i].data);
+ }
+ }
+ i = (i+1) % XP70_NB_BLOCK;
+ }
+
+ info->trace_status.prev_overwrite_count =
+ info->trace_buffer->overwrite_count;
+ info->trace_status.prev_block_id = info->trace_buffer->block_id;
+out:
+ /* Schedule work */
+ if (!schedule_delayed_work(&info->trace_work,
+ msecs_to_jiffies(XP70_TIMEOUT_MSEC)))
+ printk(KERN_INFO MMIO_NAME "failed to schedule work");
+out_err:
+ return;
+}
+
+
+/**
+* mmio_probe() - Initialize MMIO Camera resources.
+* @pdev: Platform device.
+*
+* Initialize the module and register misc device.
+*
+* Returns:
+* 0 if there is no err.
+* -ENOMEM if allocation fails.
+* -EEXIST if device has already been started.
+* Error codes from misc_register.
+*/
+static int __devinit mmio_probe(struct platform_device *pdev)
+{
+ int err;
+ int i, ret;
+ printk(KERN_INFO "%s\n", __func__);
+
+ /* Initialize private data. */
+ info = kzalloc(sizeof(struct mmio_info), GFP_KERNEL);
+ if (!info) {
+ dev_err(&pdev->dev, "Could not alloc info struct\n");
+ err = -ENOMEM;
+ goto err_alloc;
+ }
+ /* Fill in private data */
+ info->pdata = pdev->dev.platform_data;
+ info->dev = &pdev->dev;
+ info->pdata->dev = &pdev->dev;
+ info->misc_dev.minor = MISC_DYNAMIC_MINOR;
+ info->misc_dev.name = MMIO_NAME;
+ info->misc_dev.fops = &mmio_fops;
+ info->misc_dev.parent = pdev->dev.parent;
+ info->xshutdown_enabled = 0;
+ info->xshutdown_is_active_high = 0;
+
+ /* Register Misc character device */
+ err = misc_register(&(info->misc_dev));
+ if (err) {
+ dev_err(&pdev->dev, "Error %d registering misc dev!", err);
+ goto err_miscreg;
+ }
+
+ /* Memory mapping */
+ info->siabase = ioremap(U8500_SIA_BASE, 1024 * 1024);
+ if (!info->siabase) {
+ dev_err(info->dev, "Could not ioremap(U8500_SIA_BASE)\n");
+ err = -ENOMEM;
+ goto err_ioremap_sia_base;
+ }
+ info->crbase = ioremap(U8500_CR_BASE, PAGE_SIZE);
+ if (!info->crbase) {
+ dev_err(info->dev, "Could not ioremap(U8500_CR_BASE)\n");
+ err = -ENOMEM;
+ goto err_ioremap_cr_base;
+ }
+
+ /* Initialize platform specific data */
+ err = info->pdata->platform_init(info->pdata);
+ if (err)
+ goto err_platform_init;
+
+
+ /* create sysfs entries */
+ for (i = 0; attr_name(xp70_device_attrs[i]); i++) {
+ ret = device_create_file(info->misc_dev.this_device,
+ &xp70_device_attrs[i]);
+ if (ret) {
+ dev_err(info->dev, "Error creating SYSFS entry"
+ " %s (%d)", xp70_device_attrs[i].attr.name,
+ ret);
+ }
+ }
+
+ INIT_DELAYED_WORK(&info->trace_work, xp70_buffer_wqtask);
+#ifdef DEBUG
+ info->trace_allowed = 1;
+#else
+ info->trace_allowed = 0;
+#endif
+ dev_info(&pdev->dev, "MMIO driver initialized with minor=%d\n",
+ info->misc_dev.minor);
+ return 0;
+err_platform_init:
+ iounmap(info->crbase);
+err_ioremap_cr_base:
+ iounmap(info->siabase);
+err_ioremap_sia_base:
+ misc_deregister(&info->misc_dev);
+err_miscreg:
+ kfree(info);
+ info = NULL;
+err_alloc:
+ return err;
+}
+
+/**
+* mmio_remove() - Release MMIO Camera resources.
+* @pdev: Platform device.
+*
+* Remove misc device and free resources.
+*
+* Returns:
+* 0 if success.
+* Error codes from misc_deregister.
+*/
+static int __devexit mmio_remove(struct platform_device *pdev)
+{
+ int err;
+ int i;
+ dev_info(info->dev, "%s\n", __func__);
+
+ if (!info)
+ return 0;
+
+ flush_scheduled_work();
+
+ /* sysfs parameters */
+ for (i = 0; attr_name(xp70_device_attrs[i]); i++)
+ device_remove_file(info->misc_dev.this_device,
+ &xp70_device_attrs[i]);
+
+ err = misc_deregister(&info->misc_dev);
+ if (err)
+ dev_err(&pdev->dev, "Error %d deregistering misc dev", err);
+
+ info->pdata->platform_exit(info->pdata);
+
+ iounmap(info->siabase);
+ iounmap(info->crbase);
+
+ kfree(info);
+ info = NULL;
+
+ return 0;
+}
+static struct platform_driver mmio_driver = {
+ .driver = {
+ .name = MMIO_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = mmio_probe,
+ .remove = __devexit_p(mmio_remove),
+ /* FIXME: Add suspend and resume */
+};
+
+/**
+* mmio_init() - Initialize module.
+*
+* Registers platform driver.
+*/
+static int __init mmio_init(void)
+{
+ printk(KERN_INFO "%s\n", __func__);
+ return platform_driver_register(&mmio_driver);
+}
+
+/**
+* mmio_exit() - Remove module.
+*
+* Unregisters platform driver.
+*/
+static void __exit mmio_exit(void)
+{
+ printk(KERN_INFO "%s\n", __func__);
+ platform_driver_unregister(&mmio_driver);
+}
+
+module_init(mmio_init);
+module_exit(mmio_exit);
+
+MODULE_AUTHOR("Pankaj Chauhan ST-Ericsson");
+MODULE_AUTHOR("Joakim Axelsson ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MMIO Camera driver");
+
diff --git a/drivers/staging/nmf-cm/Make.config b/drivers/staging/nmf-cm/Make.config
new file mode 100644
index 00000000000..ccbc150158b
--- /dev/null
+++ b/drivers/staging/nmf-cm/Make.config
@@ -0,0 +1,8 @@
+# Copyright (C) ST-Ericsson SA 2011. All rights reserved.
+# This code is ST-Ericsson proprietary and confidential.
+# Any use of the code for whatever purpose is subject to
+# specific written permission of ST-Ericsson SA.
+
+#CM driver file to copy but not to compile
+CMENGINESRC_COPY_NO_BUILD = cm/engine/elf/src/elfxx.c
+
diff --git a/drivers/staging/nmf-cm/Makefile b/drivers/staging/nmf-cm/Makefile
new file mode 100644
index 00000000000..446f2eb4589
--- /dev/null
+++ b/drivers/staging/nmf-cm/Makefile
@@ -0,0 +1,98 @@
+#
+# Copyright (C) ST-Ericsson SA 2010
+# Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+# License terms: GNU General Public License (GPL), version 2.
+#
+
+#
+# Rules to build kernel modules
+#
+ifdef KERNELRELEASE
+
+ # $(src): current relative dir; $(kbuild-dir): cur absolute dir
+ ifdef kbuild-dir
+ SRCDIR = $(realpath $(kbuild-dir))
+ else
+ SRCDIR = $(realpath $(src))
+ endif
+ include $(SRCDIR)/Make.config
+ ifndef FIXED_CPPFLAGS
+ # In Android env, we can not depend on files that are out of kernel tree.
+ # and thus we can't include $(SRCDIR)/../../../../mmenv/SharedARMFlags.mk
+ # where FIXED_CPPFLAGS is defined.
+ # So, define FIXED_CPPFLAGS here
+ FIXED_CPPFLAGS=-D__STN_8500=30 -DLINUX -D__ARM_LINUX
+ endif
+ EXTRA_CFLAGS := -I$(SRCDIR) $(FIXED_CPPFLAGS)
+ EXTRA_CFLAGS += -Wall -Werror
+ #EXTRA_CFLAGS += -DCM_DEBUG_ALLOC
+
+ #
+ # CM object files to compile with
+ #
+ GENERIC_CM_FILES:=$(shell cd $(SRCDIR); find cm -name "*.c")
+ GENERIC_CM_FILES := $(filter-out $(CMENGINESRC_COPY_NO_BUILD), $(GENERIC_CM_FILES))
+
+ CM_OBJS := $(GENERIC_CM_FILES:.c=.o)
+ CM_OBJS += cmld.o cm_syscall.o osal-kernel.o cm_service.o configuration.o
+
+ obj-$(CONFIG_U8500_CM) := cm.o
+
+ #Note: build system prepends the $(PWD) directory to these objects paths
+ cm-objs := $(CM_OBJS)
+
+else
+
+ # CM module is built in kernel in android env
+ # or as module otherwise (OSI env, ...)
+ export CONFIG_U8500_CM ?= m
+
+ ifeq ($(findstring install,$(MAKECMDGOALS)),)
+ # If not only performing install then include needed files for build
+ include $(MM_MAKEFILES_DIR)/SharedARMFlags.mk
+ export FIXED_CPPFLAGS
+ -include $(MM_MAKEFILES_DIR)/KernelConfig.mk
+
+ ifeq ($(findstring clean,$(MAKECMDGOALS)),)
+ ifndef KERNEL_BUILD_DIR
+ $(error KERNEL_BUILD_DIR not defined)
+ endif
+ endif
+ endif
+
+ include $(MM_MAKEFILES_DIR)/SharedConfig.mk
+
+ module:
+ $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_BUILD_DIR) \
+ M=$(PWD) INSTALL_HEADER_DIR=$(INSTALL_HEADER_DIR) \
+ modules
+
+ all: module
+ $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_BUILD_DIR) \
+ M=$(PWD) INSTALL_HEADER_DIR=$(INSTALL_HEADER_DIR) \
+ INSTALL_MOD_PATH=$(PWD)/lib/$(PLATFORM) \
+ modules_install
+ rm -f $(PWD)/lib/$(PLATFORM)/lib/modules/*/modules.*
+
+ #
+ # Rules to clean and install
+ #
+ clean:
+ @rm -rf $(PLATFORM) $(CM_OBJS) .built-in.o.cmd .cm*o.cmd Module.symvers \
+ .tmp_versions modules.order cm.ko cm.o cm.mod.* lib \
+ $(foreach f,$(CM_OBJS), $(dir $f).$(notdir $f).cmd)
+
+ realclean: clean
+ $(foreach platform, \
+ $(shell grep property ../../component/component.xml | cut -d\" -f 4), \
+ rm -rf $(platform);)
+ @rm -rf *~
+
+ install:
+ $(GEN_LN) -d lib/$(PLATFORM)/lib $(INSTALL_LIB_DIR)/lib
+
+ uninstall:
+ $(GEN_LN) -r -d lib/$(PLATFORM)/lib $(INSTALL_LIB_DIR)/lib
+
+endif #ifdef KERNELRELEASE
+
diff --git a/drivers/staging/nmf-cm/cm/engine/api/cm_engine.h b/drivers/staging/nmf-cm/cm/engine/api/cm_engine.h
new file mode 100644
index 00000000000..0f4c1e4219e
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/cm_engine.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief CM Engine API.
+ *
+ * This file contains the Component Manager Engine API.
+ */
+
+/*!
+ * \defgroup CM_ENGINE_MODULE CM Engine
+ */
+/*!
+ * \defgroup CM_ENGINE_API CM Engine API
+ *
+ * \note This API is not for user developers, this API is only an internal API.
+ *
+ * \warning All parameters in out from this API means that the parameter is a reference to a data that is complete by the call.
+ *
+ * This API is provided by CM Engine and shall be required by driver kernel part.
+ * \ingroup CM_ENGINE_MODULE
+ */
+
+#ifndef CM_ENGINE_H_
+#define CM_ENGINE_H_
+
+#include <cm/engine/api/configuration_engine.h>
+
+#include <cm/engine/api/component_engine.h>
+
+#include <cm/engine/api/memory_engine.h>
+
+#include <cm/engine/api/communication_engine.h>
+
+#include <cm/engine/api/perfmeter_engine.h>
+
+#include <cm/engine/api/executive_engine_mgt_engine.h>
+
+#include <cm/engine/api/repository_mgt_engine.h>
+
+#include <cm/engine/api/domain_engine.h>
+
+#include <cm/engine/api/migration_engine.h>
+
+#endif /*CM_ENGINE_H_*/
+
diff --git a/drivers/staging/nmf-cm/cm/engine/api/communication_engine.h b/drivers/staging/nmf-cm/cm/engine/api/communication_engine.h
new file mode 100644
index 00000000000..477a66a4002
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/communication_engine.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief Communication User Engine API.
+ *
+ * This file contains the Communication Engine API for manipulating components.
+ *
+ */
+#ifndef COMMUNICATION_ENGINE_H_
+#define COMMUNICATION_ENGINE_H_
+
+#include <cm/engine/communication/inc/communication_type.h>
+
+/*!
+ * \brief Allocate Event buffer where parameters will be marshalled.
+ *
+ * In order to optimize call, this method don't need to be exported to user space,
+ * but must be used by CM driver.
+ *
+ * See \ref HOST2MPC "Host->MPC binding" for seeing an integration example.
+ *
+ * \note This method is not called from user space!!!
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_event_params_handle CM_ENGINE_AllocEvent(t_cm_bf_host2mpc_handle host2mpcId);
+
+/*!
+ * \brief Push a event in Fifo.
+ *
+ * In order to optimize call, this method don't need to be exported to user space,
+ * but must be used by CM driver.
+ *
+ * See \ref HOST2MPC "Host->MPC binding" for seeing an integration example.
+ *
+ * \note This method is not called from user space!!!
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_PushEvent(t_cm_bf_host2mpc_handle host2mpcId, t_event_params_handle h, t_uint32 methodIndex);
+
+/*!
+ * \brief Push a event in Fifo.
+ *
+ * In order to optimize call, this method need to be exported to user space
+ * and must be implemented by CM driver.
+ *
+ * See \ref HOST2MPC "Host->MPC binding" for seeing an integration example.
+ *
+ * \note No implementation of this method is provided in kernel CM engine!!!
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC t_cm_error CM_ENGINE_PushEventWithSize(t_cm_bf_host2mpc_handle host2mpcId, t_event_params_handle h, t_uint32 size, t_uint32 methodIndex);
+
+/*!
+ * \brief Aknowledge a Fifo that the received event has been demarshalled.
+ *
+ * In order to optimize call, this method don't need to be exported to user space,
+ * but must be used by CM driver.
+ *
+ * See \ref MPC2HOST "MPC->Host binding" for seeing an integration example.
+ *
+ * \note This method is not called from user space!!!
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED void CM_ENGINE_AcknowledgeEvent(t_cm_bf_mpc2host_handle mpc2hostId);
+
+#endif /*COMMUNICATION_ENGINE_H_*/
diff --git a/drivers/staging/nmf-cm/cm/engine/api/component_engine.h b/drivers/staging/nmf-cm/cm/engine/api/component_engine.h
new file mode 100644
index 00000000000..cbd61769597
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/component_engine.h
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief Components Component Manager User Engine API.
+ *
+ * This file contains the Component Manager Engine API for manipulating components.
+ *
+ */
+
+#ifndef COMPONENT_ENGINE_H_
+#define COMPONENT_ENGINE_H_
+
+#include <cm/engine/memory/inc/domain_type.h>
+#include <cm/engine/component/inc/component_type.h>
+#include <cm/engine/communication/inc/communication_type.h>
+#include <inc/nmf-limits.h>
+
+/*!
+ * \brief Instantiate a new component.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_InstantiateComponent(
+ const char templateName[MAX_TEMPLATE_NAME_LENGTH], //!< [in] Null terminated string (Max size=\ref MAX_TEMPLATE_NAME_LENGTH)
+ t_cm_domain_id domainId, //!< [in] Domain
+ t_nmf_client_id clientId, //!< [in] Client ID (aka PID)
+ t_nmf_ee_priority priority, //!< [in] Component priority
+ const char localName[MAX_COMPONENT_NAME_LENGTH], //!< [in] Null terminated string (Max size=\ref MAX_COMPONENT_NAME_LENGTH)
+ const char *dataFile, //!< [in] Optional reference on file where component is stored
+ t_cm_instance_handle *component //!< [out] component
+ );
+
+/*!
+ * \brief Start a component.
+ *
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_StartComponent(
+ t_cm_instance_handle component,
+ t_nmf_client_id clientId);
+
+/*!
+ * \brief Stop a component.
+ *
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_StopComponent(
+ t_cm_instance_handle component,
+ t_nmf_client_id clientId);
+
+/*!
+ * \brief Destroy a component.
+ *
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_DestroyComponent(
+ t_cm_instance_handle component,
+ t_nmf_client_id clientId);
+
+/*!
+ * \brief Stop and destroy all components belonging to the given client.
+ *
+ * \param[in] client
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_FlushComponents(
+ t_nmf_client_id client);
+
+/*!
+ * \brief Bind two components together.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_BindComponent(
+ const t_cm_instance_handle client, //!<
+ const char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH], //!< Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ const t_cm_instance_handle server, //!<
+ const char providedItfServerName[MAX_INTERFACE_NAME_LENGTH], //!< Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ t_bool traced, //!< FALSE for synchronous binding, TRUE for traced one
+ t_nmf_client_id clientId, //!< Client ID
+ const char *dataFileTrace //!< Component file data in case on traced (Note: could be null if file already in cache)
+ );
+
+/*!
+ * \brief Unbind a component.
+ *
+ * \param[in] client
+ * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_UnbindComponent(
+ const t_cm_instance_handle client,
+ const char * requiredItfClientName,
+ t_nmf_client_id clientId);
+
+/*!
+ * \brief Bind a component to void (silently ignore a call).
+ *
+ * \param[in] client
+ * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_BindComponentToVoid(
+ const t_cm_instance_handle client,
+ const char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH],
+ t_nmf_client_id clientId);
+
+/*!
+ * \brief Bind two components together in an asynchronous way
+ * (the components can be on the same MPC or on two different MPC)
+ *
+ * \param[in] client
+ * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[in] server
+ * \param[in] providedItfServerName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[in] fifosize
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_BindComponentAsynchronous(
+ const t_cm_instance_handle client,
+ const char * requiredItfClientName,
+ const t_cm_instance_handle server,
+ const char * providedItfServerName,
+ t_uint32 fifosize,
+ t_cm_mpc_memory_type eventMemType,
+ t_nmf_client_id clientId,
+ const char *dataFileSkeletonOrEvent,
+ const char *dataFileStub);
+
+/*!
+ * \brief Unbind a component previously binded asynchronously
+ *
+ * \param[in] client
+ * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentAsynchronous(
+ const t_cm_instance_handle client,
+ const char * requiredItfClientName,
+ t_nmf_client_id clientId);
+
+/*!
+ * \brief Bind the Host to a component.
+ *
+ * \param[in] server
+ * \param[in] providedItfServerName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[in] fifosize
+ * \param[out] host2mpcId
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_BindComponentFromCMCore(
+ const t_cm_instance_handle server,
+ const char * providedItfServerName,
+ t_uint32 fifosize,
+ t_cm_mpc_memory_type eventMemType,
+ t_cm_bf_host2mpc_handle *host2mpcId,
+ t_nmf_client_id clientId,
+ const char *dataFileSkeleton);
+
+/*!
+ * \brief Unbind a component from the Host.
+ *
+ * \param[in] host2mpcId
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentFromCMCore(
+ t_cm_bf_host2mpc_handle host2mpcId);
+
+/*!
+ * \brief Bind a component to the Host, see \ref CM_ENGINE_BindComponentToCMCore.
+ *
+ * See \ref MPC2HOST "MPC->Host binding" for seeing an integration example.
+ *
+ * \note This method is not called from CM Proxy, its only there for wrapping purpose!!!
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_BindComponentToCMCore(
+ const t_cm_instance_handle client,
+ const char *requiredItfClientName,
+ t_uint32 fifosize,
+ t_nmf_mpc2host_handle upLayerThis,
+ const char *dataFileStub,
+ t_cm_bf_mpc2host_handle *mpc2hostId,
+ t_nmf_client_id clientId);
+
+/*!
+ * \brief Unbind a component to the Host, see \ref CM_ENGINE_UnbindComponentToCMCore.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentToCMCore(
+ const t_cm_instance_handle client,
+ const char *requiredItfClientName,
+ t_nmf_mpc2host_handle *upLayerThis,
+ t_nmf_client_id clientId);
+
+/*!
+ * \brief Read a value on an attribute exported by a component instance.
+ *
+ * \param[in] component
+ * \param[in] attrName Null terminated string (Max size=\ref MAX_ATTRIBUTE_NAME_LENGTH).
+ * \param[out] value
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_ReadComponentAttribute(
+ const t_cm_instance_handle component,
+ const char* attrName,
+ t_uint24 *value);
+
+/*!
+ * \brief Get the older component.
+ *
+ * \param[in] client
+ * \param[out] headerComponent
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentListHeader(
+ const t_nmf_client_id client,
+ t_cm_instance_handle *headerComponent);
+
+/*!
+ * \brief Get the next component.
+ *
+ * \param[in] client
+ * \param[in] prevComponent
+ * \param[out] nextComponent
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentListNext(
+ const t_nmf_client_id client,
+ const t_cm_instance_handle prevComponent,
+ t_cm_instance_handle *nextComponent);
+
+/*!
+ * \brief Get a component description
+ *
+ * \param[in] component
+ * \param[in] templateNameLength
+ * \param[in] localNameLength
+ * \param[out] templateName Null terminated string (Size=templateNameLength, Max size=\ref MAX_TEMPLATE_NAME_LENGTH).
+ * \param[out] coreId
+ * \param[out] localName Null terminated string (Size=localNameLength, Max size=\ref MAX_COMPONENT_NAME_LENGTH).
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentDescription(
+ const t_cm_instance_handle component,
+ char *templateName,
+ t_uint32 templateNameLength,
+ t_nmf_core_id *coreId,
+ char *localName,
+ t_uint32 localNameLength,
+ t_nmf_ee_priority *priority);
+
+/*!
+ * \brief Get number of interface required by a component.
+ *
+ * \param[in] component
+ * \param[out] numberRequiredInterfaces
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterfaceNumber(
+ const t_cm_instance_handle component,
+ t_uint8 *numberRequiredInterfaces);
+
+/*!
+ * \brief Return information about required interface.
+ *
+ * \param[in] component
+ * \param[in] index
+ * \param[in] itfNameLength
+ * \param[in] itfTypeLength
+ * \param[out] itfName Null terminated string (Size=itfNameLength, Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[out] itfType Null terminated string (Size=itfTypeLength, Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH).
+ * \param[out] collectionSize
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterface(
+ const t_cm_instance_handle component,
+ const t_uint8 index,
+ char *itfName,
+ t_uint32 itfNameLength,
+ char *itfType,
+ t_uint32 itfTypeLength,
+ t_cm_require_state *requireState,
+ t_sint16 *collectionSize);
+
+/*!
+ * \brief Get the component binded to a required interface.
+ *
+ * \param[in] component
+ * \param[in] itfName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[in] serverItfNameLength
+ * \param[out] server
+ * \param[out] serverItfName Null terminated string (Size=serverItfNameLength, Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterfaceBinding(
+ const t_cm_instance_handle component,
+ const char *itfName,
+ t_cm_instance_handle *server,
+ char *serverItfName,
+ t_uint32 serverItfNameLength);
+
+/*!
+ * \brief Get number of interface provided by a component.
+ *
+ * \param[in] component
+ * \param[out] numberProvidedInterfaces
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentProvidedInterfaceNumber(
+ const t_cm_instance_handle component,
+ t_uint8 *numberProvidedInterfaces);
+
+/*!
+ * \brief Return information about provided interface.
+ *
+ * \param[in] component
+ * \param[in] index
+ * \param[in] itfNameLength
+ * \param[in] itfTypeLength
+ * \param[out] itfName Null terminated string (Size=itfNameLength, Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[out] itfType Null terminated string (Size=itfTypeLength, Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH).
+ * \param[out] collectionSize
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentProvidedInterface(
+ const t_cm_instance_handle component,
+ const t_uint8 index,
+ char *itfName,
+ t_uint32 itfNameLength,
+ char *itfType,
+ t_uint32 itfTypeLength,
+ t_sint16 *collectionSize);
+
+/*!
+ * \brief Get number of properties of a component.
+ *
+ * \param[in] component
+ * \param[out] numberProperties
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyNumber(
+ const t_cm_instance_handle component,
+ t_uint8 *numberProperties);
+
+/*!
+ * \brief Return the name of a property.
+ *
+ * \param[in] component
+ * \param[in] index
+ * \param[in] propertyNameLength
+ * \param[out] propertyName Null terminated string (Size=propertyNameLength, Max size=\ref MAX_PROPERTY_NAME_LENGTH).
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyName(
+ const t_cm_instance_handle component,
+ const t_uint8 index,
+ char *propertyName,
+ t_uint32 propertyNameLength);
+
+/*!
+ * \brief Get property value of a component.
+ *
+ * \param[in] component
+ * \param[in] propertyName
+ * \param[in] propertyValueLength
+ * \param[out] propertyValue Null terminated string (Size=propertyValueLength, Max size=\ref MAX_PROPERTY_VALUE_LENGTH).
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyValue(
+ const t_cm_instance_handle component,
+ const char *propertyName,
+ char *propertyValue,
+ t_uint32 propertyValueLength);
+
+#endif /*COMPONENT_ENGINE_H_*/
diff --git a/drivers/staging/nmf-cm/cm/engine/api/configuration_engine.h b/drivers/staging/nmf-cm/cm/engine/api/configuration_engine.h
new file mode 100644
index 00000000000..0336f62265e
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/configuration_engine.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief Configuration Component Manager User Engine API.
+ *
+ * This file contains the Configuration CM Engine API for manipulating CM.
+ *
+ */
+
+#ifndef CONFIGURATION_ENGINE_H
+#define CONFIGURATION_ENGINE_H
+
+#include <cm/engine/configuration/inc/configuration_type.h>
+
+/*!
+ * \brief Dynamically set some debug parameters of the CM
+ *
+ * \param[in] aCmdID The command for the parameter to update
+ * \param[in] aParam The actual value to set for the given command
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_SetMode(t_cm_cmd_id aCmdID, t_sint32 aParam);
+
+#endif /* CONFIGURATION_ENGINE_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/api/control/configuration_engine.h b/drivers/staging/nmf-cm/cm/engine/api/control/configuration_engine.h
new file mode 100644
index 00000000000..a9543a2af39
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/control/configuration_engine.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief Configuration Component Manager User Engine API.
+ *
+ * This file contains the Configuration CM Engine API for manipulating CM.
+ */
+
+#ifndef CONTROL_CONFIGURATION_ENGINE_H
+#define CONTROL_CONFIGURATION_ENGINE_H
+
+#include <cm/engine/memory/inc/domain_type.h>
+#include <cm/engine/memory/inc/memory_type.h>
+#include <cm/engine/communication/inc/communication_type.h>
+
+/*****************************************************************************************/
+/* Component Manager dedicated (for Configuration purpose) structured types definition */
+/*****************************************************************************************/
+
+/*!
+ * \brief Description of the Nomadik HW mapping configuration
+ *
+ * Describe the Nomadik mapping that is to say:
+ * - the ESRAM memory managed by the CM (The ESRAM address space SHALL BE declared as non cacheable, non bufferable inside host MMU table)
+ * - the mapping of the System HW Semaphore IP
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+typedef struct {
+ t_nmf_memory_segment esramDesc; //!< Description of the ESRAM memory mapping into Nomadik SOC
+ t_cm_system_address hwSemaphoresMappingBaseAddr; //!< Description of the System HW Semaphores IP mapping into Nomadik SOC
+} t_nmf_hw_mapping_desc;
+
+/*!
+ * @defgroup t_nmf_nomadik_version t_nmf_nomadik_version
+ * \brief Description of the various supported Nomadik SOC version
+ * @{
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+typedef t_uint8 t_nmf_nomadik_version; //!< Fake enumeration type
+#define NOMADIK_8810 ((t_nmf_nomadik_version)0) //!< STn8810 chip (any cut)
+#define NOMADIK_8815A0 ((t_nmf_nomadik_version)1) //!< STn8815 chip (cut A0)
+#define NOMADIK_8815 ((t_nmf_nomadik_version)2) //!< STn8815 chip (other cuts)
+#define NOMADIK_8820 ((t_nmf_nomadik_version)3) //!< STn8820 chip
+#define NOMADIK_8500 ((t_nmf_nomadik_version)4) //!< STn8500 chip
+/* @} */
+
+/*!
+ * \brief Description of the configuration parameters of the Component Manager
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+typedef struct {
+ t_nmf_coms_location comsLocation; //!< Configure where CM Communications objects are put (see \ref t_nmf_coms_location)
+} t_nmf_config_desc;
+
+/*!
+ * @defgroup t_nmf_power_ctx t_nmf_power_ctx
+ * \brief Definition of the CM-engine context
+ *
+ * OS integrator uses this value to known the context where the associated OSAL routine is called
+ *
+ * @{
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+
+typedef t_uint32 t_nmf_power_ctx; //!< Fake enumeration type
+#define PWR_FLUSH_REQ_INTERRUPT_CTX ((t_nmf_power_ctx)0x00) //!< Interrupt context - called by \ref CM_ProcessMpcEvent
+#define PWR_FLUSH_REQ_NORMAL_CTX ((t_nmf_power_ctx)0x01) //!< Normal context (CM user call)
+
+/* @} */
+
+
+/****************************************************************************************************************/
+/* Component Manager dedicated (for Media Processors Cores Configuration purpose) structured types definition */
+/****************************************************************************************************************/
+/*!
+ * @defgroup t_nmf_executive_engine_id t_nmf_executive_engine_id
+ * \brief Identification of the Media Processor Executive Engine to deploy
+ * @{
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+typedef t_uint8 t_nmf_executive_engine_id; //!< Fake enumeration type
+#define SYNCHRONOUS_EXECUTIVE_ENGINE ((t_nmf_executive_engine_id)0) //!< MPC Synchronous executive engine
+#define HYBRID_EXECUTIVE_ENGINE ((t_nmf_executive_engine_id)1) //!< MPC Hybrid synchronous executive engine
+/* @} */
+
+/*!
+ * @defgroup t_nmf_semaphore_type_id t_nmf_semaphore_type_id
+ * \brief Definition of which type semaphore shall be used for the given Media Processor communication mechanism
+ * @{
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+typedef t_uint8 t_nmf_semaphore_type_id; //!< Fake enumeration type
+#define LOCAL_SEMAPHORES ((t_nmf_semaphore_type_id)0) //!< Embedded MMDSP macrocell semaphore, so CM_ProcessMpcEvent(<coreId>) shall be called under ISR connected to local MMDSP IRQ0
+#define SYSTEM_SEMAPHORES ((t_nmf_semaphore_type_id)1) //!< Shared system HW Semaphores, so CM_ProcessMpcEvent(ARM_CORE_ID) shall be called under ISR connected to shared HW Sem Host IRQ
+/* @} */
+
+
+/*!
+ * \brief Opaque type for allocator, returned at CM configuration.
+ */
+typedef t_uint32 t_cfg_allocator_id;
+
+/********************************************************************************/
+/* Configuration Component Manager API prototypes */
+/********************************************************************************/
+
+/*!
+ * \brief Initialisation part
+ *
+ * This routine initialize and configure the Component Manager.
+ *
+ * \param[in] pNmfHwMappingDesc hardware mapping description
+ * \param[in] pNmfConfigDesc NMF (mainly CM) Configuration description
+ *
+ * \exception TBD
+ * \return exception number.
+ *
+ * \warning The ESRAM address space SHALL BE declared as non cacheable, non bufferable inside host MMU table
+ *
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+PUBLIC t_cm_error CM_ENGINE_Init(
+ const t_nmf_hw_mapping_desc *pNmfHwMappingDesc,
+ const t_nmf_config_desc *pNmfConfigDesc
+ );
+
+
+/*!
+ * \brief Media Processor core initialisation part
+ *
+ * This routine configures a given Media Processor core
+ *
+ * \param[in] coreId Media Processor identifier
+ * \param[in] executiveEngineId Media Processor Executive Engine identifier
+ * \param[in] semaphoreTypeId Media Processor semaphores (to be used by communication mechanism) identifier
+ * \param[in] nbYramBanks is the number of tcm ram banks to reserved for y memory
+ * \param[in] mediaProcessorMappingBaseAddr Media Processor mapping into host CPU addressable space
+ * \param[in] commDomain Domain for allocating communication FIFOs
+ * \param[in] eeDomain Domain for EE instantiation
+ * \param[in] sdramCodeAllocId Allocator Id for the SDRAM Code segment
+ * \param[in] sdramDataAllocId Allocator Id for the SDRAM Data segment
+ *
+ * \exception TBD
+ * \return exception number.
+ *
+ * \warning The Media Processor mapping address space SHALL BE declared as non cacheable, non bufferable inside host MMU table
+ *
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+PUBLIC t_cm_error CM_ENGINE_ConfigureMediaProcessorCore(
+ t_nmf_core_id coreId,
+ t_nmf_executive_engine_id executiveEngineId,
+ t_nmf_semaphore_type_id semaphoreTypeId,
+ t_uint8 nbYramBanks,
+ const t_cm_system_address *mediaProcessorMappingBaseAddr,
+ const t_cm_domain_id eeDomain,
+ const t_cfg_allocator_id sdramCodeAllocId,
+ const t_cfg_allocator_id sdramDataAllocId
+ );
+
+/*!
+ * \brief Configure a memory segment for later
+ *
+ * \exception TBD
+ * \return TBD
+ *
+ * \warning
+ *
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+PUBLIC t_cm_error CM_ENGINE_AddMpcSdramSegment(
+ const t_nmf_memory_segment *pDesc, //!< [in] Memory segment description.
+ t_cfg_allocator_id *allocId, //!< [out] Identifier of the created allocator.
+ const char *memoryname //!< [in] Memory purpose name
+ );
+
+/********************************************************************************/
+/* Destruction Component Manager API prototypes */
+/********************************************************************************/
+/*!
+ * \brief Destruction part
+ *
+ * This routine destroyes and releases all resources used by the Component Manager.
+ *
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+PUBLIC void CM_ENGINE_Destroy(void);
+
+
+#endif /* CONTROL_CONFIGURATION_ENGINE_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/api/control/control_engine.h b/drivers/staging/nmf-cm/cm/engine/api/control/control_engine.h
new file mode 100644
index 00000000000..1d823b27fc1
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/control/control_engine.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief CM Engine API.
+ *
+ * This file contains the Component Manager Engine API.
+ */
+/*!
+ * \defgroup CM_ENGINE_CONTROL_API CM Engine Control API
+ * \note This API is not for OS integrator, it's only for low level system integration.
+ * \ingroup CM_ENGINE_MODULE
+ */
+
+#ifndef CM_CONTROL_H_
+#define CM_CONTROL_H_
+
+#include <cm/engine/api/control/configuration_engine.h>
+
+#include <cm/engine/api/control/irq_engine.h>
+
+#include <cm/engine/api/control/power_engine.h>
+
+#endif /*CM_CONTROL_H_*/
diff --git a/drivers/staging/nmf-cm/cm/engine/api/control/irq_engine.h b/drivers/staging/nmf-cm/cm/engine/api/control/irq_engine.h
new file mode 100644
index 00000000000..20313f13256
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/control/irq_engine.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief NMF API for interrupt handler.
+ *
+ * This file contains the Component Manager API for interrupt handler.
+ */
+#ifndef CONTROL_IRQ_ENGINE_H
+#define CONTROL_IRQ_ENGINE_H
+
+#include <share/inc/nmf.h>
+#include <cm/inc/cm_type.h>
+#include <nmf/inc/service_type.h>
+
+/*!
+ * \brief MPCs -> HOST communication handler
+ *
+ * This routine shall be integrated as interrupt handler into the OS
+ *
+ * If the given Media Processor Core has been configured (through CM_ConfigureMediaProcessorCore()) as using \ref LOCAL_SEMAPHORES, then
+ * the NMF communication mechanism will use the embedded MMDSP macrocell semaphore,
+ * so CM_ProcessMpcEvent(<\e coreId>) shall be called under ISR connected to local MMDSP IRQ0, with the related \e coreId as parameter.
+ *
+ * If the given Media Processor Core has been configured (through CM_ConfigureMediaProcessorCore()) as using \ref SYSTEM_SEMAPHORES, then
+ * the NMF communication mechanism will use the shared system HW Semaphores,
+ * so CM_ProcessMpcEvent(\ref ARM_CORE_ID) shall be called under ISR connected to shared HW Sem Host IRQ, with \ref ARM_CORE_ID as parameter.
+ *
+ * NB: A Media Processor Core belonging to the distribution pool shall be configured with \ref SYSTEM_SEMAPHORES
+ *
+ * \see t_nmf_semaphore_type_id description
+ *
+ * \param[in] coreId identification of the source of the interrupt
+ *
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+PUBLIC IMPORT_SHARED void CM_ProcessMpcEvent(t_nmf_core_id coreId);
+
+/*!
+ * \brief Service type
+ *
+ * \note We used an enumeration in structure since this description remain inside the kernel
+ * and we assume that everything in the kernel is compile with same compiler and option.
+ *
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+typedef enum { // Allowed since i
+ CM_MPC_SERVICE_NONE = 0, //!< No service found
+ CM_MPC_SERVICE_PANIC = 1, //!< Panic service found
+ CM_MPC_SERVICE_PRINT = 2 //!< Print service found
+} t_cm_service_type;
+ //!< Service description type
+/*!
+ * \brief Service description data
+ *
+ *
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+typedef struct {
+ union {
+ t_nmf_panic_data panic; //!< Panic description
+ struct {
+ t_uint32 dspAddress;
+ t_uint32 value1;
+ t_uint32 value2;
+ } print; //!< Printf like description
+ } u; //!< Union of service description
+} t_cm_service_description;
+
+/*!
+ * \brief MPC Panic handler
+ *
+ * This routine shall be called as interrupt handler into the OS.
+ *
+ * So CM_getPanicDescription shall be called under ISR connected to local MMDSP IRQ1, with the related \e coreId as parameter.
+ *
+ * \param[in] coreId identification of the source of the interrupt
+ * \param[out] srcType Pointer on service type
+ * \param[out] srcDescr Pointer on service description
+ *
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_getServiceDescription(
+ t_nmf_core_id coreId,
+ t_cm_service_type *srcType,
+ t_cm_service_description *srcDescr);
+
+/*!
+ * \brief Read a null terminated string inside an MPC
+ *
+ * This routine could be used to read the MPC string give as parameter during an CM_NMF_SERVICE_PRINT
+ *
+ * \param[in] coreId Identification of the code where read string
+ * \param[in] dspAddress Address of the string in the MPC
+ * \param[out] buffer Buffer pointer where returning null terminated string
+ * \param[in] bufferSize Buffer size
+ *
+ * \ingroup CM_ENGINE_CONTROL_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ReadMPCString(
+ t_nmf_core_id coreId,
+ t_uint32 dspAddress,
+ char * buffer,
+ t_uint32 bufferSize);
+
+#endif /* CONTROL_IRQ_ENGINE_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/api/domain_engine.h b/drivers/staging/nmf-cm/cm/engine/api/domain_engine.h
new file mode 100644
index 00000000000..7cc6f33ed90
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/domain_engine.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief Public Component Manager Memory User SYSCALL API.
+ *
+ * This file contains the Component Manager SYSCALL API for manipulating domains.
+ *
+ */
+
+#ifndef __INC_DOMAIN_ENGINE_H
+#define __INC_DOMAIN_ENGINE_H
+
+#include <cm/engine/memory/inc/domain_type.h>
+
+/*!
+ * \brief Create a domain.
+ *
+ * Create a memory domain for use in the CM for component instantiation and memory allocation.
+ *
+ * \param[in] client Id of the client.
+ * \param[in] domain Description of domain memories.
+ * \param[out] handle Idetifier of the created domain
+ *
+ * \exception CM_INVALID_DOMAIN_DEFINITION
+ * \exception CM_INTERNAL_DOMAIN_OVERFLOW
+ * \exception CM_OK
+ *
+ * \return Error code.
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_CreateMemoryDomain(
+ const t_nmf_client_id client,
+ const t_cm_domain_memory *domain,
+ t_cm_domain_id *handle
+ );
+
+/*!
+ * \brief Create a scratch domain.
+ *
+ * Create a scratch memory domain. Scratch domains
+ * are used to perform overlapping allocations.
+ *
+ * \param[in] client Id of the client.
+ * \param[in] parentId Identifier of the parent domain.
+ * \param[in] domain Description of domain memories.
+ * \param[out] handle Idetifier of the created domain
+ *
+ * \exception CM_INVALID_DOMAIN_DEFINITION
+ * \exception CM_INTERNAL_DOMAIN_OVERFLOW
+ * \exception CM_NO_MORE_MEMORY
+ * \exception CM_OK
+ *
+ * \return Error code.
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_CreateMemoryDomainScratch(
+ const t_nmf_client_id client,
+ const t_cm_domain_id parentId,
+ const t_cm_domain_memory *domain,
+ t_cm_domain_id *handle
+ );
+
+/*!
+ * \brief Destroy a memory domain.
+
+ * \param[in] handle Domain identifier to destroy.
+ *
+ * \exception CM_INVALID_DOMAIN_HANDLE
+ * \exception CM_OK
+ *
+ * \return Error code.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_DestroyMemoryDomain(
+ t_cm_domain_id handle);
+
+/*!
+ * \brief Destroy all domains belonging to a given client.
+ *
+ * \param[in] client
+ *
+ * \return Error code.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_FlushMemoryDomains(
+ t_nmf_client_id client);
+
+/*!
+ * \brief Retrieve the coreId for a given domain. Utility.
+
+ * \param[in] domainId Domain identifier.
+ * \param[out] coreId Core identifier.
+ *
+ * \exception CM_INVALID_DOMAIN_HANDLE Invalid domain handle
+ * \exception CM_OK
+ *
+ * \return Error code.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetDomainCoreId(const t_cm_domain_id domainId, t_nmf_core_id *coreId);
+
+#endif /* __INC_DOMAIN_ENGINE_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/api/executive_engine_mgt_engine.h b/drivers/staging/nmf-cm/cm/engine/api/executive_engine_mgt_engine.h
new file mode 100644
index 00000000000..9cb8bc1481b
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/executive_engine_mgt_engine.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief CM executive engine management Engine API.
+ *
+ * This file contains the Component Manager executive engine management Engine API.
+ */
+#ifndef CM_EXECUTIVE_ENGINE_MANAGEMENT_ENGINE_H_
+#define CM_EXECUTIVE_ENGINE_MANAGEMENT_ENGINE_H_
+
+#include <cm/inc/cm_type.h>
+
+/*!
+ * \brief Return executive engine handle for given core
+ *
+ * \param[in] coreId The core for which we want executive engine handle.
+ * \param[out] executiveEngineHandle executive engine instance (null if the executive engine is not loaded)
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetExecutiveEngineHandle(
+ t_cm_domain_id domainId,
+ t_cm_instance_handle *executiveEngineHandle);
+
+#endif /*CM_EXECUTIVE_ENGINE_MANAGEMENT_ENGINE_H_*/
diff --git a/drivers/staging/nmf-cm/cm/engine/api/memory_engine.h b/drivers/staging/nmf-cm/cm/engine/api/memory_engine.h
new file mode 100644
index 00000000000..9f5e25b3ebf
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/memory_engine.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief Public Component Manager Memory User Engine API.
+ *
+ * This file contains the Component Manager Engine API for manipulating memory.
+ *
+ */
+
+#ifndef CM_MEMORY_ENGINE_H_
+#define CM_MEMORY_ENGINE_H_
+
+#include <cm/engine/memory/inc/domain_type.h>
+#include <cm/engine/memory/inc/memory_type.h>
+
+/*!
+ * \brief Allocate memory in a Media Processor Core memory
+ *
+ * \param[in] domainId
+ * \param[in] memType
+ * \param[in] size
+ * \param[in] memAlignment
+ * \param[out] pHandle
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_AllocMpcMemory(
+ t_cm_domain_id domainId,
+ t_nmf_client_id clientId, //!< [in] Client ID (aka PID)
+ t_cm_mpc_memory_type memType,
+ t_cm_size size,
+ t_cm_mpc_memory_alignment memAlignment,
+ t_cm_memory_handle *pHandle
+ );
+
+
+/*!
+ * \brief Free a MPC memory block.
+ *
+ * \param[in] handle
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_FreeMpcMemory(t_cm_memory_handle handle);
+
+/*!
+ * \brief Get the start address of the MPC memory block seen by the host CPU (physical and logical)
+ *
+ * The logical system address returned by this method is valid only in kernel space and the physical
+ * address is accessible only from kernel space too.
+ *
+ * \see OSMem "OS Memory management" for seeing an integration example.
+ *
+ * \param[in] handle
+ * \param[out] pSystemAddress
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemorySystemAddress(
+ t_cm_memory_handle handle,
+ t_cm_system_address *pSystemAddress);
+
+/*!
+ * \brief Get the start address of the memory block seen by the Media Processor Core
+ *
+ * \param[in] handle
+ * \param[out] pMpcAddress
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemoryMpcAddress(
+ t_cm_memory_handle handle,
+ t_uint32 *pMpcAddress);
+
+/*!
+ * \brief Get the memory status for given memory type of a given Media Processor Core
+ *
+ * \param[in] domainId
+ * \param[in] memType
+ * \param[out] pStatus
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemoryStatus(
+ t_cm_domain_id domainId,
+ t_cm_mpc_memory_type memType,
+ t_cm_allocator_status *pStatus);
+
+#endif /* CM_MEMORY_ENGINE_H_ */
+
diff --git a/drivers/staging/nmf-cm/cm/engine/api/migration_engine.h b/drivers/staging/nmf-cm/cm/engine/api/migration_engine.h
new file mode 100644
index 00000000000..77a266d4459
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/migration_engine.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#ifndef CM_MIGRATION_ENGINE_H
+#define CM_MIGRATION_ENGINE_H
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/memory/inc/domain_type.h>
+
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_Migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst);
+
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_Unmigrate(void);
+
+#endif /* CM_MIGRATION_ENGINE_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/api/perfmeter_engine.h b/drivers/staging/nmf-cm/cm/engine/api/perfmeter_engine.h
new file mode 100644
index 00000000000..bead49dc81e
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/perfmeter_engine.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief CM Performance Meter Engine API.
+ *
+ * This file contains the Component Manager Performance Meter Engine API.
+ */
+#ifndef CM_ENGINE_PERFMETER_ENGINE_H_
+#define CM_ENGINE_PERFMETER_ENGINE_H_
+
+#include <cm/engine/perfmeter/inc/perfmeter_type.h>
+
+/*!
+ * \brief MPC cpu load
+ *
+ * \param[in] coreId identification of mpc from which we want cpu load
+ * \param[out] mpcLoadCounter will contain mpc cpu load counters value if success
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_getMpcLoadCounter(
+ t_nmf_core_id coreId,
+ t_cm_mpc_load_counter *mpcLoadCounter);
+
+/*!
+ * \brief MPC cpu load
+ * Same as \ref CM_ENGINE_getMpcLoadCounter() without lock
+ *
+ * \param[in] coreId identification of mpc from which we want cpu load
+ * \param[out] mpcLoadCounter will contain mpc cpu load counters value if success
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetMpcLoadCounter(
+ t_nmf_core_id coreId,
+ t_cm_mpc_load_counter *mpcLoadCounter);
+
+#endif /*CM_ENGINE_PERFMETER_ENGINE_H_*/
diff --git a/drivers/staging/nmf-cm/cm/engine/api/repository_mgt_engine.h b/drivers/staging/nmf-cm/cm/engine/api/repository_mgt_engine.h
new file mode 100644
index 00000000000..3520f974be6
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/api/repository_mgt_engine.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief Repository Component Manager User Engine API.
+ *
+ * This file contains the Component Manager Engine API for manipulating the components files.
+ */
+
+#ifndef REPOSITORY_MGT_ENGINE_H_
+#define REPOSITORY_MGT_ENGINE_H_
+
+#include <inc/nmf-limits.h>
+#include <cm/engine/repository_mgt/inc/repository_type.h>
+
+/*!
+ * \brief Get the name(s) of the component(s) to load.
+ *
+ * \param[in] client Handle of the client component (optional)
+ * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH) (optional).
+ * \param[in] server Handle of the server component (optional)
+ * \param[in] providedItfServerName Null terminated string (Max size==\ref MAX_INTERFACE_NAME_LENGTH) (optional).
+ * \param[out] fileList List of required component(s).
+ * \param[in,out] listSize Initial size of the list as input. Updated with the number of entries really used.
+ * \param[out] type Interface type of the client required or server provided interface. Null terminated string (Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH) (optional) .
+ * \param[out] methodNumber Number of method in the interface type of the client required interface. (only used when called from CM_BindComponentToUser) (optional)
+ *
+ * \note It returns the component(s) name(s) to load, depending on the first four parameters.
+ *
+ * - If all 4 are NULL, it returns the name of the Executive Engine components to load
+ * - If 'client' is NULL, it returns the name of the required components for a Bind From CMCore.
+ * - If 'server' is NULL, it returns the name of the required components for a Bind To CMCore.
+ * - If none is NULL, it returns the name of the required components for an asynchronous binding
+ *
+ * The names are returned in fileList, whose initial size is specified in listSize.
+ * (sizeList must be the number of provided entries of \ref MAX_INTERFACE_TYPE_NAME_LENGTH length
+ * If not enough space is provided, CM_NO_MORE_MEMORY is returned
+ *
+ * sizeList is updated with the number entries really filled.
+ *
+ * This method is also used to retrieve the interface type when called from CM_BindComponentToUser and CM_BindComponentFromUser
+ * and the number of methods when called from CM_BindComponentToUser.
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_GetRequiredComponentFiles(
+ // IN
+ t_action_to_do action,
+ const t_cm_instance_handle client,
+ const char *requiredItfClientName,
+ const t_cm_instance_handle server,
+ const char *providedItfServerName,
+ // OUT component to be pushed
+ char fileList[][MAX_INTERFACE_TYPE_NAME_LENGTH],
+ // IN max component allowed to be pushed
+ t_uint32 listSize,
+ // OUT interface information
+ char type[MAX_INTERFACE_TYPE_NAME_LENGTH],
+ t_uint32 *methodNumber);
+
+/*!
+ * \brief Push a component into the CM Component Cache.
+ *
+ * \param[in] name Component name, null terminated string (Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH)
+ * \param[in] data Pointer to _user_ data of the component.
+ * \param[in] size Size of the data.
+ *
+ * \note Push a component in the Component Cache
+ * The 'data' must be provided such a way that they can be freed by a call to OSAL_Free()
+ * The caller doesn't need and must NOT free the data, even in case of failure.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_PushComponent(const char *name, const void *data, t_cm_size size);
+
+/*!
+ * \brief Remove a component from the CM Component Cache.
+ *
+ * \param[in] name Component name, null terminated string (Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH)
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ENGINE_ReleaseComponent (const char *name);
+
+#endif /*REPOSITORY_MGT_ENGINE_H_*/
diff --git a/drivers/staging/nmf-cm/cm/engine/communication/fifo/inc/nmf_fifo_arm.h b/drivers/staging/nmf-cm/cm/engine/communication/fifo/inc/nmf_fifo_arm.h
new file mode 100644
index 00000000000..41c4f7a7ad6
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/communication/fifo/inc/nmf_fifo_arm.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ *
+ */
+#ifndef __INC_NMF_FIFO_ARM
+#define __INC_NMF_FIFO_ARM
+
+#include <cm/inc/cm_type.h>
+#include <share/communication/inc/nmf_fifo_desc.h>
+#include <cm/engine/memory/inc/memory.h>
+#include <cm/engine/component/inc/instance.h>
+#include <share/semaphores/inc/semaphores.h>
+#include <cm/engine/memory/inc/domain.h>
+
+/*
+ * ARM Fifo descriptor (encapsulate the share one)
+ */
+typedef struct
+{
+ t_uint32 magic;
+ t_memory_handle chunkHandle;
+ t_nmf_core_id pusherCoreId;
+ t_nmf_core_id poperCoreId;
+ t_shared_addr dspAdress;
+ t_dsp_address_info dspAddressInfo;
+ t_nmf_fifo_desc *fifoDesc; //used for all fifo operations and systematically updated by the migrated offset (see cm_AllocEvent)
+ t_nmf_fifo_desc *fifoDescShadow; //shadow desc, is used to restore state after migration and perform the update of the real desc
+
+ // ExtendedField
+ t_memory_handle extendedFieldHandle;
+ t_shared_field *extendedField;
+} t_nmf_fifo_arm_desc;
+
+PUBLIC t_uint32 fifo_isFifoIdValid(t_nmf_fifo_arm_desc *pArmFifo);
+PUBLIC t_nmf_fifo_arm_desc* fifo_alloc(
+ t_nmf_core_id pusherCoreId, t_nmf_core_id poperCoreId,
+ t_uint16 size_in_16bit, t_uint16 nbElem, t_uint16 nbExtendedSharedFields,
+ t_dsp_memory_type_id memType, t_dsp_memory_type_id memExtendedFieldType, t_cm_domain_id domainId);
+PUBLIC void fifo_free(t_nmf_fifo_arm_desc *pArmFifo);
+PUBLIC t_uint16 fifo_normalizeDepth(t_uint16 requestedDepth);
+
+PUBLIC t_shared_addr fifo_getAndAckNextElemToWritePointer(t_nmf_fifo_arm_desc *pArmFifo);
+PUBLIC t_shared_addr fifo_getAndAckNextElemToReadPointer(t_nmf_fifo_arm_desc *pArmFifo);
+PUBLIC t_shared_addr fifo_getNextElemToWritePointer(t_nmf_fifo_arm_desc *pArmFifo);
+PUBLIC t_shared_addr fifo_getNextElemToReadPointer(t_nmf_fifo_arm_desc *pArmFifo);
+PUBLIC void fifo_acknowledgeRead(t_nmf_fifo_arm_desc *pArmFifo);
+PUBLIC void fifo_acknowledgeWrite(t_nmf_fifo_arm_desc *pArmFifo);
+PUBLIC void fifo_coms_acknowledgeWriteAndInterruptGeneration(t_nmf_fifo_arm_desc *pArmFifo);
+
+PUBLIC t_cm_error fifo_params_setSharedField(t_nmf_fifo_arm_desc *pArmFifo, t_uint32 sharedFieldIndex, t_shared_field value);
+
+#endif /* __INC_NMF_FIFO_ARM */
diff --git a/drivers/staging/nmf-cm/cm/engine/communication/fifo/src/nmf_fifo_arm.c b/drivers/staging/nmf-cm/cm/engine/communication/fifo/src/nmf_fifo_arm.c
new file mode 100644
index 00000000000..48d7f9e9f03
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/communication/fifo/src/nmf_fifo_arm.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ *
+ */
+#include <share/communication/inc/nmf_fifo_desc.h>
+#include <cm/engine/semaphores/inc/semaphores.h>
+#include <cm/engine/component/inc/instance.h>
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+#include "../inc/nmf_fifo_arm.h"
+
+#include <cm/engine/dsp/inc/dsp.h>
+#include <cm/engine/memory/inc/memory.h>
+#include <cm/engine/memory/inc/domain.h>
+#include <cm/engine/trace/inc/trace.h>
+
+/* define value of fifo magic number */
+#define NMF_FIFO_MAGIC_NB 0xF1F0BEEF
+
+PRIVATE t_uint16 fifo_getCount(
+ t_uint16 writeIndex,
+ t_uint16 readIndex,
+ t_uint16 fifoSize
+)
+{
+ if (writeIndex >= readIndex) {return writeIndex - readIndex;}
+ else {return fifoSize - readIndex + writeIndex;}
+}
+
+PRIVATE t_uint16 fifo_incrementIndex(
+ t_uint16 index,
+ t_uint16 wrappingValue
+)
+{
+ if (++index == wrappingValue) {index = 0;}
+
+ return index;
+}
+
+PUBLIC t_uint16 fifo_normalizeDepth(t_uint16 requestedDepth)
+{
+ /* with new implementation we don't align on power of two */
+ return requestedDepth;
+}
+
+PUBLIC t_nmf_fifo_arm_desc* fifo_alloc(
+ t_nmf_core_id pusherCoreId, t_nmf_core_id poperCoreId,
+ t_uint16 size_in_16bit, t_uint16 nbElem, t_uint16 nbExtendedSharedFields,
+ t_dsp_memory_type_id memType, t_dsp_memory_type_id memExtendedFieldType, t_cm_domain_id domainId)
+{
+ t_uint16 realNbElem = nbElem + 1;/* we need one more elem in new implementation */
+ t_uint16 sizeToAlloc = sizeof(t_nmf_fifo_desc) + ((size_in_16bit<<1)*realNbElem);
+ t_nmf_fifo_arm_desc *pArmFifoDesc;
+
+ pArmFifoDesc = (t_nmf_fifo_arm_desc*)OSAL_Alloc(sizeof (t_nmf_fifo_arm_desc));
+ if (pArmFifoDesc == NULL)
+ goto errorde;
+
+ pArmFifoDesc->chunkHandle = cm_DM_Alloc(domainId, memType,
+ (sizeToAlloc/2), CM_MM_ALIGN_2WORDS, TRUE); /* size in 16-bit since we use EXT16 memory */
+ if (pArmFifoDesc->chunkHandle == INVALID_MEMORY_HANDLE)
+ goto errorsh;
+
+ pArmFifoDesc->magic = NMF_FIFO_MAGIC_NB;
+ pArmFifoDesc->pusherCoreId = pusherCoreId;
+ pArmFifoDesc->poperCoreId = poperCoreId;
+
+ pArmFifoDesc->fifoDesc = (t_nmf_fifo_desc *)cm_DSP_GetHostLogicalAddress(pArmFifoDesc->chunkHandle);
+ cm_DSP_GetDspAddress(pArmFifoDesc->chunkHandle, &pArmFifoDesc->dspAdress);
+
+ pArmFifoDesc->fifoDescShadow = pArmFifoDesc->fifoDesc;
+ cm_DSP_GetDspDataAddressInfo(cm_DM_GetDomainCoreId(domainId), pArmFifoDesc->dspAdress, &pArmFifoDesc->dspAddressInfo);
+
+ pArmFifoDesc->extendedFieldHandle = INVALID_MEMORY_HANDLE;
+ pArmFifoDesc->extendedField = NULL;
+
+ pArmFifoDesc->fifoDesc->elemSize = size_in_16bit;
+ pArmFifoDesc->fifoDesc->fifoFullValue = nbElem;
+ pArmFifoDesc->fifoDesc->wrappingValue = realNbElem;
+
+ pArmFifoDesc->fifoDesc->semId = cm_SEM_Alloc(pusherCoreId, poperCoreId);
+ pArmFifoDesc->fifoDesc->readIndex = 0;
+ pArmFifoDesc->fifoDesc->writeIndex = 0;
+
+ LOG_INTERNAL(2, "\n##### Fifo alloc 0x%x (0x%x)\n\n", pArmFifoDesc, pArmFifoDesc->fifoDesc, 0, 0, 0, 0);
+
+ if (nbExtendedSharedFields >= 1)
+ {
+ if(poperCoreId == ARM_CORE_ID)
+ {
+ /* Optimization: Don't put extended Field in DSP memory since use only by ARM if popper */
+ pArmFifoDesc->extendedField = (t_shared_field*)OSAL_Alloc(nbExtendedSharedFields * sizeof(t_shared_field));
+ if (pArmFifoDesc->extendedField == NULL)
+ goto errorex;
+
+ pArmFifoDesc->fifoDesc->extendedField = (t_uint32)pArmFifoDesc->extendedField;
+ }
+ else
+ {
+ pArmFifoDesc->extendedFieldHandle = cm_DM_Alloc(domainId, memExtendedFieldType,
+ nbExtendedSharedFields * sizeof(t_shared_field) / 4, CM_MM_ALIGN_WORD, TRUE);
+ if (pArmFifoDesc->extendedFieldHandle == INVALID_MEMORY_HANDLE)
+ goto errorex;
+
+ pArmFifoDesc->extendedField = (t_shared_field*)cm_DSP_GetHostLogicalAddress(pArmFifoDesc->extendedFieldHandle);
+ cm_DSP_GetDspAddress(pArmFifoDesc->extendedFieldHandle, (t_uint32*)&pArmFifoDesc->fifoDesc->extendedField);
+ }
+
+ pArmFifoDesc->extendedField[EXTENDED_FIELD_BCTHIS_OR_TOP] = (t_shared_field)0;
+ }
+
+ return pArmFifoDesc;
+
+errorex:
+ (void)cm_DM_Free(pArmFifoDesc->chunkHandle, TRUE);
+errorsh:
+ OSAL_Free(pArmFifoDesc);
+errorde:
+ return NULL;
+}
+
+PUBLIC t_uint32 fifo_isFifoIdValid(t_nmf_fifo_arm_desc *pArmFifo)
+{
+ if (((t_uint32)pArmFifo & CM_MM_ALIGN_WORD) != 0) {return FALSE;}
+ if (pArmFifo->magic == NMF_FIFO_MAGIC_NB) {return TRUE;}
+ else {return FALSE;}
+}
+
+PUBLIC void fifo_free(t_nmf_fifo_arm_desc *pArmFifo)
+{
+ CM_ASSERT(pArmFifo->pusherCoreId != ARM_CORE_ID || pArmFifo->poperCoreId != ARM_CORE_ID);
+
+ pArmFifo->magic = ~NMF_FIFO_MAGIC_NB;
+
+ if(pArmFifo->extendedFieldHandle != INVALID_MEMORY_HANDLE)
+ (void)cm_DM_Free(pArmFifo->extendedFieldHandle, TRUE);
+ else if(pArmFifo->extendedField != NULL)
+ OSAL_Free(pArmFifo->extendedField);
+
+ (void)cm_DM_Free(pArmFifo->chunkHandle, TRUE);
+ OSAL_Free(pArmFifo);
+}
+
+PUBLIC t_shared_addr fifo_getAndAckNextElemToWritePointer(t_nmf_fifo_arm_desc *pArmFifo)
+{
+ t_shared_addr retValue;
+
+ retValue = fifo_getNextElemToWritePointer(pArmFifo);
+ if (retValue != 0)
+ {
+ fifo_acknowledgeWrite(pArmFifo);
+ }
+
+ return retValue;
+}
+
+PUBLIC t_shared_addr fifo_getAndAckNextElemToReadPointer(t_nmf_fifo_arm_desc *pArmFifo)
+{
+ t_shared_addr retValue;
+
+ retValue = fifo_getNextElemToReadPointer(pArmFifo);
+ if (retValue != 0)
+ {
+ fifo_acknowledgeRead(pArmFifo);
+ }
+
+ return retValue;
+}
+
+PUBLIC t_shared_addr fifo_getNextElemToWritePointer(t_nmf_fifo_arm_desc *pArmFifo)
+{
+ t_shared_addr retValue = 0;
+ t_nmf_fifo_desc *pDesc;
+ t_uint16 count;
+
+ if ((NULL == pArmFifo) || (NULL == (pDesc = pArmFifo->fifoDesc)))
+ return 0;
+
+ count = fifo_getCount(pDesc->writeIndex, pDesc->readIndex,pDesc->wrappingValue);
+ if (count < pDesc->fifoFullValue)
+ {
+ retValue = ((t_shared_addr)pDesc + sizeof(t_nmf_fifo_desc) + (pDesc->writeIndex*(pDesc->elemSize<<1)));
+ }
+
+ return retValue;
+}
+
+PUBLIC t_shared_addr fifo_getNextElemToReadPointer(t_nmf_fifo_arm_desc *pArmFifo)
+{
+ t_shared_addr retValue = 0;
+ t_nmf_fifo_desc *pDesc;
+ t_uint16 count;
+
+ if ((NULL == pArmFifo) || (NULL == (pDesc = pArmFifo->fifoDesc)))
+ return 0;
+
+ count = fifo_getCount(pDesc->writeIndex, pDesc->readIndex,pDesc->wrappingValue);
+ if (count != 0)
+ {
+ retValue = ((t_shared_addr)pDesc+ sizeof(t_nmf_fifo_desc) + (pDesc->readIndex*(pDesc->elemSize<<1)));
+ }
+
+ return retValue;
+}
+
+PUBLIC void fifo_acknowledgeRead(t_nmf_fifo_arm_desc *pArmFifo)
+{
+ t_nmf_fifo_desc *pDesc = pArmFifo->fifoDesc;
+
+ pDesc->readIndex = fifo_incrementIndex(pDesc->readIndex, pDesc->wrappingValue);
+}
+
+PUBLIC void fifo_acknowledgeWrite(t_nmf_fifo_arm_desc *pArmFifo)
+{
+ t_nmf_fifo_desc *pDesc = pArmFifo->fifoDesc;
+
+ pDesc->writeIndex = fifo_incrementIndex(pDesc->writeIndex, pDesc->wrappingValue);
+}
+
+PUBLIC void fifo_coms_acknowledgeWriteAndInterruptGeneration(t_nmf_fifo_arm_desc *pArmFifo)
+{
+ t_nmf_fifo_desc *pDesc = pArmFifo->fifoDesc;
+
+ fifo_acknowledgeWrite(pArmFifo);
+ //Be sure before generate irq that fifo has been updated
+ OSAL_mb();
+ cm_SEM_GenerateIrq[pArmFifo->poperCoreId](pArmFifo->poperCoreId, pDesc->semId);
+ //cm_SEM_Take[pArmFifo->poperCoreId](pArmFifo->poperCoreId, pDesc->semId);
+ //cm_SEM_GiveWithInterruptGeneration[pArmFifo->poperCoreId](pArmFifo->poperCoreId, pDesc->semId);
+}
+
+PUBLIC t_cm_error fifo_params_setSharedField(t_nmf_fifo_arm_desc *pArmFifo, t_uint32 sharedFieldIndex, t_shared_field value)
+{
+ pArmFifo->extendedField[sharedFieldIndex] = value;
+
+ return CM_OK;
+}
+
diff --git a/drivers/staging/nmf-cm/cm/engine/communication/inc/communication.h b/drivers/staging/nmf-cm/cm/engine/communication/inc/communication.h
new file mode 100644
index 00000000000..53ab87b7096
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/communication/inc/communication.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Components Management internal methods - Communication part.
+ *
+ */
+#ifndef __INC_NMF_COM
+#define __INC_NMF_COM
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/communication/fifo/inc/nmf_fifo_arm.h>
+#include <cm/engine/memory/inc/memory.h>
+
+#include <cm/engine/communication/inc/communication_type.h>
+
+extern t_dsp_memory_type_id comsLocation;
+extern t_dsp_memory_type_id paramsLocation;
+extern t_dsp_memory_type_id extendedFieldLocation;
+
+PUBLIC t_cm_error cm_COM_Init(t_nmf_coms_location comsLocation);
+PUBLIC t_cm_error cm_COM_AllocateMpc(t_nmf_core_id coreId);
+PUBLIC void cm_COM_InitMpc(t_nmf_core_id coreId);
+PUBLIC void cm_COM_FreeMpc(t_nmf_core_id coreId);
+
+PUBLIC t_cm_error cm_PushEventTrace(t_nmf_fifo_arm_desc*, t_event_params_handle h, t_uint32 methodIndex, t_uint32 isTrace);
+PUBLIC t_cm_error cm_PushEvent(t_nmf_fifo_arm_desc *pArmFifo, t_event_params_handle h, t_uint32 methodIndex);
+PUBLIC void cm_AcknowledgeEvent(t_nmf_fifo_arm_desc *pArmFifo);
+PUBLIC t_event_params_handle cm_AllocEvent(t_nmf_fifo_arm_desc *pArmFifo);
+
+/*!
+ * \internal
+ * \brief Definition of custom value for userTHIS parameter of PostDfc OSAL call
+ *
+ * This value is used as 1st parameter of a pPostDfc call to indicate that a given interrupt is linked to an internal Component Manager event
+ */
+#define NMF_INTERNAL_USERTHIS ((void*)MASK_ALL32)
+
+typedef void (*t_callback_method)(t_nmf_core_id coreId, t_event_params_handle pParam);
+
+#endif /* __INC_NMF_COM */
diff --git a/drivers/staging/nmf-cm/cm/engine/communication/inc/communication_type.h b/drivers/staging/nmf-cm/cm/engine/communication/inc/communication_type.h
new file mode 100644
index 00000000000..db426845a82
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/communication/inc/communication_type.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Communication Component Manager API type.
+ */
+#ifndef COMMUNICATION_TYPE_H_
+#define COMMUNICATION_TYPE_H_
+
+#include <cm/inc/cm_type.h>
+
+
+/*!
+ * \brief Buffer type used for (un)marshalling parameters.
+ *
+ * This buffer type is used for (un)marshalling paramaters. It can either be a
+ * shared memory buffer (ESRAM or SDRAM) or a pure host software memory (stack).
+
+ * \ingroup CM_ENGINE_API
+ */
+typedef t_uint16 *t_event_params_handle;
+
+/*!
+ * \brief Component manager handle to Host -> MPC communication.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+typedef t_uint32 t_cm_bf_host2mpc_handle;
+
+/*!
+ * \brief Component manager handle to MPC -> Host communication.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+typedef t_uint32 t_cm_bf_mpc2host_handle;
+
+/*!
+ * \brief Component manager proxy handle to MPC -> Host skeleton context.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+typedef t_uint32 t_nmf_mpc2host_handle;
+
+/*!
+ * @defgroup t_nmf_coms_location t_nmf_coms_location
+ * \brief Definition of the location of the internal CM communication objects
+ *
+ * @{
+ * \ingroup CM_ENGINE_API
+ */
+typedef t_uint8 t_nmf_coms_location; //!< Fake enumeration type
+#define COMS_IN_ESRAM ((t_nmf_coms_location)0) //!< All coms objects (coms and params fifos) will be in embedded RAM
+#define COMS_IN_SDRAM ((t_nmf_coms_location)1) //!< All coms objects (coms and params fifos) will be in external RAM
+/* @} */
+
+#endif /*COMMUNICATION_TYPE_H_*/
diff --git a/drivers/staging/nmf-cm/cm/engine/communication/src/communication.c b/drivers/staging/nmf-cm/cm/engine/communication/src/communication.c
new file mode 100644
index 00000000000..8f48f02afc2
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/communication/src/communication.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#include <cm/inc/cm_type.h>
+#include "../inc/communication.h"
+#include <share/communication/inc/communication_fifo.h>
+#include <cm/engine/api/control/irq_engine.h>
+#include <cm/engine/dsp/inc/dsp.h>
+#include <cm/engine/component/inc/introspection.h>
+#include <cm/engine/communication/fifo/inc/nmf_fifo_arm.h>
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+#include <cm/engine/memory/inc/domain.h>
+#include <cm/engine/memory/inc/migration.h>
+#include <cm/engine/semaphores/inc/semaphores.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/trace/inc/xtitrace.h>
+
+#include <cm/engine/component/inc/initializer.h>
+
+#define ARM_DSP_EVENT_FIFO_SIZE 16
+
+t_dsp_memory_type_id comsLocation;
+t_dsp_memory_type_id paramsLocation;
+t_dsp_memory_type_id extendedFieldLocation;
+
+#define __DEBUG
+
+#ifdef __DEBUG
+PRIVATE volatile t_uint32 armdspCounter = 0;
+PRIVATE volatile t_uint32 armdspIrqCounter = 0;
+PRIVATE volatile t_uint32 dsparmCounter = 0;
+PRIVATE volatile t_uint32 dsparmIrqCounter = 0;
+#endif /* __DEBUG */
+
+t_nmf_fifo_arm_desc* mpc2mpcComsFifoId[NB_CORE_IDS][NB_CORE_IDS];
+
+PRIVATE const t_callback_method internalHostJumptable[] = {
+ processAsyncAcknowledge,
+ processAsyncAcknowledge,
+ processAsyncAcknowledge,
+ processSyncAcknowledge,
+ processAsyncAcknowledge,
+ processAsyncAcknowledge,
+ processAsyncAcknowledge,
+ processSyncAcknowledge,
+ processAsyncAcknowledge,
+ processSyncAcknowledge,
+ processSyncAcknowledge
+};
+
+PUBLIC t_cm_error cm_COM_Init(t_nmf_coms_location _comsLocation)
+{
+ t_nmf_core_id coreId, localCoreId;
+
+ /*
+ * Configure the default location of coms and params fifo (configuration by user) */
+ switch(_comsLocation)
+ {
+ case COMS_IN_SDRAM:
+ comsLocation = SDRAM_EXT16;
+ paramsLocation = SDRAM_EXT16;
+ extendedFieldLocation = SDRAM_EXT24;
+ break;
+ case COMS_IN_ESRAM:
+ comsLocation = ESRAM_EXT16;
+ paramsLocation = ESRAM_EXT16;
+ extendedFieldLocation = ESRAM_EXT24;
+ break;
+ default: CM_ASSERT(0);
+ }
+
+ for (coreId = ARM_CORE_ID; coreId < NB_CORE_IDS; coreId++)
+ {
+ for (localCoreId = ARM_CORE_ID; localCoreId < NB_CORE_IDS; localCoreId++)
+ {
+ mpc2mpcComsFifoId[coreId][localCoreId] = NULL;
+ }
+ }
+
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_COM_AllocateMpc(t_nmf_core_id coreId)
+{
+ t_nmf_core_id localCoreId;
+
+ /*
+ * Allocation of the coms fifo with neighbor MPCs
+ * if they are already initialized (known through initializedCoresMask)
+ */
+ for (localCoreId = ARM_CORE_ID; localCoreId < NB_CORE_IDS; localCoreId++)
+ {
+ if (localCoreId == coreId) continue; /* no coms fifo with itself ;) */
+ if(cm_DSP_GetState(localCoreId)->state != MPC_STATE_BOOTED) continue;
+
+ /*
+ * coms fifo from other initialized MPCs to the given one
+ */
+ if (mpc2mpcComsFifoId[coreId][localCoreId] != NULL) continue; /* coms fifo already allocated */
+
+ mpc2mpcComsFifoId[coreId][localCoreId] = fifo_alloc(
+ coreId, localCoreId,
+ EVENT_ELEM_SIZE_IN_BYTE/2, ARM_DSP_EVENT_FIFO_SIZE,
+ 0, comsLocation, extendedFieldLocation, cm_DSP_GetState(coreId)->domainEE
+ );
+ if (mpc2mpcComsFifoId[coreId][localCoreId] == NULL)
+ goto oom;
+
+ /*
+ * coms fifo from the given MPC to the other initialized ones
+ */
+ if (mpc2mpcComsFifoId[localCoreId][coreId] != NULL) continue; /* coms fifo already allocated */
+
+ mpc2mpcComsFifoId[localCoreId][coreId] = fifo_alloc(
+ localCoreId, coreId,
+ EVENT_ELEM_SIZE_IN_BYTE/2, ARM_DSP_EVENT_FIFO_SIZE,
+ 0, comsLocation, extendedFieldLocation, cm_DSP_GetState(coreId)->domainEE
+ );
+ if (mpc2mpcComsFifoId[localCoreId][coreId] == NULL)
+ goto oom;
+ }
+
+ return CM_OK;
+oom:
+ cm_COM_FreeMpc(coreId);
+ return CM_NO_MORE_MEMORY;
+}
+
+PUBLIC void cm_COM_InitMpc(t_nmf_core_id coreId)
+{
+ // Here we assume that attribute are in XRAM, thus we don't need memory type
+ t_uint32* toNeighborsComsFifoIdSharedVar[NB_CORE_IDS];
+ t_uint32* fromNeighborsComsFifoIdSharedVar[NB_CORE_IDS];
+
+ t_nmf_core_id localCoreId;
+
+ /*
+ * Initialization of the core identifier of a given Executive Engine
+ * Used into communication scheme so the init is done here, will be moved MAY BE into EE loading module!!!
+ */
+ cm_writeAttribute(cm_EEM_getExecutiveEngine(coreId)->instance, "semaphores/myCoreId", coreId);
+
+ /*
+ * Initialization of the coms fifo with the Host for the given coreId
+ */
+ for (localCoreId = FIRST_MPC_ID/* NOT ARM*/; localCoreId <= LAST_CORE_ID; localCoreId++)
+ {
+ // Note: This loop will also include coreId in order to fill
+ if(cm_DSP_GetState(localCoreId)->state != MPC_STATE_BOOTED) continue;/* no coms fifo initialisation with not booted MPC */
+
+ toNeighborsComsFifoIdSharedVar[localCoreId] = (t_uint32*)cm_getAttributeHostAddr(cm_EEM_getExecutiveEngine(localCoreId)->instance, "comms/toNeighborsComsFifoId");
+
+ fromNeighborsComsFifoIdSharedVar[localCoreId] = (t_uint32*)cm_getAttributeHostAddr(cm_EEM_getExecutiveEngine(localCoreId)->instance, "comms/fromNeighborsComsFifoId");
+ }
+
+ toNeighborsComsFifoIdSharedVar[coreId][ARM_CORE_ID] = mpc2mpcComsFifoId[coreId][ARM_CORE_ID]->dspAdress;
+ fromNeighborsComsFifoIdSharedVar[coreId][ARM_CORE_ID] = mpc2mpcComsFifoId[ARM_CORE_ID][coreId]->dspAdress;
+
+ for (localCoreId = FIRST_MPC_ID/* NOT ARM*/; localCoreId <= LAST_CORE_ID; localCoreId++)
+ {
+ if (localCoreId == coreId) continue; /* no coms fifo with itself ;) */
+ if(cm_DSP_GetState(localCoreId)->state != MPC_STATE_BOOTED) continue;/* no coms fifo initialisation with not booted MPC */
+
+ toNeighborsComsFifoIdSharedVar[coreId][localCoreId] = mpc2mpcComsFifoId[coreId][localCoreId]->dspAdress;
+ fromNeighborsComsFifoIdSharedVar[localCoreId][coreId] = mpc2mpcComsFifoId[coreId][localCoreId]->dspAdress;
+
+ fromNeighborsComsFifoIdSharedVar[coreId][localCoreId] = mpc2mpcComsFifoId[localCoreId][coreId]->dspAdress;
+ toNeighborsComsFifoIdSharedVar[localCoreId][coreId] = mpc2mpcComsFifoId[localCoreId][coreId]->dspAdress;
+ }
+}
+
+PUBLIC void cm_COM_FreeMpc(t_nmf_core_id coreId)
+{
+ t_nmf_core_id localCoreId;
+
+ for (localCoreId = ARM_CORE_ID; localCoreId < NB_CORE_IDS; localCoreId++)
+ {
+ /*
+ * Free coms fifo from other initialized MPCs to the given one
+ */
+ if ( mpc2mpcComsFifoId[coreId][localCoreId] != NULL)
+ {
+ fifo_free(mpc2mpcComsFifoId[coreId][localCoreId]);
+ mpc2mpcComsFifoId[coreId][localCoreId] = NULL;
+ }
+
+ /*
+ * Free coms fifo from the given MPC to the other initialized ones
+ */
+ if ( mpc2mpcComsFifoId[localCoreId][coreId] != NULL)
+ {
+ fifo_free(mpc2mpcComsFifoId[localCoreId][coreId]);
+ mpc2mpcComsFifoId[localCoreId][coreId] = NULL;
+ }
+ }
+}
+
+PUBLIC t_event_params_handle cm_AllocEvent(t_nmf_fifo_arm_desc *pArmFifo)
+
+{
+ t_uint32 retValue;
+
+ //migration impacts the ARM-side address of the fifoDesc,
+ //thus translate the fifo desc adress systematically.
+ pArmFifo->fifoDesc = (t_nmf_fifo_desc*)cm_migration_translate(pArmFifo->dspAddressInfo.segmentType, (t_shared_addr)pArmFifo->fifoDescShadow);
+
+ retValue = fifo_getAndAckNextElemToWritePointer(pArmFifo);
+
+ return (t_event_params_handle)retValue;
+}
+
+PUBLIC void cm_AcknowledgeEvent(t_nmf_fifo_arm_desc *pArmFifo)
+{
+ fifo_acknowledgeRead(pArmFifo);
+}
+
+PUBLIC t_cm_error cm_PushEventTrace(t_nmf_fifo_arm_desc *pArmFifo, t_event_params_handle h, t_uint32 methodIndex, t_uint32 isTrace)
+{
+ t_uint32 retValue;
+
+ retValue = fifo_getNextElemToWritePointer(mpc2mpcComsFifoId[ARM_CORE_ID][pArmFifo->poperCoreId]);
+
+ if(retValue != 0x0) {
+ t_shared_field *pEvent = (t_shared_field *)retValue;
+
+#ifdef __DEBUG
+ armdspCounter++;
+#endif /* __DEBUG */
+
+ pEvent[EVENT_ELEM_METHOD_IDX] = (t_shared_addr)methodIndex;
+ pEvent[EVENT_ELEM_PARAM_IDX] = pArmFifo->dspAdress + (((t_cm_logical_address)h - (t_cm_logical_address)pArmFifo->fifoDesc) >> 1); //note byte to half-word conversion
+ pEvent[EVENT_ELEM_EXTFIELD_IDX] = pArmFifo->fifoDesc->extendedField;
+
+ if (isTrace)
+ {
+ cm_TRC_traceCommunication(
+ TRACE_COMMUNICATION_COMMAND_SEND,
+ ARM_CORE_ID,
+ pArmFifo->poperCoreId);
+ }
+ fifo_coms_acknowledgeWriteAndInterruptGeneration(mpc2mpcComsFifoId[ARM_CORE_ID][pArmFifo->poperCoreId]);
+
+ return CM_OK;
+ }
+
+ ERROR("CM_MPC_NOT_RESPONDING: FIFO COM full '%s'\n", 0, 0, 0, 0, 0, 0);
+ return CM_MPC_NOT_RESPONDING;
+}
+
+PUBLIC t_cm_error cm_PushEvent(t_nmf_fifo_arm_desc *pArmFifo, t_event_params_handle h, t_uint32 methodIndex)
+{
+ return cm_PushEventTrace(pArmFifo,h,methodIndex,1);
+}
+
+static void cmProcessMPCFifo(t_nmf_core_id coreId)
+{
+ t_shared_field *pEvent;
+
+ while((pEvent = (t_shared_field *)fifo_getNextElemToReadPointer(mpc2mpcComsFifoId[coreId][ARM_CORE_ID])) != NULL)
+ {
+ t_event_params_handle pParamsAddr;
+ t_shared_field *pParamsFifoESFDesc;
+
+ pParamsAddr = (t_event_params_handle)cm_DSP_ConvertDspAddressToHostLogicalAddress(
+ coreId,
+ pEvent[EVENT_ELEM_PARAM_IDX]);
+ pParamsFifoESFDesc = (t_shared_field *)pEvent[EVENT_ELEM_EXTFIELD_IDX];
+#ifdef __DEBUG
+ dsparmCounter++;
+#endif /* __DEBUG */
+
+ if(pParamsFifoESFDesc[EXTENDED_FIELD_BCTHIS_OR_TOP] == (t_shared_field)NMF_INTERNAL_USERTHIS)
+ {
+ internalHostJumptable[pEvent[EVENT_ELEM_METHOD_IDX]](coreId, pParamsAddr);
+ }
+ else
+ {
+ cm_TRC_traceCommunication(
+ TRACE_COMMUNICATION_COMMAND_RECEIVE,
+ ARM_CORE_ID,
+ coreId);
+
+ OSAL_PostDfc(
+ pParamsFifoESFDesc[EXTENDED_FIELD_BCTHIS_OR_TOP],
+ pEvent[EVENT_ELEM_METHOD_IDX],
+ pParamsAddr,
+ pParamsFifoESFDesc[EXTENDED_FIELD_BCDESC]);
+ }
+
+ // [Pwr] mpc2hostComsFifoId value is checked to support the case where
+ // CM_PostCleanUpAndFlush method is called under interrupt context
+ // -> mpc2hostComsFifoId can be released.
+ if (mpc2mpcComsFifoId[coreId][ARM_CORE_ID] != NULL)
+ fifo_acknowledgeRead(mpc2mpcComsFifoId[coreId][ARM_CORE_ID]);
+ else
+ break;
+ }
+}
+
+PUBLIC EXPORT_SHARED void CM_ProcessMpcEvent(t_nmf_core_id coreId)
+{
+#ifdef __DEBUG
+ dsparmIrqCounter++;
+#endif /* __DEBUG */
+
+ if (coreId != ARM_CORE_ID)
+ {
+ /* Acknowledge DSP communication interrupt */
+ cm_DSP_AcknowledgeDspIrq(coreId, DSP2ARM_IRQ_0);
+
+ cmProcessMPCFifo(coreId);
+ }
+ else
+ {
+ while((coreId = cm_HSEM_GetCoreIdFromIrqSrc()) <= LAST_MPC_ID)
+ cmProcessMPCFifo(coreId);
+ }
+}
+
diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/bind.h b/drivers/staging/nmf-cm/cm/engine/component/inc/bind.h
new file mode 100644
index 00000000000..325703e3367
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/inc/bind.h
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ * \brief Binding Factories internal API.
+ *
+ * \defgroup BF_COMMON Binding factories: Common API
+ * \defgroup BF_PRIMITIVE Binding Factories: Primitive API
+ * \defgroup BF_TRACE Binding Factories: Trace API
+ * \defgroup BF_ASYNCHRONOUS Binding Factories: Asynchronous API
+ * \defgroup BF_DISTRIBUTED Binding Factories: Distributed API
+ */
+#ifndef __INC_CM_BIND_H
+#define __INC_CM_BIND_H
+
+#include <cm/engine/component/inc/introspection.h>
+#include <cm/engine/communication/inc/communication.h>
+#include <cm/engine/utils/inc/table.h>
+
+/**
+ * \internal
+ * \ingroup BF_COMMON
+ *
+ * \brief Identification number of prefedined Binding Factories
+ */
+typedef enum {
+ BF_SYNCHRONOUS, //!< Intra-DSP Synchronous Binding Factory Identifier
+ BF_TRACE, //!< Intra-DSP trace synchronous Binding Factory Identifier
+ BF_ASYNCHRONOUS, //!< Intra-DSP Asynchronous Binding Factory Identifier
+ BF_DSP2HOST, //!< DSP to Host Binding Factory Identifier
+ BF_HOST2DSP, //!< Host to DSP Binding Factory Identifier
+ BF_DSP2DSP, //!< DSP to DSP Binding Factory Identifier
+} t_bf_info_ID;
+
+/*!
+ * \internal
+ * \brief Description of a provided interface
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct _t_interface_reference {
+ const t_component_instance *instance; //!< Component instance that provide this interface
+ t_uint8 provideIndex; //!< Index of the interface in the provide array
+ t_uint8 collectionIndex;//!< Index in the collection if provided interface is a collection
+ t_bf_info_ID bfInfoID; //!< Identification of BF used for creating binding
+ void* bfInfo; //!< Storage of the binding factory info
+} t_interface_reference;
+
+/**
+ * \internal
+ * \ingroup BF_COMMON
+ *
+ * Make some basic sanity check for a client:
+ * - component stopped
+ * - Interface really required
+ *
+ * \param[in] client The client component instance handle.
+ * \param[in] requiredItfClientName The client required interface name
+ * \param[out] requiredItf return the required interface (avoid user searching)
+ */
+t_cm_error cm_checkValidClient(
+ const t_component_instance* client,
+ const char* requiredItfClientName,
+ t_interface_require_description *itfRequire,
+ t_bool *bindable);
+/**
+ * \internal
+ * \ingroup BF_COMMON
+ *
+ * Make some basic sanity check for a server:
+ * - Interface really provided
+ *
+ * \param[in] server The server component instance handle.
+ * \param[in] providedItfServerName The server provided interface name
+ * \param[out] itf return the provided interface (avoid user searching)
+ */
+t_cm_error cm_checkValidServer(
+ const t_component_instance* server,
+ const char* providedItfServerName,
+ t_interface_provide_description *itfProvide);
+
+/**
+ * \internal
+ * \ingroup BF_COMMON
+ *
+ * Make some basic sanity check for a binding:
+ * - Sanity check for a server
+ * - Sanity check for a client (and potentially wait initialisation)
+ * - Provided and required interface matches
+ *
+ * \param[in] client The client component instance handle
+ * \param[in] requiredItfClientName The client required interface name
+ * \param[in] server The server component instance handle
+ * \param[in] providedItfServerName The server provided interface name
+ * \param[out] requiredItf return the required interface (avoid user searching)
+ * \param[out] itf return the provided interface (avoid user searching)
+ */
+t_cm_error cm_checkValidBinding(
+ const t_component_instance* client,
+ const char* requiredItfClientName,
+ const t_component_instance* server,
+ const char* requiredItfServerName,
+ t_interface_require_description *itfRequire,
+ t_interface_provide_description *itfProvide,
+ t_bool *bindable);
+
+/**
+ * \internal
+ * \ingroup BF_COMMON
+ *
+ * Make some basic sanity check for each unbinding:
+ * - Interface really required
+ * - Component stopped
+ *
+ * \param[in] client The client component instance handle
+ * \param[in] requiredItfClientName The client required interface name
+ * \param[out] itfRequire return the previously binded required interface (avoid user searching)
+ * \param[out] itfProvide return the previously binded provided interface (avoid user searching)
+ * \param[out] bfInfoID return the binding factory identifiant which done the previously bind
+ * \param[out] bfInfo return the binding factory information which done the previously bind
+ */
+t_cm_error cm_checkValidUnbinding(
+ const t_component_instance* client,
+ const char* requiredItfClientName,
+ t_interface_require_description *itfRequire,
+ t_interface_provide_description *itfProvide);
+
+/**
+ * \internal
+ * \ingroup BF_PRIMITIVE
+ *
+ * Create a primitive binding between a client to a server interface.
+ *
+ * \param[in] itfRequire The client required interface description
+ * \param[in] itfProvide The server provided interface description
+ */
+t_cm_error cm_bindInterface(
+ const t_interface_require_description *itfRequire,
+ const t_interface_provide_description *itfProvide);
+
+/**
+ * \internal
+ * \ingroup BF_PRIMITIVE
+ *
+ * Unbind a previously binded client.
+ *
+ * \param[in] itfRequire The client required interafce description
+ */
+void cm_unbindInterface(
+ const t_interface_require_description *itfRequire);
+
+/**
+ * \internal
+ * \ingroup BF_PRIMITIVE
+ *
+ * Get a server interface previouly binded to a client
+ *
+ * \param[in] client The client component instance handle
+ * \param[in] requiredItfClientName The client required interface name
+ * \param[out] itf The server interface
+ */
+t_cm_error cm_lookupInterface(
+ const t_interface_require_description *itfRequire,
+ t_interface_provide_description *itfProvide);
+
+/**
+ * \internal
+ * \ingroup BF_PRIMITIVE
+ *
+ * Create a void binding.
+ *
+ * \param[in] client The client component instance handle
+ * \param[in] requiredItfClientName The client required interface name
+ */
+t_cm_error cm_bindInterfaceToVoid(
+ const t_interface_require_description *itfRequire);
+
+/**
+ * \internal
+ * \ingroup BF_TRACE
+ *
+ * Trace synchronous binding factory Information
+ */
+typedef struct {
+ t_component_instance *traceInstance; //!< Trace binding component instance
+} t_trace_bf_info;
+
+/**
+ * \internal
+ * \ingroup BF_TRACE
+ *
+ * Create a traced binding between a client to a server interface.
+ *
+ * \param[in] itfRequire The client required interface description
+ * \param[in] itfProvide The server provided interface description
+ */
+t_cm_error cm_bindInterfaceTrace(
+ const t_interface_require_description *itfRequire,
+ const t_interface_provide_description *itfProvide,
+ t_elfdescription *elfhandleTrace);
+
+/**
+ * \internal
+ * \ingroup BF_TRACE
+ *
+ * Unbind a previously binded client.
+ *
+ * \param[in] itfRequire The client required interafce description
+ */
+void cm_unbindInterfaceTrace(
+ const t_interface_require_description *itfRequire,
+ t_trace_bf_info *bfInfo);
+
+
+/**
+ * \internal
+ * \ingroup BF_ASYNCHRONOUS
+ *
+ * Asynchronous binding factory Information
+ */
+typedef struct {
+ t_component_instance *eventInstance; //!< Event binding component instance
+ t_memory_handle dspfifoHandle; //!< Memory handle of allocated event fifo (pass to the event binding component)
+} t_async_bf_info;
+
+/**
+ * \internal
+ * \ingroup BF_ASYNCHRONOUS
+ *
+ * Create a asynchronous binding between a client to a server interface.
+ * \param[in] client The client component instance handle
+ * \param[in] requiredItfClientName The client required interface name
+ * \param[in] itf The server interface
+ * \param[in] fifosize Number of waited event in the fifo
+ */
+t_cm_error cm_bindInterfaceAsynchronous(
+ const t_interface_require_description *itfRequire,
+ const t_interface_provide_description *itfProvide,
+ t_uint32 fifosize,
+ t_dsp_memory_type_id dspEventMemType,
+ t_elfdescription *elfhandleEvent);
+/**
+ * \internal
+ * \ingroup BF_ASYNCHRONOUS
+ *
+ * Destroy a asynchronous binding between a client to a server interface.
+ * \param[in] itfRequire the required interface
+ */
+void cm_unbindInterfaceAsynchronous(
+ const t_interface_require_description *itfRequire,
+ t_async_bf_info *bfInfo);
+
+/**
+ * \internal
+ * \ingroup BF_DISTRIBUTED
+ *
+ * Stub information in distributed binding factory (client side)
+ */
+typedef struct {
+ t_component_instance *stubInstance; //!< Stub
+} t_dspstub_bf_info;
+
+/**
+ * \internal
+ * \ingroup BF_DISTRIBUTED
+ *
+ * Skeleton information in distributed binding factory (server side)
+ */
+typedef struct {
+ t_component_instance *skelInstance; //!< Skeleton binding component instance
+ t_memory_handle dspfifoHandle; //!< Memory handle of allocated event fifo (pass to the event binding component)
+} t_dspskel_bf_info;
+
+/**
+ * \internal
+ * \ingroup BF_DISTRIBUTED
+ *
+ * Host to DSP distributed binding factory Information
+ */
+typedef struct {
+ t_dspskel_bf_info dspskeleton; //!< Information about the DSP skeleton (server side)
+ t_nmf_fifo_arm_desc* fifo; //!< Handle of the fifo params
+ t_nmf_client_id clientId; //!< Client ID of the host client
+} t_host2mpc_bf_info;
+
+/*
+ * Table of instantiated of host2mpc bindings
+ */
+extern t_nmf_table Host2MpcBindingTable; /**< list (table) of host2mpc bindings */
+
+/**
+ * \internal
+ * \ingroup BF_DISTRIBUTED
+ *
+ * Create a Host to DSP distributed binding between a host client interface to a server interface.
+ * (Not manage in the same way as distributed binding since the Host programming model is not component aware).
+ * \param[in] itfServer The server interface
+ * \param[in] fifosize Number of waited event in the fifo
+ * \param[in] dspEventMemType The type of memory to use
+ * \param[in] bfInfo info structure
+ */
+t_cm_error cm_bindComponentFromCMCore(
+ const t_interface_provide_description *itfProvide,
+ t_uint32 fifosize,
+ t_dsp_memory_type_id dspEventMemType,
+ t_elfdescription *elfhandleSkeleton,
+ t_host2mpc_bf_info **bfInfo);
+
+/**
+ * \internal
+ * \ingroup BF_DISTRIBUTED
+ *
+ * Destroy a Host to DSP distributed binding between a host client interface to a server interface.
+ * \param[in] bfInfo The Host to DSP distributed binding factory information
+ */
+void cm_unbindComponentFromCMCore(
+ t_host2mpc_bf_info *bfInfo);
+
+/**
+ * \internal
+ * \ingroup BF_DISTRIBUTED
+ *
+ * DSP to Host distributed binding factory Information
+ */
+typedef struct {
+ t_dspstub_bf_info dspstub; //!< Information about the DSP stub (client side)
+ t_nmf_fifo_arm_desc* fifo; //!< Handle of the fifo params
+ t_uint32 context;
+} t_mpc2host_bf_info;
+
+/**
+ * \internal
+ * \ingroup BF_DISTRIBUTED
+ *
+ * Create a DSP to Host distributed binding between a client interface to a host server interface.
+ * (Not manage in the same way as distributed binding since the Host programming model is not component aware).
+ * \param[in] client The client component instance handle
+ * \param[in] requiredItfClientName The client required interface name
+ * \param[in] itfref The host server interface to be called
+ * \param[in] fifosize Number of waited event in the fifo
+ */
+t_cm_error cm_bindComponentToCMCore(
+ const t_interface_require_description *itfRequire,
+ t_uint32 fifosize,
+ t_uint32 context,
+ t_elfdescription *elfhandleStub,
+ t_mpc2host_bf_info ** bfInfo);
+
+/**
+ * \internal
+ * \ingroup BF_DISTRIBUTED
+ *
+ * Destroy a DSP to Host distributed binding between a client interface to a server interface.
+ * \param[in] itfRequire The required interface
+ * \param[out] upLayerThis The 'THIS' context of upper layer
+ */
+void cm_unbindComponentToCMCore(
+ const t_interface_require_description *itfRequire,
+ t_mpc2host_bf_info *bfInfo);
+
+/**
+ * \internal
+ * \ingroup BF_DISTRIBUTED
+ *
+ * Asynchronous distributed binding factory Information
+ */
+typedef struct {
+ t_nmf_fifo_arm_desc* fifo; //!< Handle of the fifo params
+ t_dspstub_bf_info dspstub; //!< Information about the DSP stub (client side)
+ t_dspskel_bf_info dspskeleton; //!< Information about the DSP skeleton (server side)
+} t_mpc2mpc_bf_info;
+
+/**
+ * \internal
+ * \ingroup BF_DISTRIBUTED
+ *
+ * Create a asynchronous distributed binding between a client interface to a server interface.
+ * \param[in] client The client component instance handle
+ * \param[in] requiredItfClientName The client required interface name
+ * \param[in] itf The server interface
+ * \param[in] fifosize Number of waited event in the fifo
+ */
+t_cm_error cm_bindInterfaceDistributed(
+ const t_interface_require_description *itfRequire,
+ const t_interface_provide_description *itfProvide,
+ t_uint32 fifosize,
+ t_dsp_memory_type_id dspEventMemType,
+ t_elfdescription *elfhandleSkeleton,
+ t_elfdescription *elfhandleStub);
+
+/**
+ * \internal
+ * \ingroup BF_DISTRIBUTED
+ *
+ * Destroy a asynchronous distributed binding between a client interface to a server interface.
+ * \param[in] itfRequire The required interface
+ */
+void cm_unbindInterfaceDistributed(
+ const t_interface_require_description *itfRequire,
+ t_mpc2mpc_bf_info *bfInfo);
+
+/**
+ * \internal
+ *
+ * Bind a static interrupt to server provide interface name.
+ * \param[in] coreId The core to which component is loaded
+ * \param[in] interruptLine Interrupt line number to use
+ * \param[in] server Server instance that provide interrupt service
+ * \param[in] providedItfServerName Interface name hat provide interrupt service
+ */
+t_cm_error cm_bindInterfaceStaticInterrupt(
+ const t_nmf_core_id coreId,
+ const int interruptLine,
+ const t_component_instance *server,
+ const char* providedItfServerName);
+
+/**
+ * \internal
+ *
+ * Unbind a static interrupt.
+ * \param[in] coreId The core to which component is loaded
+ * \param[in] interruptLine Interrupt line number to use
+ */
+t_cm_error cm_unbindInterfaceStaticInterrupt(
+ const t_nmf_core_id coreId,
+ const int interruptLine);
+
+void cm_destroyRequireInterface(t_component_instance* component, t_nmf_client_id clientId);
+void cm_registerSingletonBinding(
+ t_component_instance* component,
+ t_interface_require_description* itfRequire,
+ t_interface_provide_description* itfProvide,
+ t_nmf_client_id clientId);
+t_bool cm_unregisterSingletonBinding(
+ t_component_instance* component,
+ t_interface_require_description* itfRequire,
+ t_interface_provide_description* itfProvide,
+ t_nmf_client_id clientId);
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/component_type.h b/drivers/staging/nmf-cm/cm/engine/component/inc/component_type.h
new file mode 100644
index 00000000000..35571dde06d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/inc/component_type.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Components Component Manager API type.
+ *
+ * \defgroup COMPONENT CM Components API
+ * \ingroup CM_USER_API
+ */
+
+#ifndef COMPONENT_TYPE_H_
+#define COMPONENT_TYPE_H_
+
+#include <cm/inc/cm_type.h>
+#include <nmf/inc/component_type.h>
+
+/*!
+ * @defgroup t_nmf_ee_priority t_nmf_ee_priority
+ * \brief Identification of the execution engine priority and sub priority.
+ * @{
+ * \ingroup COMPONENT
+ */
+typedef t_uint32 t_nmf_ee_priority; //!< Fake enumeration type
+
+#define NMF_SCHED_BACKGROUND ((t_nmf_ee_priority)0) //!< Background priority
+#define NMF_SCHED_NORMAL ((t_nmf_ee_priority)1) //!< Normal priority
+#define NMF_SCHED_URGENT ((t_nmf_ee_priority)2) //!< Urgent priority
+/* @} */
+
+
+/*!
+ * \brief Identification of host component returned during introspection
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+#define NMF_HOST_COMPONENT ((t_cm_instance_handle)0xFFFFFFFF)
+
+/*!
+ * \brief Identification of void component returned during introspection
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+#define NMF_VOID_COMPONENT ((t_cm_instance_handle)0xFFFFFFFE)
+
+
+/*!
+ * @defgroup t_nmf_ee_priority t_nmf_ee_priority
+ * \brief Identification of the execution engine priority and sub priority.
+ * @{
+ * \ingroup COMPONENT
+ */
+typedef t_uint8 t_cm_require_state; //!< Fake enumeration type
+
+#define CM_REQUIRE_STATIC ((t_cm_require_state)0) //!< Required interface is static
+#define CM_REQUIRE_OPTIONAL ((t_cm_require_state)1) //!< Required interface is optional
+#define CM_REQUIRE_COLLECTION ((t_cm_require_state)2) //!< Required interface is a collection
+
+/* @} */
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/description.h b/drivers/staging/nmf-cm/cm/engine/component/inc/description.h
new file mode 100644
index 00000000000..b7d3c34654d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/inc/description.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#ifndef __INC_CM_COMPONENT_DESCRIPTION_H
+#define __INC_CM_COMPONENT_DESCRIPTION_H
+
+#include <cm/engine/elf/inc/memory.h>
+#include <cm/engine/utils/inc/string.h>
+
+#include <inc/nmf-limits.h>
+
+/*!
+ * \internal
+ * \brief Description of an interface
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct _t_interface_description {
+ t_dup_char type; //!< Type of the interface
+ t_uint16 referenceCounter; //!< Number of template referencing the interface
+ t_uint8 methodNumber; //!< Number of method in the interfaces
+ struct _t_interface_description* next;
+ t_dup_char methodNames[]; //!< Array of method names
+} t_interface_description;
+
+/*!
+ * \internal
+ * \brief Description of a variable memory on a collection index
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct {
+ t_uint32 offset; //!< Offset in the memory
+ const t_elfmemory *memory; //!< Memory
+} t_memory_reference;
+
+/*!
+ * \internal
+ * \brief Description of a required interface on a collection index
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct {
+ t_uint32 numberOfClient; //!< Number of interface descriptor really connected to this interface
+ t_memory_reference *memories; /*!< Memory where each interface reference descriptor resides
+ \note memories[numberOfClient] */
+} t_interface_require_index;
+
+/*!
+ * \internal
+ * \brief Description of a required interface
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct {
+ t_dup_char name; //!< Name of the interface
+ t_interface_description *interface; //!< Description of the interface
+ t_uint8 requireTypes; //!< Mask of t_elf_interface_require_type
+ t_uint8 collectionSize; //!< Size of the collection (1 if not a collection)
+ t_interface_require_index *indexes; /*!< Require information for each collection index
+ \note indexes[collectionSize] */
+} t_interface_require;
+
+/*!
+ * \internal
+ * \brief Description of a provided interface method on a collection index
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct {
+ t_uint32 methodAddresses; //!< Address of each method ; Available only when template loaded
+ t_memory_reference memory; //!< Memory of the method
+} t_interface_provide_index;
+
+/*!
+ * \internal
+ * \brief Description of a provided interface
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct {
+ t_dup_char name; //!< Name of the interface
+ t_interface_description *interface; //!< Description of the interface
+ t_uint8 provideTypes; //!< Mask of t_elf_interface_provide_type
+ t_uint8 interruptLine; //!< Interrupt line if interrupt (0 if not)
+ t_uint8 collectionSize; //!< Size of the collection (1 if not a collection)
+ t_interface_provide_index **indexes; //!< Provide information for each collection index
+} t_interface_provide;
+
+/*!
+ * \internal
+ * \brief Description of a attribute
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct {
+ t_dup_char name; //!< Name of the attribute
+ t_memory_reference memory; //!< Memory where the attribute reside
+} t_attribute;
+
+/*!
+ * \internal
+ * \brief Description of a property
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct {
+ t_dup_char name; //!< Name of this attribute
+ t_dup_char value; //!< String of the value
+} t_property;
+
+
+
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/dspevent.h b/drivers/staging/nmf-cm/cm/engine/component/inc/dspevent.h
new file mode 100644
index 00000000000..bb47363c0ae
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/inc/dspevent.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef __INC_DSP_EVENT
+#define __INC_DSP_EVENT
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/component/inc/instance.h>
+#include <cm/engine/memory/inc/memory.h>
+
+/* value should be size of t_remote_event in mmdsp word */
+#define DSP_REMOTE_EVENT_SIZE_IN_DSPWORD 5
+
+t_cm_error dspevent_createDspEventFifo(
+ const t_component_instance *pComp,
+ const char* nameOfTOP,
+ t_uint32 fifoNbElem,
+ t_uint32 fifoElemSizeInWord,
+ t_dsp_memory_type_id dspEventMemType,
+ t_memory_handle *pHandle);
+void dspevent_destroyDspEventFifo(t_memory_handle handle);
+
+#endif /* __INC_DSP_EVENT */
diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/initializer.h b/drivers/staging/nmf-cm/cm/engine/component/inc/initializer.h
new file mode 100644
index 00000000000..b4416f3ce20
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/inc/initializer.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef __INC_NMF_INITIALIZER
+#define __INC_NMF_INITIALIZER
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/component/inc/instance.h>
+#include <share/communication/inc/initializer.h>
+
+PUBLIC t_cm_error cm_COMP_INIT_Init(t_nmf_core_id coreId);
+PUBLIC t_cm_error cm_COMP_CallService(int serviceIndex, t_component_instance *pComp, t_uint32 methodAddress);
+PUBLIC void cm_COMP_INIT_Close(t_nmf_core_id coreId);
+PUBLIC t_cm_error cm_COMP_UpdateStack(t_nmf_core_id coreId, t_uint32 stackSize);
+PUBLIC t_cm_error cm_COMP_ULPForceWakeup(t_nmf_core_id coreId);
+PUBLIC t_cm_error cm_COMP_ULPAllowSleep(t_nmf_core_id coreId);
+PUBLIC t_cm_error cm_COMP_InstructionCacheLock(t_nmf_core_id coreId, t_uint32 mmdspAddr, t_uint32 mmdspSize);
+PUBLIC t_cm_error cm_COMP_InstructionCacheUnlock(t_nmf_core_id coreId, t_uint32 mmdspAddr, t_uint32 mmdspSize);
+
+
+PUBLIC void processAsyncAcknowledge(t_nmf_core_id coreId, t_event_params_handle pParam);
+PUBLIC void processSyncAcknowledge(t_nmf_core_id coreId, t_event_params_handle pParam);
+
+#endif /* __INC_NMF_INITIALIZER */
diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/instance.h b/drivers/staging/nmf-cm/cm/engine/component/inc/instance.h
new file mode 100644
index 00000000000..1bef76501de
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/inc/instance.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Components Management internal methods - Instance API.
+ *
+ */
+#ifndef __INC_CM_INSTANCE_H
+#define __INC_CM_INSTANCE_H
+
+#include <cm/engine/component/inc/template.h>
+#include <cm/engine/repository_mgt/inc/repository_mgt.h>
+#include <cm/engine/memory/inc/domain.h>
+#include <cm/engine/utils/inc/table.h>
+#include <cm/engine/utils/inc/string.h>
+
+/*----------------------------------------------------------------------------
+ * Component Instance API.
+ *----------------------------------------------------------------------------*/
+struct _t_interface_reference;
+
+/*!
+ * \internal
+ * \brief Component life cycle state
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef enum {
+ STATE_NONE,
+ STATE_STOPPED,
+ STATE_RUNNABLE,
+ // STATE_DESTROYED identified when component remove from component list
+} t_component_state;
+
+struct t_client_of_singleton
+{
+ struct t_client_of_singleton *next;
+ t_nmf_client_id clientId;
+ t_uint16 numberOfInstance;
+ t_uint16 numberOfStart;
+ t_uint16 numberOfBind;
+};
+
+/*!
+ * \internal
+ * \brief Description of a component instance
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct t_component_instance {
+ t_dup_char pathname; //!< Path Name of this component in the components architecture
+
+ t_component_state state; //!< Component state
+ t_nmf_ee_priority priority; //!< Executive engine component priority
+ t_component_template *template; //!< Component template
+
+ t_uint32 thisAddress; //!< Cached value of cm_DSP_GetDspAddress(component->memories[data], &thisAddress);
+
+ t_memory_handle memories[NUMBER_OF_MMDSP_MEMORY]; //!<Reference in different memory where datas are (YES, we fix implementation to MMDSP)
+
+ struct _t_interface_reference **interfaceReferences; /*!< Interface references
+ (Share same index as template->u.p.requires)
+ type == targets[interface_index][collection_index] */
+
+ t_uint16 providedItfUsedCount; //!< Use count to reference the number of components binded to this once, ie count the number of provided interfaces in use
+ t_cm_instance_handle instance; //!< index of this component within the ComponentTable
+ t_cm_domain_id domainId; //!< Domain where the component has been installed
+
+ struct t_client_of_singleton *clientOfSingleton; //!< Client of singleton list
+} t_component_instance;
+
+t_component_template* cm_lookupTemplate(t_nmf_core_id dspId, t_dup_char str);
+
+/*!
+ * \internal
+ * \brief Load a component template.
+ *
+ * ...
+ *
+ * \param[in] templateName name of the template to load
+ * \param[in] coreId DSP where template must be loaded
+ * \praem[in] pRepComponent Pointer to the component entry stored in the Component Cache Repository
+ * \param[in, out] template reference to put the loaded template (null if first instance)
+ *
+ * \exception CM_COMPONENT_NOT_FOUND
+ * \exception CM_NO_MORE_MEMORY
+ *
+ * \return exception number.
+ *
+ * \warning For Component manager use only.
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_cm_error cm_loadComponent(
+ t_dup_char templateName,
+ t_cm_domain_id domainId,
+ t_elfdescription* elfhandle,
+ t_component_template **reftemplate);
+
+/*!
+ * \internal
+ * \brief Unload a component template.
+ *
+ * ...
+ *
+ * \param[in] template template to be unloaded
+ * \praem[in] Private memories that has been created from component binary file
+ *
+ * \return exception number.
+ *
+ * \warning For Component manager use only.
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_cm_error cm_unloadComponent(
+ t_component_template *template);
+
+/*!
+ * \internal
+ * \brief Instantiate a component.
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_cm_error cm_instantiateComponent(const char* templateName,
+ t_cm_domain_id domainId,
+ t_nmf_ee_priority priority,
+ const char* pathName,
+ t_elfdescription *elfhandle,
+ t_component_instance** refcomponent);
+
+struct t_client_of_singleton* cm_getClientOfSingleton(t_component_instance* component, t_bool createdIfNotExist, t_nmf_client_id clientId);
+
+/*!
+ * \internal
+ * \brief Start a component.
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_cm_error cm_startComponent(t_component_instance* component, t_nmf_client_id clientId);
+
+/*!
+ * \internal
+ * \brief Stop a component.
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_cm_error cm_stopComponent(t_component_instance* component, t_nmf_client_id clientId);
+
+/*!
+ * \internal
+ */
+typedef enum {
+ DESTROY_NORMAL,
+ DESTROY_WITHOUT_CHECK,
+ DESTROY_WITHOUT_CHECK_CALL
+} t_destroy_state;
+
+/*!
+ * \internal
+ * \brief Destroy a component instance.
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_cm_error cm_destroyInstance(t_component_instance* component, t_destroy_state forceDestroy);
+
+/*!
+ * \internal
+ * \brief Destroy a component instance.
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_cm_error cm_destroyInstanceForClient(t_component_instance* component, t_destroy_state forceDestroy, t_nmf_client_id clientId);
+
+/*!
+ * \internal
+ * \brief
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+void cm_delayedDestroyComponent(t_component_instance *component);
+
+/*!
+ * \internal
+ * \brief
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_component_instance *cm_lookupComponent(const t_cm_instance_handle hdl);
+
+/*!
+ * \internal
+ * \brief
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_bool cm_isComponentOnCoreId(t_nmf_core_id coreId);
+
+/*!
+ * \internal
+ * \brief
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_cm_error cm_COMP_Init(void);
+
+/*!
+ * \internal
+ * \brief
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+void cm_COMP_Destroy(void);
+
+/*
+ * Table of instantiated components.
+ */
+extern t_nmf_table ComponentTable; /**< list (table) of components */
+#define componentEntry(i) ((t_component_instance *)ComponentTable.entries[i])
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/introspection.h b/drivers/staging/nmf-cm/cm/engine/component/inc/introspection.h
new file mode 100644
index 00000000000..cfb55c91779
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/inc/introspection.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Components Management internal methods - Introspection.
+ *
+ */
+#ifndef __INC_CM_INTROSPECTION_H
+#define __INC_CM_INTROSPECTION_H
+
+#include <cm/engine/component/inc/instance.h>
+
+/*!
+ * \internal
+ * \brief Description of a required interface reference
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct {
+ const t_component_instance *client; //!< Component that provide this interface
+ t_uint8 requireIndex; //!< Index of the interface in the require array
+ t_uint8 collectionIndex; //!< Index in the collection if required interface is a collection
+ const char* origName; //!< Name of the component interface
+} t_interface_require_description;
+
+/*!
+ * \internal
+ * \brief Description of a provided interface
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct {
+ const t_component_instance *server; //!< Component that provide this interface
+ t_uint8 provideIndex; //!< Index of the interface in the provide array
+ t_uint8 collectionIndex; //!< Index in the collection if provided interface is a collection
+ const char* origName; //!< Name of the component interface
+} t_interface_provide_description;
+
+
+/*!
+ * \internal
+ * \brief Get property of a component.
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_cm_error cm_getComponentProperty(
+ const t_component_instance *component,
+ const char *propName,
+ char value[MAX_PROPERTY_VALUE_LENGTH],
+ t_uint32 valueLength);
+
+
+t_dsp_address cm_getAttributeMpcAddress(
+ const t_component_instance *component,
+ const char *attrName);
+
+t_cm_logical_address cm_getAttributeHostAddr(
+ const t_component_instance *component,
+ const char *attrName);
+
+t_uint32 cm_readAttributeNoError(
+ const t_component_instance *component,
+ const char *attrName);
+
+t_cm_error cm_readAttribute(
+ const t_component_instance *component,
+ const char *attrName,
+ t_uint32 *value);
+
+t_cm_error cm_writeAttribute(
+ const t_component_instance *component,
+ const char *attrName,
+ t_uint32 value);
+
+/*!
+ * \internal
+ * \brief Get internal component symbol
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_dsp_address cm_getFunction(
+ const t_component_instance* component,
+ const char* interfaceName,
+ const char* methodName);
+
+/*!
+ * \internal
+ * \brief Get interface provided by a component instance.
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_cm_error cm_getProvidedInterface(const t_component_instance* server,
+ const char* itfName,
+ t_interface_provide_description *itfProvide);
+
+/*!
+ * \internal
+ * \brief Get interface required by a component instance.
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+t_cm_error cm_getRequiredInterface(const t_component_instance* server,
+ const char* itfName,
+ t_interface_require_description *itfRequire);
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/nmfheaderabi.h b/drivers/staging/nmf-cm/cm/engine/component/inc/nmfheaderabi.h
new file mode 100644
index 00000000000..59d2186f157
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/inc/nmfheaderabi.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief NMF component description ABI
+ *
+ * \defgroup NMF_HEADER NMF Component Description ABI
+ * The NMF component description ABI is stored in the nmf_segment in the ELF component file.
+ * The NMF component description section start by the t_elf_component_header structure.
+ *
+ * \warning <B>The format of this section is not fixed and is able to be changed without concerting.</B>
+ * \note You can use the nmfHeaderVersion to check if the format has changed.
+ * \note Each pointers in this section is relative to the beginning of the section and must be relocated before used.
+ * \ingroup NMF_ABI
+ */
+#ifndef __INC_CM_NMF_HEADERABI_H
+#define __INC_CM_NMF_HEADERABI_H
+
+#include <cm/inc/cm_type.h>
+
+/*!
+ * \brief Description of a interface
+ * \ingroup NMF_HEADER
+ */
+typedef struct {
+ char *type; //!< Type of this Interface
+ t_uint8 methodNumber; //!< Number of method in the interfaces
+ t_uint8 reserved1, reserved2, reserved3;
+ char *methodNames[]; //!< Array of method names
+} t_elf_interface_description;
+
+/*!
+ * \brief Description of required interface type (value could be combinated)
+ * \ingroup NMF_HEADER
+ */
+typedef enum {
+ COLLECTION_REQUIRE = 1, //!< Required interface is a collection
+ OPTIONAL_REQUIRE = 2, //!< Required interface if optional
+ STATIC_REQUIRE = 4, //!< Required interface is static
+ VIRTUAL_REQUIRE = 8, //!< Required interface is virtual (only for introspection purpose)
+ INTRINSEC_REQUIRE = 16 //!< Required interface is intrinsec (bind automatically done by runtime)
+} t_elf_interface_require_type;
+
+/*!
+ * \brief Description of a required interface on a collection index
+ * \ingroup NMF_HEADER
+ */
+typedef struct {
+ t_uint32 numberOfClient; //!< Number of interface descriptor really connected to this interface
+ t_uint32 symbols[1]; /*!< Symbol of the real name of the attribute
+ \note Real type symbols[numberOfClient]
+ \note Use relocation in order to get symbol information */
+} t_elf_interface_require_index;
+
+/*!
+ * \brief Description of an interface required
+ * \ingroup NMF_HEADER
+ */
+typedef struct {
+ char *name; //!< name of the interface: offset in string segment
+ t_uint8 requireTypes; //!< Mask of t_elf_interface_require_type
+ t_uint8 collectionSize; //!< Size of the collection (1 if not a collection)
+ t_uint8 reserved1, reserved2;
+ t_elf_interface_description *interface; //!< Interface description
+ t_elf_interface_require_index indexes[]; /*!< Require information for each collection index
+ \note Real type: indexes[collectionSize],
+ available only if not static interface */
+} t_elf_required_interface;
+
+/*!
+ * \brief Description of provided interface type (value could be combinated)
+ * \ingroup NMF_HEADER
+ */
+typedef enum {
+ COLLECTION_PROVIDE = 1, //!< Provided interface is a collection
+ VIRTUAL_PROVIDE = 2 //!< Provided interface is virtual (only for introspection purpose)
+} t_elf_interface_provide_type;
+
+/*!
+ * \brief Description of an interface provided
+ * \ingroup NMF_HEADER
+ */
+typedef struct {
+ char* name; //!< name of the interface: offset in string segment
+ t_uint8 provideTypes; //!< Mask of t_elf_interface_provide_type
+ t_uint8 interruptLine; //!< Interrupt line if interrupt (0 if not)
+ t_uint8 collectionSize; //!< Size of the collection (1 if not a collection)
+ t_uint8 reserved1;
+ t_elf_interface_description *interface; //!< Interface description
+ t_uint32 methodSymbols[]; /*!< Symbol of the real name of methods of the interface for each collection index
+ \note Real type: methodSymbols[collectionSize][methodNumber]
+ \note Use relocation in order to get symbol information*/
+} t_elf_provided_interface;
+
+/*!
+ * \brief Description of an attribute
+ * \ingroup NMF_HEADER
+ */
+typedef struct {
+ char* name; //!< Name of this attribute
+ t_uint32 symbols; /*!< Symbol of the real name of the attribute
+ \note Use relocation in order to get symbol information */
+} t_elf_attribute;
+
+/*!
+ * \brief Description of an property
+ * \ingroup NMF_HEADER
+ */
+typedef struct {
+ char* name; //!< Name of this attribute
+ char* value; //!< String of the value
+} t_elf_property;
+
+#define MAGIC_COMPONENT 0x123 //!< Magic Number for a component \ingroup NMF_HEADER
+#define MAGIC_SINGLETON 0x321 //!< Magic Number for a singleton component \ingroup NMF_HEADER
+#define MAGIC_FIRMWARE 0x456 //!< Magic Number for Execution Engine Component \ingroup NMF_HEADER
+
+/*!
+ * \brief Description of a ELF component header
+ *
+ * The NMF component description section start by this structure.
+ *
+ * \ingroup NMF_HEADER
+ */
+typedef struct {
+ t_uint32 magic; //!< Magic Number
+ t_uint32 nmfVersion; //!< Version of the NMF Header
+
+ char* templateName; //!< Name of the component template
+
+ t_uint32 LCCConstruct; //!< Life cycle Constructor offset
+ t_uint32 LCCStart; //!< Life cycle Starter offset
+ t_uint32 LCCStop; //!< Life cycle Stopper offset
+ t_uint32 LCCDestroy; //!< Life cycle Destructer offset
+
+ t_uint32 minStackSize; //!< Minimum stack size
+
+ t_uint32 attributeNumber;//!< Number of attributes
+ t_elf_attribute *attributes; //!< Array of attributes (be careful, this reference must be relocated before use)
+
+ t_uint32 propertyNumber; //!< Number of properties
+ t_elf_property *properties; //!< Array of properties (be careful, this reference must be relocated before use)
+
+ t_uint32 provideNumber; //!< Number of interfaces provided
+ t_elf_provided_interface *provides; //!< Array of interfaces provided (be careful, this reference must be relocated before use)
+
+ t_uint32 requireNumber; //!< Array of interfaces required
+ t_elf_required_interface *requires; //!< Array of interfaces required (be careful, this reference must be relocated before use)
+
+} t_elf_component_header;
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/component/inc/template.h b/drivers/staging/nmf-cm/cm/engine/component/inc/template.h
new file mode 100644
index 00000000000..3e1a2c001e4
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/inc/template.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Components Management internal methods - Template API.
+ *
+ * \defgroup COMPONENT_INTERNAL Private component instances API
+ */
+#ifndef __INC_CM_TEMPLATE_H
+#define __INC_CM_TEMPLATE_H
+
+#include <cm/engine/dsp/inc/dsp.h>
+#include <cm/engine/component/inc/description.h>
+#include <cm/engine/elf/inc/elfapi.h>
+#include <cm/engine/utils/inc/string.h>
+
+
+/*!
+ * \internal
+ * \brief Class of a component
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef enum {
+ COMPONENT, //!< Primitive component
+ SINGLETON, //!< Singleton component
+ FIRMWARE, //!< Firmware composite component
+} t_component_classe;
+
+/*!
+ * \internal
+ * \brief Description of delayed relocation
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct _t_function_relocation {
+ t_dup_char symbol_name;
+ t_uint32 type;
+ char *reloc_addr;
+ struct _t_function_relocation *next;
+} t_function_relocation;
+
+struct t_component_instance;
+
+/*!
+ * \internal
+ * \brief Description of a component template
+ * \ingroup COMPONENT_INTERNAL
+ */
+typedef struct _t_component_template {
+ t_dup_char name; //!< Template name (a.k.a component type)
+
+ t_component_classe classe; //!< Class of the component
+ //TODO, juraj, remove dspId
+ t_nmf_core_id dspId; //!< Reference on DSP where template is loaded
+
+ t_uint8 numberOfInstance; //!< Number of same instance (or singleton copy) create from this template
+
+ t_uint8 propertyNumber; //!< Number of properties in this template
+ t_uint8 attributeNumber; //!< Number of attributes in this template
+ t_uint8 provideNumber; //!< Number of interface provided by this template
+ t_uint8 requireNumber; //!< Number of interface required by this template
+
+ t_uint32 LCCConstructAddress; //!< Life cycle Constructor address
+ t_uint32 LCCStartAddress; //!< Life cycle Starter address
+ t_uint32 LCCStopAddress; //!< Life cycle Stopper address
+ t_uint32 LCCDestroyAddress; //!< Life cycle Destructer address
+
+ t_uint32 minStackSize; //!< Minimum stack size
+
+ t_memory_handle memories[NUMBER_OF_MMDSP_MEMORY]; //!< Reference in different memory where datas are (YES, we fix implementation to MMDSP)
+ const t_elfmemory *thisMemory; //!< Memory used to determine this
+ const t_elfmemory *codeMemory; //!< Memory used to determine code
+
+ t_function_relocation *delayedRelocation; //!< List of reference that can't been relocatable while appropritae binding done.
+
+ t_property *properties; //!< Array of properties in this template
+ t_attribute *attributes; //!< Array of attributes in this template
+ t_interface_provide *provides; //!< Array of interface provided by this template
+ t_interface_require *requires; //!< Array of interface required by this template
+
+ t_bool descriptionAssociatedWithTemplate;
+
+ struct _t_component_template *prev, *next;
+ struct t_component_instance *singletonIfAvaliable;
+} t_component_template;
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/binder.c b/drivers/staging/nmf-cm/cm/engine/component/src/binder.c
new file mode 100644
index 00000000000..ab6c4c65d2b
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/src/binder.c
@@ -0,0 +1,1301 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include "../inc/bind.h"
+#include "../inc/dspevent.h"
+#include <cm/engine/communication/fifo/inc/nmf_fifo_arm.h>
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+#include <cm/engine/component/inc/introspection.h>
+
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/trace/inc/xtitrace.h>
+
+#include <cm/engine/utils/inc/string.h>
+
+#define CM_IT_NAME_MAX_LENGTH 8
+
+t_nmf_table Host2MpcBindingTable; /**< list (table) of host2mpc bindings */
+
+static void cm_fillItName(int interruptLine, char *itName);
+static t_uint16 getNumberOfBind(t_component_instance* component);
+
+/*
+ * Bind virtual interface, here we assume that:
+ * - client component require this interface as last one and without collection,
+ * - server component provide only this interface and without collection.
+ * Fixed in loader.c.
+ */
+static void cm_bindVirtualInterface(
+ t_component_instance* client,
+ const t_component_instance* server) {
+ t_interface_require_description itfRequire;
+
+ if(cm_getRequiredInterface(client, "coms", &itfRequire) == CM_OK)
+ {
+ t_interface_reference* itfRef = client->interfaceReferences[itfRequire.requireIndex];
+
+ /*
+ * Memorise this reference
+ */
+ itfRef->provideIndex = 0;
+ itfRef->collectionIndex = 0;
+ itfRef->instance = server;
+ itfRef->bfInfoID = (t_bf_info_ID)0;
+ itfRef->bfInfo = (void*)-1; // TODO
+ }
+ else
+ {
+ ERROR("Internal Error in cm_bindVirtualInterface\n", 0, 0, 0, 0, 0, 0);
+ }
+}
+
+static void cm_unbindVirtualInterface(
+ t_component_instance* client) {
+ t_interface_require_description itfRequire;
+
+ if(cm_getRequiredInterface(client, "coms", &itfRequire) == CM_OK)
+ {
+ t_interface_reference* itfRef = client->interfaceReferences[itfRequire.requireIndex];
+ itfRef->instance = NULL;
+ }
+ else
+ {
+ ERROR("Internal Error in cm_unbindVirtualInterface\n", 0, 0, 0, 0, 0, 0);
+ }
+}
+
+/*
+ * Bind component
+ */
+static void cm_bindLowLevelInterface(
+ const t_interface_require_description *itfRequire,
+ const t_interface_provide_description *itfLocalBC, /* On the same DSP */
+ t_bf_info_ID bfInfoID, void* bfInfo)
+{
+ const t_component_instance* client = itfRequire->client;
+ t_component_instance* server = (t_component_instance*)itfLocalBC->server;
+ t_interface_require *require = &client->template->requires[itfRequire->requireIndex];
+ t_interface_provide* provide = &server->template->provides[itfLocalBC->provideIndex];
+ int k, j;
+
+ if(require->indexes != NULL)
+ {
+ t_interface_require_index *requireindex = &require->indexes[itfRequire->collectionIndex];
+
+ for(k = 0; k < requireindex->numberOfClient; k++) {
+ t_uint32 *hostAddr;
+
+ hostAddr = (t_uint32*)(
+ cm_DSP_GetHostLogicalAddress(client->memories[requireindex->memories[k].memory->id]) +
+ requireindex->memories[k].offset * requireindex->memories[k].memory->memEntSize);
+
+ LOG_INTERNAL(2, "Fill ItfRef %s.%s mem=%s Off=%x @=%x\n",
+ client->pathname, require->name,
+ requireindex->memories[k].memory->memoryName,
+ requireindex->memories[k].offset,
+ hostAddr, 0);
+
+ /*
+ * Fill the interface references. We start by This then methods in order to keep
+ * Unbinded panic as long as possible and not used method with wrong This. This is
+ * relevent only for optional since we must go in stop state before rebinding other
+ * required interface.
+ *
+ * Direct write to DSP memory without go through DSP abstraction since we know we are in 24bits
+ */
+ // Write THIS reference into the Data field of the interface reference
+ // Write the interface methods reference
+
+ if(((t_uint32)hostAddr & 0x7) == 0 && require->interface->methodNumber > 0)
+ {
+ // We are 64word byte aligned, combine this write with first method
+ *(volatile t_uint64*)hostAddr =
+ ((t_uint64)server->thisAddress << 0) |
+ ((t_uint64)provide->indexes[itfLocalBC->collectionIndex][0].methodAddresses << 32);
+ hostAddr += 2;
+ j = 1;
+ }
+ else
+ {
+ // We are not, write this which will align us
+ *hostAddr++ = (t_uint32)server->thisAddress;
+ j = 0;
+ }
+
+ // Word align copy
+ for(; j < require->interface->methodNumber - 1; j+=2) {
+ *(volatile t_uint64*)hostAddr =
+ ((t_uint64)provide->indexes[itfLocalBC->collectionIndex][j].methodAddresses << 0) |
+ ((t_uint64)provide->indexes[itfLocalBC->collectionIndex][j+1].methodAddresses << 32);
+ hostAddr += 2;
+ }
+
+ // Last word align if required
+ if(j < require->interface->methodNumber)
+ *hostAddr = provide->indexes[itfLocalBC->collectionIndex][j].methodAddresses;
+ }
+ }
+ else
+ {
+ t_function_relocation *reloc = client->template->delayedRelocation;
+ while(reloc != NULL) {
+ for(j = 0; j < provide->interface->methodNumber; j++)
+ {
+ if(provide->interface->methodNames[j] == reloc->symbol_name) {
+ cm_ELF_performRelocation(
+ reloc->type,
+ reloc->symbol_name,
+ provide->indexes[itfLocalBC->collectionIndex][j].methodAddresses,
+ reloc->reloc_addr);
+ break;
+ }
+ }
+
+ reloc = reloc -> next;
+ }
+ }
+
+ /*
+ * Memorise this reference
+ */
+ {
+ t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex];
+
+ itfRef->provideIndex = itfLocalBC->provideIndex;
+ itfRef->collectionIndex = itfLocalBC->collectionIndex;
+ itfRef->instance = itfLocalBC->server;
+ itfRef->bfInfoID = bfInfoID;
+ itfRef->bfInfo = bfInfo;
+
+ /*
+ * Do not count binding from EE (ie interrupt line), as this will prevent
+ * cm_destroyInstance() of server to succeed (interrupt line bindings are
+ * destroyed after the check in cm_destroyInstance()
+ */
+ if (client->template->classe != FIRMWARE)
+ server->providedItfUsedCount++;
+ }
+}
+
+static void cm_registerLowLevelInterfaceToConst(
+ const t_interface_require_description *itfRequire,
+ const t_component_instance* targetInstance)
+{
+ const t_component_instance* client = itfRequire->client;
+
+ /*
+ * Memorise this no reference
+ */
+ {
+ t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex];
+
+ // This is an unbind from a true component (not to void)
+ // Do not count bindings from EE (ie interrupt line)
+ if ((targetInstance == NULL)
+ && (client->template->classe != FIRMWARE)
+ && (itfRef->instance != (t_component_instance *)NMF_VOID_COMPONENT)
+ && (itfRef->instance != NULL))
+ {
+ ((t_component_instance*)itfRef->instance)->providedItfUsedCount--;
+ }
+
+ itfRef->instance = targetInstance;
+ itfRef->bfInfoID = BF_SYNCHRONOUS; // Just to memorize no Binding component used and unbind ToVoid happy ;-).
+ }
+}
+
+static void cm_bindLowLevelInterfaceToConst(
+ const t_interface_require_description *itfRequire,
+ const t_dsp_address functionAddress,
+ const t_component_instance* targetInstance) {
+ const t_component_instance* client = itfRequire->client;
+ t_interface_require *require = &client->template->requires[itfRequire->requireIndex];
+ int j, k;
+
+
+ // If DSP is off/panic/... -> write nothing
+ if(
+ require->indexes != NULL
+ && cm_DSP_GetState(client->template->dspId)->state == MPC_STATE_BOOTED)
+ {
+ t_interface_require_index *requireindex = &require->indexes[itfRequire->collectionIndex];
+
+ for(k = 0; k < requireindex->numberOfClient; k++) {
+ t_uint32 *hostAddr;
+
+ hostAddr = (t_uint32*)(
+ cm_DSP_GetHostLogicalAddress(client->memories[requireindex->memories[k].memory->id]) +
+ requireindex->memories[k].offset * requireindex->memories[k].memory->memEntSize);
+
+ /*
+ * Fill the interface references. We start by Methods then This in order to swith to
+ * Unbinded panic as fast as possible and not used method with wrong This. This is
+ * relevent only for optional since we must go in stop state before rebinding other
+ * required interface.
+ *
+ * Direct write to DSP memory without go through DSP abstraction since we know we are in 24bits
+ */
+ /*
+ * Write THIS reference into the Data field of the interface reference
+ * Hack for simplifying debug just to keep THIS reference with caller one
+ * (could be removed if __return_address MMDSP intrinsec provided by compiler).
+ */
+ // Write the interface methods reference
+
+ if(((t_uint32)hostAddr & 0x7) == 0 && require->interface->methodNumber > 0)
+ {
+ // We are 64word byte aligned, combine this write with first method
+ *(volatile t_uint64*)hostAddr =
+ ((t_uint64)client->thisAddress << 0) |
+ ((t_uint64)functionAddress << 32);
+ hostAddr += 2;
+ j = 1;
+ }
+ else
+ {
+ // We are not, write this which will align us
+ *hostAddr++ = (t_uint32)client->thisAddress;
+ j = 0;
+ }
+
+ // Word align copy
+ for(; j < require->interface->methodNumber - 1; j+=2) {
+ *(volatile t_uint64*)hostAddr =
+ ((t_uint64)functionAddress << 0) |
+ ((t_uint64)functionAddress << 32);
+ hostAddr += 2;
+ }
+
+ // Last word align if required
+ if(j < require->interface->methodNumber)
+ *hostAddr = functionAddress;
+ }
+ }
+
+ cm_registerLowLevelInterfaceToConst(itfRequire, targetInstance);
+}
+
+/*
+ * Bind User component though primitive binding factory
+ */
+t_cm_error cm_bindInterface(
+ const t_interface_require_description *itfRequire,
+ const t_interface_provide_description *itfProvide) {
+
+ LOG_INTERNAL(1, "\n##### Bind Synchronous %s/%x.%s -> %s/%x.%s #####\n",
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName,
+ itfProvide->server->pathname, itfProvide->server, itfProvide->origName);
+
+ cm_bindLowLevelInterface(
+ itfRequire,
+ itfProvide,
+ BF_SYNCHRONOUS, NULL);
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_SYNCHRONOUS,
+ itfRequire->client, itfProvide->server,
+ itfRequire->client->template->requires[itfRequire->requireIndex].name,
+ itfProvide->server->template->provides[itfProvide->provideIndex].name);
+
+ return CM_OK;
+}
+
+/*
+ *
+ */
+void cm_unbindInterface(
+ const t_interface_require_description *itfRequire) {
+
+ LOG_INTERNAL(1, "\n##### UnBind synchronous %s/%x.%s #####\n",
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0);
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_SYNCHRONOUS,
+ itfRequire->client, NULL,
+ itfRequire->client->template->requires[itfRequire->requireIndex].name,
+ NULL);
+
+ cm_bindLowLevelInterfaceToConst(itfRequire,
+ 0x0,
+ NULL);
+}
+
+/*
+ *
+ */
+t_cm_error cm_bindInterfaceToVoid(
+ const t_interface_require_description *itfRequire) {
+ LOG_INTERNAL(1, "\n##### Bind %s/%x.%s -> Void #####\n",
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0);
+
+ cm_bindLowLevelInterfaceToConst(itfRequire,
+ cm_EEM_getExecutiveEngine(itfRequire->client->template->dspId)->voidAddr,
+ (t_component_instance*)NMF_VOID_COMPONENT);
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_SYNCHRONOUS,
+ itfRequire->client, NULL,
+ itfRequire->client->template->requires[itfRequire->requireIndex].name,
+ NULL);
+
+ return CM_OK;
+}
+/*
+ * Find the server and its interface inded to a given required interface for a given component
+ */
+t_cm_error cm_lookupInterface(
+ const t_interface_require_description *itfRequire,
+ t_interface_provide_description *itfProvide) {
+ const t_component_instance* client = itfRequire->client;
+ t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex];
+
+ if(itfRef->instance != NULL)
+ {
+ itfProvide->server = itfRef->instance;
+ itfProvide->provideIndex = itfRef->provideIndex;
+ itfProvide->collectionIndex = itfRef->collectionIndex;
+
+ return CM_OK;
+ } else {
+ itfProvide->server = NULL;
+ return CM_INTERFACE_NOT_BINDED;
+ }
+}
+
+/*
+ *
+ */
+t_cm_error cm_bindInterfaceTrace(
+ const t_interface_require_description *itfRequire,
+ const t_interface_provide_description *itfProvide,
+ t_elfdescription *elfhandleTrace)
+{
+ t_interface_require *require = &itfRequire->client->template->requires[itfRequire->requireIndex];
+ t_interface_require_description bcitfRequire;
+ t_interface_provide_description bcitfProvide;
+ t_trace_bf_info *bfInfo;
+ t_cm_error error;
+
+ LOG_INTERNAL(1, "\n##### Bind Synchronous Trace %s/%x.%s -> %s/%x.%s #####\n",
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName,
+ itfProvide->server->pathname, itfProvide->server, itfProvide->origName);
+
+ /* Allocate aynchronous binding factory information */
+ bfInfo = (t_trace_bf_info*)OSAL_Alloc(sizeof(t_trace_bf_info));
+ if(bfInfo == 0)
+ return CM_NO_MORE_MEMORY;
+
+ /*
+ * Instantiate related trace on dsp
+ */
+ {
+ char traceTemplateName[4 + MAX_INTERFACE_TYPE_NAME_LENGTH + 1];
+
+ cm_StringCopy(traceTemplateName,"_tr.", sizeof(traceTemplateName));
+ cm_StringConcatenate(traceTemplateName, require->interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH);
+
+ if ((error = cm_instantiateComponent(
+ traceTemplateName,
+ itfRequire->client->domainId,
+ itfProvide->server->priority,
+ traceDup,
+ elfhandleTrace,
+ &bfInfo->traceInstance)) != CM_OK) {
+ OSAL_Free(bfInfo);
+ return (error == CM_COMPONENT_NOT_FOUND)?CM_BINDING_COMPONENT_NOT_FOUND : error;
+ }
+ }
+
+ /* Bind event to server interface (Error must not occure) */
+ CM_ASSERT(cm_getRequiredInterface(bfInfo->traceInstance, "target", &bcitfRequire) == CM_OK);
+
+ cm_bindLowLevelInterface(&bcitfRequire, itfProvide, BF_SYNCHRONOUS, NULL);
+
+ /* Get the event interface (Error must not occure) */
+ CM_ASSERT(cm_getProvidedInterface(bfInfo->traceInstance, "target", &bcitfProvide) == CM_OK);
+
+ /* Bind client to event (Error must not occure) */
+ cm_bindLowLevelInterface(itfRequire, &bcitfProvide, BF_TRACE, bfInfo);
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_SYNCHRONOUS,
+ itfRequire->client, itfProvide->server,
+ itfRequire->client->template->requires[itfRequire->requireIndex].name,
+ itfProvide->server->template->provides[itfProvide->provideIndex].name);
+
+ return CM_OK;
+}
+
+void cm_unbindInterfaceTrace(
+ const t_interface_require_description *itfRequire,
+ t_trace_bf_info *bfInfo)
+{
+ t_interface_require_description traceitfRequire;
+
+ LOG_INTERNAL(1, "\n##### UnBind trace synchronous %s/%x.%s #####\n",
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0);
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_SYNCHRONOUS,
+ itfRequire->client, NULL,
+ itfRequire->client->template->requires[itfRequire->requireIndex].name,
+ NULL);
+
+ /* Unbind Client from Event Binding Component */
+ cm_bindLowLevelInterfaceToConst(itfRequire, 0x0, NULL);
+
+ /* Unbind explicitly Event from Server Binding Component */
+ /* This is mandatory to fix the providedItfUsedCount of the server */
+ CM_ASSERT(cm_getRequiredInterface(bfInfo->traceInstance, "target", &traceitfRequire) == CM_OK);
+
+ cm_registerLowLevelInterfaceToConst(&traceitfRequire, NULL);
+
+ /* Destroy Event Binding Component */
+ cm_destroyInstance(bfInfo->traceInstance, DESTROY_WITHOUT_CHECK);
+
+ /* Free BF info */
+ OSAL_Free(bfInfo);
+}
+
+
+/*
+ *
+ */
+t_cm_error cm_bindInterfaceAsynchronous(
+ const t_interface_require_description *itfRequire,
+ const t_interface_provide_description *itfProvide,
+ t_uint32 fifosize,
+ t_dsp_memory_type_id dspEventMemType,
+ t_elfdescription *elfhandleEvent) {
+ t_interface_require *require = &itfRequire->client->template->requires[itfRequire->requireIndex];
+ t_interface_require_description eventitfRequire;
+ t_interface_provide_description eventitfProvide;
+ t_async_bf_info *bfInfo;
+ t_cm_error error;
+
+ LOG_INTERNAL(1, "\n##### Bind Asynchronous %s/%x.%s -> %s/%x.%s #####\n",
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName,
+ itfProvide->server->pathname, itfProvide->server, itfProvide->origName);
+
+ /* Allocate aynchronous binding factory information */
+ bfInfo = (t_async_bf_info*)OSAL_Alloc(sizeof(t_async_bf_info));
+ if(bfInfo == 0)
+ return CM_NO_MORE_MEMORY;
+
+ /*
+ * Instantiate related event on dsp
+ */
+ {
+ char eventTemplateName[4 + MAX_INTERFACE_TYPE_NAME_LENGTH + 1];
+
+ cm_StringCopy(eventTemplateName,"_ev.", sizeof(eventTemplateName));
+ cm_StringConcatenate(eventTemplateName, require->interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH);
+
+ if ((error = cm_instantiateComponent(
+ eventTemplateName,
+ itfRequire->client->domainId,
+ itfProvide->server->priority,
+ eventDup,
+ elfhandleEvent,
+ &bfInfo->eventInstance)) != CM_OK) {
+ OSAL_Free(bfInfo);
+ return (error == CM_COMPONENT_NOT_FOUND)?CM_BINDING_COMPONENT_NOT_FOUND : error;
+ }
+ }
+
+ /*
+ * Initialize the event component
+ */
+ {
+ unsigned int size;
+
+ // Get fifo elem size (which was store in TOP by convention)
+ size = cm_readAttributeNoError(bfInfo->eventInstance, "TOP");
+ LOG_INTERNAL(3, "DspEvent Fifo element size = %d\n", size, 0, 0, 0, 0, 0);
+
+ // Allocate fifo
+ if ((error = dspevent_createDspEventFifo(bfInfo->eventInstance,
+ "TOP",
+ fifosize, size,
+ dspEventMemType,
+ &bfInfo->dspfifoHandle)) != CM_OK)
+ {
+ cm_destroyInstance(bfInfo->eventInstance, DESTROY_WITHOUT_CHECK);
+ OSAL_Free(bfInfo);
+ return error;
+ }
+ }
+
+ /* Bind event to server interface (Error must not occure) */
+ CM_ASSERT(cm_getRequiredInterface(bfInfo->eventInstance, "target", &eventitfRequire) == CM_OK);
+
+ cm_bindLowLevelInterface(&eventitfRequire, itfProvide, BF_SYNCHRONOUS, NULL);
+
+ /* Get the event interface (Error must not occure) */
+ CM_ASSERT(cm_getProvidedInterface(bfInfo->eventInstance, "target", &eventitfProvide) == CM_OK);
+
+ /* Bind client to event (Error must not occure) */
+ cm_bindLowLevelInterface(itfRequire, &eventitfProvide, BF_ASYNCHRONOUS, bfInfo);
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_ASYNCHRONOUS,
+ itfRequire->client, itfProvide->server,
+ itfRequire->client->template->requires[itfRequire->requireIndex].name,
+ itfProvide->server->template->provides[itfProvide->provideIndex].name);
+
+ return CM_OK;
+}
+
+void cm_unbindInterfaceAsynchronous(
+ const t_interface_require_description *itfRequire,
+ t_async_bf_info *bfInfo)
+{
+ t_interface_require_description eventitfRequire;
+
+ LOG_INTERNAL(1, "\n##### UnBind asynchronous %s/%x.%s #####\n",
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0);
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_ASYNCHRONOUS,
+ itfRequire->client, NULL,
+ itfRequire->client->template->requires[itfRequire->requireIndex].name,
+ NULL);
+
+ /* Unbind Client from Event Binding Component */
+ cm_bindLowLevelInterfaceToConst(itfRequire, 0x0, NULL);
+
+ /* Unbind explicitly Event from Server Binding Component */
+ /* This is mandatory to fix the providedItfUsedCount of the server */
+ CM_ASSERT(cm_getRequiredInterface(bfInfo->eventInstance, "target", &eventitfRequire) == CM_OK);
+
+ cm_registerLowLevelInterfaceToConst(&eventitfRequire, NULL);
+
+ /* Destroy Event fifo */
+ dspevent_destroyDspEventFifo(bfInfo->dspfifoHandle);
+
+ /* Destroy Event Binding Component */
+ cm_destroyInstance(bfInfo->eventInstance, DESTROY_WITHOUT_CHECK);
+
+ /* Free BF info */
+ OSAL_Free(bfInfo);
+}
+
+/*!
+ * Create Shared FIFO and set stub and skeleton to it
+ */
+PRIVATE t_cm_error cm_createParamsFifo(t_component_instance *stub,
+ t_component_instance *skeleton,
+ t_cm_domain_id domainId,
+ t_uint32 fifosize,
+ t_nmf_fifo_arm_desc **fifo,
+ t_uint32 *fifoElemSize,
+ t_uint32 bcDescSize)
+{
+ t_nmf_core_id stubcore = (stub != NULL) ?(stub->template->dspId): ARM_CORE_ID;
+ t_nmf_core_id skelcore = (skeleton != NULL) ?(skeleton->template->dspId) : ARM_CORE_ID;
+ t_component_instance *bcnotnull = (stub != NULL) ? stub : skeleton;
+ int _fifoelemsize;
+
+ CM_ASSERT(bcnotnull != NULL);
+
+ /* Get fifo param elem size (which was store in FIFO by convention) */
+ _fifoelemsize = cm_readAttributeNoError(bcnotnull, "FIFO");
+ LOG_INTERNAL(3, "Fifo Params element size = %d\n", _fifoelemsize, 0, 0, 0, 0, 0);
+ if(fifoElemSize != NULL)
+ *fifoElemSize = _fifoelemsize;
+
+ /* Allocation of the fifo params */
+ *fifo = fifo_alloc(stubcore, skelcore, _fifoelemsize, fifosize, 1+bcDescSize, paramsLocation, extendedFieldLocation, domainId); /* 1+nbMethods fro hostBCThis_or_TOP space */
+ if(*fifo == NULL)
+ return CM_NO_MORE_MEMORY;
+
+ if(stub != NULL)
+ {
+ /* Set stub FIFO attribute (Error mut not occure) */
+ cm_writeAttribute(stub, "FIFO", (*fifo)->dspAdress);
+
+ LOG_INTERNAL(2, " FIFO param %x:%x\n", *fifo, (*fifo)->dspAdress, 0, 0, 0, 0);
+ }
+
+ if(skeleton != NULL)
+ {
+ /* Set Skeleton FIFO attribute (Error mut not occure) */
+ cm_writeAttribute(skeleton, "FIFO", (*fifo)->dspAdress);
+
+ LOG_INTERNAL(2, " FIFO param %x:%x\n", *fifo, (*fifo)->dspAdress, 0, 0, 0, 0);
+ }
+
+ return CM_OK;
+}
+/**
+ *
+ */
+static void cm_destroyParamsFifo(t_nmf_fifo_arm_desc *fifo) {
+ fifo_free(fifo);
+}
+
+/*!
+ * Create DSP skeleton
+ */
+PRIVATE t_cm_error cm_createDSPSkeleton(
+ const t_interface_provide_description *itfProvide,
+ t_uint32 fifosize,
+ t_dsp_memory_type_id dspEventMemType, //INTERNAL_XRAM24
+ t_elfdescription *elfhandleSkeleton,
+ t_dspskel_bf_info *bfInfo)
+{
+ t_interface_provide *provide = &itfProvide->server->template->provides[itfProvide->provideIndex];
+ t_interface_require_description skelitfRequire;
+ t_cm_error error;
+ unsigned int fifoeventsize = 0;
+
+ /* Instantiate related stub on dsp */
+ {
+ char stubTemplateName[4 + MAX_INTERFACE_TYPE_NAME_LENGTH + 1];
+
+ cm_StringCopy(stubTemplateName,"_sk.", sizeof(stubTemplateName));
+ cm_StringConcatenate(stubTemplateName, provide->interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH);
+
+ if ((error = cm_instantiateComponent(
+ stubTemplateName,
+ itfProvide->server->domainId,
+ itfProvide->server->priority,
+ skeletonDup,
+ elfhandleSkeleton,
+ &bfInfo->skelInstance)) != CM_OK) {
+ return ((error == CM_COMPONENT_NOT_FOUND)?CM_BINDING_COMPONENT_NOT_FOUND:error);
+ }
+ }
+
+ /* Get fifo elem size (which was store in TOP by convention) */
+ fifoeventsize = cm_readAttributeNoError(bfInfo->skelInstance, "TOP");
+ LOG_INTERNAL(3, "DspEvent Fifo element size = %d\n", fifoeventsize, 0, 0, 0, 0, 0);
+
+ /* Allocation of the itf event dsp fifo */
+ if ((error = dspevent_createDspEventFifo(
+ bfInfo->skelInstance,
+ "TOP",
+ fifosize,
+ fifoeventsize,
+ dspEventMemType,
+ &bfInfo->dspfifoHandle)) != CM_OK)
+ {
+ cm_destroyInstance(bfInfo->skelInstance, DESTROY_WITHOUT_CHECK);
+ return error;
+ }
+
+ /* Bind stub to server component (Error must not occure) */
+ CM_ASSERT(cm_getRequiredInterface(bfInfo->skelInstance, "target", &skelitfRequire) == CM_OK);
+
+ cm_bindLowLevelInterface(&skelitfRequire, itfProvide, BF_SYNCHRONOUS, NULL);
+
+ return CM_OK;
+}
+
+/**
+ * Destroy DSP Skeleton
+ */
+PRIVATE t_cm_error cm_destroyDSPSkeleton(t_dspskel_bf_info *bfInfo) {
+ t_interface_require_description skelitfRequire;
+
+ /* Unbind explicitly stub from server component (Error must not occure) */
+ /* This is mandatory to fix the providedItfUsedCount of the server */
+ CM_ASSERT(cm_getRequiredInterface(bfInfo->skelInstance, "target", &skelitfRequire) == CM_OK);
+
+ cm_registerLowLevelInterfaceToConst(&skelitfRequire, NULL);
+
+ /* Destroy Event fifo */
+ dspevent_destroyDspEventFifo(bfInfo->dspfifoHandle);
+
+ /* Destroy Event Binding Component */
+ return cm_destroyInstance(bfInfo->skelInstance, DESTROY_WITHOUT_CHECK);
+}
+
+/*
+ *
+ */
+t_cm_error cm_bindComponentFromCMCore(
+ const t_interface_provide_description *itfProvide,
+ t_uint32 fifosize,
+ t_dsp_memory_type_id dspEventMemType,
+ t_elfdescription *elfhandleSkeleton,
+ t_host2mpc_bf_info **bfInfo) {
+ t_interface_provide *provide = &itfProvide->server->template->provides[itfProvide->provideIndex];
+ t_dsp_offset shareVarOffset;
+ t_cm_error error;
+
+ LOG_INTERNAL(1, "\n##### Bind HOST -> %s/%x.%s #####\n",
+ itfProvide->server->pathname, itfProvide->server, itfProvide->origName, 0, 0, 0);
+
+ /* Allocate host2dsp binding factory information */
+ *bfInfo = (t_host2mpc_bf_info*)OSAL_Alloc(sizeof(t_host2mpc_bf_info));
+ if((*bfInfo) == 0)
+ return CM_NO_MORE_MEMORY;
+
+ /* Create the Skeleton */
+ if ((error = cm_createDSPSkeleton(itfProvide,
+ fifo_normalizeDepth(fifosize), /* We SHALL create DSP Skeleton before creating the Params Fifo, but we need in advance the real depth of this fifo */
+ dspEventMemType,
+ elfhandleSkeleton,
+ &(*bfInfo)->dspskeleton)) != CM_OK)
+ {
+ OSAL_Free((*bfInfo));
+ return error;
+ }
+
+ /* Create the FIFO Params */
+ if ((error = cm_createParamsFifo(NULL,
+ (*bfInfo)->dspskeleton.skelInstance,
+ itfProvide->server->domainId,
+ fifosize,
+ &(*bfInfo)->fifo,
+ NULL,
+ provide->interface->methodNumber)) != CM_OK)
+ {
+ cm_destroyDSPSkeleton(&(*bfInfo)->dspskeleton);
+ OSAL_Free((*bfInfo));
+ return error;
+ }
+
+ /* Set Target info in FIFO param to TOP */
+ shareVarOffset = cm_getAttributeMpcAddress((*bfInfo)->dspskeleton.skelInstance, "TOP");
+
+ /*
+ * Set Target info in FIFO param to armThis
+ * Should not return any error
+ */
+ fifo_params_setSharedField((*bfInfo)->fifo, 0, (t_shared_field)shareVarOffset /* ArmBCThis_or_TOP */);
+
+ /* Initialise FIFO Param bcDesc with Skeleton methods */
+ {
+ int i;
+ t_component_instance *skel = (*bfInfo)->dspskeleton.skelInstance;
+ for (i=0; i < provide->interface->methodNumber; i++)
+ {
+ /* should not return error */
+ fifo_params_setSharedField(
+ (*bfInfo)->fifo,
+ 1+i,
+ skel->template->provides[0].indexes[0][i].methodAddresses
+ );
+ }
+ }
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_ASYNCHRONOUS,
+ ARM_TRACE_COMPONENT, itfProvide->server,
+ NULL,
+ itfProvide->server->template->provides[itfProvide->provideIndex].name);
+
+ return CM_OK;
+}
+
+void cm_unbindComponentFromCMCore(
+ t_host2mpc_bf_info* bfInfo) {
+ t_component_instance *skel = bfInfo->dspskeleton.skelInstance;
+ t_interface_reference* itfProvide = &skel->interfaceReferences[0][0];
+ t_interface_provide *provide = &itfProvide->instance->template->provides[itfProvide->provideIndex];
+
+ LOG_INTERNAL(1, "\n##### UnBind HOST -> %s/%x.%s #####\n",
+ itfProvide->instance->pathname, itfProvide->instance, provide->name, 0, 0, 0);
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_ASYNCHRONOUS,
+ ARM_TRACE_COMPONENT, itfProvide->instance,
+ NULL,
+ itfProvide->instance->template->provides[itfProvide->provideIndex].name);
+
+ // Destroy FIFO params
+ cm_destroyParamsFifo(bfInfo->fifo);
+
+ // Destory Skeleton
+ cm_destroyDSPSkeleton(&bfInfo->dspskeleton);
+
+ // Free BF info (which contains bcDecr(==dspfct) and arm This)
+ OSAL_Free(bfInfo);
+}
+
+/**
+ * Create DSP Stub
+ */
+PRIVATE t_cm_error cm_createDSPStub(
+ const t_interface_require_description *itfRequire,
+ const char* itfType,
+ t_dspstub_bf_info* bfInfo,
+ t_elfdescription *elfhandleStub,
+ t_interface_provide_description *itfstubProvide) {
+ t_cm_error error;
+
+ /*
+ * Instantiate related skel on dsp
+ */
+ {
+ char skelTemplateName[4 + MAX_INTERFACE_TYPE_NAME_LENGTH + 1];
+
+ cm_StringCopy(skelTemplateName, "_st.", sizeof(skelTemplateName));
+ cm_StringConcatenate(skelTemplateName, itfType, MAX_INTERFACE_TYPE_NAME_LENGTH);
+
+ if ((error = cm_instantiateComponent(
+ skelTemplateName,
+ itfRequire->client->domainId,
+ itfRequire->client->priority,
+ stubDup,
+ elfhandleStub,
+ &bfInfo->stubInstance)) != CM_OK) {
+ return (error == CM_COMPONENT_NOT_FOUND)?CM_BINDING_COMPONENT_NOT_FOUND : error;
+ }
+ }
+
+ /* Get the internal component that serve this interface (Error must not occure) */
+ (void)cm_getProvidedInterface(bfInfo->stubInstance, "source", itfstubProvide);
+
+ return CM_OK;
+}
+
+PRIVATE t_cm_error cm_destroyDSPStub(
+ const t_interface_require_description *itfRequire,
+ t_dspstub_bf_info* bfInfo) {
+
+ /* Unbind Client from Event Binding Component */
+ cm_bindLowLevelInterfaceToConst(itfRequire,
+ 0x0,
+ NULL);
+
+ /* Destroy Event Binding Component */
+ return cm_destroyInstance(bfInfo->stubInstance, DESTROY_WITHOUT_CHECK);
+}
+/*
+ *
+ */
+t_cm_error cm_bindComponentToCMCore(
+ const t_interface_require_description *itfRequire,
+ t_uint32 fifosize,
+ t_uint32 context,
+ t_elfdescription *elfhandleStub,
+ t_mpc2host_bf_info ** bfInfo) {
+ t_interface_require *require = &itfRequire->client->template->requires[itfRequire->requireIndex];
+ t_interface_provide_description itfstubProvide;
+ t_cm_error error;
+ t_uint32 fifoelemsize;
+
+ LOG_INTERNAL(1, "\n##### Bind %s/%x.%s -> HOST #####\n",
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0);
+
+ /* Allocate dsp2host binding factory information */
+ *bfInfo = (t_mpc2host_bf_info*)OSAL_Alloc(sizeof(t_mpc2host_bf_info));
+ if(*bfInfo == 0)
+ return CM_NO_MORE_MEMORY;
+ (*bfInfo)->context = context;
+
+ if ((error = cm_createDSPStub(itfRequire,
+ require->interface->type,
+ &(*bfInfo)->dspstub,
+ elfhandleStub,
+ &itfstubProvide)) != CM_OK)
+ {
+ OSAL_Free(*bfInfo);
+ return error;
+ }
+
+ /* Create the FIFO Params */
+ if ((error = cm_createParamsFifo(
+ (*bfInfo)->dspstub.stubInstance,
+ NULL,
+ itfRequire->client->domainId,
+ fifosize,
+ &(*bfInfo)->fifo,
+ &fifoelemsize,
+ 1)) != CM_OK) /* 1 => we used first field as max params size */
+ {
+ cm_destroyDSPStub(itfRequire, &(*bfInfo)->dspstub);
+ OSAL_Free(*bfInfo);
+ return error;
+ }
+
+ /* Bind client to stub component (Error must not occure) */
+ cm_bindLowLevelInterface(itfRequire, &itfstubProvide, BF_DSP2HOST, *bfInfo);
+
+ /* Bind stub component to host (virtual bind) */
+ cm_bindVirtualInterface((*bfInfo)->dspstub.stubInstance, (t_component_instance*)NMF_HOST_COMPONENT);
+
+ /*
+ * Set Target info in FIFO param to armThis
+ * Initialise FIFO Param bcDesc with Jumptable
+ * Should not return any error
+ */
+ fifo_params_setSharedField((*bfInfo)->fifo, 0, (t_shared_field)context /* ArmBCThis_or_TOP */);
+ fifo_params_setSharedField((*bfInfo)->fifo, 1, (t_shared_field)fifoelemsize * 2/* bcDescRef */);
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_ASYNCHRONOUS,
+ itfRequire->client, ARM_TRACE_COMPONENT,
+ itfRequire->client->template->requires[itfRequire->requireIndex].name,
+ NULL);
+
+ return error;
+}
+
+void cm_unbindComponentToCMCore(
+ const t_interface_require_description *itfRequire,
+ t_mpc2host_bf_info *bfInfo)
+{
+ LOG_INTERNAL(1, "\n##### UnBind %s/%x.%s -> HOST #####\n",
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0);
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_ASYNCHRONOUS,
+ itfRequire->client, ARM_TRACE_COMPONENT,
+ itfRequire->client->template->requires[itfRequire->requireIndex].name,
+ NULL);
+
+ /* Unbind virtual interface coms */
+ cm_unbindVirtualInterface(bfInfo->dspstub.stubInstance);
+
+ // Destroy FIFO params
+ cm_destroyParamsFifo(bfInfo->fifo);
+
+ // Destroy DSP Stub
+ cm_destroyDSPStub(itfRequire, &bfInfo->dspstub);
+
+ /* Free BF info */
+ OSAL_Free(bfInfo);
+}
+
+/*!
+ *
+ */
+t_cm_error cm_bindInterfaceDistributed(
+ const t_interface_require_description *itfRequire,
+ const t_interface_provide_description *itfProvide,
+ t_uint32 fifosize,
+ t_dsp_memory_type_id dspEventMemType,
+ t_elfdescription *elfhandleSkeleton,
+ t_elfdescription *elfhandleStub) {
+ t_interface_require *require = &itfRequire->client->template->requires[itfRequire->requireIndex];
+ t_interface_provide_description itfstubProvide;
+ t_cm_error error;
+ t_mpc2mpc_bf_info *bfInfo;
+ t_dsp_offset shareVarOffset;
+
+ LOG_INTERNAL(1, "\n##### Bind Distributed %s/%x.%s -> %s/%x.%s #####\n",
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName,
+ itfProvide->server->pathname, itfProvide->server, itfProvide->origName);
+
+ /* Allocate aynchronous binding factory information */
+ bfInfo = (t_mpc2mpc_bf_info*)OSAL_Alloc(sizeof(t_mpc2mpc_bf_info));
+ if(bfInfo == 0)
+ return CM_NO_MORE_MEMORY;
+
+ /* Create the Skeleton */
+ if ((error = cm_createDSPSkeleton(itfProvide,
+ fifo_normalizeDepth(fifosize), /* We SHALL create DSP Skeleton before creating the Params Fifo, but we need in advance the real depth of this fifo */
+ dspEventMemType,
+ elfhandleSkeleton,
+ &bfInfo->dspskeleton)) != CM_OK)
+ {
+ OSAL_Free(bfInfo);
+ return error;
+ }
+
+ // Create DSP Stub
+ if ((error = cm_createDSPStub(itfRequire,
+ require->interface->type,
+ &bfInfo->dspstub,
+ elfhandleStub,
+ &itfstubProvide)) != CM_OK)
+ {
+ cm_destroyDSPSkeleton(&bfInfo->dspskeleton);
+ OSAL_Free(bfInfo);
+ return error;
+ }
+
+ /* Bind client to stub component (Error must not occure) */
+ cm_bindLowLevelInterface(itfRequire, &itfstubProvide, BF_DSP2DSP, bfInfo);
+
+ /* Create the FIFO Params */
+ if ((error = cm_createParamsFifo(
+ bfInfo->dspstub.stubInstance,
+ bfInfo->dspskeleton.skelInstance,
+ itfProvide->server->domainId,
+ fifosize,
+ &bfInfo->fifo,
+ NULL,
+ require->interface->methodNumber)) != CM_OK)
+ {
+ cm_destroyDSPStub(itfRequire, &bfInfo->dspstub);
+ cm_destroyDSPSkeleton(&bfInfo->dspskeleton);
+ OSAL_Free(bfInfo);
+ return error;
+ }
+
+ /* Bind stub component to host (virtual bind) */
+ cm_bindVirtualInterface(bfInfo->dspstub.stubInstance, bfInfo->dspskeleton.skelInstance);
+
+ /* Set Target info in FIFO param to TOP */
+ shareVarOffset = cm_getAttributeMpcAddress(bfInfo->dspskeleton.skelInstance, "TOP");
+
+ /*
+ * Set Target info in FIFO param to armThis
+ * Should not return any error
+ */
+ fifo_params_setSharedField(bfInfo->fifo, 0, (t_shared_field)shareVarOffset /* ArmBCThis_or_TOP */);
+
+ /* Initialise FIFO Param bcDesc with Skeleton methods */
+ {
+ int i;
+ t_component_instance *skel = bfInfo->dspskeleton.skelInstance;
+ for (i=0; i < require->interface->methodNumber; i++)
+ {
+ /* should not return error */
+ fifo_params_setSharedField(
+ bfInfo->fifo,
+ 1+i,
+ skel->template->provides[0].indexes[0][i].methodAddresses
+ );
+ }
+ }
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_BIND_ASYNCHRONOUS,
+ itfRequire->client, itfProvide->server,
+ itfRequire->client->template->requires[itfRequire->requireIndex].name,
+ itfProvide->server->template->provides[itfProvide->provideIndex].name);
+
+ return CM_OK;
+}
+
+/*!
+ *
+ */
+void cm_unbindInterfaceDistributed(
+ const t_interface_require_description *itfRequire,
+ t_mpc2mpc_bf_info *bfInfo)
+{
+ LOG_INTERNAL(1, "\n##### UnBind distributed %s/%x.%s #####\n",
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0, 0);
+
+ cm_TRC_traceBinding(TRACE_BIND_COMMAND_UNBIND_ASYNCHRONOUS,
+ itfRequire->client, NULL,
+ itfRequire->client->template->requires[itfRequire->requireIndex].name,
+ NULL);
+
+ /* Unbind virtual interface */
+ cm_unbindVirtualInterface(bfInfo->dspstub.stubInstance);
+
+ // Destroy FIFO params
+ cm_destroyParamsFifo(bfInfo->fifo);
+
+ // Destroy DSP Stub
+ cm_destroyDSPStub(itfRequire, &bfInfo->dspstub);
+
+ // Destory DSP Skeleton
+ cm_destroyDSPSkeleton(&bfInfo->dspskeleton);
+
+ // Destroy BF Info
+ OSAL_Free(bfInfo);
+}
+
+t_cm_error cm_bindInterfaceStaticInterrupt(
+ const t_nmf_core_id coreId,
+ const int interruptLine,
+ const t_component_instance *server,
+ const char* providedItfServerName
+)
+{
+ char requiredItfClientName[CM_IT_NAME_MAX_LENGTH];
+ t_component_instance *client = cm_EEM_getExecutiveEngine(coreId)->instance;
+ t_interface_require_description itfRequire;
+ t_interface_provide_description itfProvide;
+ t_cm_error error;
+
+ //build it[%d] name
+ if (interruptLine < 0 || interruptLine > 255) {return CM_OUT_OF_LIMITS;}
+ cm_fillItName(interruptLine, requiredItfClientName);
+
+ //do binding
+ if ((error = cm_getRequiredInterface(client,requiredItfClientName,&itfRequire)) != CM_OK) {return error;}
+ if ((error = cm_getProvidedInterface(server,providedItfServerName,&itfProvide)) != CM_OK) {return error;}
+ if((error = cm_bindInterface(&itfRequire, &itfProvide)) != CM_OK) {return error;}
+
+ return CM_OK;
+}
+
+t_cm_error cm_unbindInterfaceStaticInterrupt(
+ const t_nmf_core_id coreId,
+ const int interruptLine
+)
+{
+ char requiredItfClientName[CM_IT_NAME_MAX_LENGTH];
+ t_component_instance *client = cm_EEM_getExecutiveEngine(coreId)->instance;
+ t_interface_require_description itfRequire;
+ t_cm_error error;
+
+ //build it[%d] name
+ if (interruptLine < 0 || interruptLine > 255) {return CM_OUT_OF_LIMITS;}
+ cm_fillItName(interruptLine, requiredItfClientName);
+
+ //do unbinding
+ if ((error = cm_getRequiredInterface(client,requiredItfClientName,&itfRequire)) != CM_OK) {return error;}
+ cm_unbindInterface(&itfRequire);
+
+ return CM_OK;
+}
+
+void cm_destroyRequireInterface(t_component_instance* component, t_nmf_client_id clientId)
+{
+ int i, j;
+
+ for(i = 0; i < component->template->requireNumber; i++)
+ {
+ int nb = component->template->requires[i].collectionSize;
+ for(j = 0; j < nb; j++)
+ {
+ if(component->interfaceReferences[i][j].instance != NULL)
+ {
+ t_interface_reference* itfRef = &component->interfaceReferences[i][j];
+ t_interface_require_description itfRequire;
+
+ itfRequire.client = component;
+ itfRequire.requireIndex = i;
+ itfRequire.collectionIndex = j;
+ itfRequire.origName = component->template->requires[i].name;
+
+ switch (itfRef->bfInfoID) {
+ case BF_SYNCHRONOUS:
+ /* Error ignored as it is always OK */
+ cm_unbindInterface(&itfRequire);
+ break;
+ case BF_TRACE:
+ cm_unbindInterfaceTrace(&itfRequire,
+ (t_trace_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo);
+ break;
+ case BF_ASYNCHRONOUS:
+ cm_unbindInterfaceAsynchronous(&itfRequire,
+ (t_async_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo);
+ break;
+ case BF_DSP2HOST:
+ /* This 'mpc2host handle' is provided by the host at OS Integration level.
+ It must then be handled and released in OS specific part.
+ */
+ cm_unbindComponentToCMCore(&itfRequire,
+ (t_mpc2host_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo);
+ break;
+ case BF_HOST2DSP:
+ /* These bindings are from CM Core to DSP, they are not listed
+ here and must be handles/freed by host at OS Integration level
+ */
+ break;
+ case BF_DSP2DSP:
+ cm_unbindInterfaceDistributed(&itfRequire,
+ (t_mpc2mpc_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+void cm_registerSingletonBinding(
+ t_component_instance* component,
+ t_interface_require_description* itfRequire,
+ t_interface_provide_description* itfProvide,
+ t_nmf_client_id clientId)
+{
+ if(component->template->classe == SINGLETON)
+ {
+ struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, FALSE, clientId);
+ if(cl != NULL)
+ cl->numberOfBind++;
+
+ if(itfProvide != NULL)
+ LOG_INTERNAL(1, " -> Singleton[%d] : Register binding %s/%x.%s -> %s/%x\n",
+ clientId,
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName,
+ itfProvide->server->pathname, itfProvide->server);
+ else
+ LOG_INTERNAL(1, " -> Singleton[%d] : Register binding %s/%x.%s -> ARM/VOID\n",
+ clientId,
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0);
+ }
+}
+
+t_bool cm_unregisterSingletonBinding(
+ t_component_instance* component,
+ t_interface_require_description* itfRequire,
+ t_interface_provide_description* itfProvide,
+ t_nmf_client_id clientId)
+{
+ if(component->template->classe == SINGLETON)
+ {
+ struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, FALSE, clientId);
+ if(cl != NULL)
+ cl->numberOfBind--;
+
+ if(itfProvide->server == (t_component_instance *)NMF_VOID_COMPONENT)
+ LOG_INTERNAL(1, " -> Singleton[%d] : Unregister binding %s/%x.%s -> ARM/VOID\n",
+ clientId,
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0);
+ else if(itfProvide->server == NULL)
+ LOG_INTERNAL(1, " -> Singleton[%d] : Unregister binding %s/%x.%s -> ?? <already unbound>\n",
+ clientId,
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName, 0, 0);
+ else
+ LOG_INTERNAL(1, " -> Singleton[%d] : Unregister binding %s/%x.%s -> %s/%x\n",
+ clientId,
+ itfRequire->client->pathname, itfRequire->client, itfRequire->origName,
+ itfProvide->server->pathname, itfProvide->server);
+
+ if(getNumberOfBind(component) == 0)
+ {
+ LOG_INTERNAL(1, " -> Singleton[%d] : All required of %s/%x logically unbound, perform physical unbind\n",
+ clientId, itfRequire->client->pathname, itfRequire->client, 0, 0, 0);
+
+ (void)cm_EEM_ForceWakeup(component->template->dspId);
+
+ // This is the last binding unbind all !!!
+ cm_destroyRequireInterface(component, clientId);
+
+ cm_EEM_AllowSleep(component->template->dspId);
+ }
+ else if(itfProvide->server != NULL)
+ {
+ t_interface_require* itfReq;
+ itfReq = &itfRequire->client->template->requires[itfRequire->requireIndex];
+ if((itfReq->requireTypes & OPTIONAL_REQUIRE) != 0x0)
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static t_uint16 getNumberOfBind(t_component_instance* component)
+{
+ t_uint16 bindNumber = 0;
+ struct t_client_of_singleton* cur = component->clientOfSingleton;
+
+ for( ; cur != NULL ; cur = cur->next)
+ {
+ bindNumber += cur->numberOfBind;
+ }
+
+ return bindNumber;
+}
+
+static void cm_fillItName(int interruptLine, char *itName)
+{
+ int divider = 10000;
+
+ *itName++ = 'i';
+ *itName++ = 't';
+ *itName++ = '[';
+
+ // Find first significant divider
+ while(divider > interruptLine)
+ divider /= 10;
+
+ // Compute number
+ do
+ {
+ *itName++ = "0123456789"[interruptLine / divider];
+ interruptLine %= divider;
+ divider /= 10;
+ } while(divider != 0);
+
+ *itName++ = ']';
+ *itName++ = '\0';
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/binder_check.c b/drivers/staging/nmf-cm/cm/engine/component/src/binder_check.c
new file mode 100644
index 00000000000..b141064c1c1
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/src/binder_check.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include "../inc/bind.h"
+#include <cm/engine/trace/inc/trace.h>
+
+#include <cm/engine/utils/inc/string.h>
+
+t_cm_error cm_checkValidClient(
+ const t_component_instance* client,
+ const char* requiredItfClientName,
+ t_interface_require_description *itfRequire,
+ t_bool *bindable) {
+ t_cm_error error;
+
+ // Component LC state check
+ if (NULL == client)
+ return CM_INVALID_COMPONENT_HANDLE;
+
+ // Check if the requiredItfClientName is required by client component
+ if ((error = cm_getRequiredInterface(client, requiredItfClientName, itfRequire)) != CM_OK)
+ return error;
+
+ // Check required interface not already binded
+ {
+ t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex];
+
+ if(itfRef->instance != (t_component_instance*)NULL)
+ {
+ if(client->template->classe == SINGLETON)
+ {
+ // Singleton is immutable thus we can't rebind it, nevertheless it's not an issue
+ *bindable = FALSE;
+ return CM_OK;
+ }
+ else
+ {
+ t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex];
+
+ if(itfRef->instance == (const t_component_instance*)NMF_VOID_COMPONENT)
+ ERROR("CM_INTERFACE_ALREADY_BINDED(): Component (%s<%s>.s) already bound to VOID\n",
+ client->pathname, client->template->name, requiredItfClientName, 0, 0, 0);
+ else
+ ERROR("CM_INTERFACE_ALREADY_BINDED(): Component (%s<%s>.s) already bound to another server (%s<%s>.%s)\n",
+ client->pathname, client->template->name, requiredItfClientName,
+ itfRef->instance->pathname, itfRef->instance->template->name, itfRef->instance->template->provides[itfRef->provideIndex].name);
+ return CM_INTERFACE_ALREADY_BINDED;
+ }
+ }
+ }
+
+ // Delayed Component LC state check done only if not optional required interface or intrinsic one that has been solved by loader
+ {
+ t_interface_require* itfReq = &client->template->requires[itfRequire->requireIndex];
+
+ if((itfReq->requireTypes & (OPTIONAL_REQUIRE | INTRINSEC_REQUIRE)) == 0) {
+ if(client->state == STATE_RUNNABLE)
+ return CM_COMPONENT_NOT_STOPPED;
+ }
+ }
+
+ *bindable = TRUE;
+
+ return CM_OK;
+}
+
+t_cm_error cm_checkValidServer(
+ const t_component_instance* server,
+ const char* providedItfServerName,
+ t_interface_provide_description *itfProvide) {
+ t_cm_error error;
+
+ // Check if the components are initialized
+ //if (server->state == STATE_INSTANCIATED)
+ // return CM_COMPONENT_NOT_INITIALIZED;
+ if(NULL == server)
+ return CM_INVALID_COMPONENT_HANDLE;
+
+ // Check if the providedItfServerName is provided by server component
+ if((error = cm_getProvidedInterface(server, providedItfServerName, itfProvide)) != CM_OK)
+ return error;
+
+ return CM_OK;
+}
+
+t_cm_error cm_checkValidBinding(
+ const t_component_instance* client,
+ const char* requiredItfClientName,
+ const t_component_instance* server,
+ const char* providedItfServerName,
+ t_interface_require_description *itfRequire,
+ t_interface_provide_description *itfProvide,
+ t_bool *bindable) {
+ t_interface_require *require;
+ t_interface_provide *provide;
+ t_cm_error error;
+
+ // Check Server
+ if((error = cm_checkValidServer(server, providedItfServerName, itfProvide)) != CM_OK)
+ return error;
+
+ // Check Client
+ if((error = cm_checkValidClient(client, requiredItfClientName, itfRequire, bindable)) != CM_OK)
+ return error;
+
+ // If this is a singleton which has been already bound check that next binding is at the same server
+ if(*bindable == FALSE
+ && client->template->classe == SINGLETON)
+ {
+ t_interface_reference* itfRef = &client->interfaceReferences[itfRequire->requireIndex][itfRequire->collectionIndex];
+ while( itfRef->instance != server
+ || itfRef->provideIndex != itfProvide->provideIndex
+ || itfRef->collectionIndex != itfProvide->collectionIndex )
+ {
+ if(itfRef->instance == (const t_component_instance*)NMF_VOID_COMPONENT)
+ {
+ ERROR("CM_INTERFACE_ALREADY_BINDED(): Singleton (%s<%s>.s) already bound to VOID\n",
+ client->pathname, client->template->name, requiredItfClientName, 0, 0, 0);
+ return CM_INTERFACE_ALREADY_BINDED;
+ }
+ else if(itfRef->bfInfoID == BF_ASYNCHRONOUS || itfRef->bfInfoID == BF_TRACE)
+ {
+ t_interface_require_description eventitfRequire;
+ CM_ASSERT(cm_getRequiredInterface(itfRef->instance, "target", &eventitfRequire) == CM_OK);
+ itfRef = &itfRef->instance->interfaceReferences[eventitfRequire.requireIndex][eventitfRequire.collectionIndex];
+
+ // Go to see client of event if the same
+ }
+ else
+ {
+ ERROR("CM_INTERFACE_ALREADY_BINDED(): Singleton (%s<%s>.s) already bound to different server (%s<%s>.%s)\n",
+ client->pathname, client->template->name, requiredItfClientName,
+ itfRef->instance->pathname, itfRef->instance->template->name, itfRef->instance->template->provides[itfRef->provideIndex].name);
+ return CM_INTERFACE_ALREADY_BINDED;
+ }
+ }
+ }
+
+ // Check if provided and required type matches
+ require = &client->template->requires[itfRequire->requireIndex];
+ provide = &server->template->provides[itfProvide->provideIndex];
+ if(require->interface != provide->interface)
+ {
+ ERROR("CM_ILLEGAL_BINDING(%s, %s)\n", require->interface->type, provide->interface->type, 0, 0, 0, 0);
+ return CM_ILLEGAL_BINDING;
+ }
+
+ // Check if static required interface binded to singleton component
+ if((require->requireTypes & STATIC_REQUIRE) &&
+ (server->template->classe != SINGLETON))
+ {
+ ERROR("CM_ILLEGAL_BINDING(): Can't bind static required interface to not singleton component\n",
+ 0, 0, 0, 0, 0, 0);
+ return CM_ILLEGAL_BINDING;
+ }
+
+ return CM_OK;
+}
+
+t_cm_error cm_checkValidUnbinding(
+ const t_component_instance* client,
+ const char* requiredItfClientName,
+ t_interface_require_description *itfRequire,
+ t_interface_provide_description *itfProvide) {
+ t_cm_error error;
+ t_interface_require* itfReq;
+
+ // Component LC state check
+ if (NULL == client)
+ return CM_INVALID_COMPONENT_HANDLE;
+
+ // Check if the requiredItfClientName is required by client component
+ if ((error = cm_getRequiredInterface(client, requiredItfClientName, itfRequire)) != CM_OK)
+ return error;
+
+ itfReq = &client->template->requires[itfRequire->requireIndex];
+
+ // Check if the requiredItfClientName is required by client component
+ if ((error = cm_lookupInterface(itfRequire, itfProvide)) != CM_OK)
+ {
+ // We allow to unbind optional required of singleton even if not binded, since it could have been unbound previously but we don't
+ // want to break bind singleton reference counter
+ if((client->template->classe == SINGLETON) &&
+ (itfReq->requireTypes & OPTIONAL_REQUIRE) != 0x0)
+ return CM_OK;
+
+ return error;
+ }
+
+ // Singleton is immutable, don't unbind it
+ if(client->template->classe == SINGLETON)
+ return CM_OK;
+
+ /* if interface is optionnal then allow unbinding even if not stop */
+ if((itfReq->requireTypes & OPTIONAL_REQUIRE) == 0x0)
+ {
+ if(client->state == STATE_RUNNABLE)
+ return CM_COMPONENT_NOT_STOPPED;
+ }
+
+ return CM_OK;
+}
+
diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/component_wrapper.c b/drivers/staging/nmf-cm/cm/engine/component/src/component_wrapper.c
new file mode 100644
index 00000000000..dafbcf5e843
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/src/component_wrapper.c
@@ -0,0 +1,1297 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/api/component_engine.h>
+#include <cm/engine/api/communication_engine.h>
+
+#include <cm/engine/component/inc/bind.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/component/inc/introspection.h>
+#include <cm/engine/utils/inc/string.h>
+#include <cm/engine/memory/inc/domain.h>
+
+#include <cm/engine/configuration/inc/configuration.h>
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+
+/*
+ * Component mangement wrapping.
+ */
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_InstantiateComponent(
+ const char* templateName,
+ t_cm_domain_id domainId,
+ t_nmf_client_id clientId,
+ t_nmf_ee_priority priority,
+ const char localName[MAX_COMPONENT_NAME_LENGTH],
+ const char *dataFile,
+ t_cm_instance_handle *instance) {
+ t_cm_error error;
+ t_nmf_core_id coreId;
+ t_component_instance *comp;
+ t_elfdescription *elfhandle = NULL;
+
+ OSAL_LOCK_API();
+
+ /*
+ * Load Elf File
+ */
+ if(dataFile != NULL &&
+ (error = cm_ELF_CheckFile(
+ dataFile,
+ TRUE,
+ &elfhandle)) != CM_OK)
+ goto out;
+
+ //only allow instantiation in non-scratch domains (ie. DOMAIN_NORMAL)!
+ if ((error = cm_DM_CheckDomainWithClient(domainId, DOMAIN_NORMAL, clientId)) != CM_OK)
+ goto out;
+
+ coreId = cm_DM_GetDomainCoreId(domainId);
+
+ if(coreId < FIRST_MPC_ID || coreId > LAST_CORE_ID)
+ {
+ error = CM_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if ((error = cm_CFG_CheckMpcStatus(coreId)) != CM_OK)
+ goto out;
+
+ if ((error = cm_EEM_ForceWakeup(coreId)) != CM_OK)
+ goto out;
+
+ error = cm_instantiateComponent(
+ templateName,
+ domainId,
+ priority,
+ localName,
+ elfhandle,
+ &comp);
+ if(error == CM_OK)
+ *instance = comp->instance;
+
+ cm_EEM_AllowSleep(coreId);
+
+out:
+ cm_ELF_CloseFile(TRUE, elfhandle);
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_StartComponent(
+ t_cm_instance_handle instance,
+ t_nmf_client_id clientId) {
+ t_cm_error error;
+ t_component_instance *component;
+
+ OSAL_LOCK_API();
+
+ component = cm_lookupComponent(instance);
+ if (NULL == component)
+ error = CM_INVALID_COMPONENT_HANDLE;
+ else
+ {
+ error = cm_startComponent(component, clientId);
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_StopComponent(
+ t_cm_instance_handle instance,
+ t_nmf_client_id clientId) {
+ t_cm_error error;
+ t_component_instance *component;
+
+ OSAL_LOCK_API();
+
+ component = cm_lookupComponent(instance);
+ if (NULL == component)
+ error = CM_INVALID_COMPONENT_HANDLE;
+ else
+ {
+ error = cm_stopComponent(component, clientId);
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_DestroyComponent(
+ t_cm_instance_handle instance,
+ t_nmf_client_id clientId)
+{
+ t_cm_error error;
+ t_component_instance *component;
+
+ OSAL_LOCK_API();
+
+ component = cm_lookupComponent(instance);
+ if (NULL == component)
+ {
+ error = CM_INVALID_COMPONENT_HANDLE;
+ }
+ else
+ {
+ t_nmf_core_id coreId = component->template->dspId;
+
+ (void)cm_EEM_ForceWakeup(coreId);
+
+ error = cm_destroyInstanceForClient(component, DESTROY_NORMAL, clientId);
+
+ cm_CFG_ReleaseMpc(coreId);
+
+ cm_EEM_AllowSleep(coreId);
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_FlushComponents(t_nmf_client_id clientId)
+{
+ t_cm_error error = CM_OK;
+ t_component_instance *instance;
+ t_uint32 i;
+
+ if (clientId == 0)
+ return CM_INVALID_PARAMETER;
+
+ OSAL_LOCK_API();
+
+ // We don't know exactly where components will be, wake up everybody !!
+ (void)cm_EEM_ForceWakeup(SVA_CORE_ID);
+ (void)cm_EEM_ForceWakeup(SIA_CORE_ID);
+
+ /* Destroy all host2mpc bindings */
+ for (i=0; i<Host2MpcBindingTable.idxMax; i++)
+ {
+ t_host2mpc_bf_info* bfInfo;
+ OSAL_LOCK_COM();
+ bfInfo = Host2MpcBindingTable.entries[i];
+ if ((bfInfo != NULL) && (bfInfo->clientId == clientId)) {
+ cm_delEntry(&Host2MpcBindingTable, i);
+ OSAL_UNLOCK_COM();
+ cm_unbindComponentFromCMCore(bfInfo);
+ } else
+ OSAL_UNLOCK_COM();
+ }
+
+ /* First, stop all remaining components for this client */
+ for (i=0; i<ComponentTable.idxMax; i++)
+ {
+ if ((instance = componentEntry(i)) == NULL)
+ continue;
+ if (/* skip EE */
+ (instance->template->classe == FIRMWARE) ||
+ /* Skip all binding components */
+ (cm_StringCompare(instance->template->name, "_ev.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_st.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_sk.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_tr.", 4) == 0))
+ continue;
+
+ /*
+ * Special code for SINGLETON handling
+ */
+ if(instance->template->classe == SINGLETON)
+ {
+ struct t_client_of_singleton* cl = cm_getClientOfSingleton(instance, FALSE, clientId);
+ if(cl == NULL)
+ continue;
+
+ cl->numberOfStart = 1; // == 1 since it will go to 0 in cm_stopComponent
+ cl->numberOfInstance = 1; // == 1 since it will go to 0 in cm_destroyInstanceForClient
+ cl->numberOfBind = 1; // == 1 since it will go to 0 in cm_destroyRequireInterface
+ }
+ else if(domainDesc[instance->domainId].client != clientId)
+ /* Skip all components not belonging to our client */
+ continue;
+
+ // Stop the component
+ error = cm_stopComponent(instance, clientId);
+ if (error != CM_OK && error != CM_COMPONENT_NOT_STARTED)
+ LOG_INTERNAL(0, "Error stopping component %s/%x (%s, error=%d, client=%u)\n", instance->pathname, instance, instance->template->name, error, clientId, 0);
+
+ // Destroy dependencies
+ cm_destroyRequireInterface(instance, clientId);
+ }
+
+ /* Destroy all remaining components for this client */
+ for (i=0; i<ComponentTable.idxMax; i++)
+ {
+ if ((instance = componentEntry(i)) == NULL)
+ continue;
+ if (/* skip EE */
+ (instance->template->classe == FIRMWARE) ||
+ /* Skip all binding components */
+ (cm_StringCompare(instance->template->name, "_ev.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_st.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_sk.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_tr.", 4) == 0)) {
+ continue;
+ }
+
+
+ /*
+ * Special code for SINGLETON handling
+ */
+ if(instance->template->classe == SINGLETON)
+ {
+ struct t_client_of_singleton* cl = cm_getClientOfSingleton(instance, FALSE, clientId);
+ if(cl == NULL)
+ continue;
+ }
+ else if(domainDesc[instance->domainId].client != clientId)
+ /* Skip all components not belonging to our client */
+ continue;
+
+
+ // Destroy the component
+ error = cm_destroyInstanceForClient(instance, DESTROY_WITHOUT_CHECK, clientId);
+
+ if (error != CM_OK)
+ {
+ /* FIXME : add component name instance in log message but need to make a copy before cm_flushComponent()
+ * because it's no more available after.
+ */
+ LOG_INTERNAL(0, "Error flushing component (error=%d, client=%u)\n", error, clientId, 0, 0, 0, 0);
+ }
+ }
+
+ cm_CFG_ReleaseMpc(SVA_CORE_ID);
+ cm_CFG_ReleaseMpc(SIA_CORE_ID);
+
+ cm_EEM_AllowSleep(SVA_CORE_ID);
+ cm_EEM_AllowSleep(SIA_CORE_ID);
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+/*
+ * Component binding wrapping.
+ */
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_BindComponent(
+ const t_cm_instance_handle clientInstance,
+ const char* requiredItfClientName,
+ const t_cm_instance_handle serverInstance,
+ const char* providedItfServerName,
+ t_bool traced,
+ t_nmf_client_id clientId,
+ const char *dataFileTrace) {
+ t_interface_require_description itfRequire;
+ t_interface_provide_description itfProvide;
+ t_bool bindable;
+ t_cm_error error;
+ t_component_instance *client, *server;
+ t_elfdescription *elfhandleTrace = NULL;
+
+ OSAL_LOCK_API();
+
+ /*
+ * Load Elf File
+ */
+ if(dataFileTrace != NULL &&
+ (error = cm_ELF_CheckFile(
+ dataFileTrace,
+ TRUE,
+ &elfhandleTrace)) != CM_OK)
+ goto out;
+
+ client = cm_lookupComponent(clientInstance);
+ server = cm_lookupComponent(serverInstance);
+ // Sanity check
+ if((error = cm_checkValidBinding(client, requiredItfClientName,
+ server, providedItfServerName,
+ &itfRequire, &itfProvide, &bindable)) != CM_OK)
+ goto out;
+
+ // Check that client and server component run on same DSP
+ if (itfRequire.client->template->dspId != itfProvide.server->template->dspId)
+ {
+ error = CM_ILLEGAL_BINDING;
+ goto out;
+ }
+
+ // Check if we really need to bind
+ if(bindable)
+ {
+ if ((error = cm_EEM_ForceWakeup(itfRequire.client->template->dspId)) != CM_OK)
+ goto out;
+
+ /*
+ * Synchronous binding, so no binding component
+ */
+ if(traced)
+ error = cm_bindInterfaceTrace(&itfRequire, &itfProvide, elfhandleTrace);
+ else
+ error = cm_bindInterface(&itfRequire, &itfProvide);
+
+ cm_EEM_AllowSleep(itfRequire.client->template->dspId);
+ }
+
+ cm_registerSingletonBinding(client, &itfRequire, &itfProvide, clientId);
+
+out:
+ cm_ELF_CloseFile(TRUE, elfhandleTrace);
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_UnbindComponent(
+ const t_cm_instance_handle clientInstance,
+ const char* requiredItfClientName,
+ t_nmf_client_id clientId) {
+ t_interface_require_description itfRequire;
+ t_interface_provide_description itfProvide;
+ t_bf_info_ID bfInfoID;
+ t_cm_error error;
+ t_component_instance *client;
+
+ OSAL_LOCK_API();
+
+ client = cm_lookupComponent(clientInstance);
+ // Sanity check
+ if((error = cm_checkValidUnbinding(client, requiredItfClientName,
+ &itfRequire, &itfProvide)) != CM_OK)
+ goto out;
+
+ // Check if this is a Primitive binding
+ bfInfoID = itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfoID;
+ if(bfInfoID != BF_SYNCHRONOUS && bfInfoID != BF_TRACE)
+ {
+ error = CM_ILLEGAL_UNBINDING;
+ goto out;
+ }
+
+ // Check if we really need to unbind
+ if(cm_unregisterSingletonBinding(client, &itfRequire, &itfProvide, clientId))
+ {
+ (void)cm_EEM_ForceWakeup(itfRequire.client->template->dspId);
+
+ if(bfInfoID == BF_SYNCHRONOUS)
+ cm_unbindInterface(&itfRequire);
+ else
+ cm_unbindInterfaceTrace(
+ &itfRequire,
+ (t_trace_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo);
+
+ cm_EEM_AllowSleep(itfRequire.client->template->dspId);
+
+ error = CM_OK;
+ }
+
+out:
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_BindComponentToVoid(
+ const t_cm_instance_handle clientInstance,
+ const char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH],
+ t_nmf_client_id clientId)
+{
+ t_interface_require_description itfRequire;
+ t_bool bindable;
+ t_cm_error error;
+ t_component_instance *client;
+
+ OSAL_LOCK_API();
+
+ client = cm_lookupComponent(clientInstance);
+ // Check invalid binding
+ if((error = cm_checkValidClient(client, requiredItfClientName,
+ &itfRequire, &bindable)) != CM_OK)
+ goto out;
+
+ // Check if we really need to bind
+ if(bindable)
+ {
+ if ((error = cm_EEM_ForceWakeup(itfRequire.client->template->dspId)) != CM_OK)
+ goto out;
+
+ error = cm_bindInterfaceToVoid(&itfRequire);
+
+ cm_EEM_AllowSleep(itfRequire.client->template->dspId);
+ }
+
+ cm_registerSingletonBinding(client, &itfRequire, NULL, clientId);
+
+out:
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_BindComponentAsynchronous(
+ const t_cm_instance_handle clientInstance,
+ const char* requiredItfClientName,
+ const t_cm_instance_handle serverInstance,
+ const char* providedItfServerName,
+ t_uint32 fifosize,
+ t_cm_mpc_memory_type eventMemType,
+ t_nmf_client_id clientId,
+ const char *dataFileSkeletonOrEvent,
+ const char *dataFileStub) {
+ t_interface_require_description itfRequire;
+ t_interface_provide_description itfProvide;
+ t_dsp_memory_type_id dspEventMemType;
+ t_bool bindable;
+ t_cm_error error;
+ t_component_instance *client, *server;
+ t_elfdescription *elfhandleSkeletonOrEvent = NULL;
+ t_elfdescription *elfhandleStub = NULL;
+
+ OSAL_LOCK_API();
+
+ /*
+ * Load Elf File
+ */
+ if(dataFileSkeletonOrEvent != NULL &&
+ (error = cm_ELF_CheckFile(
+ dataFileSkeletonOrEvent,
+ TRUE,
+ &elfhandleSkeletonOrEvent)) != CM_OK)
+ goto out;
+ if(dataFileStub != NULL &&
+ (error = cm_ELF_CheckFile(
+ dataFileStub,
+ TRUE,
+ &elfhandleStub)) != CM_OK)
+ goto out;
+
+ client = cm_lookupComponent(clientInstance);
+ server = cm_lookupComponent(serverInstance);
+ // Check invalid binding
+ if((error = cm_checkValidBinding(client, requiredItfClientName,
+ server, providedItfServerName,
+ &itfRequire, &itfProvide, &bindable)) != CM_OK)
+ goto out;
+
+ switch(eventMemType)
+ {
+ case CM_MM_MPC_TCM24_X:
+ dspEventMemType = INTERNAL_XRAM24;
+ break;
+ case CM_MM_MPC_ESRAM24:
+ dspEventMemType = ESRAM_EXT24;
+ break;
+ case CM_MM_MPC_SDRAM24:
+ dspEventMemType = SDRAM_EXT24;
+ break;
+ default:
+ error = CM_INVALID_PARAMETER;
+ goto out;
+ }
+
+ // Check if we really need to bind
+ if(bindable)
+ {
+ // Create the binding and bind it to the client (or all sub-components clients ....)
+ if (itfRequire.client->template->dspId != itfProvide.server->template->dspId)
+ {
+ if ((error = cm_EEM_ForceWakeup(itfRequire.client->template->dspId)) != CM_OK)
+ goto out;
+ if ((error = cm_EEM_ForceWakeup(itfProvide.server->template->dspId)) != CM_OK)
+ {
+ cm_EEM_AllowSleep(itfRequire.client->template->dspId);
+ goto out;
+ }
+
+ // This is a distribute communication
+ error = cm_bindInterfaceDistributed(
+ &itfRequire,
+ &itfProvide,
+ fifosize,
+ dspEventMemType,
+ elfhandleSkeletonOrEvent,
+ elfhandleStub);
+
+ cm_EEM_AllowSleep(itfRequire.client->template->dspId);
+ cm_EEM_AllowSleep(itfProvide.server->template->dspId);
+ }
+ else
+ {
+ if ((error = cm_EEM_ForceWakeup(itfRequire.client->template->dspId)) != CM_OK)
+ goto out;
+
+ // This is a acynchronous communication
+ error = cm_bindInterfaceAsynchronous(
+ &itfRequire,
+ &itfProvide,
+ fifosize,
+ dspEventMemType,
+ elfhandleSkeletonOrEvent);
+
+ cm_EEM_AllowSleep(itfRequire.client->template->dspId);
+ }
+ }
+
+ cm_registerSingletonBinding(client, &itfRequire, &itfProvide, clientId);
+
+out:
+ cm_ELF_CloseFile(TRUE, elfhandleSkeletonOrEvent);
+ cm_ELF_CloseFile(TRUE, elfhandleStub);
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentAsynchronous(
+ const t_cm_instance_handle instance,
+ const char* requiredItfClientName,
+ t_nmf_client_id clientId) {
+ t_interface_require_description itfRequire;
+ t_interface_provide_description itfProvide;
+ t_bf_info_ID bfInfoID;
+ t_cm_error error;
+ t_component_instance *client;
+
+ OSAL_LOCK_API();
+
+ client = cm_lookupComponent(instance);
+ // Sanity check
+ if((error = cm_checkValidUnbinding(client, requiredItfClientName,
+ &itfRequire, &itfProvide)) != CM_OK)
+ goto out;
+
+ bfInfoID = itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfoID;
+
+ // Check if we really need to unbind
+ if(cm_unregisterSingletonBinding(client, &itfRequire, &itfProvide, clientId))
+ {
+ // Check if this is a Asynchronous binding
+ if(bfInfoID == BF_DSP2DSP)
+ {
+ t_nmf_core_id clientDsp = itfRequire.client->template->dspId;
+ t_nmf_core_id serverDsp = itfProvide.server->template->dspId;
+
+ (void)cm_EEM_ForceWakeup(clientDsp);
+ (void)cm_EEM_ForceWakeup(serverDsp);
+
+ cm_unbindInterfaceDistributed(
+ &itfRequire,
+ (t_mpc2mpc_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo);
+
+ cm_EEM_AllowSleep(clientDsp);
+ cm_EEM_AllowSleep(serverDsp);
+
+ error = CM_OK;
+ }
+ else if(bfInfoID == BF_ASYNCHRONOUS)
+ {
+ t_nmf_core_id clientDsp = itfRequire.client->template->dspId;
+
+ (void)cm_EEM_ForceWakeup(clientDsp);
+
+ cm_unbindInterfaceAsynchronous(
+ &itfRequire,
+ (t_async_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo);
+
+ cm_EEM_AllowSleep(clientDsp);
+
+ error = CM_OK;
+ }
+ else
+ error = CM_ILLEGAL_UNBINDING;
+ }
+
+ out:
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_BindComponentFromCMCore(
+ const t_cm_instance_handle server,
+ const char* providedItfServerName,
+ t_uint32 fifosize,
+ t_cm_mpc_memory_type eventMemType,
+ t_cm_bf_host2mpc_handle *bfHost2mpcHdl,
+ t_nmf_client_id clientId,
+ const char *dataFileSkeleton) {
+ t_interface_provide_description itfProvide;
+ t_dsp_memory_type_id dspEventMemType;
+ t_cm_error error;
+ t_component_instance* component;
+ t_host2mpc_bf_info *bfInfo;
+ t_elfdescription *elfhandleSkeleton = NULL;
+
+ OSAL_LOCK_API();
+
+ /*
+ * Load Elf File
+ */
+ if(dataFileSkeleton != NULL &&
+ (error = cm_ELF_CheckFile(
+ dataFileSkeleton,
+ TRUE,
+ &elfhandleSkeleton)) != CM_OK)
+ goto out;
+
+ component = cm_lookupComponent(server);
+ // Check server validity
+ if((error = cm_checkValidServer(component, providedItfServerName,
+ &itfProvide)) != CM_OK)
+ goto out;
+
+ if ((error = cm_EEM_ForceWakeup(itfProvide.server->template->dspId)) != CM_OK)
+ goto out;
+
+ switch(eventMemType)
+ {
+ case CM_MM_MPC_TCM24_X:
+ dspEventMemType = INTERNAL_XRAM24;
+ break;
+ case CM_MM_MPC_ESRAM24:
+ dspEventMemType = ESRAM_EXT24;
+ break;
+ case CM_MM_MPC_SDRAM24:
+ dspEventMemType = SDRAM_EXT24;
+ break;
+ default:
+ goto out;
+ }
+
+ error = cm_bindComponentFromCMCore(&itfProvide,
+ fifosize,
+ dspEventMemType,
+ elfhandleSkeleton,
+ &bfInfo);
+
+ cm_EEM_AllowSleep(itfProvide.server->template->dspId);
+
+out:
+ cm_ELF_CloseFile(TRUE, elfhandleSkeleton);
+ OSAL_UNLOCK_API();
+
+ if (error == CM_OK) {
+ bfInfo->clientId = clientId;
+ OSAL_LOCK_COM();
+ *bfHost2mpcHdl = cm_addEntry(&Host2MpcBindingTable, bfInfo);
+ if (*bfHost2mpcHdl == 0)
+ error = CM_NO_MORE_MEMORY;
+ OSAL_UNLOCK_COM();
+
+ if (error != CM_OK) {
+ OSAL_LOCK_API();
+ (void)cm_EEM_ForceWakeup(itfProvide.server->template->dspId);
+ cm_unbindComponentFromCMCore(bfInfo);
+ cm_EEM_AllowSleep(itfProvide.server->template->dspId);
+ OSAL_UNLOCK_API();
+ }
+ }
+
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentFromCMCore(
+ t_cm_bf_host2mpc_handle bfHost2mpcId) {
+ t_host2mpc_bf_info* bfInfo;
+ t_nmf_core_id coreId;
+
+ OSAL_LOCK_COM();
+ bfInfo = cm_lookupEntry(&Host2MpcBindingTable, bfHost2mpcId);
+ if (bfInfo)
+ cm_delEntry(&Host2MpcBindingTable, bfHost2mpcId & INDEX_MASK);
+ OSAL_UNLOCK_COM();
+ if (NULL == bfInfo)
+ return CM_INVALID_PARAMETER;
+
+ OSAL_LOCK_API();
+
+ // Check if this is a DSP to Host binding
+ //if(bfInfo->id != BF_HOST2DSP)
+ // return CM_ILLEGAL_UNBINDING;
+ coreId = bfInfo->dspskeleton.skelInstance->template->dspId;
+
+ (void)cm_EEM_ForceWakeup(coreId);
+
+ cm_unbindComponentFromCMCore(bfInfo);
+
+ cm_EEM_AllowSleep(coreId);
+
+ OSAL_UNLOCK_API();
+ return CM_OK;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_BindComponentToCMCore(
+ const t_cm_instance_handle instance,
+ const char *requiredItfClientName,
+ t_uint32 fifosize,
+ t_nmf_mpc2host_handle upLayerThis,
+ const char *dataFileStub,
+ t_cm_bf_mpc2host_handle *mpc2hostId,
+ t_nmf_client_id clientId) {
+ t_interface_require_description itfRequire;
+ t_bool bindable;
+ t_cm_error error;
+ t_component_instance* client;
+ t_elfdescription *elfhandleStub = NULL;
+
+ OSAL_LOCK_API();
+
+ /*
+ * Load Elf File
+ */
+ if(dataFileStub != NULL &&
+ (error = cm_ELF_CheckFile(
+ dataFileStub,
+ TRUE,
+ &elfhandleStub)) != CM_OK)
+ goto out;
+
+ client = cm_lookupComponent(instance);
+ // Check invalid binding
+ if((error = cm_checkValidClient(client, requiredItfClientName,
+ &itfRequire, &bindable)) != CM_OK)
+ goto out;
+
+ // Check if we really need to bind
+ if(bindable)
+ {
+ if ((error = cm_EEM_ForceWakeup(itfRequire.client->template->dspId)) != CM_OK)
+ goto out;
+
+ error = cm_bindComponentToCMCore(
+ &itfRequire,
+ fifosize,
+ upLayerThis,
+ elfhandleStub,
+ (t_mpc2host_bf_info**)mpc2hostId);
+
+ cm_EEM_AllowSleep(itfRequire.client->template->dspId);
+ }
+
+ cm_registerSingletonBinding(client, &itfRequire, NULL, clientId);
+
+out:
+ cm_ELF_CloseFile(TRUE, elfhandleStub);
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_UnbindComponentToCMCore(
+ const t_cm_instance_handle instance,
+ const char *requiredItfClientName,
+ t_nmf_mpc2host_handle *upLayerThis,
+ t_nmf_client_id clientId) {
+ t_interface_require_description itfRequire;
+ t_interface_provide_description itfProvide;
+ t_cm_error error;
+ t_mpc2host_bf_info *bfInfo;
+ t_component_instance* client;
+
+ OSAL_LOCK_API();
+
+ client = cm_lookupComponent(instance);
+ // Sanity check
+ if((error = cm_checkValidUnbinding(client, requiredItfClientName,
+ &itfRequire, &itfProvide)) != CM_OK)
+ goto out;
+
+ // Check if this is a DSP to Host binding
+ if(itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfoID != BF_DSP2HOST)
+ {
+ error = CM_ILLEGAL_UNBINDING;
+ goto out;
+ }
+
+ bfInfo = (t_mpc2host_bf_info*)itfRequire.client->interfaceReferences[itfRequire.requireIndex][itfRequire.collectionIndex].bfInfo;
+
+ // Get client information
+ *upLayerThis = bfInfo->context;
+
+ // Check if we really need to unbind
+ if(cm_unregisterSingletonBinding(client, &itfRequire, &itfProvide, clientId))
+ {
+ (void)cm_EEM_ForceWakeup(itfRequire.client->template->dspId);
+
+ cm_unbindComponentToCMCore(&itfRequire, bfInfo);
+
+ cm_EEM_AllowSleep(itfRequire.client->template->dspId);
+
+ error = CM_OK;
+ }
+out:
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_event_params_handle CM_ENGINE_AllocEvent(t_cm_bf_host2mpc_handle host2mpcId) {
+ t_host2mpc_bf_info* bfInfo;
+ t_event_params_handle eventHandle;
+
+ OSAL_LOCK_COM();
+ bfInfo = cm_lookupEntry(&Host2MpcBindingTable, host2mpcId);
+ if (NULL == bfInfo) {
+ OSAL_UNLOCK_COM();
+ return NULL;
+ }
+
+ if(bfInfo->dspskeleton.skelInstance->interfaceReferences[0][0].instance->state != STATE_RUNNABLE) {
+ ERROR("CM_COMPONENT_NOT_STARTED: Call interface before start component %s<%s>\n",
+ bfInfo->dspskeleton.skelInstance->pathname,
+ bfInfo->dspskeleton.skelInstance->template->name, 0, 0, 0, 0);
+ }
+
+ eventHandle = cm_AllocEvent(bfInfo->fifo);
+
+ OSAL_UNLOCK_COM();
+
+ return eventHandle;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_PushEvent(t_cm_bf_host2mpc_handle host2mpcId, t_event_params_handle h, t_uint32 methodIndex) {
+ t_host2mpc_bf_info* bfInfo;
+ t_cm_error error;
+
+ OSAL_LOCK_COM();
+ bfInfo = cm_lookupEntry(&Host2MpcBindingTable, host2mpcId);
+ if (NULL == bfInfo) {
+ OSAL_UNLOCK_COM();
+ return CM_INVALID_PARAMETER;
+ }
+ error = cm_PushEvent(bfInfo->fifo, h, methodIndex);
+ OSAL_UNLOCK_COM();
+
+ return error;
+}
+
+PUBLIC EXPORT_SHARED void CM_ENGINE_AcknowledgeEvent(t_cm_bf_mpc2host_handle mpc2hostId) {
+ t_mpc2host_bf_info* bfInfo = (t_mpc2host_bf_info*)mpc2hostId;
+
+ //t_dsp2host_bf_info* bfInfo = (t_host2mpc_bf_info*)mpc2hostId;
+ OSAL_LOCK_COM();
+ cm_AcknowledgeEvent(bfInfo->fifo);
+ OSAL_UNLOCK_COM();
+}
+
+/*
+ * Get a reference on a given attribute of a given component
+ */
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_ReadComponentAttribute(
+ const t_cm_instance_handle instance,
+ const char* attrName,
+ t_uint24 *attrValue)
+{
+ t_cm_error error;
+ t_component_instance* component;
+
+ OSAL_LOCK_API();
+
+ component = cm_lookupComponent(instance);
+ if (NULL == component)
+ error = CM_INVALID_COMPONENT_HANDLE;
+ else
+ {
+ if ((error = cm_EEM_ForceWakeup(component->template->dspId)) != CM_OK)
+ goto out;
+
+ // t_uint24 -> t_uint32 possible since we know it same size
+ error = cm_readAttribute(component, attrName, (t_uint32*)attrValue);
+
+ cm_EEM_AllowSleep(component->template->dspId);
+ }
+
+out:
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+/*===============================================================================
+ * Introspection API
+ *===============================================================================*/
+/*
+ * Component
+ */
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentListHeader(
+ const t_nmf_client_id client,
+ t_cm_instance_handle *headerComponent) {
+ t_uint32 i;
+
+ OSAL_LOCK_API();
+
+ *headerComponent = 0;
+ for (i=0; i < ComponentTable.idxMax; i++) {
+ if ((componentEntry(i) != NULL) &&
+ (componentEntry(i)->template->classe != FIRMWARE) &&
+ (domainDesc[componentEntry(i)->domainId].client == client)) {
+ *headerComponent = ENTRY2HANDLE(componentEntry(i), i);;
+ break;
+ }
+ }
+
+ OSAL_UNLOCK_API();
+
+ return CM_OK;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentListNext(
+ const t_nmf_client_id client,
+ const t_cm_instance_handle prevComponent,
+ t_cm_instance_handle *nextComponent){
+ t_cm_error error;
+ t_uint32 i = prevComponent & INDEX_MASK;
+
+ OSAL_LOCK_API();
+
+ // Sanity check
+ if ((i >= ComponentTable.idxMax)
+ || (((unsigned int)componentEntry(i) << INDEX_SHIFT) != (prevComponent & ~INDEX_MASK)))
+ error = CM_INVALID_COMPONENT_HANDLE;
+ else {
+ *nextComponent = 0;
+ for (i++; i < ComponentTable.idxMax; i++) {
+ if ((componentEntry(i) != NULL) &&
+ (componentEntry(i)->template->classe != FIRMWARE) &&
+ (domainDesc[componentEntry(i)->domainId].client == client)) {
+ *nextComponent = ENTRY2HANDLE(componentEntry(i), i);;
+ break;
+ }
+ }
+
+ error = CM_OK;
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentDescription(
+ const t_cm_instance_handle instance,
+ char *templateName,
+ t_uint32 templateNameLength,
+ t_nmf_core_id *coreId,
+ char *localName,
+ t_uint32 localNameLength,
+ t_nmf_ee_priority *priority) {
+ t_component_instance *comp;
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+
+ comp = cm_lookupComponent(instance);
+ // Sanity check
+ if (NULL == comp) {
+ error = CM_INVALID_COMPONENT_HANDLE;
+ } else {
+ cm_StringCopy(
+ templateName,
+ comp->template->name,
+ templateNameLength);
+ *coreId = comp->template->dspId;
+ cm_StringCopy(
+ localName,
+ comp->pathname,
+ localNameLength);
+ if (priority)
+ *priority = comp->priority;
+ error = CM_OK;
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+/*
+ * Require interface
+ */
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterfaceNumber(
+ const t_cm_instance_handle instance,
+ t_uint8 *numberRequiredInterfaces) {
+ t_component_instance *comp;
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+
+ comp = cm_lookupComponent(instance);
+ // Sanity check
+ if (NULL == comp) {
+ error = CM_INVALID_COMPONENT_HANDLE;
+ } else {
+ *numberRequiredInterfaces = comp->template->requireNumber;
+
+ error = CM_OK;
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterface(
+ const t_cm_instance_handle instance,
+ const t_uint8 index,
+ char *itfName,
+ t_uint32 itfNameLength,
+ char *itfType,
+ t_uint32 itfTypeLength,
+ t_cm_require_state *requireState,
+ t_sint16 *collectionSize) {
+ t_component_instance *comp;
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+
+ comp = cm_lookupComponent(instance);
+ // Sanity check
+ if (NULL == comp) {
+ error = CM_INVALID_COMPONENT_HANDLE;
+ } else if(index >= comp->template->requireNumber) {
+ error = CM_NO_SUCH_REQUIRED_INTERFACE;
+ } else {
+ cm_StringCopy(
+ itfName,
+ comp->template->requires[index].name,
+ itfNameLength);
+ cm_StringCopy(
+ itfType,
+ comp->template->requires[index].interface->type,
+ itfTypeLength);
+ if(comp->template->requires[index].requireTypes & COLLECTION_REQUIRE)
+ *collectionSize = comp->template->requires[index].collectionSize;
+ else
+ *collectionSize = -1;
+
+ if(requireState != NULL) {
+ *requireState = 0;
+ if(comp->template->requires[index].requireTypes & COLLECTION_REQUIRE)
+ *requireState |= CM_REQUIRE_COLLECTION;
+ if(comp->template->requires[index].requireTypes & OPTIONAL_REQUIRE)
+ *requireState |= CM_REQUIRE_OPTIONAL;
+ if(comp->template->requires[index].requireTypes & STATIC_REQUIRE)
+ *requireState |= CM_REQUIRE_STATIC;
+ }
+
+ error = CM_OK;
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentRequiredInterfaceBinding(
+ const t_cm_instance_handle instance,
+ const char *itfName,
+ t_cm_instance_handle *server,
+ char *serverItfName,
+ t_uint32 serverItfNameLength) {
+ t_component_instance *comp;
+ t_interface_require_description itfRequire;
+ t_interface_provide_description itfProvide;
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+
+ comp = cm_lookupComponent(instance);
+ // Sanity check
+ if(NULL == comp) {
+ error = CM_INVALID_COMPONENT_HANDLE;
+ } else if ((error = cm_getRequiredInterface(comp, itfName, &itfRequire)) != CM_OK) {
+ // Check if the requiredItfClientName is required by client component
+ } else if ((error = cm_lookupInterface(&itfRequire, &itfProvide)) != CM_OK) {
+ // Check if the requiredItfClientName is required by client component
+ } else {
+ if ((t_cm_instance_handle)itfProvide.server == NMF_HOST_COMPONENT
+ || (t_cm_instance_handle)itfProvide.server == NMF_VOID_COMPONENT)
+ *server = (t_cm_instance_handle)itfProvide.server;
+ else
+ *server = itfProvide.server->instance;
+ if(*server == NMF_HOST_COMPONENT) {
+ cm_StringCopy(
+ serverItfName,
+ "unknown",
+ serverItfNameLength);
+ } else if(*server == NMF_VOID_COMPONENT) {
+ cm_StringCopy(
+ serverItfName,
+ "void",
+ serverItfNameLength);
+ } else if(*server != 0) {
+ cm_StringCopy(
+ serverItfName,
+ itfProvide.server->template->provides[itfProvide.provideIndex].name,
+ serverItfNameLength);
+ if(itfProvide.server->template->provides[itfProvide.provideIndex].provideTypes & COLLECTION_PROVIDE) {
+ int len = cm_StringLength(serverItfName, serverItfNameLength);
+ serverItfName[len++] = '[';
+ if(itfProvide.collectionIndex >= 100)
+ serverItfName[len++] = '0' + (itfProvide.collectionIndex / 100);
+ if(itfProvide.collectionIndex >= 10)
+ serverItfName[len++] = '0' + ((itfProvide.collectionIndex % 100) / 10);
+ serverItfName[len++] = '0' + (itfProvide.collectionIndex % 10);
+ serverItfName[len++] = ']';
+ serverItfName[len] = 0;
+ }
+ }
+
+ error = CM_OK;
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+/*
+ * Provide interface
+ */
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentProvidedInterfaceNumber(
+ const t_cm_instance_handle instance,
+ t_uint8 *numberProvidedInterfaces) {
+ t_component_instance *comp;
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+
+ comp = cm_lookupComponent(instance);
+ // Sanity check
+ if (NULL == comp) {
+ error = CM_INVALID_COMPONENT_HANDLE;
+ } else {
+ *numberProvidedInterfaces = comp->template->provideNumber;
+
+ error = CM_OK;
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentProvidedInterface(
+ const t_cm_instance_handle instance,
+ const t_uint8 index,
+ char *itfName,
+ t_uint32 itfNameLength,
+ char *itfType,
+ t_uint32 itfTypeLength,
+ t_sint16 *collectionSize) {
+ t_component_instance *comp;
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+
+ comp = cm_lookupComponent(instance);
+ // Sanity check
+ if (NULL == comp) {
+ error = CM_INVALID_COMPONENT_HANDLE;
+ } else if(index >= comp->template->provideNumber) {
+ error = CM_NO_SUCH_PROVIDED_INTERFACE;
+ } else {
+ cm_StringCopy(
+ itfName,
+ comp->template->provides[index].name,
+ itfNameLength);
+ cm_StringCopy(
+ itfType,
+ comp->template->provides[index].interface->type,
+ itfTypeLength);
+ if(comp->template->provides[index].provideTypes & COLLECTION_PROVIDE)
+ *collectionSize = comp->template->provides[index].collectionSize;
+ else
+ *collectionSize = -1;
+
+ error = CM_OK;
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+/*
+ * Component Property
+ */
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyNumber(
+ const t_cm_instance_handle instance,
+ t_uint8 *numberProperties) {
+ t_component_instance *comp;
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+
+ comp = cm_lookupComponent(instance);
+ // Sanity check
+ if (NULL == comp) {
+ error = CM_INVALID_COMPONENT_HANDLE;
+ } else {
+ *numberProperties = comp->template->propertyNumber;
+
+ error = CM_OK;
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyName(
+ const t_cm_instance_handle instance,
+ const t_uint8 index,
+ char *propertyName,
+ t_uint32 propertyNameLength) {
+ t_component_instance *comp;
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+
+ comp = cm_lookupComponent(instance);
+ // Sanity check
+ if (NULL == comp) {
+ error = CM_INVALID_COMPONENT_HANDLE;
+ } else if(index >= comp->template->propertyNumber) {
+ error = CM_NO_SUCH_PROPERTY;
+ } else {
+ cm_StringCopy(
+ propertyName,
+ comp->template->properties[index].name,
+ propertyNameLength);
+
+ error = CM_OK;
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetComponentPropertyValue(
+ const t_cm_instance_handle instance,
+ const char *propertyName,
+ char *propertyValue,
+ t_uint32 propertyValueLength)
+{
+ t_component_instance *comp;
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+
+ comp = cm_lookupComponent(instance);
+ if (NULL == comp)
+ error = CM_INVALID_COMPONENT_HANDLE;
+ else
+ {
+ error = cm_getComponentProperty(
+ comp,
+ propertyName,
+ propertyValue,
+ propertyValueLength);
+
+ if(error == CM_NO_SUCH_PROPERTY)
+ ERROR("CM_NO_SUCH_PROPERTY(%s, %s)\n", comp->pathname, propertyName, 0, 0, 0, 0);
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/dspevent.c b/drivers/staging/nmf-cm/cm/engine/component/src/dspevent.c
new file mode 100644
index 00000000000..0c055c7f6f9
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/src/dspevent.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/inc/cm_type.h>
+#include <cm/engine/component/inc/introspection.h>
+#include <cm/engine/communication/inc/communication.h>
+#include <cm/engine/memory/inc/memory.h>
+#include <cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h>
+#include <cm/engine/trace/inc/trace.h>
+#include "../inc/dspevent.h"
+
+
+#define DSP_REMOTE_EVENT_SIZE_IN_BYTE (4*DSP_REMOTE_EVENT_SIZE_IN_DSPWORD)
+#define DSP_REMOTE_EVENT_NEXT_FIELD_OFFSET 0
+#define DSP_REMOTE_EVENT_REACTION_FIELD_OFFSET 1
+#define DSP_REMOTE_EVENT_THIS_FIELD_OFFSET 2
+#define DSP_REMOTE_EVENT_PRIORITY_FIELD_OFFSET 3
+#define DSP_REMOTE_EVENT_DATA_FIELD_OFFSET 4
+
+t_cm_error dspevent_createDspEventFifo(
+ const t_component_instance *pComp,
+ const char* nameOfTOP,
+ t_uint32 fifoNbElem,
+ t_uint32 fifoElemSizeInWord,
+ t_dsp_memory_type_id dspEventMemType,
+ t_memory_handle *pHandle)
+{
+ t_uint32 dspElementAddr;
+ t_uint32 *elemAddr32;
+ int i;
+
+ // Allocate fifo
+ *pHandle = cm_DM_Alloc(pComp->domainId, dspEventMemType, fifoNbElem*fifoElemSizeInWord, CM_MM_ALIGN_2WORDS, TRUE);
+ if(*pHandle == INVALID_MEMORY_HANDLE)
+ return CM_NO_MORE_MEMORY;
+
+ cm_DSP_GetDspAddress(*pHandle, &dspElementAddr);
+
+ elemAddr32 = (t_uint32*)cm_DSP_GetHostLogicalAddress(*pHandle);
+
+ LOG_INTERNAL(2, "\n##### FIFO (dsp event): ARM=0x%x DSP=0x%x\n", elemAddr32, dspElementAddr, 0, 0, 0, 0);
+
+ // Read attribute addr (we assume that variable in XRAM)
+ cm_writeAttribute(pComp, nameOfTOP, dspElementAddr);
+
+ // Initialise the linked list (next...)
+ for (i = 0; i < fifoNbElem - 1; i++)
+ {
+ dspElementAddr += fifoElemSizeInWord;
+
+ /* Write next field */
+ *elemAddr32 = dspElementAddr;
+ /* Write THIS field & priority field */
+ *(volatile t_uint64*)&elemAddr32[DSP_REMOTE_EVENT_THIS_FIELD_OFFSET] =
+ ((t_uint64)pComp->thisAddress | (((t_uint64)pComp->priority) << 32));
+
+ elemAddr32 += fifoElemSizeInWord;
+ }
+
+ /* Last element: Write next field */
+ *elemAddr32 = 0x0 /* NULL */;
+ /* Last element: Write THIS field & priority field */
+ *(volatile t_uint64*)&elemAddr32[DSP_REMOTE_EVENT_THIS_FIELD_OFFSET] =
+ ((t_uint64)pComp->thisAddress | (((t_uint64)pComp->priority) << 32));
+
+ return CM_OK;
+}
+
+
+
+void dspevent_destroyDspEventFifo(t_memory_handle handle)
+{
+ (void)cm_DM_Free(handle, TRUE);
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/initializer.c b/drivers/staging/nmf-cm/cm/engine/component/src/initializer.c
new file mode 100644
index 00000000000..7391669419e
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/src/initializer.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/inc/cm_type.h>
+#include <cm/engine/component/inc/introspection.h>
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+#include <cm/engine/communication/inc/communication.h>
+#include <cm/engine/dsp/inc/dsp.h>
+#include <cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h>
+
+#include <cm/engine/power_mgt/inc/power.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+
+#include <cm/engine/trace/inc/trace.h>
+
+#include "../inc/dspevent.h"
+#include "../inc/initializer.h"
+
+// Since now due to semaphore use call is synchrone so we only need a fifo size of three
+// (due to updateStack + (InstructionCacheLock or InstructionCacheUnlock))
+#define DEFAULT_INITIALIZER_FIFO_SIZE 3
+
+/* private prototype */
+PRIVATE t_cm_error cm_COMP_generic(t_nmf_core_id coreId, t_event_params_handle paramArray, t_uint32 paramNumber, t_uint32 serviceIndex);
+
+/*
+ * This module is tightly coupled with cm_DSP_components one (communication/initializer)
+ */
+static struct {
+ t_nmf_fifo_arm_desc* downlinkFifo;
+ t_nmf_fifo_arm_desc* uplinkFifo;
+ t_memory_handle dspfifoHandle;
+ t_nmf_osal_sem_handle fifoSemHandle;
+} initializerDesc[NB_CORE_IDS];
+
+PUBLIC t_cm_error cm_COMP_INIT_Init(t_nmf_core_id coreId)
+{
+ t_uint32 i;
+ t_cm_error error;
+ t_component_instance *ee;
+ t_dsp_offset sharedVarOffset;
+ t_interface_provide_description itfProvide;
+ t_interface_provide* provide;
+
+ ee = cm_EEM_getExecutiveEngine(coreId)->instance;
+
+ // Get interface description
+ if((error = cm_getProvidedInterface(ee, "service", &itfProvide)) != CM_OK)
+ return error;
+ provide = &ee->template->provides[itfProvide.provideIndex];
+
+
+ if ((error = dspevent_createDspEventFifo(
+ ee, "comms/TOP",
+ DEFAULT_INITIALIZER_FIFO_SIZE,
+ DSP_REMOTE_EVENT_SIZE_IN_DSPWORD,
+ INTERNAL_XRAM24,
+ &initializerDesc[coreId].dspfifoHandle)) != CM_OK)
+ return error;
+
+ /* create fifo semaphore */
+ initializerDesc[coreId].fifoSemHandle = OSAL_CreateSemaphore(DEFAULT_INITIALIZER_FIFO_SIZE);
+ if (initializerDesc[coreId].fifoSemHandle == 0) {
+ dspevent_destroyDspEventFifo(initializerDesc[coreId].dspfifoHandle);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ /* static armTHis initialisation */
+ /*
+ * In the two next fifo_alloc call (1+n) means that we want to manage the hostThis_or_TOP and one method for each params fifos */
+ initializerDesc[coreId].downlinkFifo =
+ fifo_alloc(ARM_CORE_ID, coreId,
+ INIT_COMPONENT_CMD_SIZE, DEFAULT_INITIALIZER_FIFO_SIZE,
+ (1+provide->interface->methodNumber), paramsLocation, extendedFieldLocation, cm_DSP_GetState(coreId)->domainEE);
+ if (initializerDesc[coreId].downlinkFifo == NULL)
+ {
+ OSAL_DestroySemaphore(initializerDesc[coreId].fifoSemHandle);
+ dspevent_destroyDspEventFifo(initializerDesc[coreId].dspfifoHandle);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ initializerDesc[coreId].uplinkFifo =
+ fifo_alloc(coreId, ARM_CORE_ID,
+ INIT_COMPONENT_ACK_SIZE, DEFAULT_INITIALIZER_FIFO_SIZE,
+ (1), paramsLocation, extendedFieldLocation, cm_DSP_GetState(coreId)->domainEE); /* 1 is mandatory to compute internally the indexMask */
+ /* this statement is acceptable only written by skilled man ;) */
+ /* We don't used bcDescRef, since we assume that we don't need params size */
+ if (initializerDesc[coreId].uplinkFifo == NULL)
+ {
+ OSAL_DestroySemaphore(initializerDesc[coreId].fifoSemHandle);
+ fifo_free(initializerDesc[coreId].downlinkFifo);
+ dspevent_destroyDspEventFifo(initializerDesc[coreId].dspfifoHandle);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ cm_writeAttribute(ee, "comms/FIFOcmd", initializerDesc[coreId].downlinkFifo->dspAdress);
+
+ cm_writeAttribute(ee, "comms/FIFOack", initializerDesc[coreId].uplinkFifo->dspAdress);
+
+ sharedVarOffset = cm_getAttributeMpcAddress(ee, "comms/TOP");
+
+ /* HOST->DSP ParamsFifo extended fields initialisation */
+ fifo_params_setSharedField(
+ initializerDesc[coreId].downlinkFifo,
+ 0,
+ (t_shared_field)sharedVarOffset /* TOP DSP Address */
+ );
+ for(i=0; i<provide->interface->methodNumber; i++)
+ {
+ fifo_params_setSharedField(
+ initializerDesc[coreId].downlinkFifo,
+ i + 1,
+ provide->indexes[itfProvide.collectionIndex][i].methodAddresses);
+ }
+
+ /* DSP->HOST ParamsFifo extended fields initialisation */
+ fifo_params_setSharedField(
+ initializerDesc[coreId].uplinkFifo,
+ 0,
+ (t_shared_field)NMF_INTERNAL_USERTHIS
+ );
+
+ return CM_OK;
+}
+
+
+PUBLIC t_cm_error cm_COMP_CallService(
+ int serviceIndex,
+ t_component_instance *pComp,
+ t_uint32 methodAddress) {
+ t_cm_error error;
+ t_uint16 params[INIT_COMPONENT_CMD_SIZE];
+ t_bool isSynchronous = (serviceIndex == NMF_CONSTRUCT_SYNC_INDEX ||
+ serviceIndex == NMF_START_SYNC_INDEX ||
+ serviceIndex == NMF_DESTROY_INDEX)?TRUE:FALSE;
+
+ params[INIT_COMPONENT_CMD_HANDLE_INDEX] = (t_uint16)((unsigned int)pComp & 0xFFFF);
+ params[INIT_COMPONENT_CMD_HANDLE_INDEX+1] = (t_uint16)((unsigned int)pComp >> 16);
+ params[INIT_COMPONENT_CMD_THIS_INDEX] = (t_uint16)(pComp->thisAddress & 0xFFFF);
+ params[INIT_COMPONENT_CMD_THIS_INDEX+1] = (t_uint16)(pComp->thisAddress >> 16);
+ params[INIT_COMPONENT_CMD_METHOD_INDEX] = (t_uint16)(methodAddress & 0xFFFF);
+ params[INIT_COMPONENT_CMD_METHOD_INDEX+1] = (t_uint16)(methodAddress >> 16);
+
+ error = cm_COMP_generic(pComp->template->dspId, params, sizeof(params) / sizeof(t_uint16), serviceIndex);
+
+ if (isSynchronous == TRUE && error == CM_OK) {
+ if (OSAL_SEMAPHORE_WAIT_TIMEOUT(semHandle) != SYNC_OK)
+ error = CM_MPC_NOT_RESPONDING;
+ }
+
+ return error;
+}
+
+PUBLIC void cm_COMP_INIT_Close(t_nmf_core_id coreId)
+{
+ unsigned int i;
+
+ /* wait for semaphore to be sure it would not be touch later on */
+ /* in case of timeout we break and try to clean everythink */
+ for(i = 0; i < DEFAULT_INITIALIZER_FIFO_SIZE; i++) {
+ if (OSAL_SEMAPHORE_WAIT_TIMEOUT(initializerDesc[coreId].fifoSemHandle) != SYNC_OK)
+ break;
+ }
+
+ /* destroy semaphore */
+ OSAL_DestroySemaphore(initializerDesc[coreId].fifoSemHandle);
+
+ /* Unallocate initializerDesc[index].uplinkFifo */
+ /* (who is used in this particular case to store dummy (with no data space (only descriptor)) DSP->HOST params fifo */
+ fifo_free(initializerDesc[coreId].uplinkFifo);
+
+ /* Unallocate initializerDesc[index].downlinkFifo */
+ fifo_free(initializerDesc[coreId].downlinkFifo);
+
+ /* Unallocate initializerDesc[index].dspfifoHandle */
+ dspevent_destroyDspEventFifo(initializerDesc[coreId].dspfifoHandle);
+}
+
+PUBLIC void processAsyncAcknowledge(t_nmf_core_id coreId, t_event_params_handle pParam)
+{
+ cm_AcknowledgeEvent(initializerDesc[coreId].uplinkFifo);
+
+ OSAL_SemaphorePost(initializerDesc[coreId].fifoSemHandle,1);
+}
+
+PUBLIC void processSyncAcknowledge(t_nmf_core_id coreId, t_event_params_handle pParam)
+{
+ cm_AcknowledgeEvent(initializerDesc[coreId].uplinkFifo);
+
+ OSAL_SemaphorePost(initializerDesc[coreId].fifoSemHandle,1);
+ OSAL_SemaphorePost(semHandle,1);
+}
+
+PUBLIC t_cm_error cm_COMP_UpdateStack(
+ t_nmf_core_id coreId,
+ t_uint32 stackSize
+)
+{
+ t_uint16 params[2];
+
+ // Marshall parameter
+ params[0] = (t_uint16)((unsigned int)stackSize & 0xFFFF);
+ params[1] = (t_uint16)((unsigned int)stackSize >> 16);
+
+ return cm_COMP_generic(coreId, params, sizeof(params) / sizeof(t_uint16), NMF_UPDATE_STACK);
+}
+
+PUBLIC t_cm_error cm_COMP_ULPForceWakeup(
+ t_nmf_core_id coreId
+)
+{
+ t_cm_error error;
+
+ error = cm_COMP_generic(coreId, NULL, 0, NMF_ULP_FORCEWAKEUP);
+
+ if (error == CM_OK) {
+ if (OSAL_SEMAPHORE_WAIT_TIMEOUT(semHandle) != SYNC_OK)
+ error = CM_MPC_NOT_RESPONDING;
+ }
+
+ return error;
+}
+
+PUBLIC t_cm_error cm_COMP_ULPAllowSleep(
+ t_nmf_core_id coreId
+)
+{
+ return cm_COMP_generic(coreId, NULL, 0, NMF_ULP_ALLOWSLEEP);
+}
+
+PUBLIC t_cm_error cm_COMP_InstructionCacheLock(
+ t_nmf_core_id coreId,
+ t_uint32 mmdspAddr,
+ t_uint32 mmdspSize
+)
+{
+ t_uint16 params[4];
+ t_uint32 startAddr = cm_DSP_GetState(coreId)->locked_offset;
+ int way;
+
+ for(way = 1; startAddr < mmdspAddr + mmdspSize; startAddr += MMDSP_CODE_CACHE_WAY_SIZE, way++)
+ {
+ if(mmdspAddr < startAddr + MMDSP_CODE_CACHE_WAY_SIZE)
+ {
+ t_cm_error error;
+
+ // Marshall parameter
+ params[0] = (t_uint16)((unsigned int)startAddr & 0xFFFF);
+ params[1] = (t_uint16)((unsigned int)startAddr >> 16);
+ params[2] = (t_uint16)((unsigned int)way & 0xFFFF);
+ params[3] = (t_uint16)((unsigned int)way >> 16);
+
+ if((error = cm_COMP_generic(coreId, params, sizeof(params) / sizeof(t_uint16), NMF_LOCK_CACHE)) != CM_OK)
+ return error;
+ }
+ }
+
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_COMP_InstructionCacheUnlock(
+ t_nmf_core_id coreId,
+ t_uint32 mmdspAddr,
+ t_uint32 mmdspSize
+)
+{
+ t_uint16 params[2];
+ t_uint32 startAddr = cm_DSP_GetState(coreId)->locked_offset;
+ int way;
+
+ for(way = 1; startAddr < mmdspAddr + mmdspSize; startAddr += MMDSP_CODE_CACHE_WAY_SIZE, way++)
+ {
+ if(mmdspAddr < startAddr + MMDSP_CODE_CACHE_WAY_SIZE)
+ {
+ t_cm_error error;
+
+ // Marshall parameter
+ params[0] = (t_uint16)((unsigned int)way & 0xFFFF);
+ params[1] = (t_uint16)((unsigned int)way >> 16);
+
+ if((error = cm_COMP_generic(coreId, params, sizeof(params) / sizeof(t_uint16), NMF_UNLOCK_CACHE)) != CM_OK)
+ return error;
+ }
+ }
+
+ return CM_OK;
+}
+
+/* private method */
+PRIVATE t_cm_error cm_COMP_generic(
+ t_nmf_core_id coreId,
+ t_event_params_handle paramArray,
+ t_uint32 paramNumber,
+ t_uint32 serviceIndex
+)
+{
+ t_event_params_handle _xyuv_data;
+ t_cm_error error;
+ t_uint32 i;
+
+ // wait for an event in fifo
+ if (OSAL_SEMAPHORE_WAIT_TIMEOUT(initializerDesc[coreId].fifoSemHandle) != SYNC_OK)
+ return CM_MPC_NOT_RESPONDING;
+
+ // AllocEvent
+ if((_xyuv_data = cm_AllocEvent(initializerDesc[coreId].downlinkFifo)) == NULL)
+ {
+ ERROR("CM_INTERNAL_FIFO_OVERFLOW: service FIFO full\n", 0, 0, 0, 0, 0, 0);
+ error = CM_INTERNAL_FIFO_OVERFLOW;
+ goto unlock;
+ }
+
+ // Copy param
+ for(i=0;i<paramNumber;i++)
+ _xyuv_data[i] = paramArray[i];
+
+ OSAL_LOCK_COM();
+
+ // Send Command
+ error = cm_PushEventTrace(initializerDesc[coreId].downlinkFifo, _xyuv_data, serviceIndex,0);
+
+unlock:
+ OSAL_UNLOCK_COM();
+
+ return error;
+}
+
diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/instantiater.c b/drivers/staging/nmf-cm/cm/engine/component/src/instantiater.c
new file mode 100644
index 00000000000..04e4aba5410
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/src/instantiater.c
@@ -0,0 +1,818 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/component/inc/instance.h>
+#include <cm/engine/component/inc/bind.h>
+#include <cm/engine/component/inc/initializer.h>
+
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+#include <cm/engine/configuration/inc/configuration_status.h>
+
+#include <cm/engine/dsp/inc/dsp.h>
+
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/trace/inc/xtitrace.h>
+
+#include <cm/engine/memory/inc/domain.h>
+
+#include <cm/engine/utils/inc/string.h>
+#include <cm/engine/utils/inc/mem.h>
+#include <cm/engine/utils/inc/convert.h>
+
+#include <cm/engine/power_mgt/inc/power.h>
+
+
+t_nmf_table ComponentTable; /**< list (table) of components */
+
+static t_uint32 cm_getMaxStackValue(t_component_instance *pComponent);
+static t_uint16 getNumberOfInstance(t_component_instance* component);
+static t_uint16 getNumberOfStart(t_component_instance* component);
+
+
+t_cm_error cm_COMP_Init(void) {
+ t_cm_error error;
+ error = cm_initTable(&ComponentTable);
+ if (error == CM_OK)
+ error = cm_initTable(&Host2MpcBindingTable);
+ return error;
+}
+
+void cm_COMP_Destroy(void) {
+ cm_destroyTable(&ComponentTable);
+ cm_destroyTable(&Host2MpcBindingTable);
+}
+
+/** cm_addComponent - Add an internal handler to the list
+ *
+ * 1. Increase the size of the list if it's full
+ * 2. Search an empty entry
+ * 3. Add the element to the list
+ * 4. Compute and return the "user handle" (= t_cm_instance_handle)
+ */
+static t_cm_instance_handle cm_addComponent(t_component_instance *comp)
+{
+ OSAL_DisableServiceMessages();
+ comp->instance = cm_addEntry(&ComponentTable, comp);
+ OSAL_EnableServiceMessages();
+
+ return comp->instance;
+}
+
+/** cm_delComponent - remove the given component from the list
+ *
+ * 1. Check if the handle is valid
+ * 2. Search the entry and free it
+ */
+static void cm_delComponent(t_component_instance *comp)
+{
+ if (comp == NULL)
+ return;
+
+ OSAL_DisableServiceMessages();
+ cm_delEntry(&ComponentTable, comp->instance & INDEX_MASK);
+ OSAL_EnableServiceMessages();
+}
+
+/** cm_lookupComponent - search the component corresponding to
+ * the component instance.
+ *
+ * 1. Check if the instance is valid
+ * 2. Return a pointer to the component
+ */
+t_component_instance *cm_lookupComponent(const t_cm_instance_handle hdl)
+{
+ return cm_lookupEntry(&ComponentTable, hdl);
+}
+
+static void cm_DestroyComponentMemory(t_component_instance *component)
+{
+ int i;
+
+ /*
+ * Remove instance from list
+ */
+ cm_delComponent(component);
+
+ /*
+ * Destroy instance
+ */
+ {
+ struct t_client_of_singleton* cur = component->clientOfSingleton;
+
+ for( ; cur != NULL ; )
+ {
+ struct t_client_of_singleton* tmp = cur;
+ cur = cur->next;
+
+ OSAL_Free(tmp);
+ }
+ }
+
+ for(i = 0; i < component->template->requireNumber; i++)
+ {
+ OSAL_Free(component->interfaceReferences[i]);
+ }
+
+ cm_StringRelease(component->pathname);
+
+ cm_ELF_FreeInstance(component->template->dspId, component->template->memories, component->memories);
+
+ cm_unloadComponent(component->template);
+ OSAL_Free(component);
+}
+
+/**
+ * Non-Require:
+ * - MMDSP could be sleep (Since we access it only through HSEM)
+ */
+void cm_delayedDestroyComponent(t_component_instance *component) {
+ int i;
+
+ /*
+ * Remove component from load map here
+ */
+ cm_DSPABI_RemoveLoadMap(
+ component->domainId,
+ component->template->name,
+ component->memories,
+ component->pathname,
+ component);
+
+ // Generate XTI/STM trace
+ cm_TRC_traceLoadMap(TRACE_COMPONENT_COMMAND_REMOVE, component);
+
+ /*
+ * disconnect interrupt handler if needed
+ */
+ for(i = 0; i < component->template->provideNumber; i++)
+ {
+ if(component->template->provides[i].interruptLine)
+ {
+ cm_unbindInterfaceStaticInterrupt(component->template->dspId, component->template->provides[i].interruptLine);
+ }
+ }
+
+ /*
+ * Update dsp stack size if needed
+ */
+ if (component->template->minStackSize > MIN_STACK_SIZE)
+ {
+ if (cm_EEM_isStackUpdateNeed(component->template->dspId, component->priority, FALSE, component->template->minStackSize))
+ {
+ t_uint32 newStackValue;
+ t_uint32 maxComponentStackSize;
+
+ maxComponentStackSize = cm_getMaxStackValue(component);
+ cm_EEM_UpdateStack(component->template->dspId, component->priority, maxComponentStackSize, &newStackValue);
+ cm_COMP_UpdateStack(component->template->dspId, newStackValue);
+ }
+ }
+
+ cm_DestroyComponentMemory(component);
+}
+
+/**
+ * Pre-Require:
+ * - MMDSP wakeup (when loading in TCM)
+ */
+t_cm_error cm_instantiateComponent(const char* templateName,
+ t_cm_domain_id domainId,
+ t_nmf_ee_priority priority,
+ const char* pathName,
+ t_elfdescription *elfhandle,
+ t_component_instance** refcomponent)
+{
+ t_nmf_core_id coreId = cm_DM_GetDomainCoreId(domainId);
+ t_dup_char templateNameDup;
+ t_component_template* template;
+ t_component_instance *component;
+ /* coverity[var_decl] */
+ t_cm_error error;
+ int i, j, k;
+
+ *refcomponent = NULL;
+
+ templateNameDup = cm_StringDuplicate(templateName);
+ if(templateNameDup == NULL)
+ return CM_NO_MORE_MEMORY;
+
+ /*
+ * Lookup in template list
+ */
+ template = cm_lookupTemplate(coreId, templateNameDup);
+ if(template != NULL)
+ {
+ if(template->classe == SINGLETON)
+ {
+ // Return same handle for singleton component
+ struct t_client_of_singleton* cl;
+
+ cm_StringRelease(templateNameDup);
+
+ cl = cm_getClientOfSingleton(template->singletonIfAvaliable, TRUE, domainDesc[domainId].client);
+ if(cl == NULL)
+ return CM_NO_MORE_MEMORY;
+ cl->numberOfInstance++;
+
+ *refcomponent = template->singletonIfAvaliable;
+ LOG_INTERNAL(1, "##### Singleton : New handle of %s/%x component on %s provItf=%d#####\n",
+ template->singletonIfAvaliable->pathname, template->singletonIfAvaliable, cm_getDspName(coreId),
+ template->singletonIfAvaliable->providedItfUsedCount, 0, 0);
+ return CM_OK;
+ }
+ }
+
+ // Get the dataFile (identity if already pass as parameter)
+ if((elfhandle = cm_REP_getComponentFile(templateNameDup, elfhandle)) == NULL)
+ {
+ cm_StringRelease(templateNameDup);
+ return CM_COMPONENT_NOT_FOUND;
+ }
+
+ // Load template
+ if((error = cm_loadComponent(templateNameDup, domainId, elfhandle, &template)) != CM_OK)
+ {
+ cm_StringRelease(templateNameDup);
+ return error;
+ }
+
+ // templateNameDup no more used, release it
+ cm_StringRelease(templateNameDup);
+
+ // Allocated component
+ component = (t_component_instance*)OSAL_Alloc_Zero(
+ sizeof(t_component_instance) +
+ sizeof(t_interface_reference*) * template->requireNumber);
+ if(component == NULL)
+ {
+ cm_unloadComponent(template);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ component->interfaceReferences = (t_interface_reference**)((char*)component + sizeof(t_component_instance));
+ component->template = template;
+
+ /*
+ * Update linked list
+ */
+ if (cm_addComponent(component) == 0) {
+ cm_unloadComponent(template);
+ OSAL_Free(component);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ // NOTE: From here use cm_DestroyComponentMemory
+
+ component->pathname = pathName ? cm_StringDuplicate(pathName) : cm_StringReference(anonymousDup);
+ if(component->pathname == NULL)
+ {
+ cm_DestroyComponentMemory(component);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ LOG_INTERNAL(1, "\n##### Instantiate %s/%x (%s) component on %s at priority %d #####\n", component->pathname, component, template->name, cm_getDspName(coreId), priority, 0);
+
+ if((error = cm_ELF_LoadInstance(domainId, elfhandle, template->memories, component->memories)) != CM_OK)
+ {
+ cm_DestroyComponentMemory(component);
+ return error;
+ }
+
+ if((error = cm_ELF_relocatePrivateSegments(
+ component->memories,
+ elfhandle,
+ template)) != CM_OK)
+ {
+ cm_DestroyComponentMemory(component);
+ return error;
+ }
+
+ cm_ELF_FlushInstance(coreId, template->memories, component->memories);
+
+ /*
+ * Create a new component instance
+ */
+ component->domainId = domainId;
+ component->priority = priority;
+ component->thisAddress = 0xFFFFFFFF;
+ component->state = STATE_NONE;
+
+ if(component->template->classe == SINGLETON)
+ { // Return same handle for singleton component
+ struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, TRUE, domainDesc[domainId].client);
+ if(cl == NULL)
+ {
+ cm_DestroyComponentMemory(component);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ cl->numberOfInstance = 1;
+ template->singletonIfAvaliable = component;
+ }
+
+ if(component->memories[template->thisMemory->id] != INVALID_MEMORY_HANDLE)
+ cm_DSP_GetDspAddress(component->memories[template->thisMemory->id], &component->thisAddress);
+ else {
+ // In case of singleton or component without data
+ component->thisAddress = 0;
+ }
+
+ /*
+ * Create empty required interfaces array and set method interface to Panic
+ */
+ for(i = 0; i < template->requireNumber; i++) // For all required interface
+ {
+ component->interfaceReferences[i] =
+ (t_interface_reference*)OSAL_Alloc_Zero(sizeof(t_interface_reference) * template->requires[i].collectionSize);
+ if(component->interfaceReferences[i] == NULL)
+ {
+ cm_DestroyComponentMemory(component);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ for(j = 0; j < template->requires[i].collectionSize; j++) // ... and for each index in collection (set THIS&method for each client)
+ {
+ component->interfaceReferences[i][j].instance = NULL;
+ component->interfaceReferences[i][j].bfInfoID = BF_SYNCHRONOUS; // Just to memorize no Binding component used and unbind ToVoid happy ;-).
+
+ if(template->classe == COMPONENT && template->requires[i].indexes != NULL)
+ {
+ // If component, fill THIS to itself to detect UNBINDED panic with rigth DSP
+ t_interface_require_index *requireindex = &template->requires[i].indexes[j];
+ for(k = 0; k < requireindex->numberOfClient; k++)
+ {
+ t_uint32 *hostAddr;
+
+ hostAddr = (t_uint32*)(
+ cm_DSP_GetHostLogicalAddress(
+ component->memories[requireindex->memories[k].memory->id]) +
+ requireindex->memories[k].offset * requireindex->memories[k].memory->memEntSize);
+ *hostAddr++ = (t_uint32)component->thisAddress;
+ }
+ }
+ }
+ }
+
+ /*
+ * Inform debugger about new component
+ */
+ if ((error = cm_DSPABI_AddLoadMap(
+ domainId,
+ template->name,
+ component->pathname,
+ component->memories,
+ component)) != CM_OK)
+ {
+ cm_DestroyComponentMemory(component);
+ return error;
+ }
+
+ // Generate XTI/STM trace
+ cm_TRC_traceLoadMap(TRACE_COMPONENT_COMMAND_ADD, component);
+
+ // NOTE: From here use cm_delayedDestroyComponent
+
+ /*
+ * Relocate interrupt if this is an interrupt
+ */
+ for(i = 0; i < template->provideNumber; i++)
+ {
+ if(template->provides[i].interruptLine)
+ {
+ if ((error = cm_bindInterfaceStaticInterrupt(coreId,
+ template->provides[i].interruptLine,
+ component,
+ template->provides[i].name)) != CM_OK)
+ {
+ cm_delayedDestroyComponent(component);
+ return error;
+ }
+ }
+ }
+
+ /*
+ * For first instance of a component; Update ee stack size if needed
+ */
+ if(template->classe != FIRMWARE && template->numberOfInstance == 1 && template->minStackSize > MIN_STACK_SIZE)
+ {
+ t_uint32 newStackValue;
+
+ if (cm_EEM_isStackUpdateNeed(template->dspId, priority, TRUE, template->minStackSize))
+ {
+ error = cm_EEM_UpdateStack(template->dspId, priority, template->minStackSize, &newStackValue);
+ if (error != CM_OK)
+ {
+ cm_delayedDestroyComponent(component);
+ return error;
+ }
+ cm_COMP_UpdateStack(template->dspId, newStackValue);
+ }
+ }
+
+
+ /*
+ * For component or first instance
+ */
+ if(template->classe == SINGLETON || template->classe == COMPONENT)
+ {
+ /*
+ * Call init function generated by the compiler (one per .elf)
+ */
+ LOG_INTERNAL(2, "constructor call(s) <%s>\n", template->name, 0, 0, 0, 0, 0);
+ if (cm_DSP_GetState(template->dspId)->state != MPC_STATE_BOOTED)
+ {
+ cm_delayedDestroyComponent(component);
+ return CM_MPC_NOT_RESPONDING;
+ }
+ else if ((error = cm_COMP_CallService(
+ (priority > cm_EEM_getExecutiveEngine(coreId)->instance->priority)?NMF_CONSTRUCT_SYNC_INDEX:NMF_CONSTRUCT_INDEX,
+ component,
+ template->LCCConstructAddress)) != CM_OK)
+ {
+ if (error == CM_MPC_NOT_RESPONDING)
+ ERROR("CM_MPC_NOT_RESPONDING: can't call constructor '%s'\n", component->pathname, 0, 0, 0, 0, 0);
+ cm_delayedDestroyComponent(component);
+ return error;
+ }
+ }
+ else
+ {
+ /* be sure everything is write into memory, not required elsewhere since will be done by cm_COMP_CallService */
+ OSAL_mb();
+ }
+
+ // For firmware; Directly switch to STARTED state, don't need to start it
+ if (template->classe == FIRMWARE)
+ component->state = STATE_RUNNABLE;
+ else
+ component->state = STATE_STOPPED;
+
+ *refcomponent = component;
+ return CM_OK;
+}
+
+struct t_client_of_singleton* cm_getClientOfSingleton(t_component_instance* component, t_bool createdIfNotExist, t_nmf_client_id clientId)
+{
+ struct t_client_of_singleton* cur = component->clientOfSingleton;
+
+ for( ; cur != NULL ; cur = cur->next)
+ {
+ if(cur->clientId == clientId)
+ {
+ return cur;
+ }
+ }
+
+ //if(createdIfNotExist)
+ {
+ cur = OSAL_Alloc(sizeof(struct t_client_of_singleton));
+ if(cur != NULL)
+ {
+ cur->clientId = clientId;
+ cur->next = component->clientOfSingleton;
+ cur->numberOfBind = 0;
+ cur->numberOfInstance= 0;
+ cur->numberOfStart = 0;
+ component->clientOfSingleton = cur;
+ }
+ }
+ return cur;
+}
+
+/**
+ * Non-Require:
+ * - MMDSP could be sleep (Since we access it only through HSEM)
+ */
+t_cm_error cm_startComponent(t_component_instance* component, t_nmf_client_id clientId)
+{
+ t_cm_error error;
+ char value[MAX_PROPERTY_VALUE_LENGTH];
+ int i;
+
+ /*
+ * Special code for SINGLETON handling
+ */
+ if(component->template->classe == SINGLETON)
+ {
+ struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, FALSE, clientId);
+ if(cl != NULL)
+ cl->numberOfStart++;
+ // A singleton could be started twice, thus start it only if first client starter
+ if(getNumberOfStart(component) > 1)
+ return CM_OK;
+
+ // Fall through and start really the singleton.
+ }
+
+ if(component->state == STATE_RUNNABLE)
+ return CM_COMPONENT_NOT_STOPPED;
+
+ // CM_ASSERT component->state == STATE_STOPPED
+
+ /*
+ * Check that all required binding have been binded!
+ */
+ for(i = 0; i < component->template->requireNumber; i++)
+ {
+ int nb = component->template->requires[i].collectionSize, j;
+ for(j = 0; j < nb; j++)
+ {
+ if(component->interfaceReferences[i][j].instance == NULL &&
+ (component->template->requires[i].requireTypes & (OPTIONAL_REQUIRE | INTRINSEC_REQUIRE)) == 0)
+ {
+ ERROR("CM_REQUIRE_INTERFACE_UNBINDED: Required interface '%s'.'%s' binded\n", component->pathname, component->template->requires[i].name, 0, 0, 0, 0);
+ return CM_REQUIRE_INTERFACE_UNBINDED;
+ }
+ }
+ }
+
+ component->state = STATE_RUNNABLE;
+
+ /*
+ * Power on, HW resources if required
+ */
+ if(cm_getComponentProperty(
+ component,
+ "hardware",
+ value,
+ sizeof(value)) == CM_OK)
+ {
+ // The PRCMU seem not supporting the transition of asking HW IP on while DSP in retention
+ // -> Thus force wake up of the MMDSP before asking the transition
+ if ((error = cm_EEM_ForceWakeup(component->template->dspId)) != CM_OK)
+ return error;
+
+ error = cm_PWR_EnableMPC(MPC_PWR_HWIP, component->template->dspId);
+
+ cm_EEM_AllowSleep(component->template->dspId);
+
+ if(error != CM_OK)
+ return error;
+ }
+
+ /*
+ * Call starter if available
+ */
+ if(component->template->LCCStartAddress != 0)
+ {
+ if (cm_DSP_GetState(component->template->dspId)->state != MPC_STATE_BOOTED)
+ {
+ return CM_MPC_NOT_RESPONDING;
+ }
+ else if ((error = cm_COMP_CallService(
+ (component->priority > cm_EEM_getExecutiveEngine(component->template->dspId)->instance->priority)?NMF_START_SYNC_INDEX:NMF_START_INDEX,
+ component,
+ component->template->LCCStartAddress)) != CM_OK)
+ {
+ if (error == CM_MPC_NOT_RESPONDING)
+ ERROR("CM_MPC_NOT_RESPONDING: can't call starter '%s'\n", component->pathname, 0, 0, 0, 0, 0);
+ return error;
+ }
+ }
+
+ return CM_OK;
+}
+
+/**
+ * Non-Require:
+ * - MMDSP could be sleep (Since we access it only through HSEM)
+ */
+t_cm_error cm_stopComponent(t_component_instance* component, t_nmf_client_id clientId)
+{
+ char value[MAX_PROPERTY_VALUE_LENGTH];
+ t_cm_error error = CM_OK;
+
+ /*
+ * Special code for SINGLETON handling
+ */
+ if(component->template->classe == SINGLETON)
+ {
+ struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, FALSE, clientId);
+ if(cl != NULL)
+ cl->numberOfStart--;
+ // A singleton could be started twice, thus stop it only if no more client starter
+ if(getNumberOfStart(component) > 0)
+ return CM_OK;
+
+ // Fall through and stop really the singleton.
+ }
+
+ /*
+ * Component life cycle sanity check
+ */
+ if(component->state == STATE_STOPPED)
+ return CM_COMPONENT_NOT_STARTED;
+
+ // CM_ASSERT component->state == STATE_RUNNABLE
+ component->state = STATE_STOPPED;
+
+ if (cm_DSP_GetState(component->template->dspId)->state != MPC_STATE_BOOTED)
+ {
+ error = CM_MPC_NOT_RESPONDING;
+ }
+ else
+ {
+ /*
+ * Call stopper if available
+ */
+ if(component->template->LCCStopAddress != 0)
+ {
+ if ((error = cm_COMP_CallService(
+ NMF_STOP_INDEX,
+ component,
+ component->template->LCCStopAddress)) != CM_OK)
+ {
+ if (error == CM_MPC_NOT_RESPONDING)
+ ERROR("CM_MPC_NOT_RESPONDING: can't call stopper '%s'\n", component->pathname, 0, 0, 0, 0, 0);
+ }
+ }
+ }
+
+ /*
+ * Power on, HW resources if required
+ */
+ if(cm_getComponentProperty(
+ component,
+ "hardware",
+ value,
+ sizeof(value)) == CM_OK)
+ {
+ cm_PWR_DisableMPC(MPC_PWR_HWIP, component->template->dspId);
+ }
+
+ return error;
+}
+
+t_cm_error cm_destroyInstance(t_component_instance* component, t_destroy_state forceDestroy)
+{
+ int i, j;
+
+ LOG_INTERNAL(1, "\n##### Destroy %s/%x (%s) component on %s #####\n",
+ component->pathname, component, component->template->name, cm_getDspName(component->template->dspId), 0, 0);
+
+ /*
+ * Component life cycle sanity check; do it only when destroying last reference.
+ */
+ if(forceDestroy == DESTROY_NORMAL)
+ {
+ if (component->state == STATE_RUNNABLE)
+ return CM_COMPONENT_NOT_STOPPED;
+
+ // CM_ASSERT component->state == STATE_STOPPED
+
+ // Check that all required binding have been unbound!
+ for(i = 0; i < component->template->requireNumber; i++)
+ {
+ int nb = component->template->requires[i].collectionSize;
+ for(j = 0; j < nb; j++)
+ {
+ if(component->interfaceReferences[i][j].instance != NULL)
+ {
+ ERROR("CM_COMPONENT_NOT_UNBINDED: Required interface %s/%x.%s still binded\n",
+ component->pathname, component, component->template->requires[i].name, 0, 0, 0);
+ return CM_COMPONENT_NOT_UNBINDED;
+ }
+ }
+ }
+
+ // Check that all provided bindings have been unbound!
+ if (component->providedItfUsedCount != 0)
+ {
+ unsigned idx;
+
+ ERROR("CM_COMPONENT_NOT_UNBINDED: Still %d binding to %s/%x provided interface\n",
+ component->providedItfUsedCount, component->pathname, component, 0, 0, 0);
+
+ /* Find which interface is still bound to gracefully print an error message */
+ for (idx=0; idx<ComponentTable.idxMax; idx++)
+ {
+ if ((componentEntry(idx) == NULL) || (componentEntry(idx) == component))
+ continue;
+ for (i = 0; i < componentEntry(idx)->template->requireNumber; i++)
+ {
+ for (j = 0; j < componentEntry(idx)->template->requires[i].collectionSize; j++)
+ {
+ if(componentEntry(idx)->interfaceReferences[i][j].instance == component
+ && component->template->provides[componentEntry(idx)->interfaceReferences[i][j].provideIndex].interruptLine == 0)
+ {
+ ERROR(" -> %s/%x.%s still used by %s/%x.%s\n",
+ component->pathname, component,
+ component->template->provides[componentEntry(idx)->interfaceReferences[i][j].provideIndex].name,
+ componentEntry(idx)->pathname,
+ componentEntry(idx),
+ componentEntry(idx)->template->requires[i].name);
+ }
+ }
+ }
+ }
+
+ return CM_COMPONENT_NOT_UNBINDED;
+ }
+ }
+
+ // Sanity check finished, here, we will do the JOB whatever error
+
+ if (cm_DSP_GetState(component->template->dspId)->state == MPC_STATE_BOOTED)
+ {
+ /*
+ * Call destroy if available
+ */
+ /* Call the destructor only if we don't want to force the destruction */
+ if(forceDestroy != DESTROY_WITHOUT_CHECK_CALL && component->template->LCCDestroyAddress != 0)
+ {
+ if (cm_COMP_CallService(
+ NMF_DESTROY_INDEX,
+ component,
+ component->template->LCCDestroyAddress) != CM_OK)
+ {
+ ERROR("CM_MPC_NOT_RESPONDING: can't call destroy '%s'\n", component->pathname, 0, 0, 0, 0, 0);
+ }
+ }
+ }
+
+ cm_delayedDestroyComponent(component);
+
+ return CM_OK;
+}
+
+/**
+ * Pre-Require:
+ * - MMDSP wakeup (when accessing loadmap)
+ */
+t_cm_error cm_destroyInstanceForClient(t_component_instance* component, t_destroy_state forceDestroy, t_nmf_client_id clientId)
+{
+ /*
+ * Special code for SINGLETON handling
+ */
+ if(component->template->classe == SINGLETON)
+ {
+ struct t_client_of_singleton* cl = cm_getClientOfSingleton(component, FALSE, clientId);
+ int nbinstance;
+ if(cl != NULL)
+ cl->numberOfInstance--;
+
+ // A singleton could be instantiate twice, thus destroy it only if no more client constructor
+ nbinstance = getNumberOfInstance(component);
+ if(nbinstance > 0)
+ {
+ LOG_INTERNAL(1, "##### Singleton : Delete handle of %s/%x (%s) component on %s [%d] provItf=%d #####\n",
+ component->pathname, component, component->template->name, cm_getDspName(component->template->dspId),
+ nbinstance, component->providedItfUsedCount);
+ return CM_OK;
+ }
+
+ // Fall through
+ }
+
+ return cm_destroyInstance(component, forceDestroy);
+}
+
+
+static t_uint32 cm_getMaxStackValue(t_component_instance *pComponent)
+{
+ t_nmf_executive_engine_id executiveEngineId = cm_EEM_getExecutiveEngine(pComponent->template->dspId)->executiveEngineId;
+ t_uint32 res = MIN_STACK_SIZE;
+ unsigned int i;
+
+ for (i=0; i<ComponentTable.idxMax; i++)
+ {
+ if ((componentEntry(i) != NULL) &&
+ (componentEntry(i) != pComponent) &&
+ (pComponent->template->dspId == componentEntry(i)->template->dspId) &&
+ (executiveEngineId == SYNCHRONOUS_EXECUTIVE_ENGINE || componentEntry(i)->priority == pComponent->priority))
+ {
+ if (componentEntry(i)->template->minStackSize > res)
+ res = componentEntry(i)->template->minStackSize;
+ }
+ }
+
+ return res;
+}
+
+static t_uint16 getNumberOfInstance(t_component_instance* component)
+{
+ t_uint16 instanceNumber = 0;
+ struct t_client_of_singleton* cur = component->clientOfSingleton;
+
+ for( ; cur != NULL ; cur = cur->next)
+ {
+ instanceNumber += cur->numberOfInstance;
+ }
+
+ return instanceNumber;
+}
+
+static t_uint16 getNumberOfStart(t_component_instance* component)
+{
+ t_uint16 startNumber = 0;
+ struct t_client_of_singleton* cur = component->clientOfSingleton;
+
+ for( ; cur != NULL ; cur = cur->next)
+ {
+ startNumber += cur->numberOfStart;
+ }
+
+ return startNumber;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/introspection.c b/drivers/staging/nmf-cm/cm/engine/component/src/introspection.c
new file mode 100644
index 00000000000..a4e0a01a242
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/src/introspection.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/component/inc/introspection.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/utils/inc/string.h>
+
+/*
+ *
+ */
+t_cm_error cm_getComponentProperty(
+ const t_component_instance *component,
+ const char *propName,
+ char value[MAX_PROPERTY_VALUE_LENGTH],
+ t_uint32 valueLength){
+ t_component_template* template = component->template;
+ int i;
+
+ for(i = 0; i < template->propertyNumber; i++) {
+ if(cm_StringCompare(template->properties[i].name, propName, MAX_PROPERTY_NAME_LENGTH) == 0) {
+ cm_StringCopy(
+ value,
+ template->properties[i].value,
+ valueLength);
+ return CM_OK;
+ }
+ }
+
+ return CM_NO_SUCH_PROPERTY;
+}
+
+/**
+ *
+ */
+static t_attribute* cm_getAttributeDescriptor(
+ const t_component_instance *component,
+ const char *attrName)
+{
+ int i;
+
+ for(i = 0; i < component->template->attributeNumber; i++)
+ {
+ if(cm_StringCompare(component->template->attributes[i].name, attrName, MAX_ATTRIBUTE_NAME_LENGTH) == 0)
+ {
+ return &component->template->attributes[i];
+ }
+ }
+
+ return NULL;
+}
+
+t_dsp_address cm_getAttributeMpcAddress(
+ const t_component_instance *component,
+ const char *attrName)
+{
+ t_attribute* attribute;
+ t_uint32 dspAddress;
+
+ if((attribute = cm_getAttributeDescriptor(component, attrName)) == NULL)
+ return 0x0;
+
+ cm_DSP_GetDspAddress(component->memories[attribute->memory.memory->id], &dspAddress);
+
+ return (dspAddress +
+ attribute->memory.offset);
+}
+
+t_cm_logical_address cm_getAttributeHostAddr(
+ const t_component_instance *component,
+ const char *attrName)
+{
+ t_attribute* attribute;
+
+ if((attribute = cm_getAttributeDescriptor(component, attrName)) == NULL)
+ return 0x0;
+
+ // TODO JPF: component->template->attributes[i].memory.offset could be converted in byte during load
+ return cm_DSP_GetHostLogicalAddress(component->memories[attribute->memory.memory->id]) +
+ attribute->memory.offset * attribute->memory.memory->memEntSize;
+}
+
+
+t_cm_error cm_readAttribute(
+ const t_component_instance* component,
+ const char* attrName,
+ t_uint32* value)
+{
+ t_attribute* attribute;
+ t_cm_logical_address hostAddr;
+
+ if((attribute = cm_getAttributeDescriptor(component, attrName)) == NULL)
+ {
+ ERROR("CM_NO_SUCH_ATTRIBUTE(%s, %s)\n", component->pathname, attrName, 0, 0, 0, 0);
+ return CM_NO_SUCH_ATTRIBUTE;
+ }
+
+ // TODO JPF: component->template->attributes[i].memory.offset could be converted in byte during load
+ hostAddr = cm_DSP_GetHostLogicalAddress(component->memories[attribute->memory.memory->id]) +
+ attribute->memory.offset * attribute->memory.memory->memEntSize;
+
+ if(attribute->memory.memory->memEntSize != 2)
+ *value = *((t_uint32 *)hostAddr) & ~MASK_BYTE3;
+ else
+ *value = *((t_uint16 *)hostAddr);
+
+ LOG_INTERNAL(3, "cm_readAttribute: [%s:%s, %x]=%x\n",
+ component->pathname, attrName, hostAddr, *value, 0, 0);
+
+ return CM_OK;
+}
+
+t_uint32 cm_readAttributeNoError(
+ const t_component_instance* component,
+ const char* attrName)
+{
+ t_uint32 value;
+
+ if(cm_readAttribute(component, attrName, &value) != CM_OK)
+ value = 0;
+
+ return value;
+}
+
+t_cm_error cm_writeAttribute(
+ const t_component_instance* component,
+ const char* attrName,
+ t_uint32 value)
+{
+ t_attribute* attribute;
+ t_cm_logical_address hostAddr;
+
+ if((attribute = cm_getAttributeDescriptor(component, attrName)) == NULL)
+ {
+ ERROR("CM_NO_SUCH_ATTRIBUTE(%s, %s)\n", component->pathname, attrName, 0, 0, 0, 0);
+ return CM_NO_SUCH_ATTRIBUTE;
+ }
+
+ // TODO JPF: component->template->attributes[i].memory.offset could be converted in byte during load
+ hostAddr = cm_DSP_GetHostLogicalAddress(component->memories[attribute->memory.memory->id]) +
+ attribute->memory.offset * attribute->memory.memory->memEntSize;
+
+ if(attribute->memory.memory->memEntSize != 2)
+ *((t_uint32 *)hostAddr) = value & ~MASK_BYTE3;
+ else
+ *((t_uint16 *)hostAddr) = value;
+
+ /* be sure attribute is write into memory */
+ OSAL_mb();
+
+ LOG_INTERNAL(3, "cm_writeAttribute: [%s:%s, %x]=%x\n",
+ component->pathname, attrName, hostAddr, value, 0, 0);
+
+ return CM_OK;
+}
+
+
+/**
+ *
+ */
+t_dsp_address cm_getFunction(
+ const t_component_instance* component,
+ const char* interfaceName,
+ const char* methodName)
+{
+ t_interface_provide_description itfProvide;
+ t_interface_provide* provide;
+ t_cm_error error;
+ int i;
+
+ // Get interface description
+ if((error = cm_getProvidedInterface(component, interfaceName, &itfProvide)) != CM_OK)
+ return error;
+
+ provide = &component->template->provides[itfProvide.provideIndex];
+
+ for(i = 0; i < provide->interface->methodNumber; i++)
+ {
+ if(cm_StringCompare(provide->interface->methodNames[i], methodName, MAX_INTERFACE_METHOD_NAME_LENGTH) == 0)
+ {
+ return provide->indexes[itfProvide.collectionIndex][i].methodAddresses;
+ }
+ }
+
+ return 0x0;
+}
+
+/**
+ *
+ */
+PRIVATE t_uint8 compareItfName(const char* simplename, const char* complexname, int *collectionIndex) {
+ int i;
+
+ // Search if simplename is a prefix of complexname ??
+ for(i = 0; simplename[i] != 0; i++) {
+ if(simplename[i] != complexname[i])
+ return 1; // NO
+ }
+
+ // YES
+ if(complexname[i] == '[') {
+ // This is a collection
+ int value = 0;
+ i++;
+ if(complexname[i] < '0' || complexname[i] > '9') {
+ return 1;
+ }
+ for(; complexname[i] >= '0' && complexname[i] <= '9'; i++) {
+ value = value * 10 + (complexname[i] - '0');
+ }
+ if(complexname[i++] != ']')
+ return 1;
+ *collectionIndex = value;
+ } else
+ *collectionIndex = -1;
+
+ if(complexname[i] != 0) {
+ // Complexe name has not been fully parsed -> different name
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ *
+ */
+PUBLIC t_cm_error cm_getProvidedInterface(const t_component_instance* server,
+ const char* itfName,
+ t_interface_provide_description *itfProvide){
+ int i;
+
+ for(i = 0; i < server->template->provideNumber; i++)
+ {
+ int collectionIndex;
+ if(compareItfName(server->template->provides[i].name, itfName, &collectionIndex) == 0)
+ {
+ t_interface_provide *provide = &server->template->provides[i];
+ if(collectionIndex >= 0)
+ {
+ if(! (provide->provideTypes & COLLECTION_PROVIDE)) {
+ ERROR("CM_NO_SUCH_PROVIDED_INTERFACE(%s, %s)\n",
+ server->pathname, itfName, 0, 0, 0, 0);
+ goto out;
+ }
+ if(collectionIndex >= provide->collectionSize) {
+ ERROR("CM_NO_SUCH_PROVIDED_INTERFACE(%s, %s): out of range [0..%d[\n",
+ server->pathname, itfName, provide->collectionSize,
+ 0, 0, 0);
+ goto out;
+ }
+ }
+ else
+ {
+ if(provide->provideTypes & COLLECTION_PROVIDE) {
+ ERROR("CM_NO_SUCH_PROVIDED_INTERFACE(%s, %s): interface is a collection [0..%d[\n",
+ server->pathname, itfName, provide->collectionSize,
+ 0, 0, 0);
+ goto out;
+ }
+ collectionIndex = 0;
+ }
+ itfProvide->provideIndex = i;
+ itfProvide->server = server;
+ itfProvide->collectionIndex = collectionIndex;
+ itfProvide->origName = itfName;
+ return CM_OK;
+ }
+ }
+
+ ERROR("CM_NO_SUCH_PROVIDED_INTERFACE(%s, %s)\n", server->pathname, itfName, 0, 0, 0, 0);
+out:
+ itfProvide->provideIndex = 0;
+ itfProvide->server = NULL;
+ itfProvide->collectionIndex = 0;
+ itfProvide->origName = NULL;
+ return CM_NO_SUCH_PROVIDED_INTERFACE;
+}
+
+/**
+ *
+ */
+t_cm_error cm_getRequiredInterface(const t_component_instance* client,
+ const char* itfName,
+ t_interface_require_description *itfRequire){
+ int i;
+
+ for(i = 0; i < client->template->requireNumber; i++) {
+ int collectionIndex;
+ if(compareItfName(client->template->requires[i].name, itfName, &collectionIndex) == 0) {
+ t_interface_require *require = &client->template->requires[i];
+ if(collectionIndex >= 0) {
+ if(! (require->requireTypes & COLLECTION_REQUIRE)) {
+ ERROR("CM_NO_SUCH_REQUIRED_INTERFACE(%s, %s)\n",
+ client->pathname, itfName, 0, 0, 0, 0);
+ return CM_NO_SUCH_REQUIRED_INTERFACE;
+ }
+ if(collectionIndex >= require->collectionSize) {
+ ERROR("CM_NO_SUCH_REQUIRED_INTERFACE(%s, %s): out of range [0..%d[\n",
+ client->pathname, itfName, require->collectionSize,
+ 0, 0, 0);
+ return CM_NO_SUCH_REQUIRED_INTERFACE;
+ }
+ } else {
+ if(require->requireTypes & COLLECTION_REQUIRE) {
+ ERROR("CM_NO_SUCH_REQUIRED_INTERFACE(%s, %s): interface is a collection [0..%d[\n",
+ client->pathname, itfName, require->collectionSize,
+ 0, 0, 0);
+ return CM_NO_SUCH_REQUIRED_INTERFACE;
+ }
+ collectionIndex = 0;
+ }
+ itfRequire->client = client;
+ itfRequire->requireIndex = i;
+ itfRequire->collectionIndex = collectionIndex;
+ itfRequire->origName = itfName;
+ return CM_OK;
+ }
+ }
+
+ ERROR("CM_NO_SUCH_REQUIRED_INTERFACE(%s, %s)\n", client->pathname, itfName, 0, 0, 0, 0);
+ return CM_NO_SUCH_REQUIRED_INTERFACE;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/component/src/loader.c b/drivers/staging/nmf-cm/cm/engine/component/src/loader.c
new file mode 100644
index 00000000000..762f74c4479
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/component/src/loader.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/component/inc/instance.h>
+#include <cm/engine/memory/inc/memory.h>
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+#include <cm/engine/component/inc/bind.h>
+
+#include <cm/engine/utils/inc/string.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/utils/inc/convert.h>
+
+void START(void);
+void END(char*);
+
+#define NHASH 79 //Use a prime number!
+#define MULT 17
+
+static t_component_template *templates[NB_CORE_IDS][NHASH];
+
+static unsigned int templateHash(const char *str)
+{
+ unsigned int h = 0;
+ for(; *str; str++)
+ h = MULT * h + *str;
+ return h % NHASH;
+}
+
+static void templateAdd(t_component_template *template)
+{
+ unsigned int h = templateHash(template->name);
+
+ if(templates[template->dspId][h] != NULL)
+ templates[template->dspId][h]->prev = template;
+ template->next = templates[template->dspId][h];
+ template->prev = NULL;
+ templates[template->dspId][h] = template;
+}
+
+static void templateRemove(t_component_template *template)
+{
+ unsigned int h = templateHash(template->name);
+
+ if(template->prev != NULL)
+ template->prev->next = template->next;
+ if(template->next != NULL)
+ template->next->prev = template->prev;
+ if(template == templates[template->dspId][h])
+ templates[template->dspId][h] = template->next;
+}
+
+
+t_component_template* cm_lookupTemplate(t_nmf_core_id dspId, t_dup_char str)
+{
+ t_component_template *template;
+
+ for(template = templates[dspId][templateHash(str)]; template != NULL; template = template->next)
+ {
+ if(str == template->name)
+ return template;
+ }
+
+ return NULL;
+}
+
+t_bool cm_isComponentOnCoreId(t_nmf_core_id coreId) {
+ t_uint32 i;
+
+ for(i = 0; i < NHASH; i++)
+ {
+ if ((templates[coreId][i] != NULL)
+ && (templates[coreId][i]->classe != FIRMWARE)) // Skip firmware
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static t_dsp_address MemoryToDspAdress(t_component_template *template, t_memory_reference *memory)
+{
+ if(memory->memory == NULL)
+ return (t_dsp_address)memory->offset;
+ else
+ {
+ t_dsp_address address;
+
+ cm_DSP_GetDspAddress(template->memories[memory->memory->id], &address);
+
+ return (t_dsp_address)(address + memory->offset);
+ }
+}
+
+/*
+ * Method callback
+ */
+t_uint32 cm_resolvSymbol(
+ void* context,
+ t_uint32 type,
+ t_dup_char symbolName,
+ char* reloc_addr)
+{
+ t_component_template *template = (t_component_template*)context;
+ t_component_instance* ee = cm_EEM_getExecutiveEngine(template->dspId)->instance;
+ int i, j;
+
+ // Search if this method is provided by EE and resolve it directly
+ for(i = 0; i < ee->template->provideNumber; i++)
+ {
+ t_interface_provide* provide = &ee->template->provides[i];
+
+ for(j = 0; j < provide->interface->methodNumber; j++)
+ {
+ if(provide->interface->methodNames[j] == symbolName)
+ {
+ return provide->indexes[0][j].methodAddresses; // Here we assume no collection provided !!
+ }
+ }
+ }
+
+ // Lookup if the method is statically required, ands delay relocation when bind occur
+ for(i = 0; i < template->requireNumber; i++)
+ {
+ if((template->requires[i].requireTypes & STATIC_REQUIRE) == 0)
+ continue;
+
+ for(j = 0; j < template->requires[i].interface->methodNumber; j++)
+ {
+ if(template->requires[i].interface->methodNames[j] == symbolName)
+ {
+ t_function_relocation* delayedRelocation = (t_function_relocation*)OSAL_Alloc(sizeof(t_function_relocation));
+ if(delayedRelocation == NULL)
+ return 0xFFFFFFFE;
+
+ delayedRelocation->type = type;
+ delayedRelocation->symbol_name = cm_StringReference(symbolName);
+ delayedRelocation->reloc_addr = reloc_addr;
+ delayedRelocation->next = template->delayedRelocation;
+ template->delayedRelocation = delayedRelocation;
+
+ return 0xFFFFFFFF;
+ }
+ }
+ }
+
+ //Symbol not found
+ return 0x0;
+}
+
+/*
+ * Template Management
+ */
+t_cm_error cm_loadComponent(
+ t_dup_char templateName,
+ t_cm_domain_id domainId,
+ t_elfdescription* elfhandle,
+ t_component_template **reftemplate)
+{
+ t_nmf_core_id coreId = cm_DM_GetDomainCoreId(domainId);
+ t_cm_error error;
+ int i, j, k;
+
+ /*
+ * Allocate new component template if first instance
+ */
+ if(*reftemplate == NULL)
+ {
+ t_component_template *template;
+
+ LOG_INTERNAL(1, "\n##### Load template %s on %s #####\n", templateName, cm_getDspName(coreId), 0, 0, 0, 0);
+
+ /*
+ * Sanity check
+ */
+ if(elfhandle->foundedTemplateName != templateName)
+ {
+ ERROR("CM_INVALID_ELF_FILE: template name %s != %s\n", templateName, elfhandle->foundedTemplateName, 0, 0, 0, 0);
+ return CM_INVALID_ELF_FILE;
+ }
+
+ // Alloc & Reset variable in order to use unloadComponent either with partial constructed template
+ *reftemplate = template = (t_component_template*)OSAL_Alloc_Zero(sizeof(t_component_template));
+ if(template == NULL)
+ return CM_NO_MORE_MEMORY;
+ template->name = cm_StringReference(elfhandle->foundedTemplateName);
+
+ // Get information from elfhandle
+ template->descriptionAssociatedWithTemplate = elfhandle->temporaryDescription;
+ template->requireNumber = elfhandle->requireNumber;
+ template->requires = elfhandle->requires;
+ template->attributeNumber = elfhandle->attributeNumber;
+ template->attributes = elfhandle->attributes;
+ template->propertyNumber = elfhandle->propertyNumber;
+ template->properties = elfhandle->properties;
+ template->provideNumber = elfhandle->provideNumber;
+ template->provides = elfhandle->provides;
+ if(template->descriptionAssociatedWithTemplate)
+ {
+ elfhandle->requires = NULL;
+ elfhandle->attributes = NULL;
+ elfhandle->properties = NULL;
+ elfhandle->provides = NULL;
+ }
+
+ // Compute simple information
+ template->numberOfInstance = 1;
+ template->dspId = coreId;
+ LOG_INTERNAL(3, "load<%x> = %s\n", (int)template, template->name, 0, 0, 0, 0);
+ switch(elfhandle->magicNumber) {
+ case MAGIC_COMPONENT:
+ template->classe = COMPONENT;
+ break;
+ case MAGIC_SINGLETON:
+ template->classe = SINGLETON;
+ break;
+ case MAGIC_FIRMWARE:
+ template->classe = FIRMWARE;
+ break;
+ }
+ template->minStackSize = elfhandle->minStackSize;
+
+ /*
+ * Load shared memory from file
+ */
+ // START();
+ if((error = cm_ELF_LoadTemplate(domainId, elfhandle, template->memories)) != CM_OK)
+ goto out;
+ MMDSP_serializeMemories(elfhandle->instanceProperty, &template->codeMemory, &template->thisMemory);
+ // END("cm_ELF_LoadTemplate");
+
+ /*
+ * Copy LCC functions information
+ * Since MMDSP require Constructor & Destructor (for cache flush and debug purpose) to be called
+ * either if not provided by user for allowing defered breakpoint, we use Void method if not provided.
+ */
+ template->LCCConstructAddress = MemoryToDspAdress(template, &elfhandle->memoryForConstruct);
+ template->LCCStartAddress = MemoryToDspAdress(template, &elfhandle->memoryForStart);
+ template->LCCStopAddress = MemoryToDspAdress(template, &elfhandle->memoryForStop);
+ template->LCCDestroyAddress = MemoryToDspAdress(template, &elfhandle->memoryForDestroy);
+ if(template->LCCConstructAddress == 0 && template->classe != FIRMWARE)
+ template->LCCConstructAddress = cm_EEM_getExecutiveEngine(coreId)->voidAddr;
+
+ // Compute provide methodIndex
+ for(i = 0; i < template->provideNumber; i++)
+ {
+ for(j = 0; j < template->provides[i].collectionSize; j++)
+ {
+ for(k = 0; k < template->provides[i].interface->methodNumber; k++)
+ {
+ template->provides[i].indexes[j][k].methodAddresses =
+ MemoryToDspAdress(template, &template->provides[i].indexes[j][k].memory);
+
+ LOG_INTERNAL(2, " [%d, %d] method '%s' @ %x\n",
+ j, k, template->provides[i].interface->methodNames[k], template->provides[i].indexes[j][k].methodAddresses, 0, 0);
+ }
+ }
+ }
+
+ /*
+ * TODO
+
+ if((error = elfhandle->errorOccured) != CM_OK)
+ goto out;
+ */
+
+ // START();
+ if(template->classe != FIRMWARE)
+ {
+ if((error = cm_ELF_relocateSharedSegments(
+ template->memories,
+ elfhandle,
+ template)) != CM_OK)
+ goto out;
+ }
+ // END("cm_ELF_relocateSharedSegments");
+
+ cm_ELF_FlushTemplate(coreId, template->memories);
+
+ templateAdd(template);
+
+ return CM_OK;
+ out:
+ cm_unloadComponent(template);
+ return error;
+ }
+ else
+ {
+ (*reftemplate)->numberOfInstance++;
+ }
+
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_unloadComponent(
+ t_component_template *template)
+{
+ /*
+ * Destroy template if last instance
+ */
+ if(--template->numberOfInstance == 0) {
+ t_function_relocation* reloc;
+
+ LOG_INTERNAL(3, "unload<%s>\n", template->name, 0, 0, 0, 0, 0);
+
+ templateRemove(template);
+
+ // Free delayedRelocation
+ reloc = template->delayedRelocation;
+ while(reloc != NULL)
+ {
+ t_function_relocation *tofree = reloc;
+ reloc = reloc->next;
+ cm_StringRelease(tofree->symbol_name);
+ OSAL_Free(tofree);
+ }
+
+ if(template->descriptionAssociatedWithTemplate)
+ {
+ cm_ELF_ReleaseDescription(
+ template->requireNumber, template->requires,
+ template->attributeNumber, template->attributes,
+ template->propertyNumber, template->properties,
+ template->provideNumber, template->provides);
+ }
+
+ // Free shared memories
+ cm_ELF_FreeTemplate(template->dspId, template->memories);
+
+ cm_StringRelease(template->name);
+
+ OSAL_Free(template);
+ }
+
+ return CM_OK;
+}
+
diff --git a/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration.h b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration.h
new file mode 100644
index 00000000000..7156769bb2d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef __INC_CONFIGURATION_H_
+#define __INC_CONFIGURATION_H_
+
+#include <cm/engine/api/control/configuration_engine.h>
+#include <cm/engine/memory/inc/memory.h>
+#include <inc/nmf-limits.h>
+#include <cm/engine/dsp/inc/dsp.h>
+
+/******************************************************************************/
+/************************ FUNCTIONS PROTOTYPES ********************************/
+/******************************************************************************/
+
+PUBLIC t_cm_error cm_CFG_ConfigureMediaProcessorCore(t_nmf_core_id coreId,
+ t_nmf_executive_engine_id executiveEngineId,
+ t_nmf_semaphore_type_id semaphoreTypeId, t_uint8 nbYramBanks,
+ const t_cm_system_address *mediaProcessorMappingBaseAddr,
+ const t_cm_domain_id eeDomain,
+ t_dsp_allocator_desc* sdramCodeAllocId,
+ t_dsp_allocator_desc* sdramDataAllocId
+ );
+
+PUBLIC t_cm_error cm_CFG_AddMpcSdramSegment(const t_nmf_memory_segment *pDesc,
+ const char *memoryname, t_dsp_allocator_desc **allocDesc);
+
+PUBLIC t_cm_error cm_CFG_CheckMpcStatus(t_nmf_core_id coreId);
+
+void cm_CFG_ReleaseMpc(t_nmf_core_id coreId);
+
+PUBLIC t_cm_error cm_CFG_GetRequiredExecutiveEngineComponentNames(
+ char fileList[][MAX_INTERFACE_TYPE_NAME_LENGTH], t_uint32 listSize);
+
+#endif /* __INC_CONFIGURATION_H_ */
diff --git a/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_status.h b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_status.h
new file mode 100644
index 00000000000..0c75b9c49b0
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_status.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef __INC_CONFIGSTATUS_H_
+#define __INC_CONFIGSTATUS_H
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/utils/inc/string.h>
+
+/*
+ * Variable to active intensive check
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+extern t_sint32 cmIntensiveCheckState;
+
+/*
+ * Variable to active trace level
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+extern t_sint32 cm_debug_level;
+
+/*
+ * Variable to active error break
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+extern t_sint32 cm_error_break;
+
+/*
+ * Variable to activate Ulp
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+extern t_bool cmUlpEnable;
+
+extern t_dup_char anonymousDup, eventDup, skeletonDup, stubDup, traceDup;
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_type.h b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_type.h
new file mode 100644
index 00000000000..abfe452cf78
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/configuration/inc/configuration_type.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Configuration Component Manager API type.
+ */
+#ifndef CONFIGURATION_TYPE_H
+#define CONFIGURATION_TYPE_H
+
+#include <cm/inc/cm_type.h>
+
+/*!
+ * @defgroup t_cm_cmd_id t_cm_cmd_id
+ * \brief Definition of the command ID
+ * \ingroup CM_CONFIGURATION_API
+ *
+ * CM_CMD_XXX designates the command ID used by the \ref CM_SetMode routine.
+ *
+ * \remarks Other command IDs are not yet implemented.
+ */
+
+typedef t_uint32 t_cm_cmd_id; //!< Fake enumeration type \ingroup t_cm_cmd_id
+#define CM_CMD_SYNC ((t_cm_cmd_id)0x01) //!< Synchronize on-going operations (no parameter) \ingroup t_cm_cmd_id
+
+#define CM_CMD_WARM_RESET ((t_cm_cmd_id)0x02) //!< Reset a part of the CM-engine (parameter indicates the part which must be reseted) \ingroup t_cm_cmd_id
+
+#define CM_CMD_PWR_MGR ((t_cm_cmd_id)0x10) //!< Enable/Disable the internal power management module (0=Disable, 1=Enable) \ingroup t_cm_cmd_id
+
+#define CM_CMD_DBG_MODE ((t_cm_cmd_id)0x40) //!< Enable/Disable DEBUG mode, Pwr Mgr is also disabled (0=Disable, 1=Enable) \ingroup t_cm_cmd_id
+
+#define CM_CMD_TRACE_ON ((t_cm_cmd_id)0x41) //!< Enable STM/XTI tracing and force network resetting and dumping \note Since MPC trace will be usable, you can enable them if not \ingroup t_cm_cmd_id
+#define CM_CMD_TRACE_OFF ((t_cm_cmd_id)0x42) //!< Disable STM/XTI tracing \note Since MPC trace will not be usable, you can also disable them \ingroup t_cm_cmd_id
+
+#define CM_CMD_MPC_TRACE_ON ((t_cm_cmd_id)0x50) //!< Enable MPC STM/XTI tracing (param == coreId). \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id
+#define CM_CMD_MPC_TRACE_OFF ((t_cm_cmd_id)0x51) //!< Disable MPC STM/XTI tracing (param == coreId) This is the default configuration. \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id
+
+#define CM_CMD_MPC_PRINT_OFF ((t_cm_cmd_id)0x52) //!< Set to OFF the level of MPC traces (param == coreId) \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id
+#define CM_CMD_MPC_PRINT_ERROR ((t_cm_cmd_id)0x53) //!< Set to ERROR the level of MPC traces param == coreId) \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id
+#define CM_CMD_MPC_PRINT_WARNING ((t_cm_cmd_id)0x54) //!< Set to WARNING the level of MPC traces param == coreId) \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id
+#define CM_CMD_MPC_PRINT_INFO ((t_cm_cmd_id)0x55) //!< Set to INFO the level of MPC traces (param == coreId) \note This command is not execute if execution engine not started on the coreId This is the default configuration. \ingroup t_cm_cmd_id
+#define CM_CMD_MPC_PRINT_VERBOSE ((t_cm_cmd_id)0x56) //!< Set to VERBOSE the level of MPC traces param == coreId) \note This command is not execute if execution engine not started on the coreId \ingroup t_cm_cmd_id
+
+/*!
+ * \brief Define the level of internal CM log traces
+ *
+ * Define the level of internal CM log traces (-1 to 3)
+ * -# <b>-1 </b> all internal LOG/ERROR traces are disabled
+ * -# <b> 0 </b> all internal LOG traces are disabled (<b>default/reset value</b>)
+ * -# <b> 1, 2, 3 </b> Most and most
+ *
+ * \ingroup t_cm_cmd_id
+ */
+#define CM_CMD_TRACE_LEVEL ((t_cm_cmd_id)0x80)
+
+/*!
+ * \brief Enable/Disable intensive internal check
+ *
+ * Enable/Disable intensive internal check (0=Disable, 1=Enable):
+ * - Component handle checking
+ *
+ * Must be used during the integration phase (additional process is time consuming).
+ *
+ * \ingroup t_cm_cmd_id
+ */
+#define CM_CMD_INTENSIVE_CHECK ((t_cm_cmd_id)0x100)
+
+/*!
+ * \brief Enable/Disable ulp mode
+ *
+ * Enable/Disable Ultra Low Power mode.
+ *
+ * \ingroup t_cm_cmd_id
+ */
+#define CM_CMD_ULP_MODE_ON ((t_cm_cmd_id)0x111) //!< Enable ULP mode \ingroup t_cm_cmd_id
+#define CM_CMD_ULP_MODE_OFF ((t_cm_cmd_id)0x110) //!< Deprecated (must be removed in 2.10) !!!
+
+#endif /* CONFIGURATION_TYPE_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration.c b/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration.c
new file mode 100644
index 00000000000..f3242af5da8
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/configuration/inc/configuration.h>
+#include <cm/engine/component/inc/initializer.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+#include <cm/engine/dsp/inc/dsp.h>
+#include <cm/engine/memory/inc/memory.h>
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+#include <cm/engine/semaphores/inc/semaphores.h>
+#include <cm/engine/communication/inc/communication.h>
+#include <cm/engine/utils/inc/string.h>
+#include <cm/engine/repository_mgt/inc/repository_mgt.h>
+#include <inc/nmf-limits.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/memory/inc/domain.h>
+
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/utils/inc/convert.h>
+
+#include <cm/engine/power_mgt/inc/power.h>
+
+t_sint32 cmIntensiveCheckState = 0;
+t_sint32 cm_debug_level = 1;
+t_sint32 cm_error_break = 0;
+t_bool cmUlpEnable = FALSE;
+
+
+#define MAX_EE_NAME_LENGTH 32
+typedef struct {
+ char eeName[MAX_EE_NAME_LENGTH];
+ t_nmf_executive_engine_id executiveEngineId;
+ t_uint32 EEmemoryCount;
+} t_cfg_mpc_desc;
+
+static t_cfg_mpc_desc cfgMpcDescArray[NB_CORE_IDS];
+
+PUBLIC t_cm_error cm_CFG_ConfigureMediaProcessorCore(
+ t_nmf_core_id coreId,
+ t_nmf_executive_engine_id executiveEngineId,
+ t_nmf_semaphore_type_id semaphoreTypeId,
+ t_uint8 nbYramBanks,
+ const t_cm_system_address *mediaProcessorMappingBaseAddr,
+ const t_cm_domain_id eeDomain,
+ t_dsp_allocator_desc *sdramCodeAllocDesc,
+ t_dsp_allocator_desc *sdramDataAllocDesc)
+{
+ /* Process requested configuration (save it) */
+ cfgMpcDescArray[coreId].EEmemoryCount = 0;
+ cfgMpcDescArray[coreId].executiveEngineId = executiveEngineId;
+ /* Build Executive Engine Name */
+ switch(executiveEngineId)
+ {
+ case SYNCHRONOUS_EXECUTIVE_ENGINE:
+ cm_StringCopy(cfgMpcDescArray[coreId].eeName, "synchronous_", MAX_EE_NAME_LENGTH);
+ break;
+ case HYBRID_EXECUTIVE_ENGINE:
+ cm_StringCopy(cfgMpcDescArray[coreId].eeName, "hybrid_", MAX_EE_NAME_LENGTH);
+ break;
+ }
+
+ switch(semaphoreTypeId)
+ {
+ case LOCAL_SEMAPHORES:
+ cm_StringConcatenate(cfgMpcDescArray[coreId].eeName, "lsem", MAX_EE_NAME_LENGTH);
+ break;
+ case SYSTEM_SEMAPHORES:
+ cm_StringConcatenate(cfgMpcDescArray[coreId].eeName, "hsem", MAX_EE_NAME_LENGTH);
+ break;
+ }
+
+ cm_SEM_InitMpc(coreId, semaphoreTypeId);
+
+ return cm_DSP_Add(coreId, nbYramBanks, mediaProcessorMappingBaseAddr, eeDomain, sdramCodeAllocDesc, sdramDataAllocDesc);
+}
+
+// TODO JPF: Move in dsp.c
+PUBLIC t_cm_error cm_CFG_AddMpcSdramSegment(const t_nmf_memory_segment *pDesc, const char* memoryname, t_dsp_allocator_desc **allocDesc)
+{
+ t_dsp_allocator_desc *desc;
+ if ( (pDesc == NULL) ||
+ ((pDesc->systemAddr.logical & CM_MM_ALIGN_64BYTES) != 0) )
+ return CM_INVALID_PARAMETER;
+
+ //TODO, juraj, the right place and way to do this?
+ desc = (t_dsp_allocator_desc*)OSAL_Alloc(sizeof (t_dsp_allocator_desc));
+ if (desc == 0)
+ return CM_NO_MORE_MEMORY;
+
+ desc->allocDesc = cm_MM_CreateAllocator(pDesc->size, 0, memoryname);
+ if (desc->allocDesc == 0) {
+ OSAL_Free(desc);
+ return CM_NO_MORE_MEMORY;
+ }
+ desc->baseAddress = pDesc->systemAddr;
+ desc->referenceCounter = 0;
+
+ *allocDesc = desc;
+
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_CFG_CheckMpcStatus(t_nmf_core_id coreId)
+{
+ t_cm_error error;
+
+ if (cm_DSP_GetState(coreId)->state == MPC_STATE_BOOTABLE)
+ {
+ /* Allocate coms fifo for a given MPC */
+ if ((error = cm_COM_AllocateMpc(coreId)) != CM_OK)
+ return error;
+
+ /* Launch EE */
+ if ((error = cm_EEM_Init(coreId,
+ cfgMpcDescArray[coreId].eeName,
+ cfgMpcDescArray[coreId].executiveEngineId)) != CM_OK)
+ {
+ cm_COM_FreeMpc(coreId);
+ return error;
+ }
+
+ /* Initialize coms fifo for a given MPC */
+ cm_COM_InitMpc(coreId);
+
+ /* Initialisation of the dedicated communication channel for component initialization */
+ if((error = cm_COMP_INIT_Init(coreId)) != CM_OK)
+ {
+ cm_EEM_Close(coreId);
+ cm_COM_FreeMpc(coreId);
+ return error;
+ }
+
+ cfgMpcDescArray[coreId].EEmemoryCount = cm_PWR_GetMPCMemoryCount(coreId);
+
+ if(cmUlpEnable)
+ {
+ // We have finish boot, allow MMDSP to go in auto idle
+ cm_EEM_AllowSleep(coreId);
+ }
+ }
+
+ if (cm_DSP_GetState(coreId)->state != MPC_STATE_BOOTED)
+ return CM_MPC_NOT_INITIALIZED;
+
+ return CM_OK;
+}
+
+void cm_CFG_ReleaseMpc(t_nmf_core_id coreId)
+{
+ t_uint32 memoryCount = cm_PWR_GetMPCMemoryCount(coreId);
+
+ // If No more memory and no more component (to avoid switch off in case of component using no memory)
+ if(
+ cm_PWR_GetMode() == NORMAL_PWR_MODE &&
+ memoryCount != 0 /* Just to see if there is something */ &&
+ memoryCount == cfgMpcDescArray[coreId].EEmemoryCount &&
+ cm_isComponentOnCoreId(coreId) == FALSE)
+ {
+ LOG_INTERNAL(1, "\n##### Shutdown %s #####\n", cm_getDspName(coreId), 0, 0, 0, 0, 0);
+
+ (void)cm_EEM_ForceWakeup(coreId);
+
+ /* remove ee from load map here */
+ cm_COMP_INIT_Close(coreId);
+ cm_EEM_Close(coreId);
+ cm_COM_FreeMpc(coreId);
+
+ cfgMpcDescArray[coreId].EEmemoryCount = 0; // For debug purpose
+ }
+}
+
+/****************************************************************************/
+/* NAME: cm_CFG_GetRequiredExecutiveEngineComponentNames( */
+/* char *fileList[], */
+/* t_uint32 *listSize */
+/* ) */
+/*--------------------------------------------------------------------------*/
+/* DESCRIPTION: retrieves the names of the required Executive Engine */
+/* component(s) to load. */
+/****************************************************************************/
+PUBLIC t_cm_error cm_CFG_GetRequiredExecutiveEngineComponentNames(
+ char fileList[][MAX_INTERFACE_TYPE_NAME_LENGTH],
+ t_uint32 listSize)
+{
+ t_nmf_core_id coreId;
+ t_uint32 nb=0;
+
+ for (coreId = ARM_CORE_ID; coreId <= LAST_CORE_ID; coreId++)
+ {
+ if ((cm_DSP_GetState(coreId)->state == MPC_STATE_BOOTABLE)
+ && (cm_REP_lookupComponent(cfgMpcDescArray[coreId].eeName, NULL) != CM_OK))
+ {
+ /* Check if we didn't already register this name in the list */
+ int j, present=0;
+ for (j=0; j<nb; j++) {
+ if (cm_StringCompare(cfgMpcDescArray[coreId].eeName, fileList[j], MAX_EE_NAME_LENGTH) == 0) {
+ present = 1;
+ break;
+ }
+ }
+ if (present)
+ continue;
+ if (nb >= listSize)
+ return CM_NO_MORE_MEMORY;
+ cm_StringCopy(fileList[nb], cfgMpcDescArray[coreId].eeName, MAX_EE_NAME_LENGTH);
+ nb++;
+ }
+ }
+ return CM_OK;
+}
+
diff --git a/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration_wrapper.c b/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration_wrapper.c
new file mode 100644
index 00000000000..34789752b7d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/configuration/src/configuration_wrapper.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/api/configuration_engine.h>
+#include <cm/engine/communication/inc/communication.h>
+#include <cm/engine/memory/inc/memory.h>
+#include <cm/engine/memory/inc/chunk_mgr.h>
+#include <cm/engine/repository_mgt/inc/repository_mgt.h>
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+#include <cm/engine/dsp/inc/dsp.h>
+#include <cm/engine/semaphores/inc/semaphores.h>
+#include <cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+#include <cm/engine/configuration/inc/configuration.h>
+#include <cm/engine/power_mgt/inc/power.h>
+#include <cm/engine/utils/inc/string.h>
+#include <cm/engine/component/inc/introspection.h>
+#include <cm/engine/component/inc/bind.h>
+#include <cm/engine/memory/inc/domain.h>
+#include <cm/engine/api/executive_engine_mgt_engine.h>
+
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/trace/inc/xtitrace.h>
+
+t_dup_char anonymousDup, eventDup, skeletonDup, stubDup, traceDup;
+
+PUBLIC t_cm_error CM_ENGINE_Init(
+ const t_nmf_hw_mapping_desc *pNmfHwMappingDesc,
+ const t_nmf_config_desc *pNmfConfigDesc
+ )
+{
+ t_cm_error error;
+
+ // The purpose of that is just to not free/unfree some String frequently used
+ anonymousDup = cm_StringDuplicate("anonymous");
+ eventDup = cm_StringDuplicate("event");
+ skeletonDup = cm_StringDuplicate("skeleton");
+ stubDup = cm_StringDuplicate("stub");
+ traceDup = cm_StringDuplicate("trace");
+
+ if ((
+ error = cm_OSAL_Init()
+ ) != CM_OK) { return error; }
+
+ if ((
+ error = cm_COMP_Init()
+ ) != CM_OK) { return error; }
+
+ if ((
+ error = cm_PWR_Init()
+ ) != CM_OK) { return error; }
+
+ cm_TRC_traceReset();
+
+ if ((
+ error = cm_DM_Init()
+ ) != CM_OK) {return error; }
+
+ if ((
+ error = cm_SEM_Init(&pNmfHwMappingDesc->hwSemaphoresMappingBaseAddr)
+ ) != CM_OK) { return error; }
+
+ if ((error = cm_COM_Init(pNmfConfigDesc->comsLocation)) != CM_OK)
+ return error;
+
+ cm_DSP_Init(&pNmfHwMappingDesc->esramDesc);
+
+ return CM_OK;
+}
+
+PUBLIC void CM_ENGINE_Destroy(void)
+{
+ t_component_instance *instance;
+ t_cm_error error;
+ t_uint32 i;
+
+ /* PP: Well, on Linux (and probably on Symbian too), this is called when driver is removed
+ * => the module (driver) can't be removed if there are some pending clients
+ * => all remaining components should have been destroyed in CM_ENGINE_FlushClient()
+ * => So, if we found some components here, we are in BIG trouble ...
+ */
+ /* First, stop all remaining components */
+ for (i=0; i<ComponentTable.idxMax; i++)
+ {
+ t_nmf_client_id clientId;
+
+ if ((instance = componentEntry(i)) == NULL)
+ continue;
+ clientId = domainDesc[instance->domainId].client;
+ LOG_INTERNAL(0, "Found a remaining component %s (%s) when destroying the CM !!!\n", instance->pathname, instance->template->name, 0, 0, 0, 0);
+ if (/* skip EE */
+ (instance->template->classe == FIRMWARE) ||
+ /* Skip all binding components */
+ (cm_StringCompare(instance->template->name, "_ev.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_st.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_sk.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_tr.", 4) == 0))
+ continue;
+
+ /*
+ * Special code for SINGLETON handling
+ */
+ if(instance->template->classe == SINGLETON)
+ {
+ struct t_client_of_singleton* cl = instance->clientOfSingleton;
+
+ clientId = instance->clientOfSingleton->clientId;
+ for( ; cl != NULL ; cl = cl->next)
+ {
+ if(cl == instance->clientOfSingleton)
+ {
+ cl->numberOfStart = 1; // == 1 since it will go to 0 in cm_stopComponent
+ cl->numberOfInstance = 1; // == 1 since it will go to 0 in cm_destroyInstanceForClient
+ }
+ else
+ {
+ cl->numberOfStart = 0;
+ cl->numberOfInstance = 0;
+ }
+ cl->numberOfBind = 0;
+ }
+ }
+
+ // Stop the component
+ error = cm_stopComponent(instance, clientId);
+ if (error != CM_OK && error != CM_COMPONENT_NOT_STARTED)
+ LOG_INTERNAL(0, "Error stopping component %s/%x (%s, error=%d, client=%u)\n", instance->pathname, instance, instance->template->name, error, clientId, 0);
+
+ // Destroy dependencies
+ cm_destroyRequireInterface(instance, clientId);
+ }
+
+ /* Destroy all remaining components */
+ for (i=0; i<ComponentTable.idxMax; i++)
+ {
+ t_nmf_client_id clientId;
+
+ if ((instance = componentEntry(i)) == NULL)
+ continue;
+ clientId = domainDesc[instance->domainId].client;
+
+ if (/* skip EE */
+ (instance->template->classe == FIRMWARE) ||
+ /* Skip all binding components */
+ (cm_StringCompare(instance->template->name, "_ev.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_st.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_sk.", 4) == 0) ||
+ (cm_StringCompare(instance->template->name, "_tr.", 4) == 0)) {
+ continue;
+ }
+
+ if(instance->template->classe == SINGLETON)
+ {
+ clientId = instance->clientOfSingleton->clientId;
+ }
+
+ // Destroy the component
+ error = cm_destroyInstanceForClient(instance, DESTROY_WITHOUT_CHECK, clientId);
+
+ if (error != CM_OK)
+ {
+ /* FIXME : add component name instance in log message but need to make a copy before cm_flushComponent()
+ * because it's no more available after.
+ */
+ LOG_INTERNAL(0, "Error flushing component (error=%d, client=%u)\n", error, clientId, 0, 0, 0, 0);
+ }
+ }
+
+ /* This will power off all ressources and destroy EE */
+ cm_PWR_SetMode(NORMAL_PWR_MODE);
+ cm_DSP_Destroy();
+ cm_DM_Destroy();
+ /* Nothing to do about SEM */
+ //cm_MM_Destroy();
+ cm_REP_Destroy();
+ cm_COMP_Destroy();
+ cm_OSAL_Destroy();
+
+ cm_StringRelease(traceDup);
+ cm_StringRelease(stubDup);
+ cm_StringRelease(skeletonDup);
+ cm_StringRelease(eventDup);
+ cm_StringRelease(anonymousDup);
+}
+
+PUBLIC t_cm_error CM_ENGINE_ConfigureMediaProcessorCore(
+ t_nmf_core_id coreId,
+ t_nmf_executive_engine_id executiveEngineId,
+ t_nmf_semaphore_type_id semaphoreTypeId,
+ t_uint8 nbYramBanks,
+ const t_cm_system_address *mediaProcessorMappingBaseAddr,
+ const t_cm_domain_id eeDomain,
+ const t_cfg_allocator_id sdramCodeAllocId,
+ const t_cfg_allocator_id sdramDataAllocId
+ )
+{
+ return cm_CFG_ConfigureMediaProcessorCore(
+ coreId,
+ executiveEngineId,
+ semaphoreTypeId,
+ nbYramBanks,
+ mediaProcessorMappingBaseAddr,
+ eeDomain,
+ (t_dsp_allocator_desc*)sdramCodeAllocId,
+ (t_dsp_allocator_desc*)sdramDataAllocId
+ );
+}
+
+PUBLIC t_cm_error CM_ENGINE_AddMpcSdramSegment(
+ const t_nmf_memory_segment *pDesc,
+ t_cfg_allocator_id *id,
+ const char *memoryname
+ )
+{
+ return cm_CFG_AddMpcSdramSegment(pDesc, memoryname == NULL ? "" : memoryname, (t_dsp_allocator_desc**)id);
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_SetMode(t_cm_cmd_id aCmdID, t_sint32 aParam)
+{
+ t_cm_error error = CM_OK;
+ int i;
+
+ OSAL_LOCK_API();
+
+ switch(aCmdID) {
+ case CM_CMD_DBG_MODE:
+ cm_PWR_SetMode(( aParam==1 ) ? DISABLE_PWR_MODE : NORMAL_PWR_MODE);
+ switch(cm_PWR_GetMode())
+ {
+ case NORMAL_PWR_MODE:
+ // Release the MPC (which will switch it off if no more used)
+ for (i=FIRST_MPC_ID; i<NB_CORE_IDS; i++)
+ {
+ cm_CFG_ReleaseMpc(i);
+ }
+ break;
+ case DISABLE_PWR_MODE:
+ // Force the load of the EE if not already done.
+ for (i=FIRST_MPC_ID; i<NB_CORE_IDS;i++)
+ {
+ if((error = cm_CFG_CheckMpcStatus(i)) != CM_OK)
+ break;
+ }
+ break;
+ }
+ break;
+ case CM_CMD_TRACE_LEVEL:
+ if (aParam<-1) cm_debug_level = -1;
+ else cm_debug_level = aParam;
+ break;
+ case CM_CMD_INTENSIVE_CHECK:
+ cmIntensiveCheckState = aParam;
+ break;
+
+ case CM_CMD_TRACE_ON:
+ cm_trace_enabled = TRUE;
+ cm_TRC_Dump();
+ break;
+ case CM_CMD_TRACE_OFF:
+ cm_trace_enabled = FALSE;
+ break;
+
+ case CM_CMD_MPC_TRACE_ON:
+ cm_EEM_setTraceMode((t_nmf_core_id)aParam, 1);
+ break;
+ case CM_CMD_MPC_TRACE_OFF:
+ cm_EEM_setTraceMode((t_nmf_core_id)aParam, 0);
+ break;
+
+ case CM_CMD_MPC_PRINT_OFF:
+ cm_EEM_setPrintLevel((t_nmf_core_id)aParam, 0);
+ break;
+ case CM_CMD_MPC_PRINT_ERROR:
+ cm_EEM_setPrintLevel((t_nmf_core_id)aParam, 1);
+ break;
+ case CM_CMD_MPC_PRINT_WARNING:
+ cm_EEM_setPrintLevel((t_nmf_core_id)aParam, 2);
+ break;
+ case CM_CMD_MPC_PRINT_INFO:
+ cm_EEM_setPrintLevel((t_nmf_core_id)aParam, 3);
+ break;
+ case CM_CMD_MPC_PRINT_VERBOSE:
+ cm_EEM_setPrintLevel((t_nmf_core_id)aParam, 4);
+ break;
+
+ case CM_CMD_ULP_MODE_ON:
+ cmUlpEnable = TRUE;
+ break;
+
+ default:
+ error = CM_INVALID_PARAMETER;
+ break;
+ }
+
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
diff --git a/drivers/staging/nmf-cm/cm/engine/dsp/inc/dsp.h b/drivers/staging/nmf-cm/cm/engine/dsp/inc/dsp.h
new file mode 100644
index 00000000000..5cd65289ff8
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/dsp/inc/dsp.h
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief DSP abstraction layer
+ *
+ * \defgroup DSP_INTERNAL Private DSP Abstraction Layer API.
+ *
+ */
+#ifndef __INC_CM_DSP_H
+#define __INC_CM_DSP_H
+
+#include <cm/inc/cm_type.h>
+#include <share/inc/nmf.h>
+#include <cm/engine/memory/inc/domain_type.h>
+#include <cm/engine/memory/inc/memory.h>
+#include <share/semaphores/inc/semaphores.h>
+#include <cm/engine/memory/inc/remote_allocator.h>
+
+
+#define SxA_NB_BLOCK_RAM 8 /*32kworks (24-bit) */
+
+#define SxA_LOCKED_WAY 1
+
+/*
+ * Type defintion to handle dsp offset in word
+ */
+typedef t_uint32 t_dsp_offset;
+
+typedef t_uint32 t_dsp_address;
+
+typedef enum {
+ DSP2ARM_IRQ_0,
+ DSP2ARM_IRQ_1
+} t_mpc2host_irq_num;
+
+typedef enum {
+ ARM2DSP_IRQ_0,
+ ARM2DSP_IRQ_1,
+ ARM2DSP_IRQ_2,
+ ARM2DSP_IRQ_3
+} t_host2mpc_irq_num;
+
+typedef enum {
+ INTERNAL_XRAM24 = 0, /* 24-bit XRAM */
+ INTERNAL_XRAM16 = 1, /* 16-bit XRAM */
+ INTERNAL_YRAM24 = 2, /* 24-bit YRAM */
+ INTERNAL_YRAM16 = 3, /* 16-bit YRAM */
+ SDRAM_EXT24 = 4, /* 24-bit external "X" memory */
+ SDRAM_EXT16 = 5, /* 16-bit external "X" memory */
+ ESRAM_EXT24 = 6, /* ESRAM24 */
+ ESRAM_EXT16 = 7, /* ESRAM16 */
+ SDRAM_CODE = 8, /* Program memory */
+ ESRAM_CODE = 9, /* ESRAM code */
+ LOCKED_CODE = 10, /* For way locking */
+ NB_DSP_MEMORY_TYPE,
+ DEFAULT_DSP_MEM_TYPE = MASK_ALL16
+} t_dsp_memory_type_id;
+
+typedef struct {
+ t_cm_allocator_desc *allocDesc;
+ t_cm_system_address baseAddress;
+ t_uint32 referenceCounter;
+} t_dsp_allocator_desc;
+
+typedef struct {
+ t_cm_system_address base;
+ t_uint32 size;
+} t_dsp_segment;
+
+typedef enum {
+#if defined(__STN_8500) && (__STN_8500 > 10)
+ SDRAM_CODE_EE,
+ SDRAM_CODE_USER,
+ SDRAM_DATA_EE,
+ SDRAM_DATA_USER,
+ NB_MIGRATION_SEGMENT,
+ ESRAM_CODE_EE = NB_MIGRATION_SEGMENT,
+ ESRAM_CODE_USER,
+ ESRAM_DATA_EE,
+ ESRAM_DATA_USER,
+#else
+ SDRAM_CODE_EE,
+ SDRAM_DATA_EE,
+ ESRAM_CODE_EE,
+ ESRAM_DATA_EE,
+#endif
+ NB_DSP_SEGMENT_TYPE
+} t_dsp_segment_type;
+
+typedef struct {
+ t_dsp_segment_type segmentType;
+ t_uint32 baseOffset;
+} t_dsp_address_info;
+
+typedef enum {
+ MPC_STATE_UNCONFIGURED,
+ MPC_STATE_BOOTABLE,
+ MPC_STATE_BOOTED,
+ MPC_STATE_PANIC,
+} t_dsp_state;
+
+typedef struct {
+ t_dsp_state state;
+ t_uint8 nbYramBank;
+ t_cm_domain_id domainEE;
+ t_dsp_allocator_desc *allocator[NB_DSP_MEMORY_TYPE];
+ t_dsp_segment segments[NB_DSP_SEGMENT_TYPE];
+ t_uint32 yram_offset;
+ t_uint32 yram_size;
+ t_uint32 locked_offset;
+ t_uint32 locked_size;
+} t_dsp_desc;
+
+typedef struct {
+ t_nmf_core_id coreId;
+ t_dsp_memory_type_id memType; // Index in MPC desc allocator
+ t_cm_allocator_desc *alloc;
+} t_dsp_chunk_info;
+
+PUBLIC const t_dsp_desc* cm_DSP_GetState(t_nmf_core_id coreId);
+PUBLIC void cm_DSP_SetStatePanic(t_nmf_core_id coreId);
+
+PUBLIC void cm_DSP_Init(const t_nmf_memory_segment *pEsramDesc);
+PUBLIC void cm_DSP_Destroy(void);
+
+/*!
+ * \brief Initialize the memory segments management of a given MPC
+ *
+ * \param[in] coreId Identifier of the DSP to initialize
+ * \param[in] pDspMapDesc DSP mapping into host space
+ * \param[in] memConf configuration of the DSP memories (standalone or shared)
+ *
+ * \retval t_cm_error
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_error cm_DSP_Add(t_nmf_core_id coreId,
+ t_uint8 nbYramBanks,
+ const t_cm_system_address *pDspMapDesc,
+ const t_cm_domain_id eeDomain,
+ t_dsp_allocator_desc *sdramCodeAllocDesc,
+ t_dsp_allocator_desc *sdramDataAllocDesc);
+
+
+
+/*!
+ * \brief Configure a given Media Processor Core
+ *
+ * This routine programs the configuration (caches, ahb wrapper, ...) registers of a given MPC.
+ *
+ * \param[in] coreId Identifier of the DSP to initialize
+ *
+ * \retval t_cm_error
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_error cm_DSP_Boot(t_nmf_core_id coreId);
+
+/*!
+ * \brief Boot a given DSP
+ *
+ * This routine allows after having initialized and loaded the EE into a given DSP to start it (boot it)
+ *
+ * \param[in] coreId identifier of the DSP to boot
+ * \param[in] panicReasonOffset offset of panic reason which will pass to NONE_PANIC when DSP booted.
+ *
+ * \retval t_cm_error
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC void cm_DSP_ConfigureAfterBoot(t_nmf_core_id coreId);
+
+PUBLIC void cm_DSP_Start(t_nmf_core_id coreId);
+
+PUBLIC void cm_DSP_Stop(t_nmf_core_id coreId);
+
+/*!
+ * \brief Shutdown a given DSP
+ *
+ * This routine allows to stop and shutdown a given DSP
+ *
+ * \param[in] coreId identifier of the DSP to shutdown
+ *
+ * \retval t_cm_error
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC void cm_DSP_Shutdown(t_nmf_core_id coreId);
+
+PUBLIC t_uint32 cm_DSP_ReadXRamWord(t_nmf_core_id coreId, t_uint32 dspOffset);
+PUBLIC void cm_DSP_WriteXRamWord(t_nmf_core_id coreId, t_uint32 dspOffset, t_uint32 value);
+
+/*!
+ * \brief Convert a Dsp address (offset inside a given DSP memory segment) into the host address (logical)
+ *
+ * \param[in] coreId identifier of the given DSP
+ * \param[in] dspAddress dsp address to be converted
+ * \param[in] memType memory type identifier
+ *
+ * \retval t_cm_logical_address
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_logical_address cm_DSP_ConvertDspAddressToHostLogicalAddress(t_nmf_core_id coreId, t_shared_addr dspAddress);
+
+/*!
+ * \brief Acknowledge the local interrupt of a given DSP (when not using HW semaphore mechanisms)
+ *
+ * \param[in] coreId identifier of the given DSP
+ * \param[in] irqNum irq identifier
+ *
+ * \retval void
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC void cm_DSP_AcknowledgeDspIrq(t_nmf_core_id coreId, t_mpc2host_irq_num irqNum);
+
+
+/*
+ * Memory Management API routines
+ */
+
+/*!
+ * \brief Retrieve DSP information for a memory chunk.
+ *
+ * This function retrieves information stored in user-data of the allocated chunk.
+ * See also \ref{t_dsp_chunk_info}.
+ *
+ * \param[in] memHandle Handle to the allocated chunk.
+ * \param[out] info Dsp information structure.
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC void cm_DSP_GetDspChunkInfo(t_memory_handle memHandle, t_dsp_chunk_info *info);
+
+/*!
+ * \brief Get memory allocator for a given memory type on a DSP.
+ *
+ * \param[in] coreId Dsp identifier.
+ * \param[in] memType Memory type identifier.
+ *
+ * \retval reference to the allocator descriptor (or null)
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_allocator_desc* cm_DSP_GetAllocator(t_nmf_core_id coreId, t_dsp_memory_type_id memType);
+
+/*!
+ * \brief Get DSP internal memory (TCM) information for allocation.
+ *
+ * For DSP-internal memories (TCMX, Y 16/24), return the offset and size of the allocation zone (for domain
+ * mechanism) and the allocation memory type.
+ *
+ * \param[in] coreId Dsp identifier.
+ * \param[in] memType Memory type identifier.
+ * \param[out] mem_info Memory information structure.
+ *
+ * \retval CM_OK
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_error cm_DSP_GetInternalMemoriesInfo(t_cm_domain_id domainId, t_dsp_memory_type_id memType,
+ t_uint32 *offset, t_uint32 *size);
+
+
+/*!
+ * \brief Convert word size to byte size.
+ *
+ * \param[in] memType Memory type identifier.
+ * \param[in] wordSize Word size to be converted.
+ *
+ * \retval Byte size.
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_uint32 cm_DSP_ConvertSize(t_dsp_memory_type_id memType, t_uint32 wordSize);
+
+/*!
+ * \brief Provide the Memory status of a given memory type for a given DSP
+ *
+ * \param[in] coreId dsp identifier.
+ * \param[in] memType Type of memory.
+ * \param[out] pStatus requested memory status
+ *
+ * \retval t_cm_error
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_error cm_DSP_GetAllocatorStatus(t_nmf_core_id coreId, t_dsp_memory_type_id memType, t_uint32 offset, t_uint32 size, t_cm_allocator_status *pStatus);
+
+/*!
+ * \brief Provide DSP memory host shared address
+ *
+ * \param[in] memHandle Allocated block handle
+ * \param[out] pAddr Returned system address.
+ *
+ * \retval t_cm_error
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC void cm_DSP_GetHostSystemAddress( t_memory_handle memHandle, t_cm_system_address *pAddr);
+
+/*!
+ * \brief Get physical address of a memory chunk.
+ *
+ * \param[in] memHandle Memory handle.
+ *
+ * \retval Physical address.
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_physical_address cm_DSP_GetPhysicalAdress(t_memory_handle memHandle);
+
+/*!
+ * \brief Return Logical Address of an allocated memory chunk.
+ *
+ * \param[in] memHandle Allocated chunk handle
+ * \retval t_cm_error
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_logical_address cm_DSP_GetHostLogicalAddress(t_memory_handle memHandle);
+
+/*!
+ * \brief Provide DSP memory DSP address (offset inside a given DSP memory segment)
+ *
+ * \param[in] memHandle Allocated block handle
+ * \param[out] dspAddress allocated block address seen by the given DSP
+ *
+ * \retval t_cm_error
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC void cm_DSP_GetDspAddress(t_memory_handle handle, t_uint32 *pDspAddress);
+
+/*!
+ * \brief Return the adress of the DSP base associated to the memory type.
+ * Caution, this information is valid only in normal state (not when migrated).
+ *
+ * \param[in] coreId DSP Identifier.
+ * \param[in] memType Type of memory.
+ * \param[out] pAddr Base address.
+ *
+ * \retval t_cm_error
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_error cm_DSP_GetDspBaseAddress(t_nmf_core_id coreId, t_dsp_memory_type_id memType, t_cm_system_address *pAddr);
+
+/*!
+ * \brief Return DSP memory handle offset (offset inside a given DSP memory)
+ *
+ * \param[in] coreId dsp identifier.
+ * \param[in] memType Type of memory.
+ * \param[in] memHandle Allocated block handle
+ *
+ * \retval t_uint32: Offset of memory handle inside memory
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_uint32 cm_DSP_GetDspMemoryHandleOffset(
+ t_nmf_core_id coreId,
+ t_dsp_memory_type_id dspMemType,
+ t_memory_handle memHandle);
+
+/*!
+ * \brief Provide DSP memory handle size
+ *
+ * \param[in] memHandle Allocated block handle
+ * \param[out] pDspSize Size of the given memory handle
+
+ *
+ * \retval t_cm_error
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC void cm_DSP_GetDspMemoryHandleSize(t_memory_handle memHandle, t_uint32 *pDspSize);
+
+/*!
+ * \brief Resize xram allocator to reserve spave for stack.
+ *
+ * \param[in] coreId dsp identifier.
+ * \param[in] newStackSize New required stack size.
+
+ *
+ * \retval t_cm_error
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_error cm_DSP_setStackSize(t_nmf_core_id coreId, t_uint32 newStackSize);
+
+/*!
+ * \brief Allow to know if nbYramBanks parameter is valid for coreId. This api is need since use of nbYramBanks
+ * is deferred.
+ *
+ * \param[in] coreId dsp identifier.
+ * \param[in] nbYramBanks number of yramBanks to use.
+ *
+ * \retval t_cm_error
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_error cm_DSP_IsNbYramBanksValid(t_nmf_core_id coreId, t_uint8 nbYramBanks);
+
+/*!
+ * \brief Allow to know stack base address according to coreId and nbYramBanks use.
+ *
+ * \param[in] coreId dsp identifier.
+ * \param[in] nbYramBanks number of yramBanks to use.
+ *
+ * \retval t_uint32 return stack address
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_uint32 cm_DSP_getStackAddr(t_nmf_core_id coreId);
+
+/*!
+ * \brief For a give dsp adress return the offset from the hardware base that the adress is relative to.
+ *
+ * \param[in] coreId DSP identifier.
+ * \param[in] adr DSP address.
+ * \param[out] info Info structure containing (hw base id, offset)
+ *
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_error cm_DSP_GetDspDataAddressInfo(t_nmf_core_id coreId, t_uint32 adr, t_dsp_address_info *info);
+
+/*!
+ * \brief Modify the mapping of a code hardware base. Used for memory migration.
+ *
+ * The function calculates the new hardware base so that in the DSP address-space,
+ * the source address will be mapped to the destination address.
+ *
+ * \param[in] coreId DSP Identifier.
+ * \param[in] hwSegment Identifier of the hardware segment (thus hardware base).
+ * \param[in] src Source address
+ * \param[in] dst Destination address
+ *
+ * \retval t_cm_error
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_error cm_DSP_updateCodeBase(t_nmf_core_id coreId, t_dsp_segment_type hwSegment, t_cm_system_address src, t_cm_system_address dst);
+
+/*!
+ * \brief Modify the mapping of a data hardware base. Used for memory migration.
+ *
+ * The function calculates the new hardware base so that in the DSP address-space,
+ * the source address will be mapped to the destination address.
+ *
+ * \param[in] coreId DSP Identifier.
+ * \param[in] hwSegment Identifier of the hardware segment (thus hardware base).
+ * \param[in] src Source address
+ * \param[in] dst Destination address
+ *
+ * \retval t_cm_error
+ * \ingroup DSP_INTERNAL
+ */
+PUBLIC t_cm_error cm_DSP_updateDataBase(t_nmf_core_id coreId, t_dsp_segment_type hwSegment, t_cm_system_address src, t_cm_system_address dst);
+
+#endif /* __INC_CM_DSP_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/dsp/inc/semaphores_dsp.h b/drivers/staging/nmf-cm/cm/engine/dsp/inc/semaphores_dsp.h
new file mode 100644
index 00000000000..1bb1c34cced
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/dsp/inc/semaphores_dsp.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef __INC_CM_SEMAPHORES_DSP_H
+#define __INC_CM_SEMAPHORES_DSP_H
+
+#include <share/semaphores/inc/semaphores.h>
+#include <cm/engine/dsp/inc/dsp.h>
+
+PUBLIC void cm_DSP_SEM_Take(t_nmf_core_id coreId, t_semaphore_id semId);
+PUBLIC void cm_DSP_SEM_Give(t_nmf_core_id coreId, t_semaphore_id semId);
+PUBLIC void cm_DSP_SEM_GenerateIrq(t_nmf_core_id coreId, t_semaphore_id semId);
+PUBLIC void cm_DSP_AssertDspIrq(t_nmf_core_id coreId, t_host2mpc_irq_num irqNum);
+
+PUBLIC void cm_DSP_AcknowledgeDspIrq(t_nmf_core_id coreId, t_mpc2host_irq_num irqNum);
+
+#endif /* __INC_CM_SEMAPHORES_DSP_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h b/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h
new file mode 100644
index 00000000000..0ddc71d2c4f
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h
@@ -0,0 +1,959 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef __INC_MMDSP_HWP_H
+#define __INC_MMDSP_HWP_H
+
+#include <cm/inc/cm_type.h>
+
+#define MMDSP_NB_BLOCK_RAM 8
+#define MMDSP_RAM_BLOCK_SIZE 4096 /* 0x1000 */
+#define MMDSP_NB_TIMER 3
+#define MMDSP_NB_BIT_SEM 8
+#define MMDSP_NB_DMA_IF 8
+#define MMDSP_NB_DMA_CTRL 4
+#define MMDSP_NB_ITREMAP_REG 32
+
+#define MMDSP_INSTRUCTION_WORD_SIZE (sizeof(t_uint64))
+#define MMDSP_ICACHE_LINE_SIZE_IN_INST (4)
+#define MMDSP_ICACHE_LINE_SIZE (MMDSP_ICACHE_LINE_SIZE_IN_INST * MMDSP_INSTRUCTION_WORD_SIZE)
+
+#define MMDSP_DATA_WORD_SIZE (3)
+#define MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE (sizeof(t_uint32))
+#define MMDSP_DATA_WORD_SIZE_IN_EXT24 (sizeof(t_uint32))
+#define MMDSP_DATA_WORD_SIZE_IN_EXT16 (sizeof(t_uint16))
+#define MMDSP_DCACHE_LINE_SIZE_IN_WORDS (8)
+#define MMDSP_DCACHE_LINE_SIZE (MMDSP_DCACHE_LINE_SIZE_IN_WORDS * sizeof(t_uint32))
+
+#define MMDSP_NB_IO 16
+
+#define MMDSP_CODE_CACHE_WAY_SIZE 256
+
+//#define MMDSP_ESRAM_DSP_BASE_ADDR 0xE0000 /* 64-bit words */
+//#define MMDSP_DATA24_DSP_BASE_ADDR 0x10000
+//#define MMDSP_DATA16_DSP_BASE_ADDR 0x800000
+//#define MMDSP_MMIO_DSP_BASE_ADDR 0xF80000
+
+/* Specified according MMDSP & ELF convention */
+/* Note: Here we assume that ESRAM is less than 2MB */
+#define SDRAMTEXT_BASE_ADDR 0x00000000
+#define ESRAMTEXT_BASE_ADDR 0x000E0000
+
+#define SDRAMMEM24_BASE_ADDR 0x00010000
+#define ESRAMMEM24_BASE_ADDR 0x00600000 /* ELF == 0x00400000 TODO: Update it in MMDSP ELF compiler */
+#define SDRAMMEM16_BASE_ADDR 0x00800000
+#define ESRAMMEM16_BASE_ADDR 0x00D80000 /* ELF == 0x00BC0000 TODO: Update it in MMDSP ELF compiler */
+
+#define MMIO_BASE_ADDR 0x00F80000
+
+/*
+ * Definition of indirect host registers
+ */
+#define IHOST_ICACHE_FLUSH_REG 0x0
+#define IHOST_ICACHE_FLUSH_CMD_ENABLE (t_uint64)MASK_BIT0
+#define IHOST_ICACHE_FLUSH_ALL_ENTRIES_CMD (t_uint64)0x0
+#if 0
+#define IHOST_ICACHE_INVALID_ALL_UNLOCKED_L2_LINES_CMD (t_uint64)0x8
+#define IHOST_ICACHE_INVALID_ALL_LOCKED_L2_LINES_CMD (t_uint64)0xA
+#define IHOST_ICACHE_UNLOCK_ALL_LOCKED_L2_LINES_CMD (t_uint64)0xC
+#define IHOST_ICACHE_LOCK_ALL_WAYS_LESSER_THAN_LOCK_V_CMD (t_uint64)0xE
+#else
+#define IHOST_ICACHE_INVALID_ALL_UNLOCKED_L2_LINES_CMD (t_uint64)0x10
+#define IHOST_ICACHE_INVALID_ALL_LOCKED_L2_LINES_CMD (t_uint64)0x12
+#define IHOST_ICACHE_UNLOCK_ALL_LOCKED_L2_LINES_CMD (t_uint64)0x14
+#define IHOST_ICACHE_LOCK_ALL_WAYS_LESSER_THAN_LOCK_V_CMD (t_uint64)0x16
+#define IHOST_ICACHE_FLUSH_BY_SERVICE (t_uint64)0x18
+#define IHOST_ICACHE_FLUSH_OUTSIDE_RANGE (t_uint64)0x1A
+#endif
+
+#define IHOST_ICACHE_LOCK_V_REG 0x1
+
+#define IHOST_ICACHE_MODE_REG 0x2
+#define IHOST_ICACHE_MODE_PERFMETER_ON (t_uint64)MASK_BIT0
+#define IHOST_ICACHE_MODE_PERFMETER_OFF (t_uint64)0x0
+#define IHOST_ICACHE_MODE_L2_CACHE_ON (t_uint64)MASK_BIT1
+#define IHOST_ICACHE_MODE_L2_CACHE_OFF (t_uint64)0x0
+#define IHOST_ICACHE_MODE_L1_CACHE_ON (t_uint64)MASK_BIT2
+#define IHOST_ICACHE_MODE_L1_CACHE_OFF (t_uint64)0x0
+#define IHOST_ICACHE_MODE_FILL_MODE_ON (t_uint64)MASK_BIT3
+#define IHOST_ICACHE_MODE_FILL_MODE_OFF (t_uint64)0x0
+
+#define IHOST_CLEAR_PERFMETER_REG 0x3
+#define IHOST_CLEAR_PERFMETER_ON (t_uint64)0x1
+#define IHOST_CLEAR_PERFMETER_OFF (t_uint64)0x0
+
+#define IHOST_PERF_HIT_STATUS_REG 0x4
+
+#define IHOST_PERF_MISS_STATUS_REG 0x5
+
+#define IHOST_FILL_START_WAY_REG 0x6
+#define IHOST_FILL_START_ADDR_VALUE_SHIFT 0U
+#define IHOST_FILL_WAY_NUMBER_SHIFT 20U
+
+#define IHOST_PRG_BASE_ADDR_REG 0x7
+#define IHOST_PRG_BASE1_ADDR_SHIFT 0
+#define IHOST_PRG_BASE2_ADDR_SHIFT 32
+
+#if defined(__STN_8500) && (__STN_8500>10)
+#define IHOST_PRG_BASE_34_ADDR_REG 0x1A
+#define IHOST_PRG_BASE3_ADDR_SHIFT 0
+#define IHOST_PRG_BASE4_ADDR_SHIFT 32
+#endif
+
+#if defined(__STN_8815) /* __STN_8815 */
+#define IHOST_PRG_AHB_CONF_REG 0x8
+#define IHOST_PRG_AHB_LOCKED_SHIFT 0U
+#define IHOST_PRG_AHB_PROT_SHIFT 1U
+
+#define AHB_LOCKED_ON (t_uint64)1
+#define AHB_LOCKED_OFF (t_uint64)0
+
+#define AHB_PROT_USER (t_uint64)0
+#define AHB_PROT_PRIVILEGED (t_uint64)MASK_BIT0
+#define AHB_PROT_NONBUFFERABLE (t_uint64)0
+#define AHB_PROT_BUFFERABLE (t_uint64)MASK_BIT1
+#define AHB_PROT_NONCACHEABLE (t_uint64)0
+#define AHB_PROT_CACHEABLE (t_uint64)MASK_BIT2
+
+
+#define IHOST_DATA_AHB_CONF_REG 0x9
+#define IHOST_DATA_AHB_LOCKED_SHIFT 0U
+#define IHOST_DATA_AHB_PROT_SHIFT 1U
+#else /* def __STN_8820 or __STN_8500 */
+#define IHOST_STBUS_ID_CONF_REG 0x8
+#define SAA_STBUS_ID 176 /* = 0xB0 */
+#define SVA_STBUS_ID 4 /* = 0x4 */
+#define SIA_STBUS_ID 180 /* = 0xB4 */
+
+#define IHOST_STBUF_CONF_REG 0x9 /* RESERVED */
+#endif /* __STN_8820 or __STN_8500 */
+
+#define IHOST_DATA_EXT_BUS_BASE_REG 0xA
+#define IHOST_DATA_EXT_BUS_BASE_16_SHIFT 32ULL
+#define IHOST_DATA_EXT_BUS_BASE_24_SHIFT 0ULL
+
+#define IHOST_EXT_MMIO_BASE_DATA_EXT_BUS_TOP_REG 0xB
+#define IHOST_EXT_MMIO_DATA_EXT_BUS_TOP_SHIFT 0ULL
+#define IHOST_EXT_MMIO_BASE_ADDR_SHIFT 32ULL
+
+#define IHOST_DATA_EXT_BUS_BASE2_REG 0xC
+#define IHOST_DATA_EXT_BUS_BASE2_16_SHIFT 32ULL
+#define IHOST_DATA_EXT_BUS_BASE2_24_SHIFT 0ULL
+
+#if defined(__STN_8500) && (__STN_8500>10)
+
+#define IHOST_DATA_EXT_BUS_BASE3_REG 0x1B
+#define IHOST_DATA_EXT_BUS_BASE3_16_SHIFT 32ULL
+#define IHOST_DATA_EXT_BUS_BASE3_24_SHIFT 0ULL
+
+#define IHOST_DATA_EXT_BUS_BASE4_REG 0x1C
+#define IHOST_DATA_EXT_BUS_BASE4_16_SHIFT 32ULL
+#define IHOST_DATA_EXT_BUS_BASE4_24_SHIFT 0ULL
+
+#endif
+
+#define IHOST_ICACHE_STATE_REG 0xD
+#define IHOST_ICACHE_STATE_RESET 0x0
+#define IHOST_ICACHE_STATE_INITAGL2 0x1
+#define IHOST_ICACHE_STATE_READY_TO_START 0x2
+#define IHOST_ICACHE_STATE_WAIT_FOR_MISS 0x3
+#define IHOST_ICACHE_STATE_FILLDATARAM0 0x4
+#define IHOST_ICACHE_STATE_FILLDATARAM1 0x5
+#define IHOST_ICACHE_STATE_FILLDATARAM2 0x6
+#define IHOST_ICACHE_STATE_FILLDATARAM3 0x7
+#define IHOST_ICACHE_STATE_FLUSH 0x8
+#define IHOST_ICACHE_STATE_FILL_INIT 0x9
+#define IHOST_ICACHE_STATE_FILL_LOOP 0xA
+#define IHOST_ICACHE_STATE_FILL_LOOP0 0xB
+#define IHOST_ICACHE_STATE_FILL_LOOP1 0xC
+#define IHOST_ICACHE_STATE_FILL_LOOP2 0xD
+#define IHOST_ICACHE_STATE_FILL_LOOP3 0xE
+#define IHOST_ICACHE_STATE_FILL_END 0xF
+#define IHOST_ICACHE_STATE_SPECIFIC_FLUSH_R 0x10
+#define IHOST_ICACHE_STATE_SPECIFIC_FLUSH_W 0x11
+#define IHOST_ICACHE_STATE_SPECIFIC_FLUSH_END 0x12
+#define IHOST_ICACHE_STATE_OTHERS 0x1F
+
+#define IHOST_EN_EXT_BUS_TIMEOUT_REG 0xE
+#define IHOST_TIMEOUT_ENABLE 1ULL
+#define IHOST_TIMEOUT_DISABLE 0ULL
+
+#define IHOST_DATA2_1624_XA_BASE_REG 0xF
+#define IHOST_DATA2_24_XA_BASE_SHIFT 0ULL
+#define IHOST_DATA2_16_XA_BASE_SHIFT 32ULL
+#if defined(__STN_8500) && (__STN_8500>10)
+#define IHOST_DATA3_24_XA_BASE_SHIFT 8ULL
+#define IHOST_DATA3_16_XA_BASE_SHIFT 40ULL
+#define IHOST_DATA4_24_XA_BASE_SHIFT 16ULL
+#define IHOST_DATA4_16_XA_BASE_SHIFT 48ULL
+#endif
+
+#define IHOST_PERFMETERS_MODE_REG 0x10
+
+#if defined(__STN_8815) /* __STN_8815 */
+#define IHOST_EXT_MMIO_AHB_CONF_REG 0x11
+#define IHOST_EXT_MMIO_AHB_LOCKED_SHIFT 0U
+#define IHOST_EXT_MMIO_AHB_PROT_SHIFT 1U
+#else /* def __STN_8820 or __STN_8500 */
+#define IHOST_EXT_MMIO_STBS_CONF_REG 0x11 /* RESERVED */
+#endif /* __STN_8820 or __STN_8500 */
+
+#define IHOST_PRG_BASE_SEL_REG 0x12
+#define IHOST_PRG_BASE_SEL_OFF (t_uint64)0
+#define IHOST_PRG_BASE_SEL_ON (t_uint64)1
+
+#define IHOST_PRG_BASE2_ACTIV_REG 0x13
+#define IHOST_PRG_BASE2_ACTIV_OFF (t_uint64)0
+#if defined(__STN_8500) && (__STN_8500>10)
+/* TODO : for the moment just divide mmdsp in fix 4 spaces */
+ #define IHOST_PRG_BASE2_ACTIV_ON (t_uint64)((((t_uint64)0xf0000>>10)<<48) | (((t_uint64)0xe0000>>10)<<32) | (((t_uint64)0x70000>>10)<<16) | 1)
+#else
+ #define IHOST_PRG_BASE2_ACTIV_ON (t_uint64)1
+#endif
+
+#define IHOST_DATA_EXT_BUS_TOP_16_24_REG 0x14
+#define IHOST_DATA_EXT_BUS_TOP_24_SHIFT 0ULL
+#define IHOST_DATA_EXT_BUS_TOP_16_SHIFT 32ULL
+
+#define IHOST_DATA_TOP_16_24_CHK_REG 0x16
+#define IHOST_DATA_TOP_16_24_CHK_OFF (t_uint64)0
+#define IHOST_DATA_TOP_16_24_CHK_ON (t_uint64)1
+
+#define IHOST_EXT_BUS_TOP2_16_24_REG 0x15
+#define IHOST_DATA_EXT_BUS_TOP2_24_SHIFT 0ULL
+#define IHOST_DATA_EXT_BUS_TOP2_16_SHIFT 32ULL
+
+#if defined(__STN_8500) && (__STN_8500>10)
+
+#define IHOST_EXT_BUS_TOP3_16_24_REG 0x1D
+#define IHOST_DATA_EXT_BUS_TOP3_24_SHIFT 0ULL
+#define IHOST_DATA_EXT_BUS_TOP3_16_SHIFT 32ULL
+
+#define IHOST_EXT_BUS_TOP4_16_24_REG 0x1E
+#define IHOST_DATA_EXT_BUS_TOP4_24_SHIFT 0ULL
+#define IHOST_DATA_EXT_BUS_TOP4_16_SHIFT 32ULL
+
+#endif
+
+#define IHOST_DATA_BASE2_ACTIV_REG 0x17
+#define IHOST_DATA_BASE2_ACTIV_OFF (t_uint64)0
+#define IHOST_DATA_BASE2_ACTIV_ON (t_uint64)1
+
+#define IHOST_INST_BURST_SZ_REG 0x18
+#define IHOST_INST_BURST_SZ_ALWAYS_1_LINE (t_uint64)0x0
+#define IHOST_INST_BURST_SZ_ALWAYS_2_LINES (t_uint64)0x1
+#define IHOST_INST_BURST_SZ_AUTO (t_uint64)0x2 /* 2 lines for SDRAM [0, 0xE0000[, 1 line for ESRAM [0xE0000, 0xFFFFF] */
+
+#define IHOST_ICACHE_END_CLEAR_REG 0x19
+#define IHOST_ICACHE_START_CLEAR_REG IHOST_FILL_START_WAY_REG
+
+/*
+ * Definition of value of the ucmd register
+ */
+#define MMDSP_UCMD_WRITE 0
+#define MMDSP_UCMD_READ 4
+#define MMDSP_UCMD_CTRL_STATUS_ACCESS 0x10 // (MASK_BIT4 | !MASK_BIT3 | !MASK_BIT0)
+#define MMDSP_UCMD_DECREMENT_ADDR MASK_BIT5
+#define MMDSP_UCMD_INCREMENT_ADDR MASK_BIT1
+
+/*
+ * Definition of value of the ubkcmd register
+ */
+#define MMDSP_UBKCMD_EXT_CODE_MEM_ACCESS_ENABLE MASK_BIT3
+#define MMDSP_UBKCMD_EXT_CODE_MEM_ACCESS_DISABLE 0
+
+/*
+ * Definition of value of the clockcmd register
+ */
+#define MMDSP_CLOCKCMD_STOP_CLOCK MASK_BIT0
+#define MMDSP_CLOCKCMD_START_CLOCK 0
+
+/*
+ * Definition of macros used to access indirect addressed host register
+ */
+#define WRITE_INDIRECT_HOST_REG(pRegs, addr, value64) \
+{ \
+ (pRegs)->host_reg.emul_uaddrl = addr; \
+ (pRegs)->host_reg.emul_uaddrm = 0; \
+ (pRegs)->host_reg.emul_uaddrh = 0; \
+ (pRegs)->host_reg.emul_udata[0] = ((value64 >> 0ULL) & MASK_BYTE0); \
+ (pRegs)->host_reg.emul_udata[1] = ((value64 >> 8ULL) & MASK_BYTE0); \
+ (pRegs)->host_reg.emul_udata[2] = ((value64 >> 16ULL) & MASK_BYTE0); \
+ (pRegs)->host_reg.emul_udata[3] = ((value64 >> 24ULL) & MASK_BYTE0); \
+ (pRegs)->host_reg.emul_udata[4] = ((value64 >> 32ULL) & MASK_BYTE0); \
+ (pRegs)->host_reg.emul_udata[5] = ((value64 >> 40ULL) & MASK_BYTE0); \
+ (pRegs)->host_reg.emul_udata[6] = ((value64 >> 48ULL) & MASK_BYTE0); \
+ (pRegs)->host_reg.emul_udata[7] = ((value64 >> 56ULL) & MASK_BYTE0); \
+ (pRegs)->host_reg.emul_ucmd = (MMDSP_UCMD_CTRL_STATUS_ACCESS | MMDSP_UCMD_WRITE); \
+}
+
+#define READ_INDIRECT_HOST_REG(pRegs, addr, value64) \
+{ \
+ (pRegs)->host_reg.emul_udata[0] = 0; \
+ (pRegs)->host_reg.emul_udata[1] = 0; \
+ (pRegs)->host_reg.emul_udata[2] = 0; \
+ (pRegs)->host_reg.emul_udata[3] = 0; \
+ (pRegs)->host_reg.emul_udata[4] = 0; \
+ (pRegs)->host_reg.emul_udata[5] = 0; \
+ (pRegs)->host_reg.emul_udata[6] = 0; \
+ (pRegs)->host_reg.emul_udata[7] = 0; \
+ (pRegs)->host_reg.emul_uaddrl = addr; \
+ (pRegs)->host_reg.emul_uaddrm = 0; \
+ (pRegs)->host_reg.emul_uaddrh = 0; \
+ (pRegs)->host_reg.emul_ucmd = (MMDSP_UCMD_CTRL_STATUS_ACCESS | MMDSP_UCMD_READ); \
+ value64 = (((t_uint64)((pRegs)->host_reg.emul_udata[0])) << 0ULL) | \
+ (((t_uint64)((pRegs)->host_reg.emul_udata[1])) << 8ULL) | \
+ (((t_uint64)((pRegs)->host_reg.emul_udata[2])) << 16ULL) | \
+ (((t_uint64)((pRegs)->host_reg.emul_udata[3])) << 24ULL) | \
+ (((t_uint64)((pRegs)->host_reg.emul_udata[4])) << 32ULL) | \
+ (((t_uint64)((pRegs)->host_reg.emul_udata[5])) << 40ULL) | \
+ (((t_uint64)((pRegs)->host_reg.emul_udata[6])) << 48ULL) | \
+ (((t_uint64)((pRegs)->host_reg.emul_udata[7])) << 56ULL); \
+}
+
+/* Common type to handle 64-bit modulo field in 32-bit mode */
+typedef struct {
+ t_uint32 value;
+ t_uint32 dummy;
+} t_mmdsp_field_32;
+
+typedef struct {
+ t_uint16 value;
+ t_uint16 dummy;
+} t_mmdsp_field_16;
+
+/* DCache registers */
+#define DCACHE_MODE_ENABLE MASK_BIT0
+#define DCACHE_MODE_DISABLE 0
+#define DCACHE_MODE_DIVIDE_PER_2 MASK_BIT1
+#define DCACHE_MODE_DIVIDE_PER_4 MASK_BIT2
+#define DCACHE_MODE_CHECK_TAG_ENABLE MASK_BIT3
+#define DCACHE_MODE_CHECK_TAG_DISABLE 0
+#define DCACHE_MODE_FORCE_LOCK_MODE MASK_BIT4
+#define DCACHE_MODE_LOCK_BIT MASK_BIT5
+
+#define DCACHE_CONTROL_PREFETCH_LINE MASK_BIT0
+#define DCACHE_CONTROL_NON_BLOCKING_REFILL 0
+#define DCACHE_CONTROL_FAST_READ_DISABLE MASK_BIT1
+#define DCACHE_CONTROL_FAST_READ_ENABLE 0
+#define DCACHE_CONTROL_ON_FLY_FILL_ACCESS_OFF MASK_BIT2
+#define DCACHE_CONTROL_ON_FLY_FILL_ACCESS_ON 0
+#define DCACHE_CONTROL_BURST_1_WRAP8 MASK_BIT3
+#define DCACHE_CONTROL_BURST_2_WRAP4 0
+#define DCACHE_CONTROL_NOT_USE_DATA_BUFFER MASK_BIT4
+#define DCACHE_CONTROL_USE_DATA_BUFFER 0
+#define DCACHE_CONTROL_WRITE_POSTING_ENABLE MASK_BIT5
+#define DCACHE_CONTROL_WRITE_POSTING_DISABLE 0
+
+#define DCACHE_CMD_NOP 0
+#define DCACHE_CMD_DISCARD_WAY 2 //see Dcache_way reg
+#define DCACHE_CMD_DISCARD_LINE 3 //see Dcache_line reg
+#define DCACHE_CMD_FREE_WAY 4 //see Dcache_way reg
+#define DCACHE_CMD_FREE_LINE 5 //see Dchache_line reg
+#define DCACHE_CMD_FLUSH 7
+
+#define DCACHE_STATUS_CURRENT_WAY_MASK (MASK_BIT2 | MASK_BIT1 | MASK_BIT0)
+#define DCACHE_STATUS_TAG_HIT_MASK MASK_BIT3
+#define DCACHE_STATUS_TAG_LOCKED_MASK MASK_BIT4
+#define DCACHE_STATUS_PROTECTION_ERROR_MASK MASK_BIT5
+
+#define DCACHE_CPTRSEL_COUNTER_1_MASK (MASK_BIT3 | MASK_BIT2 | MASK_BIT1 | MASK_BIT0)
+#define DCACHE_CPTRSEL_COUNTER_1_SHIFT 0
+#define DCACHE_CPTRSEL_COUNTER_2_MASK (MASK_BIT7 | MASK_BIT6 | MASK_BIT5 | MASK_BIT4)
+#define DCACHE_CPTRSEL_COUNTER_2_SHIFT 4
+#define DCACHE_CPTRSEL_COUNTER_3_MASK (MASK_BIT11 | MASK_BIT10 | MASK_BIT9 | MASK_BIT8)
+#define DCACHE_CPTRSEL_COUNTER_3_SHIFT 8
+#define DCACHE_CPTRSEL_XBUS_ACCESS_TO_CACHE_RAM 1
+#define DCACHE_CPTRSEL_CACHE_HIT 2
+#define DCACHE_CPTRSEL_LINE_MATCH 3
+#define DCACHE_CPTRSEL_XBUS_WS 4
+#define DCACHE_CPTRSEL_EXTMEM_WS 5
+#define DCACHE_CPTRSEL_CACHE_READ 6
+#define DCACHE_CPTRSEL_CACHE_WRITE 7
+#define DCACHE_CPTRSEL_TAG_HIT_READ 8
+#define DCACHE_CPTRSEL_TAG_LOCKED_ACCESS 9
+#define DCACHE_CPTRSEL_TAG_MEM_READ_CYCLE 10
+#define DCACHE_CPTRSEL_TAG_MEM_WRITE_CYCLE 11
+
+
+typedef volatile struct {
+ t_uint16 padding_1[5];
+ t_uint16 mode;
+ t_uint16 control;
+ t_uint16 way;
+ t_uint16 line;
+ t_uint16 command;
+ t_uint16 status;
+ t_uint16 cptr1l;
+ t_uint16 cptr1h;
+ t_uint16 cptr2l;
+ t_uint16 cptr2h;
+ t_uint16 cptr3l;
+ t_uint16 cptr3h;
+ t_uint16 cptrsel;
+ t_uint16 flush_base_lsb; /* only on STn8820 and STn8500 */
+ t_uint16 flush_base_msb; /* only on STn8820 and STn8500 */
+ t_uint16 flush_top_lsb; /* only on STn8820 and STn8500 */
+ t_uint16 flush_top_msb; /* only on STn8820 and STn8500 */
+ t_uint16 padding_2[10];
+} t_mmdsp_dcache_regs_16;
+
+typedef volatile struct {
+ t_uint32 padding_1[5];
+ t_uint32 mode;
+ t_uint32 control;
+ t_uint32 way;
+ t_uint32 line;
+ t_uint32 command;
+ t_uint32 status;
+ t_uint32 cptr1l;
+ t_uint32 cptr1h;
+ t_uint32 cptr2l;
+ t_uint32 cptr2h;
+ t_uint32 cptr3l;
+ t_uint32 cptr3h;
+ t_uint32 cptrsel;
+ t_uint32 flush_base_lsb; /* only on STn8820 and STn8500 */
+ t_uint32 flush_base_msb; /* only on STn8820 and STn8500 */
+ t_uint32 flush_top_lsb; /* only on STn8820 and STn8500 */
+ t_uint32 flush_top_msb; /* only on STn8820 and STn8500 */
+ t_uint32 padding_2[10];
+} t_mmdsp_dcache_regs_32;
+
+/* TIMER Registers */
+typedef volatile struct {
+ t_mmdsp_field_16 timer_msb;
+ t_mmdsp_field_16 timer_lsb;
+} t_mmdsp_timer_regs_16;
+
+typedef volatile struct {
+ t_mmdsp_field_32 timer_msb;
+ t_mmdsp_field_32 timer_lsb;
+} t_mmdsp_timer_regs_32;
+
+
+/* DMA interface Registers */
+typedef volatile struct {
+ t_uint16 arm_dma_sreq; /* dma0: 5e800, dma1: +0x20 ...*/
+ t_uint16 arm_dma_breq; /* ... 5e802 */
+ t_uint16 arm_dma_lsreq; /* ... 5e804 */
+ t_uint16 arm_dma_lbreq;
+ t_uint16 arm_dma_maskit;
+ t_uint16 arm_dma_it;
+ t_uint16 arm_dma_auto;
+ t_uint16 arm_dma_lauto;
+ t_uint16 dma_reserved[8];
+} t_mmdsp_dma_if_regs_16;
+
+typedef volatile struct {
+ t_uint32 arm_dma_sreq; /* dma0: 3a800, dma1: +0x40 ...*/
+ t_uint32 arm_dma_breq; /* ... 3a804 */
+ t_uint32 arm_dma_lsreq; /* ... 3a808 */
+ t_uint32 arm_dma_lbreq;
+ t_uint32 arm_dma_maskit;
+ t_uint32 arm_dma_it;
+ t_uint32 arm_dma_auto;
+ t_uint32 arm_dma_lauto;
+ t_uint32 dma_reserved[8];
+} t_mmdsp_dma_if_regs_32;
+
+/* MMDSP DMA controller Registers */
+typedef volatile struct {
+ t_uint16 dma_ctrl; /* dma0: 0x5d400, dma1: +0x10 ... */
+ t_uint16 dma_int_base; /* ... 0x5d402 */
+ t_uint16 dma_int_length; /* ... 0x5d404 */
+ t_uint16 dma_ext_baseh;
+ t_uint16 dma_ext_basel;
+ t_uint16 dma_count;
+ t_uint16 dma_ext_length;
+ t_uint16 dma_it_status;
+} t_mmdsp_dma_ctrl_regs_16;
+
+typedef volatile struct {
+ t_uint32 dma_ctrl; /* dma0: 0x3a800, dma1: +0x20 ... */
+ t_uint32 dma_int_base; /* ... 0x3a804 */
+ t_uint32 dma_int_length; /* ... 0x3a808 */
+ t_uint32 dma_ext_baseh;
+ t_uint32 dma_ext_basel;
+ t_uint32 dma_count;
+ t_uint32 dma_ext_length;
+ t_uint32 dma_it_status;
+} t_mmdsp_dma_ctrl_regs_32;
+
+/* IO registers */
+typedef volatile struct {
+ t_mmdsp_field_16 io_bit[MMDSP_NB_IO];
+ t_mmdsp_field_16 io_lsb;
+ t_mmdsp_field_16 io_msb;
+ t_mmdsp_field_16 io_all;
+ t_mmdsp_field_16 io_en;
+} t_mmdsp_io_regs_16;
+
+typedef volatile struct {
+ t_mmdsp_field_32 io_bit[MMDSP_NB_IO];
+ t_mmdsp_field_32 io_lsb;
+ t_mmdsp_field_32 io_msb;
+ t_mmdsp_field_32 io_all;
+ t_mmdsp_field_32 io_en;
+} t_mmdsp_io_regs_32;
+
+/* HOST Registers bit mapping */
+#define HOST_GATEDCLK_ITREMAP MASK_BIT0
+#define HOST_GATEDCLK_SYSDMA MASK_BIT1
+#define HOST_GATEDCLK_INTEG_REGS MASK_BIT2
+#define HOST_GATEDCLK_TIMER_GPIO MASK_BIT3
+#define HOST_GATEDCLK_XBUSDMA MASK_BIT4
+#define HOST_GATEDCLK_STACKCTRL MASK_BIT5
+#define HOST_GATEDCLK_ITC MASK_BIT6
+
+/* Only for STn8820 and STn8500 */
+#define HOST_PWR_DBG_MODE MASK_BIT0
+#define HOST_PWR_DC_STATUS (MASK_BIT1 | MASK_BIT2 | MASK_BIT3 | MASK_BIT4 | MASK_BIT5)
+#define HOST_PWR_DE_STATUS MASK_BIT6
+#define HOST_PWR_STOV_STATUS MASK_BIT7
+
+/* HOST Registers */
+typedef volatile struct {
+ t_uint16 ident; /*0x...60000*/
+ t_uint16 identx[4]; /*0x...60002..8*/
+ t_uint16 r5; /*0x...6000a*/
+ t_uint16 r6; /*0x...6000c*/
+ t_uint16 inte[2]; /*0x...6000e..10*/
+ t_uint16 intx[2]; /*0x...60012..14*/
+ t_uint16 int_ris[2]; /*0x...60016..18*/
+ t_uint16 intpol; /*0x...6001a*/
+ t_uint16 pwr; /*0x...6001c*/ /* only on STn8820 and STn8500 */
+ t_uint16 gatedclk; /*0x...6001e*/
+ t_uint16 softreset; /*0x...60020*/
+ t_uint16 int_icr[2]; /*0x...60022..24*/
+ t_uint16 cmd[4]; /*0x...60026..2c*/
+ t_uint16 RESERVED4;
+ t_uint16 int_mis0; /*0x...60030*/
+ t_uint16 RESERVED5;
+ t_uint16 RESERVED6;
+ t_uint16 RESERVED7;
+ t_uint16 i2cdiv; /*0x...60038*/
+ t_uint16 int_mis1; /*0x...6003a*/
+ t_uint16 RESERVED8;
+ t_uint16 RESERVED9;
+ t_uint16 emul_udata[8]; /*0x...60040..4e*/
+ t_uint16 emul_uaddrl; /*0x...60050*/
+ t_uint16 emul_uaddrm; /*0x...60052*/
+ t_uint16 emul_ucmd; /*0x...60054*/
+ t_uint16 emul_ubkcmd; /*0x...60056*/
+ t_uint16 emul_bk2addl; /*0x...60058*/
+ t_uint16 emul_bk2addm; /*0x...6005a*/
+ t_uint16 emul_bk2addh; /*0x...6005c*/
+ t_uint16 emul_mdata[3]; /*0x...6005e..62*/
+ t_uint16 emul_maddl; /*0x...60064*/
+ t_uint16 emul_maddm; /*0x...60066*/
+ t_uint16 emul_mcmd; /*0x...60068*/
+ t_uint16 emul_maddh; /*0x...6006a*/
+ t_uint16 emul_uaddrh; /*0x...6006c*/
+ t_uint16 emul_bk_eql; /*0x...6006e*/
+ t_uint16 emul_bk_eqh; /*0x...60070*/
+ t_uint16 emul_bk_combi; /*0x...60072*/
+ t_uint16 emul_clockcmd; /*0x...60074*/
+ t_uint16 emul_stepcmd; /*0x...60076*/
+ t_uint16 emul_scanreg; /*0x...60078*/
+ t_uint16 emul_breakcountl; /*0x...6007a*/
+ t_uint16 emul_breakcounth; /*0x...6007c*/
+ t_uint16 emul_forcescan; /*0x...6007e*/
+ t_uint16 user_area[(0x200 - 0x80)>>1];
+} t_mmdsp_host_regs_16;
+
+typedef volatile struct {
+ t_uint32 ident; /*0x...60000*/
+ t_uint32 identx[4]; /*0x...60004..10*/
+ t_uint32 r5; /*0x...60014*/
+ t_uint32 r6; /*0x...60018*/
+ t_uint32 inte[2]; /*0x...6001c..20*/
+ t_uint32 intx[2]; /*0x...60024..28*/
+ t_uint32 int_ris[2]; /*0x...6002c..30*/
+ t_uint32 intpol; /*0x...60034*/
+ t_uint32 pwr; /*0x...60038*/ /* only on STn8820 and STn8500 */
+ t_uint32 gatedclk; /*0x...6003c*/
+ t_uint32 softreset; /*0x...60040*/
+ t_uint32 int_icr[2]; /*0x...60044..48*/
+ t_uint32 cmd[4]; /*0x...6004c..58*/
+ t_uint32 RESERVED4;
+ t_uint32 int_mis0; /*0x...60060*/
+ t_uint32 RESERVED5;
+ t_uint32 RESERVED6;
+ t_uint32 RESERVED7;
+ t_uint32 i2cdiv; /*0x...60070*/
+ t_uint32 int_mis1; /*0x...60074*/
+ t_uint32 RESERVED8;
+ t_uint32 RESERVED9;
+ t_uint32 emul_udata[8]; /*0x...60080..9c*/
+ t_uint32 emul_uaddrl; /*0x...600a0*/
+ t_uint32 emul_uaddrm; /*0x...600a4*/
+ t_uint32 emul_ucmd; /*0x...600a8*/
+ t_uint32 emul_ubkcmd; /*0x...600ac*/
+ t_uint32 emul_bk2addl; /*0x...600b0*/
+ t_uint32 emul_bk2addm; /*0x...600b4*/
+ t_uint32 emul_bk2addh; /*0x...600b8*/
+ t_uint32 emul_mdata[3]; /*0x...600bc..c4*/
+ t_uint32 emul_maddl; /*0x...600c8*/
+ t_uint32 emul_maddm; /*0x...600cc*/
+ t_uint32 emul_mcmd; /*0x...600d0*/
+ t_uint32 emul_maddh; /*0x...600d4*/
+ t_uint32 emul_uaddrh; /*0x...600d8*/
+ t_uint32 emul_bk_eql; /*0x...600dc*/
+ t_uint32 emul_bk_eqh; /*0x...600e0*/
+ t_uint32 emul_bk_combi; /*0x...600e4*/
+ t_uint32 emul_clockcmd; /*0x...600e8*/
+ t_uint32 emul_stepcmd; /*0x...600ec*/
+ t_uint32 emul_scanreg; /*0x...600f0*/
+ t_uint32 emul_breakcountl; /*0x...600f4*/
+ t_uint32 emul_breakcounth; /*0x...600f8*/
+ t_uint32 emul_forcescan; /*0x...600fc*/
+ t_uint32 user_area[(0x400 - 0x100)>>2];
+} t_mmdsp_host_regs_32;
+
+/* MMIO blocks */
+#if defined(__STN_8820) || defined(__STN_8500)
+typedef volatile struct {
+ t_uint16 RESERVED1[(0xD400-0x8000)>>1];
+
+ t_mmdsp_dma_ctrl_regs_16 dma_ctrl[MMDSP_NB_DMA_CTRL];
+
+ t_uint16 RESERVED2[(0xD800-0xD440)>>1];
+
+ t_mmdsp_dcache_regs_16 dcache;
+
+ t_uint16 RESERVED3[(0xE000-0xD840)>>1];
+
+ t_mmdsp_io_regs_16 io;
+
+ t_uint16 RESERVED4[(0x60-0x50)>>1];
+
+ t_mmdsp_timer_regs_16 timer[MMDSP_NB_TIMER];
+
+ t_uint16 RESERVED5[(0x410-0x78)>>1];
+
+ t_mmdsp_field_16 sem[MMDSP_NB_BIT_SEM];
+
+ t_uint16 RESERVED6[(0x450-0x430)>>1];
+
+ t_mmdsp_field_16 ipen;
+ t_uint16 itip_0;
+ t_uint16 itip_1;
+ t_uint16 itip_2;
+ t_uint16 itip_3;
+ t_uint16 itop_0;
+ t_uint16 itop_1;
+ t_uint16 itop_2;
+ t_uint16 itop_3;
+ t_uint16 RESERVED7[(0x8a-0x64)>>1];
+ t_uint16 itip_4;
+ t_uint16 itop_4;
+
+ t_uint16 RESERVED8[(0x7e0-0x48e)>>1];
+
+ t_mmdsp_field_16 id[4];
+ t_mmdsp_field_16 idp[4];
+
+ t_mmdsp_dma_if_regs_16 dma_if[MMDSP_NB_DMA_IF];
+
+ t_uint16 RESERVED9[(0xC00-0x900)>>1];
+
+ t_mmdsp_field_16 emu_unit_maskit;
+ t_mmdsp_field_16 RESERVED[3];
+ t_mmdsp_field_16 config_data_mem;
+ t_mmdsp_field_16 compatibility;
+
+ t_uint16 RESERVED10[(0xF000-0xEC18)>>1];
+
+ t_uint16 stbus_if_config;
+ t_uint16 stbus_if_mode;
+ t_uint16 stbus_if_status;
+ t_uint16 stbus_if_security;
+ t_uint16 stbus_if_flush;
+ t_uint16 stbus_reserved;
+ t_uint16 stbus_if_priority;
+ t_uint16 stbus_msb_attribut;
+
+ t_uint16 RESERVED11[(0xFC00-0xF010)>>1];
+
+ t_mmdsp_field_16 itremap_reg[MMDSP_NB_ITREMAP_REG];
+ t_mmdsp_field_16 itmsk_l_reg;
+ t_mmdsp_field_16 itmsk_h_reg;
+
+ t_uint16 RESERVED12[(0xfc9c - 0xfc88)>>1];
+
+ t_mmdsp_field_16 itmemo_l_reg;
+ t_mmdsp_field_16 itmeme_h_reg;
+
+ t_uint16 RESERVED13[(0xfd00 - 0xfca4)>>1];
+
+ t_mmdsp_field_16 itremap1_reg[MMDSP_NB_ITREMAP_REG];
+
+ t_uint16 RESERVED14[(0x60000 - 0x5fd80)>>1];
+} t_mmdsp_mmio_regs_16;
+
+
+typedef volatile struct {
+ t_uint32 RESERVED1[(0xa800)>>2];
+
+ t_mmdsp_dma_ctrl_regs_32 dma_ctrl[MMDSP_NB_DMA_CTRL];
+
+ t_uint32 RESERVED2[(0xb000-0xa880)>>2];
+
+ t_mmdsp_dcache_regs_32 dcache;
+
+ t_uint32 RESERVED3[(0xc000-0xb080)>>2];
+
+ t_mmdsp_io_regs_32 io;
+
+ t_uint32 RESERVED4[(0xc0-0xa0)>>2];
+
+ t_mmdsp_timer_regs_32 timer[MMDSP_NB_TIMER];
+
+ t_uint32 RESERVED5[(0x820-0x0f0)>>2];
+
+ t_mmdsp_field_32 sem[MMDSP_NB_BIT_SEM];
+
+ t_uint32 RESERVED6[(0x8a0-0x860)>>2];
+
+ t_mmdsp_field_32 ipen;
+ t_uint32 itip_0;
+ t_uint32 itip_1;
+ t_uint32 itip_2;
+ t_uint32 itip_3;
+ t_uint32 itop_0;
+ t_uint32 itop_1;
+ t_uint32 itop_2;
+ t_uint32 itop_3;
+ t_uint32 RESERVED7[(0x914-0x8c8)>>2];
+ t_uint32 itip_4;
+ t_uint32 itop_4;
+
+ t_uint32 RESERVED8[(0xcfc0-0xc91c)>>2];
+
+ t_mmdsp_field_32 id[4];
+ t_mmdsp_field_32 idp[4];
+
+ t_mmdsp_dma_if_regs_32 dma_if[MMDSP_NB_DMA_IF];
+
+ t_uint32 RESERVED9[(0x800-0x200)>>2];
+
+ t_mmdsp_field_32 emu_unit_maskit;
+ t_mmdsp_field_32 RESERVED[3];
+ t_mmdsp_field_32 config_data_mem;
+ t_mmdsp_field_32 compatibility;
+
+ t_uint32 RESERVED10[(0xE000-0xD830)>>2];
+
+ t_uint32 stbus_if_config;
+ t_uint32 stbus_if_mode;
+ t_uint32 stbus_if_status;
+ t_uint32 stbus_if_security;
+ t_uint32 stbus_if_flush;
+ t_uint32 stbus_reserved;
+ t_uint32 stbus_if_priority;
+ t_uint32 stbus_msb_attribut;
+
+ t_uint32 RESERVED11[(0xF800-0xE020)>>2];
+
+ t_mmdsp_field_32 itremap_reg[MMDSP_NB_ITREMAP_REG];
+ t_mmdsp_field_32 itmsk_l_reg;
+ t_mmdsp_field_32 itmsk_h_reg;
+
+ t_uint32 RESERVED12[(0xf938 - 0xf910)>>2];
+
+ t_mmdsp_field_32 itmemo_l_reg;
+ t_mmdsp_field_32 itmeme_h_reg;
+
+ t_uint32 RESERVED13[(0xfa00 - 0xf948)>>2];
+
+ t_mmdsp_field_32 itremap1_reg[MMDSP_NB_ITREMAP_REG];
+
+ t_uint32 RESERVED14[(0x40000 - 0x3fb00)>>2];
+} t_mmdsp_mmio_regs_32;
+#endif /* __STN_8820 or __STN_8500 */
+
+#ifdef __STN_8815
+typedef volatile struct {
+ t_uint16 RESERVED1[(0xD400-0x8000)>>1];
+
+ t_mmdsp_dma_ctrl_regs_16 dma_ctrl[MMDSP_NB_DMA_CTRL];
+
+ t_uint16 RESERVED2[(0xD800-0xD440)>>1];
+
+ t_mmdsp_dcache_regs_16 dcache;
+
+ t_uint16 RESERVED3[(0xE000-0xD840)>>1];
+
+ t_mmdsp_io_regs_16 io;
+
+ t_uint16 RESERVED4[(0x60-0x50)>>1];
+
+ t_mmdsp_timer_regs_16 timer[MMDSP_NB_TIMER];
+
+ t_uint16 RESERVED5[(0x410-0x78)>>1];
+
+ t_mmdsp_field_16 sem[MMDSP_NB_BIT_SEM];
+
+ t_uint16 RESERVED6[(0x450-0x430)>>1];
+
+ t_mmdsp_field_16 ipen;
+ t_uint16 itip_0;
+ t_uint16 itip_1;
+ t_uint16 itip_2;
+ t_uint16 itip_3;
+ t_uint16 itop_0;
+ t_uint16 itop_1;
+ t_uint16 itop_2;
+ t_uint16 itop_3;
+ t_uint16 RESERVED7[(0x8a-0x64)>>1];
+ t_uint16 itip_4;
+ t_uint16 itop_4;
+
+ t_uint16 RESERVED8[(0x7e0-0x48e)>>1];
+
+ t_mmdsp_field_16 id[4];
+ t_mmdsp_field_16 idp[4];
+
+ t_mmdsp_dma_if_regs_16 dma_if[MMDSP_NB_DMA_IF];
+
+ t_uint16 RESERVED9[(0xC00-0x900)>>1];
+
+ t_mmdsp_field_16 emu_unit_maskit;
+ t_mmdsp_field_16 RESERVED[3];
+ t_mmdsp_field_16 config_data_mem;
+ t_mmdsp_field_16 compatibility;
+
+ t_uint16 RESERVED10[(0xF000-0xEC18)>>1];
+
+ t_uint16 ahb_if_config;
+ t_uint16 ahb_if_mode;
+ t_uint16 ahb_if_status;
+ t_uint16 ahb_if_security;
+ t_uint16 ahb_if_flush;
+
+ t_uint16 RESERVED11[(0xFC00-0xF00A)>>1];
+
+ t_mmdsp_field_16 itremap_reg[MMDSP_NB_ITREMAP_REG];
+ t_mmdsp_field_16 itmsk_l_reg;
+ t_mmdsp_field_16 itmsk_h_reg;
+
+ t_uint16 RESERVED12[(0xfc9c - 0xfc88)>>1];
+
+ t_mmdsp_field_16 itmemo_l_reg;
+ t_mmdsp_field_16 itmeme_h_reg;
+
+ t_uint16 RESERVED13[(0xfd00 - 0xfca4)>>1];
+
+ t_mmdsp_field_16 itremap1_reg[MMDSP_NB_ITREMAP_REG];
+
+ t_uint16 RESERVED14[(0x60000 - 0x5fd80)>>1];
+} t_mmdsp_mmio_regs_16;
+
+
+typedef volatile struct {
+ t_uint32 RESERVED1[(0xa800)>>2];
+
+ t_mmdsp_dma_ctrl_regs_32 dma_ctrl[MMDSP_NB_DMA_CTRL];
+
+ t_uint32 RESERVED2[(0xb000-0xa880)>>2];
+
+ t_mmdsp_dcache_regs_32 dcache;
+
+ t_uint32 RESERVED3[(0xc000-0xb080)>>2];
+
+ t_mmdsp_io_regs_32 io;
+
+ t_uint32 RESERVED4[(0xc0-0xa0)>>2];
+
+ t_mmdsp_timer_regs_32 timer[MMDSP_NB_TIMER];
+
+ t_uint32 RESERVED5[(0x820-0x0f0)>>2];
+
+ t_mmdsp_field_32 sem[MMDSP_NB_BIT_SEM];
+
+ t_uint32 RESERVED6[(0x8a0-0x860)>>2];
+
+ t_mmdsp_field_32 ipen;
+ t_uint32 itip_0;
+ t_uint32 itip_1;
+ t_uint32 itip_2;
+ t_uint32 itip_3;
+ t_uint32 itop_0;
+ t_uint32 itop_1;
+ t_uint32 itop_2;
+ t_uint32 itop_3;
+ t_uint32 RESERVED7[(0x914-0x8c8)>>2];
+ t_uint32 itip_4;
+ t_uint32 itop_4;
+
+ t_uint32 RESERVED8[(0xcfc0-0xc91c)>>2];
+
+ t_mmdsp_field_32 id[4];
+ t_mmdsp_field_32 idp[4];
+
+ t_mmdsp_dma_if_regs_32 dma_if[MMDSP_NB_DMA_IF];
+
+ t_uint32 RESERVED9[(0x800-0x200)>>2];
+
+ t_mmdsp_field_32 emu_unit_maskit;
+ t_mmdsp_field_32 RESERVED[3];
+ t_mmdsp_field_32 config_data_mem;
+ t_mmdsp_field_32 compatibility;
+
+ t_uint32 RESERVED10[(0xE000-0xD830)>>2];
+
+ t_uint32 ahb_if_config;
+ t_uint32 ahb_if_mode;
+ t_uint32 ahb_if_status;
+ t_uint32 ahb_if_security;
+ t_uint32 ahb_if_flush;
+
+ t_uint32 RESERVED11[(0xF800-0xE014)>>2];
+
+ t_mmdsp_field_32 itremap_reg[MMDSP_NB_ITREMAP_REG];
+ t_mmdsp_field_32 itmsk_l_reg;
+ t_mmdsp_field_32 itmsk_h_reg;
+
+ t_uint32 RESERVED12[(0xf938 - 0xf910)>>2];
+
+ t_mmdsp_field_32 itmemo_l_reg;
+ t_mmdsp_field_32 itmeme_h_reg;
+
+ t_uint32 RESERVED13[(0xfa00 - 0xf948)>>2];
+
+ t_mmdsp_field_32 itremap1_reg[MMDSP_NB_ITREMAP_REG];
+
+ t_uint32 RESERVED14[(0x40000 - 0x3fb00)>>2];
+} t_mmdsp_mmio_regs_32;
+#endif /* __STN_8815 */
+
+/* Smart xx Accelerator memory map */
+typedef volatile struct {
+ t_uint32 mem24[MMDSP_NB_BLOCK_RAM*MMDSP_RAM_BLOCK_SIZE]; /* 0x0000 -> 0x20000 */
+
+ t_uint32 RESERVED1[(0x30000 - 0x20000)>>2];
+
+ t_mmdsp_mmio_regs_32 mmio_32;
+
+ t_uint16 mem16[MMDSP_NB_BLOCK_RAM*MMDSP_RAM_BLOCK_SIZE]; /* 0x40000 -> 0x50000 */
+
+ t_uint32 RESERVED2[(0x58000 - 0x50000)>>2];
+
+ t_mmdsp_mmio_regs_16 mmio_16;
+
+ t_mmdsp_host_regs_16 host_reg;
+ /*
+ union host_reg {
+ t_mmdsp_host_regs_16 reg16;
+ t_mmdsp_host_regs_32 reg32;
+ };
+ */
+} t_mmdsp_hw_regs;
+
+#endif // __INC_MMDSP_HWP_H
diff --git a/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_macros.h b/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_macros.h
new file mode 100644
index 00000000000..b8911d27609
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/dsp/mmdsp/inc/mmdsp_macros.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef __INC_MMDSP_DSP_MACROS
+#define __INC_MMDSP_DSP_MACROS
+
+#include <cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h>
+
+#define MMDSP_ENABLE_WRITE_POSTING(pRegs) \
+{ \
+ (pRegs)->mmio_16.dcache.control |= DCACHE_CONTROL_WRITE_POSTING_ENABLE; \
+}
+
+#define MMDSP_FLUSH_DCACHE(pRegs) \
+{ /* Today, only full cache flush (clear all the ways) */ \
+ (pRegs)->mmio_16.dcache.command = DCACHE_CMD_FLUSH; \
+}
+
+#define MMDSP_FLUSH_DCACHE_BY_SERVICE(pRegs, startAddr, endAddr)
+
+#define MMDSP_FLUSH_ICACHE(pRegs) \
+{ /* Flush the Instruction cache */ \
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_ICACHE_FLUSH_REG, (IHOST_ICACHE_FLUSH_ALL_ENTRIES_CMD | IHOST_ICACHE_FLUSH_CMD_ENABLE)); \
+}
+
+#ifndef __STN_8810
+#define MMDSP_FLUSH_ICACHE_BY_SERVICE(pRegs, startAddr, endAddr) \
+{ /* Flush the Instruction cache by service */ \
+ /*t_uint64 start_clear_addr = startAddr & ~(MMDSP_ICACHE_LINE_SIZE_IN_INST - 1);*/ \
+ t_uint64 start_clear_addr = (startAddr)>>2; \
+ t_uint64 end_clear_addr = ((endAddr) + MMDSP_ICACHE_LINE_SIZE_IN_INST) & ~(MMDSP_ICACHE_LINE_SIZE_IN_INST - 1); \
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_ICACHE_START_CLEAR_REG, start_clear_addr); \
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_ICACHE_END_CLEAR_REG, end_clear_addr); \
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_ICACHE_FLUSH_REG, (IHOST_ICACHE_FLUSH_BY_SERVICE | IHOST_ICACHE_FLUSH_CMD_ENABLE)); \
+}
+#else
+#define MMDSP_FLUSH_ICACHE_BY_SERVICE(pRegs, startAddr, endAddr) {(void)pRegs; (void)startAddr; (void)endAddr; }
+#endif
+
+#define MMDSP_RESET_CORE(pRegs) \
+{ /* Assert DSP core soft reset */ \
+ (pRegs)->host_reg.softreset = 1; \
+}
+
+#define MMDSP_START_CORE(pRegs) \
+{ \
+ /* Enable external memory access (set bit 3 of ubkcmd) */ \
+ (pRegs)->host_reg.emul_ubkcmd |= MMDSP_UBKCMD_EXT_CODE_MEM_ACCESS_ENABLE; \
+ \
+ /* Start core clock */ \
+ (pRegs)->host_reg.emul_clockcmd = MMDSP_CLOCKCMD_START_CLOCK; \
+}
+
+#define MMDSP_STOP_CORE(pRegs) \
+{ \
+ /* Disable external memory access (reset bit 3 of ubkcmd) */ \
+ (pRegs)->host_reg.emul_ubkcmd = MMDSP_UBKCMD_EXT_CODE_MEM_ACCESS_DISABLE; \
+ \
+ /* Stop core clock */ \
+ (pRegs)->host_reg.emul_clockcmd = MMDSP_CLOCKCMD_STOP_CLOCK; \
+}
+
+#define MMDSP_ASSERT_IRQ(pRegs, irqNum) \
+{ \
+ (pRegs)->host_reg.cmd[irqNum] = 1; \
+}
+
+#define MMDSP_ACKNOWLEDGE_IRQ(pRegs, irqNum) \
+{ \
+ volatile t_uint16 dummy; \
+ dummy =(pRegs)->host_reg.intx[irqNum]; \
+}
+
+#define MMDSP_WRITE_XWORD(pRegs, offset, value) \
+{ \
+ (pRegs)->mem24[offset] = value; \
+}
+
+#define MMDSP_READ_XWORD(pRegs, offset) (pRegs)->mem24[offset]
+
+#endif /* __INC_MMDSP_DSP_MACROS */
diff --git a/drivers/staging/nmf-cm/cm/engine/dsp/src/dsp.c b/drivers/staging/nmf-cm/cm/engine/dsp/src/dsp.c
new file mode 100644
index 00000000000..de4ea3b7ba5
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/dsp/src/dsp.c
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/dsp/inc/dsp.h>
+#include <cm/engine/dsp/mmdsp/inc/mmdsp_macros.h>
+
+#include <cm/engine/memory/inc/domain.h>
+#include <cm/engine/semaphores/inc/semaphores.h>
+#include <cm/engine/power_mgt/inc/power.h>
+#include <cm/engine/memory/inc/migration.h>
+#include <cm/engine/trace/inc/trace.h>
+
+#include <share/inc/nomadik_mapping.h>
+
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+#include <cm/engine/component/inc/component_type.h>
+
+static t_dsp_allocator_desc esramDesc;
+static t_dsp_desc mpcDesc[NB_CORE_IDS];
+static t_mmdsp_hw_regs *pMmdspRegs[NB_CORE_IDS];
+
+struct s_base_descr
+{
+ t_uint32 startAddress[2 /* DSP16 = 0, DSP24 = 1*/];
+ t_dsp_segment_type segmentType;
+};
+
+#if defined(__STN_8500) && (__STN_8500 > 10)
+
+#define DATA_BASE_NUMBER 4
+
+// In bytes
+#define SDRAM_CODE_SPACE_SPLIT 0x8000
+#define ESRAM_CODE_SPACE_SPLIT 0x4000
+#define SDRAM_DATA_SPACE_SPLIT 0x40000 // This is the modulo constraint of mmdsp
+#define ESRAM_DATA_SPACE_SPLIT 0x40000
+
+// In MMDSP word
+static const struct s_base_descr DATA_ADDRESS_BASE[DATA_BASE_NUMBER + 1 /* For guard */] = {
+ {{SDRAMMEM16_BASE_ADDR, SDRAMMEM24_BASE_ADDR}, SDRAM_DATA_EE},
+ {{SDRAMMEM16_BASE_ADDR + (SDRAM_DATA_SPACE_SPLIT / 2), SDRAMMEM24_BASE_ADDR + (SDRAM_DATA_SPACE_SPLIT / 4)}, SDRAM_DATA_USER},
+ {{ESRAMMEM16_BASE_ADDR, ESRAMMEM24_BASE_ADDR}, ESRAM_DATA_EE},
+ {{ESRAMMEM16_BASE_ADDR + (ESRAM_DATA_SPACE_SPLIT / 2), ESRAMMEM24_BASE_ADDR + (ESRAM_DATA_SPACE_SPLIT / 4)}, ESRAM_DATA_USER},
+ {{MMIO_BASE_ADDR, SDRAMMEM16_BASE_ADDR}, NB_DSP_SEGMENT_TYPE /* Not used*/}
+};
+
+#else
+
+#define DATA_BASE_NUMBER 2
+
+// In MMDSP word
+static const struct s_base_descr DATA_ADDRESS_BASE[DATA_BASE_NUMBER + 1 /* For guard */] = {
+ {{SDRAMMEM16_BASE_ADDR, SDRAMMEM24_BASE_ADDR}, SDRAM_DATA_EE},
+ {{ESRAMMEM16_BASE_ADDR, ESRAMMEM24_BASE_ADDR}, ESRAM_DATA_EE},
+ {{MMIO_BASE_ADDR, SDRAMMEM16_BASE_ADDR}, NB_DSP_SEGMENT_TYPE /* Not used*/}
+};
+
+#endif
+
+#if defined(__STN_8500) && (__STN_8500 > 10)
+// In word
+static const t_uint32 CODE_ADDRESS_BASE[4] = {
+ SDRAMTEXT_BASE_ADDR,
+ SDRAMTEXT_BASE_ADDR + (SDRAM_CODE_SPACE_SPLIT / 8),
+ ESRAMTEXT_BASE_ADDR,
+ ESRAMTEXT_BASE_ADDR + (ESRAM_CODE_SPACE_SPLIT / 8)
+};
+#endif
+
+static void arm_Init(void);
+static t_cm_error mmdsp_Init(const t_cm_system_address *dspSystemAddr,
+ t_uint8 nbXramBlocks, t_uint8 nbYramBlocks,
+ t_dsp_allocator_desc *sdramCodeDesc,
+ t_dsp_allocator_desc *sdramDataDesc,
+ t_cm_domain_id eeDomain,
+ t_dsp_desc *pDspDesc,
+ t_mmdsp_hw_regs **pRegs);
+static t_cm_error mmdsp_Configure(t_nmf_core_id coreId, t_mmdsp_hw_regs *pRegs, const t_dsp_desc *pDspDesc);
+static t_cm_error mmdsp_ConfigureAfterBoot(t_nmf_core_id coreId, t_uint8 nbXramBlocks, t_uint8 nbYramBlocks);
+static void cm_DSP_SEM_Init(t_nmf_core_id coreId);
+
+PUBLIC const t_dsp_desc* cm_DSP_GetState(t_nmf_core_id coreId)
+{
+ return &mpcDesc[coreId];
+}
+PUBLIC void cm_DSP_SetStatePanic(t_nmf_core_id coreId)
+{
+ mpcDesc[coreId].state = MPC_STATE_PANIC;
+}
+
+PUBLIC void cm_DSP_Init(const t_nmf_memory_segment *pEsramDesc)
+{
+ t_nmf_core_id coreId;
+ int i;
+
+ /* Create esram desc */
+ esramDesc.allocDesc = cm_MM_CreateAllocator(pEsramDesc->size, 0, "esram");
+ esramDesc.baseAddress = pEsramDesc->systemAddr;
+ esramDesc.referenceCounter = 1; // Don't free it with destroy mechanism
+
+ /* Create ARM */
+ arm_Init();
+
+ mpcDesc[ARM_CORE_ID].state = MPC_STATE_BOOTED;
+
+ /* Reset MPC configuration */
+ for (coreId = FIRST_MPC_ID; coreId <= LAST_CORE_ID; coreId++)
+ {
+ mpcDesc[coreId].state = MPC_STATE_UNCONFIGURED;
+
+ for(i = 0; i < NB_DSP_MEMORY_TYPE; i++)
+ mpcDesc[coreId].allocator[i] = NULL;
+ }
+
+}
+
+PUBLIC void cm_DSP_Destroy(void)
+{
+ t_nmf_core_id coreId;
+ int i;
+
+ for (coreId = ARM_CORE_ID; coreId <= LAST_CORE_ID; coreId++)
+ {
+ for(i = 0; i < NB_DSP_MEMORY_TYPE; i++)
+ {
+ if (mpcDesc[coreId].allocator[i] != NULL)
+ {
+ if(--mpcDesc[coreId].allocator[i]->referenceCounter == 0)
+ {
+ cm_MM_DeleteAllocator(mpcDesc[coreId].allocator[i]->allocDesc);
+
+ OSAL_Free(mpcDesc[coreId].allocator[i]);
+ }
+ }
+ }
+ }
+
+ cm_MM_DeleteAllocator(esramDesc.allocDesc);
+}
+
+
+PUBLIC t_cm_error cm_DSP_Add(t_nmf_core_id coreId,
+ t_uint8 nbYramBanks,
+ const t_cm_system_address *pDspMapDesc,
+ const t_cm_domain_id eeDomain,
+ t_dsp_allocator_desc *sdramCodeAllocDesc,
+ t_dsp_allocator_desc *sdramDataAllocDesc)
+{
+ t_cm_error error;
+
+ /* checking nbYramBanks is valid */
+ if (nbYramBanks >= SxA_NB_BLOCK_RAM)
+ return CM_MPC_INVALID_CONFIGURATION;
+
+ if((error = cm_DM_CheckDomain(eeDomain, DOMAIN_NORMAL)) != CM_OK)
+ return error;
+
+ mpcDesc[coreId].domainEE = eeDomain;
+ mpcDesc[coreId].nbYramBank = nbYramBanks;
+ mpcDesc[coreId].state = MPC_STATE_BOOTABLE;
+
+ return mmdsp_Init(
+ pDspMapDesc,
+ SxA_NB_BLOCK_RAM, /* nb of data tcm bank minus one (reserved for cache) */
+ nbYramBanks,
+ sdramCodeAllocDesc,
+ sdramDataAllocDesc,
+ eeDomain,
+ &mpcDesc[coreId],
+ &pMmdspRegs[coreId]
+ );
+}
+
+PUBLIC t_cm_error cm_DSP_Boot(t_nmf_core_id coreId)
+{
+ t_cm_error error;
+
+ // Enable the associated power domain
+ if((error = cm_PWR_EnableMPC(MPC_PWR_CLOCK, coreId)) != CM_OK)
+ return error;
+
+ cm_SEM_PowerOn[coreId](coreId);
+
+ if((error = mmdsp_Configure(
+ coreId,
+ pMmdspRegs[coreId],
+ &mpcDesc[coreId])) != CM_OK)
+ {
+ cm_PWR_DisableMPC(MPC_PWR_CLOCK, coreId);
+ }
+
+ // Put it in auto idle mode ; it's the default in Step 2 of power implementation
+ if((error = cm_PWR_EnableMPC(MPC_PWR_AUTOIDLE, coreId)) != CM_OK)
+ return error;
+
+ return error;
+}
+
+/*
+ * This method is required since MMDSP C bootstrap set some value that must be set differently !!!
+ */
+PUBLIC void cm_DSP_ConfigureAfterBoot(t_nmf_core_id coreId)
+{
+ mpcDesc[coreId].state = MPC_STATE_BOOTED;
+
+ mmdsp_ConfigureAfterBoot(coreId, SxA_NB_BLOCK_RAM, mpcDesc[coreId].nbYramBank);
+
+ cm_DSP_SEM_Init(coreId);
+}
+
+PUBLIC void cm_DSP_Stop(t_nmf_core_id coreId)
+{
+ MMDSP_STOP_CORE(pMmdspRegs[coreId]);
+
+ {
+ volatile t_uint32 loopme = 0xfff;
+ while(loopme--) ;
+ }
+}
+
+PUBLIC void cm_DSP_Start(t_nmf_core_id coreId)
+{
+ MMDSP_START_CORE(pMmdspRegs[coreId]);
+
+ {
+ volatile t_uint32 loopme = 0xfff;
+ while(loopme--) ;
+ }
+}
+
+PUBLIC void cm_DSP_Shutdown(t_nmf_core_id coreId)
+{
+ MMDSP_FLUSH_DCACHE(pMmdspRegs[coreId]);
+ MMDSP_FLUSH_ICACHE(pMmdspRegs[coreId]);
+
+ // Due to a hardware bug that breaks MTU when DSP are powered off, don't do that
+ // on mop500_ed for now
+#if !defined(__STN_8500) || (__STN_8500 > 10)
+ MMDSP_RESET_CORE(pMmdspRegs[coreId]);
+ {
+ volatile t_uint32 loopme = 0xfff;
+ while(loopme--) ;
+ }
+ MMDSP_STOP_CORE(pMmdspRegs[coreId]);
+ {
+ volatile t_uint32 loopme = 0xfff;
+ while(loopme--) ;
+ }
+#endif
+
+ mpcDesc[coreId].state = MPC_STATE_BOOTABLE;
+
+ cm_SEM_PowerOff[coreId](coreId);
+
+ cm_PWR_DisableMPC(MPC_PWR_AUTOIDLE, coreId);
+ cm_PWR_DisableMPC(MPC_PWR_CLOCK, coreId);
+}
+
+PUBLIC t_uint32 cm_DSP_ReadXRamWord(t_nmf_core_id coreId, t_uint32 dspOffset)
+{
+ t_uint32 value;
+
+ value = pMmdspRegs[coreId]->mem24[dspOffset];
+
+ LOG_INTERNAL(3, "cm_DSP_ReadXRamWord: [%x]=%x\n",
+ dspOffset, value,
+ 0, 0, 0, 0);
+
+ return value;
+}
+
+
+PUBLIC void cm_DSP_WriteXRamWord(t_nmf_core_id coreId, t_uint32 dspOffset, t_uint32 value)
+{
+ LOG_INTERNAL(3, "cm_DSP_WriteXRamWord: [%x]<-%x\n",
+ dspOffset, value,
+ 0, 0, 0, 0);
+
+ pMmdspRegs[coreId]->mem24[dspOffset] = value;
+}
+
+static void cm_DSP_SEM_Init(t_nmf_core_id coreId)
+{
+ pMmdspRegs[coreId]->mmio_16.sem[1].value = 1;
+}
+
+PUBLIC void cm_DSP_SEM_Take(t_nmf_core_id coreId, t_semaphore_id semId)
+{
+ /* take semaphore */
+ while(pMmdspRegs[coreId]->mmio_16.sem[1].value) ;
+}
+
+PUBLIC void cm_DSP_SEM_Give(t_nmf_core_id coreId, t_semaphore_id semId)
+{
+ /* release semaphore */
+ pMmdspRegs[coreId]->mmio_16.sem[1].value = 1;
+}
+
+PUBLIC void cm_DSP_SEM_GenerateIrq(t_nmf_core_id coreId, t_semaphore_id semId)
+{
+ MMDSP_ASSERT_IRQ(pMmdspRegs[coreId], ARM2DSP_IRQ_0);
+}
+
+
+PUBLIC void cm_DSP_AssertDspIrq(t_nmf_core_id coreId, t_host2mpc_irq_num irqNum)
+{
+ MMDSP_ASSERT_IRQ(pMmdspRegs[coreId], irqNum);
+ return;
+}
+
+PUBLIC void cm_DSP_AcknowledgeDspIrq(t_nmf_core_id coreId, t_mpc2host_irq_num irqNum)
+{
+ MMDSP_ACKNOWLEDGE_IRQ(pMmdspRegs[coreId], irqNum);
+ return;
+}
+
+//TODO, juraj, cleanup INTERNAL_XRAM vs INTERNAL_XRAM16/24
+static const t_uint32 dspMemoryTypeId2OffsetShifter[NB_DSP_MEMORY_TYPE] =
+{
+ 2, /* INTERNAL_XRAM24: Internal X memory but seen by host as 32-bit memory */
+ 2, /* INTERNAL_XRAM16: Internal X memory but seen by host as 16-bit memory */
+ 2, /* INTERNAL_YRAM24: Internal Y memory but seen by host as 32-bit memory */
+ 2, /* INTERNAL_YRAM16: Internal Y memory but seen by host as 16-bit memory */
+ 2, /* SDRAM_EXT24: 24-bit external "X" memory */
+ 1, /* SDRAM_EXT16: 16-bit external "X" memory */
+ 2, /* ESRAM_EXT24: ESRAM24 */
+ 1, /* ESRAM_EXT16: ESRAM16 */
+ 3, /* SDRAM_CODE: Program memory */
+ 3, /* ESRAM_CODE: ESRAM code */
+ 3, /* LOCKED_CODE: ESRAM code */
+};
+
+//TODO, juraj, use these values in mmdsp_Configure
+static const t_uint32 dspMemoryTypeId2DspAddressOffset[NB_DSP_MEMORY_TYPE] =
+{
+ 0, /* INTERNAL_XRAM24 */
+ 0, /* INTERNAL_XRAM16 */
+ 0, /* INTERNAL_YRAM24 */
+ 0, /* INTERNAL_YRAM16 */
+ SDRAMMEM24_BASE_ADDR, /* SDRAM_EXT24: 24-bit external "X" memory */
+ SDRAMMEM16_BASE_ADDR, /* SDRAM_EXT16: 16-bit external "X" memory */
+ ESRAMMEM24_BASE_ADDR, /* ESRAM_EXT24: ESRAM24 */
+ ESRAMMEM16_BASE_ADDR, /* ESRAM_EXT16: ESRAM16 */
+ SDRAMTEXT_BASE_ADDR, /* SDRAM_CODE: Program memory */
+ ESRAMTEXT_BASE_ADDR, /* ESRAM_CODE: ESRAM code */
+ SDRAMTEXT_BASE_ADDR, /* ESRAM_CODE: ESRAM code */
+};
+
+PUBLIC t_cm_allocator_desc* cm_DSP_GetAllocator(t_nmf_core_id coreId, t_dsp_memory_type_id memType)
+{
+ return mpcDesc[coreId].allocator[memType] ? mpcDesc[coreId].allocator[memType]->allocDesc : NULL;
+}
+
+PUBLIC void cm_DSP_GetDspChunkInfo(t_memory_handle memHandle, t_dsp_chunk_info *info)
+{
+ t_uint16 userData;
+
+ cm_MM_GetMemoryHandleUserData(memHandle, &userData, &info->alloc);
+
+ info->coreId = (t_nmf_core_id) ((userData >> SHIFT_BYTE1) & MASK_BYTE0);
+ info->memType = (t_dsp_memory_type_id)((userData >> SHIFT_BYTE0) & MASK_BYTE0);
+}
+
+PUBLIC t_cm_error cm_DSP_GetInternalMemoriesInfo(t_cm_domain_id domainId, t_dsp_memory_type_id memType,
+ t_uint32 *offset, t_uint32 *size)
+{
+ t_nmf_core_id coreId = domainDesc[domainId].domain.coreId;
+
+ switch(memType)
+ {
+ case INTERNAL_XRAM24:
+ case INTERNAL_XRAM16:
+ *offset = 0;
+ *size = mpcDesc[coreId].yram_offset;
+ break;
+ case INTERNAL_YRAM24:
+ case INTERNAL_YRAM16:
+ *offset = mpcDesc[coreId].yram_offset;
+ *size = mpcDesc[coreId].yram_size;
+ break;
+ case LOCKED_CODE:
+ *offset = mpcDesc[coreId].locked_offset;
+ *size = mpcDesc[coreId].locked_size;
+ break;
+ case SDRAM_EXT24:
+ case SDRAM_EXT16:
+ *offset = domainDesc[domainId].domain.sdramData.offset;
+ *size = domainDesc[domainId].domain.sdramData.size;
+ break;
+ case ESRAM_EXT24:
+ case ESRAM_EXT16:
+ *offset = domainDesc[domainId].domain.esramData.offset;
+ *size = domainDesc[domainId].domain.esramData.size;
+ break;
+ case SDRAM_CODE:
+ *offset = domainDesc[domainId].domain.sdramCode.offset;
+ *size = domainDesc[domainId].domain.sdramCode.size;
+
+ // update domain size to take into account .locked section
+ if(*offset + *size > mpcDesc[coreId].locked_offset)
+ *size = mpcDesc[coreId].locked_offset - *offset;
+ break;
+ case ESRAM_CODE:
+ *offset = domainDesc[domainId].domain.esramCode.offset;
+ *size = domainDesc[domainId].domain.esramCode.size;
+ break;
+ default:
+ //return CM_INVALID_PARAMETER;
+ //params are checked at the level above, so this should never occur
+ ERROR("Invalid memType\n",0,0,0,0,0,0);
+ *offset = 0;
+ *size = 0;
+ CM_ASSERT(0);
+ }
+
+ return CM_OK;
+}
+
+
+PUBLIC t_uint32 cm_DSP_ConvertSize(t_dsp_memory_type_id memType, t_uint32 wordSize)
+{
+ return wordSize << dspMemoryTypeId2OffsetShifter[memType];
+}
+
+PUBLIC t_cm_logical_address cm_DSP_ConvertDspAddressToHostLogicalAddress(t_nmf_core_id coreId, t_shared_addr dspAddress)
+{
+ t_dsp_address_info info;
+ cm_DSP_GetDspDataAddressInfo(coreId, dspAddress, &info);
+ return mpcDesc[coreId].segments[info.segmentType].base.logical + info.baseOffset;
+}
+
+PUBLIC t_cm_error cm_DSP_GetAllocatorStatus(t_nmf_core_id coreId, t_dsp_memory_type_id dspMemType, t_uint32 offset, t_uint32 size, t_cm_allocator_status *pStatus)
+{
+ t_cm_error error;
+
+ if(mpcDesc[coreId].allocator[dspMemType] == NULL)
+ return CM_UNKNOWN_MEMORY_HANDLE;
+
+ error = cm_MM_GetAllocatorStatus(cm_DSP_GetAllocator(coreId, dspMemType), offset, size, pStatus);
+ if (error != CM_OK)
+ return error;
+
+ // complete status with stack sizes, for all dsps
+ //NOTE, well, surely this isn't very clean, as dsp and memory allocator are different things ..
+ {
+ t_uint8 i;
+ for (i = 0; i < NB_CORE_IDS; i++) {
+ //*(pStatus->stack[i].sizes) = *(eeState[i].currentStackSize);
+ pStatus->stack[i].sizes[0] = eeState[i].currentStackSize[0];
+ pStatus->stack[i].sizes[1] = eeState[i].currentStackSize[1];
+ pStatus->stack[i].sizes[2] = eeState[i].currentStackSize[2];
+ }
+ }
+
+ // Change bytes to words
+ pStatus->global.accumulate_free_memory = pStatus->global.accumulate_free_memory >> dspMemoryTypeId2OffsetShifter[dspMemType];
+ pStatus->global.accumulate_used_memory = pStatus->global.accumulate_used_memory >> dspMemoryTypeId2OffsetShifter[dspMemType];
+ pStatus->global.maximum_free_size = pStatus->global.maximum_free_size >> dspMemoryTypeId2OffsetShifter[dspMemType];
+ pStatus->global.minimum_free_size = pStatus->global.minimum_free_size >> dspMemoryTypeId2OffsetShifter[dspMemType];
+
+ return error;
+}
+
+PUBLIC void cm_DSP_GetHostSystemAddress(t_memory_handle memHandle, t_cm_system_address *pAddr)
+{
+ t_dsp_chunk_info chunk_info;
+ t_uint32 offset; //in bytes
+
+ cm_DSP_GetDspChunkInfo(memHandle, &chunk_info);
+
+ offset = cm_MM_GetOffset(memHandle);
+
+ /* MMDSP mem16 array is very specific to host access, so .... */
+ /* We compute by hand the Host System address to take into account the specifities of the mmdsp mem16 array */
+ /* 1 dsp word = 2 host bytes AND mem16 array is "exported" by MMDSP External Bus wrapper at the 0x40000 offet */
+ if (chunk_info.memType == INTERNAL_XRAM16 || chunk_info.memType == INTERNAL_YRAM16) {
+ offset = (offset >> 1) + FIELD_OFFSET(t_mmdsp_hw_regs, mem16);
+ }
+
+ //TODO, juraj, calculate correct value here - based on segments desc etc..
+ pAddr->logical = mpcDesc[chunk_info.coreId].allocator[chunk_info.memType]->baseAddress.logical + offset;
+ pAddr->physical = mpcDesc[chunk_info.coreId].allocator[chunk_info.memType]->baseAddress.physical + offset;
+}
+
+
+PUBLIC t_physical_address cm_DSP_GetPhysicalAdress(t_memory_handle memHandle)
+{
+ t_cm_system_address addr;
+ cm_DSP_GetHostSystemAddress(memHandle, &addr);
+ return addr.physical;
+}
+
+PUBLIC t_cm_logical_address cm_DSP_GetHostLogicalAddress(t_memory_handle memHandle)
+{
+ t_cm_system_address addr;
+ cm_DSP_GetHostSystemAddress(memHandle, &addr);
+ return addr.logical;
+}
+
+PUBLIC void cm_DSP_GetDspAddress(t_memory_handle memHandle, t_uint32 *pDspAddress)
+{
+ t_dsp_chunk_info chunk_info;
+
+ cm_DSP_GetDspChunkInfo(memHandle, &chunk_info);
+
+ *pDspAddress =
+ (cm_MM_GetOffset(memHandle) >> dspMemoryTypeId2OffsetShifter[chunk_info.memType]) +
+ dspMemoryTypeId2DspAddressOffset[chunk_info.memType];
+}
+
+PUBLIC t_cm_error cm_DSP_GetDspBaseAddress(t_nmf_core_id coreId, t_dsp_memory_type_id memType, t_cm_system_address *pAddr)
+{
+ cm_migration_check_state(coreId, STATE_NORMAL);
+ *pAddr = mpcDesc[coreId].allocator[memType]->baseAddress;
+ return CM_OK;
+}
+
+PUBLIC void cm_DSP_GetDspMemoryHandleSize(t_memory_handle memHandle, t_uint32 *pDspSize)
+{
+ t_dsp_chunk_info chunk_info;
+ cm_DSP_GetDspChunkInfo(memHandle, &chunk_info);
+ *pDspSize = cm_MM_GetSize(memHandle) >> dspMemoryTypeId2OffsetShifter[chunk_info.memType];
+}
+
+PUBLIC t_cm_error cm_DSP_setStackSize(t_nmf_core_id coreId, t_uint32 newStackSize)
+{
+ t_uint8 nbXramBanks;
+ t_uint32 xramSize;
+
+ /* compute size of xram allocator */
+ nbXramBanks = SxA_NB_BLOCK_RAM - mpcDesc[coreId].nbYramBank;
+
+ /* check first that required stack size is less then xram memory ....*/
+ if (newStackSize >= nbXramBanks * 4 * ONE_KB)
+ return CM_NO_MORE_MEMORY;
+
+ /* compute new xram allocator size */
+ xramSize = nbXramBanks * 4 * ONE_KB - newStackSize;
+
+ /* try to resize it */
+ return cm_MM_ResizeAllocator(cm_DSP_GetAllocator(coreId, INTERNAL_XRAM24),
+ xramSize << dspMemoryTypeId2OffsetShifter[INTERNAL_XRAM24]);
+}
+
+PUBLIC t_cm_error cm_DSP_IsNbYramBanksValid(t_nmf_core_id coreId, t_uint8 nbYramBanks)
+{
+ /* we use one bank for cache */
+ t_uint8 nbOfRamBanksWithCacheReserved = SxA_NB_BLOCK_RAM;
+
+ /* we want to keep at least one bank of xram */
+ if (nbYramBanks < nbOfRamBanksWithCacheReserved) {return CM_OK;}
+ else {return CM_MPC_INVALID_CONFIGURATION;}
+}
+
+PUBLIC t_uint32 cm_DSP_getStackAddr(t_nmf_core_id coreId)
+{
+ /* we use one bank for cache */
+ //t_uint8 nbOfRamBanksWithCacheReserved = SxA_NB_BLOCK_RAM;
+ /* */
+ //return ((nbOfRamBanksWithCacheReserved * MMDSP_RAM_BLOCK_SIZE * MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE) - mpcDesc[coreId].yram_offset);
+ return mpcDesc[coreId].yram_offset / MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE;
+}
+
+static void arm_Init(void)
+{
+ mpcDesc[ARM_CORE_ID].allocator[INTERNAL_XRAM24] = 0;
+ mpcDesc[ARM_CORE_ID].allocator[INTERNAL_XRAM16] = 0;
+
+ mpcDesc[ARM_CORE_ID].allocator[INTERNAL_YRAM24] = 0;
+ mpcDesc[ARM_CORE_ID].allocator[INTERNAL_YRAM16] = 0;
+
+ mpcDesc[ARM_CORE_ID].allocator[SDRAM_CODE] = 0;
+ mpcDesc[ARM_CORE_ID].allocator[ESRAM_CODE] = 0;
+
+ mpcDesc[ARM_CORE_ID].allocator[SDRAM_EXT16] = 0;
+ mpcDesc[ARM_CORE_ID].allocator[SDRAM_EXT24] = 0;
+
+ mpcDesc[ARM_CORE_ID].allocator[ESRAM_EXT16] = &esramDesc;
+ mpcDesc[ARM_CORE_ID].allocator[ESRAM_EXT16]->referenceCounter++;
+ mpcDesc[ARM_CORE_ID].allocator[ESRAM_EXT24] = &esramDesc;
+ mpcDesc[ARM_CORE_ID].allocator[ESRAM_EXT24]->referenceCounter++;
+}
+
+static void _init_Segment(
+ t_dsp_segment *seg,
+ const t_cm_system_address base, const t_uint32 arm_offset,
+ const t_uint32 size)
+{
+ seg->base.logical = base.logical + arm_offset;
+ seg->base.physical = base.physical + arm_offset;
+ seg->size = size;
+}
+
+static t_cm_error mmdsp_Init(
+ const t_cm_system_address *dspSystemAddr,
+ t_uint8 nbXramBlocks, t_uint8 nbYramBlocks,
+ t_dsp_allocator_desc *sdramCodeDesc,
+ t_dsp_allocator_desc *sdramDataDesc,
+ t_cm_domain_id eeDomain,
+ t_dsp_desc *pDspDesc,
+ t_mmdsp_hw_regs **pRegs)
+{
+ t_cm_system_address xramSysAddr;
+ t_uint32 sizeInBytes;
+
+ /* Initialize reference on hw ressources */
+ *pRegs = (t_mmdsp_hw_regs *) dspSystemAddr->logical;
+
+ /* Initialize memory segments management */
+ xramSysAddr.logical = (t_cm_logical_address)(((t_mmdsp_hw_regs *)dspSystemAddr->logical)->mem24);
+ xramSysAddr.physical = (t_cm_physical_address)(((t_mmdsp_hw_regs *)dspSystemAddr->physical)->mem24);
+
+ /* The last (x)ram block will be used by cache, so ... */
+ /* And the NB_YRAM_BLOCKS last available block(s) will be used as YRAM */
+
+ /* XRAM*/
+ pDspDesc->allocator[INTERNAL_XRAM16] = pDspDesc->allocator[INTERNAL_XRAM24] = (t_dsp_allocator_desc*)OSAL_Alloc(sizeof (t_dsp_allocator_desc));
+ if (pDspDesc->allocator[INTERNAL_XRAM24] == NULL)
+ return CM_NO_MORE_MEMORY;
+
+ pDspDesc->allocator[INTERNAL_XRAM24]->allocDesc = cm_MM_CreateAllocator(
+ ((nbXramBlocks-nbYramBlocks)*MMDSP_RAM_BLOCK_SIZE)*MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE,
+ 0,
+ "XRAM");
+ pDspDesc->allocator[INTERNAL_XRAM24]->baseAddress = xramSysAddr;
+ pDspDesc->allocator[INTERNAL_XRAM24]->referenceCounter = 2;
+
+ /* YRAM */
+ pDspDesc->allocator[INTERNAL_YRAM16] = pDspDesc->allocator[INTERNAL_YRAM24] = (t_dsp_allocator_desc*)OSAL_Alloc(sizeof (t_dsp_allocator_desc));
+ if (pDspDesc->allocator[INTERNAL_YRAM24] == 0) {
+ OSAL_Free(pDspDesc->allocator[INTERNAL_XRAM24]);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ pDspDesc->allocator[INTERNAL_YRAM24]->allocDesc = cm_MM_CreateAllocator(
+ (nbYramBlocks*MMDSP_RAM_BLOCK_SIZE)*MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE,
+ ((nbXramBlocks-nbYramBlocks)*MMDSP_RAM_BLOCK_SIZE)*MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE,
+ "YRAM");
+ pDspDesc->allocator[INTERNAL_YRAM24]->baseAddress = xramSysAddr; /* use xram base address but offset is not null */
+ pDspDesc->allocator[INTERNAL_YRAM24]->referenceCounter = 2;
+
+ pDspDesc->yram_offset = ((nbXramBlocks-nbYramBlocks)*MMDSP_RAM_BLOCK_SIZE)*MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE;
+ pDspDesc->yram_size = (nbYramBlocks*MMDSP_RAM_BLOCK_SIZE)*MMDSP_DATA_WORD_SIZE_IN_HOST_SPACE;
+
+ /* SDRAM & ESRAM */
+ pDspDesc->allocator[SDRAM_CODE] = sdramCodeDesc;
+ pDspDesc->allocator[SDRAM_CODE]->referenceCounter++;
+ pDspDesc->allocator[ESRAM_CODE] = &esramDesc;
+ pDspDesc->allocator[ESRAM_CODE]->referenceCounter++;
+
+ /* LOCKED CODE at end of SDRAM code*/
+ pDspDesc->allocator[LOCKED_CODE] = sdramCodeDesc;
+ pDspDesc->allocator[LOCKED_CODE]->referenceCounter++;
+
+ pDspDesc->locked_offset = cm_MM_GetAllocatorSize(pDspDesc->allocator[SDRAM_CODE]->allocDesc) - MMDSP_CODE_CACHE_WAY_SIZE * 8 * SxA_LOCKED_WAY;
+ pDspDesc->locked_size = MMDSP_CODE_CACHE_WAY_SIZE * 8 * SxA_LOCKED_WAY;
+
+ /* Data_16/24 memory management */
+ pDspDesc->allocator[SDRAM_EXT16] = sdramDataDesc;
+ pDspDesc->allocator[SDRAM_EXT16]->referenceCounter++;
+ pDspDesc->allocator[SDRAM_EXT24] = sdramDataDesc;
+ pDspDesc->allocator[SDRAM_EXT24]->referenceCounter++;
+
+ pDspDesc->allocator[ESRAM_EXT16] = &esramDesc;
+ pDspDesc->allocator[ESRAM_EXT16]->referenceCounter++;
+ pDspDesc->allocator[ESRAM_EXT24] = &esramDesc;
+ pDspDesc->allocator[ESRAM_EXT24]->referenceCounter++;
+
+ sizeInBytes = cm_MM_GetAllocatorSize(pDspDesc->allocator[SDRAM_CODE]->allocDesc);
+#if defined(__STN_8500) && (__STN_8500 > 10)
+ _init_Segment(&pDspDesc->segments[SDRAM_CODE_EE],
+ pDspDesc->allocator[SDRAM_CODE]->baseAddress,
+ domainDesc[eeDomain].domain.sdramCode.offset,
+ domainDesc[eeDomain].domain.sdramCode.size);
+ _init_Segment(&pDspDesc->segments[SDRAM_CODE_USER],
+ pDspDesc->allocator[SDRAM_CODE]->baseAddress,
+ domainDesc[eeDomain].domain.sdramCode.offset + domainDesc[eeDomain].domain.sdramCode.size,
+ sizeInBytes - domainDesc[eeDomain].domain.sdramCode.size);
+#else
+ _init_Segment(&pDspDesc->segments[SDRAM_CODE_EE],
+ pDspDesc->allocator[SDRAM_CODE]->baseAddress,
+ 0x0,
+ sizeInBytes);
+#endif
+
+ sizeInBytes = cm_MM_GetAllocatorSize(pDspDesc->allocator[ESRAM_CODE]->allocDesc);
+#if defined(__STN_8500) && (__STN_8500 > 10)
+ _init_Segment(&pDspDesc->segments[ESRAM_CODE_EE],
+ pDspDesc->allocator[ESRAM_CODE]->baseAddress,
+ domainDesc[eeDomain].domain.esramCode.offset,
+ domainDesc[eeDomain].domain.esramCode.size);
+ _init_Segment(&pDspDesc->segments[ESRAM_CODE_USER],
+ pDspDesc->allocator[ESRAM_CODE]->baseAddress,
+ domainDesc[eeDomain].domain.esramCode.offset + domainDesc[eeDomain].domain.esramCode.size,
+ sizeInBytes - domainDesc[eeDomain].domain.esramCode.size);
+#else
+ _init_Segment(&pDspDesc->segments[ESRAM_CODE_EE],
+ pDspDesc->allocator[ESRAM_CODE]->baseAddress,
+ 0x0,
+ sizeInBytes);
+#endif
+
+ //the difference in the following code is the segment size used to calculate the top!!
+ sizeInBytes = cm_MM_GetAllocatorSize(pDspDesc->allocator[SDRAM_EXT16]->allocDesc);
+#if defined(__STN_8500) && (__STN_8500 > 10)
+ _init_Segment(&pDspDesc->segments[SDRAM_DATA_EE],
+ pDspDesc->allocator[SDRAM_EXT16]->baseAddress,
+ domainDesc[eeDomain].domain.sdramData.offset,
+ domainDesc[eeDomain].domain.sdramData.size);
+ _init_Segment(&pDspDesc->segments[SDRAM_DATA_USER],
+ pDspDesc->allocator[SDRAM_EXT16]->baseAddress,
+ domainDesc[eeDomain].domain.sdramData.offset + domainDesc[eeDomain].domain.sdramData.size,
+ sizeInBytes - domainDesc[eeDomain].domain.sdramData.size);
+#else
+ _init_Segment(&pDspDesc->segments[SDRAM_DATA_EE],
+ pDspDesc->allocator[SDRAM_EXT16]->baseAddress,
+ 0x0,
+ sizeInBytes);
+#endif
+
+ sizeInBytes = cm_MM_GetAllocatorSize(pDspDesc->allocator[ESRAM_EXT16]->allocDesc);
+#if defined(__STN_8500) && (__STN_8500 > 10)
+ _init_Segment(&pDspDesc->segments[ESRAM_DATA_EE],
+ pDspDesc->allocator[ESRAM_EXT16]->baseAddress,
+ domainDesc[eeDomain].domain.esramData.offset,
+ domainDesc[eeDomain].domain.esramData.size);
+ _init_Segment(&pDspDesc->segments[ESRAM_DATA_USER],
+ pDspDesc->allocator[ESRAM_EXT16]->baseAddress,
+ domainDesc[eeDomain].domain.esramData.offset + domainDesc[eeDomain].domain.esramData.size,
+ sizeInBytes - domainDesc[eeDomain].domain.esramData.size);
+#else
+ _init_Segment(&pDspDesc->segments[ESRAM_DATA_EE],
+ pDspDesc->allocator[ESRAM_EXT16]->baseAddress,
+ 0x0,
+ sizeInBytes);
+#endif
+
+ return CM_OK;
+}
+
+//TODO, juraj, reuse cm_DSP_UpdateBase functions
+static t_cm_error mmdsp_Configure(t_nmf_core_id coreId, t_mmdsp_hw_regs *pRegs, const t_dsp_desc *pDspDesc)
+{
+ t_uint64 regValue;
+ static const t_uint64 coreId2stbusId[NB_CORE_IDS] =
+ {
+ 0, /* ARM_CORE_ID no meaning */
+ SVA_STBUS_ID, /* SVA_CORE_ID */
+ SIA_STBUS_ID /* SIA_CORE_ID */
+ };
+
+ //t_cm_system_address sysAddr;
+ //t_cm_size sizeInBytes;
+
+ /* Stop core (stop clock) */
+ MMDSP_RESET_CORE(pRegs);
+ {
+ volatile t_uint32 loopme = 0xfff;
+ while(loopme--) ;
+ }
+ MMDSP_STOP_CORE(pRegs);
+ {
+ volatile t_uint32 loopme = 0xfff;
+ while(loopme--) ;
+ }
+
+#if 0
+ /* Reset DSP internal memory (xram) */
+ {
+ t_uint32 *pSrc = (t_uint32 *)(pRegs->mem24);
+ t_uint32 tcmSize;
+ int i;
+ cm_MM_GetAllocatorSize(pDspDesc->allocator[INTERNAL_XRAM], &sizeInBytes);
+ tcmSize = sizeInBytes;
+ cm_MM_GetAllocatorSize(pDspDesc->allocator[INTERNAL_YRAM], &sizeInBytes);
+ tcmSize += sizeInBytes;
+ for (i = 0; i < (tcmSize/sizeof(t_uint32)); i++)
+ *(pSrc++) = 0;
+ }
+#endif
+
+ /* Configure all blocks as X only, except the Y ones (MOVED TO mmdsp_InitAfterBoot()) */
+
+ /* __STN_8815 --> __STN_8820 or __STN_8500 */
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_STBUS_ID_CONF_REG, coreId2stbusId[coreId]);
+
+ /* Configure External Bus timeout reg */
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EN_EXT_BUS_TIMEOUT_REG, IHOST_TIMEOUT_ENABLE);
+
+ /* Program memory management */
+#if defined(__STN_8500) && (__STN_8500 > 10)
+ {
+ const t_uint32 r0 = CODE_ADDRESS_BASE[1] >> 10;
+ const t_uint32 r1 = CODE_ADDRESS_BASE[2] >> 10;
+ const t_uint32 r2 = CODE_ADDRESS_BASE[3] >> 10;
+ const t_uint32 sdram0 = pDspDesc->segments[SDRAM_CODE_EE].base.physical;
+ const t_uint32 sdram1 = pDspDesc->segments[SDRAM_CODE_USER].base.physical;
+ const t_uint32 esram0 = pDspDesc->segments[ESRAM_CODE_EE].base.physical;
+ const t_uint32 esram1 = pDspDesc->segments[ESRAM_CODE_USER].base.physical;
+
+ /* Bases for first two segments, going to sdram */
+ regValue = ((t_uint64)(sdram1) << IHOST_PRG_BASE2_ADDR_SHIFT) + (t_uint64)sdram0;
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_PRG_BASE_ADDR_REG, regValue);
+
+ /* Bases for second two segments, going to esram */
+ regValue = ((t_uint64)(esram1) << IHOST_PRG_BASE4_ADDR_SHIFT) + (t_uint64)esram0;
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_PRG_BASE_34_ADDR_REG, regValue);
+
+ /* Split mmdsp program adress-space and activate the mechanism */
+ regValue = (t_uint64)((t_uint64)(r2) << 48 | (t_uint64)(r1) <<32 | (t_uint64)(r0) << 16 | 1);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_PRG_BASE2_ACTIV_REG, regValue);
+ }
+#else
+ {
+ const t_uint32 sdram0 = pDspDesc->segments[SDRAM_CODE_EE].base.physical;
+ const t_uint32 esram0 = pDspDesc->segments[ESRAM_CODE_EE].base.physical;
+
+ regValue = (t_uint64)sdram0 | ( ((t_uint64)esram0) << IHOST_PRG_BASE2_ADDR_SHIFT );
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_PRG_BASE_ADDR_REG, regValue);
+
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_PRG_BASE2_ACTIV_REG, IHOST_PRG_BASE2_ACTIV_ON);
+ }
+#endif
+
+ /* Data_16/24 memory management */
+#if defined(__STN_8500) && (__STN_8500 > 10)
+ /* Segments 1 and 2 for 16/24 map to sdram continuously */
+ /* Base 1 */
+ regValue = (((t_uint64)pDspDesc->segments[SDRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE_24_SHIFT) |
+ (((t_uint64)pDspDesc->segments[SDRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE_REG, regValue);
+ /* Top 1 */
+ regValue = (((t_uint64)(pDspDesc->segments[SDRAM_DATA_EE].base.physical + pDspDesc->segments[SDRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP_24_SHIFT) |
+ (((t_uint64)(pDspDesc->segments[SDRAM_DATA_EE].base.physical + pDspDesc->segments[SDRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_TOP_16_24_REG, regValue);
+
+ /* Base 2 */
+ regValue = (((t_uint64)pDspDesc->segments[SDRAM_DATA_USER].base.physical) << IHOST_DATA_EXT_BUS_BASE2_24_SHIFT) |
+ (((t_uint64)pDspDesc->segments[SDRAM_DATA_USER].base.physical) << IHOST_DATA_EXT_BUS_BASE2_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE2_REG, regValue);
+ /* Top 2 */
+ regValue = (((t_uint64)(pDspDesc->segments[SDRAM_DATA_USER].base.physical + pDspDesc->segments[SDRAM_DATA_USER].size - 1)) << IHOST_DATA_EXT_BUS_TOP2_24_SHIFT) |
+ (((t_uint64)(pDspDesc->segments[SDRAM_DATA_USER].base.physical + pDspDesc->segments[SDRAM_DATA_USER].size - 1)) << IHOST_DATA_EXT_BUS_TOP2_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EXT_BUS_TOP2_16_24_REG, regValue);
+
+ /* Segments 3 and 4 for 16/24 map to esram continuously */
+ /* Base 3 */
+ regValue = (((t_uint64)pDspDesc->segments[ESRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE3_24_SHIFT) |
+ (((t_uint64)pDspDesc->segments[ESRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE3_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE3_REG, regValue);
+ /* Top 3 */
+ regValue = (((t_uint64)(pDspDesc->segments[ESRAM_DATA_EE].base.physical + pDspDesc->segments[ESRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP3_24_SHIFT) |
+ (((t_uint64)(pDspDesc->segments[ESRAM_DATA_EE].base.physical + pDspDesc->segments[ESRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP3_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EXT_BUS_TOP3_16_24_REG, regValue);
+
+ /* Base 4 */
+ regValue = (((t_uint64)pDspDesc->segments[ESRAM_DATA_USER].base.physical) << IHOST_DATA_EXT_BUS_BASE4_24_SHIFT) |
+ (((t_uint64)pDspDesc->segments[ESRAM_DATA_USER].base.physical) << IHOST_DATA_EXT_BUS_BASE4_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE4_REG, regValue);
+ /* Top 4 */
+ regValue = (((t_uint64)(pDspDesc->segments[ESRAM_DATA_USER].base.physical + pDspDesc->segments[ESRAM_DATA_USER].size - 1)) << IHOST_DATA_EXT_BUS_TOP4_24_SHIFT) |
+ (((t_uint64)(pDspDesc->segments[ESRAM_DATA_USER].base.physical + pDspDesc->segments[ESRAM_DATA_USER].size - 1)) << IHOST_DATA_EXT_BUS_TOP4_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EXT_BUS_TOP4_16_24_REG, regValue);
+
+ /* Define base 2 thresholds/offset (1MB for each up segment) */
+ regValue = ((t_uint64)DATA_ADDRESS_BASE[1].startAddress[1]>>SHIFT_HALFWORD1)<< IHOST_DATA2_24_XA_BASE_SHIFT;
+ regValue |= ((t_uint64)DATA_ADDRESS_BASE[1].startAddress[0]>>SHIFT_HALFWORD1)<< IHOST_DATA2_16_XA_BASE_SHIFT;
+
+ /* Define base 3 thresholds/offset (1MB for each up segment) */
+ regValue |= ((t_uint64)DATA_ADDRESS_BASE[2].startAddress[1]>>SHIFT_HALFWORD1)<< IHOST_DATA3_24_XA_BASE_SHIFT;
+ regValue |= ((t_uint64)DATA_ADDRESS_BASE[2].startAddress[0]>>SHIFT_HALFWORD1)<< IHOST_DATA3_16_XA_BASE_SHIFT;
+
+ /* Define base 4 thresholds/offset (1MB for each up segment) */
+ regValue |= ((t_uint64)DATA_ADDRESS_BASE[3].startAddress[1]>>SHIFT_HALFWORD1)<< IHOST_DATA4_24_XA_BASE_SHIFT;
+ regValue |= ((t_uint64)DATA_ADDRESS_BASE[3].startAddress[0]>>SHIFT_HALFWORD1)<< IHOST_DATA4_16_XA_BASE_SHIFT;
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA2_1624_XA_BASE_REG, regValue);
+
+#else
+ /* Program data24/16 base 1 */
+ regValue = (((t_uint64)pDspDesc->segments[SDRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE_24_SHIFT) |
+ (((t_uint64)pDspDesc->segments[SDRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE_REG, regValue);
+
+ /* Program data24/16 top 1 */
+ regValue = (((t_uint64)(pDspDesc->segments[SDRAM_DATA_EE].base.physical + pDspDesc->segments[SDRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP_24_SHIFT) |
+ (((t_uint64)(pDspDesc->segments[SDRAM_DATA_EE].base.physical + pDspDesc->segments[SDRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_TOP_16_24_REG, regValue);
+
+ /* Program data24/16 base 2 */
+ regValue = (((t_uint64)pDspDesc->segments[ESRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE2_24_SHIFT) |
+ (((t_uint64)pDspDesc->segments[ESRAM_DATA_EE].base.physical) << IHOST_DATA_EXT_BUS_BASE2_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_EXT_BUS_BASE2_REG, regValue);
+
+ /* Program data24/16 top 2 */
+ regValue = (((t_uint64)(pDspDesc->segments[ESRAM_DATA_EE].base.physical + pDspDesc->segments[ESRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP2_24_SHIFT) |
+ (((t_uint64)(pDspDesc->segments[ESRAM_DATA_EE].base.physical + pDspDesc->segments[ESRAM_DATA_EE].size - 1)) << IHOST_DATA_EXT_BUS_TOP2_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EXT_BUS_TOP2_16_24_REG, regValue);
+
+ /* Define base 2 thresholds/offset (1MB for each up segment) */
+ regValue = ((t_uint64)(DATA_ADDRESS_BASE[1].startAddress[1]>>SHIFT_HALFWORD1))<< IHOST_DATA2_24_XA_BASE_SHIFT; // Top address minus ONE_MB => 256KW (24/32-bit)
+ regValue |= ((t_uint64)(DATA_ADDRESS_BASE[1].startAddress[0]>>SHIFT_HALFWORD1))<< IHOST_DATA2_16_XA_BASE_SHIFT; // Top address minus ONE_MB => 512KW (16-bit)
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA2_1624_XA_BASE_REG, regValue);
+#endif
+
+ /* Enable top check */
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_TOP_16_24_CHK_REG, IHOST_DATA_TOP_16_24_CHK_ON);
+
+ /* Enable both bases */
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_DATA_BASE2_ACTIV_REG, IHOST_DATA_BASE2_ACTIV_ON);
+
+ /* MMIO management */
+ regValue = (((t_uint64)STM_BASE_ADDR) << IHOST_EXT_MMIO_BASE_ADDR_SHIFT) |
+ (((t_uint64)DMA_CTRL_END_ADDR) << IHOST_EXT_MMIO_DATA_EXT_BUS_TOP_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_EXT_MMIO_BASE_DATA_EXT_BUS_TOP_REG, regValue);
+
+ /* Configure Icache */
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_INST_BURST_SZ_REG, IHOST_INST_BURST_SZ_AUTO);
+
+ regValue = (t_uint64)(IHOST_ICACHE_MODE_PERFMETER_OFF | IHOST_ICACHE_MODE_L2_CACHE_ON |
+ IHOST_ICACHE_MODE_L1_CACHE_ON | IHOST_ICACHE_MODE_FILL_MODE_OFF);
+ WRITE_INDIRECT_HOST_REG(pRegs, IHOST_ICACHE_MODE_REG, regValue);
+
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_DSP_updateCodeBase(
+ t_nmf_core_id coreId,
+ t_dsp_segment_type hwSegment,
+ t_cm_system_address src,
+ t_cm_system_address dst
+ )
+{
+#if defined(__STN_8500) && (__STN_8500 > 10)
+ t_mmdsp_hw_regs *pRegs = pMmdspRegs[coreId];
+ t_uint32 offset = src.physical - mpcDesc[coreId].segments[hwSegment].base.physical;
+ t_cm_system_address base;
+ t_uint32 altBase = 0;
+ t_uint64 regValue = 0;
+ t_uint8 reg = 0;
+
+ base.physical = dst.physical - offset;
+ base.logical = dst.logical - offset;
+
+ switch(hwSegment) {
+ case SDRAM_CODE_EE:
+ altBase = mpcDesc[coreId].segments[SDRAM_CODE_USER].base.physical;
+ regValue = ((t_uint64)(altBase) << IHOST_PRG_BASE2_ADDR_SHIFT) + (t_uint64)base.physical;
+ reg = IHOST_PRG_BASE_ADDR_REG;
+ break;
+ case SDRAM_CODE_USER:
+ altBase = mpcDesc[coreId].segments[SDRAM_CODE_EE].base.physical;
+ regValue = ((t_uint64)(base.physical) << IHOST_PRG_BASE2_ADDR_SHIFT) + (t_uint64)altBase;
+ reg = IHOST_PRG_BASE_ADDR_REG;
+ break;
+ case ESRAM_CODE_EE:
+ altBase = mpcDesc[coreId].segments[ESRAM_CODE_USER].base.physical;
+ regValue = ((t_uint64)(altBase) << IHOST_PRG_BASE4_ADDR_SHIFT) + (t_uint64)base.physical;
+ reg = IHOST_PRG_BASE_34_ADDR_REG;
+ break;
+ case ESRAM_CODE_USER:
+ altBase = mpcDesc[coreId].segments[ESRAM_CODE_EE].base.physical;
+ regValue = ((t_uint64)(base.physical) << IHOST_PRG_BASE4_ADDR_SHIFT) + (t_uint64)altBase;
+ reg = IHOST_PRG_BASE_34_ADDR_REG;
+ break;
+ default:
+ CM_ASSERT(0);
+ }
+
+ LOG_INTERNAL(1, "##### DSP Code Base Update [%d]: 0x%x -> 0x%x (0x%x)\n",
+ hwSegment, mpcDesc[coreId].segments[hwSegment].base.physical, base.physical, base.logical, 0, 0);
+
+ WRITE_INDIRECT_HOST_REG(pRegs, reg, regValue);
+
+ mpcDesc[coreId].segments[hwSegment].base = base;
+#endif
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_DSP_updateDataBase(
+ t_nmf_core_id coreId,
+ t_dsp_segment_type hwSegment,
+ t_cm_system_address src,
+ t_cm_system_address dst
+ )
+{
+#if defined(__STN_8500) && (__STN_8500 > 10)
+ t_mmdsp_hw_regs *pRegs = pMmdspRegs[coreId];
+ t_uint32 offset = src.physical - mpcDesc[coreId].segments[hwSegment].base.physical;
+ t_cm_system_address base;
+ t_uint32 size = mpcDesc[coreId].segments[hwSegment].size; //in bytes
+ t_uint64 regValue;
+ t_uint8 reg = 0;
+ t_uint8 top = 0;
+
+ base.physical = dst.physical - offset;
+ base.logical = dst.logical - offset;
+
+ switch(hwSegment) {
+ case SDRAM_DATA_EE:
+ reg = IHOST_DATA_EXT_BUS_BASE_REG;
+ top = IHOST_DATA_EXT_BUS_TOP_16_24_REG;
+ break;
+ case SDRAM_DATA_USER:
+ reg = IHOST_DATA_EXT_BUS_BASE2_REG;
+ top = IHOST_EXT_BUS_TOP2_16_24_REG;
+ break;
+ case ESRAM_DATA_EE:
+ reg = IHOST_DATA_EXT_BUS_BASE3_REG;
+ top = IHOST_EXT_BUS_TOP3_16_24_REG;
+ break;
+ case ESRAM_DATA_USER:
+ reg = IHOST_DATA_EXT_BUS_BASE4_REG;
+ top = IHOST_EXT_BUS_TOP4_16_24_REG;
+ break;
+ default:
+ CM_ASSERT(0);
+ }
+
+ LOG_INTERNAL(1, "##### DSP Data Base Update [%d]: 0x%x -> 0x%x (0x%x)\n",
+ hwSegment, mpcDesc[coreId].segments[hwSegment].base.physical, base.physical, base.logical, 0, 0);
+
+ /* Program data24/16 base */
+ regValue = (((t_uint64)(base.physical)) << IHOST_DATA_EXT_BUS_BASE2_24_SHIFT) |
+ (((t_uint64)(base.physical)) << IHOST_DATA_EXT_BUS_BASE2_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, reg, regValue);
+
+ /* Program data24/16 top */
+ regValue = (((t_uint64)(base.physical + size - 1)) << IHOST_DATA_EXT_BUS_TOP2_24_SHIFT) |
+ (((t_uint64)(base.physical + size - 1)) << IHOST_DATA_EXT_BUS_TOP2_16_SHIFT);
+ WRITE_INDIRECT_HOST_REG(pRegs, top, regValue);
+
+ mpcDesc[coreId].segments[hwSegment].base = base;
+#endif
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_DSP_GetDspDataAddressInfo(t_nmf_core_id coreId, t_uint32 addr, t_dsp_address_info *info)
+{
+ t_uint32 i, j;
+
+ for(j = 0; j < 2; j++)
+ {
+ for(i = 0; i < DATA_BASE_NUMBER; i++)
+ {
+ if(DATA_ADDRESS_BASE[i].startAddress[j] <= addr && addr < DATA_ADDRESS_BASE[i + 1].startAddress[j])
+ {
+ info->segmentType = DATA_ADDRESS_BASE[i].segmentType;
+ info->baseOffset = (addr - DATA_ADDRESS_BASE[i].startAddress[j]) * (2 + j * 2);
+
+ return CM_OK;
+ }
+ }
+ }
+
+ CM_ASSERT(0);
+ //return CM_INVALID_PARAMETER;
+}
+
+static t_cm_error mmdsp_ConfigureAfterBoot(t_nmf_core_id coreId, t_uint8 nbXramBlocks, t_uint8 nbYramBlocks)
+{
+ /* Configure all blocks as X only, except the Y ones */
+ pMmdspRegs[coreId]->mmio_16.config_data_mem.value = (t_uint16)(~(((1U << nbYramBlocks) - 1) << (nbXramBlocks-nbYramBlocks)));
+
+#if defined(__STN_8500) && (__STN_8500 > 10)
+ /* enable write posting */
+ MMDSP_ENABLE_WRITE_POSTING(pMmdspRegs[coreId]);
+#endif
+
+ return CM_OK;
+}
+
+
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/bfd.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/bfd.h
new file mode 100644
index 00000000000..2bccf9c073b
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/bfd.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Elf bfd relocation.
+ *
+ * \defgroup ELFLOADER MMDSP ELF loader.
+ */
+#ifndef __INC_CM_ELF_BFD_H
+#define __INC_CM_ELF_BFD_H
+
+#include <cm/inc/cm_type.h>
+
+/*
+ * Relocation spcification
+ */
+enum complain_overflow
+{
+ /* Do not complain on overflow. */
+ complain_overflow_dont,
+
+ /* Complain if the bitfield overflows, whether it is considered
+ as signed or unsigned. */
+ complain_overflow_bitfield,
+
+ /* Complain if the value overflows when considered as signed
+ number. */
+ complain_overflow_signed,
+
+ /* Complain if the value overflows when considered as an
+ unsigned number. */
+ complain_overflow_unsigned
+};
+
+struct reloc_howto_struct
+{
+ /* The type field has mainly a documentary use - the back end can
+ do what it wants with it, though normally the back end's
+ external idea of what a reloc number is stored
+ in this field. For example, a PC relative word relocation
+ in a coff environment has the type 023 - because that's
+ what the outside world calls a R_PCRWORD reloc. */
+ unsigned int type;
+
+ /* The value the final relocation is shifted right by. This drops
+ unwanted data from the relocation. */
+ unsigned int rightshift;
+
+ /* The size of the item to be relocated. This is *not* a
+ power-of-two measure. To get the number of bytes operated
+ on by a type of relocation, use bfd_get_reloc_size. */
+ int size;
+
+ /* The number of bits in the item to be relocated. This is used
+ when doing overflow checking. */
+ unsigned int bitsize;
+
+ /* Notes that the relocation is relative to the location in the
+ data section of the addend. The relocation function will
+ subtract from the relocation value the address of the location
+ being relocated. */
+ t_uint64 pc_relative;
+
+ /* The bit position of the reloc value in the destination.
+ The relocated value is left shifted by this amount. */
+ unsigned int bitpos;
+
+ /* What type of overflow error should be checked for when
+ relocating. */
+ enum complain_overflow complain_on_overflow;
+
+ void (*special_function)(void);
+
+ /* The textual name of the relocation type. */
+ char *name;
+
+ /* Some formats record a relocation addend in the section contents
+ rather than with the relocation. For ELF formats this is the
+ distinction between USE_REL and USE_RELA (though the code checks
+ for USE_REL == 1/0). The value of this field is TRUE if the
+ addend is recorded with the section contents; when performing a
+ partial link (ld -r) the section contents (the data) will be
+ modified. The value of this field is FALSE if addends are
+ recorded with the relocation (in arelent.addend); when performing
+ a partial link the relocation will be modified.
+ All relocations for all ELF USE_RELA targets should set this field
+ to FALSE (values of TRUE should be looked on with suspicion).
+ However, the converse is not true: not all relocations of all ELF
+ USE_REL targets set this field to TRUE. Why this is so is peculiar
+ to each particular target. For relocs that aren't used in partial
+ links (e.g. GOT stuff) it doesn't matter what this is set to. */
+ char partial_inplace;
+
+ /* src_mask selects the part of the instruction (or data) to be used
+ in the relocation sum. If the target relocations don't have an
+ addend in the reloc, eg. ELF USE_REL, src_mask will normally equal
+ dst_mask to extract the addend from the section contents. If
+ relocations do have an addend in the reloc, eg. ELF USE_RELA, this
+ field should be zero. Non-zero values for ELF USE_RELA targets are
+ bogus as in those cases the value in the dst_mask part of the
+ section contents should be treated as garbage. */
+ t_uint64 src_mask;
+
+ /* dst_mask selects which parts of the instruction (or data) are
+ replaced with a relocated value. */
+ t_uint64 dst_mask;
+
+ /* When some formats create PC relative instructions, they leave
+ the value of the pc of the place being relocated in the offset
+ slot of the instruction, so that a PC relative relocation can
+ be made just by adding in an ordinary offset (e.g., sun3 a.out).
+ Some formats leave the displacement part of an instruction
+ empty (e.g., m88k bcs); this flag signals the fact. */
+ char pcrel_offset;
+};
+
+#define HOWTO(C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC) \
+ { (unsigned) C, R, S, B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC }
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/common.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/common.h
new file mode 100644
index 00000000000..c51845d5f96
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/common.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Elf common definition.
+ */
+#ifndef __INC_CM_ELF_COMMON_H
+#define __INC_CM_ELF_COMMON_H
+
+#include <cm/engine/component/inc/nmfheaderabi.h>
+#include <cm/engine/elf/inc/elfabi.h>
+#include <cm/engine/elf/inc/reloc.h>
+#include <cm/engine/memory/inc/domain.h>
+#include <cm/engine/component/inc/description.h>
+#include <cm/engine/utils/inc/string.h>
+
+
+#define MAX_SEGMENT 20 // Just in order to not allocate them dynamically
+
+struct XXElf;
+
+/**
+ * \brief Structure used as database of pushed component.
+ */
+typedef struct {
+ t_instance_property instanceProperty;
+ t_uint32 magicNumber; //!< Magic Number
+ t_dup_char foundedTemplateName;
+ t_uint32 minStackSize; //!< Minimum stack size
+
+ struct XXElf *ELF;
+
+ t_elfSegment segments[NUMBER_OF_MMDSP_MEMORY];
+
+ t_bool temporaryDescription;
+
+ t_memory_reference memoryForConstruct;
+ t_memory_reference memoryForStart;
+ t_memory_reference memoryForStop;
+ t_memory_reference memoryForDestroy;
+
+ t_uint8 requireNumber; //!< Number of interface required by this template
+ t_uint8 attributeNumber; //!< Number of attributes in this template
+ t_uint8 propertyNumber; //!< Number of properties in this template
+ t_uint8 provideNumber; //!< Number of interface provided by this template
+
+ t_interface_require *requires; //!< Array of interface required by this template
+ t_attribute *attributes; //!< Array of attributes in this template
+ t_property *properties; //!< Array of properties in this template
+ t_interface_provide *provides; //!< Array of interface provided by this template
+
+} t_elfdescription;
+
+/**
+ * \brief Temporary structure used as database when pushing component.
+ */
+typedef struct
+{
+ const char *elfdata;
+ const char *sectionData[50]; // YES it must be dynamic, but i'm tired.
+
+ t_bool isExecutable;
+
+ t_sint32 nmfSectionIndex;
+ const void *relaNmfSegment, *relaNmfSegmentEnd;
+ const void *relaNmfSegmentSymbols;
+ const char *relaNmfSegmentStrings;
+
+ const t_elf_component_header*elfheader;
+
+
+} t_tmp_elfdescription;
+
+
+t_cm_error ELF64_LoadComponent(
+ t_uint16 e_machine,
+ const char *elfdata,
+ t_elfdescription **elfhandlePtr,
+ t_tmp_elfdescription *elftmp);
+t_cm_error ELF64_ComputeSegment(
+ t_elfdescription *elfhandle,
+ t_tmp_elfdescription *elftmp);
+
+void ELF64_UnloadComponent(
+ t_elfdescription *elfhandle);
+
+t_cm_error ELF64_loadSegment(
+ t_elfdescription *elfhandle,
+ t_memory_handle *memory,
+ t_memory_property property);
+t_cm_error ELF64_relocateSegments(
+ t_memory_handle *memories,
+ t_elfdescription *elf,
+ t_memory_property property,
+ void *cbContext);
+t_cm_error ELF64_getRelocationMemory(
+ t_elfdescription *elfhandle,
+ t_tmp_elfdescription *elftmp,
+ t_uint32 offsetInNmf,
+ t_memory_reference *memory);
+
+const t_elfmemory* MMDSP_getMappingById(t_memory_id memId);
+const t_elfmemory* MMDSP_getMappingByName(const char* sectionName, t_instance_property property);
+void MMDSP_serializeMemories(t_instance_property property,
+ const t_elfmemory** codeMemory, const t_elfmemory** thisMemory);
+void MMDSP_copySection(t_uint32 origAddr, t_uint32 remoteAddr, t_uint32 sizeInByte);
+void MMDSP_bzeroSection(t_uint32 remoteAddr, t_uint32 sizeInByte);
+void MMDSP_loadedSection(t_nmf_core_id coreId, t_memory_id memId, t_memory_handle handle);
+void MMDSP_unloadedSection(t_nmf_core_id coreId, t_memory_id memId, t_memory_handle handle);
+
+void MMDSP_copyCode(t_uint64 * remoteAddr64, const char* origAddr, int nb);
+void MMDSP_copyData24(t_uint32 * remoteAddr32, const char* origAddr, int nb);
+void MMDSP_copyData16(t_uint16 * remoteAddr16, const char* origAddr, int nb);
+
+t_uint32 cm_resolvSymbol(
+ void* context,
+ t_uint32 type,
+ t_dup_char symbolName,
+ char* reloc_addr);
+
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/elfabi.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/elfabi.h
new file mode 100644
index 00000000000..cbcc6db3b9b
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/elfabi.h
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#ifndef _CM_ELF_H
+#define _CM_ELF_H 1
+
+typedef t_uint16 Elf32_Half;
+typedef t_uint16 Elf64_Half;
+
+typedef t_uint32 Elf32_Word;
+typedef t_sint32 Elf32_Sword;
+typedef t_uint32 Elf64_Word;
+typedef t_sint32 Elf64_Sword;
+
+typedef t_uint64 Elf32_Xword;
+typedef t_sint64 Elf32_Sxword;
+typedef t_uint64 Elf64_Xword;
+typedef t_sint64 Elf64_Sxword;
+
+typedef t_uint32 Elf32_Addr;
+typedef t_uint64 Elf64_Addr;
+
+typedef t_uint32 Elf32_Off;
+typedef t_uint64 Elf64_Off;
+
+typedef t_uint16 Elf32_Section;
+typedef t_uint16 Elf64_Section;
+
+typedef Elf32_Half Elf32_Versym;
+typedef Elf64_Half Elf64_Versym;
+
+
+/*********************************************
+ * Header
+ *********************************************/
+#define EI_NIDENT (16) //!< Size of e_ident[]
+
+#define EI_MAG0 0 //!< File identification
+#define ELFMAG0 0x7f
+
+#define EI_MAG1 1 //!< File identification
+#define ELFMAG1 'E'
+
+#define EI_MAG2 2 //!< File identification
+#define ELFMAG2 'L'
+
+#define EI_MAG3 3 //!< File identification
+#define ELFMAG3 'F'
+
+#define EI_CLASS 4 //!< File class
+#define ELFCLASSNONE 0 //!< Invalid class
+#define ELFCLASS32 1 //!< 32-bit objects
+#define ELFCLASS64 2 //!< 64-bit objects
+
+#define EI_DATA 5 //!< Data encoding
+#define ELFDATANONE 0 //!< Invalid data encoding
+#define ELFDATA2LSB 1 //!< 2's complement, little endian
+#define ELFDATA2MSB 2 //!< 2's complement, big endian
+
+#define EI_VERSION 6 //!< File version
+
+#define EI_OSABI 7 //!< OS ABI identification
+#define ELFOSABI_NONE 0 //!< No extension
+#define ELFOSABI_HPUX 1 //!< HP-UX
+#define ELFOSABI_NETBSD 2 //!< NetBSD
+#define ELFOSABI_LINUX 3 //!< Linux
+#define ELFOSABI_SOLARIS 6 //!< Sun Solaris
+#define ELFOSABI_AIX 7 //!< AIX
+#define ELFOSABI_IRIX 8 //!< IRIX
+#define ELFOSABI_FREEBSD 9 //!< FreeBSD
+#define ELFOSABI_TRU64 10 //!< Compaq TRU64 UNIX
+#define ELFOSABI_MODESTO 11 //!< Novell Modesto
+#define ELFOSABI_OPENBSD 12 //!< Open BSD
+#define ELFOSABI_OPENVMS 13 //!< Open VMS
+#define ELFOSABI_NSK 14 //!< HP Non-Stop-Kernel
+
+#define EI_ABIVERSION 8 //!< ABI version
+
+#define EI_PAD 9 //!< Start of padding byte
+
+
+typedef struct
+{
+ unsigned char e_ident[EI_NIDENT]; //!< The initial bytes mark the file as an object file and provide machine-independent data with which to decode and interpret the file's contents
+ Elf32_Half e_type; //!< This member identifies the object file type
+ Elf32_Half e_machine; //!< This member's value specifies the required architecture for an individual file
+ Elf32_Word e_version; //!< This member identifies the object file version
+ Elf32_Addr e_entry; //!< This member gives the virtual address to which the system first transfers control, thus starting the process
+ Elf32_Off e_phoff; //!< This member holds the program header table's file offset in bytes
+ Elf32_Off e_shoff; //!< This member holds the section header table's file offset in bytes
+ Elf32_Word e_flags; //!< This member holds processor-specific flags associated with the file
+ Elf32_Half e_ehsize; //!< This member holds the ELF header's size in bytes
+ Elf32_Half e_phentsize; //!< This member holds the size in bytes of one entry in the file's program header table; all entries are the same size
+ Elf32_Half e_phnum; //!< This member holds the number of entries in the program header table
+ Elf32_Half e_shentsize; //!< This member holds a section header's size in bytes
+ Elf32_Half e_shnum; //!< This member holds the number of entries in the section header table
+ Elf32_Half e_shstrndx; //!< This member holds the section header table index of the entry associated with the section name string table
+} Elf32_Ehdr; //!< 32bit Entry Header
+
+typedef struct
+{
+ unsigned char e_ident[EI_NIDENT]; //!< The initial bytes mark the file as an object file and provide machine-independent data with which to decode and interpret the file's contents
+ Elf64_Half e_type; //!< This member identifies the object file type
+ Elf64_Half e_machine; //!< This member's value specifies the required architecture for an individual file
+ Elf64_Word e_version; //!< This member identifies the object file version
+ Elf64_Addr e_entry; //!< This member gives the virtual address to which the system first transfers control, thus starting the process
+ Elf64_Off e_phoff; //!< This member holds the program header table's file offset in bytes
+ Elf64_Off e_shoff; //!< This member holds the section header table's file offset in bytes
+ Elf64_Word e_flags; //!< This member holds processor-specific flags associated with the file
+ Elf64_Half e_ehsize; //!< This member holds the ELF header's size in bytes
+ Elf64_Half e_phentsize; //!< This member holds the size in bytes of one entry in the file's program header table; all entries are the same size
+ Elf64_Half e_phnum; //!< This member holds the number of entries in the program header table
+ Elf64_Half e_shentsize; //!< This member holds a section header's size in bytes
+ Elf64_Half e_shnum; //!< This member holds the number of entries in the section header table
+ Elf64_Half e_shstrndx; //!< This member holds the section header table index of the entry associated with the section name string table
+} Elf64_Ehdr; //!< 64bit Entry Header
+
+/*
+ * e_type
+ */
+#define ET_NONE 0 //!< No file type
+#define ET_REL 1 //!< Relocatable file
+#define ET_EXEC 2 //!< Executable file
+#define ET_DYN 3 //!< Shared object file
+#define ET_CORE 4 //!< Core file
+#define ET_LOOS 0xfe00 //!< Operating system-specific
+#define ET_HIOS 0xfeff //!< Operating system-specific
+#define ET_LOPROC 0xff00 //!< Processor-specific
+#define ET_HIPROC 0xffff //!< Processor-specific
+
+/*
+ * e_machine
+ */
+#define EM_NONE 0 //!< No machine
+#define EM_M32 1 //!< AT&T WE 32100
+#define EM_SPARC 2 //!< SUN SPARC
+#define EM_386 3 //!< Intel 80386
+#define EM_68K 4 //!< Motorola 68000
+#define EM_88K 5 //!< Motorola 88000
+#define EM_860 7 //!< Intel 80860
+#define EM_MIPS 8 //!< MIPS I architecture
+#define EM_S370 9 //!< IBM System/370
+#define EM_MIPS_RS3_LE 10 //!< MIPS R3000 little-endian
+#define EM_PARISC 15 //!< HPPA
+#define EM_VPP500 17 //!< Fujitsu VPP500
+#define EM_SPARC32PLUS 18 //!< Enhanced instruction set SPARC
+#define EM_960 19 //!< Intel 80960
+#define EM_PPC 20 //!< PowerPC
+#define EM_PPC64 21 //!< 64-bit PowerPC
+#define EM_S390 22 //!< IBM System/390 Processor
+#define EM_V800 36 //!< NEC V800
+#define EM_FR20 37 //!< Fujitsu FR20
+#define EM_RH32 38 //!< TRW RH-32
+#define EM_RCE 39 //!< Motorola RCE
+#define EM_ARM 40 //!< Advanced RISC Machines ARM
+#define EM_FAKE_ALPHA 41 //!< Digital Alpha
+#define EM_SH 42 //!< Hitachi SH
+#define EM_SPARCV9 43 //!< SPARC Version 9
+#define EM_TRICORE 44 //!< Siemens TriCore embedded processor
+#define EM_ARC 45 //!< Argonaut RISC Core, Argonaut Technologies Inc
+#define EM_H8_300 46 //!< Hitachi H8/300
+#define EM_H8_300H 47 //!< Hitachi H8/300H
+#define EM_H8S 48 //!< Hitachi H8S
+#define EM_H8_500 49 //!< Hitachi H8/500
+#define EM_IA_64 50 //!< Intel IA-64 processor architecture
+#define EM_MIPS_X 51 //!< Stanford MIPS-X
+#define EM_COLDFIRE 52 //!< Motorola ColdFire
+#define EM_68HC12 53 //!< Motorola M68HC12
+#define EM_MMA 54 //!< Fujitsu MMA Multimedia Accelerator
+#define EM_PCP 55 //!< Siemens PCP
+#define EM_NCPU 56 //!< Sony nCPU embedded RISC processor
+#define EM_NDR1 57 //!< Denso NDR1 microprocessor
+#define EM_STARCORE 58 //!< Motorola Start*Core processor
+#define EM_ME16 59 //!< Toyota ME16 processor
+#define EM_ST100 60 //!< STMicroelectronics ST100 processor
+#define EM_TINYJ 61 //!< Advanced Logic Corp. TinyJ embedded processor family
+#define EM_X86_64 62 //!< AMD x86-64 architecture
+#define EM_PDSP 63 //!< Sony DSP Processor
+#define EM_PDP10 64 //!< Digital Equipment Corp. PDP-10
+#define EM_PDP11 65 //!< Digital Equipment Corp. PDP-11
+#define EM_FX66 66 //!< Siemens FX66 microcontroller
+#define EM_ST9PLUS 67 //!< STMicroelectronics ST9+ 8/16 bit microcontroller
+#define EM_ST7 68 //!< STMicroelectronics ST7 8-bit microcontroller
+#define EM_68HC16 69 //!< Motorola MC68HC16 Microcontroller
+#define EM_68HC11 70 //!< Motorola MC68HC11 Microcontroller
+#define EM_68HC08 71 //!< Motorola MC68HC08 Microcontroller
+#define EM_68HC05 72 //!< Motorola MC68HC05 Microcontroller
+#define EM_SVX 73 //!< Silicon Graphics SVx
+#define EM_ST19 74 //!< STMicroelectronics ST19 8-bit microcontroller
+#define EM_VAX 75 //!< Digital VAX
+#define EM_CRIS 76 //!< Axis Communications 32-bit embedded processor
+#define EM_JAVELIN 77 //!< Infifineon Technologies 32-bit embedded processor
+#define EM_FIREPATH 78 //!< Element 14 64-bit DSP Processor
+#define EM_ZSP 79 //!< LSI Logic 16-bit DSP Processor
+#define EM_MMIX 80 //!< Donald Knuth's educational 64-bit processor
+#define EM_HUANY 81 //!< Harvard University machine-independent object files
+#define EM_PRISM 82 //!< SiTera Prism
+#define EM_AVR 83 //!< Atmel AVR 8-bit microcontroller
+#define EM_FR30 84 //!< Fujitsu FR30
+#define EM_D10V 85 //!< Mitsubishi D10V
+#define EM_D30V 86 //!< Mitsubishi D30V
+#define EM_V850 87 //!< NEC v850
+#define EM_M32R 88 //!< Mitsubishi M32R
+#define EM_MN10300 89 //!< Matsushita MN10300
+#define EM_MN10200 90 //!< Matsushita MN10200
+#define EM_PJ 91 //!< picoJava
+#define EM_OPENRISC 92 //!< OpenRISC 32-bit embedded processor
+#define EM_ARC_A5 93 //!< ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5)
+#define EM_XTENSA 94 //!< Tensilica Xtensa Architecture
+#define EM_VIDEOCORE 95 //!< Alphamosaic VideoCore processor
+#define EM_TMM_GPP 96 //!< Thompson Multimedia General Purpose Processor
+#define EM_NS32K 97 //!< National Semiconductor 32000 series
+#define EM_TPC 98 //!< Tenor Network TPC processor
+#define EM_SNP1K 99 //!< Trebia SNP 1000 processor
+#define EM_ST200 100 //!< STMicroelectronics (www.st.com) ST200 microcontroller
+#define EM_IP2K 101 //!< Ubicom IP2xxx microcontroller family
+#define EM_MAX 102 //!< MAX Processor
+#define EM_CR 103 //!< National Semiconductor CompactRISC microprocessor
+#define EM_F2MC16 104 //!< Fujitsu F2MC16
+#define EM_MSP430 105 //!< Texaxas Instruments embedded microcontroller msp430
+#define EM_BLACKFIN 106 //!< Analog Devices Blackfin (DSP) processor
+#define EM_SE_C33 107 //!< S1C33 Family of Seiko Epspson processors
+#define EM_SEP 108 //!< Sharp embedded microprocessor
+#define EM_ARCA 109 //!< Arca RISC Microprocessor
+#define EM_UNICORE 110 //!< Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University
+#define EM_EXCESS 111 //!< eXcess: 16/32/64-bit configurable embedded CPU
+#define EM_DXP 112 //!< Icera Semiconductor Inc. Deep Execution Processor
+#define EM_ALTERA_NIOS2 113 //!< Altera Nios II soft-core processor
+#define EM_CRX 114 //!< National Semiconductor CompactRISC CRX microprocessor
+#define EM_XGATE 115 //!< Motorola XGATE embedded processor
+#define EM_C166 116 //!< Infifineon C16x/XC16x processor
+#define EM_M16C 117 //!< Renesas M16C series microprocessors
+#define EM_DSPIC30F 118 //!< Microchip Technology dsPIC30F Digital Signal Controller
+#define EM_CE 119 //!< Freescale Communication Engine RISC core
+#define EM_M32C 120 //!< Renesas M32C series microprocessors
+#define EM_TSK3000 131 //!< Altium TSK3000 core
+#define EM_RS08 132 //!< Freescale RS08 embedded processor
+#define EM_ECOG2 134 //!< Cyan Technology eCOG2 microprocessor
+#define EM_SCORE7 135 //!< Sunplus S+core7 RISC processor
+#define EM_DSP24 136 //!< New Japan Radio (NJR) 24-bit DSP Processor
+#define EM_VIDEOCORE3 137 //!< Broadcom VideoCore III processor
+#define EM_LATTICEMICO32 138 //!< RISC processor for Lattice FPGA architecture
+#define EM_SE_C17 139 //!< Seiko Epspson C17 family
+#define EM_TI_C6000 140 //!< The Texaxas Instruments TMS320C6000 DSP family
+#define EM_TI_C2000 141 //!< The Texaxas Instruments TMS320C2000 DSP family
+#define EM_TI_C5500 142 //!< The Texaxas Instruments TMS320C55x DSP family
+#define EM_MMDSP_PLUS 160 //!< STMicroelectronics 64bit VLIW Data Signal Processor
+#define EM_CYPRESS_M8C 161 //!< Cypress M8C microprocessor
+#define EM_R32C 162 //!< Renesas R32C series microprocessors
+#define EM_TRIMEDIA 163 //!< NXP Semiconductors TriMedia architecture family
+#define EM_QDSP6 164 //!< QUALCOMM DSP6 Processor
+#define EM_8051 165 //!< Intel 8051 and variants
+#define EM_STXP7X 166 //!< STMicroelectronics STxP7x family of configurable and extensible RISC processors
+#define EM_NDS32 167 //!< Andes Technology compact code size embedded RISC processor family
+#define EM_ECOG1 168 //!< Cyan Technology eCOG1X family
+#define EM_ECOG1X 168 //!< Cyan Technology eCOG1X family
+#define EM_MAXQ30 169 //!< Dallas Semiconductor MAXQ30 Core Micro-controllers
+#define EM_XIMO16 170 //!< New Japan Radio (NJR) 16-bit DSP Processor
+#define EM_MANIK 171 //!< M2000 Reconfigurable RISC Microprocessor
+#define EM_CRAYNV2 172 //!< Cray Inc. NV2 vector architecture
+#define EM_RX 173 //!< Renesas RX family
+#define EM_METAG 174 //!< Imagination Technologies META processor architecture
+#define EM_MCST_ELBRUS 175 //!< MCST Elbrus general purpose hardware architecture
+#define EM_ECOG16 176 //!< Cyan Technology eCOG16 family
+#define EM_CR16 177 //!< National Semiconductor CompactRISC CR16 16-bit microprocessor
+#define EM_ETPU 178 //!< Freescale Extended Time Processing Unit
+#define EM_SLE9X 179 //!< Infifineon Technologies SLE9X core
+#define EM_AVR32 185 //!< Atmel Corporation 32-bit microprocessor family
+#define EM_STM8 186 //!< STMicroeletronics STM8 8-bit microcontroller
+#define EM_TILE64 187 //!< Tilera TILE64 multicore architecture family
+#define EM_TILEPRO 188 //!< Tilera TILEPro multicore architecture family
+#define EM_MICROBLAZE 189 //!< Xilinx MicroBlaze 32-bit RISC soft processor core
+#define EM_CUDA 190 //!< NVIDIA CUDA architecture
+#define EM_TILEGX 191 //!< Tilera TILE-Gx multicore architecture family
+
+/*
+ * e_version (version)
+ */
+#define EV_NONE 0 //!< Invalid version
+#define EV_CURRENT 1 //!< Current version
+
+
+/*********************************************
+ * Section
+ *********************************************/
+typedef struct
+{
+ Elf32_Word sh_name; //!< This member specifies the name of the section
+ Elf32_Word sh_type; //!< This member categorizes the section's contents and semantics
+ Elf32_Word sh_flags; //!< Sections support 1-bit flags that describe miscellaneous attributes
+ Elf32_Addr sh_addr; //!< If the section will appear in the memory image of a process, this member gives the address at which the section's first byte should reside
+ Elf32_Off sh_offset; //!< This member's value gives the byte offset from the beginning of the file to the first byte in the section
+ Elf32_Word sh_size; //!< This member gives the section's size in bytes
+ Elf32_Word sh_link; //!< This member holds a section header table index link, whose interpretation depends on the section type
+ Elf32_Word sh_info; //!< This member holds extra information, whose interpretation depends on the section type
+ Elf32_Word sh_addralign; //!< Some sections have address alignment constraints
+ Elf32_Word sh_entsize; //!< Some sections hold a table of fixed-size entries, such as a symbol table
+} Elf32_Shdr; //!< 32bit Section header
+
+typedef struct
+{
+ Elf64_Word sh_name; //!< This member specifies the name of the section
+ Elf64_Word sh_type; //!< This member categorizes the section's contents and semantics
+ Elf64_Xword sh_flags; //!< Sections support 1-bit flags that describe miscellaneous attributes
+ Elf64_Addr sh_addr; //!< If the section will appear in the memory image of a process, this member gives the address at which the section's first byte should reside
+ Elf64_Off sh_offset; //!< This member's value gives the byte offset from the beginning of the file to the first byte in the section
+ Elf64_Xword sh_size; //!< This member gives the section's size in bytes
+ Elf64_Word sh_link; //!< This member holds a section header table index link, whose interpretation depends on the section type
+ Elf64_Word sh_info; //!< This member holds extra information, whose interpretation depends on the section type
+ Elf64_Xword sh_addralign; //!< Some sections have address alignment constraints
+ Elf64_Xword sh_entsize; //!< Some sections hold a table of fixed-size entries, such as a symbol table
+} Elf64_Shdr; //!< 64bit Section header
+
+/*
+ * Special Section Indexes
+ */
+#define SHN_UNDEF 0 //!< This value marks an undefined, missing, irrelevant, or otherwise meaningless section reference
+#define SHN_LORESERVE 0xff00 //!< This value specifies the lower bound of the range of reserved indexes
+#define SHN_LOPROC 0xff00 //!< Values in this inclusive range are reserved for processor-specific semantics
+#define SHN_HIPROC 0xff1f //!< Values in this inclusive range are reserved for processor-specific semantics
+#define SHN_LOOS 0xff20 //!< Values in this inclusive range are reserved for operating system-specific semantics
+#define SHN_HIOS 0xff3f //!< Values in this inclusive range are reserved for operating system-specific semantics
+#define SHN_ABS 0xfff1 //!< This value specifies absolute values for the corresponding reference
+#define SHN_COMMON 0xfff2 //!< Symbols defined relative to this section are common symbols
+#define SHN_XINDEX 0xffff //!< This value is an escape value
+#define SHN_HIRESERVE 0xffff //!< This value specifies the upper bound of the range of reserved indexes
+
+/*
+ * sh_type
+ */
+#define SHT_NULL 0 //!< This value marks the section header as inactive
+#define SHT_PROGBITS 1 //!< The section holds information defined by the program
+#define SHT_SYMTAB 2 //!< These sections hold a symbol table
+#define SHT_STRTAB 3 //!< The section holds a string table
+#define SHT_RELA 4 //!< The section holds relocation entries with explicit addends, such as type Elf32_Rela for the 32-bit class of object files or type Elf64_Rela for the 64-bit class of object files
+#define SHT_HASH 5 //!< The section holds a symbol hash table
+#define SHT_DYNAMIC 6 //!< The section holds information for dynamic linking
+#define SHT_NOTE 7 //!< The section holds information that marks the file in some way
+#define SHT_NOBITS 8 //!< A section of this type occupies no space in the file but otherwise resembles SHT_PROGBITS
+#define SHT_REL 9 //!< The section holds relocation entries without explicit addends, such as type Elf32_Rel for the 32-bit class of object files or type Elf64_Rel for the 64-bit class of object files
+#define SHT_SHLIB 10 //!< This section type is reserved but has unspecified semantics
+#define SHT_DYNSYM 11 //!<
+#define SHT_INIT_ARRAY 14 //!< This section contains an array of pointers to initialization functions
+#define SHT_FINI_ARRAY 15 //!< This section contains an array of pointers to termination functions
+#define SHT_PREINIT_ARRAY 16 //!< This section contains an array of pointers to functions that are invoked before all other initialization functions
+#define SHT_GROUP 17 //!< This section defines a section group
+#define SHT_SYMTAB_SHNDX 18 //!< This section is associated with a section of type SHT_SYMTAB and is required if any of the section header indexes referenced by that symbol table contain the escape value SHN_XINDEX
+#define SHT_LOOS 0x60000000 //!< Values in this inclusive range are reserved for operating system-specific semantics
+#define SHT_HIOS 0x6fffffff //!< Values in this inclusive range are reserved for operating system-specific semantics
+#define SHT_LOPROC 0x70000000 //!< Values in this inclusive range are reserved for processor-specific semantics
+#define SHT_HIPROC 0x7fffffff //!< Values in this inclusive range are reserved for processor-specific semantics
+#define SHT_LOUSER 0x80000000 //!< This value specifies the upper bound of the range of indexes reserved for application programs
+#define SHT_HIUSER 0x8fffffff //!< This value specifies the upper bound of the range of indexes reserved for application programs
+
+/*
+ * sh_flags
+ */
+#define SHF_WRITE 0x1 //!< The section contains data that should be writable during process execution
+#define SHF_ALLOC 0x2 //!< The section occupies memory during process execution
+#define SHF_EXECINSTR 0x4 //!< The section contains executable machine instructions
+#define SHF_MERGE 0x10 //!< The data in the section may be merged to eliminate duplication
+#define SHF_STRINGS 0x20 //!< The data elements in the section consist of null-terminated character strings
+#define SHF_INFO_LINK 0x40 //!< The sh_info field of this section header holds a section header table index
+#define SHF_LINK_ORDER 0x80 //!< This flag adds special ordering requirements for link editors
+#define SHF_OS_NONCONFORMING 0x100 //!< This section requires special OS-specific processing (beyond the standard linking rules) to avoid incorrect behavior
+#define SHF_GROUP 0x200 //!< This section is a member (perhaps the only one) of a section group
+#define SHF_TLS 0x400 //!< This section holds Thread-Local Storage, meaning that each separate execution flow has its own distinct instance of this data
+#define SHF_MASKOS 0x0ff00000 //!< All bits included in this mask are reserved for operating system-specific semantics
+#define SHF_MASKPROC 0xf0000000 //!< All bits included in this mask are reserved for processor-specific semantics
+
+
+/*********************************************
+ * Symbol
+ *********************************************/
+typedef struct
+{
+ Elf32_Word st_name; //!< This member holds an index into the object file's symbol string table, which holds the character representations of the symbol names
+ Elf32_Addr st_value; //!< This member gives the value of the associated symbol
+ Elf32_Word st_size; //!< Many symbols have associated sizes
+ unsigned char st_info; //!< This member specifies the symbol's type and binding attributes
+ unsigned char st_other; //!< This member currently specifies a symbol's visibility
+ Elf32_Section st_shndx; //!< Every symbol table entry is defined in relation to some section
+} Elf32_Sym;
+
+typedef struct
+{
+ Elf64_Word st_name; //!< This member holds an index into the object file's symbol string table, which holds the character representations of the symbol names
+ unsigned char st_info; //!< This member specifies the symbol's type and binding attributes
+ unsigned char st_other; //!< This member currently specifies a symbol's visibility
+ Elf64_Section st_shndx; //!< Every symbol table entry is defined in relation to some section
+ Elf64_Addr st_value; //!< This member gives the value of the associated symbol
+ Elf64_Xword st_size; //!< Many symbols have associated sizes
+} Elf64_Sym;
+
+/*
+ * st_info
+ */
+#define ELF32_ST_BIND(i) ((i)>>4)
+#define ELF32_ST_TYPE(i) ((i)&0xf)
+#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf))
+
+#define ELF64_ST_BIND(i) ((i)>>4)
+#define ELF64_ST_TYPE(i) ((i)&0xf)
+#define ELF64_ST_INFO(b,t) (((b)<<4)+((t)&0xf))
+
+
+/* st_info (symbol binding) */
+#define STB_LOCAL 0 //!< Local symbols are not visible outside the object file containing their definition
+#define STB_GLOBAL 1 //!< Global symbols are visible to all object files being combined
+#define STB_WEAK 2 //!< Weak symbols resemble global symbols, but their definitions have lower precedence
+#define STB_LOOS 10 //!< Values in this inclusive range are reserved for operating system-specific semantics
+#define STB_HIOS 12 //!< Values in this inclusive range are reserved for operating system-specific semantics
+#define STB_LOPROC 13 //!< Values in this inclusive range are reserved for processor-specific semantics
+#define STB_HIPROC 15 //!< Values in this inclusive range are reserved for processor-specific semantics
+
+/* st_info (symbol type) */
+#define STT_NOTYPE 0 //!< The symbol's type is not specified
+#define STT_OBJECT 1 //!< The symbol is associated with a data object, such as a variable, an array, and so on
+#define STT_FUNC 2 //!< The symbol is associated with a function or other executable code
+#define STT_SECTION 3 //!< The symbol is associated with a section
+#define STT_FILE 4 //!< Conventionally, the symbol's name gives the name of the source file associated with the object file
+#define STT_COMMON 5 //!< The symbol labels an uninitialized common block
+#define STT_TLS 6 //!< The symbol specifies a Thread-Local Storage entity
+#define STT_LOOS 10 //!< Values in this inclusive range are reserved for operating system-specific semantics
+#define STT_HIOS 12 //!< Values in this inclusive range are reserved for operating system-specific semantics
+#define STT_LOPROC 13 //!< Values in this inclusive range are reserved for processor-specific semantics
+#define STT_HIPROC 15 //!< Values in this inclusive range are reserved for processor-specific semantics
+
+/*
+ * st_other
+ */
+#define ELF32_ST_VISIBILITY(o) ((o)&0x3)
+#define ELF64_ST_VISIBILITY(o) ((o)&0x3)
+
+
+#define STV_DEFAULT 0 //!< The visibility of symbols with the STV_DEFAULT attribute is as specified by the symbol's binding type
+#define STV_INTERNAL 1 //!< A symbol defined in the current component is protected if it is visible in other components but not preemptable, meaning that any reference to such a symbol from within the defining component must be resolved to the definition in that component, even if there is a definition in another component that would preempt by the default rules
+#define STV_HIDDEN 2 //!< A symbol defined in the current component is hidden if its name is not visible to other components
+#define STV_PROTECTED 3 //!< The meaning of this visibility attribute may be defined by processor supplements to further constrain hidden symbols
+
+
+/*********************************************
+ * Relocation
+ *********************************************/
+typedef struct
+{
+ Elf32_Addr r_offset; //!< This member gives the location at which to apply the relocation action
+ Elf32_Word r_info; //!< This member gives both the symbol table index with respect to which the relocation must be made, and the type of relocation to apply
+} Elf32_Rel; //!< 32bits Relocation Entries
+
+typedef struct
+{
+ Elf64_Addr r_offset; //!< This member gives the location at which to apply the relocation action
+ Elf64_Xword r_info; //!< This member gives both the symbol table index with respect to which the relocation must be made, and the type of relocation to apply
+} Elf64_Rel; //!< 32bits Relocation Entries
+
+typedef struct
+{
+ Elf32_Addr r_offset; //!< This member gives the location at which to apply the relocation action
+ Elf32_Word r_info; //!< This member gives both the symbol table index with respect to which the relocation must be made, and the type of relocation to apply
+ Elf32_Sword r_addend; //!< This member specifies a constant addend used to compute the value to be stored into the relocatable field
+} Elf32_Rela; //!< 32bits Relocation Addend Entries
+
+typedef struct
+{
+ Elf64_Addr r_offset; //!< This member gives the location at which to apply the relocation action
+ Elf64_Xword r_info; //!< This member gives both the symbol table index with respect to which the relocation must be made, and the type of relocation to apply
+ Elf64_Sxword r_addend; //!< This member specifies a constant addend used to compute the value to be stored into the relocatable field
+} Elf64_Rela; //!< 32bits Relocation Addend Entries
+
+
+/*
+ * r_info
+ */
+#define ELF32_R_SYM(i) ((i)>>8)
+#define ELF32_R_TYPE(i) ((unsigned char)(i))
+#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t))
+
+#define ELF64_R_SYM(i) ((i)>>32)
+#define ELF64_R_TYPE(i) ((i)&0xffffffffL)
+#define ELF64_R_INFO(s,t) (((s)<<32)+((t)&0xffffffffL))
+
+
+
+/*********************************************
+ * Program
+ *********************************************/
+typedef struct
+{
+ Elf32_Word p_type; //!< This member tells what kind of segment this array element describes or how to interpret the array element's information
+ Elf32_Off p_offset; //!< This member gives the offset from the beginning of the file at which the first byte of the segment resides
+ Elf32_Addr p_vaddr; //!< This member gives the virtual address at which the first byte of the segment resides in memory
+ Elf32_Addr p_paddr; //!< On systems for which physical addressing is relevant, this member is reserved for the segment's physical address
+ Elf32_Word p_filesz; //!< This member gives the number of bytes in the file image of the segment; it may be zero
+ Elf32_Word p_memsz; //!< This member gives the number of bytes in the memory image of the segment; it may be zero
+ Elf32_Word p_flags; //!< This member gives flags relevant to the segment
+ Elf32_Word p_align; //!< As ``Program Loading'' describes in this chapter of the processor supplement, loadable process segments must have congruent values for p_vaddr and p_offset, modulo the page size
+} Elf32_Phdr; //!< 32bits Program header
+
+typedef struct
+{
+ Elf64_Word p_type; //!< This member tells what kind of segment this array element describes or how to interpret the array element's information
+ Elf64_Word p_flags; //!< This member gives flags relevant to the segment
+ Elf64_Off p_offset; //!< This member gives the offset from the beginning of the file at which the first byte of the segment resides
+ Elf64_Addr p_vaddr; //!< This member gives the virtual address at which the first byte of the segment resides in memory
+ Elf64_Addr p_paddr; //!< On systems for which physical addressing is relevant, this member is reserved for the segment's physical address
+ Elf64_Xword p_filesz; //!< This member gives the number of bytes in the file image of the segment; it may be zero
+ Elf64_Xword p_memsz; //!< This member gives the number of bytes in the memory image of the segment; it may be zero
+ Elf64_Xword p_align; //!< As ``Program Loading'' describes in this chapter of the processor supplement, loadable process segments must have congruent values for p_vaddr and p_offset, modulo the page size
+} Elf64_Phdr; //!< 64bits Program header
+
+/*
+ * p_type
+ */
+#define PT_NULL 0 //!< The array element is unused; other members' values are undefined
+#define PT_LOAD 1 //!< The array element specifies a loadable segment, described by p_filesz and p_memsz
+#define PT_DYNAMIC 2 //!< The array element specifies dynamic linking information
+#define PT_INTERP 3 //!< The array element specifies the location and size of a null-terminated path name to invoke as an interpreter
+#define PT_NOTE 4 //!< The array element specifies the location and size of auxiliary information
+#define PT_SHLIB 5 //!< This segment type is reserved but has unspecified semantics
+#define PT_PHDR 6 //!< The array element, if present, specifies the location and size of the program header table itself, both in the file and in the memory image of the program
+#define PT_TLS 7 //!< The array element specifies the Thread-Local Storage template
+#define PT_LOOS 0x60000000 //!< Values in this inclusive range are reserved for operating system-specific semantics
+#define PT_HIOS 0x6fffffff //!< Values in this inclusive range are reserved for operating system-specific semantics
+#define PT_LOPROC 0x70000000 //!< Values in this inclusive range are reserved for processor-specific semantics
+#define PT_HIPROC 0x7fffffff //!< Values in this inclusive range are reserved for processor-specific semantics
+
+/*
+ * p_flags
+ */
+#define PF_X (1 << 0) //!< Execute
+#define PF_W (1 << 1) //!< Write
+#define PF_R (1 << 2) //!< Read
+#define PF_MASKOS 0x0ff00000 //!< Unspecified
+#define PF_MASKPROC 0xf0000000 //!< Unspecified
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/elfapi.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/elfapi.h
new file mode 100644
index 00000000000..564086db26c
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/elfapi.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Elf loder internal methods.
+ *
+ * \defgroup ELFLOADER MMDSP ELF loader.
+ */
+#ifndef __INC_CM_ELFLOADER_H
+#define __INC_CM_ELFLOADER_H
+
+#include <cm/engine/elf/inc/common.h>
+
+/*!
+ * \internal
+ * \brief ELF Parsing & checking
+ * \ingroup ELFLOADER
+ */
+t_cm_error cm_ELF_CheckFile(
+ const char *elfdata,
+ t_bool temporaryDescription,
+ t_elfdescription **elfhandlePtr);
+
+void cm_ELF_ReleaseDescription(
+ t_uint32 requireNumber, t_interface_require *requires,
+ t_uint32 attributeNumber, t_attribute *attributes,
+ t_uint32 propertyNumber, t_property *properties,
+ t_uint32 provideNumber, t_interface_provide *provides);
+
+/*!
+ * \internal
+ * \brief ELF closing
+ * \ingroup ELFLOADER
+ */
+void cm_ELF_CloseFile(
+ t_bool temporaryDescription,
+ t_elfdescription *elfhandle);
+
+/*!
+ * \internal
+ * \brief Load a component template shared memories.
+ *
+ * \note In case of error, part of memory could have been allocated and must be free by calling cm_DSPABI_FreeTemplate.
+ */
+t_cm_error cm_ELF_LoadTemplate(
+ t_cm_domain_id domainId,
+ t_elfdescription *elfhandle,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY]);
+
+/*!
+ * \internal
+ * \brief Clean cache memory of a component template shared code.
+ */
+void cm_ELF_FlushTemplate(
+ t_nmf_core_id coreId,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY]);
+
+void cm_ELF_FlushInstance(
+ t_nmf_core_id coreId,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY],
+ t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY]);
+
+/*!
+ * \internal
+ * \brief Load a component instance private memories.
+ *
+ * \note In case of error, part of memory could have been allocated and must be free by calling cm_DSPABI_FreeInstance.
+ */
+t_cm_error cm_ELF_LoadInstance(
+ t_cm_domain_id domainId,
+ t_elfdescription *elfhandle,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY],
+ t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY]);
+
+void cm_ELF_FreeInstance(
+ t_nmf_core_id coreId,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY],
+ t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY]);
+void cm_ELF_FreeTemplate(
+ t_nmf_core_id coreId,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY]);
+
+
+t_cm_error cm_ELF_relocateSharedSegments(
+ t_memory_handle *memories,
+ t_elfdescription *elfhandle,
+ void *cbContext);
+t_cm_error cm_ELF_relocatePrivateSegments(
+ t_memory_handle *memories,
+ t_elfdescription *elfhandle,
+ void *cbContext);
+void cm_ELF_performRelocation(
+ t_uint32 type,
+ const char *symbol_name,
+ t_uint32 symbol_addr,
+ char *reloc_addr);
+t_cm_error cm_ELF_GetMemory(
+ t_elfdescription *elf,
+ t_tmp_elfdescription *elftmp,
+ t_uint32 address,
+ t_memory_purpose purpose,
+ t_memory_reference *memory);
+
+
+#include <cm/engine/component/inc/component_type.h>
+
+t_cm_error cm_DSPABI_AddLoadMap(
+ t_cm_domain_id domainId,
+ const char* templateName,
+ const char* localname,
+ t_memory_handle *memories,
+ void *componentHandle);
+t_cm_error cm_DSPABI_RemoveLoadMap(
+ t_cm_domain_id domainId,
+ const char* templateName,
+ t_memory_handle *memories,
+ const char* localname,
+ void *componentHandle);
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/memory.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/memory.h
new file mode 100644
index 00000000000..9eab94f173c
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/memory.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Elf memory.
+ */
+#ifndef __INC_CM_ELF_MEMORY_H
+#define __INC_CM_ELF_MEMORY_H
+
+#include <cm/engine/dsp/inc/dsp.h>
+
+/**
+ * \brief Memory identifier
+ */
+typedef t_uint8 t_memory_id;
+
+/**
+ * \brief Memory property
+ */
+typedef enum {
+ MEM_FOR_MULTIINSTANCE,
+ MEM_FOR_SINGLETON,
+ MEM_FOR_LAST
+} t_instance_property;
+
+/**
+ * \brief Memory prupose (for processor with different address space for code and data/
+ */
+typedef enum {
+ MEM_CODE,
+ MEM_DATA
+} t_memory_purpose;
+
+/**
+ * \brief Memory property
+ */
+typedef enum {
+ MEM_PRIVATE,
+ MEM_SHARABLE,
+} t_memory_property;
+
+/**
+ * \brief Elf memory mapping description
+ */
+typedef struct
+{
+ t_memory_id id;
+ t_dsp_memory_type_id dspMemType;
+ t_uint32 startAddr;
+ t_cm_memory_alignment memAlignement;
+ t_memory_property property;
+ t_memory_purpose purpose;
+ t_uint8 fileEntSize;
+ t_uint8 memEntSize;
+ char* memoryName;
+} t_elfmemory;
+
+#define NUMBER_OF_MMDSP_MEMORY 15
+
+/*
+ * \brief Elf segment description
+ */
+typedef struct {
+ // Data in Bytes
+ t_uint32 sumSize;
+ t_bool sumSizeSetted;
+ t_cm_logical_address hostAddr; // Valid only if section Load in memory
+ t_uint32 maxAlign;
+ // Data in word
+ t_uint32 mpcAddr; // Valid only if section Load in memory
+} t_elfSegment;
+
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp-loadmap.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp-loadmap.h
new file mode 100644
index 00000000000..78b00388802
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp-loadmap.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Elf writer internal methods.
+ *
+ * \defgroup LOADMAP MMDSP ELF writer (a linker in fact).
+ */
+#ifndef __INC_CM_LOADMAP_H
+#define __INC_CM_LOADMAP_H
+
+#include <cm/inc/cm_type.h>
+
+/*
+ * Align with loadmap :
+ * https://codex.cro.st.com/wiki/index.php?pagename=Specification%2FLoadmap%2Fv1.2&group_id=310
+ */
+#define LOADMAP_MAGIC_NUMBER 0xFBBF
+
+#define LOADMAP_VERSION_MSB 1
+#define LOADMAP_VERSION_LSB 2
+
+struct LoadMapItem
+{
+ const char* pSolibFilename; // Filename of shared library object
+ void* pAddrProg; // Load address of program section
+ void* pAddrEmbProg; // Load address of embedded program section
+ void* pThis; // Data base address of component instance
+ void* pARMThis; // ARM component debug ID
+ const char* pComponentName; // Pretty name of the component instance, NULL if none.
+ struct LoadMapItem* pNextItem;// Pointer on the next list item, NULL if last one.
+ void* pXROM; // Start address of XROM
+ void* pYROM; // Start address of YROM
+
+ void* memHandle; // handle of allocated memory for this structure and name, local field not use by mmdsp
+};
+
+struct LoadMapHdr
+{
+ t_uint16 nMagicNumber; // Equal to 0xFBBF.
+ t_uint16 nVersion; // The version of the load map format.
+ t_uint32 nRevision; // A counter incremented at each load map list modification.
+ struct LoadMapItem* pFirstItem;// Pointer on the first item, NULL if no shared library loaded.
+};
+
+#endif /* __INC_CM_LOADMAP_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp.h
new file mode 100644
index 00000000000..1662def6c1a
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/mmdsp.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief MMDSP elf.
+ */
+#ifndef __INC_CM_ELF_MMDSP_H
+#define __INC_CM_ELF_MMDSP_H
+
+#include <cm/engine/elf/inc/common.h>
+
+#define CODE_MEMORY_INDEX 0
+#define ECODE_MEMORY_INDEX 7
+
+#define XROM_MEMORY_INDEX 1
+#define YROM_MEMORY_INDEX 2
+#define PRIVATE_DATA_MEMORY_INDEX 8
+#define SHARE_DATA_MEMORY_INDEX 1
+
+/*
+ * Relocation
+ */
+#define R_MMDSP_IMM16 5
+#define R_MMDSP_IMM20_16 6
+#define R_MMDSP_IMM20_4 7
+#define R_MMDSP_24 13
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/mpcal.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/mpcal.h
new file mode 100644
index 00000000000..718b7f61ceb
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/mpcal.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief MPC Abraction Layer.
+ *
+ * \defgroup MPCAL MPC Abraction Layer.
+ */
+#ifndef __INC_CM_DSP_MPCAL_H
+#define __INC_CM_DSP_MPCAL_H
+
+#include <cm/inc/cm_type.h>
+#include <share/inc/nmf.h>
+
+#include <cm/engine/elf/inc/common.h>
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/inc/reloc.h b/drivers/staging/nmf-cm/cm/engine/elf/inc/reloc.h
new file mode 100644
index 00000000000..b38be48d689
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/inc/reloc.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Elf relocation.
+ */
+#ifndef __INC_CM_ELF_RELOC_H
+#define __INC_CM_ELF_RELOC_H
+
+
+void MMDSP_performRelocation(
+ t_uint32 type,
+ const char* symbol_name,
+ t_uint32 symbol_addr,
+ char* reloc_addr,
+ const char* inPlaceAddr,
+ t_uint32 reloc_offset);
+
+/*
+ *
+ * Return:
+ * 0x0 returned if symbol not found
+ * 0xFFFFFFFE returned if out of memory
+ * 0xFFFFFFFF returned if symbol found in static required binding
+ */
+typedef t_uint32 (*CBresolvSymbol)(
+ void* context,
+ t_uint32 type,
+ const char* symbolName,
+ char* reloc_addr);
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/elf64.c b/drivers/staging/nmf-cm/cm/engine/elf/src/elf64.c
new file mode 100644
index 00000000000..2e0f5928ffd
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/src/elf64.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/elf/inc/common.h>
+#include <cm/engine/elf/inc/elfabi.h>
+
+#include <cm/engine/utils/inc/swap.h>
+#include <cm/engine/trace/inc/trace.h>
+
+typedef Elf64_Half ElfXX_Half;
+typedef Elf64_Word ElfXX_Word;
+typedef Elf64_Addr ElfXX_Addr;
+typedef Elf64_Off ElfXX_Off;
+
+typedef Elf64_Xword ElfXX_Xword;
+
+typedef Elf64_Ehdr ElfXX_Ehdr;
+typedef Elf64_Shdr ElfXX_Shdr;
+typedef Elf64_Sym ElfXX_Sym;
+typedef Elf64_Rela ElfXX_Rela;
+
+#undef ELFXX_R_SYM
+#define ELFXX_R_SYM ELF64_R_SYM
+#undef ELFXX_R_TYPE
+#define ELFXX_R_TYPE ELF64_R_TYPE
+#undef ELFXX_R_INFO
+#define ELFXX_R_INFO ELF64_R_INFO
+
+// TODO Here we assume big endian (MMDSP !)
+static Elf64_Half swapHalf(Elf64_Half half)
+{
+ return (Elf64_Half)swap16(half);
+}
+
+static Elf64_Word swapWord(Elf64_Word word)
+{
+ return (Elf64_Word)swap32(word);
+}
+
+static Elf64_Xword swapXword(Elf64_Xword xword)
+{
+ return (Elf64_Xword)swap64(xword);
+}
+
+#include "elfxx.c"
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/elfload.c b/drivers/staging/nmf-cm/cm/engine/elf/src/elfload.c
new file mode 100644
index 00000000000..1209457c983
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/src/elfload.c
@@ -0,0 +1,742 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ *
+ */
+#include <cm/engine/elf/inc/elfapi.h>
+#include <cm/engine/elf/inc/mpcal.h>
+#include <cm/inc/cm_def.h>
+
+//#include <cm/engine/component/inc/introspection.h>
+
+#include <cm/engine/utils/inc/mem.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/utils/inc/swap.h>
+#include <cm/engine/utils/inc/string.h>
+
+static void* getElfHeaderReference(t_tmp_elfdescription *elftmp, void* hdrref)
+{
+ if(hdrref != NULL)
+ return (void*)((int)swap32((t_uint32)hdrref) + (int)elftmp->elfheader);
+ else
+ return NULL;
+}
+
+static t_dup_char copyElfString(t_tmp_elfdescription *elftmp, char* idx)
+{
+ return cm_StringDuplicate((char*)getElfHeaderReference(elftmp, (void*)idx));
+}
+
+static t_cm_error getMemoryOffset(
+ t_elfdescription *elfhandle,
+ t_tmp_elfdescription *elftmp,
+ t_memory_purpose purpose,
+ const t_uint32 *addressInNmf,
+ t_memory_reference *memory) {
+
+ if(elftmp->isExecutable) {
+ return cm_ELF_GetMemory(elfhandle, elftmp,
+ swap32(*addressInNmf),
+ purpose,
+ memory);
+ } else {
+ return ELF64_getRelocationMemory(elfhandle, elftmp,
+ (t_uint32)addressInNmf - (t_uint32)elftmp->elfheader,
+ memory);
+ }
+}
+
+static t_cm_error getAdressForExecutableOffsetElsewhere(
+ t_elfdescription *elfhandle,
+ t_tmp_elfdescription *elftmp,
+ const t_uint32 *addressInNmf,
+ t_memory_reference *memory) {
+ t_uint32 address;
+
+ address = swap32(*addressInNmf);
+ if(address == 0xFFFFFFFF)
+ {
+ memory->offset = 0x0;
+ memory->memory = NULL;
+ return CM_OK;
+ }
+
+ if(elftmp->isExecutable)
+ {
+ memory->offset = address;
+ memory->memory = NULL;
+ return CM_OK;
+ }
+
+ // Error log in elfhandle by previous call will be check in loadTemplate
+ return ELF64_getRelocationMemory(elfhandle, elftmp,
+ (t_uint32)addressInNmf - (t_uint32)elftmp->elfheader,
+ memory);
+}
+
+/*
+ * Interface Management
+ */
+static t_interface_description* interfaceList = NULL;
+
+static t_interface_description* getInterfaceDescription(t_tmp_elfdescription *elftmp, t_elf_interface_description* elfitf) {
+ t_dup_char itfType;
+ t_interface_description* itf;
+ int i;
+
+ itfType = copyElfString(elftmp, elfitf->type);
+ if(itfType == NULL)
+ return NULL;
+
+ // Search if interfane already loaded
+ for(itf = interfaceList; itf != NULL; itf = itf->next) {
+ if(itf->type == itfType) {
+ // TODO Sanity check
+
+ itf->referenceCounter++;
+ cm_StringRelease(itfType);
+ return itf;
+ }
+ }
+
+ // Create a new interface if not exists
+ itf = (t_interface_description*)OSAL_Alloc_Zero(sizeof(t_interface_description) + sizeof(char*) * (elfitf->methodNumber /*- 1*/));
+ if(itf == NULL)
+ goto out_itf_type;
+ itf->referenceCounter = 1;
+ itf->type = itfType;
+ itf->methodNumber = elfitf->methodNumber;
+ for(i = 0; i < itf->methodNumber; i++) {
+ itf->methodNames[i] = copyElfString(elftmp, elfitf->methodNames[i]);
+ if(itf->methodNames[i] == NULL)
+ goto out_method;
+ }
+
+ // Put it in Top
+ itf->next = interfaceList;
+ interfaceList = itf;
+
+ return itf;
+
+out_method:
+ for(i = 0; i < itf->methodNumber; i++)
+ cm_StringRelease(itf->methodNames[i]);
+ OSAL_Free(itf);
+out_itf_type:
+ cm_StringRelease(itfType);
+ return NULL;
+}
+
+static void releaseInterfaceDescription(t_interface_description* itf) {
+ if(itf == NULL)
+ return;
+
+ if(--itf->referenceCounter == 0) {
+ int i;
+
+ // Remove it from list
+ if(interfaceList == itf) {
+ interfaceList = interfaceList->next;
+ } else {
+ t_interface_description* prev = interfaceList;
+ while(prev->next != itf)
+ prev = prev->next;
+ prev->next = itf->next;
+ }
+
+ // Destroy interface description
+ for(i = 0; i < itf->methodNumber; i++) {
+ cm_StringRelease(itf->methodNames[i]);
+ }
+ cm_StringRelease(itf->type);
+ OSAL_Free(itf);
+ }
+}
+
+
+t_cm_error cm_ELF_CheckFile(
+ const char *elfdata,
+ t_bool temporaryDescription,
+ t_elfdescription **elfhandlePtr)
+{
+ t_elfdescription *elfhandle;
+ t_tmp_elfdescription elftmp;
+ t_cm_error error;
+ t_uint32 version;
+ t_uint32 compatibleVersion;
+ int i, j, k;
+
+ /*
+ * Sanity check
+ */
+ if (elfdata[EI_MAG0] != ELFMAG0 ||
+ elfdata[EI_MAG1] != ELFMAG1 ||
+ elfdata[EI_MAG2] != ELFMAG2 ||
+ elfdata[EI_MAG3] != ELFMAG3 ||
+ elfdata[EI_CLASS] != ELFCLASS64)
+ {
+ ERROR("CM_INVALID_ELF_FILE: component file is not a MMDSP ELF file\n", 0, 0, 0, 0, 0, 0);
+ return CM_INVALID_ELF_FILE;
+ }
+
+ /*
+ * Create elf data
+ */
+ if((error = ELF64_LoadComponent(EM_MMDSP_PLUS, elfdata, elfhandlePtr, &elftmp)) != CM_OK)
+ return error;
+
+ elfhandle = *elfhandlePtr;
+
+ elfhandle->temporaryDescription = temporaryDescription;
+
+ version = swap32(elftmp.elfheader->nmfVersion);
+
+ compatibleVersion = (VERSION_MAJOR(version) == VERSION_MAJOR(NMF_VERSION));
+ if(compatibleVersion)
+ {
+ switch(VERSION_MINOR(NMF_VERSION))
+ {
+ case 10: // Compatible with 2.9, 2.10
+ compatibleVersion =
+ (VERSION_MINOR(version) == 9) ||
+ (VERSION_MINOR(version) == 10);
+ break;
+ default: // Strict compatibility 2.x == 2.x
+ compatibleVersion = (VERSION_MINOR(version) == VERSION_MINOR(NMF_VERSION));
+ }
+ }
+
+ if(! compatibleVersion)
+ {
+ ERROR("CM_INVALID_ELF_FILE: incompatible version for Component %d.%d.x != CM:%d.%d.x\n",
+ VERSION_MAJOR(version), VERSION_MINOR(version),
+ VERSION_MAJOR(NMF_VERSION), VERSION_MINOR(NMF_VERSION), 0, 0);
+ error = CM_INVALID_ELF_FILE;
+ goto onerror;
+ }
+
+
+ /*
+ * Commented since to many noise !!!!
+ if(VERSION_PATCH(version) != VERSION_PATCH(NMF_VERSION))
+ {
+ WARNING("CM_INVALID_ELF_FILE: incompatible version, Component:%d.%d.%d != CM:%d.%d.%d\n",
+ VERSION_MAJOR(version), VERSION_MINOR(version), VERSION_PATCH(version),
+ VERSION_MAJOR(NMF_VERSION), VERSION_MINOR(NMF_VERSION), VERSION_PATCH(NMF_VERSION));
+ }
+ */
+
+ if((error = ELF64_ComputeSegment(elfhandle, &elftmp)) != CM_OK)
+ goto onerror;
+
+ //
+ elfhandle->foundedTemplateName = copyElfString(&elftmp, elftmp.elfheader->templateName);
+ if(elfhandle->foundedTemplateName == NULL)
+ goto oom;
+ elfhandle->minStackSize = swap32(elftmp.elfheader->minStackSize);
+
+ // Get Life-cycle memory
+ if((error = getAdressForExecutableOffsetElsewhere(elfhandle, &elftmp, &elftmp.elfheader->LCCConstruct, &elfhandle->memoryForConstruct)) != CM_OK)
+ goto onerror;
+ if((error = getAdressForExecutableOffsetElsewhere(elfhandle, &elftmp, &elftmp.elfheader->LCCStart, &elfhandle->memoryForStart)) != CM_OK)
+ goto onerror;
+ if((error = getAdressForExecutableOffsetElsewhere(elfhandle, &elftmp, &elftmp.elfheader->LCCStop, &elfhandle->memoryForStop)) != CM_OK)
+ goto onerror;
+ if((error = getAdressForExecutableOffsetElsewhere(elfhandle, &elftmp, &elftmp.elfheader->LCCDestroy, &elfhandle->memoryForDestroy)) != CM_OK)
+ goto onerror;
+
+ // Copy attributes information
+ elfhandle->attributeNumber = swap32(elftmp.elfheader->attributeNumber);
+ if(elfhandle->attributeNumber > 0)
+ {
+ elfhandle->attributes =
+ (t_attribute*)OSAL_Alloc_Zero(sizeof(t_attribute) * elfhandle->attributeNumber);
+ if(elfhandle->attributes == NULL)
+ goto oom;
+
+ if(elfhandle->attributeNumber > 0)
+ {
+ t_elf_attribute *attributes = (t_elf_attribute*)getElfHeaderReference(&elftmp, (void*)elftmp.elfheader->attributes);
+
+ for(i = 0; i < elfhandle->attributeNumber; i++)
+ {
+ elfhandle->attributes[i].name = copyElfString(&elftmp, attributes[i].name);
+ if(elfhandle->attributes[i].name == NULL)
+ goto oom;
+
+ if((error = getMemoryOffset(elfhandle, &elftmp,
+ MEM_DATA,
+ &attributes[i].symbols,
+ &elfhandle->attributes[i].memory)) != CM_OK)
+ goto onerror;
+ LOG_INTERNAL(2, " attribute %s mem=%s offset=%x\n",
+ elfhandle->attributes[i].name,
+ elfhandle->attributes[i].memory.memory->memoryName,
+ elfhandle->attributes[i].memory.offset,
+ 0, 0, 0);
+ }
+ }
+ }
+
+ // Copy properties information
+ elfhandle->propertyNumber = swap32(elftmp.elfheader->propertyNumber);
+ if(elfhandle->propertyNumber > 0)
+ {
+ elfhandle->properties =
+ (t_property*)OSAL_Alloc_Zero(sizeof(t_property) * elfhandle->propertyNumber);
+ if(elfhandle->properties == NULL)
+ goto oom;
+
+ if(elfhandle->propertyNumber > 0)
+ {
+ t_elf_property *properties = (t_elf_property*)getElfHeaderReference(&elftmp, (void*)elftmp.elfheader->properties);
+
+ for(i = 0; i < elfhandle->propertyNumber; i++)
+ {
+ elfhandle->properties[i].name = copyElfString(&elftmp, properties[i].name);
+ if(elfhandle->properties[i].name == NULL)
+ goto oom;
+
+ elfhandle->properties[i].value = copyElfString(&elftmp, properties[i].value);
+ if(elfhandle->properties[i].value == NULL)
+ goto oom;
+
+ LOG_INTERNAL(3, " property %s = %s\n",
+ elfhandle->properties[i].name,
+ elfhandle->properties[i].value,
+ 0, 0, 0, 0);
+ }
+ }
+ }
+
+ // Copy requires information
+ elfhandle->requireNumber = swap32(elftmp.elfheader->requireNumber);
+ if(elfhandle->requireNumber > 0)
+ {
+ char *ref = getElfHeaderReference(&elftmp, (void*)elftmp.elfheader->requires);
+
+ elfhandle->requires = (t_interface_require*)OSAL_Alloc_Zero(sizeof(t_interface_require) * elfhandle->requireNumber);
+ if(elfhandle->requires == NULL)
+ goto oom;
+
+ for(i = 0; i < elfhandle->requireNumber; i++)
+ {
+ t_elf_required_interface *require = (t_elf_required_interface*)ref;
+ t_elf_interface_description *interface = (t_elf_interface_description*)getElfHeaderReference(&elftmp, (void*)require->interface);
+
+ elfhandle->requires[i].name = copyElfString(&elftmp, require->name);
+ if(elfhandle->requires[i].name == NULL)
+ goto oom;
+
+ elfhandle->requires[i].requireTypes = require->requireTypes;
+ elfhandle->requires[i].collectionSize = require->collectionSize;
+ elfhandle->requires[i].interface = getInterfaceDescription(&elftmp, interface);
+ if(elfhandle->requires[i].interface == NULL)
+ goto oom;
+
+ LOG_INTERNAL(2, " require %s <%s> %x\n",
+ elfhandle->requires[i].name,
+ elfhandle->requires[i].interface->type,
+ elfhandle->requires[i].requireTypes, 0, 0, 0);
+ CM_ASSERT(elfhandle->requires[i].collectionSize != 0);
+
+ ref += sizeof(t_elf_required_interface);
+
+ if((elfhandle->requires[i].requireTypes & VIRTUAL_REQUIRE) == 0 &&
+ (elfhandle->requires[i].requireTypes & STATIC_REQUIRE) == 0)
+ {
+ elfhandle->requires[i].indexes =
+ (t_interface_require_index*)OSAL_Alloc_Zero(sizeof(t_interface_require_index) * elfhandle->requires[i].collectionSize);
+ if(elfhandle->requires[i].indexes == NULL)
+ goto oom;
+
+ for(j = 0; j < elfhandle->requires[i].collectionSize; j++)
+ {
+ t_elf_interface_require_index* index = (t_elf_interface_require_index*)ref;
+
+ elfhandle->requires[i].indexes[j].numberOfClient = swap32(index->numberOfClient);
+ if(elfhandle->requires[i].indexes[j].numberOfClient != 0)
+ {
+ elfhandle->requires[i].indexes[j].memories =
+ (t_memory_reference*)OSAL_Alloc(sizeof(t_memory_reference) * elfhandle->requires[i].indexes[j].numberOfClient);
+ if(elfhandle->requires[i].indexes[j].memories == NULL)
+ goto oom;
+
+ for(k = 0; k < elfhandle->requires[i].indexes[j].numberOfClient; k++) {
+ if((error = getMemoryOffset(elfhandle,&elftmp,
+ MEM_DATA,
+ &index->symbols[k],
+ &elfhandle->requires[i].indexes[j].memories[k])) != CM_OK)
+ goto onerror;
+ LOG_INTERNAL(2, " [%d, %d] mem=%s offset=%x\n",
+ j, k,
+ elfhandle->requires[i].indexes[j].memories[k].memory->memoryName,
+ elfhandle->requires[i].indexes[j].memories[k].offset,
+ 0, 0);
+ }
+ }
+
+ ref += sizeof(index->numberOfClient) + elfhandle->requires[i].indexes[j].numberOfClient * sizeof(index->symbols[0]);
+ }
+ }
+ }
+ }
+
+ // Copy provides informations
+ elfhandle->provideNumber = swap32(elftmp.elfheader->provideNumber);
+ if(elfhandle->provideNumber != 0)
+ {
+ elfhandle->provides =
+ (t_interface_provide*)OSAL_Alloc_Zero(sizeof(t_interface_provide) * elfhandle->provideNumber);
+ if(elfhandle->provides == NULL)
+ goto oom;
+
+ if(elfhandle->provideNumber > 0)
+ {
+ char *ref = getElfHeaderReference(&elftmp, (void*)elftmp.elfheader->provides);
+
+ for(i = 0; i < elfhandle->provideNumber; i++)
+ {
+ t_elf_provided_interface *provide = (t_elf_provided_interface*)ref;
+ t_elf_interface_description *interface = (t_elf_interface_description*)getElfHeaderReference(&elftmp, (void*)provide->interface);
+
+ elfhandle->provides[i].name = copyElfString(&elftmp, provide->name);
+ if(elfhandle->provides[i].name == NULL)
+ goto oom;
+
+ elfhandle->provides[i].provideTypes = provide->provideTypes;
+ elfhandle->provides[i].interruptLine = provide->interruptLine;
+ elfhandle->provides[i].collectionSize = provide->collectionSize;
+ elfhandle->provides[i].interface = getInterfaceDescription(&elftmp, interface);
+ if(elfhandle->provides[i].interface == NULL)
+ goto oom;
+
+ LOG_INTERNAL(2, " provide %s <%s>\n",
+ elfhandle->provides[i].name,
+ elfhandle->provides[i].interface->type,
+ 0,0, 0, 0);
+ CM_ASSERT(elfhandle->provides[i].collectionSize != 0);
+
+ ref += sizeof(t_elf_required_interface);
+
+ {
+ t_uint32 *methodSymbols = (t_uint32*)ref;
+
+ elfhandle->provides[i].indexes = (t_interface_provide_index**)OSAL_Alloc_Zero(
+ sizeof(t_interface_provide_index*) * elfhandle->provides[i].collectionSize);
+ if(elfhandle->provides[i].indexes == NULL)
+ goto oom;
+
+ if(elfhandle->provides[i].interface->methodNumber != 0)
+ {
+ for(j = 0; j < elfhandle->provides[i].collectionSize; j++)
+ {
+ elfhandle->provides[i].indexes[j] = (t_interface_provide_index*)OSAL_Alloc(
+ sizeof(t_interface_provide_index) * elfhandle->provides[i].interface->methodNumber);
+ if(elfhandle->provides[i].indexes[j] == NULL)
+ goto oom;
+
+ for(k = 0; k < elfhandle->provides[i].interface->methodNumber; k++)
+ {
+ if((error = getAdressForExecutableOffsetElsewhere(elfhandle, &elftmp,
+ methodSymbols++,
+ &elfhandle->provides[i].indexes[j][k].memory)) != CM_OK)
+ goto onerror;
+
+ LOG_INTERNAL(2, " [%d, %d] method '%s' mem=%s offset=%x\n",
+ j, k,
+ elfhandle->provides[i].interface->methodNames[k],
+ elfhandle->provides[i].indexes[j][k].memory.memory->memoryName,
+ elfhandle->provides[i].indexes[j][k].memory.offset,
+ 0);
+ }
+ }
+ }
+
+ ref += elfhandle->provides[i].collectionSize * elfhandle->provides[i].interface->methodNumber * sizeof(methodSymbols[0]);
+ }
+ }
+ }
+ }
+
+ return CM_OK;
+
+oom:
+ error = CM_NO_MORE_MEMORY;
+onerror:
+ cm_ELF_CloseFile(temporaryDescription, elfhandle);
+ *elfhandlePtr = NULL;
+ return error;
+}
+
+void cm_ELF_ReleaseDescription(
+ t_uint32 requireNumber, t_interface_require *requires,
+ t_uint32 attributeNumber, t_attribute *attributes,
+ t_uint32 propertyNumber, t_property *properties,
+ t_uint32 provideNumber, t_interface_provide *provides)
+{
+ int i, j;
+
+ // Free provides (Number set when array allocated)
+ if(provides != NULL)
+ {
+ for(i = 0; i < provideNumber; i++)
+ {
+ if(provides[i].indexes != NULL)
+ {
+ for(j = 0; j < provides[i].collectionSize; j++)
+ {
+ OSAL_Free(provides[i].indexes[j]);
+ }
+ OSAL_Free(provides[i].indexes);
+ }
+ releaseInterfaceDescription(provides[i].interface);
+ cm_StringRelease(provides[i].name);
+ }
+ OSAL_Free(provides);
+ }
+
+ // Free requires (Number set when array allocated)
+ if(requires != NULL)
+ {
+ for(i = 0; i < requireNumber; i++)
+ {
+ if(requires[i].indexes != 0)
+ {
+ for(j = 0; j < requires[i].collectionSize; j++)
+ {
+ OSAL_Free(requires[i].indexes[j].memories);
+ }
+ OSAL_Free(requires[i].indexes);
+ }
+ releaseInterfaceDescription(requires[i].interface);
+ cm_StringRelease(requires[i].name);
+ }
+ OSAL_Free(requires);
+ }
+
+ // Free properties (Number set when array allocated)
+ if(properties != NULL)
+ {
+ for(i = 0; i < propertyNumber; i++)
+ {
+ cm_StringRelease(properties[i].value);
+ cm_StringRelease(properties[i].name);
+ }
+ OSAL_Free(properties);
+ }
+
+ // Free Attributes (Number set when array allocated)
+ if(attributes != NULL)
+ {
+ for(i = 0; i < attributeNumber; i++)
+ {
+ cm_StringRelease(attributes[i].name);
+ }
+ OSAL_Free(attributes);
+ }
+}
+
+void cm_ELF_CloseFile(
+ t_bool temporaryDescription,
+ t_elfdescription *elfhandle)
+{
+ if(elfhandle == NULL)
+ return;
+
+ if(temporaryDescription && ! elfhandle->temporaryDescription)
+ return;
+
+ // Release description if not moved to template
+ cm_ELF_ReleaseDescription(
+ elfhandle->requireNumber, elfhandle->requires,
+ elfhandle->attributeNumber, elfhandle->attributes,
+ elfhandle->propertyNumber, elfhandle->properties,
+ elfhandle->provideNumber, elfhandle->provides);
+
+ cm_StringRelease(elfhandle->foundedTemplateName);
+
+ ELF64_UnloadComponent(elfhandle);
+}
+
+
+static t_cm_error allocSegment(
+ t_cm_domain_id domainId,
+ t_elfdescription *elfhandle,
+ t_memory_handle memories[NUMBER_OF_MMDSP_MEMORY],
+ t_memory_property property) {
+ t_memory_id memId;
+ const t_elfmemory *thisMemory; //!< Memory used to determine this
+ const t_elfmemory *codeMemory; //!< Memory used to determine code
+
+ MMDSP_serializeMemories(elfhandle->instanceProperty, &codeMemory, &thisMemory);
+
+ for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++)
+ {
+ const t_elfmemory* mapping;
+
+ if(elfhandle->segments[memId].sumSize == 0x0)
+ continue;
+
+ mapping = MMDSP_getMappingById(memId);
+
+ if(
+ (mapping->property == property && elfhandle->instanceProperty != MEM_FOR_SINGLETON) ||
+ (property == MEM_SHARABLE && elfhandle->instanceProperty == MEM_FOR_SINGLETON) )
+ {
+ // Allocate segment
+ memories[memId] = cm_DM_Alloc(domainId, mapping->dspMemType,
+ elfhandle->segments[memId].sumSize / mapping->fileEntSize,
+ mapping->memAlignement, TRUE);
+
+ if(memories[memId] == INVALID_MEMORY_HANDLE)
+ {
+ ERROR("CM_NO_MORE_MEMORY(%s): %x too big\n", mapping->memoryName, elfhandle->segments[memId].sumSize / mapping->fileEntSize, 0, 0, 0, 0);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ // Get reference in memory
+ elfhandle->segments[memId].hostAddr = cm_DSP_GetHostLogicalAddress(memories[memId]);
+
+ cm_DSP_GetDspAddress(memories[memId], &elfhandle->segments[memId].mpcAddr);
+
+ // Log it
+ LOG_INTERNAL(1, "\t%s%s: 0x%x..+0x%x (0x%x)\n",
+ mapping->memoryName,
+ (thisMemory == mapping) ? "(THIS)" : "",
+ elfhandle->segments[memId].mpcAddr,
+ elfhandle->segments[memId].sumSize / mapping->fileEntSize,
+ elfhandle->segments[memId].hostAddr, 0);
+ }
+ else if(property == MEM_PRIVATE) // Since we allocate private segment, if not allocate, it's a share one
+ {
+ // In order to allow further relocation based on cached address like mpcAddr & hostAddr,
+ // initialize them also !
+
+ // Get reference in memory
+ elfhandle->segments[memId].hostAddr = cm_DSP_GetHostLogicalAddress(memories[memId]);
+
+ cm_DSP_GetDspAddress(memories[memId], &elfhandle->segments[memId].mpcAddr);
+ }
+ }
+
+ return CM_OK;
+}
+
+/*
+ * Note: in case of error, part of memory could have been allocated and must be free by calling cm_DSPABI_FreeTemplate
+ */
+t_cm_error cm_ELF_LoadTemplate(
+ t_cm_domain_id domainId,
+ t_elfdescription *elfhandle,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY])
+{
+ t_cm_error error;
+
+ if((error = allocSegment(domainId, elfhandle, sharedMemories, MEM_SHARABLE)) != CM_OK)
+ return error;
+
+ // Load each readonly segment
+ if((error = ELF64_loadSegment(elfhandle, sharedMemories, MEM_SHARABLE)) != CM_OK)
+ return error;
+
+ return CM_OK;
+}
+
+t_cm_error cm_ELF_LoadInstance(
+ t_cm_domain_id domainId,
+ t_elfdescription *elfhandle,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY],
+ t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY])
+{
+ t_memory_id memId;
+ t_cm_error error;
+
+ // Erase whole memories to make free in case of error
+ for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++)
+ {
+ privateMemories[memId] = sharedMemories[memId];
+ }
+
+ if((error = allocSegment(domainId, elfhandle, privateMemories, MEM_PRIVATE)) != CM_OK)
+ return error;
+
+ // Load each writable memory
+ if((error = ELF64_loadSegment(elfhandle, privateMemories, MEM_PRIVATE)) != CM_OK)
+ return error;
+
+ return CM_OK;
+}
+
+void cm_ELF_FlushTemplate(
+ t_nmf_core_id coreId,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY])
+{
+ t_memory_id memId;
+
+ for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++)
+ {
+ if(sharedMemories[memId] != INVALID_MEMORY_HANDLE)
+ MMDSP_loadedSection(
+ coreId, memId,
+ sharedMemories[memId]);
+ }
+}
+
+void cm_ELF_FlushInstance(
+ t_nmf_core_id coreId,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY],
+ t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY])
+{
+ t_memory_id memId;
+
+ for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++)
+ {
+ if(privateMemories[memId] != INVALID_MEMORY_HANDLE && privateMemories[memId] != sharedMemories[memId])
+ MMDSP_loadedSection(
+ coreId, memId,
+ privateMemories[memId]);
+ }
+}
+
+void cm_ELF_FreeInstance(
+ t_nmf_core_id coreId,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY],
+ t_memory_handle privateMemories[NUMBER_OF_MMDSP_MEMORY])
+{
+ t_memory_id memId;
+
+ if(privateMemories == NULL)
+ return;
+
+ for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++)
+ {
+ if(privateMemories[memId] != INVALID_MEMORY_HANDLE && privateMemories[memId] != sharedMemories[memId])
+ {
+ MMDSP_unloadedSection(coreId, memId, privateMemories[memId]);
+ cm_DM_Free(privateMemories[memId], TRUE);
+ }
+ }
+}
+
+void cm_ELF_FreeTemplate(
+ t_nmf_core_id coreId,
+ t_memory_handle sharedMemories[NUMBER_OF_MMDSP_MEMORY])
+{
+ t_memory_id memId;
+
+ if(sharedMemories == NULL)
+ return;
+
+ for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++)
+ {
+ if(sharedMemories[memId] != INVALID_MEMORY_HANDLE)
+ {
+ MMDSP_unloadedSection(coreId, memId, sharedMemories[memId]);
+ cm_DM_Free(sharedMemories[memId], TRUE);
+ }
+ }
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/elfmmdsp.c b/drivers/staging/nmf-cm/cm/engine/elf/src/elfmmdsp.c
new file mode 100644
index 00000000000..452d0f1b175
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/src/elfmmdsp.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/elf/inc/mmdsp.h>
+#include <cm/engine/elf/inc/bfd.h>
+#include <cm/engine/elf/inc/mpcal.h>
+
+#include <cm/engine/component/inc/initializer.h>
+
+#include <cm/engine/utils/inc/string.h>
+#include <cm/engine/utils/inc/swap.h>
+#include <cm/engine/trace/inc/trace.h>
+
+#include <cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h>
+
+static const t_elfmemory mmdspMemories[NUMBER_OF_MMDSP_MEMORY] = {
+ {0, SDRAM_CODE, SDRAMTEXT_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_CODE, 8, 8, "SDRAM_CODE"}, /* 0: Program memory */
+ {1, INTERNAL_XRAM24, 0, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 4, "XROM"}, /* 1: Internal X memory */
+ {2, INTERNAL_YRAM24, 0, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 4, "YROM"}, /* 2: Y memory */
+ {3, SDRAM_EXT24, SDRAMMEM24_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 4, "SDR0M24"}, /* 5: SDRAM24 */
+ {4, SDRAM_EXT16, SDRAMMEM16_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 2, "SDROM16"}, /* 6: SDRAM16 */
+ {5, ESRAM_EXT24, ESRAMMEM24_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 4, "ESROM24"}, /* 8: ESRAM24 */
+ {6, ESRAM_EXT16, ESRAMMEM16_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_DATA, 3, 2, "ESROM16"}, /* 9: ESRAM16 */
+ {7, ESRAM_CODE, ESRAMTEXT_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_CODE, 8, 8, "ESRAM_CODE"}, /*10: ESRAM code */
+ {8, INTERNAL_XRAM24, 0, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 4, "XRAM"}, /* 1: Internal X memory */
+ {9, INTERNAL_YRAM24, 0, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 4, "YRAM"}, /* 2: Y memory */
+ {10, SDRAM_EXT24, SDRAMMEM24_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 4, "SDRAM24"}, /* 5: SDRAM24 */
+ {11, SDRAM_EXT16, SDRAMMEM16_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 2, "SDRAM16"}, /* 6: SDRAM16 */
+ {12, ESRAM_EXT24, ESRAMMEM16_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 4, "ESRAM24"}, /* 8: ESRAM24 */
+ {13, ESRAM_EXT16, ESRAMMEM16_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_PRIVATE, MEM_DATA, 3, 2, "ESRAM16"}, /* 9: ESRAM16 */
+ {14, LOCKED_CODE, SDRAMTEXT_BASE_ADDR, CM_MM_ALIGN_2WORDS, MEM_SHARABLE, MEM_CODE, 8, 8, "LOCKED_CODE"}, /* : .locked */
+};
+
+#define MAX_ELFSECTIONNAME 10
+struct memoryMapping {
+ char *elfSectionName;
+ t_uint32 memoryIndex[MEM_FOR_LAST]; // memoryIndex[t_instance_property]
+};
+
+static const struct memoryMapping mappingmem0[] = {
+ {"mem0.0", {0, 0}},
+ {"mem0.1", {0, 0}},
+ {"mem0.2", {0, 0}}
+};
+static const struct memoryMapping mappingmem10 =
+ {"mem10", {7, 7}};
+static const struct memoryMapping mappinglocked =
+ {".locked", {14, 14}};
+static const struct memoryMapping mappingmem1[] = {
+ {"", {0xff, 0xff}},
+ {"mem1.1", {1, 1}},
+ {"mem1.2", {8, 1}},
+ {"mem1.3", {1, 1}},
+ {"mem1.4", {8, 1}},
+ {"mem1.stack", {8, 1}}
+};
+static const struct memoryMapping mappingmem2[] = {
+ {"", {0xff, 0xff}},
+ {"mem2.1", {2, 2}},
+ {"mem2.2", {9, 2}},
+ {"mem2.3", {2, 2}},
+ {"mem2.4", {9, 2}}
+};
+static const struct memoryMapping mappingmem5[] = {
+ {"", {0xff, 0xff}},
+ {"mem5.1", {3, 3}},
+ {"mem5.2", {10, 3}},
+ {"mem5.3", {3, 3}},
+ {"mem5.4", {10, 3}}
+};
+static const struct memoryMapping mappingmem6[] = {
+ {"", {0xff, 0xff}},
+ {"mem6.1", {4, 4}},
+ {"mem6.2", {11, 4}},
+ {"mem6.3", {4, 4}},
+ {"mem6.4", {11, 4}}
+};
+static const struct memoryMapping mappingmem8[] = {
+ {"", {0xff, 0xff}},
+ {"mem8.1", {5, 5}},
+ {"mem8.2", {12, 5}},
+ {"mem8.3", {5, 5}},
+ {"mem8.4", {12, 5}}
+};
+static const struct memoryMapping mappingmem9[] = {
+ {"", {0xff, 0xff}},
+ {"mem9.1", {6, 6}},
+ {"mem9.2", {13, 6}},
+ {"mem9.3", {6, 6}},
+ {"mem9.4", {13, 6}}
+};
+
+static const struct {
+ const struct memoryMapping* mapping;
+ unsigned int number;
+} hashMappings[10] = {
+ {mappingmem0, sizeof(mappingmem0) / sizeof(mappingmem0[0])},
+ {mappingmem1, sizeof(mappingmem1) / sizeof(mappingmem1[0])},
+ {mappingmem2, sizeof(mappingmem2) / sizeof(mappingmem2[0])},
+ {0x0, 0},
+ {0x0, 0},
+ {mappingmem5, sizeof(mappingmem5) / sizeof(mappingmem5[0])},
+ {mappingmem6, sizeof(mappingmem6) / sizeof(mappingmem6[0])},
+ {0x0, 0},
+ {mappingmem8, sizeof(mappingmem8) / sizeof(mappingmem8[0])},
+ {mappingmem9, sizeof(mappingmem9) / sizeof(mappingmem9[0])},
+};
+
+const t_elfmemory* MMDSP_getMappingById(t_memory_id memId)
+{
+ return &mmdspMemories[memId];
+}
+
+const t_elfmemory* MMDSP_getMappingByName(const char* sectionName, t_instance_property property)
+{
+ if(sectionName[0] == 'm' && sectionName[1] == 'e' && sectionName[2] == 'm')
+ {
+ if(sectionName[4] == '.')
+ {
+ if(sectionName[5] >= '0' && sectionName[5] <= '9')
+ {
+ if(sectionName[3] >= '0' && sectionName[3] <= '9')
+ {
+ unsigned int m, sm;
+
+ m = sectionName[3] - '0';
+ sm = sectionName[5] - '0';
+ if(sm < hashMappings[m].number)
+ return &mmdspMemories[hashMappings[m].mapping[sm].memoryIndex[property]];
+ }
+ } else if(sectionName[3] == '1' && sectionName[5] == 's')
+ return &mmdspMemories[mappingmem1[5].memoryIndex[property]];
+ }
+ else if(sectionName[3] == '1' && sectionName[4] == '0')
+ return &mmdspMemories[mappingmem10.memoryIndex[property]];
+ }
+ else if(sectionName[0] == '.' && sectionName[1] == 'l' && sectionName[2] == 'o' && sectionName[3] == 'c' &&
+ sectionName[4] == 'k' && sectionName[5] == 'e' && sectionName[6] == 'd')
+ {
+ return &mmdspMemories[mappinglocked.memoryIndex[property]];
+ }
+
+ return NULL;
+}
+
+void MMDSP_serializeMemories(t_instance_property property,
+ const t_elfmemory** codeMemory, const t_elfmemory** thisMemory) {
+ // Return meory reference
+ *codeMemory = &mmdspMemories[0];
+ if(property == MEM_FOR_SINGLETON)
+ {
+ *thisMemory = &mmdspMemories[1];
+ }
+ else
+ {
+ *thisMemory = &mmdspMemories[8];
+ }
+}
+
+void MMDSP_copyCode(t_uint64 * remoteAddr64, const char* origAddr, int nb)
+{
+ int m;
+
+ // Linux allow unaligned access
+#ifdef LINUX
+ t_uint64 *origAddr64 = (t_uint64*)origAddr;
+#else
+ __packed t_uint64 *origAddr64 = (__packed t_uint64*)origAddr;
+#endif
+
+ for (m = 0; m < nb; m += 8)
+ {
+ *remoteAddr64++ = swap64(*origAddr64++);
+ }
+}
+
+void MMDSP_copyData24(t_uint32 * remoteAddr32, const char* origAddr, int nb)
+{
+ int m;
+
+ for (m = 0; m < nb; m+=4)
+ {
+ t_uint32 value1;
+
+ value1 = (*origAddr++ << 16);
+ value1 |= (*origAddr++ << 8);
+ value1 |= (*origAddr++ << 0);
+ *remoteAddr32++ = value1;
+ }
+}
+
+void MMDSP_copyData16(t_uint16 * remoteAddr16, const char* origAddr, int nb)
+{
+ int m;
+
+ for (m = 0; m < nb; m+=2)
+ {
+ t_uint16 value1;
+
+ origAddr++; // Skip this byte (which is put in elf file for historical reason)
+ value1 = (*origAddr++ << 8);
+ value1 |= (*origAddr++ << 0);
+ *remoteAddr16++ = value1;
+ }
+}
+
+#if 0
+__asm void MMDSP_copyCode(void* dst, const void* src, int nb)
+{
+ PUSH {r4-r8, lr}
+ SUBS r2,r2,#0x20
+ BCC l4
+
+l5
+ SETEND BE
+ LDR r4, [r1], #0x4
+ LDR r3, [r1], #0x4
+ LDR r6, [r1], #0x4
+ LDR r5, [r1], #0x4
+ LDR r8, [r1], #0x4
+ LDR r7, [r1], #0x4
+ LDR lr, [r1], #0x4
+ LDR r12, [r1], #0x4
+
+ SETEND LE
+ STM r0!,{r3-r8,r12, lr}
+ SUBS r2,r2,#0x20
+ BCS l5
+
+l4
+ LSLS r12,r2,#28
+
+ SETEND BE
+ LDRCS r4, [r1], #0x4
+ LDRCS r3, [r1], #0x4
+ LDRCS r6, [r1], #0x4
+ LDRCS r5, [r1], #0x4
+ SETEND LE
+ STMCS r0!,{r3-r6}
+
+ SETEND BE
+ LDRMI r4, [r1], #0x4
+ LDRMI r3, [r1], #0x4
+ SETEND LE
+ STMMI r0!,{r3-r4}
+
+ POP {r4-r8, pc}
+}
+#endif
+
+#ifdef LINUX
+static void PLD5(int r)
+{
+ asm volatile (
+ "PLD [r0, #0x20] \n\t"
+ "PLD [r0, #0x40] \n\t"
+ "PLD [r0, #0x60] \n\t"
+ "PLD [r0, #0x80] \n\t"
+ "PLD [r0, #0xA0]" );
+}
+
+static void PLD1(int r)
+{
+ asm volatile (
+ "PLD [r0, #0xC0]" );
+}
+#else /* Symbian, Think -> We assume ARMCC */
+static __asm void PLD5(int r)
+{
+ PLD [r0, #0x20]
+ PLD [r0, #0x40]
+ PLD [r0, #0x60]
+ PLD [r0, #0x80]
+ PLD [r0, #0xA0]
+
+ bx lr
+}
+
+static __asm void PLD1(int r)
+{
+ PLD [r0, #0xC0]
+
+ bx lr
+}
+#endif
+
+#if 0
+__asm void COPY(void* dst, const void* src, int nb)
+{
+ PUSH {r4-r8, lr}
+ SUBS r2,r2,#0x20
+ BCC l4a
+ PLD [r1, #0x20]
+ PLD [r1, #0x40]
+ PLD [r1, #0x60]
+ PLD [r1, #0x80]
+ PLD [r1, #0xA0]
+
+l5a
+ PLD [r1, #0xC0]
+ LDM r1!,{r3-r8,r12,lr}
+ STM r0!,{r3-r8,r12,lr}
+ SUBS r2,r2,#0x20
+ BCS l5a
+
+l4a
+ LSLS r12,r2,#28
+ LDMCS r1!,{r3,r4,r12,lr}
+ STMCS r0!,{r3,r4,r12,lr}
+ LDMMI r1!,{r3,r4}
+ STMMI r0!,{r3,r4}
+ POP {r4-r8,lr}
+ LSLS r12,r2,#30
+ LDRCS r3,[r1],#4
+ STRCS r3,[r0],#4
+ BXEQ lr
+l6b
+ LSLS r2,r2,#31
+ LDRHCS r3,[r1],#2
+ LDRBMI r2,[r1],#1
+ STRHCS r3,[r0],#2
+ STRBMI r2,[r0],#1
+ BX lr
+}
+#endif
+
+
+void MMDSP_copySection(t_uint32 origAddr, t_uint32 remoteAddr, t_uint32 sizeInByte) {
+ t_uint32 endAddr = remoteAddr + sizeInByte;
+
+ PLD5(origAddr);
+
+ // Align on 32bits
+ if((remoteAddr & 0x3) != 0)
+ {
+ *(t_uint16*)remoteAddr = *(t_uint16*)origAddr;
+ remoteAddr += sizeof(t_uint16);
+ origAddr += sizeof(t_uint16);
+ }
+
+ // Align on 64bits
+ if((remoteAddr & 0x7) != 0 && (remoteAddr <= endAddr - sizeof(t_uint32)))
+ {
+ *(t_uint32*)remoteAddr = *(t_uint32*)origAddr;
+ remoteAddr += sizeof(t_uint32);
+ origAddr += sizeof(t_uint32);
+ }
+
+ // 64bits burst access
+ for(; remoteAddr <= endAddr - sizeof(t_uint64); remoteAddr += sizeof(t_uint64), origAddr += sizeof(t_uint64))
+ {
+ PLD1(origAddr);
+ *(volatile t_uint64*)remoteAddr = *(t_uint64*)origAddr;
+ }
+
+ // Remain 32bits access
+ if(remoteAddr <= endAddr - sizeof(t_uint32))
+ {
+ *(t_uint32*)remoteAddr = *(t_uint32*)origAddr;
+ remoteAddr += sizeof(t_uint32);
+ origAddr += sizeof(t_uint32);
+ }
+
+ // Remain 16bits access
+ if(remoteAddr <= endAddr - sizeof(t_uint16))
+ *(t_uint16*)remoteAddr = *(t_uint16*)origAddr;
+}
+
+
+void MMDSP_bzeroSection(t_uint32 remoteAddr, t_uint32 sizeInByte) {
+ t_uint32 endAddr = remoteAddr + sizeInByte;
+
+ // Align on 32bits
+ if((remoteAddr & 0x3) != 0)
+ {
+ *(t_uint16*)remoteAddr = 0;
+ remoteAddr += sizeof(t_uint16);
+ }
+
+ // Align on 64bits
+ if((remoteAddr & 0x7) != 0 && (remoteAddr <= endAddr - sizeof(t_uint32)))
+ {
+ *(t_uint32*)remoteAddr = 0;
+ remoteAddr += sizeof(t_uint32);
+ }
+
+ // 64bits burst access
+ for(; remoteAddr <= endAddr - sizeof(t_uint64); remoteAddr += sizeof(t_uint64))
+ *(volatile t_uint64*)remoteAddr = 0ULL;
+
+ // Remain 32bits access
+ if(remoteAddr <= endAddr - sizeof(t_uint32))
+ {
+ *(t_uint32*)remoteAddr = 0;
+ remoteAddr += sizeof(t_uint32);
+ }
+
+ // Remain 16bits access
+ if(remoteAddr <= endAddr - sizeof(t_uint16))
+ *(t_uint16*)remoteAddr = 0;
+}
+
+void MMDSP_loadedSection(t_nmf_core_id coreId, t_memory_id memId, t_memory_handle handle)
+{
+ if(mmdspMemories[memId].purpose == MEM_CODE)
+ {
+ OSAL_CleanDCache(cm_DSP_GetHostLogicalAddress(handle), cm_MM_GetSize(handle));
+ }
+
+ if(memId == LOCKED_CODE)
+ {
+ t_uint32 DspAddress, DspSize;
+
+ cm_DSP_GetDspMemoryHandleSize(handle, &DspSize);
+ cm_DSP_GetDspAddress(handle, &DspAddress);
+
+ cm_COMP_InstructionCacheLock(coreId, DspAddress, DspSize);
+ }
+}
+
+void MMDSP_unloadedSection(t_nmf_core_id coreId, t_memory_id memId, t_memory_handle handle)
+{
+ if(memId == LOCKED_CODE)
+ {
+ t_uint32 DspAddress, DspSize;
+
+ cm_DSP_GetDspMemoryHandleSize(handle, &DspSize);
+ cm_DSP_GetDspAddress(handle, &DspAddress);
+
+ cm_COMP_InstructionCacheUnlock(coreId, DspAddress, DspSize);
+ }
+
+}
+
+static struct reloc_howto_struct elf64_mmdsp_howto_table[] =
+{
+ HOWTO (R_MMDSP_IMM20_16, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 8, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ 0x0, /* special_function */
+ "R_MMDSP_IMM20_16", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0x0000000000ffff00, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 4-bit absolute relocation for splitted 20 bits immediate, shifted by 56 */
+
+ HOWTO (R_MMDSP_IMM20_4, /* type */
+ 16, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 4, /* bitsize */
+ FALSE, /* pc_relative */
+ 56, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ 0x0, /* special_function */
+ "R_MMDSP_IMM20_4", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0x0f00000000000000LL, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_MMDSP_24, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 24, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ 0x0, /* special_function */
+ "R_MMDSP_24", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_MMDSP_IMM16, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 8, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ 0x0, /* special_function */
+ "R_MMDSP_IMM16", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0x0000000000ffff00, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+static const char* lastInPlaceAddr = 0;
+static long long lastInPlaceValue;
+
+void MMDSP_performRelocation(
+ t_uint32 type,
+ const char* symbol_name,
+ t_uint32 symbol_addr,
+ char* reloc_addr,
+ const char* inPlaceAddr,
+ t_uint32 reloc_offset) {
+ int i;
+
+ for(i = 0; i < sizeof(elf64_mmdsp_howto_table) / sizeof(elf64_mmdsp_howto_table[0]); i++)
+ {
+ struct reloc_howto_struct* howto = &elf64_mmdsp_howto_table[i];
+ if(howto->type == type)
+ {
+ t_uint64 relocation;
+
+ LOG_INTERNAL(2, "reloc '%s:0x%x' type %s at 0x%x (0x%x)\n",
+ symbol_name ? symbol_name : "??", symbol_addr,
+ howto->name,
+ reloc_offset, reloc_addr, 0);
+
+ relocation = symbol_addr;
+
+ if (howto->pc_relative) {
+ // Not handle yet
+ }
+
+ if (howto->complain_on_overflow != complain_overflow_dont) {
+ // Not handle yet
+ }
+
+ relocation >>= howto->rightshift;
+
+ relocation <<= howto->bitpos;
+
+#define DOIT(x) \
+ x = ( (x & ~howto->dst_mask) | (((x & howto->src_mask) + relocation) & howto->dst_mask))
+
+ switch (howto->size) {
+ case 2: {
+ long x = *(long*)inPlaceAddr;
+
+ // CM_ASSERT(*(long*)inPlaceAddr == *(long*)reloc_addr);
+
+ DOIT (x);
+ *(long*)reloc_addr = x;
+ }
+ break;
+ case 4: {
+ long long x;
+ if(lastInPlaceAddr == inPlaceAddr)
+ {
+ x = lastInPlaceValue;
+ }
+ else
+ {
+ // CM_ASSERT(*(__packed long long*)inPlaceAddr == *(long long*)reloc_addr);
+ x = *(long long*)inPlaceAddr;
+ lastInPlaceAddr = inPlaceAddr;
+ }
+
+ DOIT (x);
+ *(long long*)reloc_addr = lastInPlaceValue = x;
+ }
+ break;
+ default:
+ CM_ASSERT(0);
+ }
+
+ return;
+ }
+ }
+
+ ERROR("Relocation type %d not supported for '%s'\n", type, symbol_name, 0, 0, 0, 0);
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/elfrelocate.c b/drivers/staging/nmf-cm/cm/engine/elf/src/elfrelocate.c
new file mode 100644
index 00000000000..b08ac6a361e
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/src/elfrelocate.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ *
+ */
+#include <cm/engine/elf/inc/bfd.h>
+#include <cm/engine/elf/inc/mpcal.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/utils/inc/string.h>
+
+t_cm_error cm_ELF_relocateSharedSegments(
+ t_memory_handle *memories,
+ t_elfdescription *elfhandle,
+ void *cbContext)
+{
+ return ELF64_relocateSegments(
+ memories,
+ elfhandle,
+ MEM_SHARABLE,
+ cbContext);
+}
+
+t_cm_error cm_ELF_relocatePrivateSegments(
+ t_memory_handle *memories,
+ t_elfdescription *elfhandle,
+ void *cbContext)
+{
+ return ELF64_relocateSegments(
+ memories,
+ elfhandle,
+ MEM_PRIVATE,
+ cbContext);
+}
+
+void cm_ELF_performRelocation(
+ t_uint32 type,
+ const char* symbol_name,
+ t_uint32 symbol_addr,
+ char* reloc_addr)
+{
+ MMDSP_performRelocation(
+ type,
+ symbol_name,
+ symbol_addr,
+ reloc_addr,
+ reloc_addr,
+ 0xBEEF);
+
+ OSAL_CleanDCache((t_uint32)reloc_addr, 8);
+}
+
+t_cm_error cm_ELF_GetMemory(
+ t_elfdescription *elf,
+ t_tmp_elfdescription *elftmp,
+ t_uint32 address,
+ t_memory_purpose purpose,
+ t_memory_reference *memory) {
+ t_memory_id memId;
+
+ for(memId = 0; memId < NUMBER_OF_MMDSP_MEMORY; memId++)
+ {
+ const t_elfmemory* mem = MMDSP_getMappingById(memId);
+
+ if(mem->purpose == purpose && // Memory correspond
+ elf->segments[mem->id].sumSize != 0 && // Segment allocated
+ (elf->segments[mem->id].mpcAddr <= address) &&
+ (address < elf->segments[mem->id].mpcAddr + elf->segments[mem->id].sumSize / mem->fileEntSize)) {
+ memory->memory = mem;
+ memory->offset = address - elf->segments[mem->id].mpcAddr;
+ return CM_OK;
+ }
+ }
+
+ ERROR("Memory %x,%d not found\n", address, purpose, 0, 0, 0, 0);
+ return CM_INVALID_ELF_FILE;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/elfxx.c b/drivers/staging/nmf-cm/cm/engine/elf/src/elfxx.c
new file mode 100644
index 00000000000..4a2976a6bc1
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/src/elfxx.c
@@ -0,0 +1,591 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/elf/inc/mpcal.h>
+
+#include <cm/engine/utils/inc/string.h>
+#include <cm/engine/utils/inc/mem.h>
+
+
+static t_uint32 max(t_uint32 a, t_uint32 b)
+{
+ return (a >= b) ? a : b;
+}
+/*
+static t_uint32 min(t_uint32 a, t_uint32 b)
+{
+ return (a <= b) ? a : b;
+}
+*/
+
+struct XXrelocation
+{
+ t_uint32 st_value;
+ ElfXX_Half st_shndx;
+ Elf64_Sxword r_addend;
+ t_uint32 OffsetInElf;
+ t_uint32 type;
+
+ t_dup_char symbol_name; // Valid only if st_shndx == SHN_UNDEF
+};
+
+struct XXSection {
+ ElfXX_Word sh_type; /* Section type */
+ t_uint32 sh_size; /* Section size in bytes */
+ ElfXX_Word sh_info; /* Additional section information */
+ ElfXX_Word sh_link; /* Link to another section */
+ t_uint32 sh_addralign; /* Some sections have address alignment constraints */
+ t_uint32 sh_addr; /* Section addr */
+ ElfXX_Xword sh_flags; /* Section flags */
+
+ const char *data;
+ t_uint32 trueDataSize; /* Valid if different from sh_size */
+ const char *sectionName;
+
+ t_uint32 offsetInSegment;
+ const t_elfmemory *meminfo;
+
+ t_uint32 relocationNumber;
+ struct XXrelocation *relocations;
+};
+
+struct XXElf {
+ t_uint32 e_shnum;
+ struct XXSection sectionss[1];
+};
+
+t_cm_error ELF64_LoadComponent(
+ t_uint16 e_machine,
+ const char *elfdata,
+ t_elfdescription **elfhandlePtr,
+ t_tmp_elfdescription *elftmp)
+{
+ t_elfdescription *elfhandle;
+ const ElfXX_Ehdr *header = (ElfXX_Ehdr*)elfdata;
+ const ElfXX_Shdr *sections;
+ const char *strings;
+ struct XXElf* ELF;
+ int i, nb;
+
+ elftmp->elfdata = elfdata;
+
+ /* Sanity check */
+ if (swapHalf(header->e_machine) != e_machine)
+ {
+ ERROR("This is not a executable for such MPC\n", 0, 0, 0, 0, 0, 0);
+ return CM_INVALID_ELF_FILE;
+ }
+
+ // Cache elf file informations
+ nb = swapHalf(header->e_shnum);
+ elftmp->isExecutable = (swapHalf(header->e_type) == ET_EXEC);
+
+ elfhandle = (t_elfdescription*)OSAL_Alloc_Zero(
+ sizeof(t_elfdescription) + sizeof(struct XXElf) + sizeof(struct XXSection) * (nb - 1));
+ if(elfhandle == NULL)
+ return CM_NO_MORE_MEMORY;
+
+ ELF = elfhandle->ELF = (struct XXElf*)(elfhandle + 1);
+
+ ELF->e_shnum = nb;
+
+ sections = (ElfXX_Shdr*)&elfdata[swapXword(header->e_shoff)];
+ // Compute and swap section infromation
+ for(i = 0; i < ELF->e_shnum; i++)
+ {
+ ELF->sectionss[i].sh_type = swapWord(sections[i].sh_type);
+ ELF->sectionss[i].sh_info = swapWord(sections[i].sh_info);
+ ELF->sectionss[i].sh_link = swapWord(sections[i].sh_link);
+ ELF->sectionss[i].sh_size = (t_uint32)swapXword(sections[i].sh_size);
+ ELF->sectionss[i].sh_addralign = (t_uint32)swapXword(sections[i].sh_addralign);
+ ELF->sectionss[i].sh_addr = (t_uint32)swapXword(sections[i].sh_addr);
+ ELF->sectionss[i].sh_flags = swapXword(sections[i].sh_flags);
+
+ elftmp->sectionData[i] = &elfdata[(t_uint32)swapXword(sections[i].sh_offset)];
+ }
+
+ /*
+ * search nmf_segment
+ */
+ strings = elftmp->sectionData[swapHalf(header->e_shstrndx)];
+ for(i = 0; i < ELF->e_shnum; i++)
+ {
+ ELF->sectionss[i].sectionName = &strings[swapWord(sections[i].sh_name)];
+
+ // Found nmf_segment to see if it's
+ if(cm_StringCompare("nmf_segment", ELF->sectionss[i].sectionName, 11) == 0) {
+ elftmp->nmfSectionIndex = i;
+ elftmp->elfheader = (const t_elf_component_header*)elftmp->sectionData[i];
+ }
+ }
+
+ if(elftmp->nmfSectionIndex == 0)
+ {
+ ERROR("This is not a NMF component\n", 0, 0, 0, 0, 0, 0);
+ goto invalid;
+ }
+
+ /*
+ * Determine component type
+ */
+ elfhandle->magicNumber = swap32(elftmp->elfheader->magic);
+ switch(elfhandle->magicNumber) {
+ case MAGIC_COMPONENT:
+ elfhandle->instanceProperty = MEM_FOR_MULTIINSTANCE;
+ break;
+ case MAGIC_SINGLETON:
+ case MAGIC_FIRMWARE:
+ elfhandle->instanceProperty = MEM_FOR_SINGLETON;
+ break;
+ }
+
+ // Copy content
+ for(i = 0; i < ELF->e_shnum; i++)
+ {
+ ELF->sectionss[i].meminfo = MMDSP_getMappingByName(
+ ELF->sectionss[i].sectionName,
+ elfhandle->instanceProperty);
+
+ if(ELF->sectionss[i].meminfo != NULL)
+ ELF->sectionss[i].trueDataSize = (ELF->sectionss[i].sh_size / ELF->sectionss[i].meminfo->fileEntSize) * ELF->sectionss[i].meminfo->memEntSize;
+
+ if(ELF->sectionss[i].sh_size != 0 &&
+ ELF->sectionss[i].sh_type == SHT_PROGBITS &&
+ (ELF->sectionss[i].sh_flags & SHF_ALLOC) != 0)
+ {
+ const char* elfAddr = elftmp->sectionData[i];
+
+ ELF->sectionss[i].data = OSAL_Alloc(ELF->sectionss[i].trueDataSize);
+ if(ELF->sectionss[i].data == NULL)
+ goto oom;
+
+ if(ELF->sectionss[i].meminfo->purpose == MEM_CODE)
+ {
+ MMDSP_copyCode(
+ (t_uint64*)ELF->sectionss[i].data,
+ elfAddr,
+ ELF->sectionss[i].trueDataSize);
+ }
+ else if(ELF->sectionss[i].meminfo->purpose == MEM_DATA &&
+ // Always 3 for data ELF->sectionss[i].meminfo->fileEntSize == 3 &&
+ ELF->sectionss[i].meminfo->memEntSize == 4)
+ {
+ MMDSP_copyData24(
+ (t_uint32*)ELF->sectionss[i].data,
+ elfAddr,
+ ELF->sectionss[i].trueDataSize);
+ }
+ else if(ELF->sectionss[i].meminfo->purpose == MEM_DATA &&
+ // Always 3 for data ELF->sectionss[i].meminfo->fileEntSize == 3 &&
+ ELF->sectionss[i].meminfo->memEntSize == 2)
+ {
+ MMDSP_copyData16(
+ (t_uint16*)ELF->sectionss[i].data,
+ elfAddr,
+ ELF->sectionss[i].trueDataSize);
+ }
+ else
+ CM_ASSERT(0);
+ }
+ }
+
+ // Copy relocation
+ // Loop on all relocation section
+ for(i=0; i < ELF->e_shnum; i++)
+ {
+ int sh_info;
+
+ // Does this section is a relocation table (only RELA supported)
+ if((ELF->sectionss[i].sh_type != SHT_RELA) ||
+ ELF->sectionss[i].sh_size == 0) continue;
+
+ // Copy only relocation for loaded section
+ sh_info = ELF->sectionss[i].sh_info;
+ if(ELF->sectionss[sh_info].meminfo != NULL)
+ {
+ const ElfXX_Sym* symtab;
+ const char* strtab;
+ ElfXX_Rela* rel_start;
+ int n;
+
+ ELF->sectionss[sh_info].relocationNumber = ELF->sectionss[i].sh_size / sizeof(ElfXX_Rela);
+ ELF->sectionss[sh_info].relocations = (struct XXrelocation*)OSAL_Alloc_Zero(sizeof(struct XXrelocation) * ELF->sectionss[sh_info].relocationNumber);
+ if(ELF->sectionss[sh_info].relocations == NULL)
+ goto oom;
+
+ symtab = (ElfXX_Sym *)elftmp->sectionData[ELF->sectionss[i].sh_link];
+ strtab = elftmp->sectionData[ELF->sectionss[ELF->sectionss[i].sh_link].sh_link];
+ rel_start = (ElfXX_Rela*)elftmp->sectionData[i];
+ for(n = 0; n < ELF->sectionss[sh_info].relocationNumber; n++, rel_start++)
+ {
+ struct XXrelocation* relocation = &ELF->sectionss[sh_info].relocations[n];
+ ElfXX_Xword r_info = swapXword(rel_start->r_info);
+ int strtab_index = ELFXX_R_SYM(r_info);
+ const char* symbol_name = &strtab[swapWord(symtab[strtab_index].st_name)];
+
+ relocation->st_shndx = swapHalf(symtab[strtab_index].st_shndx);
+ relocation->st_value = (t_uint32)swapXword(symtab[strtab_index].st_value);
+ relocation->r_addend = swapXword(rel_start->r_addend);
+ relocation->OffsetInElf = (t_uint32)swapXword(rel_start->r_offset) / ELF->sectionss[sh_info].meminfo->fileEntSize;
+ relocation->type = ELFXX_R_TYPE(r_info);
+
+ switch(relocation->st_shndx) {
+ case SHN_UNDEF:
+ relocation->symbol_name = cm_StringDuplicate(symbol_name + 1); /* Remove '_' prefix */
+ if(relocation->symbol_name == NULL)
+ goto oom;
+ break;
+ case SHN_COMMON:
+ ERROR("SHN_COMMON not handle for %s\n", symbol_name, 0, 0, 0, 0, 0);
+ goto invalid;
+ }
+ }
+ }
+ }
+
+ *elfhandlePtr = elfhandle;
+ return CM_OK;
+invalid:
+ ELF64_UnloadComponent(elfhandle);
+ return CM_INVALID_ELF_FILE;
+oom:
+ ELF64_UnloadComponent(elfhandle);
+ return CM_NO_MORE_MEMORY;
+}
+
+t_cm_error ELF64_ComputeSegment(
+ t_elfdescription *elfhandle,
+ t_tmp_elfdescription *elftmp)
+{
+ struct XXElf* ELF = elfhandle->ELF;
+ int i;
+
+ for(i = 0; i < ELF->e_shnum; i++)
+ {
+ ELF->sectionss[i].offsetInSegment = 0xFFFFFFFF;
+
+ if(ELF->sectionss[i].sh_type == SHT_PROGBITS || ELF->sectionss[i].sh_type == SHT_NOBITS) {
+ // This is a loadable memory (memory size could be zero since we can have symbol on it)...
+ const t_elfmemory* meminfo = ELF->sectionss[i].meminfo;
+
+ if(meminfo != NULL) {
+ // Which correspond to MPC memory
+
+ if(elftmp->isExecutable)
+ {
+ if(! elfhandle->segments[meminfo->id].sumSizeSetted)
+ {
+ CM_ASSERT(ELF->sectionss[i].sh_addr >= meminfo->startAddr * meminfo->fileEntSize);
+
+ elfhandle->segments[meminfo->id].sumSizeSetted = TRUE;
+ elfhandle->segments[meminfo->id].sumSize = ELF->sectionss[i].sh_addr - meminfo->startAddr * meminfo->fileEntSize;
+ }
+ else
+ CM_ASSERT(elfhandle->segments[meminfo->id].sumSize == ELF->sectionss[i].sh_addr - meminfo->startAddr * meminfo->fileEntSize);
+ }
+ else
+ {
+ while(elfhandle->segments[meminfo->id].sumSize % ELF->sectionss[i].sh_addralign != 0)
+ elfhandle->segments[meminfo->id].sumSize++;
+ }
+
+ elfhandle->segments[meminfo->id].maxAlign = max(elfhandle->segments[meminfo->id].maxAlign, ELF->sectionss[i].sh_addralign);
+ ELF->sectionss[i].offsetInSegment = elfhandle->segments[meminfo->id].sumSize / meminfo->fileEntSize;
+ elfhandle->segments[meminfo->id].sumSize += ELF->sectionss[i].sh_size;
+ }
+ } else if(ELF->sectionss[i].sh_type == SHT_RELA && ELF->sectionss[i].sh_info == elftmp->nmfSectionIndex) {
+ int secsym = ELF->sectionss[i].sh_link;
+ elftmp->relaNmfSegment = (ElfXX_Rela*)elftmp->sectionData[i];
+ elftmp->relaNmfSegmentEnd = (ElfXX_Rela*)((t_uint32)elftmp->relaNmfSegment + ELF->sectionss[i].sh_size);
+ elftmp->relaNmfSegmentSymbols = (ElfXX_Sym*)elftmp->sectionData[secsym];
+ elftmp->relaNmfSegmentStrings = elftmp->sectionData[ELF->sectionss[secsym].sh_link];
+ }
+ }
+
+ return CM_OK;
+}
+
+void ELF64_UnloadComponent(
+ t_elfdescription *elfhandle)
+{
+ struct XXElf* ELF = elfhandle->ELF;
+ int i, n;
+
+ for(i = 0; i < ELF->e_shnum; i++)
+ {
+ if(ELF->sectionss[i].relocations != NULL)
+ {
+ for(n = 0; n < ELF->sectionss[i].relocationNumber; n++)
+ cm_StringRelease(ELF->sectionss[i].relocations[n].symbol_name);
+ OSAL_Free(ELF->sectionss[i].relocations);
+ }
+
+ OSAL_Free((void*)ELF->sectionss[i].data);
+ }
+ OSAL_Free(elfhandle);
+}
+
+t_cm_error ELF64_loadSegment(
+ t_elfdescription *elfhandle,
+ t_memory_handle *memory,
+ t_memory_property property)
+{
+ struct XXElf* ELF = elfhandle->ELF;
+ int i;
+
+ /*
+ * Copy ELF data in this segment
+ */
+ for(i = 0; i < ELF->e_shnum; i++)
+ {
+ const t_elfmemory* mapping = ELF->sectionss[i].meminfo;
+
+ if(mapping == NULL)
+ continue;
+ if((! (ELF->sectionss[i].sh_flags & SHF_ALLOC)) || (ELF->sectionss[i].sh_size == 0))
+ continue;
+
+ // This is a loadable memory ...
+ if(
+ (mapping->property == property && elfhandle->instanceProperty != MEM_FOR_SINGLETON) ||
+ (property == MEM_SHARABLE && elfhandle->instanceProperty == MEM_FOR_SINGLETON) )
+ {
+ // Where memory exist and waited share/private correspond
+ t_uint32 remoteData = elfhandle->segments[mapping->id].hostAddr +
+ ELF->sectionss[i].offsetInSegment * mapping->memEntSize;
+
+ if(ELF->sectionss[i].sh_type != SHT_NOBITS)
+ {
+ LOG_INTERNAL(2, "loadSection(%s, 0x%x, 0x%x, 0x%08x)\n",
+ ELF->sectionss[i].sectionName, remoteData, ELF->sectionss[i].trueDataSize,
+ (t_uint32)ELF->sectionss[i].data, 0, 0);
+
+ MMDSP_copySection((t_uint32)ELF->sectionss[i].data, remoteData, ELF->sectionss[i].trueDataSize);
+ }
+ else
+ {
+ LOG_INTERNAL(2, "bzeroSection(%s, 0x%x, 0x%x)\n",
+ ELF->sectionss[i].sectionName, remoteData, ELF->sectionss[i].trueDataSize, 0, 0, 0);
+
+ MMDSP_bzeroSection(remoteData, ELF->sectionss[i].trueDataSize);
+ }
+ }
+ }
+
+ return CM_OK;
+}
+
+
+
+static const t_elfmemory* getSectionAddress(
+ t_memory_handle *memories,
+ t_elfdescription *elfhandle,
+ t_uint32 sectionIdx,
+ t_uint32 *sectionOffset,
+ t_cm_logical_address *sectionAddr) {
+ struct XXElf* ELF = elfhandle->ELF;
+ const t_elfmemory* mapping = ELF->sectionss[sectionIdx].meminfo;
+
+ if(mapping != NULL) {
+ *sectionOffset = (elfhandle->segments[mapping->id].mpcAddr +
+ ELF->sectionss[sectionIdx].offsetInSegment);
+
+ *sectionAddr = (t_cm_logical_address)(elfhandle->segments[mapping->id].hostAddr +
+ ELF->sectionss[sectionIdx].offsetInSegment * mapping->memEntSize);
+ }
+
+ return mapping;
+}
+
+static t_uint32 getSymbolAddress(
+ t_memory_handle *memories,
+ t_elfdescription *elfhandle,
+ t_uint32 symbolSectionIdx,
+ t_uint32 symbolOffet) {
+ struct XXElf* ELF = elfhandle->ELF;
+ const t_elfmemory* mapping = ELF->sectionss[symbolSectionIdx].meminfo;
+
+ if(mapping == NULL)
+ return 0xFFFFFFFF;
+ // CM_ASSERT(elfhandle->segments[mapping->id].sumSize != 0);
+ // CM_ASSERT(elfhandle->sections[symbolSectionIdx].offsetInSegment != 0xFFFFFFFF);
+
+ return elfhandle->segments[mapping->id].mpcAddr +
+ ELF->sectionss[symbolSectionIdx].offsetInSegment +
+ symbolOffet;
+}
+
+#if 0
+t_bool ELFXX_getSymbolLocation(
+ const t_mpcal_memory *mpcalmemory,
+ t_elfdescription *elf,
+ char *symbolName,
+ const t_elfmemory **memory,
+ t_uint32 *offset) {
+ const ElfXX_Ehdr *header = (ElfXX_Ehdr*)elf->elfdata;
+ const ElfXX_Shdr *sections = (ElfXX_Shdr*)&elf->elfdata[swapXword(header->e_shoff)];
+ const char *strings = &elf->elfdata[swapXword(sections[swapHalf(header->e_shstrndx)].sh_offset)];
+ int len = cm_StringLength(symbolName, 256); // TO BE FIXED
+ int i;
+
+ for(i = 0; i < ELF->e_shnum; i++)
+ {
+ ElfXX_Sym* symtab;
+ const char* strtab;
+ unsigned int size, j;
+
+ if(ELF->sectionss[i].sh_type != SHT_SYMTAB && ELF->sectionss[i].sh_type != SHT_DYNSYM) continue;
+
+ // Section is a symbol table
+ symtab = (ElfXX_Sym*)&elf->elfdata[swapXword(sections[i].sh_offset)];
+ strtab = &elf->elfdata[swapXword(sections[swapWord(sections[i].sh_link)].sh_offset)];
+ size = ELF->sectionss[i].sh_size / (unsigned int)swapXword(sections[i].sh_entsize);
+
+ for(j = 0; j < size; j++) {
+ const char* foundName = &strtab[swapWord(symtab[j].st_name)];
+
+ if(cm_StringCompare(symbolName, foundName, len) == 0) {
+ if(swapHalf(symtab[j].st_shndx) != SHN_UNDEF) {
+ int sectionIdx = (int)swapHalf(symtab[j].st_shndx);
+ ElfXX_Xword sh_flags = swapXword(sections[sectionIdx].sh_flags);
+
+ *memory = mpcalmemory->getMappingByName(&strings[swapWord(sections[sectionIdx].sh_name)],
+ sh_flags & SHF_WRITE ? MEM_RW : (sh_flags & SHF_EXECINSTR ? MEM_X : MEM_RO));
+ *offset = (t_uint32)swapXword(symtab[j].st_value);
+
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+t_cm_error ELF64_relocateSegments(
+ t_memory_handle *memories,
+ t_elfdescription *elfhandle,
+ t_memory_property property,
+ void *cbContext) {
+ struct XXElf* ELF = elfhandle->ELF;
+ int sec, n;
+
+ // Loop on all relocation section
+ for(sec=0; sec < ELF->e_shnum; sec++)
+ {
+ t_cm_logical_address sectionAddr = 0;
+ t_uint32 sectionOffset = 0;
+ const t_elfmemory* mapping;
+
+ if(ELF->sectionss[sec].relocations == NULL)
+ continue;
+
+ // Relocate only section in memory
+ mapping = getSectionAddress(memories,
+ elfhandle,
+ sec,
+ &sectionOffset,
+ &sectionAddr);
+ if(mapping == NULL)
+ continue;
+
+ if(
+ (mapping->property == property && elfhandle->instanceProperty != MEM_FOR_SINGLETON) ||
+ (property == MEM_SHARABLE && elfhandle->instanceProperty == MEM_FOR_SINGLETON) )
+ {
+ LOG_INTERNAL(2, "relocSection(%s)\n", ELF->sectionss[sec].sectionName, 0, 0, 0, 0, 0);
+
+ for(n = 0; n < ELF->sectionss[sec].relocationNumber; n++)
+ {
+ struct XXrelocation* relocation = &ELF->sectionss[sec].relocations[n];
+ t_uint32 symbol_addr;
+ char* relocAddr = (char*)(sectionAddr + relocation->OffsetInElf * mapping->memEntSize);
+
+ switch(relocation->st_shndx) {
+ case SHN_ABS: // Absolute external reference
+ symbol_addr = relocation->st_value;
+ break;
+ case SHN_UNDEF: // External reference
+ // LOG_INTERNAL(0, "cm_resolvSymbol(%d, %s)\n", relocation->type, relocation->symbol_name, 0,0, 0, 0);
+ symbol_addr = cm_resolvSymbol(cbContext,
+ relocation->type,
+ relocation->symbol_name,
+ relocAddr);
+ if(symbol_addr == 0x0) { // Not defined symbol
+ ERROR("Symbol %s not found\n", relocation->symbol_name, 0, 0, 0, 0, 0);
+ return CM_INVALID_ELF_FILE;
+ } else if(symbol_addr == 0xFFFFFFFE) { // OOM
+ return CM_NO_MORE_MEMORY;
+ } else if(symbol_addr == 0xFFFFFFFF) { // Defined inside static binding
+ continue;
+ }
+ break;
+ default: // Internal reference in loaded section
+ symbol_addr = getSymbolAddress(
+ memories,
+ elfhandle,
+ (t_uint32)relocation->st_shndx,
+ relocation->st_value);
+ if(symbol_addr == 0xFFFFFFFF) {
+ ERROR("Symbol in section %s+%d not loaded\n",
+ ELF->sectionss[relocation->st_shndx].sectionName,
+ relocation->st_value, 0, 0, 0, 0);
+ return CM_INVALID_ELF_FILE;
+ }
+ break;
+ }
+
+ symbol_addr += relocation->r_addend;
+
+ MMDSP_performRelocation(
+ relocation->type,
+ relocation->symbol_name,
+ symbol_addr,
+ relocAddr,
+ ELF->sectionss[sec].data + relocation->OffsetInElf * mapping->memEntSize,
+ sectionOffset + relocation->OffsetInElf);
+ }
+ }
+ }
+
+ return CM_OK;
+}
+
+t_cm_error ELF64_getRelocationMemory(
+ t_elfdescription *elfhandle,
+ t_tmp_elfdescription *elftmp,
+ t_uint32 offsetInNmf,
+ t_memory_reference *memory) {
+ struct XXElf* ELF = elfhandle->ELF;
+ const ElfXX_Rela* rel_start;
+ const ElfXX_Sym* relaNmfSegmentSymbols = (ElfXX_Sym*)elftmp->relaNmfSegmentSymbols;
+
+ for(rel_start = (ElfXX_Rela*)elftmp->relaNmfSegment; rel_start < (ElfXX_Rela*)elftmp->relaNmfSegmentEnd; rel_start++)
+ {
+ if((t_uint32)swapXword(rel_start->r_offset) == offsetInNmf)
+ {
+ int strtab_index = ELFXX_R_SYM(swapXword(rel_start->r_info));
+ int sectionIdx = (int)swapHalf(relaNmfSegmentSymbols[strtab_index].st_shndx);
+
+ memory->memory = ELF->sectionss[sectionIdx].meminfo;
+
+ if(memory->memory != NULL) {
+ memory->offset = (
+ ELF->sectionss[sectionIdx].offsetInSegment + // Offset in Segment
+ (t_uint32)swapXword(relaNmfSegmentSymbols[strtab_index].st_value) + // Offset in Elf Section
+ (t_uint32)swapXword(rel_start->r_addend)); // Addend
+
+ return CM_OK;
+ } else {
+ const char* symbol_name = &elftmp->relaNmfSegmentStrings[swapWord(relaNmfSegmentSymbols[strtab_index].st_name)];
+ ERROR("Symbol %s not found\n", symbol_name, 0, 0, 0, 0, 0);
+ return CM_INVALID_ELF_FILE;
+ }
+ }
+ }
+
+ ERROR("Unknown relocation error\n", 0, 0, 0, 0, 0, 0);
+ return CM_INVALID_ELF_FILE;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/mmdsp-debug.c b/drivers/staging/nmf-cm/cm/engine/elf/src/mmdsp-debug.c
new file mode 100644
index 00000000000..0853e2d25b5
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/src/mmdsp-debug.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ *
+ */
+#include <cm/engine/elf/inc/mmdsp-loadmap.h>
+#include <cm/engine/elf/inc/mmdsp.h>
+#include <cm/engine/dsp/inc/semaphores_dsp.h>
+#include <cm/engine/dsp/mmdsp/inc/mmdsp_hwp.h>
+
+#include <cm/engine/power_mgt/inc/power.h>
+
+#include <cm/engine/utils/inc/string.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/memory/inc/domain.h>
+#include <cm/engine/component/inc/component_type.h>
+#include <inc/nmf-limits.h>
+
+#define LOADMAP_SEMAPHORE_USE_NB 7
+
+static t_memory_handle headerHandle[NB_CORE_IDS] = {INVALID_MEMORY_HANDLE, };
+static struct LoadMapHdr *headerAddresses[NB_CORE_IDS] = {0, };
+static t_uint32 headerOffsets[NB_CORE_IDS] = {0, };
+
+t_cm_error cm_DSPABI_AddLoadMap(
+ t_cm_domain_id domainId,
+ const char* templateName,
+ const char* localname,
+ t_memory_handle *memories,
+ void *componentHandle)
+{
+ t_nmf_core_id coreId = cm_DM_GetDomainCoreId(domainId);
+
+ if (headerHandle[coreId] == 0) /* Create loadmap header */
+ {
+ headerHandle[coreId] = cm_DM_Alloc(domainId, SDRAM_EXT16,
+ sizeof(struct LoadMapHdr)/2, CM_MM_ALIGN_2WORDS, TRUE);
+ if (headerHandle[coreId] == INVALID_MEMORY_HANDLE)
+ return CM_NO_MORE_MEMORY;
+
+ headerAddresses[coreId] = (struct LoadMapHdr*)cm_DSP_GetHostLogicalAddress(headerHandle[coreId]);
+
+ headerAddresses[coreId]->nMagicNumber = LOADMAP_MAGIC_NUMBER;
+ headerAddresses[coreId]->nVersion = (LOADMAP_VERSION_MSB<<8)|(LOADMAP_VERSION_LSB);
+ headerAddresses[coreId]->nRevision = 0;
+ headerAddresses[coreId]->pFirstItem = 0;
+
+ //Register Header into XRAM:2
+ cm_DSP_GetDspAddress(headerHandle[coreId], &headerOffsets[coreId]);
+ cm_DSP_WriteXRamWord(coreId, 2, headerOffsets[coreId]);
+ }
+
+ // update Header nRevision field
+ headerAddresses[coreId]->nRevision++;
+
+ /*
+ * Build loadmap entry
+ */
+ {
+ t_memory_handle handle;
+ struct LoadMapItem* pItem;
+ t_uint32 dspentry;
+ unsigned char* pos;
+ t_uint32 fnlen, lnlen;
+ t_uint32 fnlenaligned, lnlenaligned;
+ t_uint32 address;
+ t_uint32 postStringLength;
+ int i;
+
+ postStringLength = cm_StringLength(".elf", 16);
+ fnlenaligned = fnlen = cm_StringLength(templateName, MAX_COMPONENT_FILE_PATH_LENGTH) + postStringLength + 2;
+ if((fnlenaligned % 2) != 0) fnlenaligned++;
+ lnlenaligned = lnlen = cm_StringLength(localname, MAX_TEMPLATE_NAME_LENGTH);
+ if((lnlenaligned % 2) != 0) lnlenaligned++;
+
+ // Allocate new loap map
+ handle = cm_DM_Alloc(domainId, SDRAM_EXT16,
+ sizeof(struct LoadMapItem)/2 + (1 + fnlenaligned/2) + (1 + lnlenaligned/2),
+ CM_MM_ALIGN_2WORDS, TRUE);
+ if (handle == INVALID_MEMORY_HANDLE)
+ return CM_NO_MORE_MEMORY;
+
+ pItem = (struct LoadMapItem*)cm_DSP_GetHostLogicalAddress(handle);
+ cm_DSP_GetDspAddress(handle, &dspentry);
+
+ // Link this new loadmap with the previous one
+ if(headerAddresses[coreId]->pFirstItem == NULL)
+ headerAddresses[coreId]->pFirstItem = (struct LoadMapItem *)dspentry;
+ else
+ {
+ struct LoadMapItem* curItem;
+
+ curItem = headerAddresses[coreId]->pFirstItem;
+ curItem = (struct LoadMapItem*)(((t_uint32)curItem - headerOffsets[coreId]) * 2 + (t_uint32)headerAddresses[coreId]); // To ARM address
+ while(curItem->pNextItem != NULL)
+ {
+ curItem = curItem->pNextItem;
+ curItem = (struct LoadMapItem*)(((t_uint32)curItem - headerOffsets[coreId]) * 2 + (t_uint32)headerAddresses[coreId]); // To ARM address
+ }
+ curItem->pNextItem = (struct LoadMapItem *)dspentry;
+ }
+
+ // DSP Address of the string at the end of the load map
+ pos = (unsigned char*)pItem + sizeof(struct LoadMapItem);
+
+ /*
+ * Set SolibFilename address information
+ * -> string = "./origfilename"
+ */
+ pItem->pSolibFilename = (char*)(dspentry + sizeof(struct LoadMapItem) / 2);
+ *(t_uint16*)pos = fnlen;
+ pos += 2;
+ *pos++ = '.';
+ *pos++ = '\\';
+ for(i = 0; i < fnlen - 2 - postStringLength; i++)
+ {
+ *pos++ = (templateName[i] == '.') ? '\\' : templateName[i];
+ }
+ *pos++ = '.';
+ *pos++ = 'e';
+ *pos++ = 'l';
+ *pos++ = 'f';
+ // add padding if needed
+ if ((t_uint32)pos & 1)
+ *pos++ = '\0';
+
+ /*
+ * Set Component Name address information
+ */
+ if (lnlen != 0)
+ {
+ pItem->pComponentName = (char*)(dspentry + sizeof(struct LoadMapItem) / 2 + 1 + fnlenaligned / 2);
+
+ *(t_uint16*)pos = lnlen;
+ pos += 2;
+ for(i = 0; i < lnlenaligned; i++)
+ {
+ // If not aligned null ending copied
+ *pos++ = localname[i];
+ }
+ }
+ else
+ {
+ pItem->pComponentName = 0;
+ }
+
+ /*
+ * Set PROG information
+ */
+ if(memories[CODE_MEMORY_INDEX] == INVALID_MEMORY_HANDLE)
+ address = 0;
+ else
+ cm_DSP_GetDspAddress(memories[CODE_MEMORY_INDEX], &address);
+ pItem->pAddrProg = (void*)address;
+
+ /*
+ * Set ERAMCODE information
+ */
+ if(memories[ECODE_MEMORY_INDEX] == INVALID_MEMORY_HANDLE)
+ address = 0;
+ else
+ cm_DSP_GetDspAddress(memories[ECODE_MEMORY_INDEX], &address);
+ pItem->pAddrEmbProg = (void*)address;
+
+ /*
+ * Set THIS information
+ */
+ if(memories[PRIVATE_DATA_MEMORY_INDEX] != INVALID_MEMORY_HANDLE) {
+ // Standard component
+ cm_DSP_GetDspAddress(memories[PRIVATE_DATA_MEMORY_INDEX], &address);
+ } else if(memories[SHARE_DATA_MEMORY_INDEX] != INVALID_MEMORY_HANDLE) {
+ // Singleton component where data are shared (simulate THIS with shared memory)
+ cm_DSP_GetDspAddress(memories[SHARE_DATA_MEMORY_INDEX], &address);
+ } else {
+ // Component without data (take unique identifier -> arbitrary take host component handle)
+ address = (t_uint32)componentHandle;
+ }
+ pItem->pThis = (void*)address;
+
+ /*
+ * Set ARM THIS information
+ */
+ pItem->pARMThis = componentHandle;
+
+ /*
+ * Set Link to null (end of list)
+ */
+ pItem->pNextItem = 0;
+
+ /*
+ * Set XROM information
+ */
+ if(memories[XROM_MEMORY_INDEX] == INVALID_MEMORY_HANDLE)
+ address = 0;
+ else
+ cm_DSP_GetDspAddress(memories[XROM_MEMORY_INDEX], &address);
+ pItem->pXROM = (void*)address;
+
+ /*
+ * Set YROM information
+ */
+ if(memories[YROM_MEMORY_INDEX] == INVALID_MEMORY_HANDLE)
+ address = 0;
+ else
+ cm_DSP_GetDspAddress(memories[YROM_MEMORY_INDEX], &address);
+ pItem->pYROM = (void*)address;
+
+ /*
+ * Set memory handle (not used externally)
+ */
+ pItem->memHandle = (void*)handle;
+ }
+
+ OSAL_mb();
+
+ return CM_OK;
+}
+
+t_cm_error cm_DSPABI_RemoveLoadMap(
+ t_cm_domain_id domainId,
+ const char* templateName,
+ t_memory_handle *memories,
+ const char* localname,
+ void *componentHandle)
+{
+ struct LoadMapItem* curItem, **prevItemReference;
+ t_nmf_core_id coreId = cm_DM_GetDomainCoreId(domainId);
+
+ CM_ASSERT (headerHandle[coreId] != INVALID_MEMORY_HANDLE);
+
+ /* parse list until we find this */
+ prevItemReference = &headerAddresses[coreId]->pFirstItem;
+ curItem = *prevItemReference;
+ while(curItem != NULL)
+ {
+ curItem = (struct LoadMapItem*)(((t_uint32)curItem - headerOffsets[coreId]) * 2 + (t_uint32)headerAddresses[coreId]); // To ARM address
+
+ if(curItem->pARMThis == componentHandle)
+ break;
+
+ prevItemReference = &curItem->pNextItem;
+ curItem = *prevItemReference;
+ }
+
+
+ // Remove component from loadmap if founded
+ if(curItem != NULL)
+ {
+ /* take local semaphore */
+ cm_DSP_SEM_Take(coreId,LOADMAP_SEMAPHORE_USE_NB);
+
+ /* remove element from list */
+ *prevItemReference = curItem->pNextItem;
+
+ /* update nRevision field in header */
+ headerAddresses[coreId]->nRevision++;
+
+ /* If this is the last item, deallocate !!! */
+ if(headerAddresses[coreId]->pFirstItem == NULL)
+ {
+ // Deallocate memory
+ cm_DM_Free(headerHandle[coreId], TRUE);
+ headerHandle[coreId] = INVALID_MEMORY_HANDLE;
+
+ //Register Header into XRAM:2
+ cm_DSP_WriteXRamWord(coreId, 2, 0);
+ }
+
+ /* deallocate memory */
+ cm_DM_Free((t_memory_handle)curItem->memHandle, TRUE);
+
+ /* be sure memory is updated before releasing local semaphore */
+ OSAL_mb();
+
+ /* release local semaphore */
+ cm_DSP_SEM_Give(coreId,LOADMAP_SEMAPHORE_USE_NB);
+ }
+ else
+ {
+ ERROR("Component not in LoadMap %s, memory corruption????\n", localname, 0, 0, 0, 0, 0);
+ }
+
+ return CM_OK;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/elf/src/mpcal.c b/drivers/staging/nmf-cm/cm/engine/elf/src/mpcal.c
new file mode 100644
index 00000000000..93d910a5ed6
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/elf/src/mpcal.c
@@ -0,0 +1,6 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/elf/inc/mpcal.h>
diff --git a/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h b/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h
new file mode 100644
index 00000000000..3f995982088
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef __INC_EE_MGT_H
+#define __INC_EE_MGT_H
+
+#include <cm/engine/component/inc/instance.h>
+#include <cm/engine/dsp/inc/dsp.h>
+
+typedef struct {
+ t_component_instance *instance;
+ t_nmf_executive_engine_id executiveEngineId;
+ t_uint32 currentStackSize[NMF_SCHED_URGENT + 1];
+ t_uint32 voidAddr;
+ t_uint32 traceState;
+ t_uint32 printLevel;
+ t_uint32 nbOfForceWakeup;
+} t_ee_state;
+
+//TODO, juraj, this should be done more properly, like accessor method, instead making this global variable..
+extern t_ee_state eeState[NB_CORE_IDS];
+
+/******************************************************************************/
+/************************ FUNCTIONS PROTOTYPES ********************************/
+/******************************************************************************/
+
+PUBLIC t_cm_error cm_EEM_Init(t_nmf_core_id coreId, const char *eeName, t_nmf_executive_engine_id executiveEngineId);
+PUBLIC void cm_EEM_Close(t_nmf_core_id coreId);
+PUBLIC t_uint32 cm_EEM_isStackUpdateNeed(t_nmf_core_id coreId, t_nmf_ee_priority priority, t_uint32 isInstantiate, t_uint32 needMinStackSize);
+PUBLIC t_cm_error cm_EEM_UpdateStack(t_nmf_core_id coreId, t_nmf_ee_priority priority, t_uint32 needMinStackSize, t_uint32 *pNewStackValue);
+PUBLIC t_ee_state* cm_EEM_getExecutiveEngine(t_nmf_core_id coreId);
+PUBLIC void cm_EEM_setTraceMode(t_nmf_core_id coreId, t_uint32 state);
+PUBLIC void cm_EEM_setPrintLevel(t_nmf_core_id coreId, t_uint32 level);
+t_cm_error cm_EEM_ForceWakeup(t_nmf_core_id coreId);
+void cm_EEM_AllowSleep(t_nmf_core_id coreId);
+
+#endif /* __INC_EE_MGT_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/src/executive_engine_mgt.c b/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/src/executive_engine_mgt.c
new file mode 100644
index 00000000000..14889ec6e4d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/executive_engine_mgt/src/executive_engine_mgt.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*----------------------------------------------------------------------------*
+ * This module provides functions that allow to manage DSPs' Firmwares. *
+ ******************************************************************************/
+
+
+/******************************************************************* Includes
+ ****************************************************************************/
+
+#include "../inc/executive_engine_mgt.h"
+#include <cm/engine/dsp/inc/dsp.h>
+#include <cm/engine/component/inc/introspection.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/memory/inc/domain.h>
+#include <cm/engine/utils/inc/convert.h>
+#include <cm/engine/component/inc/initializer.h>
+#include <cm/engine/power_mgt/inc/power.h>
+#include <cm/engine/perfmeter/inc/mpcload.h>
+
+#include <share/communication/inc/nmf_service.h>
+
+t_ee_state eeState[NB_CORE_IDS];
+
+/****************************************************************** Functions
+ ****************************************************************************/
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetExecutiveEngineHandle(
+ t_cm_domain_id domainId,
+ t_cm_instance_handle *executiveEngineHandle)
+{
+ t_nmf_core_id coreId;
+
+ if (cm_DM_CheckDomain(domainId, DOMAIN_NORMAL) != CM_OK) {
+ return CM_INVALID_DOMAIN_HANDLE;
+ }
+
+ coreId = cm_DM_GetDomainCoreId(domainId);
+ //in case someone ask for ee on component manager !!!!
+ if (coreId == ARM_CORE_ID) {*executiveEngineHandle = 0;}
+ else {*executiveEngineHandle = eeState[coreId].instance->instance;}
+
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_EEM_Init(
+ t_nmf_core_id coreId,
+ const char *eeName,
+ t_nmf_executive_engine_id executiveEngineId)
+{
+ t_rep_component *pRepComponent;
+ t_cm_error error;
+ t_uint32 i;
+
+ eeState[coreId].instance = (t_component_instance *)0;
+ eeState[coreId].executiveEngineId = executiveEngineId;
+ for(i = NMF_SCHED_BACKGROUND; i < NMF_SCHED_URGENT + 1;i++)
+ {
+ eeState[coreId].currentStackSize[i] = MIN_STACK_SIZE;
+ }
+
+ // Try to load component file
+ if((error = cm_REP_lookupComponent(eeName, &pRepComponent)) != CM_OK)
+ {
+ if (error == CM_COMPONENT_NOT_FOUND)
+ ERROR("CM_COMPONENT_NOT_FOUND: Execution Engine %s\n", eeName, 0, 0, 0, 0, 0);
+ return error;
+ }
+
+ // Set to 1 during bootstrap since MMDSP forceWakeup is to one also in order to not go in idle state
+ // while configuration not finish !!!
+ eeState[coreId].nbOfForceWakeup = 1;
+
+ if ((error = cm_DSP_Boot(coreId)) != CM_OK)
+ return error;
+
+ if((error = cm_instantiateComponent(
+ eeName,
+ cm_DSP_GetState(coreId)->domainEE,
+ NMF_SCHED_URGENT,
+ eeName,
+ pRepComponent->elfhandle,
+ &eeState[coreId].instance)) != CM_OK)
+ {
+ cm_DSP_Shutdown(coreId);
+ return error;
+ }
+
+ /* Get Void Function */
+ eeState[coreId].voidAddr = cm_getFunction(eeState[coreId].instance, "helper", "Void");
+
+ /* allocate xram space for stack */
+ if (executiveEngineId == SYNCHRONOUS_EXECUTIVE_ENGINE)
+ {
+ error = cm_DSP_setStackSize(coreId, MIN_STACK_SIZE);
+ }
+ else
+ {
+ error = cm_DSP_setStackSize(coreId, (NMF_SCHED_URGENT + 1) * MIN_STACK_SIZE);
+ }
+ if (error != CM_OK)
+ {
+ cm_delayedDestroyComponent(eeState[coreId].instance);
+ eeState[coreId].instance = (t_component_instance *)0;
+ cm_DSP_Shutdown(coreId);
+ return error;
+ }
+
+ /* allocate sdram memory to share perfmeters data */
+ error = cm_PFM_allocatePerfmeterDataMemory(coreId, cm_DSP_GetState(coreId)->domainEE);
+ if (error != CM_OK) {
+ cm_delayedDestroyComponent(eeState[coreId].instance);
+ eeState[coreId].instance = (t_component_instance *)0;
+ cm_DSP_Shutdown(coreId);
+ return error;
+ }
+
+ /* set initial stack value */
+ cm_writeAttribute(eeState[coreId].instance, "rtos/scheduler/topOfStack", cm_DSP_getStackAddr(coreId));
+
+ /* set myCoreId for trace */
+ cm_writeAttribute(eeState[coreId].instance, "xti/myCoreId", coreId - 1);
+
+#if defined(__STN_8500) && (__STN_8500 > 10)
+ /* set myCoreId for prcmu if exist */
+ cm_writeAttribute(eeState[coreId].instance, "sleep/prcmu/myCoreId", coreId + 1);
+#endif
+
+ /* go go go ... */
+ cm_DSP_Start(coreId);
+
+ /* Waiting for End Of Boot */
+ //TODO : remove infinite while loop
+ //TODO : to be paranoiac, add a read to serviceReasonOffset before starting core and check value is MPC_SERVICE_BOOT as it should be
+ {
+ while(cm_readAttributeNoError(eeState[coreId].instance, "rtos/commonpart/serviceReason") == MPC_SERVICE_BOOT)
+ {
+ volatile t_uint32 i;
+ for (i=0; i < 1000; i++);
+ }
+ }
+
+ /* set some attributes after boot to avoid being erase by mmdsp boot */
+ cm_writeAttribute(eeState[coreId].instance, "xti/traceActive", eeState[coreId].traceState);
+ cm_writeAttribute(eeState[coreId].instance, "rtos/commonpart/printLevel", eeState[coreId].printLevel);
+
+ cm_DSP_ConfigureAfterBoot(coreId);
+
+ return CM_OK;
+}
+
+/****************************************************************************/
+/* NAME: cm_EEM_Close */
+/*--------------------------------------------------------------------------*/
+/* DESCRIPTION: Inform us that ee for coreId has been destroyed */
+/* */
+/* PARAMETERS: id: dsp identifier */
+/* */
+/* RETURN: none */
+/* */
+/****************************************************************************/
+PUBLIC void cm_EEM_Close(t_nmf_core_id coreId)
+{
+ cm_DSP_setStackSize(coreId, 0);
+ cm_delayedDestroyComponent(eeState[coreId].instance);
+ eeState[coreId].instance = (t_component_instance *)0;
+ cm_PFM_deallocatePerfmeterDataMemory(coreId);
+
+ cm_DSP_Shutdown(coreId);
+}
+
+/****************************************************************************/
+/* NAME: cm_EEM_isStackUpdateNeed( */
+/* t_nmf_core_id id, */
+/* t_nmf_ee_priority priority, */
+/* t_uint32 isInstantiate, */
+/* t_uint32 needMinStackSize */
+/* ) */
+/*--------------------------------------------------------------------------*/
+/* DESCRIPTION: Return a boolean to inform if a ee stack size update is need*/
+/* when instantiate or destroying a component */
+/****************************************************************************/
+PUBLIC t_uint32 cm_EEM_isStackUpdateNeed(
+ t_nmf_core_id coreId,
+ t_nmf_ee_priority priority,
+ t_uint32 isInstantiate,
+ t_uint32 needMinStackSize)
+{
+ /* in case of SYNCHRONOUS_EXECUTIVE_ENGINE we only use currentStackSize[NMF_SCHED_BACKGROUND] */
+ if (eeState[coreId].executiveEngineId == SYNCHRONOUS_EXECUTIVE_ENGINE) {priority = NMF_SCHED_BACKGROUND;}
+ if (isInstantiate)
+ {
+ if (needMinStackSize > eeState[coreId].currentStackSize[priority]) {return TRUE;}
+ }
+ else
+ {
+ if (needMinStackSize == eeState[coreId].currentStackSize[priority]) {return TRUE;}
+ }
+
+ return FALSE;
+}
+
+/****************************************************************************/
+/* NAME: cm_EEM_UpdateStack( */
+/* t_nmf_core_id id, */
+/* t_nmf_ee_priority priority, */
+/* t_uint32 needMinStackSize, */
+/* t_uint32 *pNewStackValue */
+/* ) */
+/*--------------------------------------------------------------------------*/
+/* DESCRIPTION: If cm_EEM_isStackUpdateNeed() has return true then caller */
+/* must inform EEM about new stack value for priority. */
+/* cm_EEM_UpdateStack() will return new global stack size to */
+/* provide to ee. */
+/****************************************************************************/
+PUBLIC t_cm_error cm_EEM_UpdateStack(
+ t_nmf_core_id coreId,
+ t_nmf_ee_priority priority,
+ t_uint32 needMinStackSize,
+ t_uint32 *pNewStackValue)
+{
+ t_cm_error error;
+ t_uint32 recoveryStackSize = eeState[coreId].currentStackSize[priority];
+ t_uint32 i;
+
+ /* in case of SYNCHRONOUS_EXECUTIVE_ENGINE we only use currentStackSize[NMF_SCHED_BACKGROUND] */
+ if (eeState[coreId].executiveEngineId == SYNCHRONOUS_EXECUTIVE_ENGINE) {priority = NMF_SCHED_BACKGROUND;}
+ eeState[coreId].currentStackSize[priority] = needMinStackSize;
+ if (eeState[coreId].executiveEngineId == SYNCHRONOUS_EXECUTIVE_ENGINE) {*pNewStackValue = needMinStackSize;}
+ else
+ {
+ *pNewStackValue = 0;
+ for(i = NMF_SCHED_BACKGROUND; i < NMF_SCHED_URGENT + 1;i++)
+ {
+ *pNewStackValue += eeState[coreId].currentStackSize[i];
+ }
+ }
+
+ /* try to increase size of stack by modifying xram allocator size */
+ error = cm_DSP_setStackSize(coreId, *pNewStackValue);
+ if (error != CM_OK) {
+ eeState[coreId].currentStackSize[priority] = recoveryStackSize;
+ } else {
+ LOG_INTERNAL(1, "\n##### Stack update: size=%d, prio=%d on %s #####\n", *pNewStackValue, priority, cm_getDspName(coreId), 0, 0, 0);
+ }
+
+ return error;
+}
+
+/****************************************************************************/
+/* NAME: t_nmf_executive_engine_id( */
+/* t_nmf_core_id id */
+/* ) */
+/*--------------------------------------------------------------------------*/
+/* DESCRIPTION: return executive engine load on id core. */
+/****************************************************************************/
+PUBLIC t_ee_state * cm_EEM_getExecutiveEngine(t_nmf_core_id coreId)
+{
+ return &eeState[coreId];
+}
+
+/****************************************************************************/
+/* NAME: cm_EEM_setTraceMode( */
+/* t_nmf_core_id id, */
+/* t_uint32 state */
+/* ) */
+/*--------------------------------------------------------------------------*/
+/* DESCRIPTION: activate/deactivate trace for ee running on id. In case ee */
+/* is not yet load then information is store. */
+/****************************************************************************/
+PUBLIC void cm_EEM_setTraceMode(t_nmf_core_id coreId, t_uint32 state)
+{
+ eeState[coreId].traceState = state;
+ if (eeState[coreId].instance)
+ {
+ if(cm_EEM_ForceWakeup(coreId) == CM_OK)
+ {
+ cm_writeAttribute(eeState[coreId].instance, "xti/traceActive", eeState[coreId].traceState);
+
+ cm_EEM_AllowSleep(coreId);
+ }
+ }
+}
+
+/****************************************************************************/
+/* NAME: cm_EEM_setPrintLevel( */
+/* t_nmf_core_id id, */
+/* t_uint32 level */
+/* ) */
+/*--------------------------------------------------------------------------*/
+/* DESCRIPTION: set print level for ee running on id. In case ee */
+/* is not yet load then information is store. */
+/****************************************************************************/
+PUBLIC void cm_EEM_setPrintLevel(t_nmf_core_id coreId, t_uint32 level)
+{
+ eeState[coreId].printLevel = level;
+ if (eeState[coreId].instance)
+ {
+ if(cm_EEM_ForceWakeup(coreId) == CM_OK)
+ {
+ cm_writeAttribute(eeState[coreId].instance, "rtos/commonpart/printLevel", eeState[coreId].printLevel);
+
+ cm_EEM_AllowSleep(coreId);
+ }
+ }
+}
+
+t_cm_error cm_EEM_ForceWakeup(t_nmf_core_id coreId)
+{
+ if(eeState[coreId].nbOfForceWakeup++ == 0)
+ {
+ t_cm_error error;
+
+ LOG_INTERNAL(2, "ARM: Try to wake up\n", 0, 0, 0, 0, 0, 0);
+
+ if (cm_DSP_GetState(coreId)->state != MPC_STATE_BOOTED)
+ {
+ return CM_MPC_NOT_RESPONDING;
+ }
+ else if ((error = cm_COMP_ULPForceWakeup(coreId)) != CM_OK)
+ {
+ if (error == CM_MPC_NOT_RESPONDING) {
+ ERROR("CM_MPC_NOT_RESPONDING: DSP %s can't be wakeup'ed\n", cm_getDspName(coreId), 0, 0, 0, 0, 0);
+ cm_DSP_SetStatePanic(coreId);
+ }
+ return error;
+ }
+ }
+ return CM_OK;
+}
+
+void cm_EEM_AllowSleep(t_nmf_core_id coreId)
+{
+ if(--eeState[coreId].nbOfForceWakeup == 0)
+ {
+ LOG_INTERNAL(2, "ARM: Allow sleep\n", 0, 0, 0, 0, 0, 0);
+
+ if (cm_DSP_GetState(coreId)->state != MPC_STATE_BOOTED)
+ {
+ }
+ else if (cm_COMP_ULPAllowSleep(coreId) != CM_OK)
+ {
+ ERROR("CM_MPC_NOT_RESPONDING: DSP %s can't be allow sleep'ed\n", cm_getDspName(coreId), 0, 0, 0, 0, 0);
+ }
+ }
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/chunk_mgr.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/chunk_mgr.h
new file mode 100644
index 00000000000..340301a9259
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/chunk_mgr.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef CHUNK_MGR_H_
+#define CHUNK_MGR_H_
+
+#include <cm/engine/memory/inc/remote_allocator.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+
+t_cm_error allocChunkPool(void);
+t_cm_error fillChunkPool(void);
+void freeChunkPool(void);
+
+/***************************************************************************/
+/*
+ * allocChunk
+ * param current : Pointer on chunck to free
+ *
+ * Add a chunk in the chunck list
+ *
+ */
+/***************************************************************************/
+t_cm_chunk* allocChunk(void);
+
+/***************************************************************************/
+/*
+ * freeChunk
+ * param current : Pointer on chunck to free
+ *
+ * Remove a chunk in the chunck list
+ *
+ */
+/***************************************************************************/
+void freeChunk(t_cm_chunk *chunk);
+
+#endif /*CHUNK_MGR_H_*/
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/domain.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/domain.h
new file mode 100644
index 00000000000..bf75915c259
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/domain.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/***************************************************************************/
+/* file : domain.h
+ * author : NMF team
+ * version : 1.0
+ *
+ * brief : NMF domain definitions
+ */
+/***************************************************************************/
+
+#ifndef DOMAIN_H_
+#define DOMAIN_H_
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/memory/inc/domain_type.h>
+#include <cm/engine/memory/inc/memory.h>
+#include <cm/engine/dsp/inc/dsp.h>
+
+/*!
+ * \brief Domain type.
+ * \internal
+ * \ingroup CM_DOMAIN_API
+ */
+typedef enum {
+ DOMAIN_ANY = 0,
+ DOMAIN_NORMAL,
+ DOMAIN_SCRATCH_PARENT,
+ DOMAIN_SCRATCH_CHILD
+} t_cm_domain_type;
+
+/*!
+ * \brief Domain descriptor. Holds offsets for all memory types present in the system.
+ * \internal
+ * \ingroup CM_DOMAIN_API
+ */
+typedef struct {
+ t_cm_domain_memory domain; // the actual memory ranges
+ t_cm_domain_type type; // domain type
+ t_uint32 refcount; // reference counter for scratch domain dependencies
+ t_nmf_client_id client; // client id for cleaning
+
+ union {
+ struct {
+ t_memory_handle handle; // memory handle of the allocated chunk the covers the esram-data scratch region
+ } parent;
+ struct {
+ t_cm_allocator_desc *alloc; //allocator descriptor for the scratch domain
+ t_cm_domain_id parent_ref; //parent domain reference
+ } child;
+ } scratch;
+} t_cm_domain_desc;
+
+#ifdef DEBUG
+#define DOMAIN_DEBUG(handle) \
+ handle = handle & ~0xc0;
+#else
+#define DOMAIN_DEBUG(handle)
+#endif
+
+/*!
+ * \brief Domain descriptor array.
+ */
+extern t_cm_domain_desc domainDesc[];
+
+typedef struct {
+ t_cm_domain_id parentId;
+ t_cm_domain_id domainId;
+ t_cm_allocator_desc *allocDesc;
+} t_cm_domain_scratch_desc;
+
+extern t_cm_domain_scratch_desc domainScratchDesc[];
+
+typedef struct {
+ t_cm_system_address sdramCode;
+ t_cm_system_address sdramData;
+ t_cm_system_address esramCode;
+ t_cm_system_address esramData;
+} t_cm_domain_info;
+
+/*!
+ * \brief Init of the domain subsystem.
+ */
+PUBLIC t_cm_error cm_DM_Init(void);
+
+/*!
+ * \brief Clean-up of the domain subsystem.
+ */
+PUBLIC void cm_DM_Destroy(void);
+
+/*!
+ * \brief Domain creation.
+ *
+ * Allocates in slot in the domain descriptors array and copies segment infos from the domain
+ * parameter to the descriptor. The resulting handle is returned via @param handle.
+ *
+ * Returns: CM_DOMAIN_INVALID in case of error, otherwise CM_OK.
+ */
+PUBLIC t_cm_error cm_DM_CreateDomain(const t_nmf_client_id client, const t_cm_domain_memory *domain, t_cm_domain_id *handle);
+
+/*!
+ * \brief Scratch (or overlap) domain creation.
+ *
+ * Create a scratch domain, ie domain where allocation may overlap.
+ */
+PUBLIC t_cm_error cm_DM_CreateDomainScratch(const t_nmf_client_id client, const t_cm_domain_id parentId, const t_cm_domain_memory *domain, t_cm_domain_id *handle);
+
+/* !
+ * \brief Retrieve the coreId from a given domain. Utility.
+ */
+PUBLIC t_nmf_core_id cm_DM_GetDomainCoreId(const t_cm_domain_id domainId);
+
+/*!
+ * \brief Destroy all domains belonging to a given client.
+ */
+PUBLIC t_cm_error cm_DM_DestroyDomains(const t_nmf_client_id client);
+
+/*!
+ * \brief Destroy a given domain.
+ */
+PUBLIC t_cm_error cm_DM_DestroyDomain(t_cm_domain_id handle);
+
+/*!
+ * \brief Check if the handle is valid.
+ */
+PUBLIC t_cm_error cm_DM_CheckDomain(t_cm_domain_id handle, t_cm_domain_type type);
+PUBLIC t_cm_error cm_DM_CheckDomainWithClient(t_cm_domain_id handle, t_cm_domain_type type, t_nmf_client_id client);
+
+/*!
+ * \brief Memory allocation in a given domain, for a given memory type (see CM_AllocMpcMemory).
+ */
+PUBLIC t_memory_handle cm_DM_Alloc(t_cm_domain_id domainId, t_dsp_memory_type_id memType, t_uint32 size, t_cm_mpc_memory_alignment memAlignment, t_bool powerOn);
+
+/*!
+ * \brief Memory free using a given domain handle
+ */
+PUBLIC void cm_DM_FreeWithInfo(t_memory_handle memHandle, t_nmf_core_id *coreId, t_dsp_memory_type_id *memType, t_bool powerOff);
+
+/*!
+ * \brief Memory free using a given domain handle
+ */
+PUBLIC void cm_DM_Free(t_memory_handle memHandle, t_bool powerOff);
+
+/*!
+ * \brief Wrapper function for CM_GetMpcMemoryStatus.
+ */
+PUBLIC t_cm_error cm_DM_GetAllocatorStatus(t_cm_domain_id domainId, t_dsp_memory_type_id memType, t_cm_allocator_status *pStatus);
+
+PUBLIC t_cm_error cm_DM_GetDomainAbsAdresses(t_cm_domain_id domainId, t_cm_domain_info *info);
+
+#endif /* DOMAIN_H_ */
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/domain_type.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/domain_type.h
new file mode 100644
index 00000000000..286c5cabebf
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/domain_type.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/***************************************************************************/
+/* file : domain.h
+ * author : NMF team
+ * version : 1.0
+ *
+ * brief : NMF domain definitions
+ */
+/***************************************************************************/
+
+#ifndef DOMAIN_TYPE_H_
+#define DOMAIN_TYPE_H_
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/memory/inc/memory_type.h>
+
+/*!
+ * \brief Domain identifier
+ * \ingroup CM_DOMAIN_API
+ */
+typedef t_uint8 t_cm_domain_id;
+
+/*!
+ * \brief Client identifier
+ * 0 (zero) is considered as an invalid or 'NO' client identifier
+ * \ingroup CM_DOMAIN_API
+ */
+typedef t_uint32 t_nmf_client_id;
+// TO BE REMOVED LATER, I guess, when default domains will be removed (PP)
+#define NMF_CORE_CLIENT (t_nmf_client_id)-1
+
+typedef struct {
+ t_uint32 offset; //!< offset relativ to segment start in memory (in bytes)
+ t_uint32 size; //!< size in bytes of the domain segment
+} t_cm_domain_segment;
+
+/*!
+ * \brief Domain memory description structure
+ * \ingroup CM_DOMAIN_API
+ */
+typedef struct {
+ t_nmf_core_id coreId; //!< MMDSP Core Id for this domain (used for TCM-X and TCM-Y at instantiate)
+ t_cm_domain_segment esramCode; //!< ESRAM code segment
+ t_cm_domain_segment esramData; //!< ESRAM data segment
+ t_cm_domain_segment sdramCode; //!< SDRAM code segment
+ t_cm_domain_segment sdramData; //!< SDRAM data segment
+} t_cm_domain_memory;
+
+#define INIT_DOMAIN_SEGMENT {0, 0}
+#define INIT_DOMAIN {MASK_ALL8, INIT_DOMAIN_SEGMENT, INIT_DOMAIN_SEGMENT, INIT_DOMAIN_SEGMENT, INIT_DOMAIN_SEGMENT}
+
+
+#endif /* DOMAIN_TYPE_H_ */
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/memory.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/memory.h
new file mode 100644
index 00000000000..c6d1fb2dfff
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/memory.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Internal Memory Management API.
+ *
+ * \defgroup MEMORY_INTERNAL Private Memory API.
+ *
+ */
+#ifndef __INC_MEMORY_H
+#define __INC_MEMORY_H
+
+#include <cm/engine/api/control/configuration_engine.h>
+#include <cm/engine/memory/inc/remote_allocator.h>
+
+#endif /* __INC_MEMORY_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/memory_type.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/memory_type.h
new file mode 100644
index 00000000000..91246f1ec0e
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/memory_type.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Public Component Manager Memory API type.
+ *
+ * This file contains the Component Manager API type for manipulating memory.
+ */
+#ifndef __INC_MEMORY_TYPE_H
+#define __INC_MEMORY_TYPE_H
+
+#include <cm/inc/cm_type.h>
+
+/*!
+ * @defgroup t_cm_mpc_memory_type t_cm_mpc_memory_type
+ * \brief Definition of symbols used to reference the various type of Media Processor Core adressable memory
+ * @{
+ * \ingroup MEMORY
+ */
+typedef t_uint8 t_cm_mpc_memory_type; //!< Fake enumeration type
+#define CM_MM_MPC_TCM16_X ((t_cm_mpc_memory_type)0)
+#define CM_MM_MPC_TCM24_X ((t_cm_mpc_memory_type)1)
+#define CM_MM_MPC_ESRAM16 ((t_cm_mpc_memory_type)2)
+#define CM_MM_MPC_ESRAM24 ((t_cm_mpc_memory_type)3)
+#define CM_MM_MPC_SDRAM16 ((t_cm_mpc_memory_type)4)
+#define CM_MM_MPC_SDRAM24 ((t_cm_mpc_memory_type)5)
+#define CM_MM_MPC_TCM16_Y ((t_cm_mpc_memory_type)6)
+#define CM_MM_MPC_TCM24_Y ((t_cm_mpc_memory_type)7)
+#define CM_MM_MPC_TCM16 CM_MM_MPC_TCM16_X
+#define CM_MM_MPC_TCM24 CM_MM_MPC_TCM24_X
+
+/* @} */
+
+/*!
+ * @defgroup t_cm_memory_alignment t_cm_memory_alignment
+ * \brief Definition of symbols used to constraint the alignment of the allocated memory
+ * @{
+ * \ingroup MEMORY
+ */
+typedef t_uint16 t_cm_memory_alignment; //!< Fake enumeration type
+#define CM_MM_ALIGN_NONE ((t_cm_memory_alignment)0x00000000)
+#define CM_MM_ALIGN_BYTE ((t_cm_memory_alignment)CM_MM_ALIGN_NONE)
+#define CM_MM_ALIGN_HALFWORD ((t_cm_memory_alignment)0x00000001)
+#define CM_MM_ALIGN_WORD ((t_cm_memory_alignment)0x00000003)
+#define CM_MM_ALIGN_2WORDS ((t_cm_memory_alignment)0x00000007)
+#define CM_MM_ALIGN_16BYTES ((t_cm_memory_alignment)0x0000000F)
+#define CM_MM_ALIGN_4WORDS ((t_cm_memory_alignment)0x0000000F)
+#define CM_MM_ALIGN_AHB_BURST ((t_cm_memory_alignment)0x0000000F)
+#define CM_MM_ALIGN_32BYTES ((t_cm_memory_alignment)0x0000001F)
+#define CM_MM_ALIGN_8WORDS ((t_cm_memory_alignment)0x0000001F)
+#define CM_MM_ALIGN_64BYTES ((t_cm_memory_alignment)0x0000003F)
+#define CM_MM_ALIGN_16WORDS ((t_cm_memory_alignment)0x0000003F)
+#define CM_MM_ALIGN_128BYTES ((t_cm_memory_alignment)0x0000007F)
+#define CM_MM_ALIGN_32WORDS ((t_cm_memory_alignment)0x0000007F)
+#define CM_MM_ALIGN_256BYTES ((t_cm_memory_alignment)0x000000FF)
+#define CM_MM_ALIGN_64WORDS ((t_cm_memory_alignment)0x000000FF)
+#define CM_MM_ALIGN_512BYTES ((t_cm_memory_alignment)0x000001FF)
+#define CM_MM_ALIGN_128WORDS ((t_cm_memory_alignment)0x000001FF)
+#define CM_MM_ALIGN_1024BYTES ((t_cm_memory_alignment)0x000003FF)
+#define CM_MM_ALIGN_256WORDS ((t_cm_memory_alignment)0x000003FF)
+#define CM_MM_ALIGN_2048BYTES ((t_cm_memory_alignment)0x000007FF)
+#define CM_MM_ALIGN_512WORDS ((t_cm_memory_alignment)0x000007FF)
+#define CM_MM_ALIGN_4096BYTES ((t_cm_memory_alignment)0x00000FFF)
+#define CM_MM_ALIGN_1024WORDS ((t_cm_memory_alignment)0x00000FFF)
+#define CM_MM_ALIGN_65536BYTES ((t_cm_memory_alignment)0x0000FFFF)
+#define CM_MM_ALIGN_16384WORDS ((t_cm_memory_alignment)0x0000FFFF)
+/* @} */
+
+/*!
+ * @defgroup t_cm_mpc_memory_alignment t_cm_mpc_memory_alignment
+ * \brief Definition of symbols used to constraint the alignment of the allocated mpc memory
+ * @{
+ * \ingroup MEMORY
+ */
+typedef t_uint16 t_cm_mpc_memory_alignment; //!< Fake enumeration type
+#define CM_MM_MPC_ALIGN_NONE ((t_cm_mpc_memory_alignment)0x00000000)
+#define CM_MM_MPC_ALIGN_HALFWORD ((t_cm_mpc_memory_alignment)0x00000001)
+#define CM_MM_MPC_ALIGN_WORD ((t_cm_mpc_memory_alignment)0x00000003)
+#define CM_MM_MPC_ALIGN_2WORDS ((t_cm_mpc_memory_alignment)0x00000007)
+#define CM_MM_MPC_ALIGN_4WORDS ((t_cm_mpc_memory_alignment)0x0000000F)
+#define CM_MM_MPC_ALIGN_8WORDS ((t_cm_mpc_memory_alignment)0x0000001F)
+#define CM_MM_MPC_ALIGN_16WORDS ((t_cm_mpc_memory_alignment)0x0000003F)
+#define CM_MM_MPC_ALIGN_32WORDS ((t_cm_mpc_memory_alignment)0x0000007F)
+#define CM_MM_MPC_ALIGN_64WORDS ((t_cm_mpc_memory_alignment)0x000000FF)
+#define CM_MM_MPC_ALIGN_128WORDS ((t_cm_mpc_memory_alignment)0x000001FF)
+#define CM_MM_MPC_ALIGN_256WORDS ((t_cm_mpc_memory_alignment)0x000003FF)
+#define CM_MM_MPC_ALIGN_512WORDS ((t_cm_mpc_memory_alignment)0x000007FF)
+#define CM_MM_MPC_ALIGN_1024WORDS ((t_cm_mpc_memory_alignment)0x00000FFF)
+#define CM_MM_MPC_ALIGN_65536BYTES ((t_cm_mpc_memory_alignment)0x0000FFFF)
+#define CM_MM_MPC_ALIGN_16384WORDS ((t_cm_mpc_memory_alignment)0x0000FFFF)
+/* @} */
+
+/*!
+ * \brief Identifier of a memory handle
+ * \ingroup MEMORY
+ */
+typedef t_uint32 t_cm_memory_handle;
+
+/*!
+ * \brief Description of a memory segment
+ *
+ * <=> allocable addressable space
+ * \ingroup MEMORY
+ */
+typedef struct {
+ t_cm_system_address systemAddr; //!< Logical AND physical segment start address
+ t_uint32 size; //!< segment size (in bytes)
+} t_nmf_memory_segment;
+#define INIT_MEMORY_SEGMENT {{0, 0}, 0}
+
+/*!
+ * \brief Definition of structure used for an allocator status
+ * \ingroup MEMORY
+ */
+typedef struct
+{
+ struct {
+ t_uint32 size; //!< size of the allocator
+ /* Block counters */
+ t_uint16 used_block_number; //!< used block number
+ t_uint16 free_block_number; //!< free block number
+
+ /* Free memory min/max */
+ t_uint32 maximum_free_size; //!< maximum free size
+ t_uint32 minimum_free_size; //!< minimum free size
+
+ /* Accumulation of free and used memory */
+ t_uint32 accumulate_free_memory; //!< accumulate free memory
+ t_uint32 accumulate_used_memory; //!< accumulate used memory
+ } global;
+
+ struct {
+ t_uint32 size; //!< size of the domain
+ t_uint32 maximum_free_size; //!< maximum free size in the given domain
+ t_uint32 minimum_free_size; //!< minimum free size in the given domain
+ t_uint32 accumulate_free_memory; //all free memory of the given domain
+ t_uint32 accumulate_used_memory; //all used memory of the given domain
+ } domain;
+
+ struct {
+ t_uint32 sizes[3];
+ } stack[NB_CORE_IDS];
+
+} t_cm_allocator_status;
+
+#endif /* __INC_MEMORY_TYPE_H */
+
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/migration.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/migration.h
new file mode 100644
index 00000000000..824d25374b3
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/migration.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Migration API.
+ *
+ * \defgroup
+ *
+ */
+#ifndef __INC_MIGRATION_H
+#define __INC_MIGRATION_H
+
+#include <cm/engine/memory/inc/domain_type.h>
+#include <cm/engine/dsp/inc/dsp.h>
+
+typedef enum {
+ STATE_MIGRATED = 1,
+ STATE_NORMAL = 0,
+} t_cm_migration_state;
+
+PUBLIC t_cm_error cm_migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst);
+
+PUBLIC t_cm_error cm_unmigrate(void);
+
+PUBLIC t_uint32 cm_migration_translate(t_dsp_segment_type segmentType, t_uint32 addr);
+
+PUBLIC void cm_migration_check_state(t_nmf_core_id coreId, t_cm_migration_state expected);
+
+#endif /* __INC_MIGRATION_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator.h
new file mode 100644
index 00000000000..e639c94cb46
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator.h
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ *
+ * \note: In this module, we assume that parameters were checked !!
+ */
+#ifndef __REMOTE_ALLOCATOR_H_
+#define __REMOTE_ALLOCATOR_H_
+
+/*
+ * Include
+ */
+#include <cm/inc/cm_type.h>
+#include <cm/engine/memory/inc/memory_type.h>
+
+
+/*
+ * Description of the memory block status
+ */
+typedef enum {
+ MEM_USED = 0, /* Memory block is used */
+ MEM_FREE = 1 /* Memory block is free */
+} t_mem_status;
+
+/*
+ * Chunk structure.
+ */
+struct cm_allocator_desc;
+typedef struct chunk_struct
+{
+ /* Double linked list of chunks */
+ struct chunk_struct *prev;
+ struct chunk_struct *next;
+
+ /* Double linked list of free memory */
+ struct chunk_struct *prev_free_mem;
+ struct chunk_struct *next_free_mem;
+
+ /* Offset of the block memory */
+ t_uint32 offset;
+
+ /* Size of the block memory */
+ t_cm_size size;
+
+ /* Status of the block memory */
+ t_mem_status status;
+
+ /* User data */
+ t_uint16 userData;
+
+ /* Alloc debug info*/
+ t_uint32 domainId;
+
+ /* Alloc desc backlink */
+ struct cm_allocator_desc *alloc;
+} t_cm_chunk;
+
+/*!
+ * \brief Identifier of an internal memory handle
+ * \ingroup MEMORY_INTERNAL
+ */
+typedef t_cm_chunk* t_memory_handle;
+
+#define INVALID_MEMORY_HANDLE ((t_cm_chunk*)NULL)
+
+
+/*
+ * Context structure
+ */
+#define BINS 63
+
+//TODO, juraj, add memType to alloc struct ?
+typedef struct cm_allocator_desc {
+ const char *pAllocName; /* Name of the allocator */
+ t_uint32 size; /* Size of the allocator */
+ t_cm_chunk *chunks; /* Array of chunk */
+ t_cm_chunk *free_mem_chunks[BINS]; /* List of free memory */
+ struct cm_allocator_desc* next; /* List of allocator */
+} t_cm_allocator_desc;
+
+int bin_index(unsigned int sz);
+
+/*
+ * Functions
+ */
+/*!
+ * \brief Create a new allocator for a piece of memory (hw mapped (xram, yram))
+ * Any further allocation into this piece of memory will return an offset inside it.
+ * (a constant offset value can be added to this offset)
+ *
+ * \retval t_cm_allocator_desc* new memory allocator identifier
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC t_cm_allocator_desc* cm_MM_CreateAllocator(
+ t_cm_size size, //!< [in] Size of the addressable space in bytes
+ t_uint32 offset, //!< [in] Constant offset to add to each allocated block base address
+ const char* name //!< [in] Name of the allocator
+ );
+
+/*!
+ * \brief Free a memory allocator descriptor
+ *
+ * \retval t_cm_error
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC t_cm_error cm_MM_DeleteAllocator(
+ t_cm_allocator_desc* alloc //!< [in] Identifier of the memory allocator to be freed
+ );
+
+
+/*!
+ * \brief Resize an allocator to the size value.
+ *
+ * \retval t_cm_error
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC t_cm_error cm_MM_ResizeAllocator(
+ t_cm_allocator_desc* alloc, //!< [in] Identifier of the memory allocator used to allocate the piece of memory
+ t_cm_size size //!< [in] Size of the addressable space in allocDesc granularity
+ );
+
+/*!
+ * \brief Check validity of a user handle
+ */
+t_cm_error cm_MM_getValidMemoryHandle(t_cm_memory_handle handle, t_memory_handle* validHandle);
+
+/*!
+ * \brief Wrapper routine to allocate some memory into a given allocator
+ *
+ * \retval t_memory_handle handle on the new allocated piece of memory
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC t_memory_handle cm_MM_Alloc(
+ t_cm_allocator_desc* alloc, //!< [in] Identifier of the memory allocator
+ t_cm_size size, //!< [in] Size of the addressable space
+ t_cm_memory_alignment memAlignment, //!< [in] Alignment constraint
+ t_uint32 seg_offset, //!< [in] Offset of range where allocating
+ t_uint32 seg_size, //!< [in] Size of range where allocating
+ t_uint32 domainId
+ );
+
+
+/*!
+ * \brief Routine to reallocate memory for a given handle
+ *
+ * Routine to reallocate memory for a given handle. The chunk can be extended or shrinked in both
+ * directions - top and bottom, depending on the offset and size arguments.
+ *
+ * \retval t_memory_handle handle on the reallocated piece of memory
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC t_memory_handle cm_MM_Realloc(
+ t_cm_allocator_desc* alloc,
+ const t_cm_size size,
+ const t_uint32 offset,
+ const t_cm_memory_alignment memAlignment,
+ const t_memory_handle handle);
+/*!
+ * \brief Frees the allocated chunk
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC void cm_MM_Free(
+ t_cm_allocator_desc* alloc, //!< [in] Identifier of the memory allocator
+ t_memory_handle memHandle //!< [in] Memory handle to free
+ );
+
+
+/*!
+ * \brief Get the allocator status
+ *
+ * \param[in] alloc Identifier of the memory allocator
+ * \param[out] pStatus Status of the allocator
+ *
+ * \retval t_cm_error
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC t_cm_error cm_MM_GetAllocatorStatus(t_cm_allocator_desc* alloc, t_uint32 offset, t_uint32 size, t_cm_allocator_status *pStatus);
+
+/*!
+ * \brief Returns the offset into a given memory allocator of an allocated piece of memory
+ *
+ * \param[in] memHandle handle on the given memory
+ *
+ * \retval t_uint32 offset into the given memory allocator
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC t_uint32 cm_MM_GetOffset(t_memory_handle memHandle);
+
+
+/*!
+ * \brief Returns the size in word size for a given memory allocator of an allocated piece of memory
+ *
+ * \param[in] memHandle handle on the given memory
+ *
+ * \retval t_uint32 size in wordsize for the given memory allocator
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC t_uint32 cm_MM_GetSize(t_memory_handle memHandle);
+
+/*!
+ * \brief Returns the size in bytes for a given memory allocator
+ *
+ * \param[in] allocDesc Identifier of the memory allocator
+ * \retval size
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC t_uint32 cm_MM_GetAllocatorSize(t_cm_allocator_desc* allocDesc);
+
+
+/*!
+ * \brief Set the user data of an allocated piece of memory
+ *
+ * \param[in] memHandle handle on the given memory
+ * \param[in] userData UsedData of the given memory piece
+ *
+ * \retval t_cm_error
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC void cm_MM_SetMemoryHandleUserData (t_memory_handle memHandle, t_uint16 userData);
+
+
+/*!
+ * \brief Return the user data of an allocated piece of memory
+ *
+ * \param[in] memHandle handle on the given memory
+ * \param[out] pUserData returned UsedData of the given memory piece
+ *
+ * \retval t_cm_error
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC void cm_MM_GetMemoryHandleUserData(t_memory_handle memHandle, t_uint16 *pUserData, t_cm_allocator_desc **alloc);
+
+/*!
+ * \brief Dump chunkd in the range of [start:end]
+ *
+ * \param[in] alloc Allocator descriptor
+ * \param[in] start Range start
+ * \param[in] end Range end
+ *
+ * \retval void
+ *
+ * \ingroup MEMORY_INTERNAL
+ */
+PUBLIC void cm_MM_DumpMemory(t_cm_allocator_desc* alloc, t_uint32 start, t_uint32 end);
+
+#endif /* _REMOTE_ALLOCATOR_H_*/
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator_utils.h b/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator_utils.h
new file mode 100644
index 00000000000..ce99e4d7a94
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/inc/remote_allocator_utils.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef REMOTE_ALLOCATOR_UTILS_H_
+#define REMOTE_ALLOCATOR_UTILS_H_
+
+#include <cm/engine/memory/inc/remote_allocator.h>
+#include <cm/engine/memory/inc/chunk_mgr.h>
+
+typedef enum {
+ FREE_CHUNK_BEFORE,
+ FREE_CHUNK_AFTER,
+} t_mem_split_position;
+
+
+PUBLIC void updateFreeList(t_cm_allocator_desc* alloc, t_cm_chunk* chunk);
+
+PUBLIC void linkChunk(t_cm_chunk* prev,t_cm_chunk* add);
+PUBLIC void unlinkChunk(t_cm_allocator_desc* alloc,t_cm_chunk* current);
+
+PUBLIC void unlinkFreeMem(t_cm_allocator_desc* alloc,t_cm_chunk* current);
+PUBLIC void linkFreeMemBefore(t_cm_chunk* add, t_cm_chunk* next);
+PUBLIC void linkFreeMemAfter(t_cm_chunk* prev,t_cm_chunk* add);
+
+PUBLIC void mergeChunk(t_cm_allocator_desc* alloc,t_cm_chunk *c,t_cm_chunk *destroy);
+PUBLIC t_cm_chunk* splitChunk(t_cm_allocator_desc* alloc, t_cm_chunk *chunk, t_uint32 offset, t_mem_split_position position);
+
+#endif /*REMOTE_ALLOCATOR_UTILS_H_*/
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/chunk_mgr.c b/drivers/staging/nmf-cm/cm/engine/memory/src/chunk_mgr.c
new file mode 100644
index 00000000000..26c00cdeb77
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/src/chunk_mgr.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ * Include
+ */
+#include <cm/inc/cm_type.h>
+#include "../inc/chunk_mgr.h"
+#include <cm/engine/trace/inc/trace.h>
+
+#define CHUNKS_PER_PAGE 500
+#define CHUNK_THRESOLD 5
+
+struct t_page_chuncks {
+ struct t_page_chuncks *nextPage;
+ // unsigned int freeChunkInPage;
+ t_cm_chunk chunks[CHUNKS_PER_PAGE];
+};
+
+static struct t_page_chuncks *firstPage;
+
+static unsigned int freeChunks;
+static t_cm_chunk *firstFreeChunk;
+
+t_cm_chunk* allocChunk()
+{
+ t_cm_chunk* chunk = firstFreeChunk;
+
+ firstFreeChunk = chunk->next;
+
+ chunk->next_free_mem = 0;
+ chunk->prev_free_mem = 0;
+ chunk->prev = 0;
+ chunk->next = 0;
+ chunk->status = MEM_FREE;
+ // chunk->offset = 0;
+ // chunk->size = 0;
+ // chunk->alloc = 0;
+ // chunk->userData = 0;
+
+ freeChunks--;
+
+ return chunk;
+}
+
+void freeChunk(t_cm_chunk* chunk)
+{
+ // Link chunk in free list
+ chunk->next = firstFreeChunk;
+ firstFreeChunk = chunk;
+
+ // Increase counter
+ freeChunks++;
+}
+
+t_cm_error allocChunkPool(void)
+{
+ struct t_page_chuncks* newPage;
+ int i;
+
+ newPage = (struct t_page_chuncks*)OSAL_Alloc(sizeof(struct t_page_chuncks));
+ if(newPage == NULL)
+ return CM_NO_MORE_MEMORY;
+
+ // Link page
+ newPage->nextPage = firstPage;
+ firstPage = newPage;
+
+ // Put chunk in free list
+ for(i = 0; i < CHUNKS_PER_PAGE; i++)
+ freeChunk(&newPage->chunks[i]);
+
+ return CM_OK;
+}
+
+t_cm_error fillChunkPool(void)
+{
+ if(freeChunks < CHUNK_THRESOLD)
+ return allocChunkPool();
+
+ return CM_OK;
+}
+
+void freeChunkPool(void)
+{
+ while(firstPage != NULL)
+ {
+ struct t_page_chuncks* tofree = firstPage;
+ firstPage = firstPage->nextPage;
+ OSAL_Free(tofree);
+ }
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/domain.c b/drivers/staging/nmf-cm/cm/engine/memory/src/domain.c
new file mode 100644
index 00000000000..61cb25b39ec
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/src/domain.c
@@ -0,0 +1,590 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/inc/cm_type.h>
+#include <inc/nmf-limits.h>
+
+#include <cm/engine/memory/inc/domain.h>
+#include <cm/engine/memory/inc/migration.h>
+#include <cm/engine/memory/inc/chunk_mgr.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/dsp/inc/dsp.h>
+#include <cm/engine/component/inc/instance.h>
+#include <cm/engine/power_mgt/inc/power.h>
+
+/*
+ * domain_memory structure is all we need
+ */
+#define MAX_USER_DOMAIN_NB 64
+#define MAX_SCRATCH_DOMAIN_NB 16
+
+t_cm_domain_desc domainDesc[MAX_USER_DOMAIN_NB];
+t_cm_domain_scratch_desc domainScratchDesc[MAX_SCRATCH_DOMAIN_NB];
+
+static t_cm_allocator_desc *cm_DM_getAllocator(t_cm_domain_id domainId, t_dsp_memory_type_id memType);
+static void cm_DM_DomainError(const t_cm_domain_id parentId, const t_nmf_client_id client);
+
+#define INIT_DOMAIN_STRUCT(domainDesc) do { \
+ domainDesc.client = 0; \
+ domainDesc.type = DOMAIN_NORMAL; \
+ domainDesc.refcount = 0; \
+ domainDesc.domain.coreId = MASK_ALL8; \
+ domainDesc.domain.esramCode.offset = 0; \
+ domainDesc.domain.esramCode.size = 0; \
+ domainDesc.domain.esramData.offset = 0; \
+ domainDesc.domain.esramData.size = 0; \
+ domainDesc.domain.sdramCode.offset = 0; \
+ domainDesc.domain.sdramCode.size = 0; \
+ domainDesc.domain.sdramData.offset = 0; \
+ domainDesc.domain.sdramData.size = 0; \
+ domainDesc.scratch.parent.handle = 0; \
+ domainDesc.scratch.child.alloc = 0; \
+ domainDesc.scratch.child.parent_ref = 0; \
+ } while (0)
+
+#define FIND_DOMAIN_ID(domainId) \
+ { \
+ domainId = 0; \
+ while (domainDesc[domainId].client != 0 && domainId < MAX_USER_DOMAIN_NB) { \
+ domainId++; \
+ } \
+ if (domainId >= MAX_USER_DOMAIN_NB) { \
+ return CM_INTERNAL_DOMAIN_OVERFLOW; \
+ } \
+ }
+
+#define FIND_SCRATCH_DOMAIN_ID(domainId) \
+ { \
+ domainId = 0; \
+ while (domainScratchDesc[domainId].allocDesc != 0 && domainId < MAX_SCRATCH_DOMAIN_NB) { \
+ domainId++; \
+ } \
+ if (domainId >= MAX_SCRATCH_DOMAIN_NB) { \
+ return CM_INTERNAL_DOMAIN_OVERFLOW; \
+ } \
+ }
+
+PUBLIC t_cm_error cm_DM_CheckDomain(t_cm_domain_id handle, t_cm_domain_type type)
+{
+ if ((handle <= 3)
+ || (handle >= MAX_USER_DOMAIN_NB)) { //remember, domain[0-3] are reserved
+ return CM_INVALID_DOMAIN_HANDLE;
+ }
+
+ if (domainDesc[handle].client == 0) {
+ return CM_INVALID_DOMAIN_HANDLE;
+ }
+
+ if (type != DOMAIN_ANY) {
+ if (domainDesc[handle].type != type) {
+ return CM_INVALID_DOMAIN_HANDLE;
+ }
+ }
+
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_DM_CheckDomainWithClient(t_cm_domain_id handle, t_cm_domain_type type, t_nmf_client_id client)
+{
+ t_cm_error error;
+
+ if((error = cm_DM_CheckDomain(handle, type)) != CM_OK)
+ return error;
+
+#ifdef CHECK_TO_BE_REACTIVATED_IN_2_11
+ if(domainDesc[handle].client != client)
+ {
+ ERROR("CM_DOMAIN_VIOLATION: domain %d created by client %d not usable by client %d.", handle, domainDesc[handle].client, client, 0, 0, 0);
+ return CM_DOMAIN_VIOLATION;
+ }
+#endif
+
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_DM_Init(void)
+{
+ t_cm_error error;
+
+ int i = 0;
+ for(i = 0; i < MAX_USER_DOMAIN_NB; i++) {
+ INIT_DOMAIN_STRUCT(domainDesc[i]);
+ }
+
+ //domains[0-3] are reserved - allows to catch some cases of incorrect usage,
+ //especially when user uses coreId instead of domainId, ie id = 1, 2, 3
+ domainDesc[0].client = NMF_CORE_CLIENT;
+ domainDesc[1].client = NMF_CORE_CLIENT;
+ domainDesc[2].client = NMF_CORE_CLIENT;
+ domainDesc[3].client = NMF_CORE_CLIENT;
+
+ for(i = 0; i < MAX_SCRATCH_DOMAIN_NB; i++) {
+ domainScratchDesc[i].domainId = 0;
+ domainScratchDesc[i].parentId = 0;
+ domainScratchDesc[i].allocDesc = 0;
+ }
+
+ // Alloc twice for having comfortable chunk
+ if((error = allocChunkPool()) != CM_OK)
+ return error;
+ if((error = allocChunkPool()) != CM_OK)
+ {
+ freeChunkPool();
+ return error;
+ }
+
+ return CM_OK;
+}
+
+PUBLIC void cm_DM_Destroy(void)
+{
+ //cm_DM_Init();
+ freeChunkPool();
+}
+
+PUBLIC t_nmf_core_id cm_DM_GetDomainCoreId(const t_cm_domain_id domainId)
+{
+
+ return domainDesc[domainId].domain.coreId;
+}
+
+#if 0
+static t_uint32 cm_DM_isSegmentOverlaping(const t_cm_domain_segment *d0, const t_cm_domain_segment *d1)
+{
+ t_uint32 min0 = d0->offset;
+ t_uint32 max0 = d0->offset + d0->size;
+ t_uint32 min1 = d1->offset;
+ t_uint32 max1 = d1->offset + d1->size;
+
+ if ( (min0 < min1) && (min1 < max0) ){ /* min0 < min1 < max0 OR min1 in [min0:max0] */
+ return 1;
+ }
+ if ( (min1 < min0) && (min0 <= max1) ){ /* min1 < min0 < max0 OR min0 in [min1:max1] */
+ return 1;
+ }
+
+ return 0;
+}
+{
+ ...
+
+ t_uint32 i;
+ //check non-overlapp with other domains
+ for (i = 0; i < MAX_USER_DOMAIN_NB; i++) {
+ if (domainDesc[i].client != 0) {
+ if (cm_DM_isSegmentOverlaping(&domainDesc[i].domain.esramData, &domain->esramData)) {
+ return CM_DOMAIN_OVERLAP;
+ }
+ /*
+ if (cm_DM_isSegmentOverlaping(&domainDesc[i].domain.esramData, &domain->esramData)) {
+ return CM_DOMAIN_OVERLAP;
+ }
+ */
+ }
+ }
+
+ ...
+}
+#endif
+
+PUBLIC t_cm_error cm_DM_CreateDomain(const t_nmf_client_id client, const t_cm_domain_memory *domain, t_cm_domain_id *handle)
+{
+ t_cm_domain_id domainId;
+ FIND_DOMAIN_ID(domainId);
+
+ if (client == 0)
+ return CM_INVALID_PARAMETER;
+
+ if (domain->coreId > LAST_CORE_ID)
+ return CM_INVALID_DOMAIN_DEFINITION;
+
+ //FIXME, juraj, check invalid domain definition
+ domainDesc[domainId].client = client;
+ domainDesc[domainId].domain = *domain;
+
+ *handle = domainId;
+
+ return CM_OK;
+}
+
+//TODO, juraj, add assert to cm_MM_GetOffset(), if domain is scratch parent
+PUBLIC t_cm_error cm_DM_CreateDomainScratch(const t_nmf_client_id client, const t_cm_domain_id parentId, const t_cm_domain_memory *domain, t_cm_domain_id *handle)
+{
+ t_cm_error error;
+ t_memory_handle memhandle;
+ t_cm_allocator_desc *alloc;
+ t_uint32 parentMin, parentMax;
+ t_uint32 scratchMin, scratchMax;
+
+ /* check if the parent domain exists */
+ /* parent could be DOMAIN_NORMAL (1st call) or DOMAIN_SCRATCH_PARENT (other calls) */
+ if ((error = cm_DM_CheckDomain(parentId, DOMAIN_ANY)) != CM_OK) {
+ return error;
+ }
+
+ parentMin = domainDesc[parentId].domain.esramData.offset;
+ parentMax = domainDesc[parentId].domain.esramData.offset + domainDesc[parentId].domain.esramData.size;
+ scratchMin = domain->esramData.offset;
+ scratchMax = domain->esramData.offset + domain->esramData.size;
+ /* check if the scratch domain respects the parent domain (esram data only )*/
+ if ( (parentMin > scratchMin) || (parentMax < scratchMax) ) {
+ return CM_INVALID_DOMAIN_DEFINITION;
+ }
+
+ /* create the scratch domain */
+ if ((error = cm_DM_CreateDomain(client, domain, handle)) != CM_OK) {
+ return error;
+ }
+
+ /* check if this is the first scratch domain */
+ if (domainDesc[parentId].scratch.parent.handle == 0) {
+ /* 1st scratch domain */
+ t_cm_domain_segment tmp;
+
+ /* reserve the zone for the scratch domain */
+ tmp = domainDesc[parentId].domain.esramData;
+ domainDesc[parentId].domain.esramData = domain->esramData;
+ memhandle = cm_DM_Alloc(parentId, ESRAM_EXT16, domain->esramData.size / 2, CM_MM_ALIGN_NONE, FALSE); //note byte to 16bit-word conversion
+ domainDesc[parentId].domain.esramData = tmp;
+ if (memhandle == 0) {
+ cm_DM_DestroyDomain(*handle);
+ cm_DM_DomainError(parentId, client);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ domainDesc[parentId].type = DOMAIN_SCRATCH_PARENT;
+ domainDesc[parentId].refcount = 0; //reinit the refcount
+ domainDesc[parentId].scratch.parent.handle = memhandle;
+
+ } else {
+ /* nth scratch domain */
+ t_uint32 i;
+ t_uint32 oldMin = domainDesc[parentId].domain.esramData.offset + domainDesc[parentId].domain.esramData.offset;
+ t_uint32 oldMax = 0;
+
+ /* compute the new scratch zone size */
+ for(i = 0; i < MAX_USER_DOMAIN_NB; i++) {
+ if ((domainDesc[i].type == DOMAIN_SCRATCH_CHILD) && (domainDesc[i].scratch.child.parent_ref == parentId)) {
+ /* ok, here we have a scratch domain created from the same child domain */
+ t_uint32 min = domainDesc[i].domain.esramData.offset;
+ t_uint32 max = domainDesc[i].domain.esramData.offset + domainDesc[i].domain.esramData.size;
+
+ oldMin = (min < oldMin)?min:oldMin;
+ oldMax = (max > oldMax)?max:oldMax;
+ }
+ }
+
+ /* resize the scratch zone */
+ if ((oldMin > scratchMin) || (oldMax < scratchMax)) {
+ t_uint32 newMin = (oldMin > scratchMin)?scratchMin:oldMin;
+ t_uint32 newMax = (oldMax < scratchMax)?scratchMax:oldMax;
+ t_uint16 userData;
+
+ /* save user data lost during realloc, user data do not change */
+ cm_MM_GetMemoryHandleUserData(domainDesc[parentId].scratch.parent.handle, &userData, 0);
+ memhandle = cm_MM_Realloc(cm_DM_getAllocator(parentId, ESRAM_EXT16), newMax - newMin, newMin,
+ CM_MM_MPC_ALIGN_NONE, domainDesc[parentId].scratch.parent.handle);
+ if (memhandle == 0) {
+ /* failed to extend the zone */
+ cm_DM_DestroyDomain(*handle);
+ cm_DM_DomainError(parentId, client);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ cm_MM_SetMemoryHandleUserData(memhandle, userData);
+ domainDesc[parentId].scratch.parent.handle = memhandle;
+ }
+ }
+
+ /* create esram-data allocator in the scratch domain */
+ alloc = cm_MM_CreateAllocator(domainDesc[*handle].domain.esramData.size,
+ domainDesc[*handle].domain.esramData.offset,
+ "scratch");
+
+ domainDesc[*handle].type = DOMAIN_SCRATCH_CHILD;
+ domainDesc[*handle].scratch.child.parent_ref = parentId;
+ domainDesc[*handle].scratch.child.alloc = alloc;
+ domainDesc[parentId].refcount++;
+
+ return error;
+}
+
+PUBLIC t_cm_error cm_DM_DestroyDomains(const t_nmf_client_id client)
+{
+ t_cm_domain_id handle;
+ t_cm_error error, status=CM_OK;
+
+ for (handle=0; handle<MAX_USER_DOMAIN_NB; handle++) {
+ if ((domainDesc[handle].client == client)
+ && ((error=cm_DM_DestroyDomain(handle)) != CM_OK)) {
+ LOG_INTERNAL(0, "Error (%d) destroying remaining domainId %d for client %u\n", error, handle, client, 0, 0, 0);
+ status = error;
+ }
+ }
+ return status;
+}
+
+PUBLIC t_cm_error cm_DM_DestroyDomain(t_cm_domain_id handle)
+{
+ t_cm_error error = CM_OK;
+ t_uint32 i;
+
+ if ((error = cm_DM_CheckDomain(handle, DOMAIN_ANY)) != CM_OK) {
+ return error;
+ }
+
+ //forbid destruction of cm domains
+ //if (handle == cm_DSP_GetState(domainDesc[handle].domain.coreId)->domainEE)
+ // return CM_INVALID_DOMAIN_HANDLE;
+
+ /* loop all components and check if there are still components instantiated with this handle */
+ //actually this check is redundant with the usage counters as component instantiations allocate memory
+ for (i=0; i<ComponentTable.idxMax; i++)
+ {
+ if (NULL != componentEntry(i) && componentEntry(i)->domainId == handle) {
+ return CM_ILLEGAL_DOMAIN_OPERATION;
+ }
+ }
+
+ //perform check based on usage counters
+ if (domainDesc[handle].refcount != 0) {
+ return CM_ILLEGAL_DOMAIN_OPERATION;
+ }
+
+ if (domainDesc[handle].type == DOMAIN_SCRATCH_PARENT) {
+ return CM_ILLEGAL_DOMAIN_OPERATION; //parent destroyed implicitly with the last scratch
+ } else if (domainDesc[handle].type == DOMAIN_SCRATCH_CHILD) {
+ t_cm_allocator_status status;
+ t_cm_domain_id parentId = domainDesc[handle].scratch.child.parent_ref;
+
+ cm_MM_GetAllocatorStatus(domainDesc[handle].scratch.child.alloc, 0, 0xffff, &status);
+ if (status.global.accumulate_used_memory != 0) {
+ //something is still allocated
+ return CM_ILLEGAL_DOMAIN_OPERATION;
+ }
+
+ domainDesc[parentId].refcount--;
+ cm_MM_DeleteAllocator(domainDesc[handle].scratch.child.alloc); //returns no error
+
+ if (domainDesc[parentId].refcount == 0) {
+ /* last scratch domain */
+ cm_DM_Free(domainDesc[parentId].scratch.parent.handle, FALSE);
+ domainDesc[parentId].scratch.parent.handle = 0;
+ domainDesc[parentId].type = DOMAIN_NORMAL;
+ } else {
+ /* other child scratch domains exist, check if the reserved zone needs resize, ie reduce */
+
+ t_uint32 i;
+ /* init oldMin and oldMax to values we are sure will get overwritten below */
+ t_uint32 oldMin = 0xffffffff;
+ t_uint32 oldMax = 0x0;
+ t_uint32 scratchMin = domainDesc[handle].domain.esramData.offset;
+ t_uint32 scratchMax = domainDesc[handle].domain.esramData.offset + domainDesc[handle].domain.esramData.size;
+ t_memory_handle memhandle;
+
+ /* compute the remaining reserved zone size */
+ for(i = 0; i < MAX_USER_DOMAIN_NB; i++) {
+ if (i == handle)
+ continue; //do not consider the current domain to be destroyed later in this function
+ if ((domainDesc[i].type == DOMAIN_SCRATCH_CHILD) && (domainDesc[i].scratch.child.parent_ref == parentId)) {
+ /* ok, here we have a scratch domain created from the same child domain */
+ t_uint32 min = domainDesc[i].domain.esramData.offset;
+ t_uint32 max = domainDesc[i].domain.esramData.offset + domainDesc[i].domain.esramData.size;
+
+ oldMin = (min < oldMin)?min:oldMin;
+ oldMax = (max > oldMax)?max:oldMax;
+ }
+ }
+
+ /* resize the scratch zone */
+ if ((oldMin > scratchMin) || (oldMax < scratchMax)) {
+ t_uint16 userData;
+
+ /* save user data lost during realloc, user data do not change */
+ cm_MM_GetMemoryHandleUserData(domainDesc[parentId].scratch.parent.handle, &userData, 0);
+ memhandle = cm_MM_Realloc(cm_DM_getAllocator(parentId, ESRAM_EXT16), oldMax - oldMin, oldMin,
+ CM_MM_MPC_ALIGN_NONE, domainDesc[parentId].scratch.parent.handle);
+ CM_ASSERT(memhandle); //the realloc shouldn't fail..
+
+ cm_MM_SetMemoryHandleUserData(memhandle, userData);
+ domainDesc[parentId].scratch.parent.handle = memhandle;
+ }
+ }
+ }
+
+ //reset the domain desc
+ INIT_DOMAIN_STRUCT(domainDesc[handle]);
+
+ return CM_OK;
+}
+
+/*
+ * - if the domainId is scratch parent, all allocations are done as in normal domains
+ * - if the domainId is scratch child
+ * if allocation type is esram, retrieve the allocator from the domainDesc
+ * else allocation is done as for normal domain
+ * - if the domainId is normal, allocator is retrieved from mpcDesc via cm_DSP_GetAllocator()
+ */
+static t_cm_allocator_desc *cm_DM_getAllocator(t_cm_domain_id domainId, t_dsp_memory_type_id memType)
+{
+ t_cm_allocator_desc *alloc = 0;
+
+ if ((domainDesc[domainId].type == DOMAIN_SCRATCH_CHILD)
+ && ((memType == ESRAM_EXT16) || (memType == ESRAM_EXT24))) {
+ alloc = domainDesc[domainId].scratch.child.alloc;
+ } else {
+ alloc = cm_DSP_GetAllocator(domainDesc[domainId].domain.coreId, memType);
+ }
+
+ return alloc;
+}
+
+void START(void);
+void END(const char*);
+
+//TODO, juraj, alloc would need to return finer errors then 0
+PUBLIC t_memory_handle cm_DM_Alloc(t_cm_domain_id domainId, t_dsp_memory_type_id memType, t_uint32 wordSize, t_cm_mpc_memory_alignment memAlignment, t_bool powerOn)
+{
+ t_nmf_core_id coreId = domainDesc[domainId].domain.coreId;
+ t_memory_handle handle;
+ t_cm_allocator_desc *alloc;
+ t_uint32 offset;
+ t_uint32 size;
+
+ cm_DSP_GetInternalMemoriesInfo(domainId, memType, &offset, &size);
+
+ if ((alloc = cm_DM_getAllocator(domainId, memType)) == 0) {
+ return 0;
+ }
+
+ handle = cm_MM_Alloc(alloc,
+ cm_DSP_ConvertSize(memType, wordSize),
+ (t_cm_memory_alignment) memAlignment,
+ offset, size, domainId);
+
+ if(handle != INVALID_MEMORY_HANDLE)
+ {
+ cm_MM_SetMemoryHandleUserData(handle, (coreId << SHIFT_BYTE1) | (memType << SHIFT_BYTE0));
+
+ if (powerOn) {
+ // [Pwr] The associated power domain can be enabled only after the Alloc request.
+ // Associated MPC memory chunk is not accessed (Remote allocator feature)
+ cm_PWR_EnableMemory(
+ coreId,
+ memType,
+ /*
+ * Compute physical address based on cm_DSP_GetHostSystemAddress but in optimized way
+ * -> See it for information
+ * -> Note TCM memory is not correctly compute, but it's not used
+ */
+ cm_DSP_GetState(coreId)->allocator[memType]->baseAddress.physical + cm_MM_GetOffset(handle),
+ cm_MM_GetSize(handle));
+ }
+ }
+
+ return handle;
+}
+
+PUBLIC void cm_DM_FreeWithInfo(t_memory_handle memHandle, t_nmf_core_id *coreId, t_dsp_memory_type_id *memType, t_bool powerOff)
+{
+ t_dsp_chunk_info chunk_info;
+
+ cm_DSP_GetDspChunkInfo(memHandle, &chunk_info);
+
+ if (powerOff) {
+ cm_PWR_DisableMemory(
+ chunk_info.coreId,
+ chunk_info.memType,
+ cm_DSP_GetPhysicalAdress(memHandle),
+ cm_MM_GetSize(memHandle));
+ }
+
+ cm_MM_Free(chunk_info.alloc, memHandle);
+
+ *coreId = chunk_info.coreId;
+ *memType = chunk_info.memType;
+}
+
+PUBLIC void cm_DM_Free(t_memory_handle memHandle, t_bool powerOff)
+{
+ t_nmf_core_id coreId;
+ t_dsp_memory_type_id memType;
+
+ cm_DM_FreeWithInfo(memHandle, &coreId, &memType, powerOff);
+}
+
+PUBLIC t_cm_error cm_DM_GetAllocatorStatus(t_cm_domain_id domainId, t_dsp_memory_type_id memType, t_cm_allocator_status *pStatus)
+{
+ t_cm_error error;
+ t_uint32 dOffset;
+ t_uint32 dSize;
+
+ //TODO, scratch
+ error = cm_DM_CheckDomain(domainId, DOMAIN_ANY);
+ if (error != CM_OK) {
+ return error;
+ }
+
+ cm_DSP_GetInternalMemoriesInfo(domainId, memType, &dOffset, &dSize);
+
+ return cm_DSP_GetAllocatorStatus(domainDesc[domainId].domain.coreId, memType,
+ dOffset, dSize, pStatus);
+}
+
+//WARNING: this function is only correct *before* migration! because
+//the computation of absolute adresses of a domain is based on the allocator for the given
+//segment (this is hidden in cm_DSP_GetDspBaseAddress and this info is not valid
+//after migration (non-contiguous address-space from the ARM-side)
+PUBLIC t_cm_error cm_DM_GetDomainAbsAdresses(t_cm_domain_id domainId, t_cm_domain_info *info)
+{
+ t_cm_error error;
+ t_nmf_core_id coreId = domainDesc[domainId].domain.coreId;
+
+ cm_migration_check_state(coreId, STATE_NORMAL);
+
+ error = cm_DM_CheckDomain(domainId, DOMAIN_NORMAL);
+ if (error != CM_OK) {
+ return error;
+ }
+
+ cm_DSP_GetDspBaseAddress(coreId, SDRAM_CODE, &info->sdramCode);
+ cm_DSP_GetDspBaseAddress(coreId, ESRAM_CODE, &info->esramCode);
+ cm_DSP_GetDspBaseAddress(coreId, SDRAM_EXT24, &info->sdramData);
+ cm_DSP_GetDspBaseAddress(coreId, ESRAM_EXT24, &info->esramData);
+
+ info->sdramCode.physical += domainDesc[domainId].domain.sdramCode.offset;
+ info->sdramCode.logical += domainDesc[domainId].domain.sdramCode.offset;
+ info->esramCode.physical += domainDesc[domainId].domain.esramCode.offset;
+ info->esramCode.logical += domainDesc[domainId].domain.esramCode.offset;
+ info->sdramData.physical += domainDesc[domainId].domain.sdramData.offset;
+ info->sdramData.logical += domainDesc[domainId].domain.sdramData.offset;
+ info->esramData.physical += domainDesc[domainId].domain.esramData.offset;
+ info->esramData.logical += domainDesc[domainId].domain.esramData.offset;
+
+ return CM_OK;
+}
+
+static void cm_DM_DomainError(const t_cm_domain_id parentId, const t_nmf_client_id client)
+{
+ int i;
+ LOG_INTERNAL(0, "NMF_DEBUG_SCRATCH failed to allocate domain (client %u): 0x%08x -> 0x%08x\n",
+ client,
+ domainDesc[parentId].domain.esramData.offset,
+ domainDesc[parentId].domain.esramData.offset + domainDesc[parentId].domain.esramData.size,
+ 0, 0, 0);
+ for(i = 0; i < MAX_USER_DOMAIN_NB; i++) {
+ if (domainDesc[i].type == DOMAIN_SCRATCH_CHILD) {
+ LOG_INTERNAL(0, "NMF_DEBUG_SCRATCH scratch domain %d allocated (client %u): 0x%08x -> 0x%08x\n",
+ i, domainDesc[i].client,
+ domainDesc[i].domain.esramData.offset,
+ domainDesc[i].domain.esramData.offset + domainDesc[i].domain.esramData.size,
+ 0, 0);
+ }
+ }
+ cm_MM_DumpMemory(cm_DM_getAllocator(parentId, ESRAM_EXT16),
+ domainDesc[parentId].domain.esramData.offset,
+ domainDesc[parentId].domain.esramData.offset + domainDesc[parentId].domain.esramData.size);
+}
+
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/domain_wrapper.c b/drivers/staging/nmf-cm/cm/engine/memory/src/domain_wrapper.c
new file mode 100644
index 00000000000..ec305812f15
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/src/domain_wrapper.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/api/domain_engine.h>
+#include <cm/engine/api/migration_engine.h>
+#include <cm/engine/memory/inc/domain.h>
+#include <cm/engine/memory/inc/migration.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_CreateMemoryDomain(
+ const t_nmf_client_id client,
+ const t_cm_domain_memory *domain,
+ t_cm_domain_id *handle
+ )
+{
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+ error = cm_DM_CreateDomain(client, domain, handle);
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_CreateMemoryDomainScratch(
+ const t_nmf_client_id client,
+ const t_cm_domain_id parentId,
+ const t_cm_domain_memory *domain,
+ t_cm_domain_id *handle
+ )
+{
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+ error = cm_DM_CreateDomainScratch(client, parentId, domain, handle);
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_DestroyMemoryDomain(
+ t_cm_domain_id handle)
+{
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+ error = cm_DM_DestroyDomain(handle);
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_FlushMemoryDomains(
+ t_nmf_client_id client)
+{
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+ error = cm_DM_DestroyDomains(client);
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetDomainCoreId(const t_cm_domain_id domainId, t_nmf_core_id *coreId)
+{
+ t_cm_error error;
+ OSAL_LOCK_API();
+ //TODO, scratch
+ error = cm_DM_CheckDomain(domainId, DOMAIN_NORMAL);
+ if (error != CM_OK) {
+ OSAL_UNLOCK_API();
+ return error;
+ }
+
+ *coreId = cm_DM_GetDomainCoreId(domainId);
+ OSAL_UNLOCK_API();
+ return CM_OK;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_Migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst)
+{
+ t_cm_error error;
+ OSAL_LOCK_API();
+ error = cm_migrate(srcShared, src, dst);
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_Unmigrate(void)
+{
+ t_cm_error error;
+ OSAL_LOCK_API();
+ error = cm_unmigrate();
+ OSAL_UNLOCK_API();
+ return error;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/memory_wrapper.c b/drivers/staging/nmf-cm/cm/engine/memory/src/memory_wrapper.c
new file mode 100644
index 00000000000..f7d47be924d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/src/memory_wrapper.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/memory/inc/memory.h>
+#include <cm/engine/dsp/inc/dsp.h>
+#include <cm/engine/component/inc/instance.h>
+#include <cm/engine/configuration/inc/configuration.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+#include <cm/engine/trace/inc/trace.h>
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_AllocMpcMemory(
+ t_cm_domain_id domainId,
+ t_nmf_client_id clientId,
+ t_cm_mpc_memory_type memType,
+ t_cm_size size,
+ t_cm_mpc_memory_alignment memAlignment,
+ t_cm_memory_handle *pHandle
+ )
+{
+ t_dsp_memory_type_id dspMemType;
+
+ switch(memType)
+ {
+ case CM_MM_MPC_TCM16_X:
+ dspMemType = INTERNAL_XRAM16;
+ break;
+ case CM_MM_MPC_TCM24_X:
+ dspMemType = INTERNAL_XRAM24;
+ break;
+ case CM_MM_MPC_TCM16_Y:
+ dspMemType = INTERNAL_YRAM16;
+ break;
+ case CM_MM_MPC_TCM24_Y:
+ dspMemType = INTERNAL_YRAM24;
+ break;
+#ifndef __STN_8810
+ case CM_MM_MPC_ESRAM16:
+ dspMemType = ESRAM_EXT16;
+ break;
+ case CM_MM_MPC_ESRAM24:
+ dspMemType = ESRAM_EXT24;
+ break;
+#endif /* ndef __STN_8810 */
+ case CM_MM_MPC_SDRAM16:
+ dspMemType = SDRAM_EXT16;
+ break;
+ case CM_MM_MPC_SDRAM24:
+ dspMemType = SDRAM_EXT24;
+ break;
+ default:
+ return CM_INVALID_PARAMETER;
+ }
+
+ OSAL_LOCK_API();
+ {
+ t_cm_error error;
+ error = cm_DM_CheckDomainWithClient(domainId, DOMAIN_ANY, clientId);
+ if (error != CM_OK) {
+ OSAL_UNLOCK_API();
+ return error;
+ }
+ }
+
+ switch(memAlignment) {
+ case CM_MM_MPC_ALIGN_NONE :
+ case CM_MM_MPC_ALIGN_HALFWORD :
+ case CM_MM_MPC_ALIGN_WORD :
+ case CM_MM_MPC_ALIGN_2WORDS :
+ case CM_MM_MPC_ALIGN_4WORDS :
+ case CM_MM_MPC_ALIGN_8WORDS :
+ case CM_MM_MPC_ALIGN_16WORDS :
+ case CM_MM_MPC_ALIGN_32WORDS :
+ case CM_MM_MPC_ALIGN_64WORDS :
+ case CM_MM_MPC_ALIGN_128WORDS :
+ case CM_MM_MPC_ALIGN_256WORDS :
+ case CM_MM_MPC_ALIGN_512WORDS :
+ case CM_MM_MPC_ALIGN_1024WORDS :
+ case CM_MM_MPC_ALIGN_65536BYTES :
+ //case CM_MM_MPC_ALIGN_16384WORDS : maps to the same value as above
+ break;
+ default:
+ OSAL_UNLOCK_API();
+ return CM_INVALID_PARAMETER;
+ }
+
+ /* in case we allocate in tcm x be sure ee is load before */
+ if ( memType == CM_MM_MPC_TCM16_X || memType == CM_MM_MPC_TCM24_X ||
+ memType == CM_MM_MPC_TCM16_Y || memType == CM_MM_MPC_TCM24_Y )
+ {
+ t_cm_error error;
+ if ((error = cm_CFG_CheckMpcStatus(cm_DM_GetDomainCoreId(domainId))) != CM_OK)
+ {
+ OSAL_UNLOCK_API();
+ return error;
+ }
+ }
+
+ /* alloc memory */
+ *pHandle = (t_cm_memory_handle)cm_DM_Alloc(domainId, dspMemType, size, memAlignment, TRUE);
+ if(*pHandle == (t_cm_memory_handle)INVALID_MEMORY_HANDLE)
+ {
+ OSAL_UNLOCK_API();
+ return CM_NO_MORE_MEMORY;
+ }
+
+ OSAL_UNLOCK_API();
+ return CM_OK;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_FreeMpcMemory(t_cm_memory_handle handle)
+{
+ t_cm_error error;
+ t_memory_handle validHandle;
+ t_nmf_core_id coreId;
+ t_dsp_memory_type_id memType;
+
+ OSAL_LOCK_API();
+
+ if((error = cm_MM_getValidMemoryHandle(handle, &validHandle)) != CM_OK)
+ {
+ OSAL_UNLOCK_API();
+ return error;
+ }
+
+ cm_DM_FreeWithInfo(validHandle, &coreId, &memType, TRUE);
+
+ /* in case we allocate in tcm x be sure ee is load before */
+ if ( memType == INTERNAL_XRAM16 || memType == INTERNAL_XRAM24 ||
+ memType == INTERNAL_YRAM16 || memType == INTERNAL_YRAM24 )
+ {
+ cm_CFG_ReleaseMpc(coreId);
+ }
+
+ OSAL_UNLOCK_API();
+ return CM_OK;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemorySystemAddress(t_cm_memory_handle handle, t_cm_system_address *pSystemAddress)
+{
+ t_cm_error error;
+ t_memory_handle validHandle;
+
+ OSAL_LOCK_API();
+
+ if((error = cm_MM_getValidMemoryHandle(handle, &validHandle)) != CM_OK)
+ {
+ OSAL_UNLOCK_API();
+ return error;
+ }
+
+ cm_DSP_GetHostSystemAddress(validHandle, pSystemAddress);
+
+ OSAL_UNLOCK_API();
+ return CM_OK;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemoryMpcAddress(t_cm_memory_handle handle, t_uint32 *pDspAddress)
+{
+ t_cm_error error;
+ t_memory_handle validHandle;
+
+ OSAL_LOCK_API();
+
+ if((error = cm_MM_getValidMemoryHandle(handle, &validHandle)) != CM_OK)
+ {
+ OSAL_UNLOCK_API();
+ return error;
+ }
+
+ cm_DSP_GetDspAddress(validHandle, pDspAddress);
+
+ OSAL_UNLOCK_API();
+ return CM_OK;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetMpcMemoryStatus(t_nmf_core_id coreId, t_cm_mpc_memory_type memType, t_cm_allocator_status *pStatus)
+{
+ t_dsp_memory_type_id dspMemType;
+ t_cm_error error;
+
+ switch(memType)
+ {
+ case CM_MM_MPC_TCM16_X:
+ dspMemType = INTERNAL_XRAM16;
+ break;
+ case CM_MM_MPC_TCM24_X:
+ dspMemType = INTERNAL_XRAM24;
+ break;
+ case CM_MM_MPC_TCM16_Y:
+ dspMemType = INTERNAL_YRAM16;
+ break;
+ case CM_MM_MPC_TCM24_Y:
+ dspMemType = INTERNAL_YRAM24;
+ break;
+#ifndef __STN_8810
+ case CM_MM_MPC_ESRAM16:
+ dspMemType = ESRAM_EXT16;
+ break;
+ case CM_MM_MPC_ESRAM24:
+ dspMemType = ESRAM_EXT24;
+ break;
+#endif /* ndef __STN_8810 */
+ case CM_MM_MPC_SDRAM16:
+ dspMemType = SDRAM_EXT16;
+ break;
+ case CM_MM_MPC_SDRAM24:
+ dspMemType = SDRAM_EXT24;
+ break;
+ default:
+ return CM_INVALID_PARAMETER;
+ }
+
+ OSAL_LOCK_API();
+ error = cm_DSP_GetAllocatorStatus(coreId, dspMemType, 0, 0, pStatus);
+ OSAL_UNLOCK_API();
+
+ return error;
+}
+
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/migration.c b/drivers/staging/nmf-cm/cm/engine/memory/src/migration.c
new file mode 100644
index 00000000000..35813a56bf2
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/src/migration.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/inc/cm_type.h>
+#include <inc/type.h>
+#include <inc/nmf-limits.h>
+
+#include <cm/engine/communication/fifo/inc/nmf_fifo_arm.h>
+#include <cm/engine/dsp/inc/dsp.h>
+#include <cm/engine/memory/inc/domain.h>
+#include <cm/engine/memory/inc/memory.h>
+#include <cm/engine/memory/inc/migration.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/utils/inc/mem.h>
+
+#if defined(__STN_8500) && (__STN_8500 > 10)
+
+typedef enum {
+ CM_MIGRATION_OK = 0,
+ CM_MIGRATION_INVALID_ARGUMENT = 1,
+ CM_MIGRATION_ERROR = 2,
+} t_cm_migration_error;
+
+extern t_nmf_fifo_arm_desc* mpc2mpcComsFifoId[NB_CORE_IDS][NB_CORE_IDS];
+
+/*!
+ * \brief Data structure representing a segment to migrate
+ *
+ * segment:
+ * - used to determine which mmdsp-hw base is to be updated, index in mpcDesc->segments[] structure
+ * * this is hard-coded in cm_migrate(), could be computed (would be nice) LIMITATION
+ * srcAdr.physical:
+ * - new base setting
+ * * computed from the src domain in cm_DM_GetAbsAdresses() which uses the start of the allocator for the memory
+ * this is a LIMITATION, as this information is valid only before migration
+ * srcAdr.logical:
+ * - cm_MemCopy()
+ * * computed as srcAdr.logical
+ * dstAdr.physical: see srcAdr.physical
+ * dstAdr.logical: see srcAdr.logical
+ * size:
+ * - cm_MemCopy()
+ * - setting the top when new base is set
+ */
+typedef struct {
+ t_dsp_segment_type segment; //!< the link to the segment type
+ t_cm_system_address srcAdr; //!< source address
+ t_cm_system_address dstAdr; //!< destination address
+ t_uint32 size; //!< size of the segment
+} t_cm_migration_segment;
+
+/*!
+ * \brief Internal data structure 1/ during migration, and 2/ between migration and unmigration calls
+ *
+ * all needed information are computed before calling _cm_migration_move()
+ */
+typedef struct {
+ t_cm_migration_state state; //!< migration state
+ t_nmf_core_id coreId; //!< migration only on one mpc
+ t_cm_migration_segment segments[NB_MIGRATION_SEGMENT]; //!< segments to migrate (selected on migration_move)
+ t_memory_handle handles[NB_MIGRATION_SEGMENT]; //!< memory handles for destination chunks allocated prior migration
+} t_cm_migration_internal_state;
+
+static t_cm_migration_internal_state migrationState = {STATE_NORMAL, };
+
+static t_cm_error _cm_migration_initSegment(
+ t_dsp_segment_type dspSegment,
+ t_cm_system_address *srcAdr,
+ t_uint32 size,
+ t_cm_domain_id dst,
+ t_cm_migration_internal_state *info
+ )
+{
+ t_cm_system_address dstAdr;
+ t_cm_migration_segment *segment = &info->segments[dspSegment];
+ t_memory_handle handle;
+
+ handle = cm_DM_Alloc(dst, ESRAM_EXT16, size >> 1, CM_MM_ALIGN_AHB_BURST, TRUE); //note: byte to half-word conversion
+ if (handle == 0) {
+ return CM_NO_MORE_MEMORY;
+ }
+
+ info->handles[dspSegment] = handle;
+
+ cm_DSP_GetHostSystemAddress(handle, &dstAdr);
+
+ segment->segment = dspSegment; //this is redundant and could be avoided by recoding move(), but nice to have for debug
+ segment->size = size;
+ segment->srcAdr = *srcAdr;
+ segment->dstAdr = dstAdr;
+
+ return CM_OK;
+}
+
+static void _cm_migration_releaseSegment(t_cm_migration_internal_state *info, t_dsp_segment_type segId)
+{
+ cm_DM_Free(info->handles[segId], TRUE);
+}
+
+static t_cm_migration_error _cm_migration_release(t_cm_migration_internal_state *info)
+{
+ t_uint32 i = 0;
+ for (i = 0; i < NB_MIGRATION_SEGMENT; i++) {
+ cm_DM_Free(info->handles[i], TRUE);
+ }
+
+ return CM_MIGRATION_OK;
+}
+
+#define SEGMENT_START(seg) \
+ seg.offset
+
+#define SEGMENT_END(seg) \
+ seg.offset + seg.size
+
+static t_cm_error _cm_migration_check(
+ const t_cm_domain_id srcShared,
+ const t_cm_domain_id src,
+ const t_cm_domain_id dst,
+ t_cm_migration_internal_state *info
+ )
+{
+ t_cm_error error = CM_OK;
+ t_cm_domain_info domainInfoSrc;
+ t_cm_domain_info domainInfoShared;
+ t_cm_domain_desc *domainEE;
+ t_cm_domain_desc *domainShared;
+ t_nmf_core_id coreId = cm_DM_GetDomainCoreId(src);
+
+ //coreIds in src, srcShared and dst match
+ if (!((domainDesc[src].domain.coreId == domainDesc[srcShared].domain.coreId)
+ && (domainDesc[src].domain.coreId == domainDesc[dst].domain.coreId))) {
+ return CM_INVALID_PARAMETER;
+ }
+
+ //check srcShared starts at 0
+ //FIXME, juraj, today EE code is in SDRAM, but this is flexible, so must find out where EE is instantiated
+ if (domainDesc[srcShared].domain.sdramCode.offset != 0x0) {
+ return CM_INVALID_PARAMETER;
+ }
+
+ //check srcShared contains EE domain
+ domainEE = &domainDesc[cm_DSP_GetState(coreId)->domainEE];
+ domainShared = &domainDesc[srcShared];
+ if ((SEGMENT_START(domainEE->domain.esramCode) < SEGMENT_START(domainShared->domain.esramCode))
+ ||(SEGMENT_END(domainEE->domain.esramCode) > SEGMENT_END(domainShared->domain.esramCode))
+ ||(SEGMENT_START(domainEE->domain.esramData) < SEGMENT_START(domainShared->domain.esramData))
+ ||(SEGMENT_END(domainEE->domain.esramData) > SEGMENT_END(domainShared->domain.esramData))
+ ||(SEGMENT_START(domainEE->domain.sdramCode) < SEGMENT_START(domainShared->domain.sdramCode))
+ ||(SEGMENT_END(domainEE->domain.sdramCode) > SEGMENT_END(domainShared->domain.sdramCode))
+ ||(SEGMENT_START(domainEE->domain.sdramData) < SEGMENT_START(domainShared->domain.sdramData))
+ ||(SEGMENT_END(domainEE->domain.sdramData) > SEGMENT_END(domainShared->domain.sdramData))
+ ) {
+ return CM_INVALID_PARAMETER;
+ }
+
+ info->coreId = coreId;
+ cm_DM_GetDomainAbsAdresses(srcShared, &domainInfoShared);
+ cm_DM_GetDomainAbsAdresses(src, &domainInfoSrc);
+
+ if ((error = _cm_migration_initSegment(SDRAM_CODE_EE, &domainInfoShared.sdramCode,
+ domainDesc[srcShared].domain.sdramCode.size, dst, info)) != CM_OK)
+ goto _migration_error1;
+ if ((error = _cm_migration_initSegment(SDRAM_CODE_USER, &domainInfoSrc.sdramCode,
+ domainDesc[src].domain.sdramCode.size, dst, info)) != CM_OK)
+ goto _migration_error2;
+ if ((error = _cm_migration_initSegment(SDRAM_DATA_EE, &domainInfoShared.sdramData,
+ domainDesc[srcShared].domain.sdramData.size, dst, info)) != CM_OK)
+ goto _migration_error3;
+ if ((error = _cm_migration_initSegment(SDRAM_DATA_USER, &domainInfoSrc.sdramData,
+ domainDesc[src].domain.sdramData.size, dst, info)) != CM_OK)
+ goto _migration_error4;
+ return error;
+
+_migration_error4: _cm_migration_releaseSegment(info, SDRAM_DATA_EE);
+_migration_error3: _cm_migration_releaseSegment(info, SDRAM_CODE_USER);
+_migration_error2: _cm_migration_releaseSegment(info, SDRAM_CODE_EE);
+_migration_error1:
+ OSAL_Log("Couldn't allocate memory for migration\n", 0, 0, 0, 0, 0, 0);
+ return CM_NO_MORE_MEMORY;
+}
+
+typedef t_cm_error (*updateBase_t)(t_nmf_core_id, t_dsp_segment_type, t_cm_system_address, t_cm_system_address);
+
+static t_cm_migration_error _cm_migration_move(
+ t_nmf_core_id coreId,
+ t_cm_migration_segment *seg,
+ updateBase_t updateBase,
+ char* name
+ )
+{
+ LOG_INTERNAL(1, "##### Migration %s: 0x%x -> 0x%x\n", name, seg->srcAdr.logical, seg->dstAdr.logical, 0, 0, 0);
+ cm_MemCopy((void*)seg->dstAdr.logical, (void*)seg->srcAdr.logical, seg->size);
+ updateBase(coreId, seg->segment, seg->srcAdr, seg->dstAdr);
+ cm_MemSet((void*)seg->srcAdr.logical, 0xdead, seg->size); //for debug, to be sure that we have actually moved the code and bases
+
+ return CM_MIGRATION_OK;
+}
+
+static t_cm_migration_error _cm_migration_update_internal(
+ t_cm_migration_internal_state *info,
+ t_cm_migration_state state
+ )
+{
+ t_nmf_fifo_arm_desc *pArmFifo;
+
+ migrationState.state = state;
+
+ switch(state) {
+ case STATE_MIGRATED:
+ //move fifos
+ pArmFifo = mpc2mpcComsFifoId[ARM_CORE_ID][info->coreId];
+ pArmFifo->fifoDesc = (t_nmf_fifo_desc*)cm_migration_translate(pArmFifo->dspAddressInfo.segmentType, (t_shared_addr)pArmFifo->fifoDescShadow);
+ pArmFifo = mpc2mpcComsFifoId[info->coreId][ARM_CORE_ID];
+ pArmFifo->fifoDesc = (t_nmf_fifo_desc*)cm_migration_translate(pArmFifo->dspAddressInfo.segmentType, (t_shared_addr)pArmFifo->fifoDescShadow);
+ break;
+
+ case STATE_NORMAL:
+ //move fifos
+ pArmFifo = mpc2mpcComsFifoId[ARM_CORE_ID][info->coreId];
+ pArmFifo->fifoDesc = pArmFifo->fifoDescShadow;
+ pArmFifo = mpc2mpcComsFifoId[info->coreId][ARM_CORE_ID];
+ pArmFifo->fifoDesc = pArmFifo->fifoDescShadow;
+ break;
+
+ default:
+ OSAL_Log("unknown state", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+
+ return CM_MIGRATION_OK;
+}
+
+PUBLIC t_cm_error cm_migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst)
+{
+ t_cm_migration_error mError;
+ t_cm_error error;
+
+ if ((error = _cm_migration_check(srcShared, src, dst, &migrationState)) != CM_OK) {
+ return error;
+ }
+
+ /* stop DSP execution */
+ cm_DSP_Stop(migrationState.coreId);
+
+ /* migrate EE and FX */
+ mError = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_CODE_EE], cm_DSP_updateCodeBase, "code");
+ if (mError) {
+ OSAL_Log("EE code migration failed", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+ mError = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_DATA_EE], cm_DSP_updateDataBase, "data");
+ if (mError) {
+ OSAL_Log("EE data migration failed", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+ /* migrate user domain */
+ mError = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_CODE_USER], cm_DSP_updateCodeBase, "code");
+ if (mError) {
+ OSAL_Log("User code migration failed", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+ mError = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_DATA_USER], cm_DSP_updateDataBase, "data");
+ if (mError) {
+ OSAL_Log("User data migration failed", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+ /* update CM internal structures */
+ mError = _cm_migration_update_internal(&migrationState, STATE_MIGRATED);
+ if (mError) {
+ OSAL_Log("Update internal data failed", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+
+ /* Be sure everything has been write before restarting mmdsp */
+ OSAL_mb();
+
+ /* resume DSP execution */
+ cm_DSP_Start(migrationState.coreId);
+
+ return CM_OK;
+}
+
+static void _cm_migration_swapSegments(
+ t_cm_migration_segment *segment
+ )
+{
+ t_cm_system_address tmp;
+ tmp = segment->dstAdr;
+ segment->dstAdr = segment->srcAdr;
+ segment->srcAdr = tmp;
+}
+
+PUBLIC t_cm_error cm_unmigrate(void)
+{
+ t_cm_migration_error merror;
+
+ if (migrationState.state != STATE_MIGRATED)
+ return CM_INVALID_PARAMETER; //TODO, juraj, define a proper error for this migration case
+
+ cm_DSP_Stop(migrationState.coreId);
+
+ _cm_migration_swapSegments(&migrationState.segments[SDRAM_CODE_EE]);
+ _cm_migration_swapSegments(&migrationState.segments[SDRAM_DATA_EE]);
+ _cm_migration_swapSegments(&migrationState.segments[SDRAM_CODE_USER]);
+ _cm_migration_swapSegments(&migrationState.segments[SDRAM_DATA_USER]);
+
+ merror = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_CODE_EE], cm_DSP_updateCodeBase, "code");
+ if (merror) {
+ OSAL_Log("EE code unmigration failed", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+ merror = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_DATA_EE], cm_DSP_updateDataBase, "data");
+ if (merror) {
+ OSAL_Log("EE data unmigration failed", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+ merror = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_CODE_USER], cm_DSP_updateCodeBase, "code");
+ if (merror) {
+ OSAL_Log("User code unmigration failed", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+ merror = _cm_migration_move(migrationState.coreId, &migrationState.segments[SDRAM_DATA_USER], cm_DSP_updateDataBase, "data");
+ if (merror) {
+ OSAL_Log("User data unmigration failed", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+
+ /* update CM internal structures */
+ merror = _cm_migration_update_internal(&migrationState, STATE_NORMAL);
+ if (merror) {
+ OSAL_Log("Update internal data failed", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+
+ /* Be sure everything has been write before restarting mmdsp */
+ OSAL_mb();
+
+ cm_DSP_Start(migrationState.coreId);
+
+ /* update CM internal structures */
+ merror = _cm_migration_release(&migrationState);
+ if (merror) {
+ OSAL_Log("Update internal data failed", 0, 0, 0, 0, 0, 0);
+ CM_ASSERT(0);
+ }
+
+ return CM_OK;
+}
+
+// here we make the assumption that the offset doesn't depend from the dsp!!
+PUBLIC t_uint32 cm_migration_translate(t_dsp_segment_type segmentType, t_uint32 addr)
+{
+ //TODO, juraj, save delta instead of recalculating it
+ t_sint32 offset;
+ if (migrationState.state == STATE_MIGRATED) {
+ offset = migrationState.segments[segmentType].dstAdr.logical - migrationState.segments[segmentType].srcAdr.logical;
+ } else {
+ offset = 0;
+ }
+ return addr + offset;
+}
+
+PUBLIC void cm_migration_check_state(t_nmf_core_id coreId, t_cm_migration_state expected)
+{
+ CM_ASSERT(migrationState.state == expected);
+}
+
+#else
+PUBLIC t_cm_error cm_migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst)
+{
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_unmigrate(void)
+{
+ return CM_OK;
+}
+
+PUBLIC t_uint32 cm_migration_translate(t_dsp_segment_type segmentType, t_uint32 addr)
+{
+ return addr;
+}
+
+PUBLIC void cm_migration_check_state(t_nmf_core_id coreId, t_cm_migration_state expected)
+{
+ return;
+}
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator.c b/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator.c
new file mode 100644
index 00000000000..3df552aca4d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator.c
@@ -0,0 +1,680 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ * Include
+ */
+#include "../inc/remote_allocator.h"
+#include "../inc/remote_allocator_utils.h"
+#include "../inc/chunk_mgr.h"
+
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/trace/inc/xtitrace.h>
+
+static t_cm_chunk* cm_MM_RA_getLastChunk(t_cm_allocator_desc* alloc);
+static void cm_MM_RA_checkAllocator(t_cm_allocator_desc* alloc);
+//static void cm_MM_RA_checkAlloc(t_cm_allocator_desc* alloc, t_uint32 size, t_uint32 align, t_uint32 min, t_uint32 max);
+
+int bin_index(unsigned int sz) {
+ /*
+ * 32 bins of size 2
+ * 16 bins of size 16
+ * 8 bins of size 128
+ * 4 bins of size 1024
+ * 2 bins of size 8192
+ * 1 bin of size what's left
+ *
+ */
+ return (((sz >> 6) == 0) ? (sz >> 1): // 0 -> 0 .. 31
+ ((sz >> 6) <= 4) ? 28 + (sz >> 4): // 64 -> 32 .. 47
+ ((sz >> 6) <= 20) ? 46 + (sz >> 7): // 320 -> 48 .. 55
+ ((sz >> 6) <= 84) ? 55 + (sz >> 10): // 1344 -> 56 .. 59
+ ((sz >> 6) <= 340) ? 59 + (sz >> 13): // 5440 -> 60 .. 61
+ 62); // 21824..
+}
+
+static t_cm_allocator_desc* ListOfAllocators = NULL;
+
+PUBLIC t_cm_allocator_desc* cm_MM_CreateAllocator(t_cm_size size, t_uint32 offset, const char* name)
+{
+ t_cm_allocator_desc *alloc;
+
+ CM_ASSERT(fillChunkPool() == CM_OK);
+
+ /* Alloc structure */
+ alloc = (t_cm_allocator_desc*)OSAL_Alloc_Zero(sizeof(t_cm_allocator_desc));
+ CM_ASSERT(alloc != NULL);
+
+ // Add allocator in list
+ alloc->next = ListOfAllocators;
+ ListOfAllocators = alloc;
+
+ /* Create first chunk */
+ alloc->chunks = allocChunk();
+
+ /* assign name */
+ alloc->pAllocName = name;
+
+ alloc->chunks->size = size;
+ alloc->chunks->offset = offset;
+ alloc->chunks->alloc = alloc;
+ alloc->free_mem_chunks[bin_index(alloc->chunks->size)] = alloc->chunks;
+
+ alloc->size = size;
+
+ //TODO, juraj, alloc impacts trace format
+ cm_TRC_traceMemAlloc(TRACE_ALLOCATOR_COMMAND_CREATE, 0, size, name);
+
+ return alloc;
+}
+
+PUBLIC t_cm_error cm_MM_DeleteAllocator(t_cm_allocator_desc *alloc)
+{
+ t_cm_chunk *chunk, *next_cm_chunk;
+
+ cm_TRC_traceMemAlloc(TRACE_ALLOCATOR_COMMAND_DESTROY, 0, 0, alloc->pAllocName);
+
+ /* Parse all chunks and free them */
+ chunk = alloc->chunks;
+ while(chunk != 0)
+ {
+ next_cm_chunk = chunk->next;
+ unlinkChunk(alloc, chunk);
+ freeChunk(chunk);
+
+ chunk = next_cm_chunk;
+ }
+
+ // Remove allocator from the list
+ if(ListOfAllocators == alloc)
+ ListOfAllocators = alloc->next;
+ else {
+ t_cm_allocator_desc *prev = ListOfAllocators;
+ while(prev->next != alloc)
+ prev = prev->next;
+ prev->next = alloc->next;
+ }
+
+
+ /* Free allocator descriptor */
+ OSAL_Free(alloc);
+
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_MM_ResizeAllocator(t_cm_allocator_desc *alloc, t_cm_size size)
+{
+ t_cm_error error;
+
+ /* sanity check */
+ if (size == 0)
+ return CM_INVALID_PARAMETER;
+
+ if((error = fillChunkPool()) != CM_OK)
+ return error;
+
+ if (size > alloc->size) {
+ /* ok, increase allocator */
+ t_uint32 deltaSize = size - alloc->size;
+ t_cm_chunk *last = cm_MM_RA_getLastChunk(alloc);
+
+ if (last->status == MEM_FREE) {
+ /* last chunk is a free one, just increase size */
+ unlinkFreeMem(alloc, last);
+ last->size += deltaSize;
+ alloc->size += deltaSize;
+ /* now list of free chunk is potentially no more ordered */
+ updateFreeList(alloc, last);
+ } else {
+ /* last chunk is a used one, add new free chunk */
+ last->size += deltaSize;
+ splitChunk(alloc, last, last->offset + deltaSize, FREE_CHUNK_AFTER);
+ alloc->size += deltaSize;
+ }
+ } else {
+ /* reduce allocator */
+ t_uint32 deltaSize = alloc->size - size;
+ t_cm_chunk *last = cm_MM_RA_getLastChunk(alloc);
+
+ /* check if resize is possible */
+ if (last->status == MEM_USED)
+ return CM_NO_MORE_MEMORY;
+ if (last->size < deltaSize)
+ return CM_NO_MORE_MEMORY;
+
+ /* ok, rezise can be performed */
+ if (last->size == deltaSize) {
+ t_cm_chunk *prev = last->prev;
+
+ /* remove last free chunk */
+ mergeChunk(alloc, prev, last);
+ prev->size -= deltaSize;
+ } else {
+ unlinkFreeMem(alloc, last);
+ /* reduce size of last free chunk */
+ last->size -= deltaSize;
+ /* now list of free chunk is potentially no more ordered */
+ updateFreeList(alloc, last);
+ }
+ }
+
+ alloc->size = size;
+
+ if (cmIntensiveCheckState) {
+ cm_MM_RA_checkAllocator(alloc);
+ }
+
+ return CM_OK;
+}
+
+t_cm_error cm_MM_getValidMemoryHandle(t_cm_memory_handle handle, t_memory_handle* validHandle)
+{
+#ifdef LINUX
+ /* On linux, there is already a check within the linux part
+ * => we don't need to check twice */
+ *validHandle = (t_memory_handle)handle;
+ return CM_OK;
+#else
+ t_cm_allocator_desc *alloc = ListOfAllocators;
+
+ for(; alloc != NULL; alloc = alloc->next)
+ {
+ t_cm_chunk* chunk = alloc->chunks;
+
+ /* Parse all chunks */
+ for(; chunk != NULL; chunk = chunk->next)
+ {
+ if(chunk == (t_memory_handle)handle)
+ {
+ if(chunk->status == MEM_FREE)
+ return CM_MEMORY_HANDLE_FREED;
+
+ *validHandle = (t_memory_handle)handle;
+
+ return CM_OK;
+ }
+ }
+ }
+
+ return CM_UNKNOWN_MEMORY_HANDLE;
+#endif
+}
+
+//TODO, juraj, add appartenance to allocHandle (of chunk) and degage setUserData
+PUBLIC t_memory_handle cm_MM_Alloc(
+ t_cm_allocator_desc* alloc,
+ t_cm_size size,
+ t_cm_memory_alignment memAlignment,
+ t_uint32 seg_offset,
+ t_uint32 seg_size,
+ t_uint32 domainId)
+{
+ t_cm_chunk* chunk;
+ t_uint32 aligned_offset;
+ t_uint32 aligned_end;
+ t_uint32 seg_end = seg_offset + seg_size;
+ int i;
+
+ /* Sanity check */
+ if ( (size == 0) || (size > seg_size) )
+ return INVALID_MEMORY_HANDLE;
+
+ if(fillChunkPool() != CM_OK)
+ return INVALID_MEMORY_HANDLE;
+
+ /* Get first chunk available for the specific size */
+ // Search a list with a free chunk
+ for(i = bin_index(size); i < BINS; i++)
+ {
+ chunk = alloc->free_mem_chunks[i];
+ while (chunk != 0)
+ {
+ /* Alignment of the lower boundary */
+ aligned_offset = ALIGN_VALUE(MAX(chunk->offset, seg_offset), (memAlignment + 1));
+
+ aligned_end = aligned_offset + size;
+
+ if ((aligned_end <= seg_end)
+ && aligned_end <= (chunk->offset + chunk->size)
+ && aligned_offset >= seg_offset
+ && aligned_offset >= chunk->offset)
+ goto found;
+
+ chunk = chunk->next_free_mem;
+ }
+ }
+
+ return INVALID_MEMORY_HANDLE;
+
+found:
+
+ /* Remove chunk from free list */
+ unlinkFreeMem(alloc, chunk);
+
+ //create an empty chunk before the allocated one
+ if (chunk->offset < aligned_offset) {
+ chunk = splitChunk(alloc, chunk, aligned_offset, FREE_CHUNK_BEFORE);
+ }
+ //create an empty chunk after the allocated one
+ if (chunk->offset + chunk->size > aligned_end) {
+ splitChunk(alloc, chunk, aligned_end, FREE_CHUNK_AFTER);
+ }
+
+ chunk->status = MEM_USED;
+ chunk->prev_free_mem = 0;
+ chunk->next_free_mem = 0;
+ chunk->domainId = domainId;
+
+ //TODO, juraj, alloc impacts trace format
+ cm_TRC_traceMem(TRACE_ALLOC_COMMAND_ALLOC, 0, chunk->offset, chunk->size);
+
+ if (cmIntensiveCheckState) {
+ cm_MM_RA_checkAllocator(alloc);
+ }
+
+ chunk->alloc = alloc;
+ return (t_memory_handle) chunk;
+}
+
+//caution - if successfull, the chunk offset will be aligned with seg_offset
+//caution++ the offset of the allocated chunk changes implicitly
+PUBLIC t_memory_handle cm_MM_Realloc(
+ t_cm_allocator_desc* alloc,
+ const t_cm_size size,
+ const t_uint32 offset,
+ const t_cm_memory_alignment memAlignment,
+ const t_memory_handle handle)
+{
+ t_cm_chunk *chunk = (t_cm_chunk*)handle;
+
+ /* check reallocation is related to this chunk! */
+ CM_ASSERT(chunk->offset <= (offset + size));
+ CM_ASSERT(offset <= (chunk->offset + chunk->size));
+ CM_ASSERT(size);
+
+ /* check if extend low */
+ if (offset < chunk->offset) {
+ /* note: it is enough to check only the previous chunk,
+ * because adjacent chunks of same status are merged
+ */
+ if ((chunk->prev == 0)
+ ||(chunk->prev->status != MEM_FREE)
+ ||(chunk->prev->offset > offset)) {
+ return INVALID_MEMORY_HANDLE;
+ }
+ }
+
+ /* check if extend high, note as above */
+ if ( (offset + size) > (chunk->offset + chunk->size)) {
+ if ((chunk->next == 0)
+ ||(chunk->next->status != MEM_FREE)
+ ||( (chunk->next->offset + chunk->next->size) < (offset + size))) {
+ return INVALID_MEMORY_HANDLE;
+ }
+ }
+
+ if(fillChunkPool() != CM_OK)
+ return INVALID_MEMORY_HANDLE;
+
+
+#if 0
+ /* extend low
+ * all conditions should have been checked
+ * this must not fail
+ */
+ if (offset < chunk->offset) {
+ t_cm_chunk *tmp = splitChunk(alloc, chunk->prev, offset, FREE_CHUNK_BEFORE); //tmp = chunk->prev
+ CM_ASSERT(tmp);
+ tmp->status = MEM_USED;
+ tmp->prev->status = MEM_FREE;
+ mergeChunk(alloc, tmp, chunk);
+ if ((tmp->prev->prev != 0)
+ && (tmp->prev->prev->status == MEM_FREE)) {
+ mergeChunk(alloc, tmp->prev->prev, tmp->prev);
+ }
+ chunk = tmp;
+ }
+
+ /* extend high */
+ if ( (offset + size) > (chunk->offset + chunk->size)) {
+ t_cm_chunk *tmp = splitChunk(alloc, chunk->next, offset + size, FREE_CHUNK_AFTER); //tmp = chunk->next->next
+ CM_ASSERT(tmp);
+ tmp->status = MEM_USED;
+ mergeChunk(alloc, chunk, tmp);
+ if ((tmp->next->next != 0)
+ && (tmp->next->next->status == MEM_FREE)) {
+ mergeChunk(alloc, tmp->next, tmp->next->next);
+ }
+ }
+
+ /* reduce top */
+ if ((offset + size) < (chunk->offset + chunk->size)) {
+ t_cm_chunk *tmp = splitChunk(alloc, chunk, offset + size, FREE_CHUNK_AFTER); //tmp = chunk, chunk = result
+ CM_ASSERT(tmp);
+ tmp->status = MEM_USED;
+ tmp->next->status = MEM_FREE;
+ if ((tmp->next->next != 0)
+ && (tmp->next->next->status == MEM_FREE)) {
+ mergeChunk(alloc, tmp->next, tmp->next->next);
+ }
+ }
+
+ /* reduce bottom */
+ if (offset > chunk->offset) {
+ t_cm_chunk *tmp = splitChunk(alloc, chunk, offset, FREE_CHUNK_BEFORE); //tmp->next = chunk, tmp = result
+ CM_ASSERT(tmp);
+ tmp->status = MEM_USED;
+ tmp->prev->status = MEM_FREE;
+ if ((tmp->prev->prev != 0)
+ &&(tmp->prev->prev->status == MEM_FREE)) {
+ mergeChunk(alloc, tmp->prev->prev, tmp->prev);
+ }
+ chunk = tmp;
+ }
+#else
+ /* extend low
+ * all conditions should have been checked
+ * this must not fail
+ */
+ if (offset < chunk->offset) {
+ t_uint32 delta = chunk->prev->offset + chunk->prev->size - offset;
+ CM_ASSERT(chunk->prev->status == MEM_FREE); //TODO, juraj, already checked
+ unlinkFreeMem(alloc, chunk->prev);
+ chunk->prev->size -= delta;
+ chunk->offset -= delta;
+ chunk->size += delta;
+ updateFreeList(alloc, chunk->prev);
+ }
+
+ /* extend high */
+ if ( (offset + size) > (chunk->offset + chunk->size)) {
+ t_uint32 delta = size - chunk->size;
+ CM_ASSERT(chunk->next->status == MEM_FREE); //TODO, juraj, already checked
+ unlinkFreeMem(alloc, chunk->next);
+ chunk->size += delta;
+ chunk->next->offset += delta;
+ chunk->next->size -= delta;
+ updateFreeList(alloc, chunk->next);
+ }
+
+ /* reduce top */
+ if ((offset + size) < (chunk->offset + chunk->size)) {
+ if (chunk->next->status == MEM_FREE) {
+ t_uint32 delta = chunk->size - size;
+ unlinkFreeMem(alloc, chunk->next);
+ chunk->size -= delta;
+ chunk->next->offset -= delta;
+ chunk->next->size += delta;
+ updateFreeList(alloc, chunk->next);
+ } else {
+ t_cm_chunk *tmp = splitChunk(alloc, chunk, offset + size, FREE_CHUNK_AFTER); //tmp = chunk, chunk = result
+ tmp->status = MEM_USED;
+ tmp->next->status = MEM_FREE;
+ }
+ }
+
+ /* reduce bottom */
+ if (offset > chunk->offset) {
+ if (chunk->prev->status == MEM_FREE) {
+ t_uint32 delta = offset - chunk->offset;
+ unlinkFreeMem(alloc, chunk->prev);
+ chunk->prev->size += delta;
+ chunk->offset = offset;
+ chunk->size -= delta;
+ updateFreeList(alloc, chunk->prev);
+ } else {
+ t_cm_chunk *tmp = splitChunk(alloc, chunk, offset, FREE_CHUNK_BEFORE); //tmp->next = chunk, tmp = result
+ tmp->status = MEM_USED;
+ tmp->prev->status = MEM_FREE;
+ }
+ }
+#endif
+ cm_MM_RA_checkAllocator(alloc);
+
+ return (t_memory_handle)chunk;
+}
+
+PUBLIC void cm_MM_Free(t_cm_allocator_desc* alloc, t_memory_handle memHandle)
+{
+ t_cm_chunk* chunk = (t_cm_chunk*)memHandle;
+
+ //TODO, juraj, alloc impacts trace format
+ cm_TRC_traceMem(TRACE_ALLOC_COMMAND_FREE, 0,
+ chunk->offset, chunk->size);
+
+ /* Update chunk status */
+ chunk->status = MEM_FREE;
+ chunk->domainId = 0x0;
+
+ /* Check if the previous chunk is free */
+ if((chunk->prev != 0) && (chunk->prev->status == MEM_FREE)) {
+ chunk = chunk->prev; //chunk, ie. chunk->next, will be freed
+ mergeChunk(alloc, chunk, chunk->next);
+ }
+
+ /* Check if the next chunk is free */
+ if((chunk->next != 0) && (chunk->next->status == MEM_FREE)) {
+ mergeChunk(alloc, chunk, chunk->next);
+ }
+
+ unlinkFreeMem(alloc, chunk);
+ updateFreeList(alloc, chunk);
+
+ if (cmIntensiveCheckState) {
+ cm_MM_RA_checkAllocator(alloc);
+ }
+}
+
+PUBLIC t_cm_error cm_MM_GetAllocatorStatus(t_cm_allocator_desc* alloc, t_uint32 offset, t_uint32 size, t_cm_allocator_status *pStatus)
+{
+ t_cm_chunk* chunk = alloc->chunks;
+ t_uint8 min_free_size_updated = FALSE;
+
+ /* Init status */
+ pStatus->global.used_block_number = 0;
+ pStatus->global.free_block_number = 0;
+ pStatus->global.maximum_free_size = 0;
+ pStatus->global.minimum_free_size = 0xFFFFFFFF;
+ pStatus->global.accumulate_free_memory = 0;
+ pStatus->global.accumulate_used_memory = 0;
+ pStatus->global.size = alloc->size;
+ pStatus->domain.maximum_free_size = 0;
+ pStatus->domain.minimum_free_size = 0xFFFFFFFF;
+ pStatus->domain.accumulate_free_memory = 0;
+ pStatus->domain.accumulate_used_memory = 0;
+ pStatus->domain.size= size;
+
+ //TODO, juraj, get allocator status for a domain
+ /* Parse all chunks */
+ while(chunk != 0)
+ {
+
+ /* Chunk is free */
+ if (chunk->status == MEM_FREE) {
+ pStatus->global.free_block_number++;
+ pStatus->global.accumulate_free_memory += chunk->size;
+
+ /* Check max size */
+ if (chunk->size > pStatus->global.maximum_free_size)
+ {
+ pStatus->global.maximum_free_size = chunk->size;
+ }
+
+ /* Check min size */
+ if (chunk->size < pStatus->global.minimum_free_size)
+ {
+ pStatus->global.minimum_free_size = chunk->size;
+ min_free_size_updated = TRUE;
+ }
+ } else {/* Chunk used */
+ pStatus->global.used_block_number++;
+ pStatus->global.accumulate_used_memory += chunk->size;
+ }
+
+ chunk = chunk->next;
+ }
+
+ /* Put max free size to min free size */
+ if (min_free_size_updated == FALSE) {
+ pStatus->global.minimum_free_size = pStatus->global.maximum_free_size;
+ }
+
+ return CM_OK;
+}
+
+PUBLIC t_uint32 cm_MM_GetOffset(t_memory_handle memHandle)
+{
+ /* Provide offset */
+ return ((t_cm_chunk*)memHandle)->offset;
+}
+
+PUBLIC t_uint32 cm_MM_GetSize(t_memory_handle memHandle)
+{
+ return ((t_cm_chunk*)memHandle)->size;
+}
+
+PUBLIC t_uint32 cm_MM_GetAllocatorSize(t_cm_allocator_desc* alloc)
+{
+ return alloc->size;
+}
+
+PUBLIC void cm_MM_SetMemoryHandleUserData(t_memory_handle memHandle, t_uint16 userData)
+{
+ ((t_cm_chunk*)memHandle)->userData = userData;
+}
+
+PUBLIC void cm_MM_GetMemoryHandleUserData(t_memory_handle memHandle, t_uint16 *pUserData, t_cm_allocator_desc **alloc)
+{
+ *pUserData = ((t_cm_chunk*)memHandle)->userData;
+ if (alloc)
+ *alloc = ((t_cm_chunk*)memHandle)->alloc;
+}
+
+/*
+ * check free list is ordered
+ * check all chunks are correctly linked
+ * check adjacent chunks are not FREE
+ */
+static void cm_MM_RA_checkAllocator(t_cm_allocator_desc* alloc)
+{
+ t_cm_chunk *chunk = alloc->chunks;
+ t_cm_chunk *first = chunk;
+ t_cm_chunk *last = chunk;
+ t_uint32 size = 0;
+ int i;
+
+ while(chunk != 0) {
+ CM_ASSERT(chunk->alloc == alloc);
+
+ if (chunk->next != 0) {
+ CM_ASSERT(!((chunk->status == MEM_FREE) && (chunk->next->status == MEM_FREE))); //two free adjacent blocks
+ CM_ASSERT(chunk->offset < chunk->next->offset); //offsets reverted
+ last = chunk->next;
+ }
+ size += chunk->size;
+ chunk = chunk->next;
+ }
+
+ CM_ASSERT(size == alloc->size);
+
+ for(i = 0; i < BINS; i++)
+ {
+ chunk = alloc->free_mem_chunks[i];
+ while(chunk != 0) {
+ if (chunk->next_free_mem != 0) {
+ CM_ASSERT(chunk->size <= chunk->next_free_mem->size); //free list not ordered
+ }
+ CM_ASSERT(!(chunk->prev == 0 && (chunk != first))); //chunk not linked properly
+ CM_ASSERT(!(chunk->next == 0 && (chunk != last))); //chunk not linked property
+ chunk = chunk->next_free_mem;
+ }
+ }
+}
+
+#if 0
+static void cm_MM_RA_checkAlloc(t_cm_allocator_desc* alloc, t_uint32 size, t_uint32 align, t_uint32 min, t_uint32 max)
+{
+ t_cm_chunk *chunk = alloc->chunks;
+
+ while(chunk != 0) {
+ if (chunk->status == MEM_USED) {
+ chunk = chunk->next;
+ continue;
+ }
+ if (chunk->size < size) {
+ chunk = chunk->next;
+ continue;
+ }
+
+ if (min < chunk->offset) {
+ t_uint32 aligned_offset = ALIGN_VALUE(chunk->offset, align + 1);
+ t_uint32 aligned_end = aligned_offset + size;
+ if ((aligned_offset + size <= chunk->offset + chunk->size)
+ && (chunk->offset + chunk->size <= aligned_end)){
+ break;
+ }
+ }
+
+ if (min >= chunk->offset) {
+ t_uint32 aligned_offset = ALIGN_VALUE(min, align + 1);
+ t_uint32 aligned_end = aligned_offset + size;
+ if ((aligned_offset + size <= chunk->offset + chunk->size)
+ && (chunk->offset + chunk->size <= aligned_end)) {
+ break;
+ }
+ }
+
+ chunk = chunk->next;
+ }
+
+ CM_ASSERT(chunk == 0);
+}
+#endif
+
+/***************************************************************************/
+/*
+ * cm_mm_ra_getLastChunk
+ * param handle : Handle of the allocator
+ * return : last
+ *
+ * Free all chunk in the allocator
+ * Free allocator descriptor
+ *
+ */
+/***************************************************************************/
+static t_cm_chunk* cm_MM_RA_getLastChunk(t_cm_allocator_desc* alloc)
+{
+ t_cm_chunk* pChunk = alloc->chunks;
+
+ while(pChunk->next != 0) {pChunk = pChunk->next;}
+
+ return pChunk;
+}
+
+PUBLIC void cm_MM_DumpMemory(t_cm_allocator_desc* alloc, t_uint32 start, t_uint32 end)
+{
+ t_cm_chunk *chunk = alloc->chunks;
+
+ LOG_INTERNAL(0, "ALLOCATOR Dumping allocator \"%s\" [0x%08x:0x%08x]\n", alloc->pAllocName, start, end, 0, 0, 0);
+ while(chunk != 0) {
+ if (((chunk->offset < start) && (chunk->offset + chunk->size > start))
+ || ((chunk->offset < end) && (chunk->offset + chunk->size > end))
+ || ((chunk->offset > start) && (chunk->offset + chunk->size < end))
+ || ((chunk->offset < start) && (chunk->offset + chunk->size > end)))
+ {
+ LOG_INTERNAL(0, "ALLOCATOR chunk 0x%08x -> 0x%08x: status:%s, domainId: 0x%x\n",
+ chunk->offset,
+ chunk->offset + chunk->size,
+ chunk->status?"FREE":"USED",
+ chunk->domainId, 0, 0);
+ }
+ chunk = chunk->next;
+ }
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator_utils.c b/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator_utils.c
new file mode 100644
index 00000000000..5c19d3e9b5e
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/memory/src/remote_allocator_utils.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/memory/inc/remote_allocator_utils.h>
+
+/***************************************************************************/
+/*
+ * linkChunk
+ * param prev : Pointer on previous chunk where the chunk will be added
+ * param add : Pointer on chunk to add
+ *
+ * Add a chunk in the memory list
+ *
+ */
+/***************************************************************************/
+PUBLIC void linkChunk(t_cm_chunk* prev, t_cm_chunk* add)
+{
+ /* Link previous */
+ add->prev = prev;
+ add->next = prev->next;
+
+ /* Link next */
+ if (prev->next != 0)
+ {
+ prev->next->prev = add;
+ }
+ prev->next = add;
+}
+
+/***************************************************************************/
+/*
+ * unlinkChunk
+ * param allocHandle : Allocator handle
+ * param current : Pointer on chunk to remove
+ *
+ * Remove a chunk in the memory list and update first pointer
+ *
+ */
+/***************************************************************************/
+PUBLIC void unlinkChunk(t_cm_allocator_desc* alloc ,t_cm_chunk* current)
+{
+ /* Unlink previous */
+ if (current->prev !=0)
+ {
+ current->prev->next = current->next;
+ }
+
+ /* Unlink next */
+ if (current->next !=0)
+ {
+ current->next->prev= current->prev;
+ }
+
+ /* Update first pointer */
+ if (alloc ->chunks == current)
+ {
+ alloc ->chunks = current->next;
+ }
+}
+
+
+/***************************************************************************/
+/*
+ * unlinkFreeMem() unlinks chunk from free memory double-linked list
+ * makes the previous and next chunk in the list point to each other..
+ * param allocHandle : Allocator handle
+ * param current : Pointer on chunk to remove
+ *
+ * Remove a chunk in the free memory list and update pointer
+ *
+ */
+/***************************************************************************/
+PUBLIC void unlinkFreeMem(t_cm_allocator_desc* alloc ,t_cm_chunk* current)
+{
+ int bin = bin_index(current->size);
+
+ /* unlink previous */
+ if (current->prev_free_mem != 0)
+ {
+ current->prev_free_mem->next_free_mem = current->next_free_mem;
+ }
+
+ /* Unlink next */
+ if (current->next_free_mem !=0 )
+ {
+ current->next_free_mem->prev_free_mem = current->prev_free_mem;
+ }
+
+ /* update first free pointer */
+ if (alloc->free_mem_chunks[bin] == current)
+ {
+ alloc->free_mem_chunks[bin] = current->next_free_mem;
+ }
+
+ current->prev_free_mem = 0;
+ current->next_free_mem = 0;
+}
+
+/***************************************************************************/
+/*
+ * linkFreeMemBefore
+ * param add : Pointer on chunk to add
+ * param next : Pointer on next chunk where the chunk will be added before
+ *
+ * Add a chunk in the free memory list
+ *
+ */
+/***************************************************************************/
+PUBLIC void linkFreeMemBefore(t_cm_chunk* add, t_cm_chunk* next)
+{
+ /* Link next */
+ add->prev_free_mem = next->prev_free_mem;
+ add->next_free_mem = next;
+
+ /* Link previous */
+ if (next->prev_free_mem != 0)
+ {
+ next->prev_free_mem->next_free_mem = add;
+ }
+ next->prev_free_mem = add;
+}
+
+/***************************************************************************/
+/*
+ * linkFreeMemAfter
+ * param add : Pointer on chunk to add
+ * param prev : Pointer on previous chunk where the chunk will be added after
+ *
+ * Add a chunk in the free memory list
+ *
+ */
+/***************************************************************************/
+PUBLIC void linkFreeMemAfter(t_cm_chunk* prev,t_cm_chunk* add)
+{
+ /* Link previous */
+ add->prev_free_mem = prev;
+ add->next_free_mem = prev->next_free_mem;
+
+ /* Link next */
+ if (prev->next_free_mem != 0)
+ {
+ prev->next_free_mem->prev_free_mem = add;
+ }
+ prev->next_free_mem = add;
+}
+
+
+/***************************************************************************/
+/*
+ * updateFreeList
+ * param allocHandle : Allocator handle
+ * param offset : Pointer on chunk
+ *
+ * Update free memory list, ordered by size
+ *
+ */
+/***************************************************************************/
+PUBLIC void updateFreeList(t_cm_allocator_desc* alloc , t_cm_chunk* chunk)
+{
+ t_cm_chunk* free_chunk;
+ int bin = bin_index(chunk->size);
+
+ /* check case with no more free block */
+ if (alloc->free_mem_chunks[bin] == 0)
+ {
+ alloc->free_mem_chunks[bin] = chunk;
+ return ;
+ }
+
+ /* order list */
+ free_chunk = alloc->free_mem_chunks[bin];
+ while ((free_chunk->next_free_mem != 0) && (chunk->size > free_chunk->size))
+ {
+ free_chunk = free_chunk->next_free_mem;
+ }
+
+ /* Add after free chunk if smaller -> we are the last */
+ if(free_chunk->size <= chunk->size)
+ {
+ linkFreeMemAfter(free_chunk,chunk);
+ }
+ else // This mean that we are smaller
+ {
+ linkFreeMemBefore(chunk,free_chunk);
+
+ /* Update first free chunk */
+ if (alloc->free_mem_chunks[bin] == free_chunk)
+ {
+ alloc->free_mem_chunks[bin] = chunk;
+ }
+ }
+}
+
+
+/***************************************************************************/
+/*
+ * mergeChunk
+ * param allocHandle : Allocator handle
+ * param merged_chunk : Pointer on merged chunk
+ * param destroy : Pointer on chunk to destroy
+ *
+ * Link and destroy merged chunks
+ *
+ */
+/***************************************************************************/
+PUBLIC void mergeChunk(t_cm_allocator_desc* alloc,t_cm_chunk *merged_chunk, t_cm_chunk *destroy)
+{
+ /* Assign offset to the merged */
+ /* assume chunks ordered!
+ if (merged_chunk->offset > destroy->offset) {
+ merged_chunk->offset = destroy->offset;
+ }
+ */
+
+ /* Remove chunk */
+ unlinkChunk(alloc, destroy);
+ unlinkFreeMem(alloc, destroy);
+
+ if (merged_chunk->status == MEM_FREE)
+ unlinkFreeMem(alloc, merged_chunk);
+
+ /* Update size */
+ merged_chunk->size += destroy->size;
+
+ if (merged_chunk->status == MEM_FREE)
+ updateFreeList(alloc, merged_chunk);
+
+ freeChunk(destroy);
+}
+
+/***************************************************************************/
+/*
+ * splitChunk
+ * param allocHandle : Allocator handle
+ * param chunk : Current chunk (modified in place)
+ * param offset : Offset address of the start memory
+ * return : New chunk handle or 0 if an error occurs
+ *
+ * Create new chunk before/after the current chunk with the size
+ */
+/***************************************************************************/
+PUBLIC t_cm_chunk* splitChunk(t_cm_allocator_desc* alloc ,t_cm_chunk *chunk,
+ t_uint32 offset, t_mem_split_position position)
+{
+ t_cm_chunk *free;
+ t_cm_chunk *returned;
+
+ t_cm_chunk* new_chunk = allocChunk();
+
+ if (position == FREE_CHUNK_AFTER) {
+ returned = chunk;
+ free = new_chunk;
+ } else { //FREE_CHUNK_BEFORE
+ returned = new_chunk;
+ free = chunk;
+ }
+
+ new_chunk->offset = offset;
+ new_chunk->size = chunk->offset + chunk->size - offset;
+ new_chunk->alloc = alloc;
+ chunk->size = offset - chunk->offset;
+
+ linkChunk(chunk, new_chunk);
+ unlinkFreeMem(alloc, free);
+ updateFreeList(alloc, free);
+
+ return returned;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h b/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h
new file mode 100644
index 00000000000..8d95436e796
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \brief OS Adaptation Layer API
+ *
+ * \defgroup CM_ENGINE_OSAL_API CM Engine OSAL (Operating System Abstraction Layer) API
+ * \ingroup CM_ENGINE_MODULE
+ */
+#ifndef __INC_CM_OSAL_H
+#define __INC_CM_OSAL_H
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/communication/inc/communication_type.h>
+
+/*!
+ * \brief Identifier of a trace channel (id in [0..255])
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+typedef t_uint8 t_nmf_trace_channel;
+
+/*!
+ * \brief Identifier of lock create by OSAL
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+typedef t_uint32 t_nmf_osal_sync_handle;
+
+/*!
+ * \brief Identifier of semaphore create by OSAL
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+typedef t_uint32 t_nmf_osal_sem_handle;
+
+/*!
+ * \brief Identifier of semaphore wait error return by semaphore OSAL API
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+typedef t_uint8 t_nmf_osal_sync_error;
+#define SYNC_ERROR_TIMEOUT ((t_nmf_osal_sync_error)-1)
+#define SYNC_OK ((t_nmf_osal_sync_error)0)
+#define SEM_TIMEOUT_NORMAL 3000
+#define SEM_TIMEOUT_DEBUG 300000
+
+/*!
+ * \brief Description of the Scheduling part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Support of uplink communication path (from Media Processors to Host (ARM))
+ *
+ * Post a function call outside of Host CPU Interrupt mode in order to minimize ISR execution time
+ * \param[in] upLayerTHIS : this one provided by user when calling CM_ENGINE_BindComponentToCMCore() (first field of the interface context) \n
+ * \param[in] methodIndex : index method to be called \n
+ * \param[in] anyPtr : internal NMF marshaled parameters block (to be passed as second parameter when calling the previous pSkeleton method) \n
+ * \param[in] ptrSize : size of anyPtr in bytes \n
+ *
+ * Called by:
+ * - CM_ProcessMpcEvent() call (shall be bound by OS integrator to HSEM IRQ)
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+
+PUBLIC void OSAL_PostDfc(
+ t_nmf_mpc2host_handle upLayerTHIS,
+ t_uint32 methodIndex,
+ t_event_params_handle anyPtr,
+ t_uint32 ptrSize);
+
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Use by CM to protect global variable against multiple call. Interrupt and scheduler function are use when
+ * we take hardware/local semaphore. Scheduler lock functions can have empty implementation but this may
+ * impact performance (dsp waiting semaphore because host thread was preempted whereas it has already take semaphore
+ * but not yet release it).
+ *
+ * \return handle of the Mutex created
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC t_nmf_osal_sync_handle OSAL_CreateLock(void);
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Use by CM to protect global variable against multiple call. Interrupt and scheduler function are use when
+ * we take hardware/local semaphore. Scheduler lock functions can have empty implementation but this may
+ * impact performance (dsp waiting semaphore because host thread was preempted whereas it has already take semaphore
+ * but not yet release it).
+ *
+ * \param[in] handle handle of the Mutex to be locked
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_Lock(
+ t_nmf_osal_sync_handle handle);
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Use by CM to protect global variable against multiple call. Interrupt and scheduler function are use when
+ * we take hardware/local semaphore. Scheduler lock functions can have empty implementation but this may
+ * impact performance (dsp waiting semaphore because host thread was preempted whereas it has already take semaphore
+ * but not yet release it).
+ *
+ * \param[in] handle handle of the Mutex to be unlocked
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_Unlock(
+ t_nmf_osal_sync_handle handle);
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Use by CM to protect global variable against multiple call. Interrupt and scheduler function are use when
+ * we take hardware/local semaphore. Scheduler lock functions can have empty implementation but this may
+ * impact performance (dsp waiting semaphore because host thread was preempted whereas it has already take semaphore
+ * but not yet release it).
+ *
+ * \param[in] handle handle of the Mutex to be destroyed
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_DestroyLock(
+ t_nmf_osal_sync_handle handle);
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Use by CM to allow to synchronize with code running on mpc side.
+ *
+ * \param[in] value : Initial value of semaphore.
+ *
+ * \return handle of the Semaphore created
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC t_nmf_osal_sem_handle OSAL_CreateSemaphore(
+ t_uint32 value);
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Use by CM to allow to synchronize with code running on mpc side. This function can be call under
+ * Irq context by CM.
+ *
+ * \param[in] handle handle of the Semaphore for which we increase value and so potentially wake up thread.
+ *
+ * \param[in] aCtx is a hint to indicate to os that we are in a none normal context (e.g under interruption).
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_SemaphorePost(
+ t_nmf_osal_sem_handle handle,
+ t_uint8 aCtx);
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Use by CM to allow to synchronize with code running on mpc side.
+ *
+ * \param[in] handle of the Semaphore for which we decrease value and so potentially block current thread.
+ *
+ * \param[in] timeOutInMs maximun time in ms after which the block thread is wake up. In this case function return SYNC_ERROR_TIMEOUT value.
+ *
+ * \return error number: SYNC_ERROR_TIMEOUT in case semaphore is not release withing timeOutInMs.
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC t_nmf_osal_sync_error OSAL_SemaphoreWaitTimed(
+ t_nmf_osal_sem_handle handle,
+ t_uint32 timeOutInMs);
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Use by CM to allow to synchronize with code running on mpc side.
+ *
+ * \param[in] handle handle of the Semaphore to be destroyed
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_DestroySemaphore(
+ t_nmf_osal_sem_handle handle);
+
+/*!
+ * \brief Description of the System Memory Allocator part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Allocate CM some cacheable and bufferable memory (SDRAM) for internal usage \n
+ * This memory will be accessed only by Host CPU (ARM)
+ *
+ * This function provide a simple, general-purpose memory allocation. The
+ * OSAL_Alloc macro returns a pointer to a block of at least size bytes
+ * suitably aligned for any use. If there is no available memory, this
+ * function returns a null pointer.
+ *
+ * \param[in] size size in bytes, of memory to be allocated
+ * \return pointer on the beginning of the allocated memory
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void* OSAL_Alloc(
+ t_cm_size size);
+
+/*!
+ * \brief Description of the System Memory Allocator part of the OS Adaptation Layer with memory set to zero
+ *
+ * Compare to \see OSAL_Alloc, same allocation is done but memory is set with zero before returning.
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void* OSAL_Alloc_Zero(
+ t_cm_size size);
+
+/*!
+ * \brief Description of the System Memory Allocator part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Free CM some cacheable and bufferable memory (SDRAM) for internal usage \n
+ * This memory will be accessed only by Host CPU (ARM)
+ *
+ * \param[in] pHandle pointer on the begining of the memory previously allocated
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_Free(
+ void *pHandle);
+
+/*!
+ * \brief Clean data cache in DDR in order to be accessible from peripheral.
+ *
+ * This method must be synchronized with MMDSP Code cache attribute.
+ * Strongly Ordered -> nothing
+ * Shared device -> dsb + L2 Write buffer drain
+ * Non cacheable, Bufferable -> dsb + L2 Write buffer drain
+ * WT or WB -> Flush cache range + dsb + L2 Write buffer drain
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_CleanDCache(
+ t_uint32 startAddr, //!< [in] Start data address of range to clean
+ t_uint32 Size //!< [in] Size of range to clean
+ );
+
+/*!
+ * \brief Flush write buffer.
+ *
+ * This method must be synchronized with MMDSP Data cache attribute.
+ * Strongly Ordered -> nothing
+ * Shared device -> dsb + L2 Write buffer drain
+ * Non cacheable, Bufferable -> dsb + L2 Write buffer drain
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_mb(void);
+
+/*!
+ * \brief Description of the System Memory part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Copy some cacheable and bufferable memory (SDRAM) provided by a client to\n
+ * internal memory.
+ *
+ * \param[in] dst : pointer on the begining of the internal memory previously allocated
+ * \param[in] src : pointer on the begining of the client's memory
+ * \param[in] size : The size of the data to copy
+ *
+ * Called by:
+ * - CM_ENGINE_PushComponent()
+ *
+ * \note This API is mainly provided for the OS were the client application does execute in the same
+ * address space as the CM.
+ * For example in Linux or Symbian, the client's address space is userland but the CM execute in
+ * kernel space. Thus, 'dst' is supposed to be a kernel address but src is supposed to be a user
+ * space address
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC t_cm_error OSAL_Copy(
+ void *dst,
+ const void *src,
+ t_cm_size size);
+
+/*!
+ * \brief Description of the internal log traces configuration of the Component Manager
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_Log(
+ const char *format,
+ int param1,
+ int param2,
+ int param3,
+ int param4,
+ int param5,
+ int param6);
+
+/*!
+ * \brief Generate an OS-Panic. Called in from CM_ASSERT().
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_Panic(void);
+
+/*!
+ * \brief Description of the configuration of the trace features
+ *
+ * (trace output itself is provided by user through his custom implementation of the generic APIs)
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_Write64(
+ t_nmf_trace_channel channel,
+ t_uint8 isTimestamped,
+ t_uint64 value);
+
+/*!
+ * \brief Power enabling/disabling commands description.
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+typedef enum
+{
+ CM_OSAL_POWER_SxA_CLOCK, //!< SxA Power & Clock, firstParam contains Core ID
+ CM_OSAL_POWER_SxA_AUTOIDLE, //!< SxA AutoIdle, firstParam contains Core ID
+ CM_OSAL_POWER_SxA_HARDWARE, //!< SxA Hardware Power, firstParam contains Core ID
+ CM_OSAL_POWER_HSEM, //!< HSEM Power
+ CM_OSAL_POWER_SDRAM, //!< SDRAM memory, firstParam contains physical resource address, secondParam contains size
+ CM_OSAL_POWER_ESRAM //!< ESRAM memory, firstParam contains physical resource address, secondParam contains size
+} t_nmf_power_resource;
+
+/*!
+ * \brief Description of the Power Management part of the OS Adaptation Layer
+ *
+ * Use by CM engine to disable a logical power domain (see \ref t_nmf_power_resource)
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_DisablePwrRessource(
+ t_nmf_power_resource resource, //!< [in] Describe the domain which must be disabled
+ t_uint32 firstParam, //!< [in] Eventual first parameter to power to disable
+ t_uint32 secondParam //!< [in] Eventual second parameter to power to disable
+ );
+
+/*!
+ * \brief Description of the Power Management part of the OS Adaptation Layer
+ *
+ * Use by CM engine to enable a logical power domain (see \ref t_nmf_power_resource)
+ *
+ * \return
+ * - \ref CM_OK
+ * - \ref CM_PWR_NOT_AVAILABLE A specified power domain is not managed (see returned value in aPowerMask)
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC t_cm_error OSAL_EnablePwrRessource(
+ t_nmf_power_resource resource, //!< [in] Describing the domains which must be enabled
+ t_uint32 firstParam, //!< [in] Eventual first parameter to power to disable
+ t_uint32 secondParam //!< [in] Eventual second parameter to power to disable
+ );
+
+
+/*!
+ * \brief return prcmu timer value.
+ *
+ * This is need for perfmeter api (see \ref t_nmf_power_resource)
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC t_uint64 OSAL_GetPrcmuTimer(void);
+
+/*!
+ * \brief Disable the service message handling (panic, etc)
+ *
+ * It must disable the handling of all service messages
+ * If a service message is currently handled, it must wait till the end
+ * of its managment before returning.
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_DisableServiceMessages(void);
+
+/*!
+ * \brief Enable the service message handling (panic, etc)
+ *
+ * It enables the handling of all service messages
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_EnableServiceMessages(void);
+
+extern /*const*/ t_nmf_osal_sync_handle lockHandleApi;
+extern /*const*/ t_nmf_osal_sync_handle lockHandleCom;
+extern /*const*/ t_nmf_osal_sem_handle semHandle;
+
+/*!
+ * \brief Take a lock before entering critical section. Can suspend current thread if lock already taken. \n
+ * Use this macro in api function. For com function use OSAL_LOCK_COM.
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+#define OSAL_LOCK_API() OSAL_Lock(lockHandleApi)
+
+/*!
+ * \brief Release lock before leaving critical section.
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+#define OSAL_UNLOCK_API() OSAL_Unlock((lockHandleApi))
+
+/*!
+ * \brief Take a lock before entering critical section. Can suspend current thread if lock already taken. \n
+ * Use this macro in com function. For com function use OSAL_LOCK_API.
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+#define OSAL_LOCK_COM() OSAL_Lock(lockHandleCom)
+
+/*!
+ * \brief Release lock before leaving critical section.
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+#define OSAL_UNLOCK_COM() OSAL_Unlock((lockHandleCom))
+
+/*!
+ * \brief Go to sleep untill post done on semaphore or timeout expire. In that case SYNC_ERROR_TIMEOUT is return.
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+#define OSAL_SEMAPHORE_WAIT_TIMEOUT(semHandle) OSAL_SemaphoreWaitTimed(semHandle, (cm_PWR_GetMode() == NORMAL_PWR_MODE)?SEM_TIMEOUT_NORMAL:SEM_TIMEOUT_DEBUG)
+
+
+/****************/
+/* Generic part */
+/****************/
+t_cm_error cm_OSAL_Init(void);
+void cm_OSAL_Destroy(void);
+
+#endif /* __INC_CM_OSAL_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/src/os_adaptation_layer.c b/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/src/os_adaptation_layer.c
new file mode 100644
index 00000000000..9beb60dea18
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/os_adaptation_layer/src/os_adaptation_layer.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+
+t_nmf_osal_sync_handle lockHandleApi;
+t_nmf_osal_sync_handle lockHandleCom;
+t_nmf_osal_sem_handle semHandle;
+
+/****************/
+/* Generic part */
+/****************/
+PUBLIC t_cm_error cm_OSAL_Init(void)
+{
+
+ /* create locks */
+ lockHandleApi = OSAL_CreateLock();
+ if (lockHandleApi == 0) {return CM_INVALID_PARAMETER;}
+ lockHandleCom = OSAL_CreateLock();
+ if (lockHandleCom == 0) {return CM_INVALID_PARAMETER;}
+
+ /* create semaphore */
+ semHandle = OSAL_CreateSemaphore(0);
+ if (semHandle == 0) {return CM_INVALID_PARAMETER;}
+
+ return CM_OK;
+}
+
+PUBLIC void cm_OSAL_Destroy(void)
+{
+ /* destroy locks */
+ OSAL_DestroyLock(lockHandleApi);
+ OSAL_DestroyLock(lockHandleCom);
+
+ /* destroy semaphore */
+ OSAL_DestroySemaphore(semHandle);
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/mpcload.h b/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/mpcload.h
new file mode 100644
index 00000000000..0831f1940ca
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/mpcload.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ */
+#ifndef MPCLOAD_H_
+#define MPCLOAD_H_
+
+#include <cm/engine/component/inc/instance.h>
+
+/******************************************************************************/
+/************************ FUNCTIONS PROTOTYPES ********************************/
+/******************************************************************************/
+
+PUBLIC t_cm_error cm_PFM_allocatePerfmeterDataMemory(t_nmf_core_id coreId, t_cm_domain_id domainId);
+PUBLIC void cm_PFM_deallocatePerfmeterDataMemory(t_nmf_core_id coreId);
+
+#endif /* MPCLOAD_H_ */
diff --git a/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/perfmeter_type.h b/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/perfmeter_type.h
new file mode 100644
index 00000000000..78de395acc2
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/perfmeter/inc/perfmeter_type.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Public Component Manager Performance Meter API type.
+ *
+ * This file contains the Component Manager API type for performance meter.
+ *
+ * \defgroup PERFMETER CM Monitoring API
+ * \ingroup CM_USER_API
+ */
+#ifndef CM_COMMON_PERFMETER_TYPE_H_
+#define CM_COMMON_PERFMETER_TYPE_H_
+
+#include <cm/inc/cm_type.h>
+/*!
+ * \brief Description of mpc load structure.
+ *
+ * This contain mpc load value.
+ *
+ * \ingroup PERFMETER
+ */
+typedef struct {
+ t_uint64 totalCounter;
+ t_uint64 loadCounter;
+} t_cm_mpc_load_counter;
+
+
+#endif /* CM_COMMON_PERFMETER_TYPE_H_ */
diff --git a/drivers/staging/nmf-cm/cm/engine/perfmeter/src/mpcload.c b/drivers/staging/nmf-cm/cm/engine/perfmeter/src/mpcload.c
new file mode 100644
index 00000000000..b157103934d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/perfmeter/src/mpcload.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/engine/perfmeter/inc/mpcload.h>
+#include <cm/engine/component/inc/introspection.h>
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+
+#include <cm/engine/api/perfmeter_engine.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+
+#define PERFMETER_MAX_RETRIES 32
+#define PERFMETER_DATA_WORD_NB 7
+
+/* private type */
+typedef struct {
+ t_memory_handle perfmeterDataHandle;
+ t_cm_logical_address perfmeterDataAddr;
+} t_mpcLoad;
+
+/* private globals */
+t_mpcLoad mpcLoad_i[NB_CORE_IDS];
+
+/* engine api */
+PUBLIC EXPORT_SHARED t_cm_error CM_GetMpcLoadCounter(
+ t_nmf_core_id coreId,
+ t_cm_mpc_load_counter *pMpcLoadCounter
+)
+{
+ t_uint24 data[PERFMETER_DATA_WORD_NB];
+ t_uint32 i;
+ t_uint64 prcmuBeforeAttributes;
+ t_uint32 retryCounter = 0;
+ volatile t_uint32 *pData;
+
+ pMpcLoadCounter->totalCounter = 0;
+ pMpcLoadCounter->loadCounter = 0;
+ /* check core id is an mpc */
+ if (coreId < FIRST_MPC_ID || coreId > LAST_CORE_ID) {return CM_INVALID_PARAMETER;}
+
+ /* check core has been booted */
+ pData = (t_uint32 *) mpcLoad_i[coreId].perfmeterDataAddr;
+ if (pData == NULL) {return CM_OK;}
+
+ do {
+ prcmuBeforeAttributes = OSAL_GetPrcmuTimer();
+ /* get attributes */
+ do
+ {
+ for(i = 0;i < PERFMETER_DATA_WORD_NB;i++)
+ data[i] = pData[i];
+ }
+ while(((data[0] & 0xff0000) != (data[1] & 0xff0000) || (data[0] & 0xff0000) != (data[2] & 0xff0000) ||
+ (data[0] & 0xff0000) != (data[3] & 0xff0000) || (data[0] & 0xff0000) != (data[4] & 0xff0000) ||
+ (data[0] & 0xff0000) != (data[5] & 0xff0000) || (data[0] & 0xff0000) != (data[6] & 0xff0000) ||
+ (data[0] & 0xff0000) != (data[6] & 0xff0000))
+ && retryCounter-- < PERFMETER_MAX_RETRIES); // check data coherence
+ if (retryCounter >= PERFMETER_MAX_RETRIES)
+ return CM_MPC_NOT_RESPONDING;
+
+ /* read forever counter for totalCounter */
+ pMpcLoadCounter->totalCounter = OSAL_GetPrcmuTimer();
+ } while(pMpcLoadCounter->totalCounter - prcmuBeforeAttributes >= 32); //we loop until it seems we have not be preempt too long (< 1ms)
+
+ /* we got coherent data, use them */
+ pMpcLoadCounter->loadCounter = ((data[0] & (t_uint64)0xffff) << 32) + ((data[1] & (t_uint64)0xffff) << 16) + ((data[2] & (t_uint64)0xffff) << 0);
+ //fix load counter if needed
+ if ((data[6] & 0xffff) == 1) {
+ t_uint64 lastEvent;
+
+ lastEvent = ((data[3] & (t_uint64)0xffff) << 32) + ((data[4] & (t_uint64)0xffff) << 16) + ((data[5] & (t_uint64)0xffff) << 0);
+ pMpcLoadCounter->loadCounter += pMpcLoadCounter->totalCounter - lastEvent;
+ }
+
+ return CM_OK;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_getMpcLoadCounter(
+ t_nmf_core_id coreId,
+ t_cm_mpc_load_counter *pMpcLoadCounter
+)
+{
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+ error = CM_GetMpcLoadCounter(coreId, pMpcLoadCounter);
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+/* internal api */
+PUBLIC t_cm_error cm_PFM_allocatePerfmeterDataMemory(t_nmf_core_id coreId, t_cm_domain_id domainId)
+{
+ t_cm_error error = CM_OK;
+ t_mpcLoad *pMpcLoad = (t_mpcLoad *) &mpcLoad_i[coreId];
+
+ pMpcLoad->perfmeterDataHandle = cm_DM_Alloc(domainId, SDRAM_EXT24, PERFMETER_DATA_WORD_NB, CM_MM_ALIGN_WORD, TRUE);
+ if (pMpcLoad->perfmeterDataHandle == INVALID_MEMORY_HANDLE)
+ error = CM_NO_MORE_MEMORY;
+ else {
+ t_uint32 mmdspAddr;
+
+ pMpcLoad->perfmeterDataAddr = cm_DSP_GetHostLogicalAddress(pMpcLoad->perfmeterDataHandle);
+ cm_DSP_GetDspAddress(pMpcLoad->perfmeterDataHandle, &mmdspAddr);
+ cm_writeAttribute(cm_EEM_getExecutiveEngine(coreId)->instance, "rtos/perfmeter/perfmeterDataAddr", mmdspAddr);
+ }
+
+ return error;
+}
+
+PUBLIC void cm_PFM_deallocatePerfmeterDataMemory(t_nmf_core_id coreId)
+{
+ mpcLoad_i[coreId].perfmeterDataAddr = 0;
+ cm_DM_Free(mpcLoad_i[coreId].perfmeterDataHandle, TRUE);
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/power_mgt/inc/power.h b/drivers/staging/nmf-cm/cm/engine/power_mgt/inc/power.h
new file mode 100644
index 00000000000..1ca1b314833
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/power_mgt/inc/power.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Enable a CM power domain by CoreID.
+ *
+ * \ingroup COMPONENT_INTERNAL
+ */
+#ifndef __INC_NMF_POWER
+#define __INC_NMF_POWER
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/memory/inc/memory.h>
+#include <cm/engine/dsp/inc/dsp.h>
+
+typedef enum
+{
+ NORMAL_PWR_MODE = 0x1, //!< Normal mode
+ DISABLE_PWR_MODE = 0x2 //!< Disable mode - CM Power management is disabled. CM Power domain are always enabled and the EEs are loaded by default
+} t_nmf_power_mode;
+
+PUBLIC t_cm_error cm_PWR_Init(void);
+void cm_PWR_SetMode(t_nmf_power_mode aMode);
+t_nmf_power_mode cm_PWR_GetMode(void);
+t_uint32 cm_PWR_GetMPCMemoryCount(t_nmf_core_id coreId);
+
+typedef enum
+{
+ MPC_PWR_CLOCK,
+ MPC_PWR_AUTOIDLE,
+ MPC_PWR_HWIP
+} t_mpc_power_request;
+
+PUBLIC t_cm_error cm_PWR_EnableMPC(
+ t_mpc_power_request request,
+ t_nmf_core_id coreId);
+PUBLIC void cm_PWR_DisableMPC(
+ t_mpc_power_request request,
+ t_nmf_core_id coreId);
+
+PUBLIC t_cm_error cm_PWR_EnableHSEM(void);
+PUBLIC void cm_PWR_DisableHSEM(void);
+
+PUBLIC t_cm_error cm_PWR_EnableMemory(
+ t_nmf_core_id coreId,
+ t_dsp_memory_type_id dspMemType,
+ t_cm_physical_address address,
+ t_cm_size size);
+PUBLIC void cm_PWR_DisableMemory(
+ t_nmf_core_id coreId,
+ t_dsp_memory_type_id dspMemType,
+ t_cm_physical_address address,
+ t_cm_size size);
+
+
+#endif /* __INC_NMF_POWER */
diff --git a/drivers/staging/nmf-cm/cm/engine/power_mgt/src/cmpower.c b/drivers/staging/nmf-cm/cm/engine/power_mgt/src/cmpower.c
new file mode 100644
index 00000000000..5b206bfb82d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/power_mgt/src/cmpower.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include "../inc/power.h"
+
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/utils/inc/convert.h>
+#include <cm/engine/dsp/inc/dsp.h>
+
+// -------------------------------------------------------------------------------
+// Compilation flags
+// -------------------------------------------------------------------------------
+#define __PWR_DEBUG_TRACE_LEVEL 2 // Debug trave level for CM power module
+
+// -------------------------------------------------------------------------------
+// Internal counter to store the TCM allocated chunk (by MPC)
+// -------------------------------------------------------------------------------
+static t_uint32 _pwrMPCHWIPCountT[NB_CORE_IDS];
+
+// -------------------------------------------------------------------------------
+// Internal counter to store the TCM allocated chunk (by MPC)
+// -------------------------------------------------------------------------------
+static t_uint32 _pwrMPCMemoryCountT[NB_CORE_IDS];
+
+
+// -------------------------------------------------------------------------------
+// Internal data to store the global Power Manager mode (see cm_PWR_Init fct)
+// -------------------------------------------------------------------------------
+static t_nmf_power_mode _pwrMode = NORMAL_PWR_MODE;
+
+// -------------------------------------------------------------------------------
+// cm_PWR_Init
+// -------------------------------------------------------------------------------
+PUBLIC t_cm_error cm_PWR_Init(void)
+{
+ int i;
+
+ for (i=0; i<NB_CORE_IDS;i++)
+ {
+ _pwrMPCHWIPCountT[i] = 0;
+ _pwrMPCMemoryCountT[i] = 0;
+ }
+
+ return CM_OK;
+}
+
+// -------------------------------------------------------------------------------
+// cm_PWR_SetMode
+// -------------------------------------------------------------------------------
+void cm_PWR_SetMode(t_nmf_power_mode aMode)
+{
+ _pwrMode = aMode;
+}
+
+t_nmf_power_mode cm_PWR_GetMode()
+{
+ return _pwrMode;
+}
+
+t_uint32 cm_PWR_GetMPCMemoryCount(t_nmf_core_id coreId)
+{
+ return _pwrMPCMemoryCountT[coreId];
+}
+
+
+PUBLIC t_cm_error cm_PWR_EnableMPC(
+ t_mpc_power_request request,
+ t_nmf_core_id coreId)
+{
+ t_cm_error error;
+
+ switch(request)
+ {
+ case MPC_PWR_CLOCK:
+ LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] MPC %s enable clock\n", cm_getDspName(coreId), 0, 0, 0, 0, 0);
+ if((error = OSAL_EnablePwrRessource(CM_OSAL_POWER_SxA_CLOCK, coreId, 0)) != CM_OK)
+ {
+ ERROR("[Pwr] MPC %s clock can't be enabled\n", cm_getDspName(coreId), 0, 0, 0, 0, 0);
+ return error;
+ }
+ break;
+ case MPC_PWR_AUTOIDLE:
+ if((error = OSAL_EnablePwrRessource(CM_OSAL_POWER_SxA_AUTOIDLE, coreId, 0)) != CM_OK)
+ {
+ ERROR("[Pwr] MPC %s clock can't be auto-idle\n", cm_getDspName(coreId), 0, 0, 0, 0, 0);
+ return error;
+ }
+ break;
+ case MPC_PWR_HWIP:
+ if(_pwrMPCHWIPCountT[coreId]++ == 0)
+ {
+ LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] MPC %s HW IP enable clock\n",cm_getDspName(coreId), 0, 0, 0, 0, 0);
+ if((error = OSAL_EnablePwrRessource(CM_OSAL_POWER_SxA_HARDWARE, coreId, 0)) != CM_OK)
+ {
+ ERROR("[Pwr] MPC %s HW IP clock can't be enabled\n", cm_getDspName(coreId), 0, 0, 0, 0, 0);
+ return error;
+ }
+ }
+ break;
+ }
+
+ return CM_OK;
+}
+
+PUBLIC void cm_PWR_DisableMPC(
+ t_mpc_power_request request,
+ t_nmf_core_id coreId)
+{
+ switch(request)
+ {
+ case MPC_PWR_CLOCK:
+ LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] MPC %s disable clock\n",cm_getDspName(coreId), 0, 0, 0, 0, 0);
+ OSAL_DisablePwrRessource(CM_OSAL_POWER_SxA_CLOCK, coreId, 0);
+ break;
+ case MPC_PWR_AUTOIDLE:
+ OSAL_DisablePwrRessource(CM_OSAL_POWER_SxA_AUTOIDLE, coreId, 0);
+ break;
+ case MPC_PWR_HWIP:
+ if(--_pwrMPCHWIPCountT[coreId] == 0)
+ {
+ LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] MPC %s HW IP disable clock\n",cm_getDspName(coreId), 0, 0, 0, 0, 0);
+ OSAL_DisablePwrRessource(CM_OSAL_POWER_SxA_HARDWARE, coreId, 0);
+ }
+ break;
+ }
+}
+
+PUBLIC t_cm_error cm_PWR_EnableHSEM(void)
+{
+ t_cm_error error;
+
+ LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] HSEM enable clock\n",0 , 0, 0, 0, 0, 0);
+ if((error = OSAL_EnablePwrRessource(CM_OSAL_POWER_HSEM, 0, 0)) != CM_OK)
+ {
+ ERROR("[Pwr] HSEM clock can't be enabled\n", 0, 0, 0, 0, 0, 0);
+ return error;
+ }
+
+ return CM_OK;
+}
+
+PUBLIC void cm_PWR_DisableHSEM(void)
+{
+ LOG_INTERNAL(__PWR_DEBUG_TRACE_LEVEL, "[Pwr] HSEM disable clock\n",0 , 0, 0, 0, 0, 0);
+ OSAL_DisablePwrRessource(CM_OSAL_POWER_HSEM, 0, 0);
+}
+
+PUBLIC t_cm_error cm_PWR_EnableMemory(
+ t_nmf_core_id coreId,
+ t_dsp_memory_type_id dspMemType,
+ t_cm_physical_address address,
+ t_cm_size size)
+{
+ switch(dspMemType)
+ {
+ case INTERNAL_XRAM24:
+ case INTERNAL_XRAM16:
+ case INTERNAL_YRAM24:
+ case INTERNAL_YRAM16:
+ _pwrMPCMemoryCountT[coreId]++;
+ break;
+ case SDRAM_EXT24:
+ case SDRAM_EXT16:
+ case SDRAM_CODE:
+ case LOCKED_CODE:
+ return OSAL_EnablePwrRessource(
+ CM_OSAL_POWER_SDRAM,
+ address,
+ size);
+ case ESRAM_EXT24:
+ case ESRAM_EXT16:
+ case ESRAM_CODE:
+ return OSAL_EnablePwrRessource(
+ CM_OSAL_POWER_ESRAM,
+ address,
+ size);
+ default:
+ CM_ASSERT(0);
+ }
+
+ return CM_OK;
+}
+
+PUBLIC void cm_PWR_DisableMemory(
+ t_nmf_core_id coreId,
+ t_dsp_memory_type_id dspMemType,
+ t_cm_physical_address address,
+ t_cm_size size)
+{
+ switch(dspMemType)
+ {
+ case INTERNAL_XRAM24:
+ case INTERNAL_XRAM16:
+ case INTERNAL_YRAM24:
+ case INTERNAL_YRAM16:
+ _pwrMPCMemoryCountT[coreId]--;
+ break;
+ case SDRAM_EXT24:
+ case SDRAM_EXT16:
+ case SDRAM_CODE:
+ case LOCKED_CODE:
+ OSAL_DisablePwrRessource(
+ CM_OSAL_POWER_SDRAM,
+ address,
+ size);
+ break;
+ case ESRAM_EXT24:
+ case ESRAM_EXT16:
+ case ESRAM_CODE:
+ OSAL_DisablePwrRessource(
+ CM_OSAL_POWER_ESRAM,
+ address,
+ size);
+ break;
+ default:
+ CM_ASSERT(0);
+ }
+}
+
+
+
+
+
diff --git a/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_mgt.h b/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_mgt.h
new file mode 100644
index 00000000000..d2c7185b24f
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_mgt.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Component repository internal methods.
+ *
+ * \defgroup REPOSITORY_INTERNAL Component repository.
+ */
+#ifndef __INC_CM_REP_MGT_H
+#define __INC_CM_REP_MGT_H
+
+#include <cm/inc/cm_type.h>
+#include <inc/nmf-limits.h>
+
+/*!
+ * \brief Identification of a component entry.
+ * \ingroup REPOSITORY_INTERNAL
+ */
+typedef struct t_rep_component {
+ t_dup_char name;
+ struct t_rep_component *prev;
+ struct t_rep_component *next;
+ t_elfdescription *elfhandle; //!< Must be last as data will be stored here
+} t_rep_component;
+
+/*!
+ * \brief Search a component entry by name.
+ *
+ * \param[in] name The name of the component to look for.
+ * \param[out] component The corresponding component entry in the repository
+ *
+ * \retval t_cm_error
+ *
+ * \ingroup REPOSITORY_INTERNAL
+ */
+PUBLIC t_cm_error cm_REP_lookupComponent(const char *name, t_rep_component **component);
+
+/*!
+ * \brief Helper method that return the dataFile found in parameter or in the cache
+ */
+t_elfdescription* cm_REP_getComponentFile(t_dup_char templateName, t_elfdescription* elfhandle);
+
+/*!
+ * \brief Destroy the full repository (remove and free all components)
+ *
+ * \retval none
+ *
+ * \ingroup REPOSITORY_INTERNAL
+ */
+PUBLIC void cm_REP_Destroy(void);
+
+#endif /* __INC_CM_REP_MGT_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_type.h b/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_type.h
new file mode 100644
index 00000000000..7c41fe1bedb
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/repository_mgt/inc/repository_type.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Components Component Manager API type.
+ *
+ * \defgroup COMPONENT CM Components API
+ * \ingroup CM_USER_API
+ */
+
+#ifndef REPOSITORY_TYPE_H_
+#define REPOSITORY_TYPE_H_
+
+typedef enum
+{
+ GET_EE_NAME,
+ BIND_ASYNC,
+ BIND_TRACE,
+ BIND_FROMUSER,
+ BIND_TOUSER
+} t_action_to_do;
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/repository_mgt/src/repository_mgt.c b/drivers/staging/nmf-cm/cm/engine/repository_mgt/src/repository_mgt.c
new file mode 100644
index 00000000000..c972783db4b
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/repository_mgt/src/repository_mgt.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ *
+ */
+#include <cm/engine/utils/inc/string.h>
+
+#include <cm/engine/component/inc/component_type.h>
+#include <cm/engine/component/inc/bind.h>
+#include <cm/engine/configuration/inc/configuration.h>
+#include <cm/engine/component/inc/introspection.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+#include <cm/engine/repository_mgt/inc/repository_mgt.h>
+#include <cm/engine/api/repository_mgt_engine.h>
+#include <cm/engine/trace/inc/trace.h>
+
+
+#define NHASH 157 //Use a prime number!
+#define MULT 17
+
+static t_rep_component *componentCaches[NHASH];
+
+static unsigned int repcomponentHash(const char *str)
+{
+ unsigned int h = 0;
+ for(; *str; str++)
+ h = MULT * h + *str;
+ return h % NHASH;
+}
+
+static void repcomponentAdd(t_rep_component *component)
+{
+ unsigned int h = repcomponentHash(component->name);
+
+ if(componentCaches[h] != NULL)
+ componentCaches[h]->prev = component;
+ component->next = componentCaches[h];
+ component->prev = NULL;
+ componentCaches[h] = component;
+}
+
+static void repcomponentRemove(t_rep_component *component)
+{
+ unsigned int h = repcomponentHash(component->name);
+
+ if(component->prev != NULL)
+ component->prev->next = component->next;
+ if(component->next != NULL)
+ component->next->prev = component->prev;
+ if(component == componentCaches[h])
+ componentCaches[h] = component->next;
+}
+
+
+PUBLIC t_cm_error cm_REP_lookupComponent(const char *name, t_rep_component **component)
+{
+ t_rep_component *tmp;
+
+ for(tmp = componentCaches[repcomponentHash(name)]; tmp != NULL; tmp = tmp->next)
+ {
+ if(cm_StringCompare(name, tmp->name, MAX_TEMPLATE_NAME_LENGTH) == 0)
+ {
+ if(component != NULL)
+ *component = tmp;
+ return CM_OK;
+ }
+ }
+
+ return CM_COMPONENT_NOT_FOUND;
+}
+
+t_elfdescription* cm_REP_getComponentFile(t_dup_char templateName, t_elfdescription* elfhandle)
+{
+ if(elfhandle == NULL)
+ {
+ t_rep_component *pRepComponent;
+
+ for(pRepComponent = componentCaches[repcomponentHash(templateName)]; pRepComponent != NULL; pRepComponent = pRepComponent->next)
+ {
+ if(pRepComponent->name == templateName)
+ return pRepComponent->elfhandle;
+ }
+
+ return NULL;
+ }
+
+ return elfhandle;
+}
+
+
+PUBLIC void cm_REP_Destroy(void)
+{
+ t_rep_component *component, *next;
+ int i;
+
+ for(i = 0; i < NHASH; i++)
+ {
+ for (component = componentCaches[i]; component != NULL; component = next)
+ {
+ next = component->next;
+ cm_ELF_CloseFile(FALSE, component->elfhandle);
+ cm_StringRelease(component->name);
+ OSAL_Free(component);
+ }
+ componentCaches[i] = NULL;
+ }
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_GetRequiredComponentFiles(
+ // IN
+ t_action_to_do action,
+ const t_cm_instance_handle client,
+ const char *requiredItfClientName,
+ const t_cm_instance_handle server,
+ const char *providedItfServerName,
+ // OUT component to be pushed
+ char fileList[][MAX_INTERFACE_TYPE_NAME_LENGTH],
+ t_uint32 listSize,
+ // OUT interface information
+ char type[MAX_INTERFACE_TYPE_NAME_LENGTH],
+ t_uint32 *methodNumber)
+{
+ t_cm_error error;
+ t_component_instance* compClient, *compServer;
+ int n;
+
+ OSAL_LOCK_API();
+
+ // No component required
+ for(n = 0; n < listSize; n++)
+ fileList[n][0] = 0;
+
+ compClient = cm_lookupComponent(client);
+ compServer = cm_lookupComponent(server);
+ switch(action)
+ {
+ case GET_EE_NAME:
+ /* Get Executive Engine names */
+ error = cm_CFG_GetRequiredExecutiveEngineComponentNames(fileList, listSize);
+ break;
+
+ case BIND_FROMUSER:{
+ t_interface_provide_description itfProvide;
+
+ // Check server validity
+ if((error = cm_checkValidServer(compServer, providedItfServerName,
+ &itfProvide)) == CM_OK)
+ {
+ cm_StringCopy(type, itfProvide.server->template->provides[itfProvide.provideIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH);
+
+ cm_StringCopy(fileList[0], "_sk.", MAX_INTERFACE_TYPE_NAME_LENGTH);
+ cm_StringConcatenate(fileList[0], itfProvide.server->template->provides[itfProvide.provideIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH);
+ }
+ } break;
+
+ case BIND_TOUSER: {
+ /* Get Components names for a BindComponentToCMCore */
+ t_interface_require_description itfRequire;
+ t_bool bindable;
+
+ // Check client validity
+ if((error = cm_checkValidClient(compClient, requiredItfClientName,
+ &itfRequire, &bindable)) == CM_OK)
+ {
+ cm_StringCopy(type, itfRequire.client->template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH);
+ *methodNumber = itfRequire.client->template->requires[itfRequire.requireIndex].interface->methodNumber;
+
+ cm_StringCopy(fileList[0], "_st.", MAX_INTERFACE_TYPE_NAME_LENGTH);
+ cm_StringConcatenate(fileList[0], itfRequire.client->template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH);
+ }
+ }; break;
+
+ case BIND_ASYNC: {
+ /* Get Components names for an asynchronous binding */
+ t_interface_require_description itfRequire;
+ t_interface_provide_description itfProvide;
+ t_bool bindable;
+
+ // Check invalid binding
+ if((error = cm_checkValidBinding(compClient, requiredItfClientName,
+ compServer, providedItfServerName,
+ &itfRequire, &itfProvide, &bindable)) == CM_OK)
+ {
+ if(compClient->template->dspId != compServer->template->dspId)
+ {
+ cm_StringCopy(fileList[0], "_sk.", MAX_INTERFACE_TYPE_NAME_LENGTH);
+ cm_StringConcatenate(fileList[0], itfRequire.client->template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH);
+
+ cm_StringCopy(fileList[1], "_st.", MAX_INTERFACE_TYPE_NAME_LENGTH);
+ cm_StringConcatenate(fileList[1], itfRequire.client->template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH);
+ }
+ else
+ {
+ cm_StringCopy(fileList[0], "_ev.", MAX_INTERFACE_TYPE_NAME_LENGTH);
+ cm_StringConcatenate(fileList[0], itfRequire.client->template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH);
+ }
+ }
+ }; break;
+
+ case BIND_TRACE: {
+ /* Get Components names for an asynchronous binding */
+ t_interface_require_description itfRequire;
+ t_interface_provide_description itfProvide;
+ t_bool bindable;
+
+ // Check invalid binding
+ if((error = cm_checkValidBinding(compClient, requiredItfClientName,
+ compServer, providedItfServerName,
+ &itfRequire, &itfProvide, &bindable)) == CM_OK)
+ {
+ cm_StringCopy(fileList[0], "_tr.", MAX_INTERFACE_TYPE_NAME_LENGTH);
+ cm_StringConcatenate(fileList[0], itfRequire.client->template->requires[itfRequire.requireIndex].interface->type, MAX_INTERFACE_TYPE_NAME_LENGTH);
+ }
+ }; break;
+
+ default:
+ error = CM_OK;
+ break;
+ }
+
+ if(error == CM_OK)
+ {
+ for(n = 0; n < listSize; n++)
+ {
+ t_rep_component *comp;
+
+ // If already loaded, don't ask to load it and put the name to NULL
+ if (fileList[n][0] != 0 &&
+ cm_REP_lookupComponent(fileList[n], &comp) == CM_OK)
+ fileList[n][0] = 0;
+ }
+ }
+
+
+ OSAL_UNLOCK_API();
+ return error;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_PushComponent(const char *name, const void *data, t_cm_size size)
+{
+ t_rep_component *comp;
+ t_cm_error error;
+
+ OSAL_LOCK_API();
+
+ if (cm_REP_lookupComponent(name, &comp) == CM_OK) {
+ /* Component is already there: silently ignore it */
+ OSAL_UNLOCK_API();
+ return CM_OK;
+ }
+
+ comp = OSAL_Alloc(sizeof(*comp));
+ if (comp == NULL) {
+ OSAL_UNLOCK_API();
+ return CM_NO_MORE_MEMORY;
+ }
+
+ comp->name = cm_StringDuplicate(name);
+ if(comp->name == NULL)
+ {
+ OSAL_Free(comp);
+ OSAL_UNLOCK_API();
+ return CM_NO_MORE_MEMORY;
+ }
+
+ if((error = cm_ELF_CheckFile(
+ data,
+ FALSE,
+ &comp->elfhandle)) != CM_OK) {
+ cm_StringRelease(comp->name);
+ OSAL_Free(comp);
+ OSAL_UNLOCK_API();
+ return error;
+ }
+/*
+ if (OSAL_Copy(comp->data, data, size)) {
+ OSAL_Free(comp);
+ OSAL_UNLOCK_API();
+ return CM_UNKNOWN_MEMORY_HANDLE;
+ }*/
+
+ repcomponentAdd(comp);
+
+ OSAL_UNLOCK_API();
+ return CM_OK;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ENGINE_ReleaseComponent (const char *name)
+{
+ t_rep_component *component;
+ t_cm_error err;
+
+ OSAL_LOCK_API();
+ err = cm_REP_lookupComponent(name , &component);
+
+ if (CM_OK == err)
+ {
+ repcomponentRemove(component);
+
+ cm_ELF_CloseFile(FALSE, component->elfhandle);
+ cm_StringRelease(component->name);
+ OSAL_Free(component);
+ }
+
+ OSAL_UNLOCK_API();
+
+ return err;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h b/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h
new file mode 100644
index 00000000000..bd914195b6d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef __INC_HW_SEMA_H_
+#define __INC_HW_SEMA_H_
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/semaphores/inc/semaphores.h>
+#include <share/semaphores/inc/hwsem_hwp.h>
+
+
+/******************************************************************************/
+/************************ FUNCTIONS PROTOTYPES ********************************/
+/******************************************************************************/
+
+PUBLIC t_cm_error cm_HSEM_Init(const t_cm_system_address *pSystemAddr);
+PUBLIC t_cm_error cm_HSEM_EnableSemIrq(t_semaphore_id semId, t_nmf_core_id toCoreId);
+PUBLIC void cm_HSEM_Take(t_nmf_core_id coreId, t_semaphore_id semId);
+PUBLIC void cm_HSEM_Give(t_nmf_core_id coreId, t_semaphore_id semId);
+PUBLIC void cm_HSEM_GiveWithInterruptGeneration(t_nmf_core_id coreId, t_semaphore_id semId);
+PUBLIC void cm_HSEM_GenerateIrq(t_nmf_core_id coreId, t_semaphore_id semId);
+PUBLIC t_nmf_core_id cm_HSEM_GetCoreIdFromIrqSrc(void);
+
+PUBLIC t_cm_error cm_HSEM_PowerOn(t_nmf_core_id coreId);
+PUBLIC void cm_HSEM_PowerOff(t_nmf_core_id coreId);
+
+#endif /* __INC_HW_SEMA_H_ */
diff --git a/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/src/hw_semaphores.c b/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/src/hw_semaphores.c
new file mode 100644
index 00000000000..932058cd4f2
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/semaphores/hw_semaphores/src/hw_semaphores.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/******************************************************************* Includes
+ ****************************************************************************/
+
+#include "../inc/hw_semaphores.h"
+#include <share/semaphores/inc/hwsem_hwp.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+#include <cm/engine/power_mgt/inc/power.h>
+static t_hw_semaphore_regs *pHwSemRegs = (t_hw_semaphore_regs *)0;
+
+static t_uint32 semaphoreUseCounter = 0;
+static t_uint32 imsc[HSEM_MAX_INTR];
+PRIVATE void restoreMask(void);
+
+/****************************************************************************/
+/* NAME: t_cm_error cm_HSEM_Init(const t_cm_system_address *pSystemAddr) */
+/*--------------------------------------------------------------------------*/
+/* DESCRIPTION: Initialize the HW Semaphores module */
+/* */
+/* PARAMETERS: */
+/* (in) pSystemAddr: system base address of the HW semaphores IP */
+/* */
+/* RETURN: CM_OK always */
+/* */
+/****************************************************************************/
+PUBLIC t_cm_error cm_HSEM_Init(const t_cm_system_address *pSystemAddr)
+{
+ t_uint8 i;
+
+ pHwSemRegs = (t_hw_semaphore_regs *)pSystemAddr->logical;
+
+ for (i=HSEM_FIRST_INTR; i < HSEM_MAX_INTR; i++)
+ {
+ imsc[i] = 0; // Mask all interrupt
+ }
+
+ return CM_OK;
+}
+
+static void cm_HSEM_ReInit(void)
+{
+ t_uint8 i;
+
+ pHwSemRegs->icrall = MASK_ALL16;
+
+ for (i=HSEM_FIRST_INTR; i < HSEM_MAX_INTR; i++)
+ {
+ pHwSemRegs->it[i].imsc = imsc[i];
+ pHwSemRegs->it[i].icr = MASK_ALL16;
+ }
+
+ for (i=0; i < NUM_HW_SEMAPHORES; i++)
+ {
+ pHwSemRegs->sem[i] = 0;
+ }
+}
+
+/****************************************************************************/
+/* NAME: t_cm_error cm_HSEM_EnableSemIrq( */
+/* t_semaphore_id semId, */
+/* t_nmf_core_id toCoreId */
+/* ) */
+/*--------------------------------------------------------------------------*/
+/* DESCRIPTION: Enable Irq for a given coreId (communication receiver) */
+/* */
+/* PARAMETERS: */
+/* (in) semId: identifier of the semaphore */
+/* (in) toCoreId: identifier of coreId destination of the coms */
+/* */
+/* RETURN: CM_OK always */
+/* */
+/****************************************************************************/
+PUBLIC t_cm_error cm_HSEM_EnableSemIrq(t_semaphore_id semId, t_nmf_core_id toCoreId)
+{
+ static t_uint32 CoreIdToIntr[NB_CORE_IDS] = {0, 2, 3};
+ int i = CoreIdToIntr[toCoreId];
+
+ imsc[i] |= (1UL << semId);
+
+ // Allow cm_HSEM_EnableSemIrq to be called before real start in order to save power
+ if(semaphoreUseCounter > 0)
+ {
+ pHwSemRegs->it[i].imsc = imsc[i];
+ }
+
+ return CM_OK;
+}
+
+/****************************************************************************/
+/* NAME: void cm_HSEM_GenerateIrq(t_semaphore_id semId) */
+/*--------------------------------------------------------------------------*/
+/* DESCRIPTION: Generate an irq toward correct core according to semId */
+/* */
+/* PARAMETERS: */
+/* (in) semId: identifier of the semaphore to handle */
+/* */
+/* RETURN: none */
+/* */
+/****************************************************************************/
+PUBLIC void cm_HSEM_GenerateIrq(t_nmf_core_id coreId, t_semaphore_id semId)
+{
+ // TODO Move restore in OS BSP or in PRCMU in order to to it only when wake-up, for now do it always !!!!!!!!!!!!
+ restoreMask();
+
+ pHwSemRegs->sem[semId] = CORE_ID_2_HW_CORE_ID(ARM_CORE_ID);
+ pHwSemRegs->sem[semId] = (HSEM_INTRA_MASK|HSEM_INTRB_MASK|HSEM_INTRC_MASK|HSEM_INTRD_MASK);
+}
+
+/****************************************************************************/
+/* NAME: t_nmf_core_id cm_HSEM_GetCoreIdFromIrqSrc(void) */
+/*--------------------------------------------------------------------------*/
+/* DESCRIPTION: Check Masked Interrupt Status to know which semaphore(s) */
+/* have pending interrupt and return the identifier of the given dsp */
+/* */
+/* PARAMETERS: none */
+/* */
+/* RETURN: none */
+/* */
+/****************************************************************************/
+PUBLIC t_nmf_core_id cm_HSEM_GetCoreIdFromIrqSrc(void)
+{
+ t_uword misValue = pHwSemRegs->it[ARM_CORE_ID].mis;
+ t_uint32 mask = 1 << FIRST_NEIGHBOR_SEMID(ARM_CORE_ID) /* == 0 here */;
+ t_nmf_core_id coreId = FIRST_MPC_ID;
+
+ while ((misValue & mask) == 0)
+ {
+ mask <<= 1;
+
+ coreId++;
+ if(coreId > LAST_MPC_ID)
+ return coreId;
+ }
+
+ /* Acknowledge Hsem interrupt */
+ pHwSemRegs->it[ARM_CORE_ID].icr = mask;
+
+ return coreId;
+}
+
+PUBLIC t_cm_error cm_HSEM_PowerOn(t_nmf_core_id coreId)
+{
+ if(semaphoreUseCounter++ == 0)
+ {
+ cm_PWR_EnableHSEM();
+
+ cm_HSEM_ReInit(); // HSEM is called one time only when the HSEM is switched ON
+ }
+
+ return CM_OK;
+}
+
+PUBLIC void cm_HSEM_PowerOff(t_nmf_core_id coreId)
+{
+ if(--semaphoreUseCounter == 0)
+ {
+ cm_PWR_DisableHSEM();
+ }
+}
+
+PRIVATE void restoreMask()
+{
+ t_uint8 i;
+
+ for (i=HSEM_FIRST_INTR; i < HSEM_MAX_INTR; i++)
+ pHwSemRegs->it[i].imsc = imsc[i];
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/semaphores/inc/semaphores.h b/drivers/staging/nmf-cm/cm/engine/semaphores/inc/semaphores.h
new file mode 100644
index 00000000000..7636d8e7c9d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/semaphores/inc/semaphores.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/**
+ * \internal
+ */
+#ifndef __INC_NMF_SEMAPHORE_H
+#define __INC_NMF_SEMAPHORE_H
+
+#include <cm/engine/api/control/configuration_engine.h>
+#include <share/semaphores/inc/semaphores.h>
+#include <cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h>
+
+PUBLIC t_cm_error cm_SEM_Init(const t_cm_system_address *pSystemAddr);
+PUBLIC t_cm_error cm_SEM_InitMpc(t_nmf_core_id coreId, t_nmf_semaphore_type_id semTypeId);
+PUBLIC t_semaphore_id cm_SEM_Alloc(t_nmf_core_id fromCoreId, t_nmf_core_id toCoreId);
+
+/* Semaphores management virtualized functions */
+extern void (*cm_SEM_GenerateIrq[NB_CORE_IDS])(t_nmf_core_id coreId, t_semaphore_id semId);
+extern t_cm_error (*cm_SEM_PowerOn[NB_CORE_IDS])(t_nmf_core_id coreId);
+extern void (*cm_SEM_PowerOff[NB_CORE_IDS])(t_nmf_core_id coreId);
+
+#endif /* __INC_NMF_SEMAPHORE_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/semaphores/src/semaphores.c b/drivers/staging/nmf-cm/cm/engine/semaphores/src/semaphores.c
new file mode 100644
index 00000000000..daf95355a56
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/semaphores/src/semaphores.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/inc/cm_type.h>
+#include <cm/engine/semaphores/inc/semaphores.h>
+#include <cm/engine/semaphores/hw_semaphores/inc/hw_semaphores.h>
+#include <cm/engine/dsp/inc/semaphores_dsp.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <share/inc/nmf.h>
+
+void (*cm_SEM_GenerateIrq[NB_CORE_IDS])(t_nmf_core_id coreId, t_semaphore_id semId);
+t_cm_error (*cm_SEM_PowerOn[NB_CORE_IDS])(t_nmf_core_id coreId);
+void (*cm_SEM_PowerOff[NB_CORE_IDS])(t_nmf_core_id coreId);
+
+#define SEM_TYPE_ID_DEFAULT_VALUE ((t_nmf_semaphore_type_id)MASK_ALL32)
+static t_nmf_semaphore_type_id semaphoreTypePerCoreId[NB_CORE_IDS];
+
+static t_cm_error cm_LSEM_PowerOn(t_nmf_core_id coreId)
+{
+ return CM_OK;
+}
+
+static void cm_LSEM_PowerOff(t_nmf_core_id coreId)
+{
+}
+
+PUBLIC t_cm_error cm_SEM_Init(const t_cm_system_address *pSystemAddr)
+{
+ t_nmf_core_id coreId;
+
+ for (coreId = ARM_CORE_ID; coreId < NB_CORE_IDS; coreId++)
+ {
+ semaphoreTypePerCoreId[coreId] = SEM_TYPE_ID_DEFAULT_VALUE;
+
+ /* By default, we suppose that we use a full feature NMF ;) */
+ cm_SEM_GenerateIrq[coreId] = NULL;
+ cm_SEM_PowerOn[coreId] = NULL;
+ cm_SEM_PowerOff[coreId] = NULL;
+ }
+
+ cm_HSEM_Init(pSystemAddr);
+ /* if needed local semaphore init will be done coreId per coreId */
+
+ return CM_OK;
+}
+
+PUBLIC t_cm_error cm_SEM_InitMpc(t_nmf_core_id coreId, t_nmf_semaphore_type_id semTypeId)
+{
+ if (semaphoreTypePerCoreId[coreId] != SEM_TYPE_ID_DEFAULT_VALUE)
+ return CM_MPC_ALREADY_INITIALIZED;
+
+ if(semTypeId == SYSTEM_SEMAPHORES)
+ {
+ cm_SEM_GenerateIrq[coreId] = cm_HSEM_GenerateIrq;
+ cm_SEM_PowerOn[coreId] = cm_HSEM_PowerOn;
+ cm_SEM_PowerOff[coreId] = cm_HSEM_PowerOff;
+ }
+ else if (semTypeId == LOCAL_SEMAPHORES)
+ {
+ cm_SEM_GenerateIrq[coreId] = cm_DSP_SEM_GenerateIrq;
+ cm_SEM_PowerOn[coreId] = cm_LSEM_PowerOn;
+ cm_SEM_PowerOff[coreId] = cm_LSEM_PowerOff;
+ }
+
+ semaphoreTypePerCoreId[coreId] = semTypeId;
+
+ return CM_OK;
+}
+
+PUBLIC t_semaphore_id cm_SEM_Alloc(t_nmf_core_id fromCoreId, t_nmf_core_id toCoreId)
+{
+ t_semaphore_id semId;
+ t_nmf_core_id corex;
+
+ semId = FIRST_NEIGHBOR_SEMID(toCoreId);
+ for (corex = FIRST_CORE_ID; corex < fromCoreId; corex++)
+ {
+ if (corex == toCoreId)
+ continue;
+ semId++;
+ }
+
+ if (
+ (toCoreId == ARM_CORE_ID && semaphoreTypePerCoreId[fromCoreId] == SYSTEM_SEMAPHORES) ||
+ (semaphoreTypePerCoreId[toCoreId] == SYSTEM_SEMAPHORES)
+ )
+ {
+ cm_HSEM_EnableSemIrq(semId, toCoreId);
+ }
+
+ return semId;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/trace/inc/trace.h b/drivers/staging/nmf-cm/cm/engine/trace/inc/trace.h
new file mode 100644
index 00000000000..111eaf1324e
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/trace/inc/trace.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Trace facilities management API
+ *
+ * \defgroup Trace Facilities
+ */
+#ifndef __INC_CM_TRACE_H
+#define __INC_CM_TRACE_H
+
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+#include <cm/engine/configuration/inc/configuration_status.h>
+
+/*********************/
+/* Log related stuff */
+/*********************/
+#define ERROR(format, param1, param2, param3, param4, param5, param6) \
+do { \
+ if (cm_debug_level != -1) \
+ OSAL_Log("Error: " format, (int)(param1), (int)(param2), (int)(param3), (int)(param4), (int)(param5), (int)(param6)); \
+ while(cm_error_break);\
+} while(0)
+
+#define WARNING(format, param1, param2, param3, param4, param5, param6) \
+do { \
+ if (cm_debug_level != -1) \
+ OSAL_Log("Warning: " format, (int)(param1), (int)(param2), (int)(param3), (int)(param4), (int)(param5), (int)(param6)); \
+} while(0)
+
+#define LOG_INTERNAL(level, format, param1, param2, param3, param4, param5, param6) \
+do { \
+ if (level <= cm_debug_level) \
+ OSAL_Log((const char *)format, (int)(param1), (int)(param2), (int)(param3), (int)(param4), (int)(param5), (int)(param6)); \
+} while(0)
+
+/*************************/
+/* Panic related stuff */
+/*************************/
+#define CM_ASSERT(cond) \
+do { \
+ if(!(cond)) { OSAL_Log("CM_ASSERT at %s:%d\n", (int)__FILE__, (int)__LINE__, 0, 0, 0, 0); OSAL_Panic(); while(1); } \
+} while (0)
+
+#endif /* __INC_CM_TRACE_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/trace/inc/xtitrace.h b/drivers/staging/nmf-cm/cm/engine/trace/inc/xtitrace.h
new file mode 100644
index 00000000000..6ab960b69b2
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/trace/inc/xtitrace.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#ifndef __INC_CM_XTITRACE_H
+#define __INC_CM_XTITRACE_H
+
+#include <cm/engine/component/inc/instance.h>
+
+#include <inc/nmf-tracedescription.h>
+
+extern t_bool cm_trace_enabled;
+
+/*************************/
+/* Trace related stuff */
+/*************************/
+void cm_TRC_Dump(void);
+
+void cm_TRC_traceReset(void);
+
+void cm_TRC_traceLoadMap(
+ t_nmfTraceComponentCommandDescription cmd,
+ const t_component_instance* component);
+
+#define ARM_TRACE_COMPONENT ((const t_component_instance*)0xFFFFFFFF)
+
+void cm_TRC_traceBinding(
+ t_nmfTraceBindCommandDescription command,
+ const t_component_instance* clientComponent, const t_component_instance* serverComponent,
+ const char *requiredItfName, const char *providedItfName);
+
+void cm_TRC_traceCommunication(
+ t_nmfTraceCommunicationCommandDescription command,
+ t_nmf_core_id coreId,
+ t_nmf_core_id remoteCoreId);
+
+void cm_TRC_traceMemAlloc(t_nmfTraceAllocatorCommandDescription command, t_uint8 allocId, t_uint32 memorySize, const char *allocname);
+
+void cm_TRC_traceMem(t_nmfTraceAllocCommandDescription command, t_uint8 allocId, t_uint32 startAddress, t_uint32 memorySize);
+
+#endif /* __INC_CM_TRACE_H */
diff --git a/drivers/staging/nmf-cm/cm/engine/trace/src/panic.c b/drivers/staging/nmf-cm/cm/engine/trace/src/panic.c
new file mode 100644
index 00000000000..832b9f28756
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/trace/src/panic.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include <cm/inc/cm_type.h>
+#include <cm/engine/component/inc/introspection.h>
+#include <cm/engine/component/inc/bind.h>
+#include <cm/engine/executive_engine_mgt/inc/executive_engine_mgt.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/api/control/irq_engine.h>
+
+#include <cm/engine/utils/inc/convert.h>
+#include <share/communication/inc/nmf_service.h>
+
+
+/*
+ * Panic
+ */
+const struct {
+ char* name;
+ unsigned int info1:1;
+ unsigned int PC:1;
+ unsigned int SP:1;
+ unsigned int interface:1;
+} reason_descrs[] = {
+ {"NONE_PANIC", 0, 0, 0, 0},
+ {"INTERNAL_PANIC", 1, 0, 0, 0},
+ {"Reserved Panic", 0, 0, 0, 0},
+ {"USER_STACK_OVERFLOW", 0, 1, 1, 0},
+ {"SYSTEM_STACK_OVERFLOW", 0, 1, 1, 0},
+ {"UNALIGNED_LONG_ACCESS", 0, 1, 0, 0},
+ {"EVENT_FIFO_OVERFLOW", 0, 0, 0, 1},
+ {"PARAM_FIFO_OVERFLOW", 0, 0, 0, 1},
+ {"INTERFACE_NOT_BINDED", 0, 0, 0, 0},
+ {"USER_PANIC", 1, 0, 0, 0}
+};
+
+static t_component_instance* getCorrespondingInstance(
+ t_panic_reason panicReason,
+ t_uint32 panicThis,
+ t_dup_char *itfName,
+ t_cm_instance_handle *instHandle) {
+ t_component_instance *instance;
+ t_uint32 k;
+
+ for (k=0; k<ComponentTable.idxMax; k++) {
+ if ((instance = componentEntry(k)) == NULL)
+ continue;
+ if(panicReason == PARAM_FIFO_OVERFLOW ||
+ panicReason == EVENT_FIFO_OVERFLOW) {
+ // Panic has been generated by binding component, search the client who has call it
+ // and return the client handle (not the BC one).
+ int i;
+
+ if(instance->thisAddress == panicThis && panicThis == 0) {
+ *itfName = "Internal NMF service";
+ *instHandle = ENTRY2HANDLE(instance, k);
+ return instance;
+ }
+
+ for(i = 0; i < instance->template->requireNumber; i++) {
+ int nb = instance->template->requires[i].collectionSize, j;
+ for(j = 0; j < nb; j++) {
+ if(instance->interfaceReferences[i][j].instance != NULL &&
+ instance->interfaceReferences[i][j].instance != (t_component_instance *)NMF_HOST_COMPONENT &&
+ instance->interfaceReferences[i][j].instance != (t_component_instance *)NMF_VOID_COMPONENT &&
+ instance->interfaceReferences[i][j].instance->thisAddress == panicThis)
+ {
+ *itfName = instance->template->requires[i].name;
+ *instHandle = ENTRY2HANDLE(instance, k);
+ return instance;
+ }
+ }
+ }
+ } else {
+ // The component which has generated the panic is the good one.
+
+ if(instance->thisAddress == panicThis) {
+ *itfName = "?";
+ *instHandle = ENTRY2HANDLE(instance, k);
+ return instance;
+ }
+ }
+ }
+
+ *itfName = "?";
+ *instHandle = 0;
+ return 0;
+}
+
+PUBLIC EXPORT_SHARED t_cm_error CM_ReadMPCString(
+ t_nmf_core_id coreId,
+ t_uint32 dspAddress,
+ char * buffer,
+ t_uint32 bufferSize) {
+
+ while(--bufferSize > 0)
+ {
+ char ch = cm_DSP_ReadXRamWord(coreId, dspAddress++);
+ if(ch == 0)
+ break;
+
+ *buffer++ = ch;
+ };
+
+ *buffer = 0;
+
+ // Reset panicReason
+ cm_writeAttribute(cm_EEM_getExecutiveEngine(coreId)->instance,
+ "rtos/commonpart/serviceReason", MPC_SERVICE_NONE);
+
+ return CM_OK;
+}
+
+/****************/
+/* Generic part */
+/****************/
+PUBLIC EXPORT_SHARED t_cm_error CM_getServiceDescription(
+ t_nmf_core_id coreId,
+ t_cm_service_type *srcType,
+ t_cm_service_description *srcDescr)
+{
+ t_uint32 serviceReason;
+ t_component_instance *ee;
+
+ // Acknowledge interrupt (do it before resetting panicReason)
+ cm_DSP_AcknowledgeDspIrq(coreId, DSP2ARM_IRQ_1);
+
+ ee = cm_EEM_getExecutiveEngine(coreId)->instance;
+
+ // Read panicReason
+ serviceReason = cm_readAttributeNoError(ee, "rtos/commonpart/serviceReason");
+ if(serviceReason == MPC_SERVICE_PRINT)
+ {
+ *srcType = CM_MPC_SERVICE_PRINT;
+
+ srcDescr->u.print.dspAddress = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo0");
+ srcDescr->u.print.value1 = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo1");
+ srcDescr->u.print.value2 = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo2");
+ }
+ else if(serviceReason != MPC_SERVICE_NONE)
+ {
+ t_uint32 panicThis;
+ t_dup_char itfName;
+ t_component_instance *instance;
+
+ *srcType = CM_MPC_SERVICE_PANIC;
+ srcDescr->u.panic.panicReason = (t_panic_reason)serviceReason;
+ srcDescr->u.panic.panicSource = MPC_EE;
+ srcDescr->u.panic.info.mpc.coreid = coreId;
+
+ // Read panicThis
+ panicThis = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo0");
+
+ instance = getCorrespondingInstance(srcDescr->u.panic.panicReason, panicThis, &itfName, &srcDescr->u.panic.info.mpc.faultingComponent);
+
+ LOG_INTERNAL(0, "Error: Panic(%s, %s), This=%x", cm_getDspName(coreId),
+ reason_descrs[srcDescr->u.panic.panicReason].name, (void*)panicThis, 0, 0, 0);
+
+ if(reason_descrs[srcDescr->u.panic.panicReason].interface != 0)
+ {
+ LOG_INTERNAL(0, ", interface=%s", itfName, 0, 0, 0, 0, 0);
+ }
+
+ if(reason_descrs[srcDescr->u.panic.panicReason].info1 != 0)
+ {
+ // Info 1
+ srcDescr->u.panic.info.mpc.panicInfo1 = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo1");
+
+ LOG_INTERNAL(0, ", Info=%x", srcDescr->u.panic.info.mpc.panicInfo1, 0, 0, 0, 0, 0);
+ }
+
+ if(reason_descrs[srcDescr->u.panic.panicReason].PC != 0)
+ {
+ t_uint32 DspAddress = 0xFFFFFFFF;
+ t_uint32 DspSize = 0x0;
+
+ // PC need to be read in rtos/commonpart/serviceInfo1
+ srcDescr->u.panic.info.mpc.panicInfo1 = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo1");
+
+ if(instance != 0)
+ {
+ cm_DSP_GetDspAddress(instance->memories[instance->template->codeMemory->id], &DspAddress);
+ cm_DSP_GetDspMemoryHandleSize(instance->memories[instance->template->codeMemory->id], &DspSize);
+ }
+
+ if(DspAddress <= srcDescr->u.panic.info.mpc.panicInfo1 &&
+ srcDescr->u.panic.info.mpc.panicInfo1 < (DspAddress + DspSize))
+ LOG_INTERNAL(0, ", PC:off=%x <abs=%x>",
+ srcDescr->u.panic.info.mpc.panicInfo1 - DspAddress,
+ srcDescr->u.panic.info.mpc.panicInfo1, 0, 0, 0, 0);
+ else
+ LOG_INTERNAL(0, ", PC:<abs=%x>", srcDescr->u.panic.info.mpc.panicInfo1, 0, 0, 0, 0, 0);
+ }
+
+ if(reason_descrs[srcDescr->u.panic.panicReason].SP != 0)
+ {
+ srcDescr->u.panic.info.mpc.panicInfo2 = cm_readAttributeNoError(ee, "rtos/commonpart/serviceInfo2");
+
+ LOG_INTERNAL(0, ", SP=%x", srcDescr->u.panic.info.mpc.panicInfo2, 0, 0, 0, 0, 0);
+ }
+
+ LOG_INTERNAL(0, "\n", 0, 0, 0, 0, 0, 0);
+
+ if(instance != 0)
+ {
+ LOG_INTERNAL(0, "Error: Component=%s<%s>\n",
+ instance->pathname, instance->template->name, 0, 0, 0, 0);
+ }
+
+ // We don't set rtos/commonpart/serviceReason = MPC_SERVICE_NONE, since we don't want the
+ // MMDSP to continue execution, and we put in in Panic state
+ cm_DSP_SetStatePanic(coreId);
+ }
+ else
+ {
+ *srcType = CM_MPC_SERVICE_NONE;
+ }
+
+ return CM_OK;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/trace/src/trace.c b/drivers/staging/nmf-cm/cm/engine/trace/src/trace.c
new file mode 100644
index 00000000000..ac948c767b4
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/trace/src/trace.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+#include "../inc/trace.h"
+#include "../inc/xtitrace.h"
+#include <inc/nmf-tracedescription.h>
+#include <inc/nmf-limits.h>
+#include <cm/engine/utils/inc/string.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+
+t_bool cm_trace_enabled = FALSE;
+
+/*
+ * STM message dump
+ */
+#define HEADER(t, s) ((t) | (s << 16))
+
+static void writeN(struct t_nmfTraceChannelHeader* header)
+{
+ t_uint64* data = (t_uint64*)header;
+ t_uint64 *end = (t_uint64*)(((unsigned int)data) + header->traceSize - sizeof(t_uint64));
+
+ while(data < end)
+ {
+ OSAL_Write64(CM_CHANNEL, 0, *data++);
+ }
+
+ OSAL_Write64(CM_CHANNEL, 1, *data);
+}
+
+void cm_TRC_Dump(void)
+{
+ t_uint32 i;
+
+ cm_TRC_traceReset();
+
+ for (i=0; i<ComponentTable.idxMax; i++)
+ {
+ if (componentEntry(i) != NULL)
+ cm_TRC_traceLoadMap(TRACE_COMPONENT_COMMAND_ADD, componentEntry(i));
+ }
+}
+
+void cm_TRC_traceReset(void)
+{
+ if(cm_trace_enabled)
+ {
+ struct t_nmfTraceReset trace;
+
+ trace.header.v = HEADER(TRACE_TYPE_RESET, sizeof(trace));
+
+ trace.minorVersion = TRACE_MINOR_VERSION;
+ trace.majorVersion = TRACE_MAJOR_VERSION;
+
+ writeN((struct t_nmfTraceChannelHeader*)&trace);
+ }
+}
+
+void cm_TRC_traceLoadMap(
+ t_nmfTraceComponentCommandDescription command,
+ const t_component_instance* component)
+{
+ if(cm_trace_enabled)
+ {
+ struct t_nmfTraceComponent trace;
+
+ /*
+ * Generate instantiate trace
+ */
+ trace.header.v = HEADER(TRACE_TYPE_COMPONENT, sizeof(trace));
+
+ trace.command = (t_uint16)command;
+ trace.domainId = (t_uint16)component->template->dspId + 1;
+ trace.componentContext = (t_uint32)component->thisAddress;
+ trace.componentUserContext = (t_uint32)component;
+ cm_StringCopy((char*)trace.componentLocalName, component->pathname, MAX_COMPONENT_NAME_LENGTH);
+ cm_StringCopy((char*)trace.componentTemplateName, component->template->name, MAX_TEMPLATE_NAME_LENGTH);
+
+ writeN((struct t_nmfTraceChannelHeader*)&trace);
+
+ if(command == TRACE_COMPONENT_COMMAND_ADD)
+ {
+ struct t_nmfTraceMethod tracemethod;
+ int i, j, k;
+
+ /*
+ * Generate method trace
+ */
+ tracemethod.header.v = HEADER(TRACE_TYPE_METHOD, sizeof(tracemethod));
+
+ tracemethod.domainId = (t_uint16)component->template->dspId + 1;
+ tracemethod.componentContext = (t_uint32)component->thisAddress;
+
+ for(i = 0; i < component->template->provideNumber; i++)
+ {
+ t_interface_provide* provide = &component->template->provides[i];
+
+ for(j = 0; j < provide->collectionSize; j++)
+ {
+ for(k = 0; k < provide->interface->methodNumber; k++)
+ {
+ tracemethod.methodId = provide->indexes[j][k].methodAddresses;
+
+ cm_StringCopy((char*)tracemethod.methodName, provide->interface->methodNames[k], MAX_INTERFACE_METHOD_NAME_LENGTH);
+
+ writeN((struct t_nmfTraceChannelHeader*)&tracemethod);
+ }
+ }
+ }
+ }
+ }
+}
+
+void cm_TRC_traceBinding(
+ t_nmfTraceBindCommandDescription command,
+ const t_component_instance* clientComponent, const t_component_instance* serverComponent,
+ const char *requiredItfName, const char *providedItfName)
+{
+ if(cm_trace_enabled)
+ {
+ struct t_nmfTraceBind trace;
+
+ trace.header.v = HEADER(TRACE_TYPE_BIND, sizeof(trace));
+
+ trace.command = (t_uint16)command;
+
+ if(clientComponent == ARM_TRACE_COMPONENT) // ARM
+ {
+ trace.clientDomainId = 0x1;
+ trace.clientComponentContext = 0x0;
+ }
+ else
+ {
+ trace.clientDomainId = (t_uint16)clientComponent->template->dspId + 1;
+ trace.clientComponentContext = (t_uint32)clientComponent->thisAddress;
+ }
+ if(requiredItfName != NULL)
+ cm_StringCopy((char*)trace.requiredItfName, requiredItfName, MAX_INTERFACE_NAME_LENGTH);
+ else
+ trace.requiredItfName[0] = 0;
+
+ if(serverComponent == NULL)
+ { // Unbind or VOID
+ trace.serverDomainId = 0;
+ trace.serverComponentContext = 0x0;
+ }
+ else if(serverComponent == ARM_TRACE_COMPONENT)
+ { // ARM
+ trace.serverDomainId = 0x1;
+ trace.serverComponentContext = 0x0;
+ }
+ else
+ {
+ trace.serverDomainId = (t_uint16)serverComponent->template->dspId + 1;
+ trace.serverComponentContext = (t_uint32)serverComponent->thisAddress;
+ }
+ if(providedItfName != NULL)
+ cm_StringCopy((char*)trace.providedItfName, providedItfName, MAX_INTERFACE_NAME_LENGTH);
+ else
+ trace.providedItfName[0] = 0;
+
+ writeN((struct t_nmfTraceChannelHeader*)&trace);
+ }
+}
+
+void cm_TRC_traceCommunication(
+ t_nmfTraceCommunicationCommandDescription command,
+ t_nmf_core_id coreId,
+ t_nmf_core_id remoteCoreId)
+{
+ if(cm_trace_enabled)
+ {
+ struct t_nmfTraceCommunication trace;
+
+ trace.header.v = HEADER(TRACE_TYPE_COMMUNICATION, sizeof(trace));
+
+ trace.command = (t_uint16)command;
+ trace.domainId = (t_uint16)coreId + 1;
+ trace.remoteDomainId = (t_uint16)remoteCoreId + 1;
+
+ writeN((struct t_nmfTraceChannelHeader*)&trace);
+ }
+}
+
+void cm_TRC_traceMemAlloc(t_nmfTraceAllocatorCommandDescription command, t_uint8 allocId, t_uint32 memorySize, const char *allocname)
+{
+ if(cm_trace_enabled)
+ {
+ struct t_nmfTraceAllocator trace;
+
+ trace.header.v = HEADER(TRACE_TYPE_ALLOCATOR, sizeof(trace));
+
+ trace.command = (t_uint16)command;
+ trace.allocId = (t_uint16)allocId;
+ trace.size = memorySize;
+ cm_StringCopy((char*)trace.name, allocname, sizeof(trace.name));
+
+ writeN((struct t_nmfTraceChannelHeader*)&trace);
+ }
+}
+
+void cm_TRC_traceMem(t_nmfTraceAllocCommandDescription command, t_uint8 allocId, t_uint32 startAddress, t_uint32 memorySize)
+{
+ if(cm_trace_enabled)
+ {
+ struct t_nmfTraceAlloc trace;
+
+ trace.header.v = HEADER(TRACE_TYPE_ALLOC, sizeof(trace));
+
+ trace.command = (t_uint16)command;
+ trace.allocId = (t_uint16)allocId;
+ trace.offset = startAddress;
+ trace.size = memorySize;
+
+ writeN((struct t_nmfTraceChannelHeader*)&trace);
+ }
+}
+
diff --git a/drivers/staging/nmf-cm/cm/engine/utils/inc/convert.h b/drivers/staging/nmf-cm/cm/engine/utils/inc/convert.h
new file mode 100644
index 00000000000..d6912e58687
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/utils/inc/convert.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Conversion utility methods.
+ */
+#ifndef H_CM_CONVERTS_MEM
+#define H_CM_CONVERTS_MEM
+
+#include <share/inc/nmf.h>
+
+/*
+ * Utils convert methods
+ */
+const char* cm_getDspName(t_nmf_core_id dsp);
+
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/utils/inc/mem.h b/drivers/staging/nmf-cm/cm/engine/utils/inc/mem.h
new file mode 100644
index 00000000000..c950a94023d
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/utils/inc/mem.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Memory manipulation.
+ */
+#ifndef H_CM_UTILS_MEM
+#define H_CM_UTILS_MEM
+
+/*
+ * Utils libc methods
+ */
+void cm_MemCopy(void* dest, const void *src, int count);
+void cm_MemSet(void *str, int c, int count);
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/utils/inc/string.h b/drivers/staging/nmf-cm/cm/engine/utils/inc/string.h
new file mode 100644
index 00000000000..d2b7c0b0823
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/utils/inc/string.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief String manipulation.
+ */
+#ifndef H_CM_UTILS_STRING
+#define H_CM_UTILS_STRING
+
+#include <cm/engine/memory/inc/memory.h>
+
+#define MAX_INTERNAL_STRING_LENGTH 2048
+
+typedef const char *t_dup_char;
+
+t_dup_char cm_StringGet(const char* str);
+t_dup_char cm_StringReference(t_dup_char str);
+t_dup_char cm_StringDuplicate(const char* orig);
+void cm_StringRelease(t_dup_char orig);
+
+/*
+ * Utils libc methods
+ */
+void cm_StringCopy(char* dest, const char* src, int count);
+int cm_StringCompare(const char* str1, const char* str2, int count);
+int cm_StringLength(const char * str, int count);
+void cm_StringConcatenate(char* dest, const char* src, int count);
+char* cm_StringSearch(const char* str, int c);
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/utils/inc/swap.h b/drivers/staging/nmf-cm/cm/engine/utils/inc/swap.h
new file mode 100644
index 00000000000..e4f5acb3010
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/utils/inc/swap.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Swap integer manipulation.
+ */
+#ifndef H_CM_UTILS_SWAP
+#define H_CM_UTILS_SWAP
+
+#include <cm/inc/cm_type.h>
+
+/*
+ * Swap methods
+ */
+t_uint16 swap16(t_uint16 x);
+t_uint32 swap32(t_uint32 x);
+t_uint64 swap64(t_uint64 x);
+t_uint32 noswap32(t_uint32 x);
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/utils/inc/table.h b/drivers/staging/nmf-cm/cm/engine/utils/inc/table.h
new file mode 100644
index 00000000000..9d9828a81f6
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/utils/inc/table.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*!
+ * \internal
+ * \brief Dynamic table manipulation.
+ */
+#ifndef H_CM_UTILS_TABLE
+#define H_CM_UTILS_TABLE
+
+#include <cm/inc/cm_type.h>
+
+/*
+ This implement a (generic) dynamic table (the size is dynamic)
+ to register some pointers of a given kind of elements
+
+ It also allows to compute/convert each kernel pointer registered in the
+ table to a user handler, that can be checked.
+
+ The "user" handler is composed by the index in this table
+ (the low INDEX_SHIFT bits) and the low bits of the "local" pointer
+ shifted by INDEX_SHIFT are stored in the high bits:
+
+ handle bits: 31 ................................ 12 11 ...... 0
+ | lower bits of of the local pointer | index |
+
+ This allows a straight translation from a user handle to a local pointer
+ + a strong check to validate the value of a user handle.
+ The reverse translation from pointer to a user handle is
+ slower as it requires an explicit search in the list.
+ */
+
+
+/* INDEX_SHIFT determines the index size and thus the max index */
+#define INDEX_SHIFT 12
+#define INDEX_MAX (1UL << INDEX_SHIFT)
+#define INDEX_MASK (INDEX_MAX-1)
+#define ENTRY2HANDLE(pointer, index) (((unsigned int)pointer << INDEX_SHIFT) | index)
+#define TABLE_DEF_SIZE 0x1000
+
+typedef struct {
+ t_uint32 idxNb; /**< number of entries used */
+ t_uint32 idxCur; /**< current index: point to next supposed
+ free entry: used to look for the next
+ free entry */
+ t_uint32 idxMax; /**< index max currently allowed */
+ void **entries; /**< table itself */
+} t_nmf_table;
+
+t_cm_error cm_initTable(t_nmf_table* table);
+void cm_destroyTable(t_nmf_table* table);
+t_uint32 cm_addEntry(t_nmf_table *table, void *entry);
+void cm_delEntry(t_nmf_table *table, t_uint32 idx);
+void *cm_lookupEntry(const t_nmf_table *table, const t_uint32 hdl);
+t_uint32 cm_lookupHandle(const t_nmf_table *table, const void *entry);
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/engine/utils/src/convert.c b/drivers/staging/nmf-cm/cm/engine/utils/src/convert.c
new file mode 100644
index 00000000000..ad6e097bfe6
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/utils/src/convert.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ *
+ */
+#include <cm/engine/utils/inc/convert.h>
+
+const char* dspNames[NB_CORE_IDS] = {
+ "ARM",
+ "SVA",
+ "SIA"
+};
+
+
+const char* cm_getDspName(t_nmf_core_id dsp) {
+ return dspNames[dsp];
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/utils/src/mem.c b/drivers/staging/nmf-cm/cm/engine/utils/src/mem.c
new file mode 100644
index 00000000000..130a044bbf8
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/utils/src/mem.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ *
+ */
+#include <cm/engine/utils/inc/mem.h>
+
+
+/*
+ * Methods
+ */
+void cm_MemCopy(void* dest, const void *src, int count) {
+ char *tmp = (char *) dest, *s = (char *) src;
+
+ while (count--)
+ *tmp++ = *s++;
+}
+
+void cm_MemSet(void *str, int c, int count) {
+ char *tmp = (char *)str;
+
+ while (count--)
+ *tmp++ = c;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/utils/src/string.c b/drivers/staging/nmf-cm/cm/engine/utils/src/string.c
new file mode 100644
index 00000000000..0ed6fb3a214
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/utils/src/string.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ *
+ * Shared string manipulation.
+ * TODO This is a list today, must be a hash later !!!!!
+ */
+#include <cm/engine/utils/inc/string.h>
+#include <cm/engine/trace/inc/trace.h>
+
+#include <cm/engine/memory/inc/memory.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+
+#define NHASH 257 //Use a prime number!
+#define MULT 17
+
+/*
+ * Data
+ */
+struct t_linkedstring
+{
+ struct t_linkedstring *next;
+ int referencer;
+ char string[1];
+};
+
+static struct t_linkedstring *list[NHASH];
+
+#define myoffsetof(st, m) \
+ ((int) ( (char *)&((st *)(0))->m - (char *)0 ))
+
+unsigned int hash(const char *str)
+{
+ unsigned int h = 0;
+ for(; *str; str++)
+ h = MULT * h + *str;
+ return h % NHASH;
+}
+/*
+ * Methods
+ */
+PRIVATE struct t_linkedstring *lookupString(
+ const char* str,
+ struct t_linkedstring *first)
+{
+ while(first != 0)
+ {
+ if(cm_StringCompare(str, first->string, MAX_INTERNAL_STRING_LENGTH) == 0)
+ break;
+ first = first->next;
+ }
+
+ return first;
+}
+
+t_dup_char cm_StringGet(const char* str)
+{
+ struct t_linkedstring *entry;
+
+ entry = lookupString(str, list[hash(str)]);
+ CM_ASSERT(entry != 0);
+
+ return (t_dup_char)entry->string;
+}
+
+t_dup_char cm_StringReference(t_dup_char str)
+{
+ struct t_linkedstring* entry = (struct t_linkedstring*)((t_uint32)str - myoffsetof(struct t_linkedstring, string));
+
+ // One more referencer
+ entry->referencer++;
+
+ return (t_dup_char)entry->string;
+}
+
+t_dup_char cm_StringDuplicate(const char* str)
+{
+ struct t_linkedstring *entry;
+ unsigned int h;
+
+ h = hash(str);
+ entry = lookupString(str, list[h]);
+ if(entry != 0)
+ {
+ // One more referencer
+ entry->referencer++;
+ }
+ else
+ {
+ // Allocate new entry
+ entry = (struct t_linkedstring *)OSAL_Alloc(sizeof(struct t_linkedstring)-1 + cm_StringLength(str, MAX_INTERNAL_STRING_LENGTH)+1);
+ if(entry == NULL)
+ return NULL;
+
+ entry->referencer = 1;
+ cm_StringCopy(entry->string, str, MAX_INTERNAL_STRING_LENGTH);
+
+ // Link it in list
+ entry->next = list[h];
+ list[h] = entry;
+ }
+
+ return (t_dup_char)entry->string;
+}
+
+void cm_StringRelease(t_dup_char str)
+{
+ if(str != NULL)
+ {
+ struct t_linkedstring* entry = (struct t_linkedstring*)((t_uint32)str - myoffsetof(struct t_linkedstring, string));
+
+ // One less referencer
+ entry->referencer--;
+
+ if(entry->referencer == 0)
+ {
+ int h = hash(entry->string);
+
+ if(list[h] == entry) // This first first one
+ {
+ list[h] = entry->next;
+ }
+ else
+ {
+ struct t_linkedstring *tmp = list[h];
+
+ // Here we assume that entry is in the list
+ while(/*tmp != NULL && */tmp->next != entry)
+ tmp = tmp->next;
+
+ tmp->next = entry->next;
+ }
+ OSAL_Free(entry);
+ }
+ }
+}
+
+#if 0
+void checkString()
+{
+ struct t_linkedstring *tmp = list;
+
+ while(tmp != 0)
+ {
+ printf(" stay %s %d\n", tmp->string, tmp->referencer);
+ tmp = tmp->next;
+ }
+}
+#endif
+
+/*
+ * LibC method
+ */
+void cm_StringCopy(char* dest, const char *src, int count)
+{
+ while (count-- && (*dest++ = *src++) != '\0')
+ /* nothing */
+ ;
+}
+#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080)
+
+int cm_StringCompare(const char* str1, const char* str2, int count)
+{
+ /* If s1 and s2 are word-aligned, compare them a word at a time. */
+ if ((((int)str1 & 3) | ((int)str2 & 3)) == 0)
+ {
+ unsigned int *a1 = (unsigned int*)str1;
+ unsigned int *a2 = (unsigned int*)str2;
+
+ while (count >= sizeof (unsigned int) && *a1 == *a2)
+ {
+ count -= sizeof (unsigned int);
+
+ /* If we've run out of bytes or hit a null, return zero since we already know *a1 == *a2. */
+ if (count == 0 || DETECTNULL (*a1))
+ return 0;
+
+ a1++;
+ a2++;
+ }
+
+ /* A difference was detected in last few bytes of s1, so search bytewise */
+ str1 = (char*)a1;
+ str2 = (char*)a2;
+ }
+
+ while (count-- > 0 && *str1 == *str2)
+ {
+ /* If we've run out of bytes or hit a null, return zero
+ since we already know *s1 == *s2. */
+ if (count == 0 || *str1 == '\0')
+ return 0;
+ str1++;
+ str2++;
+ }
+
+ return (*(unsigned char *) str1) - (*(unsigned char *) str2);
+}
+
+int cm_StringLength(const char * str, int count)
+{
+ const char *sc;
+
+ for (sc = str; count-- && *sc != '\0'; ++sc)
+ /* nothing */
+ ;
+ return sc - str;
+}
+
+void cm_StringConcatenate(char* dest, const char* src, int count)
+{
+ while ((*dest) != '\0')
+ {
+ dest++;
+ count--;
+ }
+ cm_StringCopy(dest, src, count);
+}
+
+char* cm_StringSearch(const char* str, int c)
+{
+ for(; *str != (char) c; ++str)
+ if (*str == '\0')
+ return 0;
+ return (char *) str;
+}
diff --git a/drivers/staging/nmf-cm/cm/engine/utils/src/swap.c b/drivers/staging/nmf-cm/cm/engine/utils/src/swap.c
new file mode 100644
index 00000000000..e3e2d536144
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/utils/src/swap.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ *
+ */
+#include <cm/engine/utils/inc/swap.h>
+
+
+/*
+ * Methods
+ */
+t_uint16 swap16(t_uint16 x)
+{
+ return ((x >> 8) |
+ ((x << 8) & 0xff00U));
+}
+
+#ifdef LINUX
+
+#if defined(__STN_8815) /* __STN_8815 -> ARMv5*/
+t_uint32 swap32(t_uint32 x)
+{
+ asm volatile (
+ "EOR r1, r0, r0, ROR #16 \n\t"
+ "BIC r1, r1, #0xFF0000 \n\t"
+ "MOV r0, r0, ROR #8 \n\t"
+ "EOR r0, r0, r1, LSR #8"
+ : : : "r3" );
+
+ return x;
+}
+
+t_uint64 swap64(t_uint64 x)
+{
+ asm volatile (
+ "MOV r2, r1 \n\t"
+ " \n\t"
+ "EOR r3, r0, r0, ROR #16 \n\t"
+ "BIC r3, r3, #0xFF0000 \n\t"
+ "MOV r0, r0, ROR #8 \n\t"
+ "EOR r1, r0, r3, LSR #8 \n\t"
+ " \n\t"
+ "EOR r3, r2, r2, ROR #16 \n\t"
+ "BIC r3, r3, #0xFF0000 \n\t"
+ "MOV r2, r2, ROR #8 \n\t"
+ "EOR r0, r2, r3, LSR #8"
+ : : : "r3", "r2" );
+
+ return x;
+}
+#else /* -> ARMv6 or later */
+
+t_uint32 swap32(t_uint32 x)
+{
+ asm volatile (
+ "REV %0, %0"
+ : "+r"(x) : );
+
+ return x;
+}
+
+t_uint64 swap64(t_uint64 x)
+{
+ asm volatile (
+ "REV r2, %Q0 \n\t"
+ "REV %Q0, %R0 \n\t"
+ "MOV %R0, r2"
+ : "+&r" (x) : : "r2" );
+
+ return x;
+}
+
+#endif
+
+#else /* Symbian, Think -> We assume ARMCC */
+
+#if defined(__thumb__)
+
+t_uint32 swap32(t_uint32 x)
+{
+ return ((x >> 24) |
+ ((x >> 8) & 0xff00U) |
+ ((x << 8) & 0xff0000U) |
+ ((x << 24) & 0xff000000U));
+}
+
+t_uint64 swap64(t_uint64 x)
+{
+ return ((x >> 56) |
+ ((x >> 40) & 0xff00UL) |
+ ((x >> 24) & 0xff0000UL) |
+ ((x >> 8) & 0xff000000UL) |
+ ((x << 8) & 0xff00000000ULL) |
+ ((x << 24) & 0xff0000000000ULL) |
+ ((x << 40) & 0xff000000000000ULL) |
+ ((x << 56)));
+}
+
+#elif (__TARGET_ARCH_ARM < 6)
+
+__asm t_uint32 swap32(t_uint32 x)
+{
+ EOR r1, r0, r0, ROR #16
+ BIC r1, r1, #0xFF0000
+ MOV r0, r0, ROR #8
+ EOR r0, r0, r1, LSR #8
+
+ BX lr
+}
+
+__asm t_uint64 swap64(t_uint64 x)
+{
+ MOV r2, r1
+
+ EOR r3, r0, r0, ROR #16 // Swap low (r0) and store it in high (r1)
+ BIC r3, r3, #0xFF0000
+ MOV r0, r0, ROR #8
+ EOR r1, r0, r3, LSR #8
+
+ EOR r3, r2, r2, ROR #16 // Swap high (r2 = ex r1) and store it in low (r0)
+ BIC r3, r3, #0xFF0000
+ MOV r2, r2, ROR #8
+ EOR r0, r2, r3, LSR #8
+
+ BX lr
+}
+
+#else /* -> ARMv6 or later */
+
+__asm t_uint32 swap32(t_uint32 x)
+{
+ REV r0, r0
+
+ BX lr
+}
+
+__asm t_uint64 swap64(t_uint64 x)
+{
+ REV r2, r0
+ REV r0, r1
+ MOV r1, r2
+
+ BX lr
+}
+
+#endif
+
+#endif
+
+t_uint32 noswap32(t_uint32 x) {
+ return x;
+}
+
diff --git a/drivers/staging/nmf-cm/cm/engine/utils/src/table.c b/drivers/staging/nmf-cm/cm/engine/utils/src/table.c
new file mode 100644
index 00000000000..708396a01b2
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/engine/utils/src/table.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+/*
+ *
+ */
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+#include <cm/engine/trace/inc/trace.h>
+#include <cm/engine/utils/inc/mem.h>
+#include <cm/engine/utils/inc/table.h>
+
+/*
+ * Methods
+ */
+t_cm_error cm_initTable(t_nmf_table* table)
+{
+ table->idxMax = TABLE_DEF_SIZE / sizeof(table->entries);
+
+ table->entries = OSAL_Alloc_Zero(table->idxMax*sizeof(table->entries));
+
+ if (table->entries == NULL) {
+ table->idxMax = 0;
+ return CM_NO_MORE_MEMORY;
+ }
+
+ return CM_OK;
+}
+
+void cm_destroyTable(t_nmf_table* table)
+{
+ if (table->idxNb) {
+ ERROR("Attempt to free non-empty table !!!\n", 0, 0, 0, 0, 0, 0);
+ return;
+ }
+ OSAL_Free(table->entries);
+ table->idxMax = 0;
+}
+
+static t_cm_error cm_increaseTable(t_nmf_table* table)
+{
+ t_uint32 new_max;
+ void *mem;
+
+ if (table->idxMax == INDEX_MASK) {
+ ERROR("CM_NO_MORE_MEMORY: Maximum table entries reached\n", 0, 0, 0, 0, 0, 0);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ new_max = table->idxMax
+ + TABLE_DEF_SIZE / sizeof(table->entries);
+
+ if (new_max > INDEX_MAX)
+ new_max = INDEX_MAX;
+
+ mem = OSAL_Alloc(new_max * sizeof(table->entries));
+
+ if (mem == NULL) {
+ ERROR("CM_NO_MORE_MEMORY: Unable to allocate memory for a table\n", 0, 0, 0, 0, 0, 0);
+ return CM_NO_MORE_MEMORY;
+ }
+
+ cm_MemCopy(mem, table->entries,
+ table->idxMax*sizeof(table->entries));
+ cm_MemSet((void *)((t_uint32) mem + table->idxMax*sizeof(*table->entries)), 0,
+ (new_max-table->idxMax) * sizeof(*table->entries));
+
+ OSAL_Free(table->entries);
+ table->entries = mem;
+ table->idxMax = new_max;
+
+ return CM_OK;
+}
+
+/** cm_addEntry - Add an local pointer to an element to the list
+ *
+ * 1. Increase the size of the list if it's full
+ * 2. Search an empty entry
+ * 3. Add the element to the list
+ * 4. Compute and return the "user handle"
+ */
+t_uint32 cm_addEntry(t_nmf_table *table, void *entry)
+{
+ unsigned int i;
+ t_uint32 hdl = 0;
+
+ if (table->idxNb == table->idxMax)
+ cm_increaseTable(table);
+
+ for (i = table->idxCur;
+ table->entries[i] != 0 && i != (table->idxCur-1);
+ i = (i+1)%table->idxMax);
+
+ if (table->entries[i] == 0) {
+ table->entries[i] = entry;
+ table->idxCur = (i+1) % table->idxMax;
+ table->idxNb++;
+ hdl = ENTRY2HANDLE(entry, i);
+ } else
+ ERROR("No free entry found in table\n", 0, 0, 0, 0, 0, 0);
+
+ return hdl;
+}
+
+/** cm_delEntry - remove the given element from the list
+ *
+ * 1. Check if the handle is valid
+ * 2. Search the entry and free it
+ */
+void cm_delEntry(t_nmf_table *table, t_uint32 idx)
+{
+ table->entries[idx] = NULL;
+ table->idxNb--;
+}
+
+/** cm_lookupEntry - search the entry corresponding to
+ * the user handle.
+ *
+ * 1. Check if the handle is valid
+ * 2. Return a pointer to the element
+ */
+void *cm_lookupEntry(const t_nmf_table *table, const t_uint32 hdl)
+{
+ unsigned int idx = hdl & INDEX_MASK;
+
+ if ((idx >= table->idxMax)
+ || (((unsigned int)table->entries[idx] << INDEX_SHIFT) != (hdl & ~INDEX_MASK)))
+ return NULL;
+ else
+ return table->entries[idx];
+}
+
+/** cm_lookupHandle - search the handle corresponding
+ * to the given element
+ *
+ * 1. Check if the handler is valid or is a special handler
+ * 2. Loop in the table to retrieve the entry matching and return its value
+ */
+t_uint32 cm_lookupHandle(const t_nmf_table *table, const void *entry)
+{
+ t_uint32 i;
+
+ /* NULL is an invalid value that must be handle separatly
+ as it'll match all used/free entries value */
+ if (entry == NULL)
+ return 0;
+
+ for (i=0; i < table->idxMax; i++) {
+ if (table->entries[i] == entry)
+ return ENTRY2HANDLE(table->entries[i], i);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/nmf-cm/cm/inc/cm.h b/drivers/staging/nmf-cm/cm/inc/cm.h
new file mode 100644
index 00000000000..37ccb36a5ee
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/inc/cm.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef __INC_CM_H
+#define __INC_CM_H
+
+#include <cm/inc/cm_def.h>
+
+/********************************************************************************/
+/* Component Manager API prototypes */
+/********************************************************************************/
+
+/*
+ * User level wrapper
+ */
+#include <cm/proxy/api/cm_proxy.h>
+
+#endif /* __INC_CM_H */
diff --git a/drivers/staging/nmf-cm/cm/inc/cm_def.h b/drivers/staging/nmf-cm/cm/inc/cm_def.h
new file mode 100644
index 00000000000..dc7a1fdad66
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/inc/cm_def.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Component Manager API.
+ *
+ * This file contains the Component Manager API for manipulating components.
+ *
+ */
+
+#ifndef __INC_CM_DEF_H
+#define __INC_CM_DEF_H
+
+#include <cm/inc/cm_type.h>
+#include <inc/nmf-def.h>
+
+/*!
+ * \brief Get the version of the NMF CM engine at runtime
+ *
+ * This method should be used to query the version number of the
+ * NMF Component Manager engine at runtime. This is useful when using
+ * to check if version of the engine linked with application correspond
+ * to engine used for development.
+ *
+ * Such code can be used to check compatibility: \code
+ t_uint32 nmfversion;
+
+ // Print NMF version
+ CM_GetVersion(&nmfversion);
+ LOG("NMF Version %d-%d-%d\n",
+ VERSION_MAJOR(nmfversion),
+ VERSION_MINOR(nmfversion),
+ VERSION_PATCH(nmfversion));
+ if(NMF_VERSION != nmfversion) {
+ LOG("Error: Incompatible API version %d != %d\n", NMF_VERSION, nmfversion);
+ EXIT();
+ }
+ * \endcode
+ *
+ * \param[out] version Internal hardcoded version (use \ref VERSION_MAJOR, \ref VERSION_MINOR, \ref VERSION_PATCH macros to decode it).
+ *
+ * \ingroup CM
+ */
+PUBLIC IMPORT_SHARED void CM_GetVersion(t_uint32 *version);
+
+#endif /* __INC_CM_H */
diff --git a/drivers/staging/nmf-cm/cm/inc/cm_macros.h b/drivers/staging/nmf-cm/cm/inc/cm_macros.h
new file mode 100644
index 00000000000..2279c204a20
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/inc/cm_macros.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Component Manager Macros.
+ *
+ * \defgroup CM_MACROS NMF Macros (ANSI C99)
+ * The Component Manager Macros are provided to ease FromHost interface call and ToHost callback definition.
+ * \attention <b>These macros are only ANSI C99 compliant</b> (ARM RVCT 2.x/3.x, GNU gcc 4.x, ...)
+ * \ingroup CM_USER_API
+ */
+
+#ifndef __INC_CM_MACROS_H
+#define __INC_CM_MACROS_H
+
+/*
+ * The next macros are supported only with C Ansi 99, so....
+ */
+
+/*
+ * The Symbian environment dependency, computation which uses an old gnu cpp,
+ * does not accept "..." parameters.
+ * However the actual compiler (armcc) does.
+ * So remove the macro definitions when computing dependencies.
+ */
+#if ( defined(__CC_ARM) && !defined(__STRICT_ANSI__) ) || !defined(__SYMBIAN32__)
+
+/*
+ * Only for skilled eyes ;)
+ * The following macros are used to implement NMFCALL[VOID] and NMFMETH[VOID] macros in an elegant way
+ */
+#define WITH_PARAM(...) __VA_ARGS__)
+#define WITH_NOPARAM(...) )
+
+/*!
+ * \brief Macro to ease Host to Dsp interface calling
+ *
+ * \attention <b>This macro is only ANSI C99 compliant</b>
+ *
+ * The <i>NMFCALL</i> macro can be used to call one method of any previously FromHost bounded interface.\n
+ * From Host side, today, we have no way to mask the multi-instance handling, so
+ * this macro is provided to ease FromHost interface calling and to avoid any mistake into the THIS parameter passing.
+ *
+ * So, any fromHost interface method call like: \code
+ * itf.method(itf.THIS, param1, param2, ...);
+ * \endcode
+ * can be replaced by: \code
+ * NMFCALL(itf, method)(param1, param2, ...);
+ * \endcode
+ *
+ * \warning Don't forget to use NMFCALLVOID macro when declaring a FromHost interface method having none application parameter,
+ * else it will lead to erroneous C code expansion
+ * \see NMFCALLVOID
+ * \hideinitializer
+ * \ingroup CM_MACROS
+ */
+#define NMFCALL(itfHandle, itfMethodName) \
+ (itfHandle).itfMethodName((itfHandle).THIS, WITH_PARAM
+
+/*!
+ * \brief Macro to ease Host to Dsp interface calling (method without any user parameter)
+ *
+ * \attention <b>This macro is only ANSI C99 compliant</b>
+ *
+ * The <i>NMFCALLVOID</i> macro can be used to call one method (those without any user parameter) of any previously FromHost bounded interface.\n
+ * From Host side, today, we have no way to mask the multi-instance handling, so
+ * this macro is provided to ease FromHost interface calling and to avoid any mistake into the THIS parameter passing.
+ *
+ * So, any FromHost interface method call without any application parameter like:\code
+ * itf.method(itf.THIS);
+ * \endcode
+ * can be replaced by: \code
+ * NMFCALLVOID(itf, method)();
+ * \endcode
+ * \see NMFCALL
+ * \hideinitializer
+ * \ingroup CM_MACROS
+ */
+#define NMFCALLVOID(itfHandle, itfMethodName) \
+ (itfHandle).itfMethodName((itfHandle).THIS WITH_NOPARAM
+
+/*!
+ * \brief Macro to ease Dsp to Host interface method declaration
+ *
+ * \attention <b>This macro definition is only ANSI C99 compliant</b>
+ *
+ * The <i>NMFMETH</i> macro can be used to ease the ToHost interface method declaration.\n
+ * From Host side, today, we have no way to mask the multi-intance handling, so the user shall handle it by hand
+ * by passing the "component" context as first parameter of each ToHost interface method through the void *THIS parameter.
+ * This macro could avoid any mistake into the THIS parameter declaration when never used by the user code.
+ *
+ * So, any ToHost interface method declaration like:\code
+ * void mynotify(void *THIS, mytype1 myparam1, mytype2 myparam2, ...) {
+ * <body of the interface routine>
+ * }
+ * \endcode
+ * can be replaced by: \code
+ * void NMFMETH(mynotify)(mytype1 myparam1, mytype2 myparam2, ...) {
+ * <body of the interface routine>
+ * }
+ * \endcode
+ *
+ * \warning Don't forget to use NMFMETHVOID macro when declaring a ToHost interface method having none application parameter,
+ * else it will lead to erroneous C code expansion
+ *
+ * \see NMFMETHVOID
+ * \hideinitializer
+ * \ingroup CM_MACROS
+ */
+#define NMFMETH(itfMethodName) \
+ itfMethodName(void *THIS, WITH_PARAM
+
+/*!
+ * \brief Macro to ease Dsp to Host interface method declaration (method without any user parameter)
+ *
+ * \attention <b>This macro is only ANSI C99 compliant</b>
+ *
+ * The <i>NMFMETHVOID</i> macro can be used to ease the ToHost interface method (those without any user parameter) declaration.\n
+ * From Host side, today, we have no way to mask the multi-intance handling, so the user shall handle it by hand
+ * by passing the "component" context as first parameter of each ToHost interface method through the void *THIS parameter.
+ * This macro could avoid any mistake into the THIS parameter declaration when never used by the user code.
+ *
+ * So, any ToHost interface method declaration having none application parameter like:\code
+ * void mynotify(void *THIS) {
+ * <body of the interface routine>
+ * }
+ * \endcode
+ * can be replaced by: \code
+ * void NMFMETHVOID(mynotify)(void) {
+ * <body of the interface routine>
+ * }
+ * \endcode
+ *
+ * \see NMFMETH
+ * \hideinitializer
+ * \ingroup CM_MACROS
+ */
+#define NMFMETHVOID(itfMethodName) \
+ itfMethodName(void *THIS WITH_NOPARAM
+
+#endif /* not Symbian environment or compiling with ARMCC and not in strict ANSI */
+
+#endif /* __INC_CM_MACROS_H */
+
diff --git a/drivers/staging/nmf-cm/cm/inc/cm_type.h b/drivers/staging/nmf-cm/cm/inc/cm_type.h
new file mode 100644
index 00000000000..b2f97090a3a
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/inc/cm_type.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Component Manager types.
+ *
+ * This file contains the Component Manager types.
+ *
+ * \defgroup CM CM Type Definitions
+ * \ingroup CM_USER_API
+ */
+#ifndef _CM_TYPE_H_
+#define _CM_TYPE_H_
+
+#include <share/inc/nmf.h>
+#include <share/inc/macros.h>
+
+#include <nmf/inc/channel_type.h>
+
+/*!
+ * @defgroup t_cm_error t_cm_error
+ * \brief Description of the various errors returned by CM API routines
+ * @{
+ * \ingroup CM
+ */
+typedef t_nmf_error t_cm_error; //!< Error type returned by CM API routines
+
+/*********************************************************************************/
+/* WARNING: UPDATE CM_StringError() func each time an error is added/removed !!! */
+/* CM_StringError() is defined twice in: */
+/* nmf_core/host/cm/proxy/common/wrapper/src/wrapper.c */
+/* tests/src/common/nte/src/nte.c */
+/*********************************************************************************/
+#define CM_LAST_ERROR_ID ((t_cm_error)-128)
+#define CM_INTEGRATION_ERROR NMF_INTEGRATION_ERROR0 //!< \ref NMF_INTEGRATION_ERROR0
+
+ /* Communication */
+#define CM_FLUSH_MESSAGE NMF_FLUSH_MESSAGE //!< Message send after call to CM_FlushChannel()
+#define CM_BUFFER_OVERFLOW ((t_cm_error)-105) //!< Buffer overflow (interface binding message bigger than buffer)
+#define CM_USER_NOT_REGISTERED ((t_cm_error)-104) //!< User not registered
+#define CM_NO_MESSAGE NMF_NO_MESSAGE //!< \ref NMF_NO_MESSAGE
+#define CM_PARAM_FIFO_OVERFLOW ((t_cm_error)-102) //!< Param fifo overflow
+#define CM_INTERNAL_FIFO_OVERFLOW ((t_cm_error)-101) //!< Internal services fifo overflow (not returned to user)
+#define CM_MPC_NOT_RESPONDING ((t_cm_error)-100) //!< MPC not responding (either crash, interrupt handler too long, internal NMF fifo coms overflow, ...).
+
+ /* ELF & File system */
+#define CM_FS_ERROR ((t_cm_error)-96) //!< FileSystem error
+#define CM_NO_SUCH_FILE ((t_cm_error)-95) //!< No such file or directory
+#define CM_INVALID_ELF_FILE ((t_cm_error)-94) //!< File isn't a valid MMDSP ELF file
+#define CM_NO_SUCH_BASE ((t_cm_error)-93) //!< The memory base doesn't exist
+
+ /* Introspection */
+#define CM_NO_SUCH_ATTRIBUTE NMF_NO_SUCH_ATTRIBUTE //!< \ref NMF_NO_SUCH_ATTRIBUTE
+#define CM_NO_SUCH_PROPERTY NMF_NO_SUCH_PROPERTY //!< \ref NMF_NO_SUCH_PROPERTY
+
+ /* Component Life Cycle */
+#define CM_COMPONENT_NOT_STOPPED NMF_COMPONENT_NOT_STOPPED //!< \ref NMF_COMPONENT_NOT_STOPPED
+#define CM_COMPONENT_NOT_UNBINDED ((t_cm_error)-79) //!< Component must be fully unbinded before perform operation
+#define CM_COMPONENT_NOT_STARTED ((t_cm_error)-78) //!< Component must be started to perform operation
+#define CM_COMPONENT_WAIT_RUNNABLE ((t_cm_error)-76) //!< Component need acknowlegdment of life cycle start function before perform operation
+#define CM_REQUIRE_INTERFACE_UNBINDED ((t_cm_error)-75) //!< Required component interfaces must be binded before perform operation
+#define CM_INVALID_COMPONENT_HANDLE ((t_cm_error)-74) //!< Try to access a component already destroyed
+
+ /* Binder */
+#define CM_NO_SUCH_PROVIDED_INTERFACE NMF_NO_SUCH_PROVIDED_INTERFACE //!< \ref NMF_NO_SUCH_PROVIDED_INTERFACE
+#define CM_NO_SUCH_REQUIRED_INTERFACE NMF_NO_SUCH_REQUIRED_INTERFACE //!< \ref NMF_NO_SUCH_REQUIRED_INTERFACE
+#define CM_ILLEGAL_BINDING ((t_cm_error)-62) //!< Client and server interface type mismatch
+#define CM_ILLEGAL_UNBINDING ((t_cm_error)-61) //!< Try to unbind component with bad binding Factories
+#define CM_INTERFACE_ALREADY_BINDED NMF_INTERFACE_ALREADY_BINDED//!< \ref NMF_INTERFACE_ALREADY_BINDED
+#define CM_INTERFACE_NOT_BINDED NMF_INTERFACE_NOT_BINDED //!< \ref NMF_INTERFACE_NOT_BINDED
+
+ /* Loader */
+#define CM_BINDING_COMPONENT_NOT_FOUND ((t_cm_error)-48) //!< Binding Component template name don't exist on components repository (should be generated thanks nkitf tool)
+#define CM_COMPONENT_NOT_FOUND ((t_cm_error)-47) //!< Component template name doesn't exist on components repository
+#define CM_NO_SUCH_SYMBOL ((t_cm_error)-46) //!< Symbol name doesn't exported by the underlying component
+#define CM_COMPONENT_EXIST ((t_cm_error)-45) //!< Component name already exists in the component cache
+
+ /* Fifo management related ones */
+#define CM_FIFO_FULL ((t_cm_error)-40) //!< Fifo is full
+#define CM_FIFO_EMPTY ((t_cm_error)-39) //!< Fifo is empty
+#define CM_UNKNOWN_FIFO_ID ((t_cm_error)-38) //!< Fifo handle doesn't exist
+
+ /* Memory management related ones */
+#define CM_DOMAIN_VIOLATION ((t_cm_error)-33) //!< Domain violation
+#define CM_CREATE_ALLOC_ERROR ((t_cm_error)-32) //!< Error during allocator creation
+#define CM_UNKNOWN_MEMORY_HANDLE ((t_cm_error)-31) //!< Handle doesn't exists
+#define CM_NO_MORE_MEMORY NMF_NO_MORE_MEMORY //!< \ref NMF_NO_MORE_MEMORY
+#define CM_BAD_MEMORY_ALIGNMENT ((t_cm_error)-29) //!< Memory alignment wanted is not correct
+#define CM_MEMORY_HANDLE_FREED ((t_cm_error)-28) //!< Handle was alread freed
+#define CM_INVALID_DOMAIN_DEFINITION ((t_cm_error)-27) //!< Domain to be created is not correctly defined
+#define CM_INTERNAL_DOMAIN_OVERFLOW ((t_cm_error)-26) //!< Internal domain descriptor overflow (too many domains) //TODO, juraj, remove this error
+#define CM_INVALID_DOMAIN_HANDLE ((t_cm_error)-25) //!< Invalid domain handle
+#define CM_ILLEGAL_DOMAIN_OPERATION ((t_cm_error)-21) //!< Operation on a domain is illegal (like destroy of a domain with referenced components)
+
+ /* Media Processor related ones */
+#define CM_MPC_INVALID_CONFIGURATION ((t_cm_error)-24) //!< Media Processor Core invalid configuration
+#define CM_MPC_NOT_INITIALIZED ((t_cm_error)-23) //!< Media Processor Core not yet initialized
+#define CM_MPC_ALREADY_INITIALIZED ((t_cm_error)-22) //!< Media Processor Core already initialized
+//ERROR 21 is defined above, with the domains
+
+ /* Power Mgt related ones */
+#define CM_PWR_NOT_AVAILABLE ((t_cm_error)-16) //!< No modification of the state of the power input
+
+ /* Common errors */
+#define CM_OUT_OF_LIMITS ((t_cm_error)-3) //!< User reach an internal nmf limits of limits.h file
+#define CM_INVALID_PARAMETER NMF_INVALID_PARAMETER //!< \ref NMF_INVALID_PARAMETER
+#define CM_NOT_YET_IMPLEMENTED ((t_cm_error)-1) //!< CM API not yet implemented
+#define CM_OK NMF_OK //!< \ref NMF_OK
+
+/** @} */
+
+/*!
+ * \brief Definition of a physical memory address
+ * \ingroup MEMORY
+ */
+typedef t_uint32 t_cm_physical_address;
+
+/*!
+ * \brief Definition of a logical memory address
+ * \ingroup MEMORY
+ */
+typedef t_uint32 t_cm_logical_address;
+
+/*!
+ * \brief Definition of a system address into a system with MMU
+ * \ingroup MEMORY
+ */
+typedef struct {
+ t_cm_physical_address physical; //!< Physical memory address
+ t_cm_logical_address logical; //!< Logical memory address
+} t_cm_system_address;
+#define INVALID_SYSTEM_ADDRESS {(t_cm_physical_address)MASK_ALL32, (t_cm_logical_address)MASK_ALL32}
+
+
+/*!
+ * \brief Define a type used to manipulate size of various buffers
+ * \ingroup MEMORY
+ */
+typedef t_uint32 t_cm_size;
+
+#endif /* _CM_TYPE_H_ */
+
diff --git a/drivers/staging/nmf-cm/cm/os/api/channel_os.h b/drivers/staging/nmf-cm/cm/os/api/channel_os.h
new file mode 100644
index 00000000000..72f621cf1f1
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/os/api/channel_os.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Communication Component Manager internal API type.
+ */
+
+#ifndef CHANNEL_OS_H
+#define CHANNEL_OS_H
+
+#include <nmf/inc/channel_type.h>
+#include <nmf/inc/service_type.h>
+#include <cm/engine/communication/inc/communication_type.h>
+
+/*!
+ * \brief Internal channel identification.
+ *
+ * Same as t_nmf_channel meaning but this the channel used internaly by
+ * OS Integration part
+ *
+ * \ingroup CM_OS_API
+ */
+typedef t_uint32 t_os_channel;
+
+/*!
+ * \brief Invalid value for os_channel
+ *
+ * Invalid value for os channel.
+ *
+ * \ingroup CM_OS_API
+ */
+#define NMF_OS_CHANNEL_INVALID_HANDLE 0xffffffff
+
+/*!
+ * \brief Structure used for storing required parameters for Interface Callback
+ * messages.
+ *
+ * This struture is used internally by CM_GetMessage() and CM_ExecuteMessage() as
+ * the message content in the given buffer.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+typedef struct {
+ t_nmf_mpc2host_handle THIS; //!< Context of interface implementation
+ t_uint32 methodIndex; //!< Method index in interface
+ char params[1]; //!< Is of variable length concretely
+} t_interface_data;
+
+/*!
+ * \brief Structure used for storing required parameters for Service Callback
+ * messages.
+ *
+ * This struture is used internally by CM_GetMessage() and CM_ExecuteMessage() as
+ * the message content in the given buffer.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+typedef struct {
+ t_nmf_service_type type; //!< Type of the service message
+ t_nmf_service_data data;
+} t_service_data;
+
+typedef enum {
+ MSG_INTERFACE,
+ MSG_SERVICE
+} t_message_type;
+
+/*!
+ * \brief Structure used for storing required parameters for the internal NMF
+ * messages.
+ *
+ * This struture is used internally by CM_GetMessage() and CM_ExecuteMessage() as
+ * the message content in the given buffer.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+typedef struct {
+ t_message_type type; //!< Type of the nmf message
+ union {
+ t_interface_data itf;
+ t_service_data srv;
+ } data;
+} t_os_message;
+
+/*!
+ * \brief Structure used for storing required parameters for the internal NMF
+ * messages.
+ *
+ * This struture is used internally by CM_GetMessage() and CM_ExecuteMessage() as
+ * the message content in the given buffer.
+ *
+ * \ingroup CM_ENGINE_API
+ */
+typedef struct {
+ t_nmf_channel channel; //!< Channel (required to handle service message)
+ t_os_message osMsg;
+} t_nmf_message;
+
+#endif /* CHANNEL_OS_H */
diff --git a/drivers/staging/nmf-cm/cm/os/api/cm_os.h b/drivers/staging/nmf-cm/cm/os/api/cm_os.h
new file mode 100644
index 00000000000..5fd5f04dee0
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/os/api/cm_os.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief CM Operating System API.
+ *
+ * This file contains the Component Manager Operating System API.
+ */
+
+/*!
+ * \defgroup CM_OS_MODULE CM Driver
+ */
+
+/*!
+ * \defgroup CM_OS_API CM Operating System API (a.ka. CM Driver API)
+ *
+ * \note This API is not for user developers, this API is only an internal API.
+ *
+ * \warning All parameters in out from this API means that the parameter is a reference to a data that is complete by the call.
+ *
+ * This API is required by CM Proxy to call CM service and shall be implemented in driver user part.
+ *
+ * This API must be implemented by NMF OS integrator which must at the end call CM ENGINE API if existing.
+ * If kernel space can't access user space, a copying to temporary data must be implement in driver.
+ * \ingroup CM_OS_MODULE
+ */
+
+#include <cm/os/api/configuration_os.h>
+
+#include <cm/os/api/domain_os.h>
+
+#include <cm/os/api/component_os.h>
+
+#include <cm/os/api/memory_os.h>
+
+#include <cm/os/api/migration_os.h>
+
+#include <cm/os/api/communication_os.h>
+
+#include <cm/os/api/perfmeter_os.h>
+
+#include <cm/os/api/executive_engine_mgt_os.h>
+
+#include <cm/os/api/repository_mgt_os.h>
diff --git a/drivers/staging/nmf-cm/cm/os/api/communication_os.h b/drivers/staging/nmf-cm/cm/os/api/communication_os.h
new file mode 100644
index 00000000000..37ab40ec9ee
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/os/api/communication_os.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Communication User OS API.
+ *
+ * This file contains the Communication OS API for manipulating components.
+ *
+ */
+#ifndef COMMUNICATION_OS_H_
+#define COMMUNICATION_OS_H_
+
+#include <cm/os/api/channel_os.h>
+#include <cm/engine/communication/inc/communication_type.h>
+
+/*!
+ * \brief Allocate Event buffer where parameters will be marshalled.
+ *
+ * In order to optimize call, this method don't need to be exported to user space,
+ * but must be used by CM driver.
+ *
+ * See \ref HOST2MPC "Host->MPC binding" for seeing an integration example.
+ *
+ * \note This method is not called from user space!!!
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_event_params_handle CM_OS_AllocEvent(t_cm_bf_host2mpc_handle host2mpcId);
+
+/*!
+ * \brief Push a event in Fifo.
+ *
+ * In order to optimize call, this method don't need to be exported to user space,
+ * but must be used by CM driver.
+ *
+ * See \ref HOST2MPC "Host->MPC binding" for seeing an integration example.
+ *
+ * \note This method is not called from user space!!!
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_PushEvent(t_cm_bf_host2mpc_handle host2mpcId, t_event_params_handle h, t_uint32 methodIndex);
+
+/*!
+ * \brief Push a event in Fifo.
+ *
+ * In order to optimize call, this method need to be exported to user space
+ * and must be implemented by CM driver.
+ *
+ * See \ref HOST2MPC "Host->MPC binding" for seeing an integration example.
+ *
+ * \note No implementation of this method is provided in kernel CM engine!!!
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_PushEventWithSize(t_cm_bf_host2mpc_handle host2mpcId, t_event_params_handle h, t_uint32 size, t_uint32 methodIndex);
+
+/*!
+ * \brief Aknowledge a Fifo that the received event has been demarshalled.
+ *
+ * In order to optimize call, this method don't need to be exported to user space,
+ * but must be used by CM driver.
+ *
+ * See \ref MPC2HOST "MPC->Host binding" for seeing an integration example.
+ *
+ * \note This method is not called from user space!!!
+ *
+ * \ingroup CM_OS_API
+ */
+
+PUBLIC void CM_OS_AcknowledgeEvent(t_cm_bf_mpc2host_handle mpc2hostId);
+
+/*!
+ * \brief Get received message from specified fifo.
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetMessage(
+ t_os_channel channel,
+ t_os_message * message,
+ t_uint32 messagesize,
+ t_bool block);
+
+#endif /*COMMUNICATION_OS_H_*/
diff --git a/drivers/staging/nmf-cm/cm/os/api/component_os.h b/drivers/staging/nmf-cm/cm/os/api/component_os.h
new file mode 100644
index 00000000000..7430188ec5c
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/os/api/component_os.h
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Components Component Manager Operating System API.
+ *
+ * This file contains the Component Manager Operating System API for manipulating components.
+ *
+ */
+
+#ifndef COMPONENT_OS_H_
+#define COMPONENT_OS_H_
+
+#include <cm/engine/component/inc/component_type.h>
+#include <cm/engine/communication/inc/communication_type.h>
+#include <cm/os/api/channel_os.h>
+#include <inc/nmf-limits.h>
+
+/*!
+ * \brief Instantiate a new component.
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_InstantiateComponent(
+ const char templateName[MAX_TEMPLATE_NAME_LENGTH], //!< [in] templateName Null terminated string (Max size=\ref MAX_TEMPLATE_NAME_LENGTH)
+ t_cm_domain_id domainId, //!< [in] Domain
+ t_nmf_ee_priority priority, //!< [in] Component priority
+ const char localName[MAX_COMPONENT_NAME_LENGTH], //!< [in] Null terminated string (Max size=\ref MAX_COMPONENT_NAME_LENGTH)
+ const char *dataFile, //!< [in] Data File (null if take in cache), according your OS integration, don't forgot to do CopyFromUser
+ t_uint32 dataFileSize, //!< [in] Data File Size
+ t_cm_instance_handle *component //!< [out] Component handle
+ );
+
+/*!
+ * \brief Start a component.
+ *
+ * \param[in] component
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_StartComponent(
+ t_cm_instance_handle component);
+
+/*!
+ * \brief Stop a component.
+ *
+ * \param[in] component
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_StopComponent(
+ t_cm_instance_handle component);
+
+/*!
+ * \brief Destroy a component.
+ *
+ * \param[in] component
+ *
+ * \ingroup CM_ENGINE_API
+ */
+PUBLIC t_cm_error CM_OS_DestroyComponent(
+ t_cm_instance_handle component);
+
+/*!
+ * \brief Bind two components together.
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_BindComponent(
+ const t_cm_instance_handle client, //!<
+ const char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH], //!< Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ const t_cm_instance_handle server, //!<
+ const char providedItfServerName[MAX_INTERFACE_NAME_LENGTH], //!< Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ t_bool traced, //!< FALSE for synchronous binding, TRUE for traced one
+ const char *dataFileTrace, //!< Component file data in case on traced (Note: could be null if file already in cache)
+ t_uint32 dataFileTraceSize //!< Component file data size
+ );
+
+/*!
+ * \brief Unbind a component.
+ *
+ * \param[in] client
+ * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_UnbindComponent(
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName);
+
+/*!
+ * \brief Bind a component to void (silently ignore a call).
+ *
+ * \param[in] client
+ * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_BindComponentToVoid(
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName);
+
+/*!
+ * \brief Bind two components together in an asynchronous way
+ * (the components can be on the same MPC or on two different MPC)
+ *
+ * \param[in] client
+ * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[in] server
+ * \param[in] providedItfServerName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[in] fifosize
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_BindComponentAsynchronous(
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName,
+ const t_cm_instance_handle server,
+ const char* providedItfServerName,
+ t_uint32 fifosize,
+ t_cm_mpc_memory_type eventMemType,
+ const char *dataFileSkeletonOrEvent, t_uint32 dataFileSkeletonOrEventSize,
+ const char *dataFileStub, t_uint32 dataFileStubSize);
+
+/*!
+ * \brief Unbind a component previously binded asynchronously
+ *
+ * \param[in] client
+ * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_UnbindComponentAsynchronous(
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName);
+
+/*!
+ * \brief Bind the Host to a component.
+ *
+ * \param[in] server
+ * \param[in] providedItfServerName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[in] fifosize
+ * \param[out] host2mpcId
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_BindComponentFromCMCore(
+ const t_cm_instance_handle server,
+ const char* providedItfServerName,
+ t_uint32 fifosize,
+ t_cm_mpc_memory_type eventMemType,
+ t_cm_bf_host2mpc_handle *host2mpcId,
+ const char *dataFileSkeleton,
+ t_uint32 dataFileSkeletonSize);
+
+/*!
+ * \brief Unbind a component from the Host.
+ *
+ * \param[in] host2mpcId
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_UnbindComponentFromCMCore(t_cm_bf_host2mpc_handle host2mpcId);
+
+/*!
+ * \brief Bind a component to the Host.
+ *
+ * This method is called by CM Proxy. The implementation of this method must
+ * do wrapping required by OS integration and call \ref CM_ENGINE_BindComponentToCMCore.
+ *
+ * See \ref MPC2HOST "MPC->Host binding" for seeing an integration example.
+ *
+ * \param[in] channel The channel on which the bind must be done
+ * \param[in] client Handle of the client component
+ * \param[in] requiredItfClientName Interface name of the client component. Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[in] fifosize Number of simultaneous communication
+ * \param[in] upLayerThis Context of the upper layer (a.k.a CM Proxy)
+ * \param[out] mpc2hostId Handle to the MPC->Host binding.
+ *
+ * \note No implementation of this method is provided in kernel CM engine!!!
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_BindComponentToCMCore(
+ const t_os_channel channel,
+ const t_cm_instance_handle client,
+ const char *requiredItfClientName,
+ t_uint32 fifosize,
+ t_nmf_mpc2host_handle upLayerThis,
+ const char *dataFileStub,
+ t_uint32 dataFileStubSize,
+ t_cm_bf_mpc2host_handle *mpc2hostId);
+
+/*!
+ * \brief Unbind a component to the Host.
+ *
+ * \param[in] channel The channel on which the bind has been done
+ * \param[in] client Handle of the client component
+ * \param[in] requiredItfClientName Interface name of the client component. Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[out] upLayerThis Return the upLayerThis handle that have been gived during call to \ref CM_OS_BindComponentToCMCore.
+ * The purpose of this information was to allow easy free of user data without need to memorize them by top level user.
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_UnbindComponentToCMCore(
+ const t_os_channel channel,
+ const t_cm_instance_handle client,
+ const char *requiredItfClientName,
+ t_nmf_mpc2host_handle *upLayerThis);
+
+/*!
+ * \brief Read a value on an attribute exported by a component instance.
+ *
+ * \param[in] component
+ * \param[in] attrName Null terminated string (Max size=\ref MAX_ATTRIBUTE_NAME_LENGTH).
+ * \param[out] value
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_ReadComponentAttribute(
+ const t_cm_instance_handle component,
+ const char* attrName,
+ t_uint24 *value);
+
+/*!
+ * \brief Get the older component.
+ *
+ * \param[out] headerComponent
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetComponentListHeader(
+ t_cm_instance_handle *headerComponent);
+
+/*!
+ * \brief Get the next component.
+ *
+ * \param[in] prevComponent
+ * \param[out] nextComponent
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetComponentListNext(
+ const t_cm_instance_handle prevComponent,
+ t_cm_instance_handle *nextComponent);
+
+/*!
+ * \brief Get a component description
+ *
+ * \param[in] component
+ * \param[in] templateNameLength
+ * \param[in] localNameLength
+ * \param[out] templateName Null terminated string (Size=templateNameLength, Max size=\ref MAX_TEMPLATE_NAME_LENGTH).
+ * \param[out] coreId
+ * \param[out] localName Null terminated string (Size=localNameLength, Max size=\ref MAX_COMPONENT_NAME_LENGTH).
+ * \param[out] priority The priority of the component
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetComponentDescription(
+ const t_cm_instance_handle component,
+ char *templateName,
+ t_uint32 templateNameLength,
+ t_nmf_core_id *coreId,
+ char *localName,
+ t_uint32 localNameLength,
+ t_nmf_ee_priority *priority);
+
+/*!
+ * \brief Get number of interface required by a component.
+ *
+ * \param[in] component
+ * \param[out] numberRequiredInterfaces
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetComponentRequiredInterfaceNumber(
+ const t_cm_instance_handle component,
+ t_uint8 *numberRequiredInterfaces);
+
+/*!
+ * \brief Return information about required interface.
+ *
+ * \param[in] component
+ * \param[in] index
+ * \param[in] itfNameLength
+ * \param[in] itfTypeLength
+ * \param[out] itfName Null terminated string (Size=itfNameLength, Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[out] itfType Null terminated string (Size=itfTypeLength, Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH).
+ * \param[out] requireState
+ * \param[out] collectionSize
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetComponentRequiredInterface(
+ const t_cm_instance_handle component,
+ const t_uint8 index,
+ char *itfName,
+ t_uint32 itfNameLength,
+ char *itfType,
+ t_uint32 itfTypeLength,
+ t_cm_require_state *requireState,
+ t_sint16 *collectionSize);
+
+/*!
+ * \brief Get the component binded to a required interface.
+ *
+ * \param[in] component
+ * \param[in] itfName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[in] serverItfNameLength
+ * \param[out] server
+ * \param[out] serverItfName Null terminated string (Size=serverItfNameLength, Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetComponentRequiredInterfaceBinding(
+ const t_cm_instance_handle component,
+ const char *itfName,
+ t_cm_instance_handle *server,
+ char *serverItfName,
+ t_uint32 serverItfNameLength);
+
+/*!
+ * \brief Get number of interface provided by a component.
+ *
+ * \param[in] component
+ * \param[out] numberProvidedInterfaces
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetComponentProvidedInterfaceNumber(
+ const t_cm_instance_handle component,
+ t_uint8 *numberProvidedInterfaces);
+
+/*!
+ * \brief Return information about provided interface.
+ *
+ * \param[in] component
+ * \param[in] index
+ * \param[in] itfNameLength
+ * \param[in] itfTypeLength
+ * \param[out] itfName Null terminated string (Size=itfNameLength, Max size=\ref MAX_INTERFACE_NAME_LENGTH).
+ * \param[out] itfType Null terminated string (Size=itfTypeLength, Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH).
+ * \param[out] collectionSize
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetComponentProvidedInterface(
+ const t_cm_instance_handle component,
+ const t_uint8 index,
+ char *itfName,
+ t_uint32 itfNameLength,
+ char *itfType,
+ t_uint32 itfTypeLength,
+ t_sint16 *collectionSize);
+
+/*!
+ * \brief Get number of properties of a component.
+ *
+ * \param[in] component
+ * \param[out] numberProperties
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetComponentPropertyNumber(
+ const t_cm_instance_handle component,
+ t_uint8 *numberProperties);
+
+/*!
+ * \brief Return the name of a property.
+ *
+ * \param[in] component
+ * \param[in] index
+ * \param[in] propertyNameLength
+ * \param[out] propertyName Null terminated string (Size=propertyNameLength, Max size=\ref MAX_PROPERTY_NAME_LENGTH).
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetComponentPropertyName(
+ const t_cm_instance_handle component,
+ const t_uint8 index,
+ char *propertyName,
+ t_uint32 propertyNameLength);
+
+/*!
+ * \brief Get property value of a component.
+ *
+ * \param[in] component
+ * \param[in] propertyName
+ * \param[in] propertyValueLength
+ * \param[out] propertyValue Null terminated string (Size=propertyValueLength, Max size=\ref MAX_PROPERTY_VALUE_LENGTH).
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetComponentPropertyValue(
+ const t_cm_instance_handle component,
+ const char *propertyName,
+ char *propertyValue,
+ t_uint32 propertyValueLength);
+
+#endif /*COMPONENT_OS_H_*/
diff --git a/drivers/staging/nmf-cm/cm/os/api/configuration_os.h b/drivers/staging/nmf-cm/cm/os/api/configuration_os.h
new file mode 100644
index 00000000000..dfc44f94dff
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/os/api/configuration_os.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Configuration Component Manager User OS API.
+ *
+ * This file contains the Configuration CM OS API for manipulating CM.
+ *
+ */
+
+#ifndef CONFIGURATION_OS_H
+#define CONFIGURATION_OS_H
+
+#include <cm/engine/configuration/inc/configuration_type.h>
+#include <cm/os/api/channel_os.h>
+
+/*!
+ * \brief Dynamically set some debug parameters of the CM
+ *
+ * \param[in] aCmdID The command for the parameter to update
+ * \param[in] aParam The actual value to set for the given command
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_SetMode(t_cm_cmd_id aCmdID, t_sint32 aParam);
+
+#endif /* CONFIGURATION_OS_H */
diff --git a/drivers/staging/nmf-cm/cm/os/api/domain_os.h b/drivers/staging/nmf-cm/cm/os/api/domain_os.h
new file mode 100644
index 00000000000..875ccbe3c84
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/os/api/domain_os.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Public Component Manager Domain OS API.
+ *
+ * This file contains the Component Manager OS API for manipulating domains.
+ */
+#ifndef CM_DOMAIN_OS_H_
+#define CM_DOMAIN_OS_H_
+
+#include <cm/engine/memory/inc/domain_type.h>
+
+/*!
+ * \brief Create a memory domain.
+ *
+ * Creates a domain in the CM, valid for component instantiation or memory allocation.
+ *
+ * \param[in] domain Domain description
+ * \param[out] handle Domain handle
+ *
+ * \exception CM_INVALID_DOMAIN_DEFINITION
+ * \exception CM_INTERNAL_DOMAIN_OVERFLOW
+ * \exception CM_OK
+ *
+ * \return Error code.
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_CreateMemoryDomain(
+ const t_cm_domain_memory *domain,
+ t_cm_domain_id *handle);
+
+/*!
+ * \brief Create a scratch domain.
+ *
+ * Create a scratch memory domain. Scratch domains
+ * are used to perform overlapping allocations.
+ *
+ * \param[in] parentId Identifier of the parent domain.
+ * \param[in] domain Description of domain memories.
+ * \param[out] handle Idetifier of the created domain
+ *
+ * \exception CM_INVALID_DOMAIN_DEFINITION
+ * \exception CM_INTERNAL_DOMAIN_OVERFLOW
+ * \exception CM_NO_MORE_MEMORY
+ * \exception CM_OK
+ *
+ * \return Error code.
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_CreateMemoryDomainScratch(
+ const t_cm_domain_id parentId,
+ const t_cm_domain_memory *domain,
+ t_cm_domain_id *handle
+ );
+
+/*!
+ * \brief Destroy a memory domain.
+ *
+ * \param[in] domain Domain identifier to destroy.
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_DestroyMemoryDomain(const t_cm_domain_id domain);
+
+/*!
+ * \brief Retrieve the coreId for a given domain. Utility.
+
+ * \param[in] domainId Domain identifier.
+ * \param[out] coreId Core identifier.
+ *
+ * \exception CM_INVALID_DOMAIN_HANDLE Invalid domain handle
+ * \exception CM_OK
+ *
+ * \return Error code.
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetDomainCoreId(const t_cm_domain_id domainId, t_nmf_core_id *coreId);
+#endif
diff --git a/drivers/staging/nmf-cm/cm/os/api/executive_engine_mgt_os.h b/drivers/staging/nmf-cm/cm/os/api/executive_engine_mgt_os.h
new file mode 100644
index 00000000000..b96f5028919
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/os/api/executive_engine_mgt_os.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief CM executive engine management Engine API.
+ *
+ * This file contains the Component Manager executive engine management Engine API.
+ */
+#ifndef CM_EXECUTIVE_ENGINE_MANAGEMENT_OS_H_
+#define CM_EXECUTIVE_ENGINE_MANAGEMENT_OS_H_
+
+#include <cm/inc/cm_type.h>
+
+/*!
+ * \brief Return executive engine handle for given core
+ *
+ * \param[in] domainId The domain identifier for which we want executive engine handle.
+ * \param[out] executiveEngineHandle executive engine instance (null if the executive engine is not loaded)
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetExecutiveEngineHandle(
+ t_cm_domain_id domainId,
+ t_cm_instance_handle *executiveEngineHandle);
+
+#endif /*CM_EXECUTIVE_ENGINE_MANAGEMENT_OS_H_*/
diff --git a/drivers/staging/nmf-cm/cm/os/api/memory_os.h b/drivers/staging/nmf-cm/cm/os/api/memory_os.h
new file mode 100644
index 00000000000..bde4d1b9268
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/os/api/memory_os.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Public Component Manager Memory User OS API.
+ *
+ * This file contains the Component Manager OS API for manipulating memory.
+ */
+
+#ifndef CM_MEMORY_OS_H_
+#define CM_MEMORY_OS_H_
+
+#include <cm/engine/memory/inc/memory_type.h>
+
+PUBLIC t_cm_error CM_OS_Free(t_cm_memory_handle handle);
+
+
+/*!
+ * \brief Get the memory status of the standard memory allocator (CM_Alloc/CM_Free)
+ *
+ * \param[out] pStatus
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetAllocStatus(t_cm_allocator_status *pStatus);
+
+
+/*!
+ * \brief Allocate memory in a Media Processor Core memory
+ *
+ * \param[in] domainId
+ * \param[in] memType
+ * \param[in] size
+ * \param[in] memAlignment
+ * \param[out] pHandle
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_AllocMpcMemory(
+ t_cm_domain_id domainId,
+ t_cm_mpc_memory_type memType,
+ t_cm_size size,
+ t_cm_mpc_memory_alignment memAlignment,
+ t_cm_memory_handle *pHandle
+ );
+
+
+/*!
+ * \brief Free a MPC memory block.
+ *
+ * \param[in] handle
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_FreeMpcMemory(t_cm_memory_handle handle);
+
+/*!
+ * \brief Get the start address of the MPC memory block seen by the host CPU (physical and logical)
+ *
+ * The logical system address returned by this method is valid only in kernel space and the physical
+ * address is accessible only from kernel space too.
+ *
+ * \see OSMem "OS Memory management" for seeing an integration example.
+ *
+ * \param[in] handle
+ * \param[out] pSystemAddress
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetMpcMemorySystemAddress(
+ t_cm_memory_handle handle,
+ t_cm_system_address *pSystemAddress);
+
+/*!
+ * \brief Get the start address of the memory block seen by the Media Processor Core
+ *
+ * \param[in] handle
+ * \param[out] pMpcAddress
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetMpcMemoryMpcAddress(
+ t_cm_memory_handle handle,
+ t_uint32 *pMpcAddress);
+
+/*!
+ * \brief Get the memory status for given memory type of a given Media Processor Core
+ *
+ * \param[in] domainId
+ * \param[in] memType
+ * \param[out] pStatus
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetMpcMemoryStatus(
+ t_nmf_core_id coreId,
+ t_cm_mpc_memory_type memType,
+ t_cm_allocator_status *pStatus);
+
+#endif /* CM_MEMORY_OS_H_ */
+
diff --git a/drivers/staging/nmf-cm/cm/os/api/migration_os.h b/drivers/staging/nmf-cm/cm/os/api/migration_os.h
new file mode 100644
index 00000000000..4026dd61a1b
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/os/api/migration_os.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef MIGRATION_OS_H
+#define MIGRATION_OS_H
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/memory/inc/domain_type.h>
+
+PUBLIC t_cm_error CM_OS_Migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst);
+
+PUBLIC t_cm_error CM_OS_Unmigrate(void);
+
+#endif /* MIGRATION_OS_H */
diff --git a/drivers/staging/nmf-cm/cm/os/api/perfmeter_os.h b/drivers/staging/nmf-cm/cm/os/api/perfmeter_os.h
new file mode 100644
index 00000000000..579b366be4b
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/os/api/perfmeter_os.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief CM Performance Meter OS API.
+ *
+ * This file contains the Component Manager Performance Meter OS API.
+ */
+#ifndef CM_ENGINE_PERFMETER_OS_H_
+#define CM_ENGINE_PERFMETER_OS_H_
+
+#include <cm/engine/perfmeter/inc/perfmeter_type.h>
+
+/*!
+ * \brief MPC cpu load
+ *
+ * \param[in] coreId identification of mpc from which we want cpu load
+ * \param[out] mpcLoadCounter will contain mpc cpu load counters value if success
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_getMpcLoadCounter(
+ t_nmf_core_id coreId,
+ t_cm_mpc_load_counter *mpcLoadCounter);
+
+#endif /*CM_ENGINE_PERFMETER_OS_H_*/
diff --git a/drivers/staging/nmf-cm/cm/os/api/repository_mgt_os.h b/drivers/staging/nmf-cm/cm/os/api/repository_mgt_os.h
new file mode 100644
index 00000000000..9e4d6bf9693
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/os/api/repository_mgt_os.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Repository Component Manager User OS API.
+ *
+ * This file contains the Component Manager OS API for manipulating the components files.
+ */
+
+#ifndef REPOSITORY_MGT_OS_H_
+#define REPOSITORY_MGT_OS_H_
+
+#include <cm/engine/component/inc/component_type.h>
+#include <cm/engine/repository_mgt/inc/repository_type.h>
+#include <inc/nmf-limits.h>
+
+/*!
+ * \brief Get the name(s) of the component(s) to load.
+ *
+ * \param[in] client Handle of the client component (optional)
+ * \param[in] requiredItfClientName Null terminated string (Max size=\ref MAX_INTERFACE_NAME_LENGTH) (optional).
+ * \param[in] server Handle of the server component (optional)
+ * \param[in] providedItfServerName Null terminated string (Max size==\ref MAX_INTERFACE_NAME_LENGTH) (optional).
+ * \param[out] fileList List of required component(s).
+ * \param[in,out] listSize Initial size of the list as input. Updated with the number of entries really used.
+ * \param[out] type Interface type of the client required or server provided interface. Null terminated string (Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH) (optional) .
+ * \param[out] methodNumber Number of method in the interface type of the client required interface. (only used when called from CM_BindComponentToUser) (optional)
+ *
+ * \note It returns the component(s) name(s) to load, depending on the first four parameters.
+ *
+ * - If all 4 are NULL, it returns the name of the Executive Engine components to load
+ * - If 'client' is NULL, it returns the name of the required components for a Bind From CMCore.
+ * - If 'server' is NULL, it returns the name of the required components for a Bind To CMCore.
+ * - If none is NULL, it returns the name of the required components for an asynchronous binding
+ *
+ * The names are returned in fileList, whose initial size is specified in listSize.
+ * (sizeList must be the number of provided entries of \ref MAX_INTERFACE_TYPE_NAME_LENGTH length
+ * If not enough space is provided, CM_NO_MORE_MEMORY is returned
+ *
+ * sizeList is updated with the number entries really filled.
+ *
+ * This method is also used to retrieve the interface type when called from CM_BindComponentToUser and CM_BindComponentFromUser
+ * and the number of methods when called from CM_BindComponentToUser.
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_GetRequiredComponentFiles(
+ // IN
+ t_action_to_do action,
+ const t_cm_instance_handle client,
+ const char *requiredItfClientName,
+ const t_cm_instance_handle server,
+ const char *providedItfServerName,
+ // OUT component to be pushed
+ char fileList[][MAX_INTERFACE_TYPE_NAME_LENGTH],
+ t_uint32 listSize,
+ // OUT interface information
+ char type[MAX_INTERFACE_TYPE_NAME_LENGTH],
+ t_uint32 *methodNumber);
+
+/*!
+ * \brief Push a component into the CM Component Cache.
+ *
+ * \param[in] name Component name, null terminated string (Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH)
+ * \param[in] data Pointer to _user_ data of the component.
+ * \param[in] size Size of the data.
+ * \param[in] locked Specify if the component data must be persistent
+ *
+ * \note Push a component in the Component Cache
+ * The 'data' must be provided such a way that they can be freed by a call to OSAL_Free()
+ * The caller doesn't need and must NOT free the data, even in case of failure.
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_PushComponent(const char *name, const void *data, t_cm_size size);
+
+/*!
+ * \brief Remove a component from the CM Component Cache.
+ *
+ * \param[in] name Component name, null terminated string (Max size=\ref MAX_INTERFACE_TYPE_NAME_LENGTH)
+ *
+ * \ingroup CM_OS_API
+ */
+PUBLIC t_cm_error CM_OS_ReleaseComponent(const char *name);
+
+#endif /*REPOSITORY_MGT_OS_H_*/
diff --git a/drivers/staging/nmf-cm/cm/proxy/api/cm_proxy.h b/drivers/staging/nmf-cm/cm/proxy/api/cm_proxy.h
new file mode 100644
index 00000000000..d91c788b7be
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/api/cm_proxy.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \defgroup CM_USER_API CM User API
+ */
+/*!
+ * \defgroup CM_PROXY_MODULE CM Proxy
+ */
+#ifndef COMMON_WRAPPER_H
+#define COMMON_WRAPPER_H
+
+/*
+ * Configuration management part
+ */
+#include <cm/proxy/api/configuration_proxy.h>
+#include <cm/proxy/api/communication_proxy.h>
+
+/*
+ * Component management part
+ */
+#include <cm/proxy/api/component_proxy.h>
+#include <cm/proxy/api/user_proxy.h>
+
+/*
+ * Component management part
+ */
+#include <cm/proxy/api/introspection_proxy.h>
+
+/*
+ * Memory management part
+ */
+#include <cm/proxy/api/memory_proxy.h>
+
+#include <cm/proxy/api/migration_proxy.h>
+
+/*
+ * Domain management
+ */
+#include <cm/proxy/api/domain_proxy.h>
+
+/*
+ * Performance Meter part
+ */
+#include <cm/proxy/api/perfmeter_proxy.h>
+
+#endif /* COMMON_WRAPPER_H */
diff --git a/drivers/staging/nmf-cm/cm/proxy/api/communication_proxy.h b/drivers/staging/nmf-cm/cm/proxy/api/communication_proxy.h
new file mode 100644
index 00000000000..30756ce41bf
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/api/communication_proxy.h
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Stub configuration Component Manager API.
+ *
+ * This file contains the Component Manager API part for intializing NMF and registering Host generated stubs.
+ *
+ * \defgroup CM_CALLBACK_API CM Proxy CallBack API
+ * \ingroup CM_PROXY_MODULE
+ */
+
+#ifndef PROXY_COMMUNICATION_H
+#define PROXY_COMMUNICATION_H
+
+#include <cm/proxy/api/private/stub-requiredapi.h>
+
+/*****************************************************************************************/
+/* Component Manager dedicated (for host stubs registering purpose) macros definition */
+/*****************************************************************************************/
+
+/*!
+ * \brief Macro to be used used to register a set of Host stubs and skeletons
+ *
+ * This macro shall be called to register the generated Host stubs and skeletons.
+ *
+ * \note Host stubs are generated by: nmfitfc -host2mpc \<list of interface names\>.
+ * Host skels are generated by: nmfitfc -mpc2host \<list of interface names\>.
+ *
+ * \warning By example, if the generated Host stubs and skeletons are linked into a dll, then this macro should be called when loading and initializing the given dll.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+#define CM_REGISTER_STUBS_SKELS(name) \
+{ \
+ extern t_nmf_stub_register name##_stub_register; \
+ extern t_nmf_skel_register name##_skel_register; \
+ CM_INTERNAL_RegisterHost2MpcInterfaces(&name##_stub_register); \
+ CM_INTERNAL_RegisterMpc2HostInterfaces(&name##_skel_register); \
+}
+
+/*!
+ * \brief Macro to be used used to unregister a set of Host stubs and skeletons
+ *
+ * This macro shall be called to unregister the generated Host stubs and skeletons for example before
+ * unloading a DLL or at the end of the process.
+ *
+ * \note If you register N times an stubs and skeletons, you must unregister them N times too.
+ * \note Don't call this macro if you have call the corresponding register macro before.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+#define CM_UNREGISTER_STUBS_SKELS(name) \
+{ \
+ extern t_nmf_stub_register name##_stub_register; \
+ extern t_nmf_skel_register name##_skel_register; \
+ CM_INTERNAL_UnregisterHost2MpcInterfaces(&name##_stub_register); \
+ CM_INTERNAL_UnregisterMpc2HostInterfaces(&name##_skel_register); \
+}
+
+/*!
+ * \brief Macro to be used used to register a set of Host stubs
+ *
+ * This macro shall be called to register the generated Host stubs.
+ *
+ * \note Host stubs are generated by: nmfitfc -host2mpc \<list of interface names\>
+ *
+ * \warning By example, if the generated Host stubs are linked into a dll, then this macro should be called when loading and initializing the given dll.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+#define CM_REGISTER_STUBS(name) \
+{ \
+ extern t_nmf_stub_register name##_stub_register; \
+ CM_INTERNAL_RegisterHost2MpcInterfaces(&name##_stub_register); \
+}
+
+/*!
+ * \brief Macro to be used used to unregister a set of Host stubs
+ *
+ * This macro shall be called to unregister the generated Host stubs for example before
+ * unloading a DLL or at the end of the process.
+ *
+ * \note If you register N times a stubs, you must unregister them N times too.
+ * \note Don't call this macro if you have call the corresponding register macro before.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+#define CM_UNREGISTER_STUBS(name) \
+{ \
+ extern t_nmf_stub_register name##_stub_register; \
+ CM_INTERNAL_UnregisterHost2MpcInterfaces(&name##_stub_register); \
+}
+
+/*!
+ * \brief Macro to be used used to register a set of Host skeletons
+ *
+ * This macro shall be called to register the generated Host skeletons.
+ *
+ * \note Host skels are generated by: nmfitfc -mpc2host \<list of interface names\>
+ *
+ * \warning By example, if the generated Host skels are linked into a dll, then this macro should be called when loading and initializing the given dll.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+#define CM_REGISTER_SKELS(name) \
+{ \
+ extern t_nmf_skel_register name##_skel_register; \
+ CM_INTERNAL_RegisterMpc2HostInterfaces(&name##_skel_register); \
+}
+
+/*!
+ * \brief Macro to be used used to unregister a set of Host skeletons
+ *
+ * This macro shall be called to unregister the generated Host skeletons for example before
+ * unloading a DLL or at the end of the process.
+ *
+ * \note If you register N times a stubs, you must unregister them N times too.
+ * \note Don't call this macro if you have call the corresponding register macro before.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+#define CM_UNREGISTER_SKELS(name) \
+{ \
+ extern t_nmf_skel_register name##_skel_register; \
+ CM_INTERNAL_UnregisterMpc2HostInterfaces(&name##_skel_register); \
+}
+
+
+/*!
+ * \brief Host skeletons calling facilities.
+ *
+ * This method allow the message retrieved through CM_GetMessage to the right skeleton.
+ *
+ * \param[in] itfref Interface reference.
+ * \param[in] message Reference on the message to be unmarshalled.
+ *
+ * \ingroup CM_CALLBACK_API
+ */
+PUBLIC IMPORT_SHARED void CM_ExecuteMessage(
+ t_nmf_interface_desc * itfref,
+ char * message);
+
+/*!
+ * \brief Get received message from specified callback channel.
+ *
+ * This method can be used to retrieve callback message from CM Engine. Returned message could then
+ * be dispatch through CM_ExecuteMessage.
+ *
+ * \note This method must be fully synchronize with potential wrapping done in \ref CM_BindComponentToUser.
+ *
+ * In an environment where the client context give in \ref CM_BindComponentToUser is not wrapped,
+ * message could be dispatch in the code like that:
+ * \code
+{
+ char buffer[MAX_MESSAGE_SIZE];
+ t_nmf_interface_desc *itfref;
+ ...
+ if(CM_GetMessage(channel, &itfref, buffer, sizeof(buffer), FALSE) == CM_OK)
+ {
+ CM_ExecuteMessage(itfref, buffer);
+ }
+ ...
+}
+ * \endcode
+ * Note that this code is able to dispatch interface reference in C or C++.
+ *
+ * In an environment where the client context give in \ref CM_BindComponentToUser is wrapped,
+ * message could be dispatch in the code like that:
+ * \code
+{
+ char buffer[MAX_MESSAGE_SIZE];
+ ENSWrapperCtx *wrapper;
+ ...
+ if(CM_GetMessage(channel, (void*)&wrapper, buffer, sizeof(buffer), FALSE) == CM_OK)
+ {
+ CM_ExecuteMessage(wrapper->WrapperItfref, buffer);
+ }
+ ...
+}
+ * \endcode
+ * And where, the wrapping code of \ref CM_BindComponentToUser will look like that:
+ * \code
+t_cm_error ENS_Wrapper::bindComponentToUser(
+ t_cm_instance_handle component,
+ const char *itfname,
+ t_nmf_interface_desc *itfref,
+ t_uint32 fifosize)
+{
+ ENSWrapperCtx * wrapper = new ENSWrapperCtx();
+ wrapper->WrapperItfref = itfref;
+
+ return CM_BindComponentToUser(
+ component, itfname, (void*)wrapperThis, fifosize);
+}
+ * \endcode
+ *
+ *
+ * In an multi-threaded environment, message could be dispatch in the code like that:
+ * \code
+void MessagePuller(void)
+{
+ char buffer[MAX_MESSAGE_SIZE];
+ t_nmf_interface_desc *itfref;
+
+ while(1) {
+ if(CM_GetMessage(channel, &itfref, buffer, sizeof(buffer), TRUE) == CM_OK) {
+ CM_ExecuteMessage(itfref, buffer);
+ }
+ }
+}
+
+{
+ ...
+ CreateThread(MessagePuller);
+ ...
+}
+ * \endcode
+ *
+ * \param[in] channel The channel from which the message must be retrieved
+ * \param[out] clientContext client context.
+ * \param[out] message Reference on buffer to be unmarshalled. The buffer is allocated internally.
+ * \param[in] blockable Indicate if the call could blocked or not.
+ *
+ * \exception CM_NO_MESSAGE No waited message.
+ * \exception CM_BUFFER_OVERFLOW Received message is bigger that buffer give in parameter.
+ *
+ * \ingroup CM_CALLBACK_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetMessage(
+ t_nmf_channel channel,
+ void **clientContext,
+ char **message,
+ t_bool blockable);
+
+#endif /* PROXY_COMMUNICATION_H */
diff --git a/drivers/staging/nmf-cm/cm/proxy/api/component_proxy.h b/drivers/staging/nmf-cm/cm/proxy/api/component_proxy.h
new file mode 100644
index 00000000000..ddf09e0ae75
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/api/component_proxy.h
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Components Component Manager API.
+ *
+ * This file contains the Component Manager API for manipulating components.
+ */
+
+#ifndef COMMON_COMPONENT_WRAPPER_H
+#define COMMON_COMPONENT_WRAPPER_H
+
+#include <cm/engine/component/inc/component_type.h>
+#include <cm/engine/communication/inc/communication_type.h>
+#include <cm/proxy/common/component/inc/interface_type.h>
+
+#include <cm/engine/memory/inc/domain_type.h>
+
+/*!
+ * \brief Push a file component in the Component Manager file cache.
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_PushComponent(
+ const char* templateName);
+
+/*!
+ * \brief Release a file component in the Component Manager file cache.
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ReleaseComponent(
+ const char* templateName);
+
+/*!
+ * \brief Instantiate a new component.
+ *
+ * Instantiate a new component from a specific template type into
+ * a specific MPC by allocating memory for component instance inside the
+ * MPC memories (XRAM, YRAM, EMBEDDED, EXTERNAL).
+ *
+ * If the template has not been loaded yet, this method
+ * load the code and the shared read only data inside memories
+ * (XROM, YROM, EMBEDDED_ROM, EXTERNAL_ROM, PROG, EMBEDDED_PROG).
+ *
+ * This method can be used like this: \code
+ * t_cm_instance_handle mytest;
+ * t_cm_error error;
+ *
+ * error = CM_InstantiateComponent(
+ * "test",
+ * SVA_CORE_ID,
+ * NMF_SCHED_NORMAL,
+ * "mytest",
+ * &mytest);
+ *
+ * error = CM_StartComponent(mytest);
+ *
+ * ...
+ *
+ * error = CM_StopComponent(mytest);
+ *
+ * error = CM_DestroyComponent(mytest);
+ * \endcode
+ *
+ * In fact, this method do exactly the same thing as the definition below in the
+ * architecture file in the static NMF approach:
+ * \code
+ * contains test as mytest
+ * \endcode
+ *
+ * Concerning Singleton behavior:
+ * - Same instance handle is returned.
+ * - First instance of the singleton is instantiated in the domain passed as parameter
+ * - Binding is immutable and thus first binding will be the winner, other binding must be exactly the same for coherence purpose
+ * - Component is start during the first start
+ * - Component is stop during the last stop
+ * - Each interface required by the singleton is really unbinded when the last binded interface is really unbinded
+ * - Component is really destroy when the last handle is destroyed
+ * Internally client action is memorized in order to correctly manage singleton in multi-process management.
+ *
+ * \param[in] templateName The template type name.
+ * \param[in] domainId Domain identifier where the component must be instantiated.
+ * \param[in] priority Priority of the component into the MPC Executive Engine (all the provided interface methods will be executed at this given priority). A
+ * sub priority can be given by bitwise or with priority level.
+ * \param[in] localName A optional name inside the MPC.
+ * \param[out] component The component instance handle reference.
+ *
+ * \exception CM_COMPONENT_NOT_FOUND The ELF file corresponding to the component template name is not found.
+ * \exception CM_INVALID_ELF_FILE The ELF file is not a MPC ELF file or not conform to the file name.
+ * \exception CM_MPC_NOT_RESPONDING Fifo not responding to sended command
+ * \exception CM_INVALID_DOMAIN_HANDLE Invalid domain handle
+ * \exception CM_NO_MORE_MEMORY No more memory
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_InstantiateComponent(
+ const char* templateName,
+ t_cm_domain_id domainId,
+ t_nmf_ee_priority priority,
+ const char* localName,
+ t_cm_instance_handle *component);
+
+/*!
+ * \brief Start execution of a component.
+ *
+ * Start a component which has been fully configured and which has all required
+ * interfaces binded.
+ *
+ * See \ref CM_InstantiateComponent for example.
+ *
+ * \exception CM_COMPONENT_NOT_STOPPED Component already started.
+ * \exception CM_REQUIRE_INTERFACE_UNBINDED Each required interfaces must be binded before starting a component.
+ * \exception CM_MPC_NOT_RESPONDING Fifo not responding to sent command and the component was not started
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_StartComponent(
+ const t_cm_instance_handle component //!< [in] The component instance handle
+ );
+
+/*!
+ * \brief Stop execution of a component.
+ *
+ * Stop a component previously started using \ref CM_StartComponent.
+ *
+ * See \ref CM_InstantiateComponent for example.
+ *
+ * \exception CM_COMPONENT_WAIT_RUNNABLE Start acknowledgment must be receive before stopping it.
+ * \exception CM_COMPONENT_NOT_STARTED Component must has been started before stopping.
+ * \exception CM_MPC_NOT_RESPONDING Fifo not responding to sent command (nevertheless, the component has been stopped)
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_StopComponent(
+ const t_cm_instance_handle component //!< [in] The component instance handle
+ );
+
+
+/*!
+ * \brief Destroy a component.
+ *
+ * Destroy a previously instanciated component with \ref CM_InstantiateComponent
+ * by freeing memories allocated for component instance. This component must been
+ * previously stopped using \ref CM_StopComponent.
+ *
+ * Furthermore, if it is the last instance of a component
+ * template, this method unload it and free all code and shared data memories.
+ *
+ * See \ref CM_InstantiateComponent for example.
+ *
+ * \exception CM_COMPONENT_NOT_STOPPED Component must be stopped before perform destroy operation.
+ * \exception CM_COMPONENT_NOT_UNBINDED Component must be fully unbinded before perform destroy operation.
+ * \exception CM_MPC_NOT_RESPONDING Fifo not responding to sent command (nevertheless, the component has been stopped)
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_DestroyComponent(
+ t_cm_instance_handle component //!< [in] The component instance handle
+ );
+
+/*!
+ * \brief Bind two components together.
+ *
+ * Bind a required interface of a client component with a provided interface
+ * of a server component.
+ *
+ * This method can be used like this: \code
+ * t_cm_instance_handle client, server;
+ * t_cm_error error;
+ *
+ * error = CM_BindComponent(
+ * client,
+ * "output",
+ * server,
+ * "input");
+ *
+ * error = CM_UnbindComponent(
+ * client,
+ * "output");
+ * \endcode
+ *
+ * In fact, this method do exactly the same thing as the definition bellow in the
+ * architecture file in the static Nomadik Kernel approach:\code
+ * binds clientName.output to serverName.input
+ * \endcode
+ *
+ * \param[in] client The client component instance handle.
+ * \param[in] requiredItfClientName The client required interface name (either collection or not collection name allowed).
+ * \param[in] server The server component instance handle.
+ * \param[in] providedItfServerName The server provided interface name (either collection or not collection name allowed).
+ *
+ * \exception CM_NO_SUCH_REQUIRED_INTERFACE Interface name not required by the component.
+ * \exception CM_NO_SUCH_PROVIDED_INTERFACE Interface name not provided by the component.
+ * \exception CM_ILLEGAL_BINDING Type mismatch between client and server interfaces.
+ * \exception CM_INTERFACE_ALREADY_BINDED Required interface must be unbinded before rebind.
+ * \exception CM_COMPONENT_NOT_STOPPED Client component must be stopped before reconfigure it
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_BindComponent(
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName,
+ const t_cm_instance_handle server,
+ const char* providedItfServerName);
+
+/*!
+ * \brief Unbind a component either traced or not.
+ *
+ * Unbind a required interface of a client component previoulsy binded with
+ * \ref CM_BindComponent or CM_BindComponentTrace.
+ *
+ * See \ref CM_BindComponent for example.
+ *
+ * \param[in] client The client component instance handle.
+ * \param[in] requiredItfClientName The client required interface name (either collection or not collection name allowed).
+ *
+ * \exception CM_NO_SUCH_REQUIRED_INTERFACE Interface name not required by the component.
+ * \exception CM_INTERFACE_NOT_BINDED The required interface is not binded.
+ * \exception CM_COMPONENT_NOT_STOPPED Client component must be stopped before reconfigure it
+ * \exception CM_ILLEGAL_UNBINDING Try to unbind component bind with other binding factories.
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_UnbindComponent(
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName);
+
+/*!
+ * \brief Bind two components together with trace.
+ *
+ * Bind a required interface of a client component with a provided interface
+ * of a server component.
+ *
+ * This method can be used like this: \code
+ * t_cm_instance_handle client, server;
+ * t_cm_error error;
+ *
+ * error = CM_BindComponentTrace(
+ * client,
+ * "output",
+ * server,
+ * "input");
+ *
+ * error = CM_UnbindComponent(
+ * client,
+ * "output");
+ * \endcode
+ *
+ * In fact, this method do exactly the same thing as the definition bellow in the
+ * architecture file in the static ADL approach:\code
+ * binds clientName.output to serverName.input
+ * \endcode
+ *
+ * \param[in] client The client component instance handle.
+ * \param[in] requiredItfClientName The client required interface name (either collection or not collection name allowed).
+ * \param[in] server The server component instance handle.
+ * \param[in] providedItfServerName The server provided interface name (either collection or not collection name allowed).
+ *
+ * \exception CM_NO_SUCH_REQUIRED_INTERFACE Interface name not required by the component.
+ * \exception CM_NO_SUCH_PROVIDED_INTERFACE Interface name not provided by the component.
+ * \exception CM_ILLEGAL_BINDING Type mismatch between client and server interfaces.
+ * \exception CM_INTERFACE_ALREADY_BINDED Required interface must be unbinded before rebind.
+ * \exception CM_COMPONENT_NOT_STOPPED Client component must be stopped before reconfigure it
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_BindComponentTrace(
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName,
+ const t_cm_instance_handle server,
+ const char* providedItfServerName);
+
+/*!
+ * \brief Bind a component to void (silently ignore a call).
+ *
+ * Bind to void mean that calling a method from this interface do nothing and
+ * the caller can continu its execution.
+ *
+ * This method can be used like this: \code
+ * t_cm_instance_handle client;
+ * t_cm_error error;
+ *
+ * error = CM_BindComponentToVoid(
+ * client,
+ * "output");
+ *
+ * error = CM_UnbindComponent(
+ * client,
+ * "output");
+ * \endcode
+ *
+ * \param[in] client The client component instance handle.
+ * \param[in] requiredItfClientName The client required interface name (either collection or not collection name allowed).
+ *
+ * \exception CM_NO_SUCH_REQUIRED_INTERFACE Interface name not required by the component.
+ * \exception CM_INTERFACE_ALREADY_BINDED Required interface must be unbinded before rebind.
+ * \exception CM_COMPONENT_NOT_STOPPED Client component must be stopped before reconfigure it
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_BindComponentToVoid(
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName);
+
+/*!
+ * \brief Bind two components together in an asynchronous way
+ * (the components can be on the same MPC or on two different MPC)
+ *
+ * Bind a required interface of a client component with a provided interface
+ * of a server component via a asynchronous binding. If client and server components reside on
+ * the same MPC then a pure asynchronous binding is created. If they are residing on distinct MPC
+ * then a distributed binding is created. This distributed binding automatically marshall parameters
+ * directly in the shared memory (DMA is not involve in this binding).
+ *
+ * This method can be used like this: \code
+ * t_cm_instance_handle client, server;
+ * t_cm_error error;
+ *
+ * error = CM_BindComponentAsynchronous(
+ * client,
+ * "output",
+ * server,
+ * "input",
+ * 4);
+ *
+ * error = CM_UnbindComponentAsynchronous(
+ * client,
+ * "output");
+ * \endcode
+ *
+ * In fact, this method do exactly the same thing as the definition bellow in the
+ * architecture file in the static Nomadik Kernel approach:\code
+ * binds clientName.output to serverName.input
+ * \endcode
+ *
+ * \param[in] client The client component instance handle.
+ * \param[in] requiredItfClientName The client required interface name (either collection or not collection name allowed).
+ * \param[in] server The server component instance handle.
+ * \param[in] providedItfServerName The server provided interface name (either collection or not collection name allowed).
+ * \param[in] fifosize the number of element in the event fifo (number of simultaneously waiting request)
+ *
+ * \exception CM_NO_SUCH_REQUIRED_INTERFACE Interface name not required by the component.
+ * \exception CM_NO_SUCH_PROVIDED_INTERFACE Interface name not provided by the component.
+ * \exception CM_ILLEGAL_BINDING Type mismatch between client and server interfaces.
+ * \exception CM_INTERFACE_ALREADY_BINDED Required interface must be unbinded before rebind.
+ * \exception CM_BINDING_COMPONENT_NOT_FOUND Binding component require for this binding not found in the
+ * component repository.
+ * \exception CM_COMPONENT_NOT_STOPPED Client component must be stopped before reconfigure it
+ * \exception CM_INVALID_PARAMETER Invalid parameter (Fifo too bug, ...)
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_BindComponentAsynchronous(
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName,
+ const t_cm_instance_handle server,
+ const char* providedItfServerName,
+ t_uint32 fifosize);
+
+/*!
+ * \brief Bind two components together in an asynchronous way and put event fifo in specified memory.
+ * (the components can be on the same MPC or on two different MPC)
+ *
+ * \param[in] client The client component instance handle.
+ * \param[in] requiredItfClientName The client required interface name (either collection or not collection name allowed).
+ * \param[in] server The server component instance handle.
+ * \param[in] providedItfServerName The server provided interface name (either collection or not collection name allowed).
+ * \param[in] fifosize the number of element in the event fifo (number of simultaneously waiting request)
+ * \param[in] eventMemType Memory type where allocating event fifo used for scheduling
+ *
+*/
+PUBLIC IMPORT_SHARED t_cm_error CM_BindComponentAsynchronousEx(
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName,
+ const t_cm_instance_handle server,
+ const char* providedItfServerName,
+ t_uint32 fifosize,
+ t_cm_mpc_memory_type eventMemType
+ );
+
+/*!
+ * \brief Unbind a component previously binded asynchronously
+ *
+ * Unbind a required interface of a client component previoulsy binded with
+ * \ref CM_BindComponentAsynchronous.
+ *
+ * See \ref CM_BindComponentAsynchronous for example.
+ *
+ * \param[in] client The client component instance handle.
+ * \param[in] requiredItfClientName The client required interface name (either collection or not collection name allowed).
+ *
+ * \exception CM_NO_SUCH_REQUIRED_INTERFACE Interface name not required by the component.
+ * \exception CM_INTERFACE_NOT_BINDED The required interface is not binded.
+ * \exception CM_COMPONENT_NOT_STOPPED Client component must be stopped before reconfigure it
+ * \exception CM_ILLEGAL_UNBINDING Try to unbind component bind with other binding factories.
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_UnbindComponentAsynchronous(
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName);
+
+
+/*!
+ * \brief Read a value on an attribute exported by a component instance.
+ *
+ * This attribute must have been declared as an attribute in the component configuration
+ * file.
+ *
+ * \warning {
+ * Be careful, only attribute filling in one MPC word could be get through this method.
+ *
+ * Thus on MMDSP only attribute of t_[u|s]int[24|16|8] could be well returned.
+ * Elsewhere, only one part of attribute could be returned.
+ * }
+ *
+ * This method can be used like this: \code
+ * t_cm_instance_handle component;
+ * t_cm_error error;
+ * t_uint24 value;
+ *
+ * error = CM_ReadComponentAttribute(
+ * component,
+ * "count",
+ * &value);
+ * \endcode
+ *
+ * \param[in] component The component instance handle.
+ * \param[in] attrName The component attribute name.
+ * \param[out] value Read value
+ *
+ * \exception CM_NO_SUCH_ATTRIBUTE Attribute name not provide by a component.
+ * \exception CM_INVALID_COMPONENT_HANDLE
+ *
+ * \ingroup COMPONENT
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_ReadComponentAttribute(
+ const t_cm_instance_handle component,
+ const char* attrName,
+ t_uint24 *value);
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/proxy/api/configuration_proxy.h b/drivers/staging/nmf-cm/cm/proxy/api/configuration_proxy.h
new file mode 100644
index 00000000000..d5e53b6bb09
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/api/configuration_proxy.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Component Manager Configuration API.
+*/
+/*!
+ * \defgroup CM_CONFIGURATION_API CM Proxy Configuration API
+ * \ingroup CM_PROXY_MODULE
+ */
+
+#ifndef COMMON_CONFIGURATION_WRAPPER_H
+#define COMMON_CONFIGURATION_WRAPPER_H
+
+#include <nmf/inc/channel_type.h>
+#include <nmf/inc/service_type.h>
+#include <cm/engine/configuration/inc/configuration_type.h>
+#include <cm/engine/communication/inc/communication_type.h>
+
+/*!
+ * \brief Create/get a channel.
+ *
+ * The purpose of the function is to
+ * create a channel or get a channel, regarding the flag parameter
+ *
+ * \param[in] flags Whether the caller want to create a new channel (\ref NMF_CHANNEL_PRIVATE)
+ * or use the shared one (\ref NMF_CHANNEL_SHARED) (it will be created
+ * if it does not yet exist)
+ * \param[out] channel Channel number.
+ *
+ * \exception CM_NO_MORE_MEMORY Not enough memory to create the callback Channel.
+ * \exception CM_INVALID_PARAMETER The specified flags is invalid.
+ * \exception CM_INTEGRATION_ERROR OS specific error.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+PUBLIC IMPORT_SHARED t_nmf_error CM_CreateChannel(t_nmf_channel_flag flags,
+ t_nmf_channel *channel);
+
+/*!
+ * \brief Flush a channel to allow user to safely close it.
+ *
+ * The purpose of the function is to allow safe call of CM_CloseChannel() later on. Calling
+ * CM_FlushChannel() will allow a blocking call to CM_GetMessage() to exit with an error
+ * CM_FLUSH_MESSAGE. After CM_GetMessage() has exit with such a value user must no more
+ * call CM_GetMessage() and can safely call CM_CloseChannel() that will destroy channel.
+ * In case of the share channel CM_FlushChannel() will return false for isFlushMessageGenerated if
+ * it's internal reference counter is not zero, in that case no CM_FLUSH_MESSAGE error is return
+ * by CM_GetMessage() and user can immediatly call CM_CloseChannel().
+ * In case user know that no usage of channel is done when he want to destroy channel, call to this api
+ * is optionnal and user can safely call CM_CloseChannel().
+ *
+ * \param[in] channel Channel number
+ * \param[out] isFlushMessageGenerated Allow user to know if it must wait for CM_FLUSH_MESSAGE return
+ * of CM_GetMessage() before calling CM_CloseChannel()
+ *
+ * \exception NMF_INVALID_PARAMETER The specified flags is invalid.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+PUBLIC IMPORT_SHARED t_nmf_error CM_FlushChannel(t_nmf_channel channel, t_bool *isFlushMessageGenerated);
+
+/*!
+ * \brief Close a channel
+ *
+ * The purpose of the function is to
+ * destroy a channel from the user to the CM engine.
+ *
+ * The user must call CM_CloseChannel() as many time as \ref CM_CreateChannel().
+ * At the last CM_CloseChannel() call, the channel is closed and definitely destroyed.
+ * All service callback must be unregistered first.
+ *
+ * \param[in] channel Channel number:
+ *
+ * \exception CM_INVALID_PARAMETER The specified channel is invalid or some callback are still registered.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+PUBLIC IMPORT_SHARED t_nmf_error CM_CloseChannel(t_nmf_channel channel);
+
+/*!
+ * \brief Register a service callback to this channel.
+ *
+ * \param[in] channel The channel on which the callback must be registered.
+ * \param[in] handler The given callback.
+ * \param[in] ctx The context associated with this callback (the one passed to the handler).
+ *
+ * The handler must be a function with the following declaration
+ * \code
+ * void service(void *contextHandler, t_nmf_service_type serviceType, t_nmf_service_data *serviceData)
+ * {
+ * switch (servicetype) {
+ * case NMF_SERVICE_PANIC:
+ * t_nmf_panic_data *panic = &serviceData->panic;
+ * ...
+ * break;
+ * default:
+ * break;
+ * }
+ *
+ * }
+ * \endcode
+ *
+ * This 'service' function will be called from \ref CM_ExecuteMessage() for each incoming 'service messages'
+ * - The 'contextHandler' passed is the context given when registering this service callback
+ * - 'serviceType' specifies the service
+ *
+ * \exception CM_INVALID_PARAMETER The channel doesn't exist or the callback is NULL
+ * \exception CM_NO_MORE_MEMORY Not enough memory to associate service with the Channel.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+PUBLIC IMPORT_SHARED t_nmf_error CM_RegisterService(t_nmf_channel channel,
+ t_nmf_serviceCallback handler,
+ void *ctx);
+
+/*!
+ * \brief Unregister a service callback from this channel.
+ *
+ * \param[in] channel The channel on which the callback must be registered.
+ * \param[in] handler The given callback.
+ * \param[in] ctx The context associated with this callback.
+ *
+ * \exception CM_INVALID_PARAMETER The channel or the callback doesn't exist.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+PUBLIC IMPORT_SHARED t_nmf_error CM_UnregisterService(t_nmf_channel channel,
+ t_nmf_serviceCallback handler,
+ void *ctx);
+
+/*!
+ * \brief Set the mode of the Component Manager engine.
+ *
+ * According the (\ref t_cm_cmd_id) value, this routine allows to modify dynamically the behavior of the CM-engine.
+ *
+ * \param[in] aCmdID Command ID.
+ * \param[in] aParam Parameter of command ID if required.
+ *
+ * \ingroup CM_CONFIGURATION_API
+ */
+
+PUBLIC IMPORT_SHARED t_cm_error CM_SetMode(t_cm_cmd_id aCmdID, t_sint32 aParam);
+
+PUBLIC IMPORT_SHARED const char * CM_StringError(t_nmf_error error);
+#endif // COMMON_CONFIGURATION_WRAPPER_H
+
diff --git a/drivers/staging/nmf-cm/cm/proxy/api/domain_proxy.h b/drivers/staging/nmf-cm/cm/proxy/api/domain_proxy.h
new file mode 100644
index 00000000000..cdabe8903f0
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/api/domain_proxy.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/***************************************************************************/
+/* file : domain.h
+ * author : NMF team
+ * version : 1.0
+ *
+ * brief : NMF domain definitions
+ */
+/***************************************************************************/
+/*!
+ * \defgroup CM_DOMAIN_API CM Domain API
+ * \ingroup CM_USER_API
+ */
+#ifndef DOMAIN_COMMON_H_
+#define DOMAIN_COMMON_H_
+
+#include <cm/engine/memory/inc/domain_type.h>
+
+/*!
+ * \brief Create a domain.
+ *
+ * Create a memory domain for use in the CM for component instantiation and memory allocation.
+ *
+ * \param[in] domain Description of domain memories.
+ * \param[out] handle Idetifier of the created domain
+ *
+ * \exception CM_INVALID_DOMAIN_DEFINITION
+ * \exception CM_INTERNAL_DOMAIN_OVERFLOW
+ * \exception CM_OK
+ *
+ * \return Error code.
+ *
+ * \ingroup CM_DOMAIN_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_CreateMemoryDomain(const t_cm_domain_memory *domain, t_cm_domain_id *handle);
+
+/*!
+ * \brief Create a scratch domain.
+ *
+ * Create a scratch memory domain. Scratch domains are used to perform overlapping allocations.
+ *
+ * \param[in] parentId Identifier of the (logical) parent domain
+ * \param[in] domain Description of domain memories.
+ * \param[out] handle Idetifier of the created domain
+ *
+ * \exception CM_INVALID_DOMAIN_DEFINITION
+ * \exception CM_INTERNAL_DOMAIN_OVERFLOW
+ * \exception CM_NO_MORE_MEMORY
+ * \exception CM_OK
+ *
+ * \return Error code.
+ *
+ * \ingroup CM_DOMAIN_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_CreateMemoryDomainScratch(const t_cm_domain_id parentId, const t_cm_domain_memory *domain, t_cm_domain_id *handle);
+
+/*!
+ * \brief Destroy a memory domain.
+
+ * \param[in] handle Domain identifier to destroy.
+ *
+ * \exception CM_INVALID_DOMAIN_HANDLE
+ * \exception CM_OK
+ *
+ * \return Error code.
+ *
+ * \ingroup CM_DOMAIN_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_DestroyMemoryDomain(t_cm_domain_id handle);
+
+/*!
+ * \brief Retrieve the coreId for a given domain. Utility.
+
+ * \param[in] domainId Domain identifier.
+ * \param[out] coreId Core identifier.
+ *
+ * \exception CM_INVALID_DOMAIN_HANDLE Invalid domain handle
+ * \exception CM_OK
+ *
+ * \return Error code.
+ *
+ * \ingroup CM_DOMAIN_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetDomainCoreId(const t_cm_domain_id domainId, t_nmf_core_id *coreId);
+
+#endif /* DOMAIN_COMMON_H_ */
diff --git a/drivers/staging/nmf-cm/cm/proxy/api/introspection_proxy.h b/drivers/staging/nmf-cm/cm/proxy/api/introspection_proxy.h
new file mode 100644
index 00000000000..006b5259f99
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/api/introspection_proxy.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Component Manager Introspection API.
+ *
+ * This file contains the Component Manager API for introspect components.
+ */
+/*!
+ * \defgroup COMPONENT_INTROSPECTION CM Components Network Introspection API
+ * \ingroup CM_USER_API
+ */
+
+#ifndef COMMON_INTROSPECTION_WRAPPER_H
+#define COMMON_INTROSPECTION_WRAPPER_H
+
+#include <cm/engine/component/inc/component_type.h>
+#include <cm/proxy/common/component/inc/component_info.h>
+#include <inc/nmf-limits.h>
+
+/*!
+ * \brief Get the executive engine handle for given core.
+ *
+ * Return the executive engine handle for coreId. In case executive engine is not loaded it return null.
+ *
+ * \param[in] domainId The domain identifier for which we want the executive engine handle.
+ * \param[out] executiveEngineHandle executive engine instance (null if the executive engine is not loaded)
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetExecutiveEngineHandle(
+ t_cm_domain_id domainId,
+ t_cm_instance_handle *executiveEngineHandle);
+
+/*!
+ * \brief Get a component created by the process.
+ *
+ * Return a loaded component of the instantiated component network.
+ *
+ * This method can be used to obtain a component that can be used as an entry point
+ * for browsing instantiated component network (used conjointly with \ref CM_GetComponentListNext).
+ *
+ * This code illustrate how browsing component network: \code
+ t_cm_instance_handle component;
+
+ CM_GetComponentListHeader(&component);
+ while(component != 0) {
+ t_nmf_core_id coreId;
+ char templateName[MAX_TEMPLATE_NAME_LENGTH];
+ char localName[MAX_COMPONENT_NAME_LENGTH];
+
+ CM_GetComponentDescription(component, templateName, MAX_TEMPLATE_NAME_LENGTH, &coreId, localName, MAX_COMPONENT_NAME_LENGTH);
+ LOG("[%x] type=%s dsp=%d name=%s",
+ component, templateName, coreId, localName);
+
+ CM_GetComponentListNext(component, &component);
+ }
+ * \endcode
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentListHeader(
+ t_cm_instance_handle *headerComponent //!< [out] The instance of a component loaded by the process (null if the no component loaded)
+ );
+
+
+/*!
+ * \brief Get a next component.
+ *
+ * Return a next component in the instantiated component network.
+ *
+ * This method may be called repeatedly to iterate through the network components.
+ * The list resulting from calling this method is ordered by date of instantiation.
+ *
+ * See \ref CM_GetComponentListHeader for detail about how this method can be used.
+ *
+ * \exception CM_INVALID_COMPONENT_HANDLE
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentListNext(
+ const t_cm_instance_handle prevComponent, //!< [in] A component instance handle
+ t_cm_instance_handle *nextComponent //!< [out] The instance of a next component loaded by the process (null if the last one)
+ );
+
+
+/*!
+ * \brief Get a component description
+ *
+ * Get some description about the component:
+ * - the component template name,
+ * - the core where the component is loaded,
+ * - the component local name (gived during instantiation).
+ *
+ * This method can be used like this: \code
+ t_cm_instance_handle component;
+ t_cm_error error;
+ t_nmf_core_id coreId;
+ char templateName[MAX_TEMPLATE_NAME_LENGTH];
+ char localName[MAX_COMPONENT_NAME_LENGTH];
+
+ error = CM_GetComponentDescription(component,
+ templateName, MAX_TEMPLATE_NAME_LENGTH, &coreId,
+ localName, MAX_COMPONENT_NAME_LENGTH);
+ * \endcode
+ *
+ * \param[in] component The component instance handle.
+ * \param[in] templateName Address of buffer where component template name mut be write.
+ * \param[in] templateNameLength Size of templateName buffer.
+ * \param[out] coreId The core where the component is loaded.
+ * \param[in] localName Address of buffer where component local name mut be write.
+ * \param[in] localNameLength Size of localName buffer.
+ * \param[out] priority The priority of the component (optional, may be NULL).
+ *
+ * \exception CM_INVALID_COMPONENT_HANDLE
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentDescription(
+ const t_cm_instance_handle component,
+ char *templateName,
+ t_uint32 templateNameLength,
+ t_nmf_core_id *coreId,
+ char *localName,
+ t_uint32 localNameLength,
+ t_nmf_ee_priority *priority);
+
+
+/*!
+ * \brief Get number of interface required by a component.
+ *
+ * Return the number of required interface that have been declared in the
+ * component configuration file.
+ *
+ * Component introspection required interface methods can be used like this: \code
+ t_cm_instance_handle component;
+ t_uint8 requireNb, i;
+
+ CM_GetComponentRequiredInterfaceNumber(component, &requireNb);
+ for(i = 0; i < requireNb; i++) {
+ char itfName[MAX_INTERFACE_NAME_LENGTH];
+ char itfType[MAX_INTERFACE_TYPE_NAME_LENGTH];
+ t_sint16 collectionSize;
+ t_cm_instance_handle server;
+ char serverItfName[MAX_INTERFACE_NAME_LENGTH];
+
+ CM_GetComponentRequiredInterface(component, i,
+ itfName, MAX_INTERFACE_NAME_LENGTH,
+ itfType, MAX_INTERFACE_TYPE_NAME_LENGTH,
+ &collectionSize);
+
+ if(collectionSize == -1) {
+ LOG(" -> %s:%s", itfName, itfType);
+
+ CM_GetComponentRequiredInterfaceBinding(component, itfName,
+ &server, serverItfName, MAX_INTERFACE_NAME_LENGTH);
+
+ if(server != NULL)
+ LOG(" [%x].%s", server, serverItfName);
+ } else {
+ int j;
+
+ LOG(" -> %s:%s [%d]", itfName, itfType, collectionSize);
+
+ for(j = 0; j < collectionSize; j++) {
+ char tmpname[256];
+ sprintf(tmpname, "%s[%d]", itfName, j);
+
+ CM_GetComponentRequiredInterfaceBinding(component, tmpname,
+ &server, serverItfName, MAX_INTERFACE_NAME_LENGTH);
+
+ if(server != NULL)
+ LOG(" %d: [%x].%s", j, server, serverItfName);
+ }
+ }
+ }
+ * \endcode
+ *
+ * \param[in] component The component instance handle.
+ * \param[out] numberRequiredInterfaces The number of required interface
+ *
+ * \exception CM_INVALID_COMPONENT_HANDLE
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentRequiredInterfaceNumber(
+ const t_cm_instance_handle component,
+ t_uint8 *numberRequiredInterfaces);
+
+
+/*!
+ * \brief Return information about required interface.
+ *
+ * Return the description of a required interface that have been declared in the
+ * component configuration file according its index.
+ *
+ * See \ref CM_GetComponentRequiredInterfaceNumber for detail about how this method can be used.
+ *
+ * \param[in] component The component instance handle.
+ * \param[in] index Required interface index.
+ * \param[out] itfName Address of buffer where name of the required interface must be write.
+ * \param[in] itfNameLength Size of itfName buffer.
+ * \param[out] itfType Address of buffer where type of the required interface must be write.
+ * \param[in] itfTypeLength Size of itfType buffer.
+ * \param[out] requireState Address where the required interface state must be write.
+ * \param[out] collectionSize Size of the collection (-1 if not a collection).
+ *
+ * \exception CM_NO_SUCH_REQUIRED_INTERFACE The required interface index is out of range [0 .. numberRequiredInterfaces[.
+ * \exception CM_INVALID_COMPONENT_HANDLE
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentRequiredInterface(
+ const t_cm_instance_handle component,
+ const t_uint8 index,
+ char *itfName,
+ t_uint32 itfNameLength,
+ char *itfType,
+ t_uint32 itfTypeLength,
+ t_cm_require_state *requireState,
+ t_sint16 *collectionSize);
+
+
+/*!
+ * \brief Get the component binded to a required interface.
+ *
+ * Return information about the interface provided by a server component that has been
+ * binded to a required interface.
+ *
+ * See \ref CM_GetComponentRequiredInterfaceNumber for detail about how this method can be used.
+ *
+ * \param[in] component The component instance handle.
+ * \param[in] itfName Name of the required interface.
+ * \param[out] server Server component instance (NMF_HOST_COMPONENT if component on host call through MPC->Host binding).
+ * \param[in] serverItfName Address of buffer where interface name provided by the server component where
+ * is bind the client component must be write.
+ * \param[in] serverItfNameLength Size of serverItfName buffer.
+ *
+ * \exception CM_NO_SUCH_REQUIRED_INTERFACE The required interface name is unknown.
+ * \exception CM_INTERFACE_NOT_BINDED The required interface is not binded.
+ * \exception CM_INVALID_COMPONENT_HANDLE
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentRequiredInterfaceBinding(
+ const t_cm_instance_handle component,
+ const char *itfName,
+ t_cm_instance_handle *server,
+ char *serverItfName,
+ t_uint32 serverItfNameLength);
+
+/*!
+ * \brief Get number of interface provided by a component.
+ *
+ * Return the number of provided interface that have been declared in the
+ * component configuration file.
+ *
+ * Component introspection provided interface methods can be used like this: \code
+ t_cm_instance_handle component;
+ t_uint8 provideNb, i;
+
+ CM_GetComponentProvidedInterfaceNumber(component, &provideNb);
+ for(i = 0; i < provideNb; i++) {
+ char itfName[MAX_INTERFACE_NAME_LENGTH];
+ char itfType[MAX_INTERFACE_TYPE_NAME_LENGTH];
+ t_sint16 collectionSize;
+
+ CM_GetComponentProvidedInterface(component, i,
+ itfName, MAX_INTERFACE_NAME_LENGTH,
+ itfType, MAX_INTERFACE_TYPE_NAME_LENGTH,
+ &collectionSize);
+
+ if(collectionSize != -1)
+ LOG(" <- %s:%s [%d]", itfName, itfType, collectionSize);
+ else
+ LOG(" <- %s:%s", itfName, itfType);
+ }
+ * \endcode
+ *
+ * \param[in] component The component instance handle.
+ * \param[out] numberProvidedInterfaces The number of provided interface
+ *
+ * \exception CM_INVALID_COMPONENT_HANDLE
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentProvidedInterfaceNumber(
+ const t_cm_instance_handle component,
+ t_uint8 *numberProvidedInterfaces);
+
+
+/*!
+ * \brief Return information about provided interface.
+ *
+ * Return the description of a provided interface that have been declared in the
+ * component configuration file according its index.
+ *
+ * See \ref CM_GetComponentProvidedInterfaceNumber for detail about how this method can be used.
+ *
+ * \param[in] component The component instance handle.
+ * \param[in] index Provided interface index.
+ * \param[in] itfName Address of buffer where name of the provided interface must be write.
+ * \param[in] itfNameLength Size of itfName buffer.
+ * \param[in] itfType Address of buffer where type of the provided interface must be write.
+ * \param[in] itfTypeLength Size of itfType buffer.
+ * \param[out] collectionSize Size of the collection (-1 if not a collection).
+ *
+ * \exception CM_NO_SUCH_PROVIDED_INTERFACE The provided interface index is out of range [0 .. numberProvidedInterfaces[.
+ * \exception CM_INVALID_COMPONENT_HANDLE
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentProvidedInterface(
+ const t_cm_instance_handle component,
+ const t_uint8 index,
+ char *itfName,
+ t_uint32 itfNameLength,
+ char *itfType,
+ t_uint32 itfTypeLength,
+ t_sint16 *collectionSize);
+
+/*!
+ * \brief Get number of properties of a component.
+ *
+ * Return the number of properties that have been declared in the
+ * component configuration file.
+ *
+ * Component introspection property methods can be used like this: \code
+ t_cm_instance_handle component;
+ t_uint8 propertyNumber, index;
+
+ CM_GetComponentPropertyNumber(
+ component,
+ &propertyNumber);
+
+ for(index = 0; index < propertyNumber; index++) {
+ char propertyName[MAX_PROPERTY_NAME_LENGTH];
+ char propertyValue[MAX_PROPERTY_VALUE_LENGTH];
+ CM_GetComponentPropertyName(component, index, propertyName, MAX_PROPERTY_NAME_LENGTH);
+ CM_GetComponentPropertyValue(component, propertyName, propertyValue, MAX_PROPERTY_VALUE_LENGTH);
+
+ LOG("Property %s = %s\n", propertyName, propertyValue);
+ }
+ * \endcode
+ *
+ * \param[in] component The component instance handle.
+ * \param[out] numberProperties The number of properties.
+ *
+ * \exception CM_INVALID_COMPONENT_HANDLE
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentPropertyNumber(
+ const t_cm_instance_handle component,
+ t_uint8 *numberProperties);
+
+/*!
+ * \brief Return the name of a property.
+ *
+ * Return the name of a property that have been declared in the
+ * component configuration file according its index.
+ *
+ * See \ref CM_GetComponentPropertyNumber for detail about how this method can be used.
+ *
+ * \param[in] component The component instance handle.
+ * \param[in] index Property index.
+ * \param[in] propertyName Address of buffer where property name mut be write.
+ * \param[in] propertyNameLength Size of propertyName buffer.
+ *
+ * \exception CM_NO_SUCH_PROPERTY The property index is out of range [0 .. numberProperties[.
+ * \exception CM_INVALID_COMPONENT_HANDLE
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentPropertyName(
+ const t_cm_instance_handle component,
+ const t_uint8 index,
+ char *propertyName,
+ t_uint32 propertyNameLength);
+
+/*!
+ * \brief Get property value of a component.
+ *
+ * Get the property value of a component. This property must have been declared as an property in the
+ * component configuration file.
+ *
+ * This method can be used like this: \code
+ t_cm_instance_handle component;
+ t_cm_error error;
+ char value[MAX_PROPERTY_VALUE_LENGTH];
+
+ error = CM_GetComponentPropertyValue(
+ component,
+ "prop",
+ value,
+ MAX_PROPERTY_VALUE_LENGTH);
+ * \endcode
+ * The name of the property can be either well-known for the component type or founded dynamically through
+ * \ref CM_GetComponentPropertyNumber and \ref CM_GetComponentPropertyName.
+ *
+ * \param[in] component The component instance handle.
+ * \param[in] propertyName The component property name.
+ * \param[in] propertyValue Address of buffer where property value mut be write.
+ * \param[in] propertyValueLength Size of propertyValue buffer.
+ *
+ * \exception CM_NO_SUCH_PROPERTY The property name is unknown.
+ * \exception CM_INVALID_COMPONENT_HANDLE
+ *
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentPropertyValue(
+ const t_cm_instance_handle component,
+ const char *propertyName,
+ char *propertyValue,
+ t_uint32 propertyValueLength);
+
+/*!
+ * \brief Get component memory info
+ *
+ * Get the information about how much memory the compnent would occupy after its instantiation.
+ * All memory types are accounted for and two instantiation types are considered
+ * - initial instantiation, and
+ * - subsequent instantiations.
+ *
+ * \param[in] templateName The component name.
+ * \param[out] pInfo The info structure gathering all above information
+ *
+ * \exception CM_INVALID_COMPONENT_HANDLE
+
+ * \ingroup COMPONENT_INTROSPECTION
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetComponentMemoryInfo(
+ const char* templateName,
+ t_cm_component_memory_info *pInfo);
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/proxy/api/memory_proxy.h b/drivers/staging/nmf-cm/cm/proxy/api/memory_proxy.h
new file mode 100644
index 00000000000..ec42338e694
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/api/memory_proxy.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Public Component Manager Memory API.
+ *
+ * This file contains the Component Manager API for manipulating memory.
+ */
+/*!
+ * \defgroup MEMORY CM Memories API
+ * \ingroup CM_USER_API
+ *
+ */
+
+#ifndef COMMON_MEMORY_WRAPPER_H
+#define COMMON_MEMORY_WRAPPER_H
+
+#include <cm/engine/memory/inc/memory_type.h>
+
+/*!
+ * \brief Allocate memory in a Media Processor Core memory
+ *
+ * The memory is allocated for a given MPC, into a given addressable memory segment and for a given size and alignment.\n
+ *
+ * To obtain memory, this method can be used like this: \code
+ * t_cm_memory_handle block;
+ * t_cm_domain_id domainId
+ * t_cm_system_address blockAddress;
+ * char* blockPtr;
+ *
+ * CM_AllocMpcMemory(domainId, CM_MM_MPC_ESRAM16, size, CM_MM_ALIGN_WORD, &block);
+ * CM_GetMpcMemorySystemAddress(block, &blockAddress);
+ * blockPtr = (char*)blockAddress.logical;
+ *
+ * ...
+ *
+ * CM_FreeMpcMemory(block);
+ * \endcode
+ *
+ * \param[in] domainId Domain identifier.
+ * \param[in] memType One of the addressable (by the given MPC) memory
+ * \param[in] preferredLocationArray Specify in which preferred banks we shall allocate in priority (array => ordered list)
+ * \param[in] size Minimum memory block size (in memType word size).
+ * \param[in] memAlignment Required alignment for the allocated memory block.
+ * \param[out] pHandle Address to put the memory handle of the allocated memory block.
+ *
+ * \exception CM_NO_MORE_MEMORY
+ * \exception CM_INVALID_PARAMETER
+ * \exception CM_OK
+ *
+ * \return exception number.
+ *
+ *
+ * \ingroup MEMORY
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_AllocMpcMemory(
+ t_cm_domain_id domainId,
+ t_cm_mpc_memory_type memType,
+ t_cm_size size,
+ t_cm_mpc_memory_alignment memAlignment,
+ t_cm_memory_handle *pHandle
+ );
+
+
+/*!
+ * \brief Free a MPC memory block.
+ *
+ * Free a memory block previously allocated by \ref CM_AllocMpcMemory.
+ *
+ * See \ref CM_AllocMpcMemory for how using this method.
+ *
+ * \param[in] handle Handle of a memory block
+ *
+ * \exception CM_UNKNOWN_MEMORY_HANDLE
+ * \exception CM_OK
+ *
+ * \return exception number.
+ *
+ * \ingroup MEMORY
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_FreeMpcMemory( t_cm_memory_handle handle);
+
+/*!
+ * \brief Get the start address of the MPC memory block seen by the host CPU (physical and logical)
+ *
+ * \param[in] handle Handle of a memory block
+ * \param[out] pSystemAddress Returned host system address
+ *
+ * \exception CM_UNKNOWN_MEMORY_HANDLE
+ * \exception CM_OK
+ *
+ * \return exception number.
+ *
+ * \ingroup MEMORY
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetMpcMemorySystemAddress(t_cm_memory_handle handle, t_cm_system_address *pSystemAddress);
+
+/*!
+ * \brief Get the start address of the memory block seen by the Media Processor Core
+ *
+ * \param[in] handle Handle of a memory block
+ * \param[out] pMpcAddress Returned mpc address
+ *
+ * \exception CM_UNKNOWN_MEMORY_HANDLE
+ * \exception CM_OK
+ *
+ * \return exception number.
+ *
+ * \ingroup MEMORY
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetMpcMemoryMpcAddress(t_cm_memory_handle handle, t_uint32 *pMpcAddress);
+
+/*!
+ * \brief Get the memory status for given memory type of a given Media Processor Core
+ *
+ * \param[in] domainId domain identifier.
+ * \param[in] memType One of the addressable (by the given MPC) memory
+ * \param[out] pStatus memory status
+ *
+ * \exception CM_UNKNOWN_MEMORY_HANDLE
+ * \exception CM_INVALID_DOMAIN
+ * \exception CM_OK
+ *
+ * \return exception number.
+ *
+ * \ingroup MEMORY
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetMpcMemoryStatus(t_cm_domain_id domainId, t_cm_mpc_memory_type memType, t_cm_allocator_status *pStatus);
+
+/*!
+ * \brief Get the memory status for given memory type of a given Media Processor Core
+ *
+ * \param[in] coreId core identifier.
+ * \param[in] memType One of the addressable (by the given MPC) memory
+ * \param[out] pStatus memory status
+ *
+ * \exception CM_UNKNOWN_MEMORY_HANDLE
+ * \exception CM_INVALID_DOMAIN
+ * \exception CM_OK
+ *
+ * \return exception number.
+ *
+ * \ingroup MEMORY
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_GetMpcMemoryStatusEx(t_nmf_core_id coreId, t_cm_mpc_memory_type memType, t_cm_allocator_status *pStatus);
+#endif /* COMMON_MEMORY_WRAPPER_H */
+
diff --git a/drivers/staging/nmf-cm/cm/proxy/api/migration_proxy.h b/drivers/staging/nmf-cm/cm/proxy/api/migration_proxy.h
new file mode 100644
index 00000000000..aaacad0f3d0
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/api/migration_proxy.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef COMMON_MIGRATION_PROXY_H
+#define COMMON_MIGRATION_PROXY_H
+
+#include <cm/inc/cm_type.h>
+#include <cm/engine/memory/inc/domain_type.h>
+
+PUBLIC IMPORT_SHARED t_cm_error CM_Migrate(const t_cm_domain_id srcShared, const t_cm_domain_id src, const t_cm_domain_id dst);
+
+PUBLIC IMPORT_SHARED t_cm_error CM_Unmigrate(void);
+
+#endif /* COMMON_MIGRATION_PROXY_H */
diff --git a/drivers/staging/nmf-cm/cm/proxy/api/perfmeter_proxy.h b/drivers/staging/nmf-cm/cm/proxy/api/perfmeter_proxy.h
new file mode 100644
index 00000000000..425882f011e
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/api/perfmeter_proxy.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief CM Performance Meter API.
+ *
+ * This file contains the Component Manager Performance Meter API.
+ */
+#ifndef COMMON_PERFMETER_WRAPPER_H
+#define COMMON_PERFMETER_WRAPPER_H
+
+#include <cm/engine/perfmeter/inc/perfmeter_type.h>
+
+/*!
+ * \brief MPC cpu load
+ *
+ * This routine will return counters that allow to compute mpc load.
+ *
+ * \param[in] coreId identification of mpc from which we want cpu load
+ * \param[out] mpcLoadCounter will contain mpc cpu load counters value if success
+ *
+ * \exception CM_INVALID_PARAMETER provide coreId is not a valid mpc coreId
+ *
+ * \ingroup PERFMETER
+ */
+
+PUBLIC IMPORT_SHARED t_cm_error CM_getMpcLoadCounter(
+ t_nmf_core_id coreId,
+ t_cm_mpc_load_counter *mpcLoadCounter);
+
+
+#endif /* COMMON_PERFMETER_WRAPPER_H */
diff --git a/drivers/staging/nmf-cm/cm/proxy/api/private/stub-requiredapi.h b/drivers/staging/nmf-cm/cm/proxy/api/private/stub-requiredapi.h
new file mode 100644
index 00000000000..4969b8756c2
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/api/private/stub-requiredapi.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Component Manager Internal API for stubs.
+ */
+/*!
+ * \defgroup CM_INTERNAL_API CM Proxy Internal API (used by generated code)
+ * \note This API is not for developers, this API is only an internal API.\n
+ *
+ * This API is used internally by code generated by NMF. This code will be linked with
+ * application and CM proxy.
+ *
+ * If no concurrent access will be perform, a method call is enough and dependencies
+ * will be handle by link. If not, it could be necessary to wrap this API during the
+ * system integration.
+ *
+ * \ingroup CM_PROXY_MODULE
+ */
+#ifndef COMMON_STUB_REQUIREAPI_H
+#define COMMON_STUB_REQUIREAPI_H
+
+#include <cm/proxy/common/component/inc/interface_type.h>
+#include <cm/engine/communication/inc/communication_type.h>
+
+
+typedef t_uint8 t_nmf_bc_coding_style;
+
+#define NMF_BC_IN_C ((t_nmf_bc_coding_style)0)
+#define NMF_BC_IN_CPP ((t_nmf_bc_coding_style)1)
+
+/*!
+ * \internal
+ * \brief ARM->DSP communication ARM this description
+ *
+ * \ingroup COMMUNICATION_INTERNAL
+ */
+typedef struct {
+ t_cm_bf_host2mpc_handle host2mpcId; //!< Handle of the host->MPC binding factory
+} t_nmf_arm_dsp_this;
+
+/*!
+ * \internal
+ * \brief Stub init function API (Host->MPC communication)
+ * For each interface, the NMF tools generate (nkitfc -[host|user]2mpc <interface name>) the stub functions code and an init function providing needed information
+ * regarding the given interface
+ * A reference of this init function is added into the dedicated array and this last one is referenced into a t_nmf_stub_register variable
+ *
+ * \ingroup COMMUNICATION_INTERNAL
+ */
+typedef t_cm_bf_host2mpc_handle* (*t_nmf_init_stub_fct)(t_nmf_interface_desc* itfref);
+
+/*!
+ * \internal
+ * \brief Stub init function API description (Host->MPC communication)
+ * \see t_nmf_stub_function description
+ *
+ * \ingroup COMMUNICATION_INTERNAL
+ */
+typedef struct {
+ const char *itfType;
+ t_nmf_init_stub_fct initStubFct;
+} t_nmf_stub_function;
+
+/*!
+ * \internal
+ * \brief Stub init function register entry (ARM->DSP communication)
+ * \see t_nmf_stub_function description
+ *
+ * \ingroup COMMUNICATION_INTERNAL
+ */
+typedef struct {
+ t_nmf_bc_coding_style codingStyle;
+ t_uint32 numElems;
+ const t_nmf_stub_function *pStubFctArray;
+} t_nmf_stub_register;
+
+
+/*!
+ * \internal
+ * \brief Internal type used to manage jumptable for a given interface (DSP->ARM communication)
+ *
+ * \ingroup COMMUNICATION_INTERNAL
+ */
+typedef void (*t_jump_method)(t_nmf_interface_desc * itfref, t_event_params_handle);
+
+/*!
+ * \internal
+ * \brief MPC->Host communication ARM this description
+ *
+ * \ingroup COMMUNICATION_INTERNAL
+ */
+typedef struct {
+ t_cm_bf_mpc2host_handle mpc2hostId; //!< Handle of the MPC->Host binding factory
+ void *clientContext; //!< Pointer to the callback client context provided by the user
+ t_jump_method *jumpMethods; //!< Jump method of skeleton
+} t_nmf_dsp_arm_this;
+
+/*!
+ * \internal
+ * \brief Skeleton init function API description (MPC->Host communication)
+ * For each interface, the NMF tools generate (nkitfc -mpc2[host|user] <interface name>) skeleton jump methods array.
+ * regarding the given interface
+ * A reference of this array is added into the dedicated array and this last one is referenced into a t_nmf_skel_register variable
+ *
+ * \ingroup COMMUNICATION_INTERNAL
+ */
+typedef struct {
+ const char *itfType;
+ t_jump_method *jumpMethods;
+} t_nmf_skel_function;
+
+
+/*!
+ * \internal
+ * \brief Skel init function register entry (DSP->ARM communication)
+ * \see t_nmf_skel_function description
+ *
+ * \ingroup COMMUNICATION_INTERNAL
+ */
+typedef struct {
+ t_nmf_bc_coding_style codingStyle;
+ t_uint32 numElems;
+ const t_nmf_skel_function *pSkelFctArray;
+} t_nmf_skel_register;
+
+
+/*!
+ * \brief Allocate Event buffer where parameters will be marshalled.
+ *
+ * The purpose of this pre-allocation is to avoid copy.
+ *
+ * \param[in] host2mpcId Host -> MPC param id.
+ *
+ * \return Event param handle.
+
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC t_event_params_handle CM_INTERNAL_AllocEvent(t_cm_bf_host2mpc_handle host2mpcId);
+
+/*!
+ * \brief Push a event in Fifo.
+ *
+ * \param[in] host2mpcId Host -> MPC param id.
+ * \param[in] h Event param handle.
+ * \param[in] methodIndex Index in the interface of the method to be called.
+ *
+ * \exception CM_MPC_NOT_RESPONDING
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC t_cm_error CM_INTERNAL_PushEvent(
+ t_cm_bf_host2mpc_handle host2mpcId,
+ t_event_params_handle h,
+ t_uint32 methodIndex);
+
+/*!
+ * \brief Push a event in Fifo.
+ *
+ * \param[in] host2mpcId Host -> MPC param id.
+ * \param[in] h Event param handle.
+ * \param[in] size Event param size.
+ * \param[in] methodIndex Index in the interface of the method to be called.
+ *
+ * \exception CM_PARAM_FIFO_FULL
+ * \exception CM_MPC_NOT_RESPONDING
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_INTERNAL_PushEventWithSize(
+ t_cm_bf_host2mpc_handle host2mpcId,
+ t_event_params_handle h,
+ t_uint32 size,
+ t_uint32 methodIndex);
+
+/********************************************************************************/
+/* Configuration Component Manager API internal prototypes */
+/* Not used directly by user (used only by REGISTER_XX macros) */
+/********************************************************************************/
+/*!
+ * \brief Host stubs registering facilities.
+ *
+ * Not used directly by user (used only by CM_REGISTER_XX macros)
+ *
+ * \param[in] pInterfacesArray Stubs array.
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC IMPORT_SHARED void CM_INTERNAL_RegisterHost2MpcInterfaces(t_nmf_stub_register *pInterfacesArray);
+
+/*!
+ * \brief Host stubs unregistering facilities.
+ *
+ * Not used directly by user (used only by CM_REGISTER_XX macros)
+ *
+ * \param[in] pInterfacesArray Stubs array.
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC IMPORT_SHARED void CM_INTERNAL_UnregisterHost2MpcInterfaces(t_nmf_stub_register *pInterfacesArray);
+
+/*!
+ * \brief Host skeletons registering facilities.
+ *
+ * Not used directly by user (used only by CM_REGISTER_XX macros)
+ *
+ * \param[in] pInterfacesArray Skels array.
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC IMPORT_SHARED void CM_INTERNAL_RegisterMpc2HostInterfaces(t_nmf_skel_register *pInterfacesArray);
+
+/*!
+ * \brief Host skeletons unregistering facilities.
+ *
+ * Not used directly by user (used only by CM_REGISTER_XX macros)
+ *
+ * \param[in] pInterfacesArray Skels array.
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC IMPORT_SHARED void CM_INTERNAL_UnregisterMpc2HostInterfaces(t_nmf_skel_register *pInterfacesArray);
+
+
+#endif /* COMMON_STUB_REQUIREAPI_H */
diff --git a/drivers/staging/nmf-cm/cm/proxy/api/user_proxy.h b/drivers/staging/nmf-cm/cm/proxy/api/user_proxy.h
new file mode 100644
index 00000000000..ca8b4f9664e
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/api/user_proxy.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Components Component Manager API.
+ *
+ * This file contains the Component Manager API for manipulating components.
+ */
+/*!
+ * \defgroup COMPONENT_HOST CM API for using interface in C
+ * \note The purpose of this dedicated API is mainly for dealing with host C core programming model which is not
+ * the same as the component base one used on sub-system core.
+ * \ingroup CM_USER_API
+ */
+#ifndef COMMON_USER_WRAPPER_H
+#define COMMON_USER_WRAPPER_H
+
+/*!
+ * \brief Bind the Host to a component in C.
+ *
+ * Create a binding between the Host and a server component running on a MPC in C.
+ * \brief Bind the User client to a component.
+ *
+ * Create a binding between the User Client and a server component running on a MPC.
+ *
+ * This method and the returned interface can be used like this: \code
+ * t_cm_instance_handle server;
+ * Iservice T;
+ * t_cm_error error;
+ *
+ * error = CM_BindComponentFromUser(
+ * server,
+ * "input",
+ * 8,
+ * (t_cm_interface_desc*)&T);
+ *
+ * T.meth(T.THIS, 123);
+ * \endcode
+ * In this example, we assume that the Iservice type was generated by tools
+ * using the "service.itf" interface file description that can look like this:\code
+ * void meth(t_uint24 i);
+ * \endcode
+ *
+ * In fact, this method do exactly the same thing as the definition bellow in the
+ * architecture file in the static Nomadik Kernel approach:\code
+ * binds arm.T to serverName.input
+ * \endcode
+ *
+ * \param[in] server The server component instance handle.
+ * \param[in] providedItfServerName The server provided interface name (either collection or not collection name allowed).
+ * \param[in] fifosize the number of element in the event fifo (number of simultaneously waiting request)
+ * \param[out] itfref The itfref to be filled and called during a User to MPC
+ * communication.
+ *
+ * \exception CM_NO_SUCH_PROVIDED_INTERFACE Interface name not provided by the component.
+ * \exception CM_BINDING_COMPONENT_NOT_FOUND Binding component require for this binding not found in the
+ * component repository.
+ * \exception CM_INVALID_PARAMETER Invalid parameter (Fifo too bug, ...)
+ * \exception CM_NO_MORE_MEMORY
+ *
+ * \ingroup COMPONENT_HOST
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_BindComponentFromUser(
+ const t_cm_instance_handle server,
+ const char* providedItfServerName,
+ t_uint32 fifosize,
+ t_cm_interface_desc* itfref);
+
+
+/*!
+ * \brief Bind the Host to a component in C.
+ *
+ * This is the same API as \ref CM_BindComponentFromUser, except that you are able to specify
+ * memory where storing event associated with communication channel.
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_BindComponentFromUserEx(
+ const t_cm_instance_handle server,
+ const char* providedItfServerName,
+ t_uint32 fifosize,
+ t_cm_mpc_memory_type eventMemType,
+ t_cm_interface_desc* itfref);
+
+/*!
+ * \brief unBind a component from the Host in C.
+ *
+ * Destroy a binding between the Host and a server component running on a MPC in C.
+ *
+ * \param[in] itfref Interface handle to be unbound.
+ *
+ * \exception CM_INVALID_PARAMETER Invalid parameter (NULL reference)
+ * \exception CM_ILLEGAL_UNBINDING Try to unbind component bind with other binding factories.
+ *
+ * \ingroup COMPONENT_HOST
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_UnbindComponentFromUser(
+ t_cm_interface_desc* itfref);
+
+
+
+/*!
+ * \brief Bind a component to the user process.
+ *
+ * Create a asynchronous binding between client component running on a MPC and the user programm
+ * by using the specified channel (the one where all messages concerning this binding will arrive)
+ *
+ * This method can be used like this in C: \code
+ *
+ * void mymeth(void *THIS, t_uint24 i) {
+ * // Automatically called by the client MPC component.
+ * }
+ *
+ * Iservice myT = {0, {mymeth}};
+ *
+ * void main() {
+ * t_cm_instance_handle client;
+ * t_nmf_channel channel;
+ * t_cm_error error;
+ *
+ * error = CM_CreateChannel(NMF_CHANNEL_PRIVATE, &channel);
+ * //check error
+ * ...
+ * error = CM_BindComponentToUser(
+ * channel,
+ * client,
+ * "output",
+ * &myT,
+ * 8);
+ * }
+ * \endcode
+ *
+ * This method can be used like this in C++: \code
+ *
+ * class mycb: public serviceDescriptor {
+ * void meth(t_uint24) {
+ * // Automatically called by the client MPC component.
+ * }
+ * };
+ *
+ * mycb mycbi;
+ *
+ * void main() {
+ * t_cm_instance_handle client;
+ * t_cm_error error;
+ * t_nmf_channel channel;
+ *
+ * error = CM_CreateChannel(NMF_CHANNEL_PRIVATE, &channel);
+ * //check error
+ *
+ * error = CM_BindComponentToUser(
+ * channel,
+ * client,
+ * "output",
+ * &mycbi,
+ * 8);
+ * }
+ * \endcode
+ *
+ * See \ref CM_BindComponentFromUser for detail about Iservice.
+ *
+ * In fact, this method do exactly the same thing as the definition bellow in the
+ * architecture file in the static Nomadik Kernel approach:\code
+ * binds clientName.output to arm.myT
+ * \endcode
+ *
+ * \warning Be careful, since no copy is done of the content of the clientContext.
+ *
+ * \param[in] channel The channel on which the binding must be done
+ * \param[in] client The client component instance handle.
+ * \param[in] requiredItfClientName The client required interface name (either collection or not collection name allowed).
+ * \param[in] clientContext The user context to be returned when getting incoming message.
+ * \param[in] fifosize the number of element in the event fifo (number of simultaneously waiting request)
+ *
+ * \exception CM_NO_SUCH_REQUIRED_INTERFACE Interface name not required by the component.
+ * \exception CM_BINDING_COMPONENT_NOT_FOUND Binding component require for this binding not found in the
+ * component repository.
+ * \exception CM_INTERFACE_ALREADY_BINDED Required interface must be unbinded before rebind.
+ * \exception CM_COMPONENT_NOT_STOPPED Client component must be stopped before reconfigure it
+ * \exception CM_INVALID_PARAMETER Invalid parameter (Fifo too bug, ...)
+ * \exception CM_NO_MORE_MEMORY
+ *
+ * \ingroup COMPONENT_HOST
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_BindComponentToUser(
+ const t_nmf_channel channel,
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName,
+ void* clientContext,
+ t_uint32 fifosize);
+
+
+/*!
+ * \brief Unbind a component to the User.
+ *
+ * \param[in] channel The channel on which the binding has been done
+ * \param[in] client The client component instance handle.
+ * \param[in] requiredItfClientName The client required interface name (either collection or not collection name allowed).
+ * \param[out] clientContext return user context, pointer can be NULL.
+ *
+ * \warning It is up to the caller to garantee that the given channel is the one used for this binding
+ * (ie is the same as the one given to the CM_BindComponentToUser() call)
+ *
+ * \exception CM_NO_SUCH_REQUIRED_INTERFACE Interface name not required by the component.
+ * \exception CM_INTERFACE_NOT_BINDED The required interface is not binded.
+ * \exception CM_COMPONENT_NOT_STOPPED Client component must be stopped before reconfigure it
+ * \exception CM_ILLEGAL_UNBINDING Try to unbind component bind with other binding factories.
+ *
+ * \ingroup COMPONENT_HOST
+ */
+PUBLIC IMPORT_SHARED t_cm_error CM_UnbindComponentToUser(
+ const t_nmf_channel channel,
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName,
+ void **clientContext);
+
+/*!
+ * \brief Print some statistics about CM API duration
+ *
+ * \warning Only implemented in debug mode
+ *
+ * \ingroup COMPONENT_HOST
+ */
+PUBLIC IMPORT_SHARED void CM_PrintNmfUsage(void);
+#endif
diff --git a/drivers/staging/nmf-cm/cm/proxy/common/communication/inc/communication_internal.h b/drivers/staging/nmf-cm/cm/proxy/common/communication/inc/communication_internal.h
new file mode 100644
index 00000000000..35077a07c28
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/common/communication/inc/communication_internal.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Component Manager Communication Type.
+ */
+
+#ifndef PROXY_COMMUNICATION_INTERNAL_H
+#define PROXY_COMMUNICATION_INTERNAL_H
+
+#include <nmf/inc/channel_type.h>
+#include <cm/proxy/inc/osal.h>
+#include <cm/proxy/common/utils/inc/list.h>
+#include <cm/os/api/channel_os.h>
+
+/*!
+ * \brief Structure used to store a service callback and its context
+ *
+ * There is one list of service callback per channel.
+ * A same duo (service-callback, context) can be registered on several channels:
+ * In this case, the entry is duplicated in each channel list. (Later, this entry
+ * could be shared through each channel).
+ * 'count' is an "usage count": when equal to zero, the entry can be freed.
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+typedef struct {
+ t_uint32 count;
+ t_nmf_serviceCallback service;
+ void * serviceCtx;
+ t_list_entry this_entry;
+} t_service_entry;
+
+/*!
+ * \brief There is one such entry per opened channel.
+ *
+ * There is a list of service-callback per channel.
+ * 'count' is an "usage count": when equal to zero, the entry can be freed.
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+typedef struct {
+ t_uint32 count;
+ t_os_channel channel;
+ t_list_entry service_list;
+ t_nmf_osal_sync_handle service_lock;
+ char msg[1024];
+} t_channel_entry;
+
+extern t_channel_entry *shared_channel;
+
+/*!
+ * \brief Very early init of the CM Proxy
+ *
+ * The purpose of the function is to:
+ * - initialize the CM User Proxy library.
+ *
+ * It must be called once, before the main() function of the user program is called
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC void CM_INTERNAL_ProxyInit(void);
+
+/*!
+ * \brief Destroy of the CM Proxy
+ *
+ * The purpose of the function is to:
+ * - destroy the cery last ressources used in the CM User Proxy library.
+ *
+ * It must be called once, at exit() of the user program is called
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC void CM_INTERNAL_ProxyDestroy(void);
+
+/*!
+ * \brief Initialize the CM Proxy
+ *
+ * The purpose of the function is to:
+ * - initialize the CM User Proxy.
+ *
+ * It must be called once, by the OS Integrator, at the first \ref CM_CreateChannel() call.
+ * It's up to the OS Integrator to manage concurrent access to this call.
+ *
+ * \warning It MUST be called only once, unless CM_INTERNAL_Destroy() has been called before.
+ *
+ * \exception CM_NO_MORE_MEMORY Not enough memory to create the callback Channel.
+ * \exception CM_INTEGRATION_ERROR OS specific error.
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC t_nmf_error CM_INTERNAL_Init(void);
+
+/*!
+ * \brief Destroy the CM Proxy
+ *
+ * The purpose of the function is to:
+ * - destroy all ressources allocated by the CM User Proxy.
+ *
+ * It must be called, once, by the OS Integrator at the last \ref CM_CloseChannel() call.
+ * It's up to the OS Integrator to manage concurrent access to this call.
+ *
+ * \warning It MUST be called only once, if a CM_INTERNAL_Init() has been called before, once all
+ * previously allocated ressources have been freed
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC void CM_INTERNAL_Destroy(void);
+
+/*!
+ * \brief Create/get a channel.
+ *
+ * The purpose of the function is to create a channel or get a channel a channel entry
+ * in the common User Proxy Part, regarding the flag parameter
+ * This function must be called by \ref CM_CreateChannel()
+ *
+ * \param[in] flags Whether the caller want to create a new channel (\ref NMF_CHANNEL_PRIVATE)
+ * or use the shared one (\ref NMF_CHANNEL_SHARED) (it will be created
+ * if it does not yet exist)
+ * \param[out] channel Channel number.
+ * \param[out] OSchannel If *OSchannel is not NULL, the OS Integrator has to create an os_channel and set **OSchannel.
+ *
+ * \exception CM_NO_MORE_MEMORY Not enough memory to create the callback Channel.
+ * \exception CM_INVALID_PARAMETER The specified flags is invalid.
+ * \exception CM_INTEGRATION_ERROR OS specific error.
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC t_nmf_error CM_INTERNAL_CreateChannel(t_nmf_channel_flag flags,
+ t_nmf_channel *channel,
+ t_os_channel **OSchannel);
+
+/*!
+ * \brief Flush a channel.
+ *
+ * The purpose of this function is to return OSchannel need by OS Integration. In case of shared channel if
+ * reference counter will not be zero on next close then we return 0 in OSchannel, in that case OS integration
+ * must not generate CM_FLUSH_MESSAGE.
+ *
+ * \param[in] channel Channel number:
+ * \param[out] OSchannel Is *OSchannel is not NULL, the OS integrator has to generate a CM_FLUSH_MESSAGE error in CM_GetMessage().
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC t_nmf_error CM_INTERNAL_FlushChannel(t_nmf_channel channel,
+ t_os_channel *OSchannel);
+
+/*!
+ * \brief Close a channel
+ *
+ * The purpose of the function is to destroy or clear a reference to a channel from the user to the CM engine
+ * in the common User Proxy Part. This function must be called by \ref CM_CloseChannel()
+ *
+ *
+ *
+ * \param[in] channel Channel number:
+ * \param[out] pOSchannel If successful, *pOSchannel is filled. If *pOSchannel is not 0, the OS Integration have to destroy the channel pointed by pOSchannel
+ *
+ * \exception CM_INVALID_PARAMETER The specified channel is invalid or some callback are still registered.
+ *
+ * \ingroup CM_INTERNAL_API
+ */
+PUBLIC t_nmf_error CM_INTERNAL_CloseChannel(t_nmf_channel channel,
+ t_os_channel *pOSchannel);
+#endif /* PROXY_COMMUNICATION_INTERNAL_H */
diff --git a/drivers/staging/nmf-cm/cm/proxy/common/communication/inc/hoststubs_mgt.h b/drivers/staging/nmf-cm/cm/proxy/common/communication/inc/hoststubs_mgt.h
new file mode 100644
index 00000000000..61ebeaee145
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/common/communication/inc/hoststubs_mgt.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/**
+ * \internal
+ */
+#ifndef COMMON_HOSTSTUBS_MGT_H
+#define COMMON_HOSTSTUBS_MGT_H
+
+#include <cm/proxy/api/private/stub-requiredapi.h>
+
+PUBLIC t_cm_error cm_HSM_getItfStubInitFct(t_nmf_bc_coding_style style, const char *itfType, t_nmf_init_stub_fct *pInitStubFct);
+PUBLIC t_cm_error cm_HSM_getItfSkelInitFct(t_nmf_bc_coding_style style, const char *itfType, t_jump_method **jumpMethods);
+
+#endif /* COMMON_HOSTSTUBS_MGT_H */
diff --git a/drivers/staging/nmf-cm/cm/proxy/common/component/inc/component_info.h b/drivers/staging/nmf-cm/cm/proxy/common/component/inc/component_info.h
new file mode 100644
index 00000000000..46ab323aee2
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/common/component/inc/component_info.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief Component Manager Internal API for stubs.
+ */
+#ifndef COMMON_COMPONENT_INFO_H
+#define COMMON_COMPONENT_INFO_H
+
+/*!
+ * \brief Component Memory Types.
+ * \ingroup COMPONENT
+ */
+typedef t_uint32 t_cm_mpc_info_memory_type;
+#define CM_MM_ESRAM_TYPE ((t_cm_mpc_info_memory_type)0)
+#define CM_MM_SDRAM_TYPE ((t_cm_mpc_info_memory_type)1)
+#define CM_MM_XRAM_TYPE ((t_cm_mpc_info_memory_type)2)
+#define CM_MM_YRAM_TYPE ((t_cm_mpc_info_memory_type)3)
+#define CM_MM_NB_MEMTYPES 4
+
+/*!
+ * \brief Purpose of the memory section - code or data.
+ * \ingroup COMPONENT
+ */
+typedef t_uint8 t_cm_mpc_info_memory_purpose;
+#define CM_MM_CODE_TYPE ((t_cm_mpc_info_memory_purpose)0)
+#define CM_MM_DATA_TYPE ((t_cm_mpc_info_memory_purpose)1)
+#define CM_MM_NB_PURPOSES 2
+
+/*!
+ * \brief Definition of structure returned by the component memory info functions.
+ * \ingroup COMPONENT
+ */
+typedef struct {
+ t_uint32 firstInstantiate[CM_MM_NB_PURPOSES][CM_MM_NB_MEMTYPES]; //!< First instantiation sizes
+ t_uint32 subsequentInstantiate[CM_MM_NB_PURPOSES][CM_MM_NB_MEMTYPES]; //!< Following instantiation sizes
+} t_cm_component_memory_info;
+
+extern t_cm_error cm_getComponentMemoryInfo(
+ const char* templateName,
+ t_cm_component_memory_info *pInfo);
+
+#endif /* COMMON_COMPONENT_INFO_H */
diff --git a/drivers/staging/nmf-cm/cm/proxy/common/component/inc/interface_type.h b/drivers/staging/nmf-cm/cm/proxy/common/component/inc/interface_type.h
new file mode 100644
index 00000000000..20041f14840
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/common/component/inc/interface_type.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/**
+ * \internal
+ */
+#ifndef COMMON_INTERFACE_TYPE_H
+#define COMMON_INTERFACE_TYPE_H
+
+#include <cm/engine/communication/inc/communication_type.h>
+
+/*!
+ * \brief Generic Interface method type.
+ *
+ * \exception CM_PARAM_FIFO_OVERFLOW
+ *
+ * \ingroup COMPONENT_HOST
+ */
+typedef t_cm_error (*t_nmf_itf_method)(void* THIS);
+
+
+/*!
+ * \brief Identifier of the interface descriptor in C
+ *
+ * This interface descriptor can be used to call a component in C.
+ * This generic type must be casted into a specific generated C interface descriptor.
+ * Contrary to the Media Processor Core toolset which handle multi-instances automatically,
+ * each method in this structure must have a THIS as the first parameter and look like this: \code
+ * xxx meth(void* THIS, ...);
+ * \endcode
+ * This THIS reference must be filled by the data field of this structure
+ * before when calling a mathod. Thus, a call look like this:\code
+ * t_cm_error error;
+ * error = I.meth(i.THIS, ...);
+ * \endcode
+ * or by using NMFCALL macro (see CM Macros chapter):\code
+ * t_cm_error error;
+ * error = NMFCALL(I, meth)(...);
+ * \endcode
+ *
+ * This THIS parameter as no type and must be cast manually into the real
+ * structure of the Host instance.
+ *
+ * On a Host to MPC communication, this reference the STUB that must be
+ * called to call a component on the MPC.
+ *
+ * On a MPC to Host communication, this reference the client
+ * component that must be called by the MPC component.\n
+ * The NMFMETH macro is provided to ease the client method writting.\n
+ * By example, the user can write:\code
+ * void mynotify(void *THIS, mytype1 myparam1, mytype2 myparam2, ...) {
+ * <body of the interface routine>
+ * }
+ * \endcode
+ * or by using the NMFMETH macro (see CM Macros chapter):\code
+ * void NMFMETH(mynotify)(mytype1 myparam1, mytype2 myparam2, ...) {
+ * <body of the interface routine>
+ * }
+ * \endcode
+ *
+ * \ingroup COMPONENT_HOST
+ */
+typedef struct {
+ void *THIS; //!< Reference on the component instance address
+ t_nmf_itf_method methods[1]; //!< Reference the methods address
+} t_cm_interface_desc;
+
+/*!
+ * \brief Identifier of the abstract interface descriptor
+ *
+ * This interface descriptor can be abstract a interface on a component in C and C++.
+ *
+ * \ingroup COMPONENT_HOST
+ */
+typedef void t_nmf_interface_desc;
+
+#endif /* CM_INTERFACE_TYPE_H */
diff --git a/drivers/staging/nmf-cm/cm/proxy/common/repository/inc/repository.h b/drivers/staging/nmf-cm/cm/proxy/common/repository/inc/repository.h
new file mode 100644
index 00000000000..bb1188ebaf0
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/common/repository/inc/repository.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/**
+ * \internal
+ */
+#ifndef REPOSITORY_H_
+#define REPOSITORY_H_
+
+#include <cm/inc/cm_type.h>
+#include <cm/proxy/inc/osal.h>
+#include <inc/nmf-limits.h>
+
+t_cm_error cm_openComponent(const char *templateName, t_nmf_osal_fs_file_handle *file, void **data, t_cm_size *size, t_bool userspace);
+void cm_closeComponent(t_nmf_osal_fs_file_handle file);
+
+t_cm_error cm_pushComponent(int nb, char fileList[][MAX_INTERFACE_TYPE_NAME_LENGTH]);
+
+t_cm_error cm_openComponents(
+ int nb,
+ char fileList[][MAX_INTERFACE_TYPE_NAME_LENGTH],
+ t_nmf_osal_fs_file_handle files[],
+ void *datas[],
+ t_cm_size sizes[],
+ t_bool userspace);
+void cm_closeComponents(
+ int nb,
+ t_nmf_osal_fs_file_handle files[]);
+
+#endif /*REPOSITORY_H_*/
diff --git a/drivers/staging/nmf-cm/cm/proxy/common/utils/inc/list.h b/drivers/staging/nmf-cm/cm/proxy/common/utils/inc/list.h
new file mode 100644
index 00000000000..ebebc99ed50
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/common/utils/inc/list.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/**
+ * \internal
+ */
+#ifndef LIST_H_
+#define LIST_H_
+
+typedef struct list_element {
+ struct list_element *prev, *next;
+} t_list_entry;
+
+#define LIST_ENTRY(name) \
+ t_list_entry name = { &(name), &(name) }
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+/*!
+ * \brief get the struct for this entry
+ *
+ * \param[in] ptr the &t_list_entry pointer.
+ * \param[in] type the type of the struct this is embedded in.
+ * \param[in] member the name of the t_list_entry within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ ((type *)( (char *)ptr - ((unsigned int) &((type *)0)->member)))
+/*!
+ * \brief Initialize an entry
+ *
+ * \param[in] entry Entry to initialize
+ */
+void list_init_entry(t_list_entry *entry);
+
+/*!
+ * \brief Add a new entry
+ *
+ * \param[in] n: new entry to be added
+ * \param[in] entry list entry to add it after
+ *
+ * Insert a new entry after the specified entry.
+ */
+void list_add(t_list_entry *n, t_list_entry *entry);
+
+/*!
+ * \brief Deletes entry from list.
+ *
+ * \param[in] entry: the element to delete from the list.
+ */
+void list_del(t_list_entry *entry);
+
+/*!
+ * \brief Tests whether a list is empty
+ *
+ * \param[in] entry The list to test.
+ */
+int list_empty(const t_list_entry *entry);
+
+#endif /*LIST_H_*/
+
diff --git a/drivers/staging/nmf-cm/cm/proxy/common/wrapper/inc/stub.h b/drivers/staging/nmf-cm/cm/proxy/common/wrapper/inc/stub.h
new file mode 100644
index 00000000000..1f52c51c4c3
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/common/wrapper/inc/stub.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/**
+ * \internal
+ */
+#ifndef CM_PROXY_WRAPPER_STUB_H
+#define CM_PROXY_WRAPPER_STUB_H
+
+#include <cm/inc/cm_type.h>
+#include <cm/proxy/api/private/stub-requiredapi.h>
+#include <cm/engine/component/inc/component_type.h>
+
+PUBLIC t_cm_error CM_BindComponentFromUserR(
+ const t_cm_instance_handle server,
+ const char * providedItfServerName,
+ t_uint32 fifosize,
+ t_cm_mpc_memory_type eventMemType,
+ t_nmf_interface_desc * itfref,
+ t_nmf_bc_coding_style codingStyle);
+
+PUBLIC t_cm_error CM_UnbindComponentFromUserR(
+ t_cm_bf_host2mpc_handle host2mpc);
+
+PUBLIC t_cm_error CM_BindComponentToUserR(
+ const t_nmf_channel channel,
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName,
+ void* clientContext,
+ t_uint32 fifosize,
+ t_nmf_bc_coding_style codingStyle);
+
+PUBLIC t_cm_error CM_UnbindComponentToUserR(
+ const t_nmf_channel channel,
+ const t_cm_instance_handle client,
+ const char* requiredItfClientName,
+ void **clientContext);
+
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm/proxy/inc/osal.h b/drivers/staging/nmf-cm/cm/proxy/inc/osal.h
new file mode 100644
index 00000000000..a5775ce481f
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm/proxy/inc/osal.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief OSAL CM Proxy OSAL (Operating System Abstraction Layer) API
+ *
+ * This file contains the OSAL API for CM proxy that must be implemented by integrator for each
+ * user or kernel application that access CM API. This CM API was provided through a CM user or kernel proxy.
+ *
+ * \defgroup CM_PROXY_OSAL_API CM Proxy OSAL (Operating System Abstraction Layer) API
+ * \ingroup CM_PROXY_MODULE
+ */
+#ifndef CM_COMMON_OSAL_H_
+#define CM_COMMON_OSAL_H_
+
+#include <cm/inc/cm_type.h>
+
+/*!
+ * \brief Identifier of file open by OSAL
+ * \ingroup CM_PROXY_OSAL_API
+ */
+typedef t_uint32* t_nmf_osal_fs_file_handle;
+
+/*!
+ * \brief Identifier of lock create by OSAL
+ * \ingroup CM_PROXY_OSAL_API
+ */
+typedef t_uint32 t_nmf_osal_sync_handle;
+
+/*!
+ * \brief Global User Proxy lock declaration
+ */
+extern t_nmf_osal_sync_handle cmProxyLock;
+
+/*!
+ * \brief Memory Allocation routine (libc malloc-like).
+ *
+ * \param size minium size in byte to be allocated
+ *
+ * \ingroup CM_PROXY_OSAL_API
+ */
+PUBLIC void* OSAL_Alloc(t_cm_size size);
+
+/*!
+ * \brief Memory Free routine (libc free-like).
+ *
+ * \param pHandle previously allocated chunk handle
+ *
+ * \ingroup CM_PROXY_OSAL_API
+ */
+PUBLIC void OSAL_Free(void *pHandle);
+
+/*!
+ * \brief Log routine (libc free-like).
+ *
+ * \param format sprintf string limited to integer value and 3 parameters
+ * \param param1
+ * \param param2
+ * \param param3
+ *
+ * \ingroup CM_PROXY_OSAL_API
+ */
+PUBLIC void OSAL_Log(const char *format, void* param1, void* param2, void* param3);
+
+/* FILESYSTEM */
+
+/*!
+ * \brief Description of the File System part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Load dynamically the component file from the component repository.
+ * Here, it loads the file and read the data into a buffer
+ *
+ * Called (indirectly) by:
+ * - CM_RepositoryInit() call once at initialization of the client
+ * - CM_InstantiateComponent() call (only for the first user component instantiation and when loading the EE on a given Media Processor)
+ * - CM_BindComponentxxx() call (only for the first binding component instantiation)
+ *
+ * \param[in] fileName: (const char *) path of the file to be checked
+ * \param[out] buffer: (void **) pointer to the data read (content of the file)
+ * \param[out] pSize: (t_cm_size *) pointer to the size of the data read
+ * \param[in] userspace: (t_bool) boolean to specify whether the handler/pointer returned will be used in userspace or kernelspace
+ *
+ * \return file handle on the file opened or Null in case of error
+ *
+ * \ingroup CM_PROXY_OSAL_API
+ */
+PUBLIC t_nmf_osal_fs_file_handle OSAL_ReadFile(const char *fileName, void **buffer, t_cm_size *pSize, t_bool userspace);
+
+/*!
+ * \brief Description of the File System part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Load dynamically the component file from the component repository.
+ * Here, it closes the file
+ *
+ * Called (indirectly through cm_pushComponent()) by:
+ * - CM_Init() call once at initialization of the client
+ * - CM_InstantiateComponent() call (only for the first user component instantiation and when loading the EE on a given Media Processor)
+ * - CM_BindComponentxxx() call (only for the first binding component instantiation)
+ *
+ * \param[in] handle: (t_nmf_osal_fs_file_handle) handle of the file returned when OSAL_ReadFile is performed
+ *
+ * \ingroup CM_PROXY_OSAL_API
+ */
+PUBLIC void OSAL_CloseFile(t_nmf_osal_fs_file_handle handle);
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Use synchronize multiple call and access to some shared variables
+ * in a multithreaded context.
+ *
+ * \return handle of the Mutex created
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_PROXY_OSAL_API
+ */
+PUBLIC t_nmf_osal_sync_handle OSAL_CreateLock(void);
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Get the ownership of the given lock
+ *
+ * \param[in] handle handle of the Mutex to be locked
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_PROXY_OSAL_API
+ */
+PUBLIC void OSAL_Lock(
+ t_nmf_osal_sync_handle handle);
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Release the lock (caller must be the one get the ownership of the lock)
+ *
+ * \param[in] handle handle of the Mutex to be unlocked
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_PROXY_OSAL_API
+ */
+PUBLIC void OSAL_Unlock(
+ t_nmf_osal_sync_handle handle);
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * <B>Goal:</B> Destroy a lock. It can't be used any more after this.
+ *
+ * \param[in] handle handle of the Mutex to be destroyed
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup CM_PROXY_OSAL_API
+ */
+PUBLIC void OSAL_DestroyLock(
+ t_nmf_osal_sync_handle handle);
+
+#ifdef CM_API_TIMING
+/*!
+ * The following are used in debugging mode only to trace the timing of each CM calls.
+ * Return the current time in microsecond
+ */
+PUBLIC unsigned long long OSAL_GetTime(void);
+
+/*!
+ * The following table is used to register the timing in each CM call.
+ * The index used for each CM API is hard-coded (not fun, I know).
+ * Here are the index currenlty used:
+ *
+ * 0 CM_InstantiateComponent
+ * 1 CM_StartComponent
+ * 2 CM_StopComponent
+ * 3 CM_DestroyComponent
+ * 4 CM_BindComponent
+ * 5 CM_BindComponentTrace
+ * 6 CM_UnbindComponent
+ * 7 CM_BindComponentToVoid
+ * 8 CM_BindComponentAsynchronous
+ * 9 CM_UnbindComponentAsynchronous
+ * 10 CM_AllocMpcMemory
+ * 11 CM_FreeMpcMemory
+ * 12 CM_GetMpcMemorySystemAddress
+ * 13 CM_GetMpcMemoryMpcAddress
+ * 14 CM_GetMpcMemoryStatus
+ * 15 CM_GetMpcMemoryStatusEx
+ * 16 CM_CreateMemoryDomain
+ * 17 CM_DestroyMemoryDomain
+ * 18 CM_CreateMemoryDomainScratch
+ * 19 CM_GetDomainCoreId
+ * 20 CM_UnbindComponentFromUser
+ * 21 CM_BindComponentFromUser
+ * 22 CM_BindComponentFromUserEx
+ * 23 CM_BindComponentToUser
+ * 24 CM_UnbindComponentToUser
+ * 25 _Z24CM_BindComponentFromUsermPKcmPN3NMF18InterfaceReferenceE
+ * 26 _Z26CM_BindComponentFromUserExmPKcmhPN3NMF18InterfaceReferenceE
+ * 27 _Z26CM_UnbindComponentFromUserPN3NMF18InterfaceReferenceE
+ * 28 _Z22CM_BindComponentToUsermmPKcPvm
+ * 29 _Z24CM_UnbindComponentToUsermmPKcPPv
+ * 30 CM_GetComponentDescription
+ * 31 CM_ReadComponentAttribute
+ * 32 CM_GetComponentRequiredInterfaceNumber
+ * 33 CM_GetComponentRequiredInterface
+ * 34 CM_GetComponentRequiredInterfaceBinding
+ * 35 CM_GetComponentProvidedInterfaceNumber
+ * 36 CM_GetComponentProvidedInterface
+ * 37 CM_GetComponentPropertyNumber
+ * 38 CM_GetComponentPropertyName
+ * 39 CM_GetComponentPropertyValue
+ * 40 CM_GetComponentMemoryInfo
+ * 41 CM_GetComponentListHeader
+ * 42 CM_GetComponentListNext
+ *
+ * The following API are not traced today:
+ * CM_BindComponentAsynchronousEx (traced under CM_BindComponentAsynchronous)
+ * CM_CloseChannel
+ * CM_CreateChannel
+ * CM_ExecuteMessage
+ * CM_FlushChannel
+ * CM_GetExecutiveEngineHandle
+ * CM_GetMessage
+ * CM_GetVersion
+ * CM_INTERNAL_PushEventWithSize
+ * CM_INTERNAL_RegisterHost2MpcInterfaces
+ * CM_INTERNAL_RegisterMpc2HostInterfaces
+ * CM_INTERNAL_UnregisterHost2MpcInterfaces
+ * CM_INTERNAL_UnregisterMpc2HostInterfaces
+ * CM_Migrate
+ * CM_PushComponent
+ * CM_RegisterService
+ * CM_SetMode
+ * CM_StringError
+ * CM_Unmigrate
+ * CM_UnregisterService
+ * CM_getMpcLoadCounter
+ */
+#define NB_CM_API 64
+struct func_entry {
+ const char *name;
+ unsigned int time;
+ unsigned int call_count;
+};
+
+extern struct func_entry func[NB_CM_API];
+#endif
+
+#endif /*CM_COMMON_OSAL_H_ */
diff --git a/drivers/staging/nmf-cm/cm_service.c b/drivers/staging/nmf-cm/cm_service.c
new file mode 100644
index 00000000000..1b5ee111e09
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm_service.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+/** \file cm_service.c
+ *
+ * Nomadik Multiprocessing Framework Linux Driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/plist.h>
+#include <linux/slab.h>
+#include <linux/spinlock_types.h>
+
+#include <cm/engine/api/control/irq_engine.h>
+
+#include "osal-kernel.h"
+#include "cmld.h"
+#include "cm_service.h"
+
+/* Panic managment */
+static void service_tasklet_func(unsigned long);
+unsigned long service_tasklet_data = 0;
+DECLARE_TASKLET(cmld_service_tasklet, service_tasklet_func, 0);
+
+void dispatch_service_msg(struct osal_msg *msg)
+{
+ struct list_head *head, *next;
+
+ /* Note: no lock needed to protect the channel_list to protect against list
+ changes, as the current tasklet is disabled each time we modify the list */
+ list_for_each_safe(head, next, &channel_list) {
+ struct cm_channel_priv *channelPriv = list_entry(head, struct cm_channel_priv, entry);
+ struct osal_msg *new_msg;
+ size_t msg_size;
+
+ if (channelPriv->state == CHANNEL_CLOSED)
+ continue;
+ msg_size = sizeof(new_msg->hdr) + sizeof(new_msg->d.srv);
+ new_msg = kmalloc(msg_size, GFP_ATOMIC);
+ if (new_msg == NULL) {
+ pr_err("[CM] %s: can't allocate memory, service"
+ " message not dispatched !!\n", __func__);
+ continue;
+ }
+ memcpy(new_msg, msg, msg_size);
+ plist_node_init(&new_msg->msg_entry, 0);
+ spin_lock_bh(&channelPriv->bh_lock);
+ plist_add(&new_msg->msg_entry, &channelPriv->messageQueue);
+ spin_unlock_bh(&channelPriv->bh_lock);
+ wake_up_interruptible(&channelPriv->waitq);
+ }
+}
+
+static void service_tasklet_func(unsigned long unused)
+{
+ t_cm_service_type type;
+ t_cm_service_description desc;
+ int i=0;
+
+ do {
+ if (test_and_clear_bit(i, &service_tasklet_data)) {
+ CM_getServiceDescription(osalEnv.mpc[i].coreId, &type, &desc);
+
+ switch (type) {
+ case CM_MPC_SERVICE_PANIC: {
+ struct osal_msg msg;
+
+ msg.msg_type = MSG_SERVICE;
+ msg.d.srv.srvType = NMF_SERVICE_PANIC;
+ msg.d.srv.srvData.panic = desc.u.panic;
+
+ dispatch_service_msg(&msg);
+ break;
+ }
+ case CM_MPC_SERVICE_PRINT: {
+ char msg[256];
+ if (CM_ReadMPCString(osalEnv.mpc[i].coreId,
+ desc.u.print.dspAddress, msg,
+ sizeof(msg)) == CM_OK)
+ printk(msg, desc.u.print.value1, desc.u.print.value2);
+ break;
+ }
+ default:
+ pr_err("[CM] %s: MPC Service Type %d not supported\n", __func__, type);
+ }
+ enable_irq(osalEnv.mpc[i].interrupt1);
+ }
+ i = (i+1) % NB_MPC;
+ } while (service_tasklet_data != 0);
+}
diff --git a/drivers/staging/nmf-cm/cm_service.h b/drivers/staging/nmf-cm/cm_service.h
new file mode 100644
index 00000000000..39582eae573
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm_service.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+/** \file cm_service.c
+ *
+ * Nomadik Multiprocessing Framework Linux Driver
+ *
+ */
+
+#ifndef CM_SERVICE_H
+#define CM_SERVICE_H
+
+#include <linux/interrupt.h>
+
+extern unsigned long service_tasklet_data;
+extern struct tasklet_struct cmld_service_tasklet;
+void dispatch_service_msg(struct osal_msg *msg);
+
+#endif
diff --git a/drivers/staging/nmf-cm/cm_syscall.c b/drivers/staging/nmf-cm/cm_syscall.c
new file mode 100644
index 00000000000..ce38adc3b13
--- /dev/null
+++ b/drivers/staging/nmf-cm/cm_syscall.c
@@ -0,0 +1,1341 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <cm/engine/api/cm_engine.h>
+#include "cmioctl.h"
+#include "osal-kernel.h"
+#include "cmld.h"
+
+/** Dequeue and free per-process messages for specific binding
+ *
+ * \note
+ * This is only safe if the per process mutex is held when called.
+ */
+static inline void freeMessages(struct cm_channel_priv* cPriv, t_skelwrapper* binding)
+{
+ struct osal_msg *this, *next;
+
+ spin_lock_bh(&cPriv->bh_lock);
+
+ /* free any pending messages */
+ plist_for_each_entry_safe(this, next, &cPriv->messageQueue, msg_entry) {
+ if (this->msg_type == MSG_INTERFACE
+ && this->d.itf.skelwrap == binding) {
+ plist_del(&this->msg_entry, &cPriv->messageQueue);
+ kfree(this);
+ }
+ }
+ spin_unlock_bh(&cPriv->bh_lock);
+}
+
+static t_cm_error copy_string_from_user(char *dst, const char __user *src, int len)
+{
+ int ret;
+
+ ret = strncpy_from_user(dst, src, len);
+ if (ret < 0) /* -EFAULT */
+ return CM_INVALID_PARAMETER;
+
+ if (ret >= len)
+ return CM_OUT_OF_LIMITS;
+
+ return 0;
+}
+
+inline int cmld_InstantiateComponent(struct cm_process_priv* procPriv,
+ CM_InstantiateComponent_t __user *param)
+{
+ CM_InstantiateComponent_t data;
+ char templateName[MAX_TEMPLATE_NAME_LENGTH];
+ char localName[MAX_COMPONENT_NAME_LENGTH];
+ char *dataFile = NULL;
+
+ /* Copy all user data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if (data.in.dataFile != NULL) {
+ dataFile = OSAL_Alloc(data.in.dataFileSize);
+ if (dataFile == NULL) {
+ data.out.error = CM_NO_MORE_MEMORY;
+ goto out;
+ }
+ /* coverity[tainted_data : FALSE] */
+ if (copy_from_user(dataFile, data.in.dataFile, data.in.dataFileSize)) {
+ data.out.error = CM_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+
+ if ((data.out.error = copy_string_from_user(templateName,
+ data.in.templateName,
+ sizeof(templateName))))
+ goto out;
+
+ if ((data.in.localName != NULL) &&
+ (data.out.error = copy_string_from_user(localName,
+ data.in.localName,
+ sizeof(localName))))
+ goto out;
+
+ /* Do appropriate CM Engine call */
+ data.out.error = CM_ENGINE_InstantiateComponent(templateName,
+ data.in.domainId,
+ procPriv->pid,
+ data.in.priority,
+ data.in.localName ? localName : NULL,
+ dataFile,
+ &data.out.component);
+
+out:
+ if (dataFile)
+ OSAL_Free(dataFile);
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_BindComponentFromCMCore(struct cm_process_priv* procPriv,
+ CM_BindComponentFromCMCore_t __user *param)
+{
+ CM_BindComponentFromCMCore_t data;
+ char providedItfServerName[MAX_INTERFACE_NAME_LENGTH];
+ char *dataFileSkeleton = NULL;
+
+ /* Copy all user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(providedItfServerName,
+ data.in.providedItfServerName,
+ sizeof(providedItfServerName))))
+ goto out;
+
+ if (data.in.dataFileSkeleton != NULL) {
+ dataFileSkeleton = OSAL_Alloc(data.in.dataFileSkeletonSize);
+ if (dataFileSkeleton == NULL) {
+ data.out.error = CM_NO_MORE_MEMORY;
+ goto out;
+ }
+ /* coverity[tainted_data : FALSE] */
+ if (copy_from_user(dataFileSkeleton, data.in.dataFileSkeleton,
+ data.in.dataFileSkeletonSize)) {
+ data.out.error = CM_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+
+ data.out.error = CM_ENGINE_BindComponentFromCMCore(data.in.server,
+ providedItfServerName,
+ data.in.fifosize,
+ data.in.eventMemType,
+ &data.out.host2mpcId,
+ procPriv->pid,
+ dataFileSkeleton);
+out:
+ if (dataFileSkeleton)
+ OSAL_Free(dataFileSkeleton);
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+
+ return 0;
+}
+
+inline int cmld_UnbindComponentFromCMCore(CM_UnbindComponentFromCMCore_t __user *param)
+{
+ CM_UnbindComponentFromCMCore_t data;
+
+ /* Copy all user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_UnbindComponentFromCMCore(data.in.host2mpcId);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_BindComponentToCMCore(struct cm_channel_priv* channelPriv,
+ CM_BindComponentToCMCore_t __user *param)
+{
+ CM_BindComponentToCMCore_t data;
+ t_skelwrapper *skelwrapper;
+ struct cm_process_priv *procPriv = channelPriv->proc;
+ char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH];
+ char *dataFileStub = NULL;
+
+ /* Copy all user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(requiredItfClientName,
+ data.in.requiredItfClientName,
+ sizeof(requiredItfClientName))))
+ goto out;
+
+ /* Do appropriate CM Engine call */
+ skelwrapper = (t_skelwrapper *)OSAL_Alloc(sizeof(*skelwrapper));
+ if (skelwrapper == NULL) {
+ data.out.error = CM_NO_MORE_MEMORY;
+ goto out;
+ }
+
+ if (data.in.dataFileStub != NULL) {
+ dataFileStub = OSAL_Alloc(data.in.dataFileStubSize);
+ if (dataFileStub == NULL) {
+ data.out.error = CM_NO_MORE_MEMORY;
+ goto out;
+ }
+ /* coverity[tainted_data : FALSE] */
+ if (copy_from_user(dataFileStub, data.in.dataFileStub, data.in.dataFileStubSize)) {
+ data.out.error = CM_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+
+ if ((data.out.error = CM_ENGINE_BindComponentToCMCore(
+ data.in.client,
+ requiredItfClientName,
+ data.in.fifosize,
+ (t_nmf_mpc2host_handle)skelwrapper,
+ dataFileStub,
+ &data.out.mpc2hostId,
+ procPriv->pid)) != CM_OK) {
+ OSAL_Free(skelwrapper);
+ goto out;
+ }
+
+ skelwrapper->upperLayerThis = data.in.upLayerThis;
+ skelwrapper->mpc2hostId = data.out.mpc2hostId;
+ skelwrapper->channelPriv = channelPriv;
+ mutex_lock(&channelPriv->skelListLock);
+ list_add(&skelwrapper->entry, &channelPriv->skelList);
+ mutex_unlock(&channelPriv->skelListLock);
+out:
+ if (dataFileStub != NULL)
+ OSAL_Free(dataFileStub);
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_UnbindComponentToCMCore(struct cm_process_priv* procPriv,
+ CM_UnbindComponentToCMCore_t __user *param)
+{
+ CM_UnbindComponentToCMCore_t data;
+ t_skelwrapper *skelwrapper;
+ char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH];
+
+ /* Copy all user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(requiredItfClientName,
+ data.in.requiredItfClientName,
+ sizeof(requiredItfClientName))))
+ goto out;
+
+ data.out.error = CM_ENGINE_UnbindComponentToCMCore(
+ data.in.client, requiredItfClientName,
+ (t_nmf_mpc2host_handle*)&skelwrapper,
+ procPriv->pid);
+
+ if (data.out.error != CM_OK && data.out.error != CM_MPC_NOT_RESPONDING)
+ goto out;
+
+ data.out.upLayerThis = skelwrapper->upperLayerThis;
+
+ mutex_lock(&skelwrapper->channelPriv->msgQueueLock);
+ freeMessages(skelwrapper->channelPriv, skelwrapper);
+ mutex_lock(&skelwrapper->channelPriv->skelListLock);
+ list_del(&skelwrapper->entry);
+ mutex_unlock(&skelwrapper->channelPriv->skelListLock);
+ mutex_unlock(&skelwrapper->channelPriv->msgQueueLock);
+ OSAL_Free(skelwrapper);
+out:
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_BindComponentAsynchronous(struct cm_process_priv* procPriv,
+ CM_BindComponentAsynchronous_t __user *param)
+{
+ CM_BindComponentAsynchronous_t data;
+ char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH];
+ char providedItfServerName[MAX_INTERFACE_NAME_LENGTH];
+ char *dataFileSkeletonOrEvent = NULL;
+ char *dataFileStub = NULL;
+
+ /* Copy all user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(requiredItfClientName,
+ data.in.requiredItfClientName,
+ sizeof(requiredItfClientName))))
+ goto out;
+
+ if ((data.out.error = copy_string_from_user(providedItfServerName,
+ data.in.providedItfServerName,
+ sizeof(providedItfServerName))))
+ goto out;
+
+ if (data.in.dataFileSkeletonOrEvent != NULL) {
+ dataFileSkeletonOrEvent =
+ OSAL_Alloc(data.in.dataFileSkeletonOrEventSize);
+ if (dataFileSkeletonOrEvent == NULL) {
+ data.out.error = CM_NO_MORE_MEMORY;
+ goto out;
+ }
+ /* coverity[tainted_data : FALSE] */
+ if (copy_from_user(dataFileSkeletonOrEvent, data.in.dataFileSkeletonOrEvent, data.in.dataFileSkeletonOrEventSize)) {
+ data.out.error = CM_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+
+ if (data.in.dataFileStub != NULL) {
+ dataFileStub = OSAL_Alloc(data.in.dataFileStubSize);
+ if (dataFileStub == NULL) {
+ data.out.error = CM_NO_MORE_MEMORY;
+ goto out;
+ }
+ /* coverity[tainted_data : FALSE] */
+ if (copy_from_user(dataFileStub, data.in.dataFileStub, data.in.dataFileStubSize)) {
+ data.out.error = CM_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+
+ /* Do appropriate CM Engine call */
+ data.out.error = CM_ENGINE_BindComponentAsynchronous(data.in.client,
+ requiredItfClientName,
+ data.in.server,
+ providedItfServerName,
+ data.in.fifosize,
+ data.in.eventMemType,
+ procPriv->pid,
+ dataFileSkeletonOrEvent,
+ dataFileStub);
+
+out:
+ if (dataFileSkeletonOrEvent != NULL)
+ OSAL_Free(dataFileSkeletonOrEvent);
+ if (dataFileStub != NULL)
+ OSAL_Free(dataFileStub);
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_UnbindComponentAsynchronous(struct cm_process_priv* procPriv,
+ CM_UnbindComponentAsynchronous_t __user *param)
+{
+ CM_UnbindComponentAsynchronous_t data;
+ char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH];
+
+ /* Copy all user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(requiredItfClientName,
+ data.in.requiredItfClientName,
+ sizeof(requiredItfClientName))))
+ goto out;
+
+ /* Do appropriate CM Engine call */
+ /* coverity[tainted_data : FALSE] */
+ data.out.error = CM_ENGINE_UnbindComponentAsynchronous(data.in.client,
+ requiredItfClientName,
+ procPriv->pid);
+out:
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_BindComponent(struct cm_process_priv* procPriv,
+ CM_BindComponent_t __user *param)
+{
+ CM_BindComponent_t data;
+ char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH];
+ char providedItfServerName[MAX_INTERFACE_NAME_LENGTH];
+ char *dataFileTrace = NULL;
+
+ /* Copy all user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(requiredItfClientName,
+ data.in.requiredItfClientName,
+ sizeof(requiredItfClientName))))
+ goto out;
+
+ if ((data.out.error = copy_string_from_user(providedItfServerName,
+ data.in.providedItfServerName,
+ sizeof(providedItfServerName))))
+ goto out;
+
+ if (data.in.dataFileTrace != NULL) {
+ dataFileTrace = OSAL_Alloc(data.in.dataFileTraceSize);
+ if (dataFileTrace == NULL) {
+ data.out.error = CM_NO_MORE_MEMORY;
+ goto out;
+ }
+ /* coverity[tainted_data : FALSE] */
+ if (copy_from_user(dataFileTrace, data.in.dataFileTrace,
+ data.in.dataFileTraceSize)) {
+ data.out.error = CM_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+
+ /* Do appropriate CM Engine call */
+ data.out.error = CM_ENGINE_BindComponent(data.in.client,
+ requiredItfClientName,
+ data.in.server,
+ providedItfServerName,
+ data.in.traced,
+ procPriv->pid,
+ dataFileTrace);
+out:
+ if (dataFileTrace != NULL)
+ OSAL_Free(dataFileTrace);
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_UnbindComponent(struct cm_process_priv* procPriv,
+ CM_UnbindComponent_t __user *param)
+{
+ CM_UnbindComponent_t data;
+ char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH];
+
+ /* Copy all user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(requiredItfClientName,
+ data.in.requiredItfClientName,
+ sizeof(requiredItfClientName))))
+ goto out;
+
+ /* Do appropriate CM Engine call */
+ data.out.error = CM_ENGINE_UnbindComponent(data.in.client,
+ requiredItfClientName,
+ procPriv->pid);
+
+out:
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_BindComponentToVoid(struct cm_process_priv* procPriv,
+ CM_BindComponentToVoid_t __user *param)
+{
+ CM_BindComponentToVoid_t data;
+ char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH];
+
+ /* Copy all user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(requiredItfClientName,
+ data.in.requiredItfClientName,
+ sizeof(requiredItfClientName))))
+ goto out;
+
+ data.out.error = CM_ENGINE_BindComponentToVoid(data.in.client,
+ requiredItfClientName,
+ procPriv->pid);
+
+out:
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_DestroyComponent(struct cm_process_priv* procPriv,
+ CM_DestroyComponent_t __user *param)
+{
+ CM_DestroyComponent_t data;
+
+ /* Copy all user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_DestroyComponent(data.in.component,
+ procPriv->pid);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_CreateMemoryDomain(struct cm_process_priv *procPriv,
+ CM_CreateMemoryDomain_t __user *param)
+{
+ CM_CreateMemoryDomain_t data;
+ t_cm_domain_memory domain;
+
+ /* Copy all user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if (copy_from_user(&domain, data.in.domain, sizeof(domain)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_CreateMemoryDomain(procPriv->pid,
+ &domain, &data.out.handle);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_CreateMemoryDomainScratch(struct cm_process_priv *procPriv,
+ CM_CreateMemoryDomainScratch_t __user *param)
+{
+ CM_CreateMemoryDomainScratch_t data;
+ t_cm_domain_memory domain;
+
+ /* Copy all user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if (copy_from_user(&domain, data.in.domain, sizeof(domain)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_CreateMemoryDomainScratch(procPriv->pid,
+ data.in.parentId,
+ &domain,
+ &data.out.handle);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_DestroyMemoryDomain(CM_DestroyMemoryDomain_t __user *param)
+{
+ CM_DestroyMemoryDomain_t data;
+
+ /* Copy all user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ /* coverity[tainted_data : FALSE] */
+ data.out.error = CM_ENGINE_DestroyMemoryDomain(data.in.domainId);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetDomainCoreId(CM_GetDomainCoreId_t __user *param)
+{
+ CM_GetDomainCoreId_t data;
+
+ /* Copy all user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ /* coverity[tainted_data : FALSE] */
+ data.out.error = CM_ENGINE_GetDomainCoreId(data.in.domainId,
+ &data.out.coreId);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_AllocMpcMemory(struct cm_process_priv *procPriv,
+ CM_AllocMpcMemory_t __user *param)
+{
+ t_cm_error err;
+ CM_AllocMpcMemory_t data;
+ t_cm_memory_handle handle = 0;
+ struct memAreaDesc_t* memAreaDesc;
+ t_cm_system_address systemAddress;
+ t_uint32 mpcAddress;
+
+ /* Copy all user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ /* Disregard alignment information and force 4kB memory alignment,
+ in any case (see devnotes.txt) */
+ /* PP: Disable this 'force' for now, because of the low amount of
+ available MPC Memory */
+ //data.in.memAlignment = CM_MM_MPC_ALIGN_1024WORDS;
+
+ /* coverity[tainted_data : FALSE] */
+ data.out.error = CM_ENGINE_AllocMpcMemory(data.in.domainId,
+ procPriv->pid,
+ data.in.memType,
+ data.in.size,
+ data.in.memAlignment,
+ &handle);
+
+ data.out.pHandle = handle;
+
+ if (data.out.error != CM_OK)
+ goto out;
+
+ /* Get memory area decriptors in advance
+ so to fill in list elements right now */
+ err = CM_ENGINE_GetMpcMemorySystemAddress(handle, &systemAddress);
+ if (err != CM_OK) {
+ pr_err("%s: failed CM_ENGINE_GetMpcMemorySystemAddress (%i)\n", __func__, err);
+ /* If we can't manage internally this allocated memory latter, it's
+ better to report the error now.
+ Free the handle to not let the driver in an inconsistent state */
+ CM_ENGINE_FreeMpcMemory(handle);
+ return -EFAULT;
+ }
+
+ /* Get MPC address in advance so to fill in list elements right now */
+ err = CM_ENGINE_GetMpcMemoryMpcAddress(handle, &mpcAddress);
+ if (err != CM_OK) {
+ pr_err("%s: failed CM_ENGINE_GetMpcMemoryMpcAddress (%i)\n", __func__, err);
+ /* see comments above */
+ CM_ENGINE_FreeMpcMemory(handle);
+ return -EFAULT;
+ }
+
+ /* Allocate and fill a new memory area descriptor. Add it to the list */
+ memAreaDesc = OSAL_Alloc(sizeof(struct memAreaDesc_t));
+ if (memAreaDesc == NULL) {
+ pr_err("%s: failed allocating memAreaDesc\n", __func__);
+ /* see comments above */
+ CM_ENGINE_FreeMpcMemory(handle);
+ return -ENOMEM;
+ }
+
+ memAreaDesc->procPriv = procPriv;
+ memAreaDesc->handle = handle;
+ memAreaDesc->tid = 0;
+ memAreaDesc->physAddr = systemAddress.physical;
+ memAreaDesc->kernelLogicalAddr = systemAddress.logical;
+ memAreaDesc->userLogicalAddr = 0;
+ memAreaDesc->mpcPhysAddr = mpcAddress;
+ memAreaDesc->size = data.in.size * ((data.in.memType % 2) ? 4 : 2); // betzw: set size in bytes for host (ugly version)
+ atomic_set(&memAreaDesc->count, 0);
+
+ if (lock_process(procPriv)) {
+ /* may be rather call lock_process_uninterruptible() */
+ CM_ENGINE_FreeMpcMemory(handle);
+ OSAL_Free(memAreaDesc);
+ return -ERESTARTSYS;
+ }
+ list_add(&memAreaDesc->list, &procPriv->memAreaDescList);
+ unlock_process(procPriv);
+out:
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_FreeMpcMemory(struct cm_process_priv *procPriv,
+ CM_FreeMpcMemory_t __user *param)
+{
+ CM_FreeMpcMemory_t data;
+ struct list_head *cursor, *next;
+
+ /* Copy user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ /* check that it is actually owned by the process */
+ data.out.error = CM_UNKNOWN_MEMORY_HANDLE;
+
+ if (lock_process(procPriv))
+ return -ERESTARTSYS;
+ list_for_each_safe(cursor, next, &procPriv->memAreaDescList){
+ struct memAreaDesc_t* curr;
+ curr = list_entry(cursor, struct memAreaDesc_t, list);
+ if (curr->handle == data.in.handle){
+ if (atomic_read(&curr->count) != 0) {
+ pr_err("%s: Memory area (phyAddr: %x, size: %d) "
+ "still in use (count=%d)!\n", __func__,
+ curr->physAddr, curr->size,
+ atomic_read(&curr->count));
+ data.out.error = CM_INVALID_PARAMETER;
+ } else {
+ data.out.error =
+ CM_ENGINE_FreeMpcMemory(data.in.handle);
+ if (data.out.error == CM_OK) {
+ list_del(cursor);
+ OSAL_Free(curr);
+ }
+ }
+ break;
+ }
+ }
+ unlock_process(procPriv);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetMpcMemoryStatus(CM_GetMpcMemoryStatus_t __user *param)
+{
+ CM_GetMpcMemoryStatus_t data;
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ /* coverity[tainted_data : FALSE] */
+ data.out.error = CM_ENGINE_GetMpcMemoryStatus(data.in.coreId,
+ data.in.memType,
+ &data.out.pStatus);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_StartComponent(struct cm_process_priv *procPriv,
+ CM_StartComponent_t __user *param)
+{
+ CM_StartComponent_t data;
+
+ /* Copy user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_StartComponent(data.in.client,
+ procPriv->pid);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_StopComponent(struct cm_process_priv *procPriv,
+ CM_StopComponent_t __user *param)
+{
+ CM_StopComponent_t data;
+
+ /* Copy user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_StopComponent(data.in.client,
+ procPriv->pid);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetMpcLoadCounter(CM_GetMpcLoadCounter_t __user *param)
+{
+ CM_GetMpcLoadCounter_t data;
+
+ /* Copy user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_getMpcLoadCounter(data.in.coreId,
+ &data.out.pMpcLoadCounter);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetComponentDescription(struct cm_process_priv *procPriv,
+ CM_GetComponentDescription_t __user *param)
+{
+ CM_GetComponentDescription_t data;
+ char templateName[MAX_TEMPLATE_NAME_LENGTH];
+ char localName[MAX_COMPONENT_NAME_LENGTH];
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_GetComponentDescription(data.in.component,
+ templateName,
+ data.in.templateNameLength,
+ &data.out.coreId,
+ localName,
+ data.in.localNameLength,
+ &data.out.priority);
+
+ /* Copy results back to userspace */
+ if (data.out.error == CM_OK) {
+ /* coverity[tainted_data : FALSE] */
+ if (copy_to_user(data.in.templateName, templateName, data.in.templateNameLength))
+ return -EFAULT;
+ /* coverity[tainted_data : FALSE] */
+ if (copy_to_user(data.in.localName, localName, data.in.localNameLength))
+ return -EFAULT;
+ }
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetComponentListHeader(struct cm_process_priv *procPriv,
+ CM_GetComponentListHeader_t __user *param)
+{
+ CM_GetComponentListHeader_t data;
+
+ data.out.error = CM_ENGINE_GetComponentListHeader(procPriv->pid,
+ &data.out.headerComponent);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetComponentListNext(struct cm_process_priv *procPriv,
+ CM_GetComponentListNext_t __user *param)
+{
+ CM_GetComponentListNext_t data;
+
+ /* Copy user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_GetComponentListNext(procPriv->pid,
+ data.in.prevComponent,
+ &data.out.nextComponent);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetComponentRequiredInterfaceNumber(struct cm_process_priv *procPriv,
+ CM_GetComponentRequiredInterfaceNumber_t __user *param)
+{
+ CM_GetComponentRequiredInterfaceNumber_t data;
+
+ /* Copy user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_GetComponentRequiredInterfaceNumber(data.in.component,
+ &data.out.numberRequiredInterfaces);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetComponentRequiredInterface(struct cm_process_priv *procPriv,
+ CM_GetComponentRequiredInterface_t __user *param)
+{
+ CM_GetComponentRequiredInterface_t data;
+ char itfName[MAX_INTERFACE_NAME_LENGTH];
+ char itfType[MAX_INTERFACE_TYPE_NAME_LENGTH];
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_GetComponentRequiredInterface(data.in.component,
+ data.in.index,
+ itfName,
+ data.in.itfNameLength,
+ itfType,
+ data.in.itfTypeLength,
+ &data.out.requireState,
+ &data.out.collectionSize);
+
+ /* Copy results back to userspace */
+ if (data.out.error == CM_OK) {
+ /* coverity[tainted_data : FALSE] */
+ if (copy_to_user(data.in.itfName, itfName, data.in.itfNameLength))
+ return -EFAULT;
+ /* coverity[tainted_data : FALSE] */
+ if (copy_to_user(data.in.itfType, itfType, data.in.itfTypeLength))
+ return -EFAULT;
+ }
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetComponentRequiredInterfaceBinding(struct cm_process_priv *procPriv,
+ CM_GetComponentRequiredInterfaceBinding_t __user *param)
+{
+ CM_GetComponentRequiredInterfaceBinding_t data;
+ char itfName[MAX_INTERFACE_NAME_LENGTH];
+ char serverItfName[MAX_INTERFACE_NAME_LENGTH];
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+ if ((data.out.error = copy_string_from_user(itfName,
+ data.in.itfName,
+ sizeof(itfName))))
+ goto out;
+
+ data.out.error = CM_ENGINE_GetComponentRequiredInterfaceBinding(data.in.component,
+ itfName,
+ &data.out.server,
+ serverItfName,
+ data.in.serverItfNameLength);
+
+ /* Copy results back to userspace */
+ if (data.out.error != CM_OK)
+ goto out;
+
+ /* coverity[tainted_data : FALSE] */
+ if (copy_to_user(data.in.serverItfName, serverItfName, data.in.serverItfNameLength))
+ return -EFAULT;
+out:
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetComponentProvidedInterfaceNumber(struct cm_process_priv *procPriv,
+ CM_GetComponentProvidedInterfaceNumber_t __user *param)
+{
+ CM_GetComponentProvidedInterfaceNumber_t data;
+
+ /* Copy user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_GetComponentProvidedInterfaceNumber(data.in.component,
+ &data.out.numberProvidedInterfaces);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetComponentProvidedInterface(struct cm_process_priv *procPriv,
+ CM_GetComponentProvidedInterface_t __user *param)
+{
+ CM_GetComponentProvidedInterface_t data;
+ char itfName[MAX_INTERFACE_NAME_LENGTH];
+ char itfType[MAX_INTERFACE_TYPE_NAME_LENGTH];
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_GetComponentProvidedInterface(data.in.component,
+ data.in.index,
+ itfName,
+ data.in.itfNameLength,
+ itfType,
+ data.in.itfTypeLength,
+ &data.out.collectionSize);
+
+ /* Copy results back to userspace */
+ if (data.out.error == CM_OK) {
+ /* coverity[tainted_data : FALSE] */
+ if (copy_to_user(data.in.itfName, itfName, data.in.itfNameLength))
+ return -EFAULT;
+ /* coverity[tainted_data : FALSE] */
+ if (copy_to_user(data.in.itfType, itfType, data.in.itfTypeLength))
+ return -EFAULT;
+ }
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetComponentPropertyNumber(struct cm_process_priv *procPriv,
+ CM_GetComponentPropertyNumber_t __user *param)
+{
+ CM_GetComponentPropertyNumber_t data;
+
+ /* Copy user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_GetComponentPropertyNumber(data.in.component,
+ &data.out.numberProperties);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetComponentPropertyName(struct cm_process_priv *procPriv,
+ CM_GetComponentPropertyName_t __user *param)
+{
+ CM_GetComponentPropertyName_t data;
+ char propertyName[MAX_PROPERTY_NAME_LENGTH];
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_GetComponentPropertyName(data.in.component,
+ data.in.index,
+ propertyName,
+ data.in.propertyNameLength);
+
+ /* Copy results back to userspace */
+ /* coverity[tainted_data : FALSE] */
+ if ((data.out.error == CM_OK) &&
+ copy_to_user(data.in.propertyName, propertyName, data.in.propertyNameLength))
+ return -EFAULT;
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetComponentPropertyValue(struct cm_process_priv *procPriv,
+ CM_GetComponentPropertyValue_t __user *param)
+{
+ CM_GetComponentPropertyValue_t data;
+ char propertyName[MAX_PROPERTY_NAME_LENGTH];
+ char propertyValue[MAX_PROPERTY_VALUE_LENGTH];
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(propertyName,
+ data.in.propertyName,
+ sizeof(propertyName))))
+ goto out;
+
+ data.out.error = CM_ENGINE_GetComponentPropertyValue(data.in.component,
+ propertyName,
+ propertyValue,
+ data.in.propertyValueLength);
+ /* Copy results back to userspace */
+ /* coverity[tainted_data : FALSE] */
+ if ((data.out.error == CM_OK) &&
+ copy_to_user(data.in.propertyValue, propertyValue, data.in.propertyValueLength))
+ return -EFAULT;
+out:
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_ReadComponentAttribute(struct cm_process_priv *procPriv,
+ CM_ReadComponentAttribute_t __user *param)
+{
+ CM_ReadComponentAttribute_t data;
+ char attrName[MAX_ATTRIBUTE_NAME_LENGTH];
+
+ /* Copy user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(attrName,
+ data.in.attrName,
+ sizeof(attrName))))
+ goto out;
+
+ data.out.error = CM_ENGINE_ReadComponentAttribute(data.in.component,
+ attrName,
+ &data.out.value);
+out:
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetExecutiveEngineHandle(struct cm_process_priv *procPriv,
+ CM_GetExecutiveEngineHandle_t __user *param)
+{
+ CM_GetExecutiveEngineHandle_t data;
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ /* coverity[tainted_data : FALSE] */
+ data.out.error = CM_ENGINE_GetExecutiveEngineHandle(data.in.domainId,
+ &data.out.executiveEngineHandle);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_SetMode(CM_SetMode_t __user *param)
+{
+ CM_SetMode_t data;
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ /* coverity[tainted_data : FALSE] */
+ data.out.error = CM_ENGINE_SetMode(data.in.aCmdID, data.in.aParam);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_GetRequiredComponentFiles(struct cm_process_priv *procPriv,
+ CM_GetRequiredComponentFiles_t __user *param)
+{
+ CM_GetRequiredComponentFiles_t data;
+ char components[4][MAX_INTERFACE_TYPE_NAME_LENGTH];
+ char requiredItfClientName[MAX_INTERFACE_NAME_LENGTH];
+ char providedItfServerName[MAX_INTERFACE_NAME_LENGTH];
+ char type[MAX_INTERFACE_TYPE_NAME_LENGTH];
+ unsigned int i;
+ int err;
+
+ /* Copy user input data in kernel space */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if (data.in.requiredItfClientName &&
+ (data.out.error = copy_string_from_user(requiredItfClientName,
+ data.in.requiredItfClientName,
+ sizeof(requiredItfClientName))))
+ goto out;
+
+ if (data.in.providedItfServerName &&
+ (data.out.error = copy_string_from_user(providedItfServerName,
+ data.in.providedItfServerName,
+ sizeof(providedItfServerName))))
+ goto out;
+
+ data.out.error = CM_ENGINE_GetRequiredComponentFiles(data.in.action,
+ data.in.client,
+ requiredItfClientName,
+ data.in.server,
+ providedItfServerName,
+ components,
+ data.in.listSize,
+ data.in.type ? type : NULL,
+ &data.out.methodNumber);
+
+ if (data.out.error)
+ goto out;
+
+ if (data.in.fileList) {
+ /* Copy results back to userspace */
+ for (i=0; i<data.in.listSize; i++) {
+ err = copy_to_user(&((char*)data.in.fileList)[i*MAX_INTERFACE_TYPE_NAME_LENGTH], components[i], MAX_INTERFACE_TYPE_NAME_LENGTH);
+ if (err)
+ return -EFAULT;
+ }
+ }
+ if (data.in.type
+ && copy_to_user(data.in.type, type, MAX_INTERFACE_TYPE_NAME_LENGTH))
+ return -EFAULT;
+out:
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_Migrate(CM_Migrate_t __user *param)
+{
+ CM_Migrate_t data;
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ data.out.error = CM_ENGINE_Migrate(data.in.srcShared, data.in.src, data.in.dst);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_Unmigrate(CM_Unmigrate_t __user *param)
+{
+ CM_Unmigrate_t data;
+
+ data.out.error = CM_ENGINE_Unmigrate();
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_PushComponent(CM_PushComponent_t __user *param)
+{
+ CM_PushComponent_t data;
+ char name[MAX_INTERFACE_TYPE_NAME_LENGTH];
+ void *dataFile = NULL;
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(name,
+ data.in.name,
+ sizeof(name))))
+ goto out;
+
+ if (data.in.data != NULL) {
+ dataFile = OSAL_Alloc(data.in.size);
+ if (dataFile == NULL) {
+ data.out.error = CM_NO_MORE_MEMORY;
+ goto out;
+ }
+ /* coverity[tainted_data : FALSE] */
+ if (copy_from_user(dataFile, data.in.data, data.in.size))
+ data.out.error = CM_INVALID_PARAMETER;
+ else
+ data.out.error = CM_ENGINE_PushComponent(name, dataFile,
+ data.in.size);
+ OSAL_Free(dataFile);
+ }
+
+out:
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_ReleaseComponent(CM_ReleaseComponent_t __user *param)
+{
+ CM_ReleaseComponent_t data;
+ char name[MAX_INTERFACE_TYPE_NAME_LENGTH];
+
+ /* Copy user input data in kernel space */
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if ((data.out.error = copy_string_from_user(name,
+ data.in.name,
+ sizeof(name))))
+ goto out;
+
+ /* coverity[tainted_data : FALSE] */
+ data.out.error = CM_ENGINE_ReleaseComponent(name);
+
+out:
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+inline int cmld_PrivGetMPCMemoryDesc(struct cm_process_priv *procPriv, CM_PrivGetMPCMemoryDesc_t __user *param)
+{
+ CM_PrivGetMPCMemoryDesc_t data;
+ struct list_head* cursor;
+
+ if (copy_from_user(&data.in, &param->in, sizeof(data.in)))
+ return -EFAULT;
+
+ if (lock_process(procPriv))
+ return -ERESTARTSYS;
+ /* Scan the memory descriptors list looking for the requested handle */
+ data.out.error = CM_UNKNOWN_MEMORY_HANDLE;
+ list_for_each(cursor, &procPriv->memAreaDescList) {
+ struct memAreaDesc_t* curr;
+ curr = list_entry(cursor, struct memAreaDesc_t, list);
+ if (curr->handle == data.in.handle) {
+ data.out.size = curr->size;
+ data.out.physAddr = curr->physAddr;
+ data.out.kernelLogicalAddr = curr->kernelLogicalAddr;
+ data.out.userLogicalAddr = curr->userLogicalAddr;
+ data.out.mpcPhysAddr = curr->mpcPhysAddr;
+ data.out.error = CM_OK;
+ break;
+ }
+ }
+ unlock_process(procPriv);
+
+ /* Copy results back to userspace */
+ if (copy_to_user(&param->out, &data.out, sizeof(data.out)))
+ return -EFAULT;
+ return 0;
+}
+
+int cmld_PrivReserveMemory(struct cm_process_priv *procPriv, unsigned int physAddr)
+{
+ struct list_head* cursor;
+ struct memAreaDesc_t* curr;
+ int err = -ENXIO;
+
+ if (lock_process(procPriv))
+ return -ERESTARTSYS;
+ list_for_each(cursor, &procPriv->memAreaDescList) {
+ curr = list_entry(cursor, struct memAreaDesc_t, list);
+ if (curr->physAddr == physAddr) {
+ /* Mark this memory area reserved for a mapping for this thread ID */
+ /* It must not be already reserved but this should not happen */
+ if (curr->tid) {
+ pr_err("%s: thread %d can't reseveved memory %x already "
+ "reserved for %d\n",
+ __func__, current->pid, physAddr, curr->tid);
+ err = -EBUSY;
+ } else {
+ curr->tid = current->pid;
+ err = 0;
+ }
+ break;
+ }
+ }
+ unlock_process(procPriv);
+ return err;
+}
diff --git a/drivers/staging/nmf-cm/cmioctl.h b/drivers/staging/nmf-cm/cmioctl.h
new file mode 100644
index 00000000000..6e431b5295f
--- /dev/null
+++ b/drivers/staging/nmf-cm/cmioctl.h
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+#ifndef __CMIOCTL_H
+#define __CMIOCTL_H
+
+#include <cm/engine/component/inc/component_type.h>
+#include <cm/engine/communication/inc/communication_type.h>
+#include <cm/engine/configuration/inc/configuration_type.h>
+#include <cm/engine/memory/inc/domain_type.h>
+#include <cm/engine/memory/inc/memory_type.h>
+#include <cm/engine/perfmeter/inc/perfmeter_type.h>
+#include <cm/engine/repository_mgt/inc/repository_type.h>
+
+#define CMLD_DEV_NAME \
+ { "cm_control", \
+ "cm_channel" \
+ }
+
+/*
+ * The following structures are used to exchange CM_SYSCALL parameters with
+ * the driver. There is one structure per ioctl command, ie per CM_SYSCAL.
+ * Each of them contains:
+ * - One set of fields placed in a struture 'in' which are all input
+ * parameters of the syscall (parameters that kernel side must retrieve
+ * from user space)
+ * - One set of fields placed in a struture 'out' which contains all output
+ * parameters of the syscall plus the error code.
+ *
+ * NOTE: all pointers to (user) buffer are always placed in struct 'in', including
+ * buffers used as output parameters; because the pointer itself is considered as
+ * an input parameter, as it is directly accessed from kernel space.
+ */
+typedef struct{
+ struct {
+ const char * templateName;
+ t_cm_domain_id domainId;
+ t_nmf_ee_priority priority;
+ const char * localName;
+ const char *dataFile;
+ t_uint32 dataFileSize;
+ } in;
+ struct {
+ t_cm_instance_handle component; /** < Output parameter */
+ t_cm_error error;
+ } out;
+} CM_InstantiateComponent_t;
+
+typedef struct {
+ struct {
+ t_cm_bf_host2mpc_handle host2mpcId;
+ t_event_params_handle h;
+ t_uint32 size;
+ t_uint32 methodIndex;
+ } in;
+ struct {
+ t_cm_error error;
+ } out;
+} CM_PushEventWithSize_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle server;
+ const char * providedItfServerName;
+ t_uint32 fifosize;
+ t_cm_mpc_memory_type eventMemType;
+ const char *dataFileSkeleton;
+ t_uint32 dataFileSkeletonSize;
+ } in;
+ struct {
+ t_cm_bf_host2mpc_handle host2mpcId; /** < Output parameter */
+ t_cm_error error;
+ } out;
+} CM_BindComponentFromCMCore_t;
+
+typedef struct {
+ struct {
+ t_cm_bf_host2mpc_handle host2mpcId;
+ } in;
+ struct {
+ t_cm_error error;
+ } out;
+} CM_UnbindComponentFromCMCore_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle client;
+ const char *requiredItfClientName;
+ t_uint32 fifosize;
+ t_nmf_mpc2host_handle upLayerThis;
+ const char *dataFileStub;
+ t_uint32 dataFileStubSize;
+ } in;
+ struct {
+ t_cm_bf_mpc2host_handle mpc2hostId; /** < Output parameter */
+ t_cm_error error;
+ } out;
+} CM_BindComponentToCMCore_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle client;
+ const char *requiredItfClientName;
+ } in;
+ struct {
+ t_nmf_mpc2host_handle upLayerThis; /** < Output parameter */
+ t_cm_error error;
+ } out;
+} CM_UnbindComponentToCMCore_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle component;
+ } in;
+ struct {
+ t_cm_error error; /** < Output parameter */
+ } out; /** < Output parameter */
+} CM_DestroyComponent_t;
+
+typedef struct {
+ struct {
+ const t_cm_domain_memory *domain;
+ } in;
+ struct {
+ t_cm_domain_id handle; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out; /** < Out parameter */
+} CM_CreateMemoryDomain_t;
+
+typedef struct {
+ struct {
+ t_cm_domain_id parentId;
+ const t_cm_domain_memory *domain;
+ } in;
+ struct {
+ t_cm_domain_id handle; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out; /** < Out parameter */
+} CM_CreateMemoryDomainScratch_t;
+
+typedef struct {
+ struct {
+ t_cm_domain_id domainId;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out; /** < Out parameter */
+} CM_DestroyMemoryDomain_t;
+
+typedef struct {
+ struct {
+ t_cm_domain_id domainId; /** < In parameter */
+ } in;
+ struct {
+ t_nmf_core_id coreId; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out; /** < Out parameter */
+} CM_GetDomainCoreId_t;
+
+typedef struct {
+ struct {
+ t_cm_domain_id domainId;
+ t_cm_mpc_memory_type memType;
+ t_cm_size size;
+ t_cm_memory_alignment memAlignment;
+ } in;
+ struct {
+ t_cm_memory_handle pHandle; /** < Output parameter */
+ t_cm_error error;
+ } out; /** < Output parameter */
+} CM_AllocMpcMemory_t;
+
+typedef struct{
+ struct {
+ t_cm_memory_handle handle;
+ } in;
+ struct {
+ t_cm_error error;
+ } out; /** < Output parameter */
+} CM_FreeMpcMemory_t;
+
+typedef struct {
+ struct {
+ t_cm_memory_handle handle;
+ } in;
+ struct {
+ t_uint32 size; /** < Out parameter */
+ t_uint32 physAddr; /** < Out parameter */
+ t_uint32 kernelLogicalAddr; /** < Out parameter */
+ t_uint32 userLogicalAddr; /** < Out parameter */
+ t_uint32 mpcPhysAddr; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out; /** < Output parameter */
+} CM_PrivGetMPCMemoryDesc_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle client;
+ const char *requiredItfClientName;
+ t_cm_instance_handle server;
+ const char *providedItfServerName;
+ t_uint32 fifosize;
+ t_cm_mpc_memory_type eventMemType;
+ const char *dataFileSkeletonOrEvent;
+ t_uint32 dataFileSkeletonOrEventSize;
+ const char *dataFileStub;
+ t_uint32 dataFileStubSize;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_BindComponentAsynchronous_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle client;
+ const char* requiredItfClientName;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_UnbindComponentAsynchronous_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle client;
+ const char *requiredItfClientName;
+ t_cm_instance_handle server;
+ const char *providedItfServerName;
+ t_bool traced;
+ const char *dataFileTrace;
+ t_uint32 dataFileTraceSize;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_BindComponent_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle client;
+ const char* requiredItfClientName;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_UnbindComponent_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle client;
+ const char* requiredItfClientName;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_BindComponentToVoid_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle client;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_StartComponent_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle client;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_StopComponent_t;
+
+typedef struct {
+ struct {
+ t_nmf_core_id coreId;
+ } in;
+ struct {
+ t_cm_mpc_load_counter pMpcLoadCounter; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetMpcLoadCounter_t;
+
+typedef struct {
+ struct {
+ t_nmf_core_id coreId;
+ t_cm_mpc_memory_type memType;
+ } in;
+ struct {
+ t_cm_allocator_status pStatus; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out; /** < Out parameter */
+} CM_GetMpcMemoryStatus_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle component;
+ t_uint32 templateNameLength;
+ t_uint32 localNameLength;
+ char *templateName; /** < Out parameter */
+ char *localName; /** < Out parameter */
+ } in;
+ struct {
+ t_nmf_core_id coreId; /** < Out parameter */
+ t_nmf_ee_priority priority; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out; /** < Out parameter */
+} CM_GetComponentDescription_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle headerComponent; /** < Output parameter */
+ t_cm_error error; /** < Out parameter */
+ } out; /** < Out parameter */
+} CM_GetComponentListHeader_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle prevComponent;
+ } in;
+ struct {
+ t_cm_instance_handle nextComponent; /** < Output parameter */
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetComponentListNext_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle component;
+ } in;
+ struct {
+ t_uint8 numberRequiredInterfaces; /** < Output parameter */
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetComponentRequiredInterfaceNumber_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle component;
+ t_uint8 index;
+ t_uint32 itfNameLength;
+ t_uint32 itfTypeLength;
+ char *itfName; /** < Out parameter */
+ char *itfType; /** < Out parameter */
+ } in;
+ struct {
+ t_cm_require_state requireState; /** < Out parameter */
+ t_sint16 collectionSize; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetComponentRequiredInterface_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle component;
+ const char *itfName;
+ t_uint32 serverItfNameLength;
+ char *serverItfName; /** < Out parameter */
+ } in;
+ struct {
+ t_cm_instance_handle server; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetComponentRequiredInterfaceBinding_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle component;
+ } in;
+ struct {
+ t_uint8 numberProvidedInterfaces; /** < Output parameter */
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetComponentProvidedInterfaceNumber_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle component;
+ t_uint8 index;
+ t_uint32 itfNameLength;
+ t_uint32 itfTypeLength;
+ char *itfName; /** < Out parameter */
+ char *itfType; /** < Out parameter */
+ } in;
+ struct {
+ t_sint16 collectionSize; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetComponentProvidedInterface_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle component;
+ } in;
+ struct {
+ t_uint8 numberProperties; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetComponentPropertyNumber_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle component;
+ const char *attrName;
+ t_uint8 index;
+ t_uint32 propertyNameLength;
+ char *propertyName; /** < Out parameter */
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetComponentPropertyName_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle component;
+ const char *propertyName;
+ t_uint32 propertyValueLength;
+ char *propertyValue; /** < Out parameter */
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetComponentPropertyValue_t;
+
+typedef struct {
+ struct {
+ t_cm_instance_handle component;
+ const char *attrName;
+ } in;
+ struct {
+ t_uint32 value; /** < Out parameter */
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_ReadComponentAttribute_t;
+
+typedef struct {
+ struct {
+ t_cm_domain_id domainId;
+ } in;
+ struct {
+ t_cm_instance_handle executiveEngineHandle;
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetExecutiveEngineHandle_t;
+
+typedef struct {
+ struct {
+ t_cm_cmd_id aCmdID;
+ t_sint32 aParam;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_SetMode_t;
+
+typedef struct {
+ struct {
+ t_action_to_do action;
+ t_cm_instance_handle client;
+ const char *requiredItfClientName;
+ t_cm_instance_handle server;
+ const char *providedItfServerName;
+ char **fileList;
+ unsigned int listSize;
+ char *type;
+ } in;
+ struct {
+ t_uint32 methodNumber; /** < Output parameter */
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_GetRequiredComponentFiles_t;
+
+typedef struct {
+ struct {
+ const char *name;
+ const void *data;
+ t_cm_size size;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_PushComponent_t;
+
+typedef struct {
+ struct {
+ const char *name;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_ReleaseComponent_t;
+
+typedef struct {
+ struct {
+ t_cm_domain_id srcShared;
+ t_cm_domain_id src;
+ t_cm_domain_id dst;
+ } in;
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_Migrate_t;
+
+typedef struct {
+ struct {
+ t_cm_error error; /** < Out parameter */
+ } out;
+} CM_Unmigrate_t;
+
+#define CM_PUSHEVENTWITHSIZE _IOWR('c', 0, CM_PushEventWithSize_t)
+#define CM_GETVERSION _IOR('c', 1, t_uint32)
+#define CM_INSTANTIATECOMPONENT _IOWR('c', 2, CM_InstantiateComponent_t)
+#define CM_BINDCOMPONENTFROMCMCORE _IOWR('c', 3, CM_BindComponentFromCMCore_t)
+#define CM_UNBINDCOMPONENTFROMCMCORE _IOWR('c', 4, CM_UnbindComponentFromCMCore_t)
+#define CM_BINDCOMPONENTTOCMCORE _IOWR('c', 5, CM_BindComponentToCMCore_t)
+#define CM_UNBINDCOMPONENTTOCMCORE _IOWR('c', 6, CM_UnbindComponentToCMCore_t)
+#define CM_DESTROYCOMPONENT _IOWR('c', 7, CM_DestroyComponent_t)
+#define CM_CREATEMEMORYDOMAIN _IOWR('c', 8, CM_CreateMemoryDomain_t)
+#define CM_CREATEMEMORYDOMAINSCRATCH _IOWR('c', 9, CM_CreateMemoryDomainScratch_t)
+#define CM_DESTROYMEMORYDOMAIN _IOWR('c', 10, CM_DestroyMemoryDomain_t)
+#define CM_GETDOMAINCOREID _IOWR('c', 11, CM_GetDomainCoreId_t)
+#define CM_ALLOCMPCMEMORY _IOWR('c', 12, CM_AllocMpcMemory_t)
+#define CM_FREEMPCMEMORY _IOWR('c', 13, CM_FreeMpcMemory_t)
+#define CM_BINDCOMPONENTASYNCHRONOUS _IOWR('c', 14, CM_BindComponentAsynchronous_t)
+#define CM_UNBINDCOMPONENTASYNCHRONOUS _IOWR('c', 15, CM_UnbindComponentAsynchronous_t)
+#define CM_BINDCOMPONENT _IOWR('c', 16, CM_BindComponent_t)
+#define CM_UNBINDCOMPONENT _IOWR('c', 17, CM_UnbindComponent_t)
+#define CM_BINDCOMPONENTTOVOID _IOWR('c', 18, CM_BindComponentToVoid_t)
+#define CM_STARTCOMPONENT _IOWR('c', 19, CM_StartComponent_t)
+#define CM_STOPCOMPONENT _IOWR('c', 20, CM_StopComponent_t)
+#define CM_GETMPCLOADCOUNTER _IOWR('c', 21, CM_GetMpcLoadCounter_t)
+#define CM_GETMPCMEMORYSTATUS _IOWR('c', 22, CM_GetMpcMemoryStatus_t)
+#define CM_GETCOMPONENTDESCRIPTION _IOWR('c', 23, CM_GetComponentDescription_t)
+#define CM_GETCOMPONENTLISTHEADER _IOWR('c', 24, CM_GetComponentListHeader_t)
+#define CM_GETCOMPONENTLISTNEXT _IOWR('c', 25, CM_GetComponentListNext_t)
+#define CM_GETCOMPONENTREQUIREDINTERFACENUMBER _IOWR('c', 26, CM_GetComponentRequiredInterfaceNumber_t)
+#define CM_GETCOMPONENTREQUIREDINTERFACE _IOWR('c', 27, CM_GetComponentRequiredInterface_t)
+#define CM_GETCOMPONENTREQUIREDINTERFACEBINDING _IOWR('c', 28, CM_GetComponentRequiredInterfaceBinding_t)
+#define CM_GETCOMPONENTPROVIDEDINTERFACENUMBER _IOWR('c', 29, CM_GetComponentProvidedInterfaceNumber_t)
+#define CM_GETCOMPONENTPROVIDEDINTERFACE _IOWR('c', 30, CM_GetComponentProvidedInterface_t)
+#define CM_GETCOMPONENTPROPERTYNUMBER _IOWR('c', 31, CM_GetComponentPropertyNumber_t)
+#define CM_GETCOMPONENTPROPERTYNAME _IOWR('c', 32, CM_GetComponentPropertyName_t)
+#define CM_GETCOMPONENTPROPERTYVALUE _IOWR('c', 33, CM_GetComponentPropertyValue_t)
+#define CM_READCOMPONENTATTRIBUTE _IOWR('c', 34, CM_ReadComponentAttribute_t)
+#define CM_GETEXECUTIVEENGINEHANDLE _IOWR('c', 35, CM_GetExecutiveEngineHandle_t)
+#define CM_SETMODE _IOWR('c', 36, CM_SetMode_t)
+#define CM_GETREQUIREDCOMPONENTFILES _IOWR('c', 37, CM_GetRequiredComponentFiles_t)
+#define CM_PUSHCOMPONENT _IOWR('c', 38, CM_PushComponent_t)
+#define CM_FLUSHCHANNEL _IO('c', 39)
+#define CM_MIGRATE _IOWR('c', 40, CM_Migrate_t)
+#define CM_UNMIGRATE _IOR('c', 41, CM_Unmigrate_t)
+#define CM_RELEASECOMPONENT _IOWR('c', 42, CM_ReleaseComponent_t)
+
+#define CM_PRIVGETMPCMEMORYDESC _IOWR('c', 100, CM_PrivGetMPCMemoryDesc_t)
+#define CM_PRIVRESERVEMEMORY _IOW('c', 101, unsigned int)
+#define CM_PRIV_GETBOARDVERSION _IOR('c', 102, unsigned int)
+
+enum board_version {
+ U8500_V1,
+ U8500_V2
+};
+#endif
diff --git a/drivers/staging/nmf-cm/cmld.c b/drivers/staging/nmf-cm/cmld.c
new file mode 100644
index 00000000000..7ef43dffb2e
--- /dev/null
+++ b/drivers/staging/nmf-cm/cmld.c
@@ -0,0 +1,1328 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+/** \file cmld.c
+ *
+ * Nomadik Multiprocessing Framework Linux Driver
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <cm/inc/cm_def.h>
+#include <cm/engine/api/cm_engine.h>
+#include <cm/engine/api/control/irq_engine.h>
+
+#include "cmioctl.h"
+#include "osal-kernel.h"
+#include "cmld.h"
+#include "cm_service.h"
+
+#define CMDRIVER_PATCH_VERSION 103
+#define O_FLUSH 0x1000000
+
+static int cmld_major;
+static struct cdev cmld_cdev;
+static struct class cmld_class = {
+ .name = "cm",
+ .owner = THIS_MODULE,
+};
+const char *cmld_devname[] = CMLD_DEV_NAME;
+static struct device *cmld_dev[ARRAY_SIZE(cmld_devname)];
+
+/* List of per process structure (struct cm_process_priv list) */
+LIST_HEAD(process_list);
+static DEFINE_MUTEX(process_lock); /* lock used to protect previous list */
+/* List of per channel structure (struct cm_channel_priv list).
+ A channel == One file descriptor */
+LIST_HEAD(channel_list);
+static DEFINE_MUTEX(channel_lock); /* lock used to protect previous list */
+
+/* Variables used to manage temporary allocation of ESRAM
+ reserved for DMA and B2R2 + MCDE */
+/* As of now, the reservation is done in RME */
+static int cfgESRAM_ReserveMCDE = 0;
+static int cfgESRAM_ReserveDMA = 0;
+static int cfgESRAMDmaSize = 4; /* in Kb */
+static int cfgESRAMMcdeSize = 128; /* in Kb */
+module_param(cfgESRAM_ReserveMCDE, bool, S_IRUGO);
+module_param(cfgESRAM_ReserveDMA, bool, S_IRUGO);
+module_param(cfgESRAMDmaSize, uint, S_IRUGO);
+module_param(cfgESRAMMcdeSize, uint, S_IRUGO);
+static t_cm_domain_id dmaDomainId, mcdeDomainId;
+static t_cm_memory_handle dmaMemoryHdl, mcdeMemoryHdl;
+
+static inline struct cm_process_priv *getProcessPriv(void)
+{
+ struct list_head* head;
+ struct cm_process_priv *entry;
+
+ mutex_lock(&process_lock);
+
+ /* Look for an entry for the calling process */
+ list_for_each(head, &process_list) {
+ entry = list_entry(head, struct cm_process_priv, entry);
+ if (entry->pid == current->tgid) {
+ kref_get(&entry->ref);
+ goto out;
+ }
+ }
+ mutex_unlock(&process_lock);
+
+ /* Allocate, init and register a new one otherwise */
+ entry = OSAL_Alloc(sizeof(*entry));
+ if (entry == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ /* init host2mpcLock */
+ mutex_init(&entry->host2mpcLock);
+
+ INIT_LIST_HEAD(&entry->memAreaDescList);
+ kref_init(&entry->ref);
+ mutex_init(&entry->mutex);
+
+ entry->pid = current->tgid;
+ mutex_lock(&process_lock);
+ list_add(&entry->entry, &process_list);
+out:
+ mutex_unlock(&process_lock);
+ return entry;
+}
+
+/* Free all messages */
+static inline void freeMessages(struct cm_channel_priv* channelPriv)
+{
+ struct osal_msg *this, *next;
+ int warn = 0;
+
+ spin_lock_bh(&channelPriv->bh_lock);
+ plist_for_each_entry_safe(this, next, &channelPriv->messageQueue, msg_entry) {
+ plist_del(&this->msg_entry, &channelPriv->messageQueue);
+ kfree(this);
+ warn = 1;
+ }
+ spin_unlock_bh(&channelPriv->bh_lock);
+ if (warn) {
+ pr_err("[CM - PID=%d]: Some remaining"
+ " message(s) freed\n", current->tgid);
+ warn = 0;
+ }
+}
+
+/* Free all pending memory areas and relative descriptors */
+static inline void freeMemHandles(struct cm_process_priv* processPriv)
+{
+ struct list_head* head, *next;
+ int warn = 0;
+
+ list_for_each_safe(head, next, &processPriv->memAreaDescList) {
+ struct memAreaDesc_t* curr;
+ int err;
+ curr = list_entry(head, struct memAreaDesc_t, list);
+ err=CM_ENGINE_FreeMpcMemory(curr->handle);
+ if (err)
+ pr_err("[CM - PID=%d]: Error (%d) freeing remaining memory area "
+ "handle\n", current->tgid, err);
+ list_del(head);
+ OSAL_Free(curr);
+ warn = 1;
+ }
+ if (warn) {
+ pr_err("[CM - PID=%d]: Some remaining memory area "
+ "handle(s) freed\n", current->tgid);
+ warn = 0;
+ }
+}
+
+/* Free any skeleton, called when freeing the process entry */
+static inline void freeSkelList(struct list_head* skelList)
+{
+ struct list_head* head, *next;
+ int warn = 0;
+
+ /* No lock held, we know that we are the only and the last user
+ of the list */
+ list_for_each_safe(head, next, skelList) {
+ t_skelwrapper* curr;
+ curr = list_entry(head, t_skelwrapper, entry);
+ list_del(head);
+ OSAL_Free(curr);
+ warn = 1;
+ }
+ if (warn)
+ pr_err("[CM - PID=%d]: Some remaining skeleton "
+ "wrapper(s) freed\n", current->tgid);
+}
+
+/* Free any remaining channels belonging to this process */
+/* Called _only_ when freeing the process entry, once the network constructed by
+ this process has been destroyed.
+ See cmld_release() to see why there can be some remaining non-freed channels */
+static inline void freeChannels(struct cm_process_priv* processPriv)
+{
+ struct list_head* head, *next;
+ int warn = 0;
+
+ list_for_each_safe(head, next, &channel_list) {
+ struct cm_channel_priv *channelPriv;
+ channelPriv = list_entry(head, struct cm_channel_priv, entry);
+ /* Only channels belonging to this process are concerned */
+ if (channelPriv->proc == processPriv) {
+ tasklet_disable(&cmld_service_tasklet);
+ mutex_lock(&channel_lock);
+ list_del(&channelPriv->entry);
+ mutex_unlock(&channel_lock);
+ tasklet_enable(&cmld_service_tasklet);
+
+ /* Free all remaining messages if any
+ (normally none, but double check) */
+ freeMessages(channelPriv);
+
+ /* Free any pending skeleton wrapper */
+ /* Here it's safe, we know that all bindings have been undone */
+ freeSkelList(&channelPriv->skelList);
+
+ /* Free the per-channel descriptor */
+ OSAL_Free(channelPriv);
+ }
+ warn = 1;
+ }
+ if (warn)
+ pr_err("[CM - PID=%d]: Some remaining channel entries "
+ "freed\n", current->tgid);
+}
+
+/* Free the process priv structure and all related stuff */
+/* Called only when the last ref to this structure is released */
+static void freeProcessPriv(struct kref *ref)
+{
+ struct cm_process_priv *entry = container_of(ref, struct cm_process_priv, ref);
+ t_nmf_error err;
+
+ mutex_lock(&process_lock);
+ list_del(&entry->entry);
+ mutex_unlock(&process_lock);
+
+ /* Destroy all remaining components */
+ err=CM_ENGINE_FlushComponents(entry->pid);
+ if (err != NMF_OK)
+ pr_err("[CM - PID=%d]: Error while flushing some remaining"
+ " components: error=%d\n", current->tgid, err);
+
+ freeChannels(entry);
+
+ /* Free any pending memory areas and relative descriptors */
+ freeMemHandles(entry);
+
+ /* Destroy all remaining domains */
+ err=CM_ENGINE_FlushMemoryDomains(entry->pid);
+ if (err != NMF_OK)
+ pr_err("[CM - PID=%d]: Error while flushing some remaining"
+ " domains: error=%d\n", current->tgid, err);
+
+ /* Free the per-process descriptor */
+ OSAL_Free(entry);
+}
+
+/** Driver's open method
+ * Allocates per-process resources: private data, wait queue,
+ * memory area descriptors linked list, message queue.
+ *
+ * \return POSIX error code
+ */
+static int cmld_open(struct inode *inode, struct file *file)
+{
+ struct cm_process_priv *procPriv = getProcessPriv();
+
+ if (IS_ERR(procPriv))
+ return PTR_ERR(procPriv);
+
+ if (iminor(inode) == 0)
+ file->private_data = procPriv;
+ else {
+ struct cm_channel_priv *channelPriv = (struct cm_channel_priv*)OSAL_Alloc(sizeof(*channelPriv));
+ if (channelPriv == NULL) {
+ kref_put(&procPriv->ref, freeProcessPriv);
+ return -ENOMEM;
+ }
+
+ channelPriv->proc = procPriv;
+ channelPriv->state = CHANNEL_OPEN;
+
+ /* Initialize wait_queue, lists and mutexes */
+ init_waitqueue_head(&channelPriv->waitq);
+ plist_head_init(&channelPriv->messageQueue, &channelPriv->bh_lock);
+ INIT_LIST_HEAD(&channelPriv->skelList);
+ spin_lock_init(&channelPriv->bh_lock);
+ mutex_init(&channelPriv->msgQueueLock);
+ mutex_init(&channelPriv->skelListLock);
+
+ tasklet_disable(&cmld_service_tasklet);
+ mutex_lock(&channel_lock);
+ list_add(&channelPriv->entry, &channel_list);
+ mutex_unlock(&channel_lock);
+ tasklet_enable(&cmld_service_tasklet);
+
+ file->private_data = channelPriv; // store channel private struct in file descriptor
+ }
+ return 0;
+}
+
+/** Driver's release method.
+ * Frees any per-process pending resource: components, bindings, memory areas.
+ *
+ * \return POSIX error code
+ */
+static int cmld_release(struct inode *inode, struct file *file)
+{
+ struct cm_process_priv* procPriv;
+
+ BUG_ON(file->private_data == NULL);
+
+ /* The driver must guarantee that all related resources are released.
+ Thus all these checks below are necessary to release all remaining
+ resources still linked to this 'client', in case of abnormal process
+ exit.
+ => These are error cases !
+ In the usual case, nothing should be done except the free of
+ the cmPriv itself
+ */
+
+ if (iminor(inode) != 0) {
+ struct cm_channel_priv* channelPriv;
+ channelPriv = file->private_data;
+ procPriv = channelPriv->proc;
+
+ /* We don't need to synchronize here by using the skelListLock:
+ the list is only accessed during ioctl() and we can't be here
+ if an ioctl() is on-going */
+ if (list_empty(&channelPriv->skelList)) {
+ /* There is no pending MPC->HOST binding
+ => we can quietly delete the channel */
+ tasklet_disable(&cmld_service_tasklet);
+ mutex_lock(&channel_lock);
+ list_del(&channelPriv->entry);
+ mutex_unlock(&channel_lock);
+ tasklet_enable(&cmld_service_tasklet);
+
+ /* Free all remaining messages if any */
+ freeMessages(channelPriv);
+
+ /* Free the per-channel descriptor */
+ OSAL_Free(channelPriv);
+ } else {
+ /* Uh: there are still some MPC->HOST binding but we don't have the
+ required info to unbind them.
+ => we must keep all skel structures because possibly used in OSAL_PostDfc
+ (incoming callback msg) */
+ /* We flag the channel as closed to discard any new msg that will never be read anyway */
+ channelPriv->state = CHANNEL_CLOSED;
+
+ /* Already Free all remaining messages if any,
+ they will never be read anyway */
+ freeMessages(channelPriv);
+ }
+ } else
+ procPriv = file->private_data;
+
+ kref_put(&procPriv->ref, freeProcessPriv);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+/** Reads Component Manager messages destinated to this process.
+ * The message is composed by three fields:
+ * 1) mpc2host handle (distinguishes interfaces)
+ * 2) methodIndex (distinguishes interface's methods)
+ * 3) Variable length parameters (method's parameters values)
+ *
+ * \note cfr GetEvent()
+ * \return POSIX error code
+ */
+static ssize_t cmld_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ int err = 0;
+ struct cm_channel_priv* channelPriv = (struct cm_channel_priv*)(file->private_data);
+ int msgSize = 0;
+ struct plist_head* messageQueue;
+ struct osal_msg* msg;
+ t_os_message *os_msg = (t_os_message *)buf;
+ int block = !(file->f_flags & O_NONBLOCK);
+
+ if (iminor(file->f_dentry->d_inode) == 0)
+ return -ENOSYS;
+
+ BUG_ON(channelPriv == NULL);
+ messageQueue = &channelPriv->messageQueue;
+
+ if (mutex_lock_killable(&channelPriv->msgQueueLock))
+ return -ERESTARTSYS;
+wait:
+ while (plist_head_empty(messageQueue)) {
+ mutex_unlock(&channelPriv->msgQueueLock);
+ if (block == 0)
+ return -EAGAIN;
+ /* Wait until there is a message to ferry up */
+ if (wait_event_interruptible(channelPriv->waitq, ((!plist_head_empty(messageQueue)) || (file->f_flags & O_FLUSH))))
+ return -ERESTARTSYS;
+ if (file->f_flags & O_FLUSH) {
+ file->f_flags &= ~O_FLUSH;
+ return 0;
+ }
+ if (mutex_lock_killable(&channelPriv->msgQueueLock))
+ return -ERESTARTSYS;
+ }
+
+ /* Pick up the first message from the queue, making sure that the
+ * hwsem tasklet does not wreak havoc the queue in the meantime
+ */
+ spin_lock_bh(&channelPriv->bh_lock);
+ msg = plist_first_entry(messageQueue, struct osal_msg, msg_entry);
+ plist_del(&msg->msg_entry, messageQueue);
+ spin_unlock_bh(&channelPriv->bh_lock);
+
+ switch (msg->msg_type) {
+ case MSG_INTERFACE: {
+
+ /* Check if enough space is available */
+ msgSize = sizeof(msg->msg_type) + msg->d.itf.ptrSize + sizeof(os_msg->data.itf) - sizeof(os_msg->data.itf.params) ;
+ if (msgSize > count) {
+ mutex_unlock(&channelPriv->msgQueueLock);
+ pr_err("CM: message size bigger than buffer size silently ignored!\n");
+ err = -EMSGSIZE;
+ goto out;
+ }
+
+ /* Copy to user message type */
+ err = put_user(msg->msg_type, &os_msg->type);
+ if (err) goto ack_evt;
+
+ /* Copy to user the t_nmf_mpc2host_handle */
+ err = put_user(msg->d.itf.skelwrap->upperLayerThis, &os_msg->data.itf.THIS);
+ if (err) goto ack_evt;
+
+ /* The methodIndex */
+ err = put_user(msg->d.itf.methodIdx, &os_msg->data.itf.methodIndex);
+ if (err) goto ack_evt;
+
+ /* And the parameters */
+ err = copy_to_user(os_msg->data.itf.params, msg->d.itf.anyPtr, msg->d.itf.ptrSize);
+
+ ack_evt:
+ /* This call is void */
+ /* Note: that we cannot release the lock before having called this function
+ as acknowledgements MUST be executed in the same order as their
+ respective messages have arrived! */
+ CM_ENGINE_AcknowledgeEvent(msg->d.itf.skelwrap->mpc2hostId);
+
+ mutex_unlock(&channelPriv->msgQueueLock);
+ break;
+ }
+ case MSG_SERVICE: {
+ mutex_unlock(&channelPriv->msgQueueLock);
+ msgSize = sizeof(msg->msg_type) + sizeof(msg->d.srv.srvType)
+ + sizeof(msg->d.srv.srvData);
+ if (count < msgSize) {
+ pr_err("CM: service message size bigger than buffer size - silently ignored!\n");
+ err = -EMSGSIZE;
+ }
+
+ /* Copy to user message type */
+ err = put_user(msg->msg_type, &os_msg->type);
+ if (err) goto out;
+ err = copy_to_user(&os_msg->data.srv, &msg->d.srv,
+ sizeof(msg->d.srv.srvType) + sizeof(msg->d.srv.srvData));
+ break;
+ }
+ default:
+ mutex_unlock(&channelPriv->msgQueueLock);
+ pr_err("CM: invalid message type %d discarded\n", msg->msg_type);
+ goto wait;
+ }
+out:
+ /* Destroy the message */
+ kfree(msg);
+
+ return err ? err : msgSize;
+}
+
+/** Part of driver's release method. (ie userspace close())
+ * It wakes up all waiter.
+ *
+ * \return POSIX error code
+ */
+static int cmld_flush(struct file *file, fl_owner_t id)
+{
+ if (iminor(file->f_dentry->d_inode) != 0) {
+ struct cm_channel_priv* channelPriv = (struct cm_channel_priv*)(file->private_data);
+ //channelPriv->closed = CHANNEL_CLOSED;
+ file->f_flags |= O_FLUSH;
+ wake_up_all(&channelPriv->waitq);
+ }
+ return 0;
+}
+
+static long cmld_channel_ctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct cm_channel_priv *channelPriv = file->private_data;
+
+ switch(cmd) {
+ /*
+ * All channel CM SYSCALL
+ */
+ case CM_BINDCOMPONENTTOCMCORE:
+ return cmld_BindComponentToCMCore(channelPriv, (CM_BindComponentToCMCore_t *)arg);
+ case CM_FLUSHCHANNEL:
+ return cmld_flush(file, 0);
+ default:
+ pr_err("CM(%s): unsupported command %i\n", __func__, cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static long cmld_control_ctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct cm_process_priv* procPriv = file->private_data;
+ switch(cmd) {
+ /*
+ * All wrapped CM SYSCALL
+ */
+ case CM_INSTANTIATECOMPONENT:
+ return cmld_InstantiateComponent(procPriv,
+ (CM_InstantiateComponent_t *)arg);
+
+ case CM_BINDCOMPONENTFROMCMCORE:
+ return cmld_BindComponentFromCMCore(procPriv,
+ (CM_BindComponentFromCMCore_t *)arg);
+
+ case CM_UNBINDCOMPONENTFROMCMCORE:
+ return cmld_UnbindComponentFromCMCore((CM_UnbindComponentFromCMCore_t *)arg);
+
+ case CM_UNBINDCOMPONENTTOCMCORE:
+ return cmld_UnbindComponentToCMCore(procPriv, (CM_UnbindComponentToCMCore_t *)arg);
+
+ case CM_BINDCOMPONENTASYNCHRONOUS:
+ return cmld_BindComponentAsynchronous(procPriv, (CM_BindComponentAsynchronous_t *)arg);
+
+ case CM_UNBINDCOMPONENTASYNCHRONOUS:
+ return cmld_UnbindComponentAsynchronous(procPriv, (CM_UnbindComponentAsynchronous_t *)arg);
+
+ case CM_BINDCOMPONENT:
+ return cmld_BindComponent(procPriv, (CM_BindComponent_t *)arg);
+
+ case CM_UNBINDCOMPONENT:
+ return cmld_UnbindComponent(procPriv, (CM_UnbindComponent_t *)arg);
+
+ case CM_BINDCOMPONENTTOVOID:
+ return cmld_BindComponentToVoid(procPriv, (CM_BindComponentToVoid_t *)arg);
+
+ case CM_DESTROYCOMPONENT:
+ return cmld_DestroyComponent(procPriv, (CM_DestroyComponent_t *)arg);
+
+ case CM_CREATEMEMORYDOMAIN:
+ return cmld_CreateMemoryDomain(procPriv, (CM_CreateMemoryDomain_t *)arg);
+
+ case CM_CREATEMEMORYDOMAINSCRATCH:
+ return cmld_CreateMemoryDomainScratch(procPriv, (CM_CreateMemoryDomainScratch_t *)arg);
+
+ case CM_DESTROYMEMORYDOMAIN:
+ return cmld_DestroyMemoryDomain((CM_DestroyMemoryDomain_t *)arg);
+
+ case CM_GETDOMAINCOREID:
+ return cmld_GetDomainCoreId((CM_GetDomainCoreId_t *)arg);
+
+ case CM_ALLOCMPCMEMORY:
+ return cmld_AllocMpcMemory(procPriv, (CM_AllocMpcMemory_t *)arg);
+
+ case CM_FREEMPCMEMORY:
+ return cmld_FreeMpcMemory(procPriv, (CM_FreeMpcMemory_t *)arg);
+
+ case CM_GETMPCMEMORYSTATUS:
+ return cmld_GetMpcMemoryStatus((CM_GetMpcMemoryStatus_t *)arg);
+
+ case CM_STARTCOMPONENT:
+ return cmld_StartComponent(procPriv, (CM_StartComponent_t *)arg);
+
+ case CM_STOPCOMPONENT:
+ return cmld_StopComponent(procPriv, (CM_StopComponent_t *)arg);
+
+ case CM_GETMPCLOADCOUNTER:
+ return cmld_GetMpcLoadCounter((CM_GetMpcLoadCounter_t *)arg);
+
+ case CM_GETCOMPONENTDESCRIPTION:
+ return cmld_GetComponentDescription(procPriv, (CM_GetComponentDescription_t *)arg);
+
+ case CM_GETCOMPONENTLISTHEADER:
+ return cmld_GetComponentListHeader(procPriv, (CM_GetComponentListHeader_t *)arg);
+
+ case CM_GETCOMPONENTLISTNEXT:
+ return cmld_GetComponentListNext(procPriv, (CM_GetComponentListNext_t *)arg);
+
+ case CM_GETCOMPONENTREQUIREDINTERFACENUMBER:
+ return cmld_GetComponentRequiredInterfaceNumber(procPriv,
+ (CM_GetComponentRequiredInterfaceNumber_t *)arg);
+
+ case CM_GETCOMPONENTREQUIREDINTERFACE:
+ return cmld_GetComponentRequiredInterface(procPriv,
+ (CM_GetComponentRequiredInterface_t *)arg);
+
+ case CM_GETCOMPONENTREQUIREDINTERFACEBINDING:
+ return cmld_GetComponentRequiredInterfaceBinding(procPriv,
+ (CM_GetComponentRequiredInterfaceBinding_t *)arg);
+
+ case CM_GETCOMPONENTPROVIDEDINTERFACENUMBER:
+ return cmld_GetComponentProvidedInterfaceNumber(procPriv,
+ (CM_GetComponentProvidedInterfaceNumber_t *)arg);
+
+ case CM_GETCOMPONENTPROVIDEDINTERFACE:
+ return cmld_GetComponentProvidedInterface(procPriv,
+ (CM_GetComponentProvidedInterface_t *)arg);
+
+ case CM_GETCOMPONENTPROPERTYNUMBER:
+ return cmld_GetComponentPropertyNumber(procPriv,
+ (CM_GetComponentPropertyNumber_t *)arg);
+
+ case CM_GETCOMPONENTPROPERTYNAME:
+ return cmld_GetComponentPropertyName(procPriv,
+ (CM_GetComponentPropertyName_t *)arg);
+
+ case CM_GETCOMPONENTPROPERTYVALUE:
+ return cmld_GetComponentPropertyValue(procPriv,
+ (CM_GetComponentPropertyValue_t *)arg);
+
+ case CM_READCOMPONENTATTRIBUTE:
+ return cmld_ReadComponentAttribute(procPriv,
+ (CM_ReadComponentAttribute_t *)arg);
+
+ case CM_GETEXECUTIVEENGINEHANDLE:
+ return cmld_GetExecutiveEngineHandle(procPriv,
+ (CM_GetExecutiveEngineHandle_t *)arg);
+
+ case CM_SETMODE:
+ return cmld_SetMode((CM_SetMode_t *)arg);
+
+ case CM_GETREQUIREDCOMPONENTFILES:
+ return cmld_GetRequiredComponentFiles(procPriv,
+ (CM_GetRequiredComponentFiles_t *)arg);
+
+ case CM_MIGRATE:
+ return cmld_Migrate((CM_Migrate_t *)arg);
+
+ case CM_UNMIGRATE:
+ return cmld_Unmigrate((CM_Unmigrate_t *)arg);
+
+ case CM_PUSHCOMPONENT:
+ return cmld_PushComponent((CM_PushComponent_t *)arg);
+
+ case CM_RELEASECOMPONENT:
+ return cmld_ReleaseComponent((CM_ReleaseComponent_t *)arg);
+
+ /*
+ * NMF CALLS (Host->MPC bindings)
+ */
+ case CM_PUSHEVENTWITHSIZE: {
+ CM_PushEventWithSize_t data;
+ t_event_params_handle event;
+
+ /* coverity[tainted_data_argument : FALSE] */
+ if (copy_from_user(&data.in, (CM_PushEventWithSize_t*)arg, sizeof(data.in)))
+ return -EFAULT;
+
+ /* Take the lock to synchronize CM_ENGINE_AllocEvent()
+ * and CM_ENGINE_PushEvent()
+ */
+ if (mutex_lock_killable(&procPriv->host2mpcLock))
+ return -ERESTARTSYS;
+
+ event = CM_ENGINE_AllocEvent(data.in.host2mpcId);
+ if (event == NULL) {
+ mutex_unlock(&procPriv->host2mpcLock);
+ return put_user(CM_PARAM_FIFO_OVERFLOW,
+ &((CM_PushEventWithSize_t*)arg)->out.error);
+ }
+ if (data.in.size != 0)
+ /* coverity[tainted_data : FALSE] */
+ if (copy_from_user(event, data.in.h, data.in.size)) {
+ mutex_unlock(&procPriv->host2mpcLock);
+ return -EFAULT; // TODO: what about the already allocated and acknowledged event!?!
+ }
+
+ data.out.error = CM_ENGINE_PushEvent(data.in.host2mpcId, event, data.in.methodIndex);
+ mutex_unlock(&procPriv->host2mpcLock);
+
+ /* copy error value back */
+ return put_user(data.out.error, &((CM_PushEventWithSize_t*)arg)->out.error);
+ }
+
+ /*
+ * All private (internal) commands
+ */
+ case CM_PRIVGETMPCMEMORYDESC:
+ return cmld_PrivGetMPCMemoryDesc(procPriv, (CM_PrivGetMPCMemoryDesc_t *)arg);
+
+ case CM_PRIVRESERVEMEMORY:
+ return cmld_PrivReserveMemory(procPriv, arg);
+
+ case CM_GETVERSION: {
+ t_uint32 nmfversion = NMF_VERSION;
+ return copy_to_user((void*)arg, &nmfversion, sizeof(nmfversion));
+ }
+ case CM_PRIV_GETBOARDVERSION:
+ if (cpu_is_u8500v1()) {
+ enum board_version v = U8500_V1;
+ return copy_to_user((void*)arg, &v, sizeof(v));
+ } else if (cpu_is_u8500v2()) {
+ enum board_version v = U8500_V2;
+ return copy_to_user((void*)arg, &v, sizeof(v));
+ } else
+ return -EINVAL;
+
+ default: {
+ pr_err("CM(%s): unsupported command %i\n", __func__, cmd);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/** Driver's ioctl method
+ * Implements user/kernel crossing for SYSCALL API.
+ *
+ * \return POSIX error code
+ */
+static long cmld_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ BUG_ON(filp->private_data == NULL);
+
+ if (iminor(filp->f_dentry->d_inode) == 0) {
+ return cmld_control_ctl(filp, cmd, arg);
+ } else {
+ return cmld_channel_ctl(filp, cmd, arg);
+ }
+}
+
+/** VMA open callback function
+ */
+static void cmld_vma_open(struct vm_area_struct* vma) {
+ struct memAreaDesc_t* curr = (struct memAreaDesc_t*)vma->vm_private_data;
+
+ atomic_inc(&curr->count);
+}
+
+/** VMA close callback function
+ */
+static void cmld_vma_close(struct vm_area_struct* vma) {
+ struct memAreaDesc_t* curr = (struct memAreaDesc_t*)vma->vm_private_data;
+
+ atomic_dec(&curr->count);
+}
+
+static struct vm_operations_struct cmld_remap_vm_ops = {
+ .open = cmld_vma_open,
+ .close = cmld_vma_close,
+};
+
+/** mmap implementation.
+ * Remaps just once.
+ *
+ * \return POSIX error code
+ */
+static int cmld_mmap(struct file* file, struct vm_area_struct* vma)
+{
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ struct list_head* listHead;
+ struct list_head* cursor;
+ struct cm_process_priv* procPriv = file->private_data;
+ struct memAreaDesc_t* curr = NULL;
+ unsigned int vma_size = vma->vm_end-vma->vm_start;
+
+ BUG_ON(procPriv == NULL);
+
+ listHead = &procPriv->memAreaDescList;
+
+ if (lock_process(procPriv)) return -ERESTARTSYS;
+ /* Make sure the memory area has not already been remapped */
+ list_for_each(cursor, listHead) {
+ curr = list_entry(cursor, struct memAreaDesc_t, list);
+ /* For now, the user space aligns any requested physaddr to a page-size limit
+ This is not safe and must be fixed. But this is the only way to
+ minimize the allocated TCM memory, needed because of low amount of
+ TCM memory
+ Another way is to add some more check before doing this mmap()
+ to allow this mmap, for example.
+ NOTE: this memory must be first reserved via the CM_PRIVRESERVEMEMORY ioctl()
+ */
+ if ((curr->physAddr&PAGE_MASK) == offset &&
+ curr->tid == current->pid) {
+ if (curr->userLogicalAddr) {
+ unlock_process(procPriv);
+ return -EINVAL; // already mapped!
+ }
+ /* reset the thread id value, to not confuse any further mmap() */
+ curr->tid = 0;
+ break;
+ }
+ }
+
+ if (cursor == listHead) {
+ unlock_process(procPriv);
+ return -EINVAL; // no matching memory area descriptor found!
+ }
+
+ /* Very, very important to have consistent buffer transition */
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTEXPAND | VM_DONTCOPY;
+
+ if (remap_pfn_range(vma, vma->vm_start, offset>>PAGE_SHIFT,
+ vma_size, vma->vm_page_prot)) {
+ unlock_process(procPriv);
+ return -EAGAIN;
+ }
+
+ /* Offset represents the physical address.
+ * Update the list entry filling in the logical address assigned to the user
+ */
+ /*
+ * NOTE: here the useLogicalAddr is page-aligned, but not necessaly the
+ * phycical address. We mmap() more than originaly requested by the
+ * user, see in CM User Proxy (file cmsyscallwrapper.c)
+ */
+ curr->userLogicalAddr = vma->vm_start;
+
+ /* increment reference counter */
+ atomic_inc(&curr->count);
+
+ unlock_process(procPriv);
+
+ /* set private data structure and callbacks */
+ vma->vm_private_data = (void *)curr;
+ vma->vm_ops = &cmld_remap_vm_ops;
+
+ return 0;
+}
+
+/** MPC Events tasklet
+ * The parameter is used to know from which interrupts we're comming
+ * and which core to pass to CM_ProcessMpcEvent():
+ * 0 means HSEM => ARM_CORE_ID
+ * otherwise, it gives the index+1 of MPC within osalEnv.mpc table
+ */
+static void mpc_events_tasklet_handler(unsigned long core)
+{
+ /* This serves internal events directly. No propagation to user space.
+ * Calls OSAL_PostDfc implementation for user interface events */
+ if (core == 0) {
+ CM_ProcessMpcEvent(ARM_CORE_ID);
+ enable_irq(IRQ_DB8500_HSEM);
+ } else {
+ --core;
+ CM_ProcessMpcEvent(osalEnv.mpc[core].coreId);
+ enable_irq(osalEnv.mpc[core].interrupt0);
+ }
+}
+
+/** Hardware semaphore and MPC interrupt handler
+ * 'data' param is the one given when registering the IRQ hanlder,
+ * contains the source core (ARM or MPC), and follows the same logic
+ * as for mpc_events_tasklet_handler()
+ * This handler is used for all IRQ handling some com (ie HSEM or
+ * all MPC IRQ line0)
+ */
+static irqreturn_t mpc_events_irq_handler(int irq, void *data)
+{
+ unsigned core = (unsigned)data;
+
+ if (core != 0)
+ --core;
+ disable_irq_nosync(irq);
+ tasklet_schedule(&osalEnv.mpc[core].tasklet);
+
+ return IRQ_HANDLED;
+}
+
+/** MPC panic handler
+ * 'idx' contains the index of the core within the osalEnv.mpc table.
+ * This handler is used for all MPC IRQ line1
+ */
+static irqreturn_t panic_handler(int irq, void *idx)
+{
+ set_bit((int)idx, &service_tasklet_data);
+ disable_irq_nosync(irq);
+ tasklet_schedule(&cmld_service_tasklet);
+ return IRQ_HANDLED;
+}
+
+/** Driver's operations
+ */
+static struct file_operations cmld_fops = {
+ .owner = THIS_MODULE,
+ .llseek = NULL,
+ .read = cmld_read,
+ .write = NULL,
+ .poll = NULL,
+ .unlocked_ioctl = cmld_ioctl,
+ .ioctl = NULL,
+ .mmap = cmld_mmap,
+ .open = cmld_open,
+ .flush = cmld_flush,
+ .release = cmld_release,
+ .fsync = NULL,
+ .fasync = NULL,
+ .lock = NULL,
+};
+
+/**
+ * Configure a MPC, called for each MPC to configure
+ *
+ * \param i index of the MPC to configure (refer to the index
+ * of the MPC within the osalEnvironment.mpc table)
+ * \param dataAllocId allocId of the data segment, passed through each call of
+ * this function, and initialized at the first call in case
+ * shared data segment
+ */
+static int configureMpc(unsigned i, t_cfg_allocator_id *dataAllocId)
+{
+ int err;
+ t_cm_system_address mpcSystemAddress;
+ t_nmf_memory_segment codeSegment, dataSegment;
+ t_cfg_allocator_id codeAllocId;
+ t_cm_domain_id eeDomainId;
+ t_cm_domain_memory eeDomain = INIT_DOMAIN;
+ char regulator_name[14];
+
+ getMpcSystemAddress(i, &mpcSystemAddress);
+ getMpcSdramSegments(i, &codeSegment, &dataSegment);
+
+ /* Create code segment */
+ err = CM_ENGINE_AddMpcSdramSegment(&codeSegment, &codeAllocId, "Code");
+ if (err != CM_OK) {
+ pr_err("CM_ENGINE_AddMpcSdramSegment() error code: %d\n", err);
+ return -EAGAIN;
+ }
+
+ /* Create data segment
+ * NOTE: in case of shared data segment, all MPC point to the same data segment
+ * (see in remapRegions()) and we need to create the segment only at first call.
+ * => we reuse the same allocId for the following MPCs
+ */
+ if ((osalEnv.mpc[i].sdramDataL != osalEnv.mpc[0].sdramDataL)
+ || *dataAllocId == -1) {
+ err = CM_ENGINE_AddMpcSdramSegment(&dataSegment, dataAllocId, "Data");
+ if (err != CM_OK) {
+ pr_err("CM_ENGINE_AddMpcSdramSegment() error code: %d\n", err);
+ return -EAGAIN;
+ }
+ }
+
+ /* create default domain for the given coreId
+ * this serves for instanciating EE and the LoadMap, only sdram segment is present
+ * this domain will probably overlap with other user domains
+ */
+ eeDomain.coreId = osalEnv.mpc[i].coreId;
+ eeDomain.sdramCode.offset = 0x0;
+ eeDomain.sdramData.offset = 0x0;
+ eeDomain.sdramCode.size = 0x8000;
+ eeDomain.sdramData.size = 0x40000;
+ eeDomain.esramCode.size = 0x4000;
+ eeDomain.esramData.size = 0x40000;
+ err = CM_ENGINE_CreateMemoryDomain(NMF_CORE_CLIENT, &eeDomain, &eeDomainId);
+ if (err != CM_OK) {
+ pr_err("Create EE domain on %s failed with error code: %d\n", osalEnv.mpc[i].name, err);
+ return -EAGAIN;
+ }
+
+ err = CM_ENGINE_ConfigureMediaProcessorCore(
+ osalEnv.mpc[i].coreId,
+ osalEnv.mpc[i].eeId,
+ (cfgSemaphoreTypeHSEM ? SYSTEM_SEMAPHORES : LOCAL_SEMAPHORES),
+ osalEnv.mpc[i].nbYramBanks,
+ &mpcSystemAddress,
+ eeDomainId,
+ codeAllocId,
+ *dataAllocId);
+
+ if (err != CM_OK) {
+ pr_err("CM_ConfigureMediaProcessorCore failed with error code: %d\n", err);
+ return -EAGAIN;
+ }
+
+ // Communication channel
+ if (! cfgSemaphoreTypeHSEM) {
+ tasklet_init(&osalEnv.mpc[i].tasklet, mpc_events_tasklet_handler, i+1);
+ err = request_irq(osalEnv.mpc[i].interrupt0, mpc_events_irq_handler, IRQF_DISABLED, osalEnv.mpc[i].name, (void*)(i+1));
+ if (err != 0) {
+ pr_err("CM: request_irq failed to register irq0 %i for %s (%i)\n", osalEnv.mpc[i].interrupt0, osalEnv.mpc[i].name, err);
+ return err;
+ }
+ }
+
+ // Panic channel
+ err = request_irq(osalEnv.mpc[i].interrupt1, panic_handler, IRQF_DISABLED, osalEnv.mpc[i].name, (void*)i);
+ if (err != 0) {
+ pr_err("CM: request_irq failed to register irq1 %i for %s (%i)\n", osalEnv.mpc[i].interrupt1, osalEnv.mpc[i].name, err);
+ free_irq(osalEnv.mpc[i].interrupt0, (void*)(i+1));
+ return err;
+ }
+
+ // Retrieve the regulators used for this MPCs
+ sprintf(regulator_name, "%s-mmdsp", osalEnv.mpc[i].name);
+ osalEnv.mpc[i].mmdsp_regulator = regulator_get(cmld_dev[0], regulator_name);
+ if (IS_ERR(osalEnv.mpc[i].mmdsp_regulator)) {
+ long err = PTR_ERR(osalEnv.mpc[i].mmdsp_regulator);
+ pr_err("CM: Error while retrieving the regulator %s: %ld\n", regulator_name, err);
+ osalEnv.mpc[i].mmdsp_regulator = NULL;
+ return err;
+ }
+ sprintf(regulator_name, "%s-pipe", osalEnv.mpc[i].name);
+ osalEnv.mpc[i].pipe_regulator = regulator_get(cmld_dev[0], regulator_name);
+ if (IS_ERR(osalEnv.mpc[i].pipe_regulator)) {
+ long err = PTR_ERR(osalEnv.mpc[i].pipe_regulator);
+ pr_err("CM: Error while retrieving the regulator %s: %ld\n", regulator_name, err);
+ osalEnv.mpc[i].pipe_regulator = NULL;
+ return err;
+ }
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_init(&osalEnv.mpc[i].wakelock, WAKE_LOCK_SUSPEND, osalEnv.mpc[i].name);
+#endif
+ return 0;
+}
+
+/* Free all used MPC irqs and clocks.
+ * max_mpc allows it to be called from init_module and free
+ * only the already configured irqs.
+ */
+static void free_mpc_irqs(int max_mpc)
+{
+ int i;
+ for (i=0; i<max_mpc; i++) {
+ if (! cfgSemaphoreTypeHSEM)
+ free_irq(osalEnv.mpc[i].interrupt0, (void*)(i+1));
+ free_irq(osalEnv.mpc[i].interrupt1, (void*)i);
+ if (osalEnv.mpc[i].mmdsp_regulator)
+ regulator_put(osalEnv.mpc[i].mmdsp_regulator);
+ if (osalEnv.mpc[i].pipe_regulator)
+ regulator_put(osalEnv.mpc[i].pipe_regulator);
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_destroy(&osalEnv.mpc[i].wakelock);
+#endif
+ }
+}
+
+/** Module entry point
+ * Allocate memory chunks. Register hardware semaphore, SIA and SVA interrupts.
+ * Initialize Component Manager. Register hotplug for components download.
+ *
+ * \return POSIX error code
+ */
+static int __init cmld_init_module(void)
+{
+ int err;
+ unsigned i=0;
+ dev_t dev;
+ t_cfg_allocator_id dataAllocId = -1;
+ void *htim_base=NULL;
+
+ /* Component manager initialization descriptors */
+ t_nmf_hw_mapping_desc nmfHwMappingDesc;
+ t_nmf_config_desc nmfConfigDesc = { cfgCommunicationLocationInSDRAM ? COMS_IN_SDRAM : COMS_IN_ESRAM };
+
+ /* OSAL_*Resources() assumes the following, so check that it is correct */
+ if (SVA != COREIDX((int)SVA_CORE_ID)) {
+ pr_err("SVA and (SVA_CORE_ID-1) differs : code must be fixed !\n");
+ return -EIO;
+ }
+ if (SIA != COREIDX((int)SIA_CORE_ID)) {
+ pr_err("SIA and (SIA_CORE_ID-1) differs : code must be fixed !\n");
+ return -EIO;
+ }
+
+#ifdef CM_DEBUG_ALLOC
+ init_debug_alloc();
+#endif
+
+ err = -EIO;
+ prcmu_base = __io_address(U8500_PRCMU_BASE);
+ if (cpu_is_u8500v1()) {
+ /* power on a clock/timer 90KHz used on SVA */
+ htim_base = ioremap_nocache(U8500_CR_BASE_ED/*0xA03D8000*/, SZ_4K);
+ prcmu_tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE_V1);
+ } else if (cpu_is_u8500v2()) {
+ /* power on a clock/timer 90KHz used on SVA */
+ htim_base = ioremap_nocache(U8500_CR_BASE /*0xA03C8000*/, SZ_4K);
+ prcmu_tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
+ } else {
+ pr_err("CM: Unsupported chip version\n");
+ goto out;
+ }
+
+ /* Activate SVA 90 KHz timer */
+ if (htim_base == NULL)
+ goto out;
+ iowrite32((1<<26), htim_base);
+ iounmap(htim_base);
+
+ /*i = ioread32(PRCM_SVAMMDSPCLK_MGT) & 0xFF;
+ if (i != 0x22)
+ pr_alert("CM: Looks like SVA is not clocked at 200MHz (PRCM_SVAMMDSPCLK_MGT=%x)\n", i);
+ i = ioread32(PRCM_SIAMMDSPCLK_MGT) & 0xFF;
+ if (i != 0x22)
+ pr_alert("CM: Looks like SIA is not clocked at 200MHz (PRCM_SIAMMDSPCLK_MGT=%x)\n", i);
+
+ i = 0;*/
+ err = init_config();
+ if (err)
+ goto out;
+
+ /* Remap all needed regions and store in osalEnv base addresses */
+ err = remapRegions();
+ if (err != 0)
+ goto out;
+
+ /* Initialize linux devices */
+ err = class_register(&cmld_class);
+ if (err) {
+ pr_err("CM: class_register failed (%d)\n", err);
+ goto out;
+ }
+
+ /* Register char device */
+ err = alloc_chrdev_region(&dev, 0, ARRAY_SIZE(cmld_devname), "cm");
+ if (err) {
+ pr_err("CM: alloc_chrdev_region failed (%d)\n", err);
+ goto out_destroy_class;
+ }
+ cmld_major = MAJOR(dev);
+
+ cdev_init(&cmld_cdev, &cmld_fops);
+ cmld_cdev.owner = THIS_MODULE;
+ err = cdev_add (&cmld_cdev, dev, ARRAY_SIZE(cmld_devname));
+ if (err) {
+ pr_err("CM: cdev_add failed (%d)\n", err);
+ goto out_destroy_chrdev;
+ }
+
+ for (i=0; i<ARRAY_SIZE(cmld_devname); i++) {
+ cmld_dev[i] = device_create(&cmld_class, NULL, MKDEV(cmld_major, i), NULL,
+ "%s", cmld_devname[i]);
+ if (IS_ERR(cmld_dev[i])) {
+ err = PTR_ERR(cmld_dev[i]);
+ pr_err("CM: device_create failed (%d)\n", err);
+ goto out_destroy_device;
+ }
+ }
+
+ osalEnv.esram_regulator[ESRAM_12] = regulator_get(cmld_dev[0], "esram12");
+ if (IS_ERR(osalEnv.esram_regulator[ESRAM_12])) {
+ err = PTR_ERR(osalEnv.esram_regulator[ESRAM_12]);
+ pr_err("CM: Error while retrieving the regulator for esram12: %d\n", err);
+ osalEnv.esram_regulator[ESRAM_12] = NULL;
+ goto out_destroy_device;
+ }
+ osalEnv.esram_regulator[ESRAM_34] = regulator_get(cmld_dev[0], "esram34");
+ if (IS_ERR(osalEnv.esram_regulator[ESRAM_34])) {
+ err = PTR_ERR(osalEnv.esram_regulator[ESRAM_34]);
+ pr_err("CM: Error while retrieving the regulator for esram34: %d\n", err);
+ osalEnv.esram_regulator[ESRAM_34] = NULL;
+ goto out_destroy_device;
+ }
+
+ /* Fill in the descriptors needed by CM_ENGINE_Init() */
+ getNmfHwMappingDesc(&nmfHwMappingDesc);
+
+ /* Initialize Component Manager */
+ err = CM_ENGINE_Init(&nmfHwMappingDesc, &nmfConfigDesc);
+ if (err != CM_OK) {
+ pr_err("CM: CM_Init failed with error code: %d\n", err);
+ err = -EAGAIN;
+ goto out_destroy_device;
+ } else {
+ pr_info("Initialize NMF %d.%d.%d Component Manager......\n",
+ VERSION_MAJOR(NMF_VERSION),
+ VERSION_MINOR(NMF_VERSION),
+ VERSION_PATCH(NMF_VERSION));
+ pr_info("[ CM Linux Driver %d.%d.%d ]\n",
+ VERSION_MAJOR(NMF_VERSION),
+ VERSION_MINOR(NMF_VERSION),
+ CMDRIVER_PATCH_VERSION);
+ }
+
+ /* Reserve DMA and MCDE ESRAM if needed */
+ if (cfgESRAM_ReserveDMA) {
+ t_cm_domain_memory domain = INIT_DOMAIN;
+
+ // Reserve memory used by DMA
+ domain.coreId = ARM_CORE_ID;
+ domain.esramData.offset = 0x0;
+ domain.esramData.size = cfgESRAMDmaSize * ONE_KB;
+ err = CM_ENGINE_CreateMemoryDomain(NMF_CORE_CLIENT,
+ &domain, &dmaDomainId);
+ if (err != CM_OK) {
+ pr_err("CM: Create DMA/ESRAM domain failed with error code: %d\n", err);
+ err = -EAGAIN;
+ goto out_all;
+ }
+
+ err = CM_ENGINE_AllocMpcMemory(dmaDomainId,
+ NMF_CORE_CLIENT,
+ CM_MM_MPC_ESRAM16,
+ cfgESRAMDmaSize * ONE_KB / 2,
+ CM_MM_MPC_ALIGN_NONE,
+ &dmaMemoryHdl);
+ if (err != CM_OK) {
+ pr_err("CM: Alloc DMA in ESRAM domain failed with error code: %d\n", err);
+ err = -EAGAIN;
+ goto out_all;
+ }
+ }
+
+ if (cfgESRAM_ReserveMCDE) {
+ t_cm_domain_memory domain = INIT_DOMAIN;
+
+ // Reserve memory used by MCDE
+ domain.coreId = ARM_CORE_ID;
+ domain.esramData.offset = (cfgESRAMSize - cfgESRAMMcdeSize) * ONE_KB;
+ domain.esramData.size = cfgESRAMMcdeSize * ONE_KB;
+ err = CM_ENGINE_CreateMemoryDomain(NMF_CORE_CLIENT,
+ &domain, &mcdeDomainId);
+ if (err != CM_OK) {
+ pr_err("CM: Create MCDE/ESRAM domain failed with error code: %d\n", err);
+ err = -EAGAIN;
+ goto out_all;
+ }
+
+ err = CM_ENGINE_AllocMpcMemory(mcdeDomainId,
+ NMF_CORE_CLIENT,
+ CM_MM_MPC_ESRAM16,
+ cfgESRAMMcdeSize * ONE_KB / 2,
+ CM_MM_MPC_ALIGN_NONE,
+ &mcdeMemoryHdl);
+ if (err != CM_OK) {
+ pr_err("CM: Alloc MCDE in ESRAM domain failed with error code: %d\n", err);
+ err = -EAGAIN;
+ goto out_all;
+ }
+ }
+
+ /* Configure MPC Cores */
+ for (i=0; i<NB_MPC; i++) {
+ err = configureMpc(i, &dataAllocId);
+ if (err)
+ goto out_all;
+ }
+ /* End of Component Manager initialization phase */
+
+
+ if (cfgSemaphoreTypeHSEM) {
+ /* We use tasklet of mpc[0]. See comments above osalEnvironnent struct */
+ tasklet_init(&osalEnv.mpc[0].tasklet, mpc_events_tasklet_handler, 0);
+ err = request_irq(IRQ_DB8500_HSEM, mpc_events_irq_handler, IRQF_DISABLED,
+ "hwsem", 0);
+ if (err) {
+ pr_err("CM: request_irq failed to register hwsem irq %i (%i)\n",
+ IRQ_DB8500_HSEM, err);
+ goto out_all;
+ }
+ }
+
+ return 0;
+
+out_all:
+ if (err) {
+ if (cfgESRAM_ReserveMCDE) {
+ if (mcdeMemoryHdl)
+ CM_ENGINE_FreeMpcMemory(mcdeMemoryHdl);
+ CM_ENGINE_DestroyMemoryDomain(mcdeDomainId);
+ }
+ if (cfgESRAM_ReserveDMA) {
+ if (dmaMemoryHdl)
+ CM_ENGINE_FreeMpcMemory(dmaMemoryHdl);
+ CM_ENGINE_DestroyMemoryDomain(dmaDomainId);
+ }
+ }
+ free_mpc_irqs(i);
+ CM_ENGINE_Destroy();
+ i=ARRAY_SIZE(cmld_devname);
+out_destroy_device:
+ if (osalEnv.esram_regulator[ESRAM_12])
+ regulator_put(osalEnv.esram_regulator[ESRAM_12]);
+ if (osalEnv.esram_regulator[ESRAM_34])
+ regulator_put(osalEnv.esram_regulator[ESRAM_34]);
+ while (i--)
+ device_destroy(&cmld_class, MKDEV(cmld_major, i));
+ cdev_del(&cmld_cdev);
+out_destroy_chrdev:
+ unregister_chrdev_region(dev, ARRAY_SIZE(cmld_devname));
+out_destroy_class:
+ class_unregister(&cmld_class);
+out:
+ unmapRegions();
+#ifdef CM_DEBUG_ALLOC
+ cleanup_debug_alloc();
+#endif
+ return err;
+}
+
+/** Module exit point
+ * Unregister the driver. This will lead to a 'remove' call.
+ */
+static void __exit cmld_cleanup_module(void)
+{
+ unsigned i;
+
+ if (!list_empty(&channel_list))
+ pr_err("CM Driver ending with non empty channel list\n");
+ if (!list_empty(&process_list))
+ pr_err("CM Driver ending with non empty process list\n");
+
+ if (cfgESRAM_ReserveMCDE) {
+ if (mcdeMemoryHdl)
+ CM_ENGINE_FreeMpcMemory(mcdeMemoryHdl);
+ CM_ENGINE_DestroyMemoryDomain(mcdeDomainId);
+ }
+ if (cfgESRAM_ReserveDMA) {
+ if (dmaMemoryHdl)
+ CM_ENGINE_FreeMpcMemory(dmaMemoryHdl);
+ CM_ENGINE_DestroyMemoryDomain(dmaDomainId);
+ }
+ if (cfgSemaphoreTypeHSEM)
+ free_irq(IRQ_DB8500_HSEM, NULL);
+ free_mpc_irqs(NB_MPC);
+ tasklet_kill(&cmld_service_tasklet);
+
+ if (osalEnv.esram_regulator[ESRAM_12])
+ regulator_put(osalEnv.esram_regulator[ESRAM_12]);
+ if (osalEnv.esram_regulator[ESRAM_34])
+ regulator_put(osalEnv.esram_regulator[ESRAM_34]);
+ for (i=0; i<ARRAY_SIZE(cmld_devname); i++)
+ device_destroy(&cmld_class, MKDEV(cmld_major, i));
+ cdev_del(&cmld_cdev);
+ unregister_chrdev_region(MKDEV(cmld_major, 0), ARRAY_SIZE(cmld_devname));
+ class_unregister(&cmld_class);
+
+ CM_ENGINE_Destroy();
+
+ unmapRegions();
+#ifdef CM_DEBUG_ALLOC
+ cleanup_debug_alloc();
+#endif
+}
+module_init(cmld_init_module);
+module_exit(cmld_cleanup_module);
+
+MODULE_AUTHOR("David Siorpaes");
+MODULE_AUTHOR("Wolfgang Betz");
+MODULE_AUTHOR("Pierre Peiffer");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Nomadik Multiprocessing Framework Component Manager Linux driver");
diff --git a/drivers/staging/nmf-cm/cmld.h b/drivers/staging/nmf-cm/cmld.h
new file mode 100644
index 00000000000..2fbb05a0a38
--- /dev/null
+++ b/drivers/staging/nmf-cm/cmld.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef CMLD_H
+#define CMLD_H
+
+#include <linux/kref.h>
+#include <linux/mutex.h>
+#include <linux/version.h>
+#include <linux/wait.h>
+#include <inc/nmf-limits.h>
+#include "cmioctl.h"
+
+/** Channel state used within the per-channel private structure 'cm_channel_priv'
+ */
+enum channel_state {
+ CHANNEL_CLOSED = 0, /**< Channel already closed */
+ CHANNEL_OPEN, /**< Channel still open */
+};
+
+/** Component Manager per-process private structure
+ * It is created the first time a process opens /dev/cm0 or /dev/cm1
+ */
+struct cm_process_priv
+{
+ struct kref ref; /**< ref count */
+ struct list_head entry; /**< This entry */
+ pid_t pid; /**< pid of process owner */
+ struct mutex mutex; /**< per process mutex: protect memAreaDescList */
+ struct list_head memAreaDescList; /**< memAreaDesc_t list */
+ struct mutex host2mpcLock; /**< used to synchronize each AllocEvent + PushEvent */
+};
+
+/** Component Manager per-channel private structure
+ * It is created when a user opens /dev/cm1
+ */
+struct cm_channel_priv
+{
+ enum channel_state state; /**< Channel state */
+ struct list_head entry; /**< This entry */
+ struct cm_process_priv *proc; /**< Pointer to the owner process structure */
+ struct list_head skelList; /**< t_skelwrapper list */
+ struct mutex skelListLock; /**< skelList mutex */
+ struct plist_head messageQueue; /**< queueelem_t list */
+ struct mutex msgQueueLock; /**< lock used to synchronize MPC to HOST bindings
+ in case of multiple read (see cmld_read comments) */
+ spinlock_t bh_lock; /**< lock used to synchronize add/removal of element in/from
+ the message queue in both user context and tasklet */
+ wait_queue_head_t waitq; /**< wait queue used to block read() call */
+};
+
+/** Memory area descriptor.
+ */
+struct memAreaDesc_t {
+ struct list_head list; /**< Doubly linked list descriptor */
+ atomic_t count; /**< Reference counter */
+ pid_t tid; /**< tid of the process this area is reserved for */
+ t_cm_memory_handle handle; /**< Component Manager handle */
+ unsigned int size; /**< Size */
+ unsigned int physAddr; /**< Physical address */
+ unsigned int kernelLogicalAddr; /**< Logical address as seen by kernel */
+ unsigned int userLogicalAddr; /**< Logical address as seen by user */
+ unsigned int mpcPhysAddr; /**< Physicaladdress as seen by MPC */
+ struct cm_process_priv* procPriv; /**< link to per process private structure */
+};
+
+extern struct list_head channel_list; /**< List of all allocated channel structures */
+extern struct list_head process_list; /**< List of all allocated process private structure */
+
+/** Lock/unlock per process mutex
+ *
+ * \note Must be taken before tasklet_disable (if necessary)!
+ */
+#define lock_process_uninterruptible(proc) (mutex_lock(&proc->mutex))
+#define lock_process(proc) (mutex_lock_killable(&proc->mutex))
+#define unlock_process(proc) (mutex_unlock(&proc->mutex))
+
+
+
+int cmld_InstantiateComponent(struct cm_process_priv *, CM_InstantiateComponent_t __user *);
+int cmld_BindComponentFromCMCore(struct cm_process_priv *,
+ CM_BindComponentFromCMCore_t __user *);
+int cmld_UnbindComponentFromCMCore(CM_UnbindComponentFromCMCore_t __user *);
+int cmld_BindComponentToCMCore(struct cm_channel_priv *, CM_BindComponentToCMCore_t __user *);
+int cmld_UnbindComponentToCMCore(struct cm_process_priv*, CM_UnbindComponentToCMCore_t __user *);
+int cmld_BindComponentAsynchronous(struct cm_process_priv*, CM_BindComponentAsynchronous_t __user *);
+int cmld_UnbindComponentAsynchronous(struct cm_process_priv*, CM_UnbindComponentAsynchronous_t __user *);
+int cmld_BindComponent(struct cm_process_priv*, CM_BindComponent_t __user *);
+int cmld_UnbindComponent(struct cm_process_priv*, CM_UnbindComponent_t __user *);
+int cmld_BindComponentToVoid(struct cm_process_priv*, CM_BindComponentToVoid_t __user *);
+int cmld_DestroyComponent(struct cm_process_priv*, CM_DestroyComponent_t __user *);
+int cmld_CreateMemoryDomain(struct cm_process_priv*, CM_CreateMemoryDomain_t __user *);
+int cmld_CreateMemoryDomainScratch(struct cm_process_priv*, CM_CreateMemoryDomainScratch_t __user *);
+int cmld_DestroyMemoryDomain(CM_DestroyMemoryDomain_t __user *);
+int cmld_GetDomainCoreId(CM_GetDomainCoreId_t __user *);
+int cmld_AllocMpcMemory(struct cm_process_priv *, CM_AllocMpcMemory_t __user *);
+int cmld_FreeMpcMemory(struct cm_process_priv *, CM_FreeMpcMemory_t __user *);
+int cmld_GetMpcMemoryStatus(CM_GetMpcMemoryStatus_t __user *);
+int cmld_StartComponent(struct cm_process_priv *, CM_StartComponent_t __user *);
+int cmld_StopComponent(struct cm_process_priv *, CM_StopComponent_t __user *);
+int cmld_GetMpcLoadCounter(CM_GetMpcLoadCounter_t __user *);
+int cmld_GetComponentDescription(struct cm_process_priv *, CM_GetComponentDescription_t __user *);
+int cmld_GetComponentListHeader(struct cm_process_priv *, CM_GetComponentListHeader_t __user *);
+int cmld_GetComponentListNext(struct cm_process_priv *, CM_GetComponentListNext_t __user *);
+int cmld_GetComponentRequiredInterfaceNumber(struct cm_process_priv *,
+ CM_GetComponentRequiredInterfaceNumber_t __user *);
+int cmld_GetComponentRequiredInterface(struct cm_process_priv *,
+ CM_GetComponentRequiredInterface_t __user *);
+int cmld_GetComponentRequiredInterfaceBinding(struct cm_process_priv *,
+ CM_GetComponentRequiredInterfaceBinding_t __user *);
+int cmld_GetComponentProvidedInterfaceNumber(struct cm_process_priv *,
+ CM_GetComponentProvidedInterfaceNumber_t __user *);
+int cmld_GetComponentProvidedInterface(struct cm_process_priv *,
+ CM_GetComponentProvidedInterface_t __user *);
+int cmld_GetComponentPropertyNumber(struct cm_process_priv *,
+ CM_GetComponentPropertyNumber_t __user *);
+int cmld_GetComponentPropertyName(struct cm_process_priv *, CM_GetComponentPropertyName_t __user *);
+int cmld_GetComponentPropertyValue(struct cm_process_priv *, CM_GetComponentPropertyValue_t __user *);
+int cmld_ReadComponentAttribute(struct cm_process_priv *, CM_ReadComponentAttribute_t __user *);
+int cmld_GetExecutiveEngineHandle(struct cm_process_priv *, CM_GetExecutiveEngineHandle_t __user *);
+int cmld_SetMode(CM_SetMode_t __user *);
+int cmld_GetRequiredComponentFiles(struct cm_process_priv *cmPriv,
+ CM_GetRequiredComponentFiles_t __user *);
+int cmld_Migrate(CM_Migrate_t __user *);
+int cmld_Unmigrate(CM_Unmigrate_t __user *);
+int cmld_PushComponent(CM_PushComponent_t __user *);
+int cmld_ReleaseComponent(CM_ReleaseComponent_t __user *);
+int cmld_PrivGetMPCMemoryDesc(struct cm_process_priv *, CM_PrivGetMPCMemoryDesc_t __user *);
+int cmld_PrivReserveMemory(struct cm_process_priv *, unsigned int);
+#endif
diff --git a/drivers/staging/nmf-cm/configuration.c b/drivers/staging/nmf-cm/configuration.c
new file mode 100644
index 00000000000..175b6152b40
--- /dev/null
+++ b/drivers/staging/nmf-cm/configuration.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+/** \file configuration.c
+ *
+ * Nomadik Multiprocessing Framework Linux Driver
+ *
+ */
+
+#include <cm/engine/api/configuration_engine.h>
+#include <cm/engine/configuration/inc/configuration_status.h>
+
+#include "osal-kernel.h"
+
+/* Per-driver environment */
+struct OsalEnvironment osalEnv =
+{
+ .mpc = {
+ {
+ .coreId = SVA_CORE_ID,
+ .name = "sva",
+ .baseP = (void*)SVA_BASE_ADDR,
+ .interrupt0 = IRQ_DB8500_SVA,
+ .interrupt1 = IRQ_DB8500_SVA2,
+ .mmdsp_regulator = NULL,
+ .pipe_regulator = NULL,
+ .monitor_tsk = NULL,
+ .hwmemCode = NULL,
+ .hwmemData = NULL,
+ },
+ {
+ .coreId = SIA_CORE_ID,
+ .name = "sia",
+ .baseP = (void*)SIA_BASE_ADDR,
+ .interrupt0 = IRQ_DB8500_SIA,
+ .interrupt1 = IRQ_DB8500_SIA2,
+ .mmdsp_regulator = NULL,
+ .pipe_regulator = NULL,
+ .monitor_tsk = NULL,
+ .hwmemCode = NULL,
+ .hwmemData = NULL,
+ }
+ },
+ .esram_regulator = { NULL, NULL},
+ .dsp_sleep = {
+ .sia_auto_pm_enable = PRCMU_AUTO_PM_OFF,
+ .sia_power_on = 0,
+ .sia_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF,
+ .sva_auto_pm_enable = PRCMU_AUTO_PM_OFF,
+ .sva_power_on = 0,
+ .sva_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF,
+ },
+ .dsp_idle = {
+ .sia_auto_pm_enable = PRCMU_AUTO_PM_OFF,
+ .sia_power_on = 0,
+ .sia_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF,
+ .sva_auto_pm_enable = PRCMU_AUTO_PM_OFF,
+ .sva_power_on = 0,
+ .sva_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF,
+ },
+};
+
+module_param_call(cm_debug_level, param_set_int, param_get_int,
+ &cm_debug_level, S_IWUSR|S_IRUGO);
+MODULE_PARM_DESC(cm_debug_level, "Debug level of NMF Core");
+
+module_param_call(cm_error_break, param_set_bool, param_get_bool,
+ &cm_error_break, S_IWUSR|S_IRUGO);
+MODULE_PARM_DESC(cm_error_break, "Stop on error (in an infinite loop, for debugging purpose)");
+
+module_param_call(cmIntensiveCheckState, param_set_bool, param_get_bool,
+ &cmIntensiveCheckState, S_IWUSR|S_IRUGO);
+MODULE_PARM_DESC(cmIntensiveCheckState, "Add additional intensive checks");
+
+DECLARE_MPC_PARAM(SVA, SDRAM_DATA_SIZE, "", 1);
+
+DECLARE_MPC_PARAM(SIA, 0, "\n\t\t\t(0 means shared with SVA)", 2);
+
+int cfgCommunicationLocationInSDRAM = 1;
+module_param(cfgCommunicationLocationInSDRAM, bool, S_IRUGO);
+MODULE_PARM_DESC(cfgCommunicationLocationInSDRAM, "Location of communications (SDRAM or ESRAM)");
+
+int cfgSemaphoreTypeHSEM = 1;
+module_param(cfgSemaphoreTypeHSEM, bool, S_IRUGO);
+MODULE_PARM_DESC(cfgSemaphoreTypeHSEM, "Semaphore used (HSEM or LSEM)");
+
+int cfgESRAMSize = ESRAM_SIZE;
+module_param(cfgESRAMSize, uint, S_IRUGO);
+MODULE_PARM_DESC(cfgESRAMSize, "Size of ESRAM used in the CM (in Kb)");
+
+int init_config(void)
+{
+ if (cfgMpcSDRAMCodeSize_SVA == 0 || cfgMpcSDRAMCodeSize_SIA == 0) {
+ pr_err("SDRAM code size must be greater than 0\n");
+ return -EINVAL;
+ }
+
+ if (cfgMpcSDRAMDataSize_SVA == 0) {
+ pr_err("SDRAM data size for SVA must be greater than 0\n");
+ return -EINVAL;
+ }
+ osalEnv.mpc[SVA].nbYramBanks = cfgMpcYBanks_SVA;
+ osalEnv.mpc[SVA].eeId = cfgSchedulerTypeHybrid_SVA ? HYBRID_EXECUTIVE_ENGINE : SYNCHRONOUS_EXECUTIVE_ENGINE;
+ osalEnv.mpc[SVA].sdramCodeSize = cfgMpcSDRAMCodeSize_SVA * ONE_KB;
+ osalEnv.mpc[SVA].sdramDataSize = cfgMpcSDRAMDataSize_SVA * ONE_KB;
+ osalEnv.mpc[SIA].nbYramBanks = cfgMpcYBanks_SIA;
+ osalEnv.mpc[SIA].eeId = cfgSchedulerTypeHybrid_SIA ? HYBRID_EXECUTIVE_ENGINE : SYNCHRONOUS_EXECUTIVE_ENGINE;
+ osalEnv.mpc[SIA].sdramCodeSize = cfgMpcSDRAMCodeSize_SIA * ONE_KB;
+ osalEnv.mpc[SIA].sdramDataSize = cfgMpcSDRAMDataSize_SIA * ONE_KB;
+
+ return 0;
+}
diff --git a/drivers/staging/nmf-cm/configuration.h b/drivers/staging/nmf-cm/configuration.h
new file mode 100644
index 00000000000..a3bc1ca9063
--- /dev/null
+++ b/drivers/staging/nmf-cm/configuration.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef CONFIGURATION_H
+#define CONFIGURATION_H
+
+/** Peripherals description.
+ * Some of these values are taken from kernel header description (which should be the
+ * right place of these definition); the missing ones are defined here.
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
+#include <generated/autoconf.h>
+#else
+#include <linux/autoconf.h>
+#endif
+
+/** Nomadik embedded Static RAM base address*/
+#define ESRAM_BASE (U8500_ESRAM_BASE + 0x10000) // V1/V2 config: 0-64k: secure;
+#define HWSEM_BASE U8500_HSEM_BASE
+
+/** SxA base address */
+#define SVA_BASE_ADDR U8500_SVA_BASE
+#define SIA_BASE_ADDR U8500_SIA_BASE
+
+/** Nomadik embedded ram size for CM (in Kb) */
+#define ESRAM_SIZE 576
+enum {
+ ESRAM_12,
+ ESRAM_34,
+ NB_ESRAM,
+};
+
+/** MPCs */
+enum {
+ SVA,
+ SIA,
+ NB_MPC,
+};
+#define COREIDX(id) (id-1)
+
+/** Base address of shared SDRAM: use upper SDRAM. We reserve a rather */
+#define SDRAM_CODE_SIZE_SVA (2*ONE_KB)
+#define SDRAM_CODE_SIZE_SIA (2*ONE_KB)
+#define SDRAM_DATA_SIZE (8*ONE_KB)
+
+extern int cfgCommunicationLocationInSDRAM;
+extern int cfgSemaphoreTypeHSEM;
+extern int cfgESRAMSize;
+
+int init_config(void);
+
+#define DECLARE_MPC_PARAM(mpc, sdramDataSize, extension, ybank) \
+ static unsigned int cfgMpcYBanks_##mpc = ybank; \
+ module_param(cfgMpcYBanks_##mpc, uint, S_IRUGO); \
+ MODULE_PARM_DESC(cfgMpcYBanks_##mpc, "Nb of Y-Ram banks used on " #mpc); \
+ \
+ static int cfgSchedulerTypeHybrid_##mpc = 1; \
+ module_param(cfgSchedulerTypeHybrid_##mpc, bool, S_IRUGO); \
+ MODULE_PARM_DESC(cfgSchedulerTypeHybrid_##mpc, "Scheduler used on " #mpc " (Hybrid or Synchronous)"); \
+ \
+ static unsigned int cfgMpcSDRAMCodeSize_##mpc = SDRAM_CODE_SIZE_##mpc; \
+ module_param(cfgMpcSDRAMCodeSize_##mpc, uint, S_IRUGO); \
+ MODULE_PARM_DESC(cfgMpcSDRAMCodeSize_##mpc, "Size of code segment on " #mpc " (in Kb)"); \
+ \
+ static unsigned int cfgMpcSDRAMDataSize_##mpc = sdramDataSize; \
+ module_param(cfgMpcSDRAMDataSize_##mpc, uint, S_IRUGO); \
+ MODULE_PARM_DESC(cfgMpcSDRAMDataSize_##mpc, "Size of data segment on " #mpc " (in Kb)" extension)
+
+#endif
diff --git a/drivers/staging/nmf-cm/cpp.hpp b/drivers/staging/nmf-cm/cpp.hpp
new file mode 100644
index 00000000000..32dccadf98b
--- /dev/null
+++ b/drivers/staging/nmf-cm/cpp.hpp
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \brief C++ component abstract definition
+ */
+
+#ifndef NMF_CPP_H_
+#define NMF_CPP_H_
+
+#include <inc/typedef.h>
+#include <inc/nmf_type.idt>
+
+/*
+ * \internal
+ */
+namespace NMF {
+
+/*!
+ * \brief C++ Primitive component abstract class
+ *
+ * This class is extended by each primitive template class generated by NMF tools according your ADL description.
+ */
+class Primitive {
+public:
+ /*!
+ * \brief This mutex allow to prevent parallel scheduling on multiple core.
+ *
+ * \warning This variable is used internally, you must not use it.
+ */
+ t_uint32 mutexHandle;
+
+ /*!
+ * \brief ADL declaration name
+ *
+ * Debug information containing the ADL declaration name of the component.
+ */
+ const char * name;
+
+
+ /*!
+ * \brief ADL template name
+ *
+ * Debug information containing the ADL template name of the component.
+ */
+ const char* templateName;
+
+public:
+ Primitive(const char* _templateName): name("unknown"), templateName(_templateName)
+ {
+ mutexHandle = 0;
+ }
+
+ virtual ~Primitive()
+ {
+ // Nothing To do, just to keep GCC happy that trig warning while its not necessary
+ }
+};
+
+/*!
+ * \brief C++ Interface descriptor abstract class
+ *
+ * This class is extended by each C++ interface class generated by NMF tools according your IDL description.
+ */
+class InterfaceDescriptor {
+public:
+ virtual ~InterfaceDescriptor()
+ {
+ // Nothing To do, just to keep GCC happy that trig warning while its not necessary
+ }
+};
+
+/*!
+ * \brief C++ Interface reference abstract class
+ *
+ * This class is extended by each C++ interface reference class generated by NMF tools according your IDL description.
+ */
+class InterfaceReference {
+protected:
+ /*!
+ * \brief Target reference
+ *
+ * \warning This method is used internally, you must not use it.
+ */
+ NMF::InterfaceDescriptor* reference;
+
+public:
+ InterfaceReference(NMF::InterfaceDescriptor* _reference):
+ reference(_reference) {}
+
+public:
+ /*!
+ * \brief Get targeted interface
+ *
+ * \warning This method is used internally, you must not use it.
+ */
+ NMF::InterfaceDescriptor* getReference() {
+ return reference;
+ }
+
+ /*!
+ * \brief Test if a interface reference is really bind.
+ */
+ bool IsNullInterface() {
+ return reference == 0x0;
+ }
+};
+
+/*!
+ * \brief C++ composite abstract class
+ */
+class Composite {
+protected:
+ /*!
+ * \brief Internal method for comparing two null terminated string.
+ */
+ virtual int compositeStrcmp(const char* str1, const char* str2)
+ {
+ signed char res;
+
+ while (1) {if ((res = *str1 - *str2++) != 0 || !*str1++) break;}
+ return res;
+ }
+ /*!
+ * \brief Internal method for copying null terminated string.
+ */
+ virtual void compositeCpycmp(char* dest, const char *src, int count)
+ {
+ while (count-- && (*dest++ = *src++) != '\0')
+ /* nothing */;
+ }
+
+ virtual ~Composite()
+ {
+ // Nothing To do, just to keep GCC happy that trig warning while its not necessary
+ }
+
+public:
+ /*!
+ * \brief Construct a composite network
+ *
+ * Instantiate a new composite network.
+ */
+ virtual t_nmf_error construct(void) = 0;
+
+ /*!
+ * \brief Start a composite network
+ *
+ * Start a component which has been fully configured and which has all required
+ * interfaces binded.
+ *
+ * \note Start/stop is recursive, thus you can start multiple time same component, but you
+ * have to stop same number of time.
+ */
+ virtual void start(void) = 0;
+
+ /*!
+ * \brief Stop a composite network
+ *
+ * Stop a component previously started using \ref start.
+ *
+ * \note Start/stop is recursive, thus you can start multiple time same component, but you
+ * have to stop same number of time.
+ */
+ virtual void stop(void) = 0;
+
+ /*!
+ * \brief Destroy a composite network
+ *
+ * Destroy a previously instanciated component with \ref construct
+ * by freeing memories allocated for component instance. This component must been
+ * previously stopped using \ref stop.
+ */
+ virtual t_nmf_error destroy(void) = 0;
+
+public:
+ /*!
+ * \brief Bind synchronously the Host to the component
+ *
+ * Create a synchronous binding between the User and the server component.
+ *
+ * \note Either synchronous (with at least one method returning parameter) or asynchronous interface could be return by such method.
+ *
+ * \warning Using the interface descriptor reference returned by this method break the execution model
+ * by making synchronous call which could be reentrant. Thus use this method with many precaution
+ * for example only with state-less library.
+ *
+ * \exception NMF_NO_SUCH_PROVIDED_INTERFACE
+ */
+ virtual t_nmf_error getInterface(
+ const char *name, //!< [in] The server provided interface name (either collection or not collection name allowed).
+ NMF::InterfaceReference *reference //!< [out] The interface descriptor reference
+ ) = 0;
+
+
+public:
+ /*!
+ * \brief Bind asynchronously the User to the component
+ *
+ * Create a asynchronous binding between the User and the server component.
+ *
+ * \exception NMF_NO_SUCH_PROVIDED_INTERFACE
+ * \exception NMF_NO_MORE_MEMORY
+ */
+ virtual t_nmf_error bindFromUser(
+ const char *name, //!< [in] The server provided interface name (either collection or not collection name allowed).
+ unsigned int size, //!< [in] The number of element in the event fifo (number of simultaneously waiting request)
+ NMF::InterfaceReference *reference, //!< [out] The interface descriptor reference
+ unsigned int subpriority = 0 //!< [in] The sub-priority belonging the priority of the target component
+ ) = 0;
+
+ /*!
+ * \brief Unbind asynchronously the User to the component
+ *
+ * Destroy a asynchronous binding between the User and the server component.
+ *
+ * \exception NMF_NO_SUCH_PROVIDED_INTERFACE
+ * \exception NMF_INTERFACE_NOT_BINDED
+ */
+ virtual t_nmf_error unbindFromUser(
+ const char *name //!< [in] The server provided interface name (either collection or not collection name allowed).
+ ) = 0;
+
+ /*!
+ * \brief Bind asynchronously the component to the User
+ *
+ * Create a asynchronous binding between client component and the User.
+ *
+ * \exception NMF_NO_SUCH_REQUIRED_INTERFACE
+ * \exception NMF_NO_MORE_MEMORY
+ * \exception NMF_INTERFACE_ALREADY_BINDED
+ */
+ virtual t_nmf_error bindToUser(
+ t_nmf_channel channel, //!< [in] The channel where the user will retrieve callback to execute.
+ const char *compositeitfname, //!< [in] The client required interface name (either collection or not collection name allowed).
+ void *clientContext, //!< [in] The client context that will be given back to user when calling EE_GetMessage()
+ unsigned int size //!< [in] The number of element in the event fifo (number of simultaneously waiting request)
+ ) = 0;
+
+ /*!
+ * \brief Unbind asynchronously the component to the User
+ *
+ * Destroy a asynchronous binding between client component and the User.
+ *
+ * \exception NMF_NO_SUCH_REQUIRED_INTERFACE
+ * \exception NMF_INTERFACE_NOT_BINDED
+ */
+ virtual t_nmf_error unbindToUser(
+ t_nmf_channel channel, //!< [in] The channel where the user will retrieve callback to execute.
+ const char *compositeitfname, //!< [in] The client required interface name (either collection or not collection name allowed).
+ void **pClientContext //!< [in] A reference where returning the client context gived during bind
+ ) = 0;
+
+ /*!
+ * \brief Bind asynchronously two components together
+ *
+ * Bind a required interface of a client component with a provided interface
+ * of a server component via a asynchronous binding.
+ *
+ * \warning In order to save memory, we are not embedding description about interface type, thus we are not able to detect
+ * mismatch of interface types during binding.
+ *
+ * \exception NMF_NO_SUCH_REQUIRED_INTERFACE
+ * \exception NMF_NO_SUCH_PROVIDED_INTERFACE
+ * \exception NMF_NO_MORE_MEMORY
+ * \exception NMF_INTERFACE_ALREADY_BINDED
+ */
+ virtual t_nmf_error bindAsynchronous(
+ const char * name, //!< [in] The client required interface name (either collection or not collection name allowed).
+ unsigned int size, //!< [in] The number of element in the event fifo (number of simultaneously waiting request).
+ NMF::Composite *target, //!< [in] The server component instance handle.
+ const char *targetname, //!< [in] The server provided interface name (either collection or not collection name allowed).
+ unsigned int subpriority = 0 //!< [in] The sub-priority belonging the priority of the target component
+ ) = 0;
+
+ /*!
+ * \brief Unbind asynchronously two components together
+ *
+ * Unbind a required interface of a client component with a provided interface
+ * of a server component via a asynchronous binding.
+ *
+ * \exception NMF_NO_SUCH_REQUIRED_INTERFACE
+ * \exception NMF_NO_SUCH_PROVIDED_INTERFACE
+ * \exception NMF_INTERFACE_NOT_BINDED
+ */
+ virtual t_nmf_error unbindAsynchronous(
+ const char *name, //!< [in] The client required interface name (either collection or not collection name allowed).
+ NMF::Composite *target, //!< [in] The server component instance handle.
+ const char *targetname //!< [in] The server provided interface name (either collection or not collection name allowed).
+ ) = 0;
+
+ /*!
+ * \brief Bind synchronously two components together
+ *
+ * Bind a required interface of this client component with a provided interface
+ * of a server component.
+ *
+ * \note Either synchronous (with at least one method returning parameter) or asynchronous interface could be bind by such method.
+ *
+ * \warning Creating such binding break the execution model by making synchronous call which could be reentrant.
+ * Thus use this method with many precaution for example only with state-less server library.
+ *
+ * \warning In order to save memory, we are not embedding description about interface type, thus we are not able to detect
+ * mismatch of interface types during binding.
+ *
+ * \exception NMF_NO_SUCH_REQUIRED_INTERFACE
+ * \exception NMF_NO_SUCH_PROVIDED_INTERFACE
+ * \exception NMF_INTERFACE_ALREADY_BINDED
+ */
+ virtual t_nmf_error bindComponent(
+ const char *name, //!< [in] The client required interface name (either collection or not collection name allowed).
+ NMF::Composite *target, //!< [in] The server component instance handle.
+ const char *targetname //!< [in] The server provided interface name (either collection or not collection name allowed).
+ ) = 0;
+
+ /*!
+ * \brief Unbind synchronously two components together
+ *
+ * Unbind a required interface of this client component with a provided interface
+ * of a server component.
+ *
+ * \exception NMF_NO_SUCH_REQUIRED_INTERFACE
+ * \exception NMF_NO_SUCH_PROVIDED_INTERFACE
+ * \exception NMF_INTERFACE_NOT_BINDED
+ */
+ virtual t_nmf_error unbindComponent(
+ const char *name, //!< [in] The client required interface name (either collection or not collection name allowed).
+ NMF::Composite *target, //!< [in] The server component instance handle.
+ const char *targetname //!< [in] The server provided interface name (either collection or not collection name allowed).
+ ) = 0;
+
+ /*!
+ * \brief Read a value on an attribute exported by a component instance.
+ *
+ * This attribute must have been declared as an attribute in the component configuration
+ * file.
+ *
+ * \exception NMF_NO_SUCH_ATTRIBUTE
+ */
+ virtual t_nmf_error readAttribute(
+ const char *name, //!< [in] The component attribute path-name (sub-component's/attribute)
+ t_uint32 *value //!< [out] Read value
+ ) = 0;
+
+ /*!
+ * \brief Get a component property declared in ADL.
+ *
+ * \exception NMF_NO_SUCH_PROPERTY
+ */
+ virtual t_nmf_error getProperty(
+ const char *name, //!< [in] The component property path-name (sub-component's/attribute)
+ char *value, //!< [out] Address where putting property value
+ t_uint32 valuelen //!< [in] value length
+ ) = 0;
+
+ /*!
+ * \brief Get a component instance of a component instantiate on MPC.
+ *
+ * This method allow you to get a component instance of a component instantiate automatically by
+ * this composite. This reference could be used to create sub-binding (like tunneling) with other MPC
+ * components (either obtain by this method on other composite or directly instantiate using
+ * Component Manager.
+ *
+ * \note Potential created sub-binding must be unbound before destroying the composite.
+ *
+ * \return NULL if component not exist or if not instantiate, elsewhere return the component handle
+ */
+ virtual t_nmf_component_handle getMPCComponentHandle(
+ const char *nameinowner //!< [in] MMDSP component name inside this composite component.
+ ) = 0;
+
+ virtual t_nmf_component_handle getComponentInterface(
+ const char *compositeitfname, //!< [in] MMDSP component name inside this composite component.
+ char *realitfname
+ ) = 0;
+
+ /*!
+ * \brief Bind a composite sub-component (ARM or MPC) from a MPC component deployed through CM API.
+ */
+ virtual t_nmf_error bindFromMPC(
+ t_nmf_component_handle client, //!< [in] Client MPC component handle
+ const char* clientitfname, //!< [in] Client MPC component require interface name
+ const char *compositeitfname, //!< [in] The server provided interface name (either collection or not collection name allowed).
+ unsigned int size //!< [in] The number of element in the event fifo (number of simultaneously waiting request).
+ ) = 0;
+
+ /*!
+ * \brief Unbind a composite sub-component (ARM or MPC) from a MPC component deployed through CM API.
+ */
+ virtual t_nmf_error unbindFromMPC(
+ t_nmf_component_handle client, //!< [in] Client MPC component handle
+ const char* clientitfname, //!< [in] Client MPC component require interface name
+ const char *compositeitfname //!< [in] The server provided interface name (either collection or not collection name allowed).
+ ) = 0;
+
+ /*!
+ * \brief Bind a composite sub-component (ARM or MPC) to a MPC component deployed through CM API.
+ */
+ virtual t_nmf_error bindToMPC(
+ const char *compositeitfname, //!< [in] The client required interface name (either collection or not collection name allowed).
+ t_nmf_component_handle server, //!< [in] Server MPC component handle
+ const char* serveritfname, //!< [in] Server MPC component provided interface name
+ unsigned int size //!< [in] The number of element in the event fifo (number of simultaneously waiting request).
+ ) = 0;
+
+ /*!
+ * \brief Bind a composite sub-component (ARM or MPC) to a MPC component deployed through CM API.
+ */
+ virtual t_nmf_error unbindToMPC(
+ const char *compositeitfname, //!< [in] The client required interface name (either collection or not collection name allowed).
+ t_nmf_component_handle server, //!< [in] Server MPC component handle
+ const char* serveritfname //!< [in] Server MPC component provided interface name
+ ) = 0;
+
+};
+
+/*!
+ * \brief C++ composite abstract class
+ */
+class Component {
+protected:
+ /*!
+ * \brief Internal method for comparing two null terminated string.
+ */
+ virtual int compositeStrcmp(const char* str1, const char* str2)
+ {
+ signed char res;
+
+ while (1) {if ((res = *str1 - *str2++) != 0 || !*str1++) break;}
+ return res;
+ }
+
+ virtual ~Component()
+ {
+ // Nothing To do, just to keep GCC happy that trig warning while its not necessary
+ }
+
+public:
+ /*!
+ * \brief Construct a composite network
+ */
+ virtual t_nmf_error construct(void) = 0;
+
+ /*!
+ * \brief Start a composite network
+ */
+ virtual void start(void) = 0;
+
+ /*!
+ * \brief Stop a composite network
+ */
+ virtual void stop(void) = 0;
+
+ /*!
+ * \brief Destroy a composite network
+ */
+ virtual t_nmf_error destroy(void) = 0;
+
+public:
+ /*!
+ * \brief Get interface reference
+ * \exception NMF_NO_SUCH_PROVIDED_INTERFACE
+ */
+ virtual t_nmf_error getInterface(
+ const char *name, //!< [in] The server provided interface name (either collection or not collection name allowed).
+ NMF::InterfaceReference *reference //!< [out] The interface descriptor reference
+ ) = 0;
+
+ /*!
+ * \brief Bind synchronously two components together
+ * \exception NMF_NO_SUCH_REQUIRED_INTERFACE
+ */
+ virtual t_nmf_error bindComponent(
+ const char *name, //!< [in] The client required interface name (either collection or not collection name allowed)
+ NMF::Component *target, //!< [in] The server component instance handle
+ const char *targetname //!< [in] The server provided interface name (either collection or not collection name allowed)
+ ) = 0;
+
+ /*!
+ * \brief Unbind synchronously two components together
+ * \exception NMF_NO_SUCH_REQUIRED_INTERFACE
+ */
+ virtual t_nmf_error unbindComponent(
+ const char *name //!< [in] The client required interface name (either collection or not collection name allowed).
+ ) = 0;
+};
+
+}
+
+/*!
+ * \brief C++ macro for testing if a optional required interface is bound.
+ */
+#define IS_NULL_INTERFACE(itf, meth) (itf.IsNullInterface())
+
+#endif /* NMF_CPP_H_ */
diff --git a/drivers/staging/nmf-cm/ee/api/ee.h b/drivers/staging/nmf-cm/ee/api/ee.h
new file mode 100644
index 00000000000..bde3cee9425
--- /dev/null
+++ b/drivers/staging/nmf-cm/ee/api/ee.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+/*!
+ * \defgroup HOSTEE_MODULE Host Execution Engine
+ */
+
+/*!
+ * \defgroup HOSTEE Host Execution Engine API
+ * \ingroup HOSTEE_MODULE
+ */
+
+#ifndef HOST_EE_H
+#define HOST_EE_H
+
+
+#include <inc/typedef.h>
+
+#include <nmf/inc/channel_type.h>
+#include <nmf/inc/service_type.h>
+
+#include <ee/api/ee_type.h>
+
+/*!
+ * \brief Get the version of the NMF Host EE at runtime
+ *
+ * This method should be used to query the version number of the
+ * NMF Component Manager engine at runtime. This is useful when using
+ * to check if version of the engine linked with application correspond
+ * to engine used for development.
+ *
+ * Such code can be used to check compatibility: \code
+ t_uint32 nmfversion;
+
+ // Print NMF version
+ EE_GetVersion(&nmfversion);
+ LOG("NMF Version %d-%d-%d\n",
+ VERSION_MAJOR(nmfversion),
+ VERSION_MINOR(nmfversion),
+ VERSION_PATCH(nmfversion));
+ if(NMF_VERSION != nmfversion) {
+ LOG("Error: Incompatible API version %d != %d\n", NMF_VERSION, nmfversion);
+ EXIT();
+ }
+ * \endcode
+ *
+ * \param[out] version Internal hardcoded version (use \ref VERSION_MAJOR, \ref VERSION_MINOR, \ref VERSION_PATCH macros to decode it).
+ *
+ * \ingroup HOSTEE
+ */
+PUBLIC IMPORT_SHARED void EE_GetVersion(t_uint32 *version);
+
+/*!
+ * \brief Set the mode of the Host EE.
+ *
+ * According the (\ref t_ee_cmd_id) value, this routine allows to modify dynamically the behavior of the Host EE.
+ *
+ * \param[in] aCmdID Command ID.
+ * \param[in] aParam Parameter of command ID if required.
+ *
+ * \ingroup HOSTEE
+ */
+PUBLIC IMPORT_SHARED t_nmf_error EE_SetMode(t_ee_cmd_id aCmdID, t_sint32 aParam);
+
+/*!
+ * \brief Create a channel for communication between host ee and user.
+ *
+ * The purpose of the function is to:
+ * - create a channel or get a channel, regarding the flag parameter
+ *
+ * \param[in] flags Whether the caller want to create a new channel (CHANNEL_PRIVATE)
+ * or use the shared one (CHANNEL_PRIVATE) (it will be created
+ * if it does not yet exist)
+ * \param[out] channel Channel number.
+ *
+ * \exception NMF_NO_MORE_MEMORY Not enough memory to create the callback Channel.
+ * \exception NMF_INVALID_PARAMETER The specified flags is invalid.
+ *
+ * \ingroup HOSTEE
+ */
+PUBLIC IMPORT_SHARED t_nmf_error EE_CreateChannel(t_nmf_channel_flag flags, t_nmf_channel *channel);
+
+/*!
+ * \brief Flush a channel to allow user to safely close it.
+ *
+ * The purpose of the function is to allow safe call of EE_CloseChannel() later on. Calling
+ * EE_FlushChannel() will allow a blocking call to EE_GetMessage() to exit with an error
+ * NMF_FLUSH_MESSAGE. After EE_GetMessage() has exit with such a value user must no more
+ * call EE_GetMessage() and can safely call EE_CloseChannel() that will destroy channel.
+ * In case of the share channel EE_FlushChannel() will return false for isFlushMessageGenerated if
+ * it's internal reference counter is not zero, in that case no NMF_FLUSH_MESSAGE error is return
+ * by EE_GetMessage() and user can immediatly call EE_CloseChannel().
+ * In case user know that no usage of channel is done when he want to destroy channel, call to this api
+ * is optionnal and user can safely call EE_CloseChannel().
+ *
+ * \param[in] channel Channel number
+ * \param[out] isFlushMessageGenerated Allow user to know if it must wait for NMF_FLUSH_MESSAGE return
+ * of EE_GetMessage() before calling EE_CloseChannel()
+ *
+ * \exception NMF_INVALID_PARAMETER The specified flags is invalid.
+ *
+ * \ingroup HOSTEE
+ */
+PUBLIC IMPORT_SHARED t_nmf_error EE_FlushChannel(t_nmf_channel channel, t_bool *isFlushMessageGenerated);
+
+/*!
+ * \brief Unregister channel
+ *
+ * The purpose of the function is to:
+ * - destroy a channel from the user to the Host ee.
+ *
+ * The user must call EE_UserDone() as many time as EE_UserInit().
+ * At the last EE_UserDone() call, the channel is closed and definitely destroyed
+ * All service callback must be unregistered first.
+ *
+ * \param[in] channel Channel number:
+ *
+ * \ingroup HOSTEE
+ */
+PUBLIC IMPORT_SHARED t_nmf_error EE_CloseChannel(t_nmf_channel channel);
+
+/*!
+ * \brief Register a service callback to this channel.
+ *
+ * \param[in] channel The channel on which the callback must be registered.
+ * \param[in] handler The given callback.
+ * \param[in] contextHandler The context associated with this callback.
+ *
+ * \exception NMF_NO_MORE_MEMORY Not enough memory to associate service with the Channel.
+ *
+ * \ingroup HOSTEE
+ */
+PUBLIC IMPORT_SHARED t_nmf_error EE_RegisterService(t_nmf_channel channel, t_nmf_serviceCallback handler, void *contextHandler);
+
+/*!
+ * \brief Unregister a service callback from this channel.
+ *
+ * \param[in] channel The channel on which the callback must be registered.
+ * \param[in] handler The given callback.
+ * \param[in] contextHandler The context associated with this callback.
+ *
+ * \exception NMF_INVALID_PARAMETER The channel or the callback doesn't exist.
+ *
+ * \ingroup HOSTEE
+ */
+PUBLIC IMPORT_SHARED t_nmf_error EE_UnregisterService(t_nmf_channel channel, t_nmf_serviceCallback handler, void *contextHandler);
+
+/*!
+ * \brief Unregister a notify callback for this channel.
+ *
+ * This method will register a callback that will be call each time a message has
+ * been push in queue of the channel.
+ * To unregister your callback just register a null notify.
+ *
+ * \param[in] channel The channel on which the callback must be registered.
+ * \param[in] notify The given callback.
+ * \param[in] contextHandler The context associated with this callback.
+ *
+ * \ingroup HOSTEE
+ */
+PUBLIC IMPORT_SHARED t_nmf_error EE_RegisterNotify(t_nmf_channel channel, t_nmf_notify notify, void *contextHandler);
+
+/*!
+ * \brief Get received message from specified callback channel.
+ *
+ * This method can be used to retrieve callback message from Host ee. Returned message could then
+ * be dispatch through EE_ExecuteMessage.
+ *
+ * \param[in] channel The channel from which the message must be retrieved
+ * \param[out] clientContext client context.
+ * \param[out] message Reference on buffer to be unmarshalled. The buffer is allocated internally.
+ * \param[in] blockable Indicate if the call could blocked or not.
+ *
+ * \exception NMF_NO_MESSAGE No waited message.
+ * \exception NMF_INVALID_PARAMETER At least one input parameters is invalid
+ *
+ * \ingroup HOSTEE
+ */
+PUBLIC IMPORT_SHARED t_nmf_error EE_GetMessage(t_nmf_channel channel, void **clientContext, char **message, t_bool blockable);
+
+/*!
+ * \brief Execute a message. User callback will be execute.
+ *
+ * This method allow the message retrieved through EE_GetMessage to the right user callback.
+ *
+ * \param[in] itfref Interface reference.
+ * \param[in] message Reference on buffer to be unmarshalled.
+ *
+ * \ingroup HOSTEE
+ */
+PUBLIC IMPORT_SHARED void EE_ExecuteMessage(void *itfref, char *message);
+
+#endif
diff --git a/drivers/staging/nmf-cm/ee/api/ee_type.h b/drivers/staging/nmf-cm/ee/api/ee_type.h
new file mode 100644
index 00000000000..368cd6d29ba
--- /dev/null
+++ b/drivers/staging/nmf-cm/ee/api/ee_type.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef HOST_EE_TYPE_H
+#define HOST_EE_TYPE_H
+
+/*!
+ * \brief Notify callback method type.
+ *
+ * \ingroup HOSTEE
+ */
+typedef void (*t_nmf_notify)(void *contextHandler);
+
+/*!
+ * \brief Definition of the command ID type
+ */
+typedef t_uint32 t_ee_cmd_id;
+
+/*!
+ * \brief Definition of the command ID
+ */
+typedef enum {
+ EE_CMD_TRACE_ON, //!< Enable tracing and force network resetting and dumping
+ EE_CMD_TRACE_OFF //!< Disable tracing
+} t_ee_cmd_idDescription;
+
+
+#endif
diff --git a/drivers/staging/nmf-cm/ee/api/list.idt b/drivers/staging/nmf-cm/ee/api/list.idt
new file mode 100644
index 00000000000..a53a39734d8
--- /dev/null
+++ b/drivers/staging/nmf-cm/ee/api/list.idt
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef LIST_IDT
+#define LIST_IDT
+
+/* linking elem */
+struct _t_list_link {
+ struct _t_list_link *pNext;
+};
+typedef struct _t_list_link t_list_link;
+
+#endif
+
diff --git a/drivers/staging/nmf-cm/ee/api/outnotify.h b/drivers/staging/nmf-cm/ee/api/outnotify.h
new file mode 100644
index 00000000000..c88185fac16
--- /dev/null
+++ b/drivers/staging/nmf-cm/ee/api/outnotify.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef OUT_IDT
+#define OUT_IDT
+
+typedef void (*t_nmf_notify)(void *contextHandler);
+
+#endif
+
diff --git a/drivers/staging/nmf-cm/ee/api/panic.idt b/drivers/staging/nmf-cm/ee/api/panic.idt
new file mode 100644
index 00000000000..612dfcc8b4b
--- /dev/null
+++ b/drivers/staging/nmf-cm/ee/api/panic.idt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \defgroup NMF_EE_TYPE Execution Engine Common Type Definitions
+ * \ingroup COMMON
+ */
+
+#ifndef __INC_PANIC_IDT
+#define __INC_PANIC_IDT
+
+/*!
+ * \brief Panic reason type
+ *
+ * For values, see \ref t_panic_reasonDescription.
+ *
+ * \ingroup NMF_EE_TYPE
+ */
+typedef t_uint8 t_panic_reason;
+
+/*!
+ * \brief The different panic reasons
+ *
+ * \verbatim
+ * Reason | Information | Behavior
+ * -------------------------------------------------------------------
+ * INTERNAL_PANIC | Not interpreted | Fatal panic, stop MPC
+ * USER_STACK_OVERFLOW | Faulting address & SPu | Fatal panic, stop MPC
+ * SYSTEM_STACK_OVERFLOW | Faulting address & SPu | Fatal panic, stop MPC
+ * UNALIGNED_LONG_ACCESS | Indicative Faulting address & SPu | Fatal panic, stop MPC
+ * EVENT_FIFO_OVERFLOW | 0 | Abort current task, stop MPC
+ * PARAM_FIFO_OVERFLOW | 0 | idem
+ * INTERFACE_NOT_BINDED | 0 | idem
+ * USER_PANIC | Not interpreted | idem
+ * UNBIND_INTERRUPT | Interrupt number | Do nothing, just return from interrupt.
+ * EVENT_FIFO_IN_USE | Destroy event Fifo while event already schedule (only for HostEE)
+ * \endverbatim
+ *
+ * \ingroup NMF_EE_TYPE
+ */
+typedef enum {
+ INTERNAL_PANIC = 1,
+ RESERVED_PANIC = 2,
+ USER_STACK_OVERFLOW = 3,
+ SYSTEM_STACK_OVERFLOW = 4,
+ UNALIGNED_LONG_ACCESS = 5,
+ EVENT_FIFO_OVERFLOW = 6,
+ PARAM_FIFO_OVERFLOW = 7,
+ INTERFACE_NOT_BINDED = 8,
+ USER_PANIC = 9,
+ UNBIND_INTERRUPT = 10,
+ EVENT_FIFO_IN_USE = 11
+} t_panic_reasonDescription;
+
+/*!
+ * \brief Define the source of the panic
+ *
+ * It indicates the source core of the panic message.\n
+ * It gives the member to use within \ref t_nmf_panic_data (which is a member of the t_nmf_service_data service data structure).
+
+ * \ingroup NMF_EE_TYPE
+ */
+typedef enum {
+ HOST_EE, //!< If the source is the Executive Engine running on the ARM Core
+ MPC_EE //!< If the source is the Executive Engine running on one of the MPC Core
+} t_panic_source;
+
+#endif
diff --git a/drivers/staging/nmf-cm/ee/api/priority.idt b/drivers/staging/nmf-cm/ee/api/priority.idt
new file mode 100644
index 00000000000..a276ba57727
--- /dev/null
+++ b/drivers/staging/nmf-cm/ee/api/priority.idt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef PRIORITY_IDT
+#define PRIORITY_IDT
+
+/**
+ * 0 : background
+ * 1 : normal
+ * 2 : urgent
+ * 3 :
+ * 4 : CScall
+ * 5 : CScall
+ * 6 : IO (not linked with priority ordering)
+ */
+#define MAX_SCHEDULER_PRIORITY_NUMBER 7
+#define MAX_SCHEDULER_SUBPRIORITY_NUMBER 4
+
+#endif
+
diff --git a/drivers/staging/nmf-cm/inc/nmf-def.h b/drivers/staging/nmf-cm/inc/nmf-def.h
new file mode 100644
index 00000000000..a0f421235b1
--- /dev/null
+++ b/drivers/staging/nmf-cm/inc/nmf-def.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+ /*!
+ * \brief NMF Version.
+ *
+ * This file contains the NMF Version.
+ *
+ * \defgroup NMF_VERSION NMF Version
+ * \ingroup COMMON
+ */
+
+#ifndef __INC_NMF_DEF_H
+#define __INC_NMF_DEF_H
+
+/*!
+ * \brief Current NMF version number
+ *
+ * \ingroup NMF_VERSION
+ */
+#define NMF_VERSION ((2 << 16) | (10 << 8) | (103))
+
+/*!
+ * \brief Get NMF major version corresponding to NMF version number
+ * \ingroup NMF_VERSION
+ */
+#define VERSION_MAJOR(version) (((version) >> 16) & 0xFF)
+/*!
+ * \brief Get NMF minor version corresponding to NMF version number
+ * \ingroup NMF_VERSION
+ */
+#define VERSION_MINOR(version) (((version) >> 8) & 0xFF)
+/*!
+ * \brief Get NMF patch version corresponding to NMF version number
+ * \ingroup NMF_VERSION
+ */
+#define VERSION_PATCH(version) (((version) >> 0) & 0xFF)
+
+#endif /* __INC_NMF_DEF_H */
diff --git a/drivers/staging/nmf-cm/inc/nmf-limits.h b/drivers/staging/nmf-cm/inc/nmf-limits.h
new file mode 100644
index 00000000000..a942e542233
--- /dev/null
+++ b/drivers/staging/nmf-cm/inc/nmf-limits.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Common Nomadik Multiprocessing Framework limits definition
+ *
+ * This file contains the limit definitions used into NMF.
+ *
+ * \warning Don't modify it since it is also hardcoded in tools
+ *
+ * \defgroup NMF_LIMITS NMF limits definition
+ * \ingroup COMMON
+ */
+#ifndef __INC_NMF_LIMITS_H
+#define __INC_NMF_LIMITS_H
+
+/*!
+ * \brief Maximum interface name length
+ *
+ * Define the maximum interface name length allowed by NMF.
+ *
+ * \ingroup NMF_LIMITS
+ */
+#define MAX_INTERFACE_NAME_LENGTH 32
+
+/*!
+ * \brief Maximum interface method name length
+ *
+ * Define the maximum interface method name length allowed by NMF.
+ *
+ * \ingroup NMF_LIMITS
+ */
+#define MAX_INTERFACE_METHOD_NAME_LENGTH 64
+
+/*!
+ * \brief Maximum interface type name length
+ *
+ * Define the maximum interface type name length allowed by NMF.
+ *
+ * \ingroup NMF_LIMITS
+ */
+#define MAX_INTERFACE_TYPE_NAME_LENGTH 128
+
+
+/*!
+ * \brief Maximum template name length
+ *
+ * Define the maximum template name length allowed by NMF.
+ *
+ * \ingroup NMF_LIMITS
+ */
+#define MAX_TEMPLATE_NAME_LENGTH 128
+
+/*!
+ * \brief Maximum component local name length
+ *
+ * Define the maximum component local name length inside a composite component allowed by NMF.
+ *
+ * \ingroup NMF_LIMITS
+ */
+#define MAX_COMPONENT_NAME_LENGTH 32
+
+/*!
+ * \brief Maximum property name length
+ *
+ * Define the maximum property name length allowed by NMF.
+ *
+ * \ingroup NMF_LIMITS
+ */
+#define MAX_PROPERTY_NAME_LENGTH 32
+
+/*!
+ * \brief Maximum property value length
+ *
+ * Define the maximum property value length allowed by NMF.
+ *
+ * \ingroup NMF_LIMITS
+ */
+#define MAX_PROPERTY_VALUE_LENGTH 128
+
+/*!
+ * \brief Maximum attribute name length
+ *
+ * Define the maximum attribute name length allowed by NMF.
+ *
+ * \ingroup NMF_LIMITS
+ */
+#define MAX_ATTRIBUTE_NAME_LENGTH 32
+
+/*!
+ * \brief Maximum fifo size allowed for binding component
+ *
+ * Define the maximum fifo size allowed for binding component allowed by NMF when calling
+ * CM_BindComponentFromHost and CM_BindComponentAsynchronous.
+ *
+ * \ingroup NMF_LIMITS
+ */
+#define MAX_COMMUNICATION_FIFO_SIZE 256
+
+#define MAX_COMPONENT_FILE_PATH_LENGTH 1024
+
+#endif /* __INC_NMF_LIMITS_H */
diff --git a/drivers/staging/nmf-cm/inc/nmf-tracedescription.h b/drivers/staging/nmf-cm/inc/nmf-tracedescription.h
new file mode 100644
index 00000000000..9611803b978
--- /dev/null
+++ b/drivers/staging/nmf-cm/inc/nmf-tracedescription.h
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief NMF xti/stm trace format description
+ *
+ * \defgroup NMF_TRACE_FORMAT NMF xti/stm trace format description
+ *
+ * The NMF trace is output by either xti ip on 8815 or stm ip on 8820 and 8500.
+ * Each type of trace is output on a dedicated channel. Following is a description
+ * of each of this traces.
+ *
+ * Traces have generally a timestamp added by hardware but is not described here.
+ * \ingroup NMF_ABI
+ */
+#ifndef TRACE_FORMAT_H_
+#define TRACE_FORMAT_H_
+
+#include <inc/nmf-limits.h>
+
+/*!
+ * \brief XTI/STM Channel where trace are dumped
+ *
+ * \note This type is only for defining constants, please not reference it.
+ *
+ * \note Ever if this format is able to be generated on same channel, Host EE & CM channel are separated
+ * in order to avoir concurrency and access STM IP without require mutual exclusion.
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+typedef enum {
+ MPC_EE_CHANNEL = 100, //!< MPC EE channel (MPC activity) in 32bits bundle
+ CM_CHANNEL = 101, //!< CM channel (MPC deployment) in 64bits bundle
+ HOST_EE_CHANNEL = 151 //!< Host EE channel (deployment & activity) in 64bits bundle
+} t_nmfTraceChannelDescription;
+
+/*!
+ * \brief Message trace type
+ *
+ * \note This type is only for defining constants, please not reference it.
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+typedef enum {
+ TRACE_TYPE_RESET = 1, //!< Reset trace type
+ TRACE_TYPE_COMPONENT = 2, //!< Component instantiate trace type
+ TRACE_TYPE_BIND = 3, //!< Component bind trace type
+ TRACE_TYPE_METHOD = 4, //!< Component method trace type
+ TRACE_TYPE_ACTIVITY = 5, //!< Activity trace type
+ TRACE_TYPE_PANIC = 6, //!< Panic trace type
+ TRACE_TYPE_COMMUNICATION = 7, //!< Communication trace type
+ TRACE_TYPE_ALLOCATOR = 8, //!< Allocator trace type
+ TRACE_TYPE_ALLOC = 9, //!< Alloc trace type
+ TRACE_TYPE_USER = 10 //!< User trace type
+} t_nmfTraceTypeDescription;
+
+#define TRACE_MAJOR_VERSION 1 //!< Current major trace version number \ingroup NMF_TRACE_FORMAT
+#define TRACE_MINOR_VERSION 2 //!< Current minor trace version number \ingroup NMF_TRACE_FORMAT
+
+/*!
+ * \brief Trace header description.
+ *
+ * \note XTI will add 64bits time-stamp in first field of this structure, but not generated by us !
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+struct t_nmfTraceChannelHeader {
+ // t_uint64 timeStamp;
+ t_uint8 traceType; //!< Trace type
+ t_uint8 reserved;
+ t_uint16 traceSize; //!< Trace size (depending on trace type description)
+};
+
+/*!
+ * \brief Trace header union
+ *
+ * The purpose of this is to optimize header setting in one instruction.
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+typedef union {
+ struct t_nmfTraceChannelHeader s;
+ t_uint32 v;
+} t_nmfTraceChannelHeaderUnion;
+
+
+/*!
+ * \brief Trace reset description
+ *
+ * Inform tools to reset their internal state because network will be dumped new time.
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+struct t_nmfTraceReset {
+ t_nmfTraceChannelHeaderUnion header; //!< Trace header
+
+ t_uint16 minorVersion; //!< NMF trace format minor version
+ t_uint16 majorVersion; //!< NMF trace format major version
+};
+
+/**
+ * \brief Component instantiation trace command description
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+typedef enum {
+ TRACE_COMPONENT_COMMAND_ADD = 0x1,
+ TRACE_COMPONENT_COMMAND_REMOVE = 0x2
+} t_nmfTraceComponentCommandDescription;
+
+
+/*!
+ * \brief Component instantiation trace description
+ *
+ * Component instantiation trace is generated each time an instance of a component is added or removed.
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+struct t_nmfTraceComponent {
+ t_nmfTraceChannelHeaderUnion header; //!< Trace header
+
+ t_uint16 command; //!< See \ref t_nmfTraceComponentCommandDescription
+ t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA, in SMPEE: 0x1
+ t_uint32 componentContext; //!< Component context belonging domain (DSP this or ARM class this)
+ t_uint32 componentUserContext; //!< User friendly component Id belonging the channel (CM handle or ARM class this)
+ t_uint8 componentLocalName[MAX_COMPONENT_NAME_LENGTH]; //!< local name of component as given by user (null terminated)
+ t_uint8 componentTemplateName[MAX_TEMPLATE_NAME_LENGTH];//!< template name of component (null terminated)
+};
+
+/**
+ * \brief Component binding trace command description
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+typedef enum {
+ TRACE_BIND_COMMAND_BIND_SYNCHRONOUS = 0x1,
+ TRACE_BIND_COMMAND_UNBIND_SYNCHRONOUS = 0x2,
+ TRACE_BIND_COMMAND_BIND_ASYNCHRONOUS = 0x3,
+ TRACE_BIND_COMMAND_UNBIND_ASYNCHRONOUS = 0x4
+} t_nmfTraceBindCommandDescription;
+
+/**
+ * \brief Component binding trace description
+ *
+ * \note clientComponentContext & serverComponentContext take value 0xffffffff when client or server are Component Manager.
+ * \note serverComponentContext take value 0x00000000 when binding to void.
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+struct t_nmfTraceBind {
+ t_nmfTraceChannelHeaderUnion header; //!< Trace header
+
+ t_uint16 command; //!< See \ref t_nmfTraceBindCommandDescription
+ t_uint16 reserved;
+ t_uint16 clientDomainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA, in SMPEE: 0x1
+ t_uint16 serverDomainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA, in SMPEE: 0x1
+ t_uint32 clientComponentContext; //!< Component context belonging domain (DSP this or ARM class this)
+ t_uint32 serverComponentContext; //!< Component context belonging domain (DSP this or ARM class this)
+ t_uint8 requiredItfName[MAX_INTERFACE_NAME_LENGTH]; //!< Required interface name
+ t_uint8 providedItfName[MAX_INTERFACE_NAME_LENGTH]; //!< Provided interface name
+};
+
+/*!
+ * \brief Component interface method name trace description
+ *
+ * For each methods of each interfaces provided by a component, one such trace is dumped.
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+struct t_nmfTraceMethod {
+ t_nmfTraceChannelHeaderUnion header; //!< Trace header
+
+ t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA, in SMPEE: 0x1
+ t_uint16 reserved;
+ t_uint32 methodId; //!< Unique Method Id belonging the component
+ t_uint32 componentContext; //!< Component context belonging domain (DSP this or ARM class this)
+ t_uint8 methodName[MAX_INTERFACE_METHOD_NAME_LENGTH]; //!< Symbolic method name
+};
+
+/**
+ * \brief Activity trace trace command description
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+typedef enum {
+ TRACE_ACTIVITY_START = 0x1, //!< Start method
+ TRACE_ACTIVITY_END = 0x2, //!< End method
+ TRACE_ACTIVITY_POST = 0x3, //!< Post method
+ TRACE_ACTIVITY_CALL = 0x4, //!< Synchronous call method
+ TRACE_ACTIVITY_RETURN = 0x5 //!< Synchronous return method
+} t_nmfTraceActivityCommandDescription;
+
+/*!
+ * \brief Execution Engine scheduling activity trace description
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+struct t_nmfTraceActivity {
+ t_nmfTraceChannelHeaderUnion header; //!< Trace header
+
+ t_uint16 command; //!< See \ref t_nmfTraceActivityCommandDescription
+ t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA
+ t_uint32 componentContext; //!< Unique component Id (Component Handle for CM, Component this for EE)
+ t_uint32 methodId; //!< Unique Method Id belonging the component
+};
+
+/**
+ * \brief Component instantiation trace command description
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+typedef enum {
+ TRACE_COMMUNICATION_COMMAND_SEND = 0x1,
+ TRACE_COMMUNICATION_COMMAND_RECEIVE = 0x2
+} t_nmfTraceCommunicationCommandDescription;
+
+/**
+ * \brief Inter-processor communication signaling trace description
+ *
+ * Use when trigging interrupt through core.
+ *
+ * \note Not used on SMP EE
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+struct t_nmfTraceCommunication {
+ t_nmfTraceChannelHeaderUnion header; //!< Trace header
+
+ t_uint16 command; //!< See \ref t_nmfTraceCommunicationCommandDescription
+ t_uint16 reserved_0;
+ t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA
+ t_uint16 remoteDomainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA
+};
+
+/**
+ * \brief Component instantiation trace command description
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+typedef enum {
+ TRACE_ALLOCATOR_COMMAND_CREATE = 0x1,
+ TRACE_ALLOCATOR_COMMAND_DESTROY = 0x2
+} t_nmfTraceAllocatorCommandDescription;
+
+/*!
+ * \brief Panic trace description
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+struct t_nmfTraceAllocator {
+ t_nmfTraceChannelHeaderUnion header; //!< Trace header
+
+ t_uint16 command; //!< See \ref t_nmfTraceAllocatorCommandDescription
+ t_uint16 allocId; //!< Memory allocator ID
+ t_uint32 size; //!< Memory allocator size
+ t_uint8 name[32]; //!< Memory allocator name
+};
+
+/**
+ * \brief Component instantiation trace command description
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+typedef enum {
+ TRACE_ALLOC_COMMAND_ALLOC = 0x1,
+ TRACE_ALLOC_COMMAND_FREE = 0x2
+} t_nmfTraceAllocCommandDescription;
+
+/*!
+ * \brief Panic trace description
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+struct t_nmfTraceAlloc {
+ t_nmfTraceChannelHeaderUnion header; //!< Trace header
+
+ t_uint16 command; //!< See \ref t_nmfTraceAllocatorCommandDescription
+ t_uint16 allocId; //!< Memory allocator ID
+ t_uint32 offset; //!< Memory chunk offet
+ t_uint32 size; //!< Memory chunk size
+};
+
+/*!
+ * \brief Panic trace description
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+struct t_nmfTracePanic {
+ t_nmfTraceChannelHeaderUnion header; //!< Trace header
+
+ t_uint16 reason; //!< See \ref t_panic_reason for description
+ t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA
+ t_uint32 componentContext; //!< Unique component Id (Component Handle for CM, Component this for EE)
+ t_uint32 information1; //!< Reason dependent information 1st
+ t_uint32 information2; //!< Reason dependent information 2nd
+};
+
+/*!
+ * \brief User trace description
+ *
+ * \ingroup NMF_TRACE_FORMAT
+ */
+struct t_nmfTraceUser {
+ t_nmfTraceChannelHeaderUnion header; //!< Trace header
+
+ t_uint32 key; //!< User key
+ t_uint16 domainId; //!< In CM: 0x01:Arm | 0x02:SAA | 0x03:SVA | 0x04:SIA
+ t_uint16 reserved;
+ t_uint32 componentContext; //!< Unique component Id (Component Handle for CM, Component this for EE)
+ t_uint32 callerAddress; //!< Unique code address belonging the component
+};
+
+/*
+struct t_nmfTracePower{
+ struct t_nmfTraceChannelHeader header;
+};
+*/
+
+#endif /* TRACE_FORMAT_H_ */
diff --git a/drivers/staging/nmf-cm/inc/nmf_type.idt b/drivers/staging/nmf-cm/inc/nmf_type.idt
new file mode 100644
index 00000000000..e8cc4e09946
--- /dev/null
+++ b/drivers/staging/nmf-cm/inc/nmf_type.idt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+#ifndef NMF_TYPE_H_
+#define NMF_TYPE_H_
+
+/*!
+ * \defgroup NMF_COMMON_TYPE NMF Common Type
+ * \ingroup COMMON
+ */
+
+/*!
+ * \brief Error type returned by NMF API routines
+ *
+ * Possible value describe by \ref t_nmf_errorDescription
+ *
+ * \ingroup NMF_COMMON_TYPE
+ */
+typedef t_sint8 t_nmf_error;
+
+/*!
+ * \brief Error type values
+ *
+ * \ingroup NMF_COMMON_TYPE
+ */
+typedef enum {
+ NMF_OK = 0, //!< No error
+ NMF_INVALID_PARAMETER = -2, //!< Invalid parameter
+ NMF_NO_MORE_MEMORY = -30, //!< Out of memory
+ NMF_INTERFACE_NOT_BINDED = -59, //!< Try to unbind not binded interface
+ NMF_INTERFACE_ALREADY_BINDED = -60, //!< Try to bind already binded interface
+ NMF_NO_SUCH_REQUIRED_INTERFACE = -63, //!< Interface name not required by a component
+ NMF_NO_SUCH_PROVIDED_INTERFACE = -64, //!< Interface name not provided by a component
+ NMF_COMPONENT_NOT_STOPPED = -80, //!< Component must be stopped to perform operation
+ NMF_INVALID_COMPONENT_STATE_TRANSITION = -81, //!< Invalid component state transition caused by user action
+ NMF_NO_SUCH_PROPERTY = -87, //!< Property name doesn't exported by the underlying component
+ NMF_NO_SUCH_ATTRIBUTE = -88, //!< Attribute name not shared (exported) by a component
+ NMF_NO_MESSAGE = -103, //!< No message available
+ NMF_FLUSH_MESSAGE = -106, //!< Message send after call to EE_FlushChannel()
+ NMF_INTEGRATION_ERROR0 = -112, //!< OS dependent integration Error [-112 -> -121]
+ NMF_INTEGRATION_ERROR1 = -113, //!< OS dependent integration Error [-112 -> -121]
+ NMF_INTEGRATION_ERROR2 = -114, //!< OS dependent integration Error [-112 -> -121]
+ NMF_INTEGRATION_ERROR3 = -115, //!< OS dependent integration Error [-112 -> -121]
+ NMF_INTEGRATION_ERROR4 = -116, //!< OS dependent integration Error [-112 -> -121]
+ NMF_INTEGRATION_ERROR5 = -117, //!< OS dependent integration Error [-112 -> -121]
+ NMF_INTEGRATION_ERROR6 = -118, //!< OS dependent integration Error [-112 -> -121]
+ NMF_INTEGRATION_ERROR7 = -119, //!< OS dependent integration Error [-112 -> -121]
+ NMF_INTEGRATION_ERROR8 = -120, //!< OS dependent integration Error [-112 -> -121]
+ NMF_INTEGRATION_ERROR9 = -121 //!< OS dependent integration Error [-112 -> -121]
+} t_nmf_errorDescription;
+
+/*!
+ * \brief Define t_nmf_channel type that identify a communication channel between nmf and user.
+ *
+ * \ingroup NMF_COMMON_TYPE
+ */
+typedef t_uint32 t_nmf_channel;
+
+#endif /* NMF_TYPE_H_ */
diff --git a/drivers/staging/nmf-cm/inc/type.h b/drivers/staging/nmf-cm/inc/type.h
new file mode 100644
index 00000000000..d6eafe9aea5
--- /dev/null
+++ b/drivers/staging/nmf-cm/inc/type.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/* inc/type.h - Programming Model.
+ *
+ * Copyright (c) 2006, 2007, 2008 STMicroelectronics.
+ *
+ * Reproduction and Communication of this document is strictly prohibited
+ * unless specifically authorized in writing by STMicroelectronics.
+ *
+ * Written by NMF team.
+ */
+#ifndef _NMF_TYPE_H_
+#define _NMF_TYPE_H_
+
+#include <inc/typedef.h>
+
+PUBLIC IMPORT_SHARED void NMF_LOG(const char* fmt, ...);
+PUBLIC IMPORT_SHARED void NMF_PANIC(const char* fmt, ...);
+
+#define NMF_ASSERT(cond) do { if(!(cond)) NMF_PANIC("NMF_ASSERT at %s:%d\n", (int)__FILE__, (int)__LINE__); } while(0)
+
+#ifndef EXPORT_NMF_COMPONENT
+ #define EXPORT_NMF_COMPONENT EXPORT_SHARED
+#endif
+
+#ifndef IMPORT_NMF_COMPONENT
+ #define IMPORT_NMF_COMPONENT IMPORT_SHARED
+#endif
+
+#endif /* _NMF_TYPE_H_ */
diff --git a/drivers/staging/nmf-cm/inc/typedef.h b/drivers/staging/nmf-cm/inc/typedef.h
new file mode 100644
index 00000000000..97af9dec2c2
--- /dev/null
+++ b/drivers/staging/nmf-cm/inc/typedef.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \defgroup COMMON Common types and definitions
+ *
+ * \defgroup NMF_COMMON NMF common definition
+ * \ingroup COMMON
+ *
+ * \defgroup NMF_ABI NMF ABI specification
+ * \warning This page is not for multimedia developers !
+ */
+/*!
+ * \brief Primitive Type Definition
+ *
+ * \defgroup NMF_PRIMITIVE_TYPE Primitive type definition
+ * \ingroup COMMON
+ */
+
+#ifndef NMF_TYPEDEF_H_
+#define NMF_TYPEDEF_H_
+
+#undef PRIVATE
+#define PRIVATE static //!< Private macro declaration \ingroup NMF_PRIMITIVE_TYPE
+
+#undef PUBLIC
+#ifdef __cplusplus
+#define PUBLIC extern "C" //!< Public macro declaration \ingroup NMF_PRIMITIVE_TYPE
+#else
+#define PUBLIC extern //!< Public macro declaration \ingroup NMF_PRIMITIVE_TYPE
+#endif
+
+#if defined(__SYMBIAN32__)
+/*!
+ * \brief Declared IMPORT_SHARED to allow dll/shared library creation
+ *
+ * \note Value depend on OS.
+ *
+ * \ingroup NMF_PRIMITIVE_TYPE
+ */
+ #ifndef IMPORT_SHARED
+ #define IMPORT_SHARED IMPORT_C
+ #endif
+/*!
+ * \brief Declared EXPORT_SHARED to allow dll/shared library creation
+ *
+ * \note Value depend on OS.
+ *
+ * \ingroup NMF_PRIMITIVE_TYPE
+ */
+ #ifndef EXPORT_SHARED
+ #define EXPORT_SHARED EXPORT_C
+ #endif
+#elif defined(LINUX)
+ #ifndef IMPORT_SHARED
+ #define IMPORT_SHARED
+ #endif
+ #ifndef EXPORT_SHARED
+ #define EXPORT_SHARED __attribute__ ((visibility ("default")))
+ #endif
+#else
+ #ifndef IMPORT_SHARED
+ #define IMPORT_SHARED
+ #endif
+
+ #ifndef EXPORT_SHARED
+ #define EXPORT_SHARED
+ #endif
+#endif
+
+/*
+ * Definition of type that are used by interface.
+ */
+
+typedef unsigned int t_uword;
+typedef signed int t_sword;
+
+#ifdef __flexcc2__
+
+typedef unsigned char t_bool;
+
+#ifdef __mode16__
+
+typedef signed char t_sint8;
+typedef signed int t_sint16;
+typedef signed long t_sint24;
+typedef signed long t_sint32;
+typedef signed long long t_sint40;
+// bigger type are not handle on this mode
+
+typedef unsigned char t_uint8;
+typedef unsigned int t_uint16;
+typedef unsigned long t_uint24;
+typedef unsigned long t_uint32;
+typedef unsigned long long t_uint40;
+// bigger type are not handle on this mode
+
+// shared addr type definition
+//typedef __SHARED16 t_uint16 * t_shared_addr;
+typedef void * t_shared_field;
+
+#else /* __mode16__ -> __mode24__ */
+
+typedef signed char t_sint8;
+typedef signed short t_sint16;
+typedef signed int t_sint24;
+typedef signed long t_sint32;
+typedef signed long t_sint40;
+typedef signed long t_sint48;
+typedef signed long long t_sint56;
+
+typedef unsigned char t_uint8;
+typedef unsigned short t_uint16;
+typedef unsigned int t_uint24;
+typedef unsigned long t_uint32;
+typedef unsigned long t_uint40;
+typedef unsigned long t_uint48;
+typedef unsigned long long t_uint56;
+
+// shared addr type definition
+//typedef __SHARED16 t_uint16 * t_shared_addr;
+typedef t_uint24 t_shared_field;
+
+#endif /* MMDSP mode24 */
+
+// shared register (ARM world) type definition
+#if 0
+typedef struct {
+ t_uint16 lsb;
+ t_uint16 msb;
+} t_shared_reg;
+#endif
+typedef t_uint32 t_shared_reg;
+
+typedef t_uint32 t_physical_address;
+
+#include <stwdsp.h>
+
+#else /* __flexcc2__ -> RISC 32 Bits */
+
+#ifndef _HCL_DEFS_H
+typedef unsigned char t_bool; //!< Boolean primitive type \ingroup NMF_PRIMITIVE_TYPE
+
+typedef unsigned char t_uint8; //!< Unsigned 8 bits primitive type \ingroup NMF_PRIMITIVE_TYPE
+typedef signed char t_sint8; //!< Signed 8 bits primitive type \ingroup NMF_PRIMITIVE_TYPE
+typedef unsigned short t_uint16; //!< Unsigned 16 bits primitive type \ingroup NMF_PRIMITIVE_TYPE
+typedef signed short t_sint16; //!< Signed 16 bits primitive type \ingroup NMF_PRIMITIVE_TYPE
+typedef unsigned long t_uint32; //!< Unsigned 32 bits primitive type \ingroup NMF_PRIMITIVE_TYPE
+typedef signed long t_sint32; //!< Signed 32 bits primitive type \ingroup NMF_PRIMITIVE_TYPE
+typedef unsigned long long t_uint64; //!< Unsigned 64 bits primitive type \ingroup NMF_PRIMITIVE_TYPE
+typedef signed long long t_sint64; //!< Signed 64 bits primitive type \ingroup NMF_PRIMITIVE_TYPE
+
+typedef t_uint32 t_physical_address;
+#endif /* _HCL_DEFS_H */
+
+typedef unsigned long t_uint24;
+typedef signed long t_sint24;
+typedef unsigned long long t_uint48;
+typedef signed long long t_sint48;
+
+// shared addr type definition
+typedef t_uint32 t_shared_addr;
+
+// shared register (ARM world) type definition
+typedef t_uint32 t_shared_reg;
+typedef t_uint32 t_shared_field;
+
+#endif /* RISC 32 Bits */
+
+/*
+ * Define boolean type
+ */
+#undef FALSE
+#define FALSE 0 //!< Boolean FALSE value
+#undef TRUE
+#define TRUE 1 //!< Boolean TRUE value
+
+#ifndef NULL
+ #if defined __flexcc2__ || defined __SYMBIAN32__
+ #define NULL (0x0) //!< Null type \ingroup NMF_PRIMITIVE_TYPE
+ #else
+ #define NULL ((void*)0x0) //!< Null type \ingroup NMF_PRIMITIVE_TYPE
+ #endif
+#endif
+
+typedef t_uint32 t_nmf_component_handle;
+
+#endif /* NMF_TYPEDEF_H_ */
diff --git a/drivers/staging/nmf-cm/nmf/inc/channel_type.h b/drivers/staging/nmf-cm/nmf/inc/channel_type.h
new file mode 100644
index 00000000000..7d439ebf0cf
--- /dev/null
+++ b/drivers/staging/nmf-cm/nmf/inc/channel_type.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Common Nomadik Multiprocessing Framework type definition
+ *
+ * This file contains the shared between cm and ee type definitions used into NMF for callback.
+ */
+/*!
+ * \defgroup _t_nmf_channel_flag t_nmf_channel_flag
+ * \ingroup NMF_COMMON
+ */
+
+#ifndef __INC_CHANNEL_TYPE_H
+#define __INC_CHANNEL_TYPE_H
+
+#include <inc/typedef.h>
+#include <inc/nmf_type.idt>
+
+/*!
+ * \brief Define t_nmf_channel_flag type that allow to control if/how a new communication channel is created.
+ * \ingroup _t_nmf_channel_flag
+ */
+typedef t_uint32 t_nmf_channel_flag;
+
+#define NMF_CHANNEL_SHARED ((t_nmf_channel_flag)0) //!< \ingroup _t_nmf_channel_flag
+#define NMF_CHANNEL_PRIVATE ((t_nmf_channel_flag)1) //!< \ingroup _t_nmf_channel_flag
+
+/*!
+ * \brief Define t_nmf_virtualInterruptHandler function type to allow to dispatch virtual interrupt
+ * \ingroup VIRTUAL_INTERRUPT
+ */
+typedef void (*t_nmf_virtualInterruptHandler)(void *interruptContext);
+
+#endif
+
diff --git a/drivers/staging/nmf-cm/nmf/inc/component_type.h b/drivers/staging/nmf-cm/nmf/inc/component_type.h
new file mode 100644
index 00000000000..08b63b21225
--- /dev/null
+++ b/drivers/staging/nmf-cm/nmf/inc/component_type.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Common Nomadik Multiprocessing Framework type definition
+ *
+ * This file contains the shared between cm and ee type definitions used into NMF for callback.
+ */
+#ifndef __INC_COMPONENT_TYPE_H
+#define __INC_COMPONENT_TYPE_H
+
+#include <inc/typedef.h>
+
+/*!
+ * \brief Identifier of a component instance handle
+ *
+ * \ingroup NMF_COMMON
+ */
+typedef t_nmf_component_handle t_cm_instance_handle;
+
+#endif
+
diff --git a/drivers/staging/nmf-cm/nmf/inc/service_type.h b/drivers/staging/nmf-cm/nmf/inc/service_type.h
new file mode 100644
index 00000000000..3e5473338ee
--- /dev/null
+++ b/drivers/staging/nmf-cm/nmf/inc/service_type.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Service type and data used through service callback.
+ * \defgroup NMF_SERVICE NMF Service Callback types and data definition
+ * \ingroup NMF_COMMON
+ */
+#ifndef SERVICE_TYPE_H
+#define SERVICE_TYPE_H
+
+#include <ee/api/panic.idt>
+#include <nmf/inc/component_type.h>
+#include <share/inc/nmf.h>
+
+/*!
+ * \brief Define t_nmf_service_type type
+ *
+ * It gives the type of service message passed to service callback.
+ * \ingroup NMF_SERVICE
+ */
+typedef t_uint32 t_nmf_service_type;
+#define NMF_SERVICE_PANIC ((t_nmf_service_type)0) //!< \ingroup NMF_SERVICE
+#define NMF_SERVICE_SHUTDOWN ((t_nmf_service_type)1) //!< \ingroup NMF_SERVICE
+
+/*
+ * The following structured define each data structure used for each service type
+ * and given to each serviceCallback
+ */
+
+/*!
+ * \brief Define t_nmf_panic_data type
+ *
+ * This is the data structure passed to the service callback (inside \ref t_nmf_service_data)
+ * when t_nmf_service_type == NMF_SERVICE_PANIC
+ * \ingroup NMF_SERVICE
+ */
+typedef struct {
+ t_panic_reason panicReason; //!< The reason of the panic
+ t_panic_source panicSource; //!< THe source of the panic (One of the MPC or the ARM-EE)
+ /*!
+ * union of structures containing specific info, depending on the panicSource
+ */
+ union {
+ struct {
+ t_nmf_core_id coreid; //!< The coreId of the MPC on which the panic occured
+ t_cm_instance_handle faultingComponent; //!< The faulting component handle
+ t_uint32 panicInfo1; //!< First info (depend on \ref panicReason)
+ t_uint32 panicInfo2; //!< Second info (depend on \ref panicReason)
+ } mpc; //!< member to use if panicSource == MPC_EE
+ struct {
+ void * faultingComponent; //!< The faulting component handle
+ t_uint32 panicInfo1; //!< First info (depend on \ref panicReason)
+ t_uint32 panicInfo2; //!< Second info (depend on \ref panicReason)
+ } host; //!< member to use if panicSource == HOST_EE
+ } info; //!< union of structures containing specific info, depending on the panicSource
+} t_nmf_panic_data;
+
+/*!
+ * \brief Define t_nmf_shutdown_data type
+ *
+ * This is the data structure passed to the service callback (inside \ref t_nmf_service_data)
+ * when t_nmf_service_type == NMF_SERVICE_SHUTDOWN
+ * \ingroup NMF_SERVICE
+ */
+typedef struct {
+ t_nmf_core_id coreid; //!< The coreId of the MPC on which has been shutdown
+} t_nmf_shutdown_data;
+
+/*!
+ * \brief Define t_nmf_service_data type
+ *
+ * It gives the data passed to the service callbacks for each service type
+ * This is an union whose member to use is defined by the given \ref t_nmf_service_type
+ *
+ * \ingroup NMF_SERVICE
+ */
+typedef union {
+ t_nmf_panic_data panic; //!< if service_type == NMF_SERVICE_PANIC
+ t_nmf_shutdown_data shutdown; //!< if service_type == NMF_SERVICE_SHUTDOWN
+} t_nmf_service_data;
+
+/*!
+ * \brief Define t_nmf_serviceCallback function type to allow to dispatch service message to user.
+ * \ingroup NMF_SERVICE
+ */
+typedef void (*t_nmf_serviceCallback)(void *contextHandler, t_nmf_service_type serviceType, t_nmf_service_data *serviceData);
+
+#endif //SERVICE_TYPE_H
diff --git a/drivers/staging/nmf-cm/osal-kernel.c b/drivers/staging/nmf-cm/osal-kernel.c
new file mode 100644
index 00000000000..fa2bf219811
--- /dev/null
+++ b/drivers/staging/nmf-cm/osal-kernel.c
@@ -0,0 +1,1148 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+/** \file osal-kernel.c
+ *
+ * Implements NMF OSAL for Linux kernel-space environment
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kthread.h>
+#include <linux/mm.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <mach/prcmu-fw-api.h>
+
+#include <trace/stm.h>
+
+#include <cm/engine/configuration/inc/configuration_status.h>
+
+#include "cmioctl.h"
+#include "osal-kernel.h"
+#include "cm_service.h"
+#include "cmld.h"
+
+__iomem void *prcmu_base = NULL;
+__iomem void *prcmu_tcdm_base = NULL;
+
+/* DSP Load Monitoring */
+#define FULL_OPP 100
+#define HALF_OPP 50
+static unsigned long running_dsp = 0;
+static unsigned int dspLoadMonitorPeriod = 1000;
+module_param(dspLoadMonitorPeriod, uint, S_IWUSR|S_IRUGO);
+MODULE_PARM_DESC(dspLoadMonitorPeriod, "Period of the DSP-Load monitoring in ms");
+static unsigned int dspLoadHighThreshold = 85;
+module_param(dspLoadHighThreshold, uint, S_IWUSR|S_IRUGO);
+MODULE_PARM_DESC(dspLoadHighThreshold, "Threshold above which 100 APE OPP is requested");
+static unsigned int dspLoadLowThreshold = 35;
+module_param(dspLoadLowThreshold, uint, S_IWUSR|S_IRUGO);
+MODULE_PARM_DESC(dspLoadLowThreshold, "Threshold below which 100 APE OPP request is removed");
+
+/** \defgroup ENVIRONMENT_INITIALIZATION Environment initialization
+ * Includes functions that initialize the Linux OSAL itself plus functions that
+ * are responsible to factor configuration objects needed to initialize Component Manager library
+ */
+
+/** \defgroup OSAL_IMPLEMENTATION OSAL implementation
+ * Linux-specific implementation of the Component Manager OSAL interface.
+ */
+
+
+/** \ingroup ENVIRONMENT_INITIALIZATION
+ * Remaps IO, SDRAM and ESRAM regions
+ *
+ * \osalEnvironment NMF-Osal descriptor
+ * \return POSIX error code
+ */
+int remapRegions(void)
+{
+ unsigned i;
+
+ /* Remap DSP base areas */
+ for (i=0; i<NB_MPC; i++) {
+ osalEnv.mpc[i].baseL = ioremap_nocache((int)osalEnv.mpc[i].baseP, (1*ONE_MB));
+ if(osalEnv.mpc[i].baseL == NULL){
+ pr_err("%s: could not remap base address for %s\n", __func__, osalEnv.mpc[i].name);
+ return -ENOMEM;
+ }
+ }
+
+ /* Remap hardware semaphores */
+ osalEnv.hwsem_base = ioremap_nocache(HWSEM_BASE, (4*ONE_KB));
+ if(osalEnv.hwsem_base == NULL){
+ pr_err("%s: could not remap HWSEM Base\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* Remap _all_ ESRAM banks */
+ osalEnv.esram_base = ioremap_nocache(ESRAM_BASE, cfgESRAMSize*ONE_KB);
+ if(osalEnv.esram_base == NULL){
+ pr_err("%s: could not remap ESRAM Base\n", __func__);
+ return -ENOMEM;
+ }
+ memset(osalEnv.esram_base, 0x0, cfgESRAMSize*ONE_KB);
+
+
+ /* Allocate code and data sections for MPC (SVA, SIA) */
+ for (i=0; i<NB_MPC; i++) {
+ /* Allocate MPC SDRAM code area */
+ struct hwmem_mem_chunk mem_chunk;
+ size_t mem_chunk_length;
+ osalEnv.mpc[i].hwmemCode = hwmem_alloc(osalEnv.mpc[i].sdramCodeSize,
+ //HWMEM_ALLOC_HINT_CACHE_WB,
+ HWMEM_ALLOC_HINT_WRITE_COMBINE | HWMEM_ALLOC_HINT_UNCACHED,
+ HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE,
+ HWMEM_MEM_CONTIGUOUS_SYS);
+ if (IS_ERR(osalEnv.mpc[i].hwmemCode)) {
+ int err = PTR_ERR(osalEnv.mpc[i].hwmemCode);
+ osalEnv.mpc[i].hwmemCode = NULL;
+ pr_err("%s: could not allocate SDRAM Code for %s\n",
+ __func__, osalEnv.mpc[i].name);
+ return err;
+ }
+ osalEnv.mpc[i].sdramCodeL = hwmem_kmap(osalEnv.mpc[i].hwmemCode);
+ if (IS_ERR(osalEnv.mpc[i].sdramCodeL)) {
+ int err = PTR_ERR(osalEnv.mpc[i].sdramCodeL);
+ osalEnv.mpc[i].sdramCodeL = NULL;
+ pr_err("%s: could not map SDRAM Code for %s\n", __func__, osalEnv.mpc[i].name);
+ return err;
+ }
+ mem_chunk_length = 1;
+ (void)hwmem_pin(osalEnv.mpc[i].hwmemCode, &mem_chunk, &mem_chunk_length);
+ osalEnv.mpc[i].sdramCodeP = mem_chunk.paddr;
+ /* Allocate MPC SDRAM data area by taking care wether the data are shared or not */
+ if (osalEnv.mpc[i].sdramDataSize == 0) {
+ /* size of 0 means shared data segment, reuse the same param as for first MPC */
+ osalEnv.mpc[i].sdramDataP = osalEnv.mpc[0].sdramDataP;
+ osalEnv.mpc[i].sdramDataL = osalEnv.mpc[0].sdramDataL;
+ osalEnv.mpc[i].sdramDataSize = osalEnv.mpc[0].sdramDataSize;
+ } else {
+ /* If we do not share the data segment or if this is the first MPC */
+ osalEnv.mpc[i].hwmemData = hwmem_alloc(osalEnv.mpc[i].sdramDataSize,
+ HWMEM_ALLOC_HINT_WRITE_COMBINE | HWMEM_ALLOC_HINT_UNCACHED,
+ HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE,
+ HWMEM_MEM_CONTIGUOUS_SYS);
+ if (IS_ERR(osalEnv.mpc[i].hwmemData)) {
+ int err = PTR_ERR(osalEnv.mpc[i].hwmemData);
+ osalEnv.mpc[i].hwmemData = NULL;
+ pr_err("%s: could not allocate SDRAM Data for %s\n",
+ __func__, osalEnv.mpc[i].name);
+ return err;
+ }
+ mem_chunk_length = 1;
+ (void)hwmem_pin(osalEnv.mpc[i].hwmemData,
+ &mem_chunk, &mem_chunk_length);
+ osalEnv.mpc[i].sdramDataP = mem_chunk.paddr;
+ osalEnv.mpc[i].sdramDataL = hwmem_kmap(osalEnv.mpc[i].hwmemData);
+ if (IS_ERR(osalEnv.mpc[i].sdramDataL)) {
+ int err = PTR_ERR(osalEnv.mpc[i].sdramDataL);
+ osalEnv.mpc[i].sdramDataL = NULL;
+ pr_err("%s: could not map SDRAM Data for %s\n",
+ __func__, osalEnv.mpc[i].name);
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/** \ingroup ENVIRONMENT_INITIALIZATION
+ * Unmaps IO, SDRAM and ESRAM regions
+ *
+ * \return POSIX error code
+ */
+void unmapRegions(void)
+{
+ unsigned i;
+
+ /* Release SVA, SIA, Hardware sempahores and embedded SRAM mappings */
+ for (i=0; i<NB_MPC; i++) {
+ if(osalEnv.mpc[i].baseL != NULL)
+ iounmap(osalEnv.mpc[i].baseL);
+ }
+
+ if(osalEnv.hwsem_base != NULL)
+ iounmap(osalEnv.hwsem_base);
+
+ if(osalEnv.esram_base != NULL)
+ iounmap(osalEnv.esram_base);
+
+ /*
+ * Free SVA and SIA code and data sections or release their mappings
+ * according on how memory allocations has been achieved
+ */
+ for (i=0; i<NB_MPC; i++) {
+ if (osalEnv.mpc[i].sdramCodeL != NULL) {
+ hwmem_unpin(osalEnv.mpc[i].hwmemCode);
+ hwmem_kunmap(osalEnv.mpc[i].hwmemCode);
+ if (osalEnv.mpc[i].hwmemCode != NULL)
+ hwmem_release(osalEnv.mpc[i].hwmemCode);
+ }
+
+ /* If data segment is shared, we must free only the first data segment */
+ if (((i == 0) || (osalEnv.mpc[i].sdramDataL != osalEnv.mpc[0].sdramDataL))
+ && (osalEnv.mpc[i].sdramDataL != NULL)) {
+ hwmem_unpin(osalEnv.mpc[i].hwmemData);
+ hwmem_kunmap(osalEnv.mpc[i].hwmemData);
+ if (osalEnv.mpc[i].hwmemData != NULL)
+ hwmem_release(osalEnv.mpc[i].hwmemData);
+ }
+ }
+}
+
+
+/** \ingroup ENVIRONMENT_INITIALIZATION
+ * Fills a t_nmf_hw_mapping_desc object
+ *
+ * \param nmfHwMappingDesc Pointer to a t_nmf_hw_mapping_desc object
+ * \return POSIX error code
+ */
+int getNmfHwMappingDesc(t_nmf_hw_mapping_desc* nmfHwMappingDesc)
+{
+
+ if (nmfHwMappingDesc == NULL)
+ return -ENXIO;
+
+ nmfHwMappingDesc->esramDesc.systemAddr.physical = ESRAM_BASE;
+ nmfHwMappingDesc->esramDesc.systemAddr.logical = (t_cm_logical_address)osalEnv.esram_base;
+ nmfHwMappingDesc->esramDesc.size = cfgESRAMSize*ONE_KB;
+
+ nmfHwMappingDesc->hwSemaphoresMappingBaseAddr.physical = HWSEM_BASE;
+ nmfHwMappingDesc->hwSemaphoresMappingBaseAddr.logical = (t_cm_logical_address)osalEnv.hwsem_base;
+
+ return 0;
+}
+
+/** \ingroup ENVIRONMENT_INITIALIZATION
+ * Fills a t_cm_system_address object
+ *
+ * \param mpcSystemAddress Pointer to a t_cm_system_address object
+ * \return POSIX error code
+ */
+void getMpcSystemAddress(unsigned i, t_cm_system_address* mpcSystemAddress)
+{
+ mpcSystemAddress->physical = (t_cm_physical_address)osalEnv.mpc[i].baseP;
+ mpcSystemAddress->logical = (t_cm_logical_address)osalEnv.mpc[i].baseL;
+}
+
+
+/** \ingroup ENVIRONMENT_INITIALIZATION
+ * Fills t_nmf_memory_segment objects for MPC code and data segments
+ *
+ * \param i Index of the MPC to initialize
+ * \param codeSegment Pointer to a t_nmf_memory_segment (code segment)
+ * \param dataSegment Pointer to a t_nmf_memory_segment (data segment)
+ * \return Always 0
+ */
+void getMpcSdramSegments(unsigned i, t_nmf_memory_segment* codeSegment, t_nmf_memory_segment* dataSegment)
+{
+ codeSegment->systemAddr.logical = (t_cm_logical_address)osalEnv.mpc[i].sdramCodeL;
+ codeSegment->systemAddr.physical = osalEnv.mpc[i].sdramCodeP;
+ codeSegment->size = osalEnv.mpc[i].sdramCodeSize;
+
+ dataSegment->systemAddr.logical = (t_cm_logical_address)osalEnv.mpc[i].sdramDataL;
+ dataSegment->systemAddr.physical = osalEnv.mpc[i].sdramDataP;
+ dataSegment->size = osalEnv.mpc[i].sdramDataSize;
+}
+
+#ifdef CM_DEBUG_ALLOC
+#include <linux/kallsyms.h>
+struct cm_alloc cm_alloc;
+
+/**
+ * These routines initializes the structures used to trace all alloc/free.
+ * These are used in debug mode to track all memory leak about the allocations
+ * done through the OSAL.
+ */
+void init_debug_alloc(void)
+{
+ INIT_LIST_HEAD(&cm_alloc.chain);
+ spin_lock_init(&cm_alloc.lock);
+}
+
+void cleanup_debug_alloc(void)
+{
+ struct cm_alloc_elem *entry, *next;
+ char buffer[128];
+
+ list_for_each_entry_safe(entry, next, &cm_alloc.chain, elem) {
+ sprint_symbol(buffer, (int)entry->caller);
+ pr_err("/!\\ ALLOC(size=%d) not freed from: 0x%p (%s)\n",
+ entry->size, entry->caller, buffer);
+ list_del(&entry->elem);
+ if ((void*)entry >= (void*)VMALLOC_START
+ && (void*)entry < (void*)VMALLOC_END)
+ vfree(entry);
+ else
+ kfree(entry);
+ }
+}
+
+void dump_debug_alloc(void)
+{
+ struct cm_alloc_elem *entry, *next;
+ char buffer[128];
+
+ pr_err("Current allocated memory:\n");
+ list_for_each_entry_safe(entry, next, &cm_alloc.chain, elem) {
+ sprint_symbol(buffer, (int)entry->caller);
+ pr_err("=> Alloc of size=%d from: 0x%p (%s)\n",
+ entry->size, entry->caller, buffer);
+ }
+}
+#endif
+
+
+/** \ingroup OSAL_IMPLEMENTATION
+ * Called by CM_ProcessMpcEvent in interrupt/tasklet context. Schedules the DFC.
+ * Enqueues the new event in the process' message queue.
+ *
+ * \note This is _not_ called in response to internal events such as in
+ * response to a CM_InstantiateComponent. It is called when user-defined
+ * functions need to be called in skeletons. This behavior is different
+ * from 0.8.1 version.
+ */
+void OSAL_PostDfc(t_nmf_mpc2host_handle upLayerTHIS, t_uint32 methodIndex, t_event_params_handle ptr, t_uint32 size)
+{
+ /* skelwrapper has been created in CM_SYSCALL_BindComponentToCMCore and conveys per-process private data */
+ t_skelwrapper* skelwrapper = (t_skelwrapper*)upLayerTHIS;
+ struct osal_msg* message;
+
+ /* If the clannel has been closed, no more reader exists
+ => discard the message */
+ if (skelwrapper->channelPriv->state == CHANNEL_CLOSED) {
+ pr_warning("%s: message discarded (channel closed)\n",
+ __func__ );
+ return;
+ }
+
+ /* Create a new message */
+ message = kmalloc(sizeof(*message), GFP_ATOMIC);
+ if (!message) {
+ pr_err("%s: message discarded (alloc failed)\n", __func__ );
+ return;
+ }
+
+ /* Stuff it */
+ plist_node_init(&message->msg_entry, 0);
+ message->msg_type = MSG_INTERFACE;
+ message->d.itf.skelwrap = skelwrapper;
+ message->d.itf.methodIdx = methodIndex;
+ message->d.itf.anyPtr = ptr;
+ message->d.itf.ptrSize = size;
+
+ /* Enqueue it */
+ /* Should be protected with the cmPriv->msgQueueLock held
+ But we know by design that we are safe here. (Alone here in
+ tasklet (soft-interrupt) context.
+ When accessed in process context, soft-irq are disable)
+ */
+ spin_lock_bh(&skelwrapper->channelPriv->bh_lock);
+ plist_add(&message->msg_entry, &skelwrapper->channelPriv->messageQueue);
+ spin_unlock_bh(&skelwrapper->channelPriv->bh_lock);
+
+ /* Wake up process' wait queue */
+ wake_up_interruptible(&skelwrapper->channelPriv->waitq);
+}
+
+
+#define MAX_LOCKS 8 // max number of locks/semaphores creatable
+static unsigned long semused = 0; // bit field for used semaphores
+static unsigned long lockused = 0; // bit field for used mutexes
+static struct mutex cmld_locks[MAX_LOCKS];
+
+/** \ingroup OSAL_IMPLEMENTATION
+ */
+t_nmf_osal_sync_handle OSAL_CreateLock(void)
+{
+ int i;
+
+ for (i=0; i<MAX_LOCKS; i++)
+ if (!test_and_set_bit(i, &lockused)) {
+ struct mutex* mutex = &cmld_locks[i];
+ mutex_init(mutex);
+ return (t_nmf_osal_sync_handle)mutex;
+ }
+
+ return (t_nmf_osal_sync_handle)NULL;
+}
+
+
+/** \ingroup OSAL_IMPLEMENTATION
+ */
+void OSAL_Lock(t_nmf_osal_sync_handle handle)
+{
+ // unfortunately there is no return value to this function
+ // so we cannot use 'down_interruptible()'
+ mutex_lock((struct mutex*)handle);
+}
+
+
+/** \ingroup OSAL_IMPLEMENTATION
+ */
+void OSAL_Unlock(t_nmf_osal_sync_handle handle)
+{
+ mutex_unlock((struct mutex*)handle);
+}
+
+
+/** \ingroup OSAL_IMPLEMENTATION
+ */
+void OSAL_DestroyLock(t_nmf_osal_sync_handle handle)
+{
+ int i;
+
+ // clear the bit in the bits field about used locks
+ i = ((struct mutex*)handle - cmld_locks);
+
+ clear_bit(i, &lockused);
+}
+
+static struct semaphore cmld_semaphores[MAX_LOCKS];
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * Goal: Use by CM to allow to synchronize with code running on mpc side.
+ *
+ * \param[in] value : Initial value of semaphore.
+ *
+ * \return handle of the Semaphore created
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup OSAL
+ */
+t_nmf_osal_sem_handle OSAL_CreateSemaphore(t_uint32 value)
+{
+ int i;
+
+ for (i=0; i<MAX_LOCKS; i++)
+ if (!test_and_set_bit(i, &semused)) {
+ struct semaphore* sem = &cmld_semaphores[i];
+ sema_init(sem, value);
+ return (t_nmf_osal_sem_handle)sem;
+ }
+
+ return (t_nmf_osal_sem_handle)NULL;
+}
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * Goal: Use by CM to allow to synchronize with code running on mpc side. This function can be called under
+ * Irq context by CM.
+ *
+ * param[in] : handle of the Semaphore for which we increase value and so potentially wake up thread.
+ *
+ * param[in] : aCtx is a hint to indicate to os that we are in a none normal context (e.g under interruption).
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup OSAL
+ */
+void OSAL_SemaphorePost(t_nmf_osal_sem_handle handle, t_uint8 aCtx)
+{
+ up((struct semaphore*)handle);
+}
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * Goal: Use by CM to allow to synchronize with code running on mpc side.
+ *
+ * param[in] : handle of the Semaphore for which we decrease value and so potentially block current thread.
+ *
+ * param[in] : maximun time in ms after which the block thread is wake up. In this case function return SYNC_ERROR_TIMEOUT value.
+ *
+ * \return error number: SYNC_ERROR_TIMEOUT in case semaphore is not release withing timeOutInMs.
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup OSAL
+ */
+t_nmf_osal_sync_error OSAL_SemaphoreWaitTimed(t_nmf_osal_sem_handle handle,
+ t_uint32 timeOutInMs)
+{
+ if (down_timeout((struct semaphore*)handle, msecs_to_jiffies(timeOutInMs)))
+ return SYNC_ERROR_TIMEOUT;
+ else
+ return SYNC_OK;
+}
+
+/*!
+ * \brief Description of the Synchronization part of the OS Adaptation Layer
+ *
+ * Goal: Use by CM to allow to synchronize with code running on mpc side.
+ *
+ * param[in] : handle of the Semaphore to be destroyed
+ *
+ * Called by:
+ * - any CM API call
+ *
+ * \ingroup OSAL
+ */
+void OSAL_DestroySemaphore(t_nmf_osal_sem_handle handle)
+{
+ int i;
+
+ // clear the bit in the bits field about used locks
+ i = ((struct semaphore*)handle - cmld_semaphores);
+
+ clear_bit(i, &semused);
+}
+
+/** \ingroup OSAL_IMPLEMENTATION
+ * OSAL alloc implementation
+ *
+ * In both OSAL_Alloc() and OSAL_Alloc_Zero() function, the strategy is to use
+ * kmalloc() as it is the more efficient and most common way to allocate memory.
+ * For big allocation, kmalloc may fail because memory is very fragmented
+ * (kmalloc() allocates contiguous memory). In that case, we fall to vmalloc()
+ * instead.
+ * In OSAL_Free(), we rely on the virtual address to know which of kfree() or
+ * vfree() to use (vmalloc() use its own range of virtual addresses)
+ */
+void* OSAL_Alloc(t_cm_size size)
+{
+#ifdef CM_DEBUG_ALLOC
+ struct cm_alloc_elem *entry;
+
+ if (size == 0)
+ return NULL;
+
+ entry = kmalloc(size + sizeof(*entry), GFP_KERNEL);
+
+ if (entry == NULL) {
+ entry = vmalloc(size + sizeof(*entry));
+
+ if (entry == NULL) {
+ pr_alert("%s: kmalloc(%d) and vmalloc(%d) failed\n",
+ __func__, (int)size, (int)size);
+ dump_debug_alloc();
+ return NULL;
+ }
+ }
+ /* return address of the caller */
+ entry->caller = __builtin_return_address(0);
+ entry->size = size;
+
+ spin_lock(&cm_alloc.lock);
+ list_add_tail(&entry->elem, &cm_alloc.chain);
+ spin_unlock(&cm_alloc.lock);
+
+ return entry->addr;
+#else
+ void* mem;
+
+ if (size == 0)
+ return NULL;
+ mem = kmalloc(size, GFP_KERNEL);
+ if (mem == NULL) {
+ mem = vmalloc(size);
+ if (mem == NULL)
+ pr_alert("CM (%s): No more memory (requested "
+ "size=%d) !!!\n", __func__, (int)size);
+ }
+ return mem;
+#endif
+}
+
+
+/** \ingroup OSAL_IMPLEMENTATION
+ * OSAL alloc implementation
+ */
+void* OSAL_Alloc_Zero(t_cm_size size)
+{
+#ifdef CM_DEBUG_ALLOC
+ struct cm_alloc_elem *entry;
+
+ if (size == 0)
+ return NULL;
+
+ entry = kzalloc(size + sizeof(*entry), GFP_KERNEL);
+ if (entry == NULL) {
+ entry = vmalloc(size + sizeof(*entry));
+ if (entry == NULL) {
+ pr_alert("%s: kmalloc(%d) and vmalloc(%d) failed\n",
+ __func__, (int)size, (int)size);
+ dump_debug_alloc();
+ return NULL;
+ } else {
+ memset(entry, 0, size + sizeof(*entry));
+ }
+ }
+
+ /* return address of the caller */
+ entry->caller = __builtin_return_address(0);
+ entry->size = size;
+
+ spin_lock(&cm_alloc.lock);
+ list_add_tail(&entry->elem, &cm_alloc.chain);
+ spin_unlock(&cm_alloc.lock);
+
+ return entry->addr;
+#else
+ void* mem;
+
+ if (size == 0)
+ return NULL;
+ mem = kzalloc(size, GFP_KERNEL);
+ if (mem == NULL) {
+ mem = vmalloc(size);
+ if (mem == NULL)
+ pr_alert("CM (%s): No more memory (requested "
+ "size=%d) !!!\n", __func__, (int)size);
+ else
+ memset(mem, 0, size);
+ }
+
+ return mem;
+#endif
+}
+
+
+/** \ingroup OSAL_IMPLEMENTATION
+ * OSAL free implementation
+ */
+void OSAL_Free(void* mem)
+{
+#ifdef CM_DEBUG_ALLOC
+ struct cm_alloc_elem *entry = container_of(mem, struct cm_alloc_elem, addr);
+ unsigned int i;
+ char pattern[4] = { 0xEF, 0xBE, 0xAD, 0xDE };
+
+ if (mem == NULL)
+ return;
+
+ /* fill with a pattern to detect bad re-use of this area */
+ for (i=0; i<entry->size; i++)
+ entry->addr[i] = pattern[i%4];
+
+ spin_lock(&cm_alloc.lock);
+ list_del(&entry->elem);
+ spin_unlock(&cm_alloc.lock);
+
+ if ((void*)entry >= (void*)VMALLOC_START
+ && (void*)entry < (void*)VMALLOC_END)
+ vfree(entry);
+ else
+ kfree(entry);
+#else
+ if (mem >= (void*)VMALLOC_START && mem < (void*)VMALLOC_END)
+ vfree(mem);
+ else
+ kfree(mem);
+#endif
+}
+
+/** \ingroup OSAL_IMPLEMENTATION
+ * OSAL Copy implementation
+ * This copy some data from userspace (address to kernel space.
+ * This implementation differs on Symbian.
+ */
+t_cm_error OSAL_Copy(void *dst, const void *src, t_cm_size size)
+{
+ if (copy_from_user(dst, src, size))
+ return CM_UNKNOWN_MEMORY_HANDLE;
+ return CM_OK;
+}
+
+/** \ingroup OSAL_IMPLEMENTATION
+ * OSAL write64 function implementation
+ */
+void OSAL_Write64(t_nmf_trace_channel channel, t_uint8 isTimestamped, t_uint64 value)
+{
+#ifdef CONFIG_STM_TRACE
+ if (isTimestamped)
+ stm_tracet_64(channel, value);
+ else
+ stm_trace_64(channel, value);
+#endif
+}
+
+
+/** \ingroup OSAL_IMPLEMENTATION
+ * OSAL log function implementation
+ */
+void OSAL_Log(const char *format, int param1, int param2, int param3, int param4, int param5, int param6)
+{
+ printk(format, param1, param2, param3, param4, param5, param6);
+}
+
+/**
+ * compute the dsp load
+ *
+ * return -1 if in case of failure, a value between 0 and 100 otherwise
+ */
+static s8 computeDspLoad(t_cm_mpc_load_counter *oldCounter, t_cm_mpc_load_counter *counter)
+{
+ u32 t, l;
+
+ if ((oldCounter->totalCounter == 0) && (oldCounter->loadCounter == 0))
+ return -1; // Failure or not started ?
+ if ((counter->totalCounter == 0) && (counter->loadCounter == 0))
+ return -1; // Failure or already stopped ?
+
+ if (counter->totalCounter < oldCounter->totalCounter)
+ t = (u32)((((u64)-1) - oldCounter->totalCounter)
+ + counter->totalCounter + 1);
+ else
+ t = (u32)(counter->totalCounter - oldCounter->totalCounter);
+
+ if (counter->loadCounter < oldCounter->loadCounter)
+ l = (u32)((((u64)-1) - oldCounter->loadCounter)
+ + counter->loadCounter + 1);
+ else
+ l = (u32)(counter->loadCounter - oldCounter->loadCounter);
+
+ if (t == 0) // not significant
+ return -1;
+
+ if (l > t) // not significant
+ return -1;
+
+ return (l*100) / t;
+}
+
+/**
+ * Thread function entry for monitorin the CPU load
+ */
+static int dspload_monitor(void *idx)
+{
+ long remaining_time = 0;
+ int i = (int)idx;
+ unsigned char current_opp_request = HALF_OPP;
+ struct mpcConfig *mpc = &osalEnv.mpc[i];
+
+ if (prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
+ (char*)mpc->name,
+ current_opp_request))
+ pr_err("CM Driver: Add QoS failed\n");
+
+ // init counter
+ if (CM_GetMpcLoadCounter(mpc->coreId,
+ &mpc->oldLoadCounter) != CM_OK)
+ pr_err("CM Driver: Failed to init load counter for %s\n",
+ mpc->name);
+
+ while (!kthread_should_stop()) {
+ t_cm_mpc_load_counter loadCounter;
+ s8 load = -1;
+ long timeout;
+
+ timeout = msecs_to_jiffies(dspLoadMonitorPeriod) - remaining_time;
+ remaining_time = schedule_timeout_uninterruptible(timeout);
+
+ // woken up by something else than a timeout
+ if (remaining_time)
+ continue;
+ // Keep this test ?
+ if (!test_bit(i, &running_dsp))
+ continue;
+
+ if (CM_GetMpcLoadCounter(mpc->coreId,
+ &loadCounter) != CM_OK)
+ loadCounter = mpc->oldLoadCounter;
+
+ load = computeDspLoad(&mpc->oldLoadCounter, &loadCounter);
+ mpc->oldLoadCounter = loadCounter;
+
+ if (load == -1)
+ continue;
+ /* check if we must request more opp */
+ if ((current_opp_request == HALF_OPP)
+ && (load > dspLoadHighThreshold)) {
+ current_opp_request = FULL_OPP;
+ if (cm_debug_level)
+ pr_info("CM Driver: Request QoS OPP %d for %s\n",
+ current_opp_request, mpc->name);
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ (char*)mpc->name,
+ current_opp_request);
+ }
+ /* check if we can request less opp */
+ else if ((current_opp_request == FULL_OPP)
+ && (load < dspLoadLowThreshold)) {
+ current_opp_request = HALF_OPP;
+ if (cm_debug_level)
+ pr_info("CM Driver: Request QoS OPP %d for %s\n",
+ current_opp_request, mpc->name);
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ (char*)mpc->name,
+ current_opp_request);
+ }
+ }
+
+ if (cm_debug_level)
+ pr_info("CM Driver: Remove QoS OPP for %s\n", mpc->name);
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
+ (char*)mpc->name);
+ return 0;
+}
+
+static int enable_auto_pm = 0;
+module_param(enable_auto_pm, bool, S_IWUSR|S_IRUGO);
+
+/** \ingroup OSAL_IMPLEMENTATION
+ * Used by CM to disable a power resource
+ */
+void OSAL_DisablePwrRessource(t_nmf_power_resource resource, t_uint32 firstParam, t_uint32 secondParam)
+{
+ switch (resource) {
+ case CM_OSAL_POWER_SxA_CLOCK: {
+ unsigned idx = COREIDX(firstParam);
+ struct osal_msg msg;
+
+ if (idx >= NB_MPC) {
+ pr_err("CM Driver(%s(res=%d)): core %u unknown\n",
+ __func__, (int)resource, (unsigned)firstParam);
+ return;
+ }
+
+ /* Stop the DSP load monitoring */
+ clear_bit(idx, &running_dsp);
+ if (osalEnv.mpc[idx].monitor_tsk) {
+ kthread_stop(osalEnv.mpc[idx].monitor_tsk);
+ osalEnv.mpc[idx].monitor_tsk = NULL;
+ }
+
+ /* Stop the DSP */
+ if (regulator_disable(osalEnv.mpc[idx].mmdsp_regulator) < 0)
+ pr_err("CM Driver(%s): can't disable regulator %s-mmsdp\n",
+ __func__, osalEnv.mpc[idx].name);
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_unlock(&osalEnv.mpc[idx].wakelock);
+#endif
+
+ /* Create and dispatch a shutdown service message */
+ msg.msg_type = MSG_SERVICE;
+ msg.d.srv.srvType = NMF_SERVICE_SHUTDOWN;
+ msg.d.srv.srvData.shutdown.coreid = firstParam;
+ dispatch_service_msg(&msg);
+ break;
+ }
+ case CM_OSAL_POWER_SxA_AUTOIDLE:
+ switch (firstParam) {
+ case SVA_CORE_ID:
+ osalEnv.dsp_sleep.sva_auto_pm_enable = PRCMU_AUTO_PM_OFF;
+ osalEnv.dsp_sleep.sva_power_on = 0;
+ osalEnv.dsp_sleep.sva_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF;
+ break;
+ case SIA_CORE_ID:
+ osalEnv.dsp_sleep.sia_auto_pm_enable = PRCMU_AUTO_PM_OFF;
+ osalEnv.dsp_sleep.sia_power_on = 0;
+ osalEnv.dsp_sleep.sia_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF;
+ break;
+ default:
+ pr_err("CM Driver(%s(res=%d)): core %u unknown\n", __func__, (int)resource, (unsigned)firstParam);
+ return;
+ }
+ if (enable_auto_pm)
+ prcmu_configure_auto_pm(&osalEnv.dsp_sleep, &osalEnv.dsp_idle);
+ break;
+ case CM_OSAL_POWER_SxA_HARDWARE: {
+ unsigned idx = COREIDX(firstParam);
+ if (idx >= NB_MPC) {
+ pr_err("CM Driver(%s(res=%d)): core %u unknown\n",
+ __func__, (int)resource, (unsigned)firstParam);
+ return;
+ }
+ if (regulator_disable(osalEnv.mpc[idx].pipe_regulator) < 0)
+ pr_err("CM Driver(%s): can't disable regulator %s-pipe\n",
+ __func__, osalEnv.mpc[idx].name);
+ break;
+ }
+ case CM_OSAL_POWER_HSEM:
+ break;
+ case CM_OSAL_POWER_SDRAM:
+ break;
+ case CM_OSAL_POWER_ESRAM: {
+ int i;
+ /* firstParam: base address; secondParam: size
+ U8500_ESRAM_BASE is the start address of BANK 0,
+ BANK size=0x20000 */
+
+ /* Compute the relative end address of the range,
+ relative to base address of BANK1 */
+ secondParam = (firstParam+secondParam-(U8500_ESRAM_BASE+0x20000)-1);
+
+ /* if end is below base address of BANK1, it means that full
+ range of addresses is on Bank0 */
+ if (((int)secondParam) < 0)
+ break;
+ /* Compute the index of the last bank accessed among
+ esram 1+2 and esram 3+4 banks */
+ secondParam /= 0x40000;
+ WARN_ON(secondParam > 1);
+
+ /* Compute the index of the first bank accessed among esram 1+2
+ and esram 3+4 banks
+ Do not manage Bank 0 (secured, must be always ON) */
+ if (firstParam < (U8500_ESRAM_BASE+0x20000))
+ firstParam = 0;
+ else
+ firstParam = (firstParam-(U8500_ESRAM_BASE+0x20000))/0x40000;
+
+ /* power off the banks 1+2 and 3+4 if accessed. */
+ for (i=firstParam; i<=secondParam; i++) {
+ if (regulator_disable(osalEnv.esram_regulator[i]) < 0)
+ pr_err("CM Driver(%s): can't disable regulator"
+ "for esram bank %s\n", __func__,
+ i ? "34" : "12");
+ }
+ break;
+ }
+ default:
+ pr_err("CM Driver(%s): resource %d unknown/not supported\n",
+ __func__, (int)resource);
+ }
+}
+
+/** \ingroup OSAL_IMPLEMENTATION
+ * Used by CM to enable a power resource
+ */
+t_cm_error OSAL_EnablePwrRessource(t_nmf_power_resource resource, t_uint32 firstParam, t_uint32 secondParam)
+{
+ switch (resource) {
+ case CM_OSAL_POWER_SxA_CLOCK: {
+ unsigned idx = COREIDX(firstParam);
+
+ if (idx > NB_MPC) {
+ pr_err("CM Driver(%s(res=%d)): core %u unknown\n", __func__, (int)resource, (unsigned)firstParam);
+ return CM_INVALID_PARAMETER;
+ }
+
+ /* Start the DSP */
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock(&osalEnv.mpc[idx].wakelock);
+#endif
+ if (regulator_enable(osalEnv.mpc[idx].mmdsp_regulator) < 0)
+ pr_err("CM Driver(%s): can't enable regulator %s-mmsdp\n", __func__, osalEnv.mpc[idx].name);
+
+ /* Start the DSP load monitoring for this dsp */
+ set_bit(idx, &running_dsp);
+ osalEnv.mpc[idx].monitor_tsk = kthread_run(&dspload_monitor,
+ (void*)idx,
+ "%s-loadd",
+ osalEnv.mpc[idx].name);
+ if (IS_ERR(osalEnv.mpc[idx].monitor_tsk)) {
+ pr_err("CM Driver: failed to start dspmonitord "
+ "thread: %ld\n", PTR_ERR(osalEnv.mpc[idx].monitor_tsk));
+ osalEnv.mpc[idx].monitor_tsk = NULL;
+ }
+ break;
+ }
+ case CM_OSAL_POWER_SxA_AUTOIDLE:
+ switch (firstParam) {
+ case SVA_CORE_ID:
+ osalEnv.dsp_sleep.sva_auto_pm_enable = PRCMU_AUTO_PM_ON;
+ osalEnv.dsp_sleep.sva_power_on = PRCMU_AUTO_PM_POWER_ON_HSEM | PRCMU_AUTO_PM_POWER_ON_ABB_FIFO_IT;
+ osalEnv.dsp_sleep.sva_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_RAMRET_HWP_OFF;
+ break;
+ case SIA_CORE_ID:
+ osalEnv.dsp_sleep.sia_auto_pm_enable = PRCMU_AUTO_PM_ON;
+ osalEnv.dsp_sleep.sia_power_on = PRCMU_AUTO_PM_POWER_ON_HSEM | PRCMU_AUTO_PM_POWER_ON_ABB_FIFO_IT;
+ osalEnv.dsp_sleep.sia_policy = PRCMU_AUTO_PM_POLICY_DSP_OFF_RAMRET_HWP_OFF;
+ break;
+ default:
+ pr_err("CM Driver(%s(res=%d)): core %u unknown\n", __func__, (int)resource, (unsigned)firstParam);
+ return CM_INVALID_PARAMETER;
+ }
+ if (enable_auto_pm)
+ prcmu_configure_auto_pm(&osalEnv.dsp_sleep, &osalEnv.dsp_idle);
+ break;
+ case CM_OSAL_POWER_SxA_HARDWARE: {
+ unsigned idx = COREIDX(firstParam);
+
+ if (idx > NB_MPC) {
+ pr_err("CM Driver(%s(res=%d)): core %u unknown\n", __func__, (int)resource, (unsigned)firstParam);
+ return CM_INVALID_PARAMETER;
+ }
+ if (regulator_enable(osalEnv.mpc[idx].pipe_regulator) < 0)
+ pr_err("CM Driver(%s): can't enable regulator %s-pipe\n", __func__, osalEnv.mpc[idx].name);
+ break;
+ }
+ case CM_OSAL_POWER_HSEM:
+ return CM_OK;
+ case CM_OSAL_POWER_SDRAM:
+ break;
+ case CM_OSAL_POWER_ESRAM:
+ {
+ int i;
+ /* firstParam: base address; secondParam: size
+ U8500_ESRAM_BASE is the start address of BANK 0,
+ BANK size=0x20000 */
+
+ /* Compute the relative end address of the range, relative
+ to base address of BANK1 */
+ secondParam = (firstParam+secondParam-(U8500_ESRAM_BASE+0x20000)-1);
+
+ /* if end is below base address of BANK1, it means that full
+ range of addresses is on Bank0 */
+ if (((int)secondParam) < 0)
+ break;
+ /* Compute the index of the last bank accessed among esram 1+2
+ and esram 3+4 banks */
+ secondParam /= 0x40000;
+ WARN_ON(secondParam > 1);
+
+ /* Compute the index of the first bank accessed among esram 1+2
+ and esram 3+4 banks
+ Do not manage Bank 0 (secured, must be always ON) */
+ if (firstParam < (U8500_ESRAM_BASE+0x20000))
+ firstParam = 0;
+ else
+ firstParam = (firstParam-(U8500_ESRAM_BASE+0x20000))/0x40000;
+
+ /* power on the banks 1+2 and 3+4 if accessed. */
+ for (i=firstParam; i<=secondParam; i++) {
+ if (regulator_enable(osalEnv.esram_regulator[i]) < 0)
+ pr_err("CM Driver(%s): can't enable regulator "
+ "for esram bank %s\n", __func__,
+ i ? "34" : "12");
+ }
+ break;
+ }
+ default:
+ pr_err("CM Driver(%s): resource %x unknown/not supported\n",
+ __func__, (int)resource);
+ return CM_INVALID_PARAMETER;
+ }
+
+ return CM_OK;
+}
+
+/*!
+ * \brief Generate an OS-Panic. Called in from CM_ASSERT().
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+void OSAL_Panic(void)
+{
+ panic("FATAL ISSUE IN THE CM DRIVER !!");
+}
+#include <mach/dcache.h>
+/*!
+ * \brief Clean data cache in DDR in order to be accessible from peripheral.
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+void OSAL_CleanDCache(t_uint32 startAddr, t_uint32 size)
+{
+#if 0
+ /*
+ * Currently, the code sections are is non-cached/buffered,
+ * which normally doesn't required the maintenance done below.
+ * At the cost is low (doesn't do much thing), I keep it in case
+ * of the memory settings are changed later.
+ */
+
+ struct hwmem_region region;
+ struct mpcConfig *mpc;
+ t_uint32 endAddr = startAddr + size;
+
+ if (startAddr >= (u32)osalEnv.mpc[0].sdramCodeL
+ && endAddr <= (u32)(osalEnv.mpc[0].sdramCodeL
+ + osalEnv.mpc[0].sdramCodeSize)) {
+ mpc = &osalEnv.mpc[0];
+ } else if (startAddr >= (u32)osalEnv.mpc[1].sdramCodeL
+ && endAddr <= (u32)(osalEnv.mpc[1].sdramCodeL
+ + osalEnv.mpc[1].sdramCodeSize)) {
+ mpc = &osalEnv.mpc[1];
+ } else {
+ /* The code may be in esram, in that case, nothing to do */
+ return;
+ }
+
+ region.offset = startAddr - (u32)mpc->sdramCodeL;
+ region.count = 1;
+ region.start = 0;
+ region.end = size;
+ region.size = size;
+ hwmem_set_domain(mpc->hwmemCode, HWMEM_ACCESS_READ,
+ HWMEM_DOMAIN_SYNC, &region);
+ /*
+ * The hwmem keep track of region being sync or not.
+ * Mark the region as being write-accessed here right now
+ * to let following clean being done as expected. Today,
+ * there is no other place to do that in CM Core right now
+ */
+ hwmem_set_domain(mpc->hwmemCode, HWMEM_ACCESS_WRITE,
+ HWMEM_DOMAIN_CPU, &region);
+#else
+ dsb();
+ outer_cache.sync();
+#endif
+}
+
+/*!
+ * \brief Flush write-buffer of L2 cache
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+void OSAL_mb(void)
+{
+ mb();
+}
+
+/*!
+ * \brief return prcmu timer value.
+ *
+ * This is need for perfmeter api (see \ref t_nmf_power_resource)
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+t_uint64 OSAL_GetPrcmuTimer()
+{
+ t_uint64 msbBefore;
+ t_uint32 lsb;
+ t_uint64 msbAfter;
+
+ /* read prcmu timers */
+ msbBefore = ~ioread32(prcmu_tcdm_base+0xDE4);
+ lsb = ~ioread32(prcmu_base+0x454);
+ msbAfter = ~ioread32(prcmu_tcdm_base+0xDE4);
+
+ /* handle rollover test case */
+ // NOTE : there is still a window in prcmu side between counter rollover
+ // and prcmu interrupt handling
+ // to update msb register => this can lead to erroneous value return here
+ if (msbBefore == msbAfter || lsb >= 0x80000000UL)
+ return (((msbBefore & 0xffffffUL) << 32) + lsb);
+ else
+ return (((msbAfter & 0xffffffUL) << 32) + lsb);
+}
+
+/*!
+ * \brief Disable the service message handling (panic, etc)
+ *
+ * It must disable the handling of all service messages
+ * If a service message is currently handled, it must wait till the end
+ * of its managment before returning.
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_DisableServiceMessages(void) {
+ tasklet_disable(&cmld_service_tasklet);
+}
+
+/*!
+ * \brief Enable the service message handling (panic, etc)
+ *
+ * It enables the handling of all service messages
+ *
+ * \ingroup CM_ENGINE_OSAL_API
+ */
+PUBLIC void OSAL_EnableServiceMessages(void) {
+ tasklet_enable(&cmld_service_tasklet);
+}
diff --git a/drivers/staging/nmf-cm/osal-kernel.h b/drivers/staging/nmf-cm/osal-kernel.h
new file mode 100644
index 00000000000..31dffc4a82b
--- /dev/null
+++ b/drivers/staging/nmf-cm/osal-kernel.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef OSAL_KERNEL_H
+#define OSAL_KERNEL_H
+
+#include <linux/interrupt.h>
+#include <linux/hwmem.h>
+#include <linux/regulator/consumer.h>
+#ifdef CONFIG_HAS_WAKELOCK
+#include <linux/wakelock.h>
+#endif
+#include <mach/prcmu-fw-api.h>
+#include <cm/engine/api/control/configuration_engine.h>
+#include <cm/engine/api/perfmeter_engine.h>
+#include <cm/engine/os_adaptation_layer/inc/os_adaptation_layer.h>
+#include <cm/os/api/channel_os.h>
+
+#include "configuration.h"
+
+/** Per-MPC configuration structure */
+struct mpcConfig {
+ const t_nmf_core_id coreId; /**< MPC coreId */
+ const char *name; /**< MPC name */
+ t_uint8 nbYramBanks; /**< number of TCM ram banks to reserve for y memory */
+ t_nmf_executive_engine_id eeId; /**< Type of Executive Engine */
+ const void *baseP; /**< Physical base address of the MPC */
+ void *baseL; /**< Remapped base address of the MPC */
+ struct hwmem_alloc *hwmemCode; /**< hwmem code segment */
+ u32 sdramCodeP; /**< Physical base address for MPC SDRAM Code region */
+ void *sdramCodeL; /**< Remapped base address for MPC SDRAM Code region */
+ size_t sdramCodeSize; /**< Size of MPC SDRAM Code region */
+ struct hwmem_alloc *hwmemData; /**< hwmem data segment */
+ u32 sdramDataP; /**< Physical base address for MPC SDRAM Data region */
+ void *sdramDataL; /**< Remapped base address for MPC SDRAM Data region */
+ size_t sdramDataSize; /**< Size of MPC SDRAM Data region */
+ const unsigned int interrupt0; /**< interrupt line triggered by the MPC, for MPC events (if HSEM not used) */
+ const unsigned int interrupt1; /**< interrupt line triggered by the MPC, for PANIC events */
+ struct tasklet_struct tasklet; /**< taskket used to process MPC events */
+ struct regulator *mmdsp_regulator; /**< mmdsp regulator linked to this MPC */
+ struct regulator *pipe_regulator; /**< hardware pipe linked to this MPC */
+#ifdef CONFIG_HAS_WAKELOCK
+ struct wake_lock wakelock; /**< wakelock for this MPC to prevent ARM to go in APSLEEP state */
+#endif
+ struct task_struct *monitor_tsk;/**< task to monitor the dsp load; */
+ t_cm_mpc_load_counter oldLoadCounter; /**< previous load counter of the DSP */
+};
+
+/** Describes current Kernel OSAL environment
+ *
+ * Note about mpc.tasklet : we declare one tasklet per MPC but their usage depends
+ * on cfgSemaphoreTypeHSEM.
+ *
+ * This tasklet is scheduled by the interrupt handler to process MPC Events.
+ * - If we use Hardware Semaphore, there is only one interrupt handler used
+ * and thus only one tasklet, tasklet of MPC 0 (ie osalEnv.mpc[0].tasklet)
+ * - If we use local semaphore, there is one interrupt handler and tasklet per mpc
+ */
+struct OsalEnvironment
+{
+ struct mpcConfig mpc[NB_MPC];
+ void* hwsem_base; /** < Remapped base address of the hardware semaphores */
+ void* esram_base; /** < Remapped base address for embedded RAM used within the CM */
+ struct regulator *esram_regulator[NB_ESRAM]; /**< regulator for ESRAM bank 1+2 and 3+4 */
+ struct prcmu_auto_pm_config dsp_sleep;
+ struct prcmu_auto_pm_config dsp_idle;
+};
+
+
+/** Structure used to store the skeleton related data.
+ * It is used for communicattion from a MPC to a user process (=host)
+ */
+typedef struct {
+ struct list_head entry; /**< Doubly linked list descriptor */
+ t_cm_bf_mpc2host_handle mpc2hostId; /**< mpc2host ID */
+ t_nmf_mpc2host_handle upperLayerThis;/**< upper-layer handle */
+ struct cm_channel_priv* channelPriv; /**< Per-channel private data. The actual message queue is hold here */
+} t_skelwrapper;
+
+/** Message description for MPC to HOST communication
+ */
+struct osal_msg {
+ struct {
+ struct plist_node entry; /**< Doubly linked list descriptor */
+ t_message_type type; /**< Type of message (callback, service or interrupt for now) */
+ } hdr; /**< Header of the message */
+#define msg_entry hdr.entry
+#define msg_type hdr.type
+ union {
+ struct {
+ t_skelwrapper *skelwrap; /**< Link to the skelwrapper, to retrieve the channel on which this message has to be forwarded */
+ t_uint32 methodIdx; /**< callback data: method index*/
+ t_event_params_handle anyPtr; /**< callback data: method parameters */
+ t_uint32 ptrSize; /**< size of the parameters */
+ } itf; /**< structure holding callback data */
+ struct {
+ t_nmf_service_type srvType; /**< Type of the service */
+ t_nmf_service_data srvData; /**< Data of the service */
+ } srv; /**< structure holding service data */
+ } d; /**< data */
+};
+
+extern struct OsalEnvironment osalEnv;
+
+/** Environment initialization/deinitialization */
+int remapRegions(void);
+void unmapRegions(void);
+
+/** Component manager configuration getters for CM_ENGINE_Init() */
+int getNmfHwMappingDesc(t_nmf_hw_mapping_desc* nmfHwMappingDesc);
+
+/** Component manager configuration getters for CM_ConfigureMediaProcessorCore (SVA and SIA) */
+void getMpcSystemAddress(unsigned i, t_cm_system_address* mpcSystemAddress);
+void getMpcSdramSegments(unsigned i, t_nmf_memory_segment* codeSegment, t_nmf_memory_segment* dataSegment);
+
+#ifdef CM_DEBUG_ALLOC
+struct cm_alloc {
+ spinlock_t lock;
+ struct list_head chain;
+};
+
+struct cm_alloc_elem {
+ struct list_head elem;
+ void *caller;
+ size_t size;
+ char addr[0];
+};
+
+void init_debug_alloc(void);
+void cleanup_debug_alloc(void);
+#endif /* CM_DEBUG_ALLOC */
+
+/* TODO: To remove later */
+extern __iomem void *prcmu_base;
+extern __iomem void *prcmu_tcdm_base;
+extern const char *cmld_devname[];
+
+#define PRCM_SVAMMDSPCLK_MGT (prcmu_base + 0x008)
+#define PRCM_SIAMMDSPCLK_MGT (prcmu_base + 0x00c)
+
+#endif /* OSAL_KERNEL_H */
diff --git a/drivers/staging/nmf-cm/share/communication/inc/communication_fifo.h b/drivers/staging/nmf-cm/share/communication/inc/communication_fifo.h
new file mode 100644
index 00000000000..ea24e82ceae
--- /dev/null
+++ b/drivers/staging/nmf-cm/share/communication/inc/communication_fifo.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef __INC_NMF_COM_FIFO
+#define __INC_NMF_COM_FIFO
+
+#include <inc/typedef.h>
+
+#define EVENT_ELEM_METHOD_IDX 0
+#define EVENT_ELEM_PARAM_IDX 1
+#define EVENT_ELEM_EXTFIELD_IDX 2
+
+#define EVENT_ELEM_SIZE_IN_BYTE (3 * sizeof(t_shared_field))
+
+#endif /* __INC_NMF_COM_FIFO */
diff --git a/drivers/staging/nmf-cm/share/communication/inc/initializer.h b/drivers/staging/nmf-cm/share/communication/inc/initializer.h
new file mode 100644
index 00000000000..ea5aa68f2dc
--- /dev/null
+++ b/drivers/staging/nmf-cm/share/communication/inc/initializer.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef __INC_SHARE_INITIALIZER
+#define __INC_SHARE_INITIALIZER
+
+#define NMF_CONSTRUCT_INDEX 0
+#define NMF_START_INDEX 1
+#define NMF_STOP_INDEX 2
+#define NMF_DESTROY_INDEX 3
+#define NMF_UPDATE_STACK 4
+#define NMF_LOCK_CACHE 5
+#define NMF_UNLOCK_CACHE 6
+#define NMF_ULP_FORCEWAKEUP 7
+#define NMF_ULP_ALLOWSLEEP 8
+#define NMF_CONSTRUCT_SYNC_INDEX 9
+#define NMF_START_SYNC_INDEX 10
+
+/*
+ * Index of datas in command parameter format
+ */
+#define INIT_COMPONENT_CMD_HANDLE_INDEX 0
+#define INIT_COMPONENT_CMD_THIS_INDEX 2
+#define INIT_COMPONENT_CMD_METHOD_INDEX 4
+#define INIT_COMPONENT_CMD_SIZE 6
+
+/*
+ * Index of datas in acknowledge parameter format
+ */
+#define INIT_COMPONENT_ACK_HANDLE_INDEX 0
+#define INIT_COMPONENT_ACK_SIZE 2
+
+#endif /* __INC_SHARE_INITIALIZER */
diff --git a/drivers/staging/nmf-cm/share/communication/inc/nmf_fifo_desc.h b/drivers/staging/nmf-cm/share/communication/inc/nmf_fifo_desc.h
new file mode 100644
index 00000000000..99caa48b05c
--- /dev/null
+++ b/drivers/staging/nmf-cm/share/communication/inc/nmf_fifo_desc.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef __INC_NMF_FIFO_DESC
+#define __INC_NMF_FIFO_DESC
+
+#include <inc/typedef.h>
+#include <share/semaphores/inc/semaphores.h>
+
+/*
+ * SHOULD be mapped onto a AHB burst (16 bytes=8x16-bit)
+ */
+typedef struct {
+ t_semaphore_id semId;
+
+ t_uint16 elemSize;
+ t_uint16 fifoFullValue;
+ t_uint16 readIndex;
+ t_uint16 writeIndex;
+ t_uint16 wrappingValue;
+
+ t_uint32 extendedField; /* in DSP 24 memory when to MPC in Logical Host when to ARM */
+} t_nmf_fifo_desc;
+
+#define EXTENDED_FIELD_BCTHIS_OR_TOP 0 //<! This field will be used:
+ //<! - as hostBCThis for DSP->HOST binding
+ //<! - as TOP else
+#define EXTENDED_FIELD_BCDESC 1 //<! This field will be used for:
+ //<! - interface method address for ->MPC binding
+ //<! - for params size for ->Host binding (today only [0] is used as max size)
+
+#endif /* __INC_NMF_FIFO */
diff --git a/drivers/staging/nmf-cm/share/communication/inc/nmf_service.h b/drivers/staging/nmf-cm/share/communication/inc/nmf_service.h
new file mode 100644
index 00000000000..5ec08434492
--- /dev/null
+++ b/drivers/staging/nmf-cm/share/communication/inc/nmf_service.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef __INC_NMF_SERVICE_H
+#define __INC_NMF_SERVICE_H
+
+/* 1 - 0xff Reserved for Panic Reason */
+#define MPC_SERVICE_NONE 0
+#define MPC_SERVICE_BOOT 0xB001
+#define MPC_SERVICE_PRINT 0x1234
+
+#endif
diff --git a/drivers/staging/nmf-cm/share/inc/macros.h b/drivers/staging/nmf-cm/share/inc/macros.h
new file mode 100644
index 00000000000..c96fe031c25
--- /dev/null
+++ b/drivers/staging/nmf-cm/share/inc/macros.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief NMF Macro API.
+ */
+
+#ifndef _COMMON_MACROS_H_
+#define _COMMON_MACROS_H_
+
+#undef ALIGN_VALUE
+#define ALIGN_VALUE(value, alignment) (((value) + (alignment - 1)) & ~(alignment - 1))
+
+#undef MIN
+#define MIN(a,b) (((a)>(b))?(b):(a))
+
+#undef MAX
+#define MAX(a,b) (((a)<(b))?(b):(a))
+
+/*-----------------------------------------------------------------------------
+ * endianess switch macros (32 bits and 16 bits)
+ *---------------------------------------------------------------------------*/
+#define ENDIANESS_32_SWITCH(value) ( \
+ (((value) & MASK_BYTE3) >> SHIFT_BYTE3) | \
+ (((value) & MASK_BYTE2) >> SHIFT_BYTE1) | \
+ (((value) & MASK_BYTE1) << SHIFT_BYTE1) | \
+ (((value) & MASK_BYTE0) << SHIFT_BYTE3) \
+ )
+
+#define ENDIANESS_16_SWITCH(value) ( \
+ (((value) & MASK_BYTE0) << SHIFT_BYTE1) | \
+ (((value) & MASK_BYTE1) >> SHIFT_BYTE1) \
+ )
+
+/*-----------------------------------------------------------------------------
+ * field offset extraction from a structure
+ *---------------------------------------------------------------------------*/
+#undef FIELD_OFFSET
+#define FIELD_OFFSET(typeName, fieldName) ((t_uint32)(&(((typeName *)0)->fieldName)))
+
+#undef MASK_BIT
+#define MASK_BIT(n) (1UL << ((n) - 1))
+
+/*-----------------------------------------------------------------------------
+ * Misc definition
+ *---------------------------------------------------------------------------*/
+
+#undef ONE_KB
+#define ONE_KB (1024)
+#undef ONE_MB
+#define ONE_MB (ONE_KB * ONE_KB)
+
+/*-----------------------------------------------------------------------------
+ * Bit mask definition
+ *---------------------------------------------------------------------------*/
+#undef MASK_NULL8
+#define MASK_NULL8 0x00U
+#undef MASK_NULL16
+#define MASK_NULL16 0x0000U
+#undef MASK_NULL32
+#define MASK_NULL32 0x00000000UL
+#undef MASK_ALL8
+#define MASK_ALL8 0xFFU
+#undef MASK_ALL16
+#define MASK_ALL16 0xFFFFU
+#undef MASK_ALL32
+#define MASK_ALL32 0xFFFFFFFFUL
+
+#undef MASK_BIT0
+#define MASK_BIT0 (1UL<<0)
+#undef MASK_BIT1
+#define MASK_BIT1 (1UL<<1)
+#undef MASK_BIT2
+#define MASK_BIT2 (1UL<<2)
+#undef MASK_BIT3
+#define MASK_BIT3 (1UL<<3)
+#undef MASK_BIT4
+#define MASK_BIT4 (1UL<<4)
+#undef MASK_BIT5
+#define MASK_BIT5 (1UL<<5)
+#undef MASK_BIT6
+#define MASK_BIT6 (1UL<<6)
+#undef MASK_BIT7
+#define MASK_BIT7 (1UL<<7)
+#undef MASK_BIT8
+#define MASK_BIT8 (1UL<<8)
+#undef MASK_BIT9
+#define MASK_BIT9 (1UL<<9)
+#undef MASK_BIT10
+#define MASK_BIT10 (1UL<<10)
+#undef MASK_BIT11
+#define MASK_BIT11 (1UL<<11)
+#undef MASK_BIT12
+#define MASK_BIT12 (1UL<<12)
+#undef MASK_BIT13
+#define MASK_BIT13 (1UL<<13)
+#undef MASK_BIT14
+#define MASK_BIT14 (1UL<<14)
+#undef MASK_BIT15
+#define MASK_BIT15 (1UL<<15)
+#undef MASK_BIT16
+#define MASK_BIT16 (1UL<<16)
+#undef MASK_BIT17
+#define MASK_BIT17 (1UL<<17)
+#undef MASK_BIT18
+#define MASK_BIT18 (1UL<<18)
+#undef MASK_BIT19
+#define MASK_BIT19 (1UL<<19)
+#undef MASK_BIT20
+#define MASK_BIT20 (1UL<<20)
+#undef MASK_BIT21
+#define MASK_BIT21 (1UL<<21)
+#undef MASK_BIT22
+#define MASK_BIT22 (1UL<<22)
+#undef MASK_BIT23
+#define MASK_BIT23 (1UL<<23)
+#undef MASK_BIT24
+#define MASK_BIT24 (1UL<<24)
+#undef MASK_BIT25
+#define MASK_BIT25 (1UL<<25)
+#undef MASK_BIT26
+#define MASK_BIT26 (1UL<<26)
+#undef MASK_BIT27
+#define MASK_BIT27 (1UL<<27)
+#undef MASK_BIT28
+#define MASK_BIT28 (1UL<<28)
+#undef MASK_BIT29
+#define MASK_BIT29 (1UL<<29)
+#undef MASK_BIT30
+#define MASK_BIT30 (1UL<<30)
+#undef MASK_BIT31
+#define MASK_BIT31 (1UL<<31)
+
+/*-----------------------------------------------------------------------------
+ * quartet shift definition
+ *---------------------------------------------------------------------------*/
+#undef MASK_QUARTET
+#define MASK_QUARTET (0xFUL)
+#undef SHIFT_QUARTET0
+#define SHIFT_QUARTET0 0
+#undef SHIFT_QUARTET1
+#define SHIFT_QUARTET1 4
+#undef SHIFT_QUARTET2
+#define SHIFT_QUARTET2 8
+#undef SHIFT_QUARTET3
+#define SHIFT_QUARTET3 12
+#undef SHIFT_QUARTET4
+#define SHIFT_QUARTET4 16
+#undef SHIFT_QUARTET5
+#define SHIFT_QUARTET5 20
+#undef SHIFT_QUARTET6
+#define SHIFT_QUARTET6 24
+#undef SHIFT_QUARTET7
+#define SHIFT_QUARTET7 28
+#undef MASK_QUARTET0
+#define MASK_QUARTET0 (MASK_QUARTET << SHIFT_QUARTET0)
+#undef MASK_QUARTET1
+#define MASK_QUARTET1 (MASK_QUARTET << SHIFT_QUARTET1)
+#undef MASK_QUARTET2
+#define MASK_QUARTET2 (MASK_QUARTET << SHIFT_QUARTET2)
+#undef MASK_QUARTET3
+#define MASK_QUARTET3 (MASK_QUARTET << SHIFT_QUARTET3)
+#undef MASK_QUARTET4
+#define MASK_QUARTET4 (MASK_QUARTET << SHIFT_QUARTET4)
+#undef MASK_QUARTET5
+#define MASK_QUARTET5 (MASK_QUARTET << SHIFT_QUARTET5)
+#undef MASK_QUARTET6
+#define MASK_QUARTET6 (MASK_QUARTET << SHIFT_QUARTET6)
+#undef MASK_QUARTET7
+#define MASK_QUARTET7 (MASK_QUARTET << SHIFT_QUARTET7)
+
+/*-----------------------------------------------------------------------------
+ * Byte shift definition
+ *---------------------------------------------------------------------------*/
+#undef MASK_BYTE
+#define MASK_BYTE (0xFFUL)
+#undef SHIFT_BYTE0
+#define SHIFT_BYTE0 0U
+#undef SHIFT_BYTE1
+#define SHIFT_BYTE1 8U
+#undef SHIFT_BYTE2
+#define SHIFT_BYTE2 16U
+#undef SHIFT_BYTE3
+#define SHIFT_BYTE3 24U
+#undef MASK_BYTE0
+#define MASK_BYTE0 (MASK_BYTE << SHIFT_BYTE0)
+#undef MASK_BYTE1
+#define MASK_BYTE1 (MASK_BYTE << SHIFT_BYTE1)
+#undef MASK_BYTE2
+#define MASK_BYTE2 (MASK_BYTE << SHIFT_BYTE2)
+#undef MASK_BYTE3
+#define MASK_BYTE3 (MASK_BYTE << SHIFT_BYTE3)
+
+/*-----------------------------------------------------------------------------
+ * Halfword shift definition
+ *---------------------------------------------------------------------------*/
+#undef MASK_HALFWORD
+#define MASK_HALFWORD (0xFFFFUL)
+#undef SHIFT_HALFWORD0
+#define SHIFT_HALFWORD0 0U
+#undef SHIFT_HALFWORD1
+#define SHIFT_HALFWORD1 16U
+#undef MASK_HALFWORD0
+#define MASK_HALFWORD0 (MASK_HALFWORD << SHIFT_HALFWORD0)
+#undef MASK_HALFWORD1
+#define MASK_HALFWORD1 (MASK_HALFWORD << SHIFT_HALFWORD1)
+
+#endif /* _COMMON_MACROS_H_ */
+
diff --git a/drivers/staging/nmf-cm/share/inc/nmf.h b/drivers/staging/nmf-cm/share/inc/nmf.h
new file mode 100644
index 00000000000..8be8b41e5e3
--- /dev/null
+++ b/drivers/staging/nmf-cm/share/inc/nmf.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Jean-Philippe FASSINO <jean-philippe.fassino@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2, with
+ * user space exemption described in the top-level COPYING file in
+ * the Linux kernel source tree.
+ */
+
+/*!
+ * \brief Common Nomadik Multiprocessing Framework type definition
+ *
+ * This file contains the shared type definitions used into NMF.
+ */
+
+#ifndef __INC_NMF_H
+#define __INC_NMF_H
+
+#include <inc/typedef.h>
+
+/*!
+ * \brief Identification of the various cores (host cpu and Media Processors) into Nomadik Platform
+ * In order to improve performance, these ids are those used to interconnect HW Semaphores IP with Cores (Interrupt lines)
+ * \ingroup NMF_COMMON
+ */
+#if defined(__STN_8500)
+ //#warning "TODO : mapping below is not correct, need to think how to change it"
+#endif
+typedef t_uint8 t_nmf_core_id;
+#define ARM_CORE_ID ((t_nmf_core_id)0) //!< HOST CPU Id
+#define SVA_CORE_ID ((t_nmf_core_id)1) //!< Smart Video Accelerator Media Processor Code Id
+#define SIA_CORE_ID ((t_nmf_core_id)2) //!< Smart Imaging Accelerator Media Processor Code Id
+#define NB_CORE_IDS ((t_nmf_core_id)3)
+
+#define FIRST_CORE_ID ((t_nmf_core_id)ARM_CORE_ID)
+#define FIRST_MPC_ID ((t_nmf_core_id)SVA_CORE_ID)
+#define LAST_CORE_ID ((t_nmf_core_id)SIA_CORE_ID)
+#define LAST_MPC_ID ((t_nmf_core_id)SIA_CORE_ID)
+
+
+/*!
+ * \brief Define minimal stack size use by execution engine
+ */
+#define MIN_STACK_SIZE 128
+
+
+
+#endif /* __INC_NMF_H */
diff --git a/drivers/staging/nmf-cm/share/inc/nomadik_mapping.h b/drivers/staging/nmf-cm/share/inc/nomadik_mapping.h
new file mode 100644
index 00000000000..bec221aa111
--- /dev/null
+++ b/drivers/staging/nmf-cm/share/inc/nomadik_mapping.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef __INC_NOMADIK_MAPPING_H
+#define __INC_NOMADIK_MAPPING_H
+
+/*--------------------------------------------------------------------------*/
+#if defined(__STN_8810)
+
+/* XTI (CPU OSMO/OSMOT address space) */
+#define XTI_CPU_BASE_ADDR 0x10000000
+#define XTI_CPU_END_ADDR 0x100FFFFF
+
+/* XTI configuration registers */
+#define XTI_CFG_REG_BASE_ADDR 0x101A0000
+#define XTI_CFG_REG_END_ADDR 0x101AFFFF
+
+/* Core APB Peripherals */
+#define CORE_APB_BASE_ADDR 0x101E0000
+#define CORE_APB_END_ADDR 0x101EFFFF
+
+/* DMA APB Peripherals */
+#define DMA_APB_BASE_ADDR 0x101F0000
+#define DMA_APB_END_ADDR 0x101FFFFF
+
+/* XTI (DSP OSMO/OSMOT address space) */
+#define XTI_DSP_BASE_ADDR 0x10200000
+#define XTI_DSP_END_ADDR 0x1020FFFF
+
+#endif /* defined(__STN_8810) */
+
+/*--------------------------------------------------------------------------*/
+#if defined(__STN_8815)
+
+/* XTI (CPU OSMO/OSMOT address space) */
+#define XTI_CPU_BASE_ADDR 0x10000000
+#define XTI_CPU_END_ADDR 0x100FFFFF
+
+/* XTI configuration registers */
+#define XTI_CFG_REG_BASE_ADDR 0x101A0000
+#define XTI_CFG_REG_END_ADDR 0x101AFFFF
+
+/* Core APB Peripherals */
+#define CORE_APB_BASE_ADDR 0x101E0000
+#define CORE_APB_END_ADDR 0x101EFFFF
+
+/* DMA APB Peripherals */
+#define DMA_APB_BASE_ADDR 0x101F0000
+#define DMA_APB_END_ADDR 0x101FFFFF
+
+/* XTI (DSP OSMO/OSMOT address space) */
+#define XTI_DSP_BASE_ADDR 0x10220000
+#define XTI_DSP_END_ADDR 0x1022FFFF
+
+#endif /* defined(__STN_8815) */
+
+
+/*--------------------------------------------------------------------------*/
+#if defined(__STN_8820)
+
+/* STM (System Trace Module address space) */
+#define STM_BASE_ADDR 0x700F0000
+#define STM_END_ADDR 0x700FFFFF
+
+/* AHB2 Peripherals */
+#define AHB2_PERIPH_BASE_ADDR 0x70100000
+#define AHB2_PERIPH_END_ADDR 0x7010FFFF
+
+/* APB2 Peripherals */
+#define APB2_PERIPH_BASE_ADDR 0x70110000
+#define APB2_PERIPH_END_ADDR 0x7011FFFF
+
+/* APB1 Peripherals */
+#define APB1_PERIPH_BASE_ADDR 0x70120000
+#define APB1_PERIPH_END_ADDR 0x7012FFFF
+
+#endif /* defined(__STN_8820) */
+
+/*--------------------------------------------------------------------------*/
+#if defined(__STN_8500)
+/* STM (System Trace Module address space) */
+#define STM_BASE_ADDR 0x80100000
+#define STM_END_ADDR 0x8010FFFF
+
+#define HSEM_BASE_ADDR 0x80140000
+#define HSEM_END_ADDR 0x8014FFFF
+
+#define DMA_CTRL_BASE_ADDR 0x801C0000
+#define DMA_CTRL_END_ADDR 0x801C0FFF
+
+
+#endif /* defined(__STN_8500) */
+
+#endif /*__INC_NOMADIK_MAPPING_H */
diff --git a/drivers/staging/nmf-cm/share/semaphores/inc/hwsem_hwp.h b/drivers/staging/nmf-cm/share/semaphores/inc/hwsem_hwp.h
new file mode 100644
index 00000000000..b573627beae
--- /dev/null
+++ b/drivers/staging/nmf-cm/share/semaphores/inc/hwsem_hwp.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef __INC_HWSEM_HWP_H
+#define __INC_HWSEM_HWP_H
+
+#include <share/semaphores/inc/semaphores.h>
+
+#define CORE_ID_2_HW_CORE_ID(coreId) (1U << (coreId))
+
+/*
+ * Definition of the number of hw semaphores into the Nomadik IP
+ */
+#define NUM_HW_SEMAPHORES 32
+
+
+/*
+ * Definition of how HSEM IP interrupts are interconnected with cores
+ */
+typedef enum {
+ HSEM_FIRST_INTR = 0,
+ HSEM_INTRA = HSEM_FIRST_INTR,
+ HSEM_INTRB = 1,
+ HSEM_INTRC = 2,
+ HSEM_INTRD = 3,
+ HSEM_INTRE = 4,
+ HSEM_MAX_INTR
+} t_hw_semaphore_irq_id;
+
+/*
+ * Description of the registers of the HW Sem IP
+ */
+#define HSEM_INTRA_MASK (1<<(4+HSEM_INTRA))
+#define HSEM_INTRB_MASK (1<<(4+HSEM_INTRB))
+#define HSEM_INTRC_MASK (1<<(4+HSEM_INTRC))
+#define HSEM_INTRD_MASK (1<<(4+HSEM_INTRD))
+#define HSEM_INTRE_MASK (1<<(4+HSEM_INTRE))
+
+typedef struct {
+ t_shared_reg imsc;
+ t_shared_reg ris;
+ t_shared_reg mis;
+ t_shared_reg icr;
+} t_hsem_it_regs;
+
+typedef volatile struct {
+#if defined(__STN_8500)
+ t_shared_reg cr;
+ t_shared_reg dummy;
+#endif
+ t_shared_reg sem[NUM_HW_SEMAPHORES];
+#if defined(__STN_8820)
+ t_shared_reg RESERVED1[(0x90 - 0x80)>>2];
+#elif defined(__STN_8500)
+ t_shared_reg RESERVED1[(0x90 - 0x88)>>2];
+#else /* __STN_8820 or __STN_8500 -> _STN_8815 */
+ t_shared_reg RESERVED1[(0x90 - 0x40)>>2];
+#endif /* __STN_8820 or __STN_8500 -> _STN_8815 */
+ t_shared_reg icrall;
+ t_shared_reg RESERVED2[(0xa0 - 0x94)>>2];
+ t_hsem_it_regs it[HSEM_MAX_INTR];
+#if defined(__STN_8820) || defined(__STN_8500)
+ t_shared_reg RESERVED3[(0x100 - 0xf0)>>2];
+#else /* __STN_8820 or __STN_8500 -> _STN_8815 */
+ t_shared_reg RESERVED3[(0x100 - 0xe0)>>2];
+#endif /* __STN_8820 or __STN_8500 -> _STN_8815 */
+ t_shared_reg itcr;
+ t_shared_reg RESERVED4;
+ t_shared_reg itop;
+ t_shared_reg RESERVED5[(0xfe0 - 0x10c)>>2];
+ t_shared_reg pid0;
+ t_shared_reg pid1;
+ t_shared_reg pid2;
+ t_shared_reg pid3;
+ t_shared_reg pcid0;
+ t_shared_reg pcid1;
+ t_shared_reg pcid2;
+ t_shared_reg pcid3;
+} t_hw_semaphore_regs, *tp_hw_semaphore_regs;
+
+#endif /* __INC_HWSEM_HWP_H */
diff --git a/drivers/staging/nmf-cm/share/semaphores/inc/semaphores.h b/drivers/staging/nmf-cm/share/semaphores/inc/semaphores.h
new file mode 100644
index 00000000000..c72b64cd709
--- /dev/null
+++ b/drivers/staging/nmf-cm/share/semaphores/inc/semaphores.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ */
+
+#ifndef __INC_SHARED_SEMAPHORE_H
+#define __INC_SHARED_SEMAPHORE_H
+
+#include <share/inc/nmf.h>
+
+typedef t_uint16 t_semaphore_id;
+
+/*
+ * HW semaphore allocation
+ * -----------------------
+ * We want to optimize interrupt demultiplexing at dsp interrupt handler level
+ * so a good solution would be to have sequentially the semaphores for each neighbors
+ *
+ * STn8500 :
+ * ---------
+ * ARM <- SVA COMS => 0
+ * ARM <- SIA COMS => 1
+ * SVA <- ARM COMS => 2
+ * SVA <- SIA COMS => 3
+ * SIA <- ARM COMS => 4
+ * SIA <- SVA COMS => 5
+
+ * The first neighbor is always the ARM, then the other ones (SVA,SIA)
+ */
+
+/*
+ * Local semaphore allocation
+ * -----------------------
+ * 0 : ARM <- DSP
+ * 1 : DSP <- ARM
+ */
+
+#define NB_USED_HSEM_PER_CORE (NB_CORE_IDS - 1)
+#define FIRST_NEIGHBOR_SEMID(coreId) ((coreId)*NB_USED_HSEM_PER_CORE)
+
+#endif /* __INC_SHARED_SEMAPHORE_H */
diff --git a/drivers/staging/ste_rmi4/Kconfig b/drivers/staging/ste_rmi4/Kconfig
new file mode 100644
index 00000000000..e8679509e52
--- /dev/null
+++ b/drivers/staging/ste_rmi4/Kconfig
@@ -0,0 +1,9 @@
+config TOUCHSCREEN_SYNAPTICS_I2C_RMI4
+ tristate "Synaptics i2c rmi4 touchscreen"
+ depends on I2C && INPUT
+ help
+ Say Y here if you have a Synaptics RMI4 and
+ want to enable support for the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called synaptics_rmi4_ts.
diff --git a/drivers/staging/ste_rmi4/Makefile b/drivers/staging/ste_rmi4/Makefile
new file mode 100644
index 00000000000..6cce2ed187e
--- /dev/null
+++ b/drivers/staging/ste_rmi4/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for the RMI4 touchscreen driver.
+#
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += synaptics_i2c_rmi4.o
diff --git a/drivers/staging/ste_rmi4/TODO b/drivers/staging/ste_rmi4/TODO
new file mode 100644
index 00000000000..9be2437da85
--- /dev/null
+++ b/drivers/staging/ste_rmi4/TODO
@@ -0,0 +1,7 @@
+TODO
+----
+
+Wait for the official upstream synaptics rmi4 clearpad drivers as promised over the past few months
+Merge any device support needed from this driver into it
+Delete this driver
+
diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c
new file mode 100644
index 00000000000..871ff199506
--- /dev/null
+++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c
@@ -0,0 +1,1367 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) I2C Physical Layer Driver.
+ * Copyright (c) 2007-2010, Synaptics Incorporated
+ *
+ * Author: Js HA <js.ha@stericsson.com> for ST-Ericsson
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * Copyright 2010 (c) ST-Ericsson SA
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/earlysuspend.h>
+#include <linux/input/synaptics_i2c_rmi4.h>
+
+/* TODO: for multiple device support will need a per-device mutex */
+#define DRIVER_NAME "synaptics_rmi4_i2c"
+
+#define DELTA 8
+#define MAX_ERROR_REPORT 6
+#define TIMEOUT_PERIOD 1
+#define MAX_WIDTH_MAJOR 255
+#define MAX_RETRY_COUNT 5
+#define STD_QUERY_LEN 21
+#define PAGE_LEN 2
+#define DATA_BUF_LEN 32
+#define BUF_LEN 37
+#define QUERY_LEN 9
+#define DATA_LEN 12
+#define RESUME_DELAY 100 /* msecs */
+#define HAS_TAP 0x01
+#define HAS_PALMDETECT 0x01
+#define HAS_ROTATE 0x02
+#define HAS_TAPANDHOLD 0x02
+#define HAS_DOUBLETAP 0x04
+#define HAS_EARLYTAP 0x08
+#define HAS_RELEASE 0x08
+#define HAS_FLICK 0x10
+#define HAS_PRESS 0x20
+#define HAS_PINCH 0x40
+
+#define MASK_16BIT 0xFFFF
+#define MASK_8BIT 0xFF
+#define MASK_7BIT 0x7F
+#define MASK_5BIT 0x1F
+#define MASK_4BIT 0x0F
+#define MASK_3BIT 0x07
+#define MASK_2BIT 0x03
+#define TOUCHPAD_CTRL_INTR 0x8
+#define PDT_START_SCAN_LOCATION (0x00E9)
+#define PDT_END_SCAN_LOCATION (0x000A)
+#define PDT_ENTRY_SIZE (0x0006)
+#define RMI4_NUMBER_OF_MAX_FINGERS (8)
+#define SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM (0x11)
+#define SYNAPTICS_RMI4_DEVICE_CONTROL_FUNC_NUM (0x01)
+
+/**
+ * struct synaptics_rmi4_fn_desc - contains the funtion descriptor information
+ * @query_base_addr: base address for query
+ * @cmd_base_addr: base address for command
+ * @ctrl_base_addr: base address for control
+ * @data_base_addr: base address for data
+ * @intr_src_count: count for the interrupt source
+ * @fn_number: function number
+ *
+ * This structure is used to gives the function descriptor information
+ * of the particular functionality.
+ */
+struct synaptics_rmi4_fn_desc {
+ unsigned char query_base_addr;
+ unsigned char cmd_base_addr;
+ unsigned char ctrl_base_addr;
+ unsigned char data_base_addr;
+ unsigned char intr_src_count;
+ unsigned char fn_number;
+};
+
+/**
+ * struct synaptics_rmi4_fn - contains the funtion information
+ * @fn_number: function number
+ * @num_of_data_sources: number of data sources
+ * @num_of_data_points: number of fingers touched
+ * @size_of_data_register_block: data register block size
+ * @index_to_intr_reg: index for interrupt register
+ * @intr_mask: interrupt mask value
+ * @fn_desc: variable for function descriptor structure
+ * @link: linked list for function descriptors
+ *
+ * This structure gives information about the number of data sources and
+ * the number of data registers associated with the function.
+ */
+struct synaptics_rmi4_fn {
+ unsigned char fn_number;
+ unsigned char num_of_data_sources;
+ unsigned char num_of_data_points;
+ unsigned char size_of_data_register_block;
+ unsigned char index_to_intr_reg;
+ unsigned char intr_mask;
+ struct synaptics_rmi4_fn_desc fn_desc;
+ struct list_head link;
+};
+
+/**
+ * struct synaptics_rmi4_device_info - contains the rmi4 device information
+ * @version_major: protocol major version number
+ * @version_minor: protocol minor version number
+ * @manufacturer_id: manufacturer identification byte
+ * @product_props: product properties information
+ * @product_info: product info array
+ * @date_code: device manufacture date
+ * @tester_id: tester id array
+ * @serial_number: serial number for that device
+ * @product_id_string: product id for the device
+ * @support_fn_list: linked list for device information
+ *
+ * This structure gives information about the number of data sources and
+ * the number of data registers associated with the function.
+ */
+struct synaptics_rmi4_device_info {
+ unsigned int version_major;
+ unsigned int version_minor;
+ unsigned char manufacturer_id;
+ unsigned char product_props;
+ unsigned char product_info[2];
+ unsigned char date_code[3];
+ unsigned short tester_id;
+ unsigned short serial_number;
+ unsigned char product_id_string[11];
+ struct list_head support_fn_list;
+};
+
+/**
+ * struct synaptics_rmi4_data - contains the rmi4 device data
+ * @rmi4_mod_info: structure variable for rmi4 device info
+ * @input_dev: pointer for input device
+ * @i2c_client: pointer for i2c client
+ * @board: constant pointer for touch platform data
+ * @fn_list_mutex: mutex for funtion list
+ * @rmi4_page_mutex: mutex for rmi4 page
+ * @current_page: variable for integer
+ * @number_of_interrupt_register: interrupt registers count
+ * @fn01_ctrl_base_addr: control base address for fn01
+ * @fn01_query_base_addr: query base address for fn01
+ * @fn01_data_base_addr: data base address for fn01
+ * @sensor_max_x: sensor maximum x value
+ * @sensor_max_y: sensor maximum y value
+ * @regulator: pointer to the regulator structure
+ * @wait: wait queue structure variable
+ * @touch_stopped: flag to stop the thread function
+ * @enable: flag to enable/disable the driver event.
+ * @resume_wq_handler: work queue for resume the device
+ * @early_suspend: early suspend structure variable
+ *
+ * This structure gives the device data information.
+ */
+struct synaptics_rmi4_data {
+ struct synaptics_rmi4_device_info rmi4_mod_info;
+ struct input_dev *input_dev;
+ struct i2c_client *i2c_client;
+ const struct synaptics_rmi4_platform_data *board;
+ struct mutex fn_list_mutex;
+ struct mutex rmi4_page_mutex;
+ int current_page;
+ unsigned int number_of_interrupt_register;
+ unsigned short fn01_ctrl_base_addr;
+ unsigned short fn01_query_base_addr;
+ unsigned short fn01_data_base_addr;
+ int sensor_max_x;
+ int sensor_max_y;
+ struct regulator *regulator;
+ wait_queue_head_t wait;
+ bool touch_stopped;
+ bool enable;
+ struct work_struct resume_wq_handler;
+ struct early_suspend early_suspend;
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void synaptics_rmi4_early_suspend(struct early_suspend *data);
+static void synaptics_rmi4_late_resume(struct early_suspend *data);
+#endif
+
+/**
+ * synaptics_rmi4_set_page() - sets the page
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @address: set the address of the page
+ *
+ * This function is used to set the page and returns integer.
+ */
+static int synaptics_rmi4_set_page(struct synaptics_rmi4_data *pdata,
+ unsigned int address)
+{
+ unsigned char txbuf[PAGE_LEN];
+ int retval;
+ unsigned int page;
+ struct i2c_client *i2c = pdata->i2c_client;
+
+ page = ((address >> 8) & MASK_8BIT);
+ if (page != pdata->current_page) {
+ txbuf[0] = MASK_8BIT;
+ txbuf[1] = page;
+ retval = i2c_master_send(i2c, txbuf, PAGE_LEN);
+ if (retval != PAGE_LEN)
+ dev_err(&i2c->dev, "%s:failed:%d\n", __func__, retval);
+ else
+ pdata->current_page = page;
+ } else
+ retval = PAGE_LEN;
+ return retval;
+}
+/**
+ * synaptics_rmi4_i2c_block_read() - read the block of data
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @address: read the block of data from this offset
+ * @valp: pointer to a buffer containing the data to be read
+ * @size: number of bytes to read
+ *
+ * This function is to read the block of data and returns integer.
+ */
+static int synaptics_rmi4_i2c_block_read(struct synaptics_rmi4_data *pdata,
+ unsigned short address,
+ unsigned char *valp, int size)
+{
+ int retval = 0;
+ int retry_count = 0;
+ int index;
+ struct i2c_client *i2c = pdata->i2c_client;
+
+ mutex_lock(&(pdata->rmi4_page_mutex));
+ retval = synaptics_rmi4_set_page(pdata, address);
+ if (retval != PAGE_LEN)
+ goto exit;
+ index = address & MASK_8BIT;
+retry:
+ retval = i2c_smbus_read_i2c_block_data(i2c, index, size, valp);
+ if (retval != size) {
+ if (++retry_count == MAX_RETRY_COUNT)
+ dev_err(&i2c->dev,
+ "%s:address 0x%04x size %d failed:%d\n",
+ __func__, address, size, retval);
+ else {
+ synaptics_rmi4_set_page(pdata, address);
+ goto retry;
+ }
+ }
+exit:
+ mutex_unlock(&(pdata->rmi4_page_mutex));
+ return retval;
+}
+
+/**
+ * synaptics_rmi4_i2c_byte_write() - write the single byte data
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @address: write the block of data from this offset
+ * @data: data to be write
+ *
+ * This function is to write the single byte data and returns integer.
+ */
+static int synaptics_rmi4_i2c_byte_write(struct synaptics_rmi4_data *pdata,
+ unsigned short address,
+ unsigned char data)
+{
+ unsigned char txbuf[2];
+ int retval = 0;
+ struct i2c_client *i2c = pdata->i2c_client;
+
+ /* Can't have anyone else changing the page behind our backs */
+ mutex_lock(&(pdata->rmi4_page_mutex));
+
+ retval = synaptics_rmi4_set_page(pdata, address);
+ if (retval != PAGE_LEN)
+ goto exit;
+ txbuf[0] = address & MASK_8BIT;
+ txbuf[1] = data;
+ retval = i2c_master_send(pdata->i2c_client, txbuf, 2);
+ /* Add in retry on writes only in certian error return values */
+ if (retval != 2) {
+ dev_err(&i2c->dev, "%s:failed:%d\n", __func__, retval);
+ retval = -EIO;
+ } else
+ retval = 1;
+exit:
+ mutex_unlock(&(pdata->rmi4_page_mutex));
+ return retval;
+}
+
+/**
+ * synaptics_rmi4_enable() - enable the touchpad driver event
+ * @pdata: pointer to synaptics_rmi4_data structure
+ *
+ * This function is to enable the touchpad driver event and returns integer.
+ */
+static int synaptics_rmi4_enable(struct synaptics_rmi4_data *pdata)
+{
+ int retval;
+ unsigned char intr_status;
+
+ if (pdata->board->regulator_en)
+ regulator_enable(pdata->regulator);
+ enable_irq(pdata->board->irq_number);
+ pdata->touch_stopped = false;
+
+ msleep(RESUME_DELAY);
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ pdata->fn01_data_base_addr + 1,
+ &intr_status,
+ pdata->number_of_interrupt_register);
+ if (retval < 0)
+ return retval;
+
+ retval = synaptics_rmi4_i2c_byte_write(pdata,
+ pdata->fn01_ctrl_base_addr + 1,
+ (intr_status | TOUCHPAD_CTRL_INTR));
+ if (retval < 0)
+ return retval;
+
+ return 0;
+}
+
+/**
+ * synaptics_rmi4_disable() - disable the touchpad driver event
+ * @pdata: pointer to synaptics_rmi4_data structure
+ *
+ * This function is to disable the driver event and returns integer.
+ */
+
+static int synaptics_rmi4_disable(struct synaptics_rmi4_data *pdata)
+{
+ int retval;
+ unsigned char intr_status;
+
+ pdata->touch_stopped = true;
+ disable_irq(pdata->board->irq_number);
+
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ pdata->fn01_data_base_addr + 1,
+ &intr_status,
+ pdata->number_of_interrupt_register);
+ if (retval < 0)
+ return retval;
+
+ retval = synaptics_rmi4_i2c_byte_write(pdata,
+ pdata->fn01_ctrl_base_addr + 1,
+ (intr_status & ~TOUCHPAD_CTRL_INTR));
+ if (retval < 0)
+ return retval;
+ if (pdata->board->regulator_en)
+ regulator_disable(pdata->regulator);
+
+ return 0;
+}
+
+/**
+ * synaptics_rmi4_show_attr_enable() - show the touchpad enable value
+ * @dev: pointer to device data structure
+ * @attr: pointer to attribute structure
+ * @buf: pointer to character buffer
+ *
+ * This function is to show the touchpad enable value and returns ssize_t.
+ */
+static ssize_t synaptics_rmi4_show_attr_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *pdata = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", pdata->enable);
+}
+
+/**
+ * synaptics_rmi4_store_attr_enable() - store the touchpad enable value
+ * @dev: pointer to device data structure
+ * @attr: pointer to attribute structure
+ * @buf: pointer to character buffer
+ * @count: number fo arguments
+ *
+ * This function is to store the touchpad enable value and returns ssize_t.
+ */
+static ssize_t synaptics_rmi4_store_attr_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct synaptics_rmi4_data *pdata = dev_get_drvdata(dev);
+ unsigned long val;
+ int retval = 0;
+
+ if (strict_strtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if ((val != 0) && (val != 1))
+ return -EINVAL;
+
+ if (pdata->enable != val) {
+ pdata->enable = val ? true : false;
+ if (pdata->enable)
+ retval = synaptics_rmi4_enable(pdata);
+ else
+ retval = synaptics_rmi4_disable(pdata);
+
+ }
+ return ((retval < 0) ? retval : count);
+}
+
+static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
+ synaptics_rmi4_show_attr_enable, synaptics_rmi4_store_attr_enable);
+
+static struct attribute *synaptics_rmi4_attrs[] = {
+ &dev_attr_enable.attr,
+ NULL,
+};
+
+static struct attribute_group synaptics_rmi4_attr_group = {
+ .attrs = synaptics_rmi4_attrs,
+};
+
+/**
+ * synpatics_rmi4_touchpad_report() - reports for the rmi4 touchpad device
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @rfi: pointer to synaptics_rmi4_fn structure
+ *
+ * This function calls to reports for the rmi4 touchpad device
+ */
+static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
+ struct synaptics_rmi4_fn *rfi)
+{
+ /* number of touch points - fingers down in this case */
+ int touch_count = 0;
+ int finger;
+ int fingers_supported;
+ int finger_registers;
+ int reg;
+ int finger_shift;
+ int finger_status;
+ int retval;
+ unsigned short data_base_addr;
+ unsigned short data_offset;
+ unsigned char data_reg_blk_size;
+ unsigned char values[2];
+ unsigned char data[DATA_LEN];
+ int x[RMI4_NUMBER_OF_MAX_FINGERS];
+ int y[RMI4_NUMBER_OF_MAX_FINGERS];
+ int w[RMI4_NUMBER_OF_MAX_FINGERS];
+ static int prv_x[RMI4_NUMBER_OF_MAX_FINGERS];
+ static int prv_y[RMI4_NUMBER_OF_MAX_FINGERS];
+ struct i2c_client *client = pdata->i2c_client;
+
+ /* get 2D sensor finger data */
+ /*
+ * First get the finger status field - the size of the finger status
+ * field is determined by the number of finger supporte - 2 bits per
+ * finger, so the number of registers to read is:
+ * registerCount = ceil(numberOfFingers/4).
+ * Read the required number of registers and check each 2 bit field to
+ * determine if a finger is down:
+ * 00 = finger not present,
+ * 01 = finger present and data accurate,
+ * 10 = finger present but data may not be accurate,
+ * 11 = reserved for product use.
+ */
+ fingers_supported = rfi->num_of_data_points;
+ finger_registers = (fingers_supported + 3)/4;
+ data_base_addr = rfi->fn_desc.data_base_addr;
+ retval = synaptics_rmi4_i2c_block_read(pdata, data_base_addr, values,
+ finger_registers);
+ if (retval != finger_registers) {
+ dev_err(&client->dev, "%s:read status registers failed\n",
+ __func__);
+ return 0;
+ }
+ /*
+ * For each finger present, read the proper number of registers
+ * to get absolute data.
+ */
+ data_reg_blk_size = rfi->size_of_data_register_block;
+ for (finger = 0; finger < fingers_supported; finger++) {
+ /* determine which data byte the finger status is in */
+ reg = finger/4;
+ /* bit shift to get finger's status */
+ finger_shift = (finger % 4) * 2;
+ finger_status = (values[reg] >> finger_shift) & 3;
+ /*
+ * if finger status indicates a finger is present then
+ * read the finger data and report it
+ */
+ if (finger_status == 1 || finger_status == 2) {
+ /* Read the finger data */
+ data_offset = data_base_addr +
+ ((finger * data_reg_blk_size) +
+ finger_registers);
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ data_offset, data,
+ data_reg_blk_size);
+ if (retval != data_reg_blk_size) {
+ printk(KERN_ERR "%s:read data failed\n",
+ __func__);
+ return 0;
+ } else {
+ x[touch_count] =
+ (data[0] << 4) | (data[2] & MASK_4BIT);
+ y[touch_count] =
+ (data[1] << 4) |
+ ((data[2] >> 4) & MASK_4BIT);
+ w[touch_count] = data[3];
+ if (pdata->board->x_flip)
+ x[touch_count] =
+ pdata->sensor_max_x -
+ x[touch_count];
+ if (pdata->board->y_flip)
+ y[touch_count] =
+ pdata->sensor_max_y -
+ y[touch_count];
+ if (x[touch_count] < 0)
+ x[touch_count] = 0;
+ else if (x[touch_count] >= pdata->sensor_max_x)
+ x[touch_count] =
+ pdata->sensor_max_x - 1;
+
+ if (y[touch_count] < 0)
+ y[touch_count] = 0;
+ else if (y[touch_count] >= pdata->sensor_max_y)
+ y[touch_count] =
+ pdata->sensor_max_y - 1;
+ }
+ if ((abs(x[finger] - prv_x[finger]) < DELTA) &&
+ (abs(y[finger] - prv_y[finger]) < DELTA)) {
+ x[finger] = prv_x[finger];
+ y[finger] = prv_y[finger];
+ } else {
+ prv_x[finger] = x[finger];
+ prv_y[finger] = y[finger];
+ }
+ /* number of active touch points */
+ touch_count++;
+ }
+ }
+
+ /* report to input subsystem */
+ if (touch_count) {
+ for (finger = 0; finger < touch_count; finger++) {
+ input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR,
+ max(x[finger] , y[finger]));
+ input_report_abs(pdata->input_dev, ABS_MT_WIDTH_MAJOR,
+ w[finger]);
+ input_report_abs(pdata->input_dev, ABS_MT_POSITION_X,
+ x[finger]);
+ input_report_abs(pdata->input_dev, ABS_MT_POSITION_Y,
+ y[finger]);
+ input_mt_sync(pdata->input_dev);
+ }
+ } else
+ input_mt_sync(pdata->input_dev);
+
+ /* sync after groups of events */
+ input_sync(pdata->input_dev);
+ /* return the number of touch points */
+ return touch_count;
+}
+
+/**
+ * synaptics_rmi4_report_device() - reports the rmi4 device
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @rfi: pointer to synaptics_rmi4_fn
+ *
+ * This function is used to call the report function of the rmi4 device.
+ */
+static int synaptics_rmi4_report_device(struct synaptics_rmi4_data *pdata,
+ struct synaptics_rmi4_fn *rfi)
+{
+ int touch = 0;
+ struct i2c_client *client = pdata->i2c_client;
+ static int num_error_reports;
+ if (rfi->fn_number != SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM) {
+ num_error_reports++;
+ if (num_error_reports < MAX_ERROR_REPORT)
+ dev_err(&client->dev, "%s:report not supported\n",
+ __func__);
+ } else
+ touch = synpatics_rmi4_touchpad_report(pdata, rfi);
+ return touch;
+}
+/**
+ * synaptics_rmi4_sensor_report() - reports to input subsystem
+ * @pdata: pointer to synaptics_rmi4_data structure
+ *
+ * This function is used to reads in all data sources and reports
+ * them to the input subsystem.
+ */
+static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *pdata)
+{
+ unsigned char intr_status[4];
+ /* number of touch points - fingers or buttons */
+ int touch = 0;
+ unsigned int retval;
+ struct synaptics_rmi4_fn *rfi;
+ struct synaptics_rmi4_device_info *rmi;
+ struct i2c_client *client = pdata->i2c_client;
+
+ /*
+ * Get the interrupt status from the function $01
+ * control register+1 to find which source(s) were interrupting
+ * so we can read the data from the source(s) (2D sensor, buttons..)
+ */
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ pdata->fn01_data_base_addr + 1,
+ intr_status,
+ pdata->number_of_interrupt_register);
+ if (retval != pdata->number_of_interrupt_register) {
+ dev_err(&client->dev,
+ "could not read interrupt status registers\n");
+ return 0;
+ }
+ /*
+ * check each function that has data sources and if the interrupt for
+ * that triggered then call that RMI4 functions report() function to
+ * gather data and report it to the input subsystem
+ */
+ rmi = &(pdata->rmi4_mod_info);
+ list_for_each_entry(rfi, &rmi->support_fn_list, link) {
+ if (rfi->num_of_data_sources) {
+ if (intr_status[rfi->index_to_intr_reg] &
+ rfi->intr_mask)
+ touch = synaptics_rmi4_report_device(pdata,
+ rfi);
+ }
+ }
+ /* return the number of touch points */
+ return touch;
+}
+
+/**
+ * synaptics_rmi4_irq() - thread function for rmi4 attention line
+ * @irq: irq value
+ * @data: void pointer
+ *
+ * This function is interrupt thread function. It just notifies the
+ * application layer that attention is required.
+ */
+static irqreturn_t synaptics_rmi4_irq(int irq, void *data)
+{
+ struct synaptics_rmi4_data *pdata = data;
+ int touch_count;
+ do {
+ touch_count = synaptics_rmi4_sensor_report(pdata);
+ if (touch_count)
+ wait_event_timeout(pdata->wait, pdata->touch_stopped,
+ msecs_to_jiffies(TIMEOUT_PERIOD));
+ else
+ break;
+ } while (!pdata->touch_stopped);
+ return IRQ_HANDLED;
+}
+
+/**
+ * synpatics_rmi4_touchpad_detect() - detects the rmi4 touchpad device
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @rfi: pointer to synaptics_rmi4_fn structure
+ * @fd: pointer to synaptics_rmi4_fn_desc structure
+ * @interruptcount: count the number of interrupts
+ *
+ * This function calls to detects the rmi4 touchpad device
+ */
+static int synpatics_rmi4_touchpad_detect(struct synaptics_rmi4_data *pdata,
+ struct synaptics_rmi4_fn *rfi,
+ struct synaptics_rmi4_fn_desc *fd,
+ unsigned int interruptcount)
+{
+ unsigned char queries[QUERY_LEN];
+ unsigned short intr_offset;
+ unsigned char abs_data_size;
+ unsigned char abs_data_blk_size;
+ unsigned char egr_0, egr_1;
+ unsigned int all_data_blk_size;
+ int has_pinch, has_flick, has_tap;
+ int has_tapandhold, has_doubletap;
+ int has_earlytap, has_press;
+ int has_palmdetect, has_rotate;
+ int has_rel;
+ int i;
+ int retval;
+ struct i2c_client *client = pdata->i2c_client;
+
+ rfi->fn_desc.query_base_addr = fd->query_base_addr;
+ rfi->fn_desc.data_base_addr = fd->data_base_addr;
+ rfi->fn_desc.intr_src_count = fd->intr_src_count;
+ rfi->fn_desc.fn_number = fd->fn_number;
+ rfi->fn_number = fd->fn_number;
+ rfi->num_of_data_sources = fd->intr_src_count;
+ rfi->fn_desc.ctrl_base_addr = fd->ctrl_base_addr;
+ rfi->fn_desc.cmd_base_addr = fd->cmd_base_addr;
+
+ /*
+ * need to get number of fingers supported, data size, etc.
+ * to be used when getting data since the number of registers to
+ * read depends on the number of fingers supported and data size.
+ */
+ retval = synaptics_rmi4_i2c_block_read(pdata, fd->query_base_addr,
+ queries,
+ sizeof(queries));
+ if (retval != sizeof(queries)) {
+ dev_err(&client->dev, "%s:read function query registers\n",
+ __func__);
+ return retval;
+ }
+ /*
+ * 2D data sources have only 3 bits for the number of fingers
+ * supported - so the encoding is a bit wierd.
+ */
+ if ((queries[1] & MASK_3BIT) <= 4)
+ /* add 1 since zero based */
+ rfi->num_of_data_points = (queries[1] & MASK_3BIT) + 1;
+ else {
+ /*
+ * a value of 5 is up to 10 fingers - 6 and 7 are reserved
+ * (shouldn't get these i int retval;n a normal 2D source).
+ */
+ if ((queries[1] & MASK_3BIT) == 5)
+ rfi->num_of_data_points = 10;
+ }
+ /* Need to get interrupt info for handling interrupts */
+ rfi->index_to_intr_reg = (interruptcount + 7)/8;
+ if (rfi->index_to_intr_reg != 0)
+ rfi->index_to_intr_reg -= 1;
+ /*
+ * loop through interrupts for each source in fn $11
+ * and or in a bit to the interrupt mask for each.
+ */
+ intr_offset = interruptcount % 8;
+ rfi->intr_mask = 0;
+ for (i = intr_offset;
+ i < ((fd->intr_src_count & MASK_3BIT) + intr_offset); i++)
+ rfi->intr_mask |= 1 << i;
+
+ /* Size of just the absolute data for one finger */
+ abs_data_size = queries[5] & MASK_2BIT;
+ /* One each for X and Y, one for LSB for X & Y, one for W, one for Z */
+ abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0));
+ rfi->size_of_data_register_block = abs_data_blk_size;
+
+ /*
+ * need to determine the size of data to read - this depends on
+ * conditions such as whether Relative data is reported and if Gesture
+ * data is reported.
+ */
+ egr_0 = queries[7];
+ egr_1 = queries[8];
+
+ /*
+ * Get info about what EGR data is supported, whether it has
+ * Relative data supported, etc.
+ */
+ has_pinch = egr_0 & HAS_PINCH;
+ has_flick = egr_0 & HAS_FLICK;
+ has_tap = egr_0 & HAS_TAP;
+ has_earlytap = egr_0 & HAS_EARLYTAP;
+ has_press = egr_0 & HAS_PRESS;
+ has_rotate = egr_1 & HAS_ROTATE;
+ has_rel = queries[1] & HAS_RELEASE;
+ has_tapandhold = egr_0 & HAS_TAPANDHOLD;
+ has_doubletap = egr_0 & HAS_DOUBLETAP;
+ has_palmdetect = egr_1 & HAS_PALMDETECT;
+
+ /*
+ * Size of all data including finger status, absolute data for each
+ * finger, relative data and EGR data
+ */
+ all_data_blk_size =
+ /* finger status, four fingers per register */
+ ((rfi->num_of_data_points + 3) / 4) +
+ /* absolute data, per finger times number of fingers */
+ (abs_data_blk_size * rfi->num_of_data_points) +
+ /*
+ * two relative registers (if relative is being reported)
+ */
+ 2 * has_rel +
+ /*
+ * F11_2D_data8 is only present if the egr_0
+ * register is non-zero.
+ */
+ !!(egr_0) +
+ /*
+ * F11_2D_data9 is only present if either egr_0 or
+ * egr_1 registers are non-zero.
+ */
+ (egr_0 || egr_1) +
+ /*
+ * F11_2D_data10 is only present if EGR_PINCH or EGR_FLICK of
+ * egr_0 reports as 1.
+ */
+ !!(has_pinch | has_flick) +
+ /*
+ * F11_2D_data11 and F11_2D_data12 are only present if
+ * EGR_FLICK of egr_0 reports as 1.
+ */
+ 2 * !!(has_flick);
+ return retval;
+}
+
+/**
+ * synpatics_rmi4_touchpad_config() - confiures the rmi4 touchpad device
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @rfi: pointer to synaptics_rmi4_fn structure
+ *
+ * This function calls to confiures the rmi4 touchpad device
+ */
+int synpatics_rmi4_touchpad_config(struct synaptics_rmi4_data *pdata,
+ struct synaptics_rmi4_fn *rfi)
+{
+ /*
+ * For the data source - print info and do any
+ * source specific configuration.
+ */
+ unsigned char data[BUF_LEN];
+ int retval = 0;
+ struct i2c_client *client = pdata->i2c_client;
+
+ /* Get and print some info about the data source... */
+ /* To Query 2D devices we need to read from the address obtained
+ * from the function descriptor stored in the RMI function info.
+ */
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ rfi->fn_desc.query_base_addr,
+ data, QUERY_LEN);
+ if (retval != QUERY_LEN)
+ dev_err(&client->dev, "%s:read query registers failed\n",
+ __func__);
+ else {
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ rfi->fn_desc.ctrl_base_addr,
+ data, DATA_BUF_LEN);
+ if (retval != DATA_BUF_LEN) {
+ dev_err(&client->dev,
+ "%s:read control registers failed\n",
+ __func__);
+ return retval;
+ }
+ /* Store these for use later*/
+ pdata->sensor_max_x = ((data[6] & MASK_8BIT) << 0) |
+ ((data[7] & MASK_4BIT) << 8);
+ pdata->sensor_max_y = ((data[8] & MASK_5BIT) << 0) |
+ ((data[9] & MASK_4BIT) << 8);
+ }
+ return retval;
+}
+
+/**
+ * synaptics_rmi4_i2c_query_device() - query the rmi4 device
+ * @pdata: pointer to synaptics_rmi4_data structure
+ *
+ * This function is used to query the rmi4 device.
+ */
+static int synaptics_rmi4_i2c_query_device(struct synaptics_rmi4_data *pdata)
+{
+ int i;
+ int retval;
+ unsigned char std_queries[STD_QUERY_LEN];
+ unsigned char intr_count = 0;
+ int data_sources = 0;
+ unsigned int ctrl_offset;
+ struct synaptics_rmi4_fn *rfi;
+ struct synaptics_rmi4_fn_desc rmi_fd;
+ struct synaptics_rmi4_device_info *rmi;
+ struct i2c_client *client = pdata->i2c_client;
+
+ /*
+ * init the physical drivers RMI module
+ * info list of functions
+ */
+ INIT_LIST_HEAD(&pdata->rmi4_mod_info.support_fn_list);
+
+ /*
+ * Read the Page Descriptor Table to determine what functions
+ * are present
+ */
+ for (i = PDT_START_SCAN_LOCATION; i > PDT_END_SCAN_LOCATION;
+ i -= PDT_ENTRY_SIZE) {
+ retval = synaptics_rmi4_i2c_block_read(pdata, i,
+ (unsigned char *)&rmi_fd,
+ sizeof(rmi_fd));
+ if (retval != sizeof(rmi_fd)) {
+ /* failed to read next PDT entry */
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+ rfi = NULL;
+ if (rmi_fd.fn_number) {
+ switch (rmi_fd.fn_number & MASK_8BIT) {
+ case SYNAPTICS_RMI4_DEVICE_CONTROL_FUNC_NUM:
+ pdata->fn01_query_base_addr =
+ rmi_fd.query_base_addr;
+ pdata->fn01_ctrl_base_addr =
+ rmi_fd.ctrl_base_addr;
+ pdata->fn01_data_base_addr =
+ rmi_fd.data_base_addr;
+ break;
+ case SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM:
+ if (rmi_fd.intr_src_count) {
+ rfi = kmalloc(sizeof(*rfi),
+ GFP_KERNEL);
+ if (!rfi) {
+ dev_err(&client->dev,
+ "%s:kmalloc failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+ retval = synpatics_rmi4_touchpad_detect
+ (pdata, rfi,
+ &rmi_fd,
+ intr_count);
+ if (retval < 0) {
+ kfree(rfi);
+ return retval;
+ }
+ }
+ break;
+ }
+ /* interrupt count for next iteration */
+ intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
+ /*
+ * We only want to add functions to the list
+ * that have data associated with them.
+ */
+ if (rfi && rmi_fd.intr_src_count) {
+ /* link this function info to the RMI module */
+ mutex_lock(&(pdata->fn_list_mutex));
+ list_add_tail(&rfi->link,
+ &pdata->rmi4_mod_info.support_fn_list);
+ mutex_unlock(&(pdata->fn_list_mutex));
+ }
+ } else {
+ /*
+ * A zero in the function number
+ * signals the end of the PDT
+ */
+ dev_dbg(&client->dev,
+ "%s:end of PDT\n", __func__);
+ break;
+ }
+ }
+ /*
+ * calculate the interrupt register count - used in the
+ * ISR to read the correct number of interrupt registers
+ */
+ pdata->number_of_interrupt_register = (intr_count + 7) / 8;
+ /*
+ * Function $01 will be used to query the product properties,
+ * and product ID so we had to read the PDT above first to get
+ * the Fn $01 query address and prior to filling in the product
+ * info. NOTE: Even an unflashed device will still have FN $01.
+ */
+
+ /* Load up the standard queries and get the RMI4 module info */
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ pdata->fn01_query_base_addr,
+ std_queries,
+ sizeof(std_queries));
+ if (retval != sizeof(std_queries)) {
+ dev_err(&client->dev, "%s:Failed reading queries\n",
+ __func__);
+ return -EIO;
+ }
+
+ /* Currently supported RMI version is 4.0 */
+ pdata->rmi4_mod_info.version_major = 4;
+ pdata->rmi4_mod_info.version_minor = 0;
+ /*
+ * get manufacturer id, product_props, product info,
+ * date code, tester id, serial num and product id (name)
+ */
+ pdata->rmi4_mod_info.manufacturer_id = std_queries[0];
+ pdata->rmi4_mod_info.product_props = std_queries[1];
+ pdata->rmi4_mod_info.product_info[0] = std_queries[2];
+ pdata->rmi4_mod_info.product_info[1] = std_queries[3];
+ /* year - 2001-2032 */
+ pdata->rmi4_mod_info.date_code[0] = std_queries[4] & MASK_5BIT;
+ /* month - 1-12 */
+ pdata->rmi4_mod_info.date_code[1] = std_queries[5] & MASK_4BIT;
+ /* day - 1-31 */
+ pdata->rmi4_mod_info.date_code[2] = std_queries[6] & MASK_5BIT;
+ pdata->rmi4_mod_info.tester_id = ((std_queries[7] & MASK_7BIT) << 8) |
+ (std_queries[8] & MASK_7BIT);
+ pdata->rmi4_mod_info.serial_number =
+ ((std_queries[9] & MASK_7BIT) << 8) |
+ (std_queries[10] & MASK_7BIT);
+ memcpy(pdata->rmi4_mod_info.product_id_string, &std_queries[11], 10);
+
+ /* Check if this is a Synaptics device - report if not. */
+ if (pdata->rmi4_mod_info.manufacturer_id != 1)
+ dev_err(&client->dev, "%s: non-Synaptics mfg id:%d\n",
+ __func__, pdata->rmi4_mod_info.manufacturer_id);
+
+ list_for_each_entry(rfi, &pdata->rmi4_mod_info.support_fn_list, link)
+ data_sources += rfi->num_of_data_sources;
+ if (data_sources) {
+ rmi = &(pdata->rmi4_mod_info);
+ list_for_each_entry(rfi, &rmi->support_fn_list, link) {
+ if (rfi->num_of_data_sources) {
+ if (rfi->fn_number ==
+ SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM) {
+ retval = synpatics_rmi4_touchpad_config
+ (pdata, rfi);
+ if (retval < 0)
+ return retval;
+ } else
+ dev_err(&client->dev,
+ "%s:fn_number not supported\n",
+ __func__);
+ /*
+ * Turn on interrupts for this
+ * function's data sources.
+ */
+ ctrl_offset = pdata->fn01_ctrl_base_addr + 1 +
+ rfi->index_to_intr_reg;
+ retval = synaptics_rmi4_i2c_byte_write(pdata,
+ ctrl_offset,
+ rfi->intr_mask);
+ if (retval < 0)
+ return retval;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * synaptics_rmi4_resume_handler() - work queue for resume handler
+ * @work:work_struct structure pointer
+ *
+ * This work queue handler used to resume the device and returns none
+ */
+static void synaptics_rmi4_resume_handler(struct work_struct *work)
+{
+ struct synaptics_rmi4_data *prmi4_data = container_of(work,
+ struct synaptics_rmi4_data, resume_wq_handler);
+ struct i2c_client *client = prmi4_data->i2c_client;
+ int retval;
+
+ retval = synaptics_rmi4_enable(prmi4_data);
+ if (retval < 0)
+ dev_err(&client->dev, "%s: resume failed\n", __func__);
+}
+
+/**
+ * synaptics_rmi4_probe() - Initialze the i2c-client touchscreen driver
+ * @i2c: i2c client structure pointer
+ * @id:i2c device id pointer
+ *
+ * This function will allocate and initialize the instance
+ * data and request the irq and set the instance data as the clients
+ * platform data then register the physical driver which will do a scan of
+ * the rmi4 Physical Device Table and enumerate any rmi4 functions that
+ * have data sources associated with them.
+ */
+static int __devinit synaptics_rmi4_probe
+ (struct i2c_client *client, const struct i2c_device_id *dev_id)
+{
+ int retval;
+ unsigned char intr_status[4];
+ struct synaptics_rmi4_data *rmi4_data;
+ const struct synaptics_rmi4_platform_data *platformdata =
+ client->dev.platform_data;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "i2c smbus byte data not supported\n");
+ return -EIO;
+ }
+
+ if (!platformdata) {
+ dev_err(&client->dev, "%s: no platform data\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Allocate and initialize the instance data for this client */
+ rmi4_data = kzalloc(sizeof(struct synaptics_rmi4_data) * 2,
+ GFP_KERNEL);
+ if (!rmi4_data) {
+ dev_err(&client->dev, "%s: no memory allocated\n", __func__);
+ return -ENOMEM;
+ }
+
+ rmi4_data->input_dev = input_allocate_device();
+ if (rmi4_data->input_dev == NULL) {
+ dev_err(&client->dev, "%s:input device alloc failed\n",
+ __func__);
+ retval = -ENOMEM;
+ goto err_input;
+ }
+
+ if (platformdata->regulator_en) {
+ rmi4_data->regulator = regulator_get(&client->dev, "vdd");
+ if (IS_ERR(rmi4_data->regulator)) {
+ dev_err(&client->dev, "%s:get regulator failed\n",
+ __func__);
+ retval = PTR_ERR(rmi4_data->regulator);
+ goto err_regulator;
+ }
+ regulator_enable(rmi4_data->regulator);
+ }
+
+ init_waitqueue_head(&rmi4_data->wait);
+ /*
+ * Copy i2c_client pointer into RTID's i2c_client pointer for
+ * later use in rmi4_read, rmi4_write, etc.
+ */
+ rmi4_data->i2c_client = client;
+ /* So we set the page correctly the first time */
+ rmi4_data->current_page = MASK_16BIT;
+ rmi4_data->board = platformdata;
+ rmi4_data->touch_stopped = false;
+
+ /* init the mutexes for maintain the lists */
+ mutex_init(&(rmi4_data->fn_list_mutex));
+ mutex_init(&(rmi4_data->rmi4_page_mutex));
+
+ /*
+ * Register physical driver - this will call the detect function that
+ * will then scan the device and determine the supported
+ * rmi4 functions.
+ */
+ retval = synaptics_rmi4_i2c_query_device(rmi4_data);
+ if (retval) {
+ dev_err(&client->dev, "%s: rmi4 query device failed\n",
+ __func__);
+ goto err_query_dev;
+ }
+
+ /* Store the instance data in the i2c_client */
+ i2c_set_clientdata(client, rmi4_data);
+
+ /*initialize the input device parameters */
+ rmi4_data->input_dev->name = DRIVER_NAME;
+ rmi4_data->input_dev->phys = "Synaptics_Clearpad";
+ rmi4_data->input_dev->id.bustype = BUS_I2C;
+ rmi4_data->input_dev->dev.parent = &client->dev;
+ input_set_drvdata(rmi4_data->input_dev, rmi4_data);
+
+ /* Initialize the function handlers for rmi4 */
+ set_bit(EV_SYN, rmi4_data->input_dev->evbit);
+ set_bit(EV_KEY, rmi4_data->input_dev->evbit);
+ set_bit(EV_ABS, rmi4_data->input_dev->evbit);
+
+ input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_X, 0,
+ rmi4_data->sensor_max_x, 0, 0);
+ input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, 0,
+ rmi4_data->sensor_max_y, 0, 0);
+ input_set_abs_params(rmi4_data->input_dev, ABS_MT_TOUCH_MAJOR, 0,
+ max(rmi4_data->sensor_max_y, rmi4_data->sensor_max_y),
+ 0, 0);
+ input_set_abs_params(rmi4_data->input_dev, ABS_MT_WIDTH_MAJOR, 0,
+ MAX_WIDTH_MAJOR, 0, 0);
+
+ retval = input_register_device(rmi4_data->input_dev);
+ if (retval) {
+ dev_err(&client->dev, "%s:input register failed\n", __func__);
+ goto err_input_register;
+ }
+
+ /* Clear interrupts */
+ synaptics_rmi4_i2c_block_read(rmi4_data,
+ rmi4_data->fn01_data_base_addr + 1, intr_status,
+ rmi4_data->number_of_interrupt_register);
+ retval = request_threaded_irq(platformdata->irq_number, NULL,
+ synaptics_rmi4_irq,
+ platformdata->irq_type,
+ DRIVER_NAME, rmi4_data);
+ if (retval) {
+ dev_err(&client->dev, "%s:Unable to get attn irq %d\n",
+ __func__, platformdata->irq_number);
+ goto err_request_irq;
+ }
+
+ INIT_WORK(&rmi4_data->resume_wq_handler, synaptics_rmi4_resume_handler);
+
+ /* sysfs implementation for dynamic enable/disable the input event */
+ retval = sysfs_create_group(&client->dev.kobj,
+ &synaptics_rmi4_attr_group);
+ if (retval) {
+ dev_err(&client->dev, "failed to create sysfs entries\n");
+ goto err_sysfs;
+ }
+ rmi4_data->enable = true;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ rmi4_data->early_suspend.level =
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend;
+ rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume;
+ register_early_suspend(&rmi4_data->early_suspend);
+#endif
+ return retval;
+
+err_sysfs:
+ cancel_work_sync(&rmi4_data->resume_wq_handler);
+err_request_irq:
+ free_irq(platformdata->irq_number, rmi4_data);
+ input_unregister_device(rmi4_data->input_dev);
+err_input_register:
+ i2c_set_clientdata(client, NULL);
+err_query_dev:
+ if (platformdata->regulator_en) {
+ regulator_disable(rmi4_data->regulator);
+ regulator_put(rmi4_data->regulator);
+ }
+err_regulator:
+ input_free_device(rmi4_data->input_dev);
+ rmi4_data->input_dev = NULL;
+err_input:
+ kfree(rmi4_data);
+
+ return retval;
+}
+/**
+ * synaptics_rmi4_remove() - Removes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ *
+ * This funtion uses to remove the i2c-client
+ * touchscreen driver and returns integer.
+ */
+static int __devexit synaptics_rmi4_remove(struct i2c_client *client)
+{
+ struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client);
+ const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board;
+
+ sysfs_remove_group(&client->dev.kobj, &synaptics_rmi4_attr_group);
+ rmi4_data->touch_stopped = true;
+ wake_up(&rmi4_data->wait);
+ cancel_work_sync(&rmi4_data->resume_wq_handler);
+ free_irq(pdata->irq_number, rmi4_data);
+ input_unregister_device(rmi4_data->input_dev);
+ if (pdata->regulator_en) {
+ regulator_disable(rmi4_data->regulator);
+ regulator_put(rmi4_data->regulator);
+ }
+ kfree(rmi4_data);
+
+ return 0;
+}
+
+#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM))
+/**
+ * synaptics_rmi4_suspend() - suspend the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This funtion is used to suspend the
+ * touch panel controller and returns integer
+ */
+static int synaptics_rmi4_suspend(struct device *dev)
+{
+ /* Touch sleep mode */
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ return synaptics_rmi4_disable(rmi4_data);
+}
+
+/**
+ * synaptics_rmi4_resume() - resume the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This funtion is used to resume the touch panel
+ * controller and returns integer.
+ */
+static int synaptics_rmi4_resume(struct device *dev)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ schedule_work(&rmi4_data->resume_wq_handler);
+
+ return 0;
+}
+
+static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = {
+ .suspend = synaptics_rmi4_suspend,
+ .resume = synaptics_rmi4_resume,
+};
+
+#else
+static void synaptics_rmi4_early_suspend(struct early_suspend *data)
+{
+ struct synaptics_rmi4_data *rmi4_data =
+ container_of(data, struct synaptics_rmi4_data, early_suspend);
+ struct i2c_client *client = rmi4_data->i2c_client;
+ int retval;
+
+ retval = synaptics_rmi4_disable(rmi4_data);
+ if (retval < 0)
+ dev_err(&client->dev, "rmi4 disable failed\n");
+}
+
+static void synaptics_rmi4_late_resume(struct early_suspend *data)
+{
+ struct synaptics_rmi4_data *rmi4_data =
+ container_of(data, struct synaptics_rmi4_data, early_suspend);
+ struct i2c_client *client = rmi4_data->i2c_client;
+ int retval;
+
+ retval = synaptics_rmi4_enable(rmi4_data);
+ if (retval < 0)
+ dev_err(&client->dev, "rmi4 enable failed\n");
+}
+#endif
+
+static const struct i2c_device_id synaptics_rmi4_id_table[] = {
+ { DRIVER_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table);
+
+static struct i2c_driver synaptics_rmi4_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM))
+ .pm = &synaptics_rmi4_dev_pm_ops,
+#endif
+ },
+ .probe = synaptics_rmi4_probe,
+ .remove = __devexit_p(synaptics_rmi4_remove),
+ .id_table = synaptics_rmi4_id_table,
+};
+/**
+ * synaptics_rmi4_init() - Initialize the touchscreen driver
+ *
+ * This funtion uses to initializes the synaptics
+ * touchscreen driver and returns integer.
+ */
+static int __init synaptics_rmi4_init(void)
+{
+ return i2c_add_driver(&synaptics_rmi4_driver);
+}
+/**
+ * synaptics_rmi4_exit() - De-initialize the touchscreen driver
+ *
+ * This funtion uses to de-initialize the synaptics
+ * touchscreen driver and returns none.
+ */
+static void __exit synaptics_rmi4_exit(void)
+{
+ i2c_del_driver(&synaptics_rmi4_driver);
+}
+
+
+module_init(synaptics_rmi4_init);
+module_exit(synaptics_rmi4_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("naveen.gaddipati@stericsson.com, js.ha@stericsson.com");
+MODULE_DESCRIPTION("synaptics rmi4 i2c touch Driver");
+MODULE_ALIAS("i2c:synaptics_rmi4_ts");
diff --git a/drivers/switch/Kconfig b/drivers/switch/Kconfig
new file mode 100644
index 00000000000..52385914b9a
--- /dev/null
+++ b/drivers/switch/Kconfig
@@ -0,0 +1,15 @@
+menuconfig SWITCH
+ tristate "Switch class support"
+ help
+ Say Y here to enable switch class support. This allows
+ monitoring switches by userspace via sysfs and uevent.
+
+if SWITCH
+
+config SWITCH_GPIO
+ tristate "GPIO Swith support"
+ depends on GENERIC_GPIO
+ help
+ Say Y here to enable GPIO based switch support.
+
+endif # SWITCH
diff --git a/drivers/switch/Makefile b/drivers/switch/Makefile
new file mode 100644
index 00000000000..f7606ed4a71
--- /dev/null
+++ b/drivers/switch/Makefile
@@ -0,0 +1,4 @@
+# Switch Class Driver
+obj-$(CONFIG_SWITCH) += switch_class.o
+obj-$(CONFIG_SWITCH_GPIO) += switch_gpio.o
+
diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c
new file mode 100644
index 00000000000..e05fc259114
--- /dev/null
+++ b/drivers/switch/switch_class.c
@@ -0,0 +1,174 @@
+/*
+ * drivers/switch/switch_class.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/switch.h>
+
+struct class *switch_class;
+static atomic_t device_count;
+
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct switch_dev *sdev = (struct switch_dev *)
+ dev_get_drvdata(dev);
+
+ if (sdev->print_state) {
+ int ret = sdev->print_state(sdev, buf);
+ if (ret >= 0)
+ return ret;
+ }
+ return sprintf(buf, "%d\n", sdev->state);
+}
+
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct switch_dev *sdev = (struct switch_dev *)
+ dev_get_drvdata(dev);
+
+ if (sdev->print_name) {
+ int ret = sdev->print_name(sdev, buf);
+ if (ret >= 0)
+ return ret;
+ }
+ return sprintf(buf, "%s\n", sdev->name);
+}
+
+static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL);
+static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL);
+
+void switch_set_state(struct switch_dev *sdev, int state)
+{
+ char name_buf[120];
+ char state_buf[120];
+ char *prop_buf;
+ char *envp[3];
+ int env_offset = 0;
+ int length;
+
+ if (sdev->state != state) {
+ sdev->state = state;
+
+ prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
+ if (prop_buf) {
+ length = name_show(sdev->dev, NULL, prop_buf);
+ if (length > 0) {
+ if (prop_buf[length - 1] == '\n')
+ prop_buf[length - 1] = 0;
+ snprintf(name_buf, sizeof(name_buf),
+ "SWITCH_NAME=%s", prop_buf);
+ envp[env_offset++] = name_buf;
+ }
+ length = state_show(sdev->dev, NULL, prop_buf);
+ if (length > 0) {
+ if (prop_buf[length - 1] == '\n')
+ prop_buf[length - 1] = 0;
+ snprintf(state_buf, sizeof(state_buf),
+ "SWITCH_STATE=%s", prop_buf);
+ envp[env_offset++] = state_buf;
+ }
+ envp[env_offset] = NULL;
+ kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);
+ free_page((unsigned long)prop_buf);
+ } else {
+ printk(KERN_ERR "out of memory in switch_set_state\n");
+ kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(switch_set_state);
+
+static int create_switch_class(void)
+{
+ if (!switch_class) {
+ switch_class = class_create(THIS_MODULE, "switch");
+ if (IS_ERR(switch_class))
+ return PTR_ERR(switch_class);
+ atomic_set(&device_count, 0);
+ }
+
+ return 0;
+}
+
+int switch_dev_register(struct switch_dev *sdev)
+{
+ int ret;
+
+ if (!switch_class) {
+ ret = create_switch_class();
+ if (ret < 0)
+ return ret;
+ }
+
+ sdev->index = atomic_inc_return(&device_count);
+ sdev->dev = device_create(switch_class, NULL,
+ MKDEV(0, sdev->index), NULL, sdev->name);
+ if (IS_ERR(sdev->dev))
+ return PTR_ERR(sdev->dev);
+
+ ret = device_create_file(sdev->dev, &dev_attr_state);
+ if (ret < 0)
+ goto err_create_file_1;
+ ret = device_create_file(sdev->dev, &dev_attr_name);
+ if (ret < 0)
+ goto err_create_file_2;
+
+ dev_set_drvdata(sdev->dev, sdev);
+ sdev->state = 0;
+ return 0;
+
+err_create_file_2:
+ device_remove_file(sdev->dev, &dev_attr_state);
+err_create_file_1:
+ device_destroy(switch_class, MKDEV(0, sdev->index));
+ printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(switch_dev_register);
+
+void switch_dev_unregister(struct switch_dev *sdev)
+{
+ device_remove_file(sdev->dev, &dev_attr_name);
+ device_remove_file(sdev->dev, &dev_attr_state);
+ device_destroy(switch_class, MKDEV(0, sdev->index));
+ dev_set_drvdata(sdev->dev, NULL);
+}
+EXPORT_SYMBOL_GPL(switch_dev_unregister);
+
+static int __init switch_class_init(void)
+{
+ return create_switch_class();
+}
+
+static void __exit switch_class_exit(void)
+{
+ class_destroy(switch_class);
+}
+
+module_init(switch_class_init);
+module_exit(switch_class_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("Switch class driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/switch/switch_gpio.c b/drivers/switch/switch_gpio.c
new file mode 100644
index 00000000000..7e9faa211e4
--- /dev/null
+++ b/drivers/switch/switch_gpio.c
@@ -0,0 +1,172 @@
+/*
+ * drivers/switch/switch_gpio.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/switch.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+
+struct gpio_switch_data {
+ struct switch_dev sdev;
+ unsigned gpio;
+ const char *name_on;
+ const char *name_off;
+ const char *state_on;
+ const char *state_off;
+ int irq;
+ struct work_struct work;
+};
+
+static void gpio_switch_work(struct work_struct *work)
+{
+ int state;
+ struct gpio_switch_data *data =
+ container_of(work, struct gpio_switch_data, work);
+
+ state = gpio_get_value(data->gpio);
+ switch_set_state(&data->sdev, state);
+}
+
+static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_switch_data *switch_data =
+ (struct gpio_switch_data *)dev_id;
+
+ schedule_work(&switch_data->work);
+ return IRQ_HANDLED;
+}
+
+static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf)
+{
+ struct gpio_switch_data *switch_data =
+ container_of(sdev, struct gpio_switch_data, sdev);
+ const char *state;
+ if (switch_get_state(sdev))
+ state = switch_data->state_on;
+ else
+ state = switch_data->state_off;
+
+ if (state)
+ return sprintf(buf, "%s\n", state);
+ return -1;
+}
+
+static int gpio_switch_probe(struct platform_device *pdev)
+{
+ struct gpio_switch_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_switch_data *switch_data;
+ int ret = 0;
+
+ if (!pdata)
+ return -EBUSY;
+
+ switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL);
+ if (!switch_data)
+ return -ENOMEM;
+
+ switch_data->sdev.name = pdata->name;
+ switch_data->gpio = pdata->gpio;
+ switch_data->name_on = pdata->name_on;
+ switch_data->name_off = pdata->name_off;
+ switch_data->state_on = pdata->state_on;
+ switch_data->state_off = pdata->state_off;
+ switch_data->sdev.print_state = switch_gpio_print_state;
+
+ ret = switch_dev_register(&switch_data->sdev);
+ if (ret < 0)
+ goto err_switch_dev_register;
+
+ ret = gpio_request(switch_data->gpio, pdev->name);
+ if (ret < 0)
+ goto err_request_gpio;
+
+ ret = gpio_direction_input(switch_data->gpio);
+ if (ret < 0)
+ goto err_set_gpio_input;
+
+ INIT_WORK(&switch_data->work, gpio_switch_work);
+
+ switch_data->irq = gpio_to_irq(switch_data->gpio);
+ if (switch_data->irq < 0) {
+ ret = switch_data->irq;
+ goto err_detect_irq_num_failed;
+ }
+
+ ret = request_irq(switch_data->irq, gpio_irq_handler,
+ IRQF_TRIGGER_LOW, pdev->name, switch_data);
+ if (ret < 0)
+ goto err_request_irq;
+
+ /* Perform initial detection */
+ gpio_switch_work(&switch_data->work);
+
+ return 0;
+
+err_request_irq:
+err_detect_irq_num_failed:
+err_set_gpio_input:
+ gpio_free(switch_data->gpio);
+err_request_gpio:
+ switch_dev_unregister(&switch_data->sdev);
+err_switch_dev_register:
+ kfree(switch_data);
+
+ return ret;
+}
+
+static int __devexit gpio_switch_remove(struct platform_device *pdev)
+{
+ struct gpio_switch_data *switch_data = platform_get_drvdata(pdev);
+
+ cancel_work_sync(&switch_data->work);
+ gpio_free(switch_data->gpio);
+ switch_dev_unregister(&switch_data->sdev);
+ kfree(switch_data);
+
+ return 0;
+}
+
+static struct platform_driver gpio_switch_driver = {
+ .probe = gpio_switch_probe,
+ .remove = __devexit_p(gpio_switch_remove),
+ .driver = {
+ .name = "switch-gpio",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init gpio_switch_init(void)
+{
+ return platform_driver_register(&gpio_switch_driver);
+}
+
+static void __exit gpio_switch_exit(void)
+{
+ platform_driver_unregister(&gpio_switch_driver);
+}
+
+module_init(gpio_switch_init);
+module_exit(gpio_switch_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("GPIO Switch driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig
new file mode 100644
index 00000000000..a452e888d77
--- /dev/null
+++ b/drivers/tee/Kconfig
@@ -0,0 +1,13 @@
+#
+# Copyright (C) ST-Ericsson SA 2010
+# Author: Martin Hovang (martin.xm.hovang@stericsson.com)
+# License terms: GNU General Public License (GPL) version 2
+#
+
+# Trursted Execution Environment Configuration
+config TEE_SUPPORT
+ bool "Trusted Execution Environment Support"
+ default y
+ ---help---
+ This implements the Trusted Execution Environment (TEE) Client
+ API Specification from GlobalPlatform Device Technology.
diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile
new file mode 100644
index 00000000000..b937eb19d72
--- /dev/null
+++ b/drivers/tee/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright (C) ST-Ericsson SA 2010
+# Author: Martin Hovang (martin.xm.hovang@stericsson.com)
+# License terms: GNU General Public License (GPL) version 2
+#
+
+obj-$(CONFIG_TEE_SUPPORT) += tee_service.o
+obj-$(CONFIG_TEE_SUPPORT) += tee_driver.o
diff --git a/drivers/tee/tee_driver.c b/drivers/tee/tee_driver.c
new file mode 100644
index 00000000000..73c62871ddc
--- /dev/null
+++ b/drivers/tee/tee_driver.c
@@ -0,0 +1,643 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Martin Hovang <martin.xm.hovang@stericsson.com>
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/tee.h>
+#include <linux/slab.h>
+
+#define TEED_NAME "tee"
+
+#define TEED_STATE_OPEN_DEV 0
+#define TEED_STATE_OPEN_SESSION 1
+
+static struct mutex sync;
+
+static int tee_open(struct inode *inode, struct file *file);
+static int tee_release(struct inode *inode, struct file *file);
+static int tee_read(struct file *filp, char __user *buffer,
+ size_t length, loff_t *offset);
+static int tee_write(struct file *filp, const char __user *buffer,
+ size_t length, loff_t *offset);
+
+static inline void set_emsg(struct tee_session *ts, u32 msg)
+{
+ ts->err = msg;
+ ts->origin = TEED_ORIGIN_DRIVER;
+}
+
+static void reset_session(struct tee_session *ts)
+{
+ ts->state = TEED_STATE_OPEN_DEV;
+ ts->err = TEED_SUCCESS;
+ ts->origin = TEED_ORIGIN_DRIVER;
+ ts->id = 0;
+ ts->ta = NULL;
+ ts->uuid = NULL;
+ ts->cmd = 0;
+ ts->driver_cmd = TEED_OPEN_SESSION;
+ ts->ta_size = 0;
+ ts->op = NULL;
+}
+
+static int copy_ta(struct tee_session *ts,
+ struct tee_session *ku_buffer)
+{
+ ts->ta = kmalloc(ku_buffer->ta_size, GFP_KERNEL);
+ if (ts->ta == NULL) {
+ pr_err("[%s] error, out of memory (ta)\n",
+ __func__);
+ set_emsg(ts, TEED_ERROR_OUT_OF_MEMORY);
+ return -ENOMEM;
+ }
+
+ ts->ta_size = ku_buffer->ta_size;
+
+ memcpy(ts->ta, ku_buffer->ta, ku_buffer->ta_size);
+ return 0;
+}
+
+static int copy_uuid(struct tee_session *ts,
+ struct tee_session *ku_buffer)
+{
+ ts->uuid = kmalloc(sizeof(struct tee_uuid), GFP_KERNEL);
+
+ if (ts->uuid == NULL) {
+ pr_err("[%s] error, out of memory (uuid)\n",
+ __func__);
+ set_emsg(ts, TEED_ERROR_OUT_OF_MEMORY);
+ return -ENOMEM;
+ }
+
+ memcpy(ts->uuid, ku_buffer->uuid, sizeof(struct tee_uuid));
+
+ return 0;
+}
+
+static inline void free_operation(struct tee_session *ts)
+{
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ kfree(ts->op->shm[i].buffer);
+ ts->op->shm[i].buffer = NULL;
+ }
+
+ kfree(ts->op);
+ ts->op = NULL;
+}
+
+static inline void memrefs_phys_to_virt(struct tee_session *ts)
+{
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ if (ts->op->flags & (1 << i)) {
+ ts->op->shm[i].buffer =
+ phys_to_virt((unsigned long)
+ ts->op->shm[i].buffer);
+ }
+ }
+}
+
+static int copy_memref_to_user(struct tee_operation *op,
+ struct tee_operation *ubuf_op,
+ int memref)
+{
+ unsigned long bytes_left;
+
+ bytes_left = copy_to_user(ubuf_op->shm[memref].buffer,
+ op->shm[memref].buffer,
+ op->shm[memref].size);
+
+ if (bytes_left != 0) {
+ pr_err("[%s] Failed to copy result to user space (%lu "
+ "bytes left of buffer).\n", __func__, bytes_left);
+ return bytes_left;
+ }
+
+ bytes_left = put_user(op->shm[memref].size, &ubuf_op->shm[memref].size);
+
+ if (bytes_left != 0) {
+ pr_err("[%s] Failed to copy result to user space (%lu "
+ "bytes left of size).\n", __func__, bytes_left);
+ return -EINVAL;
+ }
+
+ bytes_left = put_user(op->shm[memref].flags,
+ &ubuf_op->shm[memref].flags);
+ if (bytes_left != 0) {
+ pr_err("[%s] Failed to copy result to user space (%lu "
+ "bytes left of flags).\n", __func__, bytes_left);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int copy_memref_to_kernel(struct tee_operation *op,
+ struct tee_operation *kbuf_op,
+ int memref)
+{
+ /* Buffer freed in invoke_command if this function fails */
+ op->shm[memref].buffer = kmalloc(kbuf_op->shm[memref].size, GFP_KERNEL);
+
+ if (!op->shm[memref].buffer) {
+ pr_err("[%s] out of memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ /*
+ * Copy shared memory operations to a local kernel
+ * buffer if they are of type input.
+ */
+ if (kbuf_op->shm[memref].flags & TEEC_MEM_INPUT) {
+ memcpy(op->shm[memref].buffer,
+ kbuf_op->shm[memref].buffer,
+ kbuf_op->shm[memref].size);
+ }
+
+ op->shm[memref].size = kbuf_op->shm[memref].size;
+ op->shm[memref].flags = kbuf_op->shm[memref].flags;
+
+ /* Secure world expects physical addresses. */
+ op->shm[memref].buffer = (void *)virt_to_phys(op->shm[memref].buffer);
+
+ return 0;
+}
+
+static int open_tee_device(struct tee_session *ts,
+ struct tee_session *ku_buffer)
+{
+ int ret;
+
+ if (ku_buffer->driver_cmd != TEED_OPEN_SESSION) {
+ set_emsg(ts, TEED_ERROR_BAD_STATE);
+ return -EINVAL;
+ }
+
+ if (ku_buffer->ta) {
+ ret = copy_ta(ts, ku_buffer);
+ } else if (ku_buffer->uuid) {
+ ret = copy_uuid(ts, ku_buffer);
+ } else {
+ set_emsg(ts, TEED_ERROR_COMMUNICATION);
+ return -EINVAL;
+ }
+
+ ts->id = 0;
+ ts->state = TEED_STATE_OPEN_SESSION;
+ return ret;
+}
+
+static int invoke_command(struct tee_session *ts,
+ struct tee_session *ku_buffer,
+ struct tee_session __user *u_buffer)
+{
+ int i;
+ int ret = 0;
+ struct tee_operation *kbuf_op =
+ (struct tee_operation *)ku_buffer->op;
+
+ ts->op = kmalloc(sizeof(struct tee_operation), GFP_KERNEL);
+
+ if (!ts->op) {
+ if (ts->op == NULL) {
+ pr_err("[%s] error, out of memory "
+ "(op)\n", __func__);
+ set_emsg(ts, TEED_ERROR_OUT_OF_MEMORY);
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+
+ /* Copy memrefs to kernel space. */
+ ts->op->flags = kbuf_op->flags;
+ ts->cmd = ku_buffer->cmd;
+
+ for (i = 0; i < 4; ++i) {
+ /* We only want to copy memrefs in use. */
+ if (kbuf_op->flags & (1 << i)) {
+ ret = copy_memref_to_kernel(ts->op, kbuf_op, i);
+
+ if (ret)
+ goto err;
+ } else {
+ ts->op->shm[i].buffer = NULL;
+ ts->op->shm[i].size = 0;
+ ts->op->shm[i].flags = 0;
+ }
+ }
+
+ /* To call secure world */
+ if (call_sec_world(ts, TEED_INVOKE)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /*
+ * Convert physical addresses back to virtual address so the
+ * kernel can free the buffers when closing the session.
+ */
+ memrefs_phys_to_virt(ts);
+
+ for (i = 0; i < 4; ++i) {
+ if ((kbuf_op->flags & (1 << i)) &&
+ (kbuf_op->shm[i].flags & TEEC_MEM_OUTPUT)) {
+ struct tee_operation *ubuf_op =
+ (struct tee_operation *)u_buffer->op;
+
+ ret = copy_memref_to_user(ts->op, ubuf_op, i);
+ }
+ }
+err:
+ free_operation(ts);
+
+ return ret;
+}
+
+static int tee_open(struct inode *inode, struct file *filp)
+{
+ struct tee_session *ts;
+
+ filp->private_data = kmalloc(sizeof(struct tee_session),
+ GFP_KERNEL);
+
+ if (filp->private_data == NULL)
+ return -ENOMEM;
+
+ ts = (struct tee_session *) (filp->private_data);
+
+ reset_session(ts);
+
+ return 0;
+}
+
+static int tee_release(struct inode *inode, struct file *filp)
+{
+ struct tee_session *ts;
+ int i;
+
+ ts = (struct tee_session *) (filp->private_data);
+
+ if (ts == NULL)
+ goto no_ts;
+
+ if (ts->op) {
+ for (i = 0; i < 4; ++i) {
+ kfree(ts->op->shm[i].buffer);
+ ts->op->shm[i].buffer = NULL;
+ }
+ }
+
+ kfree(ts->op);
+ ts->op = NULL;
+
+ kfree(ts->ta);
+ ts->ta = NULL;
+
+no_ts:
+ kfree(filp->private_data);
+ filp->private_data = NULL;
+
+ return 0;
+}
+
+/*
+ * Called when a process, which already opened the dev file, attempts
+ * to read from it. This function gets the current status of the session.
+ */
+static int tee_read(struct file *filp, char __user *buffer,
+ size_t length, loff_t *offset)
+{
+ struct tee_read buf;
+ struct tee_session *ts;
+
+ if (length != sizeof(struct tee_read)) {
+ pr_err("[%s] error, incorrect input length\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ts = (struct tee_session *) (filp->private_data);
+
+ if (ts == NULL) {
+ pr_err("[%s] error, private_data not "
+ "initialized\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&sync);
+
+ buf.err = ts->err;
+ buf.origin = ts->origin;
+
+ mutex_unlock(&sync);
+
+ if (copy_to_user(buffer, &buf, length)) {
+ pr_err("[%s] error, copy_to_user failed!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return length;
+}
+
+/*
+ * Called when a process writes to a dev file
+ */
+static int tee_write(struct file *filp, const char __user *buffer,
+ size_t length, loff_t *offset)
+{
+ struct tee_session ku_buffer;
+ struct tee_session *ts;
+ int ret = length;
+
+ if (length != sizeof(struct tee_session)) {
+ pr_err("[%s] error, incorrect input length\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&ku_buffer, buffer, length)) {
+ pr_err("[%s] error, tee_session "
+ "copy_from_user failed\n", __func__);
+ return -EINVAL;
+ }
+
+ ts = (struct tee_session *) (filp->private_data);
+
+ if (ts == NULL) {
+ pr_err("[%s] error, private_data not "
+ "initialized\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&sync);
+
+ switch (ts->state) {
+ case TEED_STATE_OPEN_DEV:
+ ret = open_tee_device(ts, &ku_buffer);
+ break;
+
+ case TEED_STATE_OPEN_SESSION:
+ switch (ku_buffer.driver_cmd) {
+ case TEED_INVOKE:
+ ret = invoke_command(ts, &ku_buffer,
+ (struct tee_session *)buffer);
+ break;
+
+ case TEED_CLOSE_SESSION:
+ /* no caching implemented yet... */
+ if (call_sec_world(ts, TEED_CLOSE_SESSION))
+ ret = -EINVAL;
+
+ kfree(ts->ta);
+ ts->ta = NULL;
+
+ reset_session(ts);
+ break;
+
+ default:
+ set_emsg(ts, TEED_ERROR_BAD_PARAMETERS);
+ ret = -EINVAL;
+ }
+ break;
+ default:
+ pr_err("[%s] unknown state\n", __func__);
+ set_emsg(ts, TEED_ERROR_BAD_STATE);
+ ret = -EINVAL;
+ }
+
+ /*
+ * We expect that ret has value zero when reaching the end here.
+ * If it has any other value some error must have occured.
+ */
+ if (!ret)
+ ret = length;
+ else
+ ret = -EINVAL;
+
+ mutex_unlock(&sync);
+
+ return ret;
+}
+
+int teec_initialize_context(const char *name, struct tee_context *context)
+{
+ return TEED_SUCCESS;
+}
+EXPORT_SYMBOL(teec_initialize_context);
+
+int teec_finalize_context(struct tee_context *context)
+{
+ return TEED_SUCCESS;
+}
+EXPORT_SYMBOL(teec_finalize_context);
+
+int teec_open_session(struct tee_context *context,
+ struct tee_session *session,
+ const struct tee_uuid *destination,
+ unsigned int connection_method,
+ void *connection_data, struct tee_operation *operation,
+ unsigned int *error_origin)
+{
+ int res = TEED_SUCCESS;
+
+ if (session == NULL || destination == NULL) {
+ pr_err("[%s] session or destination == NULL\n", __func__);
+ if (error_origin != NULL)
+ *error_origin = TEED_ORIGIN_DRIVER;
+ res = TEED_ERROR_BAD_PARAMETERS;
+ goto exit;
+ }
+
+ reset_session(session);
+
+ /*
+ * Open a session towards an application already loaded inside
+ * the TEE
+ */
+ session->uuid = kmalloc(sizeof(struct tee_uuid), GFP_KERNEL);
+
+ if (session->uuid == NULL) {
+ pr_err("[%s] error, out of memory (uuid)\n",
+ __func__);
+ if (error_origin != NULL)
+ *error_origin = TEED_ORIGIN_DRIVER;
+ res = TEED_ERROR_OUT_OF_MEMORY;
+ goto exit;
+ }
+
+ memcpy(session->uuid, destination, sizeof(struct tee_uuid));
+
+ session->ta = NULL;
+ session->id = 0;
+
+exit:
+ return res;
+}
+EXPORT_SYMBOL(teec_open_session);
+
+int teec_close_session(struct tee_session *session)
+{
+ int res = TEED_SUCCESS;
+
+ mutex_lock(&sync);
+
+ if (session == NULL) {
+ pr_err("[%s] error, session == NULL\n", __func__);
+ res = TEED_ERROR_BAD_PARAMETERS;
+ goto exit;
+ }
+
+ if (call_sec_world(session, TEED_CLOSE_SESSION)) {
+ pr_err("[%s] error, call_sec_world failed\n", __func__);
+ res = TEED_ERROR_GENERIC;
+ goto exit;
+ }
+
+exit:
+ if (session != NULL) {
+ kfree(session->uuid);
+ session->uuid = NULL;
+ }
+
+ mutex_unlock(&sync);
+ return res;
+}
+EXPORT_SYMBOL(teec_close_session);
+
+int teec_invoke_command(
+ struct tee_session *session, unsigned int command_id,
+ struct tee_operation *operation,
+ unsigned int *error_origin)
+{
+ int res = TEED_SUCCESS;
+ int i;
+
+ mutex_lock(&sync);
+
+ if (session == NULL || operation == NULL || error_origin == NULL) {
+ pr_err("[%s] error, input parameters == NULL\n", __func__);
+ if (error_origin != NULL)
+ *error_origin = TEED_ORIGIN_DRIVER;
+ res = TEED_ERROR_BAD_PARAMETERS;
+ goto exit;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ /* We only want to translate memrefs in use. */
+ if (operation->flags & (1 << i)) {
+ operation->shm[i].buffer =
+ (void *)virt_to_phys(
+ operation->shm[i].buffer);
+ }
+ }
+ session->op = operation;
+ session->cmd = command_id;
+
+ /*
+ * Call secure world
+ */
+ if (call_sec_world(session, TEED_INVOKE)) {
+ pr_err("[%s] error, call_sec_world failed\n", __func__);
+ if (error_origin != NULL)
+ *error_origin = TEED_ORIGIN_DRIVER;
+ res = TEED_ERROR_GENERIC;
+ }
+ if (session->err != TEED_SUCCESS) {
+ pr_err("[%s] error, call_sec_world failed\n", __func__);
+ if (error_origin != NULL)
+ *error_origin = session->origin;
+ res = session->err;
+ }
+
+ memrefs_phys_to_virt(session);
+ session->op = NULL;
+
+exit:
+ mutex_unlock(&sync);
+ return res;
+}
+EXPORT_SYMBOL(teec_invoke_command);
+
+int teec_allocate_shared_memory(struct tee_context *context,
+ struct tee_sharedmemory *shared_memory)
+{
+ int res = TEED_SUCCESS;
+
+ if (shared_memory == NULL) {
+ res = TEED_ERROR_BAD_PARAMETERS;
+ goto exit;
+ }
+
+ shared_memory->buffer = kmalloc(shared_memory->size,
+ GFP_KERNEL);
+
+ if (shared_memory->buffer == NULL) {
+ res = TEED_ERROR_OUT_OF_MEMORY;
+ goto exit;
+ }
+
+exit:
+ return res;
+}
+EXPORT_SYMBOL(teec_allocate_shared_memory);
+
+void teec_release_shared_memory(struct tee_sharedmemory *shared_memory)
+{
+ kfree(shared_memory->buffer);
+}
+EXPORT_SYMBOL(teec_release_shared_memory);
+
+static const struct file_operations tee_fops = {
+ .owner = THIS_MODULE,
+ .read = tee_read,
+ .write = tee_write,
+ .open = tee_open,
+ .release = tee_release,
+};
+
+static struct miscdevice tee_dev = {
+ MISC_DYNAMIC_MINOR,
+ TEED_NAME,
+ &tee_fops
+};
+
+static int __init tee_init(void)
+{
+ int err = 0;
+
+ err = misc_register(&tee_dev);
+
+ if (err) {
+ pr_err("[%s] error %d adding character device "
+ "TEE\n", __func__, err);
+ }
+
+ mutex_init(&sync);
+
+ return err;
+}
+
+static void __exit tee_exit(void)
+{
+ misc_deregister(&tee_dev);
+}
+
+module_init(tee_init);
+module_exit(tee_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("Trusted Execution Enviroment driver");
diff --git a/drivers/tee/tee_service.c b/drivers/tee/tee_service.c
new file mode 100644
index 00000000000..b01e9d0ac39
--- /dev/null
+++ b/drivers/tee/tee_service.c
@@ -0,0 +1,17 @@
+/*
+ * TEE service to handle the calls to trusted applications.
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#include <linux/kernel.h>
+#include <linux/tee.h>
+#include <linux/device.h>
+
+int __weak call_sec_world(struct tee_session *ts, int sec_cmd)
+{
+ pr_info("[%s] Generic call_sec_world called!\n", __func__);
+
+ return 0;
+}
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index a6bd53ace03..b66fc04caa5 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1208,6 +1208,21 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
* and flush any outstanding URBs.
*/
} else {
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+ /* According to OTG supplement Rev 2.0 section 6.3
+ * Unless an A-device enables b_hnp_enable before entering
+ * suspend it shall also continue polling while the bus is
+ * suspended.
+ *
+ * We don't have to perform HNP polling, as we are going to
+ * enable b_hnp_enable before suspending.
+ */
+ if (udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port)
+ cancel_delayed_work(&udev->bus->hnp_polling);
+#endif
+#endif
udev->can_submit = 0;
for (i = 0; i < 16; ++i) {
usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
@@ -1270,6 +1285,51 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
return status;
}
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+void usb_hnp_polling_work(struct work_struct *work)
+{
+ int ret;
+ struct usb_bus *bus =
+ container_of(work, struct usb_bus, hnp_polling.work);
+ struct usb_device *udev =
+ bus->root_hub->children[bus->otg_port - 1];
+ u8 *status = kmalloc(sizeof(*status), GFP_KERNEL);
+
+ if (!status) {
+ schedule_delayed_work(&bus->hnp_polling,
+ msecs_to_jiffies(THOST_REQ_POLL));
+ return;
+ }
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_DEVICE,
+ 0, OTG_STATUS_SELECTOR, status, sizeof(*status),
+ USB_CTRL_GET_TIMEOUT);
+ if (ret < 0) {
+ /* Peripheral may not be supporting HNP polling */
+ dev_vdbg(&udev->dev, "HNP polling failed."
+ "status %d\n", ret);
+ goto out;
+ }
+
+ /* Spec says host must suspend the bus with in 2 sec. */
+ if (*status & (1 << HOST_REQUEST_FLAG)) {
+ do_unbind_rebind(udev, DO_UNBIND);
+ udev->do_remote_wakeup = 0;
+ ret = usb_suspend_both(udev, PMSG_USER_SUSPEND);
+ if (ret)
+ dev_info(&udev->dev, "suspend failed\n");
+ } else {
+ schedule_delayed_work(&bus->hnp_polling,
+ msecs_to_jiffies(THOST_REQ_POLL));
+ }
+out:
+ kfree(status);
+}
+#endif
+#endif
+
static void choose_wakeup(struct usb_device *udev, pm_message_t msg)
{
int w;
@@ -1659,7 +1719,7 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface_no_resume);
/* Internal routine to check whether we may autosuspend a device. */
static int autosuspend_check(struct usb_device *udev)
{
- int w, i;
+ int w, i, audio_class;
struct usb_interface *intf;
unsigned long suspend_time, j;
@@ -1667,6 +1727,7 @@ static int autosuspend_check(struct usb_device *udev)
* any interface drivers require remote wakeup but it isn't available.
*/
w = 0;
+ audio_class = 0;
if (udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
@@ -1694,13 +1755,32 @@ static int autosuspend_check(struct usb_device *udev)
intf->needs_remote_wakeup)
return -EOPNOTSUPP;
}
+ /* Check for audio interface class */
+ if (intf->cur_altsetting->desc.bInterfaceClass
+ == USB_CLASS_AUDIO) {
+ dev_dbg(&udev->dev,
+ "audio interface class present\n");
+ audio_class = 1;
+ }
}
}
- if (w && !device_can_wakeup(&udev->dev)) {
- dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n");
- return -EOPNOTSUPP;
+
+ /* To work with usb audio device having HID interface
+ * without remote wakeup support.
+ */
+ if (audio_class) {
+ /* Audio class is enabled, disable remote wakeup */
+ dev_dbg(&udev->dev,
+ "disabling remote wakeup for audio class\n");
+ udev->do_remote_wakeup = 0;
+ } else {
+ if (w && !device_can_wakeup(&udev->dev)) {
+ dev_dbg(&udev->dev,
+ "remote wakeup needed for autosuspend\n");
+ return -EOPNOTSUPP;
+ }
+ udev->do_remote_wakeup = w;
}
- udev->do_remote_wakeup = w;
/* If everything is okay but the device hasn't been idle for long
* enough, queue a delayed autosuspend request.
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 12742f152f4..834684afbea 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -870,6 +870,11 @@ static void usb_bus_init (struct usb_bus *bus)
bus->bandwidth_isoc_reqs = 0;
INIT_LIST_HEAD (&bus->bus_list);
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+ INIT_DELAYED_WORK(&bus->hnp_polling, usb_hnp_polling_work);
+#endif
+#endif
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index ffc80e3241e..b0b470e4a24 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -31,6 +31,12 @@
#include "usb.h"
+#ifdef CONFIG_ARCH_U8500
+#define MAX_TOPO_LEVEL_U8500 2
+#define MAX_USB_DEVICE_U8500 8
+int usb_device_count;
+#endif
+
/* if we are in debug mode, always announce new devices */
#ifdef DEBUG
#ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES
@@ -128,12 +134,19 @@ MODULE_PARM_DESC(initial_descriptor_timeout,
* otherwise the new scheme is used. If that fails and "use_both_schemes"
* is set, then the driver will make another attempt, using the other scheme.
*/
+#ifndef CONFIG_USB_OTG_13
static int old_scheme_first = 0;
+#else
+static int old_scheme_first = 1;
+#endif
module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(old_scheme_first,
"start with the old device initialization scheme");
-
+#ifndef CONFIG_USB_OTG_13
static int use_both_schemes = 1;
+#else
+static int use_both_schemes;
+#endif
module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(use_both_schemes,
"try the other device initialization scheme if the "
@@ -593,7 +606,7 @@ static int hub_hub_status(struct usb_hub *hub,
"%s failed (err = %d)\n", __func__, ret);
else {
*status = le16_to_cpu(hub->status->hub.wHubStatus);
- *change = le16_to_cpu(hub->status->hub.wHubChange);
+ *change = le16_to_cpu(hub->status->hub.wHubChange);
ret = 0;
}
mutex_unlock(&hub->status_mutex);
@@ -1233,11 +1246,20 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
/* Hubs have proper suspend/resume support */
usb_enable_autosuspend(hdev);
+#ifdef CONFIG_ARCH_U8500
+ if (hdev->level > MAX_TOPO_LEVEL_U8500) {
+ dev_err(&intf->dev,
+ "Unsupported bus topology: > %d "
+ " hub nesting\n", MAX_TOPO_LEVEL_U8500);
+ return -E2BIG;
+ }
+#else
if (hdev->level == MAX_TOPO_LEVEL) {
dev_err(&intf->dev,
"Unsupported bus topology: hub nested too deep\n");
return -E2BIG;
}
+#endif
#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
@@ -1510,12 +1532,14 @@ static void choose_address(struct usb_device *udev)
* bus->devnum_next. */
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
bus->devnum_next);
- if (devnum >= 128)
+ /* Due to Hardware bugs we need to reserve a device address
+ * for flushing of endpoints. */
+ if (devnum >= 127)
devnum = find_next_zero_bit(bus->devmap.devicemap,
128, 1);
- bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
+ bus->devnum_next = ( devnum >= 126 ? 1 : devnum + 1);
}
- if (devnum < 128) {
+ if (devnum < 127) {
set_bit(devnum, bus->devmap.devicemap);
udev->devnum = devnum;
}
@@ -1578,6 +1602,14 @@ void usb_disconnect(struct usb_device **pdev)
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+ if (udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) {
+ cancel_delayed_work_sync(&udev->bus->hnp_polling);
+ udev->bus->hnp_support = 0;
+ }
+#endif
+#endif
usb_lock_device(udev);
/* Free up all the children before we remove this device */
@@ -1681,16 +1713,42 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
"Dual-Role OTG device on %sHNP port\n",
(port1 == bus->otg_port)
? "" : "non-");
-
+#ifndef CONFIG_USB_OTG_20
/* enable HNP before suspend, it's simpler */
if (port1 == bus->otg_port)
bus->b_hnp_enable = 1;
+#else
+ /* a_alt_hnp_support is obsoleted */
+ if (port1 != bus->otg_port)
+ goto fail;
+
+ bus->hnp_support = 1;
+
+ /* a_hnp_support is not required for devices
+ * compliant to revision 2.0 or subsequent
+ * versions.
+ */
+ if (desc->bLength == sizeof(*desc) &&
+ le16_to_cpu(desc->bcdOTG) >= 0x0200)
+ goto out;
+
+ /* Legacy B-device i.e compliant to spec
+ * revision 1.3 expect A-device to set
+ * a_hnp_support or b_hnp_enable before
+ * selecting configuration. b_hnp_enable
+ * is set before suspending the port.
+ */
+#endif
err = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, 0,
+#ifndef CONFIG_USB_OTG_20
bus->b_hnp_enable
? USB_DEVICE_B_HNP_ENABLE
: USB_DEVICE_A_ALT_HNP_SUPPORT,
+#else
+ USB_DEVICE_A_HNP_SUPPORT,
+#endif
0, NULL, 0, USB_CTRL_SET_TIMEOUT);
if (err < 0) {
/* OTG MESSAGE: report errors here,
@@ -1699,24 +1757,133 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
dev_info(&udev->dev,
"can't set HNP mode: %d\n",
err);
+#ifndef CONFIG_USB_OTG_20
bus->b_hnp_enable = 0;
+#else
+ bus->hnp_support = 0;
+#endif
}
}
}
}
- if (!is_targeted(udev)) {
+#ifdef CONFIG_USB_OTG_20
+out:
+#endif
+#ifdef CONFIG_USB_OTG
+ {
+ u16 idVendor = le16_to_cpu(udev->descriptor.idVendor);
+ if (idVendor == USB_OTG_TEST_MODE_VID) {
+ u16 wValue, typeReq, wIndex;
+ u32 set_feature = 0;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ u16 idProduct = le16_to_cpu(
+ udev->descriptor.idProduct);
+ /* Convert the Test Mode Request to control request*/
+ wValue = USB_PORT_FEAT_TEST;
+ typeReq = SetPortFeature;
+ wIndex = 1;
+
+ switch (idProduct) {
+ case USB_OTG_TEST_SE0_NAK_PID:
+ wIndex |= USB_OTG_TEST_SE0_NAK << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_J_PID:
+ wIndex |= USB_OTG_TEST_J << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_K_PID:
+ wIndex |= USB_OTG_TEST_K << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_PACKET_PID:
+ wIndex |= USB_OTG_TEST_PACKET << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_HS_HOST_PORT_SUSPEND_RESUME_PID:
+ /* Sleep for 15 sec. Suspend for 15 Sec, Then Resume */
+ ssleep(15);
+
+ err = usb_port_suspend(udev, PMSG_SUSPEND);
+ if (err < 0)
+ dev_err(&udev->dev, "OTG TEST_MODE:"
+ "Suspend Fail, %d\n", err);
+ ssleep(15);
+ err = usb_autoresume_device(udev);
+ if (err < 0) {
+ dev_err(&udev->dev,
+ "can't autoresume for"
+ "OTG TEST_MODE: %d\n", err);
+ }
+ break;
+ case USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_PID:
+ /* Sleep for 15 Sec. Issue the GetDeviceDescriptor */
+ ssleep(15);
+ err = usb_get_device_descriptor(udev,
+ sizeof(udev->descriptor));
+ if (err < 0) {
+ dev_err(&udev->dev, "can't re-read"
+ "device descriptor for "
+ "OTG TEST MODE: %d\n", err);
+ }
+ break;
+ case USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_DATA_PID:
+ /* Issue GetDeviceDescriptor, Sleep for 15 Sec. */
+ err = usb_get_device_descriptor(udev,
+ sizeof(udev->descriptor));
+ if (err < 0) {
+ dev_err(&udev->dev, "can't re-read"
+ "device descriptor for "
+ "OTG TEST MODE: %d\n", err);
+ }
+ ssleep(15);
+ break;
+ default:
+ /* is_targeted() will take care for wrong PID */
+ dev_dbg(&udev->dev, "OTG TEST_MODE:Wrong PID %d\n",
+ idProduct);
+ break;
+ }
+
+ if (set_feature) {
+ err = hcd->driver->hub_control(hcd,
+ typeReq, wValue, wIndex,
+ NULL, 0);
+ }
+ }
+ }
+#endif
+
+ if (!is_targeted(udev)) {
/* Maybe it can talk to us, though we can't talk to it.
- * (Includes HNP test device.)
+ *(Includes HNP test device.)
*/
if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
- err = usb_port_suspend(udev, PMSG_SUSPEND);
- if (err < 0)
- dev_dbg(&udev->dev, "HNP fail, %d\n", err);
+#ifdef CONFIG_USB_OTG_20
+ if (udev->bus->hnp_support) {
+#endif
+ err = usb_port_suspend(udev, PMSG_SUSPEND);
+ if (err < 0)
+ dev_dbg(&udev->dev, "HNP fail, %d\n"
+ , err);
}
err = -ENOTSUPP;
+#ifndef CONFIG_USB_OTG_20
goto fail;
+#else
+ } else if (udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port) {
+ /* HNP polling is introduced in OTG supplement Rev 2.0
+ * and older devices does not support. Work is not
+ * re-armed if device returns STALL. B-Host also
+ * performs HNP polling.
+ */
+ schedule_delayed_work(&udev->bus->hnp_polling,
+ msecs_to_jiffies(THOST_REQ_POLL));
+ }
+#endif
}
fail:
#endif
@@ -2197,6 +2364,22 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
return status;
}
}
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+ if (!udev->bus->is_b_host && udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port) {
+ status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, 0,
+ USB_DEVICE_B_HNP_ENABLE,
+ 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (status < 0)
+ dev_dbg(&udev->dev, "can't enable HNP on port %d, "
+ "status %d\n", port1, status);
+ else
+ udev->bus->b_hnp_enable = 1;
+ }
+#endif
+#endif
/* see 7.1.7.6 */
status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);
@@ -2340,6 +2523,13 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
int status;
u16 portchange, portstatus;
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+ if (!udev->bus->is_b_host && udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port)
+ udev->bus->b_hnp_enable = 0;
+#endif
+#endif
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND))
@@ -2516,7 +2706,7 @@ EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
* Between connect detection and reset signaling there must be a delay
* of 100ms at least for debounce and power-settling. The corresponding
* timer shall restart whenever the downstream port detects a disconnect.
- *
+ *
* Apparently there are some bluetooth and irda-dongles and a number of
* low-speed devices for which this debounce period may last over a second.
* Not covered by the spec - but easy to deal with.
@@ -2694,7 +2884,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
default:
goto fail;
}
-
+
type = "";
switch (udev->speed) {
case USB_SPEED_LOW: speed = "low"; break;
@@ -2724,7 +2914,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->tt = &hub->tt;
udev->ttport = port1;
}
-
+
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
* Because device hardware and firmware is sometimes buggy in
* this area, and this is how Linux has done it for ages.
@@ -2869,7 +3059,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
}
-
+
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
@@ -3104,6 +3294,20 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
status = -ENOTCONN; /* Don't retry */
goto loop;
}
+#ifdef CONFIG_ARCH_U8500
+ if (hdev->parent == NULL)
+ usb_device_count = 1;
+
+ if (usb_device_count > MAX_USB_DEVICE_U8500) {
+
+ dev_err(&udev->dev,
+ "device connected is more than %d\n",
+ MAX_USB_DEVICE_U8500);
+
+ status = -ENOTCONN; /* Don't retry */
+ goto loop;
+ }
+#endif
}
/* reset (non-USB 3.0 devices) and get descriptor */
@@ -3145,7 +3349,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
goto loop_disable;
}
}
-
+
/* check for devices running slower than they could */
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
&& udev->speed == USB_SPEED_FULL
@@ -3203,7 +3407,7 @@ loop:
!(hcd->driver->port_handed_over)(hcd, port1))
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
port1);
-
+
done:
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent)
@@ -3329,7 +3533,7 @@ static void hub_events(void)
* EM interference sometimes causes badly
* shielded USB devices to be shutdown by
* the hub, this hack enables them again.
- * Works at least with mouse driver.
+ * Works at least with mouse driver.
*/
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
@@ -3367,7 +3571,7 @@ static void hub_events(void)
"resume on port %d, status %d\n",
i, ret);
}
-
+
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
dev_err (hub_dev,
"over-current change on port %d\n",
@@ -3646,7 +3850,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (ret < 0)
goto re_enumerate;
-
+
/* Device might have changed firmware (DFU or similar) */
if (descriptors_changed(udev, &descriptor)) {
dev_info(&udev->dev, "device firmware changed\n");
@@ -3719,7 +3923,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
done:
return 0;
-
+
re_enumerate:
hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV;
diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c
index 7542dce3f5a..b76a8a8961e 100644
--- a/drivers/usb/core/notify.c
+++ b/drivers/usb/core/notify.c
@@ -45,11 +45,17 @@ EXPORT_SYMBOL_GPL(usb_unregister_notify);
void usb_notify_add_device(struct usb_device *udev)
{
+#ifdef CONFIG_ARCH_U8500
+ usb_device_count++;
+#endif
blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev);
}
void usb_notify_remove_device(struct usb_device *udev)
{
+#ifdef CONFIG_ARCH_U8500
+ usb_device_count--;
+#endif
/* Protect against simultaneous usbfs open */
mutex_lock(&usbfs_mutex);
blocking_notifier_call_chain(&usb_notifier_list,
diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h
index e8cdce571bb..9cbac092e77 100644
--- a/drivers/usb/core/otg_whitelist.h
+++ b/drivers/usb/core/otg_whitelist.h
@@ -43,12 +43,43 @@ static struct usb_device_id whitelist_table [] = {
{ USB_DEVICE(0x0525, 0xa4a0), },
#endif
+#if defined(CONFIG_USB_OTG_20)
+{ USB_DEVICE_INFO(8, 6, 80) },/* Mass Storage Devices */
+#endif
+{ USB_DEVICE(0x4CC, 0x2323), },/* U8500 Board */
+{ USB_DEVICE(0x4D9, 0x1702), },/* HP Keyboard */
+{ USB_DEVICE(0x4E8, 0x1F06), },/* USB Hard disk */
+{ USB_DEVICE(0x46D, 0x0A0C), },/*USB Headset */
+{ USB_DEVICE(0x93A, 0x2510), },/* USB mouse */
+
{ } /* Terminating entry */
};
+/* The TEST_MODE Definition for OTG as per 6.4 of OTG Rev 2.0 */
+
+#ifdef CONFIG_USB_OTG
+#define USB_OTG_TEST_MODE_VID 0x1A0A
+#define USB_OTG_TEST_SE0_NAK_PID 0x0101
+#define USB_OTG_TEST_J_PID 0x0102
+#define USB_OTG_TEST_K_PID 0x0103
+#define USB_OTG_TEST_PACKET_PID 0x0104
+#define USB_OTG_TEST_HS_HOST_PORT_SUSPEND_RESUME_PID 0x0106
+#define USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_PID 0x0107
+#define USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_DATA_PID 0x0108
+
+#define USB_OTG_TEST_SE0_NAK 0x01
+#define USB_OTG_TEST_J 0x02
+#define USB_OTG_TEST_K 0x03
+#define USB_OTG_TEST_PACKET 0x04
+#endif
+
static int is_targeted(struct usb_device *dev)
{
struct usb_device_id *id = whitelist_table;
+#ifdef CONFIG_USB_OTG_20
+ u8 number_configs = 0;
+ u8 number_interface = 0;
+#endif
/* possible in developer configs only! */
if (!dev->bus->otg_port)
@@ -97,6 +128,33 @@ static int is_targeted(struct usb_device *dev)
}
/* add other match criteria here ... */
+#ifdef CONFIG_USB_OTG_20
+/* Checking class,subclass and protocal at interface level */
+for (number_configs = dev->descriptor.bNumConfigurations;
+ number_configs > 0; number_configs--)
+ for (number_interface = dev->config->desc.bNumInterfaces;
+ number_interface > 0; number_interface--)
+ for (id = whitelist_table; id->match_flags; id++) {
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
+ (id->bDeviceClass !=
+ dev->config->intf_cache[number_interface-1]
+ ->altsetting[0].desc.bInterfaceClass))
+ continue;
+ if ((id->match_flags &
+ USB_DEVICE_ID_MATCH_DEV_SUBCLASS)
+ && (id->bDeviceSubClass !=
+ dev->config->intf_cache[number_interface-1]
+ ->altsetting[0].desc.bInterfaceSubClass))
+ continue;
+ if ((id->match_flags &
+ USB_DEVICE_ID_MATCH_DEV_PROTOCOL)
+ && (id->bDeviceProtocol !=
+ dev->config->intf_cache[number_interface-1]
+ ->altsetting[0].desc.bInterfaceProtocol))
+ continue;
+ return 1;
+ }
+#endif
/* OTG MESSAGE: report errors here, customize to match your product */
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index cd882203ad3..4e9bc71440e 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -1,5 +1,8 @@
#include <linux/pm.h>
+#ifdef CONFIG_ARCH_U8500
+extern int usb_device_count;
+#endif
/* Functions local to drivers/usb/core/ */
extern int usb_create_sysfs_dev_files(struct usb_device *dev);
@@ -72,6 +75,17 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
#endif
+#ifdef CONFIG_USB_OTG
+#ifdef CONFIG_USB_OTG_20
+extern void usb_hnp_polling_work(struct work_struct *work);
+#else
+static inline void usb_hnp_polling_work(struct work_struct *work)
+{
+}
+#endif
+#endif
+
+
#ifdef CONFIG_USB_SUSPEND
extern void usb_autosuspend_device(struct usb_device *udev);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 591ae9fde19..70c5fe66931 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -836,6 +836,49 @@ config USB_G_PRINTER
For more information, see Documentation/usb/gadget_printer.txt
which includes sample code for accessing the device file.
+config USB_ANDROID
+ boolean "Android Gadget"
+ depends on SWITCH
+ help
+ The Android gadget driver supports multiple USB functions.
+ The functions can be configured via a board file and may be
+ enabled and disabled dynamically.
+
+config USB_ANDROID_ACM
+ boolean "Android gadget ACM serial function"
+ depends on USB_ANDROID
+ help
+ Provides ACM serial function for android gadget driver.
+
+config USB_ANDROID_ADB
+ boolean "Android gadget adb function"
+ depends on USB_ANDROID
+ help
+ Provides adb function for android gadget driver.
+
+config USB_ANDROID_MASS_STORAGE
+ boolean "Android gadget mass storage function"
+ depends on USB_ANDROID && SWITCH
+ help
+ Provides USB mass storage function for android gadget driver.
+
+config USB_ANDROID_MTP
+ boolean "Android MTP function"
+ depends on USB_ANDROID
+ help
+ Provides Media Transfer Protocol (MTP) support for android gadget driver.
+
+config USB_ANDROID_ETHER
+ boolean "Android gadget Ethernet function"
+ depends on USB_ANDROID
+ help
+ Provides CDC Ethernat (ECM: Ethernet Control Model) or
+ RNDIS (Remote NDIS) for android gadget driver.
+
+ ECM: The drivers are not provided by Microsoft.
+
+ RNDIS: The drivers are provided by Microsoft.
+
config USB_CDC_COMPOSITE
tristate "CDC Composite Device (Ethernet and ACM)"
depends on NET
@@ -928,4 +971,64 @@ config USB_G_WEBCAM
endchoice
+choice
+ prompt "Ether Mode"
+ depends on USB_ANDROID_ETHER
+
+config USB_ANDROID_ECM
+ boolean "Android gadget ECM ether function"
+ depends on USB_ANDROID
+ help
+ Provides CDC Ethernet (ECM) ether for android gadget driver.
+
+ ECM and RNDIS should be mutually exclusive as both classes are
+ serving the same purpose.
+
+config USB_ANDROID_RNDIS
+ boolean "Android gadget RNDIS ethernet function"
+ depends on USB_ANDROID
+ help
+ Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
+ and Microsoft provides redistributable binary RNDIS drivers for
+ older versions of Windows.
+
+ If you say "y" here, the Ethernet gadget driver will try to provide
+ a second device configuration, supporting RNDIS to talk to such
+ Microsoft USB hosts.
+
+ To make MS-Windows work with this, use Documentation/usb/linux.inf
+ as the "driver info file". For versions of MS-Windows older than
+ XP, you'll need to download drivers from Microsoft's website; a URL
+ is given in comments found in that info file.
+
+config USB_ANDROID_RNDIS_WCEIS
+ boolean "Use Windows Internet Sharing Class/SubClass/Protocol"
+ depends on USB_ANDROID_RNDIS
+ help
+ Causes the driver to look like a Windows-compatible Internet
+ Sharing device, so Windows auto-detects it.
+
+ If you enable this option, the device is no longer CDC ethernet
+ compatible.
+
+endchoice
+
+config USB_OTG_13
+ bool "OTG 1.3 USB SUPPORT"
+ select PM_RUNTIME
+ select USB_OTG_WHITELIST
+ select USB_SUSPEND
+ help
+ Enabling the whitelist (Target Peripheral List-TPL) and runtime power
+ management at system level and usb level for OTG 1.3.
+
+config USB_OTG_20
+ bool "OTG 2.0 USB SUPPORT"
+ select PM_RUNTIME
+ select USB_OTG_WHITELIST
+ select USB_SUSPEND
+ help
+ Enabling the whitelist (Target Peripheral List-TPL) and runtime power
+ management at system level and usb level for OTG 2.0.
+
endif # USB_GADGET
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 9bcde110feb..84a72fb0773 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -63,4 +63,11 @@ obj-$(CONFIG_USB_G_HID) += g_hid.o
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
+obj-$(CONFIG_USB_ANDROID) += android.o
+obj-$(CONFIG_USB_ANDROID_ACM) += f_acm.o u_serial.o
+obj-$(CONFIG_USB_ANDROID_ADB) += f_adb.o
+obj-$(CONFIG_USB_ANDROID_ECM) += f_ecm.o u_ether.o
+obj-$(CONFIG_USB_ANDROID_MASS_STORAGE) += f_mass_storage.o
+obj-$(CONFIG_USB_ANDROID_MTP) += f_mtp.o
+obj-$(CONFIG_USB_ANDROID_RNDIS) += f_rndis.o u_ether.o
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
new file mode 100644
index 00000000000..1b316f092bf
--- /dev/null
+++ b/drivers/usb/gadget/android.c
@@ -0,0 +1,478 @@
+/*
+ * Gadget Driver for Android
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/platform_device.h>
+
+#include <linux/usb/android_composite.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+
+#include "gadget_chips.h"
+
+/*
+ * Kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module. So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+#include "composite.c"
+
+MODULE_AUTHOR("Mike Lockwood");
+MODULE_DESCRIPTION("Android Composite USB Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+static const char longname[] = "Gadget Android";
+
+/* Default vendor and product IDs, overridden by platform data */
+#define VENDOR_ID 0x18D1
+#define PRODUCT_ID 0x0001
+
+struct android_dev {
+ struct usb_composite_dev *cdev;
+ struct usb_configuration *config;
+ int num_products;
+ struct android_usb_product *products;
+ int num_functions;
+ char **functions;
+ u8 bound;
+
+ int product_id;
+ int version;
+};
+
+static struct android_dev *_android_dev;
+
+/* string IDs are assigned dynamically */
+
+#define STRING_MANUFACTURER_IDX 0
+#define STRING_PRODUCT_IDX 1
+#define STRING_SERIAL_IDX 2
+
+/* String Table */
+static struct usb_string strings_dev[] = {
+ /* These dummy values should be overridden by platform data */
+ [STRING_MANUFACTURER_IDX].s = "Android",
+ [STRING_PRODUCT_IDX].s = "Android",
+ [STRING_SERIAL_IDX].s = "0123456789ABCDEF",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof(device_desc),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .idVendor = __constant_cpu_to_le16(VENDOR_ID),
+ .idProduct = __constant_cpu_to_le16(PRODUCT_ID),
+ .bcdDevice = __constant_cpu_to_le16(0xffff),
+ .bNumConfigurations = 1,
+};
+
+#if defined(CONFIG_USB_OTG_20) || defined(CONFIG_USB_OTG_13)
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+#endif
+
+static struct list_head _functions = LIST_HEAD_INIT(_functions);
+static int _registered_function_count = 0;
+
+static struct android_usb_function *get_function(const char *name)
+{
+ struct android_usb_function *f;
+ list_for_each_entry(f, &_functions, list) {
+ if (!strcmp(name, f->name))
+ return f;
+ }
+ return 0;
+}
+
+static void bind_functions(struct android_dev *dev)
+{
+ struct android_usb_function *f;
+ char **functions = dev->functions;
+ int i;
+
+ for (i = 0; i < dev->num_functions; i++) {
+ char *name = *functions++;
+ f = get_function(name);
+ if (f)
+ f->bind_config(dev->config);
+ else
+ printk(KERN_ERR "function %s not found in bind_functions\n", name);
+ }
+ dev->bound = 1;
+}
+
+static void rebind_functions(struct android_dev *dev)
+
+{
+ struct android_usb_function *android_f;
+ struct usb_function *f;
+
+ dev->config->next_interface_id = 0;
+ list_for_each_entry(f, &dev->config->functions, list) {
+ if (f->disabled)
+ continue;
+
+ /* rebind our functions if they are enabled */
+ android_f = get_function(f->name);
+ if (android_f && android_f->rebind_config)
+ android_f->rebind_config(dev->config, f);
+ else
+ printk(KERN_ERR "function %s not found in rebind_functions\n",
+ f->name);
+ }
+ dev->config->interface[dev->config->next_interface_id] = NULL;
+}
+
+static int android_bind_config(struct usb_configuration *c)
+{
+ struct android_dev *dev = _android_dev;
+
+ printk(KERN_DEBUG "android_bind_config\n");
+#if defined(CONFIG_USB_OTG_20) || defined(CONFIG_USB_OTG_13)
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+#endif
+ dev->config = c;
+
+ /* bind our functions if they have all registered */
+ if (_registered_function_count == dev->num_functions)
+ bind_functions(dev);
+
+ return 0;
+}
+
+static int android_setup_config(struct usb_configuration *c,
+ const struct usb_ctrlrequest *ctrl);
+
+static struct usb_configuration android_config_driver = {
+ .label = "android",
+ .bind = android_bind_config,
+ .setup = android_setup_config,
+ .bConfigurationValue = 1,
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
+};
+
+static int android_setup_config(struct usb_configuration *c,
+ const struct usb_ctrlrequest *ctrl)
+{
+ int i;
+ int ret = -EOPNOTSUPP;
+
+ for (i = 0; i < android_config_driver.next_interface_id; i++) {
+ if (android_config_driver.interface[i]->setup) {
+ ret = android_config_driver.interface[i]->setup(
+ android_config_driver.interface[i], ctrl);
+ if (ret >= 0)
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int product_has_function(struct android_usb_product *p,
+ struct usb_function *f)
+{
+ char **functions = p->functions;
+ int count = p->num_functions;
+ const char *name = f->name;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (!strcmp(name, *functions++))
+ return 1;
+ }
+ return 0;
+}
+
+static int product_matches_functions(struct android_usb_product *p)
+{
+ struct usb_function *f;
+ list_for_each_entry(f, &android_config_driver.functions, list) {
+ if (product_has_function(p, f) == !!f->disabled)
+ return 0;
+ }
+ return 1;
+}
+
+static int get_product_id(struct android_dev *dev)
+{
+ struct android_usb_product *p = dev->products;
+ int count = dev->num_products;
+ int i;
+
+ if (p) {
+ for (i = 0; i < count; i++, p++) {
+ if (product_matches_functions(p))
+ return p->product_id;
+ }
+ }
+ /* use default product ID */
+ return dev->product_id;
+}
+
+static int android_bind(struct usb_composite_dev *cdev)
+{
+ struct android_dev *dev = _android_dev;
+ struct usb_gadget *gadget = cdev->gadget;
+ int gcnum, id, product_id, ret;
+
+ printk(KERN_INFO "android_bind\n");
+
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_MANUFACTURER_IDX].id = id;
+ device_desc.iManufacturer = id;
+
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_PRODUCT_IDX].id = id;
+ device_desc.iProduct = id;
+
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_SERIAL_IDX].id = id;
+ device_desc.iSerialNumber = id;
+
+
+ /*
+ * As per USB compliance update, a device that is actively drawing
+ * more than 100mA from USB must report itself as bus-powered in
+ * the GetStatus(DEVICE) call.
+ */
+ if (android_config_driver.bMaxPower <=
+ (USB_SELF_POWER_VBUS_MAX_DRAW / 2)) {
+ android_config_driver.bmAttributes =
+ USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
+ usb_gadget_set_selfpowered(gadget);
+ } else
+ android_config_driver.bmAttributes = USB_CONFIG_ATT_ONE;
+
+ /* register our configuration */
+ ret = usb_add_config(cdev, &android_config_driver);
+ if (ret) {
+ printk(KERN_ERR "usb_add_config failed\n");
+ return ret;
+ }
+
+ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum >= 0)
+ device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
+ else {
+ /* gadget zero is so simple (for now, no altsettings) that
+ * it SHOULD NOT have problems with bulk-capable hardware.
+ * so just warn about unrcognized controllers -- don't panic.
+ *
+ * things like configuration and altsetting numbering
+ * can need hardware-specific attention though.
+ */
+ pr_warning("%s: controller '%s' not recognized\n",
+ longname, gadget->name);
+ device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+ }
+
+ dev->cdev = cdev;
+ product_id = get_product_id(dev);
+ device_desc.idProduct = __constant_cpu_to_le16(product_id);
+ cdev->desc.idProduct = device_desc.idProduct;
+
+ return 0;
+}
+
+static struct usb_composite_driver android_usb_driver = {
+ .name = "android_usb",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .bind = android_bind,
+ .enable_function = android_enable_function,
+};
+
+void android_register_function(struct android_usb_function *f)
+{
+ struct android_dev *dev = _android_dev;
+
+ printk(KERN_INFO "android_register_function %s\n", f->name);
+ list_add_tail(&f->list, &_functions);
+ _registered_function_count++;
+
+ /* bind our functions if they have all registered
+ * and the main driver has bound.
+ */
+ if (dev && dev->config && _registered_function_count == dev->num_functions)
+ bind_functions(dev);
+}
+
+void android_enable_function(struct usb_function *f, int enable)
+{
+ struct android_dev *dev = _android_dev;
+ int disable = !enable;
+ int product_id;
+
+ if (!!f->disabled != disable) {
+ usb_function_set_enabled(f, !disable);
+
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ if (!strcmp(f->name, "rndis")) {
+ struct usb_function *func;
+
+ /* We need to specify the COMM class in the device descriptor
+ * if we are using RNDIS.
+ */
+ if (enable)
+#ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS
+ dev->cdev->desc.bDeviceClass = USB_CLASS_WIRELESS_CONTROLLER;
+#else
+ dev->cdev->desc.bDeviceClass = USB_CLASS_COMM;
+#endif
+ else
+ dev->cdev->desc.bDeviceClass = USB_CLASS_PER_INTERFACE;
+
+ /* Windows does not support other interfaces when RNDIS is enabled,
+ * so we disable UMS and MTP when RNDIS is on.
+ */
+ list_for_each_entry(func, &android_config_driver.functions, list) {
+ if (!strcmp(func->name, "usb_mass_storage")
+ || !strcmp(func->name, "mtp")
+ || !strcmp(func->name, "adb")
+ || !strcmp(func->name, "acm")) {
+ usb_function_set_enabled(func, !enable);
+ }
+ }
+ }
+#endif
+
+ product_id = get_product_id(dev);
+ device_desc.idProduct = __constant_cpu_to_le16(product_id);
+ if (dev->cdev)
+ dev->cdev->desc.idProduct = device_desc.idProduct;
+ if (dev->bound)
+ rebind_functions(dev);
+ usb_composite_force_reset(dev->cdev);
+ }
+}
+
+static int android_probe(struct platform_device *pdev)
+{
+ struct android_usb_platform_data *pdata = pdev->dev.platform_data;
+ struct android_dev *dev = _android_dev;
+
+ printk(KERN_INFO "android_probe pdata: %p\n", pdata);
+
+ if (pdata) {
+ dev->products = pdata->products;
+ dev->num_products = pdata->num_products;
+ dev->functions = pdata->functions;
+ dev->num_functions = pdata->num_functions;
+ dev->bound = 0;
+ if (pdata->vendor_id)
+ device_desc.idVendor =
+ __constant_cpu_to_le16(pdata->vendor_id);
+ if (pdata->product_id) {
+ dev->product_id = pdata->product_id;
+ device_desc.idProduct =
+ __constant_cpu_to_le16(pdata->product_id);
+ }
+ if (pdata->version)
+ dev->version = pdata->version;
+
+ if (pdata->product_name)
+ strings_dev[STRING_PRODUCT_IDX].s = pdata->product_name;
+ if (pdata->manufacturer_name)
+ strings_dev[STRING_MANUFACTURER_IDX].s =
+ pdata->manufacturer_name;
+ if (pdata->serial_number)
+ strings_dev[STRING_SERIAL_IDX].s = pdata->serial_number;
+ }
+
+ return usb_composite_register(&android_usb_driver);
+}
+
+static struct platform_driver android_platform_driver = {
+ .driver = { .name = "android_usb", },
+ .probe = android_probe,
+};
+
+static int __init init(void)
+{
+ struct android_dev *dev;
+
+ printk(KERN_INFO "android init\n");
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* set default values, which should be overridden by platform data */
+ dev->product_id = PRODUCT_ID;
+ _android_dev = dev;
+
+ return platform_driver_register(&android_platform_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+ usb_composite_unregister(&android_usb_driver);
+ platform_driver_unregister(&android_platform_driver);
+ kfree(_android_dev);
+ _android_dev = NULL;
+}
+module_exit(cleanup);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 391d169f8d0..5b20f6ba268 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -24,9 +24,10 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
-
+#include <linux/kdev_t.h>
+#include <linux/delay.h>
#include <linux/usb/composite.h>
-
+#include <linux/switch.h>
/*
* The code in this file is utility code, used to build a gadget driver
@@ -71,6 +72,56 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
/*-------------------------------------------------------------------------*/
+static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_function *f = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", !f->disabled);
+}
+
+static ssize_t enable_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct usb_function *f = dev_get_drvdata(dev);
+ struct usb_composite_driver *driver = f->config->cdev->driver;
+ int value;
+
+ sscanf(buf, "%d", &value);
+ if (driver->enable_function)
+ driver->enable_function(f, value);
+ else
+ usb_function_set_enabled(f, value);
+
+ return size;
+}
+
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
+
+void usb_function_set_enabled(struct usb_function *f, int enabled)
+{
+ f->disabled = !enabled;
+ kobject_uevent(&f->dev->kobj, KOBJ_CHANGE);
+}
+
+
+void usb_composite_force_reset(struct usb_composite_dev *cdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+ /* force reenumeration */
+ if (cdev && cdev->gadget && cdev->gadget->speed != USB_SPEED_UNKNOWN) {
+ spin_unlock_irqrestore(&cdev->lock, flags);
+
+ usb_gadget_disconnect(cdev->gadget);
+ msleep(10);
+ usb_gadget_connect(cdev->gadget);
+ } else {
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ }
+}
+
/**
* usb_add_function() - add a function to a configuration
* @config: the configuration
@@ -88,15 +139,30 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
int usb_add_function(struct usb_configuration *config,
struct usb_function *function)
{
+ struct usb_composite_dev *cdev = config->cdev;
int value = -EINVAL;
+ int index;
- DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
+ DBG(cdev, "adding '%s'/%p to config '%s'/%p\n",
function->name, function,
config->label, config);
if (!function->set_alt || !function->disable)
goto done;
+ index = atomic_inc_return(&cdev->driver->function_count);
+ function->dev = device_create(cdev->driver->class, NULL,
+ MKDEV(0, index), NULL, function->name);
+ if (IS_ERR(function->dev))
+ return PTR_ERR(function->dev);
+
+ value = device_create_file(function->dev, &dev_attr_enable);
+ if (value < 0) {
+ device_destroy(cdev->driver->class, MKDEV(0, index));
+ return value;
+ }
+ dev_set_drvdata(function->dev, function);
+
function->config = config;
list_add_tail(&function->list, &config->functions);
@@ -122,7 +188,7 @@ int usb_add_function(struct usb_configuration *config,
done:
if (value)
- DBG(config->cdev, "adding '%s'/%p --> %d\n",
+ DBG(cdev, "adding '%s'/%p --> %d\n",
function->name, function, value);
return value;
}
@@ -232,17 +298,19 @@ static int config_buf(struct usb_configuration *config,
enum usb_device_speed speed, void *buf, u8 type)
{
struct usb_config_descriptor *c = buf;
+ struct usb_interface_descriptor *intf;
void *next = buf + USB_DT_CONFIG_SIZE;
int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
struct usb_function *f;
int status;
+ int interfaceCount = 0;
+ u8 *dest;
/* write the config descriptor */
c = buf;
c->bLength = USB_DT_CONFIG_SIZE;
c->bDescriptorType = type;
- /* wTotalLength is written later */
- c->bNumInterfaces = config->next_interface_id;
+ /* wTotalLength and bNumInterfaces are written later */
c->bConfigurationValue = config->bConfigurationValue;
c->iConfiguration = config->iConfiguration;
c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
@@ -261,23 +329,40 @@ static int config_buf(struct usb_configuration *config,
/* add each function's descriptors */
list_for_each_entry(f, &config->functions, list) {
struct usb_descriptor_header **descriptors;
+ struct usb_descriptor_header *descriptor;
if (speed == USB_SPEED_HIGH)
descriptors = f->hs_descriptors;
else
descriptors = f->descriptors;
- if (!descriptors)
+ if (f->disabled || !descriptors || descriptors[0] == NULL)
continue;
status = usb_descriptor_fillbuf(next, len,
(const struct usb_descriptor_header **) descriptors);
if (status < 0)
return status;
+
+ /* set interface numbers dynamically */
+ dest = next;
+ while ((descriptor = *descriptors++) != NULL) {
+ intf = (struct usb_interface_descriptor *)dest;
+ if (intf->bDescriptorType == USB_DT_INTERFACE) {
+ /* don't increment bInterfaceNumber for alternate settings */
+ if (intf->bAlternateSetting == 0)
+ intf->bInterfaceNumber = interfaceCount++;
+ else
+ intf->bInterfaceNumber = interfaceCount - 1;
+ }
+ dest += intf->bLength;
+ }
+
len -= status;
next += status;
}
len = next - buf;
c->wTotalLength = cpu_to_le16(len);
+ c->bNumInterfaces = interfaceCount;
return len;
}
@@ -424,6 +509,8 @@ static int set_config(struct usb_composite_dev *cdev,
if (!f)
break;
+ if (f->disabled)
+ continue;
/*
* Record which endpoints are used by the function. This is used
@@ -463,6 +550,8 @@ static int set_config(struct usb_composite_dev *cdev,
power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
done:
usb_gadget_vbus_draw(gadget, power);
+
+ schedule_work(&cdev->switch_work);
return result;
}
@@ -717,6 +806,14 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
struct usb_function *f = NULL;
u8 endp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+ if (!cdev->connected) {
+ cdev->connected = 1;
+ schedule_work(&cdev->switch_work);
+ }
+ spin_unlock_irqrestore(&cdev->lock, flags);
/* partial re-init of the response message; the function or the
* gadget might need to intercept e.g. a control-OUT completion
* when we delegate to it.
@@ -759,6 +856,21 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
case USB_DT_STRING:
value = get_string(cdev, req->buf,
w_index, w_value & 0xff);
+
+ /* Allow functions to handle USB_DT_STRING.
+ * This is required for MTP.
+ */
+ if (value < 0) {
+ struct usb_configuration *cfg;
+ list_for_each_entry(cfg, &cdev->configs, list) {
+ if (cfg && cfg->setup) {
+ value = cfg->setup(cfg, ctrl);
+ if (value >= 0)
+ break;
+ }
+ }
+ }
+
if (value >= 0)
value = min(w_length, (u16) value);
break;
@@ -784,11 +896,11 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
case USB_REQ_GET_CONFIGURATION:
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
- if (cdev->config)
+ if (cdev->config) {
*(u8 *)req->buf = cdev->config->bConfigurationValue;
- else
+ value = min(w_length, (u16) 1);
+ } else
*(u8 *)req->buf = 0;
- value = min(w_length, (u16) 1);
break;
/* function drivers must handle get/set altsetting; if there's
@@ -838,6 +950,8 @@ unknown:
*/
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
+ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
+ break;
f = cdev->config->interface[intf];
break;
@@ -862,6 +976,25 @@ unknown:
value = c->setup(c, ctrl);
}
+ /* If the vendor request is not processed (value < 0),
+ * call all device registered configure setup callbacks
+ * to process it.
+ * This is used to handle the following cases:
+ * - vendor request is for the device and arrives before
+ * setconfiguration.
+ * - Some devices are required to handle vendor request before
+ * setconfiguration such as MTP, USBNET.
+ */
+
+ if (value < 0) {
+ struct usb_configuration *cfg;
+
+ list_for_each_entry(cfg, &cdev->configs, list) {
+ if (cfg && cfg->setup)
+ value = cfg->setup(cfg, ctrl);
+ }
+ }
+
goto done;
}
@@ -887,12 +1020,19 @@ static void composite_disconnect(struct usb_gadget *gadget)
struct usb_composite_dev *cdev = get_gadget_data(gadget);
unsigned long flags;
+#ifdef CONFIG_USB_OTG_20
+ gadget->host_request = 0;
+#endif
+
/* REVISIT: should we have config and device level
* disconnect callbacks?
*/
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->config)
reset_config(cdev);
+
+ cdev->connected = 0;
+ schedule_work(&cdev->switch_work);
spin_unlock_irqrestore(&cdev->lock, flags);
}
@@ -910,6 +1050,25 @@ static ssize_t composite_show_suspended(struct device *dev,
static DEVICE_ATTR(suspended, 0444, composite_show_suspended, NULL);
+#ifdef CONFIG_USB_OTG_20
+static ssize_t composite_set_host_request(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ int value;
+
+ if (sscanf(buf, "%d", &value) != 1)
+ return -EINVAL;
+
+ gadget->host_request = !!value;
+ return count;
+
+}
+
+static DEVICE_ATTR(host_request, S_IWUSR, NULL, composite_set_host_request);
+#endif
+
static void
composite_unbind(struct usb_gadget *gadget)
{
@@ -954,6 +1113,13 @@ composite_unbind(struct usb_gadget *gadget)
kfree(cdev->req->buf);
usb_ep_free_request(gadget->ep0, cdev->req);
}
+
+ switch_dev_unregister(&cdev->sw_connected);
+ switch_dev_unregister(&cdev->sw_config);
+#ifdef CONFIG_USB_OTG_20
+ device_remove_file(&gadget->dev, &dev_attr_host_request);
+#endif
+
kfree(cdev);
set_gadget_data(gadget, NULL);
device_remove_file(&gadget->dev, &dev_attr_suspended);
@@ -982,6 +1148,30 @@ string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
}
}
+static void
+composite_switch_work(struct work_struct *data)
+{
+ struct usb_composite_dev *cdev =
+ container_of(data, struct usb_composite_dev, switch_work);
+ struct usb_configuration *config = cdev->config;
+ int connected;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+ if (cdev->connected != cdev->sw_connected.state) {
+ connected = cdev->connected;
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ switch_set_state(&cdev->sw_connected, connected);
+ } else {
+ spin_unlock_irqrestore(&cdev->lock, flags);
+}
+
+ if (config)
+ switch_set_state(&cdev->sw_config, config->bConfigurationValue);
+ else
+ switch_set_state(&cdev->sw_config, 0);
+}
+
static int composite_bind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev;
@@ -1009,7 +1199,13 @@ static int composite_bind(struct usb_gadget *gadget)
cdev->bufsiz = USB_BUFSIZ;
cdev->driver = composite;
- usb_gadget_set_selfpowered(gadget);
+ /*
+ * As per USB compliance update, a device that is actively drawing
+ * more than 100mA from USB must report itself as bus-powered in
+ * the GetStatus(DEVICE) call.
+ */
+ if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW)
+ usb_gadget_set_selfpowered(gadget);
/* interface and string IDs start at zero via kzalloc.
* we force endpoints to start unassigned; few controller
@@ -1033,6 +1229,24 @@ static int composite_bind(struct usb_gadget *gadget)
if (status < 0)
goto fail;
+
+ cdev->sw_connected.name = "usb_connected";
+ status = switch_dev_register(&cdev->sw_connected);
+ if (status < 0)
+ goto fail;
+ cdev->sw_config.name = "usb_configuration";
+ status = switch_dev_register(&cdev->sw_config);
+ if (status < 0)
+ goto fail;
+ INIT_WORK(&cdev->switch_work, composite_switch_work);
+
+#ifdef CONFIG_USB_OTG_20
+ status = device_create_file(&gadget->dev, &dev_attr_host_request);
+ if (status)
+ DBG(cdev, "unable to create host_request sysfs file\n");
+#endif
+
+
cdev->desc = *composite->dev;
cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
@@ -1107,6 +1321,23 @@ composite_resume(struct usb_gadget *gadget)
cdev->suspended = 0;
}
+static int
+composite_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct usb_function *f = dev_get_drvdata(dev);
+
+ if (!f) {
+ /* this happens when the device is first created */
+ return 0;
+ }
+
+ if (add_uevent_var(env, "FUNCTION=%s", f->name))
+ return -ENOMEM;
+ if (add_uevent_var(env, "ENABLED=%d", !f->disabled))
+ return -ENOMEM;
+ return 0;
+}
+
/*-------------------------------------------------------------------------*/
static struct usb_gadget_driver composite_driver = {
@@ -1152,6 +1383,11 @@ int usb_composite_register(struct usb_composite_driver *driver)
composite_driver.driver.name = driver->name;
composite = driver;
+ driver->class = class_create(THIS_MODULE, "usb_composite");
+ if (IS_ERR(driver->class))
+ return PTR_ERR(driver->class);
+ driver->class->dev_uevent = composite_uevent;
+
return usb_gadget_register_driver(&composite_driver);
}
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 8a832488ccd..dc2bd3af7d8 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -2,6 +2,7 @@
* epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
*
* Copyright (C) 2004 David Brownell
+ * Copyright (C) 2009 ST Ericsson
*
* 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
@@ -79,6 +80,12 @@ ep_matches (
if (USB_ENDPOINT_XFER_CONTROL == type)
return 0;
+#if defined(CONFIG_ARCH_U8500)
+ /* 512 size endpoints are scarce, so leave them for bulk type */
+ if (ep->maxpacket == 512 && USB_ENDPOINT_XFER_INT == type)
+ return 0;
+#endif
+
/* some other naming convention */
if ('e' != ep->name[0])
return 0;
@@ -287,10 +294,22 @@ struct usb_ep *usb_ep_autoconfig (
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
- if (ep_matches (gadget, ep, desc))
- return ep;
- }
+#if (defined(CONFIG_ARCH_U8500) && !defined(CONFIG_MUSB_PIO_ONLY))
+ if (!strcmp(((gadget->dev).driver)->name, "g_file_storage")) {
+ if ((strcmp(ep->name, "ep1in") == 0) ||
+ (strcmp(ep->name, "ep1out") == 0)) {
+ if (ep_matches (gadget, ep, desc))
+ return ep;
+ }
+ } else {
+#endif
+ if (ep_matches (gadget, ep, desc))
+ return ep;
+#if (defined(CONFIG_ARCH_U8500) && !defined(CONFIG_MUSB_PIO_ONLY))
+ }
+#endif
+ }
/* Fail */
return NULL;
}
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index d47a123f15a..7cc8228bf49 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/usb/android_composite.h>
#include "u_serial.h"
#include "gadget_chips.h"
@@ -111,7 +112,7 @@ acm_iad_descriptor = {
.bInterfaceCount = 2, // control + data
.bFunctionClass = USB_CLASS_COMM,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
- .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
/* .iFunction = DYNAMIC */
};
@@ -405,10 +406,10 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
usb_ep_disable(acm->notify);
} else {
VDBG(cdev, "init acm ctrl interface %d\n", intf);
- acm->notify_desc = ep_choose(cdev->gadget,
- acm->hs.notify,
- acm->fs.notify);
}
+ acm->notify_desc = ep_choose(cdev->gadget,
+ acm->hs.notify,
+ acm->fs.notify);
usb_ep_enable(acm->notify, acm->notify_desc);
acm->notify->driver_data = acm;
@@ -418,11 +419,11 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
gserial_disconnect(&acm->port);
} else {
DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
- acm->port.in_desc = ep_choose(cdev->gadget,
- acm->hs.in, acm->fs.in);
- acm->port.out_desc = ep_choose(cdev->gadget,
- acm->hs.out, acm->fs.out);
}
+ acm->port.in_desc = ep_choose(cdev->gadget,
+ acm->hs.in, acm->fs.in);
+ acm->port.out_desc = ep_choose(cdev->gadget,
+ acm->hs.out, acm->fs.out);
gserial_connect(&acm->port, acm->port_num);
} else
@@ -782,3 +783,110 @@ int acm_bind_config(struct usb_configuration *c, u8 port_num)
kfree(acm);
return status;
}
+
+#ifdef CONFIG_USB_ANDROID_ACM
+
+int acm_function_bind_config(struct usb_configuration *c)
+{
+ int ret = acm_bind_config(c, 0);
+ if (ret == 0)
+ gserial_setup(c->cdev->gadget, 1);
+ return ret;
+}
+
+static int acm_function_rebind_config(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_acm *acm = func_to_acm(f);
+ int status1, status2 = 0;
+ struct usb_descriptor_header **desc = f->descriptors;
+
+ /* re-allocate instance-specific interface IDs, and patch descriptors */
+ status1 = usb_interface_id(c, f);
+ if (status1 < 0)
+ goto fail;
+ acm->ctrl_id = status1;
+
+ acm_iad_descriptor.bFirstInterface = status1;
+ acm_control_interface_desc.bInterfaceNumber = status1;
+ acm_union_desc .bMasterInterface0 = status1;
+
+ ((struct usb_interface_assoc_descriptor *)desc[0])
+ ->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)desc[1])
+ ->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bMasterInterface0 = status1;
+
+ status2 = usb_interface_id(c, f);
+ if (status2 < 0)
+ goto fail;
+ acm->data_id = status2;
+
+ acm_data_interface_desc.bInterfaceNumber = status2;
+ acm_union_desc.bSlaveInterface0 = status2;
+ acm_call_mgmt_descriptor.bDataInterface = status2;
+
+ ((struct usb_interface_descriptor *)desc[7])
+ ->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bSlaveInterface0 = status2;
+ ((struct usb_cdc_call_mgmt_descriptor *)desc[3])
+ ->bDataInterface = status2;
+
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ desc = f->hs_descriptors;
+
+ ((struct usb_interface_assoc_descriptor *)desc[0])
+ ->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)desc[1])
+ ->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bMasterInterface0 = status1;
+
+ ((struct usb_interface_descriptor *)desc[7])
+ ->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bSlaveInterface0 = status2;
+ ((struct usb_cdc_call_mgmt_descriptor *)desc[3])
+ ->bDataInterface = status2;
+ }
+
+ return 0;
+
+fail:
+ if (acm->notify_req)
+ gs_free_req(acm->notify, acm->notify_req);
+
+ /* we might as well release our claims on endpoints */
+ if (acm->notify)
+ acm->notify->driver_data = NULL;
+ if (acm->port.out)
+ acm->port.out->driver_data = NULL;
+ if (acm->port.in)
+ acm->port.in->driver_data = NULL;
+
+ ERROR(cdev, "%s/%p: can't re-bind, err %d %d\n",
+ f->name, f, status1, status2);
+
+ if (status2 < 0)
+ status1 = status2;
+ return status1;
+}
+
+static struct android_usb_function acm_function = {
+ .name = "acm",
+ .bind_config = acm_function_bind_config,
+ .rebind_config = acm_function_rebind_config,
+};
+
+static int __init init(void)
+{
+ printk(KERN_INFO "f_acm init\n");
+ android_register_function(&acm_function);
+ return 0;
+}
+module_init(init);
+
+#endif /* CONFIG_USB_ANDROID_ACM */
diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c
new file mode 100644
index 00000000000..1319b158d72
--- /dev/null
+++ b/drivers/usb/gadget/f_adb.c
@@ -0,0 +1,670 @@
+/*
+ * Gadget Driver for Android ADB
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+
+#include <linux/usb/android_composite.h>
+
+#define BULK_BUFFER_SIZE 4096
+
+/* number of tx requests to allocate */
+#define TX_REQ_MAX 4
+
+static const char shortname[] = "android_adb";
+
+struct adb_dev {
+ struct usb_function function;
+ struct usb_composite_dev *cdev;
+ spinlock_t lock;
+
+ struct usb_ep *ep_in;
+ struct usb_ep *ep_out;
+
+ int online;
+ int error;
+
+ atomic_t read_excl;
+ atomic_t write_excl;
+ atomic_t open_excl;
+
+ struct list_head tx_idle;
+
+ wait_queue_head_t read_wq;
+ wait_queue_head_t write_wq;
+ struct usb_request *rx_req;
+ int rx_done;
+};
+
+static struct usb_interface_descriptor adb_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0xFF,
+ .bInterfaceSubClass = 0x42,
+ .bInterfaceProtocol = 1,
+};
+
+static struct usb_endpoint_descriptor adb_highspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor adb_highspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor adb_fullspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor adb_fullspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_adb_descs[] = {
+ (struct usb_descriptor_header *) &adb_interface_desc,
+ (struct usb_descriptor_header *) &adb_fullspeed_in_desc,
+ (struct usb_descriptor_header *) &adb_fullspeed_out_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *hs_adb_descs[] = {
+ (struct usb_descriptor_header *) &adb_interface_desc,
+ (struct usb_descriptor_header *) &adb_highspeed_in_desc,
+ (struct usb_descriptor_header *) &adb_highspeed_out_desc,
+ NULL,
+};
+
+
+/* temporary variable used between adb_open() and adb_gadget_bind() */
+static struct adb_dev *_adb_dev;
+
+static atomic_t adb_enable_excl;
+
+static inline struct adb_dev *func_to_dev(struct usb_function *f)
+{
+ return container_of(f, struct adb_dev, function);
+}
+
+
+static struct usb_request *adb_request_new(struct usb_ep *ep, int buffer_size)
+{
+ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req)
+ return NULL;
+
+ /* now allocate buffers for the requests */
+ req->buf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static void adb_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+ if (req) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static inline int _lock(atomic_t *excl)
+{
+ if (atomic_inc_return(excl) == 1) {
+ return 0;
+ } else {
+ atomic_dec(excl);
+ return -1;
+ }
+}
+
+static inline void _unlock(atomic_t *excl)
+{
+ atomic_dec(excl);
+}
+
+/* add a request to the tail of a list */
+void req_put(struct adb_dev *dev, struct list_head *head,
+ struct usb_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_add_tail(&req->list, head);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/* remove a request from the head of a list */
+struct usb_request *req_get(struct adb_dev *dev, struct list_head *head)
+{
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (list_empty(head)) {
+ req = 0;
+ } else {
+ req = list_first_entry(head, struct usb_request, list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return req;
+}
+
+static void adb_complete_in(struct usb_ep *ep, struct usb_request *req)
+{
+ struct adb_dev *dev = _adb_dev;
+
+ if (req->status != 0)
+ dev->error = 1;
+
+ req_put(dev, &dev->tx_idle, req);
+
+ wake_up(&dev->write_wq);
+}
+
+static void adb_complete_out(struct usb_ep *ep, struct usb_request *req)
+{
+ struct adb_dev *dev = _adb_dev;
+
+ dev->rx_done = 1;
+ if (req->status != 0)
+ dev->error = 1;
+
+ wake_up(&dev->read_wq);
+}
+
+static int __init create_bulk_endpoints(struct adb_dev *dev,
+ struct usb_endpoint_descriptor *in_desc,
+ struct usb_endpoint_descriptor *out_desc)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req;
+ struct usb_ep *ep;
+ int i;
+
+ DBG(cdev, "create_bulk_endpoints dev: %p\n", dev);
+
+ ep = usb_ep_autoconfig(cdev->gadget, in_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_in = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, out_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for adb ep_out got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_out = ep;
+
+ /* now allocate requests for our endpoints */
+ req = adb_request_new(dev->ep_out, BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = adb_complete_out;
+ dev->rx_req = req;
+
+ for (i = 0; i < TX_REQ_MAX; i++) {
+ req = adb_request_new(dev->ep_in, BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = adb_complete_in;
+ req_put(dev, &dev->tx_idle, req);
+ }
+
+ return 0;
+
+fail:
+ printk(KERN_ERR "adb_bind() could not allocate requests\n");
+ return -1;
+}
+
+static ssize_t adb_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct adb_dev *dev = fp->private_data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req;
+ int r = count, xfer;
+ int ret;
+
+ DBG(cdev, "adb_read(%d)\n", count);
+
+ if (count > BULK_BUFFER_SIZE)
+ return -EINVAL;
+
+ if (_lock(&dev->read_excl))
+ return -EBUSY;
+
+ /* we will block until we're online */
+ while (!(dev->online || dev->error)) {
+ DBG(cdev, "adb_read: waiting for online state\n");
+ ret = wait_event_interruptible(dev->read_wq,
+ (dev->online || dev->error));
+ if (ret < 0) {
+ _unlock(&dev->read_excl);
+ return ret;
+ }
+ }
+ if (dev->error) {
+ r = -EIO;
+ goto done;
+ }
+
+requeue_req:
+ /* queue a request */
+ req = dev->rx_req;
+ req->length = count;
+ dev->rx_done = 0;
+ ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC);
+ if (ret < 0) {
+ DBG(cdev, "adb_read: failed to queue req %p (%d)\n", req, ret);
+ r = -EIO;
+ dev->error = 1;
+ goto done;
+ } else {
+ DBG(cdev, "rx %p queue\n", req);
+ }
+
+ /* wait for a request to complete */
+ ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
+ if (ret < 0) {
+ dev->error = 1;
+ r = ret;
+ goto done;
+ }
+ if (!dev->error) {
+ /* If we got a 0-len packet, throw it back and try again. */
+ if (req->actual == 0)
+ goto requeue_req;
+
+ DBG(cdev, "rx %p %d\n", req, req->actual);
+ xfer = (req->actual < count) ? req->actual : count;
+ if (copy_to_user(buf, req->buf, xfer))
+ r = -EFAULT;
+ } else
+ r = -EIO;
+
+done:
+ _unlock(&dev->read_excl);
+ DBG(cdev, "adb_read returning %d\n", r);
+ return r;
+}
+
+static ssize_t adb_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct adb_dev *dev = fp->private_data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req = 0;
+ int r = count, xfer;
+ int ret;
+
+ DBG(cdev, "adb_write(%d)\n", count);
+
+ if (_lock(&dev->write_excl))
+ return -EBUSY;
+
+ while (count > 0) {
+ if (dev->error) {
+ DBG(cdev, "adb_write dev->error\n");
+ r = -EIO;
+ break;
+ }
+
+ /* get an idle tx request to use */
+ req = 0;
+ ret = wait_event_interruptible(dev->write_wq,
+ ((req = req_get(dev, &dev->tx_idle)) || dev->error));
+
+ if (ret < 0) {
+ r = ret;
+ break;
+ }
+
+ if (req != 0) {
+ if (count > BULK_BUFFER_SIZE)
+ xfer = BULK_BUFFER_SIZE;
+ else
+ xfer = count;
+ if (copy_from_user(req->buf, buf, xfer)) {
+ r = -EFAULT;
+ break;
+ }
+
+ req->length = xfer;
+ ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC);
+ if (ret < 0) {
+ DBG(cdev, "adb_write: xfer error %d\n", ret);
+ dev->error = 1;
+ r = -EIO;
+ break;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ /* zero this so we don't try to free it on error exit */
+ req = 0;
+ }
+ }
+
+ if (req)
+ req_put(dev, &dev->tx_idle, req);
+
+ _unlock(&dev->write_excl);
+ DBG(cdev, "adb_write returning %d\n", r);
+ return r;
+}
+
+static int adb_open(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "adb_open\n");
+ if (_lock(&_adb_dev->open_excl))
+ return -EBUSY;
+
+ fp->private_data = _adb_dev;
+
+ /* clear the error latch */
+ _adb_dev->error = 0;
+
+ return 0;
+}
+
+static int adb_release(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "adb_release\n");
+ _unlock(&_adb_dev->open_excl);
+ return 0;
+}
+
+/* file operations for ADB device /dev/android_adb */
+static struct file_operations adb_fops = {
+ .owner = THIS_MODULE,
+ .read = adb_read,
+ .write = adb_write,
+ .open = adb_open,
+ .release = adb_release,
+};
+
+static struct miscdevice adb_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = shortname,
+ .fops = &adb_fops,
+};
+
+static int adb_enable_open(struct inode *ip, struct file *fp)
+{
+ if (atomic_inc_return(&adb_enable_excl) != 1) {
+ atomic_dec(&adb_enable_excl);
+ return -EBUSY;
+ }
+
+ printk(KERN_INFO "enabling adb\n");
+ android_enable_function(&_adb_dev->function, 1);
+
+ return 0;
+}
+
+static int adb_enable_release(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "disabling adb\n");
+ android_enable_function(&_adb_dev->function, 0);
+ atomic_dec(&adb_enable_excl);
+ return 0;
+}
+
+static const struct file_operations adb_enable_fops = {
+ .owner = THIS_MODULE,
+ .open = adb_enable_open,
+ .release = adb_enable_release,
+};
+
+static struct miscdevice adb_enable_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "android_adb_enable",
+ .fops = &adb_enable_fops,
+};
+
+static int
+adb_function_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct adb_dev *dev = func_to_dev(f);
+ int id;
+ int ret;
+
+ dev->cdev = cdev;
+ DBG(cdev, "adb_function_bind dev: %p\n", dev);
+
+ /* allocate interface ID(s) */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ adb_interface_desc.bInterfaceNumber = id;
+
+ /* allocate endpoints */
+ ret = create_bulk_endpoints(dev, &adb_fullspeed_in_desc,
+ &adb_fullspeed_out_desc);
+ if (ret)
+ return ret;
+
+ /* support high speed hardware */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ adb_highspeed_in_desc.bEndpointAddress =
+ adb_fullspeed_in_desc.bEndpointAddress;
+ adb_highspeed_out_desc.bEndpointAddress =
+ adb_fullspeed_out_desc.bEndpointAddress;
+ }
+
+ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ f->name, dev->ep_in->name, dev->ep_out->name);
+ return 0;
+}
+
+static void
+adb_function_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct adb_dev *dev = func_to_dev(f);
+ struct usb_request *req;
+
+ spin_lock_irq(&dev->lock);
+
+ adb_request_free(dev->rx_req, dev->ep_out);
+ while ((req = req_get(dev, &dev->tx_idle)))
+ adb_request_free(req, dev->ep_in);
+
+ dev->online = 0;
+ dev->error = 1;
+ spin_unlock_irq(&dev->lock);
+
+ misc_deregister(&adb_device);
+ misc_deregister(&adb_enable_device);
+ kfree(_adb_dev);
+ _adb_dev = NULL;
+}
+
+static int adb_function_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct adb_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int ret;
+
+ DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt);
+ ret = usb_ep_enable(dev->ep_in,
+ ep_choose(cdev->gadget,
+ &adb_highspeed_in_desc,
+ &adb_fullspeed_in_desc));
+ if (ret)
+ return ret;
+ ret = usb_ep_enable(dev->ep_out,
+ ep_choose(cdev->gadget,
+ &adb_highspeed_out_desc,
+ &adb_fullspeed_out_desc));
+ if (ret) {
+ usb_ep_disable(dev->ep_in);
+ return ret;
+ }
+ dev->online = 1;
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+ return 0;
+}
+
+static void adb_function_disable(struct usb_function *f)
+{
+ struct adb_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = dev->cdev;
+
+ DBG(cdev, "adb_function_disable\n");
+ dev->online = 0;
+ dev->error = 1;
+ usb_ep_disable(dev->ep_in);
+ usb_ep_disable(dev->ep_out);
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+
+ VDBG(cdev, "%s disabled\n", dev->function.name);
+}
+
+static int adb_bind_config(struct usb_configuration *c)
+{
+ struct adb_dev *dev;
+ int ret;
+
+ printk(KERN_INFO "adb_bind_config\n");
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->lock);
+
+ init_waitqueue_head(&dev->read_wq);
+ init_waitqueue_head(&dev->write_wq);
+
+ atomic_set(&dev->open_excl, 0);
+ atomic_set(&dev->read_excl, 0);
+ atomic_set(&dev->write_excl, 0);
+
+ INIT_LIST_HEAD(&dev->tx_idle);
+
+ dev->cdev = c->cdev;
+ dev->function.name = "adb";
+ dev->function.descriptors = fs_adb_descs;
+ dev->function.hs_descriptors = hs_adb_descs;
+ dev->function.bind = adb_function_bind;
+ dev->function.unbind = adb_function_unbind;
+ dev->function.set_alt = adb_function_set_alt;
+ dev->function.disable = adb_function_disable;
+
+ /* start disabled */
+ dev->function.disabled = 1;
+
+ /* _adb_dev must be set before calling usb_gadget_register_driver */
+ _adb_dev = dev;
+
+ ret = misc_register(&adb_device);
+ if (ret)
+ goto err1;
+ ret = misc_register(&adb_enable_device);
+ if (ret)
+ goto err2;
+
+ ret = usb_add_function(c, &dev->function);
+ if (ret)
+ goto err3;
+
+ return 0;
+
+err3:
+ misc_deregister(&adb_enable_device);
+err2:
+ misc_deregister(&adb_device);
+err1:
+ kfree(dev);
+ printk(KERN_ERR "adb gadget driver failed to initialize\n");
+ return ret;
+}
+
+static int adb_rebind_config(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ int id;
+
+ /* re-allocate interface ID(s) */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ adb_interface_desc.bInterfaceNumber = id;
+
+ return 0;
+}
+
+static struct android_usb_function adb_function = {
+ .name = "adb",
+ .bind_config = adb_bind_config,
+ .rebind_config = adb_rebind_config,
+};
+
+static int __init init(void)
+{
+ printk(KERN_INFO "f_adb init\n");
+ android_register_function(&adb_function);
+ return 0;
+}
+module_init(init);
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 544257a89ed..0f038a87a91 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -26,6 +26,11 @@
#include <linux/device.h>
#include <linux/etherdevice.h>
+#ifdef CONFIG_USB_ANDROID_ECM
+#include <linux/usb/android_composite.h>
+#include <linux/platform_device.h>
+#endif
+
#include "u_ether.h"
@@ -113,6 +118,19 @@ static inline unsigned ecm_bitrate(struct usb_gadget *g)
/* interface descriptor: */
+static struct usb_interface_assoc_descriptor
+ecm_iad_descriptor = {
+ .bLength = sizeof ecm_iad_descriptor,
+ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+ /* .bFirstInterface = DYNAMIC, */
+ .bInterfaceCount = 2, /* control + data */
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
+ .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ /* .iFunction = DYNAMIC */
+};
+
static struct usb_interface_descriptor ecm_control_intf = {
.bLength = sizeof ecm_control_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -215,6 +233,7 @@ static struct usb_endpoint_descriptor fs_ecm_out_desc = {
static struct usb_descriptor_header *ecm_fs_function[] = {
/* CDC ECM control descriptors */
+ (struct usb_descriptor_header *) &ecm_iad_descriptor,
(struct usb_descriptor_header *) &ecm_control_intf,
(struct usb_descriptor_header *) &ecm_header_desc,
(struct usb_descriptor_header *) &ecm_union_desc,
@@ -260,6 +279,7 @@ static struct usb_endpoint_descriptor hs_ecm_out_desc = {
static struct usb_descriptor_header *ecm_hs_function[] = {
/* CDC ECM control descriptors */
+ (struct usb_descriptor_header *) &ecm_iad_descriptor,
(struct usb_descriptor_header *) &ecm_control_intf,
(struct usb_descriptor_header *) &ecm_header_desc,
(struct usb_descriptor_header *) &ecm_union_desc,
@@ -280,6 +300,7 @@ static struct usb_string ecm_string_defs[] = {
[0].s = "CDC Ethernet Control Model (ECM)",
[1].s = NULL /* DYNAMIC */,
[2].s = "CDC Ethernet Data",
+ [3].s = "CDC ECM",
{ } /* end of list */
};
@@ -293,6 +314,10 @@ static struct usb_gadget_strings *ecm_strings[] = {
NULL,
};
+#ifdef CONFIG_USB_ANDROID_ECM
+static struct usb_ether_platform_data *ecm_pdata;
+#endif
+
/*-------------------------------------------------------------------------*/
static void ecm_do_notify(struct f_ecm *ecm)
@@ -466,10 +491,10 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
usb_ep_disable(ecm->notify);
} else {
VDBG(cdev, "init ecm ctrl %d\n", intf);
- ecm->notify_desc = ep_choose(cdev->gadget,
- ecm->hs.notify,
- ecm->fs.notify);
}
+ ecm->notify_desc = ep_choose(cdev->gadget,
+ ecm->hs.notify,
+ ecm->fs.notify);
usb_ep_enable(ecm->notify, ecm->notify_desc);
ecm->notify->driver_data = ecm;
@@ -485,11 +510,11 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (!ecm->port.in) {
DBG(cdev, "init ecm\n");
- ecm->port.in = ep_choose(cdev->gadget,
- ecm->hs.in, ecm->fs.in);
- ecm->port.out = ep_choose(cdev->gadget,
- ecm->hs.out, ecm->fs.out);
}
+ ecm->port.in = ep_choose(cdev->gadget,
+ ecm->hs.in, ecm->fs.in);
+ ecm->port.out = ep_choose(cdev->gadget,
+ ecm->hs.out, ecm->fs.out);
/* CDC Ethernet only sends data in non-default altsettings.
* Changing altsettings resets filters, statistics, etc.
@@ -610,6 +635,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0)
goto fail;
ecm->ctrl_id = status;
+ ecm_iad_descriptor.bFirstInterface = status;
ecm_control_intf.bInterfaceNumber = status;
ecm_union_desc.bMasterInterface0 = status;
@@ -795,6 +821,13 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
return status;
ecm_string_defs[1].id = status;
ecm_desc.iMACAddress = status;
+
+ /* IAD label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ ecm_string_defs[3].id = status;
+ ecm_iad_descriptor.iFunction = status;
}
/* allocate and initialize one new instance */
@@ -828,3 +861,138 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
}
return status;
}
+
+#ifdef CONFIG_USB_ANDROID_ECM
+static int __init ecm_probe(struct platform_device *pdev)
+{
+ printk(KERN_INFO "ecm_probe\n");
+ ecm_pdata = pdev->dev.platform_data;
+ return 0;
+}
+
+static struct platform_driver ecm_platform_driver = {
+ .driver = { .name = "cdc_ethernet", },
+};
+
+int ecm_function_bind_config(struct usb_configuration *c)
+{
+ int ret;
+
+ if (!ecm_pdata) {
+ printk(KERN_ERR "ecm_pdata null in ecm_function_bind_config\n");
+ return -1;
+ }
+
+ printk(KERN_INFO
+ "ecm_function_bind_config MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ ecm_pdata->ethaddr[0], ecm_pdata->ethaddr[1],
+ ecm_pdata->ethaddr[2], ecm_pdata->ethaddr[3],
+ ecm_pdata->ethaddr[4], ecm_pdata->ethaddr[5]);
+
+ ret = gether_setup(c->cdev->gadget, ecm_pdata->ethaddr);
+ if (ret == 0)
+ ret = ecm_bind_config(c, ecm_pdata->ethaddr);
+ return ret;
+}
+
+static int ecm_function_rebind_config(struct usb_configuration *c,
+ struct usb_function *f)
+{
+
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_ecm *ecm = func_to_ecm(f);
+ int status1, status2 = 0;
+ struct usb_descriptor_header **desc = f->descriptors;
+
+ /* re-allocate instance-specific interface IDs */
+ status1 = usb_interface_id(c, f);
+ if (status1 < 0)
+ goto fail;
+ ecm->ctrl_id = status1;
+
+ ecm_iad_descriptor.bFirstInterface = status1;
+ ecm_control_intf.bInterfaceNumber = status1;
+ ecm_union_desc.bMasterInterface0 = status1;
+
+ ((struct usb_interface_assoc_descriptor *)desc[0])
+ ->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)desc[1])
+ ->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)desc[3])
+ ->bMasterInterface0 = status1;
+
+ status2 = usb_interface_id(c, f);
+ if (status2 < 0)
+ goto fail;
+ ecm->data_id = status2;
+
+ ecm_data_nop_intf.bInterfaceNumber = status2;
+ ecm_data_intf.bInterfaceNumber = status2;
+ ecm_union_desc.bSlaveInterface0 = status2;
+
+ ((struct usb_interface_descriptor *)desc[6])
+ ->bInterfaceNumber = status2;
+ ((struct usb_interface_descriptor *)desc[7])
+ ->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)desc[3])
+ ->bSlaveInterface0 = status2;
+
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ desc = f->hs_descriptors;
+ ((struct usb_interface_assoc_descriptor *)desc[0])
+ ->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)desc[1])
+ ->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)desc[3])
+ ->bMasterInterface0 = status1;
+
+ ((struct usb_interface_descriptor *)desc[6])
+ ->bInterfaceNumber = status2;
+ ((struct usb_interface_descriptor *)desc[7])
+ ->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)desc[3])
+ ->bSlaveInterface0 = status2;
+ }
+
+ return 0;
+
+fail:
+ if (f->descriptors)
+ usb_free_descriptors(f->descriptors);
+
+ if (ecm->notify_req) {
+ kfree(ecm->notify_req->buf);
+ usb_ep_free_request(ecm->notify, ecm->notify_req);
+ }
+
+ /* we might as well release our claims on endpoints */
+ if (ecm->notify)
+ ecm->notify->driver_data = NULL;
+ if (ecm->port.out)
+ ecm->port.out_ep->driver_data = NULL;
+ if (ecm->port.in)
+ ecm->port.in_ep->driver_data = NULL;
+
+ ERROR(cdev, "%s: can't re-bind, err %d %d\n",
+ f->name, status1, status2);
+
+ if (status2 < 0)
+ status1 = status2;
+ return status1;
+}
+
+static struct android_usb_function ecm_function = {
+ .name = "cdc_ethernet",
+ .bind_config = ecm_function_bind_config,
+ .rebind_config = ecm_function_rebind_config,
+};
+
+static int __init init(void)
+{
+ printk(KERN_INFO "f_ecm init\n");
+ platform_driver_probe(&ecm_platform_driver, ecm_probe);
+ android_register_function(&ecm_function);
+ return 0;
+}
+module_init(init);
+#endif /* CONFIG_USB_ANDROID_ECM */
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 4ce899c9b16..1a08f15c4b8 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -295,7 +295,12 @@
#include "gadget_chips.h"
+#ifdef CONFIG_USB_ANDROID_MASS_STORAGE
+#include <linux/usb/android_composite.h>
+#include <linux/platform_device.h>
+#define FUNCTION_NAME "usb_mass_storage"
+#endif
/*------------------------------------------------------------------------*/
@@ -408,6 +413,10 @@ struct fsg_config {
u16 release;
char can_stall;
+
+#ifdef CONFIG_USB_ANDROID_MASS_STORAGE
+ struct platform_device *pdev;
+#endif
};
@@ -454,19 +463,6 @@ static int exception_in_progress(struct fsg_common *common)
return common->state > FSG_STATE_IDLE;
}
-/* Make bulk-out requests be divisible by the maxpacket size */
-static void set_bulk_out_req_length(struct fsg_common *common,
- struct fsg_buffhd *bh, unsigned int length)
-{
- unsigned int rem;
-
- bh->bulk_out_intended_length = length;
- rem = length % common->bulk_out_maxpacket;
- if (rem > 0)
- length += common->bulk_out_maxpacket - rem;
- bh->outreq->length = length;
-}
-
/*-------------------------------------------------------------------------*/
static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
@@ -564,10 +560,9 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
struct fsg_buffhd *bh = req->context;
dump_msg(common, "bulk-out", req->buf, req->actual);
- if (req->status || req->actual != bh->bulk_out_intended_length)
+ if (req->status || req->actual != req->length)
DBG(common, "%s --> %d, %u/%u\n", __func__,
- req->status, req->actual,
- bh->bulk_out_intended_length);
+ req->status, req->actual, req->length);
if (req->status == -ECONNRESET) /* Request was cancelled */
usb_ep_fifo_flush(ep);
@@ -609,6 +604,7 @@ static int fsg_setup(struct usb_function *f,
/* Raise an exception to stop the current operation
* and reinitialize our state. */
DBG(fsg, "bulk reset request\n");
+ fsg->common->ep0req->length = 0;
raise_exception(fsg->common, FSG_STATE_RESET);
return DELAYED_STATUS;
@@ -939,7 +935,6 @@ static int do_write(struct fsg_common *common)
/* amount is always divisible by 512, hence by
* the bulk-out maxpacket size */
bh->outreq->length = amount;
- bh->bulk_out_intended_length = amount;
bh->outreq->short_not_ok = 1;
START_TRANSFER_OR(common, bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state)
@@ -1343,30 +1338,7 @@ static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
limit = 65535; /* Should really be FSG_BUFLEN */
}
- /* No block descriptors */
-
- /* The mode pages, in numerical order. The only page we support
- * is the Caching page. */
- if (page_code == 0x08 || all_pages) {
- valid_page = 1;
- buf[0] = 0x08; /* Page code */
- buf[1] = 10; /* Page length */
- memset(buf+2, 0, 10); /* None of the fields are changeable */
-
- if (!changeable_values) {
- buf[2] = 0x04; /* Write cache enable, */
- /* Read cache not disabled */
- /* No cache retention priorities */
- put_unaligned_be16(0xffff, &buf[4]);
- /* Don't disable prefetch */
- /* Minimum prefetch = 0 */
- put_unaligned_be16(0xffff, &buf[8]);
- /* Maximum prefetch */
- put_unaligned_be16(0xffff, &buf[10]);
- /* Maximum prefetch ceiling */
- }
- buf += 12;
- }
+ valid_page = 1;
/* Check that a valid page was requested and the mode data length
* isn't too long. */
@@ -1602,7 +1574,6 @@ static int throw_away_data(struct fsg_common *common)
/* amount is always divisible by 512, hence by
* the bulk-out maxpacket size */
bh->outreq->length = amount;
- bh->bulk_out_intended_length = amount;
bh->outreq->short_not_ok = 1;
START_TRANSFER_OR(common, bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state)
@@ -2253,8 +2224,8 @@ static int get_next_command(struct fsg_common *common)
}
/* Queue a request to read a Bulk-only CBW */
- set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN);
- bh->outreq->short_not_ok = 1;
+ bh->outreq->length = USB_BULK_CB_WRAP_LEN;
+ bh->outreq->short_not_ok = 0;
START_TRANSFER_OR(common, bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state)
/* Don't know what to do if common->fsg is NULL */
@@ -2717,7 +2688,13 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
curlun->ro = lcfg->cdrom || lcfg->ro;
curlun->removable = lcfg->removable;
curlun->dev.release = fsg_lun_release;
+
+#ifdef CONFIG_USB_ANDROID_MASS_STORAGE
+ /* use "usb_mass_storage" platform device as parent */
+ curlun->dev.parent = &cfg->pdev->dev;
+#else
curlun->dev.parent = &gadget->dev;
+#endif
/* curlun->dev.driver = &fsg_driver.driver; XXX */
dev_set_drvdata(&curlun->dev, &common->filesem);
dev_set_name(&curlun->dev,
@@ -3001,7 +2978,11 @@ static int fsg_add(struct usb_composite_dev *cdev,
if (unlikely(!fsg))
return -ENOMEM;
+#ifdef CONFIG_USB_ANDROID_MASS_STORAGE
+ fsg->function.name = FUNCTION_NAME;
+#else
fsg->function.name = FSG_DRIVER_DESC;
+#endif
fsg->function.strings = fsg_strings_array;
fsg->function.bind = fsg_bind;
fsg->function.unbind = fsg_unbind;
@@ -3118,3 +3099,91 @@ fsg_common_from_params(struct fsg_common *common,
return fsg_common_init(common, cdev, &cfg);
}
+#ifdef CONFIG_USB_ANDROID_MASS_STORAGE
+
+static struct fsg_config fsg_cfg;
+
+static int fsg_probe(struct platform_device *pdev)
+{
+ struct usb_mass_storage_platform_data *pdata = pdev->dev.platform_data;
+ int i, nluns;
+
+ printk(KERN_INFO "fsg_probe pdev: %p, pdata: %p\n", pdev, pdata);
+ if (!pdata)
+ return -1;
+
+ nluns = pdata->nluns;
+ if (nluns > FSG_MAX_LUNS)
+ nluns = FSG_MAX_LUNS;
+ fsg_cfg.nluns = nluns;
+ for (i = 0; i < nluns; i++)
+ fsg_cfg.luns[i].removable = 1;
+
+ fsg_cfg.vendor_name = pdata->vendor;
+ fsg_cfg.product_name = pdata->product;
+ fsg_cfg.release = pdata->release;
+ fsg_cfg.can_stall = 0;
+ fsg_cfg.pdev = pdev;
+
+ return 0;
+}
+
+static struct platform_driver fsg_platform_driver = {
+ .driver = { .name = FUNCTION_NAME, },
+ .probe = fsg_probe,
+};
+
+int mass_storage_bind_config(struct usb_configuration *c)
+{
+ struct fsg_common *common = fsg_common_init(NULL, c->cdev, &fsg_cfg);
+ if (IS_ERR(common))
+ return -1;
+ return fsg_add(c->cdev, c, common);
+}
+
+static int mass_storage_rebind_config(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct usb_gadget *gadget = c->cdev->gadget;
+ int i;
+
+ fsg->gadget = gadget;
+
+ /* New interface */
+ i = usb_interface_id(c, f);
+ if (i < 0)
+ return i;
+ fsg_intf_desc.bInterfaceNumber = i;
+ fsg->interface_number = i;
+
+#ifndef FSG_NO_OTG
+ ((struct usb_interface_assoc_descriptor *)f->descriptors[1]
+ ->bFirstInterface = i;
+#else
+ ((struct usb_interface_assoc_descriptor *)f->descriptors[0])
+ ->bFirstInterface = i;
+#endif
+
+ return 0;
+}
+
+static struct android_usb_function mass_storage_function = {
+ .name = FUNCTION_NAME,
+ .bind_config = mass_storage_bind_config,
+ .rebind_config = mass_storage_rebind_config,
+};
+
+static int __init init(void)
+{
+ int rc;
+ printk(KERN_INFO "f_mass_storage init\n");
+ rc = platform_driver_register(&fsg_platform_driver);
+ if (rc != 0)
+ return rc;
+ android_register_function(&mass_storage_function);
+ return 0;
+}module_init(init);
+
+#endif /* CONFIG_USB_ANDROID_MASS_STORAGE */
+
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
new file mode 100644
index 00000000000..2c61a261347
--- /dev/null
+++ b/drivers/usb/gadget/f_mtp.c
@@ -0,0 +1,1273 @@
+/*
+ * Gadget Function Driver for MTP
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <linux/types.h>
+#include <linux/file.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+
+#include <linux/usb.h>
+#include <linux/usb_usual.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/android_composite.h>
+#include <linux/usb/f_mtp.h>
+
+#define BULK_BUFFER_SIZE 16384
+#define INTR_BUFFER_SIZE 28
+
+/* String IDs */
+#define INTERFACE_STRING_INDEX 0
+
+/* values for mtp_dev.state */
+#define STATE_OFFLINE 0 /* initial state, disconnected */
+#define STATE_READY 1 /* ready for userspace calls */
+#define STATE_BUSY 2 /* processing userspace calls */
+#define STATE_CANCELED 3 /* transaction canceled by host */
+#define STATE_ERROR 4 /* error from completion routine */
+
+/* number of tx and rx requests to allocate */
+#define TX_REQ_MAX 4
+#define RX_REQ_MAX 2
+
+/* IO Thread commands */
+#define ANDROID_THREAD_QUIT 1
+#define ANDROID_THREAD_SEND_FILE 2
+#define ANDROID_THREAD_RECEIVE_FILE 3
+
+/* ID for Microsoft MTP OS String */
+#define MTP_OS_STRING_ID 0xEE
+
+/* MTP class reqeusts */
+#define MTP_REQ_CANCEL 0x64
+#define MTP_REQ_GET_EXT_EVENT_DATA 0x65
+#define MTP_REQ_RESET 0x66
+#define MTP_REQ_GET_DEVICE_STATUS 0x67
+
+/* constants for device status */
+#define MTP_RESPONSE_OK 0x2001
+#define MTP_RESPONSE_DEVICE_BUSY 0x2019
+
+static const char shortname[] = "mtp_usb";
+
+struct mtp_dev {
+ struct usb_function function;
+ struct usb_composite_dev *cdev;
+ spinlock_t lock;
+
+ /* appear as MTP or PTP when enumerating */
+ int interface_mode;
+
+ struct usb_ep *ep_in;
+ struct usb_ep *ep_out;
+ struct usb_ep *ep_intr;
+
+ int state;
+
+ /* synchronize access to our device file */
+ atomic_t open_excl;
+
+ struct list_head tx_idle;
+
+ wait_queue_head_t read_wq;
+ wait_queue_head_t write_wq;
+ wait_queue_head_t intr_wq;
+ struct usb_request *rx_req[RX_REQ_MAX];
+ struct usb_request *intr_req;
+ int rx_done;
+
+ /* synchronize access to interrupt endpoint */
+ struct mutex intr_mutex;
+ /* true if interrupt endpoint is busy */
+ int intr_busy;
+
+ /* for our file IO thread */
+ struct task_struct *thread;
+ /* current command for IO thread (or zero for none) */
+ int thread_command;
+ struct file *thread_file;
+ loff_t thread_file_offset;
+ size_t thread_file_length;
+ /* used to wait for thread to complete current command */
+ struct completion thread_wait;
+ /* result from current command */
+ int thread_result;
+};
+
+static struct usb_interface_descriptor mtp_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 3,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC,
+ .bInterfaceProtocol = 0,
+};
+
+static struct usb_interface_descriptor ptp_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 3,
+ .bInterfaceClass = USB_CLASS_STILL_IMAGE,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 1,
+};
+
+static struct usb_endpoint_descriptor mtp_highspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor mtp_highspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor mtp_fullspeed_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor mtp_fullspeed_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor mtp_intr_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(INTR_BUFFER_SIZE),
+ .bInterval = 6,
+};
+
+static struct usb_descriptor_header *fs_mtp_descs[] = {
+ (struct usb_descriptor_header *) &mtp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *hs_mtp_descs[] = {
+ (struct usb_descriptor_header *) &mtp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *fs_ptp_descs[] = {
+ (struct usb_descriptor_header *) &ptp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_fullspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *hs_ptp_descs[] = {
+ (struct usb_descriptor_header *) &ptp_interface_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_in_desc,
+ (struct usb_descriptor_header *) &mtp_highspeed_out_desc,
+ (struct usb_descriptor_header *) &mtp_intr_desc,
+ NULL,
+};
+
+static struct usb_string mtp_string_defs[] = {
+ /* Naming interface "MTP" so libmtp will recognize us */
+ [INTERFACE_STRING_INDEX].s = "MTP",
+ { }, /* end of list */
+};
+
+static struct usb_gadget_strings mtp_string_table = {
+ .language = 0x0409, /* en-US */
+ .strings = mtp_string_defs,
+};
+
+static struct usb_gadget_strings *mtp_strings[] = {
+ &mtp_string_table,
+ NULL,
+};
+
+/* Microsoft MTP OS String */
+static u8 mtp_os_string[] = {
+ 18, /* sizeof(mtp_os_string) */
+ USB_DT_STRING,
+ /* Signature field: "MSFT100" */
+ 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0,
+ /* vendor code */
+ 1,
+ /* padding */
+ 0
+};
+
+/* Microsoft Extended Configuration Descriptor Header Section */
+struct mtp_ext_config_desc_header {
+ __le32 dwLength;
+ __u16 bcdVersion;
+ __le16 wIndex;
+ __u8 bCount;
+ __u8 reserved[7];
+};
+
+/* Microsoft Extended Configuration Descriptor Function Section */
+struct mtp_ext_config_desc_function {
+ __u8 bFirstInterfaceNumber;
+ __u8 bInterfaceCount;
+ __u8 compatibleID[8];
+ __u8 subCompatibleID[8];
+ __u8 reserved[6];
+};
+
+/* MTP Extended Configuration Descriptor */
+struct {
+ struct mtp_ext_config_desc_header header;
+ struct mtp_ext_config_desc_function function;
+} mtp_ext_config_desc = {
+ .header = {
+ .dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)),
+ .bcdVersion = __constant_cpu_to_le16(0x0100),
+ .wIndex = __constant_cpu_to_le16(4),
+ .bCount = __constant_cpu_to_le16(1),
+ },
+ .function = {
+ .bFirstInterfaceNumber = 0,
+ .bInterfaceCount = 1,
+ .compatibleID = { 'M', 'T', 'P' },
+ },
+};
+
+struct mtp_device_status {
+ __le16 wLength;
+ __le16 wCode;
+};
+
+/* temporary variable used between mtp_open() and mtp_gadget_bind() */
+static struct mtp_dev *_mtp_dev;
+
+static inline struct mtp_dev *func_to_dev(struct usb_function *f)
+{
+ return container_of(f, struct mtp_dev, function);
+}
+
+static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size)
+{
+ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req)
+ return NULL;
+
+ /* now allocate buffers for the requests */
+ req->buf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static void mtp_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+ if (req) {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static inline int _lock(atomic_t *excl)
+{
+ if (atomic_inc_return(excl) == 1) {
+ return 0;
+ } else {
+ atomic_dec(excl);
+ return -1;
+ }
+}
+
+static inline void _unlock(atomic_t *excl)
+{
+ atomic_dec(excl);
+}
+
+/* add a request to the tail of a list */
+static void req_put(struct mtp_dev *dev, struct list_head *head,
+ struct usb_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_add_tail(&req->list, head);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/* remove a request from the head of a list */
+static struct usb_request *req_get(struct mtp_dev *dev, struct list_head *head)
+{
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (list_empty(head)) {
+ req = 0;
+ } else {
+ req = list_first_entry(head, struct usb_request, list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return req;
+}
+
+static void mtp_complete_in(struct usb_ep *ep, struct usb_request *req)
+{
+ struct mtp_dev *dev = _mtp_dev;
+
+ if (req->status != 0)
+ dev->state = STATE_ERROR;
+
+ req_put(dev, &dev->tx_idle, req);
+
+ wake_up(&dev->write_wq);
+}
+
+static void mtp_complete_out(struct usb_ep *ep, struct usb_request *req)
+{
+ struct mtp_dev *dev = _mtp_dev;
+
+ dev->rx_done = 1;
+ if (req->status != 0)
+ dev->state = STATE_ERROR;
+
+ wake_up(&dev->read_wq);
+}
+
+static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req)
+{
+ struct mtp_dev *dev = _mtp_dev;
+
+ DBG(dev->cdev, "mtp_complete_intr status: %d actual: %d\n", req->status, req->actual);
+ dev->intr_busy = 0;
+ if (req->status != 0)
+ dev->state = STATE_ERROR;
+
+ wake_up(&dev->intr_wq);
+}
+
+static int __init create_bulk_endpoints(struct mtp_dev *dev,
+ struct usb_endpoint_descriptor *in_desc,
+ struct usb_endpoint_descriptor *out_desc,
+ struct usb_endpoint_descriptor *intr_desc)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req;
+ struct usb_ep *ep;
+ int i;
+
+ DBG(cdev, "create_bulk_endpoints dev: %p\n", dev);
+
+ ep = usb_ep_autoconfig(cdev->gadget, in_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_in = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, out_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_out = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, out_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_out = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, intr_desc);
+ if (!ep) {
+ DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n");
+ return -ENODEV;
+ }
+ DBG(cdev, "usb_ep_autoconfig for mtp ep_intr got %s\n", ep->name);
+ ep->driver_data = dev; /* claim the endpoint */
+ dev->ep_intr = ep;
+
+ /* now allocate requests for our endpoints */
+ for (i = 0; i < TX_REQ_MAX; i++) {
+ req = mtp_request_new(dev->ep_in, BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = mtp_complete_in;
+ req_put(dev, &dev->tx_idle, req);
+ }
+ for (i = 0; i < RX_REQ_MAX; i++) {
+ req = mtp_request_new(dev->ep_out, BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = mtp_complete_out;
+ dev->rx_req[i] = req;
+ }
+ req = mtp_request_new(dev->ep_intr, INTR_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = mtp_complete_intr;
+ dev->intr_req = req;
+
+ return 0;
+
+fail:
+ printk(KERN_ERR "mtp_bind() could not allocate requests\n");
+ return -1;
+}
+
+static ssize_t mtp_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mtp_dev *dev = fp->private_data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req;
+ int r = count, xfer;
+ int ret = 0;
+
+ DBG(cdev, "mtp_read(%d)\n", count);
+
+ if (count > BULK_BUFFER_SIZE)
+ return -EINVAL;
+
+ /* we will block until we're online */
+ DBG(cdev, "mtp_read: waiting for online state\n");
+ ret = wait_event_interruptible(dev->read_wq,
+ dev->state != STATE_OFFLINE);
+ if (ret < 0) {
+ r = ret;
+ goto done;
+ }
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED) {
+ /* report cancelation to userspace */
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+ return -ECANCELED;
+ }
+ dev->state = STATE_BUSY;
+ spin_unlock_irq(&dev->lock);
+
+requeue_req:
+ /* queue a request */
+ req = dev->rx_req[0];
+ req->length = count;
+ dev->rx_done = 0;
+ ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
+ if (ret < 0) {
+ r = -EIO;
+ goto done;
+ } else {
+ DBG(cdev, "rx %p queue\n", req);
+ }
+
+ /* wait for a request to complete */
+ ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
+ if (ret < 0) {
+ r = ret;
+ goto done;
+ }
+ if (dev->state == STATE_BUSY) {
+ /* If we got a 0-len packet, throw it back and try again. */
+ if (req->actual == 0)
+ goto requeue_req;
+
+ DBG(cdev, "rx %p %d\n", req, req->actual);
+ xfer = (req->actual < count) ? req->actual : count;
+ r = xfer;
+ if (copy_to_user(buf, req->buf, xfer))
+ r = -EFAULT;
+ } else
+ r = -EIO;
+
+done:
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED)
+ r = -ECANCELED;
+ else if (dev->state != STATE_OFFLINE)
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+
+ DBG(cdev, "mtp_read returning %d\n", r);
+ return r;
+}
+
+static ssize_t mtp_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mtp_dev *dev = fp->private_data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req = 0;
+ int r = count, xfer;
+ int ret;
+
+ DBG(cdev, "mtp_write(%d)\n", count);
+
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED) {
+ /* report cancelation to userspace */
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+ return -ECANCELED;
+ }
+ if (dev->state == STATE_OFFLINE) {
+ spin_unlock_irq(&dev->lock);
+ return -ENODEV;
+ }
+ dev->state = STATE_BUSY;
+ spin_unlock_irq(&dev->lock);
+
+ while (count > 0) {
+ if (dev->state != STATE_BUSY) {
+ DBG(cdev, "mtp_write dev->error\n");
+ r = -EIO;
+ break;
+ }
+
+ /* get an idle tx request to use */
+ req = 0;
+ ret = wait_event_interruptible(dev->write_wq,
+ ((req = req_get(dev, &dev->tx_idle))
+ || dev->state != STATE_BUSY));
+ if (!req) {
+ r = ret;
+ break;
+ }
+
+ if (count > BULK_BUFFER_SIZE)
+ xfer = BULK_BUFFER_SIZE;
+ else
+ xfer = count;
+ if (copy_from_user(req->buf, buf, xfer)) {
+ r = -EFAULT;
+ break;
+ }
+
+ req->length = xfer;
+ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);
+ if (ret < 0) {
+ DBG(cdev, "mtp_write: xfer error %d\n", ret);
+ r = -EIO;
+ break;
+ }
+
+ buf += xfer;
+ count -= xfer;
+
+ /* zero this so we don't try to free it on error exit */
+ req = 0;
+ }
+
+ if (req)
+ req_put(dev, &dev->tx_idle, req);
+
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED)
+ r = -ECANCELED;
+ else if (dev->state != STATE_OFFLINE)
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+
+ DBG(cdev, "mtp_write returning %d\n", r);
+ return r;
+}
+
+static int mtp_send_file(struct mtp_dev *dev, struct file *filp,
+ loff_t offset, size_t count)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *req = 0;
+ int r = count, xfer, ret;
+
+ DBG(cdev, "mtp_send_file(%lld %d)\n", offset, count);
+
+ while (count > 0) {
+ /* get an idle tx request to use */
+ req = 0;
+ ret = wait_event_interruptible(dev->write_wq,
+ (req = req_get(dev, &dev->tx_idle))
+ || dev->state != STATE_BUSY);
+ if (!req) {
+ r = ret;
+ break;
+ }
+
+ if (count > BULK_BUFFER_SIZE)
+ xfer = BULK_BUFFER_SIZE;
+ else
+ xfer = count;
+ ret = vfs_read(filp, req->buf, xfer, &offset);
+ if (ret < 0) {
+ r = ret;
+ break;
+ }
+ xfer = ret;
+
+ req->length = xfer;
+ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);
+ if (ret < 0) {
+ DBG(cdev, "mtp_write: xfer error %d\n", ret);
+ dev->state = STATE_ERROR;
+ r = -EIO;
+ break;
+ }
+
+ count -= xfer;
+
+ /* zero this so we don't try to free it on error exit */
+ req = 0;
+ }
+
+ if (req)
+ req_put(dev, &dev->tx_idle, req);
+
+ DBG(cdev, "mtp_write returning %d\n", r);
+ return r;
+}
+
+static int mtp_receive_file(struct mtp_dev *dev, struct file *filp,
+ loff_t offset, size_t count)
+{
+ struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_request *read_req = NULL, *write_req = NULL;
+ int r = count;
+ int ret;
+ int cur_buf = 0;
+
+ DBG(cdev, "mtp_receive_file(%d)\n", count);
+
+ while (count > 0 || write_req) {
+ if (count > 0) {
+ /* queue a request */
+ read_req = dev->rx_req[cur_buf];
+ cur_buf = (cur_buf + 1) % RX_REQ_MAX;
+
+ read_req->length = (count > BULK_BUFFER_SIZE
+ ? BULK_BUFFER_SIZE : count);
+ dev->rx_done = 0;
+ ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
+ if (ret < 0) {
+ r = -EIO;
+ dev->state = STATE_ERROR;
+ break;
+ }
+ count -= ret;
+ }
+
+ if (write_req) {
+ DBG(cdev, "rx %p %d\n", write_req, write_req->actual);
+ ret = vfs_write(filp, write_req->buf, write_req->actual,
+ &offset);
+ DBG(cdev, "vfs_write %d\n", ret);
+ if (ret != write_req->actual) {
+ r = -EIO;
+ dev->state = STATE_ERROR;
+ break;
+ }
+ write_req = NULL;
+ }
+
+ if (read_req) {
+ /* wait for our last read to complete */
+ ret = wait_event_interruptible(dev->read_wq,
+ dev->rx_done || dev->state != STATE_BUSY);
+ if (ret < 0 || dev->state != STATE_BUSY) {
+ r = ret;
+ break;
+ }
+ count -= read_req->actual;
+ write_req = read_req;
+ read_req = NULL;
+ }
+ }
+
+ DBG(cdev, "mtp_read returning %d\n", r);
+ return r;
+}
+
+/* Kernel thread for handling file IO operations */
+static int mtp_thread(void *data)
+{
+ struct mtp_dev *dev = (struct mtp_dev *)data;
+ struct usb_composite_dev *cdev = dev->cdev;
+ int flags;
+
+ DBG(cdev, "mtp_thread started\n");
+
+ while (1) {
+ /* wait for a command */
+ while (1) {
+ try_to_freeze();
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (dev->thread_command != 0)
+ break;
+ schedule();
+ }
+ __set_current_state(TASK_RUNNING);
+
+ if (dev->thread_command == ANDROID_THREAD_QUIT) {
+ DBG(cdev, "ANDROID_THREAD_QUIT\n");
+ dev->thread_result = 0;
+ goto done;
+ }
+
+ if (dev->thread_command == ANDROID_THREAD_SEND_FILE)
+ flags = O_RDONLY | O_LARGEFILE;
+ else
+ flags = O_WRONLY | O_LARGEFILE | O_CREAT;
+
+ if (dev->thread_command == ANDROID_THREAD_SEND_FILE) {
+ dev->thread_result = mtp_send_file(dev,
+ dev->thread_file,
+ dev->thread_file_offset,
+ dev->thread_file_length);
+ } else {
+ dev->thread_result = mtp_receive_file(dev,
+ dev->thread_file,
+ dev->thread_file_offset,
+ dev->thread_file_length);
+ }
+
+ if (dev->thread_file) {
+ fput(dev->thread_file);
+ dev->thread_file = NULL;
+ }
+ dev->thread_command = 0;
+ complete(&dev->thread_wait);
+ }
+
+done:
+ DBG(cdev, "android_thread done\n");
+ complete_and_exit(&dev->thread_wait, 0);
+}
+
+static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event)
+{
+ struct usb_request *req;
+ int ret;
+ int length = event->length;
+
+ DBG(dev->cdev, "mtp_send_event(%d)\n", event->length);
+
+ if (length < 0 || length > INTR_BUFFER_SIZE)
+ return -EINVAL;
+
+ mutex_lock(&dev->intr_mutex);
+
+ /* wait for a request to complete */
+ ret = wait_event_interruptible(dev->intr_wq, !dev->intr_busy || dev->state == STATE_OFFLINE);
+ if (ret < 0)
+ goto done;
+ if (dev->state == STATE_OFFLINE) {
+ ret = -ENODEV;
+ goto done;
+ }
+ req = dev->intr_req;
+ if (copy_from_user(req->buf, (void __user *)event->data, length)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ req->length = length;
+ dev->intr_busy = 1;
+ ret = usb_ep_queue(dev->ep_intr, req, GFP_KERNEL);
+ if (ret)
+ dev->intr_busy = 0;
+
+done:
+ mutex_unlock(&dev->intr_mutex);
+ return ret;
+}
+
+static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
+{
+ struct mtp_dev *dev = fp->private_data;
+ struct file *filp = NULL;
+ int ret = -EINVAL;
+
+ switch (code) {
+ case MTP_SEND_FILE:
+ case MTP_RECEIVE_FILE:
+ {
+ struct mtp_file_range mfr;
+
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED) {
+ /* report cancelation to userspace */
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+ return -ECANCELED;
+ }
+ if (dev->state == STATE_OFFLINE) {
+ spin_unlock_irq(&dev->lock);
+ return -ENODEV;
+ }
+ dev->state = STATE_BUSY;
+ spin_unlock_irq(&dev->lock);
+
+ if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ filp = fget(mfr.fd);
+ if (!filp) {
+ ret = -EBADF;
+ goto fail;
+ }
+
+ dev->thread_file = filp;
+ dev->thread_file_offset = mfr.offset;
+ dev->thread_file_length = mfr.length;
+
+ if (code == MTP_SEND_FILE)
+ dev->thread_command = ANDROID_THREAD_SEND_FILE;
+ else
+ dev->thread_command = ANDROID_THREAD_RECEIVE_FILE;
+
+ /* wake up the thread */
+ init_completion(&dev->thread_wait);
+ wake_up_process(dev->thread);
+
+ /* wait for the thread to complete the command */
+ wait_for_completion(&dev->thread_wait);
+ ret = dev->thread_result;
+ DBG(dev->cdev, "thread returned %d\n", ret);
+ break;
+ }
+ case MTP_SET_INTERFACE_MODE:
+ if (value == MTP_INTERFACE_MODE_MTP ||
+ value == MTP_INTERFACE_MODE_PTP) {
+ dev->interface_mode = value;
+ if (value == MTP_INTERFACE_MODE_PTP) {
+ dev->function.descriptors = fs_ptp_descs;
+ dev->function.hs_descriptors = hs_ptp_descs;
+ } else {
+ dev->function.descriptors = fs_mtp_descs;
+ dev->function.hs_descriptors = hs_mtp_descs;
+ }
+ ret = 0;
+ }
+ break;
+ case MTP_SEND_EVENT:
+ {
+ struct mtp_event event;
+ /* return here so we don't change dev->state below,
+ * which would interfere with bulk transfer state.
+ */
+ if (copy_from_user(&event, (void __user *)value, sizeof(event)))
+ return -EFAULT;
+ else
+ return mtp_send_event(dev, &event);
+ }
+ }
+
+fail:
+ if (filp)
+ fput(filp);
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_CANCELED)
+ ret = -ECANCELED;
+ else if (dev->state != STATE_OFFLINE)
+ dev->state = STATE_READY;
+ spin_unlock_irq(&dev->lock);
+ DBG(dev->cdev, "ioctl returning %d\n", ret);
+ return ret;
+}
+
+static int mtp_open(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "mtp_open\n");
+ if (_lock(&_mtp_dev->open_excl))
+ return -EBUSY;
+
+ _mtp_dev->thread = kthread_create(mtp_thread, _mtp_dev, "f_mtp");
+ if (IS_ERR(_mtp_dev->thread))
+ return -ENOMEM;
+
+ /* clear any error condition */
+ if (_mtp_dev->state != STATE_OFFLINE)
+ _mtp_dev->state = STATE_READY;
+
+ fp->private_data = _mtp_dev;
+ return 0;
+}
+
+static int mtp_release(struct inode *ip, struct file *fp)
+{
+ printk(KERN_INFO "mtp_release\n");
+
+ /* tell the thread to quit */
+ if (_mtp_dev->thread) {
+ _mtp_dev->thread_command = ANDROID_THREAD_QUIT;
+ init_completion(&_mtp_dev->thread_wait);
+ wake_up_process(_mtp_dev->thread);
+ wait_for_completion(&_mtp_dev->thread_wait);
+ }
+
+ _unlock(&_mtp_dev->open_excl);
+ return 0;
+}
+
+/* file operations for /dev/mtp_usb */
+static const struct file_operations mtp_fops = {
+ .owner = THIS_MODULE,
+ .read = mtp_read,
+ .write = mtp_write,
+ .unlocked_ioctl = mtp_ioctl,
+ .open = mtp_open,
+ .release = mtp_release,
+};
+
+static struct miscdevice mtp_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = shortname,
+ .fops = &mtp_fops,
+};
+
+static int
+mtp_function_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct mtp_dev *dev = func_to_dev(f);
+ int id;
+ int ret;
+
+ dev->cdev = cdev;
+ DBG(cdev, "mtp_function_bind dev: %p\n", dev);
+
+ /* allocate interface ID(s) */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ mtp_interface_desc.bInterfaceNumber = id;
+
+ /* allocate endpoints */
+ ret = create_bulk_endpoints(dev, &mtp_fullspeed_in_desc,
+ &mtp_fullspeed_out_desc, &mtp_intr_desc);
+ if (ret)
+ return ret;
+
+ /* support high speed hardware */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ mtp_highspeed_in_desc.bEndpointAddress =
+ mtp_fullspeed_in_desc.bEndpointAddress;
+ mtp_highspeed_out_desc.bEndpointAddress =
+ mtp_fullspeed_out_desc.bEndpointAddress;
+ }
+
+ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ f->name, dev->ep_in->name, dev->ep_out->name);
+ return 0;
+}
+
+static void
+mtp_function_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct mtp_dev *dev = func_to_dev(f);
+ struct usb_request *req;
+ int i;
+
+ spin_lock_irq(&dev->lock);
+ while ((req = req_get(dev, &dev->tx_idle)))
+ mtp_request_free(req, dev->ep_in);
+ for (i = 0; i < RX_REQ_MAX; i++)
+ mtp_request_free(dev->rx_req[i], dev->ep_out);
+ mtp_request_free(dev->intr_req, dev->ep_intr);
+ dev->state = STATE_OFFLINE;
+ spin_unlock_irq(&dev->lock);
+ wake_up(&dev->intr_wq);
+
+ misc_deregister(&mtp_device);
+ kfree(_mtp_dev);
+ _mtp_dev = NULL;
+}
+
+static int mtp_function_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct mtp_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = dev->cdev;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+ unsigned long flags;
+
+ /* do nothing if we are disabled */
+ if (dev->function.disabled)
+ return value;
+
+ VDBG(cdev, "mtp_function_setup "
+ "%02x.%02x v%04x i%04x l%u\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+
+ /* Handle MTP OS string */
+ if (dev->interface_mode == MTP_INTERFACE_MODE_MTP
+ && ctrl->bRequestType ==
+ (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)
+ && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR
+ && (w_value >> 8) == USB_DT_STRING
+ && (w_value & 0xFF) == MTP_OS_STRING_ID) {
+ value = (w_length < sizeof(mtp_os_string)
+ ? w_length : sizeof(mtp_os_string));
+ memcpy(cdev->req->buf, mtp_os_string, value);
+ /* return here since composite.c will send for us */
+ return value;
+ }
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
+ /* Handle MTP OS descriptor */
+ DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n",
+ ctrl->bRequest, w_index, w_value, w_length);
+
+ if (dev->interface_mode == MTP_INTERFACE_MODE_MTP
+ && ctrl->bRequest == 1
+ && (ctrl->bRequestType & USB_DIR_IN)
+ && (w_index == 4 || w_index == 5)) {
+ value = (w_length < sizeof(mtp_ext_config_desc) ?
+ w_length : sizeof(mtp_ext_config_desc));
+ memcpy(cdev->req->buf, &mtp_ext_config_desc, value);
+ }
+ }
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+ DBG(cdev, "class request: %d index: %d value: %d length: %d\n",
+ ctrl->bRequest, w_index, w_value, w_length);
+
+ if (ctrl->bRequest == MTP_REQ_CANCEL && w_index == 0
+ && w_value == 0) {
+ DBG(cdev, "MTP_REQ_CANCEL\n");
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state == STATE_BUSY) {
+ dev->state = STATE_CANCELED;
+ wake_up(&dev->read_wq);
+ wake_up(&dev->write_wq);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* We need to queue a request to read the remaining
+ * bytes, but we don't actually need to look at
+ * the contents.
+ */
+ value = w_length;
+ } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS
+ && w_index == 0 && w_value == 0) {
+ struct mtp_device_status *status = cdev->req->buf;
+ status->wLength =
+ __constant_cpu_to_le16(sizeof(*status));
+
+ DBG(cdev, "MTP_REQ_GET_DEVICE_STATUS\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ /* device status is "busy" until we report
+ * the cancelation to userspace
+ */
+ if (dev->state == STATE_BUSY
+ || dev->state == STATE_CANCELED)
+ status->wCode =
+ __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY);
+ else
+ status->wCode =
+ __cpu_to_le16(MTP_RESPONSE_OK);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ value = sizeof(*status);
+ }
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ int rc;
+ cdev->req->zero = value < w_length;
+ cdev->req->length = value;
+ rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
+ if (rc < 0)
+ ERROR(cdev, "%s setup response queue error\n", __func__);
+ }
+
+ if (value == -EOPNOTSUPP)
+ VDBG(cdev,
+ "unknown class-specific control req "
+ "%02x.%02x v%04x i%04x l%u\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ return value;
+}
+
+static int mtp_function_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct mtp_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int ret;
+
+ DBG(cdev, "mtp_function_set_alt intf: %d alt: %d\n", intf, alt);
+ ret = usb_ep_enable(dev->ep_in,
+ ep_choose(cdev->gadget,
+ &mtp_highspeed_in_desc,
+ &mtp_fullspeed_in_desc));
+ if (ret)
+ return ret;
+ ret = usb_ep_enable(dev->ep_out,
+ ep_choose(cdev->gadget,
+ &mtp_highspeed_out_desc,
+ &mtp_fullspeed_out_desc));
+ if (ret) {
+ usb_ep_disable(dev->ep_in);
+ return ret;
+ }
+ ret = usb_ep_enable(dev->ep_intr, &mtp_intr_desc);
+ if (ret) {
+ usb_ep_disable(dev->ep_out);
+ usb_ep_disable(dev->ep_in);
+ return ret;
+ }
+ dev->state = STATE_READY;
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+ return 0;
+}
+
+static void mtp_function_disable(struct usb_function *f)
+{
+ struct mtp_dev *dev = func_to_dev(f);
+ struct usb_composite_dev *cdev = dev->cdev;
+
+ DBG(cdev, "mtp_function_disable\n");
+ dev->state = STATE_OFFLINE;
+ usb_ep_disable(dev->ep_in);
+ usb_ep_disable(dev->ep_out);
+ usb_ep_disable(dev->ep_intr);
+
+ /* readers may be blocked waiting for us to go online */
+ wake_up(&dev->read_wq);
+ wake_up(&dev->intr_wq);
+
+ VDBG(cdev, "%s disabled\n", dev->function.name);
+}
+
+static int mtp_bind_config(struct usb_configuration *c)
+{
+ struct mtp_dev *dev;
+ int ret;
+
+ printk(KERN_INFO "mtp_bind_config\n");
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* allocate a string ID for our interface */
+ if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) {
+ ret = usb_string_id(c->cdev);
+ if (ret < 0)
+ return ret;
+ mtp_string_defs[INTERFACE_STRING_INDEX].id = ret;
+ mtp_interface_desc.iInterface = ret;
+ }
+
+ spin_lock_init(&dev->lock);
+ init_completion(&dev->thread_wait);
+ init_waitqueue_head(&dev->read_wq);
+ init_waitqueue_head(&dev->write_wq);
+ init_waitqueue_head(&dev->intr_wq);
+ atomic_set(&dev->open_excl, 0);
+ INIT_LIST_HEAD(&dev->tx_idle);
+ mutex_init(&dev->intr_mutex);
+
+ dev->cdev = c->cdev;
+ dev->function.name = "mtp";
+ dev->function.strings = mtp_strings,
+ dev->function.descriptors = fs_mtp_descs;
+ dev->function.hs_descriptors = hs_mtp_descs;
+ dev->function.bind = mtp_function_bind;
+ dev->function.unbind = mtp_function_unbind;
+ dev->function.setup = mtp_function_setup;
+ dev->function.set_alt = mtp_function_set_alt;
+ dev->function.disable = mtp_function_disable;
+
+ /* MTP mode by default */
+ dev->interface_mode = MTP_INTERFACE_MODE_MTP;
+
+ /* _mtp_dev must be set before calling usb_gadget_register_driver */
+ _mtp_dev = dev;
+
+ ret = misc_register(&mtp_device);
+ if (ret)
+ goto err1;
+
+ ret = usb_add_function(c, &dev->function);
+ if (ret)
+ goto err2;
+
+ return 0;
+
+err2:
+ misc_deregister(&mtp_device);
+err1:
+ kfree(dev);
+ printk(KERN_ERR "mtp gadget driver failed to initialize\n");
+ return ret;
+}
+
+static struct android_usb_function mtp_function = {
+ .name = "mtp",
+ .bind_config = mtp_bind_config,
+};
+
+static int __init init(void)
+{
+ printk(KERN_INFO "f_mtp init\n");
+ android_register_function(&mtp_function);
+ return 0;
+}
+module_init(init);
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index 882484a4039..6e6489b4958 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -26,8 +26,9 @@
#include <linux/slab.h>
#include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
#include <linux/etherdevice.h>
+#include <linux/usb/android_composite.h>
#include <asm/atomic.h>
@@ -129,9 +130,16 @@ static struct usb_interface_descriptor rndis_control_intf = {
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
+#ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS
+ /* "Wireless" RNDIS; auto-detected by Windows */
+ .bInterfaceClass = USB_CLASS_WIRELESS_CONTROLLER,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 3,
+#else
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+#endif
/* .iInterface = DYNAMIC */
};
@@ -190,9 +198,16 @@ rndis_iad_descriptor = {
.bFirstInterface = 0, /* XXX, hardcoded */
.bInterfaceCount = 2, // control + data
+#ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS
+ /* "Wireless" RNDIS; auto-detected by Windows */
+ .bFunctionClass = USB_CLASS_WIRELESS_CONTROLLER,
+ .bFunctionSubClass = 1,
+ .bFunctionProtocol = 3,
+#else
.bFunctionClass = USB_CLASS_COMM,
- .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
- .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
+ .bFunctionProtocol = USB_CDC_ACM_PROTO_VENDOR,
+#endif
/* .iFunction = DYNAMIC */
};
@@ -304,6 +319,10 @@ static struct usb_gadget_strings *rndis_strings[] = {
NULL,
};
+#ifdef CONFIG_USB_ANDROID_RNDIS
+static struct usb_ether_platform_data *rndis_pdata;
+#endif
+
/*-------------------------------------------------------------------------*/
static struct sk_buff *rndis_add_header(struct gether *port,
@@ -487,10 +506,10 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
usb_ep_disable(rndis->notify);
} else {
VDBG(cdev, "init rndis ctrl %d\n", intf);
- rndis->notify_desc = ep_choose(cdev->gadget,
- rndis->hs.notify,
- rndis->fs.notify);
}
+ rndis->notify_desc = ep_choose(cdev->gadget,
+ rndis->hs.notify,
+ rndis->fs.notify);
usb_ep_enable(rndis->notify, rndis->notify_desc);
rndis->notify->driver_data = rndis;
@@ -504,11 +523,11 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (!rndis->port.in) {
DBG(cdev, "init rndis\n");
- rndis->port.in = ep_choose(cdev->gadget,
- rndis->hs.in, rndis->fs.in);
- rndis->port.out = ep_choose(cdev->gadget,
- rndis->hs.out, rndis->fs.out);
}
+ rndis->port.in = ep_choose(cdev->gadget,
+ rndis->hs.in, rndis->fs.in);
+ rndis->port.out = ep_choose(cdev->gadget,
+ rndis->hs.out, rndis->fs.out);
/* Avoid ZLPs; they can be troublesome. */
rndis->port.is_zlp_ok = false;
@@ -590,6 +609,33 @@ static void rndis_close(struct gether *geth)
rndis_signal_disconnect(rndis->config);
}
+static rndis_release(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_rndis *rndis = func_to_rndis(f);
+ int status;
+ struct usb_ep *ep;
+
+ if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors)
+ usb_free_descriptors(f->hs_descriptors);
+ if (f->descriptors)
+ usb_free_descriptors(f->descriptors);
+
+ if (rndis->notify_req) {
+ kfree(rndis->notify_req->buf);
+ usb_ep_free_request(rndis->notify, rndis->notify_req);
+ }
+
+ /* we might as well release our claims on endpoints */
+ if (rndis->notify)
+ rndis->notify->driver_data = NULL;
+ if (rndis->port.out)
+ rndis->port.out_ep->driver_data = NULL;
+ if (rndis->port.in)
+ rndis->port.in_ep->driver_data = NULL;
+
+}
+
/*-------------------------------------------------------------------------*/
/* ethernet function driver setup/binding */
@@ -707,11 +753,12 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0);
rndis_set_host_mac(rndis->config, rndis->ethaddr);
-#if 0
-// FIXME
- if (rndis_set_param_vendor(rndis->config, vendorID,
- manufacturer))
- goto fail0;
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ if (rndis_pdata) {
+ if (rndis_set_param_vendor(rndis->config, rndis_pdata->vendorID,
+ rndis_pdata->vendorDescr))
+ goto fail;
+ }
#endif
/* NOTE: all that is done without knowing or caring about
@@ -726,23 +773,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
- if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors)
- usb_free_descriptors(f->hs_descriptors);
- if (f->descriptors)
- usb_free_descriptors(f->descriptors);
-
- if (rndis->notify_req) {
- kfree(rndis->notify_req->buf);
- usb_ep_free_request(rndis->notify, rndis->notify_req);
- }
-
- /* we might as well release our claims on endpoints */
- if (rndis->notify)
- rndis->notify->driver_data = NULL;
- if (rndis->port.out)
- rndis->port.out_ep->driver_data = NULL;
- if (rndis->port.in)
- rndis->port.in_ep->driver_data = NULL;
+ rndis_release(c, f);
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
@@ -850,6 +881,11 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
rndis->port.func.setup = rndis_setup;
rndis->port.func.disable = rndis_disable;
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ /* start disabled */
+ rndis->port.func.disabled = 1;
+#endif
+
status = usb_add_function(c, &rndis->port.func);
if (status) {
kfree(rndis);
@@ -858,3 +894,122 @@ fail:
}
return status;
}
+
+#ifdef CONFIG_USB_ANDROID_RNDIS
+#include "rndis.c"
+
+static int rndis_probe(struct platform_device *pdev)
+{
+ rndis_pdata = pdev->dev.platform_data;
+ return 0;
+}
+
+static struct platform_driver rndis_platform_driver = {
+ .driver = { .name = "rndis", },
+ .probe = rndis_probe,
+};
+
+int rndis_function_bind_config(struct usb_configuration *c)
+{
+ int ret;
+
+ if (!rndis_pdata) {
+ printk(KERN_ERR "rndis_pdata null in rndis_function_bind_config\n");
+ return -1;
+ }
+
+ printk(KERN_INFO
+ "rndis_function_bind_config MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ rndis_pdata->ethaddr[0], rndis_pdata->ethaddr[1],
+ rndis_pdata->ethaddr[2], rndis_pdata->ethaddr[3],
+ rndis_pdata->ethaddr[4], rndis_pdata->ethaddr[5]);
+
+ ret = gether_setup(c->cdev->gadget, rndis_pdata->ethaddr);
+ if (ret == 0)
+ ret = rndis_bind_config(c, rndis_pdata->ethaddr);
+ return ret;
+}
+
+static int rndis_function_rebind_config(struct usb_configuration *c,
+ struct usb_function *f)
+{
+
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_rndis *rndis = func_to_rndis(f);
+ int status1, status2 = 0;
+ struct usb_descriptor_header **desc = f->descriptors;
+
+ /* re-allocate instance-specific interface IDs */
+ status1 = usb_interface_id(c, f);
+
+ if (status1 < 0)
+ goto fail;
+ rndis->ctrl_id = status1;
+
+ rndis_iad_descriptor.bFirstInterface = status1;
+ rndis_control_intf.bInterfaceNumber = status1;
+ rndis_union_desc.bMasterInterface0 = status1;
+
+ ((struct usb_interface_assoc_descriptor *)desc[0])
+ ->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)desc[1])
+ ->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bMasterInterface0 = status1;
+
+ status2 = usb_interface_id(c, f);
+ if (status2 < 0)
+ goto fail;
+ rndis->data_id = status2;
+
+ rndis_data_intf.bInterfaceNumber = status2;
+ rndis_union_desc.bSlaveInterface0 = status2;
+
+ ((struct usb_interface_descriptor *)desc[7])
+ ->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)desc[5])
+ ->bSlaveInterface0 = status2;
+
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ desc = f->hs_descriptors;
+ ((struct usb_interface_assoc_descriptor *)
+ desc[0])->bFirstInterface = status1;
+ ((struct usb_interface_descriptor *)
+ desc[1])->bInterfaceNumber = status1;
+ ((struct usb_cdc_union_desc *)
+ desc[3])->bMasterInterface0 = status1;
+
+ ((struct usb_interface_descriptor *)
+ desc[7])->bInterfaceNumber = status2;
+ ((struct usb_cdc_union_desc *)
+ desc[5])->bSlaveInterface0 = status2;
+ }
+
+ return 0;
+
+fail:
+ rndis_release(c, f);
+
+ ERROR(cdev, "%s: can't re-bind, err %d %d\n", f->name,
+ status1, status2);
+ if (status2 < 0)
+ status1 = status2;
+ return status1;
+}
+
+static struct android_usb_function rndis_function = {
+ .name = "rndis",
+ .bind_config = rndis_function_bind_config,
+ .rebind_config = rndis_function_rebind_config,
+};
+
+static int __init init(void)
+{
+ printk(KERN_INFO "f_rndis init\n");
+ platform_driver_register(&rndis_platform_driver);
+ android_register_function(&rndis_function);
+ return 0;
+}
+module_init(init);
+
+#endif /* CONFIG_USB_ANDROID_RNDIS */
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index b49d86e3e45..8ef770214b2 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -486,19 +486,6 @@ static int exception_in_progress(struct fsg_dev *fsg)
return (fsg->state > FSG_STATE_IDLE);
}
-/* Make bulk-out requests be divisible by the maxpacket size */
-static void set_bulk_out_req_length(struct fsg_dev *fsg,
- struct fsg_buffhd *bh, unsigned int length)
-{
- unsigned int rem;
-
- bh->bulk_out_intended_length = length;
- rem = length % fsg->bulk_out_maxpacket;
- if (rem > 0)
- length += fsg->bulk_out_maxpacket - rem;
- bh->outreq->length = length;
-}
-
static struct fsg_dev *the_fsg;
static struct usb_gadget_driver fsg_driver;
@@ -719,10 +706,10 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
struct fsg_buffhd *bh = req->context;
dump_msg(fsg, "bulk-out", req->buf, req->actual);
- if (req->status || req->actual != bh->bulk_out_intended_length)
+ if (req->status || req->actual != req->length)
DBG(fsg, "%s --> %d, %u/%u\n", __func__,
req->status, req->actual,
- bh->bulk_out_intended_length);
+ req->length);
if (req->status == -ECONNRESET) // Request was cancelled
usb_ep_fifo_flush(ep);
@@ -1337,8 +1324,7 @@ static int do_write(struct fsg_dev *fsg)
/* amount is always divisible by 512, hence by
* the bulk-out maxpacket size */
- bh->outreq->length = bh->bulk_out_intended_length =
- amount;
+ bh->outreq->length = amount;
bh->outreq->short_not_ok = 1;
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
@@ -1909,6 +1895,18 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
return -EINTR;
rc = usb_ep_set_halt(fsg->bulk_in);
}
+#ifdef CONFIG_ARCH_U8500
+ /* temporary HACK: There is a problem using mass storage with
+ * the musb driver. The problem is that the status command
+ * wrapper block gets queued in hardware before the clear-stall
+ * is executed. When the clear stall gets executed the status
+ * command wrapper block gets dropped and the USB host OS
+ * starts hanging. We are not sure where the proper place to
+ * fix this bug is. Until further, add a delay here so that
+ * the USB host gets time to execute the clear-stall request.
+ */
+ msleep_interruptible(100); /* temporary HACK */
+#endif
return rc;
}
@@ -1998,8 +1996,7 @@ static int throw_away_data(struct fsg_dev *fsg)
/* amount is always divisible by 512, hence by
* the bulk-out maxpacket size */
- bh->outreq->length = bh->bulk_out_intended_length =
- amount;
+ bh->outreq->length = amount;
bh->outreq->short_not_ok = 1;
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
@@ -2676,8 +2673,8 @@ static int get_next_command(struct fsg_dev *fsg)
}
/* Queue a request to read a Bulk-only CBW */
- set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN);
- bh->outreq->short_not_ok = 1;
+ bh->outreq->length = USB_BULK_CB_WRAP_LEN;
+ bh->outreq->short_not_ok = 0;
start_transfer(fsg, fsg->bulk_out, bh->outreq,
&bh->outreq_busy, &bh->state);
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index a930d7fd7e7..fe1ed688518 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -107,7 +107,11 @@ static struct usb_otg_descriptor otg_descriptor = {
/* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
+#ifndef USB_OTG_20
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+#else
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP | USB_OTG_ADP,
+#endif
};
static const struct usb_descriptor_header *otg_desc[] = {
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 04c462ff0ea..744291dd5ab 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -312,12 +312,6 @@ struct fsg_buffhd {
#endif
enum fsg_buffer_state state;
struct fsg_buffhd *next;
-
- /* The NetChip 2280 is faster, and handles some protocol faults
- * better, if we don't submit any short bulk-out read requests.
- * So we will record the intended request length here. */
- unsigned int bulk_out_intended_length;
-
struct usb_request *inreq;
int inreq_busy;
struct usb_request *outreq;
@@ -750,10 +744,16 @@ static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
struct rw_semaphore *filesem = dev_get_drvdata(dev);
int rc = 0;
+
+#ifndef CONFIG_USB_ANDROID_MASS_STORAGE
+ /* disabled in android because we need to allow closing the backing file
+ * if the media was removed
+ */
if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
LDBG(curlun, "eject attempt prevented\n");
return -EBUSY; /* "Door is locked" */
}
+#endif
/* Remove a trailing newline */
if (count > 0 && buf[count-1] == '\n')
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 1da755a1c85..8bccb6211db 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -947,7 +947,6 @@ void gether_disconnect(struct gether *link)
struct eth_dev *dev = link->ioport;
struct usb_request *req;
- WARN_ON(!dev);
if (!dev)
return;
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h
index 3c8c0c9f9d7..99e4aa3b137 100644
--- a/drivers/usb/gadget/u_ether.h
+++ b/drivers/usb/gadget/u_ether.h
@@ -105,7 +105,7 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
int eem_bind_config(struct usb_configuration *c);
-#ifdef USB_ETH_RNDIS
+#if defined(USB_ETH_RNDIS) || defined(CONFIG_USB_ANDROID_RNDIS)
int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 807280d069f..a7c60c23265 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -292,7 +292,10 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
device_desc.iSerialNumber = id;
setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);
-
+#ifdef CONFIG_USB_OTG_20
+ if (gadget_is_otg2(cdev->gadget))
+ otg_descriptor.bcdOTG = __constant_cpu_to_le16(0x0200);
+#endif
/* Register primary, then secondary configuration. Note that
* SH3 only allows one config...
*/
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index cfd38edfcf9..8d5caddc03e 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -41,6 +41,7 @@ config USB_MUSB_SOC
default y if ARCH_OMAP4
default y if (BF54x && !BF544)
default y if (BF52x && !BF522 && !BF523)
+ default y if ARCH_U8500
comment "DaVinci 35x and 644x USB support"
depends on USB_MUSB_HDRC && ARCH_DAVINCI_DMx
@@ -57,6 +58,17 @@ comment "OMAP 44xx high speed USB support"
comment "Blackfin high speed USB Support"
depends on USB_MUSB_HDRC && ((BF54x && !BF544) || (BF52x && !BF522 && !BF523))
+comment "U8500 USB support"
+ depends on USB_MUSB_HDRC && ARCH_U8500
+
+config U8500_USB_HS_OTG
+ boolean "USB HS-OTG support"
+ depends on USB_MUSB_HDRC && ARCH_U8500
+ default y
+ help
+ Say Y here if you want to enable support for the U8500 USB
+ high speed on-the-go USB interface with external ULPI transceiver.
+
config USB_TUSB6010
boolean "TUSB 6010 support"
depends on USB_MUSB_HDRC && !USB_MUSB_SOC
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index 9705f716386..bddb323c3f2 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -26,6 +26,10 @@ ifeq ($(CONFIG_ARCH_OMAP4),y)
musb_hdrc-objs += omap2430.o
endif
+ifeq ($(CONFIG_ARCH_U8500),y)
+ musb_hdrc-objs += stm_musb.o
+endif
+
ifeq ($(CONFIG_BF54x),y)
musb_hdrc-objs += blackfin.o
endif
@@ -67,6 +71,7 @@ ifneq ($(CONFIG_MUSB_PIO_ONLY),y)
endif
endif
endif
+ musb_hdrc-objs += stm_musb_dma.o
endif
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 3b795c56221..ee10bf1f031 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -4,6 +4,7 @@
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2009 ST Ericsson
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -106,12 +107,16 @@
#endif
#include "musb_core.h"
-
+#ifdef CONFIG_ARCH_U8500
+#include "ste_config.h"
+#endif
#ifdef CONFIG_ARCH_DAVINCI
#include "davinci.h"
#endif
+#include <mach/stm_musb.h>
+
#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON)
@@ -459,6 +464,29 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl,
int_usb);
+ /*
+ * XXX The following code has been inserted here as a temporary hack
+ * to get some USB events directly from the USB hardware. This code
+ * and callbacks should eventually be integrated into the generic
+ * USB gadget stack.
+ */
+ if (!(devctl & MUSB_DEVCTL_HM)) {
+ if (int_usb & MUSB_INTR_RESET) {
+ if (power & MUSB_POWER_HSMODE)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESET_HS);
+ else
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESET_FS);
+ }
+ if (int_usb & MUSB_INTR_RESUME)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESUME);
+ else if (int_usb & MUSB_INTR_SUSPEND)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_SUSPEND);
+ }
+
/* in host mode, the peripheral may issue remote wakeup.
* in peripheral mode, the host may resume the link.
* spurious RESUME irqs happen too, paired with SUSPEND.
@@ -492,7 +520,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
(USB_PORT_STAT_C_SUSPEND << 16)
| MUSB_PORT_STAT_RESUME;
musb->rh_timer = jiffies
- + msecs_to_jiffies(20);
+ + msecs_to_jiffies(30);
musb->xceiv->state = OTG_STATE_A_HOST;
musb->is_active = 1;
@@ -987,6 +1015,10 @@ void musb_start(struct musb *musb)
}
musb_platform_enable(musb);
musb_writeb(regs, MUSB_DEVCTL, devctl);
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_U8500)
+ musb_save_context(musb);
+ clk_disable(musb->clock);
+#endif
}
@@ -995,6 +1027,9 @@ static void musb_generic_disable(struct musb *musb)
void __iomem *mbase = musb->mregs;
u16 temp;
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_U8500)
+ clk_enable(musb->clock);
+#endif
/* disable interrupts */
musb_writeb(mbase, MUSB_INTRUSBE, 0);
musb_writew(mbase, MUSB_INTRTXE, 0);
@@ -1008,6 +1043,9 @@ static void musb_generic_disable(struct musb *musb)
temp = musb_readw(mbase, MUSB_INTRTX);
temp = musb_readw(mbase, MUSB_INTRRX);
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_U8500)
+ clk_disable(musb->clock);
+#endif
}
/*
@@ -1067,7 +1105,11 @@ static void musb_shutdown(struct platform_device *pdev)
|| defined(CONFIG_ARCH_OMAP4)
static ushort __initdata fifo_mode = 4;
#else
+#ifndef CONFIG_ARCH_U8500
static ushort __initdata fifo_mode = 2;
+#else
+static ushort __initdata fifo_mode = 5;
+#endif
#endif
/* "modprobe ... fifo_mode=1" etc */
@@ -1150,8 +1192,8 @@ static struct musb_fifo_cfg __initdata mode_4_cfg[] = {
/* mode 5 - fits in 8KB */
static struct musb_fifo_cfg __initdata mode_5_cfg[] = {
-{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
-{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
@@ -1536,7 +1578,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) || \
- defined(CONFIG_ARCH_OMAP4)
+ defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500)
static irqreturn_t generic_interrupt(int irq, void *__hci)
{
@@ -1591,6 +1633,14 @@ irqreturn_t musb_interrupt(struct musb *musb)
}
#endif
+ /**
+ * HACK for detecting the AX8817X series Ethernet over USB
+ * Adapters for U8500 platform
+ */
+#if (defined(CONFIG_ARCH_U8500) && defined(CONFIG_USB_NET_AX8817X))
+ mdelay(10);
+#endif
+
/* the core can interrupt us for multiple reasons; docs have
* a generic interrupt flowchart to follow
*/
@@ -2417,26 +2467,42 @@ static int musb_suspend(struct device *dev)
spin_lock_irqsave(&musb->lock, flags);
if (is_peripheral_active(musb)) {
- /* FIXME force disconnect unless we know USB will wake
+ /*
+ * FIXME force disconnect unless we know USB will wake
* the system up quickly enough to respond ...
+ * For ux500 platform if usb is connected return busy
+ * state
*/
+ if (musb->is_active == 1) {
+ spin_unlock_irqrestore(&musb->lock, flags);
+ return -EBUSY;
+ }
} else if (is_host_active(musb)) {
- /* we know all the children are suspended; sometimes
+ /*
+ * we know all the children are suspended; sometimes
* they will even be wakeup-enabled.
+ * For ux500 platform if usb is connected return busy
+ * state
*/
+ if (musb->is_active == 1) {
+ spin_unlock_irqrestore(&musb->lock, flags);
+ return -EBUSY;
+ }
}
+#ifndef CONFIG_ARCH_U8500
musb_save_context(musb);
if (musb->set_clock)
musb->set_clock(musb->clock, 0);
else
clk_disable(musb->clock);
+#endif
spin_unlock_irqrestore(&musb->lock, flags);
return 0;
}
-static int musb_resume_noirq(struct device *dev)
+static int musb_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct musb *musb = dev_to_musb(&pdev->dev);
@@ -2444,12 +2510,14 @@ static int musb_resume_noirq(struct device *dev)
if (!musb->clock)
return 0;
+#ifndef CONFIG_ARCH_U8500
if (musb->set_clock)
musb->set_clock(musb->clock, 1);
else
clk_enable(musb->clock);
musb_restore_context(musb);
+#endif
/* for static cmos like DaVinci, register values were preserved
* unless for some reason the whole soc powered down or the USB
@@ -2460,7 +2528,7 @@ static int musb_resume_noirq(struct device *dev)
static const struct dev_pm_ops musb_dev_pm_ops = {
.suspend = musb_suspend,
- .resume_noirq = musb_resume_noirq,
+ .resume = musb_resume,
};
#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops)
@@ -2513,10 +2581,18 @@ static int __init musb_init(void)
return platform_driver_probe(&musb_driver, musb_probe);
}
+#ifndef CONFIG_ARCH_U8500
/* make us init after usbcore and i2c (transceivers, regulators, etc)
* and before usb gadget and host-side drivers start to register
*/
fs_initcall(musb_init);
+#else
+/* with fs_initcall the dma controller driver was loaded after mentor IP
+ * driver so when DMA is enabled, it will break as DMA controller driver is
+ * not loaded. This has been done to correct the order
+ */
+module_init(musb_init);
+#endif
static void __exit musb_cleanup(void)
{
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 91d67794e35..c037939a8a2 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -72,8 +72,6 @@ struct musb_ep;
#include <linux/usb/hcd.h>
#include "musb_host.h"
-
-
#ifdef CONFIG_USB_MUSB_OTG
#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST)
@@ -204,6 +202,7 @@ enum musb_g_ep0_state {
#define OTG_TIME_A_WAIT_BCON 1100 /* min 1 second */
#define OTG_TIME_A_AIDL_BDIS 200 /* min 200 msec */
#define OTG_TIME_B_ASE0_BRST 100 /* min 3.125 ms */
+#define USB_SUSP_DET_DURATION 5 /* suspend time 5ms */
/*************************** REGISTER ACCESS ********************************/
@@ -492,8 +491,10 @@ extern void musb_platform_save_context(struct musb *musb,
extern void musb_platform_restore_context(struct musb *musb,
struct musb_context_registers *musb_context);
#else
-#define musb_platform_save_context(m, x) do {} while (0)
-#define musb_platform_restore_context(m, x) do {} while (0)
+static inline void musb_platform_save_context(struct musb *musb,
+ struct musb_context_registers *musb_context) { }
+static inline void musb_platform_restore_context(struct musb *musb,
+ struct musb_context_registers *musb_context) { }
#endif
#endif
@@ -598,18 +599,31 @@ extern void musb_hnp_stop(struct musb *musb);
extern int musb_platform_set_mode(struct musb *musb, u8 musb_mode);
+#ifdef CONFIG_PM
+void musb_save_context(struct musb *);
+extern void musb_restore_context(struct musb *musb);
+#endif
+
#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN) || \
defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \
- defined(CONFIG_ARCH_OMAP4)
+ defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500)
extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout);
#else
-#define musb_platform_try_idle(x, y) do {} while (0)
+static inline void musb_platform_try_idle(struct musb *musb, unsigned long timeout) { }
#endif
#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN)
extern int musb_platform_get_vbus_status(struct musb *musb);
#else
-#define musb_platform_get_vbus_status(x) 0
+static inline int musb_platform_get_vbus_status(struct musb *musb) { return 0; }
+#endif
+
+#if defined(CONFIG_ARCH_U8500)
+extern void musb_platform_device_en(int enable);
+extern void musb_platform_session_req(void);
+#else
+static inline void musb_platform_device_en(int enable) { }
+static inline void musb_platform_session_req(void) { }
#endif
extern int __init musb_platform_init(struct musb *musb, void *board_data);
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 916065ba9e7..a114af6c330 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -36,7 +36,7 @@
#define __MUSB_DMA_H__
struct musb_hw_ep;
-
+struct musb_request;
/*
* DMA Controller Abstraction
*
@@ -91,6 +91,10 @@ struct musb_hw_ep;
# endif
#endif
+#ifdef CONFIG_USB_U8500_DMA
+#undef USE_MODE1
+#endif
+
/*
* DMA channel status ... updated by the dma controller driver whenever that
* status changes, and protected by the overall controller spinlock.
@@ -155,6 +159,10 @@ dma_channel_status(struct dma_channel *c)
* @channel_release: call this to release a DMA channel
* @channel_abort: call this to abort a pending DMA transaction,
* returning it to FREE (but allocated) state
+ * @is_compatible:allow dma code to indicate incompatibility
+ * with usb request. Gadget musb driver call this api, if
+ * available, before dma mappings to avoid any unnecessary
+ * mapping operations.
*
* Controllers manage dma channels.
*/
@@ -169,6 +177,8 @@ struct dma_controller {
dma_addr_t dma_addr,
u32 length);
int (*channel_abort)(struct dma_channel *);
+ int (*is_compatible)(struct dma_channel *channel,
+ struct musb_request *);
};
/* called after channel_program(), may indicate a fault */
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 6fca870e957..9ac40c4bab9 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -5,6 +5,7 @@
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
* Copyright (C) 2009 MontaVista Software, Inc. <source@mvista.com>
+ * Copyright (C) 2009 ST Ericsson
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -43,11 +44,14 @@
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/dma-mapping.h>
+#include <asm/cacheflush.h>
#include <linux/slab.h>
#include "musb_core.h"
-
-
+#ifdef CONFIG_ARCH_U8500
+#include "ste_config.h"
+#endif
+#define USB_ENABLE_PHY 1
/* MUSB PERIPHERAL status 3-mar-2006:
*
* - EP0 seems solid. It passes both USBCV and usbtest control cases.
@@ -91,6 +95,8 @@
*/
/* ----------------------------------------------------------------------- */
+#define is_buffer_mapped(req) (is_dma_capable() && \
+ (req->map_state != UN_MAPPED))
/*
* Immediately complete a request.
@@ -109,6 +115,7 @@ __acquires(ep->musb->lock)
struct musb_request *req;
struct musb *musb;
int busy = ep->busy;
+ int dma_len;
req = to_musb_request(request);
@@ -119,20 +126,34 @@ __acquires(ep->musb->lock)
ep->busy = 1;
spin_unlock(&musb->lock);
+#ifdef CONFIG_ARM
+ /* If DMA is enabled we need to flush all data we read. */
+ if (ep->hw_ep->musb->controller->dma_mask != 0)
+ dmac_flush_range((const u8 *)req->request.buf,
+ (const u8 *)req->request.buf + req->request.length);
+#endif
+#ifdef CONFIG_USB_U8500_DMA
+ if (!(req->request.actual < ep->packet_sz)
+ && is_buffer_mapped(req)
+ && (ep->packet_sz >= DMA_PACKET_THRESHOLD)) {
+#else
if (is_dma_capable()) {
- if (req->mapped) {
+#endif
+ dma_len = req->request.actual - (req->request.actual % 512) ;
+
+ if (req->map_state == MUSB_MAPPED) {
dma_unmap_single(musb->controller,
req->request.dma,
- req->request.length,
+ req->request.actual - dma_len,
req->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
req->request.dma = DMA_ADDR_INVALID;
- req->mapped = 0;
- } else if (req->request.dma != DMA_ADDR_INVALID)
+ req->map_state = UN_MAPPED;
+ } else if (req->map_state == PRE_MAPPED)
dma_sync_single_for_cpu(musb->controller,
req->request.dma,
- req->request.length,
+ req->request.actual - dma_len,
req->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
@@ -209,7 +230,20 @@ static void nuke(struct musb_ep *ep, const int status)
static inline int max_ep_writesize(struct musb *musb, struct musb_ep *ep)
{
+#ifdef CONFIG_USB_U8500_DMA
+ /*
+ * In case of full speed mode with double buffering, bulk split
+ * does not work as defined. Hence in case of FS, return the EP
+ * size instead of EP FIFO size.
+ */
+ u8 power;
+
+ power = musb_readb(musb->mregs, MUSB_POWER);
+
+ if (can_bulk_split(musb, ep->type) && (power & MUSB_POWER_HSMODE))
+#else
if (can_bulk_split(musb, ep->type))
+#endif
return ep->hw_ep->max_packet_sz_tx;
else
return ep->packet_sz;
@@ -267,12 +301,17 @@ static void txstate(struct musb *musb, struct musb_request *req)
int use_dma = 0;
musb_ep = req->ep;
-
+#ifdef CONFIG_USB_U8500_DMA
+ if (musb_ep->dma) {
+#endif
/* we shouldn't get here while DMA is active ... but we do ... */
if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) {
DBG(4, "dma pending...\n");
return;
}
+#ifdef CONFIG_USB_U8500_DMA
+ }
+#endif
/* read TXCSR before */
csr = musb_readw(epio, MUSB_TXCSR);
@@ -298,10 +337,25 @@ static void txstate(struct musb *musb, struct musb_request *req)
csr);
#ifndef CONFIG_MUSB_PIO_ONLY
+#ifdef CONFIG_USB_U8500_DMA
+ if (musb_ep->dma && is_buffer_mapped(req)) {
+#else
if (is_dma_capable() && musb_ep->dma) {
+#endif
struct dma_controller *c = musb->dma_controller;
use_dma = (request->dma != DMA_ADDR_INVALID);
+#ifdef CONFIG_USB_U8500_DMA
+ if (request->length >= DMA_PACKET_THRESHOLD) {
+ csr |= (MUSB_TXCSR_AUTOSET|
+ MUSB_TXCSR_DMAENAB
+ | MUSB_TXCSR_DMAMODE
+ | MUSB_TXCSR_MODE);
+ csr &= ~MUSB_TXCSR_P_UNDERRUN;
+ musb_writew(epio, MUSB_TXCSR, csr);
+ }
+ use_dma = use_dma && c->channel_program(musb_ep->dma, musb_ep->packet_sz, DMA_MODE_1, request->dma, request->length);
+#endif
/* MUSB_TXCSR_P_ISO is still set correctly */
@@ -422,6 +476,7 @@ void musb_g_tx(struct musb *musb, u8 epnum)
struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_in;
void __iomem *epio = musb->endpoints[epnum].regs;
struct dma_channel *dma;
+ u16 bytes_left = 0;
musb_ep_select(mbase, epnum);
request = next_request(musb_ep);
@@ -463,13 +518,41 @@ void musb_g_tx(struct musb *musb, u8 epnum)
u8 is_dma = 0;
if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
+ int count = 0;
is_dma = 1;
+
+ /* ensure writebuffer is empty */
+ csr = musb_readw(epio, MUSB_TXCSR);
+ bytes_left = request->length
+ -musb_ep->dma->actual_len;
+ for (count = 0; count < MAX_COUNT; count++) {
+ if (!(csr&MUSB_TXCSR_FIFONOTEMPTY))
+ break;
+ csr = musb_readw(epio, MUSB_TXCSR);
+ }
+
csr |= MUSB_TXCSR_P_WZC_BITS;
+#ifdef CONFIG_ARCH_U8500
+ csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_MODE
+ | MUSB_TXCSR_AUTOSET
+ | MUSB_TXCSR_P_UNDERRUN
+ | MUSB_TXCSR_DMAMODE
+ | MUSB_TXCSR_TXPKTRDY);
+#else
csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN |
- MUSB_TXCSR_TXPKTRDY);
+ MUSB_TXCSR_DMAMODE | MUSB_TXCSR_TXPKTRDY);
+#endif
musb_writew(epio, MUSB_TXCSR, csr);
- /* Ensure writebuffer is empty. */
- csr = musb_readw(epio, MUSB_TXCSR);
+ if (bytes_left) {
+ musb_write_fifo(musb_ep->hw_ep,
+ bytes_left,
+ (u8 *) (request->buf +
+ musb_ep->dma->actual_len));
+ musb_writew(epio, MUSB_TXCSR,
+ MUSB_TXCSR_TXPKTRDY);
+ request->actual += bytes_left;
+ }
+
request->actual += musb_ep->dma->actual_len;
DBG(4, "TXCSR%d %04x, DMA off, len %zu, req %p\n",
epnum, csr, musb_ep->dma->actual_len, request);
@@ -502,25 +585,33 @@ void musb_g_tx(struct musb *musb, u8 epnum)
}
/* ... or if not, then complete it. */
- musb_g_giveback(musb_ep, request, 0);
+ if (request->actual == request->length) {
+ musb_g_giveback(musb_ep, request, 0);
- /*
- * Kickstart next transfer if appropriate;
- * the packet that just completed might not
- * be transmitted for hours or days.
- * REVISIT for double buffering...
- * FIXME revisit for stalls too...
- */
- musb_ep_select(mbase, epnum);
- csr = musb_readw(epio, MUSB_TXCSR);
- if (csr & MUSB_TXCSR_FIFONOTEMPTY)
- return;
-
- request = musb_ep->desc ? next_request(musb_ep) : NULL;
- if (!request) {
- DBG(4, "%s idle now\n",
- musb_ep->end_point.name);
- return;
+ /*
+ * Kickstart next transfer if appropriate;
+ * the packet that just completed might not
+ * be transmitted for hours or days.
+ * REVISIT for double buffering...
+ * FIXME revisit for stalls too...
+ */
+ musb_ep_select(mbase, epnum);
+ csr = musb_readw(epio, MUSB_TXCSR);
+#ifdef CONFIG_ARCH_U8500
+ if ((csr & MUSB_TXCSR_FIFONOTEMPTY) &&
+ !(musb_ep->hw_ep->tx_double_buffered))
+#else
+ if (csr & MUSB_TXCSR_FIFONOTEMPTY)
+#endif
+ return;
+
+ request =
+ musb_ep->desc ? next_request(musb_ep) : NULL;
+ if (!request) {
+ DBG(4, "%s idle now\n",
+ musb_ep->end_point.name);
+ return;
+ }
}
}
@@ -616,8 +707,14 @@ static void rxstate(struct musb *musb, struct musb_request *req)
if (csr & MUSB_RXCSR_RXPKTRDY) {
len = musb_readw(epio, MUSB_RXCOUNT);
if (request->actual < request->length) {
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_U8500_DMA)
+
#ifdef CONFIG_USB_INVENTRA_DMA
if (is_dma_capable() && musb_ep->dma) {
+#else
+ if (is_buffer_mapped(req) &&
+ (len >= DMA_PACKET_THRESHOLD)) {
+#endif
struct dma_controller *c;
struct dma_channel *channel;
int use_dma = 0;
@@ -646,44 +743,54 @@ static void rxstate(struct musb *musb, struct musb_request *req)
* then becomes usable as a runtime "use mode 1" hint...
*/
- csr |= MUSB_RXCSR_DMAENAB;
-#ifdef USE_MODE1
- csr |= MUSB_RXCSR_AUTOCLEAR;
- /* csr |= MUSB_RXCSR_DMAMODE; */
-
- /* this special sequence (enabling and then
- * disabling MUSB_RXCSR_DMAMODE) is required
- * to get DMAReq to activate
- */
- musb_writew(epio, MUSB_RXCSR,
- csr | MUSB_RXCSR_DMAMODE);
-#endif
- musb_writew(epio, MUSB_RXCSR, csr);
-
if (request->actual < request->length) {
int transfer_size = 0;
-#ifdef USE_MODE1
- transfer_size = min(request->length,
- channel->max_len);
-#else
- transfer_size = len;
-#endif
- if (transfer_size <= musb_ep->packet_sz)
- musb_ep->dma->desired_mode = 0;
- else
+
+ /* In case first packet is short */
+ if (len < musb_ep->packet_sz)
+ transfer_size = len;
+ else if (request->short_not_ok) {
+
+ csr &= ~MUSB_RXCSR_DMAMODE;
+ csr |= MUSB_RXCSR_AUTOCLEAR;
+ csr |= MUSB_RXCSR_DMAENAB;
+ musb_writew(epio,
+ MUSB_RXCSR, csr);
+
+ transfer_size =
+ min(request->length -
+ request->actual,
+ channel->max_len);
+
+ csr |= MUSB_RXCSR_DMAMODE;
+ musb_writew(epio,
+ MUSB_RXCSR, csr);
musb_ep->dma->desired_mode = 1;
+ } else {
+ csr &= ~MUSB_RXCSR_DMAMODE;
+ csr |= MUSB_RXCSR_DMAENAB;
+ musb_writew(epio,
+ MUSB_RXCSR, csr);
+
+ transfer_size =
+ min(request->length -
+ request->actual,
+ (unsigned)len);
+
+ musb_ep->dma->desired_mode = 0;
+ }
use_dma = c->channel_program(
- channel,
- musb_ep->packet_sz,
- channel->desired_mode,
- request->dma
- + request->actual,
- transfer_size);
- }
+ channel,
+ musb_ep->packet_sz,
+ channel->desired_mode,
+ request->dma
+ + request->actual,
+ transfer_size);
- if (use_dma)
- return;
+ if (use_dma)
+ return;
+ }
}
#endif /* Mentor's DMA */
@@ -743,6 +850,7 @@ void musb_g_rx(struct musb *musb, u8 epnum)
struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out;
void __iomem *epio = musb->endpoints[epnum].regs;
struct dma_channel *dma;
+ u16 bytes_left = 0;
musb_ep_select(mbase, epnum);
@@ -791,7 +899,7 @@ void musb_g_rx(struct musb *musb, u8 epnum)
| MUSB_RXCSR_DMAMODE);
musb_writew(epio, MUSB_RXCSR,
MUSB_RXCSR_P_WZC_BITS | csr);
-
+ csr = musb_readw(epio, MUSB_RXCSR);
request->actual += musb_ep->dma->actual_len;
DBG(4, "RXCSR%d %04x, dma off, %04x, len %zu, req %p\n",
@@ -799,7 +907,9 @@ void musb_g_rx(struct musb *musb, u8 epnum)
musb_readw(epio, MUSB_RXCSR),
musb_ep->dma->actual_len, request);
-#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA)
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) \
+ || defined(CONFIG_USB_U8500_DMA)
+
/* Autoclear doesn't clear RxPktRdy for short packets */
if ((dma->desired_mode == 0)
|| (dma->actual_len
@@ -963,6 +1073,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
* for some reason you run out of channels here.
*/
if (is_dma_capable() && musb->dma_controller) {
+
struct dma_controller *c = musb->dma_controller;
musb_ep->dma = c->channel_alloc(c, hw_ep,
@@ -1102,6 +1213,8 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
struct musb *musb;
int status = 0;
unsigned long lockflags;
+ int compatible = 0;
+ struct dma_controller *dma;
if (!ep || !req)
return -EINVAL;
@@ -1110,6 +1223,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
musb_ep = to_musb_ep(ep);
musb = musb_ep->musb;
+ dma = musb->dma_controller;
request = to_musb_request(req);
request->musb = musb;
@@ -1125,7 +1239,15 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
request->epnum = musb_ep->current_epnum;
request->tx = musb_ep->is_in;
- if (is_dma_capable() && musb_ep->dma) {
+ request->map_state = UN_MAPPED;
+
+ if (musb_ep->dma && dma) {
+ if (dma->is_compatible)
+ compatible = dma->is_compatible(musb_ep->dma, request);
+
+ }
+
+ if (is_dma_capable() && musb_ep->dma && compatible) {
if (request->request.dma == DMA_ADDR_INVALID) {
request->request.dma = dma_map_single(
musb->controller,
@@ -1134,7 +1256,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
request->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
- request->mapped = 1;
+ request->map_state = MUSB_MAPPED;
} else {
dma_sync_single_for_device(musb->controller,
request->request.dma,
@@ -1142,12 +1264,12 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
request->tx
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
- request->mapped = 0;
+ request->map_state = PRE_MAPPED;
}
} else if (!req->buf) {
return -ENODATA;
} else
- request->mapped = 0;
+ request->map_state = UN_MAPPED;
spin_lock_irqsave(&musb->lock, lockflags);
@@ -1417,6 +1539,10 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
u8 power, devctl;
int retries;
+
+ musb_platform_device_en(USB_ENABLE_PHY);
+ musb_platform_session_req();
+
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->state) {
@@ -1894,6 +2020,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
void musb_g_resume(struct musb *musb)
{
musb->is_suspended = 0;
+ stm_prcmu_qos_handler(SET_OPP);
switch (musb->xceiv->state) {
case OTG_STATE_B_IDLE:
break;
@@ -1940,6 +2067,7 @@ void musb_g_suspend(struct musb *musb)
WARNING("unhandled SUSPEND transition (%s)\n",
otg_state_string(musb));
}
+ stm_prcmu_qos_handler(!SET_OPP);
}
/* Called during SRP */
diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h
index c8b140325d8..20d6b064542 100644
--- a/drivers/usb/musb/musb_gadget.h
+++ b/drivers/usb/musb/musb_gadget.h
@@ -34,6 +34,11 @@
#ifndef __MUSB_GADGET_H
#define __MUSB_GADGET_H
+enum buffer_map_state {
+ UN_MAPPED = 0,
+ PRE_MAPPED,
+ MUSB_MAPPED
+};
struct musb_request {
struct usb_request request;
@@ -41,7 +46,7 @@ struct musb_request {
struct musb *musb;
u8 tx; /* endpoint direction */
u8 epnum;
- u8 mapped;
+ enum buffer_map_state map_state;
};
static inline struct musb_request *to_musb_request(struct usb_request *req)
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 877d20b1dff..25cbeadb8b0 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -45,6 +45,9 @@
#include "musb_core.h"
#include "musb_host.h"
+#ifdef CONFIG_ARCH_U8500
+#include "ste_config.h"
+#endif
/* MUSB HOST status 22-mar-2006
*
@@ -106,24 +109,41 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
{
void __iomem *epio = ep->regs;
+ void __iomem *regs = ep->musb->mregs;
u16 csr;
- u16 lastcsr = 0;
- int retries = 1000;
+ u8 addr;
+ int retries = 3000; /* 3ms */
+ /*
+ * NOTE: We are using a hack here because the FIFO-FLUSH
+ * bit is broken in hardware! The hack consists of changing
+ * the TXFUNCADDR to an unused device address and waiting
+ * for any pending USB packets to hit the 3-strikes and your
+ * gone rule.
+ */
+ addr = musb_readb(regs, MUSB_BUSCTL_OFFSET(ep->epnum, MUSB_TXFUNCADDR));
csr = musb_readw(epio, MUSB_TXCSR);
while (csr & MUSB_TXCSR_FIFONOTEMPTY) {
- if (csr != lastcsr)
- DBG(3, "Host TX FIFONOTEMPTY csr: %02x\n", csr);
- lastcsr = csr;
- csr |= MUSB_TXCSR_FLUSHFIFO;
- musb_writew(epio, MUSB_TXCSR, csr);
+ musb_writeb(regs, MUSB_BUSCTL_OFFSET(ep->epnum,
+ MUSB_TXFUNCADDR), 127);
csr = musb_readw(epio, MUSB_TXCSR);
- if (WARN(retries-- < 1,
- "Could not flush host TX%d fifo: csr: %04x\n",
- ep->epnum, csr))
- return;
- mdelay(1);
+ retries--;
+ if (retries == 0) {
+ /* can happen if the USB clocks are OFF */
+ DBG(3, "Could not flush host TX%d "
+ "fifo: csr=0x%04x\n", ep->epnum, csr);
+ break;
+ }
+ udelay(1);
}
+ /* clear any errors */
+ csr &= ~(MUSB_TXCSR_H_ERROR
+ | MUSB_TXCSR_H_RXSTALL
+ | MUSB_TXCSR_H_NAKTIMEOUT);
+ musb_writew(epio, MUSB_TXCSR, csr);
+
+ /* restore endpoint address */
+ musb_writeb(regs, MUSB_BUSCTL_OFFSET(ep->epnum, MUSB_TXFUNCADDR), addr);
}
static void musb_h_ep0_flush_fifo(struct musb_hw_ep *ep)
@@ -175,9 +195,16 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep)
/* NOTE: no locks here; caller should lock and select EP */
txcsr = musb_readw(ep->regs, MUSB_TXCSR);
+
+#ifdef CONFIG_USB_U8500_DMA
+ txcsr |= MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_DMAENAB |
+ MUSB_TXCSR_H_WZC_BITS;
+ txcsr |= MUSB_TXCSR_DMAMODE;
+#else
txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS;
if (is_cppi_enabled())
txcsr |= MUSB_TXCSR_DMAMODE;
+#endif
musb_writew(ep->regs, MUSB_TXCSR, txcsr);
}
@@ -290,8 +317,13 @@ start:
if (!hw_ep->tx_channel)
musb_h_tx_start(hw_ep);
+#ifdef CONFIG_USB_U8500_DMA
+ else
+ musb_h_tx_dma_start(hw_ep);
+#else
else if (is_cppi_enabled() || tusb_dma_omap())
musb_h_tx_dma_start(hw_ep);
+#endif
}
}
@@ -323,8 +355,15 @@ __acquires(musb->lock)
urb->actual_length, urb->transfer_buffer_length
);
+
usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb);
spin_unlock(&musb->lock);
+#ifdef CONFIG_USB_U8500_DMA
+ /* Make it safe to call this routine more than once */
+ urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
+ URB_DMA_MAP_SG | URB_DMA_MAP_PAGE |
+ URB_DMA_MAP_SINGLE | URB_MAP_LOCAL);
+#endif
usb_hcd_giveback_urb(musb_to_hcd(musb), urb, status);
spin_lock(&musb->lock);
}
@@ -627,7 +666,35 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
u16 csr;
u8 mode;
-#ifdef CONFIG_USB_INVENTRA_DMA
+#ifdef CONFIG_USB_U8500_DMA
+ if (length > channel->max_len)
+ length = channel->max_len;
+
+ csr = musb_readw(epio, MUSB_TXCSR);
+ if (length > pkt_size) {
+ mode = 1;
+ csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB;
+ /* autoset shouldn't be set in high bandwidth */
+ if (qh->hb_mult == 1)
+ csr |= MUSB_TXCSR_AUTOSET;
+ } else {
+ mode = 0;
+ csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
+ csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */
+ }
+ channel->desired_mode = mode;
+ musb_writew(epio, MUSB_TXCSR, csr);
+
+ channel->actual_len = 0;
+
+ /*
+ * TX uses "RNDIS" mode automatically but needs help
+ * to identify the zero-length-final-packet case.
+ */
+ mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
+
+#else
+#ifdef CONFIG_USB_INVENTRA_DMA
if (length > channel->max_len)
length = channel->max_len;
@@ -657,6 +724,7 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
*/
mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
#endif
+#endif
qh->segsize = length;
@@ -842,7 +910,6 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
}
/* kick things off */
-
if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) {
/* candidate for DMA */
if (dma_channel) {
@@ -872,7 +939,6 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
csr |= MUSB_RXCSR_DMAENAB;
}
}
-
csr |= MUSB_RXCSR_H_REQPKT;
DBG(7, "RXCSR%d := %04x\n", epnum, csr);
musb_writew(hw_ep->regs, MUSB_RXCSR, csr);
@@ -1141,7 +1207,9 @@ void musb_host_tx(struct musb *musb, u8 epnum)
DBG(3, "TX 3strikes on ep=%d\n", epnum);
status = -ETIMEDOUT;
-
+ } else if (tx_csr & MUSB_TXCSR_TXPKTRDY) {
+ /* BUSY - can happen during USB transfer cancel */
+ return;
} else if (tx_csr & MUSB_TXCSR_H_NAKTIMEOUT) {
DBG(6, "TX end=%d device not responding\n", epnum);
@@ -1437,6 +1505,9 @@ void musb_host_rx(struct musb *musb, u8 epnum)
u32 status;
struct dma_channel *dma;
+#if defined(CONFIG_USB_U8500_DMA)
+ u16 tmp_val;
+#endif
musb_ep_select(mbase, epnum);
urb = next_urb(qh);
@@ -1527,7 +1598,6 @@ void musb_host_rx(struct musb *musb, u8 epnum)
done = true;
goto finish;
}
-
if (unlikely(dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY)) {
/* SHOULD NEVER HAPPEN ... but at least DaVinci has done it */
ERR("RX%d dma busy, csr %04x\n", epnum, rx_csr);
@@ -1541,7 +1611,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
/* FIXME this is _way_ too much in-line logic for Mentor DMA */
-#ifndef CONFIG_USB_INVENTRA_DMA
+#if !defined(CONFIG_USB_INVENTRA_DMA) && !defined(CONFIG_USB_U8500_DMA)
if (rx_csr & MUSB_RXCSR_H_REQPKT) {
/* REVISIT this happened for a while on some short reads...
* the cleanup still needs investigation... looks bad...
@@ -1564,6 +1634,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
MUSB_RXCSR_H_WZC_BITS | rx_csr);
}
#endif
+
if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) {
xfer_len = dma->actual_len;
@@ -1573,7 +1644,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
| MUSB_RXCSR_RXPKTRDY);
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_U8500_DMA)
if (usb_pipeisoc(pipe)) {
struct usb_iso_packet_descriptor *d;
@@ -1629,7 +1700,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
}
/* we are expecting IN packets */
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_U8500_DMA)
if (dma) {
struct dma_controller *c;
u16 rx_count;
@@ -1713,6 +1784,14 @@ void musb_host_rx(struct musb *musb, u8 epnum)
*/
val = musb_readw(epio, MUSB_RXCSR);
+
+#if defined(CONFIG_USB_U8500_DMA)
+ /* retain the original value,
+ *which will be used to reset CSR
+ */
+ tmp_val = val;
+#endif
+
val &= ~MUSB_RXCSR_H_REQPKT;
if (dma->desired_mode == 0)
@@ -1741,9 +1820,13 @@ void musb_host_rx(struct musb *musb, u8 epnum)
hw_ep->rx_channel = NULL;
dma = NULL;
/* REVISIT reset CSR */
+
+#if defined(CONFIG_USB_U8500_DMA)
+ musb_writew(epio, MUSB_RXCSR, tmp_val);
+#endif
}
}
-#endif /* Mentor DMA */
+#endif /* Mentor DMA || U8500 DMA */
if (!dma) {
done = musb_host_packet_rx(musb, urb,
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 92e85e027cf..663dda191dd 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -84,8 +84,9 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
&& musb->xceiv->host->b_hnp_enable;
if (musb->is_active)
mod_timer(&musb->otg_timer, jiffies
- + msecs_to_jiffies(
- OTG_TIME_A_AIDL_BDIS));
+ + msecs_to_jiffies((
+ OTG_TIME_A_AIDL_BDIS +
+ USB_SUSP_DET_DURATION)));
musb_platform_try_idle(musb, 0);
break;
#ifdef CONFIG_USB_MUSB_OTG
diff --git a/drivers/usb/musb/ste_config.h b/drivers/usb/musb/ste_config.h
new file mode 100644
index 00000000000..daa336369fa
--- /dev/null
+++ b/drivers/usb/musb/ste_config.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright (C) 2009 ST-Ericsson SA
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STE_CONFIG_H__
+#define __STE_CONFIG_H__
+
+#if (defined(CONFIG_ARCH_U8500) && !defined(CONFIG_MUSB_PIO_ONLY))
+#define CONFIG_USB_U8500_DMA
+#endif
+#define U8500_DMA_END_POINTS 7
+#define DMA_MODE_0 0
+#define DMA_MODE_1 1
+#define DMA_PACKET_THRESHOLD 512
+#define RX_END_POINT_OFFSET 6
+#define DELAY_IN_MICROSECONDS 10
+#define MAX_COUNT 35000
+void stm_prcmu_qos_handler(int);
+#define SET_OPP 1
+
+enum nmdk_dma_tx_rx_channel {
+ TX_CHANNEL_1 = 0,
+ TX_CHANNEL_2,
+ TX_CHANNEL_3,
+ TX_CHANNEL_4,
+ TX_CHANNEL_5,
+ TX_CHANNEL_6,
+ TX_CHANNEL_7,
+ RX_CHANNEL_1,
+ RX_CHANNEL_2,
+ RX_CHANNEL_3,
+ RX_CHANNEL_4,
+ RX_CHANNEL_5,
+ RX_CHANNEL_6,
+ RX_CHANNEL_7
+};
+#endif
+
diff --git a/drivers/usb/musb/stm_musb.c b/drivers/usb/musb/stm_musb.c
new file mode 100644
index 00000000000..5ce3c4a75dc
--- /dev/null
+++ b/drivers/usb/musb/stm_musb.c
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2009 STMicroelectronics
+ * Copyright (C) 2009 ST-Ericsson SA
+ *
+ * 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.
+ */
+
+/** @file stm_musb.c
+ * @brief This file contains the USB controller and Phy initialization
+ * with default as interrupt mode was implemented
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mfd/ab8500/ab8500-bm.h>
+#include <mach/stm_musb.h>
+#include <mach/musb_db8500.h>
+#include <mach/prcmu-fw-api.h>
+#include "musb_core.h"
+
+static u8 ulpi_read_register(struct musb *musb, u8 address);
+static u8 ulpi_write_register(struct musb *musb, u8 address, u8 data);
+/* callback argument for AB8500 callback functions */
+static struct musb *musb_status;
+static spinlock_t musb_ulpi_spinlock;
+static unsigned musb_power;
+#ifdef CONFIG_USB_OTG_20
+static int userrequest;
+#endif
+static struct workqueue_struct *stm_usb_power_wq;
+static struct work_struct stm_prcmu_qos;
+static int musb_qos_req;
+
+#define PERI5_CLK_ENABLE 1
+#define PERI5_CLK_DISABLE 0
+
+/**
+ * musb_set_session() - Start the USB session
+ *
+ * This function is used to start the USB sessios in USB host mode
+ * once the A cable is plugged in
+ */
+void musb_set_session(void)
+{
+ u8 val;
+ void __iomem *regs;
+
+ if (musb_status == NULL) {
+ printk(KERN_ERR "Error: devctl session cannot be set\n");
+ return;
+ }
+ regs = musb_status->mregs;
+ val = musb_readb(regs, MUSB_DEVCTL);
+ musb_writeb(regs, MUSB_DEVCTL, val | MUSB_DEVCTL_SESSION);
+}
+EXPORT_SYMBOL(musb_set_session);
+
+
+void stm_prcmu_qos_handler(int value)
+{
+ if (value)
+ musb_qos_req = 100;
+ else
+ musb_qos_req = 50;
+ queue_work(stm_usb_power_wq, &stm_prcmu_qos);
+}
+EXPORT_SYMBOL(stm_prcmu_qos_handler);
+
+static void stm_prcmu_qos_work(struct work_struct *work)
+{
+ if (musb_qos_req == 100) {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ "musb_qos", 100);
+ } else {
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ "musb_qos", 50);
+ prcmu_release_usb_wakeup_state();
+ }
+}
+
+void stm_set_peripheral_clock(int enable)
+{
+ if (enable) {
+ if (musb_status->set_clock)
+ musb_status->set_clock(musb_status->clock, 1);
+ else
+ clk_enable(musb_status->clock);
+ } else {
+ if (musb_status->set_clock)
+ musb_status->set_clock(musb_status->clock, 0);
+ else
+ clk_disable(musb_status->clock);
+ }
+}
+
+#ifdef CONFIG_PM
+void stm_musb_context(int enable)
+{
+ void __iomem *regs;
+
+ if (enable) {
+ stm_set_peripheral_clock(PERI5_CLK_ENABLE);
+ musb_restore_context(musb_status);
+ regs = musb_status->mregs;
+ } else
+ stm_set_peripheral_clock(PERI5_CLK_DISABLE);
+}
+EXPORT_SYMBOL(stm_musb_context);
+#endif
+
+void
+ab8500_bm_usb_state_changed_wrapper(u8 bm_usb_state)
+{
+ if ((bm_usb_state == AB8500_BM_USB_STATE_RESET_HS) ||
+ (bm_usb_state == AB8500_BM_USB_STATE_RESET_FS)) {
+ musb_power = 0;
+ }
+
+ /*
+ * TODO: Instead of using callbacks, we should be using notify
+ * to tell the battery manager when there is s state change
+ */
+ ab8500_charger_usb_state_changed(bm_usb_state, musb_power);
+}
+
+#ifdef CONFIG_USB_OTG_20
+int musb_adp(void)
+{
+ if (userrequest == 0)
+ return 0;
+ else
+ return 1;
+}
+EXPORT_SYMBOL(musb_adp);
+#endif
+
+/* Sys interfaces */
+static struct kobject *usbstatus_kobj;
+static ssize_t usb_cable_status
+ (struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ u8 is_active = 0;
+
+ if (strcmp(attr->name, "cable_connect") == 0) {
+ is_active = musb_status->is_active;
+ sprintf(buf, "%d\n", is_active);
+ }
+ return strlen(buf);
+}
+
+static struct attribute usb_cable_connect_attribute = \
+ {.name = "cable_connect", .mode = S_IRUGO};
+static struct attribute *usb_status[] = {
+ &usb_cable_connect_attribute,
+ NULL
+};
+
+struct sysfs_ops usb_sysfs_ops = {
+ .show = usb_cable_status,
+};
+
+static struct kobj_type ktype_usbstatus = {
+ .sysfs_ops = &usb_sysfs_ops,
+ .default_attrs = usb_status,
+};
+
+/*
+ * A structure was declared as global for timer in USB host mode
+ */
+static struct timer_list notify_timer;
+
+/* TODO: Remove or use!! */
+#ifdef UNUSED_USB_STUFF
+
+/**
+ * ulpi_read_register() - Read the usb register from address writing into ULPI
+ * @musb: struct musb pointer.
+ * @address: address for reading from ULPI register of USB
+ *
+ * This function read the value from the specific address in USB host mode.
+ */
+static u8 ulpi_read_register(struct musb *musb, u8 address)
+{
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags;
+ int count = 200;
+ u8 val;
+
+ spin_lock_irqsave(&musb_ulpi_spinlock, flags);
+
+ /* set ULPI register address */
+ musb_writeb(mbase, OTG_UREGADDR, address);
+
+ /* request a read access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val |= OTG_UREGCTRL_URW;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* perform access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val |= OTG_UREGCTRL_REGREQ;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* wait for completion with a time-out */
+ do {
+ udelay(10);
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ count--;
+ } while (!(val & OTG_UREGCTRL_REGCMP) && (count > 0));
+
+ /* check for time-out */
+ if (!(val & OTG_UREGCTRL_REGCMP) && (count == 0)) {
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+ if (printk_ratelimit())
+ printk(KERN_ALERT "U8500 USB : ULPI read timed out\n");
+ return 0;
+ }
+
+ /* acknowledge completion */
+ val &= ~OTG_UREGCTRL_REGCMP;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* get data */
+ val = musb_readb(mbase, OTG_UREGDATA);
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+
+ return val;
+}
+#endif
+
+/**
+ * ulpi_write_register() - Write to a usb phy's ULPI register
+ * using the Mentor ULPI wrapper functionality
+ * @musb: struct musb pointer.
+ * @address: address of ULPI register
+ * @data: data for ULPI register
+ * This function writes the value given by data to the specific address
+ */
+static u8 ulpi_write_register(struct musb *musb, u8 address, u8 data)
+{
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags;
+ int count = 200;
+ u8 val;
+
+ spin_lock_irqsave(&musb_ulpi_spinlock, flags);
+
+ /* First write to ULPI wrapper registers */
+ /* set ULPI register address */
+ musb_writeb(mbase, OTG_UREGADDR, address);
+
+ /* request a write access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val &= ~OTG_UREGCTRL_URW;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* Write data to ULPI wrapper data register */
+ musb_writeb(mbase, OTG_UREGDATA, data);
+
+ /* perform access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val |= OTG_UREGCTRL_REGREQ;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* wait for completion with a time-out */
+ do {
+ udelay(10);
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ count--;
+ } while (!(val & OTG_UREGCTRL_REGCMP) && (count > 0));
+
+ /* check for time-out */
+ if (!(val & OTG_UREGCTRL_REGCMP) && (count == 0)) {
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+ if (printk_ratelimit())
+ printk(KERN_ALERT "U8500 USB : ULPI write timed out\n");
+ return 0;
+ }
+
+ /* acknowledge completion */
+ val &= ~OTG_UREGCTRL_REGCMP;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+
+ return 0;
+
+}
+
+
+/**
+ * musb_stm_hs_otg_init() - Initialize the USB for paltform specific.
+ * @musb: struct musb pointer.
+ *
+ * This function initialize the USB with the given musb structure information.
+ */
+int __init musb_stm_hs_otg_init(struct musb *musb)
+{
+ u8 val;
+
+ if (musb->clock)
+ clk_enable(musb->clock);
+
+ /* enable ULPI interface */
+ val = musb_readb(musb->mregs, OTG_TOPCTRL);
+ val |= OTG_TOPCTRL_MODE_ULPI;
+ musb_writeb(musb->mregs, OTG_TOPCTRL, val);
+
+ /* do soft reset */
+ val = musb_readb(musb->mregs, 0x7F);
+ val |= 0x2;
+ musb_writeb(musb->mregs, 0x7F, val);
+
+ return 0;
+}
+/**
+ * musb_stm_fs_init() - Initialize the file system for the USB.
+ * @musb: struct musb pointer.
+ *
+ * This function initialize the file system of USB.
+ */
+int __init musb_stm_fs_init(struct musb *musb)
+{
+ return 0;
+}
+/**
+ * musb_platform_enable() - Enable the USB.
+ * @musb: struct musb pointer.
+ *
+ * This function enables the USB.
+ */
+void musb_platform_enable(struct musb *musb)
+{
+}
+/**
+ * musb_platform_disable() - Disable the USB.
+ * @musb: struct musb pointer.
+ *
+ * This function disables the USB.
+ */
+void musb_platform_disable(struct musb *musb)
+{
+}
+
+/**
+ * musb_platform_try_idle() - Check the USB state active or not.
+ * @musb: struct musb pointer.
+ * @timeout: set the timeout to keep the host in idle mode.
+ *
+ * This function keeps the USB host in idle state based on the musb inforamtion.
+ */
+void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
+{
+ if (musb->board_mode != MUSB_PERIPHERAL) {
+ unsigned long default_timeout =
+ jiffies + msecs_to_jiffies(10);
+ static unsigned long last_timer;
+
+ if (timeout == 0)
+ timeout = default_timeout;
+
+ /* Never idle if active, or when VBUS
+ timeout is not set as host */
+ if (musb->is_active || ((musb->a_wait_bcon == 0)
+ && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
+ DBG(4, "%s active, deleting timer\n",
+ otg_state_string(musb));
+ del_timer(&notify_timer);
+ last_timer = jiffies;
+ return;
+ }
+
+ if (time_after(last_timer, timeout)) {
+ if (!timer_pending(&notify_timer))
+ last_timer = timeout;
+ else {
+ DBG(4,
+ "Longer idle timer already pending,ignoring\n");
+ return;
+ }
+ }
+ last_timer = timeout;
+
+ DBG(4, "%s inactive, for idle timer for %lu ms\n",
+ otg_state_string(musb),
+ (unsigned long)jiffies_to_msecs(timeout - jiffies));
+ mod_timer(&notify_timer, timeout);
+ }
+}
+
+/**
+ * set_vbus() - Set the Vbus for the USB.
+ * @musb: struct musb pointer.
+ * @is_on: set Vbus for USB or not.
+ *
+ * This function set the Vbus for USB.
+ */
+static void set_vbus(struct musb *musb, int is_on)
+{
+ u8 devctl;
+ /* HDRC controls CPEN, but beware current surges during device
+ * connect. They can trigger transient overcurrent conditions
+ * that must be ignored.
+ */
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ if (is_on) {
+ musb->is_active = 1;
+ musb->xceiv->default_a = 1;
+ musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ devctl |= MUSB_DEVCTL_SESSION;
+
+ MUSB_HST_MODE(musb);
+ } else {
+ musb->is_active = 0;
+
+ /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
+ * jumping right to B_IDLE...
+ */
+
+ musb->xceiv->default_a = 0;
+ musb->xceiv->state = OTG_STATE_B_IDLE;
+ devctl &= ~MUSB_DEVCTL_SESSION;
+
+ MUSB_DEV_MODE(musb);
+ }
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ DBG(1, "VBUS %s, devctl %02x "
+ /* otg %3x conf %08x prcm %08x */ "\n",
+ otg_state_string(musb),
+ musb_readb(musb->mregs, MUSB_DEVCTL));
+ if (!is_on) {
+ /* Discahrge the VBUS */
+ if (musb_status == NULL)
+ return;
+ ulpi_write_register(musb_status, ULPI_OCTRL, 0x08);
+ }
+}
+/**
+ * set_power() - Set the power for the USB transceiver.
+ * @x: struct usb_transceiver pointer.
+ * @mA: set mA power for USB.
+ *
+ * This function set the power for the USB.
+ */
+static int set_power(struct otg_transceiver *x, unsigned mA)
+{
+ if (mA > 100) {
+ /* AB V2 has eye diagram issues when drawing more
+ * than 100mA from VBUS.So setting charging current
+ * to 100mA in case of standard host
+ */
+ if (musb_get_abx500_rev() < 0x30)
+ mA = 100;
+ else
+ mA = 300;
+ }
+ musb_power = mA;
+ DBG(1, "Set VBUS Power = %d mA\n", mA);
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_CONFIGURED);
+ return 0;
+}
+/**
+ * musb_platform_set_mode() - Set the mode for the USB driver.
+ * @musb: struct musb pointer.
+ * @musb_mode: usb mode.
+ *
+ * This function set the mode for the USB.
+ */
+int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
+{
+ u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ switch (musb_mode) {
+ case MUSB_HOST:
+ otg_set_host(musb->xceiv, musb->xceiv->host);
+ break;
+ case MUSB_PERIPHERAL:
+ otg_set_peripheral(musb->xceiv, musb->xceiv->gadget);
+ break;
+ case MUSB_OTG:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+/**
+ * funct_host_notify_timer() - Initialize the timer for USB host driver.
+ * @data: usb host data.
+ *
+ * This function runs the timer for the USB host mode.
+ */
+static void funct_host_notify_timer(unsigned long data)
+{
+ struct musb *musb = (void *)data;
+ unsigned long flags;
+ u8 devctl;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ switch (musb->xceiv->state) {
+ case OTG_STATE_A_WAIT_BCON:
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE) {
+ musb->xceiv->state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+ } else {
+ musb->xceiv->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ }
+#ifdef CONFIG_PM
+ stm_musb_context(USB_DISABLE);
+#endif
+
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+ DBG(1, "otg_state %s devctl %d\n", otg_state_string(musb), devctl);
+}
+
+/**
+ * musb_platform_init() - Initialize the platform USB driver.
+ * @musb: struct musb pointer.
+ *
+ * This function initialize the USB controller and Phy.
+ */
+int __init musb_platform_init(struct musb *musb, void *board_data)
+{
+ int ret;
+
+ usb_nop_xceiv_register();
+
+ musb->xceiv = otg_get_transceiver();
+ if (!musb->xceiv) {
+ pr_err("U8500 USB : no transceiver configured\n");
+ ret = -ENODEV;
+ goto done;
+ }
+
+ ret = musb_stm_hs_otg_init(musb);
+ if (ret < 0)
+ goto done;
+ if (is_host_enabled(musb))
+ musb->board_set_vbus = set_vbus;
+ if (is_peripheral_enabled(musb))
+ musb->xceiv->set_power = set_power;
+
+ ret = musb_phy_en(musb->board_mode);
+ if (ret < 0)
+ goto done;
+
+ if (musb_status == NULL) {
+ musb_status = musb;
+ spin_lock_init(&musb_ulpi_spinlock);
+ }
+
+ /* Registering usb device for sysfs */
+ usbstatus_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
+
+ if (usbstatus_kobj == NULL)
+ ret = -ENOMEM;
+ usbstatus_kobj->ktype = &ktype_usbstatus;
+ kobject_init(usbstatus_kobj, usbstatus_kobj->ktype);
+
+ ret = kobject_set_name(usbstatus_kobj, "usb_status");
+ if (ret)
+ kfree(usbstatus_kobj);
+
+ ret = kobject_add(usbstatus_kobj, NULL, "usb_status");
+ if (ret)
+ kfree(usbstatus_kobj);
+
+ if (musb->board_mode != MUSB_PERIPHERAL) {
+ init_timer(&notify_timer);
+ notify_timer.expires = jiffies + msecs_to_jiffies(1000);
+ notify_timer.function = funct_host_notify_timer;
+ notify_timer.data = (unsigned long)musb;
+ add_timer(&notify_timer);
+ }
+
+ stm_usb_power_wq = create_singlethread_workqueue(
+ "stm_usb_power_wq");
+ if (stm_usb_power_wq == NULL)
+ return -ENOMEM;
+
+ INIT_WORK(&stm_prcmu_qos, stm_prcmu_qos_work);
+
+ ret = musb_force_detect(musb->board_mode);
+ if (ret < 0)
+ goto done;
+ return 0;
+
+done:
+ usb_nop_xceiv_unregister();
+ return ret;
+}
+/**
+ * musb_platform_exit() - unregister the platform USB driver.
+ * @musb: struct musb pointer.
+ *
+ * This function unregisters the USB controller.
+ */
+int musb_platform_exit(struct musb *musb)
+{
+ musb->clock = 0;
+
+ if (musb->board_mode != MUSB_PERIPHERAL)
+ del_timer_sync(&notify_timer);
+
+ usb_nop_xceiv_unregister();
+
+ musb_status = NULL;
+
+ return 0;
+}
diff --git a/drivers/usb/musb/stm_musb_dma.c b/drivers/usb/musb/stm_musb_dma.c
new file mode 100644
index 00000000000..468e96bdcea
--- /dev/null
+++ b/drivers/usb/musb/stm_musb_dma.c
@@ -0,0 +1,722 @@
+/* Copyright (C) 2009 ST-Ericsson SA
+ * Copyright (C) 2009 STMicroelectronics
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/pfn.h>
+#include "musb_core.h"
+#include "ste_config.h"
+#include "stm_musb_dma.h"
+#include <plat/ste_dma40.h>
+#include <mach/ste-dma40-db8500.h>
+
+/*
+ * U8500 system DMA used for USB can't transfer less
+ * than max packet size of Buldk EP which is 512
+ */
+#define U8500_USB_DMA_MIN_TRANSFER_SIZE 512
+
+/**
+ * dma_controller_start() - creates the logical channels pool and registers callbacks
+ * @c: pointer to DMA Controller
+ *
+ * This function requests the logical channels from the DMA driver and creates
+ * logical channels based on event lines and also registers the callbacks which
+ * are invoked after data transfer in the transmit or receive direction.
+*/
+
+static int dma_controller_start(struct dma_controller *c)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel = NULL;
+ struct stedma40_chan_cfg *info;
+ u8 bit;
+ struct dma_channel *channel = NULL;
+ /*bit 0 for receive and bit 1 for transmit*/
+#ifndef CONFIG_USB_U8500_DMA
+ for (bit = 0; bit < 2; bit++) {
+#else
+ for (bit = 0; bit < (U8500_DMA_END_POINTS*2); bit++) {
+#endif
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ musb_channel = &(controller->channel[bit]);
+ info = kzalloc(sizeof(struct stedma40_chan_cfg), GFP_KERNEL);
+ if (!info) {
+ ERR("could not allocate dma info structure\n");
+ return -1;
+ }
+ musb_channel->info = info;
+ musb_channel->controller = controller;
+#ifdef CONFIG_USB_U8500_DMA
+ info->high_priority = true;
+#else
+ info->mode = STEDMA40_MODE_PHYSICAL;
+ info->high_priority = true;
+#endif
+
+#ifndef CONFIG_USB_U8500_DMA
+ if (bit) {
+#else
+ if ((bit <= TX_CHANNEL_7)) {
+#endif
+ int dst_dev_type;
+
+ info->dir = STEDMA40_MEM_TO_PERIPH;
+ info->src_dev_type = STEDMA40_DEV_SRC_MEMORY;
+
+#ifdef CONFIG_USB_U8500_DMA
+ switch (bit) {
+
+ case TX_CHANNEL_1:
+ dst_dev_type = DB8500_DMA_DEV38_USB_OTG_OEP_1_9;
+ break;
+ case TX_CHANNEL_2:
+ dst_dev_type =
+ DB8500_DMA_DEV37_USB_OTG_OEP_2_10;
+ break;
+ case TX_CHANNEL_3:
+ dst_dev_type =
+ DB8500_DMA_DEV36_USB_OTG_OEP_3_11;
+ break;
+ case TX_CHANNEL_4:
+ dst_dev_type =
+ DB8500_DMA_DEV19_USB_OTG_OEP_4_12;
+ break;
+ case TX_CHANNEL_5:
+ dst_dev_type =
+ DB8500_DMA_DEV18_USB_OTG_OEP_5_13;
+ break;
+ case TX_CHANNEL_6:
+ dst_dev_type =
+ DB8500_DMA_DEV17_USB_OTG_OEP_6_14;
+ break;
+ case TX_CHANNEL_7:
+ dst_dev_type =
+ DB8500_DMA_DEV16_USB_OTG_OEP_7_15;
+ break;
+
+ }
+
+ info->dst_dev_type = dst_dev_type;
+#endif
+
+ } else {
+ int src_dev_type;
+
+ info->dir = STEDMA40_PERIPH_TO_MEM;
+
+#ifdef CONFIG_USB_U8500_DMA
+ switch (bit) {
+ case RX_CHANNEL_1:
+ src_dev_type = DB8500_DMA_DEV38_USB_OTG_IEP_1_9;
+ break;
+ case RX_CHANNEL_2:
+ src_dev_type =
+ DB8500_DMA_DEV37_USB_OTG_IEP_2_10;
+ break;
+ case RX_CHANNEL_3:
+ src_dev_type =
+ DB8500_DMA_DEV36_USB_OTG_IEP_3_11;
+ break;
+ case RX_CHANNEL_4:
+ src_dev_type =
+ DB8500_DMA_DEV19_USB_OTG_IEP_4_12;
+ break;
+ case RX_CHANNEL_5:
+ src_dev_type =
+ DB8500_DMA_DEV18_USB_OTG_IEP_5_13;
+ break;
+ case RX_CHANNEL_6:
+ src_dev_type =
+ DB8500_DMA_DEV17_USB_OTG_IEP_6_14;
+ break;
+ case RX_CHANNEL_7:
+ src_dev_type =
+ DB8500_DMA_DEV16_USB_OTG_IEP_7_15;
+ break;
+ }
+
+ info->src_dev_type = src_dev_type;
+#endif
+ info->dst_dev_type = STEDMA40_DEV_DST_MEMORY;
+ }
+ info->src_info.data_width = STEDMA40_WORD_WIDTH;
+ info->src_info.psize = STEDMA40_PSIZE_LOG_16;
+
+ info->dst_info.data_width = STEDMA40_WORD_WIDTH ;
+ info->dst_info.psize = STEDMA40_PSIZE_LOG_16;
+ musb_channel->is_pipe_allocated = 1;
+ channel = &(musb_channel->channel);
+ channel->private_data = musb_channel;
+ channel->status = MUSB_DMA_STATUS_FREE;
+ channel->max_len = 0x10000;
+ /* Tx => mode 1; Rx => mode 0 */
+ channel->desired_mode = bit;
+ channel->actual_len = 0;
+
+ musb_channel->dma_chan = dma_request_channel(mask,
+ stedma40_filter,
+ info);
+ if (!musb_channel->dma_chan)
+ ERR("dma pipe can't be allocated\n");
+#ifndef CONFIG_USB_U8500_DMA
+ /* Tx => mode 1; Rx => mode 0 */
+ if (bit) {
+#else
+ if ((bit <= TX_CHANNEL_7)) {
+#endif
+ INIT_WORK(&musb_channel->channel_data_tx,
+ musb_channel_work_tx);
+ DBG(2, "channel allocated for TX, %s\n",
+ dma_chan_name(musb_channel->dma_chan));
+ } else {
+ INIT_WORK(&musb_channel->channel_data_rx,
+ musb_channel_work_rx);
+ DBG(2, "channel allocated for RX, %s\n",
+ dma_chan_name(musb_channel->dma_chan));
+ }
+
+ }
+ return 0;
+}
+
+static void dma_channel_release(struct dma_channel *channel);
+
+/**
+ * dma_controller_stop() - releases all the channels and frees the DMA pipes
+ * @c: pointer to DMA controller
+ *
+ * This function frees all of the logical channels and frees the DMA pipes
+*/
+
+static int dma_controller_stop(struct dma_controller *c)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel;
+ struct dma_channel *channel;
+ u8 bit;
+#ifndef CONFIG_USB_U8500_DMA
+ for (bit = 0; bit < 2; bit++) {
+#else
+ for (bit = 0; bit < (U8500_DMA_END_POINTS*2); bit++) {
+#endif
+ channel = &controller->channel[bit].channel;
+ musb_channel = channel->private_data;
+ dma_channel_release(channel);
+ if (musb_channel->info) {
+ dma_release_channel(musb_channel->dma_chan);
+ kfree(musb_channel->info);
+ musb_channel->info = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * dma_controller_allocate() - allocates the DMA channels
+ * @c: pointer to DMA controller
+ * @hw_ep: pointer to endpoint
+ * @transmit: transmit or receive direction
+ *
+ * This function allocates the DMA channel and initializes
+ * the channel
+*/
+
+static struct dma_channel *dma_channel_allocate(struct dma_controller *c,
+ struct musb_hw_ep *hw_ep, u8 transmit)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel = NULL;
+ struct dma_channel *channel = NULL;
+ u8 bit;
+
+#ifndef CONFIG_USB_U8500_DMA
+
+ /*bit 0 for receive and bit 1 for transmit*/
+ for (bit = 0; bit < 2; bit++) {
+ if (!(controller->used_channels & (1 << bit))) {
+
+ if ((transmit && !bit))
+ continue;
+ if ((!transmit && bit))
+ break;
+#else
+ if (hw_ep->epnum > 0
+ && hw_ep->epnum <= U8500_DMA_END_POINTS) {
+ if (transmit)
+ bit = hw_ep->epnum - 1;
+ else
+ bit =
+ hw_ep->epnum + RX_END_POINT_OFFSET;
+ } else
+ return NULL;
+#endif
+ controller->used_channels |= (1 << bit);
+ musb_channel = &(controller->channel[bit]);
+ musb_channel->idx = bit;
+ musb_channel->epnum = hw_ep->epnum;
+ musb_channel->hw_ep = hw_ep;
+ musb_channel->transmit = transmit;
+ musb_channel->is_pipe_allocated = 1;
+ channel = &(musb_channel->channel);
+#ifndef CONFIG_USB_U8500_DMA
+ break;
+ }
+ }
+#endif
+ return channel;
+}
+
+/**
+ * dma_channel_release() - releases the DMA channel
+ * @channel: channel to be released
+ *
+ * This function releases the DMA channel
+ *
+*/
+
+static void dma_channel_release(struct dma_channel *channel)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ channel->actual_len = 0;
+ musb_channel->start_addr = 0;
+ musb_channel->len = 0;
+
+ DBG(2, "enter\n");
+ musb_channel->controller->used_channels &=
+ ~(1 << musb_channel->idx);
+
+ channel->status = MUSB_DMA_STATUS_FREE;
+ if (musb_channel->is_pipe_allocated)
+ musb_channel->is_pipe_allocated = 0;
+ DBG(2, "exit\n");
+}
+
+/**
+ * configure_channel() - configures the source, destination addresses and
+ * starts the transfer
+ * @channel: pointer to DMA channel
+ * @packet_sz: packet size
+ * @mode: Dma mode
+ * @dma_addr: DMA source address for transmit direction
+ * or DMA destination address for receive direction
+ * @len: length
+ * This function configures the source and destination addresses for DMA
+ * operation and initiates the DMA transfer
+*/
+
+static bool configure_channel(struct dma_channel *channel,
+ u16 packet_sz, u8 mode,
+ dma_addr_t dma_addr, u32 len)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ u32 dma_count;
+ struct dma_chan *dma_chan = musb_channel->dma_chan;
+ struct dma_async_tx_descriptor *dma_desc;
+ enum dma_data_direction direction;
+ struct scatterlist sg;
+
+#ifndef CONFIG_USB_U8500_DMA
+ struct musb_qh *qh;
+ struct urb *urb;
+#endif
+ unsigned int usb_fifo_addr =
+ (unsigned int)(MUSB_FIFO_OFFSET(hw_ep->epnum) + mbase);
+
+#ifndef CONFIG_USB_U8500_DMA
+ if (musb_channel->transmit)
+ qh = hw_ep->out_qh;
+ else
+ qh = hw_ep->in_qh;
+ urb = next_urb(qh);
+#endif
+
+ dma_count = len - (len % packet_sz);
+ musb_channel->cur_len = dma_count;
+ usb_fifo_addr =
+ U8500_USBOTG_BASE + ((unsigned int)usb_fifo_addr & 0xFFFF);
+
+#ifndef CONFIG_USB_U8500_DMA
+ if (!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+ if (musb_channel->transmit)
+ dma_addr = musb->tx_dma_phy;
+ else
+ dma_addr = musb->rx_dma_phy;
+ }
+#endif
+
+ stedma40_set_dev_addr(dma_chan, usb_fifo_addr, usb_fifo_addr);
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, pfn_to_page(PFN_DOWN(dma_addr)), dma_count,
+ offset_in_page(dma_addr));
+ sg_dma_address(&sg) = dma_addr;
+ sg_dma_len(&sg) = dma_count;
+
+ direction = musb_channel->transmit ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ dma_desc = dma_chan->device->
+ device_prep_slave_sg(dma_chan, &sg, 1, direction,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!dma_desc)
+ return false;
+
+ dma_desc->callback = musb_channel->transmit ?
+ musb_tx_dma_controller_handler :
+ musb_rx_dma_controller_handler;
+ dma_desc->callback_param = channel;
+ dma_desc->tx_submit(dma_desc);
+ dma_async_issue_pending(dma_chan);
+
+ return true;
+}
+
+/**
+ * dma_channel_program() - Configures the channel and initiates transfer
+ * @channel: pointer to DMA channel
+ * @packet_sz: packet size
+ * @mode: mode
+ * @dma_addr: physical address of memory
+ * @len: length
+ *
+ * This function configures the channel and initiates the DMA transfer
+*/
+
+static int dma_channel_program(struct dma_channel *channel,
+ u16 packet_sz, u8 mode,
+ dma_addr_t dma_addr, u32 len)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ bool ret;
+
+ BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
+ channel->status == MUSB_DMA_STATUS_BUSY);
+ if (len < U8500_USB_DMA_MIN_TRANSFER_SIZE)
+ return false;
+ if (!musb_channel->transmit && len < packet_sz)
+ return false;
+ channel->actual_len = 0;
+ musb_channel->start_addr = dma_addr;
+ musb_channel->len = len;
+ musb_channel->max_packet_sz = packet_sz;
+ channel->status = MUSB_DMA_STATUS_BUSY;
+
+
+ if ((mode == 1) && (len >= packet_sz))
+ ret = configure_channel(channel, packet_sz, 1, dma_addr, len);
+ else
+ ret = configure_channel(channel, packet_sz, 0, dma_addr, len);
+ return ret;
+}
+
+/**
+ * dma_channel_abort() - aborts the DMA transfer
+ * @channel: pointer to DMA channel.
+ *
+ * This function aborts the DMA transfer.
+*/
+
+static int dma_channel_abort(struct dma_channel *channel)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ void __iomem *mbase = musb_channel->controller->base;
+ u16 csr;
+ if (channel->status == MUSB_DMA_STATUS_BUSY) {
+ if (musb_channel->transmit) {
+
+ csr = musb_readw(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum,
+ MUSB_TXCSR));
+ csr &= ~(MUSB_TXCSR_AUTOSET |
+ MUSB_TXCSR_DMAENAB |
+ MUSB_TXCSR_DMAMODE);
+ musb_writew(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum, MUSB_TXCSR),
+ csr);
+ } else {
+ csr = musb_readw(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum,
+ MUSB_RXCSR));
+ csr &= ~(MUSB_RXCSR_AUTOCLEAR |
+ MUSB_RXCSR_DMAENAB |
+ MUSB_RXCSR_DMAMODE);
+ musb_writew(mbase,
+ MUSB_EP_OFFSET(musb_channel->epnum, MUSB_RXCSR),
+ csr);
+ }
+
+ if (musb_channel->is_pipe_allocated) {
+ musb_channel->dma_chan->device->
+ device_control(musb_channel->dma_chan, DMA_TERMINATE_ALL, 0);
+ channel->status = MUSB_DMA_STATUS_FREE;
+ }
+ }
+ return 0;
+}
+
+#include <linux/usb/composite.h>
+
+static int dma_is_compatible(struct dma_channel *channel,
+ struct musb_request *req)
+{
+
+ struct musb_dma_channel *musb_channel = channel->private_data;
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ struct usb_descriptor_header **descriptors;
+ struct usb_function *f;
+
+ struct usb_gadget *gadget = &musb->g;
+
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+
+ list_for_each_entry(f, &cdev->config->functions, list) {
+
+ if (!strcmp(f->name, "cdc_ethernet") ||
+ !strcmp(f->name, "rndis") ||
+ !strcmp(f->name, "adb")) {
+
+ if (gadget->speed == USB_SPEED_HIGH)
+ descriptors = f->hs_descriptors;
+ else
+ descriptors = f->descriptors;
+
+ for (; *descriptors; ++descriptors) {
+ struct usb_endpoint_descriptor *ep;
+
+ if ((*descriptors)->bDescriptorType !=
+ USB_DT_ENDPOINT)
+ continue;
+
+ ep = (struct usb_endpoint_descriptor *)
+ *descriptors;
+
+ if (ep->bEndpointAddress == req->epnum)
+ return 0;
+ }
+
+ }
+ }
+
+ if (req->request.length < U8500_USB_DMA_MIN_TRANSFER_SIZE)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * musb_rx_dma_controller_handler() - callback invoked when the data is received in the receive direction
+ * @private_data: DMA channel
+ *
+ * This callback is invoked when the DMA transfer is completed
+ * in the receive direction.
+*/
+void musb_rx_dma_controller_handler(void *private_data)
+{
+ struct dma_channel *channel = (struct dma_channel *)private_data;
+ struct musb_dma_channel *musb_channel = channel->private_data;
+#ifndef CONFIG_USB_U8500_DMA
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags, pio;
+ unsigned int rxcsr;
+ struct musb_qh *qh = hw_ep->in_qh;
+ struct urb *urb;
+ spin_lock_irqsave(&musb->lock, flags);
+ urb = next_urb(qh);
+ musb_ep_select(mbase, hw_ep->epnum);
+ channel->actual_len = musb_channel->cur_len;
+ pio = musb_channel->len - channel->actual_len;
+ if (!(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+ memcpy(urb->transfer_buffer,
+ (void *)musb->rx_dma_log, channel->actual_len);
+ musb_memcpy(urb->transfer_buffer,
+ (void *)musb->rx_dma_log, channel->actual_len);
+ }
+ if (!pio) {
+ channel->status = MUSB_DMA_STATUS_FREE;
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+#else
+ schedule_work(&musb_channel->channel_data_rx);
+#endif
+}
+
+/**
+ * musb_tx_dma_controller_handler() - callback invoked on the transmit direction DMA data transfer
+ * @private_data: pointer to DMA channel.
+ *
+ * This callback is invoked when the DMA tranfer is completed
+ * in the transmit direction
+*/
+
+void musb_tx_dma_controller_handler(void *private_data)
+{
+ struct dma_channel *channel = (struct dma_channel *)private_data;
+ struct musb_dma_channel *musb_channel = channel->private_data;
+#ifndef CONFIG_USB_U8500_DMA
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags, pio;
+ unsigned int txcsr;
+ struct musb_qh *qh = hw_ep->out_qh;
+ struct urb *urb;
+ spin_lock_irqsave(&musb->lock, flags);
+ musb_ep_select(mbase, hw_ep->epnum);
+ channel->actual_len = musb_channel->cur_len;
+ pio = musb_channel->len - channel->actual_len;
+ if (!pio) {
+ channel->status = MUSB_DMA_STATUS_FREE;
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ }
+ if (pio) {
+ channel->status = MUSB_DMA_STATUS_FREE;
+ urb = next_urb(qh);
+ qh->offset += channel->actual_len;
+ buf = urb->transfer_buffer + qh->offset;
+ musb_write_fifo(hw_ep, pio, buf);
+ qh->segsize = pio;
+ musb_writew(hw_ep->regs, MUSB_TXCSR, MUSB_TXCSR_TXPKTRDY);
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+#else
+ schedule_work(&musb_channel->channel_data_tx);
+#endif
+}
+
+/**
+ * dma_controller_destroy() - deallocates the DMA controller
+ * @c: pointer to dma controller.
+ *
+ * This function deallocates the DMA controller.
+*/
+
+void dma_controller_destroy(struct dma_controller *c)
+{
+ struct musb_dma_controller *controller = container_of(c,
+ struct musb_dma_controller, controller);
+ struct musb_dma_channel *musb_channel = NULL;
+ u8 bit;
+ if (!controller)
+ return;
+ for (bit = 0; bit < MUSB_HSDMA_CHANNELS; bit++)
+ musb_channel = &(controller->channel[bit]);
+ if (controller->irq)
+ free_irq(controller->irq, c);
+
+ kfree(controller);
+}
+
+/**
+ * musb_channel_work_tx() - Invoked by worker thread
+ * @data: worker queue data
+ *
+ * This function is invoked by worker thread when the DMA transfer
+ * is completed in the transmit direction.
+*/
+
+static void musb_channel_work_tx(struct work_struct *data)
+{
+ struct musb_dma_channel *musb_channel = container_of(data,
+ struct musb_dma_channel, channel_data_tx);
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ unsigned long flags;
+ spin_lock_irqsave(&musb->lock, flags);
+ musb_channel->channel.actual_len = musb_channel->cur_len;
+ musb_channel->channel.status = MUSB_DMA_STATUS_FREE;
+ musb_ep_select(musb->mregs, hw_ep->epnum);
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+/**
+ * musb_channel_work_tx() - Invoked by worker thread
+ * @data: worker queue data
+ *
+ * This function is invoked by worker thread when the
+ * DMA transfer is completed in the receive direction.
+*/
+
+static void musb_channel_work_rx(struct work_struct *data)
+{
+ struct musb_dma_channel *musb_channel = container_of(data,
+ struct musb_dma_channel, channel_data_rx);
+ struct musb_hw_ep *hw_ep = musb_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags;
+ spin_lock_irqsave(&musb->lock, flags);
+ musb_channel->channel.actual_len = musb_channel->cur_len;
+ musb_channel->channel.status = MUSB_DMA_STATUS_FREE;
+ musb_ep_select(mbase, hw_ep->epnum);
+ musb_dma_completion(musb, musb_channel->epnum,
+ musb_channel->transmit);
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+/**
+ * dma_controller_create() - creates the dma controller and initializes callbacks
+ *
+ * @musb: pointer to mentor core driver data instance|
+ * @base: base address of musb registers.
+ *
+ * This function creates the DMA controller and initializes the callbacks
+ * that are invoked from the Mentor IP core.
+*/
+
+struct dma_controller *__init
+dma_controller_create(struct musb *musb, void __iomem *base)
+{
+ struct musb_dma_controller *controller;
+
+ controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+ if (!controller)
+ return NULL;
+
+ controller->channel_count = MUSB_HSDMA_CHANNELS;
+ controller->private_data = musb;
+ controller->base = base;
+
+ controller->controller.start = dma_controller_start;
+ controller->controller.stop = dma_controller_stop;
+ controller->controller.channel_alloc = dma_channel_allocate;
+ controller->controller.channel_release = dma_channel_release;
+ controller->controller.channel_program = dma_channel_program;
+ controller->controller.channel_abort = dma_channel_abort;
+ controller->controller.is_compatible = dma_is_compatible;
+
+ return &controller->controller;
+}
diff --git a/drivers/usb/musb/stm_musb_dma.h b/drivers/usb/musb/stm_musb_dma.h
new file mode 100644
index 00000000000..753cf08a882
--- /dev/null
+++ b/drivers/usb/musb/stm_musb_dma.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * Copyright (C) 2009 ST-Ericsson SA
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STM_MUSB_DMA_H__
+#define __STM_MUSB_DMA_H__
+
+#define MUSB_HSDMA_CHANNELS 16
+struct musb_dma_controller;
+struct dma_chan;
+struct stedma40_chan_cfg;
+struct musb_dma_channel {
+ struct dma_channel channel;
+ struct musb_dma_controller *controller;
+ struct stedma40_chan_cfg *info;
+ struct musb_hw_ep *hw_ep;
+ struct work_struct channel_data_tx;
+ struct work_struct channel_data_rx;
+ u32 start_addr;
+ u32 len;
+ u32 is_pipe_allocated;
+ u16 max_packet_sz;
+ u8 idx;
+ struct dma_chan *dma_chan;
+ unsigned int cur_len;
+ u8 epnum;
+ u8 last_xfer;
+ u8 transmit;
+};
+
+struct musb_dma_controller {
+ struct dma_controller controller;
+ struct musb_dma_channel channel[MUSB_HSDMA_CHANNELS];
+ void *private_data;
+ void __iomem *base;
+ u8 channel_count;
+ u8 used_channels;
+ u8 irq;
+};
+void musb_rx_dma_controller_handler(void *private_data);
+void musb_tx_dma_controller_handler(void *private_data);
+static void musb_channel_work_tx(struct work_struct *data);
+static void musb_channel_work_rx(struct work_struct *data);
+#endif
+
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3d94a147172..98121ccf24e 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -17,6 +17,8 @@ source "drivers/gpu/vga/Kconfig"
source "drivers/gpu/drm/Kconfig"
+source "drivers/gpu/mali/Kconfig"
+
config VGASTATE
tristate
default n
@@ -245,6 +247,7 @@ config FB_TILEBLITTING
comment "Frame buffer hardware drivers"
depends on FB
+source "drivers/video/mcde/Kconfig"
config FB_CIRRUS
tristate "Cirrus Logic support"
depends on FB && (ZORRO || PCI)
@@ -304,6 +307,7 @@ config FB_ARMCLCD
inserted into and removed from the running kernel), say M
here and read <file:Documentation/kbuild/modules.txt>. The module
will be called amba-clcd.
+source "drivers/video/b2r2/Kconfig"
choice
@@ -2234,6 +2238,7 @@ source "drivers/video/omap2/Kconfig"
source "drivers/video/backlight/Kconfig"
source "drivers/video/display/Kconfig"
+source "drivers/video/av8100/Kconfig"
if VT
source "drivers/video/console/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index ddc2af2ba45..e16c38f2537 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -126,6 +126,9 @@ obj-$(CONFIG_FB_XILINX) += xilinxfb.o
obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o
obj-$(CONFIG_FB_OMAP) += omap/
obj-y += omap2/
+obj-$(CONFIG_FB_MCDE) += mcde/
+obj-$(CONFIG_AV8100) += av8100/
+obj-y += b2r2/
obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o
obj-$(CONFIG_FB_CARMINE) += carminefb.o
obj-$(CONFIG_FB_MB862XX) += mb862xx/
diff --git a/drivers/video/av8100/Kconfig b/drivers/video/av8100/Kconfig
new file mode 100644
index 00000000000..40b9943aaa9
--- /dev/null
+++ b/drivers/video/av8100/Kconfig
@@ -0,0 +1,48 @@
+config AV8100
+ tristate "AV8100 driver support(HDMI/CVBS)"
+ default n
+ help
+ Please enable this feature if hdmi/tvout driver support is required.
+
+config HDMI_AV8100_DEBUG
+ bool "HDMI and AV8100 debug messages"
+ default n
+ depends on AV8100
+ ---help---
+ Say Y here if you want the HDMI and AV8100 driver to
+ output debug messages.
+
+choice
+ prompt "AV8100 HW trig method"
+ default AV8100_HWTRIG_DSI_TE
+
+config AV8100_HWTRIG_INT
+ bool "AV8100 HW trig on INT"
+ depends on AV8100
+ ---help---
+ If you say Y here AV8100 will use HW triggering
+ from AV8100 INT to MCDE sync0.
+
+config AV8100_HWTRIG_I2SDAT3
+ bool "AV8100 HW trig on I2SDAT3"
+ depends on AV8100
+ ---help---
+ If you say Y here AV8100 will use HW triggering
+ from AV8100 I2SDAT3 to MCDE sync1.
+
+config AV8100_HWTRIG_DSI_TE
+ bool "AV8100 HW trig on DSI"
+ depends on AV8100
+ ---help---
+ If you say Y here AV8100 will use HW triggering
+ using DSI TE polling between AV8100 and MCDE.
+
+config AV8100_HWTRIG_NONE
+ bool "AV8100 SW trig"
+ depends on AV8100
+ ---help---
+ If you say Y here AV8100 will use SW triggering
+ between AV8100 and MCDE.
+
+endchoice
+
diff --git a/drivers/video/av8100/Makefile b/drivers/video/av8100/Makefile
new file mode 100644
index 00000000000..2d3028b18ca
--- /dev/null
+++ b/drivers/video/av8100/Makefile
@@ -0,0 +1,10 @@
+# Make file for compiling and loadable module HDMI
+
+obj-$(CONFIG_AV8100) += av8100.o hdmi.o
+
+ifdef CONFIG_HDMI_AV8100_DEBUG
+EXTRA_CFLAGS += -DDEBUG
+endif
+
+clean-files := av8100.o hdmi.o built-in.o modules.order
+
diff --git a/drivers/video/av8100/av8100.c b/drivers/video/av8100/av8100.c
new file mode 100644
index 00000000000..d43bd8963d0
--- /dev/null
+++ b/drivers/video/av8100/av8100.c
@@ -0,0 +1,3757 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * AV8100 driver
+ *
+ * Author: Per Persson <per.xb.persson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/timer.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <mach/prcmu-fw-api.h>
+
+#include "av8100_regs.h"
+#include <video/av8100.h>
+#include <video/hdmi.h>
+#include "av8100_fw.h"
+
+/* Interrupts */
+#define AV8100_INT_EVENT 0x1
+#define AV8100_PLUGSTARTUP_EVENT 0x4
+
+#define AV8100_PLUGSTARTUP_TIME 100
+
+/* Standby search time */
+#define AV8100_ON_TIME 1 /* 9 ms step */
+#define AV8100_DENC_OFF_TIME 3 /* 275 ms step if > V1. Not used if V1 */
+#define AV8100_HDMI_OFF_TIME 2 /* 140 ms step if V2. 80 ms step if V1 */
+
+/* Command offsets */
+#define AV8100_COMMAND_OFFSET 0x10
+#define AV8100_COMMAND_MAX_LENGTH 0x81
+#define AV8100_CMD_BUF_OFFSET (AV8100_COMMAND_OFFSET + 1)
+#define AV8100_2ND_RET_BYTE_OFFSET (AV8100_COMMAND_OFFSET + 1)
+#define AV8100_CEC_RET_BUF_OFFSET (AV8100_COMMAND_OFFSET + 4)
+#define AV8100_HDCP_RET_BUF_OFFSET (AV8100_COMMAND_OFFSET + 2)
+#define AV8100_EDID_RET_BUF_OFFSET (AV8100_COMMAND_OFFSET + 1)
+#define AV8100_FUSE_CRC_OFFSET (AV8100_COMMAND_OFFSET + 2)
+#define AV8100_FUSE_PRGD_OFFSET (AV8100_COMMAND_OFFSET + 3)
+#define AV8100_CRC32_OFFSET (AV8100_COMMAND_OFFSET + 2)
+#define AV8100_CEC_ADDR_OFFSET (AV8100_COMMAND_OFFSET + 3)
+
+/* Tearing effect line numbers */
+#define AV8100_TE_LINE_NB_14 14
+#define AV8100_TE_LINE_NB_17 17
+#define AV8100_TE_LINE_NB_18 18
+#define AV8100_TE_LINE_NB_21 21
+#define AV8100_TE_LINE_NB_22 22
+#define AV8100_TE_LINE_NB_24 24
+#define AV8100_TE_LINE_NB_25 25
+#define AV8100_TE_LINE_NB_26 26
+#define AV8100_TE_LINE_NB_29 29
+#define AV8100_TE_LINE_NB_30 30
+#define AV8100_TE_LINE_NB_32 32
+#define AV8100_TE_LINE_NB_38 38
+#define AV8100_TE_LINE_NB_40 40
+#define AV8100_UI_X4_DEFAULT 6
+
+#define HDMI_REQUEST_FOR_REVOCATION_LIST_INPUT 2
+#define HDMI_CEC_MESSAGE_WRITE_BUFFER_SIZE 16
+#define HDMI_HDCP_SEND_KEY_SIZE 7
+#define HDMI_INFOFRAME_DATA_SIZE 28
+#define HDMI_FUSE_AES_KEY_SIZE 16
+#define HDMI_FUSE_AES_KEY_RET_SIZE 2
+#define HDMI_LOADAES_END_BLK_NR 145
+#define HDMI_CRC32_SIZE 4
+#define HDMI_REVOC_LIST_SIZE 30
+#define HDMI_EDIDREAD_SIZE 0x7F
+
+#define HPDS_INVALID 0xF
+#define CPDS_INVALID 0xF
+#define CECRX_INVALID 0xF
+
+#define REG_16_8_LSB(p) ((u8)(p & 0xFF))
+#define REG_16_8_MSB(p) ((u8)((p & 0xFF00)>>8))
+#define REG_32_8_MSB(p) ((u8)((p & 0xFF000000)>>24))
+#define REG_32_8_MMSB(p) ((u8)((p & 0x00FF0000)>>16))
+#define REG_32_8_MLSB(p) ((u8)((p & 0x0000FF00)>>8))
+#define REG_32_8_LSB(p) ((u8)(p & 0x000000FF))
+#define REG_10_8_MSB(p) ((u8)((p & 0x300)>>8))
+#define REG_12_8_MSB(p) ((u8)((p & 0xf00)>>8))
+
+#define AV8100_WAITTIME_1MS 1
+#define AV8100_WAITTIME_5MS 5
+#define AV8100_WAITTIME_10MS 10
+#define AV8100_WAITTIME_50MS 50
+#define AV8100_WATTIME_100US 100
+
+/* Master clock timing */
+#define MCLK_RNG_17_22 0 /* Use with 19.2 MHZ clock */
+#define MCLK_RNG_22_27 1
+#define MCLK_RNG_27_33 2
+#define MCLK_RNG_31_38 3 /* Use with 38.4 MHZ clock */
+#define MCLK_RNG_36_44 4
+#define MCLK_RNG_40_49 5
+#define MCLK_RNG_45_55 6
+#define MCLK_RNG_49_60 7
+#define MCLK_RNG_54_66 8
+#define MCLK_RNG_58_71 9
+#define MCLK_RNG_63_77 10
+#define MCLK_RNG_67_82 11
+#define MCLK_RNG_72_88 12
+#define MCLK_RNG_76_93 13
+#define MCLK_RNG_85_104 14
+#define MCLK_RNG_94_115 15
+
+DEFINE_MUTEX(av8100_hw_mutex);
+#define LOCK_AV8100_HW mutex_lock(&av8100_hw_mutex)
+#define UNLOCK_AV8100_HW mutex_unlock(&av8100_hw_mutex)
+
+struct color_conversion_cmd {
+ unsigned short c0;
+ unsigned short c1;
+ unsigned short c2;
+ unsigned short c3;
+ unsigned short c4;
+ unsigned short c5;
+ unsigned short c6;
+ unsigned short c7;
+ unsigned short c8;
+ unsigned short aoffset;
+ unsigned short boffset;
+ unsigned short coffset;
+ unsigned char lmax;
+ unsigned char lmin;
+ unsigned char cmax;
+ unsigned char cmin;
+};
+
+struct av8100_config_t {
+ struct i2c_client *client;
+ struct i2c_device_id *id;
+ struct av8100_video_input_format_cmd hdmi_video_input_cmd;
+ struct av8100_audio_input_format_cmd hdmi_audio_input_cmd;
+ struct av8100_video_output_format_cmd hdmi_video_output_cmd;
+ struct av8100_video_scaling_format_cmd hdmi_video_scaling_cmd;
+ enum av8100_color_transform color_transform;
+ struct av8100_cec_message_write_format_cmd
+ hdmi_cec_message_write_cmd;
+ struct av8100_cec_message_read_back_format_cmd
+ hdmi_cec_message_read_back_cmd;
+ struct av8100_denc_format_cmd hdmi_denc_cmd;
+ struct av8100_hdmi_cmd hdmi_cmd;
+ struct av8100_hdcp_send_key_format_cmd hdmi_hdcp_send_key_cmd;
+ struct av8100_hdcp_management_format_cmd
+ hdmi_hdcp_management_format_cmd;
+ struct av8100_infoframes_format_cmd hdmi_infoframes_cmd;
+ struct av8100_edid_section_readback_format_cmd
+ hdmi_edid_section_readback_cmd;
+ struct av8100_pattern_generator_format_cmd hdmi_pattern_generator_cmd;
+ struct av8100_fuse_aes_key_format_cmd hdmi_fuse_aes_key_cmd;
+};
+
+enum av8100_plug_state {
+ AV8100_UNPLUGGED,
+ AV8100_PLUGGED_STARTUP,
+ AV8100_PLUGGED
+};
+
+struct av8100_globals_t {
+ int denc_off_time;/* 5 volt time */
+ int hdmi_off_time;/* 5 volt time */
+ int on_time;/* 5 volt time */
+ u8 hpdm;/*stby_int_mask*/
+ u8 cpdm;/*stby_int_mask*/
+ u8 cecm;/*gen_int_mask*/
+ u8 hdcpm;/*gen_int_mask*/
+ void (*hdmi_ev_cb)(enum av8100_hdmi_event);
+ enum av8100_plug_state plug_state;
+ struct clk *inputclk;
+ bool inputclk_requested;
+ bool opp_requested;
+ struct regulator *regulator_pwr;
+ bool regulator_requested;
+};
+
+/**
+ * struct av8100_cea - CEA(consumer electronic access) standard structure
+ * @cea_id:
+ * @cea_nb:
+ * @vtotale:
+ **/
+
+struct av8100_cea {
+ char cea_id[40];
+ int cea_nb;
+ int vtotale;
+ int vactive;
+ int vsbp;
+ int vslen;
+ int vsfp;
+ char vpol[5];
+ int htotale;
+ int hactive;
+ int hbp;
+ int hslen;
+ int hfp;
+ int frequence;
+ char hpol[5];
+ int reg_line_duration;
+ int blkoel_duration;
+ int uix4;
+ int pll_mult;
+ int pll_div;
+};
+
+enum av8100_command_size {
+ AV8100_COMMAND_VIDEO_INPUT_FORMAT_SIZE = 0x17,
+ AV8100_COMMAND_AUDIO_INPUT_FORMAT_SIZE = 0x8,
+ AV8100_COMMAND_VIDEO_OUTPUT_FORMAT_SIZE = 0x1E,
+ AV8100_COMMAND_VIDEO_SCALING_FORMAT_SIZE = 0x11,
+ AV8100_COMMAND_COLORSPACECONVERSION_SIZE = 0x1D,
+ AV8100_COMMAND_CEC_MESSAGE_WRITE_SIZE = 0x12,
+ AV8100_COMMAND_CEC_MESSAGE_READ_BACK_SIZE = 0x1,
+ AV8100_COMMAND_DENC_SIZE = 0x6,
+ AV8100_COMMAND_HDMI_SIZE = 0x4,
+ AV8100_COMMAND_HDCP_SENDKEY_SIZE = 0xA,
+ AV8100_COMMAND_HDCP_MANAGEMENT_SIZE = 0x4,
+ AV8100_COMMAND_INFOFRAMES_SIZE = 0x21,
+ AV8100_COMMAND_EDID_SECTION_READBACK_SIZE = 0x3,
+ AV8100_COMMAND_PATTERNGENERATOR_SIZE = 0x4,
+ AV8100_COMMAND_FUSE_AES_KEY_SIZE = 0x12,
+ AV8100_COMMAND_FUSE_AES_CHK_SIZE = 0x2,
+};
+
+static unsigned int waittime_retry[10] = {1, 2, 4, 6, 8, 10, 10, 10, 10, 10};
+
+static void clr_plug_status(enum av8100_plugin_status status);
+static void set_plug_status(enum av8100_plugin_status status);
+static void cec_rx(void);
+static void cec_txerr(void);
+static void hdcp_changed(void);
+static const struct color_conversion_cmd *get_color_transform_cmd(
+ enum av8100_color_transform transform);
+static int av8100_open(struct inode *inode, struct file *filp);
+static int av8100_release(struct inode *inode, struct file *filp);
+static int av8100_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int __devinit av8100_probe(struct i2c_client *i2cClient,
+ const struct i2c_device_id *id);
+static int __devexit av8100_remove(struct i2c_client *i2cClient);
+
+static struct av8100_config_t *av8100_config;
+static struct av8100_status g_av8100_status = {0};
+static struct timer_list av8100_plugtimer;
+static wait_queue_head_t av8100_event;
+static int av8100_flag = 0x0;
+static struct av8100_globals_t *av8100_globals;
+static u8 chip_version;
+static char av8100_receivetab[AV8100_FW_SIZE];
+struct device *av8100dev;
+
+static const struct file_operations av8100_fops = {
+ .owner = THIS_MODULE,
+ .open = av8100_open,
+ .release = av8100_release,
+ .ioctl = av8100_ioctl
+};
+
+static struct miscdevice av8100_miscdev = {
+ MISC_DYNAMIC_MINOR,
+ "av8100",
+ &av8100_fops
+};
+
+struct av8100_cea av8100_all_cea[29] = {
+/* cea id
+ * cea_nr vtot vact vsbpp vslen
+ * vsfp vpol htot hact hbp hslen hfp freq
+ * hpol rld bd uix4 pm pd */
+{ "0 CUSTOM ",
+ 0, 0, 0, 0, 0,
+ 0, "-", 800, 640, 16, 96, 10, 25200000,
+ "-", 0, 0, 0, 0, 0},/*Settings to be defined*/
+{ "1 CEA 1 VESA 4 640x480p @ 60 Hz ",
+ 1, 525, 480, 33, 2,
+ 10, "-", 800, 640, 49, 290, 146, 25200000,
+ "-", 2438, 1270, 6, 32, 1},/*RGB888*/
+{ "2 CEA 2 - 3 720x480p @ 60 Hz 4:3 ",
+ 2, 525, 480, 30, 6,
+ 9, "-", 858, 720, 34, 130, 128, 27027000,
+ "-", 1828, 0x3C0, 8, 24, 1},/*RGB565*/
+{ "3 CEA 4 1280x720p @ 60 Hz ",
+ 4, 750, 720, 20, 5,
+ 5, "+", 1650, 1280, 114, 39, 228, 74250000,
+ "+", 1706, 164, 6, 32, 1},/*RGB565*/
+{ "4 CEA 5 1920x1080i @ 60 Hz ",
+ 5, 1125, 540, 20, 5,
+ 0, "+", 2200, 1920, 88, 44, 10, 74250000,
+ "+", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "5 CEA 6-7 480i (NTSC) ",
+ 6, 525, 240, 44, 5,
+ 0, "-", 858, 720, 12, 64, 10, 13513513,
+ "-", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "6 CEA 14-15 480p @ 60 Hz ",
+ 14, 525, 480, 44, 5,
+ 0, "-", 858, 720, 12, 64, 10, 27027000,
+ "-", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "7 CEA 16 1920x1080p @ 60 Hz ",
+ 16, 1125, 1080, 36, 5,
+ 0, "+", 1980, 1280, 440, 40, 10, 133650000,
+ "+", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "8 CEA 17-18 720x576p @ 50 Hz ",
+ 17, 625, 576, 44, 5,
+ 0, "-", 864, 720, 12, 64, 10, 27000000,
+ "-", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "9 CEA 19 1280x720p @ 50 Hz ",
+ 19, 750, 720, 25, 5,
+ 0, "+", 1980, 1280, 440, 40, 10, 74250000,
+ "+", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "10 CEA 20 1920 x 1080i @ 50 Hz ",
+ 20, 1125, 540, 20, 5,
+ 0, "+", 2640, 1920, 528, 44, 10, 74250000,
+ "+", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "11 CEA 21-22 576i (PAL) ",
+ 21, 625, 288, 44, 5,
+ 0, "-", 1728, 1440, 12, 64, 10, 27000000,
+ "-", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "12 CEA 29/30 576p ",
+ 29, 625, 576, 44, 5,
+ 0, "-", 864, 720, 12, 64, 10, 27000000,
+ "-", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "13 CEA 31 1080p 50Hz ",
+ 31, 1125, 1080, 44, 5,
+ 0, "-", 2640, 1920, 12, 64, 10, 148500000,
+ "-", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "14 CEA 32 1920x1080p @ 24 Hz ",
+ 32, 1125, 1080, 36, 5,
+ 4, "+", 2750, 1920, 660, 44, 153, 74250000,
+ "+", 2844, 0x530, 6, 32, 1},/*RGB565*/
+{ "15 CEA 33 1920x1080p @ 25 Hz ",
+ 33, 1125, 1080, 36, 5,
+ 4, "+", 2640, 1920, 528, 44, 10, 74250000,
+ "+", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "16 CEA 34 1920x1080p @ 30Hz ",
+ 34, 1125, 1080, 36, 5,
+ 4, "+", 2200, 1920, 91, 44, 153, 74250000,
+ "+", 2275, 0xAB, 6, 32, 1},/*RGB565*/
+{ "17 CEA 60 1280x720p @ 24 Hz ",
+ 60, 750, 720, 20, 5,
+ 5, "+", 3300, 1280, 284, 50, 2276, 59400000,
+ "+", 4266, 0xAD0, 5, 32, 1},/*RGB565*/
+{ "18 CEA 61 1280x720p @ 25 Hz ",
+ 61, 750, 720, 20, 5,
+ 5, "+", 3960, 1280, 228, 39, 2503, 74250000,
+ "+", 4096, 0x500, 5, 32, 1},/*RGB565*/
+{ "19 CEA 62 1280x720p @ 30 Hz ",
+ 62, 750, 720, 20, 5,
+ 5, "+", 3300, 1280, 228, 39, 1820, 74250000,
+ "+", 3413, 0x770, 5, 32, 1},/*RGB565*/
+{ "20 VESA 9 800x600 @ 60 Hz ",
+ 109, 628, 600, 28, 4,
+ 0, "+", 1056, 800, 40, 128, 10, 40000000,
+ "+", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "21 VESA 14 848x480 @ 60 Hz ",
+ 114, 517, 480, 20, 5,
+ 0, "+", 1088, 848, 24, 80, 10, 33750000,
+ "-", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "22 VESA 16 1024x768 @ 60 Hz ",
+ 116, 806, 768, 38, 6,
+ 0, "-", 1344, 1024, 24, 135, 10, 65000000,
+ "-", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "23 VESA 22 1280x768 @ 60 Hz ",
+ 122, 790, 768, 34, 4,
+ 0, "+", 1440, 1280, 48, 160, 10, 68250000,
+ "-", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "24 VESA 23 1280x768 @ 60 Hz ",
+ 123, 798, 768, 30, 7,
+ 0, "+", 1664, 1280, 64, 128, 10, 79500000,
+ "-", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "25 VESA 27 1280x800 @ 60 Hz ",
+ 127, 823, 800, 23, 6,
+ 0, "+", 1440, 1280, 48, 32, 10, 71000000,
+ "+", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "26 VESA 28 1280x800 @ 60 Hz ",
+ 128, 831, 800, 31, 6,
+ 0, "+", 1680, 1280, 72, 128, 10, 83500000,
+ "-", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "27 VESA 39 1360x768 @ 60 Hz ",
+ 139, 795, 768, 22, 5,
+ 0, "-", 1792, 1360, 48, 32, 10, 85500000,
+ "+", 0, 0, 0, 0, 0},/*Settings to be define*/
+{ "28 VESA 81 1366x768 @ 60 Hz ",
+ 181, 798, 768, 30, 5,
+ 0, "+", 1792, 1366, 72, 136, 10, 85750000,
+ "-", 0, 0, 0, 0, 0} /*Settings to be define*/
+};
+
+const struct color_conversion_cmd col_trans_identity = {
+ .c0 = 0x0100, .c1 = 0x0000, .c2 = 0x0000,
+ .c3 = 0x0000, .c4 = 0x0100, .c5 = 0x0000,
+ .c6 = 0x0000, .c7 = 0x0000, .c8 = 0x0100,
+ .aoffset = 0x0000, .boffset = 0x0000, .coffset = 0x0000,
+ .lmax = 0xff,
+ .lmin = 0x00,
+ .cmax = 0xff,
+ .cmin = 0x00,
+};
+
+const struct color_conversion_cmd col_trans_identity_clamp_yuv = {
+ .c0 = 0x0100, .c1 = 0x0000, .c2 = 0x0000,
+ .c3 = 0x0000, .c4 = 0x0100, .c5 = 0x0000,
+ .c6 = 0x0000, .c7 = 0x0000, .c8 = 0x0100,
+ .aoffset = 0x0000, .boffset = 0x0000, .coffset = 0x0000,
+ .lmax = 0xeb,
+ .lmin = 0x10,
+ .cmax = 0xf0,
+ .cmin = 0x10,
+};
+
+const struct color_conversion_cmd col_trans_yuv_to_rgb_v1 = {
+ .c0 = 0x0087, .c1 = 0x0000, .c2 = 0x00ba,
+ .c3 = 0x0087, .c4 = 0xffd3, .c5 = 0xffa1,
+ .c6 = 0x0087, .c7 = 0x00eb, .c8 = 0x0000,
+ .aoffset = 0xffab, .boffset = 0x004e, .coffset = 0xff92,
+ .lmax = 0xff,
+ .lmin = 0x00,
+ .cmax = 0xff,
+ .cmin = 0x00,
+};
+
+const struct color_conversion_cmd col_trans_yuv_to_rgb_v2 = {
+ .c0 = 0x0198, .c1 = 0x012a, .c2 = 0x0000,
+ .c3 = 0xff30, .c4 = 0x012a, .c5 = 0xff9c,
+ .c6 = 0x0000, .c7 = 0x012a, .c8 = 0x0204,
+ .aoffset = 0xff21, .boffset = 0x0088, .coffset = 0xfeeb,
+ .lmax = 0xff,
+ .lmin = 0x00,
+ .cmax = 0xff,
+ .cmin = 0x00,
+};
+
+const struct color_conversion_cmd col_trans_yuv_to_denc = {
+ .c0 = 0x0100, .c1 = 0x0000, .c2 = 0x0000,
+ .c3 = 0x0000, .c4 = 0x0100, .c5 = 0x0000,
+ .c6 = 0x0000, .c7 = 0x0000, .c8 = 0x0100,
+ .aoffset = 0x0000, .boffset = 0x0000, .coffset = 0x0000,
+ .lmax = 0xeb,
+ .lmin = 0x10,
+ .cmax = 0xf0,
+ .cmin = 0x10,
+};
+
+const struct color_conversion_cmd col_trans_rgb_to_denc = {
+ .c0 = 0x0070, .c1 = 0xffb6, .c2 = 0xffda,
+ .c3 = 0x0019, .c4 = 0x0081, .c5 = 0x0042,
+ .c6 = 0xffee, .c7 = 0xffa2, .c8 = 0x0070,
+ .aoffset = 0x007f, .boffset = 0x0010, .coffset = 0x007f,
+ .lmax = 0xff,
+ .lmin = 0x00,
+ .cmax = 0xff,
+ .cmin = 0x00,
+};
+
+static const struct i2c_device_id av8100_id[] = {
+ { "av8100", 0 },
+ { }
+};
+
+static struct i2c_driver av8100_driver = {
+ .probe = av8100_probe,
+ .remove = av8100_remove,
+ .driver = {
+ .name = "av8100",
+ },
+ .id_table = av8100_id,
+};
+
+static void av8100_plugtimer_int(unsigned long value)
+{
+ av8100_flag |= AV8100_PLUGSTARTUP_EVENT;
+ wake_up_interruptible(&av8100_event);
+ del_timer(&av8100_plugtimer);
+}
+
+static int av8100_int_event_handle(void)
+{
+ u8 hpdi = 0;
+ u8 cpdi = 0;
+ u8 hdcpi = 0;
+ u8 ceci = 0;
+ u8 hpds = 0;
+ u8 cpds = 0;
+ u8 hdcps = 0;
+ u8 cectxerr = 0;
+ u8 cecrx = 0;
+ u8 cectx = 0;
+
+ if (!av8100_globals)
+ return -EINVAL;
+
+ /* STANDBY_PENDING_INTERRUPT reg */
+ if (av8100_reg_stby_pend_int_r(&hpdi, &cpdi, NULL, NULL)) {
+ dev_dbg(av8100dev, "av8100_reg_stby_pend_int_r failed\n");
+ goto av8100_int_event_handle_1;
+ }
+
+ /* Plug event */
+ if (hpdi | cpdi) {
+ /* Clear pending interrupts */
+ (void)av8100_reg_stby_pend_int_w(1, 1, 1, 0);
+
+ /* STANDBY reg */
+ if (av8100_reg_stby_r(NULL, NULL, &hpds, &cpds, NULL)) {
+ dev_dbg(av8100dev, "av8100_reg_stby_r failed\n");
+ goto av8100_int_event_handle_1;
+ }
+ }
+
+ if (cpdi & av8100_globals->cpdm) {
+ /* TVout plugin change */
+ if (cpds) {
+ dev_dbg(av8100dev, "cpds 1\n");
+ set_plug_status(AV8100_CVBS_PLUGIN);
+ } else {
+ dev_dbg(av8100dev, "cpds 0\n");
+ clr_plug_status(AV8100_CVBS_PLUGIN);
+ }
+ }
+
+ if (hpdi & av8100_globals->hpdm) {
+ /* HDMI plugin change */
+ if (hpds) {
+ dev_dbg(av8100dev, "hpds 1\n");
+ set_plug_status(AV8100_HDMI_PLUGIN);
+ } else {
+ dev_dbg(av8100dev, "hpds 0\n");
+ clr_plug_status(AV8100_HDMI_PLUGIN);
+ }
+ }
+
+av8100_int_event_handle_1:
+ /* GENERAL_INTERRUPT reg */
+ if (av8100_reg_gen_int_r(NULL, NULL, NULL, &ceci,
+ &hdcpi, NULL, NULL)) {
+ dev_dbg(av8100dev, "av8100_reg_stby_pend_int_r failed\n");
+ return -EINVAL;
+ }
+
+ /* CEC or HDCP event */
+ if (ceci | hdcpi) {
+ /* Clear pending interrupts */
+ av8100_reg_gen_int_w(1, 1, 1, 1, 1, 1);
+
+ /* GENERAL_STATUS reg */
+ if (av8100_reg_gen_status_r(&cectxerr, &cecrx, &cectx, NULL,
+ NULL, &hdcps) != 0) {
+ dev_dbg(av8100dev, "av8100_reg_gen_status_r fail\n");
+ return -EINVAL;
+ }
+ }
+
+ /* CEC received */
+ if (ceci && cecrx) {
+ dev_dbg(av8100dev, "cecrx\n");
+ /* Report CEC event */
+ cec_rx();
+ }
+
+ /* CEC tx error */
+ if (ceci && cectx && cectxerr) {
+ dev_dbg(av8100dev, "cectxerr\n");
+ /* Report CEC tx error event */
+ cec_txerr();
+ }
+
+ /* HDCP event */
+ if (hdcpi) {
+ dev_dbg(av8100dev, "hdcpch:%0x\n", hdcps);
+ /* Report HDCP status change event */
+ hdcp_changed();
+ }
+
+ return 0;
+}
+
+static int av8100_plugstartup_event_handle(void)
+{
+ u8 hpds = 0;
+ u8 cpds = 0;
+
+ if (!av8100_globals)
+ return -EINVAL;
+
+ switch (av8100_globals->plug_state) {
+ case AV8100_UNPLUGGED:
+ case AV8100_PLUGGED:
+ default:
+ break;
+
+ case AV8100_PLUGGED_STARTUP:
+ /* Unmask interrupt */
+ av8100_globals->hpdm = AV8100_STANDBY_INTERRUPT_MASK_HPDM_HIGH;
+ if (av8100_reg_stby_int_mask_w(av8100_globals->hpdm,
+ av8100_globals->cpdm,
+ AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_INPUT,
+ AV8100_STANDBY_INTERRUPT_MASK_IPOL_LOW)) {
+ dev_dbg(av8100dev,
+ "av8100_reg_stby_int_mask_w fail\n");
+ }
+
+ /* Get actual plug status */
+ if (av8100_reg_stby_r(NULL, NULL, &hpds, &cpds, NULL))
+ dev_dbg(av8100dev, "av8100_reg_stby_r fail\n");
+
+ /* Set plugstate */
+ if (hpds) {
+ av8100_globals->plug_state = AV8100_PLUGGED;
+ dev_dbg(av8100dev, "plug_state:2\n");
+ } else {
+ av8100_globals->plug_state = AV8100_UNPLUGGED;
+ dev_dbg(av8100dev, "plug_state:0\n");
+
+ if (av8100_globals->hdmi_ev_cb)
+ av8100_globals->hdmi_ev_cb(
+ AV8100_HDMI_EVENT_HDMI_PLUGOUT);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int av8100_thread(void *p)
+{
+ u8 flags;
+
+ while (1) {
+ wait_event_interruptible(av8100_event, (av8100_flag != 0));
+ flags = av8100_flag;
+ av8100_flag = 0;
+
+ if (!av8100_globals ||
+ g_av8100_status.av8100_state < AV8100_OPMODE_STANDBY)
+ continue;
+
+ if (flags & AV8100_INT_EVENT)
+ (void)av8100_int_event_handle();
+
+ if (flags & AV8100_PLUGSTARTUP_EVENT)
+ (void)av8100_plugstartup_event_handle();
+ }
+
+ return 0;
+}
+
+static irqreturn_t av8100_intr_handler(int irq, void *p)
+{
+ av8100_flag |= AV8100_INT_EVENT;
+ wake_up_interruptible(&av8100_event);
+ return IRQ_HANDLED;
+}
+
+static u16 av8100_get_te_line_nb(
+ enum av8100_output_CEA_VESA output_video_format)
+{
+ u16 retval;
+
+ switch (output_video_format) {
+ case AV8100_CEA1_640X480P_59_94HZ:
+ case AV8100_CEA2_3_720X480P_59_94HZ:
+ case AV8100_VESA16_1024X768P_60HZ:
+ retval = AV8100_TE_LINE_NB_30;
+ break;
+
+ case AV8100_CEA4_1280X720P_60HZ:
+ case AV8100_CEA60_1280X720P_24HZ:
+ case AV8100_CEA61_1280X720P_25HZ:
+ case AV8100_CEA62_1280X720P_30HZ:
+ retval = AV8100_TE_LINE_NB_21;
+ break;
+
+ case AV8100_CEA5_1920X1080I_60HZ:
+ case AV8100_CEA6_7_NTSC_60HZ:
+ case AV8100_CEA20_1920X1080I_50HZ:
+ case AV8100_CEA21_22_576I_PAL_50HZ:
+ case AV8100_VESA27_1280X800P_59_91HZ:
+ retval = AV8100_TE_LINE_NB_18;
+ break;
+
+ case AV8100_CEA14_15_480p_60HZ:
+ retval = AV8100_TE_LINE_NB_32;
+ break;
+
+ case AV8100_CEA17_18_720X576P_50HZ:
+ case AV8100_CEA29_30_576P_50HZ:
+ retval = AV8100_TE_LINE_NB_40;
+ break;
+
+ case AV8100_CEA19_1280X720P_50HZ:
+ case AV8100_VESA39_1360X768P_60_02HZ:
+ retval = AV8100_TE_LINE_NB_22;
+ break;
+
+ case AV8100_CEA32_1920X1080P_24HZ:
+ case AV8100_CEA33_1920X1080P_25HZ:
+ case AV8100_CEA34_1920X1080P_30HZ:
+ retval = AV8100_TE_LINE_NB_38;
+ break;
+
+ case AV8100_VESA9_800X600P_60_32HZ:
+ retval = AV8100_TE_LINE_NB_24;
+ break;
+
+ case AV8100_VESA14_848X480P_60HZ:
+ retval = AV8100_TE_LINE_NB_29;
+ break;
+
+ case AV8100_VESA22_1280X768P_59_99HZ:
+ retval = AV8100_TE_LINE_NB_17;
+ break;
+
+ case AV8100_VESA23_1280X768P_59_87HZ:
+ case AV8100_VESA81_1366X768P_59_79HZ:
+ retval = AV8100_TE_LINE_NB_25;
+ break;
+
+ case AV8100_VESA28_1280X800P_59_81HZ:
+ retval = AV8100_TE_LINE_NB_26;
+ break;
+
+ case AV8100_CEA16_1920X1080P_60HZ:
+ case AV8100_CEA31_1920x1080P_50Hz:
+ default:
+ /* TODO */
+ retval = AV8100_TE_LINE_NB_38;
+ break;
+ }
+
+ return retval;
+}
+
+static u16 av8100_get_ui_x4(
+ enum av8100_output_CEA_VESA output_video_format)
+{
+ return AV8100_UI_X4_DEFAULT;
+}
+
+static int av8100_config_video_output_dep(enum av8100_output_CEA_VESA
+ output_format)
+{
+ int retval;
+ union av8100_configuration config;
+
+ /* video input */
+ config.video_input_format.dsi_input_mode =
+ AV8100_HDMI_DSI_COMMAND_MODE;
+ config.video_input_format.input_pixel_format = AV8100_INPUT_PIX_RGB565;
+ config.video_input_format.total_horizontal_pixel =
+ av8100_all_cea[output_format].htotale;
+ config.video_input_format.total_horizontal_active_pixel =
+ av8100_all_cea[output_format].hactive;
+ config.video_input_format.total_vertical_lines =
+ av8100_all_cea[output_format].vtotale;
+ config.video_input_format.total_vertical_active_lines =
+ av8100_all_cea[output_format].vactive;
+
+ switch (output_format) {
+ case AV8100_CEA5_1920X1080I_60HZ:
+ case AV8100_CEA20_1920X1080I_50HZ:
+ case AV8100_CEA21_22_576I_PAL_50HZ:
+ case AV8100_CEA6_7_NTSC_60HZ:
+ config.video_input_format.video_mode =
+ AV8100_VIDEO_INTERLACE;
+ break;
+
+ default:
+ config.video_input_format.video_mode =
+ AV8100_VIDEO_PROGRESSIVE;
+ break;
+ }
+
+ config.video_input_format.nb_data_lane =
+ AV8100_DATA_LANES_USED_2;
+ config.video_input_format.nb_virtual_ch_command_mode = 0;
+ config.video_input_format.nb_virtual_ch_video_mode = 0;
+ config.video_input_format.ui_x4 = av8100_get_ui_x4(output_format);
+ config.video_input_format.TE_line_nb = av8100_get_te_line_nb(
+ output_format);
+ config.video_input_format.TE_config = AV8100_TE_OFF;
+ config.video_input_format.master_clock_freq = 0;
+
+ retval = av8100_conf_prep(
+ AV8100_COMMAND_VIDEO_INPUT_FORMAT, &config);
+ if (retval)
+ return -EFAULT;
+
+ /* DENC */
+ switch (output_format) {
+ case AV8100_CEA21_22_576I_PAL_50HZ:
+ config.denc_format.cvbs_video_format = AV8100_CVBS_625;
+ config.denc_format.standard_selection = AV8100_PAL_BDGHI;
+ break;
+
+ case AV8100_CEA6_7_NTSC_60HZ:
+ config.denc_format.cvbs_video_format = AV8100_CVBS_525;
+ config.denc_format.standard_selection = AV8100_NTSC_M;
+ break;
+
+ default:
+ /* Not supported */
+ break;
+ }
+
+ return 0;
+}
+
+static int av8100_config_init(void)
+{
+ int retval;
+ union av8100_configuration config;
+
+ dev_dbg(av8100dev, "%s\n", __func__);
+
+ av8100_config = kzalloc(sizeof(struct av8100_config_t), GFP_KERNEL);
+ if (!av8100_config) {
+ dev_err(av8100dev, "%s: Failed to allocate config\n", __func__);
+ return AV8100_FAIL;
+ }
+
+ memset(&config, 0, sizeof(union av8100_configuration));
+ memset(av8100_config, 0, sizeof(union av8100_configuration));
+
+ /* Color conversion */
+ config.color_transform = AV8100_COLOR_TRANSFORM_INDENTITY;
+ retval = av8100_conf_prep(
+ AV8100_COMMAND_COLORSPACECONVERSION, &config);
+ if (retval)
+ return -EFAULT;
+
+ /* DENC */
+ config.denc_format.cvbs_video_format = AV8100_CVBS_625;
+ config.denc_format.standard_selection = AV8100_PAL_BDGHI;
+ config.denc_format.enable = 0;
+ config.denc_format.macrovision_enable = 0;
+ config.denc_format.internal_generator = 0;
+ retval = av8100_conf_prep(AV8100_COMMAND_DENC, &config);
+ if (retval)
+ return -EFAULT;
+
+ /* Video output */
+ config.video_output_format.video_output_cea_vesa =
+ AV8100_CEA4_1280X720P_60HZ;
+
+ retval = av8100_conf_prep(
+ AV8100_COMMAND_VIDEO_OUTPUT_FORMAT, &config);
+ if (retval)
+ return -EFAULT;
+
+ /* Video input */
+ av8100_config_video_output_dep(
+ config.video_output_format.video_output_cea_vesa);
+
+ /* Pattern generator */
+ config.pattern_generator_format.pattern_audio_mode =
+ AV8100_PATTERN_AUDIO_OFF;
+ config.pattern_generator_format.pattern_type =
+ AV8100_PATTERN_GENERATOR;
+ config.pattern_generator_format.pattern_video_format =
+ AV8100_PATTERN_720P;
+ retval = av8100_conf_prep(AV8100_COMMAND_PATTERNGENERATOR,
+ &config);
+ if (retval)
+ return -EFAULT;
+
+ /* Audio input */
+ config.audio_input_format.audio_input_if_format =
+ AV8100_AUDIO_I2SDELAYED_MODE;
+ config.audio_input_format.i2s_input_nb = 1;
+ config.audio_input_format.sample_audio_freq = AV8100_AUDIO_FREQ_48KHZ;
+ config.audio_input_format.audio_word_lg = AV8100_AUDIO_16BITS;
+ config.audio_input_format.audio_format = AV8100_AUDIO_LPCM_MODE;
+ config.audio_input_format.audio_if_mode = AV8100_AUDIO_MASTER;
+ config.audio_input_format.audio_mute = AV8100_AUDIO_MUTE_DISABLE;
+ retval = av8100_conf_prep(
+ AV8100_COMMAND_AUDIO_INPUT_FORMAT, &config);
+ if (retval)
+ return -EFAULT;
+
+ /* HDMI mode */
+ config.hdmi_format.hdmi_mode = AV8100_HDMI_ON;
+ config.hdmi_format.hdmi_format = AV8100_HDMI;
+ config.hdmi_format.dvi_format = AV8100_DVI_CTRL_CTL0;
+ retval = av8100_conf_prep(AV8100_COMMAND_HDMI, &config);
+ if (retval)
+ return -EFAULT;
+
+ /* EDID section readback */
+ config.edid_section_readback_format.address = 0xA0;
+ config.edid_section_readback_format.block_number = 0;
+ retval = av8100_conf_prep(
+ AV8100_COMMAND_EDID_SECTION_READBACK, &config);
+ if (retval)
+ return -EFAULT;
+
+ return 0;
+}
+
+static void av8100_config_exit(void)
+{
+ dev_dbg(av8100dev, "%s\n", __func__);
+
+ kfree(av8100_config);
+ av8100_config = NULL;
+}
+
+static int av8100_globals_init(void)
+{
+ dev_dbg(av8100dev, "%s\n", __func__);
+
+ av8100_globals = kzalloc(sizeof(struct av8100_globals_t), GFP_KERNEL);
+ if (!av8100_globals) {
+ dev_err(av8100dev, "%s: Alloc failure\n", __func__);
+ return AV8100_FAIL;
+ }
+
+ av8100_globals->denc_off_time = AV8100_DENC_OFF_TIME;
+ av8100_globals->hdmi_off_time = AV8100_HDMI_OFF_TIME;
+ av8100_globals->on_time = AV8100_ON_TIME;
+
+ av8100_globals->hpdm = AV8100_STANDBY_INTERRUPT_MASK_HPDM_HIGH;
+ av8100_globals->cpdm = AV8100_STANDBY_INTERRUPT_MASK_CPDM_HIGH;
+ av8100_globals->hdcpm = AV8100_GENERAL_INTERRUPT_MASK_HDCPM_HIGH;
+ av8100_globals->cecm = AV8100_GENERAL_INTERRUPT_MASK_CECM_HIGH;
+
+ av8100_globals->plug_state = AV8100_UNPLUGGED;
+ av8100_globals->inputclk = NULL;
+ av8100_globals->inputclk_requested = false;
+ av8100_globals->opp_requested = false;
+ av8100_globals->regulator_requested = false;
+
+ return 0;
+}
+
+static void av8100_globals_exit(void)
+{
+ dev_dbg(av8100dev, "%s\n", __func__);
+
+ kfree(av8100_globals);
+ av8100_globals = NULL;
+}
+
+static void clr_plug_status(enum av8100_plugin_status status)
+{
+ g_av8100_status.av8100_plugin_status &= ~status;
+
+ switch (status) {
+ case AV8100_HDMI_PLUGIN:
+ if (!av8100_globals)
+ return;
+
+ switch (av8100_globals->plug_state) {
+ case AV8100_UNPLUGGED:
+ case AV8100_PLUGGED_STARTUP:
+ default:
+ break;
+
+ case AV8100_PLUGGED:
+ av8100_globals->plug_state =
+ AV8100_UNPLUGGED;
+ dev_dbg(av8100dev, "plug_state:0\n");
+
+ if (av8100_globals->hdmi_ev_cb)
+ av8100_globals->hdmi_ev_cb(
+ AV8100_HDMI_EVENT_HDMI_PLUGOUT);
+ break;
+ }
+ break;
+
+ case AV8100_CVBS_PLUGIN:
+ /* TODO */
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void set_plug_status(enum av8100_plugin_status status)
+{
+ g_av8100_status.av8100_plugin_status |= status;
+
+ switch (status) {
+ case AV8100_HDMI_PLUGIN:
+ if (!av8100_globals)
+ return;
+
+ switch (av8100_globals->plug_state) {
+ case AV8100_UNPLUGGED:
+ av8100_globals->plug_state =
+ AV8100_PLUGGED_STARTUP;
+
+ dev_dbg(av8100dev, "plug_state:1\n");
+
+ /*
+ * Mask interrupts to avoid plug detect during
+ * startup
+ * */
+ av8100_globals->hpdm =
+ AV8100_STANDBY_INTERRUPT_MASK_HPDM_LOW;
+ if (av8100_reg_stby_int_mask_w(
+ av8100_globals->hpdm,
+ av8100_globals->cpdm,
+ AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_INPUT,
+ AV8100_STANDBY_INTERRUPT_MASK_IPOL_LOW)) {
+ dev_dbg(av8100dev,
+ "av8100_reg_stby_int_mask_w"
+ "fail\n");
+ }
+
+ /* Set plug startup timer */
+ init_timer(&av8100_plugtimer);
+ av8100_plugtimer.expires = jiffies +
+ AV8100_PLUGSTARTUP_TIME;
+ av8100_plugtimer.function =
+ av8100_plugtimer_int;
+ av8100_plugtimer.data = 0;
+ add_timer(&av8100_plugtimer);
+
+ if (av8100_globals->hdmi_ev_cb)
+ av8100_globals->hdmi_ev_cb(
+ AV8100_HDMI_EVENT_HDMI_PLUGIN);
+ break;
+
+ case AV8100_PLUGGED_STARTUP:
+ case AV8100_PLUGGED:
+ default:
+ break;
+ }
+ break;
+
+ case AV8100_CVBS_PLUGIN:
+ /* TODO */
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void cec_rx(void)
+{
+ if (av8100_globals && av8100_globals->hdmi_ev_cb)
+ av8100_globals->hdmi_ev_cb(AV8100_HDMI_EVENT_CEC);
+}
+
+static void cec_txerr(void)
+{
+ if (av8100_globals && av8100_globals->hdmi_ev_cb)
+ av8100_globals->hdmi_ev_cb(AV8100_HDMI_EVENT_CECTXERR);
+}
+
+static void hdcp_changed(void)
+{
+ if (av8100_globals && av8100_globals->hdmi_ev_cb)
+ av8100_globals->hdmi_ev_cb(AV8100_HDMI_EVENT_HDCP);
+}
+
+static void av8100_set_state(enum av8100_operating_mode state)
+{
+ g_av8100_status.av8100_state = state;
+
+ if (state <= AV8100_OPMODE_STANDBY) {
+ clr_plug_status(AV8100_HDMI_PLUGIN);
+ clr_plug_status(AV8100_CVBS_PLUGIN);
+ g_av8100_status.hdmi_on = false;
+ }
+}
+
+/**
+ * write_single_byte() - Write a single byte to av8100
+ * through i2c interface.
+ * @client: i2c client structure
+ * @reg: register offset
+ * @data: data byte to be written
+ *
+ * This funtion uses smbus byte write API to write a single byte to av8100
+ **/
+static int write_single_byte(struct i2c_client *client, u8 reg,
+ u8 data)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, reg, data);
+ if (ret < 0)
+ dev_dbg(av8100dev, "i2c smbus write byte failed\n");
+
+ return ret;
+}
+
+/**
+ * read_single_byte() - read single byte from av8100
+ * through i2c interface
+ * @client: i2c client structure
+ * @reg: register offset
+ * @val: register value
+ *
+ * This funtion uses smbus read block API to read single byte from the reg
+ * offset.
+ **/
+static int read_single_byte(struct i2c_client *client, u8 reg, u8 *val)
+{
+ int value;
+
+ value = i2c_smbus_read_byte_data(client, reg);
+ if (value < 0) {
+ dev_dbg(av8100dev, "i2c smbus read byte failed,read data = %x "
+ "from offset:%x\n" , value, reg);
+ return AV8100_FAIL;
+ }
+
+ *val = (u8) value;
+ return 0;
+}
+
+/**
+ * write_multi_byte() - Write a multiple bytes to av8100 through
+ * i2c interface.
+ * @client: i2c client structure
+ * @buf: buffer to be written
+ * @nbytes: nunmber of bytes to be written
+ *
+ * This funtion uses smbus block write API's to write n number of bytes to the
+ * av8100
+ **/
+static int write_multi_byte(struct i2c_client *client, u8 reg,
+ u8 *buf, u8 nbytes)
+{
+ int ret;
+
+ ret = i2c_smbus_write_i2c_block_data(client, reg, nbytes, buf);
+ if (ret < 0)
+ dev_dbg(av8100dev, "i2c smbus write multi byte error\n");
+
+ return ret;
+}
+
+static int configuration_video_input_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = av8100_config->hdmi_video_input_cmd.dsi_input_mode;
+ buffer[1] = av8100_config->hdmi_video_input_cmd.input_pixel_format;
+ buffer[2] = REG_16_8_MSB(av8100_config->hdmi_video_input_cmd.
+ total_horizontal_pixel);
+ buffer[3] = REG_16_8_LSB(av8100_config->hdmi_video_input_cmd.
+ total_horizontal_pixel);
+ buffer[4] = REG_16_8_MSB(av8100_config->hdmi_video_input_cmd.
+ total_horizontal_active_pixel);
+ buffer[5] = REG_16_8_LSB(av8100_config->hdmi_video_input_cmd.
+ total_horizontal_active_pixel);
+ buffer[6] = REG_16_8_MSB(av8100_config->hdmi_video_input_cmd.
+ total_vertical_lines);
+ buffer[7] = REG_16_8_LSB(av8100_config->hdmi_video_input_cmd.
+ total_vertical_lines);
+ buffer[8] = REG_16_8_MSB(av8100_config->hdmi_video_input_cmd.
+ total_vertical_active_lines);
+ buffer[9] = REG_16_8_LSB(av8100_config->hdmi_video_input_cmd.
+ total_vertical_active_lines);
+ buffer[10] = av8100_config->hdmi_video_input_cmd.video_mode;
+ buffer[11] = av8100_config->hdmi_video_input_cmd.nb_data_lane;
+ buffer[12] = av8100_config->hdmi_video_input_cmd.
+ nb_virtual_ch_command_mode;
+ buffer[13] = av8100_config->hdmi_video_input_cmd.
+ nb_virtual_ch_video_mode;
+ buffer[14] = REG_16_8_MSB(av8100_config->hdmi_video_input_cmd.
+ TE_line_nb);
+ buffer[15] = REG_16_8_LSB(av8100_config->hdmi_video_input_cmd.
+ TE_line_nb);
+ buffer[16] = av8100_config->hdmi_video_input_cmd.TE_config;
+ buffer[17] = REG_32_8_MSB(av8100_config->hdmi_video_input_cmd.
+ master_clock_freq);
+ buffer[18] = REG_32_8_MMSB(av8100_config->hdmi_video_input_cmd.
+ master_clock_freq);
+ buffer[19] = REG_32_8_MLSB(av8100_config->hdmi_video_input_cmd.
+ master_clock_freq);
+ buffer[20] = REG_32_8_LSB(av8100_config->hdmi_video_input_cmd.
+ master_clock_freq);
+ buffer[21] = av8100_config->hdmi_video_input_cmd.ui_x4;
+
+ *length = AV8100_COMMAND_VIDEO_INPUT_FORMAT_SIZE - 1;
+ return 0;
+
+}
+
+static int configuration_audio_input_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = av8100_config->hdmi_audio_input_cmd.
+ audio_input_if_format;
+ buffer[1] = av8100_config->hdmi_audio_input_cmd.i2s_input_nb;
+ buffer[2] = av8100_config->hdmi_audio_input_cmd.sample_audio_freq;
+ buffer[3] = av8100_config->hdmi_audio_input_cmd.audio_word_lg;
+ buffer[4] = av8100_config->hdmi_audio_input_cmd.audio_format;
+ buffer[5] = av8100_config->hdmi_audio_input_cmd.audio_if_mode;
+ buffer[6] = av8100_config->hdmi_audio_input_cmd.audio_mute;
+
+ *length = AV8100_COMMAND_AUDIO_INPUT_FORMAT_SIZE - 1;
+ return 0;
+}
+
+static int configuration_video_output_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = av8100_config->hdmi_video_output_cmd.
+ video_output_cea_vesa;
+
+ if (buffer[0] == AV8100_CUSTOM) {
+ buffer[1] = av8100_config->hdmi_video_output_cmd.
+ vsync_polarity;
+ buffer[2] = av8100_config->hdmi_video_output_cmd.
+ hsync_polarity;
+ buffer[3] = REG_16_8_MSB(av8100_config->
+ hdmi_video_output_cmd.total_horizontal_pixel);
+ buffer[4] = REG_16_8_LSB(av8100_config->
+ hdmi_video_output_cmd.total_horizontal_pixel);
+ buffer[5] = REG_16_8_MSB(av8100_config->
+ hdmi_video_output_cmd.total_horizontal_active_pixel);
+ buffer[6] = REG_16_8_LSB(av8100_config->
+ hdmi_video_output_cmd.total_horizontal_active_pixel);
+ buffer[7] = REG_16_8_MSB(av8100_config->
+ hdmi_video_output_cmd.total_vertical_in_half_lines);
+ buffer[8] = REG_16_8_LSB(av8100_config->
+ hdmi_video_output_cmd.total_vertical_in_half_lines);
+ buffer[9] = REG_16_8_MSB(av8100_config->
+ hdmi_video_output_cmd.
+ total_vertical_active_in_half_lines);
+ buffer[10] = REG_16_8_LSB(av8100_config->
+ hdmi_video_output_cmd.
+ total_vertical_active_in_half_lines);
+ buffer[11] = REG_16_8_MSB(av8100_config->
+ hdmi_video_output_cmd.hsync_start_in_pixel);
+ buffer[12] = REG_16_8_LSB(av8100_config->
+ hdmi_video_output_cmd.hsync_start_in_pixel);
+ buffer[13] = REG_16_8_MSB(av8100_config->
+ hdmi_video_output_cmd.hsync_length_in_pixel);
+ buffer[14] = REG_16_8_LSB(av8100_config->
+ hdmi_video_output_cmd.hsync_length_in_pixel);
+ buffer[15] = REG_16_8_MSB(av8100_config->
+ hdmi_video_output_cmd.vsync_start_in_half_line);
+ buffer[16] = REG_16_8_LSB(av8100_config->
+ hdmi_video_output_cmd.vsync_start_in_half_line);
+ buffer[17] = REG_16_8_MSB(av8100_config->
+ hdmi_video_output_cmd.vsync_length_in_half_line);
+ buffer[18] = REG_16_8_LSB(av8100_config->
+ hdmi_video_output_cmd.vsync_length_in_half_line);
+ buffer[19] = REG_16_8_MSB(av8100_config->
+ hdmi_video_output_cmd.hor_video_start_pixel);
+ buffer[20] = REG_16_8_LSB(av8100_config->
+ hdmi_video_output_cmd.hor_video_start_pixel);
+ buffer[21] = REG_16_8_MSB(av8100_config->
+ hdmi_video_output_cmd.vert_video_start_pixel);
+ buffer[22] = REG_16_8_LSB(av8100_config->
+ hdmi_video_output_cmd.vert_video_start_pixel);
+ buffer[23] = av8100_config->
+ hdmi_video_output_cmd.video_type;
+ buffer[24] = av8100_config->
+ hdmi_video_output_cmd.pixel_repeat;
+ buffer[25] = REG_32_8_MSB(av8100_config->
+ hdmi_video_output_cmd.pixel_clock_freq_Hz);
+ buffer[26] = REG_32_8_MMSB(av8100_config->
+ hdmi_video_output_cmd.pixel_clock_freq_Hz);
+ buffer[27] = REG_32_8_MLSB(av8100_config->
+ hdmi_video_output_cmd.pixel_clock_freq_Hz);
+ buffer[28] = REG_32_8_LSB(av8100_config->
+ hdmi_video_output_cmd.pixel_clock_freq_Hz);
+
+ *length = AV8100_COMMAND_VIDEO_OUTPUT_FORMAT_SIZE - 1;
+ } else {
+ *length = 1;
+ }
+
+ return 0;
+}
+
+static int configuration_video_scaling_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = REG_16_8_MSB(av8100_config->hdmi_video_scaling_cmd.
+ h_start_in_pixel);
+ buffer[1] = REG_16_8_LSB(av8100_config->hdmi_video_scaling_cmd.
+ h_start_in_pixel);
+ buffer[2] = REG_16_8_MSB(av8100_config->hdmi_video_scaling_cmd.
+ h_stop_in_pixel);
+ buffer[3] = REG_16_8_LSB(av8100_config->hdmi_video_scaling_cmd.
+ h_stop_in_pixel);
+ buffer[4] = REG_16_8_MSB(av8100_config->hdmi_video_scaling_cmd.
+ v_start_in_line);
+ buffer[5] = REG_16_8_LSB(av8100_config->hdmi_video_scaling_cmd.
+ v_start_in_line);
+ buffer[6] = REG_16_8_MSB(av8100_config->hdmi_video_scaling_cmd.
+ v_stop_in_line);
+ buffer[7] = REG_16_8_LSB(av8100_config->hdmi_video_scaling_cmd.
+ v_stop_in_line);
+ buffer[8] = REG_16_8_MSB(av8100_config->hdmi_video_scaling_cmd.
+ h_start_out_pixel);
+ buffer[9] = REG_16_8_LSB(av8100_config->hdmi_video_scaling_cmd
+ .h_start_out_pixel);
+ buffer[10] = REG_16_8_MSB(av8100_config->hdmi_video_scaling_cmd.
+ h_stop_out_pixel);
+ buffer[11] = REG_16_8_LSB(av8100_config->hdmi_video_scaling_cmd.
+ h_stop_out_pixel);
+ buffer[12] = REG_16_8_MSB(av8100_config->hdmi_video_scaling_cmd.
+ v_start_out_line);
+ buffer[13] = REG_16_8_LSB(av8100_config->hdmi_video_scaling_cmd.
+ v_start_out_line);
+ buffer[14] = REG_16_8_MSB(av8100_config->hdmi_video_scaling_cmd.
+ v_stop_out_line);
+ buffer[15] = REG_16_8_LSB(av8100_config->hdmi_video_scaling_cmd.
+ v_stop_out_line);
+
+ *length = AV8100_COMMAND_VIDEO_SCALING_FORMAT_SIZE - 1;
+ return 0;
+}
+
+static int configuration_colorspace_conversion_get(char *buffer,
+ unsigned int *length)
+{
+ const struct color_conversion_cmd *hdmi_color_space_conversion_cmd;
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ hdmi_color_space_conversion_cmd =
+ get_color_transform_cmd(av8100_config->color_transform);
+
+ buffer[0] = REG_12_8_MSB(hdmi_color_space_conversion_cmd->c0);
+ buffer[1] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->c0);
+ buffer[2] = REG_12_8_MSB(hdmi_color_space_conversion_cmd->c1);
+ buffer[3] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->c1);
+ buffer[4] = REG_12_8_MSB(hdmi_color_space_conversion_cmd->c2);
+ buffer[5] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->c2);
+ buffer[6] = REG_12_8_MSB(hdmi_color_space_conversion_cmd->c3);
+ buffer[7] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->c3);
+ buffer[8] = REG_12_8_MSB(hdmi_color_space_conversion_cmd->c4);
+ buffer[9] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->c4);
+ buffer[10] = REG_12_8_MSB(hdmi_color_space_conversion_cmd->c5);
+ buffer[11] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->c5);
+ buffer[12] = REG_12_8_MSB(hdmi_color_space_conversion_cmd->c6);
+ buffer[13] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->c6);
+ buffer[14] = REG_12_8_MSB(hdmi_color_space_conversion_cmd->c7);
+ buffer[15] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->c7);
+ buffer[16] = REG_12_8_MSB(hdmi_color_space_conversion_cmd->c8);
+ buffer[17] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->c8);
+ buffer[18] = REG_10_8_MSB(hdmi_color_space_conversion_cmd->aoffset);
+ buffer[19] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->aoffset);
+ buffer[20] = REG_10_8_MSB(hdmi_color_space_conversion_cmd->boffset);
+ buffer[21] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->boffset);
+ buffer[22] = REG_10_8_MSB(hdmi_color_space_conversion_cmd->coffset);
+ buffer[23] = REG_16_8_LSB(hdmi_color_space_conversion_cmd->coffset);
+ buffer[24] = hdmi_color_space_conversion_cmd->lmax;
+ buffer[25] = hdmi_color_space_conversion_cmd->lmin;
+ buffer[26] = hdmi_color_space_conversion_cmd->cmax;
+ buffer[27] = hdmi_color_space_conversion_cmd->cmin;
+
+ *length = AV8100_COMMAND_COLORSPACECONVERSION_SIZE - 1;
+ return 0;
+}
+
+static int configuration_cec_message_write_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = av8100_config->hdmi_cec_message_write_cmd.buffer_length;
+ memcpy(&buffer[1], av8100_config->hdmi_cec_message_write_cmd.buffer,
+ HDMI_CEC_MESSAGE_WRITE_BUFFER_SIZE);
+
+ *length = AV8100_COMMAND_CEC_MESSAGE_WRITE_SIZE - 1;
+ return 0;
+}
+
+static int configuration_cec_message_read_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ /* No buffer data */
+ *length = AV8100_COMMAND_CEC_MESSAGE_READ_BACK_SIZE - 1;
+ return 0;
+}
+
+static int configuration_denc_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = av8100_config->hdmi_denc_cmd.cvbs_video_format;
+ buffer[1] = av8100_config->hdmi_denc_cmd.standard_selection;
+ buffer[2] = av8100_config->hdmi_denc_cmd.enable;
+ buffer[3] = av8100_config->hdmi_denc_cmd.macrovision_enable;
+ buffer[4] = av8100_config->hdmi_denc_cmd.internal_generator;
+
+ *length = AV8100_COMMAND_DENC_SIZE - 1;
+ return 0;
+}
+
+static int configuration_hdmi_get(char *buffer, unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = av8100_config->hdmi_cmd.hdmi_mode;
+ buffer[1] = av8100_config->hdmi_cmd.hdmi_format;
+ buffer[2] = av8100_config->hdmi_cmd.dvi_format;
+
+ *length = AV8100_COMMAND_HDMI_SIZE - 1;
+ return 0;
+}
+
+static int configuration_hdcp_sendkey_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = av8100_config->hdmi_hdcp_send_key_cmd.key_number;
+ memcpy(&buffer[1], av8100_config->hdmi_hdcp_send_key_cmd.data,
+ av8100_config->hdmi_hdcp_send_key_cmd.data_len);
+
+ *length = av8100_config->hdmi_hdcp_send_key_cmd.data_len + 1;
+ return 0;
+}
+
+static int configuration_hdcp_management_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = av8100_config->hdmi_hdcp_management_format_cmd.req_type;
+ buffer[1] = av8100_config->hdmi_hdcp_management_format_cmd.req_encr;
+ buffer[2] = av8100_config->hdmi_hdcp_management_format_cmd.encr_use;
+
+ *length = AV8100_COMMAND_HDCP_MANAGEMENT_SIZE - 1;
+ return 0;
+}
+
+static int configuration_infoframe_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = av8100_config->hdmi_infoframes_cmd.type;
+ buffer[1] = av8100_config->hdmi_infoframes_cmd.version;
+ buffer[2] = av8100_config->hdmi_infoframes_cmd.length;
+ buffer[3] = av8100_config->hdmi_infoframes_cmd.crc;
+ memcpy(&buffer[4], av8100_config->hdmi_infoframes_cmd.data,
+ HDMI_INFOFRAME_DATA_SIZE);
+
+ *length = AV8100_COMMAND_INFOFRAMES_SIZE - 1;
+ return 0;
+}
+
+static int av8100_edid_section_readback_get(char *buffer, unsigned int *length)
+{
+ buffer[0] = av8100_config->hdmi_edid_section_readback_cmd.address;
+ buffer[1] = av8100_config->hdmi_edid_section_readback_cmd.
+ block_number;
+
+ *length = AV8100_COMMAND_EDID_SECTION_READBACK_SIZE - 1;
+ return 0;
+}
+
+static int configuration_pattern_generator_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = av8100_config->hdmi_pattern_generator_cmd.pattern_type;
+ buffer[1] = av8100_config->hdmi_pattern_generator_cmd.
+ pattern_video_format;
+ buffer[2] = av8100_config->hdmi_pattern_generator_cmd.
+ pattern_audio_mode;
+
+ *length = AV8100_COMMAND_PATTERNGENERATOR_SIZE - 1;
+ return 0;
+}
+
+static int configuration_fuse_aes_key_get(char *buffer,
+ unsigned int *length)
+{
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ buffer[0] = av8100_config->hdmi_fuse_aes_key_cmd.fuse_operation;
+ if (av8100_config->hdmi_fuse_aes_key_cmd.fuse_operation) {
+ /* Write key command */
+ memcpy(&buffer[1], av8100_config->hdmi_fuse_aes_key_cmd.key,
+ HDMI_FUSE_AES_KEY_SIZE);
+
+ *length = AV8100_COMMAND_FUSE_AES_KEY_SIZE - 1;
+ } else {
+ /* Check key command */
+ *length = AV8100_COMMAND_FUSE_AES_CHK_SIZE - 1;
+ }
+ return 0;
+}
+
+static int get_command_return_first(struct i2c_client *i2c,
+ enum av8100_command_type command_type) {
+ int retval = 0;
+ char val;
+
+ retval = read_single_byte(i2c, AV8100_COMMAND_OFFSET, &val);
+ if (retval) {
+ dev_dbg(av8100dev, "%s 1st ret failed\n", __func__);
+ return retval;
+ }
+
+ if (val != (0x80 | command_type)) {
+ dev_dbg(av8100dev, "%s 1st ret wrong:%x\n", __func__, val);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int get_command_return_data(struct i2c_client *i2c,
+ enum av8100_command_type command_type,
+ u8 *command_buffer,
+ u8 *buffer_length,
+ u8 *buffer)
+{
+ int retval = 0;
+ char val;
+ int index = 0;
+
+ if (buffer_length)
+ *buffer_length = 0;
+
+ switch (command_type) {
+ case AV8100_COMMAND_VIDEO_INPUT_FORMAT:
+ case AV8100_COMMAND_AUDIO_INPUT_FORMAT:
+ case AV8100_COMMAND_VIDEO_OUTPUT_FORMAT:
+ case AV8100_COMMAND_VIDEO_SCALING_FORMAT:
+ case AV8100_COMMAND_COLORSPACECONVERSION:
+ case AV8100_COMMAND_CEC_MESSAGE_WRITE:
+ case AV8100_COMMAND_DENC:
+ case AV8100_COMMAND_HDMI:
+ case AV8100_COMMAND_INFOFRAMES:
+ case AV8100_COMMAND_PATTERNGENERATOR:
+ /* Get the second return byte */
+ retval = read_single_byte(i2c,
+ AV8100_2ND_RET_BYTE_OFFSET, &val);
+ if (retval)
+ goto get_command_return_data_fail2r;
+
+ if (val) {
+ retval = AV8100_FAIL;
+ goto get_command_return_data_fail2v;
+ }
+ break;
+
+ case AV8100_COMMAND_CEC_MESSAGE_READ_BACK:
+ if ((buffer == NULL) || (buffer_length == NULL)) {
+ retval = AV8100_FAIL;
+ goto get_command_return_data_fail;
+ }
+
+ /* Get the return buffer length */
+ retval = read_single_byte(i2c, AV8100_CEC_ADDR_OFFSET, &val);
+ if (retval)
+ goto get_command_return_data_fail;
+
+ /* TODO: buffer_length is always zero */
+ /* *buffer_length = val;*/
+ dev_dbg(av8100dev, "cec buflen:%d\n", val);
+ *buffer_length = val;
+
+ if (*buffer_length >
+ HDMI_CEC_READ_MAXSIZE) {
+ dev_dbg(av8100dev, "CEC size too large %d\n",
+ *buffer_length);
+ *buffer_length = HDMI_CEC_READ_MAXSIZE;
+ }
+
+ dev_dbg(av8100dev, "return data: ");
+
+ /* Get the return buffer */
+ for (index = 0; index < *buffer_length; ++index) {
+ retval = read_single_byte(i2c,
+ AV8100_CEC_RET_BUF_OFFSET + index, &val);
+ if (retval) {
+ *buffer_length = 0;
+ goto get_command_return_data_fail;
+ } else {
+ *(buffer + index) = val;
+ dev_dbg(av8100dev, "%02x ", *(buffer + index));
+ }
+ }
+
+ dev_dbg(av8100dev, "\n");
+ break;
+
+ case AV8100_COMMAND_HDCP_MANAGEMENT:
+ /* Get the second return byte */
+ retval = read_single_byte(i2c,
+ AV8100_2ND_RET_BYTE_OFFSET, &val);
+ if (retval) {
+ goto get_command_return_data_fail2r;
+ } else {
+ /* Check the second return byte */
+ if (val)
+ goto get_command_return_data_fail2v;
+ }
+
+ if ((buffer == NULL) || (buffer_length == NULL))
+ /* Ignore return data */
+ break;
+
+ /* Get the return buffer length */
+ if (command_buffer[0] ==
+ HDMI_REQUEST_FOR_REVOCATION_LIST_INPUT) {
+ *buffer_length = HDMI_REVOC_LIST_SIZE;
+ } else {
+ *buffer_length = 0x0;
+ }
+
+ dev_dbg(av8100dev, "return data: ");
+
+ /* Get the return buffer */
+ for (index = 0; index < *buffer_length; ++index) {
+ retval = read_single_byte(i2c,
+ AV8100_HDCP_RET_BUF_OFFSET + index, &val);
+ if (retval) {
+ *buffer_length = 0;
+ goto get_command_return_data_fail;
+ } else {
+ *(buffer + index) = val;
+ dev_dbg(av8100dev, "%02x ", *(buffer + index));
+ }
+ }
+
+ dev_dbg(av8100dev, "\n");
+ break;
+
+ case AV8100_COMMAND_EDID_SECTION_READBACK:
+ if ((buffer == NULL) || (buffer_length == NULL)) {
+ retval = AV8100_FAIL;
+ goto get_command_return_data_fail;
+ }
+
+ /* Return buffer length is fixed */
+ *buffer_length = HDMI_EDIDREAD_SIZE;
+
+ dev_dbg(av8100dev, "return data: ");
+
+ /* Get the return buffer */
+ for (index = 0; index < *buffer_length; ++index) {
+ retval = read_single_byte(i2c,
+ AV8100_EDID_RET_BUF_OFFSET + index, &val);
+ if (retval) {
+ *buffer_length = 0;
+ goto get_command_return_data_fail;
+ } else {
+ *(buffer + index) = val;
+ dev_dbg(av8100dev, "%02x ", *(buffer + index));
+ }
+ }
+
+ dev_dbg(av8100dev, "\n");
+ break;
+
+ case AV8100_COMMAND_FUSE_AES_KEY:
+ if ((buffer == NULL) || (buffer_length == NULL)) {
+ retval = AV8100_FAIL;
+ goto get_command_return_data_fail;
+ }
+
+ /* Get the second return byte */
+ retval = read_single_byte(i2c,
+ AV8100_2ND_RET_BYTE_OFFSET, &val);
+
+ if (retval)
+ goto get_command_return_data_fail2r;
+
+ /* Check the second return byte */
+ if (val) {
+ retval = AV8100_FAIL;
+ goto get_command_return_data_fail2v;
+ }
+
+ /* Return buffer length is fixed */
+ *buffer_length = HDMI_FUSE_AES_KEY_RET_SIZE;
+
+ /* Get CRC */
+ retval = read_single_byte(i2c,
+ AV8100_FUSE_CRC_OFFSET, &val);
+ if (retval)
+ goto get_command_return_data_fail;
+
+ *buffer = val;
+ dev_dbg(av8100dev, "CRC:%02x ", val);
+
+ /* Get programmed status */
+ retval = read_single_byte(i2c,
+ AV8100_FUSE_PRGD_OFFSET, &val);
+ if (retval)
+ goto get_command_return_data_fail;
+
+ *(buffer + 1) = val;
+
+ dev_dbg(av8100dev, "programmed:%02x ", val);
+ break;
+
+ case AV8100_COMMAND_HDCP_SENDKEY:
+ if ((command_buffer[0] == HDMI_LOADAES_END_BLK_NR) &&
+ ((buffer == NULL) || (buffer_length == NULL))) {
+ retval = AV8100_FAIL;
+ goto get_command_return_data_fail;
+ }
+
+ /* Get the second return byte */
+ retval = read_single_byte(i2c,
+ AV8100_2ND_RET_BYTE_OFFSET, &val);
+ if (retval)
+ goto get_command_return_data_fail2r;
+
+ if (val) {
+ retval = AV8100_FAIL;
+ goto get_command_return_data_fail2v;
+ }
+
+ if (command_buffer[0] == HDMI_LOADAES_END_BLK_NR) {
+ /* Return CRC32 if last AES block */
+ int cnt;
+
+ dev_dbg(av8100dev, "CRC32:");
+ for (cnt = 0; cnt < HDMI_CRC32_SIZE; cnt++) {
+ if (read_single_byte(i2c,
+ AV8100_CRC32_OFFSET + cnt, &val))
+ goto get_command_return_data_fail;
+ *(buffer + cnt) = val;
+ dev_dbg(av8100dev, "%02x", val);
+ }
+
+ *buffer_length = HDMI_CRC32_SIZE;
+ }
+ break;
+
+ default:
+ retval = AV8100_INVALID_COMMAND;
+ break;
+ }
+
+ return retval;
+get_command_return_data_fail2r:
+ dev_dbg(av8100dev, "%s Reading 2nd return byte failed\n", __func__);
+ return retval;
+get_command_return_data_fail2v:
+ dev_dbg(av8100dev, "%s 2nd return byte is wrong:%x\n", __func__, val);
+ return retval;
+get_command_return_data_fail:
+ dev_dbg(av8100dev, "%s FAIL\n", __func__);
+ return retval;
+}
+
+static int av8100_powerup1(void)
+{
+ int retval;
+ struct av8100_platform_data *pdata = av8100dev->platform_data;
+
+ /* Regulator enable */
+ if ((av8100_globals->regulator_pwr) &&
+ (av8100_globals->regulator_requested == false)) {
+ retval = regulator_enable(av8100_globals->regulator_pwr);
+ if (retval < 0) {
+ dev_warn(av8100dev, "%s: regulator_enable failed\n",
+ __func__);
+ return retval;
+ }
+ dev_dbg(av8100dev, "regulator_enable ok\n");
+ av8100_globals->regulator_requested = true;
+ }
+
+ /* Reset av8100 */
+ gpio_set_value(pdata->reset, 1);
+
+ /* Need to wait before proceeding */
+ msleep(AV8100_WAITTIME_1MS);
+
+ if (pdata->alt_powerupseq) {
+ u8 denc_off_time;
+ u8 hdmi_off_time;
+ u8 on_time;
+
+ /* Save the cycle values */
+ denc_off_time = av8100_globals->denc_off_time;
+ hdmi_off_time = av8100_globals->hdmi_off_time;
+ on_time = av8100_globals->on_time;
+
+ dev_dbg(av8100dev, "powerup seq alt\n");
+ retval = av8100_reg_hdmi_5_volt_time_w(0, 0, AV8100_ON_TIME);
+ if (retval) {
+ dev_err(av8100dev, "%s reg_wr err 1\n", __func__);
+ goto av8100_powerup1_err;
+ }
+
+ udelay(AV8100_WATTIME_100US);
+
+ /* Get the cycle values */
+ av8100_globals->denc_off_time = denc_off_time;
+ av8100_globals->hdmi_off_time = hdmi_off_time;
+ av8100_globals->on_time = on_time;
+
+ retval = av8100_reg_stby_pend_int_w(
+ AV8100_STANDBY_PENDING_INTERRUPT_HPDI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_CPDI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_ONI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_BPDIG_HIGH);
+ if (retval) {
+ dev_err(av8100dev, "%s reg_wr err 2\n", __func__);
+ goto av8100_powerup1_err;
+ }
+
+ udelay(AV8100_WATTIME_100US);
+
+ retval = av8100_reg_stby_w(AV8100_STANDBY_CPD_LOW,
+ AV8100_STANDBY_STBY_HIGH, pdata->mclk_freq);
+ if (retval) {
+ dev_err(av8100dev, "%s reg_wr err 3\n", __func__);
+ goto av8100_powerup1_err;
+ }
+
+ msleep(AV8100_WAITTIME_1MS);
+
+ retval = av8100_reg_stby_w(AV8100_STANDBY_CPD_LOW,
+ AV8100_STANDBY_STBY_LOW, pdata->mclk_freq);
+ if (retval) {
+ dev_err(av8100dev, "%s reg_wr err 4\n", __func__);
+ goto av8100_powerup1_err;
+ }
+
+ msleep(AV8100_WAITTIME_1MS);
+
+ retval = av8100_reg_stby_pend_int_w(
+ AV8100_STANDBY_PENDING_INTERRUPT_HPDI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_CPDI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_ONI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_BPDIG_LOW);
+ if (retval) {
+ dev_err(av8100dev, "%s reg_wr err 5\n", __func__);
+ goto av8100_powerup1_err;
+ }
+
+ msleep(AV8100_WAITTIME_1MS);
+ }
+
+ av8100_set_state(AV8100_OPMODE_STANDBY);
+
+ /* Get chip version */
+ retval = av8100_reg_stby_pend_int_r(NULL, NULL, NULL, &chip_version);
+ if (retval) {
+ dev_err(av8100dev, "Failed to read chip version\n");
+ goto av8100_powerup1_err;
+ }
+
+ dev_info(av8100dev, "chip version:%d\n", chip_version);
+
+ switch (chip_version) {
+ case AV8100_CHIPVER_1:
+ case AV8100_CHIPVER_2:
+ break;
+
+ default:
+ dev_err(av8100dev, "Unsupported chip version:%d\n",
+ chip_version);
+ goto av8100_powerup1_err;
+ break;
+ }
+
+ return 0;
+
+av8100_powerup1_err:
+ av8100_powerdown();
+ return -EFAULT;
+}
+
+static int av8100_powerup2(void)
+{
+ int retval;
+ struct av8100_platform_data *pdata = av8100dev->platform_data;
+
+ /* Master clock timing, running, search for plug */
+ retval = av8100_reg_stby_w(AV8100_STANDBY_CPD_HIGH,
+ AV8100_STANDBY_STBY_HIGH, pdata->mclk_freq);
+ if (retval) {
+ dev_err(av8100dev,
+ "Failed to write the value to av8100 register\n");
+ return retval;
+ }
+
+ msleep(AV8100_WAITTIME_1MS);
+
+ /* ON time & OFF time on 5v HDMI plug detect */
+ retval = av8100_reg_hdmi_5_volt_time_w(
+ av8100_globals->denc_off_time,
+ av8100_globals->hdmi_off_time,
+ av8100_globals->on_time);
+ if (retval) {
+ dev_err(av8100dev,
+ "Failed to write the value to av8100 register\n");
+ return retval;
+ }
+
+ msleep(AV8100_WAITTIME_1MS);
+
+ av8100_set_state(AV8100_OPMODE_SCAN);
+
+ return 0;
+}
+
+int av8100_powerup(void)
+{
+ int ret = 0;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ if (av8100_status_get().av8100_state < AV8100_OPMODE_STANDBY) {
+ ret = av8100_powerup1();
+ if (ret) {
+ dev_err(av8100dev, "av8100_powerup1 fail\n");
+ return -EFAULT;
+ }
+ }
+
+ if (av8100_status_get().av8100_state < AV8100_OPMODE_SCAN)
+ ret = av8100_powerup2();
+
+ return ret;
+}
+EXPORT_SYMBOL(av8100_powerup);
+
+int av8100_powerdown(void)
+{
+ int retval = 0;
+
+ struct av8100_platform_data *pdata = av8100dev->platform_data;
+
+ if (pdata->alt_powerupseq) {
+ retval = av8100_reg_stby_pend_int_w(
+ AV8100_STANDBY_PENDING_INTERRUPT_HPDI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_CPDI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_ONI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_BPDIG_HIGH);
+
+ if (retval)
+ dev_err(av8100dev, "%s reg_wr err\n", __func__);
+ msleep(AV8100_WAITTIME_50MS);
+ }
+
+ if (gpio_get_value(pdata->reset) == 0)
+ return 0;
+
+ /* Remove APE OPP requirement */
+ if (av8100_globals->opp_requested) {
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
+ (char *)av8100_miscdev.name);
+ prcmu_qos_remove_requirement(PRCMU_QOS_DDR_OPP,
+ (char *)av8100_miscdev.name);
+ av8100_globals->opp_requested = false;
+ }
+
+ /* Clock disable */
+ if (av8100_globals->inputclk && av8100_globals->inputclk_requested) {
+ clk_disable(av8100_globals->inputclk);
+ av8100_globals->inputclk_requested = false;
+ }
+
+ /* Regulator disable */
+ if ((av8100_globals->regulator_pwr) &&
+ (av8100_globals->regulator_requested)) {
+ dev_dbg(av8100dev, "regulator_disable\n");
+ regulator_disable(av8100_globals->regulator_pwr);
+ av8100_globals->regulator_requested = false;
+ }
+
+ gpio_set_value(pdata->reset, 0);
+
+ if (pdata->alt_powerupseq)
+ msleep(AV8100_WAITTIME_5MS);
+
+ av8100_set_state(AV8100_OPMODE_SHUTDOWN);
+
+ return retval;
+}
+EXPORT_SYMBOL(av8100_powerdown);
+
+int av8100_download_firmware(char *fw_buff, int nbytes,
+ enum interface_type if_type)
+{
+ int retval;
+ int temp = 0x0;
+ int increment = 15;
+ int index = 0;
+ int size = 0x0;
+ int tempnext = 0x0;
+ char val = 0x0;
+ char CheckSum = 0;
+ int cnt;
+ int cnt_max;
+ struct i2c_client *i2c;
+ u8 uc;
+ u8 fdl;
+ u8 hld;
+ u8 wa;
+ u8 ra;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ if (!av8100_config)
+ return -EINVAL;
+
+ /* Clock enable */
+ if (av8100_globals->inputclk &&
+ av8100_globals->inputclk_requested == false) {
+ if (clk_enable(av8100_globals->inputclk)) {
+ dev_err(av8100dev, "inputclk en failed\n");
+ return -EFAULT;
+ }
+
+ av8100_globals->inputclk_requested = true;
+ }
+
+ /* Request 100% APE OPP */
+ if (av8100_globals->opp_requested == false) {
+ if (prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
+ (char *)av8100_miscdev.name, 100)) {
+ dev_err(av8100dev, "APE OPP 100 failed\n");
+ return -EFAULT;
+ }
+ if (prcmu_qos_add_requirement(PRCMU_QOS_DDR_OPP,
+ (char *)av8100_miscdev.name, 100)) {
+ dev_err(av8100dev, "DDR OPP 100 failed\n");
+ return -EFAULT;
+ }
+
+ av8100_globals->opp_requested = true;
+ }
+
+ msleep(AV8100_WAITTIME_10MS);
+
+ fw_buff = av8100_fw_buff;
+ nbytes = AV8100_FW_SIZE;
+
+ i2c = av8100_config->client;
+
+ /* Enable firmware download */
+ retval = av8100_reg_gen_ctrl_w(
+ AV8100_GENERAL_CONTROL_FDL_HIGH,
+ AV8100_GENERAL_CONTROL_HLD_HIGH,
+ AV8100_GENERAL_CONTROL_WA_LOW,
+ AV8100_GENERAL_CONTROL_RA_LOW);
+ if (retval) {
+ dev_err(av8100dev,
+ "Failed to write the value to av8100 register\n");
+ return -EFAULT;
+ }
+
+ retval = av8100_reg_gen_ctrl_r(&fdl, &hld, &wa, &ra);
+ if (retval) {
+ dev_err(av8100dev,
+ "Failed to read the value from av8100 register\n");
+ return -EFAULT;
+ } else {
+ dev_dbg(av8100dev, "GENERAL_CONTROL_REG register fdl:%d "
+ "hld:%d wa:%d ra:%d\n", fdl, hld, wa, ra);
+ }
+
+ LOCK_AV8100_HW;
+
+ temp = nbytes % increment;
+ for (size = 0; size < (nbytes-temp); size = size + increment,
+ index += increment) {
+ if (if_type == I2C_INTERFACE) {
+ retval = write_multi_byte(i2c,
+ AV8100_FIRMWARE_DOWNLOAD_ENTRY, fw_buff + size,
+ increment);
+ if (retval) {
+ dev_dbg(av8100dev, "Failed to download the "
+ "av8100 firmware\n");
+ UNLOCK_AV8100_HW;
+ return -EFAULT;
+ }
+ } else if (if_type == DSI_INTERFACE) {
+ dev_dbg(av8100dev,
+ "DSI_INTERFACE is currently not supported\n");
+ UNLOCK_AV8100_HW;
+ return -EINVAL;
+ } else {
+ UNLOCK_AV8100_HW;
+ return -EINVAL;
+ }
+
+ for (tempnext = size; tempnext < (increment+size); tempnext++)
+ av8100_receivetab[tempnext] = fw_buff[tempnext];
+ }
+
+ /* Transfer last firmware bytes */
+ if (if_type == I2C_INTERFACE) {
+ retval = write_multi_byte(i2c,
+ AV8100_FIRMWARE_DOWNLOAD_ENTRY, fw_buff + size, temp);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to download the av8100 firmware\n");
+ UNLOCK_AV8100_HW;
+ return -EFAULT;
+ }
+ } else if (if_type == DSI_INTERFACE) {
+ /* TODO: Add support for DSI firmware download */
+ UNLOCK_AV8100_HW;
+ return -EINVAL;
+ } else {
+ UNLOCK_AV8100_HW;
+ return -EINVAL;
+ }
+
+ for (tempnext = size; tempnext < (size+temp); tempnext++)
+ av8100_receivetab[tempnext] = fw_buff[tempnext];
+
+ /* check transfer*/
+ for (size = 0; size < nbytes; size++) {
+ CheckSum = CheckSum ^ fw_buff[size];
+ if (av8100_receivetab[size] != fw_buff[size]) {
+ dev_dbg(av8100dev, ">Fw download fail....i=%d\n", size);
+ dev_dbg(av8100dev, "Transm = %x, Receiv = %x\n",
+ fw_buff[size], av8100_receivetab[size]);
+ }
+ }
+
+ UNLOCK_AV8100_HW;
+
+ retval = av8100_reg_fw_dl_entry_r(&val);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to read the value from the av8100 register\n");
+ return -EFAULT;
+ }
+
+ dev_dbg(av8100dev, "CheckSum:%x,val:%x\n", CheckSum, val);
+
+ if (CheckSum != val) {
+ dev_dbg(av8100dev,
+ ">Fw downloading.... FAIL CheckSum issue\n");
+ dev_dbg(av8100dev, "Checksum = %d\n", CheckSum);
+ dev_dbg(av8100dev, "Checksum read: %d\n", val);
+ return -EFAULT;
+ } else {
+ dev_dbg(av8100dev, ">Fw downloading.... success\n");
+ }
+
+ /* Set to idle mode */
+ av8100_reg_gen_ctrl_w(AV8100_GENERAL_CONTROL_FDL_LOW,
+ AV8100_GENERAL_CONTROL_HLD_LOW, AV8100_GENERAL_CONTROL_WA_LOW,
+ AV8100_GENERAL_CONTROL_RA_LOW);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to write the value to the av8100 register\n");
+ return -EFAULT;
+ }
+
+ /* Wait Internal Micro controler ready */
+ cnt = 0;
+ cnt_max = ARRAY_SIZE(waittime_retry);
+ retval = av8100_reg_gen_status_r(NULL, NULL, NULL, &uc,
+ NULL, NULL);
+ while ((retval == 0) && (uc != 0x1) && (cnt++ < cnt_max)) {
+ msleep(waittime_retry[cnt]);
+ retval = av8100_reg_gen_status_r(NULL, NULL, NULL,
+ &uc, NULL, NULL);
+ }
+ dev_dbg(av8100dev, "av8100 fwdl cnt:%d\n", cnt);
+
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to read the value from the av8100 register\n");
+ return -EFAULT;
+ }
+
+ if (uc != 0x1)
+ dev_dbg(av8100dev, "UC is not ready\n");
+
+ av8100_set_state(AV8100_OPMODE_IDLE);
+
+ return 0;
+}
+EXPORT_SYMBOL(av8100_download_firmware);
+
+int av8100_disable_interrupt(void)
+{
+ int retval;
+ struct i2c_client *i2c;
+ u8 hpdm = 0;
+ u8 cpdm = 0;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ if (!av8100_config)
+ return -EINVAL;
+
+ i2c = av8100_config->client;
+
+ retval = av8100_reg_stby_pend_int_w(
+ AV8100_STANDBY_PENDING_INTERRUPT_HPDI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_CPDI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_ONI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_BPDIG_LOW);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to write the value to av8100 register\n");
+ return -EFAULT;
+ }
+
+ retval = av8100_reg_gen_int_mask_w(
+ AV8100_GENERAL_INTERRUPT_MASK_EOCM_LOW,
+ AV8100_GENERAL_INTERRUPT_MASK_VSIM_LOW,
+ AV8100_GENERAL_INTERRUPT_MASK_VSOM_LOW,
+ AV8100_GENERAL_INTERRUPT_MASK_CECM_LOW,
+ AV8100_GENERAL_INTERRUPT_MASK_HDCPM_LOW,
+ AV8100_GENERAL_INTERRUPT_MASK_UOVBM_LOW,
+ AV8100_GENERAL_INTERRUPT_MASK_TEM_LOW);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to write the value to av8100 register\n");
+ return -EFAULT;
+ }
+
+ if (av8100_globals) {
+ hpdm = av8100_globals->hpdm;
+ cpdm = av8100_globals->cpdm;
+ }
+
+ retval = av8100_reg_stby_int_mask_w(
+ AV8100_STANDBY_INTERRUPT_MASK_HPDM_LOW,
+ AV8100_STANDBY_INTERRUPT_MASK_CPDM_LOW,
+ AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_INPUT,
+ AV8100_STANDBY_INTERRUPT_MASK_IPOL_LOW);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to write the value to av8100 register\n");
+ return -EFAULT;
+ }
+
+ if (av8100_globals) {
+ av8100_globals->hpdm = hpdm;
+ av8100_globals->cpdm = cpdm;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(av8100_disable_interrupt);
+
+int av8100_enable_interrupt(void)
+{
+ int retval;
+ struct i2c_client *i2c;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ if (!av8100_globals || !av8100_config)
+ return -EINVAL;
+
+ i2c = av8100_config->client;
+
+ retval = av8100_reg_stby_pend_int_w(
+ AV8100_STANDBY_PENDING_INTERRUPT_HPDI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_CPDI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_ONI_LOW,
+ AV8100_STANDBY_PENDING_INTERRUPT_BPDIG_LOW);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to write the value to av8100 register\n");
+ return -EFAULT;
+ }
+
+ retval = av8100_reg_gen_int_mask_w(
+ AV8100_GENERAL_INTERRUPT_MASK_EOCM_LOW,
+ AV8100_GENERAL_INTERRUPT_MASK_VSIM_LOW,
+ AV8100_GENERAL_INTERRUPT_MASK_VSOM_LOW,
+ av8100_globals->cecm,
+ av8100_globals->hdcpm,
+ AV8100_GENERAL_INTERRUPT_MASK_UOVBM_LOW,
+ AV8100_GENERAL_INTERRUPT_MASK_TEM_LOW);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to write the value to av8100 register\n");
+ return -EFAULT;
+ }
+
+ retval = av8100_reg_stby_int_mask_w(
+ av8100_globals->hpdm,
+ av8100_globals->cpdm,
+ AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_INPUT,
+ AV8100_STANDBY_INTERRUPT_MASK_IPOL_LOW);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to write the value to av8100 register\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(av8100_enable_interrupt);
+
+static int register_write_internal(u8 offset, u8 value)
+{
+ int retval;
+ struct i2c_client *i2c;
+
+ if (!av8100_config)
+ return -EINVAL;
+
+ i2c = av8100_config->client;
+
+ /* Write to register */
+ retval = write_single_byte(i2c, offset, value);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to write the value to av8100 register\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(register_write_internal);
+
+int av8100_reg_stby_w(
+ u8 cpd, u8 stby, u8 mclkrng)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Set register value */
+ val = AV8100_STANDBY_CPD(cpd) | AV8100_STANDBY_STBY(stby) |
+ AV8100_STANDBY_MCLKRNG(mclkrng);
+
+ /* Write to register */
+ retval = register_write_internal(AV8100_STANDBY, val);
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_stby_w);
+
+int av8100_reg_hdmi_5_volt_time_w(u8 denc_off, u8 hdmi_off, u8 on)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Set register value.
+ * chip_version == 1 have one common off time
+ * chip_version > 1 support different off time for hdmi and tvout. */
+ if (chip_version == 1)
+ val = AV8100_HDMI_5_VOLT_TIME_OFF_TIME(hdmi_off) |
+ AV8100_HDMI_5_VOLT_TIME_ON_TIME(on);
+ else
+ val = AV8100_HDMI_5_VOLT_TIME_DAC_OFF_TIME(denc_off) |
+ AV8100_HDMI_5_VOLT_TIME_SU_OFF_TIME(hdmi_off) |
+ AV8100_HDMI_5_VOLT_TIME_ON_TIME(on);
+
+ /* Write to register */
+ retval = register_write_internal(AV8100_HDMI_5_VOLT_TIME, val);
+
+ /* Set vars */
+ if (chip_version > 1)
+ av8100_globals->denc_off_time = denc_off;
+
+ av8100_globals->hdmi_off_time = hdmi_off;
+ av8100_globals->on_time = on;
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_hdmi_5_volt_time_w);
+
+int av8100_reg_stby_int_mask_w(
+ u8 hpdm, u8 cpdm, u8 stbygpiocfg, u8 ipol)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Set register value */
+ val = AV8100_STANDBY_INTERRUPT_MASK_HPDM(hpdm) |
+ AV8100_STANDBY_INTERRUPT_MASK_CPDM(cpdm) |
+ AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG(stbygpiocfg) |
+ AV8100_STANDBY_INTERRUPT_MASK_IPOL(ipol);
+
+ /* Write to register */
+ retval = register_write_internal(AV8100_STANDBY_INTERRUPT_MASK, val);
+
+ av8100_globals->hpdm = hpdm;
+ av8100_globals->cpdm = cpdm;
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_stby_int_mask_w);
+
+int av8100_reg_stby_pend_int_w(
+ u8 hpdi, u8 cpdi, u8 oni, u8 bpdig)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Set register value */
+ val = AV8100_STANDBY_PENDING_INTERRUPT_HPDI(hpdi) |
+ AV8100_STANDBY_PENDING_INTERRUPT_CPDI(cpdi) |
+ AV8100_STANDBY_PENDING_INTERRUPT_ONI(oni) |
+ AV8100_STANDBY_PENDING_INTERRUPT_BPDIG(bpdig);
+
+ /* Write to register */
+ retval = register_write_internal(AV8100_STANDBY_PENDING_INTERRUPT, val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_stby_pend_int_w);
+
+int av8100_reg_gen_int_mask_w(
+ u8 eocm, u8 vsim, u8 vsom, u8 cecm, u8 hdcpm, u8 uovbm, u8 tem)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Set register value */
+ val = AV8100_GENERAL_INTERRUPT_MASK_EOCM(eocm) |
+ AV8100_GENERAL_INTERRUPT_MASK_VSIM(vsim) |
+ AV8100_GENERAL_INTERRUPT_MASK_VSOM(vsom) |
+ AV8100_GENERAL_INTERRUPT_MASK_CECM(cecm) |
+ AV8100_GENERAL_INTERRUPT_MASK_HDCPM(hdcpm) |
+ AV8100_GENERAL_INTERRUPT_MASK_UOVBM(uovbm) |
+ AV8100_GENERAL_INTERRUPT_MASK_TEM(tem);
+
+ /* Write to register */
+ retval = register_write_internal(AV8100_GENERAL_INTERRUPT_MASK, val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_gen_int_mask_w);
+
+int av8100_reg_gen_int_w(
+ u8 eoci, u8 vsii, u8 vsoi, u8 ceci, u8 hdcpi, u8 uovbi)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Set register value */
+ val = AV8100_GENERAL_INTERRUPT_EOCI(eoci) |
+ AV8100_GENERAL_INTERRUPT_VSII(vsii) |
+ AV8100_GENERAL_INTERRUPT_VSOI(vsoi) |
+ AV8100_GENERAL_INTERRUPT_CECI(ceci) |
+ AV8100_GENERAL_INTERRUPT_HDCPI(hdcpi) |
+ AV8100_GENERAL_INTERRUPT_UOVBI(uovbi);
+
+ /* Write to register */
+ retval = register_write_internal(AV8100_GENERAL_INTERRUPT, val);
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_gen_int_w);
+
+int av8100_reg_gpio_conf_w(
+ u8 dat3dir, u8 dat3val, u8 dat2dir, u8 dat2val, u8 dat1dir,
+ u8 dat1val, u8 ucdbg)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Set register value */
+ val = AV8100_GPIO_CONFIGURATION_DAT3DIR(dat3dir) |
+ AV8100_GPIO_CONFIGURATION_DAT3VAL(dat3val) |
+ AV8100_GPIO_CONFIGURATION_DAT2DIR(dat2dir) |
+ AV8100_GPIO_CONFIGURATION_DAT2VAL(dat2val) |
+ AV8100_GPIO_CONFIGURATION_DAT1DIR(dat1dir) |
+ AV8100_GPIO_CONFIGURATION_DAT1VAL(dat1val) |
+ AV8100_GPIO_CONFIGURATION_UCDBG(ucdbg);
+
+ /* Write to register */
+ retval = register_write_internal(AV8100_GPIO_CONFIGURATION, val);
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_gpio_conf_w);
+
+int av8100_reg_gen_ctrl_w(
+ u8 fdl, u8 hld, u8 wa, u8 ra)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Set register value */
+ val = AV8100_GENERAL_CONTROL_FDL(fdl) |
+ AV8100_GENERAL_CONTROL_HLD(hld) |
+ AV8100_GENERAL_CONTROL_WA(wa) |
+ AV8100_GENERAL_CONTROL_RA(ra);
+
+ /* Write to register */
+ retval = register_write_internal(AV8100_GENERAL_CONTROL, val);
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_gen_ctrl_w);
+
+int av8100_reg_fw_dl_entry_w(
+ u8 mbyte_code_entry)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Set register value */
+ val = AV8100_FIRMWARE_DOWNLOAD_ENTRY_MBYTE_CODE_ENTRY(
+ mbyte_code_entry);
+
+ /* Write to register */
+ retval = register_write_internal(AV8100_FIRMWARE_DOWNLOAD_ENTRY, val);
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_fw_dl_entry_w);
+
+int av8100_reg_w(
+ u8 offset, u8 value)
+{
+ int retval = 0;
+ struct i2c_client *i2c;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ if (!av8100_config) {
+ UNLOCK_AV8100_HW;
+ return -EINVAL;
+ }
+
+ i2c = av8100_config->client;
+
+ /* Write to register */
+ retval = write_single_byte(i2c, offset, value);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to write the value to av8100 register\n");
+ UNLOCK_AV8100_HW;
+ return -EFAULT;
+ }
+
+ UNLOCK_AV8100_HW;
+ return 0;
+}
+EXPORT_SYMBOL(av8100_reg_w);
+
+int register_read_internal(u8 offset, u8 *value)
+{
+ int retval = 0;
+ struct i2c_client *i2c;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ if (!av8100_config)
+ return -EINVAL;
+
+ i2c = av8100_config->client;
+
+ /* Read from register */
+ retval = read_single_byte(i2c, offset, value);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to read the value from av8100 register\n");
+ return -EFAULT;
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL(register_read_internal);
+
+int av8100_reg_stby_r(
+ u8 *cpd, u8 *stby, u8 *hpds, u8 *cpds, u8 *mclkrng)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Read from register */
+ retval = register_read_internal(AV8100_STANDBY, &val);
+
+ /* Set return params */
+ if (cpd)
+ *cpd = AV8100_STANDBY_CPD_GET(val);
+ if (stby)
+ *stby = AV8100_STANDBY_STBY_GET(val);
+ if (hpds)
+ *hpds = AV8100_STANDBY_HPDS_GET(val);
+ if (cpds)
+ *cpds = AV8100_STANDBY_CPDS_GET(val);
+ if (mclkrng)
+ *mclkrng = AV8100_STANDBY_MCLKRNG_GET(val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_stby_r);
+
+int av8100_reg_hdmi_5_volt_time_r(
+ u8 *denc_off_time, u8 *hdmi_off_time, u8 *on_time)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Read from register */
+ retval = register_read_internal(AV8100_HDMI_5_VOLT_TIME, &val);
+
+ /* Set return params */
+ if (chip_version == 1) {
+ if (denc_off_time)
+ *denc_off_time = 0;
+ if (hdmi_off_time)
+ *hdmi_off_time =
+ AV8100_HDMI_5_VOLT_TIME_OFF_TIME_GET(val);
+ } else {
+ if (denc_off_time)
+ *denc_off_time =
+ AV8100_HDMI_5_VOLT_TIME_DAC_OFF_TIME_GET(val);
+ if (hdmi_off_time)
+ *hdmi_off_time =
+ AV8100_HDMI_5_VOLT_TIME_SU_OFF_TIME_GET(val);
+ }
+
+ if (on_time)
+ *on_time = AV8100_HDMI_5_VOLT_TIME_ON_TIME_GET(val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_hdmi_5_volt_time_r);
+
+int av8100_reg_stby_int_mask_r(
+ u8 *hpdm, u8 *cpdm, u8 *stbygpiocfg, u8 *ipol)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Read from register */
+ retval = register_read_internal(AV8100_STANDBY_INTERRUPT_MASK, &val);
+
+ /* Set return params */
+ if (hpdm)
+ *hpdm = AV8100_STANDBY_INTERRUPT_MASK_HPDM_GET(val);
+ if (cpdm)
+ *cpdm = AV8100_STANDBY_INTERRUPT_MASK_CPDM_GET(val);
+ if (stbygpiocfg)
+ *stbygpiocfg =
+ AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_GET(val);
+ if (ipol)
+ *ipol = AV8100_STANDBY_INTERRUPT_MASK_IPOL_GET(val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_stby_int_mask_r);
+
+int av8100_reg_stby_pend_int_r(
+ u8 *hpdi, u8 *cpdi, u8 *oni, u8 *sid)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Read from register */
+ retval = register_read_internal(AV8100_STANDBY_PENDING_INTERRUPT,
+ &val);
+
+ /* Set return params */
+ if (hpdi)
+ *hpdi = AV8100_STANDBY_PENDING_INTERRUPT_HPDI_GET(val);
+ if (cpdi)
+ *cpdi = AV8100_STANDBY_PENDING_INTERRUPT_CPDI_GET(val);
+ if (oni)
+ *oni = AV8100_STANDBY_PENDING_INTERRUPT_ONI_GET(val);
+ if (sid)
+ *sid = AV8100_STANDBY_PENDING_INTERRUPT_SID_GET(val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_stby_pend_int_r);
+
+int av8100_reg_gen_int_mask_r(
+ u8 *eocm,
+ u8 *vsim,
+ u8 *vsom,
+ u8 *cecm,
+ u8 *hdcpm,
+ u8 *uovbm,
+ u8 *tem)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Read from register */
+ retval = register_read_internal(AV8100_GENERAL_INTERRUPT_MASK, &val);
+
+ /* Set return params */
+ if (eocm)
+ *eocm = AV8100_GENERAL_INTERRUPT_MASK_EOCM_GET(val);
+ if (vsim)
+ *vsim = AV8100_GENERAL_INTERRUPT_MASK_VSIM_GET(val);
+ if (vsom)
+ *vsom = AV8100_GENERAL_INTERRUPT_MASK_VSOM_GET(val);
+ if (cecm)
+ *cecm = AV8100_GENERAL_INTERRUPT_MASK_CECM_GET(val);
+ if (hdcpm)
+ *hdcpm = AV8100_GENERAL_INTERRUPT_MASK_HDCPM_GET(val);
+ if (uovbm)
+ *uovbm = AV8100_GENERAL_INTERRUPT_MASK_UOVBM_GET(val);
+ if (tem)
+ *tem = AV8100_GENERAL_INTERRUPT_MASK_TEM_GET(val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_gen_int_mask_r);
+
+int av8100_reg_gen_int_r(
+ u8 *eoci,
+ u8 *vsii,
+ u8 *vsoi,
+ u8 *ceci,
+ u8 *hdcpi,
+ u8 *uovbi,
+ u8 *tei)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Read from register */
+ retval = register_read_internal(AV8100_GENERAL_INTERRUPT, &val);
+
+ /* Set return params */
+ if (eoci)
+ *eoci = AV8100_GENERAL_INTERRUPT_EOCI_GET(val);
+ if (vsii)
+ *vsii = AV8100_GENERAL_INTERRUPT_VSII_GET(val);
+ if (vsoi)
+ *vsoi = AV8100_GENERAL_INTERRUPT_VSOI_GET(val);
+ if (ceci)
+ *ceci = AV8100_GENERAL_INTERRUPT_CECI_GET(val);
+ if (hdcpi)
+ *hdcpi = AV8100_GENERAL_INTERRUPT_HDCPI_GET(val);
+ if (uovbi)
+ *uovbi = AV8100_GENERAL_INTERRUPT_UOVBI_GET(val);
+ if (tei)
+ *tei = AV8100_GENERAL_INTERRUPT_TEI_GET(val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_gen_int_r);
+
+int av8100_reg_gen_status_r(
+ u8 *cectxerr,
+ u8 *cecrec,
+ u8 *cectrx,
+ u8 *uc,
+ u8 *onuvb,
+ u8 *hdcps)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Read from register */
+ retval = register_read_internal(AV8100_GENERAL_STATUS, &val);
+
+ /* Set return params */
+ if (cectxerr)
+ *cectxerr = AV8100_GENERAL_STATUS_CECTXERR_GET(val);
+ if (cecrec)
+ *cecrec = AV8100_GENERAL_STATUS_CECREC_GET(val);
+ if (cectrx)
+ *cectrx = AV8100_GENERAL_STATUS_CECTRX_GET(val);
+ if (uc)
+ *uc = AV8100_GENERAL_STATUS_UC_GET(val);
+ if (onuvb)
+ *onuvb = AV8100_GENERAL_STATUS_ONUVB_GET(val);
+ if (hdcps)
+ *hdcps = AV8100_GENERAL_STATUS_HDCPS_GET(val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_gen_status_r);
+
+int av8100_reg_gpio_conf_r(
+ u8 *dat3dir,
+ u8 *dat3val,
+ u8 *dat2dir,
+ u8 *dat2val,
+ u8 *dat1dir,
+ u8 *dat1val,
+ u8 *ucdbg)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Read from register */
+ retval = register_read_internal(AV8100_GPIO_CONFIGURATION, &val);
+
+ /* Set return params */
+ if (dat3dir)
+ *dat3dir = AV8100_GPIO_CONFIGURATION_DAT3DIR_GET(val);
+ if (dat3val)
+ *dat3val = AV8100_GPIO_CONFIGURATION_DAT3VAL_GET(val);
+ if (dat2dir)
+ *dat2dir = AV8100_GPIO_CONFIGURATION_DAT2DIR_GET(val);
+ if (dat2val)
+ *dat2val = AV8100_GPIO_CONFIGURATION_DAT2VAL_GET(val);
+ if (dat1dir)
+ *dat1dir = AV8100_GPIO_CONFIGURATION_DAT1DIR_GET(val);
+ if (dat1val)
+ *dat1val = AV8100_GPIO_CONFIGURATION_DAT1VAL_GET(val);
+ if (ucdbg)
+ *ucdbg = AV8100_GPIO_CONFIGURATION_UCDBG_GET(val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_gpio_conf_r);
+
+int av8100_reg_gen_ctrl_r(
+ u8 *fdl,
+ u8 *hld,
+ u8 *wa,
+ u8 *ra)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Read from register */
+ retval = register_read_internal(AV8100_GENERAL_CONTROL, &val);
+ /* Set return params */
+ if (fdl)
+ *fdl = AV8100_GENERAL_CONTROL_FDL_GET(val);
+ if (hld)
+ *hld = AV8100_GENERAL_CONTROL_HLD_GET(val);
+ if (wa)
+ *wa = AV8100_GENERAL_CONTROL_WA_GET(val);
+ if (ra)
+ *ra = AV8100_GENERAL_CONTROL_RA_GET(val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_gen_ctrl_r);
+
+int av8100_reg_fw_dl_entry_r(
+ u8 *mbyte_code_entry)
+{
+ int retval;
+ u8 val;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ /* Read from register */
+ retval = register_read_internal(AV8100_FIRMWARE_DOWNLOAD_ENTRY, &val);
+
+ /* Set return params */
+ if (mbyte_code_entry)
+ *mbyte_code_entry =
+ AV8100_FIRMWARE_DOWNLOAD_ENTRY_MBYTE_CODE_ENTRY_GET(val);
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_fw_dl_entry_r);
+
+int av8100_reg_r(
+ u8 offset,
+ u8 *value)
+{
+ int retval = 0;
+ struct i2c_client *i2c;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ if (!av8100_config) {
+ retval = AV8100_FAIL;
+ goto av8100_register_read_out;
+ }
+
+ i2c = av8100_config->client;
+
+ /* Read from register */
+ retval = read_single_byte(i2c, offset, value);
+ if (retval) {
+ dev_dbg(av8100dev,
+ "Failed to read the value from av8100 register\n");
+ retval = -EFAULT;
+ goto av8100_register_read_out;
+ }
+
+av8100_register_read_out:
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_reg_r);
+
+int av8100_conf_get(enum av8100_command_type command_type,
+ union av8100_configuration *config)
+{
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ if (!av8100_config || !config)
+ return AV8100_FAIL;
+
+ /* Put configuration data to the corresponding data struct depending
+ * on command type */
+ switch (command_type) {
+ case AV8100_COMMAND_VIDEO_INPUT_FORMAT:
+ memcpy(&config->video_input_format,
+ &av8100_config->hdmi_video_input_cmd,
+ sizeof(struct av8100_video_input_format_cmd));
+ break;
+
+ case AV8100_COMMAND_AUDIO_INPUT_FORMAT:
+ memcpy(&config->audio_input_format,
+ &av8100_config->hdmi_audio_input_cmd,
+ sizeof(struct av8100_audio_input_format_cmd));
+ break;
+
+ case AV8100_COMMAND_VIDEO_OUTPUT_FORMAT:
+ memcpy(&config->video_output_format,
+ &av8100_config->hdmi_video_output_cmd,
+ sizeof(struct av8100_video_output_format_cmd));
+ break;
+
+ case AV8100_COMMAND_VIDEO_SCALING_FORMAT:
+ memcpy(&config->video_scaling_format,
+ &av8100_config->hdmi_video_scaling_cmd,
+ sizeof(struct av8100_video_scaling_format_cmd));
+ break;
+
+ case AV8100_COMMAND_COLORSPACECONVERSION:
+ config->color_transform = av8100_config->color_transform;
+ break;
+
+ case AV8100_COMMAND_CEC_MESSAGE_WRITE:
+ memcpy(&config->cec_message_write_format,
+ &av8100_config->hdmi_cec_message_write_cmd,
+ sizeof(struct av8100_cec_message_write_format_cmd));
+ break;
+
+ case AV8100_COMMAND_CEC_MESSAGE_READ_BACK:
+ memcpy(&config->cec_message_read_back_format,
+ &av8100_config->hdmi_cec_message_read_back_cmd,
+ sizeof(struct av8100_cec_message_read_back_format_cmd));
+ break;
+
+ case AV8100_COMMAND_DENC:
+ memcpy(&config->denc_format, &av8100_config->hdmi_denc_cmd,
+ sizeof(struct av8100_denc_format_cmd));
+ break;
+
+ case AV8100_COMMAND_HDMI:
+ memcpy(&config->hdmi_format, &av8100_config->hdmi_cmd,
+ sizeof(struct av8100_hdmi_cmd));
+ break;
+
+ case AV8100_COMMAND_HDCP_SENDKEY:
+ memcpy(&config->hdcp_send_key_format,
+ &av8100_config->hdmi_hdcp_send_key_cmd,
+ sizeof(struct av8100_hdcp_send_key_format_cmd));
+ break;
+
+ case AV8100_COMMAND_HDCP_MANAGEMENT:
+ memcpy(&config->hdcp_management_format,
+ &av8100_config->hdmi_hdcp_management_format_cmd,
+ sizeof(struct av8100_hdcp_management_format_cmd));
+ break;
+
+ case AV8100_COMMAND_INFOFRAMES:
+ memcpy(&config->infoframes_format,
+ &av8100_config->hdmi_infoframes_cmd,
+ sizeof(struct av8100_infoframes_format_cmd));
+ break;
+
+ case AV8100_COMMAND_EDID_SECTION_READBACK:
+ memcpy(&config->edid_section_readback_format,
+ &av8100_config->hdmi_edid_section_readback_cmd,
+ sizeof(struct
+ av8100_edid_section_readback_format_cmd));
+ break;
+
+ case AV8100_COMMAND_PATTERNGENERATOR:
+ memcpy(&config->pattern_generator_format,
+ &av8100_config->hdmi_pattern_generator_cmd,
+ sizeof(struct av8100_pattern_generator_format_cmd));
+ break;
+
+ case AV8100_COMMAND_FUSE_AES_KEY:
+ memcpy(&config->fuse_aes_key_format,
+ &av8100_config->hdmi_fuse_aes_key_cmd,
+ sizeof(struct av8100_fuse_aes_key_format_cmd));
+ break;
+
+ default:
+ return AV8100_FAIL;
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(av8100_conf_get);
+
+int av8100_conf_prep(enum av8100_command_type command_type,
+ union av8100_configuration *config)
+{
+ if (!av8100_config || !config)
+ return AV8100_FAIL;
+
+ /* Put configuration data to the corresponding data struct depending
+ * on command type */
+ switch (command_type) {
+ case AV8100_COMMAND_VIDEO_INPUT_FORMAT:
+ memcpy(&av8100_config->hdmi_video_input_cmd,
+ &config->video_input_format,
+ sizeof(struct av8100_video_input_format_cmd));
+ break;
+
+ case AV8100_COMMAND_AUDIO_INPUT_FORMAT:
+ memcpy(&av8100_config->hdmi_audio_input_cmd,
+ &config->audio_input_format,
+ sizeof(struct av8100_audio_input_format_cmd));
+ break;
+
+ case AV8100_COMMAND_VIDEO_OUTPUT_FORMAT:
+ memcpy(&av8100_config->hdmi_video_output_cmd,
+ &config->video_output_format,
+ sizeof(struct av8100_video_output_format_cmd));
+
+ /* Set params that depend on video output */
+ av8100_config_video_output_dep(av8100_config->
+ hdmi_video_output_cmd.video_output_cea_vesa);
+ break;
+
+ case AV8100_COMMAND_VIDEO_SCALING_FORMAT:
+ memcpy(&av8100_config->hdmi_video_scaling_cmd,
+ &config->video_scaling_format,
+ sizeof(struct av8100_video_scaling_format_cmd));
+ break;
+
+ case AV8100_COMMAND_COLORSPACECONVERSION:
+ av8100_config->color_transform = config->color_transform;
+ break;
+
+ case AV8100_COMMAND_CEC_MESSAGE_WRITE:
+ memcpy(&av8100_config->hdmi_cec_message_write_cmd,
+ &config->cec_message_write_format,
+ sizeof(struct av8100_cec_message_write_format_cmd));
+ break;
+
+ case AV8100_COMMAND_CEC_MESSAGE_READ_BACK:
+ memcpy(&av8100_config->hdmi_cec_message_read_back_cmd,
+ &config->cec_message_read_back_format,
+ sizeof(struct av8100_cec_message_read_back_format_cmd));
+ break;
+
+ case AV8100_COMMAND_DENC:
+ memcpy(&av8100_config->hdmi_denc_cmd, &config->denc_format,
+ sizeof(struct av8100_denc_format_cmd));
+ break;
+
+ case AV8100_COMMAND_HDMI:
+ memcpy(&av8100_config->hdmi_cmd, &config->hdmi_format,
+ sizeof(struct av8100_hdmi_cmd));
+ break;
+
+ case AV8100_COMMAND_HDCP_SENDKEY:
+ memcpy(&av8100_config->hdmi_hdcp_send_key_cmd,
+ &config->hdcp_send_key_format,
+ sizeof(struct av8100_hdcp_send_key_format_cmd));
+ break;
+
+ case AV8100_COMMAND_HDCP_MANAGEMENT:
+ memcpy(&av8100_config->hdmi_hdcp_management_format_cmd,
+ &config->hdcp_management_format,
+ sizeof(struct av8100_hdcp_management_format_cmd));
+ break;
+
+ case AV8100_COMMAND_INFOFRAMES:
+ memcpy(&av8100_config->hdmi_infoframes_cmd,
+ &config->infoframes_format,
+ sizeof(struct av8100_infoframes_format_cmd));
+ break;
+
+ case AV8100_COMMAND_EDID_SECTION_READBACK:
+ memcpy(&av8100_config->hdmi_edid_section_readback_cmd,
+ &config->edid_section_readback_format,
+ sizeof(struct
+ av8100_edid_section_readback_format_cmd));
+ break;
+
+ case AV8100_COMMAND_PATTERNGENERATOR:
+ memcpy(&av8100_config->hdmi_pattern_generator_cmd,
+ &config->pattern_generator_format,
+ sizeof(struct av8100_pattern_generator_format_cmd));
+ break;
+
+ case AV8100_COMMAND_FUSE_AES_KEY:
+ memcpy(&av8100_config->hdmi_fuse_aes_key_cmd,
+ &config->fuse_aes_key_format,
+ sizeof(struct av8100_fuse_aes_key_format_cmd));
+ break;
+
+ default:
+ return AV8100_FAIL;
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(av8100_conf_prep);
+
+int av8100_conf_w(enum av8100_command_type command_type,
+ u8 *return_buffer_length,
+ u8 *return_buffer, enum interface_type if_type)
+{
+ int retval = 0;
+ u8 cmd_buffer[AV8100_COMMAND_MAX_LENGTH];
+ u32 cmd_length = 0;
+ struct i2c_client *i2c;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ if (return_buffer_length)
+ *return_buffer_length = 0;
+
+ if (!av8100_config)
+ return AV8100_FAIL;
+
+ i2c = av8100_config->client;
+
+ memset(&cmd_buffer, 0x00, AV8100_COMMAND_MAX_LENGTH);
+
+#define PRNK_MODE(_m) dev_dbg(av8100dev, "cmd: " #_m "\n");
+
+ /* Fill the command buffer with configuration data */
+ switch (command_type) {
+ case AV8100_COMMAND_VIDEO_INPUT_FORMAT:
+ PRNK_MODE(AV8100_COMMAND_VIDEO_INPUT_FORMAT);
+ configuration_video_input_get(cmd_buffer, &cmd_length);
+ break;
+
+ case AV8100_COMMAND_AUDIO_INPUT_FORMAT:
+ PRNK_MODE(AV8100_COMMAND_AUDIO_INPUT_FORMAT);
+ configuration_audio_input_get(cmd_buffer, &cmd_length);
+ break;
+
+ case AV8100_COMMAND_VIDEO_OUTPUT_FORMAT:
+ PRNK_MODE(AV8100_COMMAND_VIDEO_OUTPUT_FORMAT);
+ configuration_video_output_get(cmd_buffer, &cmd_length);
+ break;
+
+ case AV8100_COMMAND_VIDEO_SCALING_FORMAT:
+ PRNK_MODE(AV8100_COMMAND_VIDEO_SCALING_FORMAT);
+ configuration_video_scaling_get(cmd_buffer,
+ &cmd_length);
+ break;
+
+ case AV8100_COMMAND_COLORSPACECONVERSION:
+ PRNK_MODE(AV8100_COMMAND_COLORSPACECONVERSION);
+ configuration_colorspace_conversion_get(cmd_buffer,
+ &cmd_length);
+ break;
+
+ case AV8100_COMMAND_CEC_MESSAGE_WRITE:
+ PRNK_MODE(AV8100_COMMAND_CEC_MESSAGE_WRITE);
+ configuration_cec_message_write_get(cmd_buffer,
+ &cmd_length);
+ break;
+
+ case AV8100_COMMAND_CEC_MESSAGE_READ_BACK:
+ PRNK_MODE(AV8100_COMMAND_CEC_MESSAGE_READ_BACK);
+ configuration_cec_message_read_get(cmd_buffer,
+ &cmd_length);
+ break;
+
+ case AV8100_COMMAND_DENC:
+ PRNK_MODE(AV8100_COMMAND_DENC);
+ configuration_denc_get(cmd_buffer, &cmd_length);
+ break;
+
+ case AV8100_COMMAND_HDMI:
+ PRNK_MODE(AV8100_COMMAND_HDMI);
+ configuration_hdmi_get(cmd_buffer, &cmd_length);
+ break;
+
+ case AV8100_COMMAND_HDCP_SENDKEY:
+ PRNK_MODE(AV8100_COMMAND_HDCP_SENDKEY);
+ configuration_hdcp_sendkey_get(cmd_buffer, &cmd_length);
+ break;
+
+ case AV8100_COMMAND_HDCP_MANAGEMENT:
+ PRNK_MODE(AV8100_COMMAND_HDCP_MANAGEMENT);
+ configuration_hdcp_management_get(cmd_buffer,
+ &cmd_length);
+ break;
+
+ case AV8100_COMMAND_INFOFRAMES:
+ PRNK_MODE(AV8100_COMMAND_INFOFRAMES);
+ configuration_infoframe_get(cmd_buffer, &cmd_length);
+ break;
+
+ case AV8100_COMMAND_EDID_SECTION_READBACK:
+ PRNK_MODE(AV8100_COMMAND_EDID_SECTION_READBACK);
+ av8100_edid_section_readback_get(cmd_buffer, &cmd_length);
+ break;
+
+ case AV8100_COMMAND_PATTERNGENERATOR:
+ PRNK_MODE(AV8100_COMMAND_PATTERNGENERATOR);
+ configuration_pattern_generator_get(cmd_buffer,
+ &cmd_length);
+ break;
+
+ case AV8100_COMMAND_FUSE_AES_KEY:
+ PRNK_MODE(AV8100_COMMAND_FUSE_AES_KEY);
+ configuration_fuse_aes_key_get(cmd_buffer, &cmd_length);
+ break;
+
+ default:
+ dev_dbg(av8100dev, "Invalid command type\n");
+ retval = AV8100_INVALID_COMMAND;
+ break;
+ }
+
+ LOCK_AV8100_HW;
+
+ if (if_type == I2C_INTERFACE) {
+ int cnt = 0;
+ int cnt_max;
+
+ dev_dbg(av8100dev, "av8100_conf_w cmd_type:%02x length:%02x ",
+ command_type, cmd_length);
+ dev_dbg(av8100dev, "buffer: ");
+ while (cnt < cmd_length) {
+ dev_dbg(av8100dev, "%02x ", cmd_buffer[cnt]);
+ cnt++;
+ }
+
+ /* Write the command buffer */
+ retval = write_multi_byte(i2c,
+ AV8100_CMD_BUF_OFFSET, cmd_buffer, cmd_length);
+ if (retval) {
+ UNLOCK_AV8100_HW;
+ return retval;
+ }
+
+ /* Write the command */
+ retval = write_single_byte(i2c, AV8100_COMMAND_OFFSET,
+ command_type);
+ if (retval) {
+ UNLOCK_AV8100_HW;
+ return retval;
+ }
+
+
+ /* Get the first return byte */
+ msleep(AV8100_WAITTIME_1MS);
+ cnt = 0;
+ cnt_max = ARRAY_SIZE(waittime_retry);
+ retval = get_command_return_first(i2c, command_type);
+ while (retval && (cnt++ < cnt_max)) {
+ msleep(waittime_retry[cnt]);
+ retval = get_command_return_first(i2c, command_type);
+ }
+ dev_dbg(av8100dev, "first return cnt:%d\n", cnt);
+
+ if (retval) {
+ UNLOCK_AV8100_HW;
+ return retval;
+ }
+
+ retval = get_command_return_data(i2c, command_type, cmd_buffer,
+ return_buffer_length, return_buffer);
+ } else if (if_type == DSI_INTERFACE) {
+ /* TODO */
+ } else {
+ retval = AV8100_INVALID_INTERFACE;
+ dev_dbg(av8100dev, "Invalid command type\n");
+ }
+
+ if (command_type == AV8100_COMMAND_HDMI) {
+ g_av8100_status.hdmi_on = ((av8100_config->hdmi_cmd.
+ hdmi_mode == AV8100_HDMI_ON) &&
+ (av8100_config->hdmi_cmd.hdmi_format == AV8100_HDMI));
+ }
+
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_conf_w);
+
+int av8100_conf_w_raw(enum av8100_command_type command_type,
+ u8 buffer_length,
+ u8 *buffer,
+ u8 *return_buffer_length,
+ u8 *return_buffer)
+{
+ int retval = 0;
+ struct i2c_client *i2c;
+ int cnt;
+ int cnt_max;
+
+ if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ LOCK_AV8100_HW;
+
+ if (return_buffer_length)
+ *return_buffer_length = 0;
+
+ if (!av8100_config) {
+ retval = AV8100_FAIL;
+ goto av8100_conf_w_raw_out;
+ }
+
+ i2c = av8100_config->client;
+
+ /* Write the command buffer */
+ retval = write_multi_byte(i2c,
+ AV8100_CMD_BUF_OFFSET, buffer, buffer_length);
+ if (retval)
+ goto av8100_conf_w_raw_out;
+
+ /* Write the command */
+ retval = write_single_byte(i2c, AV8100_COMMAND_OFFSET,
+ command_type);
+ if (retval)
+ goto av8100_conf_w_raw_out;
+
+
+ /* Get the first return byte */
+ msleep(AV8100_WAITTIME_1MS);
+ cnt = 0;
+ cnt_max = ARRAY_SIZE(waittime_retry);
+ retval = get_command_return_first(i2c, command_type);
+ while (retval && (cnt++ < cnt_max)) {
+ msleep(waittime_retry[cnt]);
+ retval = get_command_return_first(i2c, command_type);
+ }
+ dev_dbg(av8100dev, "first return cnt:%d\n", cnt);
+ if (retval)
+ goto av8100_conf_w_raw_out;
+
+ retval = get_command_return_data(i2c, command_type, buffer,
+ return_buffer_length, return_buffer);
+
+av8100_conf_w_raw_out:
+ UNLOCK_AV8100_HW;
+ return retval;
+}
+EXPORT_SYMBOL(av8100_conf_w_raw);
+
+struct av8100_status av8100_status_get(void)
+{
+ return g_av8100_status;
+}
+EXPORT_SYMBOL(av8100_status_get);
+
+enum av8100_output_CEA_VESA av8100_video_output_format_get(int xres,
+ int yres,
+ int htot,
+ int vtot,
+ int pixelclk,
+ bool interlaced)
+{
+ enum av8100_output_CEA_VESA index = 1;
+ int yres_div = !interlaced ? 1 : 2;
+ int hres_div = 1;
+ long freq1;
+ long freq2;
+
+ /*
+ * 720_576_I need a divider for hact and htot since
+ * these params need to be twice as large as expected in av8100_all_cea,
+ * which is used as input parameter to video input config.
+ */
+ if ((xres == 720) && (yres == 576) && (interlaced == true))
+ hres_div = 2;
+
+ freq1 = 1000000 / htot * 1000000 / vtot / pixelclk + 1;
+ while (index < sizeof(av8100_all_cea)/sizeof(struct av8100_cea)) {
+ freq2 = av8100_all_cea[index].frequence /
+ av8100_all_cea[index].htotale /
+ av8100_all_cea[index].vtotale;
+
+ dev_dbg(av8100dev, "freq1:%ld freq2:%ld\n", freq1, freq2);
+ if ((xres == av8100_all_cea[index].hactive / hres_div) &&
+ (yres == av8100_all_cea[index].vactive * yres_div) &&
+ (htot == av8100_all_cea[index].htotale / hres_div) &&
+ (vtot == av8100_all_cea[index].vtotale) &&
+ (abs(freq1 - freq2) < 2)) {
+ goto av8100_video_output_format_get_out;
+ }
+ index++;
+ }
+
+av8100_video_output_format_get_out:
+ dev_dbg(av8100dev, "av8100_video_output_format_get %d %d %d %d %d\n",
+ xres, yres, htot, vtot, index);
+ return index;
+}
+EXPORT_SYMBOL(av8100_video_output_format_get);
+
+void av8100_hdmi_event_cb_set(void (*hdmi_ev_cb)(enum av8100_hdmi_event))
+{
+ if (av8100_globals)
+ av8100_globals->hdmi_ev_cb = hdmi_ev_cb;
+}
+EXPORT_SYMBOL(av8100_hdmi_event_cb_set);
+
+u8 av8100_ver_get(void)
+{
+ u8 ret;
+
+ LOCK_AV8100_HW;
+ ret = chip_version;
+ UNLOCK_AV8100_HW;
+
+ return ret;
+}
+EXPORT_SYMBOL(av8100_ver_get);
+
+static const struct color_conversion_cmd *get_color_transform_cmd(
+ enum av8100_color_transform transform)
+{
+ const struct color_conversion_cmd *result;
+
+ switch (transform) {
+ case AV8100_COLOR_TRANSFORM_INDENTITY:
+ result = &col_trans_identity;
+ break;
+ case AV8100_COLOR_TRANSFORM_INDENTITY_CLAMP_YUV:
+ result = &col_trans_identity_clamp_yuv;
+ break;
+ case AV8100_COLOR_TRANSFORM_YUV_TO_RGB:
+ if (chip_version == AV8100_CHIPVER_1)
+ result = &col_trans_yuv_to_rgb_v1;
+ else
+ result = &col_trans_yuv_to_rgb_v2;
+ break;
+ case AV8100_COLOR_TRANSFORM_YUV_TO_DENC:
+ result = &col_trans_yuv_to_denc;
+ break;
+ case AV8100_COLOR_TRANSFORM_RGB_TO_DENC:
+ result = &col_trans_rgb_to_denc;
+ break;
+ default:
+ dev_warn(av8100dev, "Unknown color space transform\n");
+ result = &col_trans_identity;
+ break;
+ }
+ return result;
+}
+
+static int av8100_open(struct inode *inode, struct file *filp)
+{
+ dev_dbg(av8100dev, "av8100_open is called\n");
+ return 0;
+}
+
+static int av8100_release(struct inode *inode, struct file *filp)
+{
+ dev_dbg(av8100dev, "av8100_release is called\n");
+ return 0;
+}
+
+static int av8100_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return 0;
+}
+
+static int __devinit av8100_probe(struct i2c_client *i2cClient,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct av8100_platform_data *pdata = i2cClient->dev.platform_data;
+
+ av8100dev = &i2cClient->dev;
+
+ dev_dbg(av8100dev, "%s\n", __func__);
+
+ av8100_set_state(AV8100_OPMODE_UNDEFINED);
+
+ ret = av8100_config_init();
+ if (ret) {
+ dev_info(av8100dev, "av8100_config_init failed\n");
+ goto err;
+ }
+
+ ret = av8100_globals_init();
+ if (ret) {
+ dev_info(av8100dev, "av8100_globals_init failed\n");
+ goto err;
+ }
+
+ if (!i2c_check_functionality(i2cClient->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+ ret = -ENODEV;
+ dev_info(av8100dev, "av8100 i2c_check_functionality failed\n");
+ goto err;
+ }
+
+ init_waitqueue_head(&av8100_event);
+
+ av8100_config->client = i2cClient;
+ av8100_config->id = (struct i2c_device_id *) id;
+ i2c_set_clientdata(i2cClient, av8100_config);
+
+ kthread_run(av8100_thread, NULL, "av8100_thread");
+
+ ret = request_irq(pdata->irq, av8100_intr_handler,
+ IRQF_TRIGGER_RISING, "av8100", av8100_config);
+ if (ret) {
+ dev_err(av8100dev, "av8100_hw request_irq %d failed %d\n",
+ pdata->irq, ret);
+ gpio_free(pdata->irq);
+ goto err;
+ }
+
+ /* Get regulator resource */
+ if (pdata->regulator_pwr_id) {
+ av8100_globals->regulator_pwr = regulator_get(av8100dev,
+ pdata->regulator_pwr_id);
+ if (IS_ERR(av8100_globals->regulator_pwr)) {
+ ret = PTR_ERR(av8100_globals->regulator_pwr);
+ dev_warn(av8100dev,
+ "%s: Failed to get regulator '%s'\n",
+ __func__, pdata->regulator_pwr_id);
+ av8100_globals->regulator_pwr = NULL;
+ return ret;
+ }
+ }
+
+ /* Get clock resource */
+ if (pdata->inputclk_id) {
+ av8100_globals->inputclk = clk_get(NULL, pdata->inputclk_id);
+ if (IS_ERR(av8100_globals->inputclk)) {
+ av8100_globals->inputclk = NULL;
+ dev_warn(av8100dev, "%s: Failed to get clock '%s'\n",
+ __func__, pdata->inputclk_id);
+ }
+ }
+
+ av8100_set_state(AV8100_OPMODE_SHUTDOWN);
+
+ /* Obtain the chip version */
+ if (av8100_powerup1()) {
+ dev_err(av8100dev, "av8100_powerup1 fail\n");
+ return -EFAULT;
+ }
+
+ ret = av8100_powerdown();
+
+err:
+ return ret;
+}
+
+static int __devexit av8100_remove(struct i2c_client *i2cClient)
+{
+ dev_dbg(av8100dev, "%s\n", __func__);
+
+ av8100_config_exit();
+ av8100_globals_exit();
+ if (av8100_globals->inputclk)
+ clk_put(av8100_globals->inputclk);
+
+ /* Release regulator resource */
+ if (av8100_globals->regulator_pwr)
+ regulator_put(av8100_globals->regulator_pwr);
+
+ return 0;
+}
+
+int av8100_init(void)
+{
+ int ret;
+
+ pr_debug("%s\n", __func__);
+
+ ret = i2c_add_driver(&av8100_driver);
+ if (ret) {
+ pr_err("av8100 i2c_add_driver failed\n");
+ goto av8100_init_err;
+ }
+
+ ret = misc_register(&av8100_miscdev);
+ if (ret) {
+ pr_err("av8100 misc_register failed\n");
+ goto av8100_init_err;
+ }
+
+ return ret;
+
+av8100_init_err:
+ return ret;
+}
+module_init(av8100_init);
+
+void av8100_exit(void)
+{
+ pr_debug("%s\n", __func__);
+
+ misc_deregister(&av8100_miscdev);
+ i2c_del_driver(&av8100_driver);
+}
+module_exit(av8100_exit);
+
+MODULE_AUTHOR("Per Persson <per.xb.persson@stericsson.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ST-Ericsson hdmi display driver");
diff --git a/drivers/video/av8100/av8100_fw.h b/drivers/video/av8100/av8100_fw.h
new file mode 100644
index 00000000000..2beb0736922
--- /dev/null
+++ b/drivers/video/av8100/av8100_fw.h
@@ -0,0 +1,1165 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * Author: Per Persson <per.xb.persson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+/* AV8100 Firmware version : Unified version V2.2 for av8100v1 and av8100v2.x */
+#define AV8100_FW_SIZE 18432
+char av8100_fw_buff[AV8100_FW_SIZE] = {
+0x80,0xfe,0xcb,0xfe,0x6b,0xf4,0x22,0xf6,0x22,0xf6,0xb9,0xf7,0x21,0xf5,0xbe,0xf7,
+0x06,0xf9,0xc8,0xfa,0xe5,0xfa,0xf6,0xfa,0x08,0xfb,0x1b,0xfb,0x20,0xfb,0x25,0xfb,
+0x2a,0xfb,0x2f,0xfb,0x44,0xfb,0x7e,0xfb,0x23,0xfc,0x65,0xf4,0x65,0xf4,0x66,0xf4,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x81,0xea,0xad,0xec,0xad,
+0x38,0x20,0x1b,0x72,0x80,0x81,0x4d,0x51,0x4d,0x51,0x4d,0x51,0x4d,0x51,0x4d,0x51,
+0x4d,0x51,0xb4,0x20,0xf4,0x2a,0x5a,0x90,0x82,0xfe,0xcd,0x80,0x00,0xaf,0x72,0x93,
+0xa3,0x20,0x5f,0x90,0x80,0x80,0x97,0x90,0x8d,0x20,0x0f,0xab,0x01,0xa6,0x88,0x89,
+0x90,0x96,0xad,0x97,0x90,0x9a,0xad,0x8b,0x93,0x9e,0xad,0x97,0x90,0xa2,0xad,0x80,
+0xb7,0xa6,0xad,0x81,0xb7,0xaa,0xad,0x82,0xb7,0xae,0xad,0x26,0x31,0x20,0x08,0x72,
+0x82,0xb7,0x21,0xa6,0x81,0xb7,0xc2,0xa6,0x80,0xb7,0x01,0xa6,0xb4,0x20,0xf5,0x2a,
+0x5a,0x90,0x80,0x00,0xa7,0x72,0x93,0xcc,0xad,0x49,0x27,0x97,0x90,0x5d,0x90,0xd4,
+0xad,0x82,0xb7,0xd8,0xad,0x81,0xb7,0xdc,0xad,0x80,0xb7,0xe0,0xad,0xff,0xae,0x90,
+0x81,0x59,0x29,0xea,0x26,0x5a,0xfb,0x38,0x20,0x07,0x72,0x4d,0x38,0x20,0x1a,0x72,
+0x49,0x00,0x38,0x20,0x05,0x72,0xbc,0xff,0xcd,0x08,0xae,0xf3,0x20,0x79,0x24,0x49,
+0x44,0x24,0x49,0x23,0x24,0x49,0x0b,0xad,0x82,0xfe,0xcd,0x9e,0x82,0xfe,0xcd,0x9f,
+0x5b,0x82,0xfe,0xcd,0x80,0xb6,0x82,0xfe,0xcd,0x81,0xb6,0x82,0xfe,0xcd,0x82,0xb6,
+0x82,0xad,0x9f,0x90,0x86,0xad,0x85,0x90,0x84,0x42,0x8c,0x20,0x9f,0x90,0x01,0xab,
+0x0f,0xa6,0x2a,0x31,0x20,0x08,0x72,0xfb,0x38,0x20,0x07,0x72,0x4d,0x38,0x20,0x1a,
+0x72,0xbc,0xff,0xcd,0xfb,0x38,0x20,0x07,0x72,0x30,0x20,0x10,0x72,0x31,0x20,0x1e,
+0x72,0x30,0x20,0x1c,0x72,0x95,0x05,0xa0,0x9e,0x3d,0x18,0xcc,0x5f,0x90,0x5f,0x4f,
+0x07,0x38,0x20,0x05,0x72,0x0c,0x39,0x20,0x05,0x72,0x81,0x41,0x29,0x38,0x20,0x18,
+0x72,0xd5,0x26,0x5a,0xfb,0x38,0x20,0x07,0x72,0x9d,0x9d,0x4d,0x38,0x20,0x1a,0x72,
+0x4d,0xae,0xff,0xcd,0x4d,0xae,0xff,0xcd,0x38,0x20,0x1b,0x72,0x4d,0x4d,0x4d,0x4d,
+0x4d,0x38,0x20,0x19,0x72,0x04,0x25,0x49,0x38,0x20,0x18,0x72,0x08,0xae,0x34,0x20,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x81,0xa5,0xbe,0x84,0xa3,0xb7,0xa4,0xd6,0x92,0x5c,0xa2,0xb7,0xa4,
+0xd6,0x92,0x5c,0xa1,0xb7,0xa4,0xd6,0x92,0x01,0xae,0xa0,0xb7,0xa4,0xc6,0x92,0x88,
+0xa5,0xbf,0x81,0xa0,0xb7,0xa4,0xc2,0x92,0xa0,0xb6,0xa1,0xb7,0xa4,0xd2,0x92,0x5a,
+0xa1,0xb6,0xa2,0xb7,0xa4,0xd2,0x92,0x5a,0xa2,0xb6,0xa3,0xb7,0xa4,0xd0,0x92,0xa3,
+0xb6,0x03,0xae,0xa5,0xbf,0x81,0xa3,0xb7,0xa4,0xda,0x92,0x5c,0xa3,0xb6,0xa2,0xb7,
+0xa4,0xda,0x92,0x5c,0xa2,0xb6,0xa1,0xb7,0xa4,0xda,0x92,0x01,0xae,0xa1,0xb6,0xa0,
+0xb7,0xa4,0xca,0x92,0xa0,0xb6,0xa5,0xbf,0xf1,0xfc,0xcc,0xd8,0xfd,0xcd,0x81,0xa5,
+0xbe,0xe9,0x26,0x4a,0xa4,0x00,0x39,0x72,0xa4,0x00,0x69,0x72,0x5a,0xa4,0x00,0x69,
+0x72,0x5a,0xa4,0x00,0x68,0x72,0x03,0xae,0xa5,0xbf,0x1b,0x27,0x4d,0x81,0xa0,0xb7,
+0xa4,0xc9,0x92,0xa0,0xb6,0xa1,0xb7,0xa4,0xd9,0x92,0x5a,0xa1,0xb6,0xa2,0xb7,0xa4,
+0xd9,0x92,0x5a,0xa2,0xb6,0xa3,0xb7,0xa4,0xdb,0x92,0xa3,0xb6,0x03,0xae,0xa5,0xbf,
+0xf1,0xfc,0xcc,0x90,0xfd,0xcd,0x81,0xf1,0x20,0x01,0xab,0xa4,0xd6,0x92,0x07,0x2b,
+0x5a,0x0a,0x24,0xa4,0xd7,0x92,0xa4,0xdb,0x92,0x03,0xae,0xa5,0xbf,0x81,0x01,0xa6,
+0x02,0x20,0xff,0xa6,0x04,0x24,0x08,0x27,0xa4,0xd1,0x92,0xa3,0xb6,0xa5,0x3c,0x09,
+0x26,0xa4,0xd1,0x92,0xa2,0xb6,0xa5,0x3c,0x12,0x26,0xa4,0xd1,0x92,0xa1,0xb6,0xa5,
+0x3c,0x23,0x26,0xa4,0xd1,0x92,0xa0,0xb6,0xa5,0x3f,0x81,0xa0,0x3c,0x02,0x24,0xa1,
+0xb7,0xa1,0xb9,0x9f,0xa2,0xb7,0xa2,0xbb,0x42,0x93,0x0f,0x27,0x4d,0x84,0x37,0xfd,
+0xcd,0xa8,0xb6,0x05,0x27,0xa7,0xbe,0xa3,0xb7,0xa2,0xbf,0x42,0x93,0xa8,0xb6,0xa1,
+0xb7,0xa0,0xbf,0x42,0xa7,0xb6,0x89,0xa8,0xb7,0x81,0xa0,0x3f,0xa1,0x3f,0xa2,0xbf,
+0xa3,0xb7,0x81,0xa5,0xbe,0x84,0xa4,0xd7,0x92,0xa3,0xb6,0x5c,0xa4,0xd7,0x92,0xa2,
+0xb6,0x5c,0xa4,0xd7,0x92,0xa1,0xb6,0x01,0xae,0xa4,0xc7,0x92,0xa0,0xb6,0x88,0xa5,
+0xbf,0x81,0xa5,0xbe,0xa4,0xd7,0x92,0xa3,0xb6,0x5c,0xa4,0xd7,0x92,0xa2,0xb6,0x5c,
+0xa4,0xd7,0x92,0xa1,0xb6,0x01,0xae,0xa4,0xc7,0x92,0xa0,0xb6,0xa5,0xbf,0x81,0x01,
+0xa6,0x02,0x27,0x03,0x6d,0x04,0x26,0x02,0x6d,0x08,0x26,0x01,0x6d,0x0e,0x26,0x7d,
+0x81,0x84,0xa3,0xb7,0x03,0xe6,0xa2,0xb7,0x02,0xe6,0xa1,0xb7,0x01,0xe6,0xa0,0xb7,
+0xf6,0x88,0x81,0x01,0xa6,0x02,0x27,0xa3,0x3d,0x04,0x26,0xa2,0x3d,0x08,0x26,0xa1,
+0x3d,0x0e,0x26,0xa0,0x3d,0x81,0xf5,0x26,0x4a,0xa0,0x39,0xa1,0x39,0xa2,0x39,0xa3,
+0x38,0x0b,0x27,0x4d,0x81,0xa0,0x3f,0xa1,0x3f,0x81,0xa0,0xb7,0xa1,0xb7,0xff,0xa6,
+0x07,0x2a,0xa2,0xbf,0xa3,0xb7,0x81,0x41,0xa8,0xbb,0x41,0x52,0x93,0x84,0xa8,0xb7,
+0xa8,0xbb,0x52,0x01,0x7b,0xa7,0xbe,0xa8,0xb7,0x52,0x9f,0x90,0x88,0x81,0xa5,0xbe,
+0xa3,0xb7,0xa4,0xd6,0x92,0x5c,0xa2,0xb7,0xa4,0xd6,0x92,0x5c,0xa1,0xb7,0xa4,0xd6,
+0x92,0x01,0xae,0xa0,0xb7,0xa4,0xc6,0x92,0xa5,0xbf,0x81,0xa0,0x3f,0xa2,0xb7,0xa1,
+0xbf,0x5c,0x01,0x24,0xa2,0xbb,0x42,0x93,0xa0,0xb6,0xa3,0xb7,0xa2,0xbf,0x42,0x93,
+0xa0,0xbf,0x80,0xaa,0x00,0xc7,0x51,0x20,0xc6,0x50,0x20,0x11,0x72,0x80,0x85,0x90,
+0xa6,0x00,0x32,0xa5,0x00,0x32,0xa4,0x00,0x32,0xa9,0x00,0x32,0xa8,0x00,0x32,0xa7,
+0x00,0x32,0x84,0x84,0x84,0x84,0x00,0x18,0xcd,0x63,0xa6,0x05,0x20,0xda,0x01,0x5a,
+0x72,0xd9,0x01,0x5a,0x72,0x04,0x26,0xda,0x01,0xc6,0x0f,0x27,0xd9,0x01,0xca,0xda,
+0x01,0xc6,0x00,0x18,0xcd,0x12,0xa6,0xc0,0x00,0x5f,0x72,0x09,0x27,0xc0,0x00,0xc6,
+0xa2,0x01,0x5f,0x72,0xd9,0x01,0x5f,0x72,0xda,0x01,0x18,0x35,0x08,0x25,0x00,0xa2,
+0xd9,0x01,0xc6,0x19,0xa0,0xda,0x01,0xc6,0x18,0x20,0xa2,0x01,0x01,0x35,0x06,0x27,
+0x10,0xa5,0x04,0x7b,0x01,0x6b,0x10,0x40,0xc6,0x02,0x6b,0x11,0x40,0xc6,0x03,0x6b,
+0x12,0x40,0xc6,0x04,0x6b,0x13,0x40,0xc6,0xe8,0xc4,0xcd,0x46,0x20,0xc7,0x46,0x20,
+0xc6,0x41,0x20,0x5f,0x72,0x0a,0x42,0x20,0x0d,0x72,0x88,0x88,0x88,0x88,0xa7,0x00,
+0x3b,0xa8,0x00,0x3b,0xa9,0x00,0x3b,0xa4,0x00,0x3b,0xa5,0x00,0x3b,0xa6,0x00,0x3b,
+0x89,0x90,0x80,0x85,0x90,0xa6,0x00,0x32,0xa5,0x00,0x32,0xa4,0x00,0x32,0xa9,0x00,
+0x32,0xa8,0x00,0x32,0xa7,0x00,0x32,0x00,0x18,0xcd,0x15,0x20,0x10,0x72,0x07,0x27,
+0x09,0x2b,0x10,0xb6,0x12,0x20,0x01,0x35,0xa7,0x00,0x3b,0xa8,0x00,0x3b,0xa9,0x00,
+0x3b,0xa4,0x00,0x3b,0xa5,0x00,0x3b,0xa6,0x00,0x3b,0x89,0x90,0x80,0x0c,0x40,0x00,
+0x35,0x0d,0x40,0x00,0x35,0x0e,0x40,0x00,0x35,0x0f,0x40,0x04,0x35,0x11,0x20,0x80,
+0x35,0x80,0x11,0x20,0x40,0x35,0x80,0x11,0x20,0x20,0x35,0x80,0x11,0x20,0x10,0x35,
+0x80,0x11,0x20,0x08,0x35,0x80,0x17,0x20,0x14,0x72,0x80,0x17,0x20,0x15,0x72,0x05,
+0x17,0x20,0x05,0x72,0x11,0x20,0x04,0x35,0x80,0xe7,0x01,0xc7,0x4a,0x05,0x20,0x1a,
+0x72,0x06,0x20,0x18,0x72,0x11,0x20,0xc7,0x02,0xa6,0x80,0xe7,0x01,0xc7,0x05,0x20,
+0x1a,0x72,0x06,0x20,0x19,0x72,0x11,0x20,0xc7,0x01,0xa6,0x80,0x07,0x20,0x01,0x35,
+0x04,0x88,0x00,0x35,0x05,0x88,0x30,0x35,0x06,0x88,0x00,0x35,0x07,0x88,0x00,0x35,
+0x10,0x20,0x80,0x35,0x07,0x20,0x03,0x35,0x81,0xef,0xfc,0xcd,0xbc,0xae,0xa4,0x00,
+0x84,0x35,0x81,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x80,0x85,0x90,0xa6,0x00,0x32,
+0xa5,0x00,0x32,0xa4,0x00,0x32,0xa9,0x00,0x32,0xa8,0x00,0x32,0xa7,0x00,0x32,0xa3,
+0x00,0x32,0xa2,0x00,0x32,0xa1,0x00,0x32,0xa0,0x00,0x32,0x84,0x84,0x84,0x84,0x84,
+0x84,0xef,0xfc,0xcd,0xa0,0xae,0xa4,0x00,0x84,0x35,0xd8,0xfd,0xcd,0x33,0xad,0x5b,
+0xa0,0x3f,0xa1,0x3f,0xa2,0x1f,0x0e,0xfd,0xcd,0xf4,0x01,0xce,0xf5,0x01,0xc6,0x03,
+0x6b,0x80,0xa4,0x03,0x7b,0x04,0x6b,0x4f,0x01,0x6b,0xa0,0x84,0xc6,0x02,0x6b,0xa1,
+0x84,0xc6,0x03,0x6b,0xa2,0x84,0xc6,0x04,0x6b,0xa3,0x84,0xc6,0x84,0x84,0xf4,0x01,
+0xcf,0xf5,0x01,0xc7,0x4d,0xc5,0xcd,0xbc,0x00,0xce,0xbd,0x00,0xc6,0xf6,0x01,0x3b,
+0xf7,0x01,0x3b,0x0f,0x20,0x31,0xc5,0xcd,0xbc,0x00,0xce,0xbd,0x00,0xc6,0xf6,0x01,
+0x3b,0xf7,0x01,0x3b,0x11,0x24,0x05,0xe2,0x72,0xf6,0x01,0xc6,0x06,0xe0,0x72,0xf7,
+0x01,0xc6,0x1f,0x24,0x05,0xe2,0x72,0xbc,0x00,0xc6,0x06,0xe0,0x72,0xbd,0x00,0xc6,
+0x1c,0x25,0xf6,0x01,0xc2,0x05,0x7b,0xf7,0x01,0xc0,0x06,0x7b,0x0c,0x24,0xbc,0x00,
+0xc2,0x05,0x7b,0xbd,0x00,0xc0,0x06,0x7b,0x5c,0x20,0xf4,0x01,0xcf,0xbc,0x00,0xce,
+0xf5,0x01,0xbd,0x00,0x55,0x0d,0x26,0x71,0x01,0xc6,0x0d,0x20,0xf6,0x01,0xce,0xf5,
+0x01,0xc7,0xf7,0x01,0xc6,0x0b,0x27,0x4a,0x70,0x01,0xc6,0x07,0x20,0x01,0x35,0x07,
+0x20,0x03,0x35,0x08,0x26,0x04,0xa1,0x05,0x20,0x1d,0x72,0x05,0x20,0x1c,0x72,0x08,
+0x25,0x02,0xa1,0xbb,0x00,0xc6,0x1b,0x26,0xbc,0x00,0xc1,0x05,0x7b,0x22,0x26,0xbd,
+0x00,0xc1,0x06,0x7b,0x29,0x26,0x4a,0x70,0x01,0xc6,0xf6,0x01,0x5f,0x72,0xf7,0x01,
+0xc7,0x06,0xa6,0xbe,0xfa,0xcd,0xa2,0xb7,0x18,0xaa,0xa2,0xb6,0x1e,0xfe,0xcd,0xb6,
+0xfa,0xcd,0x5b,0x12,0x20,0x0a,0xa6,0xbe,0xfa,0xcd,0xa2,0x18,0x1e,0xfe,0xcd,0xb6,
+0xfa,0xcd,0x5b,0x10,0x24,0x00,0xa2,0x05,0x7b,0x0a,0xa0,0x06,0x7b,0x1a,0x25,0x00,
+0xa2,0x05,0x7b,0x06,0xa0,0x06,0x7b,0x03,0x6b,0xc7,0xa4,0x03,0x7b,0x01,0x6b,0xbc,
+0x84,0xc6,0x02,0x6b,0xbd,0x84,0xc6,0x03,0x6b,0xbe,0x84,0xc6,0x04,0x6b,0xbf,0x84,
+0xc6,0x57,0x27,0x71,0x01,0xc6,0x06,0x6b,0xa7,0x83,0xc6,0x05,0x6b,0xa6,0x83,0xc6,
+0xf5,0x01,0x5f,0x72,0xf4,0x01,0x5f,0x72,0xf7,0x01,0x5f,0x72,0xf6,0x01,0x5f,0x72,
+0x10,0x20,0x40,0x35,0x88,0x88,0x88,0x88,0x88,0x88,0xa0,0x00,0x3b,0xa1,0x00,0x3b,
+0xa2,0x00,0x3b,0xa3,0x00,0x3b,0xa7,0x00,0x3b,0xa8,0x00,0x3b,0xa9,0x00,0x3b,0xa4,
+0x00,0x3b,0xa5,0x00,0x3b,0xa6,0x00,0x3b,0x89,0x90,0x81,0x48,0x83,0x00,0x35,0x49,
+0x83,0x00,0x35,0x4a,0x83,0x00,0x35,0x4b,0x83,0x0f,0x35,0x80,0x85,0x90,0xa6,0x00,
+0x32,0xa5,0x00,0x32,0xa4,0x00,0x32,0xa9,0x00,0x32,0xa8,0x00,0x32,0xa7,0x00,0x32,
+0x84,0x84,0x84,0x84,0xe8,0x01,0x5f,0x72,0x48,0xcb,0xcd,0x20,0xad,0x05,0x26,0x4a,
+0xe8,0x01,0xc6,0x0b,0x26,0x03,0xa1,0x70,0x01,0xc6,0xf0,0x01,0x5c,0x72,0x04,0x27,
+0x0f,0xa1,0xf0,0x01,0xc6,0x0b,0x20,0xe7,0x01,0x01,0x35,0x11,0x26,0xf0,0x01,0x5a,
+0x72,0x17,0x27,0xf0,0x01,0xc6,0x11,0x26,0x03,0xea,0x72,0x04,0x7b,0x07,0x27,0x4c,
+0x03,0x7b,0x05,0x26,0x4c,0x04,0x7b,0x11,0x27,0x03,0x7b,0x04,0x26,0x4a,0x04,0x7b,
+0x74,0xe1,0xcd,0x03,0xee,0x72,0x9f,0x03,0x6b,0x04,0xef,0x72,0x03,0xe2,0x72,0x41,
+0x04,0xe0,0x72,0x5f,0x01,0xa6,0x03,0x6b,0x01,0xe2,0x72,0x03,0x7b,0x04,0x6b,0x02,
+0xe0,0x72,0x04,0x7b,0x0e,0x24,0x03,0xe2,0x72,0x9f,0x04,0xe0,0x72,0x46,0x54,0x01,
+0xef,0x72,0x02,0x6b,0x46,0x54,0x4e,0x01,0xce,0x4f,0x01,0xc6,0x08,0x20,0x4e,0x01,
+0xce,0x4f,0x01,0xc6,0x08,0x26,0x4a,0x47,0x01,0xc6,0xe3,0x26,0x03,0xe1,0x72,0xa6,
+0x83,0xc6,0xeb,0x26,0x04,0xe1,0x72,0xa7,0x83,0xc6,0x04,0x6b,0xa7,0x83,0xc6,0x03,
+0x6b,0xa6,0x83,0xc6,0xc6,0xf8,0xcc,0x84,0x84,0x84,0x84,0xaf,0x00,0x35,0x35,0xb0,
+0x00,0x86,0x35,0xb1,0x00,0x37,0x35,0xb2,0x00,0xbd,0x35,0xf0,0x01,0x0f,0x35,0xf2,
+0x01,0x01,0x35,0xf5,0xf8,0xcd,0x69,0xc5,0xcd,0xb3,0x00,0x3b,0xb4,0x00,0x3b,0xb5,
+0x00,0x3b,0xb6,0x00,0x3b,0x31,0x26,0xf2,0x01,0xc6,0x16,0xcb,0xcd,0x03,0x27,0xf0,
+0x01,0xc6,0xdc,0xf8,0xcc,0x03,0x26,0x02,0xa1,0xe6,0x01,0xc6,0xc6,0xf8,0xcc,0x03,
+0x27,0x02,0xa1,0x70,0x01,0xc6,0x10,0x20,0x20,0x35,0x88,0x88,0x88,0x88,0xa7,0x00,
+0x3b,0xa8,0x00,0x3b,0xa9,0x00,0x3b,0xa4,0x00,0x3b,0xa5,0x00,0x3b,0xa6,0x00,0x3b,
+0x89,0x90,0x80,0x10,0x20,0x08,0x35,0x81,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,
+0x02,0xab,0x80,0x85,0x90,0xa6,0x00,0x32,0xa5,0x00,0x32,0xa4,0x00,0x32,0xa9,0x00,
+0x32,0xa8,0x00,0x32,0xa7,0x00,0x32,0xa3,0x00,0x32,0xa2,0x00,0x32,0xa1,0x00,0x32,
+0xa0,0x00,0x32,0x84,0x84,0x84,0x84,0x84,0xef,0xfc,0xcd,0xa4,0xbf,0x84,0xae,0xa3,
+0x1a,0x2f,0xad,0x5b,0x09,0x20,0x04,0xae,0xa4,0x00,0x44,0x35,0xa0,0x3f,0x3c,0xad,
+0x5b,0x02,0x6b,0x08,0x84,0xc6,0x03,0x6b,0x09,0x84,0xc6,0x04,0x6b,0x0a,0x84,0xc6,
+0x05,0x6b,0x0b,0x84,0xc6,0xef,0xfc,0xcd,0xa4,0xbf,0x84,0xae,0xa3,0x1b,0x5c,0xad,
+0x5b,0x2d,0x27,0x72,0x01,0xc6,0x32,0x27,0xc2,0x00,0xc6,0x02,0x6b,0x84,0x84,0xc6,
+0x03,0x6b,0x85,0x84,0xc6,0x04,0x6b,0x86,0x84,0xc6,0x05,0x6b,0x87,0x84,0xc6,0x84,
+0x84,0x84,0xd3,0xcc,0xcd,0x41,0x00,0xa9,0x41,0xd4,0xab,0x5f,0x52,0x21,0xae,0xd2,
+0x00,0xc6,0x02,0x4b,0x10,0x4b,0x00,0x4b,0xd2,0x00,0x5c,0x72,0x04,0x20,0xd2,0x00,
+0x5f,0x72,0x06,0x26,0xa8,0xb1,0xa8,0xbf,0x90,0x0d,0x26,0xa7,0xb3,0xa7,0x3f,0xd2,
+0x00,0xce,0x90,0x4a,0x5a,0x01,0x26,0x4d,0x5f,0x3a,0x25,0x02,0xa1,0xd3,0x00,0xc6,
+0xd6,0xca,0xcd,0xe7,0x01,0x5f,0x72,0x07,0x26,0x4a,0xe7,0x01,0xc6,0x06,0x27,0x4a,
+0x70,0x01,0xc6,0xc6,0xd8,0xcd,0xe9,0x01,0xc7,0x06,0x26,0x4a,0xe9,0x01,0xc6,0x86,
+0x01,0x5c,0x72,0x04,0x27,0x4c,0x86,0x01,0xc6,0x0a,0x20,0x86,0x01,0x5f,0x72,0x06,
+0x27,0xf0,0x01,0xc6,0xf1,0x01,0x5c,0x72,0x04,0x24,0xff,0xa1,0xf1,0x01,0xc6,0xe8,
+0x01,0x01,0x35,0xef,0xfc,0xcd,0xa0,0xae,0xa4,0x00,0x84,0x35,0xa0,0x1f,0xae,0xf7,
+0xcd,0x5b,0x02,0x6b,0xa0,0x84,0xc6,0x03,0x6b,0xa1,0x84,0xc6,0x04,0x6b,0xa2,0x84,
+0xc6,0x05,0x6b,0xa3,0x84,0xc6,0x84,0x84,0x84,0x84,0x69,0xc5,0xcd,0xb3,0x00,0x3b,
+0xb4,0x00,0x3b,0xb5,0x00,0x3b,0xb6,0x00,0x3b,0xf2,0x01,0x5f,0x72,0x41,0x25,0x04,
+0xa1,0xf1,0x01,0xc6,0x65,0x27,0x4a,0x70,0x01,0xc6,0xef,0xfc,0xcd,0x9c,0xae,0xa4,
+0x00,0x83,0x35,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,0xb7,0x01,0x6b,0x9f,0x83,
+0xc6,0x05,0x20,0x14,0x72,0x10,0x20,0x06,0x35,0x88,0x88,0x88,0x88,0x88,0xa0,0x00,
+0x3b,0xa1,0x00,0x3b,0xa2,0x00,0x3b,0xa3,0x00,0x3b,0xa7,0x00,0x3b,0xa8,0x00,0x3b,
+0xa9,0x00,0x3b,0xa4,0x00,0x3b,0xa5,0x00,0x3b,0xa6,0x00,0x3b,0x89,0x90,0x81,0xa4,
+0x00,0x8c,0x35,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,0xb7,0x80,0x85,0x90,0xa6,
+0x00,0x32,0xa5,0x00,0x32,0xa4,0x00,0x32,0xa9,0x00,0x32,0xa8,0x00,0x32,0xa7,0x00,
+0x32,0xa3,0x00,0x32,0xa2,0x00,0x32,0xa1,0x00,0x32,0xa0,0x00,0x32,0x84,0xef,0xfc,
+0xcd,0x0c,0xae,0x27,0xad,0xf0,0xa4,0x01,0x7b,0x87,0x01,0x5f,0x72,0x05,0x20,0x16,
+0x72,0x06,0x20,0xc7,0x05,0xaa,0x06,0x20,0xc6,0x1b,0x27,0x04,0xa5,0x01,0x7b,0xef,
+0xfc,0xcd,0x0c,0xae,0x48,0xad,0xf0,0xa4,0x05,0x20,0x16,0x72,0x06,0x20,0x14,0x72,
+0x11,0x20,0x87,0x01,0x5a,0x72,0x0f,0xf5,0xcd,0x02,0xa6,0x05,0x26,0x4a,0x05,0x20,
+0x4f,0x03,0x25,0x02,0xa1,0x87,0x01,0xc6,0xef,0xfc,0xcd,0x10,0xae,0x71,0xad,0x88,
+0x01,0xd6,0x22,0x27,0x87,0x01,0xce,0x59,0x27,0x0f,0xa5,0x01,0x7b,0xef,0xfc,0xcd,
+0x0c,0xae,0x14,0xf6,0xcd,0x0f,0xa4,0x05,0x20,0x16,0x72,0x06,0x20,0x12,0x72,0x74,
+0x01,0x01,0x35,0x18,0x27,0x20,0xa5,0x01,0x7b,0x98,0xad,0x4f,0x05,0x27,0x80,0xa5,
+0x01,0x7b,0x76,0x01,0xd7,0x17,0x8c,0xc6,0x75,0x01,0x5c,0x72,0x75,0x01,0xce,0x0f,
+0x27,0x80,0xa5,0x07,0x20,0x75,0x01,0xcf,0x06,0x20,0x13,0x72,0x09,0x27,0x10,0xa5,
+0x20,0x26,0x74,0x01,0xce,0x01,0x6b,0x0f,0x8c,0xc6,0x10,0x20,0x10,0x35,0x88,0xa0,
+0x00,0x3b,0xa1,0x00,0x3b,0xa2,0x00,0x3b,0xa3,0x00,0x3b,0xa7,0x00,0x3b,0xa8,0x00,
+0x3b,0xa9,0x00,0x3b,0xa4,0x00,0x3b,0xa5,0x00,0x3b,0xa6,0x00,0x3b,0x89,0x90,0xef,
+0xfc,0xcc,0x0c,0xae,0xa4,0x00,0x8c,0x35,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,
+0xb7,0x80,0x85,0x90,0xa6,0x00,0x32,0xa5,0x00,0x32,0xa4,0x00,0x32,0xa9,0x00,0x32,
+0xa8,0x00,0x32,0xa7,0x00,0x32,0x84,0x84,0x84,0x84,0xa0,0x84,0xc7,0x01,0x7b,0xa1,
+0x84,0xc7,0x02,0x7b,0xa2,0x84,0xc7,0x03,0x7b,0xa3,0x84,0xc7,0x04,0x7b,0xe8,0x01,
+0x01,0x35,0x01,0x6b,0x7f,0xa4,0x01,0x7b,0x0a,0x20,0x01,0x6b,0x80,0xaa,0x01,0x7b,
+0x12,0x27,0x02,0xa1,0xe6,0x01,0xc6,0x0f,0x26,0x03,0xa1,0x04,0x27,0x02,0xa1,0x70,
+0x01,0xc6,0x1a,0x27,0x71,0x01,0xc6,0x05,0x26,0x72,0x01,0xc6,0x01,0x6b,0xa0,0x84,
+0xc6,0x02,0x6b,0xa1,0x84,0xc6,0x03,0x6b,0xa2,0x84,0xc6,0x04,0x6b,0xa3,0x84,0xc6,
+0xf1,0x01,0x5f,0x72,0xe6,0x01,0xc7,0x03,0xa4,0x83,0x84,0xc6,0xfe,0xc5,0xcd,0x03,
+0x26,0x4a,0xe5,0x01,0xc6,0x05,0x20,0x12,0x72,0x10,0x20,0x01,0x35,0x88,0x88,0x88,
+0x88,0xa7,0x00,0x3b,0xa8,0x00,0x3b,0xa9,0x00,0x3b,0xa4,0x00,0x3b,0xa5,0x00,0x3b,
+0xa6,0x00,0x3b,0x89,0x90,0x80,0x12,0x20,0x10,0x35,0x80,0xfb,0xf2,0xcc,0x28,0x4b,
+0x07,0xce,0xcd,0xff,0xf2,0xcc,0xc1,0xa6,0x00,0x4b,0x08,0x4b,0x72,0xce,0xcd,0xff,
+0xf2,0xcc,0xe0,0xa6,0x01,0x4b,0xe0,0x4b,0x80,0xf3,0xcc,0x03,0x26,0x9a,0x01,0x5a,
+0x72,0x81,0x84,0x84,0x9a,0x01,0x02,0x35,0xa0,0xf2,0xcd,0xe0,0xa6,0x01,0x4b,0xe0,
+0x4b,0x10,0x27,0x4d,0x56,0xd2,0xcd,0x28,0x27,0x9c,0x01,0xc5,0x9e,0x01,0xc6,0xff,
+0xf2,0xcc,0xc1,0xa6,0x00,0x4b,0x28,0x4b,0xfd,0xf2,0xcc,0x01,0x4b,0x90,0x4b,0x07,
+0xce,0xcd,0x0a,0x27,0x9c,0x01,0xc5,0x9e,0x01,0xc6,0xc5,0x20,0x28,0x4b,0xff,0xf2,
+0xcc,0xe0,0xa6,0x01,0x4b,0x90,0x4b,0xcd,0xce,0xcd,0x0c,0x27,0x9c,0x01,0xc5,0x9e,
+0x01,0xc6,0xdd,0x20,0x08,0x4b,0xff,0xf2,0xcc,0x82,0xa6,0x01,0x4b,0x90,0x4b,0x9b,
+0x01,0x5a,0x72,0xce,0x27,0x9b,0x01,0xc6,0x12,0x26,0x4d,0xcc,0xd1,0xcd,0xff,0xf2,
+0xcc,0xc0,0xa6,0x00,0x4b,0x50,0x4b,0xb7,0x27,0x4d,0x56,0xd2,0xcd,0xe8,0x26,0x4d,
+0x85,0xcf,0xcd,0x03,0xa6,0x26,0x20,0x9b,0x01,0x0a,0x35,0xcb,0x27,0x4d,0x56,0xd2,
+0xcd,0xd3,0x20,0x60,0x4b,0x04,0x27,0x4d,0x85,0xcf,0xcd,0x83,0xa6,0x18,0x27,0x4d,
+0xae,0xd1,0xcd,0xff,0xf2,0xcc,0x81,0xa6,0x00,0x4b,0x60,0x4b,0xec,0x26,0x4d,0x85,
+0xcf,0xcd,0x02,0xa6,0x2e,0xd1,0xcd,0xff,0xf2,0xcc,0xa0,0xa6,0x00,0x4b,0x08,0x4b,
+0x09,0x27,0x4d,0x6e,0xd0,0xcd,0x06,0x26,0x4d,0x85,0xcf,0xcd,0x4c,0x8c,0x20,0x80,
+0xa6,0x88,0x88,0x4f,0x93,0x20,0x40,0xa6,0x00,0x4b,0xa0,0x4b,0x08,0x26,0x4d,0xdd,
+0xcf,0xcd,0x5d,0xf4,0xcc,0x21,0xf4,0xcc,0x03,0x26,0x1f,0xa0,0x06,0xf4,0xcc,0x03,
+0x26,0x4a,0xee,0xf3,0xcc,0x03,0x26,0x20,0xa0,0xe0,0x27,0x1e,0xa0,0xd2,0xf3,0xcc,
+0x03,0x26,0x4a,0x60,0x27,0x4a,0x39,0x27,0x20,0xa0,0xc3,0x27,0x20,0xa0,0x2c,0x27,
+0x20,0xa0,0x30,0x27,0x20,0xa0,0x5d,0xf4,0xcc,0x03,0x26,0xa8,0x01,0xc6,0xdd,0x20,
+0x60,0xa6,0x00,0x4b,0x50,0x4b,0x07,0xce,0xcd,0x03,0x25,0x61,0xa1,0xa8,0x01,0xc6,
+0x12,0x27,0x11,0xa1,0xa1,0x01,0xc6,0x07,0x27,0x9f,0x01,0xc6,0x81,0x84,0x84,0x9f,
+0xad,0x20,0xa6,0x00,0x4b,0x50,0x4b,0x0b,0x26,0x72,0x01,0xc6,0x05,0x27,0xa8,0x01,
+0xc6,0x10,0x20,0x4f,0x00,0x4b,0xa0,0x4b,0x07,0xce,0xcd,0x03,0x27,0xa8,0x01,0xc6,
+0x0f,0x26,0xa2,0x01,0xc6,0x81,0x84,0x84,0x84,0xdf,0xc4,0xcd,0x06,0xee,0x72,0x07,
+0x7b,0xa8,0x01,0xc7,0x03,0x7b,0x05,0x20,0x18,0x72,0x04,0x27,0x01,0xe1,0x72,0xf0,
+0xa4,0xa8,0x01,0xc6,0x01,0x6b,0xf0,0xa4,0x03,0x7b,0x06,0x20,0xc7,0x02,0x6b,0x02,
+0xea,0x72,0xe0,0xa4,0x03,0x7b,0x02,0x6b,0x1f,0xa4,0x06,0x20,0xc6,0x88,0x88,0x88,
+0xfe,0xce,0x80,0x3d,0xe1,0xb7,0x80,0x3d,0xab,0x93,0x80,0x3d,0x25,0x6e,0x9f,0x3d,
+0x81,0x13,0x00,0x01,0x35,0x04,0x27,0x12,0xb7,0xc0,0x84,0xc6,0x50,0x18,0xcd,0x11,
+0xb7,0x11,0xf1,0xcd,0x50,0x18,0xcc,0x11,0xb7,0x01,0xa6,0x07,0x24,0x20,0xa1,0xf3,
+0x01,0xc6,0x09,0x20,0x5f,0xef,0xcd,0xd6,0x20,0x5f,0xef,0xcd,0x05,0x26,0x04,0xa1,
+0x12,0xb6,0x06,0x27,0x02,0xa1,0x12,0xb6,0x11,0x26,0x02,0xa1,0x11,0xb6,0xb3,0x20,
+0x4a,0xef,0xcd,0x2a,0x20,0x93,0xee,0xcd,0x2f,0x20,0x0e,0xee,0xcd,0xc2,0x20,0x11,
+0xb7,0x0e,0xee,0xcd,0x07,0x26,0x02,0xa1,0x11,0xb6,0x81,0x12,0x00,0x18,0x4d,0x55,
+0x13,0x00,0x19,0x4d,0x55,0x14,0x00,0x1a,0x4d,0x55,0x15,0x00,0x1b,0x4d,0x55,0x6e,
+0x25,0x20,0xa1,0xf3,0x01,0xc6,0x50,0x18,0xcd,0x11,0xb7,0xc0,0xeb,0xcd,0x65,0x20,
+0x22,0xeb,0xcd,0x6a,0x20,0xe1,0xe7,0xcd,0x45,0x18,0xcc,0x40,0xe7,0xcd,0x75,0x20,
+0x61,0xe7,0xcd,0x7a,0x20,0x68,0xe6,0xcd,0x77,0xf2,0xcc,0x12,0xe6,0xcd,0x77,0xf2,
+0xcc,0xd2,0xe5,0xcd,0x77,0xf2,0xcc,0x25,0xe5,0xcd,0x77,0xf2,0xcc,0x02,0xe4,0xcd,
+0x81,0x88,0xaf,0xf1,0xd6,0x88,0xb0,0xf1,0xd6,0x58,0x97,0x8f,0xf2,0xcc,0x03,0x25,
+0x0f,0xa1,0x4a,0x6e,0xf2,0x52,0xf2,0x4d,0xf2,0x48,0xf2,0x36,0xf2,0x12,0xf2,0x0d,
+0xf2,0x08,0xf2,0x02,0xf2,0xfd,0xf1,0xf8,0xf1,0xf2,0xf1,0xec,0xf1,0xe6,0xf1,0xe0,
+0xf1,0x81,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x02,0xab,0xf6,0x20,0x01,0xa6,
+0x81,0x85,0x85,0x85,0x85,0x85,0x06,0x26,0x11,0xbe,0x04,0x20,0x4f,0x05,0xc5,0xcd,
+0x0a,0xa6,0x9a,0x78,0x84,0xc7,0x02,0x7b,0x79,0x84,0xc7,0x03,0x7b,0x7a,0x84,0xc7,
+0x04,0x7b,0x7b,0x84,0xc7,0x05,0x7b,0xef,0xfc,0xcd,0x78,0xae,0xa4,0x00,0x84,0x35,
+0xa0,0x18,0x36,0xad,0x5b,0x02,0x6b,0x78,0x84,0xc6,0x03,0x6b,0x79,0x84,0xc6,0x04,
+0x6b,0x7a,0x84,0xc6,0x05,0x6b,0x7b,0x84,0xc6,0x41,0xcc,0xcd,0x01,0xa6,0x41,0xcc,
+0xcd,0x4f,0x9b,0x49,0x27,0x01,0xe4,0x72,0x4f,0x01,0x20,0x4c,0x03,0x26,0x4a,0x11,
+0xb6,0x01,0x6b,0x4f,0x01,0x20,0x01,0xa6,0x04,0x26,0x9b,0xfc,0xcd,0xa1,0x3f,0xa2,
+0x3f,0xa3,0x3f,0x77,0xad,0x5b,0x02,0x6b,0xc0,0x84,0xc6,0x03,0x6b,0xc1,0x84,0xc6,
+0x04,0x6b,0xc2,0x84,0xc6,0x05,0x6b,0xc3,0x84,0xc6,0x88,0x88,0x88,0x88,0x88,0x81,
+0xef,0xfc,0xcd,0xb4,0xae,0xa4,0x00,0x84,0x35,0x81,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,
+0xa9,0x41,0x01,0xab,0x81,0x85,0x85,0x85,0x85,0x85,0x05,0x7b,0xe4,0x01,0x01,0x35,
+0x04,0x27,0x02,0xa1,0x11,0xb6,0x0a,0x26,0x4a,0x13,0xb6,0x05,0x6b,0x01,0xa6,0x04,
+0x20,0x12,0x00,0xbf,0x01,0x55,0x09,0x20,0xbd,0xcc,0xcd,0x05,0x27,0x85,0x85,0x4d,
+0xc0,0x1b,0xcd,0x13,0xbe,0x14,0xb6,0x01,0x4b,0x00,0x4b,0x1e,0x20,0x85,0x9b,0x1c,
+0xcd,0x13,0xbe,0x14,0xb6,0x01,0x4b,0xbf,0x01,0xc7,0x15,0xb6,0x21,0x20,0x33,0x27,
+0x4d,0x03,0x1d,0xcd,0x39,0x20,0x9a,0x5e,0xad,0xa2,0x18,0x57,0xad,0x5b,0x9b,0x01,
+0x6b,0xb4,0x84,0xc6,0x02,0x6b,0xb5,0x84,0xc6,0x03,0x6b,0xb6,0x84,0xc6,0x04,0x6b,
+0xb7,0x84,0xc6,0x05,0xc5,0xcd,0xfa,0xa6,0x9a,0x18,0x84,0x00,0x35,0x19,0x84,0x03,
+0x35,0x1a,0x84,0x83,0x35,0x1b,0x84,0x00,0x35,0x14,0x45,0x00,0x35,0x15,0x45,0x00,
+0x35,0x16,0x45,0x00,0x35,0x17,0x45,0x02,0x35,0x07,0xf1,0xcd,0xa2,0x16,0xa3,0x12,
+0xfc,0xf0,0xcd,0x5b,0x9b,0x01,0x6b,0xb4,0x84,0xc6,0x02,0x6b,0xb5,0x84,0xc6,0x03,
+0x6b,0xb6,0x84,0xc6,0x04,0x6b,0xb7,0x84,0xc6,0x84,0x84,0x84,0x84,0x84,0x84,0x84,
+0x84,0x65,0xde,0xcd,0x6c,0x01,0x3b,0x6d,0x01,0x3b,0x6e,0x01,0x3b,0x6f,0x01,0x3b,
+0x04,0x4b,0xc4,0x4b,0xb4,0x4b,0x00,0x4b,0xe1,0xf0,0xcc,0xc5,0xf0,0xcc,0x03,0x26,
+0x4a,0xb4,0xf0,0xcc,0x03,0x26,0x4a,0xac,0xf0,0xcc,0x03,0x26,0x4a,0x15,0x27,0x4a,
+0x12,0xb6,0xe1,0xf0,0xcc,0x03,0x27,0x02,0xa1,0x11,0xb6,0xe5,0xf0,0xcc,0xe8,0x01,
+0xc7,0x4c,0x9a,0xef,0xfc,0xcd,0x18,0xae,0xa4,0x00,0x84,0x35,0xa0,0xb7,0xa1,0xb7,
+0xa2,0xb7,0x4f,0xa3,0xb7,0x12,0xb6,0x9b,0xe5,0xf0,0xcc,0x03,0x27,0x03,0xa1,0x70,
+0x01,0xc6,0x05,0x6b,0x01,0xa6,0xee,0x01,0x5f,0x72,0x70,0x01,0x5f,0x72,0x0c,0x27,
+0x4a,0x0f,0x27,0x4a,0x12,0x27,0x4a,0x15,0x27,0x4a,0xee,0x01,0xc6,0xec,0x01,0x5f,
+0x72,0xed,0x01,0x01,0x35,0x23,0x20,0xec,0x01,0x5f,0x72,0xed,0x01,0xc7,0x02,0xa6,
+0x02,0x20,0x0a,0xa6,0x26,0x20,0x06,0x27,0x4a,0x05,0x27,0x4a,0x0c,0x27,0x4a,0xee,
+0x01,0xc6,0x1d,0x24,0x20,0xa1,0xf3,0x01,0xc6,0xee,0x01,0xc7,0x7f,0xa4,0x12,0xb6,
+0x70,0x01,0x03,0x35,0x79,0x26,0x4a,0x11,0xb6,0xe4,0x01,0x5f,0x72,0x9a,0x18,0x84,
+0x00,0x35,0x19,0x84,0x00,0x35,0x1a,0x84,0x00,0x35,0x1b,0x84,0x00,0x35,0x9b,0x70,
+0x01,0x5f,0x72,0x1a,0x27,0x02,0xa1,0x11,0xb6,0x05,0x6b,0x4f,0x88,0x88,0x88,0x88,
+0x88,0x81,0x4f,0xbd,0xcc,0xcc,0x03,0x27,0x85,0x85,0x4d,0xc0,0x1b,0xcd,0x11,0xbe,
+0x12,0xb6,0x80,0x4b,0x00,0x4b,0x81,0xd4,0x00,0xd7,0x11,0xb6,0x97,0x52,0x21,0xae,
+0x81,0x85,0x85,0x85,0x85,0x85,0x4f,0x84,0x84,0x84,0xd3,0xcc,0xcd,0x00,0xae,0x11,
+0xa6,0x88,0x04,0x7b,0x88,0x04,0x7b,0x88,0x01,0x7b,0xd3,0x00,0x5c,0x72,0x04,0x24,
+0x03,0xa1,0x08,0x26,0xd3,0x00,0xc1,0x04,0x7b,0xe2,0x22,0x05,0xe1,0x72,0x13,0xb6,
+0x05,0x6b,0x4c,0x9f,0x90,0xd7,0x00,0xd7,0x14,0xe6,0x90,0x05,0xe6,0x72,0x97,0x05,
+0xeb,0x72,0x52,0x21,0xae,0x04,0x7b,0x15,0x20,0x4f,0xd6,0x00,0xd7,0x13,0xb6,0xd5,
+0x00,0xd7,0x12,0xb6,0x54,0xad,0x3c,0x24,0x03,0xa1,0x04,0x7b,0xef,0x25,0x03,0xa1,
+0x05,0x6b,0x4c,0x05,0x7b,0x04,0x6b,0x05,0x7b,0x04,0x27,0x6b,0xad,0x05,0x6b,0x4f,
+0x04,0x6b,0xd3,0x00,0xc6,0x03,0x6b,0x10,0xa6,0x02,0x6b,0x02,0xa6,0x63,0x20,0x02,
+0x6b,0x03,0xef,0x72,0x06,0xa9,0x41,0x00,0xab,0x59,0x48,0x59,0x48,0x59,0x48,0x59,
+0x48,0x59,0x48,0x4a,0x5a,0x01,0x26,0x4d,0x5f,0x01,0x6b,0x0f,0xa4,0x11,0xb6,0x22,
+0x25,0x81,0xa1,0x11,0xb6,0x01,0x6b,0x4f,0x88,0x88,0x88,0x88,0x88,0x81,0x85,0x85,
+0x85,0x85,0x4f,0x9e,0x01,0x01,0x35,0x9f,0x01,0xc7,0x4c,0x08,0x20,0x17,0xb7,0x0c,
+0x26,0xa0,0x01,0xc6,0x16,0xb7,0x04,0x7b,0x01,0x6b,0x02,0x6b,0x03,0x6b,0x4f,0x04,
+0x6b,0xa3,0x01,0xc6,0x15,0xb7,0x01,0x7b,0x14,0xb7,0x02,0x7b,0x13,0xb7,0x03,0x7b,
+0x12,0xb7,0x04,0x7b,0x01,0x6b,0xa4,0x01,0xc6,0x02,0x6b,0xa5,0x01,0xc6,0x03,0x6b,
+0xa6,0x01,0xc6,0x04,0x6b,0xa7,0x01,0xc6,0x11,0xb7,0x4f,0x01,0x20,0x4c,0x03,0x26,
+0x9f,0x01,0xce,0x50,0x20,0x9e,0x01,0x5f,0x72,0x9f,0x01,0xc7,0x4c,0x5a,0x20,0x9e,
+0x01,0x5f,0x72,0x9f,0x01,0x5f,0x72,0x5c,0x27,0x4a,0x17,0x27,0x4a,0x10,0x27,0x4a,
+0x11,0xb6,0x9d,0x01,0x08,0x35,0x04,0x27,0x12,0x3d,0x9d,0x01,0x5f,0x72,0x88,0x88,
+0x88,0x88,0x81,0xef,0xfc,0xcd,0xb4,0xae,0xa4,0x00,0x84,0x35,0x81,0x1e,0xfe,0xcd,
+0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x81,0x10,0x4d,0x00,0x35,0x11,0x4d,0x00,0x35,
+0x12,0x4d,0x00,0x35,0x81,0x85,0xcf,0xcd,0x01,0xa6,0x07,0xce,0xcd,0x04,0x48,0x00,
+0x35,0x05,0x48,0x00,0x35,0x06,0x48,0x00,0x35,0x07,0x48,0x00,0x35,0x31,0xad,0xa3,
+0x1a,0x81,0x85,0x85,0x85,0x85,0x4f,0x9a,0xa1,0x01,0x18,0x72,0xaf,0x01,0x1a,0x00,
+0x55,0xb0,0x01,0x1b,0x00,0x55,0xb1,0x01,0x1c,0x00,0x55,0xb2,0x01,0x1d,0x00,0x55,
+0xab,0x01,0x1e,0x00,0x55,0xac,0x01,0x1f,0x00,0x55,0xad,0x01,0x20,0x00,0x55,0xae,
+0x01,0x21,0x00,0x55,0xb7,0x01,0x12,0x00,0x55,0xb8,0x01,0x13,0x00,0x55,0xb9,0x01,
+0x14,0x00,0x55,0xba,0x01,0x15,0x00,0x55,0xb3,0x01,0x16,0x00,0x55,0xb4,0x01,0x17,
+0x00,0x55,0xb5,0x01,0x18,0x00,0x55,0xb6,0x01,0x19,0x00,0x55,0x54,0x26,0x28,0xa1,
+0x11,0xb6,0x5a,0x20,0xa1,0x01,0xc7,0x10,0xa4,0xa1,0x01,0xc6,0x64,0x20,0xa1,0x01,
+0x10,0x72,0x06,0x26,0x27,0xa1,0x11,0xb6,0xd2,0xfc,0xcd,0xa4,0xb7,0x48,0xa9,0x41,
+0x44,0xab,0x52,0x08,0xae,0x11,0xb6,0xae,0xfc,0xcd,0x12,0xae,0xd2,0xfc,0xcd,0xa4,
+0xb7,0x48,0xa9,0x41,0x40,0xab,0x52,0x08,0xae,0x11,0xb6,0xae,0xfc,0xcd,0x16,0xae,
+0x14,0x48,0xaf,0x01,0x55,0x15,0x48,0xb0,0x01,0x55,0x16,0x48,0xb1,0x01,0x55,0x17,
+0x48,0xb2,0x01,0x55,0x10,0x48,0xab,0x01,0x55,0x11,0x48,0xac,0x01,0x55,0x12,0x48,
+0xad,0x01,0x55,0x13,0x48,0xae,0x01,0x55,0xcf,0xed,0xcd,0xa1,0x1b,0xf9,0xed,0xcd,
+0x5b,0x01,0x6b,0xb4,0x84,0xc6,0x02,0x6b,0xb5,0x84,0xc6,0x03,0x6b,0xb6,0x84,0xc6,
+0x04,0x6b,0xb7,0x84,0xc6,0x45,0x26,0x11,0x3d,0x6e,0xed,0xcc,0x03,0x25,0x28,0xa1,
+0x11,0xb6,0xc8,0xed,0xcc,0x04,0xee,0xcd,0xa1,0x1b,0xf9,0xed,0xcd,0x5b,0x05,0xc5,
+0xcd,0x0a,0xa6,0x01,0x6b,0xb4,0x84,0xc6,0x02,0x6b,0xb5,0x84,0xc6,0x03,0x6b,0xb6,
+0x84,0xc6,0x04,0x6b,0xb7,0x84,0xc6,0xa1,0x01,0x10,0x72,0x64,0xed,0xcc,0x03,0x27,
+0x91,0xa1,0x11,0xb6,0xec,0xed,0xcd,0x13,0x4d,0x03,0x35,0xec,0xed,0xcd,0x13,0x4d,
+0x43,0x35,0x0c,0x4d,0x1e,0x00,0x55,0x0d,0x4d,0x1f,0x00,0x55,0x0e,0x4d,0x20,0x00,
+0x55,0x0f,0x4d,0x21,0x00,0x55,0x08,0x4d,0x1a,0x00,0x55,0x09,0x4d,0x1b,0x00,0x55,
+0x0a,0x4d,0x1c,0x00,0x55,0x0b,0x4d,0x1d,0x00,0x55,0x04,0x4d,0x16,0x00,0x55,0x05,
+0x4d,0x17,0x00,0x55,0x06,0x4d,0x18,0x00,0x55,0x07,0x4d,0x19,0x00,0x55,0x00,0x4d,
+0x12,0x00,0x55,0x01,0x4d,0x13,0x00,0x55,0x02,0x4d,0x14,0x00,0x55,0x03,0x4d,0x15,
+0x00,0x55,0xc4,0x27,0x9b,0xfc,0xcd,0xa0,0x3f,0xa1,0xb7,0x06,0xa4,0xa1,0xb6,0xa2,
+0x3f,0xa3,0x3f,0xf9,0xed,0xcd,0x5b,0x01,0x6b,0x14,0x4d,0xc6,0x02,0x6b,0x15,0x4d,
+0xc6,0x03,0x6b,0x16,0x4d,0xc6,0x04,0x6b,0x17,0x4d,0xc6,0x05,0xc5,0xcd,0x0f,0xa6,
+0xec,0xed,0xcd,0x13,0x4d,0x03,0x35,0xec,0xed,0xcd,0x13,0x4d,0xc3,0x35,0x25,0x20,
+0x02,0x6b,0x03,0x6b,0x04,0x6b,0x4f,0xcf,0xed,0xcd,0xa1,0x1a,0xf9,0xed,0xcd,0x5b,
+0x01,0x6b,0xb4,0x84,0xc6,0x02,0x6b,0xb5,0x84,0xc6,0x03,0x6b,0xb6,0x84,0xc6,0x04,
+0x6b,0xb7,0x84,0xc6,0x62,0x26,0x80,0xa1,0x11,0xb6,0xf7,0x24,0x92,0xa1,0x11,0xb6,
+0xde,0xec,0xcc,0x03,0x24,0x80,0xa1,0x11,0xb6,0x9b,0xca,0xed,0xcc,0x01,0xa6,0x05,
+0x24,0x20,0xa1,0xf3,0x01,0xc6,0x0c,0x25,0x80,0xa1,0x11,0xb6,0x88,0x88,0x88,0x88,
+0x81,0x48,0x42,0x00,0x35,0x49,0x42,0x00,0x35,0x4a,0x42,0x00,0x35,0x81,0x85,0x85,
+0x85,0x85,0x4f,0x9a,0x88,0xc9,0xcd,0x72,0x01,0x01,0x35,0x6e,0xc4,0xcd,0x03,0x27,
+0xc2,0x00,0xc6,0x16,0xad,0x4b,0x42,0x05,0x35,0x04,0x20,0x4b,0x42,0x01,0x35,0x06,
+0x27,0x9c,0x01,0xc6,0xf7,0xc7,0xcd,0xef,0xfc,0xcd,0xb4,0xae,0xa4,0x00,0x84,0x35,
+0xa2,0x16,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x5b,0x01,0x6b,0xb4,
+0x84,0xc6,0x02,0x6b,0xb5,0x84,0xc6,0x03,0x6b,0xb6,0x84,0xc6,0x04,0x6b,0xb7,0x84,
+0xc6,0xeb,0xdd,0xcd,0x50,0x20,0x59,0xad,0x4b,0x42,0x05,0x35,0x04,0x20,0x4b,0x42,
+0x01,0x35,0x06,0x27,0x9c,0x01,0xc6,0x13,0x26,0x4a,0x72,0x01,0xc6,0x69,0x20,0xb7,
+0xc8,0xcd,0x97,0xc7,0xcd,0x08,0x26,0x11,0x3d,0x9b,0x9c,0x01,0x01,0x35,0x04,0x20,
+0x9c,0x01,0x5f,0x72,0x06,0x26,0x02,0xa1,0x11,0xb6,0x88,0x88,0x88,0x88,0x81,0xef,
+0xfc,0xcd,0x78,0xae,0xa4,0x00,0x84,0x35,0x81,0x18,0x80,0x00,0x35,0x19,0x80,0x00,
+0x35,0x1a,0x80,0x00,0x35,0x81,0x08,0x80,0x00,0x35,0x09,0x80,0x00,0x35,0x0a,0x80,
+0x00,0x35,0x81,0x04,0x80,0x00,0x35,0x05,0x80,0x00,0x35,0x06,0x80,0x00,0x35,0x81,
+0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x07,0xab,0x81,0x84,0x8b,0x41,0x00,0xa9,
+0x41,0x09,0xab,0x5b,0x0a,0x6b,0x4f,0x9a,0xb7,0xc8,0xcd,0x71,0x01,0x5f,0x72,0xbc,
+0x84,0x00,0x35,0xbd,0x84,0x00,0x35,0xbe,0x84,0x18,0x35,0xbf,0x84,0x00,0x35,0x4a,
+0xad,0x1b,0x80,0x80,0x35,0x14,0x80,0x00,0x35,0x15,0x80,0x00,0x35,0x16,0x80,0x00,
+0x35,0x17,0x80,0x00,0x35,0x53,0xad,0x0b,0x80,0x00,0x35,0x4c,0xad,0x07,0x80,0x00,
+0x35,0x00,0x80,0x00,0x35,0x01,0x80,0x00,0x35,0x02,0x80,0x00,0x35,0x03,0x80,0x00,
+0x35,0x18,0xeb,0xcd,0xa3,0xb7,0xf8,0xa4,0xa3,0xb6,0x60,0xad,0x5b,0x07,0x6b,0x78,
+0x84,0xc6,0x08,0x6b,0x79,0x84,0xc6,0x09,0x6b,0x7a,0x84,0xc6,0x0a,0x6b,0x7b,0x84,
+0xc6,0x69,0x20,0x88,0xc9,0xcd,0x71,0x01,0x01,0x35,0x90,0x84,0xc7,0x03,0x7b,0x91,
+0x84,0xc7,0x04,0x7b,0x92,0x84,0xc7,0x05,0x7b,0x93,0x84,0xc7,0x06,0x7b,0xd2,0xfd,
+0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x03,0xab,0x5b,0xa0,0x1e,0xe6,0xea,0xcd,0x5b,0x05,
+0x6b,0x06,0x6b,0x4f,0xb3,0xfd,0xcd,0x10,0xa6,0xa4,0xb7,0x00,0xa9,0x41,0x03,0xab,
+0x5b,0x03,0x6b,0x94,0x84,0xc6,0x04,0x6b,0x95,0x84,0xc6,0x05,0x6b,0x96,0x84,0xc6,
+0x06,0x6b,0x97,0x84,0xc6,0x73,0xfd,0xcd,0x01,0xa6,0xa4,0xb7,0x00,0xa9,0x41,0x07,
+0xab,0x5b,0x07,0x6b,0x08,0x6b,0x4f,0x07,0x6b,0x98,0x84,0xc6,0x08,0x6b,0x99,0x84,
+0xc6,0x09,0x6b,0x9a,0x84,0xc6,0x0a,0x6b,0x9b,0x84,0xc6,0x71,0x24,0x23,0xa1,0xf3,
+0x01,0xc6,0xc4,0xdc,0xcd,0xbc,0x84,0x00,0x35,0xbd,0x84,0x00,0x35,0xbe,0x84,0x1b,
+0x35,0xbf,0x84,0x40,0x35,0x04,0x20,0xbf,0x84,0x41,0x35,0x06,0x26,0x14,0x3d,0x0b,
+0xeb,0xcd,0x1b,0x80,0x81,0x35,0xef,0xfc,0xcd,0x14,0xae,0xa4,0x00,0x80,0x35,0x7a,
+0xfc,0xcd,0x52,0x80,0xae,0xfe,0xea,0xcd,0x0b,0x80,0x20,0x35,0x04,0x20,0x0b,0x80,
+0x60,0x35,0x06,0x26,0xdb,0x01,0xc7,0x16,0xb6,0xdc,0x01,0x60,0x20,0x55,0xdd,0x01,
+0x61,0x20,0x55,0xde,0x01,0x62,0x20,0x55,0xdf,0x01,0x63,0x20,0x55,0x68,0x20,0x21,
+0x35,0x64,0x20,0xdc,0x01,0x55,0x65,0x20,0xdd,0x01,0x55,0x66,0x20,0xde,0x01,0x55,
+0x67,0x20,0xdf,0x01,0x55,0x9d,0x9d,0x68,0x20,0x27,0x35,0x68,0x20,0x52,0x35,0x64,
+0x20,0x00,0x35,0x65,0x20,0x0f,0x35,0x66,0x20,0x42,0x35,0x67,0x20,0x40,0x35,0x68,
+0x20,0x22,0x35,0x60,0x20,0x6c,0x01,0x55,0x61,0x20,0x6d,0x01,0x55,0x62,0x20,0x6e,
+0x01,0x55,0x63,0x20,0x6f,0x01,0x55,0xdc,0x01,0x94,0xf2,0x55,0xdd,0x01,0x95,0xf2,
+0x55,0xde,0x01,0x96,0xf2,0x55,0xdf,0x01,0x97,0xf2,0x55,0xe0,0x01,0xc7,0xe1,0x01,
+0x21,0x35,0xe2,0x01,0xe6,0x35,0xe3,0x01,0xf0,0x35,0x23,0x20,0xdc,0x01,0x98,0xf2,
+0x55,0xdd,0x01,0x99,0xf2,0x55,0xde,0x01,0x9a,0xf2,0x55,0xdf,0x01,0x9b,0xf2,0x55,
+0xe0,0x01,0xc7,0xe1,0x01,0x21,0x35,0xe2,0x01,0xf0,0x35,0xe3,0x01,0x7c,0x35,0x48,
+0x20,0xdc,0x01,0x9c,0xf2,0x55,0xdd,0x01,0x9d,0xf2,0x55,0xde,0x01,0x9e,0xf2,0x55,
+0xdf,0x01,0x9f,0xf2,0x55,0xe0,0x01,0xc7,0xe1,0x01,0x21,0x35,0xe2,0x01,0xf6,0x35,
+0xe3,0x01,0x94,0x35,0x6d,0x20,0xf1,0xea,0xcd,0x07,0x80,0xe0,0x35,0xdc,0x01,0x90,
+0xf2,0x55,0xdd,0x01,0x91,0xf2,0x55,0xde,0x01,0x92,0xf2,0x55,0xdf,0x01,0x93,0xf2,
+0x55,0xe0,0x01,0x5f,0x72,0xe1,0x01,0x2a,0x35,0xe2,0x01,0x09,0x35,0xe3,0x01,0x8b,
+0x35,0x77,0x27,0x4a,0x55,0x27,0x4a,0x33,0x27,0x4a,0x12,0xb6,0x10,0x80,0x00,0x35,
+0x11,0x80,0x00,0x35,0x12,0x80,0x00,0x35,0x13,0x80,0x08,0x35,0xf1,0xea,0xcd,0x07,
+0x80,0xc0,0x35,0xef,0xfc,0xcd,0x5f,0xa4,0x00,0x80,0x35,0x7a,0xfc,0xcd,0x06,0xaa,
+0x41,0x01,0xea,0x72,0x41,0x02,0xea,0x72,0x5f,0x28,0xaa,0xc0,0xa4,0x52,0x40,0xae,
+0x12,0xb6,0x01,0xef,0x72,0x02,0x6b,0x52,0x10,0xae,0x15,0xb6,0x18,0xeb,0xcd,0xa3,
+0x10,0xa3,0x12,0xa3,0x14,0xe6,0xea,0xcd,0x5b,0x07,0x6b,0x78,0x84,0xc6,0x08,0x6b,
+0x79,0x84,0xc6,0x09,0x6b,0x7a,0x84,0xc6,0x0a,0x6b,0x7b,0x84,0xc6,0xef,0xfc,0xcd,
+0xb4,0xae,0xa4,0x00,0x84,0x35,0xa2,0x16,0xe6,0xea,0xcd,0x5b,0x07,0x6b,0xb4,0x84,
+0xc6,0x08,0x6b,0xb5,0x84,0xc6,0x09,0x6b,0xb6,0x84,0xc6,0x0a,0x6b,0xb7,0x84,0xc6,
+0x6f,0xea,0xcc,0x03,0x27,0x4a,0x13,0xb6,0x9b,0xda,0xea,0xcc,0x01,0xa6,0x05,0x27,
+0x9b,0xfc,0xcd,0xa0,0x3f,0xa1,0x3f,0xa2,0xb7,0x40,0xa4,0xa2,0xb6,0xa3,0x3f,0xe6,
+0xea,0xcd,0x5b,0x07,0x6b,0x94,0x84,0xc6,0x08,0x6b,0x95,0x84,0xc6,0x09,0x6b,0x96,
+0x84,0xc6,0x0a,0x6b,0x97,0x84,0xc6,0x8b,0x41,0x00,0xa2,0x41,0x0a,0xa0,0x5b,0x81,
+0x85,0x4f,0x87,0x01,0x5a,0x72,0x9a,0x0c,0x8c,0x00,0x35,0x0d,0x8c,0x00,0x35,0x0e,
+0x8c,0x00,0x35,0x0f,0x8c,0x01,0x35,0x04,0x20,0x0f,0x8c,0x03,0x35,0x06,0x26,0x4a,
+0x87,0x01,0xc6,0xef,0xfc,0xcd,0x10,0xae,0xa4,0x00,0x8c,0x35,0xa0,0xb7,0xa1,0xb7,
+0xa2,0xb7,0x4f,0xa3,0x00,0x88,0x01,0x55,0xef,0xfc,0xcd,0x1c,0xae,0xa4,0x00,0x8c,
+0x35,0x7a,0xfc,0xcd,0x5f,0x0f,0xa4,0x4e,0x88,0x01,0xc6,0x9b,0x06,0x20,0xc7,0xfa,
+0xa4,0x06,0x20,0xc6,0x52,0x27,0xe6,0x25,0x87,0x01,0xc6,0x87,0x01,0xc1,0x01,0x6b,
+0x4c,0x9f,0x90,0x88,0x01,0xd7,0x12,0xe6,0x90,0x01,0xe6,0x72,0x97,0x01,0xe0,0x72,
+0x10,0x20,0x01,0xa6,0x88,0x01,0xc7,0x12,0xb6,0x87,0x01,0xc7,0x11,0xb6,0x88,0x81,
+0x85,0x4f,0x74,0x01,0x5f,0x72,0xef,0x25,0x75,0x01,0xc1,0x01,0x6b,0x4c,0x9f,0x14,
+0xe7,0x76,0x01,0xd6,0x01,0xee,0x72,0x0a,0x20,0x4f,0x13,0x00,0x75,0x01,0x55,0x88,
+0x81,0x4f,0x9a,0xa8,0x84,0x29,0x00,0x55,0xa9,0x84,0x2a,0x00,0x55,0xaa,0x84,0x2b,
+0x00,0x55,0xab,0x84,0x2c,0x00,0x55,0xef,0xfc,0xcd,0x48,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x27,0xbe,0x28,0xb6,0xef,0xfc,0xcd,0x44,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x25,0xbe,0x26,0xb6,0xef,0xfc,0xcd,0x40,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x23,0xbe,0x24,0xb6,0xef,0xfc,0xcd,0x3c,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x21,0xbe,0x22,0xb6,0xef,0xfc,0xcd,0x38,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x1f,0xbe,0x20,0xb6,0xef,0xfc,0xcd,0x34,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x1d,0xbe,0x1e,0xb6,0xef,0xfc,0xcd,0x30,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x1b,0xbe,0x1c,0xb6,0xef,0xfc,0xcd,0x2c,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x19,0xbe,0x1a,0xb6,0xef,0xfc,0xcd,0x28,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x17,0xbe,0x18,0xb6,0xef,0xfc,0xcd,0x24,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x15,0xbe,0x16,0xb6,0xef,0xfc,0xcd,0x20,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x13,0xbe,0x14,0xb6,0xef,0xfc,0xcd,0x1c,0xae,0xa4,0x00,0x84,0x35,
+0x7a,0xfc,0xcd,0x11,0xbe,0x12,0xb6,0x9b,0x81,0x4f,0xe9,0x01,0x01,0x35,0x45,0x01,
+0xcf,0x1f,0xbe,0x46,0x01,0xc7,0x20,0xb6,0x43,0x01,0xcf,0x1d,0xbe,0x44,0x01,0xc7,
+0x1e,0xb6,0x41,0x01,0xcf,0x1b,0xbe,0x42,0x01,0xc7,0x1c,0xb6,0x3f,0x01,0xcf,0x19,
+0xbe,0x40,0x01,0xc7,0x1a,0xb6,0x3d,0x01,0xcf,0x17,0xbe,0x3e,0x01,0xc7,0x18,0xb6,
+0x3b,0x01,0xcf,0x15,0xbe,0x3c,0x01,0xc7,0x16,0xb6,0x39,0x01,0xcf,0x13,0xbe,0x3a,
+0x01,0xc7,0x14,0xb6,0x37,0x01,0xcf,0x11,0xbe,0x38,0x01,0xc7,0x12,0xb6,0x81,0x4f,
+0x9a,0x30,0x42,0x00,0x35,0x31,0x42,0x00,0x35,0x32,0x42,0x00,0x35,0x33,0x42,0x00,
+0x35,0xc6,0xd8,0xcd,0x22,0xda,0xcd,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x65,
+0xde,0xcd,0x6c,0x01,0x3b,0x6d,0x01,0x3b,0x6e,0x01,0x3b,0x6f,0x01,0x3b,0x5e,0x01,
+0x3b,0x5f,0x01,0x3b,0x60,0x01,0x3b,0x61,0x01,0x3b,0xf5,0xd5,0xcd,0x9b,0x81,0x85,
+0x85,0x85,0x85,0x85,0x4f,0xc2,0x00,0x01,0x35,0x9a,0x84,0x84,0xc7,0x02,0x7b,0x85,
+0x84,0xc7,0x03,0x7b,0x86,0x84,0xc7,0x04,0x7b,0x87,0x84,0xc7,0x9b,0x05,0x6b,0xfb,
+0xa4,0x05,0x7b,0x04,0x20,0x04,0xaa,0x05,0x7b,0x06,0x27,0x01,0x7b,0x02,0x6b,0x84,
+0x84,0xc6,0x03,0x6b,0x85,0x84,0xc6,0x04,0x6b,0x86,0x84,0xc6,0x05,0x6b,0x87,0x84,
+0xc6,0x9a,0x6e,0xc4,0xcd,0x9b,0x05,0x27,0x72,0x01,0xc6,0x00,0xc0,0xcd,0x5a,0xc4,
+0xcd,0xbf,0x00,0xc7,0x12,0xb6,0x02,0x20,0x04,0xa6,0x04,0x26,0x02,0xa1,0x11,0xb6,
+0x9a,0xef,0xfc,0xcd,0xa4,0xbf,0x84,0xae,0xa3,0x1b,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,
+0xa9,0x41,0x02,0xab,0x5b,0x9b,0x05,0x6b,0x04,0xaa,0x05,0x7b,0x02,0x6b,0x84,0x84,
+0xc6,0x03,0x6b,0x85,0x84,0xc6,0x04,0x6b,0x86,0x84,0xc6,0x05,0x6b,0x87,0x84,0xc6,
+0x16,0x00,0x01,0x35,0x01,0x6b,0x16,0xb6,0xc1,0x00,0x5f,0x72,0x04,0x20,0xc1,0x00,
+0x01,0x35,0x06,0x27,0x17,0x3d,0x88,0x88,0x88,0x88,0x88,0x81,0x85,0x85,0x85,0x85,
+0x85,0x01,0x7b,0xe8,0x01,0x01,0x35,0x9a,0xef,0xfc,0xcd,0xb4,0xae,0xa4,0x00,0x84,
+0x35,0xa3,0x1f,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x02,0xab,0x5b,0xb4,0x84,
+0xc7,0x02,0x7b,0xb5,0x84,0xc7,0x03,0x7b,0xb6,0x84,0xc7,0x04,0x7b,0xb7,0x84,0xc7,
+0x05,0x7b,0xd2,0xfd,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x02,0xab,0x5b,0xa3,0x1e,0xa0,
+0x3f,0xa1,0x3f,0xa2,0xb7,0x03,0xa4,0xa2,0xb6,0xa3,0x3f,0x17,0xfd,0xcd,0x5f,0x90,
+0xa7,0x00,0x01,0x35,0x5f,0x12,0xb6,0x04,0x6b,0xfc,0xa4,0x04,0x7b,0x02,0x6b,0xb4,
+0x84,0xc6,0x03,0x6b,0xb5,0x84,0xc6,0x04,0x6b,0xb6,0x84,0xc6,0x05,0x6b,0xb7,0x84,
+0xc6,0xcc,0x84,0x00,0x35,0xcd,0x84,0x00,0x35,0xce,0x84,0x4a,0x35,0xcf,0x84,0x00,
+0x35,0x10,0x25,0x20,0xa1,0xf3,0x01,0xc6,0x17,0x26,0x03,0xa1,0x6b,0x01,0xc6,0x40,
+0xc6,0xcd,0x1c,0xbe,0x20,0xa6,0x9b,0x6c,0x01,0x22,0x00,0x55,0x6d,0x01,0x23,0x00,
+0x55,0x6e,0x01,0x24,0x00,0x55,0x6f,0x01,0x25,0x00,0x55,0x14,0x27,0xc0,0xfc,0xcd,
+0x22,0xae,0x6b,0x01,0xc7,0x12,0xb6,0xbb,0x00,0xc7,0x21,0xb6,0xbc,0x00,0xcf,0x1f,
+0xbe,0xbd,0x00,0xc7,0x20,0xb6,0x65,0x01,0xcf,0x19,0xbe,0x66,0x01,0xc7,0x1a,0xb6,
+0x69,0x01,0xcf,0x17,0xbe,0x6a,0x01,0xc7,0x18,0xb6,0x63,0x01,0xcf,0x15,0xbe,0x64,
+0x01,0xc7,0x16,0xb6,0x67,0x01,0xcf,0x13,0xbe,0x68,0x01,0xc7,0x14,0xb6,0x1d,0xe5,
+0xcc,0xe5,0x01,0xc7,0x4c,0x1d,0xe5,0xcc,0x01,0x6b,0x01,0xa6,0x45,0x27,0x4a,0x11,
+0x27,0x4a,0x0d,0x27,0x4d,0xbb,0x00,0x5f,0x72,0xbd,0x00,0x5f,0x72,0xbc,0x00,0x5f,
+0x72,0x70,0x01,0xc7,0x11,0xb6,0x01,0x6b,0x4f,0x88,0x88,0x88,0x88,0x88,0x00,0x00,
+0xba,0x42,0x00,0x00,0xb8,0x41,0x0a,0xd7,0x23,0x3c,0x00,0x00,0x80,0x3f,0x81,0x1e,
+0xfe,0xcd,0xb3,0xae,0xa4,0x00,0x00,0x35,0x81,0xa4,0xb7,0x00,0xa9,0x41,0x17,0xab,
+0x81,0xa4,0x00,0x80,0x35,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,0xb7,0x81,0x8b,
+0x41,0x00,0xa9,0x41,0x1c,0xab,0x5b,0x20,0x80,0x00,0x35,0x21,0x80,0x00,0x35,0x22,
+0x80,0x00,0x35,0x23,0x80,0x40,0x35,0xef,0xfc,0xcd,0x30,0xae,0x1e,0xad,0x16,0x7b,
+0xef,0xfc,0xcd,0x2c,0xae,0x27,0xad,0x15,0x7b,0xef,0xfc,0xcd,0x28,0xae,0x30,0xad,
+0x14,0x7b,0x8a,0xfd,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x13,0xab,0x5b,0x1e,0xfe,0xcd,
+0xe0,0xae,0xa4,0x00,0x01,0x35,0x13,0x6b,0x60,0x20,0xc6,0x14,0x6b,0x61,0x20,0xc6,
+0x15,0x6b,0x62,0x20,0xc6,0x16,0x6b,0x63,0x20,0xc6,0x68,0x20,0x26,0x35,0x68,0x20,
+0x21,0x35,0x68,0x20,0x52,0x35,0x64,0x20,0xc7,0x0d,0x7b,0x65,0x20,0xc7,0x0e,0x7b,
+0x66,0x20,0xc7,0x0f,0x7b,0x67,0x20,0xc7,0x10,0x7b,0x60,0x20,0xdc,0x01,0x55,0x61,
+0x20,0xdd,0x01,0x55,0x62,0x20,0xde,0x01,0x55,0x63,0x20,0xdf,0x01,0x55,0xef,0xfc,
+0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x0d,0xab,0x5b,0xfb,0xfd,0xcd,0xe0,0xe3,0xcd,0x5b,
+0xe8,0xe3,0xcd,0xf0,0x27,0x1b,0xea,0x72,0x1c,0x7b,0xf7,0x26,0x4a,0xdb,0x01,0xc6,
+0xc9,0xe3,0xcc,0x03,0x27,0x4a,0x71,0x01,0xc6,0x84,0x84,0x84,0x84,0x8e,0xc5,0xcd,
+0x88,0x1a,0x7b,0x88,0x1a,0x7b,0x88,0x1a,0x7b,0x88,0x1a,0x7b,0x0f,0x20,0x69,0xc5,
+0xcd,0x88,0x1a,0x7b,0x88,0x1a,0x7b,0x88,0x1a,0x7b,0x88,0x1a,0x7b,0x11,0x27,0xf0,
+0x01,0xc6,0xf2,0x01,0x5f,0x72,0x04,0x20,0x8a,0xfd,0xcd,0xe0,0xe3,0xcd,0x5b,0xe8,
+0xe3,0xcd,0x0c,0x2b,0xff,0xa2,0x11,0x7b,0xfd,0xa0,0x12,0x7b,0x0a,0x2b,0x00,0xa2,
+0x11,0x7b,0x04,0xa0,0x12,0x6b,0x18,0x7b,0x11,0x6b,0x17,0x7b,0x17,0x6b,0x60,0x20,
+0xc6,0x18,0x6b,0x61,0x20,0xc6,0x19,0x6b,0x62,0x20,0xc6,0x1a,0x6b,0x63,0x20,0xc6,
+0x68,0x20,0x26,0x35,0x68,0x20,0x24,0x35,0x64,0x20,0xab,0x00,0x55,0x65,0x20,0xac,
+0x00,0x55,0x66,0x20,0xad,0x00,0x55,0x67,0x20,0xae,0x00,0x55,0x68,0x20,0x21,0x35,
+0x64,0x20,0xc7,0x05,0x7b,0x65,0x20,0xc7,0x06,0x7b,0x66,0x20,0xc7,0x07,0x7b,0x67,
+0x20,0xc7,0x08,0x7b,0x60,0x20,0xaf,0x00,0x55,0x61,0x20,0xb0,0x00,0x55,0x62,0x20,
+0xb1,0x00,0x55,0x63,0x20,0xb2,0x00,0x55,0xab,0x00,0x60,0x20,0x55,0xac,0x00,0x61,
+0x20,0x55,0xad,0x00,0x62,0x20,0x55,0xae,0x00,0x63,0x20,0x55,0x68,0x20,0x21,0x35,
+0x64,0x20,0xc7,0x01,0x7b,0x65,0x20,0xc7,0x02,0x7b,0x66,0x20,0xc7,0x03,0x7b,0x67,
+0x20,0xc7,0x04,0x7b,0xaf,0x00,0x64,0x20,0x55,0xb0,0x00,0x65,0x20,0x55,0xb1,0x00,
+0x66,0x20,0x55,0xb2,0x00,0x67,0x20,0x55,0x68,0x20,0x64,0x35,0x64,0x20,0xaf,0x00,
+0x55,0x65,0x20,0xb0,0x00,0x55,0x66,0x20,0xb1,0x00,0x55,0x67,0x20,0xb2,0x00,0x55,
+0x68,0x20,0x21,0x35,0x64,0x20,0xc7,0x09,0x7b,0x65,0x20,0xc7,0x0a,0x7b,0x66,0x20,
+0xc7,0x0b,0x7b,0x67,0x20,0xc7,0x0c,0x7b,0x68,0x20,0x22,0x35,0xef,0xfc,0xcd,0x60,
+0xae,0xa4,0x00,0x20,0x35,0x7a,0xfc,0xcd,0x1b,0xee,0x72,0x1c,0x7b,0xee,0xe2,0xcc,
+0x03,0x26,0x1b,0xea,0x72,0x1c,0x7b,0xef,0xfc,0xcd,0xe0,0xe3,0xcd,0x5b,0xa0,0x3f,
+0x1e,0xfe,0xcd,0x5f,0xa4,0x00,0x84,0x35,0xef,0xfc,0xcd,0xa4,0xb7,0x00,0xa9,0x41,
+0x09,0xab,0x5b,0x1e,0xfe,0xcd,0xf2,0xae,0xa4,0x00,0xe3,0x35,0x06,0x20,0xf6,0xae,
+0xa4,0x00,0xe3,0x35,0x08,0x26,0x4c,0x86,0x01,0xc6,0x05,0x6b,0xfa,0xe3,0xc6,0x06,
+0x6b,0xfb,0xe3,0xc6,0x07,0x6b,0xfc,0xe3,0xc6,0x08,0x6b,0xfd,0xe3,0xc6,0x01,0x6b,
+0xfe,0xe3,0xc6,0x02,0x6b,0xff,0xe3,0xc6,0x03,0x6b,0x00,0xe4,0xc6,0x04,0x6b,0x01,
+0xe4,0xc6,0x8b,0x41,0x00,0xa2,0x41,0x1a,0xa0,0x5b,0x89,0x88,0x81,0xa0,0xb7,0xa1,
+0xb7,0xa2,0xb7,0x4f,0xa3,0xb7,0x81,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0xd9,0xe0,
+0xcc,0x03,0xa6,0xd9,0xe0,0xcc,0x02,0xa6,0x81,0x20,0x4c,0x81,0x84,0x84,0x84,0x84,
+0xef,0xfc,0xcd,0x10,0xae,0xa4,0x00,0x84,0x35,0xa1,0x1e,0x1e,0xfe,0xcd,0x20,0xad,
+0x5b,0x10,0x84,0xc7,0x01,0x7b,0x11,0x84,0xc7,0x02,0x7b,0x12,0x84,0xc7,0x03,0x7b,
+0x13,0x84,0xc7,0x04,0x7b,0x02,0x6b,0x7f,0xa4,0x02,0x7b,0xef,0xfc,0xcd,0x40,0xad,
+0x5b,0xd8,0xfd,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x07,0xab,0x5b,0xa0,0xb7,0x03,0xa4,
+0xa0,0xb6,0xa1,0x3f,0xa2,0x3f,0xa3,0x3f,0x1e,0xfe,0xcd,0x5d,0xad,0x5b,0x01,0x6b,
+0x10,0x84,0xc6,0x02,0x6b,0x11,0x84,0xc6,0x03,0x6b,0x12,0x84,0xc6,0x04,0x6b,0x13,
+0x84,0xc6,0xd2,0xfd,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x07,0xab,0x5b,0x8c,0xfc,0xcd,
+0x14,0xa6,0x6a,0xe1,0xcd,0x0b,0x6b,0x04,0xa6,0x5d,0xe1,0xcc,0x03,0x26,0x08,0xa0,
+0x58,0xe1,0xcc,0x03,0x26,0x04,0xa0,0x55,0xe1,0xcc,0x03,0x26,0x02,0xa0,0x17,0x27,
+0x02,0xa0,0x1b,0x20,0x4c,0x1e,0x20,0x06,0xa6,0x22,0x20,0x02,0xa6,0x26,0x20,0x07,
+0xa6,0x0c,0x27,0x10,0xa0,0x0c,0x27,0x08,0xa0,0x0c,0x27,0x04,0xa0,0x34,0x27,0x02,
+0xa0,0x3a,0x27,0x02,0xa0,0x23,0x24,0x0b,0x7b,0x20,0xa1,0xf3,0x01,0xc6,0x07,0x6b,
+0x60,0x20,0xc6,0x08,0x6b,0x61,0x20,0xc6,0x09,0x6b,0x62,0x20,0xc6,0x0a,0x6b,0x63,
+0x20,0xc6,0x68,0x20,0x26,0x35,0x68,0x20,0x21,0x35,0x68,0x20,0x52,0x35,0x64,0x20,
+0x00,0x35,0x65,0x20,0x00,0x35,0x66,0x20,0x80,0x35,0x67,0x20,0x00,0x35,0x9d,0x9d,
+0x68,0x20,0x27,0x35,0x60,0x20,0xb7,0x00,0x55,0x61,0x20,0xb8,0x00,0x55,0x62,0x20,
+0xb9,0x00,0x55,0x63,0x20,0xba,0x00,0x55,0x68,0x20,0x61,0x35,0x68,0x20,0x52,0x35,
+0xef,0xfc,0xcd,0x64,0xae,0xa4,0x00,0x20,0x35,0x6a,0xe1,0xcd,0x0b,0x7b,0x68,0x20,
+0x22,0x35,0x60,0x20,0xc7,0x07,0x7b,0x61,0x20,0xc7,0x08,0x7b,0x62,0x20,0xc7,0x09,
+0x7b,0x63,0x20,0xc7,0x0a,0x7b,0x88,0x88,0x88,0x88,0x81,0x64,0x20,0x00,0x35,0x65,
+0x20,0x00,0x35,0x66,0x20,0x00,0x35,0x81,0x84,0xef,0xfc,0xcd,0x10,0xae,0xa4,0x00,
+0x84,0x35,0xa2,0xb7,0x55,0xaa,0xa2,0xb6,0xa3,0xb7,0x55,0xaa,0xa3,0xb6,0xa1,0xb7,
+0x55,0xaa,0xa1,0xb6,0x8c,0xfc,0xcd,0x18,0xa6,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,
+0xa3,0xb7,0x01,0x7b,0xef,0xfc,0xcd,0x14,0xae,0xa4,0x00,0x84,0x35,0xa0,0xb7,0x3f,
+0xa4,0xa0,0xb6,0xa1,0x3f,0xa2,0x3f,0xa3,0x3f,0x8c,0xfc,0xcd,0x18,0xa6,0x1e,0xfe,
+0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x04,0xab,0x5b,0x25,0x24,0x0f,0xa1,0x07,0x7b,0x05,
+0xc5,0xcd,0x64,0xa6,0x84,0x84,0x84,0x84,0x69,0xc5,0xcd,0xb3,0x00,0x3b,0xb4,0x00,
+0x3b,0xb5,0x00,0x3b,0xb6,0x00,0x3b,0x0f,0x20,0x8e,0xc5,0xcd,0xb3,0x00,0x3b,0xb4,
+0x00,0x3b,0xb5,0x00,0x3b,0xb6,0x00,0x3b,0x11,0x26,0x4a,0x70,0x01,0xc6,0x14,0x84,
+0x0f,0x35,0x15,0x84,0x00,0x35,0x16,0x84,0x00,0x35,0x17,0x84,0x00,0x35,0x04,0x6b,
+0x60,0x20,0xc6,0x05,0x6b,0x61,0x20,0xc6,0x06,0x6b,0x62,0x20,0xc6,0x07,0x6b,0x63,
+0x20,0xc6,0x68,0x20,0xc7,0x4a,0x9d,0x9d,0x68,0x20,0xc7,0x27,0xa6,0x68,0x20,0x52,
+0x35,0x64,0x20,0x05,0x35,0x65,0x20,0xf5,0x35,0x66,0x20,0xe1,0x35,0x67,0x20,0x00,
+0x35,0x60,0x20,0xb7,0x00,0x55,0x61,0x20,0xb8,0x00,0x55,0x62,0x20,0xb9,0x00,0x55,
+0x63,0x20,0xba,0x00,0x55,0xb3,0x00,0x60,0x20,0x55,0xb4,0x00,0x61,0x20,0x55,0xb5,
+0x00,0x62,0x20,0x55,0xb6,0x00,0x63,0x20,0x55,0x68,0x20,0x26,0x35,0x68,0x20,0x21,
+0x35,0x68,0x20,0x52,0x35,0x64,0x20,0x00,0x35,0x65,0x20,0x00,0x35,0x66,0x20,0x80,
+0x35,0x67,0x20,0x00,0x35,0x68,0x20,0x27,0x35,0xb7,0x00,0x60,0x20,0x55,0xb8,0x00,
+0x61,0x20,0x55,0xb9,0x00,0x62,0x20,0x55,0xba,0x00,0x63,0x20,0x55,0x68,0x20,0x52,
+0x35,0x64,0x20,0xc7,0x04,0x7b,0x65,0x20,0xc7,0x05,0x7b,0x66,0x20,0xc7,0x06,0x7b,
+0x67,0x20,0xc7,0x07,0x7b,0x68,0x20,0xc7,0x4a,0x68,0x20,0xc7,0x22,0xa6,0x60,0x20,
+0xc7,0x08,0x7b,0x61,0x20,0xc7,0x09,0x7b,0x62,0x20,0xc7,0x0a,0x7b,0x63,0x20,0xc7,
+0x0b,0x7b,0x68,0x20,0x52,0x35,0x10,0x84,0x02,0x35,0x11,0x84,0x55,0x35,0x12,0x84,
+0x55,0x35,0x13,0x84,0x55,0x35,0x01,0x6b,0x02,0xa6,0x09,0xe0,0xcd,0x67,0x20,0x28,
+0x35,0x09,0x20,0x01,0xa6,0x09,0xe0,0xcd,0x67,0x20,0x14,0x35,0x0b,0x25,0x02,0xa2,
+0x08,0x7b,0xdd,0xa0,0x09,0x7b,0x1e,0x20,0x4f,0x09,0xe0,0xcd,0x67,0x20,0x0a,0x35,
+0x0a,0x25,0x04,0xa2,0x08,0x7b,0xd9,0xa0,0x09,0x7b,0x88,0x81,0x52,0x01,0xce,0x53,
+0x01,0xc6,0xef,0xfc,0xcd,0x81,0x84,0x84,0xef,0xfc,0xcd,0x0c,0xae,0xa4,0x00,0x41,
+0x35,0x0e,0xfd,0xcd,0x41,0x4a,0x01,0xc9,0x41,0x4b,0x01,0xcb,0x17,0xad,0x08,0xae,
+0xa4,0x00,0x41,0x35,0x0e,0xfd,0xcd,0x5c,0x01,0x26,0x4c,0x26,0xad,0x04,0xae,0xa4,
+0x00,0x41,0x35,0x0e,0xfd,0xcd,0x41,0x48,0x01,0xc9,0x41,0x49,0x01,0xcb,0x41,0x01,
+0xe2,0x72,0x41,0x02,0xe0,0x72,0x50,0x01,0xce,0x51,0x01,0xc6,0xef,0xfc,0xcd,0x5f,
+0xa4,0x00,0x41,0x35,0x0e,0xfd,0xcd,0x5c,0x01,0x26,0x4c,0x41,0x01,0xe2,0x72,0x41,
+0x02,0xe0,0x72,0x50,0x01,0xce,0x51,0x01,0xc6,0x02,0x6b,0x55,0x01,0xc6,0x01,0x6b,
+0x54,0x01,0xc6,0x88,0x88,0x81,0x58,0x01,0xce,0x59,0x01,0xc6,0xef,0xfc,0xcd,0x81,
+0x41,0x54,0x01,0xc9,0x41,0x55,0x01,0xcb,0x46,0x54,0x4c,0x01,0xce,0x4d,0x01,0xc6,
+0x81,0x54,0x01,0xce,0x55,0x01,0xc6,0xef,0xfc,0xcd,0x81,0x8c,0xfc,0xcd,0x10,0xa6,
+0x0e,0xfd,0xcd,0x81,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x81,0x84,0x84,0x84,0x84,
+0x84,0x84,0x7c,0x83,0x00,0x35,0x7d,0x83,0x00,0x35,0x7e,0x83,0x0f,0x35,0x7f,0x83,
+0xff,0x35,0xef,0xfc,0xcd,0x6c,0xae,0xa4,0x00,0x83,0x35,0xd8,0xfd,0xcd,0x23,0xad,
+0x5b,0x2e,0xad,0x43,0xad,0xef,0xfc,0xcd,0x2d,0xad,0x5b,0x0e,0xfd,0xcd,0x4e,0xad,
+0x0d,0x20,0x48,0xad,0x39,0xad,0x5b,0x0e,0xfd,0xcd,0x54,0x01,0xce,0x55,0x01,0xc6,
+0x10,0x26,0x4a,0x47,0x01,0xc6,0xef,0xfc,0xcd,0x68,0xae,0xa4,0x00,0x83,0x35,0xd8,
+0xfd,0xcd,0x57,0xad,0x5b,0x62,0xad,0x6d,0xad,0x5e,0xad,0x5b,0x0e,0xfd,0xcd,0x75,
+0xad,0x64,0xae,0xa4,0x00,0x83,0x35,0xd8,0xfd,0xcd,0x6f,0xad,0x5b,0x7a,0xad,0x41,
+0x5a,0x01,0xc9,0x41,0x5b,0x01,0xcb,0x05,0xee,0x72,0x06,0x7b,0xef,0xfc,0xcd,0xb5,
+0xdd,0xcd,0x5b,0x0e,0xfd,0xcd,0x05,0xee,0x72,0x06,0x7b,0xef,0xfc,0xcd,0x60,0xae,
+0xa4,0x00,0x83,0x35,0xd8,0xfd,0xcd,0xb5,0xdd,0xcd,0x5b,0xbd,0xdd,0xcd,0x41,0x5a,
+0x01,0xc9,0x41,0x5b,0x01,0xcb,0xe1,0xdd,0xcd,0xb5,0xdd,0xcd,0x5b,0x0e,0xfd,0xcd,
+0xe1,0xdd,0xcd,0x5c,0xae,0xa4,0x00,0x83,0x35,0x0e,0xfd,0xcd,0x46,0x54,0x4c,0x01,
+0xce,0x4d,0x01,0xc6,0x05,0xef,0x72,0x06,0x6b,0x4a,0x5a,0x01,0x26,0x4d,0x58,0x01,
+0xce,0x59,0x01,0xc6,0x10,0x20,0x06,0x6b,0x59,0x01,0xc6,0x05,0x6b,0x58,0x01,0xc6,
+0x0c,0x26,0x4a,0x47,0x01,0xc6,0x88,0x88,0x88,0x88,0x88,0x88,0x81,0x50,0x01,0xce,
+0x51,0x01,0xc6,0xef,0xfc,0xcd,0x81,0x58,0x01,0xce,0x59,0x01,0xc6,0xef,0xfc,0xcd,
+0x81,0x4d,0x41,0x4a,0x01,0xc9,0x41,0x4b,0x01,0xcb,0x81,0x41,0x54,0x01,0xc9,0x41,
+0x55,0x01,0xcb,0x46,0x54,0x4c,0x01,0xce,0x4d,0x01,0xc6,0x81,0x0e,0xfd,0xcd,0x41,
+0xea,0x01,0xc2,0x41,0xeb,0x01,0xc0,0x31,0xad,0x81,0x4d,0x41,0xea,0x01,0xc2,0x41,
+0xeb,0x01,0xc0,0x41,0x48,0x01,0xc9,0x41,0x49,0x01,0xcb,0x45,0xad,0x81,0x54,0x01,
+0xce,0x55,0x01,0xc6,0xef,0xfc,0xcd,0x81,0x8c,0xfc,0xcd,0x10,0xa6,0x0e,0xfd,0xcd,
+0x81,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x81,0x8b,0x41,0x00,0xa9,0x41,0x0e,0xab,
+0x5b,0x98,0x83,0x00,0x35,0x99,0x83,0x00,0x35,0x9a,0x83,0x00,0x35,0x9b,0x83,0x18,
+0x35,0xef,0xfc,0xcd,0x3c,0xae,0xa4,0x00,0x83,0x35,0xd8,0xfd,0xcd,0x25,0xad,0x5b,
+0x30,0xad,0x67,0xad,0xef,0xfc,0xcd,0x2f,0xad,0x5b,0x0e,0xfd,0xcd,0x72,0xad,0x0d,
+0x20,0x4a,0xad,0x3b,0xad,0x5b,0x0e,0xfd,0xcd,0x54,0x01,0xce,0x55,0x01,0xc6,0x10,
+0x26,0x4a,0x47,0x01,0xc6,0xef,0xfc,0xcd,0x38,0xae,0xa4,0x00,0x83,0x35,0xd8,0xfd,
+0xcd,0x59,0xad,0x5b,0x64,0xad,0x6f,0xad,0x60,0xad,0x5b,0x0e,0xfd,0xcd,0x77,0xad,
+0x34,0xae,0xa4,0x00,0x83,0x35,0xd8,0xfd,0xcd,0x71,0xad,0x5b,0x7c,0xad,0x41,0x5a,
+0x01,0xc9,0x41,0x5b,0x01,0xcb,0x07,0xee,0x72,0x08,0x7b,0xef,0xfc,0xcd,0x58,0xdc,
+0xcd,0x5b,0x0e,0xfd,0xcd,0x07,0xee,0x72,0x08,0x7b,0xef,0xfc,0xcd,0x30,0xae,0xa4,
+0x00,0x83,0x35,0xd8,0xfd,0xcd,0x58,0xdc,0xcd,0x5b,0x60,0xdc,0xcd,0x41,0x5a,0x01,
+0xc9,0x41,0x5b,0x01,0xcb,0xb0,0xdc,0xcd,0x58,0xdc,0xcd,0x5b,0x0e,0xfd,0xcd,0xb0,
+0xdc,0xcd,0x2c,0xae,0xa4,0x00,0x83,0x35,0xd8,0xfd,0xcd,0x58,0xdc,0xcd,0x5b,0x60,
+0xdc,0xcd,0x41,0x56,0x01,0xc9,0x41,0x57,0x01,0xcb,0x69,0xdc,0xcd,0x58,0xdc,0xcd,
+0x5b,0x0e,0xfd,0xcd,0x69,0xdc,0xcd,0x10,0xae,0xa4,0x00,0x83,0x35,0xd8,0xfd,0xcd,
+0x58,0xdc,0xcd,0x5b,0x60,0xdc,0xcd,0x4a,0x5a,0x01,0x26,0xa6,0xdc,0xcd,0x0d,0xee,
+0x72,0x0e,0x7b,0xef,0xfc,0xcd,0x58,0xdc,0xcd,0x5b,0x0e,0xfd,0xcd,0x4a,0x5a,0x01,
+0x26,0x73,0xdc,0xcd,0x0c,0xae,0xa4,0x00,0x83,0x35,0xd8,0xfd,0xcd,0x58,0xdc,0xcd,
+0x5b,0x60,0xdc,0xcd,0x0d,0xee,0x72,0x0e,0x7b,0xef,0xfc,0xcd,0x58,0xdc,0xcd,0x5b,
+0x87,0xdc,0xcd,0x08,0xae,0xa4,0x00,0x83,0x35,0xd8,0xfd,0xcd,0x58,0xdc,0xcd,0x5b,
+0x60,0xdc,0xcd,0x4a,0x5a,0x01,0x26,0xa6,0xdc,0xcd,0x0d,0xee,0x72,0x0e,0x7b,0xef,
+0xfc,0xcd,0x58,0xdc,0xcd,0x5b,0x0e,0xfd,0xcd,0x4a,0x5a,0x01,0x26,0x73,0xdc,0xcd,
+0x04,0xae,0xa4,0x00,0x83,0x35,0xd8,0xfd,0xcd,0x58,0xdc,0xcd,0x5b,0x60,0xdc,0xcd,
+0x0d,0xee,0x72,0x0e,0x7b,0xef,0xfc,0xcd,0x58,0xdc,0xcd,0x5b,0x87,0xdc,0xcd,0x40,
+0xae,0xa4,0x00,0x83,0x35,0xa0,0x3f,0xa1,0x3f,0xa2,0xb7,0x0f,0xa4,0xa2,0xb6,0x0e,
+0xfd,0xcd,0x05,0xee,0x72,0x06,0x7b,0xef,0xfc,0xcd,0x28,0xae,0xa4,0x00,0x83,0x35,
+0xa0,0x3f,0xa1,0x3f,0xa2,0xb7,0x1f,0xa4,0xa2,0xb6,0x0e,0xfd,0xcd,0x4c,0x01,0xce,
+0x4d,0x01,0xc6,0x78,0x84,0xc7,0x09,0x7b,0x79,0x84,0xc7,0x0a,0x7b,0x7a,0x84,0xc7,
+0x0b,0x7b,0x7b,0x84,0xc7,0x0c,0x7b,0x07,0xef,0x72,0x08,0x6b,0x4a,0x5a,0x01,0x26,
+0x4d,0x58,0x01,0xce,0x59,0x01,0xc6,0x06,0x6b,0x4f,0x01,0xc6,0x05,0x6b,0x4e,0x01,
+0xc6,0x0a,0x6b,0x05,0xaa,0x0a,0x7b,0x20,0x20,0x08,0x6b,0x59,0x01,0xc6,0x07,0x6b,
+0x58,0x01,0xc6,0x05,0xef,0x72,0x06,0x6b,0x59,0x48,0x4e,0x01,0xce,0x4f,0x01,0xc6,
+0x0a,0x6b,0x01,0xaa,0x0a,0x7b,0x1f,0x26,0x4a,0x47,0x01,0xc6,0x09,0x6b,0x80,0xaa,
+0x09,0x7b,0x06,0x26,0x04,0xa1,0x6b,0x01,0xc6,0x09,0x6b,0x20,0xa6,0x0a,0x6b,0x0b,
+0x6b,0x0c,0x6b,0x4f,0x0d,0x6b,0x0e,0xef,0x72,0x58,0x01,0xc9,0x41,0x59,0x01,0xcb,
+0x52,0x01,0xce,0x53,0x01,0xc6,0x8b,0x41,0x00,0xa2,0x41,0x0e,0xa0,0x5b,0x81,0x84,
+0x84,0x84,0x84,0xe7,0x01,0x01,0x35,0xef,0xfc,0xcd,0xb4,0xae,0xa4,0x00,0x84,0x35,
+0xa0,0x16,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x5b,0x01,0x6b,0xe3,
+0xa4,0x01,0x7b,0x01,0x6b,0xb4,0x84,0xc6,0x02,0x6b,0xb5,0x84,0xc6,0x03,0x6b,0xb6,
+0x84,0xc6,0x04,0x6b,0xb7,0x84,0xc6,0x30,0x25,0x20,0xa1,0xf3,0x01,0xc6,0xef,0xfc,
+0xcd,0x78,0xae,0xa4,0x00,0x84,0x35,0xd8,0xfd,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,
+0xab,0x5b,0xa0,0xb7,0x03,0xa4,0xa0,0xb6,0xa1,0xb7,0xc0,0xa4,0xa1,0xb6,0xa2,0x3f,
+0xa3,0x3f,0x8c,0xfc,0xcd,0x16,0xa6,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,0x00,
+0x62,0x01,0x55,0x01,0x6b,0xfc,0xa4,0x01,0x7b,0x02,0x6b,0x3f,0xa4,0x02,0x7b,0x01,
+0x6b,0x78,0x84,0xc6,0x02,0x6b,0x79,0x84,0xc6,0x03,0x6b,0x7a,0x84,0xc6,0x04,0x6b,
+0x7b,0x84,0xc6,0xef,0xfc,0xcd,0x74,0xae,0xa4,0x00,0x84,0x35,0x0e,0xfd,0xcd,0x45,
+0x01,0xce,0x46,0x01,0xc6,0xef,0xfc,0xcd,0x70,0xae,0xa4,0x00,0x84,0x35,0x0e,0xfd,
+0xcd,0x43,0x01,0xce,0x44,0x01,0xc6,0xef,0xfc,0xcd,0x6c,0xae,0xa4,0x00,0x84,0x35,
+0x0e,0xfd,0xcd,0x41,0x01,0xce,0x42,0x01,0xc6,0xef,0xfc,0xcd,0x68,0xae,0xa4,0x00,
+0x84,0x35,0x0e,0xfd,0xcd,0x3f,0x01,0xce,0x40,0x01,0xc6,0xef,0xfc,0xcd,0x64,0xae,
+0xa4,0x00,0x84,0x35,0x0e,0xfd,0xcd,0x3d,0x01,0xce,0x3e,0x01,0xc6,0x12,0x20,0x64,
+0x84,0x00,0x35,0x65,0x84,0x00,0x35,0x66,0x84,0x02,0x35,0x67,0x84,0xcb,0x35,0x12,
+0x25,0x02,0xa2,0x3d,0x01,0xc6,0xcb,0xa0,0x3e,0x01,0xc6,0x1e,0x26,0x02,0xa1,0xee,
+0x01,0xc6,0x25,0x26,0x03,0xa1,0x70,0x01,0xc6,0x2c,0x24,0x20,0xa1,0xf3,0x01,0xc6,
+0xef,0xfc,0xcd,0x60,0xae,0xa4,0x00,0x84,0x35,0x0e,0xfd,0xcd,0x3b,0x01,0xce,0x3c,
+0x01,0xc6,0xef,0xfc,0xcd,0x5c,0xae,0xa4,0x00,0x84,0x35,0x0e,0xfd,0xcd,0x39,0x01,
+0xce,0x3a,0x01,0xc6,0xef,0xfc,0xcd,0x58,0xae,0xa4,0x00,0x84,0x35,0x0e,0xfd,0xcd,
+0x37,0x01,0xce,0x38,0x01,0xc6,0x88,0x88,0x88,0x88,0x81,0xa4,0xc6,0x92,0xa5,0xbf,
+0xa4,0xb7,0x81,0xa4,0xd6,0x92,0xa5,0x3f,0xa4,0xb7,0x81,0x84,0x45,0x01,0xcf,0x3d,
+0x01,0xce,0x46,0x01,0xc7,0x3e,0x01,0xc6,0x3d,0x01,0xcf,0x3e,0x01,0xc7,0x4a,0x5a,
+0x01,0x26,0x4d,0x59,0x48,0x65,0x01,0xce,0x66,0x01,0xc6,0x0b,0x20,0x0e,0x26,0x4d,
+0x65,0x01,0xce,0x66,0x01,0xc6,0x0b,0x26,0x4a,0x47,0x01,0xc6,0x43,0x01,0xcf,0x3b,
+0x01,0xce,0x44,0x01,0xc7,0x3c,0x01,0xc6,0x3c,0x01,0x5f,0x72,0x3b,0x01,0x5f,0x72,
+0x41,0x01,0xcf,0x39,0x01,0xce,0x42,0x01,0xc7,0x3a,0x01,0xc6,0x39,0x01,0xcf,0x3a,
+0x01,0xc7,0x4a,0x5a,0x01,0x26,0x4d,0xfa,0x26,0x5a,0x90,0x46,0x54,0x06,0x27,0x62,
+0x01,0xce,0x90,0x63,0x01,0xce,0x64,0x01,0xc6,0x3f,0x01,0xcf,0x37,0x01,0xce,0x40,
+0x01,0xc7,0x38,0x01,0xc6,0x38,0x01,0x5f,0x72,0x37,0x01,0x5f,0x72,0x62,0x01,0x5f,
+0x72,0x04,0x26,0x04,0xa1,0xee,0x01,0xc6,0x0b,0x26,0x03,0xa1,0x0f,0x20,0x52,0x01,
+0x5f,0x72,0x53,0x01,0x23,0x35,0x19,0x26,0x03,0xa1,0xee,0x01,0xc6,0x20,0x26,0x03,
+0xa1,0x15,0x24,0x70,0x01,0xc6,0x20,0xa1,0xf3,0x01,0xc6,0x65,0x01,0x4a,0x01,0x55,
+0x66,0x01,0x4b,0x01,0x55,0x69,0x01,0x4e,0x01,0x55,0x6a,0x01,0x4f,0x01,0x55,0x63,
+0x01,0x48,0x01,0x55,0x64,0x01,0x49,0x01,0x55,0x67,0x01,0x4c,0x01,0x55,0x68,0x01,
+0x4d,0x01,0x55,0x8e,0xd6,0xcc,0xbe,0xd8,0xcd,0xd3,0xa9,0x41,0x00,0xab,0x52,0x1c,
+0xae,0x01,0x7b,0xef,0xfc,0xcd,0x5e,0xae,0xa4,0x00,0x01,0x35,0x46,0xfc,0xcd,0xa4,
+0xb7,0xd2,0xa9,0x41,0xfc,0xab,0x52,0x1c,0xae,0x01,0x7b,0x5d,0x01,0xc7,0xbe,0xd8,
+0xcd,0xd2,0xa9,0x41,0xfb,0xab,0x52,0x1c,0xae,0x01,0x7b,0x5c,0x01,0xc7,0xbe,0xd8,
+0xcd,0xd2,0xa9,0x41,0xfa,0xab,0x52,0x1c,0xae,0x01,0x7b,0x5b,0x01,0xc7,0xa4,0xd6,
+0x92,0xa5,0x3c,0x5a,0x01,0xc7,0xb6,0xd8,0xcd,0xd2,0xa9,0x41,0xf8,0xab,0x52,0x1c,
+0xae,0x01,0x7b,0x58,0x01,0x5f,0x72,0x59,0x01,0x01,0x35,0x57,0x01,0xc7,0xa4,0xd6,
+0x92,0xa5,0x3c,0x56,0x01,0xc7,0xb6,0xd8,0xcd,0xd2,0xa9,0x41,0xf4,0xab,0x52,0x1c,
+0xae,0x01,0x7b,0x55,0x01,0xc7,0xa4,0xd6,0x92,0xa5,0x3c,0x54,0x01,0xc7,0xb6,0xd8,
+0xcd,0xd2,0xa9,0x41,0xf2,0xab,0x52,0x1c,0xae,0x01,0x7b,0x53,0x01,0xc7,0xa4,0xd6,
+0x92,0xa5,0x3c,0x52,0x01,0xc7,0xb6,0xd8,0xcd,0xd2,0xa9,0x41,0xf0,0xab,0x52,0x1c,
+0xae,0x01,0x7b,0x51,0x01,0xc7,0xa4,0xd6,0x92,0xa5,0x3c,0x50,0x01,0xc7,0xb6,0xd8,
+0xcd,0xd2,0xa9,0x41,0xee,0xab,0x52,0x1c,0xae,0x01,0x7b,0x4f,0x01,0xc7,0xa4,0xd6,
+0x92,0xa5,0x3c,0x4e,0x01,0xc7,0xb6,0xd8,0xcd,0xd2,0xa9,0x41,0xec,0xab,0x52,0x1c,
+0xae,0x01,0x7b,0x4d,0x01,0xc7,0xa4,0xd6,0x92,0xa5,0x3c,0x4c,0x01,0xc7,0xb6,0xd8,
+0xcd,0xd2,0xa9,0x41,0xea,0xab,0x52,0x1c,0xae,0x01,0x7b,0x4b,0x01,0xc7,0xa4,0xd6,
+0x92,0xa5,0x3c,0x4a,0x01,0xc7,0xb6,0xd8,0xcd,0xd2,0xa9,0x41,0xe8,0xab,0x52,0x1c,
+0xae,0x01,0x7b,0x49,0x01,0xc7,0xa4,0xd6,0x92,0xa5,0x3c,0x48,0x01,0xc7,0xb6,0xd8,
+0xcd,0xd2,0xa9,0x41,0xe6,0xab,0x52,0x1c,0xae,0x01,0x7b,0x47,0x01,0xc7,0xbe,0xd8,
+0xcd,0xd2,0xa9,0x41,0xe5,0xab,0x52,0x1c,0xae,0x01,0x7b,0xdd,0xd7,0xcc,0x05,0xd8,
+0xcc,0x03,0x27,0x02,0xa1,0xdd,0xd7,0xcc,0x03,0x26,0x03,0xa1,0x70,0x01,0xc6,0x62,
+0x01,0xc7,0x29,0xb6,0x5e,0x01,0x2a,0x00,0x55,0x5f,0x01,0x2b,0x00,0x55,0x60,0x01,
+0x2c,0x00,0x55,0x61,0x01,0x2d,0x00,0x55,0x5c,0x01,0xc7,0x13,0xb6,0x13,0xb6,0x5a,
+0x01,0xcf,0x22,0xbe,0x5b,0x01,0xc7,0x23,0xb6,0x58,0x01,0xcf,0x20,0xbe,0x59,0x01,
+0xc7,0x21,0xb6,0x56,0x01,0xcf,0x1e,0xbe,0x57,0x01,0xc7,0x1f,0xb6,0x54,0x01,0xcf,
+0x1c,0xbe,0x55,0x01,0xc7,0x1d,0xb6,0x52,0x01,0xcf,0x26,0xbe,0x53,0x01,0xc7,0x27,
+0xb6,0x50,0x01,0xcf,0x24,0xbe,0x51,0x01,0xc7,0x25,0xb6,0x4e,0x01,0xcf,0x18,0xbe,
+0x4f,0x01,0xc7,0x19,0xb6,0x4c,0x01,0xcf,0x14,0xbe,0x4d,0x01,0xc7,0x15,0xb6,0x4a,
+0x01,0xcf,0x1a,0xbe,0x4b,0x01,0xc7,0x1b,0xb6,0x48,0x01,0xcf,0x16,0xbe,0x49,0x01,
+0xc7,0x17,0xb6,0x47,0x01,0xc7,0x28,0xb6,0xa5,0xd6,0xcc,0x03,0x24,0x1d,0xa1,0x11,
+0xb6,0x09,0x27,0x11,0x3d,0x01,0x6b,0x4a,0x11,0xb6,0x88,0x00,0xf0,0x70,0x1c,0x05,
+0x01,0x01,0x03,0x00,0x03,0x00,0x8f,0x00,0x46,0x00,0x1b,0x00,0xaa,0x01,0x1e,0x03,
+0x00,0x07,0x00,0x03,0x50,0x05,0x01,0x00,0x60,0xa0,0x18,0x05,0x01,0x01,0x06,0x00,
+0x03,0x00,0x70,0x00,0x40,0x00,0x18,0x00,0xb0,0x01,0x1b,0x03,0x00,0x07,0x00,0x03,
+0x50,0x05,0x01,0x00,0xe0,0x1b,0xfa,0x04,0x01,0x00,0x06,0x00,0x03,0x00,0x80,0x00,
+0x48,0x00,0x1c,0x00,0x90,0x01,0x3f,0x03,0x90,0x06,0x20,0x03,0x00,0x05,0x01,0x00,
+0xc0,0x5f,0x3b,0x04,0x00,0x01,0x06,0x00,0x03,0x00,0x20,0x00,0x30,0x00,0x14,0x00,
+0xa0,0x00,0x37,0x03,0xa0,0x05,0x20,0x03,0x00,0x05,0x01,0x00,0xe0,0x12,0xbd,0x04,
+0x01,0x00,0x07,0x00,0x03,0x00,0x80,0x00,0x40,0x00,0x1b,0x00,0x80,0x01,0x1e,0x03,
+0x80,0x06,0x00,0x03,0x00,0x05,0x01,0x00,0x90,0x69,0x11,0x04,0x00,0x01,0x07,0x00,
+0x03,0x00,0x20,0x00,0x30,0x00,0x13,0x00,0xa0,0x00,0x16,0x03,0xa0,0x05,0x00,0x03,
+0x00,0x05,0x01,0x00,0x40,0xd2,0xdf,0x03,0x00,0x00,0x06,0x00,0x03,0x00,0x88,0x00,
+0x18,0x00,0x23,0x00,0x40,0x01,0x26,0x03,0x40,0x05,0x00,0x03,0x00,0x04,0x01,0x00,
+0xf0,0xfb,0x02,0x02,0x01,0x01,0x08,0x00,0x06,0x00,0x70,0x00,0x10,0x00,0x1f,0x00,
+0xf0,0x00,0x05,0x02,0x40,0x04,0xe0,0x01,0x50,0x03,0x01,0x00,0x00,0x5a,0x62,0x02,
+0x01,0x01,0x04,0x00,0x01,0x00,0x80,0x00,0x28,0x00,0x1b,0x00,0x00,0x01,0x74,0x02,
+0x20,0x04,0x58,0x02,0x20,0x03,0x01,0x00,0x10,0xf7,0x6c,0x04,0x01,0x01,0x05,0x00,
+0x01,0x00,0x28,0x00,0xe0,0x06,0x19,0x00,0xe4,0x07,0xee,0x02,0xe4,0x0c,0xd0,0x02,
+0x00,0x05,0x01,0x00,0x10,0xf7,0x6c,0x04,0x01,0x01,0x05,0x00,0x01,0x00,0x28,0x00,
+0x74,0x09,0x19,0x00,0x78,0x0a,0xee,0x02,0x78,0x0f,0xd0,0x02,0x00,0x05,0x01,0x00,
+0x40,0x5f,0x8a,0x03,0x01,0x01,0x05,0x00,0x01,0x00,0x28,0x00,0xe0,0x06,0x19,0x00,
+0xe4,0x07,0xee,0x02,0xe4,0x0c,0xd0,0x02,0x00,0x05,0x01,0x00,0x10,0xf7,0x6c,0x04,
+0x01,0x01,0x05,0x00,0x01,0x00,0x2c,0x00,0x58,0x00,0x29,0x00,0x18,0x01,0x65,0x04,
+0x98,0x08,0x38,0x04,0x80,0x07,0x01,0x00,0x10,0xf7,0x6c,0x04,0x01,0x01,0x05,0x00,
+0x01,0x00,0x2c,0x00,0x10,0x02,0x29,0x00,0xd0,0x02,0x65,0x04,0x50,0x0a,0x38,0x04,
+0x80,0x07,0x01,0x00,0x10,0xf7,0x6c,0x04,0x01,0x01,0x05,0x00,0x01,0x00,0x2c,0x00,
+0x7e,0x02,0x29,0x00,0x3e,0x03,0x65,0x04,0xbe,0x0a,0x38,0x04,0x80,0x07,0x01,0x00,
+0x20,0xee,0xd9,0x08,0x01,0x01,0x05,0x00,0x01,0x00,0x2c,0x00,0x10,0x02,0x29,0x00,
+0xd0,0x02,0x65,0x04,0x50,0x0a,0x38,0x04,0x80,0x07,0x01,0x00,0x80,0xf9,0x37,0x03,
+0x00,0x00,0x05,0x00,0x01,0x00,0x80,0x00,0x18,0x00,0x2c,0x00,0x20,0x01,0x71,0x02,
+0xc0,0x06,0x40,0x02,0xa0,0x05,0x01,0x01,0xc0,0xfc,0x9b,0x01,0x00,0x00,0x03,0x00,
+0x01,0x00,0x7e,0x00,0x18,0x00,0x16,0x00,0x20,0x01,0x71,0x02,0xc0,0x06,0x20,0x01,
+0xa0,0x05,0x00,0x00,0x10,0xf7,0x6c,0x04,0x01,0x01,0x05,0x00,0x01,0x00,0x2c,0x00,
+0x10,0x02,0x14,0x00,0xd0,0x02,0x65,0x04,0x50,0x0a,0x1c,0x02,0x80,0x07,0x00,0x00,
+0x10,0xf7,0x6c,0x04,0x01,0x01,0x05,0x00,0x01,0x00,0x28,0x00,0xb8,0x01,0x19,0x00,
+0xbc,0x02,0xee,0x02,0xbc,0x07,0xd0,0x02,0x00,0x05,0x01,0x00,0xc0,0xfc,0x9b,0x01,
+0x00,0x00,0x05,0x00,0x01,0x00,0x40,0x00,0x0c,0x00,0x2c,0x00,0x90,0x00,0x71,0x02,
+0x60,0x03,0x40,0x02,0xd0,0x02,0x01,0x00,0x20,0xee,0xd9,0x08,0x01,0x01,0x05,0x00,
+0x01,0x00,0x2c,0x00,0x58,0x00,0x29,0x00,0x18,0x01,0x65,0x04,0x98,0x08,0x38,0x04,
+0x80,0x07,0x01,0x00,0x80,0xf9,0x37,0x03,0x00,0x00,0x06,0x00,0x07,0x00,0x7c,0x00,
+0x20,0x00,0x24,0x00,0x14,0x01,0x0d,0x02,0xb4,0x06,0xe0,0x01,0xa0,0x05,0x01,0x01,
+0xc0,0xfc,0x9b,0x01,0x00,0x00,0x03,0x00,0x04,0x00,0x7c,0x00,0x26,0x00,0x12,0x00,
+0x14,0x01,0x0d,0x02,0xb4,0x06,0xf0,0x00,0xa0,0x05,0x00,0x00,0x10,0xf7,0x6c,0x04,
+0x01,0x01,0x05,0x00,0x01,0x00,0x2c,0x00,0x58,0x00,0x14,0x00,0x18,0x01,0x65,0x04,
+0x98,0x08,0x1c,0x02,0x80,0x07,0x00,0x00,0x10,0xf7,0x6c,0x04,0x01,0x01,0x05,0x00,
+0x01,0x00,0x28,0x00,0x6e,0x00,0x19,0x00,0x72,0x01,0xee,0x02,0x72,0x06,0xd0,0x02,
+0x00,0x05,0x01,0x00,0xc0,0xfc,0x9b,0x01,0x00,0x00,0x06,0x00,0x07,0x00,0x3e,0x00,
+0x10,0x00,0x24,0x00,0x8a,0x00,0x0d,0x02,0x5a,0x03,0xe0,0x01,0xd0,0x02,0x01,0x00,
+0x80,0x85,0x80,0x01,0x00,0x00,0x02,0x00,0x01,0x00,0x60,0x00,0x10,0x00,0x23,0x00,
+0xa0,0x00,0x0d,0x02,0x20,0x03,0xe0,0x01,0x80,0x02,0x01,0x81,0x84,0x8b,0x41,0x00,
+0xa9,0x41,0x08,0xab,0x5b,0x09,0x6b,0x01,0x6b,0x4f,0x01,0x20,0x01,0xa6,0x04,0x26,
+0xbf,0x01,0xc1,0x05,0x7b,0x0b,0x26,0x04,0xe1,0x72,0xc0,0x01,0xc6,0x08,0x27,0xbf,
+0x01,0xc6,0xb7,0x26,0x46,0xfd,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x02,0xab,0x5b,0x1e,
+0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x06,0xab,0x5b,0x06,0x6b,0x30,0x48,0xc6,0x07,
+0x6b,0x31,0x48,0xc6,0x08,0x6b,0x32,0x48,0xc6,0x09,0x6b,0x33,0x48,0xc6,0x84,0x84,
+0xc0,0x1b,0xcd,0x74,0xae,0x08,0xa6,0x02,0x4b,0x00,0x4b,0x02,0x6b,0x06,0x7b,0x03,
+0x6b,0x07,0x7b,0x04,0x6b,0x08,0x7b,0x05,0x6b,0x09,0x7b,0x06,0x6b,0x30,0x48,0xc6,
+0x07,0x6b,0x31,0x48,0xc6,0x08,0x6b,0x32,0x48,0xc6,0x09,0x6b,0x33,0x48,0xc6,0x01,
+0x6b,0x4f,0x8b,0x41,0x00,0xa2,0x41,0x09,0xa0,0x5b,0xa9,0x20,0x17,0xb6,0x17,0xb7,
+0x80,0xaa,0x02,0x7b,0x84,0x84,0xc0,0x1b,0xcd,0x74,0xae,0x20,0xa6,0x14,0x4b,0x88,
+0x07,0xab,0x01,0x7b,0xf0,0x25,0x05,0xa1,0x03,0x6b,0x4c,0x9f,0x18,0xe7,0xbf,0x01,
+0xd6,0x03,0xee,0x72,0x03,0x6b,0x12,0x26,0x4a,0x02,0x7b,0x84,0x84,0xc0,0x1b,0xcd,
+0x74,0xae,0x43,0xa6,0x88,0x02,0x7b,0x07,0x4b,0x25,0x27,0x02,0x7b,0x01,0x6b,0x52,
+0x05,0xae,0xf0,0x24,0x14,0xa1,0x02,0x6b,0x7f,0xa4,0xbf,0x01,0xc6,0x81,0x85,0x85,
+0x85,0x4f,0x05,0xc0,0x01,0x07,0x72,0x05,0x20,0x02,0xbf,0x01,0x0f,0x72,0x84,0x84,
+0xc0,0x1b,0xcd,0x74,0xae,0x41,0xa6,0x02,0x4b,0x00,0x4b,0x19,0x26,0x60,0xa1,0x60,
+0xa4,0xbf,0x01,0xc6,0x84,0x84,0xc0,0x1b,0xcd,0x74,0xae,0x40,0xa6,0x01,0x4b,0x00,
+0x4b,0x88,0x88,0x88,0x81,0xa0,0x01,0xc6,0xa0,0x01,0x01,0x35,0x04,0xbf,0x01,0x0d,
+0x72,0x84,0x84,0xc0,0x1b,0xcd,0x74,0xae,0x40,0xa6,0x01,0x4b,0x00,0x4b,0xa0,0x01,
+0x5f,0x72,0x81,0x85,0x9a,0xef,0xfc,0xcd,0x14,0xae,0xa4,0x00,0x48,0x35,0xa0,0xb7,
+0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,0x00,0xa3,0x01,0x55,0x10,0x48,0xa4,0x01,0x55,0x11,
+0x48,0xa5,0x01,0x55,0x12,0x48,0xa6,0x01,0x55,0x13,0x48,0xa7,0x01,0x55,0x9b,0xcd,
+0x25,0x05,0xa1,0x01,0x6b,0x4c,0x01,0x7b,0xd2,0xfd,0xcd,0xa4,0xae,0xa4,0x00,0x01,
+0x35,0x8c,0xfc,0xcd,0x48,0x48,0x48,0x9f,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,
+0xb7,0xbf,0x01,0xd6,0x01,0xee,0x72,0x1f,0x20,0xa3,0x01,0xc3,0x01,0x55,0x07,0x26,
+0x04,0xa1,0x01,0x6b,0x4f,0x84,0x84,0xa4,0x01,0x5f,0x72,0xa5,0x01,0x5f,0x72,0xa6,
+0x01,0x5f,0x72,0xa7,0x01,0x5f,0x72,0xc0,0x1b,0xcd,0x74,0xae,0x4f,0x05,0x4b,0x00,
+0x4b,0x88,0x81,0x84,0x8b,0x41,0x00,0xa9,0x41,0x08,0xab,0x5b,0x09,0x6b,0x01,0x6b,
+0x85,0x9b,0x1c,0xcd,0x74,0xae,0x10,0xa6,0x05,0x4b,0xc3,0x01,0xc7,0x05,0x7b,0x02,
+0x6b,0xb7,0x01,0xc6,0x03,0x6b,0xb8,0x01,0xc6,0x04,0x6b,0xb9,0x01,0xc6,0x05,0x6b,
+0xba,0x01,0xc6,0xc2,0x01,0xc7,0x02,0x7b,0xc1,0x01,0xc7,0x03,0x7b,0xc0,0x01,0xc7,
+0x04,0x7b,0xbf,0x01,0xc7,0x05,0x7b,0x02,0x6b,0xb3,0x01,0xc6,0x03,0x6b,0xb4,0x01,
+0xc6,0x04,0x6b,0xb5,0x01,0xc6,0x05,0x6b,0xb6,0x01,0xc6,0x01,0x6b,0x85,0x9b,0x1c,
+0xcd,0x74,0xae,0x18,0xa6,0x08,0x4b,0xc6,0x01,0xc7,0x06,0x7b,0xc5,0x01,0xc7,0x07,
+0x7b,0xc4,0x01,0xc7,0x08,0x7b,0xc3,0x01,0xc7,0x09,0x7b,0x06,0x6b,0x1c,0x48,0xc6,
+0x07,0x6b,0x1d,0x48,0xc6,0x08,0x6b,0x1e,0x48,0xc6,0x09,0x6b,0x1f,0x48,0xc6,0xc2,
+0x01,0xc7,0x06,0x7b,0xc1,0x01,0xc7,0x07,0x7b,0xc0,0x01,0xc7,0x08,0x7b,0xbf,0x01,
+0xc7,0x09,0x7b,0x06,0x6b,0x18,0x48,0xc6,0x07,0x6b,0x19,0x48,0xc6,0x08,0x6b,0x1a,
+0x48,0xc6,0x09,0x6b,0x1b,0x48,0xc6,0x01,0x6b,0x4f,0x8b,0x41,0x00,0xa2,0x41,0x09,
+0xa0,0x5b,0x81,0x85,0x85,0x85,0x85,0x85,0x01,0x7b,0x01,0x6b,0x01,0xa6,0x04,0x26,
+0x14,0xa1,0x02,0x7b,0x0a,0x26,0x14,0xa1,0x03,0x7b,0xd1,0x25,0x05,0xa1,0x05,0x6b,
+0x4c,0x05,0x7b,0xdd,0x25,0x08,0xa1,0x04,0x6b,0x4c,0x04,0x7b,0xbf,0x01,0x44,0x72,
+0x02,0x6b,0x4c,0x02,0x7b,0x05,0x20,0x03,0x6b,0x4c,0x03,0x7b,0x07,0x26,0x01,0xa4,
+0xbf,0x01,0xd6,0x05,0xee,0x72,0x04,0x6b,0x4f,0x05,0x6b,0x4f,0x44,0x20,0x4f,0x03,
+0x27,0x85,0x85,0x4d,0xc0,0x1b,0xcd,0x74,0xae,0x05,0x4b,0x00,0x4b,0x9a,0x14,0x48,
+0xb7,0x01,0x55,0x15,0x48,0xb8,0x01,0x55,0x16,0x48,0xb9,0x01,0x55,0x17,0x48,0xba,
+0x01,0x55,0x10,0x48,0xb3,0x01,0x55,0x11,0x48,0xb4,0x01,0x55,0x12,0x48,0xb5,0x01,
+0x55,0x13,0x48,0xb6,0x01,0x55,0x9b,0x03,0x6b,0x02,0x6b,0x01,0x6b,0x4f,0x88,0x88,
+0x88,0x88,0x88,0x81,0xa4,0x00,0x48,0x35,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,
+0xb7,0x81,0x85,0x85,0x85,0x85,0x01,0x7b,0x9a,0xef,0xfc,0xcd,0x0c,0xae,0x0d,0xad,
+0x03,0x7b,0x9b,0x01,0x6b,0x01,0xa6,0x04,0x26,0x02,0x7b,0x04,0x26,0x4a,0x03,0x7b,
+0xee,0x26,0x02,0x7b,0x06,0x26,0x03,0x7b,0x02,0x6b,0x4a,0x02,0x7b,0x03,0x6b,0x0f,
+0x48,0xc6,0x0a,0x20,0x9a,0xef,0xfc,0xcd,0x5f,0x38,0xad,0x04,0x7b,0x9b,0x02,0x6b,
+0x32,0xa6,0x01,0x6b,0x03,0x6b,0x4f,0x88,0x88,0x88,0x88,0x81,0x1e,0xfe,0xcd,0xa4,
+0xb7,0x00,0xa9,0x41,0x05,0xab,0x81,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x9a,
+0xef,0xfc,0xcd,0x04,0xae,0xa4,0x00,0x48,0x35,0xa3,0x15,0xa0,0xb7,0xa1,0xb7,0xa2,
+0xb7,0x4f,0xa3,0xb7,0x03,0xaa,0x3f,0xa4,0x08,0x7b,0x9b,0x05,0x6b,0x04,0x48,0xc6,
+0x06,0x6b,0x05,0x48,0xc6,0x07,0x6b,0x06,0x48,0xc6,0x08,0x6b,0x07,0x48,0xc6,0x9a,
+0xef,0xfc,0xcd,0x5f,0xa4,0x00,0x40,0x35,0xd8,0xfd,0xcd,0xa4,0xb7,0x00,0xa9,0x41,
+0x01,0xab,0x5b,0xa3,0x14,0x4f,0xad,0x5b,0xef,0xfc,0xcd,0xa4,0xb7,0x00,0xa9,0x41,
+0x01,0xab,0x5b,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,0x00,0x9d,0x01,0x55,0x9b,
+0x05,0x6b,0x00,0x40,0xc6,0x06,0x6b,0x01,0x40,0xc6,0x07,0x6b,0x02,0x40,0xc6,0x08,
+0x6b,0x03,0x40,0xc6,0x9a,0xef,0xfc,0xcd,0xb4,0xae,0xa4,0x00,0x84,0x35,0xa3,0x1a,
+0xa3,0x19,0x7a,0xcf,0xcd,0x5b,0x9b,0x05,0x6b,0xb4,0x84,0xc6,0x06,0x6b,0xb5,0x84,
+0xc6,0x07,0x6b,0xb6,0x84,0xc6,0x08,0x6b,0xb7,0x84,0xc6,0x88,0x88,0x88,0x88,0x88,
+0x88,0x88,0x88,0x81,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x81,0x84,
+0x84,0x84,0x84,0x9a,0xef,0xfc,0xcd,0x04,0xae,0xa4,0x00,0x48,0x35,0xa3,0x14,0x11,
+0xad,0x5b,0x9b,0x01,0x6b,0x04,0x48,0xc6,0x02,0x6b,0x05,0x48,0xc6,0x03,0x6b,0x06,
+0x48,0xc6,0x04,0x6b,0x07,0x48,0xc6,0x9a,0xef,0xfc,0xcd,0x5f,0xa4,0x00,0x40,0x35,
+0xa3,0x15,0x34,0xad,0x5b,0x9b,0x01,0x6b,0x00,0x40,0xc6,0x02,0x6b,0x01,0x40,0xc6,
+0x03,0x6b,0x02,0x40,0xc6,0x04,0x6b,0x03,0x40,0xc6,0x88,0x88,0x88,0x88,0x81,0x85,
+0x85,0x85,0x85,0x4f,0x9a,0xef,0xfc,0xcd,0x04,0xae,0xa4,0x00,0x48,0x35,0xa0,0x3f,
+0xa1,0x3f,0xa2,0x3f,0xa3,0xb7,0x30,0xa4,0xa3,0xb6,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,
+0x4f,0xa3,0xb7,0x04,0x7b,0x9b,0x01,0x6b,0x04,0x48,0xc6,0x02,0x6b,0x05,0x48,0xc6,
+0x03,0x6b,0x06,0x48,0xc6,0x04,0x6b,0x07,0x48,0xc6,0x9a,0xef,0xfc,0xcd,0x5f,0xa4,
+0x00,0x40,0x35,0xa3,0x15,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x5b,
+0x9b,0x01,0x6b,0x00,0x40,0xc6,0x02,0x6b,0x01,0x40,0xc6,0x03,0x6b,0x02,0x40,0xc6,
+0x04,0x6b,0x03,0x40,0xc6,0x88,0x88,0x88,0x88,0x81,0x9a,0xd2,0xfc,0xcd,0xa4,0xb7,
+0x40,0xa9,0x41,0x00,0xab,0x81,0xa4,0xb7,0x00,0xa9,0x41,0x07,0xab,0x81,0x8b,0x41,
+0x00,0xa9,0x41,0x0d,0xab,0x5b,0x9a,0xef,0xfc,0xcd,0x30,0xae,0xa4,0x00,0x42,0x35,
+0xd8,0xfd,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x5b,0x8c,0xfc,0xcd,0x48,0x48,
+0x12,0x7b,0xa0,0x3f,0xa1,0x3f,0xa2,0x3f,0xa3,0x00,0x03,0x35,0x9b,0x01,0x6b,0x30,
+0x42,0xc6,0x02,0x6b,0x31,0x42,0xc6,0x03,0x6b,0x32,0x42,0xc6,0x04,0x6b,0x33,0x42,
+0xc6,0x24,0xcd,0xcc,0x03,0x25,0x06,0xe1,0x72,0xa4,0xd6,0x92,0x02,0xae,0xa5,0xb7,
+0x0d,0x7b,0xa4,0xb7,0x0c,0x7b,0x06,0x6b,0x04,0xab,0x06,0x7b,0x67,0xad,0x5c,0x01,
+0x24,0x06,0xeb,0x72,0x10,0xee,0x72,0x11,0x7b,0x1e,0xfe,0xcd,0x6f,0xad,0x5b,0x9b,
+0xaf,0x25,0x04,0xa1,0x0b,0x6b,0x4c,0x0b,0x7b,0xa4,0xc7,0x92,0xa5,0xbf,0x85,0xa4,
+0x00,0x32,0xa4,0xd6,0x92,0x03,0xae,0xa5,0xbf,0xa4,0xb7,0x0e,0xe9,0x72,0x41,0x0f,
+0xeb,0x72,0x5f,0x4a,0x07,0x6b,0x4c,0x07,0x7b,0xa4,0x00,0x3b,0x89,0xa4,0x3c,0x02,
+0x24,0x97,0xa5,0xbb,0x0b,0xe0,0x72,0x03,0xa6,0xa5,0xbf,0xf3,0xcd,0xcd,0x5b,0x36,
+0x25,0x05,0xe1,0x72,0xa4,0xd6,0x92,0x02,0xae,0xa5,0xb7,0x0d,0x7b,0xa4,0xb7,0x0c,
+0x7b,0x0b,0x6b,0x07,0x6b,0x08,0x6b,0x09,0x6b,0x0a,0x6b,0x4f,0x74,0x20,0x05,0x6b,
+0x4f,0x10,0x6b,0x00,0xa9,0x10,0x7b,0x11,0x6b,0x04,0xab,0x11,0x7b,0xfb,0xcd,0xcd,
+0x10,0xee,0x72,0x11,0x7b,0x1e,0xfe,0xcd,0xf3,0xcd,0xcd,0x5b,0x9b,0x07,0x6b,0x4f,
+0x08,0x6b,0xa4,0xd6,0x92,0x5c,0x09,0x6b,0xa4,0xd6,0x92,0x01,0xae,0xa5,0xb7,0x0d,
+0x7b,0xa4,0xb7,0x0c,0x7b,0x0a,0x6b,0xa4,0xc6,0x92,0xa5,0xb7,0x0d,0x7b,0xa4,0xbf,
+0x0c,0xee,0x72,0x8b,0x41,0x00,0xa2,0x41,0x0b,0xa0,0x5b,0x89,0x88,0x81,0x01,0xa6,
+0x15,0x00,0xde,0x35,0x14,0x00,0xc0,0x35,0x13,0x00,0xad,0x35,0x12,0x00,0xde,0x35,
+0x50,0x18,0xcd,0x81,0x84,0x84,0x84,0x84,0x84,0x7e,0xcb,0xcd,0xac,0x25,0x10,0xa1,
+0x04,0x6b,0x4c,0x04,0x7b,0xc2,0x25,0x08,0xa1,0x05,0x6b,0x4c,0x05,0x7b,0xc6,0xcb,
+0xcd,0x03,0x27,0x5d,0x03,0x26,0x4d,0x41,0x01,0xe4,0x72,0x41,0x02,0xe4,0x72,0xfa,
+0x26,0x5a,0x90,0x59,0x48,0x06,0x27,0x05,0xe6,0x72,0x01,0xa6,0x01,0xef,0x72,0x02,
+0x6b,0x5f,0x03,0x7b,0xeb,0xcb,0xcd,0x03,0x20,0x9a,0xad,0x04,0x26,0x05,0x7b,0x08,
+0x26,0x04,0x7b,0x05,0x6b,0x4f,0x03,0x6b,0x12,0xe6,0x97,0x04,0xe0,0x72,0x0f,0xa6,
+0x04,0x6b,0x4f,0xf5,0x25,0x10,0xa1,0x04,0x6b,0x4c,0x04,0x7b,0x97,0xad,0x04,0x6b,
+0x4f,0xc6,0xcb,0xcd,0x7e,0xcb,0xcd,0xa8,0xcb,0xcd,0x88,0x88,0x88,0x88,0x88,0x81,
+0xb3,0x84,0x00,0x35,0xb0,0x84,0x00,0x35,0xb1,0x84,0x00,0x35,0x81,0xb0,0x84,0x00,
+0x35,0xb1,0x84,0x00,0x35,0xb2,0x84,0x00,0x35,0x0d,0xad,0xb2,0x84,0x03,0x35,0x13,
+0xad,0xb2,0x84,0x02,0x35,0x19,0xad,0xb2,0x84,0x00,0x35,0xb3,0x84,0x00,0x35,0x81,
+0xb0,0x84,0x00,0x35,0xb1,0x84,0x00,0x35,0xb2,0x84,0x00,0x35,0xb3,0x84,0x00,0x35,
+0x81,0x01,0xad,0xb0,0x84,0x00,0x35,0xb1,0x84,0x00,0x35,0xb2,0x84,0x01,0x35,0xb3,
+0x84,0x00,0x35,0x13,0xad,0x81,0x64,0xa6,0xb0,0x84,0x00,0x35,0xb1,0x84,0x00,0x35,
+0x05,0xc5,0xcc,0x03,0xad,0xb2,0x84,0x00,0x35,0xb3,0x84,0x00,0x35,0x05,0xc5,0xcd,
+0x10,0xad,0xb2,0x84,0x04,0x35,0xb3,0x84,0x00,0x35,0x81,0xad,0x84,0x00,0x35,0xae,
+0x84,0x00,0x35,0xaf,0x84,0x00,0x35,0x81,0xac,0x84,0x00,0x35,0x05,0xad,0x81,0xac,
+0x84,0x80,0x35,0x0c,0xad,0x07,0x27,0x4d,0x05,0xc5,0xcc,0x64,0xa6,0xb0,0x84,0x00,
+0x35,0xb1,0x84,0x00,0x35,0xb2,0x84,0x00,0x35,0xb3,0x84,0x00,0x35,0x05,0xc5,0xcd,
+0x0a,0xa6,0xb0,0x84,0x00,0x35,0xb1,0x84,0x00,0x35,0xb2,0x84,0x10,0x35,0xb3,0x84,
+0x00,0x35,0x81,0x84,0x84,0x84,0x84,0xe7,0x01,0x01,0x35,0xef,0xfc,0xcd,0x5f,0xa4,
+0x00,0x84,0x35,0xa0,0x1a,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x5b,
+0x01,0x6b,0x00,0x84,0xc6,0x02,0x6b,0x01,0x84,0xc6,0x03,0x6b,0x02,0x84,0xc6,0x04,
+0x6b,0x03,0x84,0xc6,0x88,0x88,0x88,0x88,0x81,0x84,0x84,0x84,0x84,0xef,0xfc,0xcd,
+0x5f,0xa4,0x00,0x84,0x35,0xa0,0x1b,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,
+0xab,0x5b,0x01,0x6b,0x00,0x84,0xc6,0x02,0x6b,0x01,0x84,0xc6,0x03,0x6b,0x02,0x84,
+0xc6,0x04,0x6b,0x03,0x84,0xc6,0x88,0x88,0x88,0x88,0x81,0xef,0xfc,0xcd,0x78,0xae,
+0xa4,0x00,0x84,0x35,0x81,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x81,
+0x84,0x84,0x84,0x84,0x10,0xad,0xa3,0x1d,0x09,0xad,0x5b,0x17,0xad,0xa3,0x1c,0x10,
+0xad,0x5b,0x01,0x6b,0x78,0x84,0xc6,0x02,0x6b,0x79,0x84,0xc6,0x03,0x6b,0x7a,0x84,
+0xc6,0x04,0x6b,0x7b,0x84,0xc6,0x88,0x88,0x88,0x88,0x81,0x0e,0xfd,0xcd,0xbc,0x00,
+0xce,0xbd,0x00,0xc6,0x81,0x84,0x84,0x84,0x84,0xa0,0x84,0xc7,0x01,0x7b,0xa1,0x84,
+0xc7,0x02,0x7b,0xa2,0x84,0xc7,0x03,0x7b,0xa3,0x84,0xc7,0x04,0x7b,0x03,0x6b,0x80,
+0xaa,0x03,0x7b,0x04,0x6b,0x06,0xaa,0x04,0x7b,0x0c,0x20,0xd2,0xfd,0xcd,0xa4,0xb7,
+0x00,0xa9,0x41,0x01,0xab,0x5b,0xa2,0x1e,0x34,0xad,0x11,0x24,0x00,0xa2,0xbc,0x00,
+0xc6,0x06,0xa0,0xbd,0x00,0xc6,0x0c,0x27,0x71,0x01,0xc6,0x22,0x25,0x02,0xa1,0xbb,
+0x00,0xc6,0x29,0x26,0x4a,0x70,0x01,0xc6,0x03,0x6b,0x80,0xa4,0x03,0x7b,0x04,0x6b,
+0x4f,0x01,0x6b,0xa0,0x84,0xc6,0x02,0x6b,0xa1,0x84,0xc6,0x03,0x6b,0xa2,0x84,0xc6,
+0x04,0x6b,0xa3,0x84,0xc6,0x6c,0x25,0x02,0xa1,0xbb,0x00,0xc6,0x73,0x26,0x4a,0x70,
+0x01,0xc6,0x0d,0x27,0x4a,0x71,0x01,0xc6,0xef,0xfc,0xcd,0x78,0xae,0xa4,0x00,0x84,
+0x35,0xa0,0x1c,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x5b,0x01,0x6b,
+0x78,0x84,0xc6,0x02,0x6b,0x79,0x84,0xc6,0x03,0x6b,0x7a,0x84,0xc6,0x04,0x6b,0x7b,
+0x84,0xc6,0x48,0x83,0x00,0x35,0x49,0x83,0x00,0x35,0x4a,0x83,0x00,0x35,0x4b,0x83,
+0x0f,0x35,0x10,0x26,0x4a,0x24,0x83,0x00,0x35,0x25,0x83,0x00,0x35,0x26,0x83,0x00,
+0x35,0x27,0x83,0x00,0x35,0x4d,0x27,0x70,0x01,0xc6,0xef,0xfc,0xcd,0xa4,0xae,0xa4,
+0x00,0x84,0x35,0xa2,0x1e,0xa0,0x3f,0xa1,0x3f,0xa2,0x1f,0xcc,0xca,0xcd,0x14,0x26,
+0x03,0xa1,0x04,0x27,0x01,0xa1,0xbb,0x00,0xc6,0x1f,0x26,0x4a,0x70,0x01,0xc6,0xef,
+0xfc,0xcd,0xa0,0xae,0xa4,0x00,0x84,0x35,0xa0,0x1f,0xa2,0x3f,0xa3,0x3f,0x8c,0xfc,
+0xcd,0x10,0xa6,0x0e,0xfd,0xcd,0x5c,0x01,0x24,0x02,0xab,0x58,0x01,0xce,0x59,0x01,
+0xc6,0x0b,0x20,0xec,0x01,0xce,0xed,0x01,0xc6,0x2a,0x26,0x4a,0x0b,0x27,0x02,0xa0,
+0x70,0x01,0xc6,0xf2,0x01,0x5f,0x72,0x48,0xca,0xcc,0x03,0x27,0x72,0x01,0xc6,0x08,
+0x27,0x71,0x01,0xc6,0x88,0x88,0x88,0x88,0x81,0x84,0x84,0x84,0x84,0xa0,0x84,0xc7,
+0x01,0x7b,0xa1,0x84,0xc7,0x02,0x7b,0xa2,0x84,0xc7,0x03,0x7b,0xa3,0x84,0xc7,0x04,
+0x7b,0xd2,0xfd,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x5b,0xa2,0x1e,0x0e,0xfd,
+0xcd,0xbc,0x00,0xce,0xbd,0x00,0xc6,0x03,0x6b,0x80,0xa4,0x03,0x7b,0x04,0x6b,0x4f,
+0x1f,0x25,0x02,0xa1,0xbb,0x00,0xc6,0x26,0x26,0x4a,0x70,0x01,0xc6,0x03,0x6b,0x7f,
+0xa4,0x03,0x7b,0x01,0x6b,0xa0,0x84,0xc6,0x02,0x6b,0xa1,0x84,0xc6,0x03,0x6b,0xa2,
+0x84,0xc6,0x04,0x6b,0xa3,0x84,0xc6,0x5a,0x26,0x71,0x01,0xc6,0x5f,0x20,0x24,0x83,
+0x00,0x35,0x25,0x83,0x00,0x35,0x26,0x83,0x02,0x35,0x27,0x83,0x00,0x35,0xa0,0x84,
+0x00,0x35,0xa1,0x84,0x00,0x35,0xa2,0x84,0x00,0x35,0xa3,0x84,0x00,0x35,0xa4,0x84,
+0x00,0x35,0xa5,0x84,0x00,0x35,0xa6,0x84,0x00,0x35,0xa7,0x84,0x00,0x35,0xef,0xfc,
+0xcd,0x78,0xae,0xa4,0x00,0x84,0x35,0xa0,0x1d,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,
+0x41,0x01,0xab,0x5b,0x01,0x6b,0x78,0x84,0xc6,0x02,0x6b,0x79,0x84,0xc6,0x03,0x6b,
+0x7a,0x84,0xc6,0x04,0x6b,0x7b,0x84,0xc6,0xf2,0x01,0xc7,0x5f,0x26,0x71,0x01,0xce,
+0x64,0x26,0x72,0x01,0xc6,0x88,0x88,0x88,0x88,0x81,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,
+0xa9,0x41,0x01,0xab,0x81,0x84,0x84,0x84,0x84,0x00,0x40,0xc7,0x01,0x7b,0x01,0x40,
+0xc7,0x02,0x7b,0x02,0x40,0xc7,0x03,0x7b,0x03,0x40,0xc7,0x04,0x7b,0xef,0xfc,0xcd,
+0x5f,0xa4,0x00,0x40,0x35,0xa0,0x1e,0x23,0xad,0x5b,0xef,0xfc,0xcd,0x1c,0xae,0xa4,
+0x00,0x40,0x35,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,0xb7,0x13,0xb6,0x14,0x20,
+0x1c,0x40,0x00,0x35,0x1d,0x40,0x00,0x35,0x1e,0x40,0x00,0x35,0x1f,0x40,0x00,0x35,
+0x04,0x6b,0x02,0xaa,0x04,0x7b,0x18,0x26,0x12,0x3d,0x04,0x6b,0x40,0xaa,0x04,0x7b,
+0x06,0x26,0x5d,0x01,0xc6,0x04,0x6b,0x10,0xaa,0x04,0x7b,0x06,0x26,0x5c,0x01,0xc6,
+0x01,0x6b,0x02,0x6b,0x03,0x6b,0x4f,0x04,0x6b,0x01,0xa6,0xef,0xfc,0xcd,0xb4,0xae,
+0xa4,0x00,0x84,0x35,0xa3,0x18,0xa2,0x18,0xac,0xc8,0xcd,0x5b,0x01,0x6b,0xb4,0x84,
+0xc6,0x02,0x6b,0xb5,0x84,0xc6,0x03,0x6b,0xb6,0x84,0xc6,0x04,0x6b,0xb7,0x84,0xc6,
+0x05,0xc5,0xcd,0xfa,0xa6,0x14,0x45,0x00,0x35,0x15,0x45,0x00,0x35,0x16,0x45,0x00,
+0x35,0x17,0x45,0x02,0x35,0x88,0x88,0x88,0x88,0x81,0x01,0x40,0x00,0x35,0x02,0x40,
+0x00,0x35,0x03,0x40,0x00,0x35,0x81,0x84,0x84,0x84,0x84,0x72,0x01,0x5f,0x72,0xef,
+0xfc,0xcd,0xb4,0xae,0xa4,0x00,0x84,0x35,0xa2,0x19,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,
+0xa9,0x41,0x01,0xab,0x5b,0x01,0x6b,0xb4,0x84,0xc6,0x02,0x6b,0xb5,0x84,0xc6,0x03,
+0x6b,0xb6,0x84,0xc6,0x04,0x6b,0xb7,0x84,0xc6,0x14,0x45,0x00,0x35,0x15,0x45,0x00,
+0x35,0x16,0x45,0x00,0x35,0x17,0x45,0x01,0x35,0x00,0x40,0x00,0x35,0x47,0xad,0x00,
+0x40,0x80,0x35,0x4d,0xad,0x88,0x88,0x88,0x88,0x81,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,
+0xa9,0x41,0x01,0xab,0x81,0xac,0x84,0x00,0x35,0xad,0x84,0x00,0x35,0xae,0x84,0x00,
+0x35,0x81,0x00,0x88,0x00,0x35,0x01,0x88,0x00,0x35,0x02,0x88,0x00,0x35,0x81,0xa2,
+0x14,0xa3,0x14,0xa0,0x3f,0xa1,0x3f,0xa2,0xb7,0x01,0xa4,0xa2,0xb6,0xa3,0xb7,0xf8,
+0xa4,0xa3,0xb6,0x2e,0xfc,0xcd,0x08,0xae,0x90,0x5f,0x26,0xb6,0x81,0x84,0x84,0x84,
+0x84,0x84,0x84,0x25,0xad,0x03,0x88,0x01,0x35,0x08,0x88,0x00,0x35,0x09,0x88,0x30,
+0x35,0x0a,0x88,0x00,0x35,0x0b,0x88,0x00,0x35,0xef,0xfc,0xcd,0x0c,0xae,0xa4,0x00,
+0x88,0x35,0xa3,0x1e,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,0xb7,0x07,0xa4,0x05,
+0x7b,0x18,0x88,0x00,0x35,0x19,0x88,0x00,0x35,0x1a,0x88,0xff,0x35,0x1b,0x88,0xff,
+0x35,0x14,0x88,0x00,0x35,0x15,0x88,0x00,0x35,0x16,0x88,0xff,0x35,0x17,0x88,0xff,
+0x35,0x10,0x88,0x00,0x35,0x11,0x88,0x5f,0x35,0x12,0x88,0xff,0x35,0x13,0x88,0xff,
+0x35,0x72,0xc7,0xcd,0x03,0x88,0x00,0x35,0xef,0xfc,0xcd,0xb4,0xae,0xa4,0x00,0x84,
+0x35,0xa2,0x1e,0xa0,0x1e,0x8c,0xc7,0xcd,0x5b,0x11,0x26,0x21,0xa1,0x0a,0x20,0xa0,
+0xb7,0x60,0xaa,0xa0,0xb6,0x8c,0xc7,0xcd,0x5b,0x0c,0x25,0x22,0xa1,0xf3,0x01,0xc6,
+0x01,0x6b,0xb4,0x84,0xc6,0x02,0x6b,0xb5,0x84,0xc6,0x03,0x6b,0xb6,0x84,0xc6,0x04,
+0x6b,0xb7,0x84,0xc6,0x7f,0xc7,0xcd,0xaf,0x84,0x20,0x35,0xef,0xfc,0xcd,0x7c,0xae,
+0xa4,0x00,0x84,0x35,0xa0,0x14,0xa1,0x1a,0xa1,0x10,0x54,0xc7,0xcd,0x09,0x20,0xa1,
+0x1a,0xa1,0x10,0x54,0xc7,0xcd,0x12,0x20,0xa1,0x10,0x54,0xc7,0xcd,0x19,0x20,0x54,
+0xc7,0xcd,0x27,0x20,0x17,0x27,0x4a,0x11,0x27,0x4a,0x0d,0x27,0x4a,0x0b,0x27,0x4a,
+0x05,0x7b,0x7f,0xc7,0xcd,0xaf,0x84,0x00,0x35,0x7c,0x84,0x00,0x35,0x7d,0x84,0x00,
+0x35,0x7e,0x84,0x00,0x35,0x7f,0x84,0x00,0x35,0x72,0xc7,0xcd,0x03,0x88,0x00,0x35,
+0xb7,0xc5,0xcd,0x21,0x25,0x20,0xa1,0xf3,0x01,0xc6,0x88,0x88,0x88,0x88,0x89,0x88,
+0x81,0xac,0x84,0x00,0x35,0xad,0x84,0x00,0x35,0xae,0x84,0x00,0x35,0xaf,0x84,0x00,
+0x35,0x7c,0x84,0x00,0x35,0x7d,0x84,0x00,0x35,0x7e,0x84,0x00,0x35,0x7f,0x84,0x00,
+0x35,0x00,0x88,0x00,0x35,0x01,0x88,0x00,0x35,0x02,0x88,0x00,0x35,0x03,0x88,0x00,
+0x35,0xa8,0xad,0x02,0x25,0x20,0xa1,0xf3,0x01,0xc6,0x70,0x01,0x5f,0x72,0xe5,0x01,
+0x5f,0x72,0x81,0x84,0x84,0x84,0x84,0xb4,0x84,0xc7,0x01,0x7b,0xb5,0x84,0xc7,0x02,
+0x7b,0xb6,0x84,0xc7,0x03,0x7b,0xb7,0x84,0xc7,0x04,0x7b,0xef,0xfc,0xcd,0xb4,0xae,
+0xa4,0x00,0x84,0x35,0xa1,0x1c,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,
+0x5b,0x01,0x6b,0xb4,0x84,0xc6,0x02,0x6b,0xb5,0x84,0xc6,0x03,0x6b,0xb6,0x84,0xc6,
+0x04,0x6b,0xb7,0x84,0xc6,0x88,0x88,0x88,0x88,0x81,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,
+0xa9,0x41,0x03,0xab,0xef,0xfc,0xcc,0x5f,0xa4,0x00,0x84,0x35,0xa0,0x00,0x30,0x35,
+0x0c,0xad,0x5b,0xef,0xfc,0xcd,0x5f,0xa4,0x00,0x84,0x35,0xa0,0x1a,0xa0,0x3f,0x1b,
+0xad,0x5b,0x81,0xa0,0x3f,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x03,0xab,0xef,
+0xfc,0xcc,0x5f,0xa4,0x00,0x84,0x35,0xa0,0x18,0x0a,0xad,0x5b,0xef,0xfc,0xcd,0x5f,
+0xa4,0x00,0x84,0x35,0x15,0xad,0x5b,0x81,0x85,0x90,0x85,0x90,0x01,0xee,0x72,0x02,
+0x7b,0x05,0x20,0x05,0xee,0x72,0x06,0x7b,0x07,0x24,0x05,0xe2,0x72,0x9f,0x06,0xe0,
+0x72,0x89,0x88,0x81,0x85,0x90,0x85,0x90,0x05,0xee,0x72,0x06,0x7b,0x05,0x20,0x01,
+0xee,0x72,0x02,0x7b,0x07,0x24,0x05,0xe2,0x72,0x9f,0x06,0xe0,0x72,0x89,0x88,0x81,
+0x84,0x84,0xe9,0x26,0xa4,0xb6,0xed,0x26,0x5d,0x01,0x6b,0x00,0xa2,0xa4,0xb7,0x01,
+0x7b,0x02,0x6b,0x01,0xa0,0x97,0x02,0x7b,0x9d,0x01,0x20,0x01,0xef,0x72,0x02,0x6b,
+0x63,0xfc,0xcd,0x03,0xae,0x90,0xa7,0x3f,0x5f,0x88,0x88,0x81,0x41,0x20,0x40,0x35,
+0x48,0x20,0x5f,0x72,0x46,0x20,0x5f,0x72,0x45,0x20,0xc7,0x01,0xa6,0x02,0x20,0xd8,
+0xa6,0x04,0x24,0x20,0xa1,0xf3,0x01,0xc6,0x81,0x9a,0xd9,0x01,0xcf,0xda,0x01,0xc7,
+0x9b,0x81,0x44,0x42,0x00,0x35,0x45,0x42,0x00,0x35,0x46,0x42,0x00,0x35,0x47,0x42,
+0x00,0x35,0x00,0x42,0x00,0x35,0x01,0x42,0x00,0x35,0x02,0x42,0x01,0x35,0x03,0x42,
+0x01,0x35,0x10,0x20,0x00,0x42,0x08,0x35,0x01,0x42,0x00,0x35,0x02,0x42,0x01,0x35,
+0x03,0x42,0x00,0x35,0x12,0x26,0x4a,0xbf,0x00,0xc6,0x20,0x40,0x00,0x35,0x21,0x40,
+0x00,0x35,0x22,0x40,0x00,0x35,0x23,0x40,0x01,0x35,0x04,0x44,0xc3,0x00,0x55,0x05,
+0x44,0xc4,0x00,0x55,0x06,0x44,0xc5,0x00,0x55,0x07,0x44,0xc6,0x00,0x55,0x00,0x44,
+0xc7,0x00,0x55,0x01,0x44,0xc8,0x00,0x55,0x02,0x44,0xc9,0x00,0x55,0x03,0x44,0xca,
+0x00,0x55,0x81,0x85,0x4f,0xf3,0x25,0xbf,0x00,0xc1,0x01,0x6b,0x4c,0x01,0x7b,0x76,
+0xc2,0xcd,0x06,0x20,0x4f,0x88,0x81,0x9a,0xd2,0xfc,0xcd,0xa0,0x3f,0xa1,0x3f,0xa2,
+0x3f,0x81,0xa0,0xb7,0xa1,0xb7,0xa2,0xb7,0x4f,0xa3,0xb7,0x81,0x9a,0xd2,0xfc,0xcd,
+0xa4,0xb7,0x01,0xaa,0x41,0x81,0xa4,0xc6,0x92,0xa5,0xbf,0xa4,0xb7,0x81,0x1d,0xad,
+0xa3,0x3f,0xa4,0xb7,0x01,0xaa,0x33,0xc3,0xcc,0x4f,0x29,0xad,0xa3,0x00,0x3d,0x35,
+0x03,0xee,0x72,0xa4,0xb7,0x02,0x7b,0x9b,0x37,0xad,0xa3,0x00,0x08,0x35,0xa4,0xb7,
+0x02,0xaa,0x41,0x24,0xaa,0x02,0xee,0x72,0x03,0x7b,0x9b,0xd3,0x26,0x04,0xe1,0x72,
+0x33,0xad,0x02,0xaa,0x41,0x03,0xaa,0x02,0xee,0x72,0x03,0x7b,0x32,0xc3,0xcc,0x03,
+0x26,0x01,0x6b,0x4a,0x01,0x7b,0x9a,0xd2,0xfc,0xcd,0xa4,0xb7,0x02,0xaa,0x02,0x7b,
+0x03,0xee,0x72,0x68,0xad,0x04,0x7b,0x9b,0x1c,0x20,0x01,0x6b,0xff,0xa6,0x04,0x6b,
+0x80,0xaa,0x04,0x7b,0x06,0x26,0x02,0xa1,0x11,0xb6,0x04,0x6b,0x03,0xa6,0x02,0x20,
+0x01,0xa6,0x06,0x20,0x02,0xa6,0x04,0x27,0x15,0x3d,0x0c,0x27,0xc1,0x00,0xc6,0x79,
+0xad,0x41,0x1c,0xaa,0x02,0xee,0x72,0x03,0x7b,0x9b,0x2a,0xc4,0xcd,0x41,0x18,0xaa,
+0x02,0xee,0x72,0x03,0x7b,0x9b,0x2a,0xc4,0xcd,0x41,0x14,0xaa,0x02,0xee,0x72,0x03,
+0x7b,0x9b,0xd7,0x26,0x04,0xe1,0x72,0x33,0xc4,0xcd,0x01,0xaa,0x41,0x13,0xaa,0x02,
+0xee,0x72,0x03,0x7b,0xb6,0x27,0x01,0x6b,0x4a,0x01,0x7b,0x3b,0xc4,0xcd,0x10,0xaa,
+0x02,0xee,0x72,0x03,0x7b,0x45,0xc4,0xcd,0x04,0x7b,0x9b,0x17,0x20,0x01,0x6b,0xff,
+0xa6,0x04,0x6b,0xcf,0xa6,0x02,0x20,0x0f,0xa6,0x06,0x20,0x0c,0xa6,0x08,0x20,0x06,
+0x27,0x4a,0x05,0x27,0x14,0xb6,0xcc,0x26,0x04,0xe1,0x72,0x33,0xc4,0xcd,0x01,0xaa,
+0x41,0x0d,0xaa,0x02,0xee,0x72,0x03,0x7b,0x81,0x85,0x85,0x85,0x85,0x4c,0x06,0x26,
+0x01,0x6b,0x4a,0x01,0x7b,0x3b,0xc4,0xcd,0x0c,0xaa,0x02,0xee,0x72,0x03,0x7b,0x8c,
+0xfc,0xcd,0x10,0xa6,0x45,0xc4,0xcd,0x04,0x7b,0x9b,0x22,0x20,0x01,0x6b,0xff,0xa6,
+0x04,0x6b,0xfc,0xa6,0x02,0x20,0xf0,0xa6,0x06,0x20,0xcc,0xa6,0x0a,0x20,0xc0,0xa6,
+0x0e,0x20,0x0c,0xa6,0x12,0x20,0x0f,0xa6,0x16,0x20,0x0c,0xa6,0x18,0x27,0x4a,0x17,
+0x27,0x4a,0x16,0x27,0x4a,0x15,0x27,0x4a,0x14,0x27,0x4a,0x29,0x27,0x4a,0x16,0x27,
+0x13,0xb6,0xc9,0x26,0x04,0xe1,0x72,0x33,0xc4,0xcd,0x01,0xaa,0x41,0x0b,0xaa,0x02,
+0xee,0x72,0x03,0x7b,0x66,0x27,0x01,0x6b,0x4a,0x01,0x7b,0x3b,0xc4,0xcd,0x08,0xaa,
+0x02,0xee,0x72,0x03,0x7b,0xa0,0xb7,0x03,0xaa,0xa0,0xb6,0xa1,0xb7,0xc3,0xaa,0xa0,
+0xb7,0xa2,0xb7,0x4f,0xa3,0xb7,0x04,0x7b,0x9b,0x25,0x20,0x01,0x6b,0xff,0xa6,0x04,
+0x6b,0x4f,0x01,0x20,0x0c,0xa6,0x04,0x27,0x15,0x3d,0x2a,0xc4,0xcd,0x41,0x04,0xaa,
+0x02,0xee,0x72,0x03,0x7b,0x9b,0x2a,0xc4,0xcd,0x9b,0x02,0x6b,0x03,0xef,0x72,0x50,
+0xaa,0x41,0x4f,0x58,0x58,0x97,0x88,0x88,0x88,0x88,0x81,0x1e,0xfe,0xcd,0xc7,0xae,
+0xa4,0x00,0x00,0x35,0x81,0xa4,0xb7,0x00,0xa9,0x41,0x01,0xab,0x81,0xa0,0xb7,0xa1,
+0xb7,0xa2,0xb7,0x4f,0xa3,0xb7,0x81,0xa0,0xb7,0x03,0xa4,0xa0,0xb6,0xa1,0x3f,0xa2,
+0x3f,0xa3,0x3f,0x8c,0xfc,0xcd,0x18,0xa6,0x7a,0xfc,0xcd,0x4a,0x81,0xa4,0xb7,0x00,
+0xa9,0x41,0x07,0xab,0x81,0xc7,0x00,0x00,0x35,0xc8,0x00,0x00,0x35,0xc9,0x00,0x18,
+0x35,0x81,0x2c,0xad,0x18,0xa4,0x48,0x48,0x48,0x4a,0x12,0xb6,0xef,0xfc,0xcd,0x43,
+0xad,0x81,0x8b,0x41,0x00,0xa9,0x41,0x0a,0xab,0x5b,0xef,0xfc,0xcd,0x0c,0xae,0xa4,
+0x00,0x84,0x35,0xa3,0x36,0xa2,0x36,0xa1,0x36,0xa0,0x34,0x67,0xad,0x0a,0x20,0xa0,
+0x39,0xa1,0x39,0xa2,0x39,0xa3,0x38,0x73,0xad,0x0c,0x26,0x02,0xa1,0x11,0xb6,0x84,
+0x84,0x84,0x84,0x84,0x16,0xe0,0xcd,0x88,0x0b,0x7b,0x88,0x0b,0x7b,0x88,0x0b,0x7b,
+0x88,0x0b,0x7b,0x88,0x06,0x7b,0x06,0x6b,0x44,0x44,0x06,0x7b,0xb3,0xfd,0xcd,0x02,
+0xa6,0x6d,0xad,0x5b,0x0e,0x26,0x02,0xa1,0x11,0xb6,0x06,0x6b,0x08,0xa6,0x07,0x6b,
+0x4f,0x08,0x6b,0xbb,0xa6,0x09,0x6b,0x80,0xa6,0x0a,0x6b,0xc7,0x00,0x00,0x35,0xc8,
+0x00,0x00,0x35,0xc9,0x00,0x60,0x35,0xca,0x00,0x00,0x35,0x18,0x20,0xac,0xa6,0x09,
+0x6b,0x44,0xa6,0x0a,0x6b,0xc7,0x00,0x00,0x35,0xc8,0x00,0x00,0x35,0xc9,0x00,0x62,
+0x35,0xca,0x00,0x00,0x35,0x39,0x20,0x10,0xa6,0x07,0x6b,0x4f,0x08,0x6b,0x5d,0xa6,
+0x09,0x6b,0xc0,0xa6,0x0a,0x6b,0xc7,0x00,0x00,0x35,0xc8,0x00,0x00,0x35,0xc9,0x00,
+0x30,0x35,0xca,0x00,0x00,0x35,0x18,0x20,0x56,0xa6,0x09,0x6b,0x22,0xa6,0x0a,0x6b,
+0xc7,0x00,0x00,0x35,0xc8,0x00,0x00,0x35,0xc9,0x00,0x31,0x35,0xca,0x00,0x00,0x35,
+0x74,0x20,0x20,0xa6,0x07,0x6b,0x4f,0x08,0x6b,0x2e,0xa6,0x09,0x6b,0xe0,0xa6,0x0a,
+0x6b,0x2f,0xc2,0xcd,0xca,0x00,0x00,0x35,0xc4,0xc1,0xcc,0x20,0xa6,0x07,0x6b,0x4f,
+0x08,0x6b,0x2b,0xa6,0x09,0x6b,0x11,0xa6,0x0a,0x6b,0x2f,0xc2,0xcd,0xca,0x00,0x80,
+0x35,0xc4,0xc1,0xcc,0x20,0xa6,0x07,0x6b,0x4f,0x08,0x6b,0x1f,0xa6,0x09,0x6b,0x40,
+0xa6,0x0a,0x6b,0xc7,0x00,0x00,0x35,0xc8,0x00,0x00,0x35,0xc9,0x00,0x10,0x35,0xca,
+0x00,0x00,0x35,0xc4,0xc1,0xcc,0x20,0xa6,0x07,0x6b,0x4f,0x08,0x6b,0x2e,0xa6,0x09,
+0x6b,0xe0,0xa6,0x0a,0x6b,0x4f,0x2f,0xc2,0xcd,0xca,0x00,0x00,0x35,0xa5,0xc1,0xcc,
+0x03,0x26,0x4a,0x8b,0xc1,0xcc,0x03,0x26,0x4a,0x6a,0xc1,0xcc,0x03,0x26,0x4a,0x50,
+0xc1,0xcc,0x03,0x26,0x4a,0x6d,0x27,0x4a,0x57,0x27,0x4a,0x38,0x27,0x13,0xb6,0x9a,
+0xef,0xfc,0xcd,0xa4,0xbf,0x84,0xae,0xa3,0x1a,0x1e,0xfe,0xcd,0x3c,0xc2,0xcd,0x5b,
+0x9b,0xd2,0xfd,0xcd,0x3c,0xc2,0xcd,0x5b,0xd8,0xfd,0xcd,0x64,0xc2,0xcd,0x5b,0x1f,
+0xc2,0xcd,0x5b,0x44,0xc2,0xcd,0x5a,0x01,0x26,0x4d,0x5f,0x12,0xb6,0x15,0x20,0xa3,
+0x10,0xd8,0xfd,0xcd,0x64,0xc2,0xcd,0x5b,0x1f,0xc2,0xcd,0x5b,0x44,0xc2,0xcd,0x5a,
+0x01,0x26,0x4d,0x5f,0x12,0xb6,0x19,0x26,0x4a,0x11,0xb6,0x3a,0x20,0x08,0x6b,0x40,
+0xaa,0x08,0x7b,0x0a,0x6b,0x02,0xaa,0x0a,0x7b,0x0e,0x26,0x02,0xa1,0x11,0xb6,0x0a,
+0x6b,0x04,0xaa,0x0a,0x7b,0x06,0x27,0x16,0x3d,0xef,0xfc,0xcd,0x3c,0xc2,0xcd,0x5b,
+0xa2,0x1e,0xa0,0xbf,0xa1,0xb7,0x3e,0xa4,0xa1,0xb6,0xa2,0xbf,0xa3,0xbf,0x8c,0xfc,
+0xcd,0x11,0xa6,0x5a,0xc2,0xcd,0x14,0x20,0xa0,0x14,0xa0,0x3f,0xa1,0xb7,0x3e,0xa4,
+0xa1,0xb6,0xa2,0x3f,0xa3,0x3f,0x8c,0xfc,0xcd,0x11,0xa6,0x5a,0xc2,0xcd,0x18,0x27,
+0xe4,0x01,0xce,0x05,0x6b,0x18,0xa6,0x02,0x20,0x14,0xa6,0x06,0x20,0x10,0xa6,0x08,
+0x20,0x06,0x27,0x4a,0x05,0x27,0x14,0xb6,0x8b,0x41,0x00,0xa2,0x41,0x0a,0xa0,0x5b,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x81,0x4f,0x13,0xb7,0x53,0x20,0xc6,0x50,0x20,0x12,0x72,0x81,0x01,
+0xa6,0x03,0x27,0x4d,0x47,0x1b,0xcd,0x88,0xa6,0x12,0xb7,0x53,0x20,0xc6,0x50,0x20,
+0x15,0x72,0x81,0x01,0xa6,0x03,0x27,0x4d,0x47,0x1b,0xcd,0x88,0xa6,0x81,0x01,0xa6,
+0x03,0x27,0x4d,0xa4,0x1b,0xcd,0x41,0xa6,0x81,0x01,0xa6,0x03,0x27,0x4d,0x8f,0x1b,
+0xcd,0xaa,0x00,0x5f,0x72,0x50,0x20,0x20,0x35,0x52,0x20,0x09,0x35,0x81,0x4d,0x47,
+0x1b,0xcd,0x88,0xa6,0x50,0x20,0x20,0x35,0x53,0x20,0xc7,0xc5,0x20,0x4f,0x50,0x20,
+0x12,0x72,0xec,0x25,0x06,0xe1,0x72,0x01,0x6b,0x4c,0x01,0x7b,0xd4,0x26,0x13,0xad,
+0xbf,0x01,0xd6,0x01,0xee,0x72,0x0d,0x20,0x4f,0x17,0x27,0x60,0xa1,0x02,0x7b,0x05,
+0xc5,0xcd,0x05,0xa6,0xec,0x26,0x2b,0xad,0x03,0x7b,0xf2,0x26,0x4d,0x47,0x1b,0xcd,
+0x88,0xa6,0x81,0x85,0x85,0x85,0x01,0xa6,0x06,0x27,0x4d,0xa4,0x1b,0xcd,0x02,0x7b,
+0x08,0x26,0x4d,0x8f,0x1b,0xcd,0xaa,0x00,0x5f,0x72,0x50,0x20,0x20,0x35,0x52,0x20,
+0x09,0x35,0x88,0x89,0x88,0x81,0x4d,0x47,0x1b,0xcd,0x88,0xa6,0xe0,0x1b,0xcc,0x4f,
+0x05,0xc5,0xcd,0x05,0xa6,0x95,0x25,0x06,0xe1,0x72,0x01,0x7b,0xbf,0x01,0xd7,0x53,
+0x20,0xc6,0x01,0x6b,0x4c,0x9f,0x01,0xee,0x72,0x0d,0x20,0x11,0xe7,0x53,0x20,0xc6,
+0x97,0x07,0xeb,0x72,0x4a,0x01,0x6b,0x4c,0x01,0x7b,0x11,0x25,0x09,0xa1,0x06,0x7b,
+0x50,0x20,0x22,0x35,0x04,0x26,0xa8,0xb1,0xa8,0xbf,0x90,0x0b,0x26,0xa7,0xb3,0xa7,
+0x3f,0x01,0xe6,0x72,0x4a,0x5a,0x01,0x26,0x4d,0x5f,0x06,0x7b,0x50,0x20,0x15,0x72,
+0x04,0x26,0xa8,0xb1,0xa8,0xbf,0x90,0x0b,0x26,0xa7,0xb3,0xa7,0x3f,0x01,0xe6,0x72,
+0x5a,0x01,0x24,0x02,0xa0,0x5f,0x1a,0x25,0x02,0xa1,0x06,0x7b,0xba,0x26,0x72,0xad,
+0x50,0x20,0x15,0x72,0x04,0x26,0x4a,0x06,0x7b,0xc7,0x26,0x4d,0x90,0xad,0x01,0xaa,
+0x02,0x7b,0xd0,0x26,0x4d,0x84,0xad,0x05,0xc5,0xcd,0x05,0xa6,0xda,0x26,0x94,0x1c,
+0xcd,0x50,0x20,0x20,0x35,0x53,0x20,0xc7,0x03,0x7b,0x02,0x20,0x52,0x80,0xae,0x03,
+0x7b,0x07,0x26,0x80,0xa1,0x06,0x7b,0xf5,0x26,0x94,0x1c,0xcd,0x81,0x85,0x85,0x85,
+0x01,0xa6,0x06,0x27,0x4d,0xc9,0xad,0x02,0x7b,0x07,0x26,0x4d,0xbb,0xad,0xaa,0x00,
+0x5f,0x72,0x01,0x6b,0x4f,0x50,0x20,0x20,0x35,0x52,0x20,0x09,0x35,0x88,0x89,0x88,
+0x81,0x4f,0x50,0x20,0x24,0x35,0x05,0xc5,0xcd,0x05,0xa6,0x81,0x01,0xa6,0x03,0x27,
+0x4d,0xb7,0xad,0x82,0xa6,0x50,0x20,0x21,0x35,0x53,0x20,0xc7,0x81,0x4f,0x05,0xc5,
+0xcd,0x05,0xa6,0x81,0x01,0xa6,0x03,0x27,0x4d,0xcf,0xad,0x81,0xa6,0x50,0x20,0x29,
+0x35,0x81,0x85,0x4f,0xaa,0x00,0x5f,0x72,0x81,0x85,0x01,0xa6,0xaa,0x00,0x5f,0x72,
+0x50,0x20,0x22,0x35,0x0c,0xaa,0x00,0x0d,0x72,0x05,0x26,0x01,0xe1,0x72,0x01,0xe4,
+0x72,0xfa,0x27,0xaa,0x00,0xc6,0x8f,0x01,0x20,0x88,0x81,0x85,0x4f,0x81,0x85,0x01,
+0xa6,0x50,0x20,0x05,0x35,0x08,0x27,0x01,0xe1,0x72,0x01,0xe4,0x72,0x51,0x20,0xc6,
+0xfa,0x51,0x20,0x0f,0x72,0x9d,0x01,0x20,0x88,0xe0,0x20,0x8f,0xe3,0x26,0x01,0x7b,
+0xdb,0xf2,0xcd,0x03,0x26,0xd9,0x01,0xca,0xda,0x01,0xc6,0xcd,0xf1,0xcd,0x03,0x24,
+0x20,0xa1,0x12,0x27,0x01,0x6b,0x13,0x18,0xcd,0x64,0x18,0xcd,0x88,0x81,0x84,0x84,
+0x84,0x84,0x84,0x06,0x20,0x08,0x35,0x9a,0x1a,0x20,0x10,0x72,0x15,0x20,0xc7,0xee,
+0xa4,0x15,0x20,0xc6,0x14,0x20,0xc7,0x7c,0xa4,0x14,0x20,0xc6,0x13,0x20,0xc7,0x08,
+0xa4,0x13,0x20,0xc6,0xe8,0xc4,0xcd,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x65,
+0xde,0xcd,0x6c,0x01,0x3b,0x6d,0x01,0x3b,0x6e,0x01,0x3b,0x6f,0x01,0x3b,0x01,0x4b,
+0x80,0x4b,0x85,0x4b,0x80,0x4b,0xb0,0x84,0x00,0x35,0xb1,0x84,0x00,0x35,0xb2,0x84,
+0x00,0x35,0xb3,0x84,0x13,0x35,0xa4,0x84,0x00,0x35,0xa5,0x84,0x00,0x35,0xa6,0x84,
+0x00,0x35,0xa7,0x84,0x00,0x35,0xa0,0x84,0x00,0x35,0xa1,0x84,0x00,0x35,0xa2,0x84,
+0x00,0x35,0xa3,0x84,0x00,0x35,0x0c,0x8c,0x00,0x35,0x0d,0x8c,0x00,0x35,0x0e,0x8c,
+0x00,0x35,0x0f,0x8c,0x00,0x35,0x18,0x8c,0x00,0x35,0x19,0x8c,0x00,0x35,0x1a,0x8c,
+0x00,0x35,0x1b,0x8c,0x03,0x35,0x04,0x8c,0x00,0x35,0x05,0x8c,0x00,0x35,0x06,0x8c,
+0x00,0x35,0x07,0x8c,0x0a,0x35,0x00,0x8c,0x00,0x35,0x01,0x8c,0x00,0x35,0x02,0x8c,
+0x00,0x35,0x03,0x8c,0x00,0x35,0x1c,0x8c,0x00,0x35,0x1d,0x8c,0x00,0x35,0x1e,0x8c,
+0x00,0x35,0x1f,0x8c,0x04,0x35,0xfe,0xc5,0xcd,0x97,0xc7,0xcd,0x24,0x83,0x00,0x35,
+0x25,0x83,0x00,0x35,0x26,0x83,0x02,0x35,0x27,0x83,0x00,0x35,0x84,0x84,0x00,0x35,
+0x85,0x84,0x00,0x35,0x86,0x84,0x00,0x35,0x87,0x84,0x20,0x35,0xb4,0x84,0x00,0x35,
+0xb5,0x84,0x20,0x35,0xb6,0x84,0x00,0x35,0xb7,0x84,0xa0,0x35,0x0c,0x20,0xb5,0x84,
+0x00,0x35,0xb6,0x84,0x00,0x35,0xb7,0x84,0x80,0x35,0x0e,0x24,0x20,0xa1,0xf3,0x01,
+0xc6,0x18,0x84,0x00,0x35,0x19,0x84,0x00,0x35,0x1a,0x84,0x00,0x35,0x1b,0x84,0x00,
+0x35,0x78,0x84,0x00,0x35,0x79,0x84,0x00,0x35,0x7a,0x84,0x00,0x35,0x7b,0x84,0x00,
+0x35,0x3f,0x20,0x01,0x35,0x3d,0x20,0xc7,0xff,0xa6,0x3c,0x20,0xc7,0x04,0xa6,0x6c,
+0x01,0x01,0x35,0x6d,0x01,0x86,0x35,0x6e,0x01,0xa0,0x35,0x6f,0x01,0xc7,0x0f,0x20,
+0x6c,0x01,0x02,0x35,0x6d,0x01,0x49,0x35,0x6e,0x01,0xf0,0x35,0x6f,0x01,0xc7,0x1c,
+0x20,0x6d,0x01,0x9b,0x35,0x6e,0x01,0xfc,0x35,0x6f,0x01,0xc0,0x35,0x2a,0x20,0x6d,
+0x01,0x8c,0x35,0x6e,0x01,0xba,0x35,0x6f,0x01,0x80,0x35,0x38,0x20,0x6d,0x01,0x24,
+0x35,0x6e,0x01,0xf8,0x35,0x6f,0x01,0xc7,0x49,0x20,0x6c,0x01,0x5f,0x72,0x6d,0x01,
+0x5f,0x72,0x6e,0x01,0x5f,0x72,0x6f,0x01,0x5f,0x72,0x4c,0x27,0x08,0xa0,0x3f,0x27,
+0x4a,0x34,0x27,0x4a,0x29,0x27,0x4a,0x1f,0x27,0x0f,0xa4,0x08,0x20,0xc6,0xea,0x01,
+0x5f,0x72,0xeb,0x01,0xc7,0x01,0xa6,0x3e,0x20,0xfe,0x35,0xf3,0x01,0x10,0x35,0x0a,
+0x20,0x03,0xa6,0x3e,0x20,0x20,0x35,0xf3,0x01,0xc7,0x21,0xa6,0x11,0x3f,0x04,0x20,
+0x22,0xa6,0x11,0x00,0x01,0x35,0x08,0x27,0x9b,0xfc,0xcd,0xa0,0x3f,0xa1,0x3f,0xa2,
+0xb7,0x02,0xa4,0xa2,0xb6,0xa3,0x3f,0x1e,0xfe,0xcd,0xa4,0xb7,0x00,0xa9,0x41,0x01,
+0xab,0x5b,0x01,0x6b,0x94,0x84,0xc6,0x02,0x6b,0x95,0x84,0xc6,0x03,0x6b,0x96,0x84,
+0xc6,0x04,0x6b,0x97,0x84,0xc6,0x47,0x25,0x10,0xa1,0x03,0x20,0xc6,0xf2,0x25,0x10,
+0xa1,0x05,0x6b,0x4c,0xc9,0x01,0x4f,0x72,0x05,0xee,0x72,0x05,0x6b,0x4f,0xd2,0x00,
+0x5f,0x72,0xd3,0x00,0x5f,0x72,0xe4,0x01,0x5f,0x72,0xbb,0x00,0x5f,0x72,0xbd,0x00,
+0x5f,0x72,0xbc,0x00,0x5f,0x72,0xda,0x01,0x5f,0x72,0xd9,0x01,0x5f,0x72,0x9b,0x01,
+0x0a,0x35,0x9e,0x01,0x5f,0x72,0x9f,0x01,0x5f,0x72,0xa9,0x01,0x5f,0x72,0xa0,0x01,
+0x5f,0x72,0xa1,0x01,0x5f,0x72,0xa2,0x01,0x5f,0x72,0x86,0x01,0x5f,0x72,0xe9,0x01,
+0x5f,0x72,0xee,0x01,0x5f,0x72,0x71,0x01,0x5f,0x72,0x72,0x01,0x5f,0x72,0xaa,0x00,
+0x5f,0x72,0x74,0x01,0x5f,0x72,0x87,0x01,0x5f,0x72,0x75,0x01,0x5f,0x72,0xbf,0x00,
+0x5f,0x72,0xc0,0x00,0x5f,0x72,0xc1,0x00,0x5f,0x72,0xc2,0x00,0x5f,0x72,0x70,0x01,
+0x5f,0x72,0x11,0x3f,0x10,0x3f,0x73,0x01,0x5f,0x72,0xc7,0x01,0x5f,0x72,0xc8,0x01,
+0x5f,0x72,0xef,0x01,0x5f,0x72,0xf0,0x01,0xff,0x35,0xf2,0x01,0x5f,0x72,0xe8,0x01,
+0x5f,0x72,0x9b,0x06,0x20,0x5f,0x72,0x88,0x88,0x88,0x88,0x88,0x81,0x84,0xe3,0xad,
+0xf4,0x25,0x40,0xa1,0x01,0x6b,0x4c,0x12,0x6f,0x01,0xee,0x72,0x01,0x6b,0x4f,0x88,
+0x81,0x05,0x20,0x10,0x72,0x15,0x20,0x11,0x72,0x10,0x1e,0x23,0x1b,0xcc,0x8b,0xff,
+0xa6,0x04,0xae,0x81,0x85,0x01,0x7b,0x9a,0xc7,0x01,0xc7,0x0f,0xa4,0xc7,0x01,0xc6,
+0xc9,0x01,0x4f,0x72,0xc7,0x01,0x5c,0x72,0x01,0x6b,0xc9,0x01,0xd6,0xc7,0x01,0xce,
+0x18,0x27,0xc7,0x01,0xc1,0xc8,0x01,0xc6,0x9b,0x01,0x6b,0x4f,0x88,0x81,0xc8,0x01,
+0xc7,0x0f,0xa4,0xc8,0x01,0xc6,0xc9,0x01,0xd7,0xc8,0x01,0x5c,0x72,0xc8,0x01,0xce,
+};
diff --git a/drivers/video/av8100/av8100_regs.h b/drivers/video/av8100/av8100_regs.h
new file mode 100644
index 00000000000..6ed9000987a
--- /dev/null
+++ b/drivers/video/av8100/av8100_regs.h
@@ -0,0 +1,346 @@
+
+#define AV8100_VAL2REG(__reg, __fld, __val) \
+ (((__val) << __reg##_##__fld##_SHIFT) & __reg##_##__fld##_MASK)
+#define AV8100_REG2VAL(__reg, __fld, __val) \
+ (((__val) & __reg##_##__fld##_MASK) >> __reg##_##__fld##_SHIFT)
+
+#define AV8100_STANDBY 0x00000000
+#define AV8100_STANDBY_CPD_SHIFT 0
+#define AV8100_STANDBY_CPD_MASK 0x00000001
+#define AV8100_STANDBY_CPD_HIGH 1
+#define AV8100_STANDBY_CPD_LOW 0
+#define AV8100_STANDBY_CPD(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY, CPD, __x)
+#define AV8100_STANDBY_CPD_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY, CPD, __x)
+#define AV8100_STANDBY_STBY_SHIFT 1
+#define AV8100_STANDBY_STBY_MASK 0x00000002
+#define AV8100_STANDBY_STBY_HIGH 1
+#define AV8100_STANDBY_STBY_LOW 0
+#define AV8100_STANDBY_STBY(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY, STBY, __x)
+#define AV8100_STANDBY_STBY_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY, STBY, __x)
+#define AV8100_STANDBY_HPDS_SHIFT 2
+#define AV8100_STANDBY_HPDS_MASK 0x00000004
+#define AV8100_STANDBY_HPDS(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY, HPDS, __x)
+#define AV8100_STANDBY_HPDS_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY, HPDS, __x)
+#define AV8100_STANDBY_CPDS_SHIFT 3
+#define AV8100_STANDBY_CPDS_MASK 0x00000008
+#define AV8100_STANDBY_CPDS(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY, CPDS, __x)
+#define AV8100_STANDBY_CPDS_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY, CPDS, __x)
+#define AV8100_STANDBY_MCLKRNG_SHIFT 4
+#define AV8100_STANDBY_MCLKRNG_MASK 0x000000F0
+#define AV8100_STANDBY_MCLKRNG(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY, MCLKRNG, __x)
+#define AV8100_STANDBY_MCLKRNG_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY, MCLKRNG, __x)
+#define AV8100_HDMI_5_VOLT_TIME 0x00000001
+#define AV8100_HDMI_5_VOLT_TIME_OFF_TIME_SHIFT 0
+#define AV8100_HDMI_5_VOLT_TIME_OFF_TIME_MASK 0x0000001F
+#define AV8100_HDMI_5_VOLT_TIME_OFF_TIME(__x) \
+ AV8100_VAL2REG(AV8100_HDMI_5_VOLT_TIME, OFF_TIME, __x)
+#define AV8100_HDMI_5_VOLT_TIME_OFF_TIME_GET(__x) \
+ AV8100_REG2VAL(AV8100_HDMI_5_VOLT_TIME, OFF_TIME, __x)
+#define AV8100_HDMI_5_VOLT_TIME_DAC_OFF_TIME_SHIFT 0
+#define AV8100_HDMI_5_VOLT_TIME_DAC_OFF_TIME_MASK 0x00000003
+#define AV8100_HDMI_5_VOLT_TIME_DAC_OFF_TIME(__x) \
+ AV8100_VAL2REG(AV8100_HDMI_5_VOLT_TIME, DAC_OFF_TIME, __x)
+#define AV8100_HDMI_5_VOLT_TIME_DAC_OFF_TIME_GET(__x) \
+ AV8100_REG2VAL(AV8100_HDMI_5_VOLT_TIME, DAC_OFF_TIME, __x)
+#define AV8100_HDMI_5_VOLT_TIME_SU_OFF_TIME_SHIFT 2
+#define AV8100_HDMI_5_VOLT_TIME_SU_OFF_TIME_MASK 0x0000001C
+#define AV8100_HDMI_5_VOLT_TIME_SU_OFF_TIME(__x) \
+ AV8100_VAL2REG(AV8100_HDMI_5_VOLT_TIME, SU_OFF_TIME, __x)
+#define AV8100_HDMI_5_VOLT_TIME_SU_OFF_TIME_GET(__x) \
+ AV8100_REG2VAL(AV8100_HDMI_5_VOLT_TIME, SU_OFF_TIME, __x)
+#define AV8100_HDMI_5_VOLT_TIME_ON_TIME_SHIFT 5
+#define AV8100_HDMI_5_VOLT_TIME_ON_TIME_MASK 0x000000E0
+#define AV8100_HDMI_5_VOLT_TIME_ON_TIME(__x) \
+ AV8100_VAL2REG(AV8100_HDMI_5_VOLT_TIME, ON_TIME, __x)
+#define AV8100_HDMI_5_VOLT_TIME_ON_TIME_GET(__x) \
+ AV8100_REG2VAL(AV8100_HDMI_5_VOLT_TIME, ON_TIME, __x)
+#define AV8100_STANDBY_INTERRUPT_MASK 0x00000002
+#define AV8100_STANDBY_INTERRUPT_MASK_HPDM_SHIFT 0
+#define AV8100_STANDBY_INTERRUPT_MASK_HPDM_MASK 0x00000001
+#define AV8100_STANDBY_INTERRUPT_MASK_HPDM_HIGH 1
+#define AV8100_STANDBY_INTERRUPT_MASK_HPDM_LOW 0
+#define AV8100_STANDBY_INTERRUPT_MASK_HPDM(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY_INTERRUPT_MASK, HPDM, __x)
+#define AV8100_STANDBY_INTERRUPT_MASK_HPDM_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY_INTERRUPT_MASK, HPDM, __x)
+#define AV8100_STANDBY_INTERRUPT_MASK_CPDM_SHIFT 1
+#define AV8100_STANDBY_INTERRUPT_MASK_CPDM_MASK 0x00000002
+#define AV8100_STANDBY_INTERRUPT_MASK_CPDM_HIGH 1
+#define AV8100_STANDBY_INTERRUPT_MASK_CPDM_LOW 0
+#define AV8100_STANDBY_INTERRUPT_MASK_CPDM(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY_INTERRUPT_MASK, CPDM, __x)
+#define AV8100_STANDBY_INTERRUPT_MASK_CPDM_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY_INTERRUPT_MASK, CPDM, __x)
+#define AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_SHIFT 2
+#define AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_MASK 0x0000000C
+#define AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_INPUT 0x00
+#define AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_ALT 0x01
+#define AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_OUTPUT0 0x02
+#define AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_OUTPUT1 0x03
+#define AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY_INTERRUPT_MASK, STBYGPIOCFG, __x)
+#define AV8100_STANDBY_INTERRUPT_MASK_STBYGPIOCFG_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY_INTERRUPT_MASK, STBYGPIOCFG, __x)
+#define AV8100_STANDBY_INTERRUPT_MASK_IPOL_SHIFT 7
+#define AV8100_STANDBY_INTERRUPT_MASK_IPOL_MASK 0x00000080
+#define AV8100_STANDBY_INTERRUPT_MASK_IPOL_HIGH 1
+#define AV8100_STANDBY_INTERRUPT_MASK_IPOL_LOW 0
+#define AV8100_STANDBY_INTERRUPT_MASK_IPOL(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY_INTERRUPT_MASK, IPOL, __x)
+#define AV8100_STANDBY_INTERRUPT_MASK_IPOL_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY_INTERRUPT_MASK, IPOL, __x)
+#define AV8100_STANDBY_PENDING_INTERRUPT 0x00000003
+#define AV8100_STANDBY_PENDING_INTERRUPT_HPDI_SHIFT 0
+#define AV8100_STANDBY_PENDING_INTERRUPT_HPDI_MASK 0x00000001
+#define AV8100_STANDBY_PENDING_INTERRUPT_HPDI_HIGH 1
+#define AV8100_STANDBY_PENDING_INTERRUPT_HPDI_LOW 0
+#define AV8100_STANDBY_PENDING_INTERRUPT_HPDI(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY_PENDING_INTERRUPT, HPDI, __x)
+#define AV8100_STANDBY_PENDING_INTERRUPT_HPDI_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY_PENDING_INTERRUPT, HPDI, __x)
+#define AV8100_STANDBY_PENDING_INTERRUPT_CPDI_SHIFT 1
+#define AV8100_STANDBY_PENDING_INTERRUPT_CPDI_MASK 0x00000002
+#define AV8100_STANDBY_PENDING_INTERRUPT_CPDI_HIGH 1
+#define AV8100_STANDBY_PENDING_INTERRUPT_CPDI_LOW 0
+#define AV8100_STANDBY_PENDING_INTERRUPT_CPDI(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY_PENDING_INTERRUPT, CPDI, __x)
+#define AV8100_STANDBY_PENDING_INTERRUPT_CPDI_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY_PENDING_INTERRUPT, CPDI, __x)
+#define AV8100_STANDBY_PENDING_INTERRUPT_ONI_SHIFT 2
+#define AV8100_STANDBY_PENDING_INTERRUPT_ONI_MASK 0x00000004
+#define AV8100_STANDBY_PENDING_INTERRUPT_ONI_HIGH 1
+#define AV8100_STANDBY_PENDING_INTERRUPT_ONI_LOW 0
+#define AV8100_STANDBY_PENDING_INTERRUPT_ONI(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY_PENDING_INTERRUPT, ONI, __x)
+#define AV8100_STANDBY_PENDING_INTERRUPT_ONI_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY_PENDING_INTERRUPT, ONI, __x)
+#define AV8100_STANDBY_PENDING_INTERRUPT_SID_SHIFT 4
+#define AV8100_STANDBY_PENDING_INTERRUPT_SID_MASK 0x000000F0
+#define AV8100_STANDBY_PENDING_INTERRUPT_SID(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY_PENDING_INTERRUPT, SID, __x)
+#define AV8100_STANDBY_PENDING_INTERRUPT_SID_GET(__x) \
+ AV8100_REG2VAL(AV8100_STANDBY_PENDING_INTERRUPT, SID, __x)
+#define AV8100_STANDBY_PENDING_INTERRUPT_BPDIG_SHIFT 6
+#define AV8100_STANDBY_PENDING_INTERRUPT_BPDIG_MASK 0x00000040
+#define AV8100_STANDBY_PENDING_INTERRUPT_BPDIG_HIGH 1
+#define AV8100_STANDBY_PENDING_INTERRUPT_BPDIG_LOW 0
+#define AV8100_STANDBY_PENDING_INTERRUPT_BPDIG(__x) \
+ AV8100_VAL2REG(AV8100_STANDBY_PENDING_INTERRUPT, BPDIG, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK 0x00000004
+#define AV8100_GENERAL_INTERRUPT_MASK_EOCM_SHIFT 0
+#define AV8100_GENERAL_INTERRUPT_MASK_EOCM_MASK 0x00000001
+#define AV8100_GENERAL_INTERRUPT_MASK_EOCM_HIGH 1
+#define AV8100_GENERAL_INTERRUPT_MASK_EOCM_LOW 0
+#define AV8100_GENERAL_INTERRUPT_MASK_EOCM(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT_MASK, EOCM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_EOCM_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT_MASK, EOCM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_VSIM_SHIFT 1
+#define AV8100_GENERAL_INTERRUPT_MASK_VSIM_MASK 0x00000002
+#define AV8100_GENERAL_INTERRUPT_MASK_VSIM_HIGH 1
+#define AV8100_GENERAL_INTERRUPT_MASK_VSIM_LOW 0
+#define AV8100_GENERAL_INTERRUPT_MASK_VSIM(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT_MASK, VSIM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_VSIM_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT_MASK, VSIM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_VSOM_SHIFT 2
+#define AV8100_GENERAL_INTERRUPT_MASK_VSOM_MASK 0x00000004
+#define AV8100_GENERAL_INTERRUPT_MASK_VSOM_HIGH 1
+#define AV8100_GENERAL_INTERRUPT_MASK_VSOM_LOW 0
+#define AV8100_GENERAL_INTERRUPT_MASK_VSOM(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT_MASK, VSOM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_VSOM_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT_MASK, VSOM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_CECM_SHIFT 3
+#define AV8100_GENERAL_INTERRUPT_MASK_CECM_MASK 0x00000008
+#define AV8100_GENERAL_INTERRUPT_MASK_CECM_HIGH 1
+#define AV8100_GENERAL_INTERRUPT_MASK_CECM_LOW 0
+#define AV8100_GENERAL_INTERRUPT_MASK_CECM(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT_MASK, CECM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_CECM_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT_MASK, CECM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_HDCPM_SHIFT 4
+#define AV8100_GENERAL_INTERRUPT_MASK_HDCPM_MASK 0x00000010
+#define AV8100_GENERAL_INTERRUPT_MASK_HDCPM_HIGH 1
+#define AV8100_GENERAL_INTERRUPT_MASK_HDCPM_LOW 0
+#define AV8100_GENERAL_INTERRUPT_MASK_HDCPM(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT_MASK, HDCPM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_HDCPM_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT_MASK, HDCPM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_UOVBM_SHIFT 5
+#define AV8100_GENERAL_INTERRUPT_MASK_UOVBM_MASK 0x00000020
+#define AV8100_GENERAL_INTERRUPT_MASK_UOVBM_HIGH 1
+#define AV8100_GENERAL_INTERRUPT_MASK_UOVBM_LOW 0
+#define AV8100_GENERAL_INTERRUPT_MASK_UOVBM(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT_MASK, UOVBM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_UOVBM_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT_MASK, UOVBM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_TEM_SHIFT 6
+#define AV8100_GENERAL_INTERRUPT_MASK_TEM_MASK 0x00000040
+#define AV8100_GENERAL_INTERRUPT_MASK_TEM_HIGH 1
+#define AV8100_GENERAL_INTERRUPT_MASK_TEM_LOW 0
+#define AV8100_GENERAL_INTERRUPT_MASK_TEM(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT_MASK, TEM, __x)
+#define AV8100_GENERAL_INTERRUPT_MASK_TEM_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT_MASK, TEM, __x)
+#define AV8100_GENERAL_INTERRUPT 0x00000005
+#define AV8100_GENERAL_INTERRUPT_EOCI_SHIFT 0
+#define AV8100_GENERAL_INTERRUPT_EOCI_MASK 0x00000001
+#define AV8100_GENERAL_INTERRUPT_EOCI(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT, EOCI, __x)
+#define AV8100_GENERAL_INTERRUPT_EOCI_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT, EOCI, __x)
+#define AV8100_GENERAL_INTERRUPT_VSII_SHIFT 1
+#define AV8100_GENERAL_INTERRUPT_VSII_MASK 0x00000002
+#define AV8100_GENERAL_INTERRUPT_VSII(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT, VSII, __x)
+#define AV8100_GENERAL_INTERRUPT_VSII_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT, VSII, __x)
+#define AV8100_GENERAL_INTERRUPT_VSOI_SHIFT 2
+#define AV8100_GENERAL_INTERRUPT_VSOI_MASK 0x00000004
+#define AV8100_GENERAL_INTERRUPT_VSOI(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT, VSOI, __x)
+#define AV8100_GENERAL_INTERRUPT_VSOI_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT, VSOI, __x)
+#define AV8100_GENERAL_INTERRUPT_CECI_SHIFT 3
+#define AV8100_GENERAL_INTERRUPT_CECI_MASK 0x00000008
+#define AV8100_GENERAL_INTERRUPT_CECI(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT, CECI, __x)
+#define AV8100_GENERAL_INTERRUPT_CECI_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT, CECI, __x)
+#define AV8100_GENERAL_INTERRUPT_HDCPI_SHIFT 4
+#define AV8100_GENERAL_INTERRUPT_HDCPI_MASK 0x00000010
+#define AV8100_GENERAL_INTERRUPT_HDCPI(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT, HDCPI, __x)
+#define AV8100_GENERAL_INTERRUPT_HDCPI_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT, HDCPI, __x)
+#define AV8100_GENERAL_INTERRUPT_UOVBI_SHIFT 5
+#define AV8100_GENERAL_INTERRUPT_UOVBI_MASK 0x00000020
+#define AV8100_GENERAL_INTERRUPT_UOVBI(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT, UOVBI, __x)
+#define AV8100_GENERAL_INTERRUPT_UOVBI_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT, UOVBI, __x)
+#define AV8100_GENERAL_INTERRUPT_TEI_SHIFT 6
+#define AV8100_GENERAL_INTERRUPT_TEI_MASK 0x00000040
+#define AV8100_GENERAL_INTERRUPT_TEI(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_INTERRUPT, TEI, __x)
+#define AV8100_GENERAL_INTERRUPT_TEI_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_INTERRUPT, TEI, __x)
+#define AV8100_GENERAL_STATUS 0x00000006
+#define AV8100_GENERAL_STATUS_CECTXERR_SHIFT 0
+#define AV8100_GENERAL_STATUS_CECTXERR_MASK 0x00000001
+#define AV8100_GENERAL_STATUS_CECTXERR_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_STATUS, CECTXERR, __x)
+#define AV8100_GENERAL_STATUS_CECREC_SHIFT 1
+#define AV8100_GENERAL_STATUS_CECREC_MASK 0x00000002
+#define AV8100_GENERAL_STATUS_CECREC_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_STATUS, CECREC, __x)
+#define AV8100_GENERAL_STATUS_CECTRX_SHIFT 2
+#define AV8100_GENERAL_STATUS_CECTRX_MASK 0x00000004
+#define AV8100_GENERAL_STATUS_CECTRX_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_STATUS, CECTRX, __x)
+#define AV8100_GENERAL_STATUS_UC_SHIFT 3
+#define AV8100_GENERAL_STATUS_UC_MASK 0x00000008
+#define AV8100_GENERAL_STATUS_UC_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_STATUS, UC, __x)
+#define AV8100_GENERAL_STATUS_ONUVB_SHIFT 4
+#define AV8100_GENERAL_STATUS_ONUVB_MASK 0x00000010
+#define AV8100_GENERAL_STATUS_ONUVB_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_STATUS, ONUVB, __x)
+#define AV8100_GENERAL_STATUS_HDCPS_SHIFT 5
+#define AV8100_GENERAL_STATUS_HDCPS_MASK 0x000000E0
+#define AV8100_GENERAL_STATUS_HDCPS_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_STATUS, HDCPS, __x)
+#define AV8100_GPIO_CONFIGURATION 0x00000007
+#define AV8100_GPIO_CONFIGURATION_DAT3DIR_SHIFT 0
+#define AV8100_GPIO_CONFIGURATION_DAT3DIR_MASK 0x00000001
+#define AV8100_GPIO_CONFIGURATION_DAT3DIR(__x) \
+ AV8100_VAL2REG(AV8100_GPIO_CONFIGURATION, DAT3DIR, __x)
+#define AV8100_GPIO_CONFIGURATION_DAT3DIR_GET(__x) \
+ AV8100_REG2VAL(AV8100_GPIO_CONFIGURATION, DAT3DIR, __x)
+#define AV8100_GPIO_CONFIGURATION_DAT3VAL_SHIFT 1
+#define AV8100_GPIO_CONFIGURATION_DAT3VAL_MASK 0x00000002
+#define AV8100_GPIO_CONFIGURATION_DAT3VAL(__x) \
+ AV8100_VAL2REG(AV8100_GPIO_CONFIGURATION, DAT3VAL, __x)
+#define AV8100_GPIO_CONFIGURATION_DAT3VAL_GET(__x) \
+ AV8100_REG2VAL(AV8100_GPIO_CONFIGURATION, DAT3VAL, __x)
+#define AV8100_GPIO_CONFIGURATION_DAT2DIR_SHIFT 2
+#define AV8100_GPIO_CONFIGURATION_DAT2DIR_MASK 0x00000004
+#define AV8100_GPIO_CONFIGURATION_DAT2DIR(__x) \
+ AV8100_VAL2REG(AV8100_GPIO_CONFIGURATION, DAT2DIR, __x)
+#define AV8100_GPIO_CONFIGURATION_DAT2DIR_GET(__x) \
+ AV8100_REG2VAL(AV8100_GPIO_CONFIGURATION, DAT2DIR, __x)
+#define AV8100_GPIO_CONFIGURATION_DAT2VAL_SHIFT 3
+#define AV8100_GPIO_CONFIGURATION_DAT2VAL_MASK 0x00000008
+#define AV8100_GPIO_CONFIGURATION_DAT2VAL(__x) \
+ AV8100_VAL2REG(AV8100_GPIO_CONFIGURATION, DAT2VAL, __x)
+#define AV8100_GPIO_CONFIGURATION_DAT2VAL_GET(__x) \
+ AV8100_REG2VAL(AV8100_GPIO_CONFIGURATION, DAT2VAL, __x)
+#define AV8100_GPIO_CONFIGURATION_DAT1DIR_SHIFT 4
+#define AV8100_GPIO_CONFIGURATION_DAT1DIR_MASK 0x00000010
+#define AV8100_GPIO_CONFIGURATION_DAT1DIR(__x) \
+ AV8100_VAL2REG(AV8100_GPIO_CONFIGURATION, DAT1DIR, __x)
+#define AV8100_GPIO_CONFIGURATION_DAT1DIR_GET(__x) \
+ AV8100_REG2VAL(AV8100_GPIO_CONFIGURATION, DAT1DIR, __x)
+#define AV8100_GPIO_CONFIGURATION_DAT1VAL_SHIFT 5
+#define AV8100_GPIO_CONFIGURATION_DAT1VAL_MASK 0x00000020
+#define AV8100_GPIO_CONFIGURATION_DAT1VAL(__x) \
+ AV8100_VAL2REG(AV8100_GPIO_CONFIGURATION, DAT1VAL, __x)
+#define AV8100_GPIO_CONFIGURATION_DAT1VAL_GET(__x) \
+ AV8100_REG2VAL(AV8100_GPIO_CONFIGURATION, DAT1VAL, __x)
+#define AV8100_GPIO_CONFIGURATION_UCDBG_SHIFT 6
+#define AV8100_GPIO_CONFIGURATION_UCDBG_MASK 0x00000040
+#define AV8100_GPIO_CONFIGURATION_UCDBG(__x) \
+ AV8100_VAL2REG(AV8100_GPIO_CONFIGURATION, UCDBG, __x)
+#define AV8100_GPIO_CONFIGURATION_UCDBG_GET(__x) \
+ AV8100_REG2VAL(AV8100_GPIO_CONFIGURATION, UCDBG, __x)
+#define AV8100_GENERAL_CONTROL 0x00000008
+#define AV8100_GENERAL_CONTROL_FDL_SHIFT 4
+#define AV8100_GENERAL_CONTROL_FDL_MASK 0x00000010
+#define AV8100_GENERAL_CONTROL_FDL_HIGH 1
+#define AV8100_GENERAL_CONTROL_FDL_LOW 0
+#define AV8100_GENERAL_CONTROL_FDL(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_CONTROL, FDL, __x)
+#define AV8100_GENERAL_CONTROL_FDL_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_CONTROL, FDL, __x)
+#define AV8100_GENERAL_CONTROL_HLD_SHIFT 5
+#define AV8100_GENERAL_CONTROL_HLD_MASK 0x00000020
+#define AV8100_GENERAL_CONTROL_HLD_HIGH 1
+#define AV8100_GENERAL_CONTROL_HLD_LOW 0
+#define AV8100_GENERAL_CONTROL_HLD(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_CONTROL, HLD, __x)
+#define AV8100_GENERAL_CONTROL_HLD_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_CONTROL, HLD, __x)
+#define AV8100_GENERAL_CONTROL_WA_SHIFT 6
+#define AV8100_GENERAL_CONTROL_WA_MASK 0x00000040
+#define AV8100_GENERAL_CONTROL_WA_HIGH 1
+#define AV8100_GENERAL_CONTROL_WA_LOW 0
+#define AV8100_GENERAL_CONTROL_WA(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_CONTROL, WA, __x)
+#define AV8100_GENERAL_CONTROL_WA_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_CONTROL, WA, __x)
+#define AV8100_GENERAL_CONTROL_RA_SHIFT 7
+#define AV8100_GENERAL_CONTROL_RA_MASK 0x00000080
+#define AV8100_GENERAL_CONTROL_RA_HIGH 1
+#define AV8100_GENERAL_CONTROL_RA_LOW 0
+#define AV8100_GENERAL_CONTROL_RA(__x) \
+ AV8100_VAL2REG(AV8100_GENERAL_CONTROL, RA, __x)
+#define AV8100_GENERAL_CONTROL_RA_GET(__x) \
+ AV8100_REG2VAL(AV8100_GENERAL_CONTROL, RA, __x)
+#define AV8100_FIRMWARE_DOWNLOAD_ENTRY 0x0000000F
+#define AV8100_FIRMWARE_DOWNLOAD_ENTRY_MBYTE_CODE_ENTRY_SHIFT 0
+#define AV8100_FIRMWARE_DOWNLOAD_ENTRY_MBYTE_CODE_ENTRY_MASK 0x000000FF
+#define AV8100_FIRMWARE_DOWNLOAD_ENTRY_MBYTE_CODE_ENTRY(__x) \
+ AV8100_VAL2REG(AV8100_FIRMWARE_DOWNLOAD_ENTRY, MBYTE_CODE_ENTRY, __x)
+#define AV8100_FIRMWARE_DOWNLOAD_ENTRY_MBYTE_CODE_ENTRY_GET(__x) \
+ AV8100_REG2VAL(AV8100_FIRMWARE_DOWNLOAD_ENTRY, MBYTE_CODE_ENTRY, __x)
diff --git a/drivers/video/av8100/hdmi.c b/drivers/video/av8100/hdmi.c
new file mode 100644
index 00000000000..be267188018
--- /dev/null
+++ b/drivers/video/av8100/hdmi.c
@@ -0,0 +1,2301 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * ST-Ericsson HDMI driver
+ *
+ * Author: Per Persson <per.xb.persson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/uaccess.h>
+#include <video/av8100.h>
+#include <video/hdmi.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+#include <linux/ctype.h>
+#include "hdmi_loc.h"
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#define SYSFS_EVENT_FILENAME "evread"
+
+DEFINE_MUTEX(hdmi_events_mutex);
+#define LOCK_HDMI_EVENTS mutex_lock(&hdmi_events_mutex)
+#define UNLOCK_HDMI_EVENTS mutex_unlock(&hdmi_events_mutex)
+#define EVENTS_MASK 0xFF
+
+static int device_open;
+static int events;
+static int events_mask;
+static bool events_received;
+static wait_queue_head_t hdmi_event_wq;
+struct device *hdmidev;
+
+static ssize_t store_storeastext(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t store_plugdeten(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t store_edidread(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_edidread(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static ssize_t store_ceceven(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_cecread(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static ssize_t store_cecsend(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t store_infofrsend(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t store_hdcpeven(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_hdcpchkaesotp(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_hdcpfuseaes(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_hdcpfuseaes(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_hdcploadaes(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_hdcploadaes(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_hdcpauthencr(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_hdcpauthencr(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t show_hdcpstateget(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t show_evread(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static ssize_t store_evclr(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t store_audiocfg(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_plugstatus(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_poweronoff(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_poweronoff(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_evwakeup(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static DEVICE_ATTR(storeastext, S_IWUSR, NULL, store_storeastext);
+static DEVICE_ATTR(plugdeten, S_IWUSR, NULL, store_plugdeten);
+static DEVICE_ATTR(edidread, S_IRUGO | S_IWUSR, show_edidread, store_edidread);
+static DEVICE_ATTR(ceceven, S_IWUSR, NULL, store_ceceven);
+static DEVICE_ATTR(cecread, S_IRUGO, show_cecread, NULL);
+static DEVICE_ATTR(cecsend, S_IWUSR, NULL, store_cecsend);
+static DEVICE_ATTR(infofrsend, S_IWUSR, NULL, store_infofrsend);
+static DEVICE_ATTR(hdcpeven, S_IWUSR, NULL, store_hdcpeven);
+static DEVICE_ATTR(hdcpchkaesotp, S_IRUGO, show_hdcpchkaesotp, NULL);
+static DEVICE_ATTR(hdcpfuseaes, S_IRUGO | S_IWUSR, show_hdcpfuseaes,
+ store_hdcpfuseaes);
+static DEVICE_ATTR(hdcploadaes, S_IRUGO | S_IWUSR, show_hdcploadaes,
+ store_hdcploadaes);
+static DEVICE_ATTR(hdcpauthencr, S_IRUGO | S_IWUSR, show_hdcpauthencr,
+ store_hdcpauthencr);
+static DEVICE_ATTR(hdcpstateget, S_IRUGO, show_hdcpstateget, NULL);
+static DEVICE_ATTR(evread, S_IRUGO, show_evread, NULL);
+static DEVICE_ATTR(evclr, S_IWUSR, NULL, store_evclr);
+static DEVICE_ATTR(audiocfg, S_IWUSR, NULL, store_audiocfg);
+static DEVICE_ATTR(plugstatus, S_IRUGO, show_plugstatus, NULL);
+static DEVICE_ATTR(poweronoff, S_IRUGO | S_IWUSR, show_poweronoff,
+ store_poweronoff);
+static DEVICE_ATTR(evwakeup, S_IWUSR, NULL, store_evwakeup);
+
+/* Hex to int conversion */
+static unsigned int htoi(const char *ptr)
+{
+ unsigned int value = 0;
+ char ch = *ptr;
+
+ if (!ptr)
+ return 0;
+
+ if (isdigit(ch))
+ value = ch - '0';
+ else
+ value = toupper(ch) - 'A' + 10;
+
+ value <<= 4;
+ ch = *(++ptr);
+
+ if (isdigit(ch))
+ value += ch - '0';
+ else
+ value += toupper(ch) - 'A' + 10;
+
+ return value;
+}
+
+static int event_enable(bool enable, enum hdmi_event ev)
+{
+ struct av8100_status status;
+
+ dev_dbg(hdmidev, "enable_event %d %02x\n", enable, ev);
+ if (enable) {
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_IDLE) {
+ av8100_disable_interrupt();
+ av8100_enable_interrupt();
+ }
+
+ events_mask |= ev;
+ } else
+ events_mask &= ~ev;
+
+ return 0;
+}
+
+static int plugdeten(struct plug_detect *pldet)
+{
+ struct av8100_status status;
+ u8 denc_off_time = 0;
+ int retval;
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_STANDBY) {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup failed\n");
+ return -EINVAL;
+ }
+ }
+
+ av8100_reg_hdmi_5_volt_time_r(&denc_off_time, NULL, NULL);
+
+ retval = av8100_reg_hdmi_5_volt_time_w(
+ denc_off_time,
+ pldet->hdmi_off_time,
+ pldet->on_time);
+
+ if (retval) {
+ dev_err(hdmidev, "Failed to write the value to av8100 "
+ "register\n");
+ return -EFAULT;
+ }
+
+ event_enable(pldet->hdmi_detect_enable != 0,
+ HDMI_EVENT_HDMI_PLUGIN);
+ event_enable(pldet->hdmi_detect_enable != 0,
+ HDMI_EVENT_HDMI_PLUGOUT);
+
+ return retval;
+}
+
+static int edidread(struct edid_read *edidread, u8 *len, u8 *data)
+{
+ union av8100_configuration config;
+ struct av8100_status status;
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_STANDBY) {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup failed\n");
+ return -EINVAL;
+ }
+ }
+
+ if (status.av8100_state < AV8100_OPMODE_INIT) {
+ if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100 dl fw FAIL\n");
+ return -EINVAL;
+ }
+ }
+
+ config.edid_section_readback_format.address = edidread->address;
+ config.edid_section_readback_format.block_number = edidread->block_nr;
+
+ dev_dbg(hdmidev, "addr:%0x blnr:%0x",
+ config.edid_section_readback_format.address,
+ config.edid_section_readback_format.block_number);
+
+ if (av8100_conf_prep(AV8100_COMMAND_EDID_SECTION_READBACK,
+ &config) != 0) {
+ dev_err(hdmidev, "av8100_conf_prep FAIL\n");
+ return -EINVAL;
+ }
+
+ if (av8100_conf_w(AV8100_COMMAND_EDID_SECTION_READBACK,
+ len, data, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100_conf_w FAIL\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(hdmidev, "len:%0x\n", *len);
+
+ return 0;
+}
+
+static int cecread(u8 *src, u8 *dest, u8 *data_len, u8 *data)
+{
+ union av8100_configuration config;
+ struct av8100_status status;
+ u8 buf_len;
+ u8 buff[HDMI_CEC_READ_MAXSIZE];
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_STANDBY) {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup failed\n");
+ return -EINVAL;
+ }
+ }
+
+ if (status.av8100_state < AV8100_OPMODE_INIT) {
+ if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100 dl fw FAIL\n");
+ return -EINVAL;
+ }
+ }
+
+ if (av8100_conf_prep(AV8100_COMMAND_CEC_MESSAGE_READ_BACK,
+ &config) != 0) {
+ dev_err(hdmidev, "av8100_conf_prep FAIL\n");
+ return -EINVAL;
+ }
+
+ if (av8100_conf_w(AV8100_COMMAND_CEC_MESSAGE_READ_BACK,
+ &buf_len, buff, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100_conf_w FAIL\n");
+ return -EINVAL;
+ }
+
+ if (buf_len > 0) {
+ *src = (buff[0] & 0xF0) >> 4;
+ *dest = buff[0] & 0x0F;
+ *data_len = buf_len - 1;
+ memcpy(data, &buff[1], buf_len - 1);
+ } else
+ *data_len = 0;
+
+ return 0;
+}
+
+static int cecsend(u8 src, u8 dest, u8 data_len, u8 *data)
+{
+ union av8100_configuration config;
+ struct av8100_status status;
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_STANDBY) {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup failed\n");
+ return -EINVAL;
+ }
+ }
+
+ if (status.av8100_state < AV8100_OPMODE_INIT) {
+ if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100 dl fw FAIL\n");
+ return -EINVAL;
+ }
+ }
+
+ config.cec_message_write_format.buffer[0] = ((src & 0x0F) << 4) +
+ (dest & 0x0F);
+ config.cec_message_write_format.buffer_length = data_len + 1;
+ memcpy(&config.cec_message_write_format.buffer[1], data, data_len);
+
+ if (av8100_conf_prep(AV8100_COMMAND_CEC_MESSAGE_WRITE,
+ &config) != 0) {
+ dev_err(hdmidev, "av8100_conf_prep FAIL\n");
+ return -EINVAL;
+ }
+
+ if (av8100_conf_w(AV8100_COMMAND_CEC_MESSAGE_WRITE,
+ NULL, NULL, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100_conf_w FAIL\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int infofrsend(u8 type, u8 version, u8 crc, u8 data_len, u8 *data)
+{
+ union av8100_configuration config;
+ struct av8100_status status;
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_STANDBY) {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup failed\n");
+ return -EINVAL;
+ }
+ }
+
+ if (status.av8100_state < AV8100_OPMODE_INIT) {
+ if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100 dl fw FAIL\n");
+ return -EINVAL;
+ }
+ }
+
+ if ((data_len < 1) || (data_len > HDMI_INFOFRAME_MAX_SIZE))
+ return -EINVAL;
+
+ config.infoframes_format.type = type;
+ config.infoframes_format.version = version;
+ config.infoframes_format.crc = crc;
+ config.infoframes_format.length = data_len;
+ memcpy(&config.infoframes_format.data, data, data_len);
+ if (av8100_conf_prep(AV8100_COMMAND_INFOFRAMES,
+ &config) != 0) {
+ dev_err(hdmidev, "av8100_conf_prep FAIL\n");
+ return -EINVAL;
+ }
+
+ if (av8100_conf_w(AV8100_COMMAND_INFOFRAMES,
+ NULL, NULL, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100_conf_w FAIL\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hdcpchkaesotp(u8 *crc, u8 *progged)
+{
+ union av8100_configuration config;
+ struct av8100_status status;
+ u8 buf_len;
+ u8 buf[2];
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_STANDBY) {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup failed\n");
+ return -EINVAL;
+ }
+ }
+
+ if (status.av8100_state < AV8100_OPMODE_INIT) {
+ if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) !=
+ 0) {
+ dev_err(hdmidev, "av8100 dl fw FAIL\n");
+ return -EINVAL;
+ }
+ }
+
+ config.fuse_aes_key_format.fuse_operation = AV8100_FUSE_READ;
+ memset(config.fuse_aes_key_format.key, 0, AV8100_FUSE_KEY_SIZE);
+ if (av8100_conf_prep(AV8100_COMMAND_FUSE_AES_KEY,
+ &config) != 0) {
+ dev_err(hdmidev, "av8100_conf_prep FAIL\n");
+ return -EINVAL;
+ }
+
+ if (av8100_conf_w(AV8100_COMMAND_FUSE_AES_KEY,
+ &buf_len, buf, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100_conf_w FAIL\n");
+ return -EINVAL;
+ }
+
+ if (buf_len == 2) {
+ *crc = buf[0];
+ *progged = buf[1];
+ }
+
+ return 0;
+}
+
+static int hdcpfuseaes(u8 *key, u8 crc, u8 *result)
+{
+ union av8100_configuration config;
+ struct av8100_status status;
+ u8 buf_len;
+ u8 buf[2];
+
+ /* Default not OK */
+ *result = HDMI_RESULT_NOT_OK;
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_STANDBY) {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup failed\n");
+ return -EINVAL;
+ }
+ }
+
+ if (status.av8100_state < AV8100_OPMODE_INIT) {
+ if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) !=
+ 0) {
+ dev_err(hdmidev, "av8100 dl fw FAIL\n");
+ return -EINVAL;
+ }
+ }
+
+ config.fuse_aes_key_format.fuse_operation = AV8100_FUSE_WRITE;
+ memcpy(config.fuse_aes_key_format.key, key, AV8100_FUSE_KEY_SIZE);
+ if (av8100_conf_prep(AV8100_COMMAND_FUSE_AES_KEY,
+ &config) != 0) {
+ dev_err(hdmidev, "av8100_conf_prep FAIL\n");
+ return -EINVAL;
+ }
+
+ if (av8100_conf_w(AV8100_COMMAND_FUSE_AES_KEY,
+ &buf_len, buf, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100_conf_w FAIL\n");
+ return -EINVAL;
+ }
+
+ if (buf_len == 2) {
+ dev_dbg(hdmidev, "buf[0]:%02x buf[1]:%02x\n", buf[0], buf[1]);
+ if ((crc == buf[0]) && (buf[1] == 1))
+ /* OK */
+ *result = HDMI_RESULT_OK;
+ else
+ *result = HDMI_RESULT_CRC_MISMATCH;
+ }
+
+ return 0;
+}
+
+static int hdcploadaes(u8 block, u8 key_len, u8 *key, u8 *result, u8 *crc32)
+{
+ union av8100_configuration config;
+ struct av8100_status status;
+ u8 buf_len;
+ u8 buf[CRC32_SIZE];
+
+ /* Default not OK */
+ *result = HDMI_RESULT_NOT_OK;
+
+ dev_dbg(hdmidev, "%s block:%d\n", __func__, block);
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_STANDBY) {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup failed\n");
+ return -EINVAL;
+ }
+ }
+
+ if (status.av8100_state < AV8100_OPMODE_INIT) {
+ if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100 dl fw FAIL\n");
+ return -EINVAL;
+ }
+ }
+
+ config.hdcp_send_key_format.key_number = block;
+ config.hdcp_send_key_format.data_len = key_len;
+ memcpy(config.hdcp_send_key_format.data, key, key_len);
+ if (av8100_conf_prep(AV8100_COMMAND_HDCP_SENDKEY, &config) != 0) {
+ dev_err(hdmidev, "av8100_conf_prep FAIL\n");
+ return -EINVAL;
+ }
+
+ if (av8100_conf_w(AV8100_COMMAND_HDCP_SENDKEY,
+ &buf_len, buf, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100_conf_w FAIL\n");
+ return -EINVAL;
+ }
+
+ if ((buf_len == CRC32_SIZE) && (crc32)) {
+ memcpy(crc32, buf, CRC32_SIZE);
+ dev_dbg(hdmidev, "crc32:%02x%02x%02x%02x\n",
+ crc32[0], crc32[1], crc32[2], crc32[3]);
+ }
+
+ *result = HDMI_RESULT_OK;
+
+ return 0;
+}
+
+static int hdcpauthencr(u8 auth_type, u8 encr_type, u8 *len, u8 *data)
+{
+ union av8100_configuration config;
+ struct av8100_status status;
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_STANDBY) {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup failed\n");
+ return -EINVAL;
+ }
+ }
+
+ if (status.av8100_state < AV8100_OPMODE_INIT) {
+ if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100 dl fw FAIL\n");
+ return -EINVAL;
+ }
+ }
+
+ switch (auth_type) {
+ case HDMI_HDCP_AUTH_OFF:
+ default:
+ config.hdcp_management_format.req_type =
+ AV8100_HDCP_AUTH_REQ_OFF;
+ break;
+
+ case HDMI_HDCP_AUTH_START:
+ config.hdcp_management_format.req_type =
+ AV8100_HDCP_AUTH_REQ_ON;
+ break;
+
+ case HDMI_HDCP_AUTH_REV_LIST_REQ:
+ config.hdcp_management_format.req_type =
+ AV8100_HDCP_REV_LIST_REQ;
+ break;
+ case HDMI_HDCP_AUTH_CONT:
+ config.hdcp_management_format.req_type =
+ AV8100_HDCP_AUTH_CONT;
+ break;
+ }
+
+ switch (encr_type) {
+ case HDMI_HDCP_ENCR_OFF:
+ config.hdcp_management_format.req_encr =
+ AV8100_HDCP_ENCR_REQ_OFF;
+ config.hdcp_management_format.encr_use =
+ AV8100_HDCP_ENCR_USE_OESS;
+ break;
+
+ case HDMI_HDCP_ENCR_OESS:
+ config.hdcp_management_format.req_encr =
+ AV8100_HDCP_ENCR_REQ_ON;
+ config.hdcp_management_format.encr_use =
+ AV8100_HDCP_ENCR_USE_OESS;
+ break;
+
+ case HDMI_HDCP_ENCR_EESS:
+ config.hdcp_management_format.req_encr =
+ AV8100_HDCP_ENCR_REQ_ON;
+ config.hdcp_management_format.encr_use =
+ AV8100_HDCP_ENCR_USE_EESS;
+ break;
+ }
+
+ if (av8100_conf_prep(AV8100_COMMAND_HDCP_MANAGEMENT,
+ &config) != 0) {
+ dev_err(hdmidev, "av8100_conf_prep FAIL\n");
+ return -EINVAL;
+ }
+
+ if (av8100_conf_w(AV8100_COMMAND_HDCP_MANAGEMENT,
+ len, data, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100_conf_w FAIL\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static u8 events_read(void)
+{
+ int ret;
+
+ LOCK_HDMI_EVENTS;
+ ret = events;
+ dev_dbg(hdmidev, "%s %02x\n", __func__, events);
+ UNLOCK_HDMI_EVENTS;
+
+ return ret;
+}
+
+static int events_clear(u8 ev)
+{
+ dev_dbg(hdmidev, "%s %02x\n", __func__, ev);
+
+ LOCK_HDMI_EVENTS;
+ events &= ~ev & EVENTS_MASK;
+ UNLOCK_HDMI_EVENTS;
+
+ return 0;
+}
+
+static int event_wakeup(void)
+{
+ struct kobject *kobj = &hdmidev->kobj;
+
+ dev_dbg(hdmidev, "%s", __func__);
+
+ LOCK_HDMI_EVENTS;
+ events |= HDMI_EVENT_WAKEUP;
+ events_received = true;
+ UNLOCK_HDMI_EVENTS;
+
+ /* Wake up application waiting for event via call to poll() */
+ sysfs_notify(kobj, NULL, SYSFS_EVENT_FILENAME);
+ wake_up_interruptible(&hdmi_event_wq);
+
+ return 0;
+}
+
+static int audiocfg(struct audio_cfg *cfg)
+{
+ union av8100_configuration config;
+ struct av8100_status status;
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_STANDBY) {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup failed\n");
+ return -EINVAL;
+ }
+ }
+
+ if (status.av8100_state < AV8100_OPMODE_INIT) {
+ if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100 dl fw FAIL\n");
+ return -EINVAL;
+ }
+ }
+
+ config.audio_input_format.audio_input_if_format = cfg->if_format;
+ config.audio_input_format.i2s_input_nb = cfg->i2s_entries;
+ config.audio_input_format.sample_audio_freq = cfg->freq;
+ config.audio_input_format.audio_word_lg = cfg->word_length;
+ config.audio_input_format.audio_format = cfg->format;
+ config.audio_input_format.audio_if_mode = cfg->if_mode;
+ config.audio_input_format.audio_mute = cfg->mute;
+
+ if (av8100_conf_prep(AV8100_COMMAND_AUDIO_INPUT_FORMAT,
+ &config) != 0) {
+ dev_err(hdmidev, "av8100_conf_prep FAIL\n");
+ return -EINVAL;
+ }
+
+ if (av8100_conf_w(AV8100_COMMAND_AUDIO_INPUT_FORMAT,
+ NULL, NULL, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100_conf_w FAIL\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* sysfs */
+static ssize_t store_storeastext(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if ((count != HDMI_STOREASTEXT_BIN_SIZE) &&
+ (count != HDMI_STOREASTEXT_TEXT_SIZE) &&
+ (count != HDMI_STOREASTEXT_TEXT_SIZE + 1))
+ return -EINVAL;
+
+ if ((count == HDMI_STOREASTEXT_BIN_SIZE) && (*buf == 0x1))
+ hdmi_driver_data->store_as_hextext = true;
+ else if (((count == HDMI_STOREASTEXT_TEXT_SIZE) ||
+ (count == HDMI_STOREASTEXT_TEXT_SIZE + 1)) && (*buf == '0') &&
+ (*(buf + 1) == '1')) {
+ hdmi_driver_data->store_as_hextext = true;
+ } else {
+ hdmi_driver_data->store_as_hextext = false;
+ }
+
+ dev_dbg(hdmidev, "store_as_hextext:%0d\n",
+ hdmi_driver_data->store_as_hextext);
+
+ return count;
+}
+
+static ssize_t store_plugdeten(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ struct plug_detect plug_detect;
+ int index = 0;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count != HDMI_PLUGDETEN_TEXT_SIZE) &&
+ (count != HDMI_PLUGDETEN_TEXT_SIZE + 1))
+ return -EINVAL;
+ plug_detect.hdmi_detect_enable = htoi(buf + index);
+ index += 2;
+ plug_detect.on_time = htoi(buf + index);
+ index += 2;
+ plug_detect.hdmi_off_time = htoi(buf + index);
+ index += 2;
+ } else {
+ if (count != HDMI_PLUGDETEN_BIN_SIZE)
+ return -EINVAL;
+ plug_detect.hdmi_detect_enable = *(buf + index++);
+ plug_detect.on_time = *(buf + index++);
+ plug_detect.hdmi_off_time = *(buf + index++);
+ }
+
+ if (plugdeten(&plug_detect))
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t store_edidread(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ struct edid_read edid_read;
+ int index = 0;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+ dev_dbg(hdmidev, "count:%d\n", count);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count != HDMI_EDIDREAD_TEXT_SIZE) &&
+ (count != HDMI_EDIDREAD_TEXT_SIZE + 1))
+ return -EINVAL;
+ edid_read.address = htoi(buf + index);
+ index += 2;
+ edid_read.block_nr = htoi(buf + index);
+ index += 2;
+ } else {
+ if (count != HDMI_EDIDREAD_BIN_SIZE)
+ return -EINVAL;
+ edid_read.address = *(buf + index++);
+ edid_read.block_nr = *(buf + index++);
+ }
+
+ if (edidread(&edid_read, &hdmi_driver_data->edid_data.buf_len,
+ hdmi_driver_data->edid_data.buf))
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t show_edidread(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ int len;
+ int index = 0;
+ int cnt;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ len = hdmi_driver_data->edid_data.buf_len;
+
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x", len);
+ index += 2;
+ } else
+ *(buf + index++) = len;
+
+ dev_dbg(hdmidev, "len:%02x\n", len);
+
+ cnt = 0;
+ while (cnt < len) {
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x",
+ hdmi_driver_data->edid_data.buf[cnt]);
+ index += 2;
+ } else
+ *(buf + index++) =
+ hdmi_driver_data->edid_data.buf[cnt];
+
+ dev_dbg(hdmidev, "%02x ",
+ hdmi_driver_data->edid_data.buf[cnt]);
+
+ cnt++;
+ }
+
+ if (hdmi_driver_data->store_as_hextext)
+ index++;
+
+ return index;
+}
+
+static ssize_t store_ceceven(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ bool enable = false;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count != HDMI_CECEVEN_TEXT_SIZE) &&
+ (count != HDMI_CECEVEN_TEXT_SIZE + 1))
+ return -EINVAL;
+ if ((*buf == '0') && (*(buf + 1) == '1'))
+ enable = true;
+ } else {
+ if (count != HDMI_CECEVEN_BIN_SIZE)
+ return -EINVAL;
+ if (*buf == 0x01)
+ enable = true;
+ }
+
+ event_enable(enable, HDMI_EVENT_CEC | HDMI_EVENT_CECTXERR);
+
+ return count;
+}
+
+static ssize_t show_cecread(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ struct cec_rw cec_read;
+ int index = 0;
+ int cnt;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (cecread(&cec_read.src, &cec_read.dest, &cec_read.length,
+ cec_read.data))
+ return -EINVAL;
+
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x", cec_read.src);
+ index += 2;
+ snprintf(buf + index, 3, "%02x", cec_read.dest);
+ index += 2;
+ snprintf(buf + index, 3, "%02x", cec_read.length);
+ index += 2;
+ } else {
+ *(buf + index++) = cec_read.src;
+ *(buf + index++) = cec_read.dest;
+ *(buf + index++) = cec_read.length;
+ }
+
+ dev_dbg(hdmidev, "len:%02x\n", cec_read.length);
+
+ cnt = 0;
+ while (cnt < cec_read.length) {
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x", cec_read.data[cnt]);
+ index += 2;
+ } else
+ *(buf + index++) = cec_read.data[cnt];
+
+ dev_dbg(hdmidev, "%02x ", cec_read.data[cnt]);
+
+ cnt++;
+ }
+
+ if (hdmi_driver_data->store_as_hextext)
+ index++;
+
+ return index;
+}
+
+static ssize_t store_cecsend(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ struct cec_rw cec_w;
+ int index = 0;
+ int cnt;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count < HDMI_CECSEND_TEXT_SIZE_MIN) ||
+ (count > HDMI_CECSEND_TEXT_SIZE_MAX))
+ return -EINVAL;
+
+ cec_w.src = htoi(buf + index);
+ index += 2;
+ cec_w.dest = htoi(buf + index);
+ index += 2;
+ cec_w.length = htoi(buf + index);
+ index += 2;
+ if (cec_w.length > HDMI_CEC_WRITE_MAXSIZE)
+ return -EINVAL;
+ cnt = 0;
+ while (cnt < cec_w.length) {
+ cec_w.data[cnt] = htoi(buf + index);
+ index += 2;
+ dev_dbg(hdmidev, "%02x ", cec_w.data[cnt]);
+ cnt++;
+ }
+ } else {
+ if ((count < HDMI_CECSEND_BIN_SIZE_MIN) ||
+ (count > HDMI_CECSEND_BIN_SIZE_MAX))
+ return -EINVAL;
+
+ cec_w.src = *(buf + index++);
+ cec_w.dest = *(buf + index++);
+ cec_w.length = *(buf + index++);
+ if (cec_w.length > HDMI_CEC_WRITE_MAXSIZE)
+ return -EINVAL;
+ memcpy(cec_w.data, buf + index, cec_w.length);
+ }
+
+ if (cecsend(cec_w.src,
+ cec_w.dest,
+ cec_w.length,
+ cec_w.data))
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t store_infofrsend(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ struct info_fr info_fr;
+ int index = 0;
+ int cnt;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count < HDMI_INFOFRSEND_TEXT_SIZE_MIN) ||
+ (count > HDMI_INFOFRSEND_TEXT_SIZE_MAX))
+ return -EINVAL;
+
+ info_fr.type = htoi(&buf[index]);
+ index += 2;
+ info_fr.ver = htoi(&buf[index]);
+ index += 2;
+ info_fr.crc = htoi(&buf[index]);
+ index += 2;
+ info_fr.length = htoi(&buf[index]);
+ index += 2;
+
+ if (info_fr.length > HDMI_INFOFRAME_MAX_SIZE)
+ return -EINVAL;
+ cnt = 0;
+ while (cnt < info_fr.length) {
+ info_fr.data[cnt] = htoi(buf + index);
+ index += 2;
+ dev_dbg(hdmidev, "%02x ", info_fr.data[cnt]);
+ cnt++;
+ }
+ } else {
+ if ((count < HDMI_INFOFRSEND_BIN_SIZE_MIN) ||
+ (count > HDMI_INFOFRSEND_BIN_SIZE_MAX))
+ return -EINVAL;
+
+ info_fr.type = *(buf + index++);
+ info_fr.ver = *(buf + index++);
+ info_fr.crc = *(buf + index++);
+ info_fr.length = *(buf + index++);
+
+ if (info_fr.length > HDMI_INFOFRAME_MAX_SIZE)
+ return -EINVAL;
+ memcpy(info_fr.data, buf + index, info_fr.length);
+ }
+
+ if (infofrsend(info_fr.type, info_fr.ver, info_fr.crc,
+ info_fr.length, info_fr.data))
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t store_hdcpeven(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ bool enable = false;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count != HDMI_HDCPEVEN_TEXT_SIZE) &&
+ (count != HDMI_HDCPEVEN_TEXT_SIZE + 1))
+ return -EINVAL;
+ if ((*buf == '0') && (*(buf + 1) == '1'))
+ enable = true;
+ } else {
+ if (count != HDMI_HDCPEVEN_BIN_SIZE)
+ return -EINVAL;
+ if (*buf == 0x01)
+ enable = true;
+ }
+
+ event_enable(enable, HDMI_EVENT_HDCP);
+
+ return count;
+}
+
+static ssize_t show_hdcpchkaesotp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ u8 crc;
+ u8 progged;
+ int index = 0;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdcpchkaesotp(&crc, &progged))
+ return -EINVAL;
+
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x", progged);
+ index += 2;
+ } else {
+ *(buf + index++) = progged;
+ }
+
+ dev_dbg(hdmidev, "progged:%02x\n", progged);
+
+ if (hdmi_driver_data->store_as_hextext)
+ index++;
+
+ return index;
+}
+
+static ssize_t store_hdcpfuseaes(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ struct hdcp_fuseaes hdcp_fuseaes;
+ int index = 0;
+ int cnt;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ /* Default not OK */
+ hdmi_driver_data->fuse_result = HDMI_RESULT_NOT_OK;
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count != HDMI_HDCP_FUSEAES_TEXT_SIZE) &&
+ (count != HDMI_HDCP_FUSEAES_TEXT_SIZE + 1))
+ return -EINVAL;
+
+ cnt = 0;
+ while (cnt < HDMI_HDCP_FUSEAES_KEYSIZE) {
+ hdcp_fuseaes.key[cnt] = htoi(buf + index);
+ index += 2;
+ dev_dbg(hdmidev, "%02x ", hdcp_fuseaes.key[cnt]);
+ cnt++;
+ }
+ hdcp_fuseaes.crc = htoi(&buf[index]);
+ index += 2;
+ dev_dbg(hdmidev, "%02x ", hdcp_fuseaes.crc);
+ } else {
+ if (count != HDMI_HDCP_FUSEAES_BIN_SIZE)
+ return -EINVAL;
+
+ memcpy(hdcp_fuseaes.key, buf + index,
+ HDMI_HDCP_FUSEAES_KEYSIZE);
+ index += HDMI_HDCP_FUSEAES_KEYSIZE;
+ hdcp_fuseaes.crc = *(buf + index++);
+ }
+
+ if (hdcpfuseaes(hdcp_fuseaes.key, hdcp_fuseaes.crc,
+ &hdcp_fuseaes.result))
+ return -EINVAL;
+
+ dev_dbg(hdmidev, "fuseresult:%02x ", hdcp_fuseaes.result);
+
+ hdmi_driver_data->fuse_result = hdcp_fuseaes.result;
+
+ return count;
+}
+
+static ssize_t show_hdcpfuseaes(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ int index = 0;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x",
+ hdmi_driver_data->fuse_result);
+ index += 2;
+ } else
+ *(buf + index++) = hdmi_driver_data->fuse_result;
+
+ dev_dbg(hdmidev, "status:%02x\n", hdmi_driver_data->fuse_result);
+
+ if (hdmi_driver_data->store_as_hextext)
+ index++;
+
+ return index;
+}
+
+static ssize_t store_hdcploadaes(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ struct hdcp_loadaesone hdcp_loadaes;
+ int index = 0;
+ int block_cnt;
+ int cnt;
+ u8 crc32_rcvd[CRC32_SIZE];
+ u8 crc;
+ u8 progged;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ /* Default not OK */
+ hdmi_driver_data->loadaes_result = HDMI_RESULT_NOT_OK;
+
+ if (hdcpchkaesotp(&crc, &progged))
+ return -EINVAL;
+
+ if (!progged) {
+ /* AES is not fused */
+ hdcp_loadaes.result = HDMI_AES_NOT_FUSED;
+ goto store_hdcploadaes_err;
+ }
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count != HDMI_HDCP_LOADAES_TEXT_SIZE) &&
+ (count != HDMI_HDCP_LOADAES_TEXT_SIZE + 1)) {
+ dev_err(hdmidev, "%s", "count mismatch\n");
+ return -EINVAL;
+ }
+
+ /* AES */
+ block_cnt = 0;
+ while (block_cnt < HDMI_HDCP_AES_NR_OF_BLOCKS) {
+ cnt = 0;
+ while (cnt < HDMI_HDCP_AES_KEYSIZE) {
+ hdcp_loadaes.key[cnt] = htoi(buf + index);
+ index += 2;
+ dev_dbg(hdmidev, "%02x ",
+ hdcp_loadaes.key[cnt]);
+ cnt++;
+ }
+
+ if (hdcploadaes(block_cnt + HDMI_HDCP_AES_BLOCK_START,
+ HDMI_HDCP_AES_KEYSIZE,
+ hdcp_loadaes.key,
+ &hdcp_loadaes.result,
+ crc32_rcvd)) {
+ dev_err(hdmidev, "%s %d\n",
+ "hdcploadaes err aes block",
+ block_cnt + HDMI_HDCP_AES_BLOCK_START);
+ return -EINVAL;
+ }
+
+ if (hdcp_loadaes.result)
+ goto store_hdcploadaes_err;
+
+ block_cnt++;
+ }
+
+ /* KSV */
+ memset(hdcp_loadaes.key, 0, HDMI_HDCP_AES_KSVZEROESSIZE);
+ cnt = HDMI_HDCP_AES_KSVZEROESSIZE;
+ while (cnt < HDMI_HDCP_AES_KSVSIZE +
+ HDMI_HDCP_AES_KSVZEROESSIZE) {
+ hdcp_loadaes.key[cnt] =
+ htoi(&buf[index]);
+ index += 2;
+ dev_dbg(hdmidev, "%02x ", hdcp_loadaes.key[cnt]);
+ cnt++;
+ }
+
+ if (hdcploadaes(HDMI_HDCP_KSV_BLOCK,
+ HDMI_HDCP_AES_KSVSIZE +
+ HDMI_HDCP_AES_KSVZEROESSIZE,
+ hdcp_loadaes.key,
+ &hdcp_loadaes.result,
+ NULL)) {
+ dev_err(hdmidev, "%s %d\n", "hdcploadaes err in ksv\n",
+ block_cnt + HDMI_HDCP_AES_BLOCK_START);
+ return -EINVAL;
+ }
+
+ if (hdcp_loadaes.result)
+ goto store_hdcploadaes_err;
+
+ /* CRC32 */
+ for (cnt = 0; cnt < CRC32_SIZE; cnt++) {
+ hdcp_loadaes.crc32[cnt] = htoi(buf + index);
+ index += 2;
+ }
+
+ if (memcmp(hdcp_loadaes.crc32, crc32_rcvd, CRC32_SIZE)) {
+ dev_dbg(hdmidev, "crc32exp:%02x%02x%02x%02x\n",
+ hdcp_loadaes.crc32[0],
+ hdcp_loadaes.crc32[1],
+ hdcp_loadaes.crc32[2],
+ hdcp_loadaes.crc32[3]);
+ hdcp_loadaes.result = HDMI_RESULT_CRC_MISMATCH;
+ goto store_hdcploadaes_err;
+ }
+ } else {
+ if (count != HDMI_HDCP_LOADAES_BIN_SIZE) {
+ dev_err(hdmidev, "%s", "count mismatch\n");
+ return -EINVAL;
+ }
+
+ /* AES */
+ block_cnt = 0;
+ while (block_cnt < HDMI_HDCP_AES_NR_OF_BLOCKS) {
+ memcpy(hdcp_loadaes.key, buf + index,
+ HDMI_HDCP_AES_KEYSIZE);
+ index += HDMI_HDCP_AES_KEYSIZE;
+
+ if (hdcploadaes(block_cnt + HDMI_HDCP_AES_BLOCK_START,
+ HDMI_HDCP_AES_KEYSIZE,
+ hdcp_loadaes.key,
+ &hdcp_loadaes.result,
+ crc32_rcvd)) {
+ dev_err(hdmidev, "%s %d\n",
+ "hdcploadaes err aes block",
+ block_cnt + HDMI_HDCP_AES_BLOCK_START);
+ return -EINVAL;
+ }
+
+ if (hdcp_loadaes.result)
+ goto store_hdcploadaes_err;
+
+ block_cnt++;
+ }
+
+ /* KSV */
+ memset(hdcp_loadaes.key, 0, HDMI_HDCP_AES_KSVZEROESSIZE);
+ memcpy(hdcp_loadaes.key + HDMI_HDCP_AES_KSVZEROESSIZE,
+ buf + index,
+ HDMI_HDCP_AES_KSVSIZE);
+ index += HDMI_HDCP_AES_KSVSIZE;
+
+ if (hdcploadaes(HDMI_HDCP_KSV_BLOCK,
+ HDMI_HDCP_AES_KSVSIZE +
+ HDMI_HDCP_AES_KSVZEROESSIZE,
+ hdcp_loadaes.key,
+ &hdcp_loadaes.result,
+ NULL)) {
+ dev_err(hdmidev, "%s %d\n", "hdcploadaes err in ksv\n",
+ block_cnt + HDMI_HDCP_AES_BLOCK_START);
+ return -EINVAL;
+ }
+
+ memcpy(hdcp_loadaes.crc32, buf + index, CRC32_SIZE);
+ index += CRC32_SIZE;
+
+ /* CRC32 */
+ if (memcmp(hdcp_loadaes.crc32, crc32_rcvd, CRC32_SIZE)) {
+ dev_dbg(hdmidev, "crc32exp:%02x%02x%02x%02x\n",
+ hdcp_loadaes.crc32[0],
+ hdcp_loadaes.crc32[1],
+ hdcp_loadaes.crc32[2],
+ hdcp_loadaes.crc32[3]);
+ hdcp_loadaes.result = HDMI_RESULT_CRC_MISMATCH;
+ }
+ }
+
+store_hdcploadaes_err:
+ hdmi_driver_data->loadaes_result = hdcp_loadaes.result;
+ return count;
+}
+
+static ssize_t show_hdcploadaes(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ int index = 0;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x",
+ hdmi_driver_data->loadaes_result);
+ index += 2;
+ } else
+ *(buf + index++) = hdmi_driver_data->loadaes_result;
+
+ dev_dbg(hdmidev, "result:%02x\n", hdmi_driver_data->loadaes_result);
+
+ if (hdmi_driver_data->store_as_hextext)
+ index++;
+
+ return index;
+}
+
+static ssize_t store_hdcpauthencr(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ struct hdcp_authencr hdcp_authencr;
+ int index = 0;
+ u8 crc;
+ u8 progged;
+ int result = HDMI_RESULT_OK;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ /* Default not OK */
+ hdmi_driver_data->authencr.result = HDMI_RESULT_NOT_OK;
+
+ if (hdcpchkaesotp(&crc, &progged))
+ return -EINVAL;
+
+ if (!progged) {
+ /* AES is not fused */
+ result = HDMI_AES_NOT_FUSED;
+ goto store_hdcpauthencr_err;
+ }
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count != HDMI_HDCPAUTHENCR_TEXT_SIZE) &&
+ (count != HDMI_HDCPAUTHENCR_TEXT_SIZE + 1))
+ return -EINVAL;
+
+ hdcp_authencr.auth_type = htoi(buf + index);
+ index += 2;
+ hdcp_authencr.encr_type = htoi(buf + index);
+ index += 2;
+ } else {
+ if (count != HDMI_HDCPAUTHENCR_BIN_SIZE)
+ return -EINVAL;
+
+ hdcp_authencr.auth_type = *(buf + index++);
+ hdcp_authencr.encr_type = *(buf + index++);
+ }
+
+ if (hdcpauthencr(hdcp_authencr.auth_type, hdcp_authencr.encr_type,
+ &hdmi_driver_data->authencr.buf_len,
+ hdmi_driver_data->authencr.buf))
+ return -EINVAL;
+
+store_hdcpauthencr_err:
+ hdmi_driver_data->authencr.result = result;
+ return count;
+}
+
+static ssize_t show_hdcpauthencr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ int len;
+ int index = 0;
+ int cnt;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ len = hdmi_driver_data->authencr.buf_len;
+ if (len > AUTH_BUF_LEN)
+ len = AUTH_BUF_LEN;
+
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x",
+ hdmi_driver_data->authencr.result);
+ index += 2;
+ } else
+ *(buf + index++) = hdmi_driver_data->authencr.result;
+
+ cnt = 0;
+ while (cnt < len) {
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x",
+ hdmi_driver_data->authencr.buf[cnt]);
+ index += 2;
+
+ dev_dbg(hdmidev, "%02x ",
+ hdmi_driver_data->authencr.buf[cnt]);
+
+ } else
+ *(buf + index++) = hdmi_driver_data->authencr.buf[cnt];
+
+ cnt++;
+ }
+
+ dev_dbg(hdmidev, "result:%02x\n", hdmi_driver_data->authencr.result);
+
+ if (hdmi_driver_data->store_as_hextext)
+ index++;
+
+ return index;
+}
+
+static ssize_t show_hdcpstateget(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ u8 hdcp_state;
+ int index = 0;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (av8100_reg_gen_status_r(NULL, NULL, NULL, NULL, NULL, &hdcp_state))
+ return -EINVAL;
+
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x", hdcp_state);
+ index += 2;
+ } else
+ *(buf + index++) = hdcp_state;
+
+ dev_dbg(hdmidev, "status:%02x\n", hdcp_state);
+
+ if (hdmi_driver_data->store_as_hextext)
+ index++;
+
+ return index;
+}
+
+static ssize_t show_evread(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ int index = 0;
+ u8 ev;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ ev = events_read();
+
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x", ev);
+ index += 2;
+ } else
+ *(buf + index++) = ev;
+
+ if (hdmi_driver_data->store_as_hextext)
+ index++;
+
+ /* Events are read: clear events */
+ events_clear(EVENTS_MASK);
+
+ return index;
+}
+
+static ssize_t store_evclr(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ u8 ev;
+ int index = 0;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count != HDMI_EVCLR_TEXT_SIZE) &&
+ (count != HDMI_EVCLR_TEXT_SIZE + 1))
+ return -EINVAL;
+
+ ev = htoi(&buf[index]);
+ index += 2;
+ } else {
+ if (count != HDMI_EVCLR_BIN_SIZE)
+ return -EINVAL;
+
+ ev = *(buf + index++);
+ }
+
+ events_clear(ev);
+
+ return count;
+}
+
+static ssize_t store_audiocfg(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ struct audio_cfg audio_cfg;
+ int index = 0;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count != HDMI_AUDIOCFG_TEXT_SIZE) &&
+ (count != HDMI_AUDIOCFG_TEXT_SIZE + 1))
+ return -EINVAL;
+
+ audio_cfg.if_format = htoi(&buf[index]);
+ index += 2;
+ audio_cfg.i2s_entries = htoi(&buf[index]);
+ index += 2;
+ audio_cfg.freq = htoi(&buf[index]);
+ index += 2;
+ audio_cfg.word_length = htoi(&buf[index]);
+ index += 2;
+ audio_cfg.format = htoi(&buf[index]);
+ index += 2;
+ audio_cfg.if_mode = htoi(&buf[index]);
+ index += 2;
+ audio_cfg.mute = htoi(&buf[index]);
+ index += 2;
+ } else {
+ if (count != HDMI_AUDIOCFG_BIN_SIZE)
+ return -EINVAL;
+
+ audio_cfg.if_format = *(buf + index++);
+ audio_cfg.i2s_entries = *(buf + index++);
+ audio_cfg.freq = *(buf + index++);
+ audio_cfg.word_length = *(buf + index++);
+ audio_cfg.format = *(buf + index++);
+ audio_cfg.if_mode = *(buf + index++);
+ audio_cfg.mute = *(buf + index++);
+ }
+
+ audiocfg(&audio_cfg);
+
+ return count;
+}
+
+static ssize_t show_plugstatus(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ int index = 0;
+ struct av8100_status av8100_status;
+ u8 plstat;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ av8100_status = av8100_status_get();
+ plstat = av8100_status.av8100_plugin_status == AV8100_HDMI_PLUGIN;
+
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x", plstat);
+ index += 2;
+ } else
+ *(buf + index++) = plstat;
+
+ if (hdmi_driver_data->store_as_hextext)
+ index++;
+
+ return index;
+}
+
+static ssize_t store_poweronoff(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ bool enable = false;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ if (hdmi_driver_data->store_as_hextext) {
+ if ((count != HDMI_POWERONOFF_TEXT_SIZE) &&
+ (count != HDMI_POWERONOFF_TEXT_SIZE + 1))
+ return -EINVAL;
+ if ((*buf == '0') && (*(buf + 1) == '1'))
+ enable = true;
+ } else {
+ if (count != HDMI_POWERONOFF_BIN_SIZE)
+ return -EINVAL;
+ if (*buf == 0x01)
+ enable = true;
+ }
+
+ if (enable == 0) {
+ if (av8100_powerdown() != 0) {
+ dev_err(hdmidev, "av8100_powerdown FAIL\n");
+ return -EINVAL;
+ }
+ } else {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup FAIL\n");
+ return -EINVAL;
+ }
+ }
+
+ return count;
+}
+
+static ssize_t show_poweronoff(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ int index = 0;
+ struct av8100_status status;
+ u8 power_state;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_SCAN)
+ power_state = 0;
+ else
+ power_state = 1;
+
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x", power_state);
+ index += 3;
+ } else {
+ *(buf + index++) = power_state;
+ }
+
+ return index;
+}
+
+static ssize_t store_evwakeup(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ event_wakeup();
+
+ return count;
+}
+
+static int hdmi_open(struct inode *inode, struct file *filp)
+{
+ if (device_open)
+ return -EBUSY;
+
+ device_open++;
+
+ return 0;
+}
+
+static int hdmi_release(struct inode *inode, struct file *filp)
+{
+ if (device_open)
+ device_open--;
+
+ return 0;
+}
+
+/* ioctl */
+static int hdmi_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ u8 value = 0;
+ struct plug_detect plug_detect;
+ struct edid_read edid_read;
+ struct cec_rw cec_read;
+ struct cec_rw cec_send;
+ struct info_fr info_fr;
+ struct hdcp_fuseaes hdcp_fuseaes;
+ struct hdcp_loadaesall hdcp_loadaesall;
+ int block_cnt;
+ struct hdcp_loadaesone hdcp_loadaesone;
+ struct hdcp_authencr hdcp_authencr;
+ struct audio_cfg audio_cfg;
+ union av8100_configuration config;
+ struct hdmi_register reg;
+ struct hdmi_command_register command_reg;
+ struct av8100_status status;
+ u8 aes_status;
+
+ switch (cmd) {
+
+ case IOC_PLUG_DETECT_ENABLE:
+ if (copy_from_user(&plug_detect, (void *)arg,
+ sizeof(struct plug_detect)))
+ return -EINVAL;
+
+ if (plugdeten(&plug_detect))
+ return -EINVAL;
+ break;
+
+ case IOC_EDID_READ:
+ if (copy_from_user(&edid_read, (void *)arg,
+ sizeof(struct edid_read)))
+ return -EINVAL;
+
+ if (edidread(&edid_read, &edid_read.data_length,
+ edid_read.data))
+ return -EINVAL;
+
+ if (copy_to_user((void *)arg, (void *)&edid_read,
+ sizeof(struct edid_read))) {
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_CEC_EVENT_ENABLE:
+ if (copy_from_user(&value, (void *)arg, sizeof(u8)))
+ return -EINVAL;
+
+ event_enable(value != 0, HDMI_EVENT_CEC | HDMI_EVENT_CECTXERR);
+ break;
+
+ case IOC_CEC_READ:
+ if (cecread(&cec_read.src, &cec_read.dest, &cec_read.length,
+ cec_read.data))
+ return -EINVAL;
+
+ if (copy_to_user((void *)arg, (void *)&cec_read,
+ sizeof(struct cec_rw))) {
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_CEC_SEND:
+ if (copy_from_user(&cec_send, (void *)arg,
+ sizeof(struct cec_rw)))
+ return -EINVAL;
+
+ if (cecsend(cec_send.src,
+ cec_send.dest,
+ cec_send.length,
+ cec_send.data))
+ return -EINVAL;
+ break;
+
+ case IOC_INFOFRAME_SEND:
+ if (copy_from_user(&info_fr, (void *)arg,
+ sizeof(struct info_fr)))
+ return -EINVAL;
+
+ if (infofrsend(info_fr.type, info_fr.ver, info_fr.crc,
+ info_fr.length, info_fr.data))
+ return -EINVAL;
+ break;
+
+ case IOC_HDCP_EVENT_ENABLE:
+ if (copy_from_user(&value, (void *)arg, sizeof(u8)))
+ return -EINVAL;
+
+ event_enable(value != 0, HDMI_EVENT_HDCP);
+ break;
+
+ case IOC_HDCP_CHKAESOTP:
+ if (hdcpchkaesotp(&value, &aes_status))
+ return -EINVAL;
+
+ if (copy_to_user((void *)arg, (void *)&aes_status,
+ sizeof(u8))) {
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_HDCP_FUSEAES:
+ if (copy_from_user(&hdcp_fuseaes, (void *)arg,
+ sizeof(struct hdcp_fuseaes)))
+ return -EINVAL;
+
+ if (hdcpfuseaes(hdcp_fuseaes.key, hdcp_fuseaes.crc,
+ &hdcp_fuseaes.result))
+ return -EINVAL;
+
+ if (copy_to_user((void *)arg, (void *)&hdcp_fuseaes,
+ sizeof(struct hdcp_fuseaes))) {
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_HDCP_LOADAES:
+ if (copy_from_user(&hdcp_loadaesall, (void *)arg,
+ sizeof(struct hdcp_loadaesall)))
+ return -EINVAL;
+
+ if (hdcpchkaesotp(&value, &aes_status))
+ return -EINVAL;
+
+ if (!aes_status) {
+ /* AES is not fused */
+ hdcp_loadaesone.result = HDMI_AES_NOT_FUSED;
+ goto ioc_hdcploadaes_err;
+ }
+
+ /* AES */
+ block_cnt = 0;
+ while (block_cnt < HDMI_HDCP_AES_NR_OF_BLOCKS) {
+ memcpy(hdcp_loadaesone.key, hdcp_loadaesall.key +
+ block_cnt * HDMI_HDCP_AES_KEYSIZE,
+ HDMI_HDCP_AES_KEYSIZE);
+
+ if (hdcploadaes(block_cnt + HDMI_HDCP_AES_BLOCK_START,
+ HDMI_HDCP_AES_KEYSIZE,
+ hdcp_loadaesone.key,
+ &hdcp_loadaesone.result,
+ hdcp_loadaesone.crc32))
+ return -EINVAL;
+
+ if (hdcp_loadaesone.result)
+ return -EINVAL;
+
+ block_cnt++;
+ }
+
+ /* KSV */
+ memset(hdcp_loadaesone.key, 0, HDMI_HDCP_AES_KSVZEROESSIZE);
+ memcpy(hdcp_loadaesone.key + HDMI_HDCP_AES_KSVZEROESSIZE,
+ hdcp_loadaesall.ksv, HDMI_HDCP_AES_KSVSIZE);
+
+ if (hdcploadaes(HDMI_HDCP_KSV_BLOCK,
+ HDMI_HDCP_AES_KSVSIZE +
+ HDMI_HDCP_AES_KSVZEROESSIZE,
+ hdcp_loadaesone.key,
+ &hdcp_loadaesone.result,
+ NULL))
+ return -EINVAL;
+
+ if (hdcp_loadaesone.result)
+ return -EINVAL;
+
+ /* CRC32 */
+ if (memcmp(hdcp_loadaesall.crc32, hdcp_loadaesone.crc32,
+ CRC32_SIZE)) {
+ dev_dbg(hdmidev, "crc32exp:%02x%02x%02x%02x\n",
+ hdcp_loadaesall.crc32[0],
+ hdcp_loadaesall.crc32[1],
+ hdcp_loadaesall.crc32[2],
+ hdcp_loadaesall.crc32[3]);
+ hdcp_loadaesone.result = HDMI_RESULT_CRC_MISMATCH;
+ goto ioc_hdcploadaes_err;
+ }
+
+ioc_hdcploadaes_err:
+ hdcp_loadaesall.result = hdcp_loadaesone.result;
+
+ if (copy_to_user((void *)arg, (void *)&hdcp_loadaesall,
+ sizeof(struct hdcp_loadaesall))) {
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_HDCP_AUTHENCR_REQ:
+ if (copy_from_user(&hdcp_authencr, (void *)arg,
+ sizeof(struct hdcp_authencr)))
+ return -EINVAL;
+
+ /* Default not OK */
+ hdcp_authencr.result = HDMI_RESULT_NOT_OK;
+
+ if (hdcpchkaesotp(&value, &aes_status))
+ return -EINVAL;
+
+ if (!aes_status) {
+ /* AES is not fused */
+ hdcp_authencr.result = HDMI_AES_NOT_FUSED;
+ break;
+ }
+
+ if (hdcpauthencr(hdcp_authencr.auth_type,
+ hdcp_authencr.encr_type,
+ &value,
+ hdcp_authencr.revoc_list))
+ return -EINVAL;
+
+ hdcp_authencr.result = HDMI_RESULT_OK;
+ break;
+
+ case IOC_HDCP_STATE_GET:
+ if (av8100_reg_gen_status_r(NULL, NULL, NULL, NULL, NULL,
+ &value))
+ return -EINVAL;
+
+ if (copy_to_user((void *)arg, (void *)&value,
+ sizeof(u8))) {
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_EVENTS_READ:
+ value = events_read();
+
+ if (copy_to_user((void *)arg, (void *)&value,
+ sizeof(u8))) {
+ return -EINVAL;
+ }
+
+ /* Events are read: clear events */
+ events_clear(EVENTS_MASK);
+ break;
+
+ case IOC_EVENTS_CLEAR:
+ if (copy_from_user(&value, (void *)arg, sizeof(u8)))
+ return -EINVAL;
+
+ events_clear(value);
+ break;
+
+ case IOC_AUDIO_CFG:
+ if (copy_from_user(&audio_cfg, (void *)arg,
+ sizeof(struct audio_cfg)))
+ return -EINVAL;
+
+ audiocfg(&audio_cfg);
+ break;
+
+ case IOC_PLUG_STATUS:
+ status = av8100_status_get();
+ value = status.av8100_plugin_status == AV8100_HDMI_PLUGIN;
+
+ if (copy_to_user((void *)arg, (void *)&value,
+ sizeof(u8))) {
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_POWERONOFF:
+ /* Get desired power state on or off */
+ if (copy_from_user(&value, (void *)arg, sizeof(u8)))
+ return -EINVAL;
+
+ if (value == 0) {
+ if (av8100_powerdown() != 0) {
+ dev_err(hdmidev, "av8100_powerdown FAIL\n");
+ return -EINVAL;
+ }
+ } else {
+ if (av8100_powerup() != 0) {
+ dev_err(hdmidev, "av8100_powerup FAIL\n");
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case IOC_EVENT_WAKEUP:
+ /* Trigger event */
+ event_wakeup();
+ break;
+
+ case IOC_POWERSTATE:
+ status = av8100_status_get();
+ value = status.av8100_state >= AV8100_OPMODE_SCAN;
+
+ if (copy_to_user((void *)arg, (void *)&value,
+ sizeof(u8))) {
+ return -EINVAL;
+ }
+ break;
+
+ /* Internal */
+ case IOC_HDMI_ENABLE_INTERRUPTS:
+ av8100_disable_interrupt();
+ if (av8100_enable_interrupt() != 0) {
+ dev_err(hdmidev, "av8100_conf_get FAIL\n");
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_HDMI_DOWNLOAD_FW:
+ if (av8100_download_firmware(NULL, 0, I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100 dl fw FAIL\n");
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_HDMI_ONOFF:
+ /* Get desired HDMI mode on or off */
+ if (copy_from_user(&value, (void *)arg, sizeof(u8)))
+ return -EFAULT;
+
+ if (av8100_conf_get(AV8100_COMMAND_HDMI, &config) != 0) {
+ dev_err(hdmidev, "av8100_conf_get FAIL\n");
+ return -EINVAL;
+ }
+ if (value == 0)
+ config.hdmi_format.hdmi_mode = AV8100_HDMI_OFF;
+ else
+ config.hdmi_format.hdmi_mode = AV8100_HDMI_ON;
+
+ if (av8100_conf_prep(AV8100_COMMAND_HDMI, &config) != 0) {
+ dev_err(hdmidev, "av8100_conf_prep FAIL\n");
+ return -EINVAL;
+ }
+ if (av8100_conf_w(AV8100_COMMAND_HDMI, NULL, NULL,
+ I2C_INTERFACE) != 0) {
+ dev_err(hdmidev, "av8100_conf_w FAIL\n");
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_HDMI_REGISTER_WRITE:
+ if (copy_from_user(&reg, (void *)arg,
+ sizeof(struct hdmi_register))) {
+ return -EINVAL;
+ }
+
+ if (av8100_reg_w(reg.offset, reg.value) != 0) {
+ dev_err(hdmidev, "hdmi_register_write FAIL\n");
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_HDMI_REGISTER_READ:
+ if (copy_from_user(&reg, (void *)arg,
+ sizeof(struct hdmi_register))) {
+ return -EINVAL;
+ }
+
+ if (av8100_reg_r(reg.offset, &reg.value) != 0) {
+ dev_err(hdmidev, "hdmi_register_write FAIL\n");
+ return -EINVAL;
+ }
+
+ if (copy_to_user((void *)arg, (void *)&reg,
+ sizeof(struct hdmi_register))) {
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_HDMI_STATUS_GET:
+ status = av8100_status_get();
+
+ if (copy_to_user((void *)arg, (void *)&status,
+ sizeof(struct av8100_status))) {
+ return -EINVAL;
+ }
+ break;
+
+ case IOC_HDMI_CONFIGURATION_WRITE:
+ if (copy_from_user(&command_reg, (void *)arg,
+ sizeof(struct hdmi_command_register)) != 0) {
+ dev_err(hdmidev, "IOC_HDMI_CONFIGURATION_WRITE "
+ "fail 1\n");
+ command_reg.return_status = EINVAL;
+ } else {
+ if (av8100_conf_w_raw(command_reg.cmd_id,
+ command_reg.buf_len,
+ command_reg.buf,
+ &(command_reg.buf_len),
+ command_reg.buf) != 0) {
+ dev_err(hdmidev, "IOC_HDMI_CONFIGURATION_WRITE "
+ "fail 2\n");
+ command_reg.return_status = EINVAL;
+ }
+ }
+
+ if (copy_to_user((void *)arg, (void *)&command_reg,
+ sizeof(struct hdmi_command_register)) != 0) {
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static unsigned int
+hdmi_poll(struct file *filp, poll_table *wait)
+{
+ unsigned int mask = 0;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ poll_wait(filp, &hdmi_event_wq , wait);
+
+ LOCK_HDMI_EVENTS;
+ if (events_received == true) {
+ events_received = false;
+ mask = POLLIN | POLLRDNORM;
+ }
+ UNLOCK_HDMI_EVENTS;
+
+ return mask;
+}
+
+static const struct file_operations hdmi_fops = {
+ .owner = THIS_MODULE,
+ .open = hdmi_open,
+ .release = hdmi_release,
+ .ioctl = hdmi_ioctl,
+ .poll = hdmi_poll
+};
+
+static struct miscdevice hdmi_miscdev = {
+ MISC_DYNAMIC_MINOR,
+ "hdmi",
+ &hdmi_fops
+};
+
+/* Event callback function called by hw driver */
+void hdmi_event(enum av8100_hdmi_event ev)
+{
+ int events_old;
+ int events_new;
+ struct kobject *kobj = &hdmidev->kobj;
+
+ dev_dbg(hdmidev, "hdmi_event %02x\n", ev);
+
+ LOCK_HDMI_EVENTS;
+
+ events_old = events;
+
+ /* Set event */
+ switch (ev) {
+ case AV8100_HDMI_EVENT_HDMI_PLUGIN:
+ events &= ~HDMI_EVENT_HDMI_PLUGOUT;
+ events |= events_mask & HDMI_EVENT_HDMI_PLUGIN;
+ break;
+
+ case AV8100_HDMI_EVENT_HDMI_PLUGOUT:
+ events &= ~HDMI_EVENT_HDMI_PLUGIN;
+ events |= events_mask & HDMI_EVENT_HDMI_PLUGOUT;
+ break;
+
+ case AV8100_HDMI_EVENT_CEC:
+ events |= events_mask & HDMI_EVENT_CEC;
+ break;
+
+ case AV8100_HDMI_EVENT_HDCP:
+ events |= events_mask & HDMI_EVENT_HDCP;
+ break;
+
+ case AV8100_HDMI_EVENT_CECTXERR:
+ events |= events_mask & HDMI_EVENT_CECTXERR;
+ break;
+
+ default:
+ break;
+ }
+
+ events_new = events;
+
+ UNLOCK_HDMI_EVENTS;
+
+ dev_dbg(hdmidev, "hdmi events:%02x, events_old:%02x mask:%02x\n",
+ events_new, events_old, events_mask);
+
+ if (events_new != events_old) {
+ /* Wake up application waiting for event via call to poll() */
+ sysfs_notify(kobj, NULL, SYSFS_EVENT_FILENAME);
+
+ LOCK_HDMI_EVENTS;
+ events_received = true;
+ UNLOCK_HDMI_EVENTS;
+
+ wake_up_interruptible(&hdmi_event_wq);
+ }
+}
+EXPORT_SYMBOL(hdmi_event);
+
+int __init hdmi_init(void)
+{
+ int ret;
+ struct hdmi_driver_data *hdmi_driver_data;
+
+ ret = misc_register(&hdmi_miscdev);
+ if (ret)
+ goto hdmi_init_out;
+
+ hdmidev = hdmi_miscdev.this_device;
+
+ hdmi_driver_data =
+ kzalloc(sizeof(struct hdmi_driver_data), GFP_KERNEL);
+
+ if (!hdmi_driver_data)
+ return -ENOMEM;
+
+ dev_set_drvdata(hdmidev, hdmi_driver_data);
+
+ /* Default sysfs file format is hextext */
+ hdmi_driver_data->store_as_hextext = true;
+
+ init_waitqueue_head(&hdmi_event_wq);
+
+ if (device_create_file(hdmidev, &dev_attr_storeastext))
+ dev_info(hdmidev, "Unable to create storeastext attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_plugdeten))
+ dev_info(hdmidev, "Unable to create plugdeten attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_edidread))
+ dev_info(hdmidev, "Unable to create edidread attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_ceceven))
+ dev_info(hdmidev, "Unable to create ceceven attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_cecread))
+ dev_info(hdmidev, "Unable to create cecread attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_cecsend))
+ dev_info(hdmidev, "Unable to create cecsend attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_infofrsend))
+ dev_info(hdmidev, "Unable to create infofrsend attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_hdcpeven))
+ dev_info(hdmidev, "Unable to create hdcpeven attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_hdcpchkaesotp))
+ dev_info(hdmidev, "Unable to create hdcpchkaesotp attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_hdcpfuseaes))
+ dev_info(hdmidev, "Unable to create hdcpfuseaes attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_hdcploadaes))
+ dev_info(hdmidev, "Unable to create hdcploadaes attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_hdcpauthencr))
+ dev_info(hdmidev, "Unable to create hdcpauthreq attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_hdcpstateget))
+ dev_info(hdmidev, "Unable to create hdcpstateget attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_evread))
+ dev_info(hdmidev, "Unable to create evread attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_evclr))
+ dev_info(hdmidev, "Unable to create evclr attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_audiocfg))
+ dev_info(hdmidev, "Unable to create audiocfg attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_plugstatus))
+ dev_info(hdmidev, "Unable to create plugstatus attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_poweronoff))
+ dev_info(hdmidev, "Unable to create poweronoff attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_evwakeup))
+ dev_info(hdmidev, "Unable to create evwakeup attribute\n");
+
+ /* Register event callback */
+ av8100_hdmi_event_cb_set(hdmi_event);
+
+hdmi_init_out:
+ return ret;
+}
+late_initcall(hdmi_init);
+
+void hdmi_exit(void)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+
+ /* Deregister event callback */
+ av8100_hdmi_event_cb_set(NULL);
+
+ device_remove_file(hdmidev, &dev_attr_storeastext);
+ device_remove_file(hdmidev, &dev_attr_plugdeten);
+ device_remove_file(hdmidev, &dev_attr_edidread);
+ device_remove_file(hdmidev, &dev_attr_ceceven);
+ device_remove_file(hdmidev, &dev_attr_cecread);
+ device_remove_file(hdmidev, &dev_attr_cecsend);
+ device_remove_file(hdmidev, &dev_attr_infofrsend);
+ device_remove_file(hdmidev, &dev_attr_hdcpeven);
+ device_remove_file(hdmidev, &dev_attr_hdcpchkaesotp);
+ device_remove_file(hdmidev, &dev_attr_hdcpfuseaes);
+ device_remove_file(hdmidev, &dev_attr_hdcploadaes);
+ device_remove_file(hdmidev, &dev_attr_hdcpauthencr);
+ device_remove_file(hdmidev, &dev_attr_hdcpstateget);
+ device_remove_file(hdmidev, &dev_attr_evread);
+ device_remove_file(hdmidev, &dev_attr_evclr);
+ device_remove_file(hdmidev, &dev_attr_audiocfg);
+ device_remove_file(hdmidev, &dev_attr_plugstatus);
+ device_remove_file(hdmidev, &dev_attr_poweronoff);
+ device_remove_file(hdmidev, &dev_attr_evwakeup);
+
+ hdmi_driver_data = dev_get_drvdata(hdmidev);
+ kfree(hdmi_driver_data);
+
+ misc_deregister(&hdmi_miscdev);
+}
diff --git a/drivers/video/av8100/hdmi_loc.h b/drivers/video/av8100/hdmi_loc.h
new file mode 100644
index 00000000000..5db30e7526b
--- /dev/null
+++ b/drivers/video/av8100/hdmi_loc.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * Author: Per Persson <per.xb.persson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+#ifndef __HDMI_LOC__H__
+#define __HDMI_LOC__H__
+
+#define EDID_BUF_LEN 128
+#define COMMAND_BUF_LEN 128
+#define AES_KEY_SIZE 16
+#define CRC32_SIZE 4
+#define AUTH_BUF_LEN 30
+
+struct edid_data {
+ u8 buf_len;
+ u8 buf[EDID_BUF_LEN];
+};
+
+struct authencr {
+ int result;
+ u8 buf_len;
+ u8 buf[AUTH_BUF_LEN];
+};
+
+struct hdmi_register {
+ unsigned char value;
+ unsigned char offset;
+};
+
+struct hdcp_loadaesone {
+ u8 key[AES_KEY_SIZE];
+ u8 result;
+ u8 crc32[CRC32_SIZE];
+};
+
+struct hdmi_driver_data {
+ bool store_as_hextext;
+ struct plug_detect plug_detect;
+ bool enable_cec_event;
+ struct edid_data edid_data;
+ struct cec_rw cec_read;
+ bool fuse_result;
+ int loadaes_result;
+ struct authencr authencr;
+};
+
+struct hdmi_command_register {
+ unsigned char cmd_id; /* input */
+ unsigned char buf_len; /* input, output */
+ unsigned char buf[COMMAND_BUF_LEN]; /* input, output */
+ unsigned char return_status; /* output */
+};
+
+/* Internal */
+#define IOC_HDMI_ENABLE_INTERRUPTS _IOWR(HDMI_IOC_MAGIC, 32, int)
+#define IOC_HDMI_DOWNLOAD_FW _IOWR(HDMI_IOC_MAGIC, 33, int)
+#define IOC_HDMI_ONOFF _IOWR(HDMI_IOC_MAGIC, 34, int)
+#define IOC_HDMI_REGISTER_WRITE _IOWR(HDMI_IOC_MAGIC, 35, int)
+#define IOC_HDMI_REGISTER_READ _IOWR(HDMI_IOC_MAGIC, 36, int)
+#define IOC_HDMI_STATUS_GET _IOWR(HDMI_IOC_MAGIC, 37, int)
+#define IOC_HDMI_CONFIGURATION_WRITE _IOWR(HDMI_IOC_MAGIC, 38, int)
+
+#endif /* __HDMI_LOC__H__ */
diff --git a/drivers/video/b2r2/Kconfig b/drivers/video/b2r2/Kconfig
new file mode 100644
index 00000000000..8cc81876de7
--- /dev/null
+++ b/drivers/video/b2r2/Kconfig
@@ -0,0 +1,134 @@
+config FB_B2R2
+ tristate "B2R2 engine support"
+ default n
+ help
+ B2R2 engine does various bit-blitting operations,post-processor operations
+ and various compositions.
+
+config B2R2_PLUG_CONF
+ bool "B2R2 bus plug configuration"
+ depends on FB_B2R2
+ default n
+ help
+ Configures how B2R2 access the memory bus. Enabling this will increase
+ the performance of B2R2 at the cost of using the bus more heavily.
+
+ If this is set to 'n', the hardware defaults will be used.
+
+choice
+ prompt "Opcode size"
+ depends on B2R2_PLUG_CONF
+ default B2R2_OPSIZE_64
+
+ config B2R2_OPSIZE_8
+ bool "8 bytes"
+ config B2R2_OPSIZE_16
+ bool "16 bytes"
+ config B2R2_OPSIZE_32
+ bool "32 bytes"
+ config B2R2_OPSIZE_64
+ bool "64 bytes"
+
+endchoice
+
+choice
+ prompt "Chunk size"
+ depends on B2R2_PLUG_CONF
+ default B2R2_CHSIZE_128
+
+ config B2R2_CHSIZE_1
+ bool "1 op"
+ config B2R2_CHSIZE_2
+ bool "2 ops"
+ config B2R2_CHSIZE_4
+ bool "4 ops"
+ config B2R2_CHSIZE_8
+ bool "8 ops"
+ config B2R2_CHSIZE_16
+ bool "16 ops"
+ config B2R2_CHSIZE_32
+ bool "32 ops"
+ config B2R2_CHSIZE_64
+ bool "64 ops"
+ config B2R2_CHSIZE_128
+ bool "128 ops"
+endchoice
+
+choice
+ prompt "Message size"
+ depends on B2R2_PLUG_CONF
+ default B2R2_MGSIZE_128
+
+ config B2R2_MGSIZE_1
+ bool "1 chunk"
+ config B2R2_MGSIZE_2
+ bool "2 chunks"
+ config B2R2_MGSIZE_4
+ bool "4 chunks"
+ config B2R2_MGSIZE_8
+ bool "8 s"
+ config B2R2_MGSIZE_16
+ bool "16 chunks"
+ config B2R2_MGSIZE_32
+ bool "32 chunks"
+ config B2R2_MGSIZE_64
+ bool "64 chunks"
+ config B2R2_MGSIZE_128
+ bool "128 chunks"
+endchoice
+
+choice
+ prompt "Page size"
+ depends on B2R2_PLUG_CONF
+ default B2R2_PGSIZE_256
+
+ config B2R2_PGSIZE_64
+ bool "64 bytes"
+ config B2R2_PGSIZE_128
+ bool "128 bytes"
+ config B2R2_PGSIZE_256
+ bool "256 bytes"
+endchoice
+
+config B2R2_DEBUG
+ bool "B2R2 debugging"
+ default n
+ depends on FB_B2R2
+ help
+ Enable debugging features for the B2R2 driver.
+
+config B2R2_PROFILER
+ tristate "B2R2 profiler"
+ default n
+ depends on FB_B2R2
+ help
+ Enables the profiler for the B2R2 driver.
+
+ It is recommended to build this as a module, since the configuration
+ of filters etc. is done at load time.
+
+config B2R2_GENERIC
+ bool "B2R2 generic path"
+ default y
+ depends on FB_B2R2
+ help
+ Enables support for the generic path in the B2R2 driver. This path should
+ be used when there is no optimized implementation for a request.
+
+choice
+ prompt "Generic usage mode"
+ depends on B2R2_GENERIC
+ default B2R2_GENERIC_FALLBACK
+
+ config B2R2_GENERIC_FALLBACK
+ bool "Fallback"
+ help
+ The optimized path will be used for all supported operations, and the
+ generic path will be used as a fallback for the ones not implemented.
+
+ config B2R2_GENERIC_ONLY
+ bool "Always"
+ help
+ The generic path will be used for all operations.
+
+endchoice
diff --git a/drivers/video/b2r2/Makefile b/drivers/video/b2r2/Makefile
new file mode 100644
index 00000000000..0150ad6f761
--- /dev/null
+++ b/drivers/video/b2r2/Makefile
@@ -0,0 +1,15 @@
+# Make file for compiling and loadable module B2R2
+
+obj-$(CONFIG_FB_B2R2) += b2r2.o
+
+b2r2-objs = b2r2_blt_main.o b2r2_core.o b2r2_mem_alloc.o b2r2_generic.o b2r2_node_gen.o b2r2_node_split.o b2r2_profiler_socket.o b2r2_timing.o b2r2_filters.o b2r2_utils.o b2r2_input_validation.o
+
+ifdef CONFIG_B2R2_DEBUG
+b2r2-objs += b2r2_debug.o
+endif
+
+ifeq ($(CONFIG_FB_B2R2),m)
+obj-y += b2r2_kernel_if.o
+endif
+
+obj-$(CONFIG_B2R2_PROFILER) += b2r2_profiler/
diff --git a/drivers/video/b2r2/b2r2_blt_main.c b/drivers/video/b2r2/b2r2_blt_main.c
new file mode 100644
index 00000000000..a2b21f4a2c1
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_blt_main.c
@@ -0,0 +1,3283 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 Blitter module
+ *
+ * Author: Robert Fekete <robert.fekete@stericsson.com>
+ * Author: Paul Wannback
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/list.h>
+#ifdef CONFIG_ANDROID_PMEM
+#include <linux/android_pmem.h>
+#endif
+#include <linux/fb.h>
+#include <linux/uaccess.h>
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
+#include <asm/cacheflush.h>
+#include <linux/smp.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/hwmem.h>
+
+#include "b2r2_internal.h"
+#include "b2r2_node_split.h"
+#include "b2r2_generic.h"
+#include "b2r2_mem_alloc.h"
+#include "b2r2_profiler_socket.h"
+#include "b2r2_timing.h"
+#include "b2r2_debug.h"
+#include "b2r2_utils.h"
+#include "b2r2_input_validation.h"
+
+#define B2R2_HEAP_SIZE (4 * PAGE_SIZE)
+#define MAX_TMP_BUF_SIZE (128 * PAGE_SIZE)
+
+/*
+ * TODO:
+ * Implementation of query cap
+ * Support for user space virtual pointer to physically consecutive memory
+ * Support for user space virtual pointer to physically scattered memory
+ * Callback reads lagging behind in blt_api_stress app
+ * Store smaller items in the report list instead of the whole request
+ * Support read of many report records at once.
+ */
+
+/**
+ * b2r2_blt_dev - Our device, /dev/b2r2_blt
+ */
+static struct miscdevice *b2r2_blt_dev;
+
+static struct {
+ struct b2r2_work_buf buf;
+ bool in_use;
+} tmp_bufs[MAX_TMP_BUFS_NEEDED];
+
+/* Statistics */
+
+/**
+ * stat_lock - Spin lock protecting the statistics
+ */
+static struct mutex stat_lock;
+/**
+ * stat_n_jobs_added - Number of jobs added to b2r2_core
+ */
+static unsigned long stat_n_jobs_added;
+/**
+ * stat_n_jobs_released - Number of jobs released (job_release called)
+ */
+static unsigned long stat_n_jobs_released;
+/**
+ * stat_n_jobs_in_report_list - Number of jobs currently in the report list
+ */
+static unsigned long stat_n_jobs_in_report_list;
+/**
+ * stat_n_in_blt - Number of client threads currently exec inside b2r2_blt()
+ */
+static unsigned long stat_n_in_blt;
+/**
+ * stat_n_in_blt_synch - Nunmber of client threads currently waiting for synch
+ */
+static unsigned long stat_n_in_blt_synch;
+/**
+ * stat_n_in_blt_add - Number of client threads currenlty adding in b2r2_blt
+ */
+static unsigned long stat_n_in_blt_add;
+/**
+ * stat_n_in_blt_wait - Number of client threads currently waiting in b2r2_blt
+ */
+static unsigned long stat_n_in_blt_wait;
+/**
+ * stat_n_in_sync_0 - Number of client threads currently in b2r2_blt_sync
+ * waiting for all client jobs to finish
+ */
+static unsigned long stat_n_in_synch_0;
+/**
+ * stat_n_in_sync_job - Number of client threads currently in b2r2_blt_sync
+ * waiting specific job to finish
+ */
+static unsigned long stat_n_in_synch_job;
+/**
+ * stat_n_in_query_cap - Number of clients currently in query cap
+ */
+static unsigned long stat_n_in_query_cap;
+/**
+ * stat_n_in_open - Number of clients currently in b2r2_blt_open
+ */
+static unsigned long stat_n_in_open;
+/**
+ * stat_n_in_release - Number of clients currently in b2r2_blt_release
+ */
+static unsigned long stat_n_in_release;
+
+/* Debug file system support */
+#ifdef CONFIG_DEBUG_FS
+/**
+ * debugfs_latest_request - Copy of the latest request issued
+ */
+struct b2r2_blt_request debugfs_latest_request;
+/**
+ * debugfs_root_dir - The debugfs root directory, i.e. /debugfs/b2r2
+ */
+static struct dentry *debugfs_root_dir;
+
+static int sprintf_req(struct b2r2_blt_request *request, char *buf, int size);
+#endif
+
+/* Local functions */
+static void inc_stat(unsigned long *stat);
+static void dec_stat(unsigned long *stat);
+static int b2r2_blt_synch(struct b2r2_blt_instance *instance,
+ int request_id);
+static int b2r2_blt_query_cap(struct b2r2_blt_instance *instance,
+ struct b2r2_blt_query_cap *query_cap);
+
+#ifndef CONFIG_B2R2_GENERIC_ONLY
+static int b2r2_blt(struct b2r2_blt_instance *instance,
+ struct b2r2_blt_request *request);
+
+static void job_callback(struct b2r2_core_job *job);
+static void job_release(struct b2r2_core_job *job);
+static int job_acquire_resources(struct b2r2_core_job *job, bool atomic);
+static void job_release_resources(struct b2r2_core_job *job, bool atomic);
+#endif
+
+#ifdef CONFIG_B2R2_GENERIC
+static int b2r2_generic_blt(struct b2r2_blt_instance *instance,
+ struct b2r2_blt_request *request);
+
+static void job_callback_gen(struct b2r2_core_job *job);
+static void job_release_gen(struct b2r2_core_job *job);
+static int job_acquire_resources_gen(struct b2r2_core_job *job, bool atomic);
+static void job_release_resources_gen(struct b2r2_core_job *job, bool atomic);
+static void tile_job_callback_gen(struct b2r2_core_job *job);
+static void tile_job_release_gen(struct b2r2_core_job *job);
+#endif
+
+
+static int resolve_buf(struct b2r2_blt_img *img,
+ struct b2r2_blt_rect *rect_2b_used,
+ bool is_dst,
+ struct b2r2_resolved_buf *resolved);
+static void unresolve_buf(struct b2r2_blt_buf *buf,
+ struct b2r2_resolved_buf *resolved);
+static void sync_buf(struct b2r2_blt_img *img,
+ struct b2r2_resolved_buf *resolved,
+ bool is_dst,
+ struct b2r2_blt_rect *rect);
+static bool is_report_list_empty(struct b2r2_blt_instance *instance);
+static bool is_synching(struct b2r2_blt_instance *instance);
+static void get_actual_dst_rect(struct b2r2_blt_req *req,
+ struct b2r2_blt_rect *actual_dst_rect);
+static void set_up_hwmem_region(struct b2r2_blt_img *img,
+ struct b2r2_blt_rect *rect, struct hwmem_region *region);
+static int resolve_hwmem(struct b2r2_blt_img *img,
+ struct b2r2_blt_rect *rect_2b_used, bool is_dst,
+ struct b2r2_resolved_buf *resolved_buf);
+static void unresolve_hwmem(struct b2r2_resolved_buf *resolved_buf);
+
+/**
+ * struct sync_args - Data for clean/flush
+ *
+ * @start: Virtual start address
+ * @end: Virtual end address
+ */
+struct sync_args {
+ unsigned long start;
+ unsigned long end;
+};
+/**
+ * flush_l1_cache_range_curr_cpu() - Cleans and invalidates L1 cache on the current CPU
+ *
+ * @arg: Pointer to sync_args structure
+ */
+static inline void flush_l1_cache_range_curr_cpu(void *arg)
+{
+ struct sync_args *sa = (struct sync_args *)arg;
+
+ dmac_flush_range((void *)sa->start, (void *)sa->end);
+}
+
+#ifdef CONFIG_SMP
+/**
+ * inv_l1_cache_range_all_cpus() - Cleans and invalidates L1 cache on all CPU:s
+ *
+ * @sa: Pointer to sync_args structure
+ */
+static void flush_l1_cache_range_all_cpus(struct sync_args *sa)
+{
+ on_each_cpu(flush_l1_cache_range_curr_cpu, sa, 1);
+}
+#endif
+
+/**
+ * clean_l1_cache_range_curr_cpu() - Cleans L1 cache on current CPU
+ *
+ * Ensures that data is written out from the CPU:s L1 cache,
+ * it will still be in the cache.
+ *
+ * @arg: Pointer to sync_args structure
+ */
+static inline void clean_l1_cache_range_curr_cpu(void *arg)
+{
+ struct sync_args *sa = (struct sync_args *)arg;
+
+ dmac_map_area((void *)sa->start,
+ (void *)sa->end - (void *)sa->start,
+ DMA_TO_DEVICE);
+}
+
+#ifdef CONFIG_SMP
+/**
+ * clean_l1_cache_range_all_cpus() - Cleans L1 cache on all CPU:s
+ *
+ * Ensures that data is written out from all CPU:s L1 cache,
+ * it will still be in the cache.
+ *
+ * @sa: Pointer to sync_args structure
+ */
+static void clean_l1_cache_range_all_cpus(struct sync_args *sa)
+{
+ on_each_cpu(clean_l1_cache_range_curr_cpu, sa, 1);
+}
+#endif
+
+/**
+ * b2r2_blt_open - Implements file open on the b2r2_blt device
+ *
+ * @inode: File system inode
+ * @filp: File pointer
+ *
+ * A B2R2 BLT instance is created and stored in the file structure.
+ */
+static int b2r2_blt_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+ struct b2r2_blt_instance *instance;
+
+ b2r2_log_info("%s\n", __func__);
+
+ inc_stat(&stat_n_in_open);
+
+ /* Allocate and initialize the instance */
+ instance = (struct b2r2_blt_instance *)
+ kmalloc(sizeof(*instance), GFP_KERNEL);
+ if (!instance) {
+ b2r2_log_err("%s: Failed to alloc\n", __func__);
+ goto instance_alloc_failed;
+ }
+ memset(instance, 0, sizeof(*instance));
+ INIT_LIST_HEAD(&instance->report_list);
+ mutex_init(&instance->lock);
+ init_waitqueue_head(&instance->report_list_waitq);
+ init_waitqueue_head(&instance->synch_done_waitq);
+
+ /*
+ * Remember the instance so that we can retrieve it in
+ * other functions
+ */
+ filp->private_data = instance;
+ goto out;
+
+instance_alloc_failed:
+out:
+ dec_stat(&stat_n_in_open);
+
+ return ret;
+}
+
+/**
+ * b2r2_blt_release - Implements last close on an instance of
+ * the b2r2_blt device
+ *
+ * @inode: File system inode
+ * @filp: File pointer
+ *
+ * All active jobs are finished or cancelled and allocated data
+ * is released.
+ */
+static int b2r2_blt_release(struct inode *inode, struct file *filp)
+{
+ int ret;
+ struct b2r2_blt_instance *instance;
+
+ b2r2_log_info("%s\n", __func__);
+
+ inc_stat(&stat_n_in_release);
+
+ instance = (struct b2r2_blt_instance *) filp->private_data;
+
+ /* Finish all outstanding requests */
+ ret = b2r2_blt_synch(instance, 0);
+ if (ret < 0)
+ b2r2_log_warn(
+ "%s: b2r2_blt_sync failed with %d\n", __func__, ret);
+
+ /* Now cancel any remaining outstanding request */
+ if (instance->no_of_active_requests) {
+ struct b2r2_core_job *job;
+
+ b2r2_log_warn("%s: %d active requests\n",
+ __func__, instance->no_of_active_requests);
+
+ /* Find and cancel all jobs belonging to us */
+ job = b2r2_core_job_find_first_with_tag((int) instance);
+ while (job) {
+ b2r2_core_job_cancel(job);
+ /* This release matches addref in b2r2_core_job_find... */
+ b2r2_core_job_release(job, __func__);
+ job = b2r2_core_job_find_first_with_tag((int) instance);
+ }
+
+ b2r2_log_warn(
+ "%s: %d active requests after cancel\n",
+ __func__, instance->no_of_active_requests);
+ }
+
+ /* Release jobs in report list */
+ mutex_lock(&instance->lock);
+ while (!list_empty(&instance->report_list)) {
+ struct b2r2_blt_request *request = list_first_entry(
+ &instance->report_list,
+ struct b2r2_blt_request,
+ list);
+ list_del_init(&request->list);
+ mutex_unlock(&instance->lock);
+ /*
+ * This release matches the addref when the job was put into
+ * the report list
+ */
+ b2r2_core_job_release(&request->job, __func__);
+ mutex_lock(&instance->lock);
+ }
+ mutex_unlock(&instance->lock);
+
+ /* Release our instance */
+ kfree(instance);
+
+ dec_stat(&stat_n_in_release);
+
+ return 0;
+}
+
+/**
+ * b2r2_blt_ioctl - This routine implements b2r2_blt ioctl interface
+ *
+ * @file: file pointer.
+ * @cmd :ioctl command.
+ * @arg: input argument for ioctl.
+ *
+ * Returns 0 if OK else negative error code
+ */
+static long b2r2_blt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ struct b2r2_blt_instance *instance;
+
+ /** Process actual ioctl */
+
+ b2r2_log_info("%s\n", __func__);
+
+ /* Get the instance from the file structure */
+ instance = (struct b2r2_blt_instance *) file->private_data;
+
+ switch (cmd) {
+ case B2R2_BLT_IOC: {
+ /* This is the "blit" command */
+
+ /* arg is user pointer to struct b2r2_blt_request */
+ struct b2r2_blt_request *request =
+ kmalloc(sizeof(*request), GFP_KERNEL);
+ if (!request) {
+ b2r2_log_err("%s: Failed to alloc mem\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /* Initialize the structure */
+ memset(request, 0, sizeof(*request));
+ INIT_LIST_HEAD(&request->list);
+ request->instance = instance;
+
+ /*
+ * The user request is a sub structure of the
+ * kernel request structure.
+ */
+
+ /* Get the user data */
+ if (copy_from_user(&request->user_req, (void *)arg,
+ sizeof(request->user_req))) {
+ b2r2_log_err(
+ "%s: copy_from_user failed\n",
+ __func__);
+ kfree(request);
+ return -EFAULT;
+ }
+
+ if (!b2r2_validate_user_req(&request->user_req)) {
+ kfree(request);
+ return -EINVAL;
+ }
+
+ request->profile = is_profiler_registered_approx();
+
+ /*
+ * If the user specified a color look-up table,
+ * make a copy that the HW can use.
+ */
+ if ((request->user_req.flags &
+ B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) != 0) {
+ request->clut = dma_alloc_coherent(b2r2_blt_device(),
+ CLUT_SIZE, &(request->clut_phys_addr),
+ GFP_DMA | GFP_KERNEL);
+ if (request->clut == NULL) {
+ b2r2_log_err("%s CLUT allocation failed.\n",
+ __func__);
+ kfree(request);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(request->clut,
+ request->user_req.clut, CLUT_SIZE)) {
+ b2r2_log_err("%s: CLUT copy_from_user failed\n",
+ __func__);
+ dma_free_coherent(b2r2_blt_device(), CLUT_SIZE,
+ request->clut, request->clut_phys_addr);
+ request->clut = NULL;
+ request->clut_phys_addr = 0;
+ kfree(request);
+ return -EFAULT;
+ }
+ }
+
+ /* Perform the blit */
+
+#ifdef CONFIG_B2R2_GENERIC_ONLY
+ /* Use the generic path for all operations */
+ ret = b2r2_generic_blt(instance, request);
+#else
+ /* Use the optimized path */
+ ret = b2r2_blt(instance, request);
+#endif
+
+#ifdef CONFIG_B2R2_GENERIC_FALLBACK
+ /* Fall back to generic path if operation was not supported */
+ if (ret == -ENOSYS) {
+ struct b2r2_blt_request *request_gen;
+ b2r2_log_info("b2r2_blt=%d Going generic.\n", ret);
+ request_gen = kmalloc(sizeof(*request_gen), GFP_KERNEL);
+ if (!request_gen) {
+ b2r2_log_err(
+ "%s: Failed to alloc mem for request_gen\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* Initialize the structure */
+ memset(request_gen, 0, sizeof(*request_gen));
+ INIT_LIST_HEAD(&request_gen->list);
+ request_gen->instance = instance;
+
+ /*
+ * The user request is a sub structure of the
+ * kernel request structure.
+ */
+
+ /* Get the user data */
+ if (copy_from_user(&request_gen->user_req, (void *)arg,
+ sizeof(request_gen->user_req))) {
+ b2r2_log_err(
+ "%s: copy_from_user failed\n",
+ __func__);
+ kfree(request_gen);
+ return -EFAULT;
+ }
+
+ /*
+ * If the user specified a color look-up table,
+ * make a copy that the HW can use.
+ */
+ if ((request_gen->user_req.flags &
+ B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) != 0) {
+ request_gen->clut = dma_alloc_coherent(b2r2_blt_device(),
+ CLUT_SIZE, &(request_gen->clut_phys_addr),
+ GFP_DMA | GFP_KERNEL);
+ if (request_gen->clut == NULL) {
+ b2r2_log_err("%s CLUT allocation failed.\n", __func__);
+ kfree(request_gen);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(request_gen->clut, request_gen->user_req.clut, CLUT_SIZE)) {
+ b2r2_log_err("%s: CLUT copy_from_user failed\n", __func__);
+ dma_free_coherent(b2r2_blt_device(), CLUT_SIZE, request_gen->clut,
+ request_gen->clut_phys_addr);
+ request_gen->clut = NULL;
+ request_gen->clut_phys_addr = 0;
+ kfree(request_gen);
+ return -EFAULT;
+ }
+ }
+
+ request_gen->profile = is_profiler_registered_approx();
+
+ ret = b2r2_generic_blt(instance, request_gen);
+ b2r2_log_info("\nb2r2_generic_blt=%d Generic done.\n", ret);
+ }
+#endif /* CONFIG_B2R2_GENERIC_FALLBACK */
+
+ break;
+ }
+
+ case B2R2_BLT_SYNCH_IOC:
+ /* This is the "synch" command */
+
+ /* arg is request_id */
+ ret = b2r2_blt_synch(instance, (int) arg);
+ break;
+
+ case B2R2_BLT_QUERY_CAP_IOC:
+ {
+ /* This is the "query capabilities" command */
+
+ /* Arg is struct b2r2_blt_query_cap */
+ struct b2r2_blt_query_cap query_cap;
+
+ /* Get the user data */
+ if (copy_from_user(&query_cap, (void *)arg,
+ sizeof(query_cap))) {
+ b2r2_log_err(
+ "%s: copy_from_user failed\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ /* Fill in our capabilities */
+ ret = b2r2_blt_query_cap(instance, &query_cap);
+
+ /* Return data to user */
+ if (copy_to_user((void *)arg, &query_cap,
+ sizeof(query_cap))) {
+ b2r2_log_err("%s: copy_to_user failed\n",
+ __func__);
+ return -EFAULT;
+ }
+ break;
+ }
+
+ default:
+ /* Unknown command */
+ b2r2_log_err(
+ "%s: Unknown cmd %d\n", __func__, cmd);
+ ret = -EINVAL;
+ break;
+
+ }
+
+ if (ret < 0)
+ b2r2_log_err("EC %d OK!\n", -ret);
+
+ return ret;
+}
+
+/**
+ * b2r2_blt_poll - Support for user-space poll, select & epoll.
+ * Used for user-space callback
+ *
+ * @filp: File to poll on
+ * @wait: Poll table to wait on
+ *
+ * This function checks if there are anything to read
+ */
+static unsigned b2r2_blt_poll(struct file *filp, poll_table *wait)
+{
+ struct b2r2_blt_instance *instance;
+ unsigned int mask = 0;
+
+ b2r2_log_info("%s\n", __func__);
+
+ /* Get the instance from the file structure */
+ instance = (struct b2r2_blt_instance *) filp->private_data;
+
+ poll_wait(filp, &instance->report_list_waitq, wait);
+ mutex_lock(&instance->lock);
+ if (!list_empty(&instance->report_list))
+ mask |= POLLIN | POLLRDNORM;
+ mutex_unlock(&instance->lock);
+
+ return mask;
+}
+
+/**
+ * b2r2_blt_read - Read report data, user for user-space callback
+ *
+ * @filp: File pointer
+ * @buf: User space buffer
+ * @count: Number of bytes to read
+ * @f_pos: File position
+ *
+ * Returns number of bytes read or negative error code
+ */
+static ssize_t b2r2_blt_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *f_pos)
+{
+ int ret = 0;
+ struct b2r2_blt_instance *instance;
+ struct b2r2_blt_request *request;
+ struct b2r2_blt_report report;
+
+ b2r2_log_info("%s\n", __func__);
+
+ /* Get the instance from the file structure */
+ instance = (struct b2r2_blt_instance *) filp->private_data;
+
+ /*
+ * We return only complete report records, one at a time.
+ * Might be more efficient to support read of many.
+ */
+ count = (count / sizeof(struct b2r2_blt_report)) *
+ sizeof(struct b2r2_blt_report);
+ if (count > sizeof(struct b2r2_blt_report))
+ count = sizeof(struct b2r2_blt_report);
+ if (count == 0)
+ return count;
+
+ /*
+ * Loop and wait here until we have anything to return or
+ * until interrupted
+ */
+ mutex_lock(&instance->lock);
+ while (list_empty(&instance->report_list)) {
+ mutex_unlock(&instance->lock);
+
+ /* Return if non blocking read */
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ b2r2_log_info("%s - Going to sleep\n", __func__);
+ if (wait_event_interruptible(
+ instance->report_list_waitq,
+ !is_report_list_empty(instance)))
+ /* signal: tell the fs layer to handle it */
+ return -ERESTARTSYS;
+
+ /* Otherwise loop, but first reaquire the lock */
+ mutex_lock(&instance->lock);
+ }
+
+ /* Ok, we have something to return */
+
+ /* Return */
+ request = NULL;
+ if (!list_empty(&instance->report_list))
+ request = list_first_entry(
+ &instance->report_list, struct b2r2_blt_request, list);
+
+ if (request) {
+ /* Remove from list to avoid reading twice */
+ list_del_init(&request->list);
+
+ report.request_id = request->request_id;
+ report.report1 = request->user_req.report1;
+ report.report2 = request->user_req.report2;
+ report.usec_elapsed = 0; /* TBD */
+
+ mutex_unlock(&instance->lock);
+ if (copy_to_user(buf,
+ &report,
+ sizeof(report)))
+ ret = -EFAULT;
+ mutex_lock(&instance->lock);
+
+ if (ret) {
+ /* copy to user failed, re-insert into list */
+ list_add(&request->list,
+ &request->instance->report_list);
+ request = NULL;
+ }
+ }
+ mutex_unlock(&instance->lock);
+
+ if (request)
+ /*
+ * Release matching the addref when the job was put into
+ * the report list
+ */
+ b2r2_core_job_release(&request->job, __func__);
+
+ return count;
+}
+
+/**
+ * b2r2_blt_fops - File operations for b2r2_blt
+ */
+static const struct file_operations b2r2_blt_fops = {
+ .owner = THIS_MODULE,
+ .open = b2r2_blt_open,
+ .release = b2r2_blt_release,
+ .unlocked_ioctl = b2r2_blt_ioctl,
+ .poll = b2r2_blt_poll,
+ .read = b2r2_blt_read,
+};
+
+/**
+ * b2r2_blt_misc_dev - Misc device config for b2r2_blt
+ */
+static struct miscdevice b2r2_blt_misc_dev = {
+ MISC_DYNAMIC_MINOR,
+ "b2r2_blt",
+ &b2r2_blt_fops
+};
+
+
+#ifndef CONFIG_B2R2_GENERIC_ONLY
+/**
+ * b2r2_blt - Implementation of the B2R2 blit request
+ *
+ * @instance: The B2R2 BLT instance
+ * @request; The request to perform
+ */
+static int b2r2_blt(struct b2r2_blt_instance *instance,
+ struct b2r2_blt_request *request)
+{
+ int ret = 0;
+ struct b2r2_blt_rect actual_dst_rect;
+ int request_id = 0;
+ struct b2r2_node *last_node = request->first_node;
+ int node_count;
+
+ u32 thread_runtime_at_start = 0;
+
+ if (request->profile) {
+ request->start_time_nsec = b2r2_get_curr_nsec();
+ thread_runtime_at_start = (u32)task_sched_runtime(current);
+ }
+
+ b2r2_log_info("%s\n", __func__);
+
+ inc_stat(&stat_n_in_blt);
+
+ /* Debug prints of incoming request */
+ b2r2_log_info(
+ "src.fmt=%#010x src.buf={%d,%d,%d} "
+ "src.w,h={%d,%d} src.rect={%d,%d,%d,%d}\n",
+ request->user_req.src_img.fmt,
+ request->user_req.src_img.buf.type,
+ request->user_req.src_img.buf.fd,
+ request->user_req.src_img.buf.offset,
+ request->user_req.src_img.width,
+ request->user_req.src_img.height,
+ request->user_req.src_rect.x,
+ request->user_req.src_rect.y,
+ request->user_req.src_rect.width,
+ request->user_req.src_rect.height);
+ b2r2_log_info(
+ "dst.fmt=%#010x dst.buf={%d,%d,%d} "
+ "dst.w,h={%d,%d} dst.rect={%d,%d,%d,%d}\n",
+ request->user_req.dst_img.fmt,
+ request->user_req.dst_img.buf.type,
+ request->user_req.dst_img.buf.fd,
+ request->user_req.dst_img.buf.offset,
+ request->user_req.dst_img.width,
+ request->user_req.dst_img.height,
+ request->user_req.dst_rect.x,
+ request->user_req.dst_rect.y,
+ request->user_req.dst_rect.width,
+ request->user_req.dst_rect.height);
+
+ inc_stat(&stat_n_in_blt_synch);
+
+ /* Wait here if synch is ongoing */
+ ret = wait_event_interruptible(instance->synch_done_waitq,
+ !is_synching(instance));
+ if (ret) {
+ b2r2_log_warn(
+ "%s: Sync wait interrupted, %d\n",
+ __func__, ret);
+ ret = -EAGAIN;
+ dec_stat(&stat_n_in_blt_synch);
+ goto synch_interrupted;
+ }
+
+ dec_stat(&stat_n_in_blt_synch);
+
+ /* Resolve the buffers */
+
+ /* Source buffer */
+ ret = resolve_buf(&request->user_req.src_img,
+ &request->user_req.src_rect, false, &request->src_resolved);
+ if (ret < 0) {
+ b2r2_log_warn(
+ "%s: Resolve src buf failed, %d\n",
+ __func__, ret);
+ ret = -EAGAIN;
+ goto resolve_src_buf_failed;
+ }
+
+ /* Source mask buffer */
+ ret = resolve_buf(&request->user_req.src_mask,
+ &request->user_req.src_rect, false,
+ &request->src_mask_resolved);
+ if (ret < 0) {
+ b2r2_log_warn(
+ "%s: Resolve src mask buf failed, %d\n",
+ __func__, ret);
+ ret = -EAGAIN;
+ goto resolve_src_mask_buf_failed;
+ }
+
+ /* Destination buffer */
+ get_actual_dst_rect(&request->user_req, &actual_dst_rect);
+ ret = resolve_buf(&request->user_req.dst_img, &actual_dst_rect,
+ true, &request->dst_resolved);
+ if (ret < 0) {
+ b2r2_log_warn(
+ "%s: Resolve dst buf failed, %d\n",
+ __func__, ret);
+ ret = -EAGAIN;
+ goto resolve_dst_buf_failed;
+ }
+
+ /* Debug prints of resolved buffers */
+ b2r2_log_info("src.rbuf={%X,%p,%d} {%p,%X,%X,%d}\n",
+ request->src_resolved.physical_address,
+ request->src_resolved.virtual_address,
+ request->src_resolved.is_pmem,
+ request->src_resolved.filep,
+ request->src_resolved.file_physical_start,
+ request->src_resolved.file_virtual_start,
+ request->src_resolved.file_len);
+
+ b2r2_log_info("dst.rbuf={%X,%p,%d} {%p,%X,%X,%d}\n",
+ request->dst_resolved.physical_address,
+ request->dst_resolved.virtual_address,
+ request->dst_resolved.is_pmem,
+ request->dst_resolved.filep,
+ request->dst_resolved.file_physical_start,
+ request->dst_resolved.file_virtual_start,
+ request->dst_resolved.file_len);
+
+ /* Calculate the number of nodes (and resources) needed for this job */
+ ret = b2r2_node_split_analyze(request, MAX_TMP_BUF_SIZE,
+ &node_count, &request->bufs, &request->buf_count,
+ &request->node_split_job);
+ if (ret == -ENOSYS) {
+ /* There was no optimized path for this request */
+ b2r2_log_info(
+ "%s: No optimized path for request\n", __func__);
+ goto no_optimized_path;
+
+ } else if (ret < 0) {
+ b2r2_log_warn(
+ "%s: Failed to analyze request, ret = %d\n",
+ __func__, ret);
+#ifdef CONFIG_DEBUG_FS
+ {
+ /* Failed, dump job to dmesg */
+ char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL);
+
+ b2r2_log_info(
+ "%s: Analyze failed for:\n", __func__);
+ if (Buf != NULL) {
+ sprintf_req(request, Buf, sizeof(char) * 4096);
+ b2r2_log_info("%s", Buf);
+ kfree(Buf);
+ } else {
+ b2r2_log_info("Unable to print the request. "
+ "Message buffer allocation failed.\n");
+ }
+ }
+#endif
+ goto generate_nodes_failed;
+ }
+
+ /* Allocate the nodes needed */
+#ifdef B2R2_USE_NODE_GEN
+ request->first_node = b2r2_blt_alloc_nodes(node_count);
+ if (request->first_node == NULL) {
+ b2r2_log_warn(
+ "%s: Failed to allocate nodes, ret = %d\n",
+ __func__, ret);
+ goto generate_nodes_failed;
+ }
+#else
+ ret = b2r2_node_alloc(node_count, &(request->first_node));
+ if (ret < 0 || request->first_node == NULL) {
+ b2r2_log_warn(
+ "%s: Failed to allocate nodes, ret = %d\n",
+ __func__, ret);
+ goto generate_nodes_failed;
+ }
+#endif
+
+ /* Build the B2R2 node list */
+ ret = b2r2_node_split_configure(&request->node_split_job,
+ request->first_node);
+
+ if (ret < 0) {
+ b2r2_log_warn(
+ "%s: Failed to perform node split, ret = %d\n",
+ __func__, ret);
+ goto generate_nodes_failed;
+ }
+
+ /* Exit here if dry run */
+ if (request->user_req.flags & B2R2_BLT_FLAG_DRY_RUN)
+ goto exit_dry_run;
+
+ /* Configure the request */
+ last_node = request->first_node;
+ while (last_node && last_node->next)
+ last_node = last_node->next;
+
+ request->job.tag = (int) instance;
+ request->job.prio = request->user_req.prio;
+ request->job.first_node_address =
+ request->first_node->physical_address;
+ request->job.last_node_address =
+ last_node->physical_address;
+ request->job.callback = job_callback;
+ request->job.release = job_release;
+ request->job.acquire_resources = job_acquire_resources;
+ request->job.release_resources = job_release_resources;
+
+ /* Synchronize memory occupied by the buffers */
+
+ /* Source buffer */
+ if (!(request->user_req.flags &
+ B2R2_BLT_FLAG_SRC_NO_CACHE_FLUSH) &&
+ (request->user_req.src_img.buf.type !=
+ B2R2_BLT_PTR_PHYSICAL) &&
+ !b2r2_is_mb_fmt(request->user_req.src_img.fmt))
+ /* MB formats are never touched by SW */
+ sync_buf(&request->user_req.src_img,
+ &request->src_resolved,
+ false, /*is_dst*/
+ &request->user_req.src_rect);
+
+ /* Source mask buffer */
+ if (!(request->user_req.flags &
+ B2R2_BLT_FLAG_SRC_MASK_NO_CACHE_FLUSH) &&
+ (request->user_req.src_mask.buf.type !=
+ B2R2_BLT_PTR_PHYSICAL) &&
+ !b2r2_is_mb_fmt(request->user_req.src_mask.fmt))
+ /* MB formats are never touched by SW */
+ sync_buf(&request->user_req.src_mask,
+ &request->src_mask_resolved,
+ false, /*is_dst*/
+ NULL);
+
+ /* Destination buffer */
+ if (!(request->user_req.flags &
+ B2R2_BLT_FLAG_DST_NO_CACHE_FLUSH) &&
+ (request->user_req.dst_img.buf.type !=
+ B2R2_BLT_PTR_PHYSICAL) &&
+ !b2r2_is_mb_fmt(request->user_req.dst_img.fmt))
+ /* MB formats are never touched by SW */
+ sync_buf(&request->user_req.dst_img,
+ &request->dst_resolved,
+ true, /*is_dst*/
+ &request->user_req.dst_rect);
+
+#ifdef CONFIG_DEBUG_FS
+ /* Remember latest request for debugfs */
+ debugfs_latest_request = *request;
+#endif
+
+ /* Submit the job */
+ b2r2_log_info("%s: Submitting job\n", __func__);
+
+ inc_stat(&stat_n_in_blt_add);
+
+ if (request->profile)
+ request->nsec_active_in_cpu =
+ (s32)((u32)task_sched_runtime(current) -
+ thread_runtime_at_start);
+
+ mutex_lock(&instance->lock);
+
+ /* Add the job to b2r2_core */
+ request_id = b2r2_core_job_add(&request->job);
+ request->request_id = request_id;
+
+ dec_stat(&stat_n_in_blt_add);
+
+ if (request_id < 0) {
+ b2r2_log_warn("%s: Failed to add job, ret = %d\n",
+ __func__, request_id);
+ ret = request_id;
+ mutex_unlock(&instance->lock);
+ goto job_add_failed;
+ }
+
+ inc_stat(&stat_n_jobs_added);
+
+ instance->no_of_active_requests++;
+ mutex_unlock(&instance->lock);
+
+ /* Wait for the job to be done if synchronous */
+ if ((request->user_req.flags & B2R2_BLT_FLAG_ASYNCH) == 0) {
+ b2r2_log_info("%s: Synchronous, waiting\n",
+ __func__);
+
+ inc_stat(&stat_n_in_blt_wait);
+
+ ret = b2r2_core_job_wait(&request->job);
+
+ dec_stat(&stat_n_in_blt_wait);
+
+ if (ret < 0 && ret != -ENOENT)
+ b2r2_log_warn(
+ "%s: Failed to wait job, ret = %d\n",
+ __func__, ret);
+ else
+ b2r2_log_info(
+ "%s: Synchronous wait done\n", __func__);
+ ret = 0;
+ }
+
+ /*
+ * Release matching the addref in b2r2_core_job_add,
+ * the request must not be accessed after this call
+ */
+ b2r2_core_job_release(&request->job, __func__);
+
+ dec_stat(&stat_n_in_blt);
+
+ return ret >= 0 ? request_id : ret;
+
+job_add_failed:
+exit_dry_run:
+no_optimized_path:
+generate_nodes_failed:
+ unresolve_buf(&request->user_req.dst_img.buf,
+ &request->dst_resolved);
+resolve_dst_buf_failed:
+ unresolve_buf(&request->user_req.src_mask.buf,
+ &request->src_mask_resolved);
+resolve_src_mask_buf_failed:
+ unresolve_buf(&request->user_req.src_img.buf,
+ &request->src_resolved);
+resolve_src_buf_failed:
+synch_interrupted:
+ job_release(&request->job);
+ dec_stat(&stat_n_jobs_released);
+ if ((request->user_req.flags & B2R2_BLT_FLAG_DRY_RUN) == 0 || ret)
+ b2r2_log_warn(
+ "%s returns with error %d\n", __func__, ret);
+
+ dec_stat(&stat_n_in_blt);
+
+ return ret;
+}
+
+/**
+ * Called when job is done or cancelled
+ *
+ * @job: The job
+ */
+static void job_callback(struct b2r2_core_job *job)
+{
+ struct b2r2_blt_request *request =
+ container_of(job, struct b2r2_blt_request, job);
+
+ if (b2r2_blt_device())
+ b2r2_log_info("%s\n", __func__);
+
+ /* Local addref / release within this func */
+ b2r2_core_job_addref(job, __func__);
+
+ /* Unresolve the buffers */
+ unresolve_buf(&request->user_req.src_img.buf,
+ &request->src_resolved);
+ unresolve_buf(&request->user_req.src_mask.buf,
+ &request->src_mask_resolved);
+ unresolve_buf(&request->user_req.dst_img.buf,
+ &request->dst_resolved);
+
+ /* Move to report list if the job shall be reported */
+ /* FIXME: Use a smaller struct? */
+ mutex_lock(&request->instance->lock);
+ if (request->user_req.flags & B2R2_BLT_FLAG_REPORT_WHEN_DONE) {
+ /* Move job to report list */
+ list_add_tail(&request->list,
+ &request->instance->report_list);
+ inc_stat(&stat_n_jobs_in_report_list);
+
+ /* Wake up poll */
+ wake_up_interruptible(
+ &request->instance->report_list_waitq);
+
+ /* Add a reference because we put the job in the report list */
+ b2r2_core_job_addref(job, __func__);
+ }
+
+ /*
+ * Decrease number of active requests and wake up
+ * synching threads if active requests reaches zero
+ */
+ BUG_ON(request->instance->no_of_active_requests == 0);
+ request->instance->no_of_active_requests--;
+ if (request->instance->synching &&
+ request->instance->no_of_active_requests == 0) {
+ request->instance->synching = false;
+ /* Wake up all syncing */
+
+ wake_up_interruptible_all(
+ &request->instance->synch_done_waitq);
+ }
+ mutex_unlock(&request->instance->lock);
+
+#ifdef CONFIG_DEBUG_FS
+ /* Dump job if cancelled */
+ if (job->job_state == B2R2_CORE_JOB_CANCELED) {
+ char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL);
+
+ b2r2_log_info("%s: Job cancelled:\n", __func__);
+ if (Buf != NULL) {
+ sprintf_req(request, Buf, sizeof(char) * 4096);
+ b2r2_log_info("%s", Buf);
+ kfree(Buf);
+ } else {
+ b2r2_log_info("Unable to print the request. "
+ "Message buffer allocation failed.\n");
+ }
+ }
+#endif
+
+ if (request->profile) {
+ request->total_time_nsec =
+ (s32)(b2r2_get_curr_nsec() - request->start_time_nsec);
+ b2r2_call_profiler_blt_done(request);
+ }
+
+ /* Local addref / release within this func */
+ b2r2_core_job_release(job, __func__);
+}
+
+/**
+ * Called when job should be released (free memory etc.)
+ *
+ * @job: The job
+ */
+static void job_release(struct b2r2_core_job *job)
+{
+ struct b2r2_blt_request *request =
+ container_of(job, struct b2r2_blt_request, job);
+
+ inc_stat(&stat_n_jobs_released);
+
+ b2r2_log_info("%s, first_node=%p, ref_count=%d\n",
+ __func__, request->first_node, request->job.ref_count);
+
+ b2r2_node_split_cancel(&request->node_split_job);
+
+ if (request->first_node) {
+ b2r2_debug_job_done(request->first_node);
+#ifdef B2R2_USE_NODE_GEN
+ b2r2_blt_free_nodes(request->first_node);
+#else
+ b2r2_node_free(request->first_node);
+#endif
+ }
+
+ /* Release memory for the request */
+ if (request->clut != NULL) {
+ dma_free_coherent(b2r2_blt_device(), CLUT_SIZE, request->clut,
+ request->clut_phys_addr);
+ request->clut = NULL;
+ request->clut_phys_addr = 0;
+ }
+ kfree(request);
+}
+
+/**
+ * Tells the job to try to allocate the resources needed to execute the job.
+ * Called just before execution of a job.
+ *
+ * @job: The job
+ * @atomic: true if called from atomic (i.e. interrupt) context. If function
+ * can't allocate in atomic context it should return error, it
+ * will then be called later from non-atomic context.
+ */
+static int job_acquire_resources(struct b2r2_core_job *job, bool atomic)
+{
+ struct b2r2_blt_request *request =
+ container_of(job, struct b2r2_blt_request, job);
+ int ret;
+ int i;
+
+ b2r2_log_info("%s\n", __func__);
+
+ if (request->buf_count == 0)
+ return 0;
+
+ if (request->buf_count > MAX_TMP_BUFS_NEEDED) {
+ b2r2_log_err("%s: request->buf_count > MAX_TMP_BUFS_NEEDED\n",
+ __func__);
+ return -ENOMSG;
+ }
+
+ /*
+ * 1 to 1 mapping between request temp buffers and temp buffers
+ * (request temp buf 0 is always temp buf 0, request temp buf 1 is
+ * always temp buf 1 and so on) to avoid starvation of jobs that
+ * require multiple temp buffers. Not optimal in terms of memory
+ * usage but we avoid get into a situation where lower prio jobs can
+ * delay higher prio jobs that require more temp buffers.
+ */
+ if (tmp_bufs[0].in_use)
+ return -EAGAIN;
+
+ for (i = 0; i < request->buf_count; i++) {
+ if (tmp_bufs[i].buf.size < request->bufs[i].size) {
+ b2r2_log_err("%s: tmp_bufs[i].buf.size < "
+ "request->bufs[i].size\n",
+ __func__);
+ ret = -ENOMSG;
+ goto error;
+ }
+
+ tmp_bufs[i].in_use = true;
+ request->bufs[i].phys_addr = tmp_bufs[i].buf.phys_addr;
+ request->bufs[i].virt_addr = tmp_bufs[i].buf.virt_addr;
+
+ b2r2_log_info("%s: phys=%p, virt=%p\n",
+ __func__, (void *)request->bufs[i].phys_addr,
+ request->bufs[i].virt_addr);
+
+ ret = b2r2_node_split_assign_buffers(&request->node_split_job,
+ request->first_node, request->bufs,
+ request->buf_count);
+ if (ret < 0)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ for (i = 0; i < request->buf_count; i++)
+ tmp_bufs[i].in_use = false;
+
+ return ret;
+}
+
+/**
+ * Tells the job to free the resources needed to execute the job.
+ * Called after execution of a job.
+ *
+ * @job: The job
+ * @atomic: true if called from atomic (i.e. interrupt) context. If function
+ * can't allocate in atomic context it should return error, it
+ * will then be called later from non-atomic context.
+ */
+static void job_release_resources(struct b2r2_core_job *job, bool atomic)
+{
+ struct b2r2_blt_request *request =
+ container_of(job, struct b2r2_blt_request, job);
+ int i;
+
+ b2r2_log_info("%s\n", __func__);
+
+ /* Free any temporary buffers */
+ for (i = 0; i < request->buf_count; i++) {
+
+ b2r2_log_info("%s: freeing %d bytes\n",
+ __func__, request->bufs[i].size);
+ tmp_bufs[i].in_use = false;
+ memset(&request->bufs[i], 0, sizeof(request->bufs[i]));
+ }
+ request->buf_count = 0;
+
+ /*
+ * Early release of nodes
+ * FIXME: If nodes are to be reused we don't want to release here
+ */
+ if (!atomic && request->first_node) {
+ b2r2_debug_job_done(request->first_node);
+
+#ifdef B2R2_USE_NODE_GEN
+ b2r2_blt_free_nodes(request->first_node);
+#else
+ b2r2_node_free(request->first_node);
+#endif
+ request->first_node = NULL;
+ }
+}
+
+#endif /* !CONFIG_B2R2_GENERIC_ONLY */
+
+#ifdef CONFIG_B2R2_GENERIC
+/**
+ * Called when job for one tile is done or cancelled
+ * in the generic path.
+ *
+ * @job: The job
+ */
+static void tile_job_callback_gen(struct b2r2_core_job *job)
+{
+ if (b2r2_blt_device())
+ b2r2_log_info("%s\n", __func__);
+
+ /* Local addref / release within this func */
+ b2r2_core_job_addref(job, __func__);
+
+#ifdef CONFIG_DEBUG_FS
+ /* Notify if a tile job is cancelled */
+ if (job->job_state == B2R2_CORE_JOB_CANCELED) {
+ b2r2_log_info("%s: Tile job cancelled:\n", __func__);
+ }
+#endif
+
+ /* Local addref / release within this func */
+ b2r2_core_job_release(job, __func__);
+}
+
+/**
+ * Called when job is done or cancelled.
+ * Used for the last tile in the generic path
+ * to notify waiting clients.
+ *
+ * @job: The job
+ */
+static void job_callback_gen(struct b2r2_core_job *job)
+{
+ struct b2r2_blt_request *request =
+ container_of(job, struct b2r2_blt_request, job);
+
+ if (b2r2_blt_device())
+ b2r2_log_info("%s\n", __func__);
+
+ /* Local addref / release within this func */
+ b2r2_core_job_addref(job, __func__);
+
+ /* Move to report list if the job shall be reported */
+ /* FIXME: Use a smaller struct? */
+ mutex_lock(&request->instance->lock);
+
+ if (request->user_req.flags & B2R2_BLT_FLAG_REPORT_WHEN_DONE) {
+ /* Move job to report list */
+ list_add_tail(&request->list,
+ &request->instance->report_list);
+ inc_stat(&stat_n_jobs_in_report_list);
+
+ /* Wake up poll */
+ wake_up_interruptible(
+ &request->instance->report_list_waitq);
+
+ /*
+ * Add a reference because we put the
+ * job in the report list
+ */
+ b2r2_core_job_addref(job, __func__);
+ }
+
+ /*
+ * Decrease number of active requests and wake up
+ * synching threads if active requests reaches zero
+ */
+ BUG_ON(request->instance->no_of_active_requests == 0);
+ request->instance->no_of_active_requests--;
+ if (request->instance->synching &&
+ request->instance->no_of_active_requests == 0) {
+ request->instance->synching = false;
+ /* Wake up all syncing */
+
+ wake_up_interruptible_all(
+ &request->instance->synch_done_waitq);
+ }
+ mutex_unlock(&request->instance->lock);
+
+#ifdef CONFIG_DEBUG_FS
+ /* Dump job if cancelled */
+ if (job->job_state == B2R2_CORE_JOB_CANCELED) {
+ char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL);
+
+ b2r2_log_info("%s: Job cancelled:\n", __func__);
+ if (Buf != NULL) {
+ sprintf_req(request, Buf, sizeof(char) * 4096);
+ b2r2_log_info("%s", Buf);
+ kfree(Buf);
+ } else {
+ b2r2_log_info("Unable to print the request. "
+ "Message buffer allocation failed.\n");
+ }
+ }
+#endif
+
+ /* Local addref / release within this func */
+ b2r2_core_job_release(job, __func__);
+}
+
+/**
+ * Called when tile job should be released (free memory etc.)
+ * Should be used only for tile jobs. Tile jobs should only be used
+ * by b2r2_core, thus making ref_count trigger their release.
+ *
+ * @job: The job
+ */
+
+static void tile_job_release_gen(struct b2r2_core_job *job)
+{
+ inc_stat(&stat_n_jobs_released);
+
+ b2r2_log_info("%s, first_node_address=0x%.8x, ref_count=%d\n",
+ __func__, job->first_node_address, job->ref_count);
+
+ /* Release memory for the job */
+ kfree(job);
+}
+
+/**
+ * Called when job should be released (free memory etc.)
+ *
+ * @job: The job
+ */
+
+static void job_release_gen(struct b2r2_core_job *job)
+{
+ struct b2r2_blt_request *request =
+ container_of(job, struct b2r2_blt_request, job);
+
+ inc_stat(&stat_n_jobs_released);
+
+ b2r2_log_info("%s, first_node=%p, ref_count=%d\n",
+ __func__, request->first_node, request->job.ref_count);
+
+ if (request->first_node) {
+ b2r2_debug_job_done(request->first_node);
+
+ /* Free nodes */
+#ifdef B2R2_USE_NODE_GEN
+ b2r2_blt_free_nodes(request->first_node);
+#else
+ b2r2_node_free(request->first_node);
+#endif
+ }
+
+ /* Release memory for the request */
+ if (request->clut != NULL) {
+ dma_free_coherent(b2r2_blt_device(), CLUT_SIZE, request->clut,
+ request->clut_phys_addr);
+ request->clut = NULL;
+ request->clut_phys_addr = 0;
+ }
+ kfree(request);
+}
+
+static int job_acquire_resources_gen(struct b2r2_core_job *job, bool atomic)
+{
+ /* Nothing so far. Temporary buffers are pre-allocated */
+ return 0;
+}
+static void job_release_resources_gen(struct b2r2_core_job *job, bool atomic)
+{
+ /* Nothing so far. Temporary buffers are pre-allocated */
+}
+
+/**
+ * b2r2_generic_blt - Generic implementation of the B2R2 blit request
+ *
+ * @instance: The B2R2 BLT instance
+ * @request; The request to perform
+ */
+static int b2r2_generic_blt(struct b2r2_blt_instance *instance,
+ struct b2r2_blt_request *request)
+{
+ int ret = 0;
+ struct b2r2_blt_rect actual_dst_rect;
+ int request_id = 0;
+ struct b2r2_node *last_node = request->first_node;
+ int node_count;
+ s32 tmp_buf_width = 0;
+ s32 tmp_buf_height = 0;
+ u32 tmp_buf_count = 0;
+ s32 x;
+ s32 y;
+ const struct b2r2_blt_rect *dst_rect = &(request->user_req.dst_rect);
+ const s32 dst_img_width = request->user_req.dst_img.width;
+ const s32 dst_img_height = request->user_req.dst_img.height;
+ const enum b2r2_blt_flag flags = request->user_req.flags;
+ /* Descriptors for the temporary buffers */
+ struct b2r2_work_buf work_bufs[4];
+ struct b2r2_blt_rect dst_rect_tile;
+ int i;
+
+ u32 thread_runtime_at_start = 0;
+ s32 nsec_active_in_b2r2 = 0;
+
+ /*
+ * Early exit if zero blt.
+ * dst_rect outside of dst_img or
+ * dst_clip_rect outside of dst_img.
+ */
+ if (dst_rect->x + dst_rect->width <= 0 ||
+ dst_rect->y + dst_rect->height <= 0 ||
+ dst_img_width <= dst_rect->x ||
+ dst_img_height <= dst_rect->y ||
+ ((flags & B2R2_BLT_FLAG_DESTINATION_CLIP) != 0 &&
+ (dst_img_width <= request->user_req.dst_clip_rect.x ||
+ dst_img_height <= request->user_req.dst_clip_rect.y ||
+ request->user_req.dst_clip_rect.x +
+ request->user_req.dst_clip_rect.width <= 0 ||
+ request->user_req.dst_clip_rect.y +
+ request->user_req.dst_clip_rect.height <= 0))) {
+ goto zero_blt;
+ }
+
+ if (request->profile) {
+ request->start_time_nsec = b2r2_get_curr_nsec();
+ thread_runtime_at_start = (u32)task_sched_runtime(current);
+ }
+
+ memset(work_bufs, 0, sizeof(work_bufs));
+
+ b2r2_log_info("%s\n", __func__);
+
+ inc_stat(&stat_n_in_blt);
+
+ /* Debug prints of incoming request */
+ b2r2_log_info(
+ "src.fmt=%#010x flags=0x%.8x src.buf={%d,%d,0x%.8x}\n"
+ "src.w,h={%d,%d} src.rect={%d,%d,%d,%d}\n",
+ request->user_req.src_img.fmt,
+ request->user_req.flags,
+ request->user_req.src_img.buf.type,
+ request->user_req.src_img.buf.fd,
+ request->user_req.src_img.buf.offset,
+ request->user_req.src_img.width,
+ request->user_req.src_img.height,
+ request->user_req.src_rect.x,
+ request->user_req.src_rect.y,
+ request->user_req.src_rect.width,
+ request->user_req.src_rect.height);
+ b2r2_log_info(
+ "dst.fmt=%#010x dst.buf={%d,%d,0x%.8x}\n"
+ "dst.w,h={%d,%d} dst.rect={%d,%d,%d,%d}\n"
+ "dst_clip_rect={%d,%d,%d,%d}\n",
+ request->user_req.dst_img.fmt,
+ request->user_req.dst_img.buf.type,
+ request->user_req.dst_img.buf.fd,
+ request->user_req.dst_img.buf.offset,
+ request->user_req.dst_img.width,
+ request->user_req.dst_img.height,
+ request->user_req.dst_rect.x,
+ request->user_req.dst_rect.y,
+ request->user_req.dst_rect.width,
+ request->user_req.dst_rect.height,
+ request->user_req.dst_clip_rect.x,
+ request->user_req.dst_clip_rect.y,
+ request->user_req.dst_clip_rect.width,
+ request->user_req.dst_clip_rect.height);
+
+ inc_stat(&stat_n_in_blt_synch);
+
+ /* Wait here if synch is ongoing */
+ ret = wait_event_interruptible(instance->synch_done_waitq,
+ !is_synching(instance));
+ if (ret) {
+ b2r2_log_warn(
+ "%s: Sync wait interrupted, %d\n",
+ __func__, ret);
+ ret = -EAGAIN;
+ dec_stat(&stat_n_in_blt_synch);
+ goto synch_interrupted;
+ }
+
+ dec_stat(&stat_n_in_blt_synch);
+
+ /* Resolve the buffers */
+
+ /* Source buffer */
+ ret = resolve_buf(&request->user_req.src_img,
+ &request->user_req.src_rect, false, &request->src_resolved);
+ if (ret < 0) {
+ b2r2_log_warn(
+ "%s: Resolve src buf failed, %d\n",
+ __func__, ret);
+ ret = -EAGAIN;
+ goto resolve_src_buf_failed;
+ }
+
+ /* Source mask buffer */
+ ret = resolve_buf(&request->user_req.src_mask,
+ &request->user_req.src_rect, false,
+ &request->src_mask_resolved);
+ if (ret < 0) {
+ b2r2_log_warn(
+ "%s: Resolve src mask buf failed, %d\n",
+ __func__, ret);
+ ret = -EAGAIN;
+ goto resolve_src_mask_buf_failed;
+ }
+
+ /* Destination buffer */
+ get_actual_dst_rect(&request->user_req, &actual_dst_rect);
+ ret = resolve_buf(&request->user_req.dst_img, &actual_dst_rect,
+ true, &request->dst_resolved);
+ if (ret < 0) {
+ b2r2_log_warn(
+ "%s: Resolve dst buf failed, %d\n",
+ __func__, ret);
+ ret = -EAGAIN;
+ goto resolve_dst_buf_failed;
+ }
+
+ /* Debug prints of resolved buffers */
+ b2r2_log_info("src.rbuf={%X,%p,%d} {%p,%X,%X,%d}\n",
+ request->src_resolved.physical_address,
+ request->src_resolved.virtual_address,
+ request->src_resolved.is_pmem,
+ request->src_resolved.filep,
+ request->src_resolved.file_physical_start,
+ request->src_resolved.file_virtual_start,
+ request->src_resolved.file_len);
+
+ b2r2_log_info("dst.rbuf={%X,%p,%d} {%p,%X,%X,%d}\n",
+ request->dst_resolved.physical_address,
+ request->dst_resolved.virtual_address,
+ request->dst_resolved.is_pmem,
+ request->dst_resolved.filep,
+ request->dst_resolved.file_physical_start,
+ request->dst_resolved.file_virtual_start,
+ request->dst_resolved.file_len);
+
+ /* Calculate the number of nodes (and resources) needed for this job */
+ ret = b2r2_generic_analyze(request, &tmp_buf_width,
+ &tmp_buf_height, &tmp_buf_count, &node_count);
+ if (ret < 0) {
+ b2r2_log_warn(
+ "%s: Failed to analyze request, ret = %d\n",
+ __func__, ret);
+#ifdef CONFIG_DEBUG_FS
+ {
+ /* Failed, dump job to dmesg */
+ char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL);
+
+ b2r2_log_info(
+ "%s: Analyze failed for:\n", __func__);
+ if (Buf != NULL) {
+ sprintf_req(request, Buf, sizeof(char) * 4096);
+ b2r2_log_info("%s", Buf);
+ kfree(Buf);
+ } else {
+ b2r2_log_info("Unable to print the request. "
+ "Message buffer allocation failed.\n");
+ }
+ }
+#endif
+ goto generate_nodes_failed;
+ }
+
+ /* Allocate the nodes needed */
+#ifdef B2R2_USE_NODE_GEN
+ request->first_node = b2r2_blt_alloc_nodes(node_count);
+ if (request->first_node == NULL) {
+ b2r2_log_warn(
+ "%s: Failed to allocate nodes, ret = %d\n",
+ __func__, ret);
+ goto generate_nodes_failed;
+ }
+#else
+ ret = b2r2_node_alloc(node_count, &(request->first_node));
+ if (ret < 0 || request->first_node == NULL) {
+ b2r2_log_warn(
+ "%s: Failed to allocate nodes, ret = %d\n",
+ __func__, ret);
+ goto generate_nodes_failed;
+ }
+#endif
+
+ /* Allocate the temporary buffers */
+ for (i = 0; i < tmp_buf_count; i++) {
+ void *virt;
+ work_bufs[i].size = tmp_buf_width * tmp_buf_height * 4;
+
+ virt = dma_alloc_coherent(b2r2_blt_device(),
+ work_bufs[i].size,
+ &(work_bufs[i].phys_addr),
+ GFP_DMA | GFP_KERNEL);
+ if (virt == NULL) {
+ ret = -ENOMEM;
+ goto alloc_work_bufs_failed;
+ }
+
+ work_bufs[i].virt_addr = virt;
+ memset(work_bufs[i].virt_addr, 0xff, work_bufs[i].size);
+ }
+ ret = b2r2_generic_configure(request,
+ request->first_node, &work_bufs[0], tmp_buf_count);
+
+ if (ret < 0) {
+ b2r2_log_warn(
+ "%s: Failed to perform generic configure, ret = %d\n",
+ __func__, ret);
+ goto generic_conf_failed;
+ }
+
+ /* Exit here if dry run */
+ if (flags & B2R2_BLT_FLAG_DRY_RUN)
+ goto exit_dry_run;
+
+ /*
+ * Configure the request and make sure
+ * that its job is run only for the LAST tile.
+ * This is when the request is complete
+ * and waiting clients should be notified.
+ */
+ last_node = request->first_node;
+ while (last_node && last_node->next)
+ last_node = last_node->next;
+
+ request->job.tag = (int) instance;
+ request->job.prio = request->user_req.prio;
+ request->job.first_node_address =
+ request->first_node->physical_address;
+ request->job.last_node_address =
+ last_node->physical_address;
+ request->job.callback = job_callback_gen;
+ request->job.release = job_release_gen;
+ /* Work buffers and nodes are pre-allocated */
+ request->job.acquire_resources = job_acquire_resources_gen;
+ request->job.release_resources = job_release_resources_gen;
+
+ /* Flush the L1/L2 cache for the buffers */
+
+ /* Source buffer */
+ if (!(flags & B2R2_BLT_FLAG_SRC_NO_CACHE_FLUSH) &&
+ (request->user_req.src_img.buf.type !=
+ B2R2_BLT_PTR_PHYSICAL) &&
+ !b2r2_is_mb_fmt(request->user_req.src_img.fmt))
+ /* MB formats are never touched by SW */
+ sync_buf(&request->user_req.src_img,
+ &request->src_resolved,
+ false, /*is_dst*/
+ &request->user_req.src_rect);
+
+ /* Source mask buffer */
+ if (!(flags & B2R2_BLT_FLAG_SRC_MASK_NO_CACHE_FLUSH) &&
+ (request->user_req.src_mask.buf.type !=
+ B2R2_BLT_PTR_PHYSICAL) &&
+ !b2r2_is_mb_fmt(request->user_req.src_mask.fmt))
+ /* MB formats are never touched by SW */
+ sync_buf(&request->user_req.src_mask,
+ &request->src_mask_resolved,
+ false, /*is_dst*/
+ NULL);
+
+ /* Destination buffer */
+ if (!(flags & B2R2_BLT_FLAG_DST_NO_CACHE_FLUSH) &&
+ (request->user_req.dst_img.buf.type !=
+ B2R2_BLT_PTR_PHYSICAL) &&
+ !b2r2_is_mb_fmt(request->user_req.dst_img.fmt))
+ /* MB formats are never touched by SW */
+ sync_buf(&request->user_req.dst_img,
+ &request->dst_resolved,
+ true, /*is_dst*/
+ &request->user_req.dst_rect);
+
+#ifdef CONFIG_DEBUG_FS
+ /* Remember latest request */
+ debugfs_latest_request = *request;
+#endif
+
+ /*
+ * Same nodes are reused for all the jobs needed to complete the blit.
+ * Nodes are NOT released together with associated job,
+ * as is the case with optimized b2r2_blt() path.
+ */
+ mutex_lock(&instance->lock);
+ instance->no_of_active_requests++;
+ mutex_unlock(&instance->lock);
+ /*
+ * Process all but the last row in the destination rectangle.
+ * Consider only the tiles that will actually end up inside
+ * the destination image.
+ * dst_rect->height - tmp_buf_height being <=0 is allright.
+ * The loop will not be entered since y will always be equal to or
+ * greater than zero.
+ * Early exit check at the beginning handles the cases when nothing
+ * at all should be processed.
+ */
+ y = 0;
+ if (dst_rect->y < 0)
+ y = -dst_rect->y;
+
+ for (; y < dst_rect->height - tmp_buf_height &&
+ y + dst_rect->y < dst_img_height - tmp_buf_height;
+ y += tmp_buf_height) {
+ /* Tile in the destination rectangle being processed */
+ struct b2r2_blt_rect dst_rect_tile;
+ dst_rect_tile.y = y;
+ dst_rect_tile.width = tmp_buf_width;
+ dst_rect_tile.height = tmp_buf_height;
+
+ x = 0;
+ if (dst_rect->x < 0)
+ x = -dst_rect->x;
+
+ for (; x < dst_rect->width &&
+ x + dst_rect->x < dst_img_width; x += tmp_buf_width) {
+ /*
+ * Tile jobs are freed by the supplied release function
+ * when ref_count on a tile_job reaches zero.
+ */
+ struct b2r2_core_job *tile_job = kmalloc(sizeof(*tile_job), GFP_KERNEL);
+ if (tile_job == NULL) {
+ /*
+ * Skip this tile. Do not abort, just hope for better luck
+ * with rest of the tiles. Memory might become available.
+ */
+ b2r2_log_info("%s: Failed to alloc job. "
+ "Skipping tile at (x, y)=(%d, %d)\n", __func__, x, y);
+ continue;
+ }
+ tile_job->tag = request->job.tag;
+ tile_job->prio = request->job.prio;
+ tile_job->first_node_address =
+ request->job.first_node_address;
+ tile_job->last_node_address =
+ request->job.last_node_address;
+ tile_job->callback = tile_job_callback_gen;
+ tile_job->release = tile_job_release_gen;
+ /* Work buffers and nodes are pre-allocated */
+ tile_job->acquire_resources = job_acquire_resources_gen;
+ tile_job->release_resources = job_release_resources_gen;
+
+ dst_rect_tile.x = x;
+ if (x + dst_rect->x + tmp_buf_width > dst_img_width) {
+ /*
+ * Only a part of the tile can be written.
+ * Limit imposed by buffer size.
+ */
+ dst_rect_tile.width = dst_img_width - (x + dst_rect->x);
+ } else if (x + tmp_buf_width > dst_rect->width) {
+ /*
+ * Only a part of the tile can be written.
+ * In this case limit imposed by dst_rect size.
+ */
+ dst_rect_tile.width = dst_rect->width - x;
+ } else {
+ /* Whole tile can be written. */
+ dst_rect_tile.width = tmp_buf_width;
+ }
+ /*
+ * Where applicable, calculate area in src buffer that is needed
+ * to generate the specified part of destination rectangle.
+ */
+ b2r2_generic_set_areas(request, request->first_node, &dst_rect_tile);
+ /* Submit the job */
+ b2r2_log_info("%s: Submitting job\n", __func__);
+
+ inc_stat(&stat_n_in_blt_add);
+
+ mutex_lock(&instance->lock);
+
+ request_id = b2r2_core_job_add(tile_job);
+
+ dec_stat(&stat_n_in_blt_add);
+
+ if (request_id < 0) {
+ b2r2_log_warn("%s: "
+ "Failed to add tile job, ret = %d\n",
+ __func__, request_id);
+ ret = request_id;
+ mutex_unlock(&instance->lock);
+ goto job_add_failed;
+ }
+
+ inc_stat(&stat_n_jobs_added);
+
+ mutex_unlock(&instance->lock);
+
+ /* Wait for the job to be done */
+ b2r2_log_info("%s: Synchronous, waiting\n",
+ __func__);
+
+ inc_stat(&stat_n_in_blt_wait);
+
+ ret = b2r2_core_job_wait(tile_job);
+
+ dec_stat(&stat_n_in_blt_wait);
+
+ if (ret < 0 && ret != -ENOENT)
+ b2r2_log_warn(
+ "%s: Failed to wait job, ret = %d\n",
+ __func__, ret);
+ else {
+ b2r2_log_info(
+ "%s: Synchronous wait done\n",
+ __func__);
+
+ nsec_active_in_b2r2 +=
+ tile_job->nsec_active_in_hw;
+ }
+ /* Release matching the addref in b2r2_core_job_add */
+ b2r2_core_job_release(tile_job, __func__);
+ }
+ }
+
+ x = 0;
+ if (dst_rect->x < 0)
+ x = -dst_rect->x;
+
+ for (; x < dst_rect->width &&
+ x + dst_rect->x < dst_img_width; x += tmp_buf_width) {
+ struct b2r2_core_job *tile_job = NULL;
+ if (x + tmp_buf_width < dst_rect->width &&
+ x + dst_rect->x + tmp_buf_width < dst_img_width) {
+ /*
+ * Tile jobs are freed by the supplied release function
+ * when ref_count on a tile_job reaches zero.
+ * Do NOT allocate a tile_job for the last tile.
+ * Send the job from the request. This way clients
+ * will be notified when the whole blit is complete
+ * and not just part of it.
+ */
+ tile_job = kmalloc(sizeof(*tile_job), GFP_KERNEL);
+ if (tile_job == NULL) {
+ b2r2_log_info("%s: Failed to alloc job. "
+ "Skipping tile at (x, y)=(%d, %d)\n",
+ __func__, x, y);
+ continue;
+ }
+ tile_job->tag = request->job.tag;
+ tile_job->prio = request->job.prio;
+ tile_job->first_node_address =
+ request->job.first_node_address;
+ tile_job->last_node_address =
+ request->job.last_node_address;
+ tile_job->callback = tile_job_callback_gen;
+ tile_job->release = tile_job_release_gen;
+ tile_job->acquire_resources = job_acquire_resources_gen;
+ tile_job->release_resources = job_release_resources_gen;
+ }
+
+ dst_rect_tile.x = x;
+ if (x + dst_rect->x + tmp_buf_width > dst_img_width) {
+ /*
+ * Only a part of the tile can be written.
+ * Limit imposed by buffer size.
+ */
+ dst_rect_tile.width = dst_img_width - (x + dst_rect->x);
+ } else if (x + tmp_buf_width > dst_rect->width) {
+ /*
+ * Only a part of the tile can be written.
+ * In this case limit imposed by dst_rect size.
+ */
+ dst_rect_tile.width = dst_rect->width - x;
+ } else {
+ /* Whole tile can be written. */
+ dst_rect_tile.width = tmp_buf_width;
+ }
+ /*
+ * y is now the last row. Either because the whole dst_rect
+ * has been processed, or because the last row that will be written
+ * to dst_img has been reached. Limits imposed in the same way
+ * as for width.
+ */
+ dst_rect_tile.y = y;
+ if (y + dst_rect->y + tmp_buf_height > dst_img_height)
+ dst_rect_tile.height = dst_img_height - (y + dst_rect->y);
+ else if (y + tmp_buf_height > dst_rect->height)
+ dst_rect_tile.height = dst_rect->height - y;
+ else
+ dst_rect_tile.height = tmp_buf_height;
+
+ b2r2_generic_set_areas(request, request->first_node, &dst_rect_tile);
+
+ b2r2_log_info("%s: Submitting job\n", __func__);
+ inc_stat(&stat_n_in_blt_add);
+
+ mutex_lock(&instance->lock);
+ if (x + tmp_buf_width < dst_rect->width &&
+ x + dst_rect->x + tmp_buf_width < dst_img_width) {
+ request_id = b2r2_core_job_add(tile_job);
+ } else {
+ /*
+ * Last tile. Send the job-struct from the request.
+ * Clients will be notified once it completes.
+ */
+ request_id = b2r2_core_job_add(&request->job);
+ }
+
+ dec_stat(&stat_n_in_blt_add);
+
+ if (request_id < 0) {
+ b2r2_log_warn("%s: Failed to add tile job, ret = %d\n",
+ __func__, request_id);
+ ret = request_id;
+ mutex_unlock(&instance->lock);
+ if (tile_job != NULL)
+ kfree(tile_job);
+ goto job_add_failed;
+ }
+
+ inc_stat(&stat_n_jobs_added);
+ mutex_unlock(&instance->lock);
+
+ b2r2_log_info("%s: Synchronous, waiting\n",
+ __func__);
+
+ inc_stat(&stat_n_in_blt_wait);
+ if (x + tmp_buf_width < dst_rect->width &&
+ x + dst_rect->x + tmp_buf_width < dst_img_width) {
+ ret = b2r2_core_job_wait(tile_job);
+ } else {
+ /*
+ * This is the last tile. Wait for the job-struct from
+ * the request.
+ */
+ ret = b2r2_core_job_wait(&request->job);
+ }
+ dec_stat(&stat_n_in_blt_wait);
+
+ if (ret < 0 && ret != -ENOENT)
+ b2r2_log_warn(
+ "%s: Failed to wait job, ret = %d\n",
+ __func__, ret);
+ else {
+ b2r2_log_info(
+ "%s: Synchronous wait done\n", __func__);
+
+ if (x + tmp_buf_width < dst_rect->width &&
+ x + dst_rect->x + tmp_buf_width < dst_img_width)
+ nsec_active_in_b2r2 += tile_job->nsec_active_in_hw;
+ else
+ nsec_active_in_b2r2 +=
+ request->job.nsec_active_in_hw;
+ }
+
+ /*
+ * Release matching the addref in b2r2_core_job_add.
+ * Make sure that the correct job-struct is released
+ * when the last tile is processed.
+ */
+ if (x + tmp_buf_width < dst_rect->width &&
+ x + dst_rect->x + tmp_buf_width < dst_img_width) {
+ b2r2_core_job_release(tile_job, __func__);
+ } else {
+ b2r2_core_job_release(&request->job, __func__);
+ }
+ }
+
+
+ ret = 0;
+
+
+ /* Unresolve the buffers */
+ unresolve_buf(&request->user_req.src_img.buf,
+ &request->src_resolved);
+ unresolve_buf(&request->user_req.src_mask.buf,
+ &request->src_mask_resolved);
+ unresolve_buf(&request->user_req.dst_img.buf,
+ &request->dst_resolved);
+
+ dec_stat(&stat_n_in_blt);
+
+ for (i = 0; i < tmp_buf_count; i++) {
+ dma_free_coherent(b2r2_blt_device(),
+ work_bufs[i].size,
+ work_bufs[i].virt_addr,
+ work_bufs[i].phys_addr);
+ memset(&(work_bufs[i]), 0, sizeof(work_bufs[i]));
+ }
+
+ if (request->profile) {
+ request->nsec_active_in_cpu =
+ (s32)((u32)task_sched_runtime(current) - thread_runtime_at_start);
+ request->total_time_nsec =
+ (s32)(b2r2_get_curr_nsec() - request->start_time_nsec);
+ request->job.nsec_active_in_hw = nsec_active_in_b2r2;
+
+ b2r2_call_profiler_blt_done(request);
+ }
+
+ return request_id;
+
+job_add_failed:
+exit_dry_run:
+generic_conf_failed:
+alloc_work_bufs_failed:
+ for (i = 0; i < 4; i++) {
+ if (work_bufs[i].virt_addr != 0) {
+ dma_free_coherent(b2r2_blt_device(),
+ work_bufs[i].size,
+ work_bufs[i].virt_addr,
+ work_bufs[i].phys_addr);
+ memset(&(work_bufs[i]), 0, sizeof(work_bufs[i]));
+ }
+ }
+
+generate_nodes_failed:
+ unresolve_buf(&request->user_req.dst_img.buf,
+ &request->dst_resolved);
+resolve_dst_buf_failed:
+ unresolve_buf(&request->user_req.src_mask.buf,
+ &request->src_mask_resolved);
+resolve_src_mask_buf_failed:
+ unresolve_buf(&request->user_req.src_img.buf,
+ &request->src_resolved);
+resolve_src_buf_failed:
+synch_interrupted:
+zero_blt:
+ job_release_gen(&request->job);
+ dec_stat(&stat_n_jobs_released);
+ dec_stat(&stat_n_in_blt);
+
+ b2r2_log_info("b2r2:%s ret=%d", __func__, ret);
+ return ret;
+}
+#endif /* CONFIG_B2R2_GENERIC */
+
+/**
+ * b2r2_blt_synch - Implements wait for all or a specified job
+ *
+ * @instance: The B2R2 BLT instance
+ * @request_id: If 0, wait for all requests on this instance to finish.
+ * Else wait for request with given request id to finish.
+ */
+static int b2r2_blt_synch(struct b2r2_blt_instance *instance,
+ int request_id)
+{
+ int ret = 0;
+ b2r2_log_info("%s, request_id=%d\n", __func__, request_id);
+
+ if (request_id == 0) {
+ /* Wait for all requests */
+ inc_stat(&stat_n_in_synch_0);
+
+ /* Enter state "synching" if we have any active request */
+ mutex_lock(&instance->lock);
+ if (instance->no_of_active_requests)
+ instance->synching = true;
+ mutex_unlock(&instance->lock);
+
+ /* Wait until no longer in state synching */
+ ret = wait_event_interruptible(instance->synch_done_waitq,
+ !is_synching(instance));
+ dec_stat(&stat_n_in_synch_0);
+ } else {
+ struct b2r2_core_job *job;
+
+ inc_stat(&stat_n_in_synch_job);
+
+ /* Wait for specific job */
+ job = b2r2_core_job_find(request_id);
+ if (job) {
+ /* Wait on find job */
+ ret = b2r2_core_job_wait(job);
+ /* Release matching the addref in b2r2_core_job_find */
+ b2r2_core_job_release(job, __func__);
+ }
+
+ /* If job not found we assume that is has been run */
+
+ dec_stat(&stat_n_in_synch_job);
+ }
+
+ b2r2_log_info(
+ "%s, request_id=%d, returns %d\n", __func__, request_id, ret);
+
+ return ret;
+}
+
+/**
+ * Query B2R2 capabilities
+ *
+ * @instance: The B2R2 BLT instance
+ * @query_cap: The structure receiving the capabilities
+ */
+static int b2r2_blt_query_cap(struct b2r2_blt_instance *instance,
+ struct b2r2_blt_query_cap *query_cap)
+{
+ /* FIXME: Not implemented yet */
+ return -ENOSYS;
+}
+
+static void get_actual_dst_rect(struct b2r2_blt_req *req,
+ struct b2r2_blt_rect *actual_dst_rect)
+{
+ struct b2r2_blt_rect dst_img_bounds;
+
+ b2r2_get_img_bounding_rect(&req->dst_img, &dst_img_bounds);
+
+ b2r2_intersect_rects(&req->dst_rect, &dst_img_bounds, actual_dst_rect);
+
+ if (req->flags & B2R2_BLT_FLAG_DESTINATION_CLIP)
+ b2r2_intersect_rects(actual_dst_rect, &req->dst_clip_rect,
+ actual_dst_rect);
+}
+
+static void set_up_hwmem_region(struct b2r2_blt_img *img,
+ struct b2r2_blt_rect *rect, struct hwmem_region *region)
+{
+ s32 img_size;
+
+ memset(region, 0, sizeof(*region));
+
+ if (b2r2_is_zero_area_rect(rect))
+ return;
+
+ img_size = b2r2_get_img_size(img);
+
+ if (b2r2_is_single_plane_fmt(img->fmt) &&
+ b2r2_is_independent_pixel_fmt(img->fmt)) {
+ int img_fmt_bpp = b2r2_get_fmt_bpp(img->fmt);
+ u32 img_pitch = b2r2_get_img_pitch(img);
+
+ region->offset = (u32)(img->buf.offset + (rect->y *
+ img_pitch));
+ region->count = (u32)rect->height;
+ region->start = (u32)((rect->x * img_fmt_bpp) / 8);
+ region->end = (u32)b2r2_div_round_up(
+ (rect->x + rect->width) * img_fmt_bpp, 8);
+ region->size = img_pitch;
+ } else {
+ /*
+ * TODO: Locking entire buffer as a quick safe solution. In the
+ * future we should lock less to avoid unecessary cache
+ * synching. Pixel interleaved YCbCr formats should be quite
+ * easy, just align start and stop points on 2.
+ */
+ region->offset = (u32)img->buf.offset;
+ region->count = 1;
+ region->start = 0;
+ region->end = (u32)img_size;
+ region->size = (u32)img_size;
+ }
+}
+
+static int resolve_hwmem(struct b2r2_blt_img *img,
+ struct b2r2_blt_rect *rect_2b_used,
+ bool is_dst,
+ struct b2r2_resolved_buf *resolved_buf)
+{
+ int return_value = 0;
+ enum hwmem_mem_type mem_type;
+ enum hwmem_access access;
+ enum hwmem_access required_access;
+ struct hwmem_mem_chunk mem_chunk;
+ size_t mem_chunk_length = 1;
+ struct hwmem_region region;
+
+ resolved_buf->hwmem_alloc =
+ hwmem_resolve_by_name(img->buf.hwmem_buf_name);
+ if (IS_ERR(resolved_buf->hwmem_alloc)) {
+ return_value = PTR_ERR(resolved_buf->hwmem_alloc);
+ b2r2_log_info("%s: hwmem_resolve_by_name failed, "
+ "error code: %i\n", __func__, return_value);
+ goto resolve_failed;
+ }
+
+ hwmem_get_info(resolved_buf->hwmem_alloc, &resolved_buf->file_len,
+ &mem_type, &access);
+
+ required_access = (is_dst ? HWMEM_ACCESS_WRITE : HWMEM_ACCESS_READ) |
+ HWMEM_ACCESS_IMPORT;
+ if ((required_access & access) != required_access) {
+ b2r2_log_info("%s: Insufficient access to hwmem buffer.\n",
+ __func__);
+ return_value = -EACCES;
+ goto access_check_failed;
+ }
+
+ if (mem_type != HWMEM_MEM_CONTIGUOUS_SYS) {
+ b2r2_log_info("%s: Hwmem buffer is scattered.\n", __func__);
+ return_value = -EINVAL;
+ goto buf_scattered;
+ }
+
+ if (resolved_buf->file_len <
+ img->buf.offset + (__u32)b2r2_get_img_size(img)) {
+ b2r2_log_info("%s: Hwmem buffer too small.\n", __func__);
+ return_value = -EINVAL;
+ goto size_check_failed;
+ }
+
+ return_value = hwmem_pin(resolved_buf->hwmem_alloc, &mem_chunk,
+ &mem_chunk_length);
+ if (return_value < 0) {
+ b2r2_log_info("%s: hwmem_pin failed, "
+ "error code: %i\n", __func__, return_value);
+ goto pin_failed;
+ }
+ resolved_buf->file_physical_start = mem_chunk.paddr;
+
+ set_up_hwmem_region(img, rect_2b_used, &region);
+ return_value = hwmem_set_domain(resolved_buf->hwmem_alloc,
+ required_access, HWMEM_DOMAIN_SYNC, &region);
+ if (return_value < 0) {
+ b2r2_log_info("%s: hwmem_set_domain failed, "
+ "error code: %i\n", __func__, return_value);
+ goto set_domain_failed;
+ }
+
+ resolved_buf->physical_address =
+ resolved_buf->file_physical_start + img->buf.offset;
+
+ goto out;
+
+set_domain_failed:
+ hwmem_unpin(resolved_buf->hwmem_alloc);
+pin_failed:
+size_check_failed:
+buf_scattered:
+access_check_failed:
+ hwmem_release(resolved_buf->hwmem_alloc);
+resolve_failed:
+
+out:
+ return return_value;
+}
+
+static void unresolve_hwmem(struct b2r2_resolved_buf *resolved_buf)
+{
+ hwmem_unpin(resolved_buf->hwmem_alloc);
+ hwmem_release(resolved_buf->hwmem_alloc);
+}
+
+/**
+ * unresolve_buf() - Must be called after resolve_buf
+ *
+ * @buf: The buffer specification as supplied from user space
+ * @resolved: Gathered information about the buffer
+ *
+ * Returns 0 if OK else negative error code
+ */
+static void unresolve_buf(struct b2r2_blt_buf *buf,
+ struct b2r2_resolved_buf *resolved)
+{
+#ifdef CONFIG_ANDROID_PMEM
+ if (resolved->is_pmem && resolved->filep)
+ put_pmem_file(resolved->filep);
+#endif
+ if (resolved->hwmem_alloc != NULL)
+ unresolve_hwmem(resolved);
+}
+
+/**
+ * resolve_buf() - Returns the physical & virtual addresses of a B2R2 blt buffer
+ *
+ * @img: The image specification as supplied from user space
+ * @rect_2b_used: The part of the image b2r2 will use.
+ * @usage: Specifies how the buffer will be used.
+ * @resolved: Gathered information about the buffer
+ *
+ * Returns 0 if OK else negative error code
+ */
+static int resolve_buf(struct b2r2_blt_img *img,
+ struct b2r2_blt_rect *rect_2b_used,
+ bool is_dst,
+ struct b2r2_resolved_buf *resolved)
+{
+ int ret = 0;
+
+ memset(resolved, 0, sizeof(*resolved));
+
+ switch (img->buf.type) {
+ case B2R2_BLT_PTR_NONE:
+ break;
+
+ case B2R2_BLT_PTR_PHYSICAL:
+ resolved->physical_address = img->buf.offset;
+ resolved->file_len = img->buf.len;
+ break;
+
+ /* FD + OFFSET type */
+ case B2R2_BLT_PTR_FD_OFFSET: {
+ /*
+ * TODO: Do we need to check if the process is allowed to
+ * read/write (depending on if it's dst or src) to the file?
+ */
+ struct file *file;
+ int put_needed;
+ int i;
+
+#ifdef CONFIG_ANDROID_PMEM
+ if (!get_pmem_file(
+ img->buf.fd,
+ (unsigned long *) &resolved->file_physical_start,
+ (unsigned long *) &resolved->file_virtual_start,
+ (unsigned long *) &resolved->file_len,
+ &resolved->filep)) {
+ resolved->physical_address =
+ resolved->file_physical_start +
+ img->buf.offset;
+ resolved->virtual_address = (void *)
+ (resolved->file_virtual_start +
+ img->buf.offset);
+ resolved->is_pmem = true;
+ } else
+#endif
+ {
+ /* Will be set to 0 if a matching dev is found */
+ ret = -EINVAL;
+
+ file = fget_light(img->buf.fd, &put_needed);
+ if (file == NULL)
+ return -EINVAL;
+#ifdef CONFIG_FB
+ if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
+ /*
+ * This is a frame buffer device, find fb_info
+ * (OK to do it like this, no locking???)
+ */
+
+ for (i = 0; i < num_registered_fb; i++) {
+ struct fb_info *info = registered_fb[i];
+
+ if (info && info->dev &&
+ MINOR(info->dev->devt) ==
+ MINOR(file->f_dentry->d_inode->i_rdev)) {
+ resolved->file_physical_start =
+ info->fix.smem_start;
+ resolved->file_virtual_start =
+ (u32)info->screen_base;
+ resolved->file_len =
+ info->fix.smem_len;
+
+ resolved->physical_address =
+ resolved->file_physical_start +
+ img->buf.offset;
+ resolved->virtual_address =
+ (void *)(resolved->file_virtual_start +
+ img->buf.offset);
+
+ ret = 0;
+ break;
+ }
+ }
+ }
+#endif
+
+ fput_light(file, put_needed);
+ }
+
+ /* Check bounds */
+ if (ret >= 0 && img->buf.offset + img->buf.len >
+ resolved->file_len) {
+ ret = -ESPIPE;
+ unresolve_buf(&img->buf, resolved);
+ }
+
+ break;
+ }
+
+ case B2R2_BLT_PTR_HWMEM_BUF_NAME_OFFSET:
+ ret = resolve_hwmem(img, rect_2b_used, is_dst, resolved);
+ break;
+
+ default:
+ b2r2_log_warn(
+ "%s: Failed to resolve buf type %d\n",
+ __func__, img->buf.type);
+
+ ret = -EINVAL;
+ break;
+
+ }
+
+ return ret;
+}
+
+/**
+ * sync_buf - Synchronizes the memory occupied by an image buffer.
+ *
+ * @buf: User buffer specification
+ * @resolved_buf: Gathered info (physical address etc.) about buffer
+ * @is_dst: true if the buffer is a destination buffer, false if the buffer is a
+ * source buffer.
+ * @rect: rectangle in the image buffer that should be synced. NULL the buffer is a source mask.
+ * @img_width: width of the complete image buffer
+ * @fmt: buffer format
+*/
+static void sync_buf(struct b2r2_blt_img *img,
+ struct b2r2_resolved_buf *resolved,
+ bool is_dst,
+ struct b2r2_blt_rect *rect)
+{
+ struct sync_args sa;
+ u32 start_phys, end_phys;
+
+ if (B2R2_BLT_PTR_NONE == img->buf.type ||
+ B2R2_BLT_PTR_HWMEM_BUF_NAME_OFFSET == img->buf.type)
+ return;
+
+ start_phys = resolved->physical_address;
+ end_phys = resolved->physical_address + img->buf.len;
+
+ /*
+ * TODO: Very ugly. We should find out whether the memory is coherent in
+ * some generic way but cache handling will be rewritten soon so there
+ * is no use spending time on it. In the new design this will probably
+ * not be a problem.
+ */
+ /* Frame buffer is coherent, at least now. */
+ if (!resolved->is_pmem) {
+ /*
+ * Drain the write buffers as they are not always part of the
+ * coherent concept.
+ */
+ wmb();
+
+ return;
+ }
+
+ /*
+ * src_mask does not have rect.
+ * Also flush full buffer for planar and semiplanar YUV formats
+ */
+ if (rect == NULL ||
+ (img->fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR) ||
+ (img->fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR) ||
+ (img->fmt == B2R2_BLT_FMT_YUV444_PACKED_PLANAR) ||
+ (img->fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR) ||
+ (img->fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR) ||
+ (img->fmt ==
+ B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE) ||
+ (img->fmt ==
+ B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE)) {
+ sa.start = (unsigned long)resolved->virtual_address;
+ sa.end = (unsigned long)resolved->virtual_address + img->buf.len;
+ start_phys = resolved->physical_address;
+ end_phys = resolved->physical_address + img->buf.len;
+ } else {
+ /* buffer is not a src_mask so make use of rect when clean & flush caches*/
+ u32 bpp; /* Bits per pixel */
+ u32 pitch;
+
+ switch (img->fmt) {
+ case B2R2_BLT_FMT_16_BIT_ARGB4444: /* Fall through */
+ case B2R2_BLT_FMT_16_BIT_ARGB1555: /* Fall through */
+ case B2R2_BLT_FMT_16_BIT_RGB565: /* Fall through */
+ case B2R2_BLT_FMT_Y_CB_Y_CR: /* Fall through */
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ bpp = 16;
+ break;
+ case B2R2_BLT_FMT_24_BIT_RGB888: /* Fall through */
+ case B2R2_BLT_FMT_24_BIT_ARGB8565: /* Fall through */
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ bpp = 24;
+ break;
+ case B2R2_BLT_FMT_32_BIT_ARGB8888: /* Fall through */
+ case B2R2_BLT_FMT_32_BIT_ABGR8888: /* Fall through */
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ bpp = 32;
+ break;
+ default:
+ bpp = 12;
+ }
+ if (img->pitch == 0)
+ pitch = (img->width * bpp) / 8;
+ else
+ pitch = img->pitch;
+
+ /*
+ * For 422I formats 2 horizontal pixels share color data.
+ * Thus, the x position must be aligned down to closest even
+ * number and width must be aligned up.
+ */
+ {
+ s32 x;
+ s32 width;
+
+ switch (img->fmt) {
+ case B2R2_BLT_FMT_Y_CB_Y_CR: /* Fall through */
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ x = (rect->x / 2) * 2;
+ width = ((rect->width + 1) / 2) * 2;
+ break;
+ default:
+ x = rect->x;
+ width = rect->width;
+ break;
+ }
+
+ sa.start = (unsigned long)resolved->virtual_address +
+ rect->y * pitch + (x * bpp) / 8;
+ sa.end = (unsigned long)sa.start +
+ (rect->height - 1) * pitch +
+ (width * bpp) / 8;
+
+ start_phys = resolved->physical_address +
+ rect->y * pitch + (x * bpp) / 8;
+ end_phys = start_phys +
+ (rect->height - 1) * pitch +
+ (width * bpp) / 8;
+ }
+ }
+
+ /*
+ * The virtual address to a pmem buffer is retrieved from ioremap, not
+ * sure if it's ok to use such an address as a kernel virtual address.
+ * When doing it at a higher level such as dma_map_single it triggers an
+ * error but at lower levels such as dmac_clean_range it seems to work,
+ * hence the low level stuff.
+ */
+
+ if (is_dst) {
+ /*
+ * According to ARM's docs you must clean before invalidating
+ * (ie flush) to avoid loosing data.
+ */
+
+ /* Flush L1 cache */
+#ifdef CONFIG_SMP
+ flush_l1_cache_range_all_cpus(&sa);
+#else
+ flush_l1_cache_range_curr_cpu(&sa);
+#endif
+
+ /* Flush L2 cache */
+ outer_flush_range(start_phys, end_phys);
+ } else {
+ /* Clean L1 cache */
+#ifdef CONFIG_SMP
+ clean_l1_cache_range_all_cpus(&sa);
+#else
+ clean_l1_cache_range_curr_cpu(&sa);
+#endif
+
+ /* Clean L2 cache */
+ outer_clean_range(start_phys, end_phys);
+ }
+}
+
+/**
+ * is_report_list_empty() - Spin lock protected check of report list
+ *
+ * @instance: The B2R2 BLT instance
+ */
+static bool is_report_list_empty(struct b2r2_blt_instance *instance)
+{
+ bool is_empty;
+
+ mutex_lock(&instance->lock);
+ is_empty = list_empty(&instance->report_list);
+ mutex_unlock(&instance->lock);
+
+ return is_empty;
+}
+
+/**
+ * is_synching() - Spin lock protected check if synching
+ *
+ * @instance: The B2R2 BLT instance
+ */
+static bool is_synching(struct b2r2_blt_instance *instance)
+{
+ bool is_synching;
+
+ mutex_lock(&instance->lock);
+ is_synching = instance->synching;
+ mutex_unlock(&instance->lock);
+
+ return is_synching;
+}
+
+/**
+ * b2r2_blt_devide() - Returns the B2R2 blt device for logging
+ */
+struct device *b2r2_blt_device(void)
+{
+ return b2r2_blt_dev ? b2r2_blt_dev->this_device : NULL;
+}
+
+/**
+ * inc_stat() - Spin lock protected increment of statistics variable
+ *
+ * @stat: Pointer to statistics variable that should be incremented
+ */
+static void inc_stat(unsigned long *stat)
+{
+ mutex_lock(&stat_lock);
+ (*stat)++;
+ mutex_unlock(&stat_lock);
+}
+
+/**
+ * inc_stat() - Spin lock protected decrement of statistics variable
+ *
+ * @stat: Pointer to statistics variable that should be decremented
+ */
+static void dec_stat(unsigned long *stat)
+{
+ mutex_lock(&stat_lock);
+ (*stat)--;
+ mutex_unlock(&stat_lock);
+}
+
+
+#ifdef CONFIG_DEBUG_FS
+/**
+ * sprintf_req() - Builds a string representing the request, for debug
+ *
+ * @request:Request that should be encoded into a string
+ * @buf: Receiving buffer
+ * @size: Size of receiving buffer
+ *
+ * Returns number of characters in string, excluding null terminator
+ */
+static int sprintf_req(struct b2r2_blt_request *request, char *buf, int size)
+{
+ size_t dev_size = 0;
+
+ dev_size += sprintf(buf + dev_size,
+ "instance: %p\n\n",
+ request->instance);
+
+ dev_size += sprintf(buf + dev_size,
+ "size: %d bytes\n",
+ request->user_req.size);
+ dev_size += sprintf(buf + dev_size,
+ "flags: %8lX\n",
+ (unsigned long) request->user_req.flags);
+ dev_size += sprintf(buf + dev_size,
+ "transform: %3lX\n",
+ (unsigned long) request->user_req.transform);
+ dev_size += sprintf(buf + dev_size,
+ "prio: %d\n",
+ request->user_req.transform);
+ dev_size += sprintf(buf + dev_size,
+ "src_img.fmt: %#010x\n",
+ request->user_req.src_img.fmt);
+ dev_size += sprintf(buf + dev_size,
+ "src_img.buf: {type=%d,hwmem_buf_name=%d,fd=%d,"
+ "offset=%d,len=%d}\n",
+ request->user_req.src_img.buf.type,
+ request->user_req.src_img.buf.hwmem_buf_name,
+ request->user_req.src_img.buf.fd,
+ request->user_req.src_img.buf.offset,
+ request->user_req.src_img.buf.len);
+ dev_size += sprintf(buf + dev_size,
+ "src_img.{width=%d,height=%d,pitch=%d}\n",
+ request->user_req.src_img.width,
+ request->user_req.src_img.height,
+ request->user_req.src_img.pitch);
+ dev_size += sprintf(buf + dev_size,
+ "src_mask.fmt: %#010x\n",
+ request->user_req.src_mask.fmt);
+ dev_size += sprintf(buf + dev_size,
+ "src_mask.buf: {type=%d,hwmem_buf_name=%d,fd=%d,"
+ "offset=%d,len=%d}\n",
+ request->user_req.src_mask.buf.type,
+ request->user_req.src_mask.buf.hwmem_buf_name,
+ request->user_req.src_mask.buf.fd,
+ request->user_req.src_mask.buf.offset,
+ request->user_req.src_mask.buf.len);
+ dev_size += sprintf(buf + dev_size,
+ "src_mask.{width=%d,height=%d,pitch=%d}\n",
+ request->user_req.src_mask.width,
+ request->user_req.src_mask.height,
+ request->user_req.src_mask.pitch);
+ dev_size += sprintf(buf + dev_size,
+ "src_rect.{x=%d,y=%d,width=%d,height=%d}\n",
+ request->user_req.src_rect.x,
+ request->user_req.src_rect.y,
+ request->user_req.src_rect.width,
+ request->user_req.src_rect.height);
+ dev_size += sprintf(buf + dev_size,
+ "src_color=%08lX\n",
+ (unsigned long) request->user_req.src_color);
+
+ dev_size += sprintf(buf + dev_size,
+ "dst_img.fmt: %#010x\n",
+ request->user_req.dst_img.fmt);
+ dev_size += sprintf(buf + dev_size,
+ "dst_img.buf: {type=%d,hwmem_buf_name=%d,fd=%d,"
+ "offset=%d,len=%d}\n",
+ request->user_req.dst_img.buf.type,
+ request->user_req.dst_img.buf.hwmem_buf_name,
+ request->user_req.dst_img.buf.fd,
+ request->user_req.dst_img.buf.offset,
+ request->user_req.dst_img.buf.len);
+ dev_size += sprintf(buf + dev_size,
+ "dst_img.{width=%d,height=%d,pitch=%d}\n",
+ request->user_req.dst_img.width,
+ request->user_req.dst_img.height,
+ request->user_req.dst_img.pitch);
+ dev_size += sprintf(buf + dev_size,
+ "dst_rect.{x=%d,y=%d,width=%d,height=%d}\n",
+ request->user_req.dst_rect.x,
+ request->user_req.dst_rect.y,
+ request->user_req.dst_rect.width,
+ request->user_req.dst_rect.height);
+ dev_size += sprintf(buf + dev_size,
+ "dst_clip_rect.{x=%d,y=%d,width=%d,height=%d}\n",
+ request->user_req.dst_clip_rect.x,
+ request->user_req.dst_clip_rect.y,
+ request->user_req.dst_clip_rect.width,
+ request->user_req.dst_clip_rect.height);
+ dev_size += sprintf(buf + dev_size,
+ "dst_color=%08lX\n",
+ (unsigned long) request->user_req.dst_color);
+ dev_size += sprintf(buf + dev_size,
+ "global_alpha=%d\n",
+ (int) request->user_req.global_alpha);
+ dev_size += sprintf(buf + dev_size,
+ "report1=%08lX\n",
+ (unsigned long) request->user_req.report1);
+ dev_size += sprintf(buf + dev_size,
+ "report2=%08lX\n",
+ (unsigned long) request->user_req.report2);
+
+ dev_size += sprintf(buf + dev_size,
+ "request_id: %d\n",
+ request->request_id);
+
+ dev_size += sprintf(buf + dev_size,
+ "src_resolved.physical: %lX\n",
+ (unsigned long) request->src_resolved.
+ physical_address);
+ dev_size += sprintf(buf + dev_size,
+ "src_resolved.virtual: %p\n",
+ request->src_resolved.virtual_address);
+ dev_size += sprintf(buf + dev_size,
+ "src_resolved.filep: %p\n",
+ request->src_resolved.filep);
+ dev_size += sprintf(buf + dev_size,
+ "src_resolved.filep_physical_start: %lX\n",
+ (unsigned long) request->src_resolved.
+ file_physical_start);
+ dev_size += sprintf(buf + dev_size,
+ "src_resolved.filep_virtual_start: %p\n",
+ (void *) request->src_resolved.file_virtual_start);
+ dev_size += sprintf(buf + dev_size,
+ "src_resolved.file_len: %d\n",
+ request->src_resolved.file_len);
+
+ dev_size += sprintf(buf + dev_size,
+ "src_mask_resolved.physical: %lX\n",
+ (unsigned long) request->src_mask_resolved.
+ physical_address);
+ dev_size += sprintf(buf + dev_size,
+ "src_mask_resolved.virtual: %p\n",
+ request->src_mask_resolved.virtual_address);
+ dev_size += sprintf(buf + dev_size,
+ "src_mask_resolved.filep: %p\n",
+ request->src_mask_resolved.filep);
+ dev_size += sprintf(buf + dev_size,
+ "src_mask_resolved.filep_physical_start: %lX\n",
+ (unsigned long) request->src_mask_resolved.
+ file_physical_start);
+ dev_size += sprintf(buf + dev_size,
+ "src_mask_resolved.filep_virtual_start: %p\n",
+ (void *) request->src_mask_resolved.
+ file_virtual_start);
+ dev_size += sprintf(buf + dev_size,
+ "src_mask_resolved.file_len: %d\n",
+ request->src_mask_resolved.file_len);
+
+ dev_size += sprintf(buf + dev_size,
+ "dst_resolved.physical: %lX\n",
+ (unsigned long) request->dst_resolved.
+ physical_address);
+ dev_size += sprintf(buf + dev_size,
+ "dst_resolved.virtual: %p\n",
+ request->dst_resolved.virtual_address);
+ dev_size += sprintf(buf + dev_size,
+ "dst_resolved.filep: %p\n",
+ request->dst_resolved.filep);
+ dev_size += sprintf(buf + dev_size,
+ "dst_resolved.filep_physical_start: %lX\n",
+ (unsigned long) request->dst_resolved.
+ file_physical_start);
+ dev_size += sprintf(buf + dev_size,
+ "dst_resolved.filep_virtual_start: %p\n",
+ (void *) request->dst_resolved.file_virtual_start);
+ dev_size += sprintf(buf + dev_size,
+ "dst_resolved.file_len: %d\n",
+ request->dst_resolved.file_len);
+
+ return dev_size;
+}
+
+/**
+ * debugfs_b2r2_blt_request_read() - Implements debugfs read for B2R2 register
+ *
+ * @filp: File pointer
+ * @buf: User space buffer
+ * @count: Number of bytes to read
+ * @f_pos: File position
+ *
+ * Returns number of bytes read or negative error code
+ */
+static int debugfs_b2r2_blt_request_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ size_t dev_size = 0;
+ int ret = 0;
+ char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL);
+
+ if (Buf == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ dev_size = sprintf_req(&debugfs_latest_request, Buf, sizeof(char) * 4096);
+
+ /* No more to read if offset != 0 */
+ if (*f_pos > dev_size)
+ goto out;
+
+ if (*f_pos + count > dev_size)
+ count = dev_size - *f_pos;
+
+ if (copy_to_user(buf, Buf, count))
+ ret = -EINVAL;
+ *f_pos += count;
+ ret = count;
+
+out:
+ if (Buf != NULL)
+ kfree(Buf);
+ return ret;
+}
+
+/**
+ * debugfs_b2r2_blt_request_fops - File operations for B2R2 request debugfs
+ */
+static const struct file_operations debugfs_b2r2_blt_request_fops = {
+ .owner = THIS_MODULE,
+ .read = debugfs_b2r2_blt_request_read,
+};
+
+/**
+ * struct debugfs_reg - Represents a B2R2 node "register"
+ *
+ * @name: Register name
+ * @offset: Offset within the node
+ */
+struct debugfs_reg {
+ const char name[30];
+ u32 offset;
+};
+
+/**
+ * debugfs_node_regs - Array with all the registers in a B2R2 node, for debug
+ */
+static const struct debugfs_reg debugfs_node_regs[] = {
+ {"GROUP0.B2R2_NIP", offsetof(struct b2r2_link_list, GROUP0.B2R2_NIP)},
+ {"GROUP0.B2R2_CIC", offsetof(struct b2r2_link_list, GROUP0.B2R2_CIC)},
+ {"GROUP0.B2R2_INS", offsetof(struct b2r2_link_list, GROUP0.B2R2_INS)},
+ {"GROUP0.B2R2_ACK", offsetof(struct b2r2_link_list, GROUP0.B2R2_ACK)},
+
+ {"GROUP1.B2R2_TBA", offsetof(struct b2r2_link_list, GROUP1.B2R2_TBA)},
+ {"GROUP1.B2R2_TTY", offsetof(struct b2r2_link_list, GROUP1.B2R2_TTY)},
+ {"GROUP1.B2R2_TXY", offsetof(struct b2r2_link_list, GROUP1.B2R2_TXY)},
+ {"GROUP1.B2R2_TSZ", offsetof(struct b2r2_link_list, GROUP1.B2R2_TSZ)},
+
+ {"GROUP2.B2R2_S1CF", offsetof(struct b2r2_link_list, GROUP2.B2R2_S1CF)},
+ {"GROUP2.B2R2_S2CF", offsetof(struct b2r2_link_list, GROUP2.B2R2_S2CF)},
+
+ {"GROUP3.B2R2_SBA", offsetof(struct b2r2_link_list, GROUP3.B2R2_SBA)},
+ {"GROUP3.B2R2_STY", offsetof(struct b2r2_link_list, GROUP3.B2R2_STY)},
+ {"GROUP3.B2R2_SXY", offsetof(struct b2r2_link_list, GROUP3.B2R2_SXY)},
+ {"GROUP3.B2R2_SSZ", offsetof(struct b2r2_link_list, GROUP3.B2R2_SSZ)},
+
+ {"GROUP4.B2R2_SBA", offsetof(struct b2r2_link_list, GROUP4.B2R2_SBA)},
+ {"GROUP4.B2R2_STY", offsetof(struct b2r2_link_list, GROUP4.B2R2_STY)},
+ {"GROUP4.B2R2_SXY", offsetof(struct b2r2_link_list, GROUP4.B2R2_SXY)},
+ {"GROUP4.B2R2_SSZ", offsetof(struct b2r2_link_list, GROUP4.B2R2_SSZ)},
+
+ {"GROUP5.B2R2_SBA", offsetof(struct b2r2_link_list, GROUP5.B2R2_SBA)},
+ {"GROUP5.B2R2_STY", offsetof(struct b2r2_link_list, GROUP5.B2R2_STY)},
+ {"GROUP5.B2R2_SXY", offsetof(struct b2r2_link_list, GROUP5.B2R2_SXY)},
+ {"GROUP5.B2R2_SSZ", offsetof(struct b2r2_link_list, GROUP5.B2R2_SSZ)},
+
+ {"GROUP6.B2R2_CWO", offsetof(struct b2r2_link_list, GROUP6.B2R2_CWO)},
+ {"GROUP6.B2R2_CWS", offsetof(struct b2r2_link_list, GROUP6.B2R2_CWS)},
+
+ {"GROUP7.B2R2_CCO", offsetof(struct b2r2_link_list, GROUP7.B2R2_CCO)},
+ {"GROUP7.B2R2_CML", offsetof(struct b2r2_link_list, GROUP7.B2R2_CML)},
+
+ {"GROUP8.B2R2_FCTL", offsetof(struct b2r2_link_list, GROUP8.B2R2_FCTL)},
+ {"GROUP8.B2R2_PMK", offsetof(struct b2r2_link_list, GROUP8.B2R2_PMK)},
+
+ {"GROUP9.B2R2_RSF", offsetof(struct b2r2_link_list, GROUP9.B2R2_RSF)},
+ {"GROUP9.B2R2_RZI", offsetof(struct b2r2_link_list, GROUP9.B2R2_RZI)},
+ {"GROUP9.B2R2_HFP", offsetof(struct b2r2_link_list, GROUP9.B2R2_HFP)},
+ {"GROUP9.B2R2_VFP", offsetof(struct b2r2_link_list, GROUP9.B2R2_VFP)},
+
+ {"GROUP10.B2R2_RSF", offsetof(struct b2r2_link_list, GROUP10.B2R2_RSF)},
+ {"GROUP10.B2R2_RZI", offsetof(struct b2r2_link_list, GROUP10.B2R2_RZI)},
+ {"GROUP10.B2R2_HFP", offsetof(struct b2r2_link_list, GROUP10.B2R2_HFP)},
+ {"GROUP10.B2R2_VFP", offsetof(struct b2r2_link_list, GROUP10.B2R2_VFP)},
+
+ {"GROUP11.B2R2_FF0", offsetof(struct b2r2_link_list,
+ GROUP11.B2R2_FF0)},
+ {"GROUP11.B2R2_FF1", offsetof(struct b2r2_link_list,
+ GROUP11.B2R2_FF1)},
+ {"GROUP11.B2R2_FF2", offsetof(struct b2r2_link_list,
+ GROUP11.B2R2_FF2)},
+ {"GROUP11.B2R2_FF3", offsetof(struct b2r2_link_list,
+ GROUP11.B2R2_FF3)},
+
+ {"GROUP12.B2R2_KEY1", offsetof(struct b2r2_link_list,
+ GROUP12.B2R2_KEY1)},
+ {"GROUP12.B2R2_KEY2", offsetof(struct b2r2_link_list,
+ GROUP12.B2R2_KEY2)},
+
+ {"GROUP13.B2R2_XYL", offsetof(struct b2r2_link_list, GROUP13.B2R2_XYL)},
+ {"GROUP13.B2R2_XYP", offsetof(struct b2r2_link_list, GROUP13.B2R2_XYP)},
+
+ {"GROUP14.B2R2_SAR", offsetof(struct b2r2_link_list, GROUP14.B2R2_SAR)},
+ {"GROUP14.B2R2_USR", offsetof(struct b2r2_link_list, GROUP14.B2R2_USR)},
+
+ {"GROUP15.B2R2_VMX0", offsetof(struct b2r2_link_list,
+ GROUP15.B2R2_VMX0)},
+ {"GROUP15.B2R2_VMX1", offsetof(struct b2r2_link_list,
+ GROUP15.B2R2_VMX1)},
+ {"GROUP15.B2R2_VMX2", offsetof(struct b2r2_link_list,
+ GROUP15.B2R2_VMX2)},
+ {"GROUP15.B2R2_VMX3", offsetof(struct b2r2_link_list,
+ GROUP15.B2R2_VMX3)},
+
+ {"GROUP16.B2R2_VMX0", offsetof(struct b2r2_link_list,
+ GROUP16.B2R2_VMX0)},
+ {"GROUP16.B2R2_VMX1", offsetof(struct b2r2_link_list,
+ GROUP16.B2R2_VMX1)},
+ {"GROUP16.B2R2_VMX2", offsetof(struct b2r2_link_list,
+ GROUP16.B2R2_VMX2)},
+ {"GROUP16.B2R2_VMX3", offsetof(struct b2r2_link_list,
+ GROUP16.B2R2_VMX3)},
+};
+
+/**
+ * debugfs_b2r2_blt_stat_read() - Implements debugfs read for B2R2 BLT
+ * statistics
+ *
+ * @filp: File pointer
+ * @buf: User space buffer
+ * @count: Number of bytes to read
+ * @f_pos: File position
+ *
+ * Returns number of bytes read or negative error code
+ */
+static int debugfs_b2r2_blt_stat_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ size_t dev_size = 0;
+ int ret = 0;
+ char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL);
+
+ if (Buf == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mutex_lock(&stat_lock);
+ dev_size += sprintf(Buf + dev_size, "Added jobs: %lu\n",
+ stat_n_jobs_added);
+ dev_size += sprintf(Buf + dev_size, "Released jobs: %lu\n",
+ stat_n_jobs_released);
+ dev_size += sprintf(Buf + dev_size, "Jobs in report list: %lu\n",
+ stat_n_jobs_in_report_list);
+ dev_size += sprintf(Buf + dev_size, "Clients in open: %lu\n",
+ stat_n_in_open);
+ dev_size += sprintf(Buf + dev_size, "Clients in release: %lu\n",
+ stat_n_in_release);
+ dev_size += sprintf(Buf + dev_size, "Clients in blt: %lu\n",
+ stat_n_in_blt);
+ dev_size += sprintf(Buf + dev_size, " synch: %lu\n",
+ stat_n_in_blt_synch);
+ dev_size += sprintf(Buf + dev_size, " add: %lu\n",
+ stat_n_in_blt_add);
+ dev_size += sprintf(Buf + dev_size, " wait: %lu\n",
+ stat_n_in_blt_wait);
+ dev_size += sprintf(Buf + dev_size, "Clients in synch 0: %lu\n",
+ stat_n_in_synch_0);
+ dev_size += sprintf(Buf + dev_size, "Clients in synch job: %lu\n",
+ stat_n_in_synch_job);
+ dev_size += sprintf(Buf + dev_size, "Clients in query_cap: %lu\n",
+ stat_n_in_query_cap);
+ mutex_unlock(&stat_lock);
+
+ /* No more to read if offset != 0 */
+ if (*f_pos > dev_size)
+ goto out;
+
+ if (*f_pos + count > dev_size)
+ count = dev_size - *f_pos;
+
+ if (copy_to_user(buf, Buf, count))
+ ret = -EINVAL;
+ *f_pos += count;
+ ret = count;
+
+out:
+ if (Buf != NULL)
+ kfree(Buf);
+ return ret;
+}
+
+/**
+ * debugfs_b2r2_blt_stat_fops() - File operations for B2R2 BLT
+ * statistics debugfs
+ */
+static const struct file_operations debugfs_b2r2_blt_stat_fops = {
+ .owner = THIS_MODULE,
+ .read = debugfs_b2r2_blt_stat_read,
+};
+#endif
+
+static void init_tmp_bufs(void)
+{
+ int i = 0;
+
+ for (i = 0; i < MAX_TMP_BUFS_NEEDED; i++) {
+ tmp_bufs[i].buf.virt_addr = dma_alloc_coherent(
+ b2r2_blt_device(), MAX_TMP_BUF_SIZE,
+ &tmp_bufs[i].buf.phys_addr, GFP_DMA);
+ if (tmp_bufs[i].buf.virt_addr != NULL)
+ tmp_bufs[i].buf.size = MAX_TMP_BUF_SIZE;
+ else {
+ b2r2_log_err("%s: Failed to allocate temp buffer %i\n",
+ __func__, i);
+
+ tmp_bufs[i].buf.size = 0;
+ }
+ }
+}
+
+static void destroy_tmp_bufs(void)
+{
+ int i = 0;
+
+ for (i = 0; i < MAX_TMP_BUFS_NEEDED; i++) {
+ if (tmp_bufs[i].buf.size != 0) {
+ dma_free_coherent(b2r2_blt_device(),
+ tmp_bufs[i].buf.size,
+ tmp_bufs[i].buf.virt_addr,
+ tmp_bufs[i].buf.phys_addr);
+
+ tmp_bufs[i].buf.size = 0;
+ }
+ }
+}
+
+/**
+ * b2r2_blt_module_init() - Module init function
+ *
+ * Returns 0 if OK else negative error code
+ */
+int b2r2_blt_module_init(void)
+{
+ int ret;
+
+ mutex_init(&stat_lock);
+
+#ifdef CONFIG_B2R2_GENERIC
+ /* Initialize generic path */
+ b2r2_generic_init();
+#endif
+
+ /* Initialize node splitter */
+ ret = b2r2_node_split_init();
+ if (ret) {
+ printk(KERN_WARNING "%s: node split init fails\n",
+ __func__);
+ goto b2r2_node_split_init_fail;
+ }
+
+ /* Register b2r2 driver */
+ ret = misc_register(&b2r2_blt_misc_dev);
+ if (ret) {
+ printk(KERN_WARNING "%s: registering misc device fails\n",
+ __func__);
+ goto b2r2_misc_register_fail;
+ }
+
+ b2r2_blt_misc_dev.this_device->coherent_dma_mask = 0xFFFFFFFF;
+ b2r2_blt_dev = &b2r2_blt_misc_dev;
+ b2r2_log_info("%s\n", __func__);
+
+ /*
+ * FIXME: This stuff should be done before the first requests i.e.
+ * before misc_register, but they need the device which is not
+ * available until after misc_register.
+ */
+ init_tmp_bufs();
+
+ /* Initialize memory allocator */
+ ret = b2r2_mem_init(b2r2_blt_device(), B2R2_HEAP_SIZE,
+ 4, sizeof(struct b2r2_node));
+ if (ret) {
+ printk(KERN_WARNING "%s: initializing B2R2 memhandler fails\n",
+ __func__);
+ goto b2r2_mem_init_fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ /* Register debug fs */
+ if (!debugfs_root_dir) {
+ debugfs_root_dir = debugfs_create_dir("b2r2_blt", NULL);
+ debugfs_create_file("latest_request",
+ 0666, debugfs_root_dir,
+ 0,
+ &debugfs_b2r2_blt_request_fops);
+ debugfs_create_file("stat",
+ 0666, debugfs_root_dir,
+ 0,
+ &debugfs_b2r2_blt_stat_fops);
+ }
+#endif
+ goto out;
+
+b2r2_misc_register_fail:
+b2r2_mem_init_fail:
+ b2r2_node_split_exit();
+
+b2r2_node_split_init_fail:
+#ifdef CONFIG_B2R2_GENERIC
+ b2r2_generic_exit();
+#endif
+out:
+ return ret;
+}
+
+/**
+ * b2r2_module_exit() - Module exit function
+ */
+void b2r2_blt_module_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ if (debugfs_root_dir) {
+ debugfs_remove_recursive(debugfs_root_dir);
+ debugfs_root_dir = NULL;
+ }
+#endif
+ if (b2r2_blt_dev) {
+ b2r2_log_info("%s\n", __func__);
+ b2r2_mem_exit();
+ destroy_tmp_bufs();
+ b2r2_blt_dev = NULL;
+ misc_deregister(&b2r2_blt_misc_dev);
+ }
+
+ b2r2_node_split_exit();
+
+#if defined(CONFIG_B2R2_GENERIC)
+ b2r2_generic_exit();
+#endif
+}
+
+MODULE_AUTHOR("Robert Fekete <robert.fekete@stericsson.com>");
+MODULE_DESCRIPTION("ST-Ericsson B2R2 Blitter module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/b2r2/b2r2_core.c b/drivers/video/b2r2/b2r2_core.c
new file mode 100644
index 00000000000..7a11a301d11
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_core.c
@@ -0,0 +1,2666 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 core driver
+ *
+ * Author: Robert Fekete <robert.fekete@stericsson.com>
+ * Author: Paul Wannback
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+/*
+ * TODO: Clock address from platform data
+ * Platform data should have string id instead of numbers
+ * b2r2_remove, some type of runtime problem when kernel hacking
+ * debug features on
+ *
+ * Is there already a priority list in kernel?
+ * Is it possible to handle clock using clock framework?
+ * uTimeOut, use mdelay instead?
+ * Measure performance
+ *
+ * Exchange our home-cooked ref count with kernel kref? See
+ * http://lwn.net/Articles/336224/
+ *
+ * B2R2:
+ * Source fill 2 bug
+ * Check with Symbian?
+ */
+
+/* include file */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include "b2r2_core.h"
+#include "b2r2_global.h"
+#include "b2r2_structures.h"
+#include "b2r2_internal.h"
+#include "b2r2_profiler_api.h"
+#include "b2r2_timing.h"
+#include "b2r2_debug.h"
+
+/**
+ * B2R2_DRIVER_TIMEOUT_VALUE - Busy loop timeout after soft reset
+ */
+#define B2R2_DRIVER_TIMEOUT_VALUE (1500)
+
+/**
+ * B2R2_CLK_FLAG - Value to write into clock reg to turn clock on
+ */
+#define B2R2_CLK_FLAG (0x125)
+
+/**
+ * DEBUG_CHECK_ADDREF_RELEASE - Define this to enable addref / release debug
+ */
+#define DEBUG_CHECK_ADDREF_RELEASE 1
+
+#ifdef CONFIG_DEBUG_FS
+/**
+ * HANDLE_TIMEOUTED_JOBS - Define this to check jobs for timeout and cancel them
+ */
+#define HANDLE_TIMEOUTED_JOBS 1
+#endif
+
+/**
+ * B2R2_CLOCK_ALWAYS_ON - Define this to disable power save clock turn off
+ */
+/* #define B2R2_CLOCK_ALWAYS_ON 1 */
+
+/**
+ * START_SENTINEL - Watch guard to detect job overwrites
+ */
+#define START_SENTINEL 0xBABEDEEA
+
+/**
+ * STOP_SENTINEL - Watch guard to detect job overwrites
+ */
+#define END_SENTINEL 0xDADBDCDD
+
+/**
+ * B2R2_CORE_LOWEST_PRIO - Lowest prio allowed
+ */
+#define B2R2_CORE_LOWEST_PRIO -19
+/**
+ * B2R2_CORE_HIGHEST_PRIO - Highest prio allowed
+ */
+#define B2R2_CORE_HIGHEST_PRIO 20
+
+
+/**
+ * B2R2 Hardware defines below
+ */
+
+/* - BLT_AQ_CTL */
+#define B2R2_AQ_Enab (0x80000000)
+#define B2R2_AQ_PRIOR_0 (0x0)
+#define B2R2_AQ_PRIOR_1 (0x1)
+#define B2R2_AQ_PRIOR_2 (0x2)
+#define B2R2_AQ_PRIOR_3 (0x3)
+#define B2R2_AQ_NODE_REPEAT_INT (0x100000)
+#define B2R2_AQ_STOP_INT (0x200000)
+#define B2R2_AQ_LNA_REACH_INT (0x400000)
+#define B2R2_AQ_COMPLETED_INT (0x800000)
+
+/* - BLT_CTL */
+#define B2R2BLT_CTLGLOBAL_soft_reset (0x80000000)
+#define B2R2BLT_CTLStep_By_Step (0x20000000)
+#define B2R2BLT_CTLBig_not_little (0x10000000)
+#define B2R2BLT_CTLMask (0xb0000000)
+#define B2R2BLT_CTLTestMask (0xb0000000)
+#define B2R2BLT_CTLInitialValue (0x0)
+#define B2R2BLT_CTLAccessType (INITIAL_TEST)
+#define B2R2BLT_CTL (0xa00)
+
+/* - BLT_ITS */
+#define B2R2BLT_ITSRLD_ERROR (0x80000000)
+#define B2R2BLT_ITSAQ4_Node_Notif (0x8000000)
+#define B2R2BLT_ITSAQ4_Node_repeat (0x4000000)
+#define B2R2BLT_ITSAQ4_Stopped (0x2000000)
+#define B2R2BLT_ITSAQ4_LNA_Reached (0x1000000)
+#define B2R2BLT_ITSAQ3_Node_Notif (0x800000)
+#define B2R2BLT_ITSAQ3_Node_repeat (0x400000)
+#define B2R2BLT_ITSAQ3_Stopped (0x200000)
+#define B2R2BLT_ITSAQ3_LNA_Reached (0x100000)
+#define B2R2BLT_ITSAQ2_Node_Notif (0x80000)
+#define B2R2BLT_ITSAQ2_Node_repeat (0x40000)
+#define B2R2BLT_ITSAQ2_Stopped (0x20000)
+#define B2R2BLT_ITSAQ2_LNA_Reached (0x10000)
+#define B2R2BLT_ITSAQ1_Node_Notif (0x8000)
+#define B2R2BLT_ITSAQ1_Node_repeat (0x4000)
+#define B2R2BLT_ITSAQ1_Stopped (0x2000)
+#define B2R2BLT_ITSAQ1_LNA_Reached (0x1000)
+#define B2R2BLT_ITSCQ2_Repaced (0x80)
+#define B2R2BLT_ITSCQ2_Node_Notif (0x40)
+#define B2R2BLT_ITSCQ2_retriggered (0x20)
+#define B2R2BLT_ITSCQ2_completed (0x10)
+#define B2R2BLT_ITSCQ1_Repaced (0x8)
+#define B2R2BLT_ITSCQ1_Node_Notif (0x4)
+#define B2R2BLT_ITSCQ1_retriggered (0x2)
+#define B2R2BLT_ITSCQ1_completed (0x1)
+#define B2R2BLT_ITSMask (0x8ffff0ff)
+#define B2R2BLT_ITSTestMask (0x8ffff0ff)
+#define B2R2BLT_ITSInitialValue (0x0)
+#define B2R2BLT_ITSAccessType (INITIAL_TEST)
+#define B2R2BLT_ITS (0xa04)
+
+/* - BLT_STA1 */
+#define B2R2BLT_STA1BDISP_IDLE (0x1)
+#define B2R2BLT_STA1Mask (0x1)
+#define B2R2BLT_STA1TestMask (0x1)
+#define B2R2BLT_STA1InitialValue (0x1)
+#define B2R2BLT_STA1AccessType (INITIAL_TEST)
+#define B2R2BLT_STA1 (0xa08)
+
+
+#ifdef DEBUG_CHECK_ADDREF_RELEASE
+
+/**
+ * struct addref_release - Represents one addref or release. Used
+ * to debug addref / release problems
+ *
+ * @addref: true if this represents an addref else it represents
+ * a release.
+ * @job: The job that was referenced
+ * @caller: The caller of the addref or release
+ * @ref_count: The job reference count after addref / release
+ */
+struct addref_release {
+ bool addref;
+ struct b2r2_core_job *job;
+ const char *caller;
+ int ref_count;
+};
+
+#endif
+
+/**
+ * struct b2r2_core - Administration data for B2R2 core
+ *
+ * @lock: Spin lock protecting the b2r2_core structure and the B2R2 HW
+ * @hw: B2R2 registers memory mapped
+ * @pmu_b2r2_clock: Control of B2R2 clock
+ * @log_dev: Device used for logging via dev_... functions
+ *
+ * @prio_queue: Queue of jobs sorted in priority order
+ * @active_jobs: Array containing pointer to zero or one job per queue
+ * @n_active_jobs: Number of active jobs
+ * @jiffies_last_active: jiffie value when adding last active job
+ * @jiffies_last_irq: jiffie value when last irq occured
+ * @timeout_work: Work structure for timeout work
+ *
+ * @next_job_id: Contains the job id that will be assigned to the next
+ * added job.
+ *
+ * @clock_request_count: When non-zero, clock is on
+ * @clock_off_timer: Kernel timer to handle delayed turn off of clock
+ *
+ * @work_queue: Work queue to handle done jobs (callbacks) and timeouts in
+ * non-interrupt context.
+ *
+ * @stat_n_irq: Number of interrupts (statistics)
+ * @stat_n_jobs_added: Number of jobs added (statistics)
+ * @stat_n_jobs_removed: Number of jobs removed (statistics)
+ * @stat_n_jobs_in_prio_list: Number of jobs in prio list (statistics)
+ *
+ * @debugfs_root_dir: Root directory for B2R2 debugfs
+ *
+ * @ar: Circular array of addref / release debug structs
+ * @ar_write: Where next write will occur
+ * @ar_read: First valid place to read. When ar_read == ar_write then
+ * the array is empty.
+ */
+struct b2r2_core {
+ spinlock_t lock;
+
+ struct b2r2_memory_map *hw;
+
+ u8 op_size;
+ u8 ch_size;
+ u8 pg_size;
+ u8 mg_size;
+ u16 min_req_time;
+ int irq;
+
+ struct device *log_dev;
+
+ struct list_head prio_queue;
+
+ struct b2r2_core_job *active_jobs[B2R2_CORE_QUEUE_NO_OF];
+ unsigned long n_active_jobs;
+
+ unsigned long jiffies_last_active;
+ unsigned long jiffies_last_irq;
+#ifdef HANDLE_TIMEOUTED_JOBS
+ struct delayed_work timeout_work;
+#endif
+ int next_job_id;
+
+ unsigned long clock_request_count;
+ struct timer_list clock_off_timer;
+
+ struct workqueue_struct *work_queue;
+
+ /* Statistics */
+ unsigned long stat_n_irq;
+ unsigned long stat_n_jobs_added;
+ unsigned long stat_n_jobs_removed;
+
+ unsigned long stat_n_jobs_in_prio_list;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_root_dir;
+ struct dentry *debugfs_regs_dir;
+#endif
+
+#ifdef DEBUG_CHECK_ADDREF_RELEASE
+ /* Tracking release bug...*/
+ struct addref_release ar[100];
+ int ar_write;
+ int ar_read;
+#endif
+
+ /* Power management variables */
+ struct mutex domain_lock;
+ struct delayed_work domain_disable_work;
+
+ /*
+ * We need to keep track of both the number of domain_enable/disable()
+ * calls and whether the power was actually turned off, since the
+ * power off is done in a delayed job.
+ */
+ bool domain_enabled;
+ int domain_request_count;
+
+ struct clk *b2r2_clock;
+ struct regulator *b2r2_reg;
+};
+
+/**
+ * b2r2_core - Administration data for B2R2 core (singleton)
+ */
+static struct b2r2_core b2r2_core;
+
+/* Local functions */
+static void check_prio_list(bool atomic);
+static void clear_interrupts(void);
+static void trigger_job(struct b2r2_core_job *job);
+static void exit_job_list(struct list_head *job_list);
+static int get_next_job_id(void);
+static void job_work_function(struct work_struct *ptr);
+static void init_job(struct b2r2_core_job *job);
+static void insert_into_prio_list(struct b2r2_core_job *job);
+static struct b2r2_core_job *find_job_in_list(
+ int job_id,
+ struct list_head *list);
+static struct b2r2_core_job *find_job_in_active_jobs(int job_id);
+static struct b2r2_core_job *find_tag_in_list(
+ int tag,
+ struct list_head *list);
+static struct b2r2_core_job *find_tag_in_active_jobs(int tag);
+
+static int domain_enable(void);
+static void domain_disable(void);
+
+static void stop_queue(enum b2r2_core_queue queue);
+
+#ifdef HANDLE_TIMEOUTED_JOBS
+static void printk_regs(void);
+static int hw_reset(void);
+static void timeout_work_function(struct work_struct *ptr);
+#endif
+
+static void reset_hw_timer(struct b2r2_core_job *job);
+static void start_hw_timer(struct b2r2_core_job *job);
+static void stop_hw_timer(struct b2r2_core_job *job);
+
+static int init_hw(void);
+static void exit_hw(void);
+
+/* Tracking release bug... */
+#ifdef DEBUG_CHECK_ADDREF_RELEASE
+/**
+ * ar_add() - Adds an addref or a release to the array
+ *
+ * @job: The job that has been referenced
+ * @caller: The caller of addref / release
+ * @addref: true if it is an addref else false for release
+ */
+void ar_add(struct b2r2_core_job *job, const char *caller, bool addref)
+{
+ b2r2_core.ar[b2r2_core.ar_write].addref = addref;
+ b2r2_core.ar[b2r2_core.ar_write].job = job;
+ b2r2_core.ar[b2r2_core.ar_write].caller = caller;
+ b2r2_core.ar[b2r2_core.ar_write].ref_count = job->ref_count;
+ b2r2_core.ar_write = (b2r2_core.ar_write + 1) %
+ ARRAY_SIZE(b2r2_core.ar);
+ if (b2r2_core.ar_write == b2r2_core.ar_read)
+ b2r2_core.ar_read = (b2r2_core.ar_read + 1) %
+ ARRAY_SIZE(b2r2_core.ar);
+}
+
+/**
+ * sprintf_ar() - Writes all addref / release to a string buffer
+ *
+ * @buf: Receiving character bufefr
+ * @job: Which job to write or NULL for all
+ *
+ * NOTE! No buffer size check!!
+ */
+char *sprintf_ar(char *buf, struct b2r2_core_job *job)
+{
+ int i;
+ int size = 0;
+
+ for (i = b2r2_core.ar_read;
+ i != b2r2_core.ar_write;
+ i = (i + 1) % ARRAY_SIZE(b2r2_core.ar)) {
+ struct addref_release *ar = &b2r2_core.ar[i];
+ if (!job || job == ar->job)
+ size += sprintf(buf + size,
+ "%s on %p from %s, ref = %d\n",
+ ar->addref ? "addref" : "release",
+ ar->job, ar->caller, ar->ref_count);
+ }
+
+ return buf;
+}
+
+/**
+ * printk_ar() - Writes all addref / release using dev_info
+ *
+ * @job: Which job to write or NULL for all
+ */
+void printk_ar(struct b2r2_core_job *job)
+{
+ int i;
+
+ for (i = b2r2_core.ar_read;
+ i != b2r2_core.ar_write;
+ i = (i + 1) % ARRAY_SIZE(b2r2_core.ar)) {
+ struct addref_release *ar = &b2r2_core.ar[i];
+ if (!job || job == ar->job)
+ b2r2_log_info("%s on %p from %s,"
+ " ref = %d\n",
+ ar->addref ? "addref" : "release",
+ ar->job, ar->caller, ar->ref_count);
+ }
+}
+#endif
+
+/**
+ * internal_job_addref() - Increments the reference count for a job
+ *
+ * @job: Which job to increment reference count for
+ * @caller: Name of function calling addref (for debug)
+ *
+ * Note that b2r2_core.lock _must_ be held
+ */
+static void internal_job_addref(struct b2r2_core_job *job, const char *caller)
+{
+ u32 ref_count;
+
+ b2r2_log_info("%s (%p) (from %s)\n",
+ __func__, job, caller);
+
+ /* Sanity checks */
+ BUG_ON(job == NULL);
+
+ if (job->start_sentinel != START_SENTINEL ||
+ job->end_sentinel != END_SENTINEL ||
+ job->ref_count == 0 || job->ref_count > 10) {
+ b2r2_log_info(
+ "%s: (%p) start=%X end=%X ref_count=%d\n",
+ __func__, job, job->start_sentinel,
+ job->end_sentinel, job->ref_count);
+
+ /* Something is wrong, print the addref / release array */
+#ifdef DEBUG_CHECK_ADDREF_RELEASE
+ printk_ar(NULL);
+#endif
+ }
+
+
+ BUG_ON(job->start_sentinel != START_SENTINEL);
+ BUG_ON(job->end_sentinel != END_SENTINEL);
+
+ /* Do the actual reference count increment */
+ ref_count = ++job->ref_count;
+
+#ifdef DEBUG_CHECK_ADDREF_RELEASE
+ /* Keep track of addref / release */
+ ar_add(job, caller, true);
+#endif
+
+ b2r2_log_info("%s called from %s (%p): Ref Count is %d\n",
+ __func__, caller, job, job->ref_count);
+}
+
+/**
+ * internal_job_release() - Decrements the reference count for a job
+ *
+ * @job: Which job to decrement reference count for
+ * @caller: Name of function calling release (for debug)
+ *
+ * Returns true if job_release should be called by caller
+ * (reference count reached zero).
+ *
+ * Note that b2r2_core.lock _must_ be held
+ */
+bool internal_job_release(struct b2r2_core_job *job, const char *caller)
+{
+ u32 ref_count;
+ bool call_release = false;
+
+ b2r2_log_info("%s (%p) (from %s)\n",
+ __func__, job, caller);
+
+ /* Sanity checks */
+ BUG_ON(job == NULL);
+
+ if (job->start_sentinel != START_SENTINEL ||
+ job->end_sentinel != END_SENTINEL ||
+ job->ref_count == 0 || job->ref_count > 10) {
+ b2r2_log_info(
+ "%s: (%p) start=%X end=%X ref_count=%d\n",
+ __func__, job, job->start_sentinel,
+ job->end_sentinel, job->ref_count);
+
+#ifdef DEBUG_CHECK_ADDREF_RELEASE
+ printk_ar(NULL);
+#endif
+ }
+
+
+ BUG_ON(job->start_sentinel != START_SENTINEL);
+ BUG_ON(job->end_sentinel != END_SENTINEL);
+
+ BUG_ON(job->ref_count == 0 || job->ref_count > 10);
+
+ /* Do the actual decrement */
+ ref_count = --job->ref_count;
+#ifdef DEBUG_CHECK_ADDREF_RELEASE
+ ar_add(job, caller, false);
+#endif
+ b2r2_log_info("%s called from %s (%p) Ref Count is %d\n",
+ __func__, caller, job, ref_count);
+
+ if (!ref_count && job->release) {
+ call_release = true;
+ /* Job will now cease to exist */
+ job->start_sentinel = 0xFFFFFFFF;
+ job->end_sentinel = 0xFFFFFFFF;
+ }
+ return call_release;
+}
+
+
+
+/* Exported functions */
+
+/* b2r2_core.lock _must_ _NOT_ be held when calling this function */
+void b2r2_core_job_addref(struct b2r2_core_job *job, const char *caller)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+ internal_job_addref(job, caller);
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+}
+
+/* b2r2_core.lock _must_ _NOT_ be held when calling this function */
+void b2r2_core_job_release(struct b2r2_core_job *job, const char *caller)
+{
+ unsigned long flags;
+ bool call_release = false;
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+ call_release = internal_job_release(job, caller);
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+
+ if (call_release)
+ job->release(job);
+}
+
+/* b2r2_core.lock _must_ _NOT_ be held when calling this function */
+int b2r2_core_job_add(struct b2r2_core_job *job)
+{
+ unsigned long flags;
+
+ b2r2_log_info("%s (%p)\n", __func__, job);
+
+ /* Enable B2R2 */
+ domain_enable();
+
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+ b2r2_core.stat_n_jobs_added++;
+
+ /* Initialise internal job data */
+ init_job(job);
+
+ /* Initial reference, should be released by caller of this function */
+ job->ref_count = 1;
+
+ /* Insert job into prio list */
+ insert_into_prio_list(job);
+
+ /* Check if we can dispatch job */
+ check_prio_list(false);
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+
+ return 0;
+}
+
+/* b2r2_core.lock _must_ _NOT_ be held when calling this function */
+struct b2r2_core_job *b2r2_core_job_find(int job_id)
+{
+ unsigned long flags;
+ struct b2r2_core_job *job;
+
+ b2r2_log_info("%s (%d)\n", __func__, job_id);
+
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+ /* Look through prio queue */
+ job = find_job_in_list(job_id, &b2r2_core.prio_queue);
+
+ if (!job)
+ job = find_job_in_active_jobs(job_id);
+
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+
+ return job;
+}
+
+/* b2r2_core.lock _must_ _NOT_ be held when calling this function */
+struct b2r2_core_job *b2r2_core_job_find_first_with_tag(int tag)
+{
+ unsigned long flags;
+ struct b2r2_core_job *job;
+
+ b2r2_log_info("%s (%d)\n", __func__, tag);
+
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+ /* Look through prio queue */
+ job = find_tag_in_list(tag, &b2r2_core.prio_queue);
+
+ if (!job)
+ job = find_tag_in_active_jobs(tag);
+
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+
+ return job;
+}
+
+/**
+ * is_job_done() - Spin lock protected check if job is done
+ *
+ * @job: Job to check
+ *
+ * Returns true if job is done or cancelled
+ *
+ * b2r2_core.lock must _NOT_ be held when calling this function
+ */
+static bool is_job_done(struct b2r2_core_job *job)
+{
+ unsigned long flags;
+ bool job_is_done;
+
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+ job_is_done =
+ job->job_state != B2R2_CORE_JOB_QUEUED &&
+ job->job_state != B2R2_CORE_JOB_RUNNING;
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+
+ return job_is_done;
+}
+
+/* b2r2_core.lock _must_ _NOT_ be held when calling this function */
+int b2r2_core_job_wait(struct b2r2_core_job *job)
+{
+ int ret = 0;
+
+ b2r2_log_info("%s (%p)\n", __func__, job);
+ /* Check that we have the job */
+ if (job->job_state == B2R2_CORE_JOB_IDLE) {
+ /* Never or not queued */
+ b2r2_log_info("%s: Job not queued\n", __func__);
+ return -ENOENT;
+ }
+
+ /* Wait for the job to be done */
+ ret = wait_event_interruptible(
+ job->event,
+ is_job_done(job));
+
+ if (ret)
+ b2r2_log_warn(
+ "%s: wait_event_interruptible returns %d, state is %d",
+ __func__, ret, job->job_state);
+ return ret;
+}
+
+/**
+ * cancel_job() - Cancels a job (removes it from prio list or active jobs) and
+ * calls the job callback
+ *
+ * @job: Job to cancel
+ *
+ * Returns true if the job was found and cancelled
+ *
+ * b2r2_core.lock must be held when calling this function
+ */
+static bool cancel_job(struct b2r2_core_job *job)
+{
+ bool found_job = false;
+ bool job_was_active = false;
+
+ /* Remove from prio list */
+ if (job->job_state == B2R2_CORE_JOB_QUEUED) {
+ list_del_init(&job->list);
+ found_job = true;
+ }
+
+ /* Remove from active jobs */
+ if (!found_job) {
+ if (b2r2_core.n_active_jobs > 0) {
+ int i;
+
+ /* Look for timeout:ed jobs and put them in tmp list */
+ for (i = 0;
+ i < ARRAY_SIZE(b2r2_core.active_jobs);
+ i++) {
+ if (b2r2_core.active_jobs[i] == job) {
+ stop_queue((enum b2r2_core_queue)i);
+ stop_hw_timer(job);
+ b2r2_core.active_jobs[i] = NULL;
+ b2r2_core.n_active_jobs--;
+ found_job = true;
+ job_was_active = true;
+ }
+ }
+ }
+ }
+
+
+ /* Handle done list & callback */
+ if (found_job) {
+ /* Job is canceled */
+ job->job_state = B2R2_CORE_JOB_CANCELED;
+
+ queue_work(b2r2_core.work_queue, &job->work);
+
+ /* Statistics */
+ if (!job_was_active)
+ b2r2_core.stat_n_jobs_in_prio_list--;
+
+ }
+
+ return found_job;
+}
+
+/* b2r2_core.lock _must_ _NOT_ be held when calling this function */
+int b2r2_core_job_cancel(struct b2r2_core_job *job)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ b2r2_log_info("%s (%p) (%d)\n", __func__,
+ job, job->job_state);
+ /* Check that we have the job */
+ if (job->job_state == B2R2_CORE_JOB_IDLE) {
+ /* Never or not queued */
+ b2r2_log_info("%s: Job not queued\n", __func__);
+ return -ENOENT;
+ }
+
+ /* Remove from prio list */
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+ cancel_job(job);
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+
+ return ret;
+}
+
+/* LOCAL FUNCTIONS BELOW */
+
+#define B2R2_DOMAIN_DISABLE_TIMEOUT (HZ/100)
+
+static void domain_disable_work_function(struct work_struct *work)
+{
+ if (!mutex_trylock(&b2r2_core.domain_lock))
+ return;
+
+ if (b2r2_core.domain_request_count == 0) {
+ exit_hw();
+ clk_disable(b2r2_core.b2r2_clock);
+ regulator_disable(b2r2_core.b2r2_reg);
+ b2r2_core.domain_enabled = false;
+ }
+
+ mutex_unlock(&b2r2_core.domain_lock);
+}
+
+#define B2R2_REGULATOR_RETRY_COUNT 10
+
+static int domain_enable(void)
+{
+ mutex_lock(&b2r2_core.domain_lock);
+ b2r2_core.domain_request_count++;
+
+ if (!b2r2_core.domain_enabled) {
+ int retry = 0;
+ int ret;
+
+again:
+ /*
+ * Since regulator_enable() may sleep we have to handle
+ * interrupts.
+ */
+ ret = regulator_enable(b2r2_core.b2r2_reg);
+ if ((ret == -EAGAIN) &&
+ ((retry++) < B2R2_REGULATOR_RETRY_COUNT))
+ goto again;
+ else if (ret < 0)
+ goto regulator_enable_failed;
+
+ clk_enable(b2r2_core.b2r2_clock);
+ if (init_hw() < 0)
+ goto init_hw_failed;
+ b2r2_core.domain_enabled = true;
+ }
+
+ mutex_unlock(&b2r2_core.domain_lock);
+
+ return 0;
+
+init_hw_failed:
+ b2r2_log_err("%s: Could not initialize hardware!\n", __func__);
+
+ clk_disable(b2r2_core.b2r2_clock);
+
+ if (regulator_disable(b2r2_core.b2r2_reg) < 0)
+ b2r2_log_err("%s: regulator_disable failed!\n", __func__);
+
+regulator_enable_failed:
+ b2r2_core.domain_request_count--;
+ mutex_unlock(&b2r2_core.domain_lock);
+
+ return -EFAULT;
+}
+
+static void domain_disable(void)
+{
+ mutex_lock(&b2r2_core.domain_lock);
+
+ if (b2r2_core.domain_request_count == 0) {
+ b2r2_log_err("%s: Unbalanced domain_disable()\n", __func__);
+ } else {
+ b2r2_core.domain_request_count--;
+
+ /* Cancel any existing work */
+ cancel_delayed_work_sync(&b2r2_core.domain_disable_work);
+
+ /* Add a work to disable the power and clock after a delay */
+ queue_delayed_work(b2r2_core.work_queue,
+ &b2r2_core.domain_disable_work,
+ B2R2_DOMAIN_DISABLE_TIMEOUT);
+ }
+
+ mutex_unlock(&b2r2_core.domain_lock);
+}
+
+/**
+ * stop_queue() - Stops the specified queue.
+ */
+static void stop_queue(enum b2r2_core_queue queue)
+{
+ /* TODO: Implement! If this function is not implemented canceled jobs will
+ use b2r2 which is a waste of resources. Not stopping jobs will also screw up
+ the hardware timing, the job the canceled job intrerrupted (if any) will be
+ billed for the time between the point where the job is cancelled and when it
+ stops. */
+}
+
+/**
+ * exit_job_list() - Empties a job queue by canceling the jobs
+ *
+ * b2r2_core.lock _must_ be held when calling this function
+ */
+static void exit_job_list(struct list_head *job_queue)
+{
+ while (!list_empty(job_queue)) {
+ struct b2r2_core_job *job =
+ list_entry(job_queue->next,
+ struct b2r2_core_job,
+ list);
+ /* Add reference to prevent job from disappearing
+ in the middle of our work, released below */
+ internal_job_addref(job, __func__);
+
+ cancel_job(job);
+
+ /* Matching release to addref above */
+ internal_job_release(job, __func__);
+
+ }
+}
+
+/**
+ * get_next_job_id() - Return a new job id.
+ */
+static int get_next_job_id(void)
+{
+ int job_id;
+
+ if (b2r2_core.next_job_id < 1)
+ b2r2_core.next_job_id = 1;
+ job_id = b2r2_core.next_job_id++;
+
+ return job_id;
+}
+
+/**
+ * job_work_function() - Work queue function that calls callback(s) and
+ * checks if B2R2 can accept a new job
+ *
+ * @ptr: Pointer to work struct (embedded in struct b2r2_core_job)
+ */
+static void job_work_function(struct work_struct *ptr)
+{
+ unsigned long flags;
+ struct b2r2_core_job *job = container_of(
+ ptr, struct b2r2_core_job, work);
+
+ /* Disable B2R2 */
+ domain_disable();
+
+ /* Release resources */
+ if (job->release_resources)
+ job->release_resources(job, false);
+
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+
+ /* Dispatch a new job if possible */
+ check_prio_list(false);
+
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+
+ /* Tell the client */
+ if (job->callback)
+ job->callback(job);
+
+ /* Drop our reference, matches the
+ addref in handle_queue_event or b2r2_core_job_cancel */
+ b2r2_core_job_release(job, __func__);
+}
+
+#ifdef HANDLE_TIMEOUTED_JOBS
+/**
+ * timeout_work_function() - Work queue function that checks for
+ * timeout:ed jobs. B2R2 might silently refuse
+ * to execute some jobs, i.e. SRC2 fill
+ *
+ * @ptr: Pointer to work struct (embedded in struct b2r2_core)
+ *
+ */
+static void timeout_work_function(struct work_struct *ptr)
+{
+ unsigned long flags;
+ struct list_head job_list;
+
+ INIT_LIST_HEAD(&job_list);
+
+ /* Cancel all jobs if too long time since last irq */
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+ if (b2r2_core.n_active_jobs > 0) {
+ unsigned long diff =
+ (long) jiffies - (long) b2r2_core.jiffies_last_irq;
+ if (diff > HZ/2) {
+ /* Active jobs and more than a second since last irq! */
+ int i;
+
+ /* Look for timeout:ed jobs and put them in tmp list. It's
+ important that the application queues are killed in order
+ of decreasing priority */
+ for (i = 0;
+ i < ARRAY_SIZE(b2r2_core.active_jobs);
+ i++) {
+ struct b2r2_core_job *job =
+ b2r2_core.active_jobs[i];
+
+ if (job) {
+ stop_hw_timer(job);
+
+ b2r2_core.active_jobs[i] = NULL;
+ b2r2_core.n_active_jobs--;
+ list_add_tail(&job->list,
+ &job_list);
+ }
+ }
+
+ /* Print the B2R2 register and reset B2R2 */
+ printk_regs();
+ hw_reset();
+ }
+ }
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+
+ /* Handle timeout:ed jobs */
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+ while (!list_empty(&job_list)) {
+ struct b2r2_core_job *job =
+ list_entry(job_list.next,
+ struct b2r2_core_job,
+ list);
+
+ b2r2_log_warn("%s: Job timeout\n", __func__);
+
+ list_del_init(&job->list);
+
+ /* Job is cancelled */
+ job->job_state = B2R2_CORE_JOB_CANCELED;
+
+ /* Handle done */
+ wake_up_interruptible(&job->event);
+
+ /* Job callbacks handled via work queue */
+ queue_work(b2r2_core.work_queue, &job->work);
+ }
+
+ /* Requeue delayed work */
+ if (b2r2_core.n_active_jobs)
+ queue_delayed_work(
+ b2r2_core.work_queue,
+ &b2r2_core.timeout_work, HZ/2);
+
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+}
+#endif
+
+/**
+ * reset_hw_timer() - Resets a job's hardware timer. Must be called before
+ * the timer is used.
+ *
+ * @job: Pointer to job struct
+ *
+ * b2r2_core.lock _must_ be held when calling this function
+ */
+static void reset_hw_timer(struct b2r2_core_job *job)
+{
+ job->nsec_active_in_hw = 0;
+}
+
+/**
+ * start_hw_timer() - Times how long a job spends in hardware (active).
+ * Should be called immediatly before starting the
+ * hardware.
+ *
+ * @job: Pointer to job struct
+ *
+ * b2r2_core.lock _must_ be held when calling this function
+ */
+static void start_hw_timer(struct b2r2_core_job *job)
+{
+ job->hw_start_time = b2r2_get_curr_nsec();
+}
+
+/**
+ * stop_hw_timer() - Times how long a job spends in hardware (active).
+ * Should be called immediatly after the hardware has
+ * finished.
+ *
+ * @job: Pointer to job struct
+ *
+ * b2r2_core.lock _must_ be held when calling this function
+ */
+static void stop_hw_timer(struct b2r2_core_job *job)
+{
+ /* Assumes only app queues are used, which is the case right now. */
+ /* Not 100% accurate. When a higher prio job interrupts a lower prio job it does
+ so after the current node of the low prio job has finished. Currently we can not
+ sense when the actual switch takes place so the time reported for a job that
+ interrupts a lower prio job will on average contain the time it takes to process
+ half a node in the lower prio job in addition to the time it takes to process the
+ job's own nodes. This could possibly be solved by adding node notifications but
+ that would involve a significant amount of work and consume system resources due
+ to the extra interrupts. */
+ /* If a job takes more than ~2s (absolute time, including idleing in the hardware)
+ the state of the hardware timer will be corrupted and it will not report valid
+ values until b2r2 becomes idle (no active jobs on any queues). The maximum length
+ can possibly be increased by using 64 bit integers. */
+
+ int i;
+
+ u32 stop_time_raw = b2r2_get_curr_nsec();
+ /* We'll add an offset to all positions in time to make the current time equal to
+ 0xFFFFFFFF. This way we can compare positions in time to each other without having
+ to wory about wrapping (so long as all positions in time are in the past). */
+ u32 stop_time = 0xFFFFFFFF;
+ u32 time_pos_offset = 0xFFFFFFFF - stop_time_raw;
+ u32 nsec_in_hw = stop_time - (job->hw_start_time + time_pos_offset);
+ job->nsec_active_in_hw += (s32)nsec_in_hw;
+
+ /* Check if we have delayed the start of higher prio jobs. Can happen as queue
+ switching only can be done between nodes. */
+ for (i = (int)job->queue - 1; i >= (int)B2R2_CORE_QUEUE_AQ1; i--) {
+ struct b2r2_core_job *queue_active_job = b2r2_core.active_jobs[i];
+ if (NULL == queue_active_job)
+ continue;
+
+ queue_active_job->hw_start_time = stop_time_raw;
+ }
+
+ /* Check if the job has stolen time from lower prio jobs */
+ for (i = (int)job->queue + 1; i < B2R2_NUM_APPLICATIONS_QUEUES; i++) {
+ struct b2r2_core_job *queue_active_job = b2r2_core.active_jobs[i];
+ u32 queue_active_job_hw_start_time;
+
+ if (NULL == queue_active_job)
+ continue;
+
+ queue_active_job_hw_start_time = queue_active_job->hw_start_time + time_pos_offset;
+
+ if (queue_active_job_hw_start_time < stop_time) {
+ u32 queue_active_job_nsec_in_hw = stop_time - queue_active_job_hw_start_time;
+ u32 num_stolen_nsec = min(queue_active_job_nsec_in_hw, nsec_in_hw);
+
+ queue_active_job->nsec_active_in_hw -= (s32)num_stolen_nsec;
+
+ nsec_in_hw -= num_stolen_nsec;
+ stop_time -= num_stolen_nsec;
+ }
+
+ if (0 == nsec_in_hw)
+ break;
+ }
+}
+
+/**
+ * init_job() - Initializes a job structure from filled in client data.
+ * Reference count will be set to 1
+ *
+ * @job: Job to initialize
+ */
+static void init_job(struct b2r2_core_job *job)
+{
+ job->start_sentinel = START_SENTINEL;
+ job->end_sentinel = END_SENTINEL;
+
+ /* Get a job id*/
+ job->job_id = get_next_job_id();
+
+ /* Job is idle, never queued */
+ job->job_state = B2R2_CORE_JOB_IDLE;
+
+ /* Initialize internal data */
+ INIT_LIST_HEAD(&job->list);
+ init_waitqueue_head(&job->event);
+ INIT_WORK(&job->work, job_work_function);
+
+ /* Map given prio to B2R2 queues */
+ if (job->prio < B2R2_CORE_LOWEST_PRIO)
+ job->prio = B2R2_CORE_LOWEST_PRIO;
+ else if (job->prio > B2R2_CORE_HIGHEST_PRIO)
+ job->prio = B2R2_CORE_HIGHEST_PRIO;
+
+ if (job->prio > 10) {
+ job->queue = B2R2_CORE_QUEUE_AQ1;
+ job->interrupt_context =
+ (B2R2BLT_ITSAQ1_LNA_Reached);
+ job->control = (B2R2_AQ_Enab | B2R2_AQ_PRIOR_3);
+ } else if (job->prio > 0) {
+ job->queue = B2R2_CORE_QUEUE_AQ2;
+ job->interrupt_context =
+ (B2R2BLT_ITSAQ2_LNA_Reached);
+ job->control = (B2R2_AQ_Enab | B2R2_AQ_PRIOR_2);
+ } else if (job->prio > -10) {
+ job->queue = B2R2_CORE_QUEUE_AQ3;
+ job->interrupt_context =
+ (B2R2BLT_ITSAQ3_LNA_Reached);
+ job->control = (B2R2_AQ_Enab | B2R2_AQ_PRIOR_1);
+ } else {
+ job->queue = B2R2_CORE_QUEUE_AQ4;
+ job->interrupt_context =
+ (B2R2BLT_ITSAQ4_LNA_Reached);
+ job->control = (B2R2_AQ_Enab | B2R2_AQ_PRIOR_0);
+ }
+}
+
+/**
+ * clear_interrupts() - Disables all interrupts
+ *
+ * b2r2_core.lock must be held
+ */
+static void clear_interrupts(void)
+{
+ writel(0x0, &b2r2_core.hw->BLT_ITM0);
+ writel(0x0, &b2r2_core.hw->BLT_ITM1);
+ writel(0x0, &b2r2_core.hw->BLT_ITM2);
+ writel(0x0, &b2r2_core.hw->BLT_ITM3);
+}
+
+/**
+ * insert_into_prio_list() - Inserts the job into the sorted list of jobs.
+ * The list is sorted by priority.
+ *
+ * @job: Job to insert
+ *
+ * b2r2_core.lock must be held
+ */
+static void insert_into_prio_list(struct b2r2_core_job *job)
+{
+ /* Ref count is increased when job put in list,
+ should be released when job is removed from list */
+ internal_job_addref(job, __func__);
+
+ b2r2_core.stat_n_jobs_in_prio_list++;
+
+ /* Sort in the job */
+ if (list_empty(&b2r2_core.prio_queue))
+ list_add_tail(&job->list, &b2r2_core.prio_queue);
+ else {
+ struct b2r2_core_job *first_job =
+ list_entry(b2r2_core.prio_queue.next,
+ struct b2r2_core_job, list);
+ struct b2r2_core_job *last_job =
+ list_entry(b2r2_core.prio_queue.prev,
+ struct b2r2_core_job, list);
+
+ /* High prio job? */
+ if (job->prio > first_job->prio)
+ /* Insert first */
+ list_add(&job->list, &b2r2_core.prio_queue);
+ else if (job->prio <= last_job->prio)
+ /* Insert last */
+ list_add_tail(&job->list, &b2r2_core.prio_queue);
+ else {
+ /* We need to find where to put it */
+ struct list_head *ptr;
+
+ list_for_each(ptr, &b2r2_core.prio_queue) {
+ struct b2r2_core_job *list_job =
+ list_entry(ptr, struct b2r2_core_job,
+ list);
+ if (job->prio > list_job->prio) {
+ /* Add before */
+ list_add_tail(&job->list,
+ &list_job->list);
+ break;
+ }
+ }
+ }
+ }
+ /* The job is now queued */
+ job->job_state = B2R2_CORE_JOB_QUEUED;
+}
+
+/**
+ * check_prio_list() - Checks if the first job(s) in the prio list can
+ * be dispatched to B2R2
+ *
+ * @atomic: true if in atomic context (i.e. interrupt context)
+ *
+ * b2r2_core.lock must be held
+ */
+static void check_prio_list(bool atomic)
+{
+ bool dispatched_job;
+ int n_dispatched = 0;
+
+ /* Do we have anything in our prio list? */
+ do {
+ dispatched_job = false;
+ if (!list_empty(&b2r2_core.prio_queue)) {
+ /* The first job waiting */
+ struct b2r2_core_job *job =
+ list_first_entry(&b2r2_core.prio_queue,
+ struct b2r2_core_job,
+ list);
+
+ /* Is the B2R2 queue available? */
+ if (b2r2_core.active_jobs[job->queue] == NULL) {
+ /* Can we acquire resources? */
+ if (!job->acquire_resources ||
+ job->acquire_resources(job, atomic) == 0) {
+ /* Ok to dispatch job */
+
+ /* Remove from list */
+ list_del_init(&job->list);
+
+ /* The job is now active */
+ b2r2_core.active_jobs[job->queue] = job;
+ b2r2_core.n_active_jobs++;
+ job->jiffies = jiffies;
+ b2r2_core.jiffies_last_active =
+ jiffies;
+
+ /* Kick off B2R2 */
+ trigger_job(job);
+
+ dispatched_job = true;
+ n_dispatched++;
+
+#ifdef HANDLE_TIMEOUTED_JOBS
+ /* Check in one half second
+ if it hangs */
+ queue_delayed_work(
+ b2r2_core.work_queue,
+ &b2r2_core.timeout_work,
+ HZ/2);
+#endif
+ } else {
+ /* No resources */
+ if (!atomic &&
+ b2r2_core.n_active_jobs == 0) {
+ b2r2_log_warn(
+ "%s: No resource",
+ __func__);
+ cancel_job(job);
+ }
+ }
+ }
+ }
+ } while (dispatched_job);
+
+ b2r2_core.stat_n_jobs_in_prio_list -= n_dispatched;
+}
+
+/**
+ * find_job_in_list() - Finds job with job_id in list
+ *
+ * @jobid: Job id to find
+ * @list: List to find job id in
+ *
+ * Reference count will be incremented for found job.
+ *
+ * b2r2_core.lock must be held
+ */
+static struct b2r2_core_job *find_job_in_list(int job_id,
+ struct list_head *list)
+{
+ struct list_head *ptr;
+
+ list_for_each(ptr, list) {
+ struct b2r2_core_job *job =
+ list_entry(ptr, struct b2r2_core_job, list);
+ if (job->job_id == job_id) {
+ /* Increase reference count, should be released by
+ the caller of b2r2_core_job_find */
+ internal_job_addref(job, __func__);
+ return job;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * find_job_in_active_jobs() - Finds job in active job queues
+ *
+ * @jobid: Job id to find
+ *
+ * Reference count will be incremented for found job.
+ *
+ * b2r2_core.lock must be held
+ */
+static struct b2r2_core_job *find_job_in_active_jobs(int job_id)
+{
+ int i;
+ struct b2r2_core_job *found_job = NULL;
+
+ if (b2r2_core.n_active_jobs) {
+ for (i = 0; i < ARRAY_SIZE(b2r2_core.active_jobs); i++) {
+ struct b2r2_core_job *job = b2r2_core.active_jobs[i];
+
+ if (job && job->job_id == job_id) {
+ internal_job_addref(job, __func__);
+ found_job = job;
+ break;
+ }
+ }
+ }
+ return found_job;
+}
+
+/**
+ * find_tag_in_list() - Finds first job with tag in list
+ *
+ * @tag: Tag to find
+ * @list: List to find job id in
+ *
+ * Reference count will be incremented for found job.
+ *
+ * b2r2_core.lock must be held
+ */
+static struct b2r2_core_job *find_tag_in_list(int tag, struct list_head *list)
+{
+ struct list_head *ptr;
+
+ list_for_each(ptr, list) {
+ struct b2r2_core_job *job =
+ list_entry(ptr, struct b2r2_core_job, list);
+ if (job->tag == tag) {
+ /* Increase reference count, should be released by
+ the caller of b2r2_core_job_find */
+ internal_job_addref(job, __func__);
+ return job;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * find_tag_in_active_jobs() - Finds job with tag in active job queues
+ *
+ * @tag: Tag to find
+ *
+ * Reference count will be incremented for found job.
+ *
+ * b2r2_core.lock must be held
+ */
+static struct b2r2_core_job *find_tag_in_active_jobs(int tag)
+{
+ int i;
+ struct b2r2_core_job *found_job = NULL;
+
+ if (b2r2_core.n_active_jobs) {
+ for (i = 0; i < ARRAY_SIZE(b2r2_core.active_jobs); i++) {
+ struct b2r2_core_job *job = b2r2_core.active_jobs[i];
+
+ if (job && job->tag == tag) {
+ internal_job_addref(job, __func__);
+ found_job = job;
+ break;
+ }
+ }
+ }
+ return found_job;
+}
+
+
+#ifdef HANDLE_TIMEOUTED_JOBS
+/**
+ * hw_reset() - Resets B2R2 hardware
+ *
+ * b2r2_core.lock must be held
+ */
+static int hw_reset(void)
+{
+ u32 uTimeOut = B2R2_DRIVER_TIMEOUT_VALUE;
+
+ /* Tell B2R2 to reset */
+ writel(readl(&b2r2_core.hw->BLT_CTL) | B2R2BLT_CTLGLOBAL_soft_reset,
+ &b2r2_core.hw->BLT_CTL);
+ writel(0x00000000, &b2r2_core.hw->BLT_CTL);
+
+ b2r2_log_info("wait for B2R2 to be idle..\n");
+
+ /** Wait for B2R2 to be idle (on a timeout rather than while loop) */
+ while ((uTimeOut > 0) &&
+ ((readl(&b2r2_core.hw->BLT_STA1) &
+ B2R2BLT_STA1BDISP_IDLE) == 0x0))
+ uTimeOut--;
+
+ if (uTimeOut == 0) {
+ b2r2_log_warn(
+ "error-> after software reset B2R2 is not idle\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+
+}
+#endif
+
+/**
+ * trigger_job() - Put job in B2R2 HW queue
+ *
+ * @job: Job to trigger
+ *
+ * b2r2_core.lock must be held
+ */
+static void trigger_job(struct b2r2_core_job *job)
+{
+ /* Debug prints */
+ b2r2_log_info("queue 0x%x \n", job->queue);
+ b2r2_log_info("BLT TRIG_IP 0x%x (first node)\n",
+ job->first_node_address);
+ b2r2_log_info("BLT LNA_CTL 0x%x (last node)\n",
+ job->last_node_address);
+ b2r2_log_info("BLT TRIG_CTL 0x%x \n", job->control);
+ b2r2_log_info("BLT PACE_CTL 0x%x \n", job->pace_control);
+
+ reset_hw_timer(job);
+ job->job_state = B2R2_CORE_JOB_RUNNING;
+
+ /* Enable interrupt */
+ writel(readl(&b2r2_core.hw->BLT_ITM0) | job->interrupt_context,
+ &b2r2_core.hw->BLT_ITM0);
+
+ writel(min_t(u8, max_t(u8, b2r2_core.op_size, B2R2_PLUG_OPCODE_SIZE_8),
+ B2R2_PLUG_OPCODE_SIZE_64),
+ &b2r2_core.hw->PLUGS1_OP2);
+ writel(min_t(u8, b2r2_core.ch_size, B2R2_PLUG_CHUNK_SIZE_128),
+ &b2r2_core.hw->PLUGS1_CHZ);
+ writel(min_t(u8, b2r2_core.mg_size, B2R2_PLUG_MESSAGE_SIZE_128) |
+ (b2r2_core.min_req_time << 16),
+ &b2r2_core.hw->PLUGS1_MSZ);
+ writel(min_t(u8, b2r2_core.pg_size, B2R2_PLUG_PAGE_SIZE_256),
+ &b2r2_core.hw->PLUGS1_PGZ);
+
+ writel(min_t(u8, max_t(u8, b2r2_core.op_size, B2R2_PLUG_OPCODE_SIZE_8),
+ B2R2_PLUG_OPCODE_SIZE_64),
+ &b2r2_core.hw->PLUGS2_OP2);
+ writel(min_t(u8, b2r2_core.ch_size, B2R2_PLUG_CHUNK_SIZE_128),
+ &b2r2_core.hw->PLUGS2_CHZ);
+ writel(min_t(u8, b2r2_core.mg_size, B2R2_PLUG_MESSAGE_SIZE_128) |
+ (b2r2_core.min_req_time << 16),
+ &b2r2_core.hw->PLUGS2_MSZ);
+ writel(min_t(u8, b2r2_core.pg_size, B2R2_PLUG_PAGE_SIZE_256),
+ &b2r2_core.hw->PLUGS2_PGZ);
+
+ writel(min_t(u8, max_t(u8, b2r2_core.op_size, B2R2_PLUG_OPCODE_SIZE_8),
+ B2R2_PLUG_OPCODE_SIZE_64),
+ &b2r2_core.hw->PLUGS3_OP2);
+ writel(min_t(u8, b2r2_core.ch_size, B2R2_PLUG_CHUNK_SIZE_128),
+ &b2r2_core.hw->PLUGS3_CHZ);
+ writel(min_t(u8, b2r2_core.mg_size, B2R2_PLUG_MESSAGE_SIZE_128) |
+ (b2r2_core.min_req_time << 16),
+ &b2r2_core.hw->PLUGS3_MSZ);
+ writel(min_t(u8, b2r2_core.pg_size, B2R2_PLUG_PAGE_SIZE_256),
+ &b2r2_core.hw->PLUGS3_PGZ);
+
+ writel(min_t(u8, max_t(u8, b2r2_core.op_size, B2R2_PLUG_OPCODE_SIZE_8),
+ B2R2_PLUG_OPCODE_SIZE_64),
+ &b2r2_core.hw->PLUGT_OP2);
+ writel(min_t(u8, b2r2_core.ch_size, B2R2_PLUG_CHUNK_SIZE_128),
+ &b2r2_core.hw->PLUGT_CHZ);
+ writel(min_t(u8, b2r2_core.mg_size, B2R2_PLUG_MESSAGE_SIZE_128) |
+ (b2r2_core.min_req_time << 16),
+ &b2r2_core.hw->PLUGT_MSZ);
+ writel(min_t(u8, b2r2_core.pg_size, B2R2_PLUG_PAGE_SIZE_256),
+ &b2r2_core.hw->PLUGT_PGZ);
+
+ /* B2R2 kicks off when LNA is written, LNA write must be last! */
+ switch (job->queue) {
+ case B2R2_CORE_QUEUE_CQ1:
+ writel(job->first_node_address, &b2r2_core.hw->BLT_CQ1_TRIG_IP);
+ writel(job->control, &b2r2_core.hw->BLT_CQ1_TRIG_CTL);
+ writel(job->pace_control, &b2r2_core.hw->BLT_CQ1_PACE_CTL);
+ break;
+
+ case B2R2_CORE_QUEUE_CQ2:
+ writel(job->first_node_address, &b2r2_core.hw->BLT_CQ2_TRIG_IP);
+ writel(job->control, &b2r2_core.hw->BLT_CQ2_TRIG_CTL);
+ writel(job->pace_control, &b2r2_core.hw->BLT_CQ2_PACE_CTL);
+ break;
+
+ case B2R2_CORE_QUEUE_AQ1:
+ writel(job->control, &b2r2_core.hw->BLT_AQ1_CTL);
+ writel(job->first_node_address, &b2r2_core.hw->BLT_AQ1_IP);
+ wmb();
+ start_hw_timer(job);
+ writel(job->last_node_address, &b2r2_core.hw->BLT_AQ1_LNA);
+ break;
+
+ case B2R2_CORE_QUEUE_AQ2:
+ writel(job->control, &b2r2_core.hw->BLT_AQ2_CTL);
+ writel(job->first_node_address, &b2r2_core.hw->BLT_AQ2_IP);
+ wmb();
+ start_hw_timer(job);
+ writel(job->last_node_address, &b2r2_core.hw->BLT_AQ2_LNA);
+ break;
+
+ case B2R2_CORE_QUEUE_AQ3:
+ writel(job->control, &b2r2_core.hw->BLT_AQ3_CTL);
+ writel(job->first_node_address, &b2r2_core.hw->BLT_AQ3_IP);
+ wmb();
+ start_hw_timer(job);
+ writel(job->last_node_address, &b2r2_core.hw->BLT_AQ3_LNA);
+ break;
+
+ case B2R2_CORE_QUEUE_AQ4:
+ writel(job->control, &b2r2_core.hw->BLT_AQ4_CTL);
+ writel(job->first_node_address, &b2r2_core.hw->BLT_AQ4_IP);
+ wmb();
+ start_hw_timer(job);
+ writel(job->last_node_address, &b2r2_core.hw->BLT_AQ4_LNA);
+ break;
+
+ /** Handle the default case */
+ default:
+ break;
+
+ } /* end switch */
+
+}
+
+/**
+ * handle_queue_event() - Handles interrupt event for specified B2R2 queue
+ *
+ * @queue: Queue to handle event for
+ *
+ * b2r2_core.lock must be held
+ */
+static void handle_queue_event(enum b2r2_core_queue queue)
+{
+ struct b2r2_core_job *job;
+
+ job = b2r2_core.active_jobs[queue];
+ if (job) {
+ if (job->job_state != B2R2_CORE_JOB_RUNNING)
+ /* Should be running
+ Severe error. TBD */
+ b2r2_log_warn(
+ "%s: Job is not running", __func__);
+
+ stop_hw_timer(job);
+
+ /* Remove from queue */
+ BUG_ON(b2r2_core.n_active_jobs == 0);
+ b2r2_core.active_jobs[queue] = NULL;
+ b2r2_core.n_active_jobs--;
+ }
+
+ if (!job) {
+ /* No job, error? */
+ b2r2_log_warn("%s: No job", __func__);
+ return;
+ }
+
+
+ /* Atomic context release resources, release resources will
+ be called again later from process context (work queue) */
+ if (job->release_resources)
+ job->release_resources(job, true);
+
+ /* Job is done */
+ job->job_state = B2R2_CORE_JOB_DONE;
+
+ /* Handle done */
+ wake_up_interruptible(&job->event);
+
+ /* Dispatch to work queue to handle callbacks */
+ queue_work(b2r2_core.work_queue, &job->work);
+}
+
+/**
+ * process_events() - Handles interrupt events
+ *
+ * @status: Contents of the B2R2 ITS register
+ */
+static void process_events(u32 status)
+{
+ u32 mask = 0xF;
+ u32 disable_itm_mask = 0;
+
+ b2r2_log_info("Enters process_events \n");
+ b2r2_log_info("status 0x%x \n", status);
+
+ /* Composition queue 1 */
+ if (status & mask) {
+ handle_queue_event(B2R2_CORE_QUEUE_CQ1);
+ disable_itm_mask |= mask;
+ }
+ mask <<= 4;
+
+ /* Composition queue 2 */
+ if (status & mask) {
+ handle_queue_event(B2R2_CORE_QUEUE_CQ2);
+ disable_itm_mask |= mask;
+ }
+ mask <<= 8;
+
+ /* Application queue 1 */
+ if (status & mask) {
+ handle_queue_event(B2R2_CORE_QUEUE_AQ1);
+ disable_itm_mask |= mask;
+ }
+ mask <<= 4;
+
+ /* Application queue 2 */
+ if (status & mask) {
+ handle_queue_event(B2R2_CORE_QUEUE_AQ2);
+ disable_itm_mask |= mask;
+ }
+ mask <<= 4;
+
+ /* Application queue 3 */
+ if (status & mask) {
+ handle_queue_event(B2R2_CORE_QUEUE_AQ3);
+ disable_itm_mask |= mask;
+ }
+ mask <<= 4;
+
+ /* Application queue 4 */
+ if (status & mask) {
+ handle_queue_event(B2R2_CORE_QUEUE_AQ4);
+ disable_itm_mask |= mask;
+ }
+
+ /* Clear received interrupt flags */
+ writel(status, &b2r2_core.hw->BLT_ITS);
+ /* Disable handled interrupts */
+ writel(readl(&b2r2_core.hw->BLT_ITM0) & ~disable_itm_mask,
+ &b2r2_core.hw->BLT_ITM0);
+
+ b2r2_log_info("Returns process_events \n");
+}
+
+/**
+ * b2r2_irq_handler() - B2R2 interrupt handler
+ *
+ * @irq: Interrupt number (not used)
+ * @x: ??? (Not used)
+ */
+static irqreturn_t b2r2_irq_handler(int irq, void *x)
+{
+ unsigned long flags;
+
+ /* Spin lock is need in irq handler (SMP) */
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+
+ /* Make sure that we have a clock */
+
+ /* Remember time for last irq (for timeout mgmt) */
+ b2r2_core.jiffies_last_irq = jiffies;
+ b2r2_core.stat_n_irq++;
+
+ /* Handle the interrupt(s) */
+ process_events(readl(&b2r2_core.hw->BLT_ITS));
+
+ /* Check if we can dispatch new jobs */
+ check_prio_list(true);
+
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+
+#ifdef CONFIG_DEBUG_FS
+/**
+ * struct debugfs_reg - Represents one B2R2 register in debugfs
+ *
+ * @name: Register name
+ * @offset: Byte offset in B2R2 for register
+ */
+struct debugfs_reg {
+ const char name[30];
+ u32 offset;
+};
+
+/**
+ * debugfs_regs - Array of B2R2 debugfs registers
+ */
+static const struct debugfs_reg debugfs_regs[] = {
+ {"BLT_SSBA17", offsetof(struct b2r2_memory_map, BLT_SSBA17)},
+ {"BLT_SSBA18", offsetof(struct b2r2_memory_map, BLT_SSBA18)},
+ {"BLT_SSBA19", offsetof(struct b2r2_memory_map, BLT_SSBA19)},
+ {"BLT_SSBA20", offsetof(struct b2r2_memory_map, BLT_SSBA20)},
+ {"BLT_SSBA21", offsetof(struct b2r2_memory_map, BLT_SSBA21)},
+ {"BLT_SSBA22", offsetof(struct b2r2_memory_map, BLT_SSBA22)},
+ {"BLT_SSBA23", offsetof(struct b2r2_memory_map, BLT_SSBA23)},
+ {"BLT_SSBA24", offsetof(struct b2r2_memory_map, BLT_SSBA24)},
+ {"BLT_STBA5", offsetof(struct b2r2_memory_map, BLT_STBA5)},
+ {"BLT_STBA6", offsetof(struct b2r2_memory_map, BLT_STBA6)},
+ {"BLT_STBA7", offsetof(struct b2r2_memory_map, BLT_STBA7)},
+ {"BLT_STBA8", offsetof(struct b2r2_memory_map, BLT_STBA8)},
+ {"BLT_CTL", offsetof(struct b2r2_memory_map, BLT_CTL)},
+ {"BLT_ITS", offsetof(struct b2r2_memory_map, BLT_ITS)},
+ {"BLT_STA1", offsetof(struct b2r2_memory_map, BLT_STA1)},
+ {"BLT_SSBA1", offsetof(struct b2r2_memory_map, BLT_SSBA1)},
+ {"BLT_SSBA2", offsetof(struct b2r2_memory_map, BLT_SSBA2)},
+ {"BLT_SSBA3", offsetof(struct b2r2_memory_map, BLT_SSBA3)},
+ {"BLT_SSBA4", offsetof(struct b2r2_memory_map, BLT_SSBA4)},
+ {"BLT_SSBA5", offsetof(struct b2r2_memory_map, BLT_SSBA5)},
+ {"BLT_SSBA6", offsetof(struct b2r2_memory_map, BLT_SSBA6)},
+ {"BLT_SSBA7", offsetof(struct b2r2_memory_map, BLT_SSBA7)},
+ {"BLT_SSBA8", offsetof(struct b2r2_memory_map, BLT_SSBA8)},
+ {"BLT_STBA1", offsetof(struct b2r2_memory_map, BLT_STBA1)},
+ {"BLT_STBA2", offsetof(struct b2r2_memory_map, BLT_STBA2)},
+ {"BLT_STBA3", offsetof(struct b2r2_memory_map, BLT_STBA3)},
+ {"BLT_STBA4", offsetof(struct b2r2_memory_map, BLT_STBA4)},
+ {"BLT_CQ1_TRIG_IP", offsetof(struct b2r2_memory_map, BLT_CQ1_TRIG_IP)},
+ {"BLT_CQ1_TRIG_CTL", offsetof(struct b2r2_memory_map,
+ BLT_CQ1_TRIG_CTL)},
+ {"BLT_CQ1_PACE_CTL", offsetof(struct b2r2_memory_map,
+ BLT_CQ1_PACE_CTL)},
+ {"BLT_CQ1_IP", offsetof(struct b2r2_memory_map, BLT_CQ1_IP)},
+ {"BLT_CQ2_TRIG_IP", offsetof(struct b2r2_memory_map, BLT_CQ2_TRIG_IP)},
+ {"BLT_CQ2_TRIG_CTL", offsetof(struct b2r2_memory_map,
+ BLT_CQ2_TRIG_CTL)},
+ {"BLT_CQ2_PACE_CTL", offsetof(struct b2r2_memory_map,
+ BLT_CQ2_PACE_CTL)},
+ {"BLT_CQ2_IP", offsetof(struct b2r2_memory_map, BLT_CQ2_IP)},
+ {"BLT_AQ1_CTL", offsetof(struct b2r2_memory_map, BLT_AQ1_CTL)},
+ {"BLT_AQ1_IP", offsetof(struct b2r2_memory_map, BLT_AQ1_IP)},
+ {"BLT_AQ1_LNA", offsetof(struct b2r2_memory_map, BLT_AQ1_LNA)},
+ {"BLT_AQ1_STA", offsetof(struct b2r2_memory_map, BLT_AQ1_STA)},
+ {"BLT_AQ2_CTL", offsetof(struct b2r2_memory_map, BLT_AQ2_CTL)},
+ {"BLT_AQ2_IP", offsetof(struct b2r2_memory_map, BLT_AQ2_IP)},
+ {"BLT_AQ2_LNA", offsetof(struct b2r2_memory_map, BLT_AQ2_LNA)},
+ {"BLT_AQ2_STA", offsetof(struct b2r2_memory_map, BLT_AQ2_STA)},
+ {"BLT_AQ3_CTL", offsetof(struct b2r2_memory_map, BLT_AQ3_CTL)},
+ {"BLT_AQ3_IP", offsetof(struct b2r2_memory_map, BLT_AQ3_IP)},
+ {"BLT_AQ3_LNA", offsetof(struct b2r2_memory_map, BLT_AQ3_LNA)},
+ {"BLT_AQ3_STA", offsetof(struct b2r2_memory_map, BLT_AQ3_STA)},
+ {"BLT_AQ4_CTL", offsetof(struct b2r2_memory_map, BLT_AQ4_CTL)},
+ {"BLT_AQ4_IP", offsetof(struct b2r2_memory_map, BLT_AQ4_IP)},
+ {"BLT_AQ4_LNA", offsetof(struct b2r2_memory_map, BLT_AQ4_LNA)},
+ {"BLT_AQ4_STA", offsetof(struct b2r2_memory_map, BLT_AQ4_STA)},
+ {"BLT_SSBA9", offsetof(struct b2r2_memory_map, BLT_SSBA9)},
+ {"BLT_SSBA10", offsetof(struct b2r2_memory_map, BLT_SSBA10)},
+ {"BLT_SSBA11", offsetof(struct b2r2_memory_map, BLT_SSBA11)},
+ {"BLT_SSBA12", offsetof(struct b2r2_memory_map, BLT_SSBA12)},
+ {"BLT_SSBA13", offsetof(struct b2r2_memory_map, BLT_SSBA13)},
+ {"BLT_SSBA14", offsetof(struct b2r2_memory_map, BLT_SSBA14)},
+ {"BLT_SSBA15", offsetof(struct b2r2_memory_map, BLT_SSBA15)},
+ {"BLT_SSBA16", offsetof(struct b2r2_memory_map, BLT_SSBA16)},
+ {"BLT_SGA1", offsetof(struct b2r2_memory_map, BLT_SGA1)},
+ {"BLT_SGA2", offsetof(struct b2r2_memory_map, BLT_SGA2)},
+ {"BLT_ITM0", offsetof(struct b2r2_memory_map, BLT_ITM0)},
+ {"BLT_ITM1", offsetof(struct b2r2_memory_map, BLT_ITM1)},
+ {"BLT_ITM2", offsetof(struct b2r2_memory_map, BLT_ITM2)},
+ {"BLT_ITM3", offsetof(struct b2r2_memory_map, BLT_ITM3)},
+ {"BLT_DFV2", offsetof(struct b2r2_memory_map, BLT_DFV2)},
+ {"BLT_DFV1", offsetof(struct b2r2_memory_map, BLT_DFV1)},
+ {"BLT_PRI", offsetof(struct b2r2_memory_map, BLT_PRI)},
+ {"PLUGS1_OP2", offsetof(struct b2r2_memory_map, PLUGS1_OP2)},
+ {"PLUGS1_CHZ", offsetof(struct b2r2_memory_map, PLUGS1_CHZ)},
+ {"PLUGS1_MSZ", offsetof(struct b2r2_memory_map, PLUGS1_MSZ)},
+ {"PLUGS1_PGZ", offsetof(struct b2r2_memory_map, PLUGS1_PGZ)},
+ {"PLUGS2_OP2", offsetof(struct b2r2_memory_map, PLUGS2_OP2)},
+ {"PLUGS2_CHZ", offsetof(struct b2r2_memory_map, PLUGS2_CHZ)},
+ {"PLUGS2_MSZ", offsetof(struct b2r2_memory_map, PLUGS2_MSZ)},
+ {"PLUGS2_PGZ", offsetof(struct b2r2_memory_map, PLUGS2_PGZ)},
+ {"PLUGS3_OP2", offsetof(struct b2r2_memory_map, PLUGS3_OP2)},
+ {"PLUGS3_CHZ", offsetof(struct b2r2_memory_map, PLUGS3_CHZ)},
+ {"PLUGS3_MSZ", offsetof(struct b2r2_memory_map, PLUGS3_MSZ)},
+ {"PLUGS3_PGZ", offsetof(struct b2r2_memory_map, PLUGS3_PGZ)},
+ {"PLUGT_OP2", offsetof(struct b2r2_memory_map, PLUGT_OP2)},
+ {"PLUGT_CHZ", offsetof(struct b2r2_memory_map, PLUGT_CHZ)},
+ {"PLUGT_MSZ", offsetof(struct b2r2_memory_map, PLUGT_MSZ)},
+ {"PLUGT_PGZ", offsetof(struct b2r2_memory_map, PLUGT_PGZ)},
+ {"BLT_NIP", offsetof(struct b2r2_memory_map, BLT_NIP)},
+ {"BLT_CIC", offsetof(struct b2r2_memory_map, BLT_CIC)},
+ {"BLT_INS", offsetof(struct b2r2_memory_map, BLT_INS)},
+ {"BLT_ACK", offsetof(struct b2r2_memory_map, BLT_ACK)},
+ {"BLT_TBA", offsetof(struct b2r2_memory_map, BLT_TBA)},
+ {"BLT_TTY", offsetof(struct b2r2_memory_map, BLT_TTY)},
+ {"BLT_TXY", offsetof(struct b2r2_memory_map, BLT_TXY)},
+ {"BLT_TSZ", offsetof(struct b2r2_memory_map, BLT_TSZ)},
+ {"BLT_S1CF", offsetof(struct b2r2_memory_map, BLT_S1CF)},
+ {"BLT_S2CF", offsetof(struct b2r2_memory_map, BLT_S2CF)},
+ {"BLT_S1BA", offsetof(struct b2r2_memory_map, BLT_S1BA)},
+ {"BLT_S1TY", offsetof(struct b2r2_memory_map, BLT_S1TY)},
+ {"BLT_S1XY", offsetof(struct b2r2_memory_map, BLT_S1XY)},
+ {"BLT_S2BA", offsetof(struct b2r2_memory_map, BLT_S2BA)},
+ {"BLT_S2TY", offsetof(struct b2r2_memory_map, BLT_S2TY)},
+ {"BLT_S2XY", offsetof(struct b2r2_memory_map, BLT_S2XY)},
+ {"BLT_S2SZ", offsetof(struct b2r2_memory_map, BLT_S2SZ)},
+ {"BLT_S3BA", offsetof(struct b2r2_memory_map, BLT_S3BA)},
+ {"BLT_S3TY", offsetof(struct b2r2_memory_map, BLT_S3TY)},
+ {"BLT_S3XY", offsetof(struct b2r2_memory_map, BLT_S3XY)},
+ {"BLT_S3SZ", offsetof(struct b2r2_memory_map, BLT_S3SZ)},
+ {"BLT_CWO", offsetof(struct b2r2_memory_map, BLT_CWO)},
+ {"BLT_CWS", offsetof(struct b2r2_memory_map, BLT_CWS)},
+ {"BLT_CCO", offsetof(struct b2r2_memory_map, BLT_CCO)},
+ {"BLT_CML", offsetof(struct b2r2_memory_map, BLT_CML)},
+ {"BLT_FCTL", offsetof(struct b2r2_memory_map, BLT_FCTL)},
+ {"BLT_PMK", offsetof(struct b2r2_memory_map, BLT_PMK)},
+ {"BLT_RSF", offsetof(struct b2r2_memory_map, BLT_RSF)},
+ {"BLT_RZI", offsetof(struct b2r2_memory_map, BLT_RZI)},
+ {"BLT_HFP", offsetof(struct b2r2_memory_map, BLT_HFP)},
+ {"BLT_VFP", offsetof(struct b2r2_memory_map, BLT_VFP)},
+ {"BLT_Y_RSF", offsetof(struct b2r2_memory_map, BLT_Y_RSF)},
+ {"BLT_Y_RZI", offsetof(struct b2r2_memory_map, BLT_Y_RZI)},
+ {"BLT_Y_HFP", offsetof(struct b2r2_memory_map, BLT_Y_HFP)},
+ {"BLT_Y_VFP", offsetof(struct b2r2_memory_map, BLT_Y_VFP)},
+ {"BLT_KEY1", offsetof(struct b2r2_memory_map, BLT_KEY1)},
+ {"BLT_KEY2", offsetof(struct b2r2_memory_map, BLT_KEY2)},
+ {"BLT_SAR", offsetof(struct b2r2_memory_map, BLT_SAR)},
+ {"BLT_USR", offsetof(struct b2r2_memory_map, BLT_USR)},
+ {"BLT_IVMX0", offsetof(struct b2r2_memory_map, BLT_IVMX0)},
+ {"BLT_IVMX1", offsetof(struct b2r2_memory_map, BLT_IVMX1)},
+ {"BLT_IVMX2", offsetof(struct b2r2_memory_map, BLT_IVMX2)},
+ {"BLT_IVMX3", offsetof(struct b2r2_memory_map, BLT_IVMX3)},
+ {"BLT_OVMX0", offsetof(struct b2r2_memory_map, BLT_OVMX0)},
+ {"BLT_OVMX1", offsetof(struct b2r2_memory_map, BLT_OVMX1)},
+ {"BLT_OVMX2", offsetof(struct b2r2_memory_map, BLT_OVMX2)},
+ {"BLT_OVMX3", offsetof(struct b2r2_memory_map, BLT_OVMX3)},
+ {"BLT_VC1R", offsetof(struct b2r2_memory_map, BLT_VC1R)},
+ {"BLT_Y_HFC0", offsetof(struct b2r2_memory_map, BLT_Y_HFC0)},
+ {"BLT_Y_HFC1", offsetof(struct b2r2_memory_map, BLT_Y_HFC1)},
+ {"BLT_Y_HFC2", offsetof(struct b2r2_memory_map, BLT_Y_HFC2)},
+ {"BLT_Y_HFC3", offsetof(struct b2r2_memory_map, BLT_Y_HFC3)},
+ {"BLT_Y_HFC4", offsetof(struct b2r2_memory_map, BLT_Y_HFC4)},
+ {"BLT_Y_HFC5", offsetof(struct b2r2_memory_map, BLT_Y_HFC5)},
+ {"BLT_Y_HFC6", offsetof(struct b2r2_memory_map, BLT_Y_HFC6)},
+ {"BLT_Y_HFC7", offsetof(struct b2r2_memory_map, BLT_Y_HFC7)},
+ {"BLT_Y_HFC8", offsetof(struct b2r2_memory_map, BLT_Y_HFC8)},
+ {"BLT_Y_HFC9", offsetof(struct b2r2_memory_map, BLT_Y_HFC9)},
+ {"BLT_Y_HFC10", offsetof(struct b2r2_memory_map, BLT_Y_HFC10)},
+ {"BLT_Y_HFC11", offsetof(struct b2r2_memory_map, BLT_Y_HFC11)},
+ {"BLT_Y_HFC12", offsetof(struct b2r2_memory_map, BLT_Y_HFC12)},
+ {"BLT_Y_HFC13", offsetof(struct b2r2_memory_map, BLT_Y_HFC13)},
+ {"BLT_Y_HFC14", offsetof(struct b2r2_memory_map, BLT_Y_HFC14)},
+ {"BLT_Y_HFC15", offsetof(struct b2r2_memory_map, BLT_Y_HFC15)},
+ {"BLT_Y_VFC0", offsetof(struct b2r2_memory_map, BLT_Y_VFC0)},
+ {"BLT_Y_VFC1", offsetof(struct b2r2_memory_map, BLT_Y_VFC1)},
+ {"BLT_Y_VFC2", offsetof(struct b2r2_memory_map, BLT_Y_VFC2)},
+ {"BLT_Y_VFC3", offsetof(struct b2r2_memory_map, BLT_Y_VFC3)},
+ {"BLT_Y_VFC4", offsetof(struct b2r2_memory_map, BLT_Y_VFC4)},
+ {"BLT_Y_VFC5", offsetof(struct b2r2_memory_map, BLT_Y_VFC5)},
+ {"BLT_Y_VFC6", offsetof(struct b2r2_memory_map, BLT_Y_VFC6)},
+ {"BLT_Y_VFC7", offsetof(struct b2r2_memory_map, BLT_Y_VFC7)},
+ {"BLT_Y_VFC8", offsetof(struct b2r2_memory_map, BLT_Y_VFC8)},
+ {"BLT_Y_VFC9", offsetof(struct b2r2_memory_map, BLT_Y_VFC9)},
+ {"BLT_HFC0", offsetof(struct b2r2_memory_map, BLT_HFC0)},
+ {"BLT_HFC1", offsetof(struct b2r2_memory_map, BLT_HFC1)},
+ {"BLT_HFC2", offsetof(struct b2r2_memory_map, BLT_HFC2)},
+ {"BLT_HFC3", offsetof(struct b2r2_memory_map, BLT_HFC3)},
+ {"BLT_HFC4", offsetof(struct b2r2_memory_map, BLT_HFC4)},
+ {"BLT_HFC5", offsetof(struct b2r2_memory_map, BLT_HFC5)},
+ {"BLT_HFC6", offsetof(struct b2r2_memory_map, BLT_HFC6)},
+ {"BLT_HFC7", offsetof(struct b2r2_memory_map, BLT_HFC7)},
+ {"BLT_HFC8", offsetof(struct b2r2_memory_map, BLT_HFC8)},
+ {"BLT_HFC9", offsetof(struct b2r2_memory_map, BLT_HFC9)},
+ {"BLT_HFC10", offsetof(struct b2r2_memory_map, BLT_HFC10)},
+ {"BLT_HFC11", offsetof(struct b2r2_memory_map, BLT_HFC11)},
+ {"BLT_HFC12", offsetof(struct b2r2_memory_map, BLT_HFC12)},
+ {"BLT_HFC13", offsetof(struct b2r2_memory_map, BLT_HFC13)},
+ {"BLT_HFC14", offsetof(struct b2r2_memory_map, BLT_HFC14)},
+ {"BLT_HFC15", offsetof(struct b2r2_memory_map, BLT_HFC15)},
+ {"BLT_VFC0", offsetof(struct b2r2_memory_map, BLT_VFC0)},
+ {"BLT_VFC1", offsetof(struct b2r2_memory_map, BLT_VFC1)},
+ {"BLT_VFC2", offsetof(struct b2r2_memory_map, BLT_VFC2)},
+ {"BLT_VFC3", offsetof(struct b2r2_memory_map, BLT_VFC3)},
+ {"BLT_VFC4", offsetof(struct b2r2_memory_map, BLT_VFC4)},
+ {"BLT_VFC5", offsetof(struct b2r2_memory_map, BLT_VFC5)},
+ {"BLT_VFC6", offsetof(struct b2r2_memory_map, BLT_VFC6)},
+ {"BLT_VFC7", offsetof(struct b2r2_memory_map, BLT_VFC7)},
+ {"BLT_VFC8", offsetof(struct b2r2_memory_map, BLT_VFC8)},
+ {"BLT_VFC9", offsetof(struct b2r2_memory_map, BLT_VFC9)},
+};
+
+#ifdef HANDLE_TIMEOUTED_JOBS
+/**
+ * printk_regs() - Print B2R2 registers to printk
+ */
+static void printk_regs(void)
+{
+#ifdef CONFIG_B2R2_DEBUG
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_regs); i++) {
+ unsigned long value = readl(
+ (unsigned long *) (((u8 *) b2r2_core.hw) +
+ debugfs_regs[i].offset));
+ b2r2_log_regdump("%s: %08lX\n",
+ debugfs_regs[i].name,
+ value);
+ }
+#endif
+}
+#endif
+
+/**
+ * debugfs_b2r2_reg_read() - Implements debugfs read for B2R2 register
+ *
+ * @filp: File pointer
+ * @buf: User space buffer
+ * @count: Number of bytes to read
+ * @f_pos: File position
+ *
+ * Returns number of bytes read or negative error code
+ */
+static int debugfs_b2r2_reg_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ size_t dev_size;
+ int ret = 0;
+
+ unsigned long value;
+ char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL);
+
+ if (Buf == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Read from B2R2 */
+ value = readl((unsigned long *) (((u8 *) b2r2_core.hw) +
+ (u32) filp->f_dentry->
+ d_inode->i_private));
+
+ /* Build the string */
+ dev_size = sprintf(Buf, "%8lX\n", value);
+
+ /* No more to read if offset != 0 */
+ if (*f_pos > dev_size)
+ goto out;
+
+ if (*f_pos + count > dev_size)
+ count = dev_size - *f_pos;
+
+ /* Return it to user space */
+ if (copy_to_user(buf, Buf, count))
+ ret = -EINVAL;
+ *f_pos += count;
+ ret = count;
+
+out:
+ if (Buf != NULL)
+ kfree(Buf);
+ return ret;
+}
+
+/**
+ * debugfs_b2r2_reg_write() - Implements debugfs write for B2R2 register
+ *
+ * @filp: File pointer
+ * @buf: User space buffer
+ * @count: Number of bytes to write
+ * @f_pos: File position
+ *
+ * Returns number of bytes written or negative error code
+ */
+static int debugfs_b2r2_reg_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ char Buf[80];
+ u32 reg_value;
+ int ret = 0;
+
+ /* Adjust count */
+ if (count >= sizeof(Buf))
+ count = sizeof(Buf) - 1;
+ /* Get it from user space */
+ if (copy_from_user(Buf, buf, count))
+ return -EINVAL;
+ Buf[count] = 0;
+ /* Convert from hex string */
+ if (sscanf(Buf, "%8lX", (unsigned long *) &reg_value) != 1)
+ return -EINVAL;
+
+ writel(reg_value, (u32 *) (((u8 *) b2r2_core.hw) +
+ (u32) filp->f_dentry->d_inode->i_private));
+
+ *f_pos += count;
+ ret = count;
+
+ return ret;
+}
+
+/**
+ * debugfs_b2r2_reg_fops() - File operations for B2R2 register debugfs
+ */
+static const struct file_operations debugfs_b2r2_reg_fops = {
+ .owner = THIS_MODULE,
+ .read = debugfs_b2r2_reg_read,
+ .write = debugfs_b2r2_reg_write,
+};
+
+/**
+ * debugfs_b2r2_regs_read() - Implements debugfs read for B2R2 register dump
+ *
+ * @filp: File pointer
+ * @buf: User space buffer
+ * @count: Number of bytes to read
+ * @f_pos: File position
+ *
+ * Returns number of bytes written or negative error code
+ */
+static int debugfs_b2r2_regs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ size_t dev_size = 0;
+ int ret = 0;
+ int i;
+ char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL);
+
+ if (Buf == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Build a giant string containing all registers */
+ for (i = 0; i < ARRAY_SIZE(debugfs_regs); i++) {
+ unsigned long value =
+ readl((unsigned long *) (((u8 *) b2r2_core.hw) +
+ debugfs_regs[i].offset));
+ dev_size += sprintf(Buf + dev_size, "%s: %08lX\n",
+ debugfs_regs[i].name,
+ value);
+ }
+
+ /* No more to read if offset != 0 */
+ if (*f_pos > dev_size)
+ goto out;
+
+ if (*f_pos + count > dev_size)
+ count = dev_size - *f_pos;
+
+ if (copy_to_user(buf, Buf, count))
+ ret = -EINVAL;
+ *f_pos += count;
+ ret = count;
+
+out:
+ if (Buf != NULL)
+ kfree(Buf);
+ return ret;
+}
+
+/**
+ * debugfs_b2r2_regs_fops() - File operations for B2R2 register dump debugfs
+ */
+static const struct file_operations debugfs_b2r2_regs_fops = {
+ .owner = THIS_MODULE,
+ .read = debugfs_b2r2_regs_read,
+};
+
+/**
+ * debugfs_b2r2_stat_read() - Implements debugfs read for B2R2 statistics
+ *
+ * @filp: File pointer
+ * @buf: User space buffer
+ * @count: Number of bytes to read
+ * @f_pos: File position
+ *
+ * Returns number of bytes read or negative error code
+ */
+static int debugfs_b2r2_stat_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ size_t dev_size = 0;
+ int ret = 0;
+ int i = 0;
+ char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL);
+
+ if (Buf == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Build a string containing all statistics */
+ dev_size += sprintf(Buf + dev_size, "Interrupts: %lu\n",
+ b2r2_core.stat_n_irq);
+ dev_size += sprintf(Buf + dev_size, "Added jobs: %lu\n",
+ b2r2_core.stat_n_jobs_added);
+ dev_size += sprintf(Buf + dev_size, "Removed jobs: %lu\n",
+ b2r2_core.stat_n_jobs_removed);
+ dev_size += sprintf(Buf + dev_size, "Jobs in prio list: %lu\n",
+ b2r2_core.stat_n_jobs_in_prio_list);
+ dev_size += sprintf(Buf + dev_size, "Active jobs: %lu\n",
+ b2r2_core.n_active_jobs);
+ for (i = 0; i < ARRAY_SIZE(b2r2_core.active_jobs); i++)
+ dev_size += sprintf(Buf + dev_size, "Job in queue %d: %p\n",
+ i, b2r2_core.active_jobs[i]);
+ dev_size += sprintf(Buf + dev_size, "Clock requests: %lu\n",
+ b2r2_core.clock_request_count);
+
+ /* No more to read if offset != 0 */
+ if (*f_pos > dev_size)
+ goto out;
+
+ if (*f_pos + count > dev_size)
+ count = dev_size - *f_pos;
+
+ if (copy_to_user(buf, Buf, count))
+ ret = -EINVAL;
+ *f_pos += count;
+ ret = count;
+
+out:
+ if (Buf != NULL)
+ kfree(Buf);
+ return ret;
+}
+
+/**
+ * debugfs_b2r2_stat_fops() - File operations for B2R2 statistics debugfs
+ */
+static const struct file_operations debugfs_b2r2_stat_fops = {
+ .owner = THIS_MODULE,
+ .read = debugfs_b2r2_stat_read,
+};
+
+
+/**
+ * debugfs_b2r2_clock_read() - Implements debugfs read for
+ * PMU B2R2 clock register
+ * @filp: File pointer
+ * @buf: User space buffer
+ * @count: Number of bytes to read
+ * @f_pos: File position
+ *
+ * Returns number of bytes read or negative error code
+ */
+static int debugfs_b2r2_clock_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ /* 10 characters hex number + newline + string terminator; */
+ char Buf[10+2];
+ size_t dev_size;
+ int ret = 0;
+
+ unsigned long value = clk_get_rate(b2r2_core.b2r2_clock);
+
+ dev_size = sprintf(Buf, "%#010lX\n", value);
+
+ /* No more to read if offset != 0 */
+ if (*f_pos > dev_size)
+ goto out;
+
+ if (*f_pos + count > dev_size)
+ count = dev_size - *f_pos;
+
+ if (copy_to_user(buf, Buf, count))
+ ret = -EINVAL;
+ *f_pos += count;
+ ret = count;
+
+out:
+ return ret;
+}
+
+/**
+ * debugfs_b2r2_clock_write() - Implements debugfs write for
+ * PMU B2R2 clock register
+ * @filp: File pointer
+ * @buf: User space buffer
+ * @count: Number of bytes to write
+ * @f_pos: File position
+ *
+ * Returns number of bytes written or negative error code
+ */
+static int debugfs_b2r2_clock_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ char Buf[80];
+ u32 reg_value;
+ int ret = 0;
+
+ if (count >= sizeof(Buf))
+ count = sizeof(Buf) - 1;
+ if (copy_from_user(Buf, buf, count))
+ return -EINVAL;
+ Buf[count] = 0;
+ if (sscanf(Buf, "%8lX", (unsigned long *) &reg_value) != 1)
+ return -EINVAL;
+
+ /*not working yet*/
+ /*clk_set_rate(b2r2_core.b2r2_clock, (unsigned long) reg_value);*/
+
+ *f_pos += count;
+ ret = count;
+
+ return ret;
+}
+
+/**
+ * debugfs_b2r2_clock_fops() - File operations for PMU B2R2 clock debugfs
+ */
+static const struct file_operations debugfs_b2r2_clock_fops = {
+ .owner = THIS_MODULE,
+ .read = debugfs_b2r2_clock_read,
+ .write = debugfs_b2r2_clock_write,
+};
+
+#endif
+
+/**
+ *
+ * init_hw() - B2R2 Hardware reset & initiliaze
+ *
+ * @pdev: B2R2 platform device
+ *
+ * 1)Register interrupt handler
+ *
+ * 2)B2R2 Register map
+ *
+ * 3)For resetting B2R2 hardware,write to B2R2 Control register the
+ * B2R2BLT_CTLGLOBAL_soft_reset and then polling for on
+ * B2R2 status register for B2R2BLT_STA1BDISP_IDLE flag.
+ *
+ * 4)Wait for B2R2 hardware to be idle (on a timeout rather than while loop)
+ *
+ * 5)Driver status reset
+ *
+ * 6)Recover from any error without any leaks.
+ *
+ */
+static int init_hw(void)
+{
+ int result = 0;
+ u32 uTimeOut = B2R2_DRIVER_TIMEOUT_VALUE;
+
+ /* Put B2R2 into reset */
+ clear_interrupts();
+
+ writel(readl(&b2r2_core.hw->BLT_CTL) | B2R2BLT_CTLGLOBAL_soft_reset,
+ &b2r2_core.hw->BLT_CTL);
+
+
+ /* Set up interrupt handler */
+ result = request_irq(b2r2_core.irq, b2r2_irq_handler, 0,
+ "b2r2-interrupt", 0);
+ if (result) {
+ b2r2_log_err("%s: failed to register IRQ for B2R2\n", __func__);
+ goto b2r2_init_request_irq_failed;
+ }
+
+ b2r2_log_info("do a global reset..\n");
+
+ /* Release reset */
+ writel(0x00000000, &b2r2_core.hw->BLT_CTL);
+
+ b2r2_log_info("wait for B2R2 to be idle..\n");
+
+ /** Wait for B2R2 to be idle (on a timeout rather than while loop) */
+ while ((uTimeOut > 0) &&
+ ((readl(&b2r2_core.hw->BLT_STA1) &
+ B2R2BLT_STA1BDISP_IDLE) == 0x0))
+ uTimeOut--;
+ if (uTimeOut == 0) {
+ b2r2_log_err(
+ "%s: B2R2 not idle after SW reset\n", __func__);
+ result = -EAGAIN;
+ goto b2r2_core_init_hw_timeout;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ /* Register debug fs files for register access */
+ if (b2r2_core.debugfs_root_dir && !b2r2_core.debugfs_regs_dir) {
+ int i;
+ b2r2_core.debugfs_regs_dir = debugfs_create_dir("regs",
+ b2r2_core.debugfs_root_dir);
+ debugfs_create_file("all", 0666, b2r2_core.debugfs_regs_dir,
+ 0, &debugfs_b2r2_regs_fops);
+ /* Create debugfs entries for all static registers */
+ for (i = 0; i < ARRAY_SIZE(debugfs_regs); i++)
+ debugfs_create_file(debugfs_regs[i].name, 0666,
+ b2r2_core.debugfs_regs_dir,
+ (void *) debugfs_regs[i].offset,
+ &debugfs_b2r2_reg_fops);
+ }
+#endif
+
+ b2r2_log_info("%s ended..\n", __func__);
+ return result;
+
+/** Recover from any error without any leaks */
+
+b2r2_core_init_hw_timeout:
+
+ /** Free B2R2 interrupt handler */
+
+ free_irq(b2r2_core.irq, 0);
+
+b2r2_init_request_irq_failed:
+
+ if (b2r2_core.hw)
+ iounmap(b2r2_core.hw);
+ b2r2_core.hw = NULL;
+
+ return result;
+
+}
+
+
+/**
+ * exit_hw() - B2R2 Hardware exit
+ *
+ * b2r2_core.lock _must_ NOT be held
+ */
+static void exit_hw(void)
+{
+ unsigned long flags;
+
+ b2r2_log_info("%s started..\n", __func__);
+
+#ifdef CONFIG_DEBUG_FS
+ /* Unregister our debugfs entries */
+ if (b2r2_core.debugfs_regs_dir) {
+ debugfs_remove_recursive(b2r2_core.debugfs_regs_dir);
+ b2r2_core.debugfs_regs_dir = NULL;
+ }
+#endif
+ b2r2_log_debug("%s: locking b2r2_core.lock\n", __func__);
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+
+ /* Cancel all pending jobs */
+ b2r2_log_debug("%s: canceling pending jobs\n", __func__);
+ exit_job_list(&b2r2_core.prio_queue);
+
+ /* Soft reset B2R2 (Close all DMA,
+ reset all state to idle, reset regs)*/
+ b2r2_log_debug("%s: putting b2r2 in reset\n", __func__);
+ writel(readl(&b2r2_core.hw->BLT_CTL) | B2R2BLT_CTLGLOBAL_soft_reset,
+ &b2r2_core.hw->BLT_CTL);
+
+ b2r2_log_debug("%s: clearing interrupts\n", __func__);
+ clear_interrupts();
+
+ /** Free B2R2 interrupt handler */
+ b2r2_log_debug("%s: freeing interrupt handler\n", __func__);
+ free_irq(b2r2_core.irq, 0);
+
+ b2r2_log_debug("%s: unlocking b2r2_core.lock\n", __func__);
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+
+ b2r2_log_info("%s ended...\n", __func__);
+}
+
+/**
+ * b2r2_probe() - This routine loads the B2R2 core driver
+ *
+ * @pdev: platform device.
+ */
+static int b2r2_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct resource *res;
+
+ BUG_ON(pdev == NULL);
+
+ ret = b2r2_debug_init(&pdev->dev);
+ if (ret < 0) {
+ dev_err(b2r2_core.log_dev, "b2r2_debug_init failed\n");
+ goto b2r2_probe_debug_init_failed;
+ }
+
+ b2r2_core.log_dev = &pdev->dev;
+ b2r2_log_info("init started.\n");
+
+ /* Init spin locks */
+ spin_lock_init(&b2r2_core.lock);
+
+ /* Init job queues */
+ INIT_LIST_HEAD(&b2r2_core.prio_queue);
+
+#ifdef HANDLE_TIMEOUTED_JOBS
+ /* Create work queue for callbacks & timeout */
+ INIT_DELAYED_WORK(&b2r2_core.timeout_work, timeout_work_function);
+#endif
+
+ /* Work queue for callbacks and timeout management */
+ b2r2_core.work_queue = create_workqueue("B2R2");
+ if (!b2r2_core.work_queue) {
+ ret = -ENOMEM;
+ goto b2r2_probe_no_work_queue;
+ }
+
+ /* Get the clock for B2R2 */
+ b2r2_core.b2r2_clock = clk_get(&pdev->dev, "b2r2");
+ if (IS_ERR(b2r2_core.b2r2_clock)) {
+ ret = PTR_ERR(b2r2_core.b2r2_clock);
+ b2r2_log_err("clk_get b2r2 failed\n");
+ goto b2r2_probe_no_clk;
+ }
+
+ /* Get the B2R2 regulator */
+ b2r2_core.b2r2_reg = regulator_get(&pdev->dev, "vsupply");
+ if (IS_ERR(b2r2_core.b2r2_reg)) {
+ ret = PTR_ERR(b2r2_core.b2r2_reg);
+ b2r2_log_err("regulator_get vsupply failed (dev_name=%s)\n",
+ dev_name(&pdev->dev));
+ goto b2r2_probe_no_reg;
+ }
+
+ /* Init power management */
+ mutex_init(&b2r2_core.domain_lock);
+ INIT_DELAYED_WORK_DEFERRABLE(&b2r2_core.domain_disable_work,
+ domain_disable_work_function);
+ b2r2_core.domain_enabled = false;
+
+ /* Map B2R2 into kernel virtual memory space */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL)
+ goto b2r2_probe_no_res;
+
+ /* Hook up irq */
+ b2r2_core.irq = platform_get_irq(pdev, 0);
+ if (b2r2_core.irq <= 0) {
+ b2r2_log_info("%s: Failed to request irq (irq=%d)\n", __func__,
+ b2r2_core.irq);
+ goto b2r2_failed_irq_get;
+ }
+
+ b2r2_core.hw = (struct b2r2_memory_map *) ioremap(res->start,
+ res->end - res->start + 1);
+ if (b2r2_core.hw == NULL) {
+
+ b2r2_log_info("%s: ioremap failed\n", __func__);
+ ret = -ENOMEM;
+ goto b2r2_probe_ioremap_failed;
+ }
+
+ dev_dbg(b2r2_core.log_dev,
+ "b2r2 structure address %p\n",
+ b2r2_core.hw);
+
+ /* Initialize b2r2_blt module. FIXME: Module of it's own
+ or perhaps a dedicated module init c file? */
+ ret = b2r2_blt_module_init();
+ if (ret < 0) {
+ b2r2_log_err("b2r2_blt_module_init failed\n");
+ goto b2r2_probe_blt_init_fail;
+ }
+
+ b2r2_core.op_size = B2R2_PLUG_OPCODE_SIZE_DEFAULT;
+ b2r2_core.ch_size = B2R2_PLUG_CHUNK_SIZE_DEFAULT;
+ b2r2_core.pg_size = B2R2_PLUG_PAGE_SIZE_DEFAULT;
+ b2r2_core.mg_size = B2R2_PLUG_MESSAGE_SIZE_DEFAULT;
+ b2r2_core.min_req_time = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ b2r2_core.debugfs_root_dir = debugfs_create_dir("b2r2", NULL);
+ debugfs_create_file("stat", 0666, b2r2_core.debugfs_root_dir,
+ 0, &debugfs_b2r2_stat_fops);
+ debugfs_create_file("clock", 0666, b2r2_core.debugfs_root_dir,
+ 0, &debugfs_b2r2_clock_fops);
+
+ debugfs_create_u8("op_size", 0666, b2r2_core.debugfs_root_dir,
+ &b2r2_core.op_size);
+ debugfs_create_u8("ch_size", 0666, b2r2_core.debugfs_root_dir,
+ &b2r2_core.ch_size);
+ debugfs_create_u8("pg_size", 0666, b2r2_core.debugfs_root_dir,
+ &b2r2_core.pg_size);
+ debugfs_create_u8("mg_size", 0666, b2r2_core.debugfs_root_dir,
+ &b2r2_core.mg_size);
+ debugfs_create_u16("min_req_time", 0666, b2r2_core.debugfs_root_dir,
+ &b2r2_core.min_req_time);
+#endif
+
+ b2r2_log_info("init done.\n");
+
+ return ret;
+
+/** Recover from any error if something fails */
+b2r2_probe_blt_init_fail:
+b2r2_probe_ioremap_failed:
+b2r2_failed_irq_get:
+b2r2_probe_no_res:
+ regulator_put(b2r2_core.b2r2_reg);
+b2r2_probe_no_reg:
+ clk_put(b2r2_core.b2r2_clock);
+b2r2_probe_no_clk:
+ destroy_workqueue(b2r2_core.work_queue);
+ b2r2_core.work_queue = NULL;
+b2r2_probe_no_work_queue:
+
+ b2r2_log_info("init done with errors.\n");
+b2r2_probe_debug_init_failed:
+
+ return ret;
+
+}
+
+
+
+/**
+ * b2r2_remove - This routine unloads b2r2 driver
+ *
+ * @pdev: platform device.
+ */
+static int b2r2_remove(struct platform_device *pdev)
+{
+ unsigned long flags;
+
+ BUG_ON(pdev == NULL);
+
+ b2r2_log_info("%s started\n", __func__);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(b2r2_core.debugfs_root_dir);
+#endif
+
+ /* Flush B2R2 work queue (call all callbacks) */
+ flush_workqueue(b2r2_core.work_queue);
+
+ /* Exit b2r2 blt module */
+ b2r2_blt_module_exit();
+
+#ifdef HANDLE_TIMEOUTED_JOBS
+ cancel_delayed_work(&b2r2_core.timeout_work);
+#endif
+
+ /* Flush B2R2 work queue (call all callbacks for
+ cancelled jobs) */
+ flush_workqueue(b2r2_core.work_queue);
+
+ /* Make sure the power is turned off */
+ cancel_delayed_work_sync(&b2r2_core.domain_disable_work);
+
+ /** Unmap B2R2 registers */
+ b2r2_log_info("unmap b2r2 registers..\n");
+ if (b2r2_core.hw) {
+ iounmap(b2r2_core.hw);
+
+ b2r2_core.hw = NULL;
+ }
+
+ destroy_workqueue(b2r2_core.work_queue);
+
+ spin_lock_irqsave(&b2r2_core.lock, flags);
+ b2r2_core.work_queue = NULL;
+ spin_unlock_irqrestore(&b2r2_core.lock, flags);
+
+ /* Return the clock */
+ clk_put(b2r2_core.b2r2_clock);
+ regulator_put(b2r2_core.b2r2_reg);
+
+ b2r2_log_info("%s ended\n", __func__);
+
+ b2r2_core.log_dev = NULL;
+
+ b2r2_debug_exit();
+
+ return 0;
+
+}
+/**
+ * b2r2_suspend() - This routine puts the B2R2 device in to sustend state.
+ * @pdev: platform device.
+ *
+ * This routine stores the current state of the b2r2 device and puts in to suspend state.
+ *
+ */
+int b2r2_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ b2r2_log_info("%s\n", __func__);
+
+ /* Flush B2R2 work queue (call all callbacks) */
+ flush_workqueue(b2r2_core.work_queue);
+
+#ifdef HANDLE_TIMEOUTED_JOBS
+ cancel_delayed_work(&b2r2_core.timeout_work);
+#endif
+
+ /* Flush B2R2 work queue (call all callbacks for
+ cancelled jobs) */
+ flush_workqueue(b2r2_core.work_queue);
+
+ /* Make sure power is turned off */
+ cancel_delayed_work_sync(&b2r2_core.domain_disable_work);
+
+ return 0;
+}
+
+
+/**
+ * b2r2_resume() - This routine resumes the B2R2 device from sustend state.
+ * @pdev: platform device.
+ *
+ * This routine restore back the current state of the b2r2 device resumes.
+ *
+ */
+int b2r2_resume(struct platform_device *pdev)
+{
+ b2r2_log_info("%s\n", __func__);
+
+ return 0;
+}
+
+/**
+ * struct platform_b2r2_driver - Platform driver configuration for the
+ * B2R2 core driver
+ */
+static struct platform_driver platform_b2r2_driver = {
+ .remove = b2r2_remove,
+ .driver = {
+ .name = "b2r2",
+ },
+ /** TODO implement power mgmt functions */
+ .suspend = b2r2_suspend,
+ .resume = b2r2_resume,
+};
+
+
+/**
+ * b2r2_init() - Module init function for the B2R2 core module
+ */
+static int __init b2r2_init(void)
+{
+ printk(KERN_INFO "%s\n", __func__);
+ return platform_driver_probe(&platform_b2r2_driver, b2r2_probe);
+}
+module_init(b2r2_init);
+
+/**
+ * b2r2_exit() - Module exit function for the B2R2 core module
+ */
+static void __exit b2r2_exit(void)
+{
+ printk(KERN_INFO "%s\n", __func__);
+ platform_driver_unregister(&platform_b2r2_driver);
+ return;
+}
+module_exit(b2r2_exit);
+
+
+/** Module is having GPL license */
+
+MODULE_LICENSE("GPL");
+
+/** Module author & discription */
+
+MODULE_AUTHOR("Robert Fekete (robert.fekete@stericsson.com)");
+MODULE_DESCRIPTION("B2R2 Core driver");
diff --git a/drivers/video/b2r2/b2r2_core.h b/drivers/video/b2r2/b2r2_core.h
new file mode 100644
index 00000000000..2f958751694
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_core.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 core driver
+ *
+ * Author: Robert Fekete <robert.fekete@stericsson.com>
+ * Author: Paul Wannback
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef __B2R2_CORE_H__
+#define __B2R2_CORE_H__
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+/**
+ * enum b2r2_core_queue - Indicates the B2R2 queue that the job belongs to
+ *
+ * @B2R2_CORE_QUEUE_AQ1: Application queue 1
+ * @B2R2_CORE_QUEUE_AQ2: Application queue 2
+ * @B2R2_CORE_QUEUE_AQ3: Application queue 3
+ * @B2R2_CORE_QUEUE_AQ4: Application queue 4
+ * @B2R2_CORE_QUEUE_CQ1: Composition queue 1
+ * @B2R2_CORE_QUEUE_CQ2: Composition queue 2
+ * @B2R2_CORE_QUEUE_NO_OF: Number of queues
+ */
+enum b2r2_core_queue {
+ B2R2_CORE_QUEUE_AQ1 = 0,
+ B2R2_CORE_QUEUE_AQ2,
+ B2R2_CORE_QUEUE_AQ3,
+ B2R2_CORE_QUEUE_AQ4,
+ B2R2_CORE_QUEUE_CQ1,
+ B2R2_CORE_QUEUE_CQ2,
+ B2R2_CORE_QUEUE_NO_OF,
+};
+
+#define B2R2_NUM_APPLICATIONS_QUEUES 4
+
+/**
+ * enum b2r2_core_job_state - Indicates the current state of the job
+ *
+ * @B2R2_CORE_JOB_IDLE: Never queued
+ * @B2R2_CORE_JOB_QUEUED: In queue but not started yet
+ * @B2R2_CORE_JOB_RUNNING: Running, executed by B2R2
+ * @B2R2_CORE_JOB_DONE: Completed
+ * @B2R2_CORE_JOB_CANCELED: Canceled
+ */
+enum b2r2_core_job_state {
+ B2R2_CORE_JOB_IDLE = 0,
+ B2R2_CORE_JOB_QUEUED,
+ B2R2_CORE_JOB_RUNNING,
+ B2R2_CORE_JOB_DONE,
+ B2R2_CORE_JOB_CANCELED,
+};
+
+/**
+ * struct b2r2_core_job - Represents a B2R2 core job
+ *
+ * @start_sentinel: Memory overwrite guard
+ *
+ * @tag: Client value. Used by b2r2_core_job_find_first_with_tag().
+ * @prio: Job priority, from -19 up to 20. Mapped to the
+ * B2R2 application queues. Filled in by the client.
+ * @first_node_address: Physical address of the first node. Filled
+ * in by the client.
+ * @last_node_address: Physical address of the last node. Filled
+ * in by the client.
+ *
+ * @callback: Function that will be called when the job is done.
+ * @acquire_resources: Function that allocates the resources needed
+ * to execute the job (i.e. SRAM alloc). Must not
+ * sleep if atomic, should fail with negative error code
+ * if resources not available.
+ * @release_resources: Function that releases the resources previously
+ * allocated by acquire_resources (i.e. SRAM alloc).
+ * @release: Function that will be called when the reference count reaches
+ * zero.
+ *
+ * @job_id: Unique id for this job, assigned by B2R2 core
+ * @job_state: The current state of the job
+ * @jiffies: Number of jiffies needed for this request
+ *
+ * @list: List entry element for internal list management
+ * @event: Wait queue event to wait for job done
+ * @work: Work queue structure, for callback implementation
+ *
+ * @queue: The queue that this job shall be submitted to
+ * @control: B2R2 Queue control
+ * @pace_control: For composition queue only
+ * @interrupt_context: Context for interrupt
+ *
+ * @end_sentinel: Memory overwrite guard
+ */
+struct b2r2_core_job {
+ u32 start_sentinel;
+
+ /* Data to be filled in by client */
+ int tag;
+ int prio;
+ u32 first_node_address;
+ u32 last_node_address;
+ void (*callback)(struct b2r2_core_job *);
+ int (*acquire_resources)(struct b2r2_core_job *,
+ bool atomic);
+ void (*release_resources)(struct b2r2_core_job *,
+ bool atomic);
+ void (*release)(struct b2r2_core_job *);
+
+ /* Output data, do not modify */
+ int job_id;
+ enum b2r2_core_job_state job_state;
+ unsigned long jiffies;
+
+ /* Data below is internal to b2r2_core, do not modify */
+
+ /* Reference counting */
+ u32 ref_count;
+
+ /* Internal data */
+ struct list_head list;
+ wait_queue_head_t event;
+ struct work_struct work;
+
+ /* B2R2 HW data */
+ enum b2r2_core_queue queue;
+ u32 control;
+ u32 pace_control;
+ u32 interrupt_context;
+
+ /* Timing data */
+ u32 hw_start_time;
+ s32 nsec_active_in_hw;
+
+ u32 end_sentinel;
+};
+
+/**
+ * b2r2_core_job_add() - Adds a job to B2R2 job queues
+ *
+ * The job reference count will be increased after this function
+ * has been called and b2r2_core_job_release() must be called to
+ * release the reference. The job callback function will be always
+ * be called after the job is done or cancelled.
+ *
+ * @job: Job to be added
+ *
+ * Returns 0 if OK else negative error code
+ *
+ */
+int b2r2_core_job_add(struct b2r2_core_job *job);
+
+/**
+ * b2r2_core_job_wait() - Waits for an added job to be done.
+ *
+ * @job: Job to wait for
+ *
+ * Returns 0 if job done else negative error code
+ *
+ */
+int b2r2_core_job_wait(struct b2r2_core_job *job);
+
+/**
+ * b2r2_core_job_cancel() - Cancel an already added job.
+ *
+ * @job: Job to cancel
+ *
+ * Returns 0 if job cancelled or done else negative error code
+ *
+ */
+int b2r2_core_job_cancel(struct b2r2_core_job *job);
+
+/**
+ * b2r2_core_job_find() - Finds job with given job id
+ *
+ * Reference count will be increased for the found job
+ *
+ * @job_id: Job id to find
+ *
+ * Returns job if found, else NULL
+ *
+ */
+struct b2r2_core_job *b2r2_core_job_find(int job_id);
+
+/**
+ * b2r2_core_job_find_first_with_tag() - Finds first job with given tag
+ *
+ * Reference count will be increased for the found job.
+ * This function can be used to find all jobs for a client, i.e.
+ * when cancelling all jobs for a client.
+ *
+ * @tag: Tag to find
+ *
+ * Returns job if found, else NULL
+ *
+ */
+struct b2r2_core_job *b2r2_core_job_find_first_with_tag(int tag);
+
+/**
+ * b2r2_core_job_addref() - Increase the job reference count.
+ *
+ * @job: Job to increase reference count for.
+ * @caller: The function calling this function (for debug)
+ */
+void b2r2_core_job_addref(struct b2r2_core_job *job, const char *caller);
+
+/**
+ * b2r2_core_job_release() - Decrease the job reference count. The
+ * job will be released (the release() function
+ * will be called) when the reference count
+ * reaches zero.
+ *
+ * @job: Job to decrease reference count for.
+ * @caller: The function calling this function (for debug)
+ */
+void b2r2_core_job_release(struct b2r2_core_job *job, const char *caller);
+
+#endif /* !defined(__B2R2_CORE_JOB_H__) */
diff --git a/drivers/video/b2r2/b2r2_debug.c b/drivers/video/b2r2/b2r2_debug.c
new file mode 100644
index 00000000000..d4711cd3e28
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_debug.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 dynamic debug
+ *
+ * Author: Fredrik Allansson <fredrik.allansson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include "b2r2_debug.h"
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+int b2r2_log_levels[B2R2_LOG_LEVEL_COUNT];
+struct device *b2r2_log_dev;
+
+static struct dentry *root_dir;
+static struct dentry *log_lvl_dir;
+static struct dentry *stats_dir;
+
+#define CHARS_IN_NODE_DUMP 1544
+
+static const size_t dumped_node_size = CHARS_IN_NODE_DUMP * sizeof(char) + 1;
+
+static void dump_node(char *dst, struct b2r2_node *node)
+{
+ dst += sprintf(dst, "node 0x%08x ------------------\n",
+ (unsigned int)node);
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_NIP:", node->node.GROUP0.B2R2_NIP);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_CIC:", node->node.GROUP0.B2R2_CIC);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_INS:", node->node.GROUP0.B2R2_INS);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_ACK:", node->node.GROUP0.B2R2_ACK);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_TBA:", node->node.GROUP1.B2R2_TBA);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_TTY:", node->node.GROUP1.B2R2_TTY);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_TXY:", node->node.GROUP1.B2R2_TXY);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_TSZ:", node->node.GROUP1.B2R2_TSZ);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S1CF:", node->node.GROUP2.B2R2_S1CF);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S2CF:", node->node.GROUP2.B2R2_S2CF);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S1BA:", node->node.GROUP3.B2R2_SBA);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S1TY:", node->node.GROUP3.B2R2_STY);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S1XY:", node->node.GROUP3.B2R2_SXY);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S1SZ:", node->node.GROUP3.B2R2_SSZ);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S2BA:", node->node.GROUP4.B2R2_SBA);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S2TY:", node->node.GROUP4.B2R2_STY);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S2XY:", node->node.GROUP4.B2R2_SXY);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S2SZ:", node->node.GROUP4.B2R2_SSZ);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S3BA:", node->node.GROUP5.B2R2_SBA);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S3TY:", node->node.GROUP5.B2R2_STY);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S3XY:", node->node.GROUP5.B2R2_SXY);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_S3SZ:", node->node.GROUP5.B2R2_SSZ);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_CWO:", node->node.GROUP6.B2R2_CWO);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_CWS:", node->node.GROUP6.B2R2_CWS);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_CCO:", node->node.GROUP7.B2R2_CCO);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_CML:", node->node.GROUP7.B2R2_CML);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_PMK:", node->node.GROUP8.B2R2_PMK);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_FCTL:", node->node.GROUP8.B2R2_FCTL);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_RSF:", node->node.GROUP9.B2R2_RSF);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_RZI:", node->node.GROUP9.B2R2_RZI);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_HFP:", node->node.GROUP9.B2R2_HFP);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_VFP:", node->node.GROUP9.B2R2_VFP);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_Y_RSF:", node->node.GROUP10.B2R2_RSF);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_Y_RZI:", node->node.GROUP10.B2R2_RZI);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_Y_HFP:", node->node.GROUP10.B2R2_HFP);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_Y_VFP:", node->node.GROUP10.B2R2_VFP);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_FF0:", node->node.GROUP11.B2R2_FF0);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_FF1:", node->node.GROUP11.B2R2_FF1);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_FF2:", node->node.GROUP11.B2R2_FF2);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_FF3:", node->node.GROUP11.B2R2_FF3);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_KEY1:", node->node.GROUP12.B2R2_KEY1);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_KEY2:", node->node.GROUP12.B2R2_KEY2);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_XYL:", node->node.GROUP13.B2R2_XYL);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_XYP:", node->node.GROUP13.B2R2_XYP);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_SAR:", node->node.GROUP14.B2R2_SAR);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_USR:", node->node.GROUP14.B2R2_USR);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_IVMX0:", node->node.GROUP15.B2R2_VMX0);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_IVMX1:", node->node.GROUP15.B2R2_VMX1);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_IVMX2:", node->node.GROUP15.B2R2_VMX2);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_IVMX3:", node->node.GROUP15.B2R2_VMX3);
+ dst += sprintf(dst, "--\n");
+
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_OVMX0:", node->node.GROUP16.B2R2_VMX0);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_OVMX1:", node->node.GROUP16.B2R2_VMX1);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_OVMX2:", node->node.GROUP16.B2R2_VMX2);
+ dst += sprintf(dst, "%s\t0x%08x\n",
+ "B2R2_OVMX3:", node->node.GROUP16.B2R2_VMX3);
+ dst += sprintf(dst, "--\n");
+
+}
+
+struct mutex last_job_lock;
+
+static struct b2r2_node *last_job;
+
+void b2r2_debug_job_done(struct b2r2_node *first_node)
+{
+ struct b2r2_node *node = first_node;
+ struct b2r2_node **dst_node;
+ unsigned int node_count = 0;
+
+ while (node != NULL) {
+ node_count++;
+ node = node->next;
+ }
+
+ mutex_lock(&last_job_lock);
+
+ if (last_job) {
+ node = last_job;
+ while (node != NULL) {
+ struct b2r2_node *tmp = node->next;
+ kfree(node);
+ node = tmp;
+ }
+ last_job = NULL;
+ }
+
+ node = first_node;
+ dst_node = &last_job;
+ while (node != NULL) {
+ *dst_node = kzalloc(sizeof(**dst_node), GFP_KERNEL);
+ if (!(*dst_node))
+ goto last_job_alloc_failed;
+
+ memcpy(*dst_node, node, sizeof(**dst_node));
+
+ dst_node = &((*dst_node)->next);
+ node = node->next;
+ }
+
+ mutex_unlock(&last_job_lock);
+
+ return;
+
+last_job_alloc_failed:
+ mutex_unlock(&last_job_lock);
+
+ while (last_job != NULL) {
+ struct b2r2_node *tmp = last_job->next;
+ kfree(last_job);
+ last_job = tmp;
+ }
+
+ return;
+}
+
+static char *last_job_chars;
+static int prev_node_count;
+
+static ssize_t last_job_read(struct file *filep, char __user *buf,
+ size_t bytes, loff_t *off)
+{
+ struct b2r2_node *node = last_job;
+ int node_count = 0;
+ int i;
+
+ size_t size;
+ size_t count;
+ loff_t offs = *off;
+
+ for (; node != NULL; node = node->next)
+ node_count++;
+
+ size = node_count * dumped_node_size;
+
+ if (node_count != prev_node_count) {
+ kfree(last_job_chars);
+
+ last_job_chars = kzalloc(size, GFP_KERNEL);
+ if (!last_job_chars)
+ return 0;
+ prev_node_count = node_count;
+ }
+
+ mutex_lock(&last_job_lock);
+ node = last_job;
+ for (i = 0; i < node_count; i++) {
+ BUG_ON(node == NULL);
+ dump_node(last_job_chars + i * dumped_node_size/sizeof(char),
+ node);
+ node = node->next;
+ }
+ mutex_unlock(&last_job_lock);
+
+ if (offs > size)
+ return 0;
+
+ if (offs + bytes > size)
+ count = size - offs;
+ else
+ count = bytes;
+
+ if (copy_to_user(buf, last_job_chars + offs, count))
+ return -EFAULT;
+
+ *off = offs + count;
+ return count;
+}
+
+static const struct file_operations last_job_fops = {
+ .read = last_job_read,
+};
+
+int b2r2_debug_init(struct device *log_dev)
+{
+ int i;
+
+ b2r2_log_dev = log_dev;
+
+ for (i = 0; i < B2R2_LOG_LEVEL_COUNT; i++)
+ b2r2_log_levels[i] = 0;
+
+ root_dir = debugfs_create_dir("b2r2_debug", NULL);
+ if (!root_dir) {
+ b2r2_log_warn("%s: could not create root dir\n", __func__);
+ return -ENODEV;
+ }
+
+#if !defined(CONFIG_DYNAMIC_DEBUG) && defined(CONFIG_DEBUG_FS)
+ /*
+ * If dynamic debug is disabled we need some other way to control the
+ * log prints
+ */
+ log_lvl_dir = debugfs_create_dir("logs", root_dir);
+
+ /* No need to save the files, they will be removed recursively */
+ (void)debugfs_create_bool("warnings", 0644, log_lvl_dir,
+ &b2r2_log_levels[B2R2_LOG_LEVEL_WARN]);
+ (void)debugfs_create_bool("info", 0644, log_lvl_dir,
+ &b2r2_log_levels[B2R2_LOG_LEVEL_INFO]);
+ (void)debugfs_create_bool("debug", 0644, log_lvl_dir,
+ &b2r2_log_levels[B2R2_LOG_LEVEL_DEBUG]);
+ (void)debugfs_create_bool("regdumps", 0644, log_lvl_dir,
+ &b2r2_log_levels[B2R2_LOG_LEVEL_REGDUMP]);
+
+#elif defined(CONFIG_DYNAMIC_DEBUG)
+ /* log_lvl_dir is never used */
+ (void)log_lvl_dir;
+#endif
+
+ stats_dir = debugfs_create_dir("stats", root_dir);
+ (void)debugfs_create_file("last_job", 0444, stats_dir, NULL,
+ &last_job_fops);
+
+ mutex_init(&last_job_lock);
+
+ return 0;
+}
+
+void b2r2_debug_exit(void)
+{
+#if !defined(CONFIG_DYNAMIC_DEBUG) && defined(CONFIG_DEBUG_FS)
+ if (root_dir)
+ debugfs_remove_recursive(root_dir);
+#endif
+}
diff --git a/drivers/video/b2r2/b2r2_debug.h b/drivers/video/b2r2/b2r2_debug.h
new file mode 100644
index 00000000000..f87ca728482
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_debug.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 dynamic debug
+ *
+ * Author: Fredrik Allansson <fredrik.allansson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef _LINUX_DRIVERS_VIDEO_B2R2_DEBUG_H_
+#define _LINUX_DRIVERS_VIDEO_B2R2_DEBUG_H_
+
+#include <linux/device.h>
+
+#include "b2r2_internal.h"
+
+#ifdef CONFIG_B2R2_DEBUG
+
+/* Log macros */
+enum b2r2_log_levels {
+ B2R2_LOG_LEVEL_WARN,
+ B2R2_LOG_LEVEL_INFO,
+ B2R2_LOG_LEVEL_DEBUG,
+ B2R2_LOG_LEVEL_REGDUMP,
+ B2R2_LOG_LEVEL_COUNT,
+};
+
+/*
+ * Booleans controlling the different log levels. The different log levels are
+ * enabled separately (i.e. you can have info prints without the warn prints).
+ */
+extern int b2r2_log_levels[B2R2_LOG_LEVEL_COUNT];
+
+extern struct device *b2r2_log_dev;
+
+#define b2r2_log_err(...) do { \
+ dev_err(b2r2_log_dev, __VA_ARGS__); \
+ } while (0)
+
+/* If dynamic debug is enabled it should be used instead of loglevels */
+#ifdef CONFIG_DYNAMIC_DEBUG
+# define b2r2_log_warn(...) do { \
+ dev_dbg(b2r2_log_dev, "WARN " __VA_ARGS__); \
+ } while (0)
+# define b2r2_log_info(...) do { \
+ dev_dbg(b2r2_log_dev, "INFO " __VA_ARGS__); \
+ } while (0)
+# define b2r2_log_debug(...) do { \
+ dev_dbg(b2r2_log_dev, "DEBUG " __VA_ARGS__); \
+ } while (0)
+# define b2r2_log_regdump(...) do { \
+ dev_dbg(b2r2_log_dev, "REGD " __VA_ARGS__); \
+ } while (0)
+#else
+# define b2r2_log_warn(...) do { \
+ if (b2r2_log_levels[B2R2_LOG_LEVEL_WARN]) \
+ dev_warn(b2r2_log_dev, "WARN " __VA_ARGS__); \
+ } while (0)
+# define b2r2_log_info(...) do { \
+ if (b2r2_log_levels[B2R2_LOG_LEVEL_INFO]) \
+ dev_info(b2r2_log_dev, "INFO " __VA_ARGS__); \
+ } while (0)
+# define b2r2_log_debug(...) do { \
+ if (b2r2_log_levels[B2R2_LOG_LEVEL_DEBUG]) \
+ dev_dbg(b2r2_log_dev, "DEBUG " __VA_ARGS__); \
+ } while (0)
+# define b2r2_log_regdump(...) do { \
+ if (b2r2_log_levels[B2R2_LOG_LEVEL_REGDUMP]) \
+ dev_vdbg(b2r2_log_dev, "REGD " __VA_ARGS__); \
+ } while (0)
+#endif
+
+
+int b2r2_debug_init(struct device *log_dev);
+void b2r2_debug_exit(void);
+
+void b2r2_debug_job_done(struct b2r2_node *node);
+
+#else
+
+#define b2r2_log_err(...)
+#define b2r2_log_warn(...)
+#define b2r2_log_info(...)
+#define b2r2_log_debug(...)
+#define b2r2_log_regdump(...)
+
+static inline int b2r2_debug_init(struct device *log_dev)
+{
+ return 0;
+}
+static inline void b2r2_debug_exit(void)
+{
+ return;
+}
+static inline void b2r2_debug_job_done(struct b2r2_node *node)
+{
+ return;
+}
+
+#endif
+
+#endif
diff --git a/drivers/video/b2r2/b2r2_filters.c b/drivers/video/b2r2/b2r2_filters.c
new file mode 100644
index 00000000000..208cdcd286e
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_filters.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 filters.
+ *
+ * Author: Fredrik Allansson <fredrik.allansson@stericsson.com> for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/dma-mapping.h>
+
+#include "b2r2_filters.h"
+#include "b2r2_internal.h"
+
+/**
+ * struct b2r2_filter_spec filters[] - Filter lookup table
+ *
+ * Lookup table for filters for different scale factors. A filter
+ * will be selected according to "min < scale_factor <= max".
+ */
+static struct b2r2_filter_spec filters[] = {
+ {
+ .min = 1024,
+ .max = 1433,
+ .h_coeffs = {
+ 0xfc, 0x06, 0xf9, 0x09, 0x34, 0x09, 0xf9, 0x06,
+ 0xfd, 0x07, 0xf7, 0x10, 0x32, 0x02, 0xfc, 0x05,
+ 0xfe, 0x07, 0xf6, 0x17, 0x2f, 0xfc, 0xff, 0x04,
+ 0xff, 0x06, 0xf5, 0x20, 0x2a, 0xf9, 0x01, 0x02,
+ 0x00, 0x04, 0xf6, 0x27, 0x25, 0xf6, 0x04, 0x00,
+ 0x02, 0x01, 0xf9, 0x2d, 0x1d, 0xf5, 0x06, 0xff,
+ 0x04, 0xff, 0xfd, 0x31, 0x15, 0xf5, 0x07, 0xfe,
+ 0x05, 0xfc, 0x02, 0x35, 0x0d, 0xf7, 0x07, 0xfd
+ },
+ .v_coeffs = {
+ 0xf8, 0x0a, 0x3c, 0x0a, 0xf8,
+ 0xf6, 0x12, 0x3b, 0x02, 0xfb,
+ 0xf4, 0x1b, 0x35, 0xfd, 0xff,
+ 0xf4, 0x23, 0x30, 0xf8, 0x01,
+ 0xf6, 0x29, 0x27, 0xf6, 0x04,
+ 0xf9, 0x2e, 0x1e, 0xf5, 0x06,
+ 0xfd, 0x31, 0x16, 0xf6, 0x06,
+ 0x02, 0x32, 0x0d, 0xf8, 0x07
+ },
+ },
+ {
+ .min = 1433,
+ .max = 1536,
+ .h_coeffs = {
+ 0xfe, 0x06, 0xf8, 0x0b, 0x30, 0x0b, 0xf8, 0x06,
+ 0xff, 0x06, 0xf7, 0x12, 0x2d, 0x05, 0xfa, 0x06,
+ 0x00, 0x04, 0xf6, 0x18, 0x2c, 0x00, 0xfc, 0x06,
+ 0x01, 0x02, 0xf7, 0x1f, 0x27, 0xfd, 0xff, 0x04,
+ 0x03, 0x00, 0xf9, 0x24, 0x24, 0xf9, 0x00, 0x03,
+ 0x04, 0xff, 0xfd, 0x29, 0x1d, 0xf7, 0x02, 0x01,
+ 0x06, 0xfc, 0x00, 0x2d, 0x17, 0xf6, 0x04, 0x00,
+ 0x06, 0xfa, 0x05, 0x30, 0x0f, 0xf7, 0x06, 0xff
+ },
+ .v_coeffs = {
+ 0xf6, 0x0e, 0x38, 0x0e, 0xf6,
+ 0xf5, 0x15, 0x38, 0x06, 0xf8,
+ 0xf5, 0x1d, 0x33, 0x00, 0xfb,
+ 0xf6, 0x23, 0x2d, 0xfc, 0xfe,
+ 0xf9, 0x28, 0x26, 0xf9, 0x00,
+ 0xfc, 0x2c, 0x1e, 0xf7, 0x03,
+ 0x00, 0x2e, 0x18, 0xf6, 0x04,
+ 0x05, 0x2e, 0x11, 0xf7, 0x05
+ },
+ },
+ {
+ .min = 1536,
+ .max = 3072,
+ .h_coeffs = {
+ 0xfc, 0xfd, 0x06, 0x13, 0x18, 0x13, 0x06, 0xfd,
+ 0xfc, 0xfe, 0x08, 0x15, 0x17, 0x12, 0x04, 0xfc,
+ 0xfb, 0xfe, 0x0a, 0x16, 0x18, 0x10, 0x03, 0xfc,
+ 0xfb, 0x00, 0x0b, 0x18, 0x17, 0x0f, 0x01, 0xfb,
+ 0xfb, 0x00, 0x0d, 0x19, 0x17, 0x0d, 0x00, 0xfb,
+ 0xfb, 0x01, 0x0f, 0x19, 0x16, 0x0b, 0x00, 0xfb,
+ 0xfc, 0x03, 0x11, 0x19, 0x15, 0x09, 0xfe, 0xfb,
+ 0xfc, 0x04, 0x12, 0x1a, 0x12, 0x08, 0xfe, 0xfc
+ },
+ .v_coeffs = {
+ 0x05, 0x10, 0x16, 0x10, 0x05,
+ 0x06, 0x11, 0x16, 0x0f, 0x04,
+ 0x08, 0x13, 0x15, 0x0e, 0x02,
+ 0x09, 0x14, 0x16, 0x0c, 0x01,
+ 0x0b, 0x15, 0x15, 0x0b, 0x00,
+ 0x0d, 0x16, 0x13, 0x0a, 0x00,
+ 0x0f, 0x17, 0x13, 0x08, 0xff,
+ 0x11, 0x18, 0x12, 0x07, 0xfe
+ },
+ },
+ {
+ .min = 3072,
+ .max = 4096,
+ .h_coeffs = {
+ 0xfe, 0x02, 0x09, 0x0f, 0x0e, 0x0f, 0x09, 0x02,
+ 0xff, 0x02, 0x09, 0x0f, 0x10, 0x0e, 0x08, 0x01,
+ 0xff, 0x03, 0x0a, 0x10, 0x10, 0x0d, 0x07, 0x00,
+ 0x00, 0x04, 0x0b, 0x10, 0x0f, 0x0c, 0x06, 0x00,
+ 0x00, 0x05, 0x0c, 0x10, 0x0e, 0x0c, 0x05, 0x00,
+ 0x00, 0x06, 0x0c, 0x11, 0x0e, 0x0b, 0x04, 0x00,
+ 0x00, 0x07, 0x0d, 0x11, 0x0f, 0x0a, 0x03, 0xff,
+ 0x01, 0x08, 0x0e, 0x11, 0x0e, 0x09, 0x02, 0xff
+ },
+ .v_coeffs = {
+ 0x09, 0x0f, 0x10, 0x0f, 0x09,
+ 0x09, 0x0f, 0x12, 0x0e, 0x08,
+ 0x0a, 0x10, 0x11, 0x0e, 0x07,
+ 0x0b, 0x11, 0x11, 0x0d, 0x06,
+ 0x0c, 0x11, 0x12, 0x0c, 0x05,
+ 0x0d, 0x12, 0x11, 0x0c, 0x04,
+ 0x0e, 0x12, 0x11, 0x0b, 0x04,
+ 0x0f, 0x13, 0x11, 0x0a, 0x03
+ },
+ },
+ {
+ .min = 4096,
+ .max = 5120,
+ .h_coeffs = {
+ 0x00, 0x04, 0x09, 0x0c, 0x0e, 0x0c, 0x09, 0x04,
+ 0x01, 0x05, 0x09, 0x0c, 0x0d, 0x0c, 0x08, 0x04,
+ 0x01, 0x05, 0x0a, 0x0c, 0x0e, 0x0b, 0x08, 0x03,
+ 0x02, 0x06, 0x0a, 0x0d, 0x0c, 0x0b, 0x07, 0x03,
+ 0x02, 0x07, 0x0a, 0x0d, 0x0d, 0x0a, 0x07, 0x02,
+ 0x03, 0x07, 0x0b, 0x0d, 0x0c, 0x0a, 0x06, 0x02,
+ 0x03, 0x08, 0x0b, 0x0d, 0x0d, 0x0a, 0x05, 0x01,
+ 0x04, 0x08, 0x0c, 0x0d, 0x0c, 0x09, 0x05, 0x01
+ },
+ .v_coeffs = {
+ 0x0a, 0x0e, 0x10, 0x0e, 0x0a,
+ 0x0b, 0x0e, 0x0f, 0x0e, 0x0a,
+ 0x0b, 0x0f, 0x10, 0x0d, 0x09,
+ 0x0c, 0x0f, 0x10, 0x0d, 0x08,
+ 0x0d, 0x0f, 0x0f, 0x0d, 0x08,
+ 0x0d, 0x10, 0x10, 0x0c, 0x07,
+ 0x0e, 0x10, 0x0f, 0x0c, 0x07,
+ 0x0f, 0x10, 0x10, 0x0b, 0x06
+ },
+ },
+};
+static const size_t filters_size = sizeof(filters)/sizeof(filters[0]);
+
+/**
+ * struct b2r2_filter_spec bilinear_filter - A bilinear filter
+ *
+ * The bilinear filter will be used if no custom filters are specified, or
+ * for upscales not matching any filter in the lookup table.
+ */
+static struct b2r2_filter_spec bilinear_filter = {
+ .min = 0,
+ .max = 0xffff,
+ .h_coeffs = {
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
+ 0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
+ 0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
+ 0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
+ 0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
+ 0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
+ 0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
+ },
+ .v_coeffs = {
+ 0x00, 0x00, 0x40, 0x00, 0x00,
+ 0xfd, 0x09, 0x3c, 0xfa, 0x04,
+ 0xf9, 0x13, 0x39, 0xf5, 0x06,
+ 0xf5, 0x1f, 0x31, 0xf3, 0x08,
+ 0xf3, 0x2a, 0x28, 0xf3, 0x08,
+ 0xf3, 0x34, 0x1d, 0xf5, 0x07,
+ 0xf5, 0x3b, 0x12, 0xf9, 0x05,
+ 0xfa, 0x3f, 0x07, 0xfd, 0x03
+ },
+};
+
+/**
+ * struct b2r2_filter_spec default_downscale_filter - Default filter for downscale
+ *
+ * The default downscale filter will be used for downscales not matching any
+ * filter in the lookup table.
+ */
+static struct b2r2_filter_spec default_downscale_filter = {
+ .min = 1 << 10,
+ .max = 0xffff,
+ .h_coeffs = {
+ 0x03, 0x06, 0x09, 0x0b, 0x09, 0x0b, 0x09, 0x06,
+ 0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05,
+ 0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05,
+ 0x04, 0x07, 0x09, 0x0b, 0x0b, 0x0a, 0x08, 0x04,
+ 0x04, 0x07, 0x0a, 0x0b, 0x0b, 0x0a, 0x07, 0x04,
+ 0x04, 0x08, 0x0a, 0x0b, 0x0b, 0x09, 0x07, 0x04,
+ 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03,
+ 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03
+ },
+ .v_coeffs = {
+ 0x0b, 0x0e, 0x0e, 0x0e, 0x0b,
+ 0x0b, 0x0e, 0x0f, 0x0d, 0x0b,
+ 0x0c, 0x0e, 0x0f, 0x0d, 0x0a,
+ 0x0c, 0x0e, 0x0f, 0x0d, 0x0a,
+ 0x0d, 0x0f, 0x0e, 0x0d, 0x09,
+ 0x0d, 0x0f, 0x0f, 0x0c, 0x09,
+ 0x0e, 0x0f, 0x0e, 0x0c, 0x09,
+ 0x0e, 0x0f, 0x0f, 0x0c, 0x08
+ },
+};
+
+/**
+ * struct b2r2_filter_spec blur_filter - Blur filter
+ *
+ * Filter for blurring an image.
+ */
+static struct b2r2_filter_spec blur_filter = {
+ .min = 0,
+ .max = 0xffff,
+ .h_coeffs = {
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+ },
+ .v_coeffs = {
+ 0x0c, 0x0c, 0x10, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x10, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x10, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x10, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x10, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x10, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x10, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x10, 0x0c, 0x0c
+ },
+};
+
+/* Private function declarations */
+static int alloc_filter_coeffs(struct b2r2_filter_spec *filter);
+static void free_filter_coeffs(struct b2r2_filter_spec *filter);
+
+/* Public functions */
+
+static int filters_initialized;
+
+int b2r2_filters_init()
+{
+ int i;
+
+ if (filters_initialized)
+ return 0;
+
+ for (i = 0; i < filters_size; i++) {
+ alloc_filter_coeffs(&filters[i]);
+ }
+
+ alloc_filter_coeffs(&bilinear_filter);
+ alloc_filter_coeffs(&default_downscale_filter);
+ alloc_filter_coeffs(&blur_filter);
+
+ filters_initialized = 1;
+
+ return 0;
+}
+
+void b2r2_filters_exit()
+{
+ int i;
+
+ if (!filters_initialized)
+ return;
+
+ for (i = 0; i < filters_size; i++) {
+ free_filter_coeffs(&filters[i]);
+ }
+
+ free_filter_coeffs(&bilinear_filter);
+ free_filter_coeffs(&default_downscale_filter);
+ free_filter_coeffs(&blur_filter);
+
+ filters_initialized = 0;
+}
+
+struct b2r2_filter_spec *b2r2_filter_find(u16 scale_factor)
+{
+ int i;
+ struct b2r2_filter_spec *filter = NULL;
+
+ for (i = 0; i < filters_size; i++) {
+ if ((filters[i].min < scale_factor) &&
+ (scale_factor <= filters[i].max) &&
+ filters[i].h_coeffs_dma_addr &&
+ filters[i].v_coeffs_dma_addr) {
+ filter = &filters[i];
+ break;
+ }
+ }
+
+ if (filter == NULL) {
+ /*
+ * No suitable filter has been found. Use default filters,
+ * bilinear for any upscale.
+ */
+ if (scale_factor < (1 << 10))
+ filter = &bilinear_filter;
+ else
+ filter = &default_downscale_filter;
+ }
+
+ /*
+ * Check so that the coefficients were successfully allocated for this
+ * filter.
+ */
+ if (!filter->h_coeffs_dma_addr || !filter->v_coeffs_dma_addr)
+ return NULL;
+ else
+ return filter;
+}
+
+struct b2r2_filter_spec *b2r2_filter_blur()
+{
+ return &blur_filter;
+}
+
+/* Private functions */
+static int alloc_filter_coeffs(struct b2r2_filter_spec *filter)
+{
+ int ret;
+
+ filter->h_coeffs_dma_addr = dma_alloc_coherent(b2r2_blt_device(),
+ B2R2_HF_TABLE_SIZE, &(filter->h_coeffs_phys_addr),
+ GFP_DMA | GFP_KERNEL);
+ if (filter->h_coeffs_dma_addr == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ filter->v_coeffs_dma_addr = dma_alloc_coherent(b2r2_blt_device(),
+ B2R2_VF_TABLE_SIZE, &(filter->v_coeffs_phys_addr),
+ GFP_DMA | GFP_KERNEL);
+ if (filter->v_coeffs_dma_addr == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ memcpy(filter->h_coeffs_dma_addr, filter->h_coeffs, B2R2_HF_TABLE_SIZE);
+ memcpy(filter->v_coeffs_dma_addr, filter->v_coeffs, B2R2_VF_TABLE_SIZE);
+
+ return 0;
+
+error:
+ free_filter_coeffs(filter);
+ return ret;
+
+}
+
+static void free_filter_coeffs(struct b2r2_filter_spec *filter)
+{
+ if (filter->h_coeffs_dma_addr != NULL)
+ dma_free_coherent(b2r2_blt_device(), B2R2_HF_TABLE_SIZE,
+ filter->h_coeffs_dma_addr,
+ filter->h_coeffs_phys_addr);
+ if (filter->v_coeffs_dma_addr != NULL)
+ dma_free_coherent(b2r2_blt_device(), B2R2_VF_TABLE_SIZE,
+ filter->v_coeffs_dma_addr,
+ filter->v_coeffs_phys_addr);
+
+ filter->h_coeffs_dma_addr = NULL;
+ filter->h_coeffs_phys_addr = 0;
+ filter->v_coeffs_dma_addr = NULL;
+ filter->v_coeffs_phys_addr = 0;
+}
diff --git a/drivers/video/b2r2/b2r2_filters.h b/drivers/video/b2r2/b2r2_filters.h
new file mode 100644
index 00000000000..0eeefc6b0e0
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_filters.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 filters.
+ *
+ * Author: Fredrik Allansson <fredrik.allansson@stericsson.com> for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef _LINUX_VIDEO_B2R2_FILTERS_H
+#define _LINUX_VIDEO_B2R2_FILTERS_H
+
+#include <linux/kernel.h>
+
+#define B2R2_HF_TABLE_SIZE 64
+#define B2R2_VF_TABLE_SIZE 40
+
+/**
+ * @struct b2r2_filter_spec - Filter specification structure
+ *
+ * @param min - Minimum scale factor for this filter (in 6.10 fixed point)
+ * @param max - Maximum scale factor for this filter (in 6.10 fixed point)
+ * @param h_coeffs - Horizontal filter coefficients
+ * @param v_coeffs - Vertical filter coefficients
+ * @param h_coeffs_dma_addr - Virtual DMA address for horizontal coefficients
+ * @param v_coeffs_dma_addr - Virtual DMA address for vertical coefficients
+ * @param h_coeffs_phys_addr - Physical address for horizontal coefficients
+ * @param v_coeffs_phys_addr - Physical address for vertical coefficients
+ */
+struct b2r2_filter_spec {
+ const u16 min;
+ const u16 max;
+
+ const u8 h_coeffs[B2R2_HF_TABLE_SIZE];
+ const u8 v_coeffs[B2R2_VF_TABLE_SIZE];
+
+ void *h_coeffs_dma_addr;
+ u32 h_coeffs_phys_addr;
+
+ void *v_coeffs_dma_addr;
+ u32 v_coeffs_phys_addr;
+};
+
+/**
+ * b2r2_filters_init() - Initilizes the B2R2 filters
+ */
+int b2r2_filters_init(void);
+
+/**
+ * b2r2_filters_init() - De-initilizes the B2R2 filters
+ */
+void b2r2_filters_exit(void);
+
+/**
+ * b2r2_filter_find() - Find a filter matching the given scale factor
+ *
+ * @param scale_factor - Scale factor to find a filter for
+ *
+ * Returns NULL if no filter could be found.
+ */
+struct b2r2_filter_spec *b2r2_filter_find(u16 scale_factor);
+
+/**
+ * b2r2_filter_blur() - Returns the blur filter
+ *
+ * Returns NULL if no blur filter is available.
+ */
+struct b2r2_filter_spec *b2r2_filter_blur(void);
+
+#endif /* _LINUX_VIDEO_B2R2_FILTERS_H */
diff --git a/drivers/video/b2r2/b2r2_generic.c b/drivers/video/b2r2/b2r2_generic.c
new file mode 100644
index 00000000000..d2228e3cf8a
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_generic.c
@@ -0,0 +1,3165 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 generic. Full coverage of user interface but
+ * non optimized implementation. For Fallback purposes.
+ *
+ * Author: Maciej Socha <maciej.socha@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+
+#include "b2r2_generic.h"
+#include "b2r2_internal.h"
+#include "b2r2_global.h"
+#include "b2r2_debug.h"
+#include "b2r2_filters.h"
+
+/*
+ * Debug printing
+ */
+#define B2R2_GENERIC_DEBUG_AREAS 0
+#define B2R2_GENERIC_DEBUG
+
+#define B2R2_GENERIC_WORK_BUF_WIDTH 16
+#define B2R2_GENERIC_WORK_BUF_HEIGHT 16
+#define B2R2_GENERIC_WORK_BUF_PITCH (16 * 4)
+#define B2R2_GENERIC_WORK_BUF_FMT B2R2_NATIVE_ARGB8888
+
+/*
+ * Private functions
+ */
+
+/**
+ * reset_nodes() - clears the node list
+ */
+static void reset_nodes(struct b2r2_node *node)
+{
+ b2r2_log_info("%s ENTRY\n", __func__);
+
+ while (node != NULL) {
+ memset(&(node->node), 0, sizeof(node->node));
+
+ /* TODO: Implement support for short linked lists */
+ node->node.GROUP0.B2R2_CIC = 0x7fffc;
+
+ if (node->next == NULL)
+ break;
+
+ node->node.GROUP0.B2R2_NIP = node->next->physical_address;
+
+ node = node->next;
+ }
+ b2r2_log_info("%s DONE\n", __func__);
+}
+
+/**
+ * dump_nodes() - prints the node list
+ */
+static void dump_nodes(struct b2r2_node *first, bool dump_all)
+{
+ struct b2r2_node *node = first;
+ b2r2_log_info("%s ENTRY\n", __func__);
+ do {
+ b2r2_log_debug("\nNODE START:\n=============\n");
+ b2r2_log_debug("B2R2_ACK: \t0x%.8x\n",
+ node->node.GROUP0.B2R2_ACK);
+ b2r2_log_debug("B2R2_INS: \t0x%.8x\n",
+ node->node.GROUP0.B2R2_INS);
+ b2r2_log_debug("B2R2_CIC: \t0x%.8x\n",
+ node->node.GROUP0.B2R2_CIC);
+ b2r2_log_debug("B2R2_NIP: \t0x%.8x\n",
+ node->node.GROUP0.B2R2_NIP);
+
+ b2r2_log_debug("B2R2_TSZ: \t0x%.8x\n",
+ node->node.GROUP1.B2R2_TSZ);
+ b2r2_log_debug("B2R2_TXY: \t0x%.8x\n",
+ node->node.GROUP1.B2R2_TXY);
+ b2r2_log_debug("B2R2_TTY: \t0x%.8x\n",
+ node->node.GROUP1.B2R2_TTY);
+ b2r2_log_debug("B2R2_TBA: \t0x%.8x\n",
+ node->node.GROUP1.B2R2_TBA);
+
+ b2r2_log_debug("B2R2_S2CF: \t0x%.8x\n",
+ node->node.GROUP2.B2R2_S2CF);
+ b2r2_log_debug("B2R2_S1CF: \t0x%.8x\n",
+ node->node.GROUP2.B2R2_S1CF);
+
+ b2r2_log_debug("B2R2_S1SZ: \t0x%.8x\n",
+ node->node.GROUP3.B2R2_SSZ);
+ b2r2_log_debug("B2R2_S1XY: \t0x%.8x\n",
+ node->node.GROUP3.B2R2_SXY);
+ b2r2_log_debug("B2R2_S1TY: \t0x%.8x\n",
+ node->node.GROUP3.B2R2_STY);
+ b2r2_log_debug("B2R2_S1BA: \t0x%.8x\n",
+ node->node.GROUP3.B2R2_SBA);
+
+ b2r2_log_debug("B2R2_S2SZ: \t0x%.8x\n",
+ node->node.GROUP4.B2R2_SSZ);
+ b2r2_log_debug("B2R2_S2XY: \t0x%.8x\n",
+ node->node.GROUP4.B2R2_SXY);
+ b2r2_log_debug("B2R2_S2TY: \t0x%.8x\n",
+ node->node.GROUP4.B2R2_STY);
+ b2r2_log_debug("B2R2_S2BA: \t0x%.8x\n",
+ node->node.GROUP4.B2R2_SBA);
+
+ b2r2_log_debug("B2R2_S3SZ: \t0x%.8x\n",
+ node->node.GROUP5.B2R2_SSZ);
+ b2r2_log_debug("B2R2_S3XY: \t0x%.8x\n",
+ node->node.GROUP5.B2R2_SXY);
+ b2r2_log_debug("B2R2_S3TY: \t0x%.8x\n",
+ node->node.GROUP5.B2R2_STY);
+ b2r2_log_debug("B2R2_S3BA: \t0x%.8x\n",
+ node->node.GROUP5.B2R2_SBA);
+
+ b2r2_log_debug("B2R2_CWS: \t0x%.8x\n",
+ node->node.GROUP6.B2R2_CWS);
+ b2r2_log_debug("B2R2_CWO: \t0x%.8x\n",
+ node->node.GROUP6.B2R2_CWO);
+
+ b2r2_log_debug("B2R2_FCTL: \t0x%.8x\n",
+ node->node.GROUP8.B2R2_FCTL);
+ b2r2_log_debug("B2R2_RSF: \t0x%.8x\n",
+ node->node.GROUP9.B2R2_RSF);
+ b2r2_log_debug("B2R2_RZI: \t0x%.8x\n",
+ node->node.GROUP9.B2R2_RZI);
+ b2r2_log_debug("B2R2_HFP: \t0x%.8x\n",
+ node->node.GROUP9.B2R2_HFP);
+ b2r2_log_debug("B2R2_VFP: \t0x%.8x\n",
+ node->node.GROUP9.B2R2_VFP);
+ b2r2_log_debug("B2R2_LUMA_RSF: \t0x%.8x\n",
+ node->node.GROUP10.B2R2_RSF);
+ b2r2_log_debug("B2R2_LUMA_RZI: \t0x%.8x\n",
+ node->node.GROUP10.B2R2_RZI);
+ b2r2_log_debug("B2R2_LUMA_HFP: \t0x%.8x\n",
+ node->node.GROUP10.B2R2_HFP);
+ b2r2_log_debug("B2R2_LUMA_VFP: \t0x%.8x\n",
+ node->node.GROUP10.B2R2_VFP);
+
+
+ b2r2_log_debug("B2R2_IVMX0: \t0x%.8x\n",
+ node->node.GROUP15.B2R2_VMX0);
+ b2r2_log_debug("B2R2_IVMX1: \t0x%.8x\n",
+ node->node.GROUP15.B2R2_VMX1);
+ b2r2_log_debug("B2R2_IVMX2: \t0x%.8x\n",
+ node->node.GROUP15.B2R2_VMX2);
+ b2r2_log_debug("B2R2_IVMX3: \t0x%.8x\n",
+ node->node.GROUP15.B2R2_VMX3);
+ b2r2_log_debug("\n=============\nNODE END\n");
+
+ node = node->next;
+ } while (node != NULL && dump_all);
+
+ b2r2_log_info("%s DONE\n", __func__);
+}
+
+/**
+ * to_native_fmt() - returns the native B2R2 format
+ */
+static inline enum b2r2_native_fmt to_native_fmt(enum b2r2_blt_fmt fmt)
+{
+
+ switch (fmt) {
+ case B2R2_BLT_FMT_UNUSED:
+ return B2R2_NATIVE_RGB565;
+ case B2R2_BLT_FMT_1_BIT_A1:
+ return B2R2_NATIVE_A1;
+ case B2R2_BLT_FMT_8_BIT_A8:
+ return B2R2_NATIVE_A8;
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ return B2R2_NATIVE_RGB565;
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ return B2R2_NATIVE_ARGB4444;
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ return B2R2_NATIVE_ARGB1555;
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ return B2R2_NATIVE_ARGB8565;
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ return B2R2_NATIVE_RGB888;
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ return B2R2_NATIVE_YCBCR888;
+ case B2R2_BLT_FMT_32_BIT_ABGR8888: /* Not actually supported by HW */
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ return B2R2_NATIVE_ARGB8888;
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ return B2R2_NATIVE_AYCBCR8888;
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ return B2R2_NATIVE_YCBCR422R;
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ return B2R2_NATIVE_YCBCR422R;
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ return B2R2_NATIVE_YCBCR42X_R2B;
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ return B2R2_NATIVE_YCBCR42X_MBN;
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ return B2R2_NATIVE_YUV;
+ default:
+ /* Should never ever happen */
+ return B2R2_NATIVE_BYTE;
+ }
+}
+
+/**
+ * get_alpha_range() - returns the alpha range of the given format
+ */
+static inline enum b2r2_ty get_alpha_range(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ case B2R2_BLT_FMT_8_BIT_A8:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ return B2R2_TY_ALPHA_RANGE_255; /* 0 - 255 */
+ break;
+ default:
+ break;
+ }
+
+ return B2R2_TY_ALPHA_RANGE_128; /* 0 - 128 */
+}
+
+static unsigned int get_pitch(enum b2r2_blt_fmt format, u32 width)
+{
+ switch (format) {
+ case B2R2_BLT_FMT_1_BIT_A1: {
+ int pitch = width >> 3;
+ /* Check for remainder */
+ if (width & 7)
+ pitch++;
+ return pitch;
+ break;
+ }
+ case B2R2_BLT_FMT_8_BIT_A8:
+ return width;
+ break;
+ case B2R2_BLT_FMT_16_BIT_RGB565: /* all 16 bits/pixel RGB formats */
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ return width * 2;
+ break;
+ case B2R2_BLT_FMT_24_BIT_RGB888: /* all 24 bits/pixel RGB formats */
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ return width * 3;
+ break;
+ case B2R2_BLT_FMT_32_BIT_ARGB8888: /* all 32 bits/pixel RGB formats */
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ return width * 4;
+ break;
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ return width * 4;
+ break;
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ /* width of the buffer must be a multiple of 4 */
+ if (width & 3) {
+ b2r2_log_warn("%s: Illegal width "
+ "for fmt=%#010x width=%d\n", __func__,
+ format, width);
+ return 0;
+ }
+ return width * 2;
+ break;
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ return width;
+ break;
+ /* fall through, same pitch and pointers */
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ /* width of the buffer must be a multiple of 2 */
+ if (width & 1) {
+ b2r2_log_warn("%s: Illegal width "
+ "for fmt=%#010x width=%d\n", __func__,
+ format, width);
+ return 0;
+ }
+ /*
+ * return pitch of the Y-buffer.
+ * U and V pitch can be derived from it.
+ */
+ return width;
+ break;
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ /* width of the buffer must be a multiple of 16. */
+ if (width & 15) {
+ b2r2_log_warn("%s: Illegal width "
+ "for fmt=%#010x width=%d\n", __func__,
+ format, width);
+ return 0;
+ }
+ /*
+ * return pitch of the Y-buffer.
+ * U and V pitch can be derived from it.
+ */
+ return width;
+ break;
+ default:
+ b2r2_log_warn("%s: Unable to determine pitch "
+ "for fmt=%#010x width=%d\n", __func__,
+ format, width);
+ return 0;
+ }
+}
+
+static s32 validate_buf(const struct b2r2_blt_img *image,
+ const struct b2r2_resolved_buf *buf)
+{
+ u32 expect_buf_size;
+ u32 pitch;
+
+ if (image->width <= 0 || image->height <= 0) {
+ b2r2_log_warn("%s: width=%d or height=%d negative.\n", __func__,
+ image->width, image->height);
+ return -EINVAL;
+ }
+
+ if (image->pitch == 0) {
+ /* autodetect pitch based on format and width */
+ pitch = get_pitch(image->fmt, image->width);
+ } else
+ pitch = image->pitch;
+
+ expect_buf_size = pitch * image->height;
+
+ if (pitch == 0) {
+ b2r2_log_warn("%s: Unable to detect pitch. "
+ "fmt=%#010x, width=%d\n",
+ __func__,
+ image->fmt, image->width);
+ return -EINVAL;
+ }
+
+ /* format specific adjustments */
+ switch (image->fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ /*
+ * Use ceil(height/2) in case buffer height
+ * is not divisible by 2.
+ */
+ expect_buf_size +=
+ (pitch >> 1) * ((image->height + 1) >> 1) * 2;
+ break;
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ expect_buf_size += (pitch >> 1) * image->height * 2;
+ break;
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ expect_buf_size += pitch * image->height * 2;
+ break;
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ /*
+ * include space occupied by U and V data.
+ * U and V interleaved, half resolution, which makes
+ * the UV pitch equal to luma pitch.
+ * Use ceil(height/2) in case buffer height
+ * is not divisible by 2.
+ */
+ expect_buf_size += pitch * ((image->height + 1) >> 1);
+ break;
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ /*
+ * include space occupied by U and V data.
+ * U and V interleaved, half resolution, which makes
+ * the UV pitch equal to luma pitch.
+ */
+ expect_buf_size += pitch * image->height;
+ break;
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ /* Height must be a multiple of 16 for macro-block format.*/
+ if (image->height & 15) {
+ b2r2_log_warn("%s: Illegal height "
+ "for fmt=%#010x height=%d\n", __func__,
+ image->fmt, image->height);
+ return -EINVAL;
+ }
+ expect_buf_size += pitch * (image->height >> 1);
+ break;
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ /* Height must be a multiple of 16 for macro-block format.*/
+ if (image->height & 15) {
+ b2r2_log_warn("%s: Illegal height "
+ "for fmt=%#010x height=%d\n", __func__,
+ image->fmt, image->height);
+ return -EINVAL;
+ }
+ expect_buf_size += pitch * image->height;
+ break;
+ default:
+ break;
+ }
+
+ if (buf->file_len < expect_buf_size) {
+ b2r2_log_warn("%s: Invalid buffer size:\n"
+ "fmt=%#010x w=%d h=%d buf.len=%d expect_buf_size=%d\n",
+ __func__,
+ image->fmt, image->width, image->height, buf->file_len,
+ expect_buf_size);
+ return -EINVAL;
+ }
+
+ if (image->buf.type == B2R2_BLT_PTR_VIRTUAL) {
+ b2r2_log_warn("%s: Virtual pointers not supported yet.\n",
+ __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Bit-expand the color from fmt to RGB888 with blue at LSB.
+ * Copy MSBs into missing LSBs.
+ */
+static u32 to_RGB888(u32 color, const enum b2r2_blt_fmt fmt)
+{
+ u32 out_color = 0;
+ u32 r = 0;
+ u32 g = 0;
+ u32 b = 0;
+ switch (fmt) {
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ r = ((color & 0xf00) << 12) | ((color & 0xf00) << 8);
+ g = ((color & 0xf0) << 8) | ((color & 0xf0) << 4);
+ b = ((color & 0xf) << 4) | (color & 0xf);
+ out_color = r | g | b;
+ break;
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ r = ((color & 0x7c00) << 9) | ((color & 0x7000) << 4);
+ g = ((color & 0x3e0) << 6) | ((color & 0x380) << 1);
+ b = ((color & 0x1f) << 3) | ((color & 0x1c) >> 2);
+ out_color = r | g | b;
+ break;
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ r = ((color & 0xf800) << 8) | ((color & 0xe000) << 3);
+ g = ((color & 0x7e0) << 5) | ((color & 0x600) >> 1);
+ b = ((color & 0x1f) << 3) | ((color & 0x1c) >> 2);
+ out_color = r | g | b;
+ break;
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ out_color = color & 0xffffff;
+ break;
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ r = (color & 0xff) << 16;
+ g = color & 0xff00;
+ b = (color & 0xff0000) >> 16;
+ out_color = r | g | b;
+ break;
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ r = ((color & 0xf800) << 8) | ((color & 0xe000) << 3);
+ g = ((color & 0x7e0) << 5) | ((color & 0x600) >> 1);
+ b = ((color & 0x1f) << 3) | ((color & 0x1c) >> 2);
+ out_color = r | g | b;
+ break;
+ default:
+ break;
+ }
+
+ return out_color;
+}
+
+
+static void setup_fill_input_stage(const struct b2r2_blt_request *req,
+ struct b2r2_node *node,
+ struct b2r2_work_buf *out_buf)
+{
+ enum b2r2_native_fmt fill_fmt = 0;
+ u32 src_color = req->user_req.src_color;
+ const struct b2r2_blt_img *dst_img = &(req->user_req.dst_img);
+ b2r2_log_info("%s ENTRY\n", __func__);
+
+ /* Determine format in src_color */
+ switch (dst_img->fmt) {
+ /* ARGB formats */
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_1_BIT_A1:
+ case B2R2_BLT_FMT_8_BIT_A8:
+ if ((req->user_req.flags & B2R2_BLT_FLAG_SOURCE_FILL) != 0) {
+ fill_fmt = B2R2_NATIVE_ARGB8888;
+ } else {
+ /* SOURCE_FILL_RAW */
+ fill_fmt = to_native_fmt(dst_img->fmt);
+ if (dst_img->fmt == B2R2_BLT_FMT_32_BIT_ABGR8888) {
+ /*
+ * Color is read from a register,
+ * where it is stored in ABGR format.
+ * Set up IVMX.
+ */
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_IVMX_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_IVMX;
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_RGB_TO_BGR;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_RGB_TO_BGR;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_RGB_TO_BGR;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_RGB_TO_BGR;
+ }
+ }
+ break;
+ /* YUV formats */
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ /*
+ * Set up IVMX
+ * The destination format is in fact YUV,
+ * but the input stage stores the data in
+ * an intermediate buffer which is RGB.
+ * Hence the conversion from YUV to RGB.
+ */
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_IVMX_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_IVMX;
+ node->node.GROUP15.B2R2_VMX0 = B2R2_VMX0_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 = B2R2_VMX1_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 = B2R2_VMX2_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 = B2R2_VMX3_YUV_TO_RGB_601_VIDEO;
+
+ if ((req->user_req.flags & B2R2_BLT_FLAG_SOURCE_FILL) != 0) {
+ fill_fmt = B2R2_NATIVE_AYCBCR8888;
+ } else {
+ /* SOURCE_FILL_RAW */
+ bool dst_yuv_planar =
+ B2R2_BLT_FMT_YUV420_PACKED_PLANAR ==
+ dst_img->fmt ||
+ B2R2_BLT_FMT_YUV422_PACKED_PLANAR ==
+ dst_img->fmt ||
+ B2R2_BLT_FMT_YVU420_PACKED_PLANAR ==
+ dst_img->fmt ||
+ B2R2_BLT_FMT_YVU422_PACKED_PLANAR ==
+ dst_img->fmt ||
+ B2R2_BLT_FMT_YUV444_PACKED_PLANAR ==
+ dst_img->fmt;
+
+ bool dst_yuv_semi_planar =
+ B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR ==
+ dst_img->fmt ||
+ B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR ==
+ dst_img->fmt ||
+ B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ==
+ dst_img->fmt ||
+ B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR ==
+ dst_img->fmt ||
+ B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE ==
+ dst_img->fmt ||
+ B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE ==
+ dst_img->fmt;
+
+ if (dst_yuv_planar || dst_yuv_semi_planar) {
+ /*
+ * SOURCE_FILL_RAW cannot be supported
+ * with multi-buffer formats.
+ * Force a legal format to prevent B2R2
+ * from misbehaving.
+ */
+ fill_fmt = B2R2_NATIVE_AYCBCR8888;
+ } else {
+ fill_fmt = to_native_fmt(dst_img->fmt);
+ }
+
+ if (dst_img->fmt == B2R2_BLT_FMT_Y_CB_Y_CR) {
+ /*
+ * Setup input VMX to convert YVU to
+ * RGB 601 VIDEO
+ * Chroma components are swapped so
+ * it is YVU and not YUV.
+ */
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_YVU_TO_RGB_601_VIDEO;
+ }
+ }
+ break;
+ default:
+ src_color = 0;
+ fill_fmt = B2R2_NATIVE_ARGB8888;
+ break;
+ }
+
+ node->node.GROUP1.B2R2_TBA = out_buf->phys_addr;
+ node->node.GROUP1.B2R2_TTY =
+ (B2R2_GENERIC_WORK_BUF_PITCH << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ B2R2_GENERIC_WORK_BUF_FMT |
+ B2R2_TY_ALPHA_RANGE_255 |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+ /* Set color fill on SRC2 channel */
+ node->node.GROUP4.B2R2_SBA = 0;
+ node->node.GROUP4.B2R2_STY =
+ (0 << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ fill_fmt |
+ get_alpha_range(dst_img->fmt) |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_2_COLOR_FILL_REGISTER;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_COLOR_FILL;
+ node->node.GROUP2.B2R2_S2CF = src_color;
+
+ node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BYPASS_S2_S3;
+ b2r2_log_info("%s DONE\n", __func__);
+}
+
+static void setup_input_stage(const struct b2r2_blt_request *req,
+ struct b2r2_node *node,
+ struct b2r2_work_buf *out_buf)
+{
+ /* Horizontal and vertical scaling factors in 6.10 fixed point format */
+ s32 h_scf = 1 << 10;
+ s32 v_scf = 1 << 10;
+ const struct b2r2_blt_rect *src_rect = &(req->user_req.src_rect);
+ const struct b2r2_blt_rect *dst_rect = &(req->user_req.dst_rect);
+ const struct b2r2_blt_img *src_img = &(req->user_req.src_img);
+ u32 src_pitch = 0;
+ /* horizontal and vertical scan order for out_buf */
+ enum b2r2_ty dst_hso = B2R2_TY_HSO_LEFT_TO_RIGHT;
+ enum b2r2_ty dst_vso = B2R2_TY_VSO_TOP_TO_BOTTOM;
+ u32 fctl = 0;
+ u32 rsf = 0;
+ u32 rzi = 0;
+ bool yuv_semi_planar =
+ src_img->fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR ||
+ src_img->fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR ||
+ src_img->fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ src_img->fmt == B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR ||
+ src_img->fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE ||
+ src_img->fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE;
+
+ bool yuv_planar =
+ src_img->fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR ||
+ src_img->fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR ||
+ src_img->fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ src_img->fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR ||
+ src_img->fmt == B2R2_BLT_FMT_YUV444_PACKED_PLANAR;
+
+ struct b2r2_filter_spec *hf;
+ struct b2r2_filter_spec *vf;
+
+ bool use_h_filter = false;
+ bool use_v_filter = false;
+
+ b2r2_log_info("%s ENTRY\n", __func__);
+
+ if (((B2R2_BLT_FLAG_SOURCE_FILL | B2R2_BLT_FLAG_SOURCE_FILL_RAW) &
+ req->user_req.flags) != 0) {
+ setup_fill_input_stage(req, node, out_buf);
+ b2r2_log_info("%s DONE\n", __func__);
+ return;
+ }
+
+ if (src_img->pitch == 0) {
+ /* Determine pitch based on format and width of the image. */
+ src_pitch = get_pitch(src_img->fmt, src_img->width);
+ } else {
+ src_pitch = src_img->pitch;
+ }
+
+ b2r2_log_info("%s transform=%#010x\n",
+ __func__, req->user_req.transform);
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) {
+ h_scf = (src_rect->width << 10) / dst_rect->height;
+ v_scf = (src_rect->height << 10) / dst_rect->width;
+ } else {
+ h_scf = (src_rect->width << 10) / dst_rect->width;
+ v_scf = (src_rect->height << 10) / dst_rect->height;
+ }
+
+ hf = b2r2_filter_find(h_scf);
+ vf = b2r2_filter_find(v_scf);
+
+ use_h_filter = h_scf != (1 << 10);
+ use_v_filter = v_scf != (1 << 10);
+
+ /* B2R2_BLT_FLAG_BLUR overrides any scaling filter. */
+ if (req->user_req.flags & B2R2_BLT_FLAG_BLUR) {
+ use_h_filter = true;
+ use_v_filter = true;
+ hf = b2r2_filter_blur();
+ vf = b2r2_filter_blur();
+ }
+
+ /* Configure horizontal rescale */
+ if (h_scf != (1 << 10)) {
+ b2r2_log_info("%s: Scaling horizontally by 0x%.8x"
+ "\ns(%d, %d)->d(%d, %d)\n", __func__,
+ h_scf, src_rect->width, src_rect->height,
+ dst_rect->width, dst_rect->height);
+ }
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER;
+ rsf &= ~(0xffff << B2R2_RSF_HSRC_INC_SHIFT);
+ rsf |= h_scf << B2R2_RSF_HSRC_INC_SHIFT;
+ rzi |= B2R2_RZI_DEFAULT_HNB_REPEAT;
+
+ /* Configure vertical rescale */
+ if (v_scf != (1 << 10)) {
+ b2r2_log_info("%s: Scaling vertically by 0x%.8x"
+ "\ns(%d, %d)->d(%d, %d)\n", __func__,
+ v_scf, src_rect->width, src_rect->height,
+ dst_rect->width, dst_rect->height);
+ }
+ fctl |= B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER;
+ rsf &= ~(0xffff << B2R2_RSF_VSRC_INC_SHIFT);
+ rsf |= v_scf << B2R2_RSF_VSRC_INC_SHIFT;
+ rzi |= 2 << B2R2_RZI_VNB_REPEAT_SHIFT;
+
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_RESCALE2D_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_RESIZE_CHROMA;
+
+ /* Adjustments that depend on the source format */
+ switch (src_img->fmt) {
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ /* Set up IVMX */
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_IVMX_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_IVMX;
+
+ node->node.GROUP15.B2R2_VMX0 = B2R2_VMX0_RGB_TO_BGR;
+ node->node.GROUP15.B2R2_VMX1 = B2R2_VMX1_RGB_TO_BGR;
+ node->node.GROUP15.B2R2_VMX2 = B2R2_VMX2_RGB_TO_BGR;
+ node->node.GROUP15.B2R2_VMX3 = B2R2_VMX3_RGB_TO_BGR;
+ break;
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ /* Set up IVMX */
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_IVMX_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_IVMX;
+ if (src_img->fmt == B2R2_BLT_FMT_Y_CB_Y_CR) {
+ /*
+ * Setup input VMX to convert YVU to RGB 601 VIDEO
+ * Chroma components are swapped so
+ * it is YVU and not YUV.
+ */
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_YVU_TO_RGB_601_VIDEO;
+ } else {
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_YUV_TO_RGB_601_VIDEO;
+ }
+ break;
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE: {
+ /*
+ * Luma handled in the same way
+ * for all YUV multi-buffer formats.
+ * Set luma rescale registers.
+ */
+ u32 rsf_luma = 0;
+ u32 rzi_luma = 0;
+
+ /* Set up IVMX */
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_IVMX_ENABLED | B2R2_INS_RESCALE2D_ENABLED;
+ node->node.GROUP0.B2R2_CIC |=
+ B2R2_CIC_IVMX | B2R2_CIC_RESIZE_LUMA;
+
+ if (src_img->fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ src_img->fmt ==
+ B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR) {
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_YVU_TO_RGB_601_VIDEO;
+ } else {
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_YUV_TO_RGB_601_VIDEO;
+ }
+
+ fctl |= B2R2_FCTL_LUMA_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_LUMA_VF2D_MODE_ENABLE_RESIZER;
+
+ if (use_h_filter && hf) {
+ fctl |= B2R2_FCTL_LUMA_HF2D_MODE_ENABLE_FILTER;
+ node->node.GROUP10.B2R2_HFP = hf->h_coeffs_phys_addr;
+ }
+
+ if (use_v_filter && vf) {
+ fctl |= B2R2_FCTL_LUMA_VF2D_MODE_ENABLE_FILTER;
+ node->node.GROUP10.B2R2_VFP = vf->v_coeffs_phys_addr;
+ }
+
+ rsf_luma |= h_scf << B2R2_RSF_HSRC_INC_SHIFT;
+ rzi_luma |= B2R2_RZI_DEFAULT_HNB_REPEAT;
+
+ rsf_luma |= v_scf << B2R2_RSF_VSRC_INC_SHIFT;
+ rzi_luma |= 2 << B2R2_RZI_VNB_REPEAT_SHIFT;
+
+ node->node.GROUP10.B2R2_RSF = rsf_luma;
+ node->node.GROUP10.B2R2_RZI = rzi_luma;
+
+ switch (src_img->fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ /*
+ * Chrominance is always half the luminance size
+ * so chrominance resizer is always active.
+ */
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER;
+
+ rsf &= ~(0xffff << B2R2_RSF_HSRC_INC_SHIFT);
+ rsf |= (h_scf >> 1) << B2R2_RSF_HSRC_INC_SHIFT;
+ rsf &= ~(0xffff << B2R2_RSF_VSRC_INC_SHIFT);
+ rsf |= (v_scf >> 1) << B2R2_RSF_VSRC_INC_SHIFT;
+ /* Select suitable filter for chroma */
+ hf = b2r2_filter_find(h_scf >> 1);
+ vf = b2r2_filter_find(v_scf >> 1);
+ use_h_filter = true;
+ use_v_filter = true;
+ break;
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ /*
+ * Chrominance is always half the luminance size
+ * only in horizontal direction.
+ */
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER;
+
+ rsf &= ~(0xffff << B2R2_RSF_HSRC_INC_SHIFT);
+ rsf |= (h_scf >> 1) << B2R2_RSF_HSRC_INC_SHIFT;
+ rsf &= ~(0xffff << B2R2_RSF_VSRC_INC_SHIFT);
+ rsf |= v_scf << B2R2_RSF_VSRC_INC_SHIFT;
+ /* Select suitable filter for chroma */
+ hf = b2r2_filter_find(h_scf >> 1);
+ use_h_filter = true;
+ break;
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ /* Chrominance is the same size as luminance.*/
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER;
+
+ rsf &= ~(0xffff << B2R2_RSF_HSRC_INC_SHIFT);
+ rsf |= h_scf << B2R2_RSF_HSRC_INC_SHIFT;
+ rsf &= ~(0xffff << B2R2_RSF_VSRC_INC_SHIFT);
+ rsf |= v_scf << B2R2_RSF_VSRC_INC_SHIFT;
+ /* Select suitable filter for chroma */
+ hf = b2r2_filter_find(h_scf);
+ vf = b2r2_filter_find(v_scf);
+ use_h_filter = true;
+ use_v_filter = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /*
+ * Set the filter control and rescale registers.
+ * GROUP9 registers are used for all single-buffer formats
+ * or for chroma in case of multi-buffer YUV formats.
+ * h/v_filter is now appropriately selected for chroma scaling,
+ * be it YUV multi-buffer, or single-buffer raster format.
+ * B2R2_BLT_FLAG_BLUR overrides any scaling filter.
+ */
+ if (req->user_req.flags & B2R2_BLT_FLAG_BLUR) {
+ use_h_filter = true;
+ use_v_filter = true;
+ hf = b2r2_filter_blur();
+ vf = b2r2_filter_blur();
+ }
+
+ if (use_h_filter && hf) {
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_COLOR_CHANNEL_FILTER;
+ node->node.GROUP9.B2R2_HFP = hf->h_coeffs_phys_addr;
+ }
+
+ if (use_v_filter && vf) {
+ fctl |= B2R2_FCTL_VF2D_MODE_ENABLE_COLOR_CHANNEL_FILTER;
+ node->node.GROUP9.B2R2_VFP = vf->v_coeffs_phys_addr;
+ }
+
+ node->node.GROUP8.B2R2_FCTL |= fctl;
+ node->node.GROUP9.B2R2_RSF |= rsf;
+ node->node.GROUP9.B2R2_RZI |= rzi;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_FILTER_CONTROL;
+
+ /*
+ * Flip transform is done before potential rotation.
+ * This can be achieved with appropriate scan order.
+ * Transform stage will only do rotation.
+ */
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_FLIP_H)
+ dst_hso = B2R2_TY_HSO_RIGHT_TO_LEFT;
+
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_FLIP_V)
+ dst_vso = B2R2_TY_VSO_BOTTOM_TO_TOP;
+
+ /* Set target buffer */
+ node->node.GROUP1.B2R2_TBA = out_buf->phys_addr;
+ node->node.GROUP1.B2R2_TTY =
+ (B2R2_GENERIC_WORK_BUF_PITCH << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ B2R2_GENERIC_WORK_BUF_FMT |
+ B2R2_TY_ALPHA_RANGE_255 |
+ dst_hso | dst_vso;
+
+ if (yuv_planar) {
+ /*
+ * Set up chrominance buffers on source 1 and 2,
+ * luminance on source 3.
+ * src_pitch and physical_address apply to luminance,
+ * corresponding chrominance values have to be derived.
+ */
+ u32 cb_addr = 0;
+ u32 cr_addr = 0;
+ u32 chroma_pitch = 0;
+ bool swapped_chroma =
+ src_img->fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ src_img->fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR;
+ enum b2r2_native_fmt src_fmt = to_native_fmt(src_img->fmt);
+
+ if (swapped_chroma)
+ cr_addr = req->src_resolved.physical_address +
+ src_pitch * src_img->height;
+ else
+ cb_addr = req->src_resolved.physical_address +
+ src_pitch * src_img->height;
+
+ switch (src_img->fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ chroma_pitch = src_pitch >> 1;
+ if (swapped_chroma)
+ cb_addr = cr_addr + chroma_pitch *
+ (src_img->height >> 1);
+ else
+ cr_addr = cb_addr + chroma_pitch *
+ (src_img->height >> 1);
+ break;
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ chroma_pitch = src_pitch >> 1;
+ if (swapped_chroma)
+ cb_addr = cr_addr + chroma_pitch *
+ src_img->height;
+ else
+ cr_addr = cb_addr + chroma_pitch *
+ src_img->height;
+ break;
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ /* Chrominance has full resolution, same as luminance.*/
+ chroma_pitch = src_pitch;
+ cr_addr =
+ cb_addr + chroma_pitch * src_img->height;
+ break;
+ default:
+ break;
+ }
+
+ node->node.GROUP3.B2R2_SBA = cr_addr;
+ node->node.GROUP3.B2R2_STY =
+ (chroma_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ src_fmt |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP4.B2R2_SBA = cb_addr;
+ node->node.GROUP4.B2R2_STY = node->node.GROUP3.B2R2_STY;
+
+ node->node.GROUP5.B2R2_SBA = req->src_resolved.physical_address;
+ node->node.GROUP5.B2R2_STY =
+ (src_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ src_fmt |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_1_FETCH_FROM_MEM |
+ B2R2_INS_SOURCE_2_FETCH_FROM_MEM |
+ B2R2_INS_SOURCE_3_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_CIC |=
+ B2R2_CIC_SOURCE_1 |
+ B2R2_CIC_SOURCE_2 |
+ B2R2_CIC_SOURCE_3;
+ } else if (yuv_semi_planar) {
+ /*
+ * Set up chrominance buffer on source 2, luminance on source 3.
+ * src_pitch and physical_address apply to luminance,
+ * corresponding chrominance values have to be derived.
+ * U and V are interleaved at half the luminance resolution,
+ * which makes the pitch of the UV plane equal
+ * to luminance pitch.
+ */
+ u32 chroma_addr = req->src_resolved.physical_address +
+ src_pitch * src_img->height;
+ u32 chroma_pitch = src_pitch;
+
+ enum b2r2_native_fmt src_fmt = to_native_fmt(src_img->fmt);
+
+ node->node.GROUP4.B2R2_SBA = chroma_addr;
+ node->node.GROUP4.B2R2_STY =
+ (chroma_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ src_fmt |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP5.B2R2_SBA = req->src_resolved.physical_address;
+ node->node.GROUP5.B2R2_STY =
+ (src_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ src_fmt |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_2_FETCH_FROM_MEM |
+ B2R2_INS_SOURCE_3_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_CIC |=
+ B2R2_CIC_SOURCE_2 | B2R2_CIC_SOURCE_3;
+ } else {
+ /* single buffer format */
+ node->node.GROUP4.B2R2_SBA = req->src_resolved.physical_address;
+ node->node.GROUP4.B2R2_STY =
+ (src_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ to_native_fmt(src_img->fmt) |
+ get_alpha_range(src_img->fmt) |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_2_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_2;
+ }
+
+ if ((req->user_req.flags &
+ B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) != 0) {
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_CLUTOP_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_CLUT;
+ node->node.GROUP7.B2R2_CCO = B2R2_CCO_CLUT_COLOR_CORRECTION |
+ B2R2_CCO_CLUT_UPDATE;
+ node->node.GROUP7.B2R2_CML = req->clut_phys_addr;
+ }
+
+ node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BYPASS_S2_S3;
+
+ b2r2_log_info("%s DONE\n", __func__);
+}
+
+static void setup_transform_stage(const struct b2r2_blt_request *req,
+ struct b2r2_node *node,
+ struct b2r2_work_buf *out_buf,
+ struct b2r2_work_buf *in_buf)
+{
+ /* vertical scan order for out_buf */
+ enum b2r2_ty dst_vso = B2R2_TY_VSO_TOP_TO_BOTTOM;
+ enum b2r2_blt_transform transform = req->user_req.transform;
+
+ b2r2_log_info("%s ENTRY\n", __func__);
+
+ if (transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) {
+ /*
+ * Scan order must be flipped otherwise contents will
+ * be mirrored vertically. Leftmost column of in_buf
+ * would become top instead of bottom row of out_buf.
+ */
+ dst_vso = B2R2_TY_VSO_BOTTOM_TO_TOP;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_ROTATION_ENABLED;
+ }
+
+ /* Set target buffer */
+ node->node.GROUP1.B2R2_TBA = out_buf->phys_addr;
+ node->node.GROUP1.B2R2_TTY =
+ (B2R2_GENERIC_WORK_BUF_PITCH << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ B2R2_GENERIC_WORK_BUF_FMT |
+ B2R2_TY_ALPHA_RANGE_255 |
+ B2R2_TY_HSO_LEFT_TO_RIGHT | dst_vso;
+
+ /* Set source buffer on SRC2 channel */
+ node->node.GROUP4.B2R2_SBA = in_buf->phys_addr;
+ node->node.GROUP4.B2R2_STY =
+ (B2R2_GENERIC_WORK_BUF_PITCH << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ B2R2_GENERIC_WORK_BUF_FMT |
+ B2R2_TY_ALPHA_RANGE_255 |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_2_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_2;
+ node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BYPASS_S2_S3;
+
+ b2r2_log_info("%s DONE\n", __func__);
+}
+
+/*
+static void setup_mask_stage(const struct b2r2_blt_request req,
+ struct b2r2_node *node,
+ struct b2r2_work_buf *out_buf,
+ struct b2r2_work_buf *in_buf);
+*/
+
+static void setup_dst_read_stage(const struct b2r2_blt_request *req,
+ struct b2r2_node *node,
+ struct b2r2_work_buf *out_buf)
+{
+ const struct b2r2_blt_img *dst_img = &(req->user_req.dst_img);
+ u32 fctl = 0;
+ u32 rsf = 0;
+ bool yuv_semi_planar =
+ dst_img->fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR ||
+ dst_img->fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR ||
+ dst_img->fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ dst_img->fmt == B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR ||
+ dst_img->fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE ||
+ dst_img->fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE;
+
+ bool yuv_planar =
+ dst_img->fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR ||
+ dst_img->fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR ||
+ dst_img->fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ dst_img->fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR ||
+ dst_img->fmt == B2R2_BLT_FMT_YUV444_PACKED_PLANAR;
+
+ u32 dst_pitch = 0;
+ if (dst_img->pitch == 0) {
+ /* Determine pitch based on format and width of the image. */
+ dst_pitch = get_pitch(dst_img->fmt, dst_img->width);
+ } else {
+ dst_pitch = dst_img->pitch;
+ }
+
+ b2r2_log_info("%s ENTRY\n", __func__);
+
+ /* Adjustments that depend on the destination format */
+ switch (dst_img->fmt) {
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ /* Set up IVMX */
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_IVMX_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_IVMX;
+
+ node->node.GROUP15.B2R2_VMX0 = B2R2_VMX0_RGB_TO_BGR;
+ node->node.GROUP15.B2R2_VMX1 = B2R2_VMX1_RGB_TO_BGR;
+ node->node.GROUP15.B2R2_VMX2 = B2R2_VMX2_RGB_TO_BGR;
+ node->node.GROUP15.B2R2_VMX3 = B2R2_VMX3_RGB_TO_BGR;
+ break;
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ /* Set up IVMX */
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_IVMX_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_IVMX;
+ if (dst_img->fmt == B2R2_BLT_FMT_Y_CB_Y_CR) {
+ /*
+ * Setup input VMX to convert YVU to RGB 601 VIDEO
+ * Chroma components are swapped
+ * so it is YVU and not YUV.
+ */
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_YVU_TO_RGB_601_VIDEO;
+ } else {
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_YUV_TO_RGB_601_VIDEO;
+ }
+ break;
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE: {
+ /* Set up IVMX */
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_IVMX_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_IVMX;
+
+ if (dst_img->fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ dst_img->fmt ==
+ B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR) {
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_YVU_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_YVU_TO_RGB_601_VIDEO;
+ } else {
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_YUV_TO_RGB_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_YUV_TO_RGB_601_VIDEO;
+ }
+
+ switch (dst_img->fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ /*
+ * Chrominance is always half the luminance size
+ * so chrominance resizer is always active.
+ */
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER;
+
+ rsf &= ~(0xffff << B2R2_RSF_HSRC_INC_SHIFT);
+ rsf |= (1 << 9) << B2R2_RSF_HSRC_INC_SHIFT;
+ rsf &= ~(0xffff << B2R2_RSF_VSRC_INC_SHIFT);
+ rsf |= (1 << 9) << B2R2_RSF_VSRC_INC_SHIFT;
+ break;
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ /*
+ * Chrominance is always half the luminance size
+ * only in horizontal direction.
+ */
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER;
+
+ rsf &= ~(0xffff << B2R2_RSF_HSRC_INC_SHIFT);
+ rsf |= (1 << 9) << B2R2_RSF_HSRC_INC_SHIFT;
+ rsf &= ~(0xffff << B2R2_RSF_VSRC_INC_SHIFT);
+ rsf |= (1 << 10) << B2R2_RSF_VSRC_INC_SHIFT;
+ break;
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ /* Chrominance is the same size as luminance.*/
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER;
+
+ rsf &= ~(0xffff << B2R2_RSF_HSRC_INC_SHIFT);
+ rsf |= (1 << 10) << B2R2_RSF_HSRC_INC_SHIFT;
+ rsf &= ~(0xffff << B2R2_RSF_VSRC_INC_SHIFT);
+ rsf |= (1 << 10) << B2R2_RSF_VSRC_INC_SHIFT;
+ break;
+ default:
+ break;
+ }
+ /* Set the filter control and rescale registers for chroma */
+ node->node.GROUP8.B2R2_FCTL |= fctl;
+ node->node.GROUP9.B2R2_RSF |= rsf;
+ node->node.GROUP9.B2R2_RZI =
+ B2R2_RZI_DEFAULT_HNB_REPEAT |
+ (2 << B2R2_RZI_VNB_REPEAT_SHIFT);
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_RESCALE2D_ENABLED;
+ node->node.GROUP0.B2R2_CIC |=
+ B2R2_CIC_FILTER_CONTROL | B2R2_CIC_RESIZE_CHROMA;
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Set target buffer */
+ node->node.GROUP1.B2R2_TBA = out_buf->phys_addr;
+ node->node.GROUP1.B2R2_TTY =
+ (B2R2_GENERIC_WORK_BUF_PITCH << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ B2R2_GENERIC_WORK_BUF_FMT |
+ B2R2_TY_ALPHA_RANGE_255 |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ if (yuv_planar) {
+ /*
+ * Set up chrominance buffers on source 1 and 2,
+ * luminance on source 3.
+ * dst_pitch and physical_address apply to luminance,
+ * corresponding chrominance values have to be derived.
+ */
+ u32 cb_addr = 0;
+ u32 cr_addr = 0;
+ u32 chroma_pitch = 0;
+ bool swapped_chroma =
+ dst_img->fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ dst_img->fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR;
+ enum b2r2_native_fmt dst_native_fmt =
+ to_native_fmt(dst_img->fmt);
+
+ if (swapped_chroma)
+ cr_addr = req->dst_resolved.physical_address +
+ dst_pitch * dst_img->height;
+ else
+ cb_addr = req->dst_resolved.physical_address +
+ dst_pitch * dst_img->height;
+
+ switch (dst_img->fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ chroma_pitch = dst_pitch >> 1;
+ if (swapped_chroma)
+ cb_addr = cr_addr + chroma_pitch *
+ (dst_img->height >> 1);
+ else
+ cr_addr = cb_addr + chroma_pitch *
+ (dst_img->height >> 1);
+ break;
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ chroma_pitch = dst_pitch >> 1;
+ if (swapped_chroma)
+ cb_addr = cr_addr + chroma_pitch *
+ dst_img->height;
+ else
+ cr_addr = cb_addr + chroma_pitch *
+ dst_img->height;
+ break;
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ chroma_pitch = dst_pitch;
+ cr_addr =
+ cb_addr + chroma_pitch * dst_img->height;
+ break;
+ default:
+ break;
+ }
+
+ node->node.GROUP3.B2R2_SBA = cr_addr;
+ node->node.GROUP3.B2R2_STY =
+ (chroma_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ dst_native_fmt |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP4.B2R2_SBA = cb_addr;
+ node->node.GROUP4.B2R2_STY = node->node.GROUP3.B2R2_STY;
+
+ node->node.GROUP5.B2R2_SBA = req->dst_resolved.physical_address;
+ node->node.GROUP5.B2R2_STY =
+ (dst_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ dst_native_fmt |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_1_FETCH_FROM_MEM |
+ B2R2_INS_SOURCE_2_FETCH_FROM_MEM |
+ B2R2_INS_SOURCE_3_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_CIC |=
+ B2R2_CIC_SOURCE_1 |
+ B2R2_CIC_SOURCE_2 |
+ B2R2_CIC_SOURCE_3;
+ } else if (yuv_semi_planar) {
+ /*
+ * Set up chrominance buffer on source 2, luminance on source 3.
+ * dst_pitch and physical_address apply to luminance,
+ * corresponding chrominance values have to be derived.
+ * U and V are interleaved at half the luminance resolution,
+ * which makes the pitch of the UV plane equal
+ * to luminance pitch.
+ */
+ u32 chroma_addr = req->dst_resolved.physical_address +
+ dst_pitch * dst_img->height;
+ u32 chroma_pitch = dst_pitch;
+
+ enum b2r2_native_fmt dst_native_fmt =
+ to_native_fmt(dst_img->fmt);
+
+ node->node.GROUP4.B2R2_SBA = chroma_addr;
+ node->node.GROUP4.B2R2_STY =
+ (chroma_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ dst_native_fmt |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP5.B2R2_SBA = req->dst_resolved.physical_address;
+ node->node.GROUP5.B2R2_STY =
+ (dst_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ dst_native_fmt |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_2_FETCH_FROM_MEM |
+ B2R2_INS_SOURCE_3_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_CIC |=
+ B2R2_CIC_SOURCE_2 | B2R2_CIC_SOURCE_3;
+ } else {
+ /* single buffer format */
+ node->node.GROUP4.B2R2_SBA = req->dst_resolved.physical_address;
+ node->node.GROUP4.B2R2_STY =
+ (dst_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ to_native_fmt(dst_img->fmt) |
+ get_alpha_range(dst_img->fmt) |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_2_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_2;
+ }
+
+ node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BYPASS_S2_S3;
+
+ b2r2_log_info("%s DONE\n", __func__);
+}
+
+static void setup_blend_stage(const struct b2r2_blt_request *req,
+ struct b2r2_node *node,
+ struct b2r2_work_buf *bg_buf,
+ struct b2r2_work_buf *fg_buf)
+{
+ u32 global_alpha = req->user_req.global_alpha;
+ b2r2_log_info("%s ENTRY\n", __func__);
+
+ node->node.GROUP0.B2R2_ACK = 0;
+
+ if (req->user_req.flags &
+ (B2R2_BLT_FLAG_GLOBAL_ALPHA_BLEND |
+ B2R2_BLT_FLAG_PER_PIXEL_ALPHA_BLEND)) {
+ /* Some kind of blending needs to be done. */
+ if (req->user_req.flags & B2R2_BLT_FLAG_SRC_IS_NOT_PREMULT)
+ node->node.GROUP0.B2R2_ACK |=
+ B2R2_ACK_MODE_BLEND_NOT_PREMULT;
+ else
+ node->node.GROUP0.B2R2_ACK |=
+ B2R2_ACK_MODE_BLEND_PREMULT;
+
+ /*
+ * global_alpha register accepts 0..128 range,
+ * global_alpha in the request is 0..255, remap needed.
+ */
+ if (req->user_req.flags & B2R2_BLT_FLAG_GLOBAL_ALPHA_BLEND) {
+ if (global_alpha == 255)
+ global_alpha = 128;
+ else
+ global_alpha >>= 1;
+ } else {
+ /*
+ * Use solid global_alpha
+ * if global alpha blending is not set.
+ */
+ global_alpha = 128;
+ }
+
+ node->node.GROUP0.B2R2_ACK |=
+ global_alpha << (B2R2_ACK_GALPHA_ROPID_SHIFT);
+
+ /* Set background on SRC1 channel */
+ node->node.GROUP3.B2R2_SBA = bg_buf->phys_addr;
+ node->node.GROUP3.B2R2_STY =
+ (B2R2_GENERIC_WORK_BUF_PITCH <<
+ B2R2_TY_BITMAP_PITCH_SHIFT) |
+ B2R2_GENERIC_WORK_BUF_FMT |
+ B2R2_TY_ALPHA_RANGE_255 |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ /* Set foreground on SRC2 channel */
+ node->node.GROUP4.B2R2_SBA = fg_buf->phys_addr;
+ node->node.GROUP4.B2R2_STY =
+ (B2R2_GENERIC_WORK_BUF_PITCH <<
+ B2R2_TY_BITMAP_PITCH_SHIFT) |
+ B2R2_GENERIC_WORK_BUF_FMT |
+ B2R2_TY_ALPHA_RANGE_255 |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ /* Set target buffer */
+ node->node.GROUP1.B2R2_TBA = bg_buf->phys_addr;
+ node->node.GROUP1.B2R2_TTY =
+ (B2R2_GENERIC_WORK_BUF_PITCH <<
+ B2R2_TY_BITMAP_PITCH_SHIFT) |
+ B2R2_GENERIC_WORK_BUF_FMT |
+ B2R2_TY_ALPHA_RANGE_255 |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_1_FETCH_FROM_MEM |
+ B2R2_INS_SOURCE_2_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_CIC |=
+ B2R2_CIC_SOURCE_1 |
+ B2R2_CIC_SOURCE_2;
+ } else {
+ /*
+ * No blending, foreground goes on SRC2. No global alpha.
+ * EMACSOC TODO: The blending stage should be skipped altogether
+ * if no blending is to be done. Probably could go directly from
+ * transform to writeback.
+ */
+ node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BYPASS_S2_S3;
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_2_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_2;
+
+ node->node.GROUP4.B2R2_SBA = fg_buf->phys_addr;
+ node->node.GROUP4.B2R2_STY =
+ (B2R2_GENERIC_WORK_BUF_PITCH <<
+ B2R2_TY_BITMAP_PITCH_SHIFT) |
+ B2R2_GENERIC_WORK_BUF_FMT |
+ B2R2_TY_ALPHA_RANGE_255 |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ node->node.GROUP1.B2R2_TBA = bg_buf->phys_addr;
+ node->node.GROUP1.B2R2_TTY =
+ (B2R2_GENERIC_WORK_BUF_PITCH <<
+ B2R2_TY_BITMAP_PITCH_SHIFT) |
+ B2R2_GENERIC_WORK_BUF_FMT |
+ B2R2_TY_ALPHA_RANGE_255 |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+ }
+
+ b2r2_log_info("%s DONE\n", __func__);
+}
+
+static void setup_writeback_stage(const struct b2r2_blt_request *req,
+ struct b2r2_node *node,
+ struct b2r2_work_buf *in_buf)
+{
+ const struct b2r2_blt_img *dst_img = &(req->user_req.dst_img);
+ const enum b2r2_blt_fmt dst_fmt = dst_img->fmt;
+ const bool yuv_planar_dst =
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV444_PACKED_PLANAR;
+
+ const bool yuv_semi_planar_dst =
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE;
+
+ const u32 group4_b2r2_sty =
+ (B2R2_GENERIC_WORK_BUF_PITCH << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ B2R2_GENERIC_WORK_BUF_FMT |
+ B2R2_TY_ALPHA_RANGE_255 |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ u32 dst_dither = 0;
+ u32 dst_pitch = 0;
+
+ b2r2_log_info("%s ENTRY\n", __func__);
+
+ if (dst_img->pitch == 0) {
+ /* Determine pitch based on format and width of the image. */
+ dst_pitch = get_pitch(dst_img->fmt, dst_img->width);
+ } else
+ dst_pitch = dst_img->pitch;
+
+ if ((req->user_req.flags & B2R2_BLT_FLAG_DITHER) != 0)
+ dst_dither = B2R2_TTY_RGB_ROUND_DITHER;
+
+ /* Set target buffer(s) */
+ if (yuv_planar_dst) {
+ /*
+ * three nodes required to write the output.
+ * Luma, blue chroma and red chroma.
+ */
+ u32 fctl = 0;
+ u32 rsf = 0;
+ const u32 group0_b2r2_ins =
+ B2R2_INS_SOURCE_2_FETCH_FROM_MEM |
+ B2R2_INS_RECT_CLIP_ENABLED |
+ B2R2_INS_IVMX_ENABLED;
+ const u32 group0_b2r2_cic =
+ B2R2_CIC_SOURCE_2 |
+ B2R2_CIC_CLIP_WINDOW |
+ B2R2_CIC_IVMX;
+
+ u32 cb_addr = 0;
+ u32 cr_addr = 0;
+ u32 chroma_pitch = 0;
+ bool swapped_chroma =
+ dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR;
+ enum b2r2_native_fmt dst_native_fmt =
+ to_native_fmt(dst_img->fmt);
+ enum b2r2_ty alpha_range = get_alpha_range(dst_img->fmt);
+
+ if (swapped_chroma)
+ cr_addr = req->dst_resolved.physical_address +
+ dst_pitch * dst_img->height;
+ else
+ cb_addr = req->dst_resolved.physical_address +
+ dst_pitch * dst_img->height;
+
+ switch (dst_fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ chroma_pitch = dst_pitch >> 1;
+ if (swapped_chroma)
+ cb_addr = cr_addr + chroma_pitch *
+ (dst_img->height >> 1);
+ else
+ cr_addr = cb_addr + chroma_pitch *
+ (dst_img->height >> 1);
+ /*
+ * Chrominance is always half the luminance size
+ * so chrominance resizer is always active.
+ */
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER;
+
+ rsf &= ~(0xffff << B2R2_RSF_HSRC_INC_SHIFT);
+ rsf |= (2 << 10) << B2R2_RSF_HSRC_INC_SHIFT;
+ rsf &= ~(0xffff << B2R2_RSF_VSRC_INC_SHIFT);
+ rsf |= (2 << 10) << B2R2_RSF_VSRC_INC_SHIFT;
+ break;
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ chroma_pitch = dst_pitch >> 1;
+ if (swapped_chroma)
+ cb_addr = cr_addr + chroma_pitch *
+ dst_img->height;
+ else
+ cr_addr = cb_addr + chroma_pitch *
+ dst_img->height;
+ /*
+ * YUV422 or YVU422
+ * Chrominance is always half the luminance size
+ * only in horizontal direction.
+ */
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER;
+
+ rsf &= ~(0xffff << B2R2_RSF_HSRC_INC_SHIFT);
+ rsf |= (2 << 10) << B2R2_RSF_HSRC_INC_SHIFT;
+ rsf &= ~(0xffff << B2R2_RSF_VSRC_INC_SHIFT);
+ rsf |= (1 << 10) << B2R2_RSF_VSRC_INC_SHIFT;
+ break;
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ chroma_pitch = dst_pitch;
+ cr_addr =
+ cb_addr + chroma_pitch * dst_img->height;
+ /*
+ * No scaling required since
+ * chrominance is not subsampled.
+ */
+ default:
+ break;
+ }
+
+ /* Luma (Y-component) */
+ node->node.GROUP1.B2R2_TBA = req->dst_resolved.physical_address;
+ node->node.GROUP1.B2R2_TTY =
+ (dst_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ dst_native_fmt | alpha_range |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM |
+ dst_dither;
+
+ node->node.GROUP15.B2R2_VMX0 = B2R2_VMX0_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 = B2R2_VMX1_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 = B2R2_VMX2_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 = B2R2_VMX3_RGB_TO_YUV_601_VIDEO;
+
+ /* bypass ALU, no blending here. Handled in its own stage. */
+ node->node.GROUP0.B2R2_ACK = B2R2_ACK_MODE_BYPASS_S2_S3;
+ node->node.GROUP0.B2R2_INS = group0_b2r2_ins;
+ node->node.GROUP0.B2R2_CIC |= group0_b2r2_cic;
+
+ /* Set source buffer on SRC2 channel */
+ node->node.GROUP4.B2R2_SBA = in_buf->phys_addr;
+ node->node.GROUP4.B2R2_STY = group4_b2r2_sty;
+
+ /* Blue chroma (U-component)*/
+ node = node->next;
+ node->node.GROUP1.B2R2_TBA = cb_addr;
+ node->node.GROUP1.B2R2_TTY =
+ (chroma_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ dst_native_fmt | alpha_range |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM |
+ dst_dither |
+ B2R2_TTY_CHROMA_NOT_LUMA;
+
+ node->node.GROUP15.B2R2_VMX0 = B2R2_VMX0_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 = B2R2_VMX1_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 = B2R2_VMX2_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 = B2R2_VMX3_RGB_TO_YUV_601_VIDEO;
+
+ node->node.GROUP0.B2R2_ACK = B2R2_ACK_MODE_BYPASS_S2_S3;
+ node->node.GROUP0.B2R2_INS = group0_b2r2_ins;
+ node->node.GROUP0.B2R2_CIC |= group0_b2r2_cic;
+ if (dst_fmt != B2R2_BLT_FMT_YUV444_PACKED_PLANAR) {
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_RESCALE2D_ENABLED;
+ node->node.GROUP0.B2R2_CIC |=
+ B2R2_CIC_FILTER_CONTROL |
+ B2R2_CIC_RESIZE_CHROMA;
+ /* Set the filter control and rescale registers */
+ node->node.GROUP8.B2R2_FCTL = fctl;
+ node->node.GROUP9.B2R2_RSF = rsf;
+ node->node.GROUP9.B2R2_RZI =
+ B2R2_RZI_DEFAULT_HNB_REPEAT |
+ (2 << B2R2_RZI_VNB_REPEAT_SHIFT);
+ }
+
+ node->node.GROUP4.B2R2_SBA = in_buf->phys_addr;
+ node->node.GROUP4.B2R2_STY = group4_b2r2_sty;
+
+
+ /*
+ * Red chroma (V-component)
+ * The flag B2R2_TTY_CB_NOT_CR actually works
+ * the other way around, i.e. as if it was
+ * CR_NOT_CB.
+ */
+ node = node->next;
+ node->node.GROUP1.B2R2_TBA = cr_addr;
+ node->node.GROUP1.B2R2_TTY =
+ (chroma_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ dst_native_fmt | alpha_range |
+ B2R2_TTY_CB_NOT_CR |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM |
+ dst_dither |
+ B2R2_TTY_CHROMA_NOT_LUMA;
+
+ node->node.GROUP15.B2R2_VMX0 = B2R2_VMX0_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 = B2R2_VMX1_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 = B2R2_VMX2_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 = B2R2_VMX3_RGB_TO_YUV_601_VIDEO;
+
+ node->node.GROUP0.B2R2_ACK = B2R2_ACK_MODE_BYPASS_S2_S3;
+ node->node.GROUP0.B2R2_INS = group0_b2r2_ins;
+ node->node.GROUP0.B2R2_CIC |= group0_b2r2_cic;
+ if (dst_fmt != B2R2_BLT_FMT_YUV444_PACKED_PLANAR) {
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_RESCALE2D_ENABLED;
+ node->node.GROUP0.B2R2_CIC |=
+ B2R2_CIC_FILTER_CONTROL |
+ B2R2_CIC_RESIZE_CHROMA;
+ /* Set the filter control and rescale registers */
+ node->node.GROUP8.B2R2_FCTL = fctl;
+ node->node.GROUP9.B2R2_RSF = rsf;
+ node->node.GROUP9.B2R2_RZI =
+ B2R2_RZI_DEFAULT_HNB_REPEAT |
+ (2 << B2R2_RZI_VNB_REPEAT_SHIFT);
+ }
+
+ node->node.GROUP4.B2R2_SBA = in_buf->phys_addr;
+ node->node.GROUP4.B2R2_STY = group4_b2r2_sty;
+ } else if (yuv_semi_planar_dst) {
+ /*
+ * two nodes required to write the output.
+ * One node for luma and one for interleaved chroma
+ * components.
+ */
+ u32 fctl = 0;
+ u32 rsf = 0;
+ const u32 group0_b2r2_ins =
+ B2R2_INS_SOURCE_2_FETCH_FROM_MEM |
+ B2R2_INS_RECT_CLIP_ENABLED |
+ B2R2_INS_IVMX_ENABLED;
+ const u32 group0_b2r2_cic =
+ B2R2_CIC_SOURCE_2 |
+ B2R2_CIC_CLIP_WINDOW |
+ B2R2_CIC_IVMX;
+
+ u32 chroma_addr = req->dst_resolved.physical_address +
+ dst_pitch * dst_img->height;
+ u32 chroma_pitch = dst_pitch;
+ enum b2r2_native_fmt dst_native_fmt =
+ to_native_fmt(dst_img->fmt);
+ enum b2r2_ty alpha_range = get_alpha_range(dst_img->fmt);
+
+ if (dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR ||
+ dst_fmt ==
+ B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE ||
+ dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR) {
+ /*
+ * Chrominance is always half the luminance size
+ * so chrominance resizer is always active.
+ */
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER;
+
+ rsf &= ~(0xffff << B2R2_RSF_HSRC_INC_SHIFT);
+ rsf |= (2 << 10) << B2R2_RSF_HSRC_INC_SHIFT;
+ rsf &= ~(0xffff << B2R2_RSF_VSRC_INC_SHIFT);
+ rsf |= (2 << 10) << B2R2_RSF_VSRC_INC_SHIFT;
+ } else {
+ /*
+ * YUV422
+ * Chrominance is always half the luminance size
+ * only in horizontal direction.
+ */
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER;
+
+ rsf &= ~(0xffff << B2R2_RSF_HSRC_INC_SHIFT);
+ rsf |= (2 << 10) << B2R2_RSF_HSRC_INC_SHIFT;
+ rsf &= ~(0xffff << B2R2_RSF_VSRC_INC_SHIFT);
+ rsf |= (1 << 10) << B2R2_RSF_VSRC_INC_SHIFT;
+ }
+
+ /* Luma (Y-component) */
+ node->node.GROUP1.B2R2_TBA = req->dst_resolved.physical_address;
+ node->node.GROUP1.B2R2_TTY =
+ (dst_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ dst_native_fmt | alpha_range |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM |
+ dst_dither;
+
+ if (dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR) {
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_RGB_TO_YVU_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_RGB_TO_YVU_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_RGB_TO_YVU_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_RGB_TO_YVU_601_VIDEO;
+ } else {
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_RGB_TO_YUV_601_VIDEO;
+ }
+
+ /* bypass ALU, no blending here. Handled in its own stage. */
+ node->node.GROUP0.B2R2_ACK = B2R2_ACK_MODE_BYPASS_S2_S3;
+ node->node.GROUP0.B2R2_INS = group0_b2r2_ins;
+ node->node.GROUP0.B2R2_CIC |= group0_b2r2_cic;
+
+ /* Set source buffer on SRC2 channel */
+ node->node.GROUP4.B2R2_SBA = in_buf->phys_addr;
+ node->node.GROUP4.B2R2_STY = group4_b2r2_sty;
+
+ /* Chroma (UV-components)*/
+ node = node->next;
+ node->node.GROUP1.B2R2_TBA = chroma_addr;
+ node->node.GROUP1.B2R2_TTY =
+ (chroma_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ dst_native_fmt | alpha_range |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM |
+ dst_dither |
+ B2R2_TTY_CHROMA_NOT_LUMA;
+
+ if (dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR) {
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_RGB_TO_YVU_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_RGB_TO_YVU_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_RGB_TO_YVU_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_RGB_TO_YVU_601_VIDEO;
+ } else {
+ node->node.GROUP15.B2R2_VMX0 =
+ B2R2_VMX0_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX1 =
+ B2R2_VMX1_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX2 =
+ B2R2_VMX2_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP15.B2R2_VMX3 =
+ B2R2_VMX3_RGB_TO_YUV_601_VIDEO;
+ }
+
+ node->node.GROUP0.B2R2_ACK = B2R2_ACK_MODE_BYPASS_S2_S3;
+ node->node.GROUP0.B2R2_INS =
+ group0_b2r2_ins | B2R2_INS_RESCALE2D_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= group0_b2r2_cic |
+ B2R2_CIC_FILTER_CONTROL |
+ B2R2_CIC_RESIZE_CHROMA;
+
+ /* Set the filter control and rescale registers */
+ node->node.GROUP8.B2R2_FCTL = fctl;
+ node->node.GROUP9.B2R2_RSF = rsf;
+ node->node.GROUP9.B2R2_RZI =
+ B2R2_RZI_DEFAULT_HNB_REPEAT |
+ (2 << B2R2_RZI_VNB_REPEAT_SHIFT);
+
+ node->node.GROUP4.B2R2_SBA = in_buf->phys_addr;
+ node->node.GROUP4.B2R2_STY = group4_b2r2_sty;
+ } else {
+ /* single buffer target */
+
+ /* Set up OVMX */
+ switch (dst_fmt) {
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_OVMX_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_OVMX;
+ node->node.GROUP16.B2R2_VMX0 = B2R2_VMX0_RGB_TO_BGR;
+ node->node.GROUP16.B2R2_VMX1 = B2R2_VMX1_RGB_TO_BGR;
+ node->node.GROUP16.B2R2_VMX2 = B2R2_VMX2_RGB_TO_BGR;
+ node->node.GROUP16.B2R2_VMX3 = B2R2_VMX3_RGB_TO_BGR;
+ break;
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_OVMX_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_OVMX;
+ node->node.GROUP16.B2R2_VMX0 =
+ B2R2_VMX0_RGB_TO_YVU_601_VIDEO;
+ node->node.GROUP16.B2R2_VMX1 =
+ B2R2_VMX1_RGB_TO_YVU_601_VIDEO;
+ node->node.GROUP16.B2R2_VMX2 =
+ B2R2_VMX2_RGB_TO_YVU_601_VIDEO;
+ node->node.GROUP16.B2R2_VMX3 =
+ B2R2_VMX3_RGB_TO_YVU_601_VIDEO;
+ break;
+ case B2R2_BLT_FMT_24_BIT_YUV888: /* fall through */
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_OVMX_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_OVMX;
+ node->node.GROUP16.B2R2_VMX0 =
+ B2R2_VMX0_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP16.B2R2_VMX1 =
+ B2R2_VMX1_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP16.B2R2_VMX2 =
+ B2R2_VMX2_RGB_TO_YUV_601_VIDEO;
+ node->node.GROUP16.B2R2_VMX3 =
+ B2R2_VMX3_RGB_TO_YUV_601_VIDEO;
+ break;
+ default:
+ break;
+ }
+
+ node->node.GROUP1.B2R2_TBA = req->dst_resolved.physical_address;
+ node->node.GROUP1.B2R2_TTY =
+ (dst_pitch << B2R2_TY_BITMAP_PITCH_SHIFT) |
+ to_native_fmt(dst_img->fmt) |
+ get_alpha_range(dst_img->fmt) |
+ B2R2_TY_HSO_LEFT_TO_RIGHT |
+ B2R2_TY_VSO_TOP_TO_BOTTOM |
+ dst_dither;
+
+ node->node.GROUP0.B2R2_ACK = B2R2_ACK_MODE_BYPASS_S2_S3;
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_2_FETCH_FROM_MEM |
+ B2R2_INS_RECT_CLIP_ENABLED;
+ node->node.GROUP0.B2R2_CIC |=
+ B2R2_CIC_SOURCE_2 | B2R2_CIC_CLIP_WINDOW;
+
+ if (req->user_req.flags & B2R2_BLT_FLAG_SOURCE_COLOR_KEY) {
+ u32 key_color = 0;
+
+ node->node.GROUP0.B2R2_ACK |=
+ B2R2_ACK_CKEY_SEL_SRC_AFTER_CLUT |
+ B2R2_ACK_CKEY_RED_MATCH_IF_BETWEEN |
+ B2R2_ACK_CKEY_GREEN_MATCH_IF_BETWEEN |
+ B2R2_ACK_CKEY_BLUE_MATCH_IF_BETWEEN;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_CKEY_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_COLOR_KEY;
+
+ key_color = to_RGB888(req->user_req.src_color,
+ req->user_req.src_img.fmt);
+ node->node.GROUP12.B2R2_KEY1 = key_color;
+ node->node.GROUP12.B2R2_KEY2 = key_color;
+ }
+
+ /* Set source buffer on SRC2 channel */
+ node->node.GROUP4.B2R2_SBA = in_buf->phys_addr;
+ node->node.GROUP4.B2R2_STY = group4_b2r2_sty;
+ }
+ /*
+ * Writeback is the last stage. Terminate the program chain
+ * to prevent out-of-control B2R2 execution.
+ */
+ node->node.GROUP0.B2R2_NIP = 0;
+
+ b2r2_log_info("%s DONE\n", __func__);
+}
+
+/*
+ * Public functions
+ */
+void b2r2_generic_init()
+{
+ b2r2_filters_init();
+}
+
+void b2r2_generic_exit(void)
+{
+ b2r2_filters_exit();
+}
+
+int b2r2_generic_analyze(const struct b2r2_blt_request *req,
+ s32 *work_buf_width,
+ s32 *work_buf_height,
+ u32 *work_buf_count,
+ u32 *node_count)
+{
+ /*
+ * Need at least 4 nodes, read or fill input, read dst, blend
+ * and write back the result */
+ u32 n_nodes = 4;
+ /* Need at least 2 bufs, 1 for blend output and 1 for input */
+ u32 n_work_bufs = 2;
+ /* Horizontal and vertical scaling factors in 6.10 fixed point format */
+ s32 h_scf = 1 << 10;
+ s32 v_scf = 1 << 10;
+ enum b2r2_blt_fmt dst_fmt = 0;
+ bool is_src_fill = false;
+ bool yuv_planar_dst;
+ bool yuv_semi_planar_dst;
+
+ struct b2r2_blt_rect src_rect;
+ struct b2r2_blt_rect dst_rect;
+
+ if (req == NULL || work_buf_width == NULL || work_buf_height == NULL ||
+ work_buf_count == NULL || node_count == NULL) {
+ b2r2_log_warn("%s: Invalid in or out pointers:\n"
+ "req=0x%p\n"
+ "work_buf_width=0x%p work_buf_height=0x%p "
+ "work_buf_count=0x%p\n"
+ "node_count=0x%p.\n",
+ __func__,
+ req,
+ work_buf_width, work_buf_height,
+ work_buf_count,
+ node_count);
+ return -EINVAL;
+ }
+
+ dst_fmt = req->user_req.dst_img.fmt;
+
+ is_src_fill = (req->user_req.flags &
+ (B2R2_BLT_FLAG_SOURCE_FILL |
+ B2R2_BLT_FLAG_SOURCE_FILL_RAW)) != 0;
+
+ yuv_planar_dst =
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV444_PACKED_PLANAR;
+ yuv_semi_planar_dst =
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE;
+
+ *node_count = 0;
+ *work_buf_width = 0;
+ *work_buf_height = 0;
+ *work_buf_count = 0;
+
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) {
+ n_nodes++;
+ n_work_bufs++;
+ }
+
+ if ((yuv_planar_dst || yuv_semi_planar_dst) &&
+ (req->user_req.flags & B2R2_BLT_FLAG_SOURCE_FILL_RAW)) {
+ b2r2_log_warn("%s: Invalid combination: source_fill_raw"
+ " and multi-buffer destination.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if ((req->user_req.flags & B2R2_BLT_FLAG_SOURCE_COLOR_KEY) != 0 &&
+ (req->user_req.flags & B2R2_BLT_FLAG_DEST_COLOR_KEY)) {
+ b2r2_log_warn("%s: Invalid combination: source and "
+ "destination color keying.\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((req->user_req.flags &
+ (B2R2_BLT_FLAG_SOURCE_FILL |
+ B2R2_BLT_FLAG_SOURCE_FILL_RAW)) &&
+ (req->user_req.flags &
+ (B2R2_BLT_FLAG_SOURCE_COLOR_KEY |
+ B2R2_BLT_FLAG_DEST_COLOR_KEY))) {
+ b2r2_log_warn("%s: Invalid combination: "
+ "source_fill and color keying.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if ((req->user_req.flags &
+ (B2R2_BLT_FLAG_PER_PIXEL_ALPHA_BLEND |
+ B2R2_BLT_FLAG_GLOBAL_ALPHA_BLEND)) &&
+ (req->user_req.flags &
+ (B2R2_BLT_FLAG_DEST_COLOR_KEY |
+ B2R2_BLT_FLAG_SOURCE_COLOR_KEY))) {
+ b2r2_log_warn("%s: Invalid combination: "
+ "blending and color keying.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if ((req->user_req.flags & B2R2_BLT_FLAG_SOURCE_MASK) &&
+ (req->user_req.flags &
+ (B2R2_BLT_FLAG_DEST_COLOR_KEY |
+ B2R2_BLT_FLAG_SOURCE_COLOR_KEY))) {
+ b2r2_log_warn("%s: Invalid combination: source mask and "
+ "color keying.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (req->user_req.flags &
+ (B2R2_BLT_FLAG_DEST_COLOR_KEY |
+ B2R2_BLT_FLAG_SOURCE_MASK)) {
+ b2r2_log_warn("%s: Unsupported: source mask, "
+ "destination color keying.\n",
+ __func__);
+ return -ENOSYS;
+ }
+
+ if ((req->user_req.flags & B2R2_BLT_FLAG_SOURCE_MASK)) {
+ enum b2r2_blt_fmt src_fmt = req->user_req.src_img.fmt;
+ bool yuv_src =
+ src_fmt == B2R2_BLT_FMT_Y_CB_Y_CR ||
+ src_fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YUV444_PACKED_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR ||
+ src_fmt ==
+ B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE ||
+ src_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE;
+ if (yuv_src || src_fmt == B2R2_BLT_FMT_1_BIT_A1 ||
+ src_fmt == B2R2_BLT_FMT_8_BIT_A8) {
+ b2r2_log_warn("%s: Unsupported: source color keying "
+ "with YUV or pure alpha formats.\n",
+ __func__);
+ return -ENOSYS;
+ }
+ }
+
+ /* Check for invalid dimensions that would hinder scale calculations */
+ src_rect = req->user_req.src_rect;
+ dst_rect = req->user_req.dst_rect;
+ /* Check for invalid src_rect unless src_fill is enabled */
+ if (!is_src_fill && (src_rect.x < 0 || src_rect.y < 0 ||
+ src_rect.x + src_rect.width > req->user_req.src_img.width ||
+ src_rect.y + src_rect.height > req->user_req.src_img.height)) {
+ b2r2_log_warn("%s: src_rect outside src_img:\n"
+ "src(x,y,w,h)=(%d, %d, %d, %d) "
+ "src_img(w,h)=(%d, %d).\n",
+ __func__,
+ src_rect.x, src_rect.y, src_rect.width, src_rect.height,
+ req->user_req.src_img.width,
+ req->user_req.src_img.height);
+ return -EINVAL;
+ }
+
+ if (!is_src_fill && (src_rect.width <= 0 || src_rect.height <= 0)) {
+ b2r2_log_warn("%s: Invalid source dimensions:\n"
+ "src(w,h)=(%d, %d).\n",
+ __func__,
+ src_rect.width, src_rect.height);
+ return -EINVAL;
+ }
+
+ if (dst_rect.width <= 0 || dst_rect.height <= 0) {
+ b2r2_log_warn("%s: Invalid dest dimensions:\n"
+ "dst(w,h)=(%d, %d).\n",
+ __func__,
+ dst_rect.width, dst_rect.height);
+ return -EINVAL;
+ }
+
+ if ((req->user_req.flags & B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) &&
+ req->user_req.clut == NULL) {
+ b2r2_log_warn("%s: Invalid request: no table specified "
+ "for CLUT color correction.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Check for invalid image params */
+ if (!is_src_fill && validate_buf(&(req->user_req.src_img),
+ &(req->src_resolved)))
+ return -EINVAL;
+
+ if (validate_buf(&(req->user_req.dst_img), &(req->dst_resolved)))
+ return -EINVAL;
+
+ if (is_src_fill) {
+ /*
+ * Params correct for a source fill operation.
+ * No need for further checking.
+ */
+ if (yuv_planar_dst)
+ n_nodes += 2;
+ else if (yuv_semi_planar_dst)
+ n_nodes++;
+
+ *work_buf_width = B2R2_GENERIC_WORK_BUF_WIDTH;
+ *work_buf_height = B2R2_GENERIC_WORK_BUF_HEIGHT;
+ *work_buf_count = n_work_bufs;
+ *node_count = n_nodes;
+ b2r2_log_info("%s DONE buf_w=%d buf_h=%d buf_count=%d "
+ "node_count=%d\n",
+ __func__,
+ *work_buf_width, *work_buf_height,
+ *work_buf_count, *node_count);
+ return 0;
+ }
+
+ /*
+ * Calculate scaling factors, all transform enum values
+ * that include rotation have the CCW_ROT_90 bit set.
+ */
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) {
+ h_scf = (src_rect.width << 10) / dst_rect.height;
+ v_scf = (src_rect.height << 10) / dst_rect.width;
+ } else {
+ h_scf = (src_rect.width << 10) / dst_rect.width;
+ v_scf = (src_rect.height << 10) / dst_rect.height;
+ }
+
+ /* Check for degenerate/out_of_range scaling factors. */
+ if (h_scf <= 0 || v_scf <= 0 || h_scf > 0x7C00 || v_scf > 0x7C00) {
+ b2r2_log_warn("%s: Dimensions result in degenerate or "
+ "out of range scaling:\n"
+ "src(w,h)=(%d, %d) "
+ "dst(w,h)=(%d,%d).\n"
+ "h_scf=0x%.8x, v_scf=0x%.8x\n",
+ __func__,
+ src_rect.width, src_rect.height,
+ dst_rect.width, dst_rect.height,
+ h_scf, v_scf);
+ return -EINVAL;
+ }
+
+ if (yuv_planar_dst)
+ n_nodes += 2;
+ else if (yuv_semi_planar_dst)
+ n_nodes++;
+
+ *work_buf_width = B2R2_GENERIC_WORK_BUF_WIDTH;
+ *work_buf_height = B2R2_GENERIC_WORK_BUF_HEIGHT;
+ *work_buf_count = n_work_bufs;
+ *node_count = n_nodes;
+ b2r2_log_info("%s DONE buf_w=%d buf_h=%d buf_count=%d node_count=%d\n",
+ __func__,
+ *work_buf_width, *work_buf_height, *work_buf_count,
+ *node_count);
+ return 0;
+}
+
+/*
+ *
+ */
+int b2r2_generic_configure(const struct b2r2_blt_request *req,
+ struct b2r2_node *first,
+ struct b2r2_work_buf *tmp_bufs,
+ u32 buf_count)
+{
+ struct b2r2_node *node = NULL;
+ struct b2r2_work_buf *in_buf = NULL;
+ struct b2r2_work_buf *out_buf = NULL;
+ struct b2r2_work_buf *empty_buf = NULL;
+
+#ifdef B2R2_GENERIC_DEBUG
+ u32 needed_bufs = 0;
+ u32 needed_nodes = 0;
+ s32 work_buf_width = 0;
+ s32 work_buf_height = 0;
+ u32 n_nodes = 0;
+ int invalid_req = b2r2_generic_analyze(req, &work_buf_width,
+ &work_buf_height, &needed_bufs,
+ &needed_nodes);
+ if (invalid_req < 0) {
+ b2r2_log_warn("%s: Invalid request supplied, ec=%d\n",
+ __func__, invalid_req);
+ return -EINVAL;
+ }
+
+ node = first;
+
+ while (node != NULL) {
+ n_nodes++;
+ node = node->next;
+ }
+ if (n_nodes < needed_nodes) {
+ b2r2_log_warn("%s: Not enough nodes %d < %d.\n",
+ __func__, n_nodes, needed_nodes);
+ return -EINVAL;
+ }
+
+ if (buf_count < needed_bufs) {
+ b2r2_log_warn("%s: Not enough buffers %d < %d.\n",
+ __func__, buf_count, needed_bufs);
+ return -EINVAL;
+ }
+
+#endif
+
+ reset_nodes(first);
+ node = first;
+ empty_buf = tmp_bufs;
+ out_buf = empty_buf;
+ empty_buf++;
+ /* Prepare input tile. Color_fill or read from src */
+ setup_input_stage(req, node, out_buf);
+ in_buf = out_buf;
+ out_buf = empty_buf;
+ empty_buf++;
+ node = node->next;
+
+ if ((req->user_req.transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) != 0) {
+ setup_transform_stage(req, node, out_buf, in_buf);
+ node = node->next;
+ in_buf = out_buf;
+ out_buf = empty_buf++;
+ }
+ /* EMACSOC TODO: mask */
+ /*
+ if (req->user_req.flags & B2R2_BLT_FLAG_SOURCE_MASK) {
+ setup_mask_stage(req, node, out_buf, in_buf);
+ node = node->next;
+ in_buf = out_buf;
+ out_buf = empty_buf++;
+ }
+ */
+ /* Read the part of destination that will be updated */
+ setup_dst_read_stage(req, node, out_buf);
+ node = node->next;
+ setup_blend_stage(req, node, out_buf, in_buf);
+ node = node->next;
+ in_buf = out_buf;
+ setup_writeback_stage(req, node, in_buf);
+ return 0;
+}
+
+void b2r2_generic_set_areas(const struct b2r2_blt_request *req,
+ struct b2r2_node *first,
+ struct b2r2_blt_rect *dst_rect_area)
+{
+ /*
+ * Nodes come in the following order: <input stage>, [transform],
+ * [src_mask], <dst_read>, <blend>, <writeback>
+ */
+ struct b2r2_node *node = first;
+ const struct b2r2_blt_rect *dst_rect = &(req->user_req.dst_rect);
+ const struct b2r2_blt_rect *src_rect = &(req->user_req.src_rect);
+ const enum b2r2_blt_fmt src_fmt = req->user_req.src_img.fmt;
+ bool yuv_multi_buffer_src =
+ src_fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YUV444_PACKED_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR ||
+ src_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE ||
+ src_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE;
+ const enum b2r2_blt_fmt dst_fmt = req->user_req.dst_img.fmt;
+ const bool yuv_multi_buffer_dst =
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV444_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE;
+ s32 h_scf = 1 << 10;
+ s32 v_scf = 1 << 10;
+ s32 src_x = 0;
+ s32 src_y = 0;
+ s32 src_w = 0;
+ s32 src_h = 0;
+ u32 b2r2_rzi = 0;
+ s32 clip_top = 0;
+ s32 clip_left = 0;
+ s32 clip_bottom = req->user_req.dst_img.height - 1;
+ s32 clip_right = req->user_req.dst_img.width - 1;
+ /* Dst coords inside the dst_rect, not the buffer */
+ s32 dst_x = dst_rect_area->x;
+ s32 dst_y = dst_rect_area->y;
+
+ b2r2_log_info("%s ENTRY\n", __func__);
+
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) {
+ h_scf = (src_rect->width << 10) / dst_rect->height;
+ v_scf = (src_rect->height << 10) / dst_rect->width;
+ } else {
+ h_scf = (src_rect->width << 10) / dst_rect->width;
+ v_scf = (src_rect->height << 10) / dst_rect->height;
+ }
+
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) {
+ /*
+ * Normally the inverse transform for 90 degree rotation
+ * is given by:
+ * | 0 1| |x| | y|
+ * | | X | | = | |
+ * |-1 0| |y| |-x|
+ * but screen coordinates are flipped in y direction
+ * (compared to usual Cartesian coordinates), hence the offsets.
+ */
+ src_x = (dst_rect->height - dst_y - dst_rect_area->height) *
+ h_scf;
+ src_y = dst_x * v_scf;
+ src_w = dst_rect_area->height * h_scf;
+ src_h = dst_rect_area->width * v_scf;
+ } else {
+ src_x = dst_x * h_scf;
+ src_y = dst_y * v_scf;
+ src_w = dst_rect_area->width * h_scf;
+ src_h = dst_rect_area->height * v_scf;
+ }
+
+ b2r2_rzi |= ((src_x & 0x3ff) << B2R2_RZI_HSRC_INIT_SHIFT) |
+ ((src_y & 0x3ff) << B2R2_RZI_VSRC_INIT_SHIFT);
+
+ /*
+ * src_w must contain all the pixels that contribute
+ * to a particular tile.
+ * ((x + 0x3ff) >> 10) is equivalent to ceiling(x),
+ * expressed in 6.10 fixed point format.
+ * Every destination tile, maps to a certain area in the source
+ * rectangle. The area in source will most likely not be a rectangle
+ * with exact integer dimensions whenever arbitrary scaling is involved.
+ * Consider the following example.
+ * Suppose, that width of the current destination tile maps
+ * to 1.7 pixels in source, starting at x == 5.4, as calculated
+ * using the scaling factor.
+ * This means that while the destination tile is written,
+ * the source should be read from x == 5.4 up to x == 5.4 + 1.7 == 7.1
+ * Consequently, color from 3 pixels (x == 5, 6 and 7)
+ * needs to be read from source.
+ * The formula below the comment yields:
+ * ceil(0.4 + 1.7) == ceil(2.1) == 3
+ * (src_x & 0x3ff) is the fractional part of src_x,
+ * which is expressed in 6.10 fixed point format.
+ * Thus, width of the source area should be 3 pixels wide,
+ * starting at x == 5.
+ * However, the reading should not start at x == 5.0
+ * but a bit inside, namely x == 5.4
+ * The B2R2_RZI register is used to instruct the HW to do so.
+ * It contains the fractional part that will be added to
+ * the first pixel coordinate, before incrementing the current source
+ * coordinate with the step specified in B2R2_RSF register.
+ * The same applies to scaling in vertical direction.
+ */
+ src_w = ((src_x & 0x3ff) + src_w + 0x3ff) >> 10;
+ src_h = ((src_y & 0x3ff) + src_h + 0x3ff) >> 10;
+
+ /*
+ * EMACSOC TODO: Remove this debug clamp, once tile size
+ * is taken into account in generic_analyze()
+ */
+ if (src_w > 128)
+ src_w = 128;
+
+ src_x >>= 10;
+ src_y >>= 10;
+
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_FLIP_H)
+ src_x = src_rect->width - src_x - src_w;
+
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_FLIP_V)
+ src_y = src_rect->height - src_y - src_h;
+
+ /*
+ * Translate the src/dst_rect coordinates into true
+ * src/dst_buffer coordinates
+ */
+ src_x += src_rect->x;
+ src_y += src_rect->y;
+
+ dst_x += dst_rect->x;
+ dst_y += dst_rect->y;
+
+ /*
+ * Clamp the src coords to buffer dimensions
+ * to prevent illegal reads.
+ */
+ if (src_x < 0)
+ src_x = 0;
+
+ if (src_y < 0)
+ src_y = 0;
+
+ if ((src_x + src_w) > req->user_req.src_img.width)
+ src_w = req->user_req.src_img.width - src_x;
+
+ if ((src_y + src_h) > req->user_req.src_img.height)
+ src_h = req->user_req.src_img.height - src_y;
+
+
+ /* The input node */
+ if (yuv_multi_buffer_src) {
+ /* Luma on SRC3 */
+ node->node.GROUP5.B2R2_SXY =
+ ((src_x & 0xffff) << B2R2_XY_X_SHIFT) |
+ ((src_y & 0xffff) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP5.B2R2_SSZ =
+ ((src_w & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((src_h & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+
+ /* Clear and set only the SRC_INIT bits */
+ node->node.GROUP10.B2R2_RZI &=
+ ~((0x3ff << B2R2_RZI_HSRC_INIT_SHIFT) |
+ (0x3ff << B2R2_RZI_VSRC_INIT_SHIFT));
+ node->node.GROUP10.B2R2_RZI |= b2r2_rzi;
+
+ node->node.GROUP9.B2R2_RZI &=
+ ~((0x3ff << B2R2_RZI_HSRC_INIT_SHIFT) |
+ (0x3ff << B2R2_RZI_VSRC_INIT_SHIFT));
+ switch (src_fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ /*
+ * Chroma goes on SRC2 and potentially on SRC1.
+ * Chroma is half the size of luma. Must round up
+ * the chroma size to handle cases when luma size is not
+ * divisible by 2.
+ * E.g. luma width==7 requires chroma width==4.
+ * Chroma width==7/2==3 is only enough
+ * for luma width==6.
+ */
+ node->node.GROUP4.B2R2_SXY =
+ (((src_x & 0xffff) >> 1) << B2R2_XY_X_SHIFT) |
+ (((src_y & 0xffff) >> 1) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP4.B2R2_SSZ =
+ ((((src_w + 1) & 0xfff) >> 1) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((((src_h + 1) & 0xfff) >> 1) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+ if (src_fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR ||
+ src_fmt ==
+ B2R2_BLT_FMT_YVU420_PACKED_PLANAR) {
+ node->node.GROUP3.B2R2_SXY =
+ node->node.GROUP4.B2R2_SXY;
+ node->node.GROUP3.B2R2_SSZ =
+ node->node.GROUP4.B2R2_SSZ;
+ }
+ node->node.GROUP9.B2R2_RZI |= (b2r2_rzi >> 1) &
+ ((0x3ff << B2R2_RZI_HSRC_INIT_SHIFT) |
+ (0x3ff << B2R2_RZI_VSRC_INIT_SHIFT));
+ break;
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ /*
+ * Chroma goes on SRC2 and potentially on SRC1.
+ * Now chroma is half the size of luma
+ * only in horizontal direction.
+ * Same rounding applies as for 420 formats above,
+ * except it is only done horizontally.
+ */
+ node->node.GROUP4.B2R2_SXY =
+ (((src_x & 0xffff) >> 1) << B2R2_XY_X_SHIFT) |
+ ((src_y & 0xffff) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP4.B2R2_SSZ =
+ ((((src_w + 1) & 0xfff) >> 1) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((src_h & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+ if (src_fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR ||
+ src_fmt ==
+ B2R2_BLT_FMT_YVU422_PACKED_PLANAR) {
+ node->node.GROUP3.B2R2_SXY =
+ node->node.GROUP4.B2R2_SXY;
+ node->node.GROUP3.B2R2_SSZ =
+ node->node.GROUP4.B2R2_SSZ;
+ }
+ node->node.GROUP9.B2R2_RZI |=
+ (((src_x & 0x3ff) >> 1) <<
+ B2R2_RZI_HSRC_INIT_SHIFT) |
+ ((src_y & 0x3ff) << B2R2_RZI_VSRC_INIT_SHIFT);
+ break;
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ /*
+ * Chroma goes on SRC2 and SRC1.
+ * It is the same size as luma.
+ */
+ node->node.GROUP4.B2R2_SXY =
+ ((src_x & 0xffff) << B2R2_XY_X_SHIFT) |
+ ((src_y & 0xffff) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP4.B2R2_SSZ =
+ ((src_w & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((src_h & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+ node->node.GROUP3.B2R2_SXY = node->node.GROUP4.B2R2_SXY;
+ node->node.GROUP3.B2R2_SSZ = node->node.GROUP4.B2R2_SSZ;
+
+ /* Clear and set only the SRC_INIT bits */
+ node->node.GROUP9.B2R2_RZI &=
+ ~((0x3ff << B2R2_RZI_HSRC_INIT_SHIFT) |
+ (0x3ff << B2R2_RZI_VSRC_INIT_SHIFT));
+ node->node.GROUP9.B2R2_RZI |= b2r2_rzi;
+ break;
+ default:
+ break;
+ }
+ } else {
+ node->node.GROUP4.B2R2_SXY =
+ ((src_x & 0xffff) << B2R2_XY_X_SHIFT) |
+ ((src_y & 0xffff) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP4.B2R2_SSZ =
+ ((src_w & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((src_h & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+
+ /* Clear and set only the SRC_INIT bits */
+ node->node.GROUP9.B2R2_RZI &=
+ ~((0x3ff << B2R2_RZI_HSRC_INIT_SHIFT) |
+ (0x3ff << B2R2_RZI_VSRC_INIT_SHIFT));
+ node->node.GROUP9.B2R2_RZI |= b2r2_rzi;
+ }
+
+ node->node.GROUP1.B2R2_TXY = 0;
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) {
+ /*
+ * dst_rect_area coordinates are specified
+ * after potential rotation.
+ * Input is read before rotation, hence the width and height
+ * need to be swapped.
+ * Horizontal and vertical flips are accomplished with
+ * suitable scanning order while writing
+ * to the temporary buffer.
+ */
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_FLIP_H) {
+ node->node.GROUP1.B2R2_TXY |=
+ ((dst_rect_area->height - 1) & 0xffff) <<
+ B2R2_XY_X_SHIFT;
+ }
+
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_FLIP_V) {
+ node->node.GROUP1.B2R2_TXY |=
+ ((dst_rect_area->width - 1) & 0xffff) <<
+ B2R2_XY_Y_SHIFT;
+ }
+
+ node->node.GROUP1.B2R2_TSZ =
+ ((dst_rect_area->height & 0xfff) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_rect_area->width & 0xfff) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+ } else {
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_FLIP_H) {
+ node->node.GROUP1.B2R2_TXY |=
+ ((dst_rect_area->width - 1) & 0xffff) <<
+ B2R2_XY_X_SHIFT;
+ }
+
+ if (req->user_req.transform & B2R2_BLT_TRANSFORM_FLIP_V) {
+ node->node.GROUP1.B2R2_TXY |=
+ ((dst_rect_area->height - 1) & 0xffff) <<
+ B2R2_XY_Y_SHIFT;
+ }
+
+ node->node.GROUP1.B2R2_TSZ =
+ ((dst_rect_area->width & 0xfff) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_rect_area->height & 0xfff) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+ }
+
+ if (req->user_req.flags &
+ (B2R2_BLT_FLAG_SOURCE_FILL | B2R2_BLT_FLAG_SOURCE_FILL_RAW)) {
+ /*
+ * Scan order for source fill should always be left-to-right
+ * and top-to-bottom. Fill the input tile from top left.
+ */
+ node->node.GROUP1.B2R2_TXY = 0;
+ node->node.GROUP4.B2R2_SSZ = node->node.GROUP1.B2R2_TSZ;
+ }
+
+ if (B2R2_GENERIC_DEBUG_AREAS && dst_rect_area->x == 0 &&
+ dst_rect_area->y == 0) {
+ dump_nodes(node, false);
+ b2r2_log_debug("%s Input node done.\n", __func__);
+ }
+
+ /* Transform */
+ if ((req->user_req.transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) != 0) {
+ /*
+ * Transform node operates on temporary buffers.
+ * Content always at top left, but scanning order
+ * has to be flipped during rotation.
+ * Width and height need to be considered as well, since
+ * a tile may not necessarily be filled completely.
+ * dst_rect_area dimensions are specified
+ * after potential rotation.
+ * Input is read before rotation, hence the width and height
+ * need to be swapped on src.
+ */
+ node = node->next;
+
+ node->node.GROUP4.B2R2_SXY = 0;
+ node->node.GROUP4.B2R2_SSZ =
+ ((dst_rect_area->height & 0xfff) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_rect_area->width & 0xfff) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+ /* Bottom line written first */
+ node->node.GROUP1.B2R2_TXY =
+ ((dst_rect_area->height - 1) & 0xffff) <<
+ B2R2_XY_Y_SHIFT;
+
+ node->node.GROUP1.B2R2_TSZ =
+ ((dst_rect_area->width & 0xfff) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_rect_area->height & 0xfff) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+
+ if (B2R2_GENERIC_DEBUG_AREAS && dst_rect_area->x == 0 &&
+ dst_rect_area->y == 0) {
+ dump_nodes(node, false);
+ b2r2_log_debug("%s Tranform node done.\n", __func__);
+ }
+ }
+
+ /* Source mask */
+ if (req->user_req.flags & B2R2_BLT_FLAG_SOURCE_MASK) {
+ node = node->next;
+ /*
+ * Same coords for mask as for the input stage.
+ * Should the mask be transformed together with source?
+ * EMACSOC TODO: Apply mask before any
+ * transform/scaling is done.
+ * Otherwise it will be dst_ not src_mask.
+ */
+ if (B2R2_GENERIC_DEBUG_AREAS && dst_rect_area->x == 0 &&
+ dst_rect_area->y == 0) {
+ dump_nodes(node, false);
+ b2r2_log_debug("%s Source mask node done.\n", __func__);
+ }
+ }
+
+ /* dst_read */
+ if (yuv_multi_buffer_dst) {
+ s32 dst_w = dst_rect_area->width;
+ s32 dst_h = dst_rect_area->height;
+ bool yuv420_dst =
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE;
+
+ bool yuv422_dst =
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE;
+ node = node->next;
+ /* Luma on SRC3 */
+ node->node.GROUP5.B2R2_SXY =
+ ((dst_x & 0xffff) << B2R2_XY_X_SHIFT) |
+ ((dst_y & 0xffff) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP5.B2R2_SSZ =
+ ((dst_w & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_h & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+
+ if (yuv420_dst) {
+ /*
+ * Chroma goes on SRC2 and potentially on SRC1.
+ * Chroma is half the size of luma. Must round up
+ * the chroma size to handle cases when luma size is not
+ * divisible by 2.
+ * E.g. luma width==7 requires chroma width==4.
+ * Chroma width==7/2==3 is only enough
+ * for luma width==6.
+ */
+ node->node.GROUP4.B2R2_SXY =
+ (((dst_x & 0xffff) >> 1) << B2R2_XY_X_SHIFT) |
+ (((dst_y & 0xffff) >> 1) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP4.B2R2_SSZ =
+ ((((dst_w + 1) & 0xfff) >> 1) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((((dst_h + 1) & 0xfff) >> 1) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+
+ if (dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR ||
+ dst_fmt ==
+ B2R2_BLT_FMT_YVU420_PACKED_PLANAR) {
+ node->node.GROUP3.B2R2_SXY =
+ node->node.GROUP4.B2R2_SXY;
+ node->node.GROUP3.B2R2_SSZ =
+ node->node.GROUP4.B2R2_SSZ;
+ }
+ } else if (yuv422_dst) {
+ /*
+ * Chroma goes on SRC2 and potentially on SRC1.
+ * Now chroma is half the size of luma
+ * only in horizontal direction.
+ * Same rounding applies as for 420 formats above,
+ * except it is only done horizontally.
+ */
+ node->node.GROUP4.B2R2_SXY =
+ (((dst_x & 0xffff) >> 1) << B2R2_XY_X_SHIFT) |
+ ((dst_y & 0xffff) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP4.B2R2_SSZ =
+ ((((dst_w + 1) & 0xfff) >> 1) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_h & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+
+ if (dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR ||
+ dst_fmt ==
+ B2R2_BLT_FMT_YVU422_PACKED_PLANAR) {
+ node->node.GROUP3.B2R2_SXY =
+ node->node.GROUP4.B2R2_SXY;
+ node->node.GROUP3.B2R2_SSZ =
+ node->node.GROUP4.B2R2_SSZ;
+ }
+ } else if (dst_fmt == B2R2_BLT_FMT_YUV444_PACKED_PLANAR) {
+ /*
+ * Chroma goes on SRC2 and SRC1.
+ * It is the same size as luma.
+ */
+ node->node.GROUP4.B2R2_SXY = node->node.GROUP5.B2R2_SXY;
+ node->node.GROUP4.B2R2_SSZ = node->node.GROUP5.B2R2_SSZ;
+ node->node.GROUP3.B2R2_SXY = node->node.GROUP5.B2R2_SXY;
+ node->node.GROUP3.B2R2_SSZ = node->node.GROUP5.B2R2_SSZ;
+ }
+
+ node->node.GROUP1.B2R2_TXY = 0;
+ node->node.GROUP1.B2R2_TSZ =
+ ((dst_w & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_h & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+ } else {
+ node = node->next;
+ node->node.GROUP4.B2R2_SXY =
+ ((dst_x & 0xffff) << B2R2_XY_X_SHIFT) |
+ ((dst_y & 0xffff) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP4.B2R2_SSZ =
+ ((dst_rect_area->width & 0xfff) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_rect_area->height & 0xfff) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+ node->node.GROUP1.B2R2_TXY = 0;
+ node->node.GROUP1.B2R2_TSZ =
+ ((dst_rect_area->width & 0xfff) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_rect_area->height & 0xfff) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+ }
+
+ if (B2R2_GENERIC_DEBUG_AREAS && dst_rect_area->x == 0 &&
+ dst_rect_area->y == 0) {
+ dump_nodes(node, false);
+ b2r2_log_debug("%s dst_read node done.\n", __func__);
+ }
+
+ /* blend */
+ node = node->next;
+ node->node.GROUP3.B2R2_SXY = 0;
+ node->node.GROUP3.B2R2_SSZ =
+ ((dst_rect_area->width & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_rect_area->height & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+ /* contents of the foreground temporary buffer always at top left */
+ node->node.GROUP4.B2R2_SXY = 0;
+ node->node.GROUP4.B2R2_SSZ =
+ ((dst_rect_area->width & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_rect_area->height & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+
+ node->node.GROUP1.B2R2_TXY = 0;
+ node->node.GROUP1.B2R2_TSZ =
+ ((dst_rect_area->width & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_rect_area->height & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+
+ if (B2R2_GENERIC_DEBUG_AREAS && dst_rect_area->x == 0 &&
+ dst_rect_area->y == 0) {
+ dump_nodes(node, false);
+ b2r2_log_debug("%s Blend node done.\n", __func__);
+ }
+
+ /* writeback */
+ node = node->next;
+ if ((req->user_req.flags & B2R2_BLT_FLAG_DESTINATION_CLIP) != 0) {
+ clip_left = req->user_req.dst_clip_rect.x;
+ clip_top = req->user_req.dst_clip_rect.y;
+ clip_right = clip_left + req->user_req.dst_clip_rect.width - 1;
+ clip_bottom = clip_top + req->user_req.dst_clip_rect.height - 1;
+ }
+ /*
+ * Clamp the dst clip rectangle to buffer dimensions to prevent
+ * illegal writes. An illegal clip rectangle, e.g. outside the
+ * buffer will be ignored, resulting in nothing being clipped.
+ */
+ if (clip_left < 0 || req->user_req.dst_img.width <= clip_left)
+ clip_left = 0;
+
+ if (clip_top < 0 || req->user_req.dst_img.height <= clip_top)
+ clip_top = 0;
+
+ if (clip_right < 0 || req->user_req.dst_img.width <= clip_right)
+ clip_right = req->user_req.dst_img.width - 1;
+
+ if (clip_bottom < 0 || req->user_req.dst_img.height <= clip_bottom)
+ clip_bottom = req->user_req.dst_img.height - 1;
+
+ /*
+ * Only allow writing inside the clip rect.
+ * INTNL bit in B2R2_CWO should be zero.
+ */
+ node->node.GROUP6.B2R2_CWO =
+ ((clip_top & 0x7fff) << B2R2_CWO_Y_SHIFT) |
+ ((clip_left & 0x7fff) << B2R2_CWO_X_SHIFT);
+ node->node.GROUP6.B2R2_CWS =
+ ((clip_bottom & 0x7fff) << B2R2_CWS_Y_SHIFT) |
+ ((clip_right & 0x7fff) << B2R2_CWS_X_SHIFT);
+
+ if (yuv_multi_buffer_dst) {
+ const s32 dst_w = dst_rect_area->width;
+ const s32 dst_h = dst_rect_area->height;
+ int i = 0;
+ /* Number of nodes required to write chroma output */
+ int n_nodes = 1;
+ if (dst_fmt == B2R2_BLT_FMT_YUV420_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV422_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU420_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YVU422_PACKED_PLANAR ||
+ dst_fmt == B2R2_BLT_FMT_YUV444_PACKED_PLANAR)
+ n_nodes = 2;
+
+ node->node.GROUP4.B2R2_SXY = 0;
+ node->node.GROUP4.B2R2_SSZ =
+ ((dst_w & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_h & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+
+ /* Luma (Y-component) */
+ node->node.GROUP1.B2R2_TXY =
+ ((dst_x & 0xffff) << B2R2_XY_X_SHIFT) |
+ ((dst_y & 0xffff) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP1.B2R2_TSZ =
+ ((dst_w & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_h & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+
+ node->node.GROUP6.B2R2_CWO =
+ ((clip_top & 0x7fff) << B2R2_CWO_Y_SHIFT) |
+ ((clip_left & 0x7fff) << B2R2_CWO_X_SHIFT);
+ node->node.GROUP6.B2R2_CWS =
+ ((clip_bottom & 0x7fff) << B2R2_CWS_Y_SHIFT) |
+ ((clip_right & 0x7fff) << B2R2_CWS_X_SHIFT);
+
+ if (B2R2_GENERIC_DEBUG_AREAS && dst_rect_area->x == 0 &&
+ dst_rect_area->y == 0) {
+ dump_nodes(node, false);
+ b2r2_log_debug("%s Writeback luma node done.\n",
+ __func__);
+ }
+
+ node = node->next;
+
+ /*
+ * Chroma components. 1 or 2 nodes
+ * for semi-planar or planar buffer respectively.
+ */
+ for (i = 0; i < n_nodes && node != NULL; ++i) {
+
+ node->node.GROUP4.B2R2_SXY = 0;
+ node->node.GROUP4.B2R2_SSZ =
+ ((dst_w & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_h & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+
+ switch (dst_fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ /*
+ * Chroma is half the size of luma.
+ * Must round up the chroma size to handle
+ * cases when luma size is not divisible by 2.
+ * E.g. luma_width==7 requires chroma_width==4.
+ * Chroma_width==7/2==3 is only enough
+ * for luma_width==6.
+ */
+ node->node.GROUP1.B2R2_TXY =
+ (((dst_x & 0xffff) >> 1) <<
+ B2R2_XY_X_SHIFT) |
+ (((dst_y & 0xffff) >> 1) <<
+ B2R2_XY_Y_SHIFT);
+ node->node.GROUP1.B2R2_TSZ =
+ ((((dst_w + 1) & 0xfff) >> 1) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((((dst_h + 1) & 0xfff) >> 1) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+ break;
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ /*
+ * Now chroma is half the size of luma only
+ * in horizontal direction.
+ * Same rounding applies as
+ * for 420 formats above, except it is only
+ * done horizontally.
+ */
+ node->node.GROUP1.B2R2_TXY =
+ (((dst_x & 0xffff) >> 1) <<
+ B2R2_XY_X_SHIFT) |
+ ((dst_y & 0xffff) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP1.B2R2_TSZ =
+ ((((dst_w + 1) & 0xfff) >> 1) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_h & 0xfff) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+ break;
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ /*
+ * Chroma has the same resolution as luma.
+ */
+ node->node.GROUP1.B2R2_TXY =
+ ((dst_x & 0xffff) << B2R2_XY_X_SHIFT) |
+ ((dst_y & 0xffff) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP1.B2R2_TSZ =
+ ((dst_w & 0xfff) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_h & 0xfff) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+ break;
+ default:
+ break;
+ }
+
+ node->node.GROUP6.B2R2_CWO =
+ ((clip_top & 0x7fff) << B2R2_CWO_Y_SHIFT) |
+ ((clip_left & 0x7fff) << B2R2_CWO_X_SHIFT);
+ node->node.GROUP6.B2R2_CWS =
+ ((clip_bottom & 0x7fff) << B2R2_CWS_Y_SHIFT) |
+ ((clip_right & 0x7fff) << B2R2_CWS_X_SHIFT);
+
+ if (B2R2_GENERIC_DEBUG_AREAS && dst_rect_area->x == 0 &&
+ dst_rect_area->y == 0) {
+ dump_nodes(node, false);
+ b2r2_log_debug("%s Writeback chroma node "
+ "%d of %d done.\n",
+ __func__, i + 1, n_nodes);
+ }
+
+ node = node->next;
+ }
+ } else {
+ node->node.GROUP4.B2R2_SXY = 0;
+ node->node.GROUP4.B2R2_SSZ =
+ ((dst_rect_area->width & 0xfff) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_rect_area->height & 0xfff) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+ node->node.GROUP1.B2R2_TXY =
+ ((dst_x & 0xffff) << B2R2_XY_X_SHIFT) |
+ ((dst_y & 0xffff) << B2R2_XY_Y_SHIFT);
+ node->node.GROUP1.B2R2_TSZ =
+ ((dst_rect_area->width & 0xfff) <<
+ B2R2_SZ_WIDTH_SHIFT) |
+ ((dst_rect_area->height & 0xfff) <<
+ B2R2_SZ_HEIGHT_SHIFT);
+
+ if (B2R2_GENERIC_DEBUG_AREAS && dst_rect_area->x == 0 &&
+ dst_rect_area->y == 0) {
+ dump_nodes(node, false);
+ b2r2_log_debug("%s Writeback node done.\n", __func__);
+ }
+ }
+
+ b2r2_log_info("%s DONE\n", __func__);
+}
diff --git a/drivers/video/b2r2/b2r2_generic.h b/drivers/video/b2r2/b2r2_generic.h
new file mode 100644
index 00000000000..35451543c5c
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_generic.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 generic. Full coverage of user interface but
+ * non optimized implementation. For Fallback purposes.
+ *
+ * Author: Maciej Socha <maciej.socha@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef _LINUX_VIDEO_B2R2_GENERIC_H
+#define _LINUX_VIDEO_B2R2_GENERIC_H
+
+#include <video/b2r2_blt.h>
+
+#include "b2r2_internal.h"
+
+/**
+ * b2r2_generic_init()
+ */
+void b2r2_generic_init(void);
+
+/**
+ * b2r2_generic_exit()
+ */
+void b2r2_generic_exit(void);
+
+/**
+ * b2r2_generic_analyze()
+ */
+int b2r2_generic_analyze(const struct b2r2_blt_request *req,
+ s32 *work_buf_width,
+ s32 *work_buf_height,
+ u32 *work_buf_count,
+ u32 *node_count);
+/**
+ * b2r2_generic_configure()
+ */
+int b2r2_generic_configure(const struct b2r2_blt_request *req,
+ struct b2r2_node *first,
+ struct b2r2_work_buf *tmp_bufs,
+ u32 buf_count);
+/**
+ * b2r2_generic_set_areas()
+ */
+void b2r2_generic_set_areas(const struct b2r2_blt_request *req,
+ struct b2r2_node *first,
+ struct b2r2_blt_rect *dst_rect_area);
+#endif
diff --git a/drivers/video/b2r2/b2r2_global.h b/drivers/video/b2r2/b2r2_global.h
new file mode 100644
index 00000000000..38cf74bb753
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_global.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 global definitions
+ *
+ * Author: Robert Fekete <robert.fekete@stericsson.com>
+ * Author: Paul Wannback
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef __B2R2_GLOBAL_H
+#define __B2R2_GLOBAL_H
+
+/** Sources involved */
+
+struct b2r2_system {
+ unsigned int B2R2_NIP;
+ unsigned int B2R2_CIC;
+ unsigned int B2R2_INS;
+ unsigned int B2R2_ACK;
+};
+
+struct b2r2_target {
+ unsigned int B2R2_TBA;
+ unsigned int B2R2_TTY;
+ unsigned int B2R2_TXY;
+ unsigned int B2R2_TSZ;
+};
+
+struct b2r2_color_fill {
+ unsigned int B2R2_S1CF;
+ unsigned int B2R2_S2CF;
+};
+
+struct b2r2_src_config {
+ unsigned int B2R2_SBA;
+ unsigned int B2R2_STY;
+ unsigned int B2R2_SXY;
+ unsigned int B2R2_SSZ;
+};
+
+struct b2r2_clip {
+ unsigned int B2R2_CWO;
+ unsigned int B2R2_CWS;
+};
+
+struct b2r2_color_key {
+ unsigned int B2R2_KEY1;
+ unsigned int B2R2_KEY2;
+};
+
+struct b2r2_clut {
+ unsigned int B2R2_CCO;
+ unsigned int B2R2_CML;
+};
+
+struct b2r2_rsz_pl_mask {
+ unsigned int B2R2_FCTL;
+ unsigned int B2R2_PMK;
+};
+
+struct b2r2_Cr_luma_rsz {
+ unsigned int B2R2_RSF;
+ unsigned int B2R2_RZI;
+ unsigned int B2R2_HFP;
+ unsigned int B2R2_VFP;
+};
+
+struct b2r2_flikr_filter {
+ unsigned int B2R2_FF0;
+ unsigned int B2R2_FF1;
+ unsigned int B2R2_FF2;
+ unsigned int B2R2_FF3;
+};
+
+struct b2r2_xyl {
+ unsigned int B2R2_XYL;
+ unsigned int B2R2_XYP;
+};
+
+struct b2r2_sau {
+ unsigned int B2R2_SAR;
+ unsigned int B2R2_USR;
+};
+
+struct b2r2_vm {
+ unsigned int B2R2_VMX0;
+ unsigned int B2R2_VMX1;
+ unsigned int B2R2_VMX2;
+ unsigned int B2R2_VMX3;
+};
+
+struct b2r2_link_list {
+
+ struct b2r2_system GROUP0;
+ struct b2r2_target GROUP1;
+ struct b2r2_color_fill GROUP2;
+ struct b2r2_src_config GROUP3;
+ struct b2r2_src_config GROUP4;
+ struct b2r2_src_config GROUP5;
+ struct b2r2_clip GROUP6;
+ struct b2r2_clut GROUP7;
+ struct b2r2_rsz_pl_mask GROUP8;
+ struct b2r2_Cr_luma_rsz GROUP9;
+ struct b2r2_Cr_luma_rsz GROUP10;
+ struct b2r2_flikr_filter GROUP11;
+ struct b2r2_color_key GROUP12;
+ struct b2r2_xyl GROUP13;
+ struct b2r2_sau GROUP14;
+ struct b2r2_vm GROUP15;
+ struct b2r2_vm GROUP16;
+
+ unsigned int B2R2_RESERVED[2];
+};
+
+
+#endif /* !defined(__B2R2_GLOBAL_H) */
diff --git a/drivers/video/b2r2/b2r2_hw.h b/drivers/video/b2r2/b2r2_hw.h
new file mode 100644
index 00000000000..2112a34dfe9
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_hw.h
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 hw definitions
+ *
+ * Author: Fredrik Allansson <fredrik.allansson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef B2R2_HW_H__
+#define B2R2_HW_H__
+
+#include <linux/bitops.h>
+
+/* Scaling works in strips 128 pixels wide */
+#define B2R2_RESCALE_MAX_WIDTH 128
+
+/* Rotation works in strips 16 pixels wide */
+#define B2R2_ROTATE_MAX_WIDTH 16
+
+/* B2R2 color formats */
+#define B2R2_COLOR_FORMAT_SHIFT 16
+enum b2r2_native_fmt {
+ /* RGB formats */
+ B2R2_NATIVE_RGB565 = 0x00 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_RGB888 = 0x01 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_ARGB8565 = 0x04 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_ARGB8888 = 0x05 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_ARGB1555 = 0x06 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_ARGB4444 = 0x07 << B2R2_COLOR_FORMAT_SHIFT,
+
+ /* YCbCr formats */
+ B2R2_NATIVE_YCBCR888 = 0x10 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_YCBCR422R = 0x12 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_AYCBCR8888 = 0x15 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_YCBCR42X_MB = 0x14 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_YCBCR42X_R2B = 0x16 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_YCBCR42X_MBN = 0x0e << B2R2_COLOR_FORMAT_SHIFT,
+
+ /* CLUT formats */
+ B2R2_NATIVE_CLUT2 = 0x09 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_CLUT8 = 0x0b << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_ACLUT44 = 0x0c << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_ACLUT88 = 0x0d << B2R2_COLOR_FORMAT_SHIFT,
+
+ /* Misc. formats */
+ B2R2_NATIVE_A1 = 0x18 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_A8 = 0x19 << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_YUV = 0x1e << B2R2_COLOR_FORMAT_SHIFT,
+ B2R2_NATIVE_BYTE = 0x1f << B2R2_COLOR_FORMAT_SHIFT,
+};
+
+/* B2R2_CIC register values */
+enum b2r2_cic {
+ B2R2_CIC_COLOR_FILL = BIT(1),/*0x00000002*/
+ B2R2_CIC_SOURCE_1 = BIT(2),/*0x00000004*/
+ B2R2_CIC_SOURCE_2 = BIT(3),/*0x00000008*/
+ B2R2_CIC_SOURCE_3 = BIT(4),/*0x00000010*/
+ B2R2_CIC_CLIP_WINDOW = BIT(5),/*0x00000020*/
+ B2R2_CIC_CLUT = BIT(6),/*0x00000040*/
+ B2R2_CIC_FILTER_CONTROL = BIT(7),/*0x00000080*/
+ B2R2_CIC_RESIZE_CHROMA = BIT(8),/*0x00000100*/
+ B2R2_CIC_RESIZE_LUMA = BIT(9),/*0x00000200*/
+ B2R2_CIC_FLICKER_COEFF = BIT(10),/*0x00000400*/
+ B2R2_CIC_COLOR_KEY = BIT(11),/*0x00000800*/
+ B2R2_CIC_XYL = BIT(12),/*0x00001000*/
+ B2R2_CIC_SAU = BIT(13),/*0x00002000*/
+ B2R2_CIC_IVMX = BIT(14),/*0x00004000*/
+ B2R2_CIC_OVMX = BIT(15),/*0x00008000*/
+ B2R2_CIC_PACEDOT = BIT(16),/*0x00010000*/
+ B2R2_CIC_VC1 = BIT(17)/*0x00020000*/
+};
+
+/* B2R2_INS register values */
+#define B2R2_INS_SOURCE_1_SHIFT 0
+#define B2R2_INS_SOURCE_2_SHIFT 3
+#define B2R2_INS_SOURCE_3_SHIFT 5
+#define B2R2_INS_IVMX_SHIFT 6
+#define B2R2_INS_CLUTOP_SHIFT 7
+#define B2R2_INS_RESCALE2D_SHIFT 8
+#define B2R2_INS_FLICK_FILT_SHIFT 9
+#define B2R2_INS_RECT_CLIP_SHIFT 10
+#define B2R2_INS_CKEY_SHIFT 11
+#define B2R2_INS_OVMX_SHIFT 12
+#define B2R2_INS_DEI_SHIFT 13
+#define B2R2_INS_PLANE_MASK_SHIFT 14
+#define B2R2_INS_XYL_SHIFT 15
+#define B2R2_INS_DOT_SHIFT 16
+#define B2R2_INS_VC1R_SHIFT 17
+#define B2R2_INS_ROTATION_SHIFT 18
+#define B2R2_INS_PACE_DOWN_SHIFT 30
+#define B2R2_INS_BLITCOMPIRQ_SHIFT 31
+enum b2r2_ins {
+ /* Source 1 config */
+ B2R2_INS_SOURCE_1_FETCH_FROM_MEM = 0x1 << B2R2_INS_SOURCE_1_SHIFT,
+ B2R2_INS_SOURCE_1_COLOR_FILL_REGISTER = 0x3 << B2R2_INS_SOURCE_1_SHIFT,
+ B2R2_INS_SOURCE_1_DIRECT_COPY = 0x4 << B2R2_INS_SOURCE_1_SHIFT,
+ B2R2_INS_SOURCE_1_DIRECT_FILL = 0x7 << B2R2_INS_SOURCE_1_SHIFT,
+
+ /* Source 2 config */
+ B2R2_INS_SOURCE_2_FETCH_FROM_MEM = 0x1 << B2R2_INS_SOURCE_2_SHIFT,
+ B2R2_INS_SOURCE_2_COLOR_FILL_REGISTER = 0x3 << B2R2_INS_SOURCE_2_SHIFT,
+
+ /* Source 3 config */
+ B2R2_INS_SOURCE_3_FETCH_FROM_MEM = 0x1 << B2R2_INS_SOURCE_3_SHIFT,
+
+ /* Other configs */
+ B2R2_INS_IVMX_ENABLED = 0x1 << B2R2_INS_IVMX_SHIFT,
+ B2R2_INS_CLUTOP_ENABLED = 0x1 << B2R2_INS_CLUTOP_SHIFT,
+ B2R2_INS_RESCALE2D_ENABLED = 0x1 << B2R2_INS_RESCALE2D_SHIFT,
+ B2R2_INS_FLICK_FILT_ENABLED = 0x1 << B2R2_INS_FLICK_FILT_SHIFT,
+ B2R2_INS_RECT_CLIP_ENABLED = 0x1 << B2R2_INS_RECT_CLIP_SHIFT,
+ B2R2_INS_CKEY_ENABLED = 0x1 << B2R2_INS_CKEY_SHIFT,
+ B2R2_INS_OVMX_ENABLED = 0x1 << B2R2_INS_OVMX_SHIFT,
+ B2R2_INS_DEI_ENABLED = 0x1 << B2R2_INS_DEI_SHIFT,
+ B2R2_INS_PLANE_MASK_ENABLED = 0x1 << B2R2_INS_PLANE_MASK_SHIFT,
+ B2R2_INS_XYL_ENABLED = 0x1 << B2R2_INS_XYL_SHIFT,
+ B2R2_INS_DOT_ENABLED = 0x1 << B2R2_INS_DOT_SHIFT,
+ B2R2_INS_VC1R_ENABLED = 0x1 << B2R2_INS_VC1R_SHIFT,
+ B2R2_INS_ROTATION_ENABLED = 0x1 << B2R2_INS_ROTATION_SHIFT,
+ B2R2_INS_PACE_DOWN_ENABLED = 0x1 << B2R2_INS_PACE_DOWN_SHIFT,
+ B2R2_INS_BLITCOMPIRQ_ENABLED = 0x1 << B2R2_INS_BLITCOMPIRQ_SHIFT,
+
+};
+
+/* B2R2_ACK register values */
+#define B2R2_ACK_MODE_SHIFT 0
+#define B2R2_ACK_SWAP_FG_BG_SHIFT 4
+#define B2R2_ACK_GALPHA_ROPID_SHIFT 8
+#define B2R2_ACK_CKEY_BLUE_SHIFT 16
+#define B2R2_ACK_CKEY_GREEN_SHIFT 18
+#define B2R2_ACK_CKEY_RED_SHIFT 20
+#define B2R2_ACK_CKEY_SEL_SHIFT 22
+enum b2r2_ack {
+ /* ALU operation modes */
+ B2R2_ACK_MODE_LOGICAL_OPERATION = 0x1 << B2R2_ACK_MODE_SHIFT,
+ B2R2_ACK_MODE_BLEND_NOT_PREMULT = 0x2 << B2R2_ACK_MODE_SHIFT,
+ B2R2_ACK_MODE_BLEND_PREMULT = 0x3 << B2R2_ACK_MODE_SHIFT,
+ B2R2_ACK_MODE_CLIPMASK_LOGICAL_FIRST_PASS = 0x4 << B2R2_ACK_MODE_SHIFT,
+ B2R2_ACK_MODE_CLIPMASK_BLEND = 0x5 << B2R2_ACK_MODE_SHIFT,
+ B2R2_ACK_MODE_BYPASS_S2_S3 = 0x7 << B2R2_ACK_MODE_SHIFT,
+ B2R2_ACK_MODE_CLIPMASK_LOGICAL_SECOND_PASS = 0x8 << B2R2_ACK_MODE_SHIFT,
+ B2R2_ACK_MODE_CLIPMASK_XYL_LOGICAL = 0x9 << B2R2_ACK_MODE_SHIFT,
+ B2R2_ACK_MODE_CLIPMASK_XYL_BLEND_NOT_PREMULT = 0xa << B2R2_ACK_MODE_SHIFT,
+ B2R2_ACK_MODE_CLIPMASK_XYL_BLEND_PREMULT = 0xb << B2R2_ACK_MODE_SHIFT,
+
+ /* ALU channel selection */
+ B2R2_ACK_SWAP_FG_BG = 0x1 << B2R2_ACK_SWAP_FG_BG_SHIFT,
+
+ /* Global alpha and ROP IDs */
+ B2R2_ACK_ROP_CLEAR = 0x0 << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_AND = 0x1 << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_AND_REV = 0x2 << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_COPY = 0x3 << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_AND_INV = 0x4 << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_NOOP = 0x5 << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_XOR = 0x6 << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_OR = 0x7 << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_NOR = 0x8 << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_EQUIV = 0x9 << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_INVERT = 0xa << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_OR_REV = 0xb << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_COPY_INV = 0xc << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_OR_INV = 0xd << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_NAND = 0xe << B2R2_ACK_GALPHA_ROPID_SHIFT,
+ B2R2_ACK_ROP_SET = 0xf << B2R2_ACK_GALPHA_ROPID_SHIFT,
+
+ /* Color key configuration bits */
+ B2R2_ACK_CKEY_BLUE_MATCH_IF_BETWEEN = 0x1 << B2R2_ACK_CKEY_BLUE_SHIFT,
+ B2R2_ACK_CKEY_BLUE_MATCH_IF_LT_OR_GT = 0x2 << B2R2_ACK_CKEY_BLUE_SHIFT,
+ B2R2_ACK_CKEY_RED_MATCH_IF_BETWEEN = 0x1 << B2R2_ACK_CKEY_GREEN_SHIFT,
+ B2R2_ACK_CKEY_RED_MATCH_IF_LT_OR_GT = 0x2 << B2R2_ACK_CKEY_GREEN_SHIFT,
+ B2R2_ACK_CKEY_GREEN_MATCH_IF_BETWEEN = 0x1 << B2R2_ACK_CKEY_RED_SHIFT,
+ B2R2_ACK_CKEY_GREEN_MATCH_IF_LT_OR_GT = 0x2 << B2R2_ACK_CKEY_RED_SHIFT,
+
+ /* Color key input selection */
+ B2R2_ACK_CKEY_SEL_DEST = 0x0 << B2R2_ACK_CKEY_SEL_SHIFT,
+ B2R2_ACK_CKEY_SEL_SRC_BEFORE_CLUT = 0x1 << B2R2_ACK_CKEY_SEL_SHIFT,
+ B2R2_ACK_CKEY_SEL_SRC_AFTER_CLUT = 0x2 << B2R2_ACK_CKEY_SEL_SHIFT,
+ B2R2_ACK_CKEY_SEL_BLANKING_S2_ALPHA = 0x3 << B2R2_ACK_CKEY_SEL_SHIFT,
+};
+
+/* Common <S/T>TY defines */
+#define B2R2_TY_BITMAP_PITCH_SHIFT 0
+#define B2R2_TY_COLOR_FORM_SHIFT 16
+#define B2R2_TY_ALPHA_RANGE_SHIFT 21
+#define B2R2_TY_MB_ACCESS_MODE_SHIFT 23
+#define B2R2_TY_HSO_SHIFT 24
+#define B2R2_TY_VSO_SHIFT 25
+#define B2R2_TY_SUBBYTE_SHIFT 28
+#define B2R2_TY_ENDIAN_SHIFT 30
+#define B2R2_TY_SECURE_SHIFT 31
+
+/* Dummy enum for generalization of <S/T>TY registers */
+enum b2r2_ty {
+ /* Alpha range */
+ B2R2_TY_ALPHA_RANGE_128 = 0x0 << B2R2_TY_ALPHA_RANGE_SHIFT,
+ B2R2_TY_ALPHA_RANGE_255 = 0x1 << B2R2_TY_ALPHA_RANGE_SHIFT,
+
+ /* Access mode in macro-block organized frame buffers */
+ B2R2_TY_MB_ACCESS_MODE_FRAME = 0x0 << B2R2_TY_MB_ACCESS_MODE_SHIFT,
+ B2R2_TY_MB_ACCESS_MODE_FIELD = 0x1 << B2R2_TY_MB_ACCESS_MODE_SHIFT,
+
+ /* Horizontal scan order */
+ B2R2_TY_HSO_LEFT_TO_RIGHT = 0x0 << B2R2_TY_HSO_SHIFT,
+ B2R2_TY_HSO_RIGHT_TO_LEFT = 0x1 << B2R2_TY_HSO_SHIFT,
+
+ /* Vertical scan order */
+ B2R2_TY_VSO_TOP_TO_BOTTOM = 0x0 << B2R2_TY_VSO_SHIFT,
+ B2R2_TY_VSO_BOTTOM_TO_TOP = 0x1 << B2R2_TY_VSO_SHIFT,
+
+ /* Pixel ordering for sub-byte formats (position of right-most pixel) */
+ B2R2_TY_SUBBYTE_MSB = 0x0 << B2R2_TY_SUBBYTE_SHIFT,
+ B2R2_TY_SUBBYTE_LSB = 0x1 << B2R2_TY_SUBBYTE_SHIFT,
+
+ /* Bitmap endianess */
+ B2R2_TY_ENDIAN_BIG_NOT_LITTLE = 0x1 << B2R2_TY_ENDIAN_SHIFT,
+
+ /* Secureness of the target memory region */
+ B2R2_TY_SECURE_UNSECURE = 0x0 << B2R2_TY_SECURE_SHIFT,
+ B2R2_TY_SECURE_SECURE = 0x1 << B2R2_TY_SECURE_SHIFT,
+
+ /* Dummy to make sure the data type is large enough */
+ B2R2_TY_DUMMY = 0xffffffff,
+};
+
+/* B2R2_TTY register values */
+#define B2R2_TTY_CB_NOT_CR_SHIFT 22
+#define B2R2_TTY_RGB_ROUND_SHIFT 26
+#define B2R2_TTY_CHROMA_NOT_LUMA_SHIFT 27
+enum b2r2_tty {
+
+ /* Chroma component selection */
+ B2R2_TTY_CB_NOT_CR = 0x1 << B2R2_TTY_CB_NOT_CR_SHIFT,
+
+ /* RGB rounding mode */
+ B2R2_TTY_RGB_ROUND_NORMAL = 0x0 << B2R2_TTY_RGB_ROUND_SHIFT,
+ B2R2_TTY_RGB_ROUND_DITHER = 0x1 << B2R2_TTY_RGB_ROUND_SHIFT,
+
+ /* Component selection for splitted frame buffer formats */
+ B2R2_TTY_CHROMA_NOT_LUMA = 0x1 << B2R2_TTY_CHROMA_NOT_LUMA_SHIFT,
+};
+
+/* B2R2_S1TY register values */
+#define B2R2_S1TY_A1_SUBST_SHIFT 22
+#define B2R2_S1TY_ROTATION_SHIFT 27
+#define B2R2_S1TY_RGB_EXPANSION_SHIFT 29
+enum b2r2_s1ty {
+
+ /* Alpha bit substitution mode for ARGB1555 */
+ B2R2_S1TY_A1_SUBST_KEY_MODE = 0x1 << B2R2_S1TY_A1_SUBST_SHIFT,
+
+ /* Input rectangle rotation (NOT YET IMPLEMENTED) */
+ B2R2_S1TY_ENABLE_ROTATION = 0x1 << B2R2_S1TY_ROTATION_SHIFT,
+
+ /* RGB expansion mode */
+ B2R2_S1TY_RGB_EXPANSION_MSB_DUP = 0x0 << B2R2_S1TY_RGB_EXPANSION_SHIFT,
+ B2R2_S1TY_RGB_EXPANSION_LSP_ZERO = 0x1 << B2R2_S1TY_RGB_EXPANSION_SHIFT,
+};
+
+/* B2R2_S1TY register values */
+#define B2R2_S2TY_A1_SUBST_SHIFT 22
+#define B2R2_S2TY_CHROMA_LEFT_SHIFT 26
+#define B2R2_S2TY_RGB_EXPANSION_SHIFT 29
+enum b2r2_s2ty {
+
+ /* Alpha bit substitution mode for ARGB1555 */
+ B2R2_S2TY_A1_SUBST_KEY_MODE = 0x1 << B2R2_S2TY_A1_SUBST_SHIFT,
+
+ /* Chroma left extension */
+ B2R2_S2TY_CHROMA_LEFT_EXT_FOLLOWING_PIXEL = 0x0
+ << B2R2_S2TY_CHROMA_LEFT_SHIFT,
+ B2R2_S2TY_CHROMA_LEFT_EXT_AVERAGE = 0x1 << B2R2_S2TY_CHROMA_LEFT_SHIFT,
+
+ /* RGB expansion mode */
+ B2R2_S2TY_RGB_EXPANSION_MSB_DUP = 0x0 << B2R2_S2TY_RGB_EXPANSION_SHIFT,
+ B2R2_S2TY_RGB_EXPANSION_LSP_ZERO = 0x1 << B2R2_S2TY_RGB_EXPANSION_SHIFT,
+};
+
+/* B2R2_S1TY register values */
+#define B2R2_S3TY_BLANK_ACC_SHIFT 26
+enum b2r2_s3ty {
+ /* Enables "blank" access on this source (nothing will be fetched from
+ memory) */
+ B2R2_S3TY_ENABLE_BLANK_ACCESS = 0x1 << B2R2_S3TY_BLANK_ACC_SHIFT,
+};
+
+/* B2R2_<S or T>XY register values */
+#define B2R2_XY_X_SHIFT 0
+#define B2R2_XY_Y_SHIFT 16
+
+/* B2R2_<S or T>SZ register values */
+#define B2R2_SZ_WIDTH_SHIFT 0
+#define B2R2_SZ_HEIGHT_SHIFT 16
+
+/* Clip window offset (top left coordinates) */
+#define B2R2_CWO_X_SHIFT 0
+#define B2R2_CWO_Y_SHIFT 16
+
+/* Clip window stop (bottom right coordinates) */
+#define B2R2_CWS_X_SHIFT 0
+#define B2R2_CWS_Y_SHIFT 16
+
+/* Color look-up table */
+enum b2r2_cco {
+ B2R2_CCO_CLUT_COLOR_CORRECTION = (1 << 16),
+ B2R2_CCO_CLUT_UPDATE = (1 << 18),
+ B2R2_CCO_CLUT_ON_S1 = (1 << 15)
+};
+
+/* Filter control (2D resize control) */
+enum b2r2_fctl {
+ /* Horizontal 2D filter mode */
+ B2R2_FCTL_HF2D_MODE_ENABLE_COLOR_CHANNEL_FILTER = BIT(0),
+ B2R2_FCTL_HF2D_MODE_ENABLE_ALPHA_CHANNEL_FILTER = BIT(1),
+ B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER = BIT(2),
+
+ /* Vertical 2D filter mode */
+ B2R2_FCTL_VF2D_MODE_ENABLE_COLOR_CHANNEL_FILTER = BIT(4),
+ B2R2_FCTL_VF2D_MODE_ENABLE_ALPHA_CHANNEL_FILTER = BIT(5),
+ B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER = BIT(6),
+
+ /* Alpha borders */
+ B2R2_FCTL_ENABLE_ALPHA_BORDER_RIGHT = BIT(12),
+ B2R2_FCTL_ENABLE_ALPHA_BORDER_LEFT = BIT(13),
+ B2R2_FCTL_ENABLE_ALPHA_BORDER_BOTTOM = BIT(14),
+ B2R2_FCTL_ENABLE_ALPHA_BORDER_TOP = BIT(15),
+
+ /* Luma path horizontal 2D filter mode */
+ B2R2_FCTL_LUMA_HF2D_MODE_ENABLE_FILTER = BIT(24),
+ B2R2_FCTL_LUMA_HF2D_MODE_ENABLE_RESIZER = BIT(25),
+
+ /* Luma path vertical 2D filter mode */
+ B2R2_FCTL_LUMA_VF2D_MODE_ENABLE_FILTER = BIT(28),
+ B2R2_FCTL_LUMA_VF2D_MODE_ENABLE_RESIZER = BIT(29),
+};
+
+/* Resize scaling factor */
+#define B2R2_RSF_HSRC_INC_SHIFT 0
+#define B2R2_RSF_VSRC_INC_SHIFT 16
+
+/* Resizer initialization */
+#define B2R2_RZI_HSRC_INIT_SHIFT 0
+#define B2R2_RZI_HNB_REPEAT_SHIFT 12
+#define B2R2_RZI_VSRC_INIT_SHIFT 16
+#define B2R2_RZI_VNB_REPEAT_SHIFT 28
+
+/* Default values for the resizer */
+#define B2R2_RZI_DEFAULT_HNB_REPEAT (3 << B2R2_RZI_HNB_REPEAT_SHIFT)
+#define B2R2_RZI_DEFAULT_VNB_REPEAT (3 << B2R2_RZI_VNB_REPEAT_SHIFT)
+
+
+/* Bus plug configuration registers */
+enum b2r2_plug_opcode_size {
+ B2R2_PLUG_OPCODE_SIZE_8 = 0x3,
+ B2R2_PLUG_OPCODE_SIZE_16 = 0x4,
+ B2R2_PLUG_OPCODE_SIZE_32 = 0x5,
+ B2R2_PLUG_OPCODE_SIZE_64 = 0x6,
+};
+
+enum b2r2_plug_chunk_size {
+ B2R2_PLUG_CHUNK_SIZE_1 = 0x0,
+ B2R2_PLUG_CHUNK_SIZE_2 = 0x1,
+ B2R2_PLUG_CHUNK_SIZE_4 = 0x2,
+ B2R2_PLUG_CHUNK_SIZE_8 = 0x3,
+ B2R2_PLUG_CHUNK_SIZE_16 = 0x4,
+ B2R2_PLUG_CHUNK_SIZE_32 = 0x5,
+ B2R2_PLUG_CHUNK_SIZE_64 = 0x6,
+ B2R2_PLUG_CHUNK_SIZE_128 = 0x7,
+};
+
+enum b2r2_plug_message_size {
+ B2R2_PLUG_MESSAGE_SIZE_1 = 0x0,
+ B2R2_PLUG_MESSAGE_SIZE_2 = 0x1,
+ B2R2_PLUG_MESSAGE_SIZE_4 = 0x2,
+ B2R2_PLUG_MESSAGE_SIZE_8 = 0x3,
+ B2R2_PLUG_MESSAGE_SIZE_16 = 0x4,
+ B2R2_PLUG_MESSAGE_SIZE_32 = 0x5,
+ B2R2_PLUG_MESSAGE_SIZE_64 = 0x6,
+ B2R2_PLUG_MESSAGE_SIZE_128 = 0x7,
+};
+
+enum b2r2_plug_page_size {
+ B2R2_PLUG_PAGE_SIZE_64 = 0x0,
+ B2R2_PLUG_PAGE_SIZE_128 = 0x1,
+ B2R2_PLUG_PAGE_SIZE_256 = 0x2,
+};
+
+/* Default opcode size */
+#if defined(CONFIG_B2R2_OPSIZE_8)
+# define B2R2_PLUG_OPCODE_SIZE_DEFAULT B2R2_PLUG_OPCODE_SIZE_8
+#elif defined(CONFIG_B2R2_OPSIZE_16)
+# define B2R2_PLUG_OPCODE_SIZE_DEFAULT B2R2_PLUG_OPCODE_SIZE_16
+#elif defined(CONFIG_B2R2_OPSIZE_32)
+# define B2R2_PLUG_OPCODE_SIZE_DEFAULT B2R2_PLUG_OPCODE_SIZE_32
+#elif defined(CONFIG_B2R2_OPSIZE_64)
+# define B2R2_PLUG_OPCODE_SIZE_DEFAULT B2R2_PLUG_OPCODE_SIZE_64
+#else
+# define B2R2_PLUG_OPCODE_SIZE_DEFAULT 0
+#endif
+
+/* Default chunk size */
+#if defined(CONFIG_B2R2_CHSIZE_1)
+# define B2R2_PLUG_CHUNK_SIZE_DEFAULT B2R2_PLUG_CHUNK_SIZE_1
+#elif defined(CONFIG_B2R2_CHSIZE_2)
+# define B2R2_PLUG_CHUNK_SIZE_DEFAULT B2R2_PLUG_CHUNK_SIZE_2
+#elif defined(CONFIG_B2R2_CHSIZE_4)
+# define B2R2_PLUG_CHUNK_SIZE_DEFAULT B2R2_PLUG_CHUNK_SIZE_4
+#elif defined(CONFIG_B2R2_CHSIZE_8)
+# define B2R2_PLUG_CHUNK_SIZE_DEFAULT B2R2_PLUG_CHUNK_SIZE_8
+#elif defined(CONFIG_B2R2_CHSIZE_16)
+# define B2R2_PLUG_CHUNK_SIZE_DEFAULT B2R2_PLUG_CHUNK_SIZE_16
+#elif defined(CONFIG_B2R2_CHSIZE_32)
+# define B2R2_PLUG_CHUNK_SIZE_DEFAULT B2R2_PLUG_CHUNK_SIZE_32
+#elif defined(CONFIG_B2R2_CHSIZE_64)
+# define B2R2_PLUG_CHUNK_SIZE_DEFAULT B2R2_PLUG_CHUNK_SIZE_64
+#elif defined(CONFIG_B2R2_CHSIZE_128)
+# define B2R2_PLUG_CHUNK_SIZE_DEFAULT B2R2_PLUG_CHUNK_SIZE_128
+#else
+# define B2R2_PLUG_CHUNK_SIZE_DEFAULT 0
+#endif
+
+/* Default message size */
+#if defined(CONFIG_B2R2_MGSIZE_1)
+# define B2R2_PLUG_MESSAGE_SIZE_DEFAULT B2R2_PLUG_MESSAGE_SIZE_1
+#elif defined(CONFIG_B2R2_MGSIZE_2)
+# define B2R2_PLUG_MESSAGE_SIZE_DEFAULT B2R2_PLUG_MESSAGE_SIZE_2
+#elif defined(CONFIG_B2R2_MGSIZE_4)
+# define B2R2_PLUG_MESSAGE_SIZE_DEFAULT B2R2_PLUG_MESSAGE_SIZE_4
+#elif defined(CONFIG_B2R2_MGSIZE_8)
+# define B2R2_PLUG_MESSAGE_SIZE_DEFAULT B2R2_PLUG_MESSAGE_SIZE_8
+#elif defined(CONFIG_B2R2_MGSIZE_16)
+# define B2R2_PLUG_MESSAGE_SIZE_DEFAULT B2R2_PLUG_MESSAGE_SIZE_16
+#elif defined(CONFIG_B2R2_MGSIZE_32)
+# define B2R2_PLUG_MESSAGE_SIZE_DEFAULT B2R2_PLUG_MESSAGE_SIZE_32
+#elif defined(CONFIG_B2R2_MGSIZE_64)
+# define B2R2_PLUG_MESSAGE_SIZE_DEFAULT B2R2_PLUG_MESSAGE_SIZE_64
+#elif defined(CONFIG_B2R2_MGSIZE_128)
+# define B2R2_PLUG_MESSAGE_SIZE_DEFAULT B2R2_PLUG_MESSAGE_SIZE_128
+#else
+# define B2R2_PLUG_MESSAGE_SIZE_DEFAULT 0
+#endif
+
+/* Default page size */
+#if defined(CONFIG_B2R2_PGSIZE_64)
+# define B2R2_PLUG_PAGE_SIZE_DEFAULT B2R2_PLUG_PAGE_SIZE_64
+#elif defined(CONFIG_B2R2_PGSIZE_128)
+# define B2R2_PLUG_PAGE_SIZE_DEFAULT B2R2_PLUG_PAGE_SIZE_128
+#elif defined(CONFIG_B2R2_PGSIZE_256)
+# define B2R2_PLUG_PAGE_SIZE_DEFAULT B2R2_PLUG_PAGE_SIZE_256
+#else
+# define B2R2_PLUG_PAGE_SIZE_DEFAULT 0
+#endif
+
+/* VMX register values for RGB to YUV color conversion */
+/* Magic numbers from 27.11 in DB8500_DesignSpecification_v2.5.pdf */
+
+/* 601 Video Matrix (standard 601 conversion) */
+#define B2R2_VMX0_RGB_TO_YUV_601_VIDEO 0x107e4beb
+#define B2R2_VMX1_RGB_TO_YUV_601_VIDEO 0x0982581d
+#define B2R2_VMX2_RGB_TO_YUV_601_VIDEO 0xfa9ea483
+#define B2R2_VMX3_RGB_TO_YUV_601_VIDEO 0x08000080
+
+/* 601 Gfx Matrix (full range conversion) */
+#define B2R2_VMX0_RGB_TO_YUV_601_GFX 0x0e1e8bee
+#define B2R2_VMX1_RGB_TO_YUV_601_GFX 0x08420419
+#define B2R2_VMX2_RGB_TO_YUV_601_GFX 0xfb5ed471
+#define B2R2_VMX3_RGB_TO_YUV_601_GFX 0x08004080
+
+/* 709 Video Matrix (standard 709 conversion) */
+#define B2R2_VMX0_RGB_TO_YUV_709_VIDEO 0x107e27f4
+#define B2R2_VMX1_RGB_TO_YUV_709_VIDEO 0x06e2dc13
+#define B2R2_VMX2_RGB_TO_YUV_709_VIDEO 0xfc5e6c83
+#define B2R2_VMX3_RGB_TO_YUV_709_VIDEO 0x08000080
+
+/* 709 Gfx Matrix (standard 709 conversion) */
+#define B2R2_VMX0_RGB_TO_YUV_709_GFX 0x0e3e6bf5
+#define B2R2_VMX1_RGB_TO_YUV_709_GFX 0x05e27410
+#define B2R2_VMX2_RGB_TO_YUV_709_GFX 0xfcdea471
+#define B2R2_VMX3_RGB_TO_YUV_709_GFX 0x08004080
+
+/* VMX register values for YUV to RGB color conversion */
+
+/* 601 Video Matrix (standard 601 conversion) */
+#define B2R2_VMX0_YUV_TO_RGB_601_VIDEO 0x2c440000
+#define B2R2_VMX1_YUV_TO_RGB_601_VIDEO 0xe9a403aa
+#define B2R2_VMX2_YUV_TO_RGB_601_VIDEO 0x0004013f
+#define B2R2_VMX3_YUV_TO_RGB_601_VIDEO 0x34f21322
+
+/* 601 Gfx Matrix (full range conversion) */
+#define B2R2_VMX0_YUV_TO_RGB_601_GFX 0x3324a800
+#define B2R2_VMX1_YUV_TO_RGB_601_GFX 0xe604ab9c
+#define B2R2_VMX2_YUV_TO_RGB_601_GFX 0x0004a957
+#define B2R2_VMX3_YUV_TO_RGB_601_GFX 0x32121eeb
+
+/* 709 Video Matrix (standard 709 conversion) */
+#define B2R2_VMX0_YUV_TO_RGB_709_VIDEO 0x31440000
+#define B2R2_VMX1_YUV_TO_RGB_709_VIDEO 0xf16403d1
+#define B2R2_VMX2_YUV_TO_RGB_709_VIDEO 0x00040145
+#define B2R2_VMX3_YUV_TO_RGB_709_VIDEO 0x33b14b18
+
+/* 709 Gfx Matrix (standard 709 conversion) */
+#define B2R2_VMX0_YUV_TO_RGB_709_GFX 0x3964a800
+#define B2R2_VMX1_YUV_TO_RGB_709_GFX 0xef04abc9
+#define B2R2_VMX2_YUV_TO_RGB_709_GFX 0x0004a95f
+#define B2R2_VMX3_YUV_TO_RGB_709_GFX 0x307132df
+
+/* VMX register values for RGB to BGR conversion */
+#define B2R2_VMX0_RGB_TO_BGR 0x00000100
+#define B2R2_VMX1_RGB_TO_BGR 0x00040000
+#define B2R2_VMX2_RGB_TO_BGR 0x20000000
+#define B2R2_VMX3_RGB_TO_BGR 0x00000000
+
+/* VMX register values for BGR to YUV color conversion */
+/* Note: All BGR -> YUV values are calculated by multiplying
+ * the RGB -> YUV matrices [A], with [S] to form [A]x[S] where
+ * |0 0 1|
+ * S = |0 1 0|
+ * |1 0 0|
+ * Essentially swapping first and third columns in
+ * the matrices (VMX0, VMX1 and VMX2 values).
+ * The offset vector VMX3 remains untouched.
+ * Put another way, the value of bits 0 through 9
+ * is swapped with the value of
+ * bits 20 through 31 in VMX0, VMX1 and VMX2,
+ * taking into consideration the compression
+ * that is used on bits 0 through 9. Bit 0 being LSB.
+ */
+
+/* 601 Video Matrix (standard 601 conversion) */
+#define B2R2_VMX0_BGR_TO_YUV_601_VIDEO 0xfd7e4883
+#define B2R2_VMX1_BGR_TO_YUV_601_VIDEO 0x03a2584c
+#define B2R2_VMX2_BGR_TO_YUV_601_VIDEO 0x107ea7d4
+#define B2R2_VMX3_BGR_TO_YUV_601_VIDEO 0x08000080
+
+/* 601 Gfx Matrix (full range conversion) */
+#define B2R2_VMX0_BGR_TO_YUV_601_GFX 0xfdde8870
+#define B2R2_VMX1_BGR_TO_YUV_601_GFX 0x03220442
+#define B2R2_VMX2_BGR_TO_YUV_601_GFX 0x0e3ed7da
+#define B2R2_VMX3_BGR_TO_YUV_601_GFX 0x08004080
+
+/* 709 Video Matrix (standard 709 conversion) */
+#define B2R2_VMX0_BGR_TO_YUV_709_VIDEO 0xfe9e2483
+#define B2R2_VMX1_BGR_TO_YUV_709_VIDEO 0x0262dc37
+#define B2R2_VMX2_BGR_TO_YUV_709_VIDEO 0x107e6fe2
+#define B2R2_VMX3_BGR_TO_YUV_709_VIDEO 0x08000080
+
+/* 709 Gfx Matrix (standard 709 conversion) */
+#define B2R2_VMX0_BGR_TO_YUV_709_GFX 0xfebe6871
+#define B2R2_VMX1_BGR_TO_YUV_709_GFX 0x0202742f
+#define B2R2_VMX2_BGR_TO_YUV_709_GFX 0x0e3ea7e6
+#define B2R2_VMX3_BGR_TO_YUV_709_GFX 0x08004080
+
+
+/* VMX register values for YUV to BGR conversion */
+/* Note: All YUV -> BGR values are constructed
+ * from the YUV -> RGB ones, by swapping
+ * first and third rows in the matrix
+ * (VMX0 and VMX2 values). Further, the first and
+ * third values in the offset vector need to be
+ * swapped as well, i.e. bits 0 through 9 are swapped
+ * with bits 20 through 29 in the VMX3 value.
+ * Bit 0 being LSB.
+ */
+
+/* 601 Video Matrix (standard 601 conversion) */
+#define B2R2_VMX0_YUV_TO_BGR_601_VIDEO 0x0004013f
+#define B2R2_VMX1_YUV_TO_BGR_601_VIDEO 0xe9a403aa
+#define B2R2_VMX2_YUV_TO_BGR_601_VIDEO 0x2c440000
+#define B2R2_VMX3_YUV_TO_BGR_601_VIDEO 0x3222134f
+
+/* 601 Gfx Matrix (full range conversion) */
+#define B2R2_VMX0_YUV_TO_BGR_601_GFX 0x0004a957
+#define B2R2_VMX1_YUV_TO_BGR_601_GFX 0xe604ab9c
+#define B2R2_VMX2_YUV_TO_BGR_601_GFX 0x3324a800
+#define B2R2_VMX3_YUV_TO_BGR_601_GFX 0x2eb21f21
+
+/* 709 Video Matrix (standard 709 conversion) */
+#define B2R2_VMX0_YUV_TO_BGR_709_VIDEO 0x00040145
+#define B2R2_VMX1_YUV_TO_BGR_709_VIDEO 0xf16403d1
+#define B2R2_VMX2_YUV_TO_BGR_709_VIDEO 0x31440000
+#define B2R2_VMX3_YUV_TO_BGR_709_VIDEO 0x31814b3b
+
+/* 709 Gfx Matrix (standard 709 conversion) */
+#define B2R2_VMX0_YUV_TO_BGR_709_GFX 0x0004a95f
+#define B2R2_VMX1_YUV_TO_BGR_709_GFX 0xef04abc9
+#define B2R2_VMX2_YUV_TO_BGR_709_GFX 0x3964a800
+#define B2R2_VMX3_YUV_TO_BGR_709_GFX 0x2df13307
+
+
+/* VMX register values for YVU to RGB conversion */
+
+/* 601 Video Matrix (standard 601 conversion) */
+#define B2R2_VMX0_YVU_TO_RGB_601_VIDEO 0x00040120
+#define B2R2_VMX1_YVU_TO_RGB_601_VIDEO 0xF544034D
+#define B2R2_VMX2_YVU_TO_RGB_601_VIDEO 0x37840000
+#define B2R2_VMX3_YVU_TO_RGB_601_VIDEO 0x34f21322
+
+/* VMX register values for RGB to YVU conversion */
+
+/* 601 Video Matrix (standard 601 conversion) */
+#define B2R2_VMX0_RGB_TO_YVU_601_VIDEO 0xfa9ea483
+#define B2R2_VMX1_RGB_TO_YVU_601_VIDEO 0x0982581d
+#define B2R2_VMX2_RGB_TO_YVU_601_VIDEO 0x107e4beb
+#define B2R2_VMX3_RGB_TO_YVU_601_VIDEO 0x08000080
+
+/* VMX register values for YVU to BGR conversion */
+
+/* 601 Video Matrix (standard 601 conversion) */
+#define B2R2_VMX0_YVU_TO_BGR_601_VIDEO 0x37840000
+#define B2R2_VMX1_YVU_TO_BGR_601_VIDEO 0xF544034D
+#define B2R2_VMX2_YVU_TO_BGR_601_VIDEO 0x00040120
+#define B2R2_VMX3_YVU_TO_BGR_601_VIDEO 0x3222134F
+
+/* VMX register values for BGR to YVU conversion */
+
+/* 601 Video Matrix (standard 601 conversion) */
+#define B2R2_VMX0_BGR_TO_YVU_601_VIDEO 0x107ea7d4
+#define B2R2_VMX1_BGR_TO_YVU_601_VIDEO 0x03a2584c
+#define B2R2_VMX2_BGR_TO_YVU_601_VIDEO 0xfd7e4883
+#define B2R2_VMX3_BGR_TO_YVU_601_VIDEO 0x08000080
+
+/* VMX register values for YVU to YUV conversion */
+
+/* 601 Video Matrix (standard 601 conversion) */
+/* Internally, the components are in fact stored
+ * with luma in the middle, i.e. UYV, which is why
+ * the values are just like for RGB->BGR conversion.
+ */
+#define B2R2_VMX0_YVU_TO_YUV_601_VIDEO 0x00000100
+#define B2R2_VMX1_YVU_TO_YUV_601_VIDEO 0x00040000
+#define B2R2_VMX2_YVU_TO_YUV_601_VIDEO 0x20000000
+#define B2R2_VMX3_YVU_TO_YUV_601_VIDEO 0x00000000
+
+#endif /* B2R2_HW_H__ */
diff --git a/drivers/video/b2r2/b2r2_input_validation.c b/drivers/video/b2r2/b2r2_input_validation.c
new file mode 100644
index 00000000000..2604d8f77e0
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_input_validation.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com> for ST-Ericsson
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "b2r2_input_validation.h"
+
+#include "b2r2_debug.h"
+#include "b2r2_utils.h"
+
+#include <video/b2r2_blt.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+
+static bool is_valid_format(enum b2r2_blt_fmt fmt);
+
+static bool is_valid_pitch_for_fmt(u32 pitch, s32 width,
+ enum b2r2_blt_fmt fmt);
+
+static bool is_aligned_width_for_fmt(s32 width, enum b2r2_blt_fmt fmt);
+static s32 width_2_complete_width(s32 width, enum b2r2_blt_fmt fmt);
+static bool is_complete_width_for_fmt(s32 width, enum b2r2_blt_fmt fmt);
+static bool is_valid_height_for_fmt(s32 height, enum b2r2_blt_fmt fmt);
+
+static bool validate_img(struct b2r2_blt_img *img);
+static bool validate_rect(struct b2r2_blt_rect *rect);
+
+
+static bool is_valid_format(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_1_BIT_A1:
+ case B2R2_BLT_FMT_8_BIT_A8:
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+
+static bool is_valid_pitch_for_fmt(u32 pitch, s32 width, enum b2r2_blt_fmt fmt)
+{
+ s32 complete_width;
+ u32 pitch_derived_from_width;
+
+ complete_width = width_2_complete_width(width, fmt);
+
+ pitch_derived_from_width =
+ b2r2_calc_pitch_from_width(complete_width, fmt);
+
+ if (pitch < pitch_derived_from_width)
+ return false;
+
+ switch (fmt) {
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ if (!b2r2_is_aligned(pitch, 2))
+ return false;
+
+ break;
+
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ if (!b2r2_is_aligned(pitch, 4))
+ return false;
+
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+
+static bool is_aligned_width_for_fmt(s32 width, enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ if (!b2r2_is_aligned(width, 4))
+ return false;
+
+ break;
+
+ case B2R2_BLT_FMT_1_BIT_A1:
+ if (!b2r2_is_aligned(width, 8))
+ return false;
+
+ break;
+
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ if (!b2r2_is_aligned(width, 2))
+ return false;
+
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static s32 width_2_complete_width(s32 width, enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ return b2r2_align_up(width, 2);
+
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ return b2r2_align_up(width, 16);
+
+ default:
+ return width;
+ }
+}
+
+static bool is_complete_width_for_fmt(s32 width, enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ if (!b2r2_is_aligned(width, 2))
+ return false;
+
+ break;
+
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ if (!b2r2_is_aligned(width, 16))
+ return false;
+
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static bool is_valid_height_for_fmt(s32 height, enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ if (!b2r2_is_aligned(height, 2))
+ return false;
+
+ break;
+
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ if (!b2r2_is_aligned(height, 16))
+ return false;
+
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static bool validate_img(struct b2r2_blt_img *img)
+{
+ /*
+ * So that we always can do width * height * bpp without overflowing a
+ * 32 bit signed integer. isqrt(s32_max / max_bpp) was used to
+ * calculate the value.
+ */
+ static const s32 max_img_width_height = 8191;
+
+ s32 img_size;
+
+ if (!is_valid_format(img->fmt)) {
+ b2r2_log_info("Validation Error: !is_valid_format(img->fmt)\n");
+ return false;
+ }
+
+ if (img->width < 0 || img->width > max_img_width_height ||
+ img->height < 0 || img->height > max_img_width_height) {
+ b2r2_log_info("Validation Error: "
+ "img->width < 0 || "
+ "img->width > max_img_width_height || "
+ "img->height < 0 || "
+ "img->height > max_img_width_height\n");
+ return false;
+ }
+
+ if (b2r2_is_mb_fmt(img->fmt)) {
+ if (!is_complete_width_for_fmt(img->width, img->fmt)) {
+ b2r2_log_info("Validation Error: "
+ "!is_complete_width_for_fmt(img->width,"
+ " img->fmt)\n");
+ return false;
+ }
+ } else {
+ if (0 == img->pitch &&
+ (!is_aligned_width_for_fmt(img->width, img->fmt) ||
+ !is_complete_width_for_fmt(img->width, img->fmt))) {
+ b2r2_log_info("Validation Error: "
+ "0 == img->pitch && "
+ "(!is_aligned_width_for_fmt(img->width,"
+ " img->fmt) || "
+ "!is_complete_width_for_fmt(img->width,"
+ " img->fmt))\n");
+ return false;
+ }
+
+ if (img->pitch != 0 &&
+ !is_valid_pitch_for_fmt(img->pitch, img->width,
+ img->fmt)) {
+ b2r2_log_info("Validation Error: "
+ "img->pitch != 0 && "
+ "!is_valid_pitch_for_fmt(img->pitch, "
+ "img->width, img->fmt)\n");
+ return false;
+ }
+ }
+
+ if (!is_valid_height_for_fmt(img->width, img->fmt)) {
+ b2r2_log_info("Validation Error: "
+ "!is_valid_height_for_fmt(img->width, "
+ "img->fmt)\n");
+ return false;
+ }
+
+ img_size = b2r2_get_img_size(img);
+
+ /*
+ * To keep the entire image inside s32 range.
+ */
+ if ((B2R2_BLT_PTR_HWMEM_BUF_NAME_OFFSET == img->buf.type ||
+ B2R2_BLT_PTR_FD_OFFSET == img->buf.type) &&
+ img->buf.offset > (u32)b2r2_s32_max - (u32)img_size) {
+ b2r2_log_info("Validation Error: "
+ "(B2R2_BLT_PTR_HWMEM_BUF_NAME_OFFSET == "
+ "img->buf.type || B2R2_BLT_PTR_FD_OFFSET == "
+ "img->buf.type) && img->buf.offset > "
+ "(u32)B2R2_MAX_S32 - (u32)img_size\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool validate_rect(struct b2r2_blt_rect *rect)
+{
+ if (rect->width < 0 || rect->height < 0) {
+ b2r2_log_info("Validation Error: "
+ "rect->width < 0 || rect->height < 0\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool b2r2_validate_user_req(struct b2r2_blt_req *req)
+{
+ bool is_src_img_used;
+ bool is_src_mask_used;
+ bool is_dst_clip_rect_used;
+
+ if (req->size != sizeof(struct b2r2_blt_req)) {
+ b2r2_log_err("Validation Error: "
+ "req->size != sizeof(struct b2r2_blt_req)\n");
+ return false;
+ }
+
+ is_src_img_used = !(req->flags & B2R2_BLT_FLAG_SOURCE_FILL ||
+ req->flags & B2R2_BLT_FLAG_SOURCE_FILL_RAW);
+ is_src_mask_used = req->flags & B2R2_BLT_FLAG_SOURCE_MASK;
+ is_dst_clip_rect_used = req->flags & B2R2_BLT_FLAG_DESTINATION_CLIP;
+
+ if (is_src_img_used || is_src_mask_used) {
+ if (!validate_rect(&req->src_rect)) {
+ b2r2_log_info("Validation Error: "
+ "!validate_rect(&req->src_rect)\n");
+ return false;
+ }
+ }
+
+ if (!validate_rect(&req->dst_rect)) {
+ b2r2_log_info("Validation Error: "
+ "!validate_rect(&req->dst_rect)\n");
+ return false;
+ }
+
+ if (is_dst_clip_rect_used) {
+ if (!validate_rect(&req->dst_clip_rect)) {
+ b2r2_log_info("Validation Error: "
+ "!validate_rect(&req->dst_clip_rect)\n");
+ return false;
+ }
+ }
+
+ if (is_src_img_used) {
+ struct b2r2_blt_rect src_img_bounding_rect;
+
+ if (!validate_img(&req->src_img)) {
+ b2r2_log_info("Validation Error: "
+ "!validate_img(&req->src_img)\n");
+ return false;
+ }
+
+ b2r2_get_img_bounding_rect(&req->src_img,
+ &src_img_bounding_rect);
+ if (!b2r2_is_rect_inside_rect(&req->src_rect,
+ &src_img_bounding_rect)) {
+ b2r2_log_info("Validation Error: "
+ "!b2r2_is_rect_inside_rect(&req->src_rect, "
+ "&src_img_bounding_rect)\n");
+ return false;
+ }
+ }
+
+ if (is_src_mask_used) {
+ struct b2r2_blt_rect src_mask_bounding_rect;
+
+ if (!validate_img(&req->src_mask)) {
+ b2r2_log_info("Validation Error: "
+ "!validate_img(&req->src_mask)\n");
+ return false;
+ }
+
+ b2r2_get_img_bounding_rect(&req->src_mask,
+ &src_mask_bounding_rect);
+ if (!b2r2_is_rect_inside_rect(&req->src_rect,
+ &src_mask_bounding_rect)) {
+ b2r2_log_info("Validation Error: "
+ "!b2r2_is_rect_inside_rect(&req->src_rect, "
+ "&src_mask_bounding_rect)\n");
+ return false;
+ }
+ }
+
+ if (!validate_img(&req->dst_img)) {
+ b2r2_log_info("Validation Error: "
+ "!validate_img(&req->dst_img)\n");
+ return false;
+ }
+
+ return true;
+}
diff --git a/drivers/video/b2r2/b2r2_input_validation.h b/drivers/video/b2r2/b2r2_input_validation.h
new file mode 100644
index 00000000000..9a736343e06
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_input_validation.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com> for ST-Ericsson
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LINUX_DRIVERS_VIDEO_B2R2_INPUT_VALIDATION_H_
+#define _LINUX_DRIVERS_VIDEO_B2R2_INPUT_VALIDATION_H_
+
+#include <video/b2r2_blt.h>
+
+bool b2r2_validate_user_req(struct b2r2_blt_req *req);
+
+#endif
diff --git a/drivers/video/b2r2/b2r2_internal.h b/drivers/video/b2r2/b2r2_internal.h
new file mode 100644
index 00000000000..18556e7193a
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_internal.h
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 internal definitions
+ *
+ * Author: Robert Fekete <robert.fekete@stericsson.com>
+ * Author: Paul Wannback
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef _LINUX_DRIVERS_VIDEO_B2R2_INTERNAL_H_
+#define _LINUX_DRIVERS_VIDEO_B2R2_INTERNAL_H_
+
+
+#include <video/b2r2_blt.h>
+
+#include "b2r2_core.h"
+#include "b2r2_global.h"
+
+#include "b2r2_hw.h"
+
+/* The maximum possible number of temporary buffers needed */
+#define MAX_TMP_BUFS_NEEDED 2
+
+/* Size of the color look-up table */
+#define CLUT_SIZE 1024
+
+/**
+ * b2r2_blt_device() - Returns the device associated with B2R2 BLT.
+ * Mainly for debugging with dev_... functions.
+ *
+ * Returns the device pointer or NULL
+ */
+struct device *b2r2_blt_device(void);
+
+/**
+ * struct b2r2_blt_instance - Represents the B2R2 instance (one per open)
+ *
+ * @lock: Lock to protect the instance
+ *
+ * @report_list: Ready requests that should be reported,
+ * @report_list_waitq: Wait queue for report list
+ * @no_of_active_requests: Number of requests added but not reported
+ * in callback.
+ * @synching: true if any client is waiting for b2r2_blt_synch(0)
+ * @synch_done_waitq: Wait queue to handle synching on request_id 0
+ */
+struct b2r2_blt_instance {
+ struct mutex lock;
+
+ /* Requests to be reported */
+ struct list_head report_list;
+ wait_queue_head_t report_list_waitq;
+
+ /* Below for synching */
+ u32 no_of_active_requests;
+ bool synching;
+ wait_queue_head_t synch_done_waitq;
+};
+
+/**
+ * struct b2r2_node - Represents a B2R2 node with reqister values, executed
+ * by B2R2. Should be allocated non-cached.
+ *
+ * @next: Next node
+ * @physical_address: Physical address to be given to B2R2
+ * (physical address of "node" member below)
+ * @node: The B2R2 node with register settings. This is the data
+ * that B2R2 will use.
+ *
+ */
+struct b2r2_node {
+ struct b2r2_node *next;
+ u32 physical_address;
+
+ int src_tmp_index;
+ int dst_tmp_index;
+
+ int src_index;
+
+ /* B2R2 regs comes here */
+ struct b2r2_link_list node;
+};
+
+/**
+ * struct b2r2_resolved_buf - Contains calculated information about
+ * image buffers.
+ *
+ * @physical_address: Physical address of the buffer
+ * @virtual_address: Virtual address of the buffer
+ * @is_pmem: true if buffer is from pmem
+ * @hwmem_session: Hwmem session
+ * @hwmem_alloc: Hwmem alloc
+ * @filep: File pointer of mapped file (like pmem device, frame buffer device)
+ * @file_physical_start: Physical address of file start
+ * @file_virtual_start: Virtual address of file start
+ * @file_len: File len
+ *
+ */
+struct b2r2_resolved_buf {
+ u32 physical_address;
+ void *virtual_address;
+ bool is_pmem;
+ struct hwmem_alloc *hwmem_alloc;
+ /* Data for validation below */
+ struct file *filep;
+ u32 file_physical_start;
+ u32 file_virtual_start;
+ u32 file_len;
+};
+
+
+/**
+ * b2r2_work_buf - specification for a temporary work buffer
+ *
+ * @size - the size of the buffer (set by b2r2_node_split)
+ * @phys_addr - the physical address of the buffer (set by b2r2_blt_main)
+ */
+struct b2r2_work_buf {
+ u32 size;
+ u32 phys_addr;
+ void *virt_addr;
+ u32 mem_handle;
+};
+
+
+/**
+ * b2r2_op_type - the type of B2R2 operation to configure
+ */
+enum b2r2_op_type {
+ B2R2_DIRECT_COPY,
+ B2R2_DIRECT_FILL,
+ B2R2_COPY,
+ B2R2_FILL,
+ B2R2_SCALE,
+ B2R2_ROTATE,
+ B2R2_SCALE_AND_ROTATE,
+ B2R2_FLIP,
+};
+
+/**
+ * b2r2_fmt_type - the type of buffer for a given format
+ */
+enum b2r2_fmt_type {
+ B2R2_FMT_TYPE_RASTER,
+ B2R2_FMT_TYPE_SEMI_PLANAR,
+ B2R2_FMT_TYPE_PLANAR,
+};
+
+/**
+ * b2r2_fmt_conv - the type of format conversion to do
+ */
+enum b2r2_fmt_conv {
+ B2R2_FMT_CONV_NONE,
+ B2R2_FMT_CONV_RGB_TO_YUV,
+ B2R2_FMT_CONV_YUV_TO_RGB,
+ B2R2_FMT_CONV_YUV_TO_YUV,
+ B2R2_FMT_CONV_RGB_TO_BGR,
+ B2R2_FMT_CONV_BGR_TO_RGB,
+ B2R2_FMT_CONV_YUV_TO_BGR,
+ B2R2_FMT_CONV_BGR_TO_YUV,
+};
+
+/**
+ * b2r2_node_split_buf - information about a source or destination buffer
+ *
+ * @addr - the physical base address
+ * @chroma_addr - the physical address of the chroma plane
+ * @chroma_cr_addr - the physical address of the Cr chroma plane
+ * @fmt - the buffer format
+ * @fmt_type - the buffer format type
+ * @rect - the rectangle of the buffer to use
+ * @color - the color value to use is case of a fill operation
+ * @pitch - the pixmap byte pitch
+ * @height - the pixmap height
+ * @alpha_range - the alpha range of the buffer (0-128 or 0-255)
+ * @hso - the horizontal scan order
+ * @vso - the vertical scan order
+ * @endian - the endianess of the buffer
+ * @plane_selection - the plane to write if buffer is planar or semi-planar
+ */
+struct b2r2_node_split_buf {
+ u32 addr;
+ u32 chroma_addr;
+ u32 chroma_cr_addr;
+
+ enum b2r2_blt_fmt fmt;
+ enum b2r2_fmt_type type;
+
+ struct b2r2_blt_rect rect;
+ struct b2r2_blt_rect win;
+
+ s32 dx;
+ s32 dy;
+
+ u32 color;
+ u16 pitch;
+ u16 width;
+ u16 height;
+
+ enum b2r2_ty alpha_range;
+ enum b2r2_ty hso;
+ enum b2r2_ty vso;
+ enum b2r2_ty endian;
+ enum b2r2_tty dither;
+
+ /* Plane selection (used when writing to a multibuffer format) */
+ enum b2r2_tty plane_selection;
+
+ /* Chroma plane selection (used when writing planar formats) */
+ enum b2r2_tty chroma_selection;
+
+ int tmp_buf_index;
+};
+
+/**
+ * b2r2_node_split_job - an instance of a node split job
+ *
+ * @type - the type of operation
+ * @ivmx - the ivmx matrix to use for color conversion
+ * @blend - determines if blending is enabled
+ * @clip - determines if destination clipping is enabled
+ * @swap_fg_bg - determines if FG and BG should be swapped when blending
+ * @flags - the flags passed in the blt request
+ * @flag_param - parameter required by certain flags,
+ * e.g. color for source color keying.
+ * @transform - the transforms passed in the blt request
+ * @global_alpha - the global alpha
+ * @clip_rect - the clipping rectangle to use
+ * @horiz_rescale - determmines if horizontal rescaling is enabled
+ * @horiz_sf - the horizontal scale factor
+ * @vert_rescale - determines if vertical rescale is enabled
+ * @vert_sf - the vertical scale factor
+ * @src - the incoming source buffer
+ * @dst - the outgoing destination buffer
+ * @work_bufs - work buffer specifications
+ * @tmp_bufs - temporary buffers
+ * @buf_count - the number of temporary buffers used for the job
+ * @node_count - the number of nodes used for the job
+ * @max_buf_size - the maximum size of temporary buffers
+ * @nbr_rows - the number of tile rows in the blit operation
+ * @nbr_cols - the number of time columns in the blit operation
+ */
+struct b2r2_node_split_job {
+ enum b2r2_op_type type;
+
+ const u32 *ivmx;
+
+ bool blend;
+ bool clip;
+ bool rotation;
+
+ bool swap_fg_bg;
+
+ u32 flags;
+ u32 flag_param;
+ u32 transform;
+ u32 global_alpha;
+
+ struct b2r2_blt_rect clip_rect;
+
+ bool h_rescale;
+ u16 h_rsf;
+
+ bool v_rescale;
+ u16 v_rsf;
+
+ struct b2r2_node_split_buf src;
+ struct b2r2_node_split_buf dst;
+
+ struct b2r2_work_buf work_bufs[MAX_TMP_BUFS_NEEDED];
+ struct b2r2_node_split_buf tmp_bufs[MAX_TMP_BUFS_NEEDED];
+
+ u32 buf_count;
+ u32 node_count;
+ u32 max_buf_size;
+};
+
+/**
+ * struct b2r2_blt_request - Represents one B2R2 blit request
+ *
+ * @instance: Back pointer to the instance structure
+ * @list: List item to keep track of requests per instance
+ * @user_req: The request received from userspace
+ * @job: The administration structure for the B2R2 job,
+ * consisting of one or more nodes
+ * @node_split_job: The administration structure for the B2R2 node split job
+ * @first_node: Pointer to the first B2R2 node
+ * @request_id: Request id for this job
+ * @node_split_handle: Handle of the node split
+ * @src_resolved: Calculated info about the source buffer
+ * @src_mask_resolved: Calculated info about the source mask buffer
+ * @dst_resolved: Calculated info about the destination buffer
+ * @profile: True if the blit shall be profiled, false otherwise
+ */
+struct b2r2_blt_request {
+ struct b2r2_blt_instance *instance;
+ struct list_head list;
+ struct b2r2_blt_req user_req;
+ struct b2r2_core_job job;
+ struct b2r2_node_split_job node_split_job;
+ struct b2r2_node *first_node;
+ int request_id;
+
+ /* Resolved buffer addresses */
+ struct b2r2_resolved_buf src_resolved;
+ struct b2r2_resolved_buf src_mask_resolved;
+ struct b2r2_resolved_buf dst_resolved;
+
+ /* TBD: Info about SRAM usage & needs */
+ struct b2r2_work_buf *bufs;
+ u32 buf_count;
+
+ /* color look-up table */
+ void *clut;
+ u32 clut_phys_addr;
+
+ /* Profiling stuff */
+ bool profile;
+
+ s32 nsec_active_in_cpu;
+
+ u32 start_time_nsec;
+ s32 total_time_nsec;
+};
+
+/* FIXME: The functions below should be removed when we are
+ switching to the new Robert Lind allocator */
+
+/**
+ * b2r2_blt_alloc_nodes() - Allocate nodes
+ *
+ * @node_count: Number of nodes to allocate
+ *
+ * Return:
+ * Returns a pointer to the first node in the node list.
+ */
+struct b2r2_node *b2r2_blt_alloc_nodes(int node_count);
+
+/**
+ * b2r2_blt_free_nodes() - Release nodes previously allocated via
+ * b2r2_generate_nodes
+ *
+ * @first_node: First node in linked list of nodes
+ */
+void b2r2_blt_free_nodes(struct b2r2_node *first_node);
+
+/**
+ * b2r2_blt_module_init() - Initialize the B2R2 blt module
+ */
+int b2r2_blt_module_init(void);
+
+/**
+ * b2r2_blt_module_exit() - Un-initialize the B2R2 blt module
+ */
+void b2r2_blt_module_exit(void);
+
+#endif
diff --git a/drivers/video/b2r2/b2r2_kernel_if.c b/drivers/video/b2r2/b2r2_kernel_if.c
new file mode 100644
index 00000000000..373311ccca5
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_kernel_if.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 kernel interface for beeing a separate module
+ *
+ * Author: Robert Fekete <robert.fekete@stericsson.com>
+ * Author: Paul Wannback
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/list.h>
+#ifdef CONFIG_ANDROID_PMEM
+#include <linux/android_pmem.h>
+#endif
+#include <linux/fb.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/cacheflush.h>
+
+EXPORT_SYMBOL(fget_light);
+EXPORT_SYMBOL(fput_light);
+EXPORT_SYMBOL(flush_cache_range);
+EXPORT_SYMBOL(task_sched_runtime);
+#ifdef CONFIG_ANDROID_PMEM
+EXPORT_SYMBOL(get_pmem_file);
+EXPORT_SYMBOL(put_pmem_file);
+EXPORT_SYMBOL(flush_pmem_file);
+#endif
diff --git a/drivers/video/b2r2/b2r2_mem_alloc.c b/drivers/video/b2r2/b2r2_mem_alloc.c
new file mode 100644
index 00000000000..0dcd9b6a55e
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_mem_alloc.c
@@ -0,0 +1,726 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 internal Memory allocator
+ *
+ * Author: Robert Lind <robert.lind@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+
+#include "b2r2_internal.h"
+#include "b2r2_mem_alloc.h"
+
+
+/* Represents one block of b2r2 physical memory, free or allocated */
+struct b2r2_mem_block {
+ struct list_head list; /* For membership in list */
+
+ u32 offset; /* Offset in b2r2 physical memory area (aligned) */
+ u32 size; /* Size of the object (requested size if busy,
+ else actual) */
+ bool free; /* True if the block is free */
+
+ u32 lock_count; /* Lock count */
+
+#ifdef CONFIG_DEBUG_FS
+ char debugfs_fname[80]; /* debugfs file name */
+ struct dentry *debugfs_block; /* debugfs dir entry for the block */
+#endif
+};
+
+/* The memory heap */
+struct b2r2_mem_heap {
+ struct device *dev; /* Device pointer for memory allocation */
+ dma_addr_t start_phys_addr;/* Physical memory start address */
+ void *start_virt_ptr; /* Virtual pointer to start */
+ u32 size; /* Memory size */
+ u32 align; /* Alignment */
+
+ struct list_head blocks; /* List of all blocks */
+
+ spinlock_t heap_lock; /* Protection for the heap */
+
+ u32 node_size; /* Size of each B2R2 node */
+ struct dma_pool *node_heap;/* Heap for B2R2 node allocations */
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_sub_root_dir; /* debugfs: B2R2 MEM root dir */
+ struct dentry *debugfs_heap_stats; /* debugfs: B2R2 memory status */
+ struct dentry *debugfs_dir_blocks; /* debugfs: Free blocks dir */
+#endif
+};
+
+static struct b2r2_mem_heap *mem_heap;
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_root_dir; /* debugfs: B2R2 MEM root dir */
+#endif
+
+/* Forward declarations */
+static struct b2r2_mem_block *b2r2_mem_block_alloc(
+ u32 offset, u32 size, bool free);
+static void b2r2_mem_block_free(struct b2r2_mem_block *mem_block);
+
+/* Align value down to specified alignment */
+static inline u32 align_down(u32 align, u32 value)
+{
+ return value & ~(align - 1);
+}
+
+/* Align value up to specified alignment */
+static inline u32 align_up(u32 align, u32 value)
+{
+ return (value + align - 1) & ~(align - 1);
+}
+
+
+#ifdef CONFIG_DEBUG_FS
+/* About debugfs:
+ * debugfs is a mountable debug file system.
+ *
+ * Mount like this:
+ * mkdir /debug
+ * mount -t debugfs none /debug
+ * ls /debug/b2r2_mem
+ *
+ * ls -al /debug/b2r2_mem/blocks
+ * cat /debug/b2r2_mem/stats
+ */
+
+
+/* Create string containing memory heap status */
+static char *get_b2r2_mem_stats(struct b2r2_mem_heap *mem_heap, char *buf)
+{
+ struct b2r2_mem_heap_status mem_heap_status;
+
+ if (b2r2_mem_heap_status(&mem_heap_status) != 0) {
+ strcpy(buf, "Error, failed to get status\n");
+ return buf;
+ }
+
+ sprintf(buf,
+ "Handle : 0x%lX\n"
+ "Physical start address : 0x%lX\n"
+ "Size : %lu\n"
+ "Align : %lu\n"
+ "No of blocks allocated : %lu\n"
+ "Allocated size : %lu\n"
+ "No of free blocks : %lu\n"
+ "Free size : %lu\n"
+ "No of locks : %lu\n"
+ "No of locked : %lu\n"
+ "No of nodes : %lu\n",
+ (unsigned long) mem_heap,
+ (unsigned long) mem_heap_status.start_phys_addr,
+ (unsigned long) mem_heap_status.size,
+ (unsigned long) mem_heap_status.align,
+ (unsigned long) mem_heap_status.num_alloc,
+ (unsigned long) mem_heap_status.allocated_size,
+ (unsigned long) mem_heap_status.num_free,
+ (unsigned long) mem_heap_status.free_size,
+ (unsigned long) mem_heap_status.num_locks,
+ (unsigned long) mem_heap_status.num_locked,
+ (unsigned long) mem_heap_status.num_nodes);
+
+ return buf;
+}
+
+/*
+ * Print memory heap status on file
+ * (Use like "cat /debug/b2r2_mem/igram/stats")
+ */
+static int debugfs_b2r2_mem_stats_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct b2r2_mem_heap *mem_heap = filp->f_dentry->d_inode->i_private;
+ char Buf[400];
+ size_t dev_size;
+ int ret = 0;
+
+ get_b2r2_mem_stats(mem_heap, Buf);
+ dev_size = strlen(Buf);
+
+ /* No more to read if offset != 0 */
+ if (*f_pos > dev_size)
+ goto out;
+
+ if (*f_pos + count > dev_size)
+ count = dev_size - *f_pos;
+
+ if (copy_to_user(buf, Buf, count))
+ ret = -EINVAL;
+ *f_pos += count;
+ ret = count;
+
+out:
+ return ret;
+}
+
+/* debugfs file operations for the "stats" file */
+static const struct file_operations debugfs_b2r2_mem_stats_fops = {
+ .owner = THIS_MODULE,
+ .read = debugfs_b2r2_mem_stats_read,
+};
+
+/* read function for file in the "blocks" sub directory */
+static int debugfs_b2r2_mem_block_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct b2r2_mem_block *mem_block = filp->f_dentry->d_inode->i_private;
+ char Buf[200];
+ size_t dev_size;
+ int ret = 0;
+
+ dev_size = sprintf(Buf, "offset: %08lX %s size: %8d "
+ "lock_count: %2d\n",
+ (unsigned long) mem_block->offset,
+ mem_block->free ? "free" : "allc",
+ mem_block->size,
+ mem_block->lock_count);
+
+ /* No more to read if offset != 0 */
+ if (*f_pos > dev_size)
+ goto out;
+
+ if (*f_pos + count > dev_size)
+ count = dev_size - *f_pos;
+
+ if (copy_to_user(buf, Buf, count))
+ ret = -EINVAL;
+ *f_pos += count;
+ ret = count;
+
+out:
+ return ret;
+}
+
+/* debugfs file operations for files in the "blocks" directory */
+static const struct file_operations debugfs_b2r2_mem_block_fops = {
+ .owner = THIS_MODULE,
+ .read = debugfs_b2r2_mem_block_read,
+};
+
+/*
+ * Create or update the debugfs directory entry for a file in the
+ * "blocks" directory (a memory allocation)
+ */
+void debugfs_create_mem_block_entry(struct b2r2_mem_block *mem_block,
+ struct dentry *parent)
+{
+ struct timespec tm = current_kernel_time();
+ struct timespec atime = tm;
+ struct timespec mtime = tm;
+ struct timespec ctime = tm;
+
+ if (mem_block->debugfs_block) {
+ atime = mem_block->debugfs_block->d_inode->i_atime;
+ ctime = mem_block->debugfs_block->d_inode->i_ctime;
+ debugfs_remove(mem_block->debugfs_block);
+ }
+
+ /* Add the block in debugfs */
+ if (mem_block->free)
+ sprintf(mem_block->debugfs_fname, "%08lX free",
+ (unsigned long) mem_block->offset);
+ else {
+ sprintf(mem_block->debugfs_fname, "%08lX allc h:%08lX "
+ "lck:%d ",
+ (unsigned long) mem_block->offset,
+ (unsigned long) mem_block,
+ mem_block->lock_count);
+ }
+
+ mem_block->debugfs_block = debugfs_create_file(
+ mem_block->debugfs_fname,
+ 0444, parent, mem_block,
+ &debugfs_b2r2_mem_block_fops);
+ if (mem_block->debugfs_block) {
+ mem_block->debugfs_block->d_inode->i_size = mem_block->size;
+ mem_block->debugfs_block->d_inode->i_atime = atime;
+ mem_block->debugfs_block->d_inode->i_mtime = mtime;
+ mem_block->debugfs_block->d_inode->i_ctime = ctime;
+ }
+}
+#endif /* CONFIG_DEBUG_FS */
+
+/* Module initialization function */
+int b2r2_mem_init(struct device *dev, u32 heap_size, u32 align, u32 node_size)
+{
+ struct b2r2_mem_block *mem_block;
+ u32 aligned_size;
+
+ printk(KERN_INFO "B2R2_MEM: Creating heap for size %d bytes\n",
+ (int) heap_size);
+
+ /* Align size */
+ aligned_size = align_down(align, heap_size);
+ if (aligned_size == 0)
+ return -EINVAL;
+
+ mem_heap = kcalloc(sizeof(struct b2r2_mem_heap), 1, GFP_KERNEL);
+ if (!mem_heap)
+ return -ENOMEM;
+
+ mem_heap->start_virt_ptr = dma_alloc_coherent(dev,
+ aligned_size, &(mem_heap->start_phys_addr), GFP_KERNEL);
+ if (!mem_heap->start_phys_addr || !mem_heap->start_virt_ptr) {
+ printk(KERN_ERR "B2R2_MEM: Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the heap */
+ mem_heap->dev = dev;
+ mem_heap->size = aligned_size;
+ mem_heap->align = align;
+
+ INIT_LIST_HEAD(&mem_heap->blocks);
+
+#ifdef CONFIG_DEBUG_FS
+ /* Register debugfs */
+
+ debugfs_root_dir = debugfs_create_dir("b2r2_mem", NULL);
+
+ mem_heap->debugfs_sub_root_dir = debugfs_create_dir("b2r2_mem",
+ debugfs_root_dir);
+ mem_heap->debugfs_heap_stats = debugfs_create_file(
+ "stats", 0444, mem_heap->debugfs_sub_root_dir, mem_heap,
+ &debugfs_b2r2_mem_stats_fops);
+ mem_heap->debugfs_dir_blocks = debugfs_create_dir(
+ "blocks", mem_heap->debugfs_sub_root_dir);
+#endif
+
+ /* Create the first _free_ memory block */
+ mem_block = b2r2_mem_block_alloc(0, aligned_size, true);
+ if (!mem_block) {
+ dma_free_coherent(dev, aligned_size,
+ mem_heap->start_virt_ptr,
+ mem_heap->start_phys_addr);
+ kfree(mem_heap);
+ printk(KERN_ERR "B2R2_MEM: Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ /* Add the free block to the blocks list */
+ list_add(&mem_block->list, &mem_heap->blocks);
+
+ /* Allocate separate heap for B2R2 nodes */
+ mem_heap->node_size = node_size;
+ mem_heap->node_heap = dma_pool_create("b2r2_node_cache",
+ dev, node_size, align, 4096);
+ if (!mem_heap->node_heap) {
+ b2r2_mem_block_free(mem_block);
+ dma_free_coherent(dev, aligned_size,
+ mem_heap->start_virt_ptr,
+ mem_heap->start_phys_addr);
+ kfree(mem_heap);
+ printk(KERN_ERR "B2R2_MEM: Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(b2r2_mem_init);
+
+/* Module exit function */
+void b2r2_mem_exit(void)
+{
+ struct list_head *ptr;
+
+ /* Free B2R2 node heap */
+ dma_pool_destroy(mem_heap->node_heap);
+
+#ifdef CONFIG_DEBUG_FS
+ /* debugfs root dir */
+ if (debugfs_root_dir) {
+ debugfs_remove_recursive(debugfs_root_dir);
+ debugfs_root_dir = NULL;
+ }
+#endif
+
+ list_for_each(ptr, &mem_heap->blocks) {
+ struct b2r2_mem_block *mem_block =
+ list_entry(ptr, struct b2r2_mem_block, list);
+
+ b2r2_mem_block_free(mem_block);
+ }
+
+ dma_free_coherent(mem_heap->dev, mem_heap->size,
+ mem_heap->start_virt_ptr,
+ mem_heap->start_phys_addr);
+ kfree(mem_heap);
+}
+EXPORT_SYMBOL(b2r2_mem_exit);
+
+/* Return status of the heap */
+int b2r2_mem_heap_status(struct b2r2_mem_heap_status *mem_heap_status)
+{
+ struct list_head *ptr;
+
+ if (!mem_heap || !mem_heap_status)
+ return -EINVAL;
+ memset(mem_heap_status, 0, sizeof(*mem_heap_status));
+
+ /* Lock the heap */
+ spin_lock(&mem_heap->heap_lock);
+
+ /* Fill in static info */
+ mem_heap_status->start_phys_addr = mem_heap->start_phys_addr;
+ mem_heap_status->size = mem_heap->size;
+ mem_heap_status->align = mem_heap->align;
+
+ list_for_each(ptr, &mem_heap->blocks) {
+ struct b2r2_mem_block *mem_block =
+ list_entry(ptr, struct b2r2_mem_block, list);
+
+ if (mem_block->free) {
+ mem_heap_status->num_free++;
+ mem_heap_status->free_size += mem_block->size;
+ } else {
+ if (mem_block->lock_count) {
+ mem_heap_status->num_locked++;
+ mem_heap_status->num_locks +=
+ mem_block->lock_count;
+ }
+ mem_heap_status->num_alloc++;
+ mem_heap_status->allocated_size += mem_block->size;
+ }
+ }
+
+ spin_unlock(&mem_heap->heap_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(b2r2_mem_heap_status);
+
+/* Internal: Allocate a housekeeping structure
+ * for an allocated or free memory block
+ */
+static struct b2r2_mem_block *b2r2_mem_block_alloc(
+ u32 offset, u32 size, bool free)
+{
+ struct b2r2_mem_block *mem_block = kmalloc(
+ sizeof(struct b2r2_mem_block), GFP_KERNEL);
+
+ if (mem_block) {
+ mem_block->offset = offset;
+ mem_block->size = size;
+ mem_block->free = free;
+ mem_block->lock_count = 0;
+
+ INIT_LIST_HEAD(&mem_block->list);
+
+#ifdef CONFIG_DEBUG_FS
+ mem_block->debugfs_block = NULL;
+ /* Add the block in debugfs */
+ debugfs_create_mem_block_entry(mem_block,
+ mem_heap->debugfs_dir_blocks);
+#endif
+ }
+
+ return mem_block;
+}
+
+/* Internal: Release housekeeping structure */
+static void b2r2_mem_block_free(struct b2r2_mem_block *mem_block)
+{
+ if (mem_block) {
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(mem_block->debugfs_block);
+#endif
+ kfree(mem_block);
+ }
+}
+
+/* Allocate a block from the heap */
+int b2r2_mem_alloc(u32 requested_size, u32 *returned_size, u32 *mem_handle)
+{
+ int ret = 0;
+ struct list_head *ptr;
+ struct b2r2_mem_block *found_mem_block = NULL;
+ u32 aligned_size;
+
+ if (!mem_handle)
+ return -EINVAL;
+
+ printk(KERN_INFO "%s: size=%d\n", __func__, requested_size);
+
+ *mem_handle = 0;
+ if (!mem_heap)
+ return -EINVAL;
+
+ /* Lock the heap */
+ spin_lock(&mem_heap->heap_lock);
+
+ aligned_size = align_up(mem_heap->align, requested_size);
+ /* Try to find the best matching free block of suitable size */
+ list_for_each(ptr, &mem_heap->blocks) {
+ struct b2r2_mem_block *mem_block =
+ list_entry(ptr, struct b2r2_mem_block, list);
+
+ if (mem_block->free && mem_block->size >= aligned_size &&
+ (!found_mem_block ||
+ mem_block->size < found_mem_block->size)) {
+ found_mem_block = mem_block;
+ if (found_mem_block->size == aligned_size)
+ break;
+ }
+ }
+
+ if (found_mem_block) {
+ struct b2r2_mem_block *new_block
+ = b2r2_mem_block_alloc(found_mem_block->offset,
+ requested_size, false);
+
+ if (new_block) {
+ /* Insert the new block before the found block */
+ list_add_tail(&new_block->list,
+ &found_mem_block->list);
+
+ /* Split the free block */
+ found_mem_block->offset += aligned_size;
+ found_mem_block->size -= aligned_size;
+
+ if (found_mem_block->size == 0)
+ b2r2_mem_block_free(found_mem_block);
+ else {
+#ifdef CONFIG_DEBUG_FS
+ debugfs_create_mem_block_entry(
+ found_mem_block,
+ mem_heap->debugfs_dir_blocks);
+#endif
+ }
+
+ *mem_handle = (u32) new_block;
+ *returned_size = aligned_size;
+ } else {
+ ret = -ENOMEM;
+ }
+ } else
+ ret = -ENOMEM;
+
+ if (ret != 0) {
+ *returned_size = 0;
+ *mem_handle = (u32) 0;
+ }
+
+ /* Unlock */
+ spin_unlock(&mem_heap->heap_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(b2r2_mem_alloc);
+
+/* Free the allocated block */
+int b2r2_mem_free(u32 mem_handle)
+{
+ int ret = 0;
+ struct b2r2_mem_block *mem_block = (struct b2r2_mem_block *) mem_handle;
+
+ if (!mem_block)
+ return -EINVAL;
+
+ /* Lock the heap */
+ spin_lock(&mem_heap->heap_lock);
+
+ if (!ret && mem_block->free)
+ ret = -EINVAL;
+
+ if (!ret) {
+ printk(KERN_INFO "%s: freeing block 0x%p\n", __func__, mem_block);
+ /* Release the block */
+
+ mem_block->free = true;
+ mem_block->size = align_up(mem_heap->align,
+ mem_block->size);
+
+ /* Join with previous block if possible */
+ if (mem_block->list.prev != &mem_heap->blocks) {
+ struct b2r2_mem_block *prev_block =
+ list_entry(mem_block->list.prev,
+ struct b2r2_mem_block, list);
+
+ if (prev_block->free &&
+ (prev_block->offset + prev_block->size) ==
+ mem_block->offset) {
+ mem_block->offset = prev_block->offset;
+ mem_block->size += prev_block->size;
+
+ b2r2_mem_block_free(prev_block);
+ }
+ }
+
+ /* Join with next block if possible */
+ if (mem_block->list.next != &mem_heap->blocks) {
+ struct b2r2_mem_block *next_block
+ = list_entry(mem_block->list.next,
+ struct b2r2_mem_block,
+ list);
+
+ if (next_block->free &&
+ (mem_block->offset + mem_block->size) ==
+ next_block->offset) {
+ mem_block->size += next_block->size;
+
+ b2r2_mem_block_free(next_block);
+ }
+ }
+#ifdef CONFIG_DEBUG_FS
+ debugfs_create_mem_block_entry(mem_block,
+ mem_heap->debugfs_dir_blocks);
+#endif
+ }
+
+ /* Unlock */
+ spin_unlock(&mem_heap->heap_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(b2r2_mem_free);
+
+/* Lock the allocated block in memory */
+int b2r2_mem_lock(u32 mem_handle, u32 *phys_addr, void **virt_ptr, u32 *size)
+{
+ struct b2r2_mem_block *mem_block =
+ (struct b2r2_mem_block *) mem_handle;
+
+ if (!mem_block)
+ return -EINVAL;
+
+ /* Lock the heap */
+ spin_lock(&mem_heap->heap_lock);
+
+ mem_block->lock_count++;
+
+ if (phys_addr)
+ *phys_addr = mem_heap->start_phys_addr + mem_block->offset;
+ if (virt_ptr)
+ *virt_ptr = (char *) mem_heap->start_virt_ptr +
+ mem_block->offset;
+ if (size)
+ *size = align_up(mem_heap->align, mem_block->size);
+#ifdef CONFIG_DEBUG_FS
+ debugfs_create_mem_block_entry(mem_block,
+ mem_heap->debugfs_dir_blocks);
+#endif
+
+ spin_unlock(&mem_heap->heap_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(b2r2_mem_lock);
+
+/* Unlock the allocated block in memory */
+int b2r2_mem_unlock(u32 mem_handle)
+{
+ struct b2r2_mem_block *mem_block =
+ (struct b2r2_mem_block *) mem_handle;
+
+ if (!mem_block)
+ return -EINVAL;
+
+ /* Lock the heap */
+ spin_lock(&mem_heap->heap_lock);
+
+ mem_block->lock_count--;
+
+ spin_unlock(&mem_heap->heap_lock);
+
+ /* debugfs will be updated in release */
+ return 0;
+/* return b2r2_mem_free(mem_handle);*/
+}
+EXPORT_SYMBOL(b2r2_mem_unlock);
+
+/* Allocate one or more b2r2 nodes from DMA pool */
+int b2r2_node_alloc(u32 num_nodes, struct b2r2_node **first_node)
+{
+ int i;
+ int ret = 0;
+ u32 physical_address;
+ struct b2r2_node *first_node_ptr;
+ struct b2r2_node *node_ptr;
+
+ /* Check input parameters */
+ if ((num_nodes <= 0) || !first_node) {
+ printk(KERN_ERR
+ "B2R2_MEM: Invalid parameter for b2r2_node_alloc, "
+ "num_nodes=%d, first_node=%ld\n",
+ (int) num_nodes, (long) first_node);
+ return -EINVAL;
+ }
+
+ /* Allocate the first node */
+ first_node_ptr = dma_pool_alloc(mem_heap->node_heap,
+ GFP_DMA | GFP_KERNEL, &physical_address);
+ if (first_node_ptr) {
+ /* Initialize first node */
+ first_node_ptr->next = NULL;
+ first_node_ptr->physical_address = physical_address +
+ offsetof(struct b2r2_node, node);
+
+ /* Allocate and initialize remaining nodes, */
+ /* and link them into a list */
+ for (i = 1, node_ptr = first_node_ptr; i < num_nodes; i++) {
+ node_ptr->next = dma_pool_alloc(mem_heap->node_heap,
+ GFP_DMA | GFP_KERNEL, &physical_address);
+ if (node_ptr->next) {
+ node_ptr = node_ptr->next;
+ node_ptr->next = NULL;
+ node_ptr->physical_address = physical_address +
+ offsetof(struct b2r2_node, node);
+ } else {
+ printk(KERN_ERR "B2R2_MEM: Failed to allocate memory for node\n");
+ ret = -ENOMEM;
+ break;
+ }
+ }
+
+ /* If all nodes were allocated successfully, */
+ /* return the first node */
+ if (!ret)
+ *first_node = first_node_ptr;
+ else
+ b2r2_node_free(first_node_ptr);
+ } else {
+ printk(KERN_ERR "B2R2_MEM: Failed to allocate memory for node\n");
+ ret = -ENOMEM;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(b2r2_node_alloc);
+
+/* Free a linked list of b2r2 nodes */
+void b2r2_node_free(struct b2r2_node *first_node)
+{
+ struct b2r2_node *current_node = first_node;
+ struct b2r2_node *next_node = NULL;
+
+ /* Traverse the linked list and free the nodes */
+ while (current_node != NULL) {
+ next_node = current_node->next;
+ dma_pool_free(mem_heap->node_heap, current_node,
+ current_node->physical_address -
+ offsetof(struct b2r2_node, node));
+ current_node = next_node;
+ }
+}
+EXPORT_SYMBOL(b2r2_node_free);
+
+MODULE_AUTHOR("Robert Lind <robert.lind@ericsson.com");
+MODULE_DESCRIPTION("Ericsson AB B2R2 physical memory driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/b2r2/b2r2_mem_alloc.h b/drivers/video/b2r2/b2r2_mem_alloc.h
new file mode 100644
index 00000000000..33309c972f5
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_mem_alloc.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 internal Memory allocator
+ *
+ * Author: Robert Lind <robert.lind@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef __B2R2_MEM_ALLOC_H
+#define __B2R2_MEM_ALLOC_H
+
+#include "b2r2_internal.h"
+
+
+/**
+ * struct b2r2_mem_heap_status - Information about current state of the heap
+ * @start_phys_addr: Physical address of the the memory area
+ * @size: Size of the memory area
+ * @align: Alignment of start and allocation sizes (in bytes).
+ * @num_alloc: Number of memory allocations
+ * @allocated_size: Size allocated (sum of requested sizes)
+ * @num_free: Number of free blocks (fragments)
+ * @free_size: Free size available for allocation
+ * @num_locks: Sum of number of number of locks on memory allocations
+ * @num_locked: Number of locked memory allocations
+ * @num_nodes: Number of node allocations
+ *
+ **/
+struct b2r2_mem_heap_status {
+ u32 start_phys_addr;
+ u32 size;
+ u32 align;
+ u32 num_alloc;
+ u32 allocated_size;
+ u32 num_free;
+ u32 free_size;
+ u32 num_locks;
+ u32 num_locked;
+ u32 num_nodes;
+};
+
+
+/* B2R2 memory API (kernel) */
+
+/**
+ * b2r2_mem_init() - Initializes the B2R2 memory manager
+ * @dev: Pointer to device to use for allocating the memory heap
+ * @heap_size: Size of the heap (in bytes)
+ * @align: Alignment to use for memory allocations on heap (in bytes)
+ * @node_size: Size of each B2R2 node (in bytes)
+ *
+ * Returns 0 if success, else negative error code
+ **/
+int b2r2_mem_init(struct device *dev, u32 heap_size, u32 align, u32 node_size);
+
+/**
+ * b2r2_mem_exit() - Cleans up the B2R2 memory manager
+ *
+ **/
+void b2r2_mem_exit(void);
+
+/**
+ * b2r2_mem_heap_status() - Get information about the current heap state
+ * @mem_heap_status: Struct containing status info on succesful return
+ *
+ * Returns 0 if success, else negative error code
+ **/
+int b2r2_mem_heap_status(struct b2r2_mem_heap_status *mem_heap_status);
+
+/**
+ * b2r2_mem_alloc() - Allocates memory block from physical memory heap
+ * @requested_size: Requested size
+ * @returned_size: Actual size of memory block. Might be adjusted due to
+ * alignment but is always >= requested size if function
+ * succeeds
+ * @mem_handle: Returned memory handle
+ *
+ * All memory allocations are movable when not locked.
+ * Returns 0 if OK else negative error value
+ **/
+int b2r2_mem_alloc(u32 requested_size, u32 *returned_size, u32 *mem_handle);
+
+/**
+ * b2r2_mem_free() - Frees an allocation
+ * @mem_handle: Memory handle
+ *
+ * Returns 0 if OK else negative error value
+ **/
+int b2r2_mem_free(u32 mem_handle);
+
+/**
+ * b2r2_mem_lock() - Lock memory in memory and return physical address
+ * @mem_handle: Memory handle
+ * @phys_addr: Returned physical address to start of memory allocation.
+ * May be NULL.
+ * @virt_ptr: Returned virtual address pointer to start of memory allocation.
+ * May be NULL.
+ * @size: Returned size of memory allocation. May be NULL.
+ *
+ * The adress of the memory allocation is locked and the physical address
+ * is returned.
+ * The lock count is incremented by one.
+ * You need to call b2r2_mem_unlock once for each call to
+ * b2r2_mem_lock.
+ * Returns 0 if OK else negative error value
+ **/
+int b2r2_mem_lock(u32 mem_handle, u32 *phys_addr, void **virt_ptr, u32 *size);
+
+/**
+ * b2r2_mem_unlock() - Unlock previously locked memory
+ * @mem_handle: Memory handle
+ *
+ * Decrements lock count. When lock count reaches 0 the
+ * memory area is movable again.
+ * Returns 0 if OK else negative error value
+ **/
+int b2r2_mem_unlock(u32 mem_handle);
+
+/**
+ * b2r2_node_alloc() - Allocates B2R2 node from physical memory heap
+ * @num_nodes: Number of linked nodes to allocate
+ * @first_node: Returned pointer to first node in linked list
+ *
+ * Returns 0 if OK else negative error value
+ **/
+int b2r2_node_alloc(u32 num_nodes, struct b2r2_node **first_node);
+
+/**
+ * b2r2_node_free() - Frees a linked list of allocated B2R2 nodes
+ * @first_node: Pointer to first node in linked list
+ *
+ * Returns 0 if OK else negative error value
+ **/
+void b2r2_node_free(struct b2r2_node *first_node);
+
+
+#endif /* __B2R2_MEM_ALLOC_H */
diff --git a/drivers/video/b2r2/b2r2_node_gen.c b/drivers/video/b2r2/b2r2_node_gen.c
new file mode 100644
index 00000000000..f452a4b11df
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_node_gen.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 node generator
+ *
+ * Author: Robert Fekete <robert.fekete@stericsson.com>
+ * Author: Paul Wannback
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <asm/dma-mapping.h>
+#include "b2r2_internal.h"
+
+static void free_nodes(struct b2r2_node *first_node)
+{
+ struct b2r2_node *node = first_node;
+ int no_of_nodes = 0;
+
+ while (node) {
+ no_of_nodes++;
+ node = node->next;
+ }
+
+ dma_free_coherent(b2r2_blt_device(),
+ no_of_nodes * sizeof(struct b2r2_node),
+ first_node,
+ first_node->physical_address -
+ offsetof(struct b2r2_node, node));
+}
+
+struct b2r2_node *b2r2_blt_alloc_nodes(int no_of_nodes)
+{
+ u32 physical_address;
+ struct b2r2_node *nodes;
+ struct b2r2_node *tmpnode;
+
+ if (no_of_nodes <= 0) {
+ dev_err(b2r2_blt_device(), "%s: Wrong number of nodes (%d)",
+ __func__, no_of_nodes);
+ return NULL;
+ }
+
+ /* Allocate the memory */
+ nodes = (struct b2r2_node *) dma_alloc_coherent(b2r2_blt_device(),
+ no_of_nodes * sizeof(struct b2r2_node),
+ &physical_address, GFP_DMA | GFP_KERNEL);
+
+ if (nodes == NULL) {
+ dev_err(b2r2_blt_device(),
+ "%s: Failed to alloc memory for nodes",
+ __func__);
+ return NULL;
+ }
+
+ /* Build the linked list */
+ tmpnode = nodes;
+ physical_address += offsetof(struct b2r2_node, node);
+ while (no_of_nodes--) {
+ tmpnode->physical_address = physical_address;
+ if (no_of_nodes)
+ tmpnode->next = tmpnode + 1;
+ else
+ tmpnode->next = NULL;
+
+ tmpnode++;
+ physical_address += sizeof(struct b2r2_node);
+ }
+
+ return nodes;
+}
+
+void b2r2_blt_free_nodes(struct b2r2_node *first_node)
+{
+ free_nodes(first_node);
+}
+
diff --git a/drivers/video/b2r2/b2r2_node_split.c b/drivers/video/b2r2/b2r2_node_split.c
new file mode 100644
index 00000000000..0d8ca195051
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_node_split.c
@@ -0,0 +1,3357 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 node splitter
+ *
+ * Author: Fredrik Allansson <fredrik.allansson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include "b2r2_debug.h"
+#include "b2r2_node_split.h"
+#include "b2r2_internal.h"
+#include "b2r2_hw.h"
+#include "b2r2_filters.h"
+
+#include <linux/kernel.h>
+
+/*
+ * Macros and constants
+ */
+#define ABS(x) ((x) < 0 ? -(x) : (x))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+#define INSTANCES_DEFAULT_SIZE 10
+#define INSTANCES_GROW_SIZE 5
+
+/*
+ * Internal types
+ */
+
+
+/*
+ * Global variables
+ */
+
+/**
+ * VMX values for different color space conversions
+ */
+static const u32 vmx_rgb_to_yuv[] = {
+ B2R2_VMX0_RGB_TO_YUV_601_VIDEO,
+ B2R2_VMX1_RGB_TO_YUV_601_VIDEO,
+ B2R2_VMX2_RGB_TO_YUV_601_VIDEO,
+ B2R2_VMX3_RGB_TO_YUV_601_VIDEO,
+};
+
+static const u32 vmx_yuv_to_rgb[] = {
+ B2R2_VMX0_YUV_TO_RGB_601_VIDEO,
+ B2R2_VMX1_YUV_TO_RGB_601_VIDEO,
+ B2R2_VMX2_YUV_TO_RGB_601_VIDEO,
+ B2R2_VMX3_YUV_TO_RGB_601_VIDEO,
+};
+
+static const u32 vmx_yvu_to_rgb[] = {
+ B2R2_VMX0_YVU_TO_RGB_601_VIDEO,
+ B2R2_VMX1_YVU_TO_RGB_601_VIDEO,
+ B2R2_VMX2_YVU_TO_RGB_601_VIDEO,
+ B2R2_VMX3_YVU_TO_RGB_601_VIDEO,
+};
+
+static const u32 vmx_rgb_to_yvu[] = {
+ B2R2_VMX0_RGB_TO_YVU_601_VIDEO,
+ B2R2_VMX1_RGB_TO_YVU_601_VIDEO,
+ B2R2_VMX2_RGB_TO_YVU_601_VIDEO,
+ B2R2_VMX3_RGB_TO_YVU_601_VIDEO,
+};
+
+static const u32 vmx_rgb_to_bgr[] = {
+ B2R2_VMX0_RGB_TO_BGR,
+ B2R2_VMX1_RGB_TO_BGR,
+ B2R2_VMX2_RGB_TO_BGR,
+ B2R2_VMX3_RGB_TO_BGR,
+};
+
+static const u32 vmx_bgr_to_yuv[] = {
+ B2R2_VMX0_BGR_TO_YUV_601_VIDEO,
+ B2R2_VMX1_BGR_TO_YUV_601_VIDEO,
+ B2R2_VMX2_BGR_TO_YUV_601_VIDEO,
+ B2R2_VMX3_BGR_TO_YUV_601_VIDEO,
+};
+
+static const u32 vmx_yuv_to_bgr[] = {
+ B2R2_VMX0_YUV_TO_BGR_601_VIDEO,
+ B2R2_VMX1_YUV_TO_BGR_601_VIDEO,
+ B2R2_VMX2_YUV_TO_BGR_601_VIDEO,
+ B2R2_VMX3_YUV_TO_BGR_601_VIDEO,
+};
+
+static const u32 vmx_bgr_to_yvu[] = {
+ B2R2_VMX0_BGR_TO_YVU_601_VIDEO,
+ B2R2_VMX1_BGR_TO_YVU_601_VIDEO,
+ B2R2_VMX2_BGR_TO_YVU_601_VIDEO,
+ B2R2_VMX3_BGR_TO_YVU_601_VIDEO,
+};
+
+static const u32 vmx_yvu_to_bgr[] = {
+ B2R2_VMX0_YVU_TO_BGR_601_VIDEO,
+ B2R2_VMX1_YVU_TO_BGR_601_VIDEO,
+ B2R2_VMX2_YVU_TO_BGR_601_VIDEO,
+ B2R2_VMX3_YVU_TO_BGR_601_VIDEO,
+};
+
+static const u32 vmx_yvu_to_yuv[] = {
+ B2R2_VMX0_YVU_TO_YUV_601_VIDEO,
+ B2R2_VMX1_YVU_TO_YUV_601_VIDEO,
+ B2R2_VMX2_YVU_TO_YUV_601_VIDEO,
+ B2R2_VMX3_YVU_TO_YUV_601_VIDEO,
+};
+
+/*
+ * Forward declaration of private functions
+ */
+
+static int analyze_fmt_conv(struct b2r2_node_split_buf *src,
+ struct b2r2_node_split_buf *dst,
+ const u32 **vmx, u32 *node_count);
+static int analyze_color_fill(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count);
+static int analyze_copy(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count);
+static int analyze_scaling(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count);
+static int analyze_rotate(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count);
+static int analyze_transform(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count);
+static int analyze_rot_scale(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count);
+static int analyze_scale_factors(struct b2r2_node_split_job *this);
+
+static void configure_src(struct b2r2_node *node,
+ struct b2r2_node_split_buf *src, const u32 *ivmx);
+static int configure_dst(struct b2r2_node *node,
+ struct b2r2_node_split_buf *dst, const u32 *ivmx,
+ struct b2r2_node **next);
+static void configure_blend(struct b2r2_node *node, u32 flags, u32 global_alpha,
+ bool swap_fg_bg);
+static void configure_clip(struct b2r2_node *node,
+ struct b2r2_blt_rect *clip_rect);
+
+static int configure_tile(struct b2r2_node_split_job *this,
+ struct b2r2_node *node, struct b2r2_node **next);
+static void configure_direct_fill(struct b2r2_node *node, u32 color,
+ struct b2r2_node_split_buf *dst, struct b2r2_node **next);
+static int configure_fill(struct b2r2_node *node, u32 color,
+ enum b2r2_blt_fmt fmt, struct b2r2_node_split_buf *dst,
+ const u32 *ivmx, struct b2r2_node **next);
+static void configure_direct_copy(struct b2r2_node *node,
+ struct b2r2_node_split_buf *src,
+ struct b2r2_node_split_buf *dst, struct b2r2_node **next);
+static int configure_copy(struct b2r2_node *node,
+ struct b2r2_node_split_buf *src,
+ struct b2r2_node_split_buf *dst, const u32 *ivmx,
+ struct b2r2_node **next,
+ struct b2r2_node_split_job *this);
+static int configure_rotate(struct b2r2_node *node,
+ struct b2r2_node_split_buf *src,
+ struct b2r2_node_split_buf *dst, const u32 *ivmx,
+ struct b2r2_node **next,
+ struct b2r2_node_split_job *this);
+static int configure_scale(struct b2r2_node *node,
+ struct b2r2_node_split_buf *src,
+ struct b2r2_node_split_buf *dst, u16 h_rsf, u16 v_rsf,
+ const u32 *ivmx, struct b2r2_node **next,
+ struct b2r2_node_split_job *this);
+static int configure_rot_scale(struct b2r2_node_split_job *this,
+ struct b2r2_node *node, struct b2r2_node **next);
+
+static int check_rect(const struct b2r2_blt_img *img,
+ const struct b2r2_blt_rect *rect,
+ const struct b2r2_blt_rect *clip);
+static void set_buf(struct b2r2_node_split_buf *buf, u32 addr,
+ const struct b2r2_blt_img *img,
+ const struct b2r2_blt_rect *rect, bool color_fill, u32 color);
+static int setup_tmp_buf(struct b2r2_node_split_buf *this, u32 max_size,
+ enum b2r2_blt_fmt pref_fmt, u32 pref_width, u32 pref_height);
+
+static enum b2r2_ty get_alpha_range(enum b2r2_blt_fmt fmt);
+static u32 set_alpha(enum b2r2_blt_fmt fmt, u8 alpha, u32 color);
+static u8 get_alpha(enum b2r2_blt_fmt fmt, u32 pixel);
+static bool fmt_has_alpha(enum b2r2_blt_fmt fmt);
+
+static bool is_rgb_fmt(enum b2r2_blt_fmt fmt);
+static bool is_bgr_fmt(enum b2r2_blt_fmt fmt);
+static bool is_yuv_fmt(enum b2r2_blt_fmt fmt);
+static bool is_yvu_fmt(enum b2r2_blt_fmt fmt);
+static bool is_yuv420_fmt(enum b2r2_blt_fmt fmt);
+static bool is_yuv422_fmt(enum b2r2_blt_fmt fmt);
+static bool is_yuv444_fmt(enum b2r2_blt_fmt fmt);
+static bool is_yvu420_fmt(enum b2r2_blt_fmt fmt);
+static bool is_yvu422_fmt(enum b2r2_blt_fmt fmt);
+
+static int fmt_byte_pitch(enum b2r2_blt_fmt fmt, u32 width);
+static enum b2r2_native_fmt to_native_fmt(enum b2r2_blt_fmt fmt);
+static u32 to_RGB888(u32 color, const enum b2r2_blt_fmt fmt);
+static enum b2r2_fmt_type get_fmt_type(enum b2r2_blt_fmt fmt);
+
+static bool is_transform(const struct b2r2_blt_request *req);
+static int calculate_scale_factor(u32 from, u32 to, u16 *sf_out);
+static s32 rescale(s32 dim, u16 sf);
+static s32 inv_rescale(s32 dim, u16 sf);
+
+static void set_target(struct b2r2_node *node, u32 addr,
+ struct b2r2_node_split_buf *buf);
+static void set_src(struct b2r2_src_config *src, u32 addr,
+ struct b2r2_node_split_buf *buf);
+static void set_src_1(struct b2r2_node *node, u32 addr,
+ struct b2r2_node_split_buf *buf);
+static void set_src_2(struct b2r2_node *node, u32 addr,
+ struct b2r2_node_split_buf *buf);
+static void set_src_3(struct b2r2_node *node, u32 addr,
+ struct b2r2_node_split_buf *buf);
+static void set_ivmx(struct b2r2_node *node, const u32 *vmx_values);
+
+static void reset_nodes(struct b2r2_node *node);
+
+/*
+ * Public functions
+ */
+
+/**
+ * b2r2_node_split_analyze() - analyzes the request
+ */
+int b2r2_node_split_analyze(const struct b2r2_blt_request *req,
+ u32 max_buf_size, u32 *node_count, struct b2r2_work_buf **bufs,
+ u32 *buf_count, struct b2r2_node_split_job *this)
+{
+ int ret;
+ bool color_fill;
+
+ b2r2_log_info("%s\n", __func__);
+
+ memset(this, 0, sizeof(*this));
+
+ /* Copy parameters */
+ this->flags = req->user_req.flags;
+ this->transform = req->user_req.transform;
+ this->max_buf_size = max_buf_size;
+ this->global_alpha = req->user_req.global_alpha;
+ this->buf_count = 0;
+ this->node_count = 0;
+
+ if (this->flags & B2R2_BLT_FLAG_BLUR) {
+ ret = -ENOSYS;
+ goto unsupported;
+ }
+
+ if ((this->flags & B2R2_BLT_FLAG_SOURCE_COLOR_KEY) &&
+ (is_yuv_fmt(req->user_req.src_img.fmt) ||
+ req->user_req.src_img.fmt == B2R2_BLT_FMT_1_BIT_A1 ||
+ req->user_req.src_img.fmt == B2R2_BLT_FMT_8_BIT_A8)) {
+ b2r2_log_warn("%s: Unsupported: source color keying with "
+ "YUV or pure alpha formats.\n",
+ __func__);
+ ret = -ENOSYS;
+ goto unsupported;
+ }
+
+ if (this->flags & (B2R2_BLT_FLAG_DEST_COLOR_KEY |
+ B2R2_BLT_FLAG_SOURCE_MASK)) {
+ b2r2_log_warn("%s: Unsupported: source mask, "
+ "destination color keying.\n",
+ __func__);
+ ret = -ENOSYS;
+ goto unsupported;
+ }
+
+ if ((req->user_req.flags & B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) &&
+ req->user_req.clut == NULL) {
+ b2r2_log_warn("%s: Invalid request: no table specified "
+ "for CLUT color correction.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Check for color fill */
+ color_fill = (this->flags & (B2R2_BLT_FLAG_SOURCE_FILL |
+ B2R2_BLT_FLAG_SOURCE_FILL_RAW)) != 0;
+
+ /* Configure the source and destination buffers */
+ set_buf(&this->src, req->src_resolved.physical_address,
+ &req->user_req.src_img, &req->user_req.src_rect,
+ color_fill, req->user_req.src_color);
+
+ set_buf(&this->dst, req->dst_resolved.physical_address,
+ &req->user_req.dst_img, &req->user_req.dst_rect, false,
+ 0);
+
+ b2r2_log_info("%s:\n"
+ "\t\tsrc.rect=(%4d, %4d, %4d, %4d)\t"
+ "dst.rect=(%4d, %4d, %4d, %4d)\n", __func__, this->src.rect.x,
+ this->src.rect.y, this->src.rect.width, this->src.rect.height,
+ this->dst.rect.x, this->dst.rect.y, this->dst.rect.width,
+ this->dst.rect.height);
+
+ if (this->flags & B2R2_BLT_FLAG_DITHER)
+ this->dst.dither = B2R2_TTY_RGB_ROUND_DITHER;
+
+ if (this->flags & B2R2_BLT_FLAG_SOURCE_COLOR_KEY)
+ this->flag_param = req->user_req.src_color;
+
+ /* Check for blending */
+ if ((this->flags & B2R2_BLT_FLAG_GLOBAL_ALPHA_BLEND) &&
+ (this->global_alpha != 255))
+ this->blend = true;
+ else if (this->flags & B2R2_BLT_FLAG_PER_PIXEL_ALPHA_BLEND)
+ this->blend = (color_fill && fmt_has_alpha(this->dst.fmt)) ||
+ fmt_has_alpha(this->src.fmt);
+
+ if (this->blend && this->src.type == B2R2_FMT_TYPE_PLANAR) {
+ b2r2_log_warn("%s: Unsupported: blend with planar source\n",
+ __func__);
+ ret = -ENOSYS;
+ goto unsupported;
+ }
+
+ /* Check for clipping */
+ this->clip = (this->flags &
+ B2R2_BLT_FLAG_DESTINATION_CLIP) != 0;
+ if (this->clip) {
+ s32 l = req->user_req.dst_clip_rect.x;
+ s32 r = l + req->user_req.dst_clip_rect.width;
+ s32 t = req->user_req.dst_clip_rect.y;
+ s32 b = t + req->user_req.dst_clip_rect.height;
+
+ /* Intersect the clip and buffer rects */
+ if (l < 0)
+ l = 0;
+ if (r > req->user_req.dst_img.width)
+ r = req->user_req.dst_img.width;
+ if (t < 0)
+ t = 0;
+ if (b > req->user_req.dst_img.height)
+ b = req->user_req.dst_img.height;
+
+ this->clip_rect.x = l;
+ this->clip_rect.y = t;
+ this->clip_rect.width = r - l;
+ this->clip_rect.height = b - t;
+ } else {
+ /* Set the clip rectangle to the buffer bounds */
+ this->clip_rect.x = 0;
+ this->clip_rect.y = 0;
+ this->clip_rect.width = req->user_req.dst_img.width;
+ this->clip_rect.height = req->user_req.dst_img.height;
+ }
+
+ /* Validate the destination */
+ ret = check_rect(&req->user_req.dst_img, &req->user_req.dst_rect,
+ &this->clip_rect);
+ if (ret < 0)
+ goto error;
+
+ /* Validate the source (if not color fill) */
+ if (!color_fill) {
+ ret = check_rect(&req->user_req.src_img,
+ &req->user_req.src_rect, NULL);
+ if (ret < 0)
+ goto error;
+ }
+
+ /* Do the analysis depending on the type of operation */
+ if (color_fill) {
+ ret = analyze_color_fill(this, req, &this->node_count);
+ } else {
+
+ bool upsample;
+ bool downsample;
+
+ /*
+ * YUV formats that are non-raster, non-yuv444 needs to be
+ * up (or down) sampled using the resizer.
+ *
+ * NOTE: The resizer needs to be enabled for YUV444 as well,
+ * even though there is no upsampling. This is most
+ * likely a bug in the hardware.
+ */
+ upsample = this->src.type != B2R2_FMT_TYPE_RASTER &&
+ is_yuv_fmt(this->src.fmt);
+ downsample = this->dst.type != B2R2_FMT_TYPE_RASTER &&
+ is_yuv_fmt(this->dst.fmt);
+
+ if (is_transform(req) || upsample || downsample)
+ ret = analyze_transform(this, req, &this->node_count,
+ &this->buf_count);
+ else
+ ret = analyze_copy(this, req, &this->node_count,
+ &this->buf_count);
+ }
+
+ if (ret == -ENOSYS) {
+ goto unsupported;
+ } else if (ret < 0) {
+ b2r2_log_warn("%s: Analysis failed!\n", __func__);
+ goto error;
+ }
+
+ /* Setup the origin and movement of the destination window */
+ if (this->dst.hso == B2R2_TY_HSO_RIGHT_TO_LEFT) {
+ this->dst.dx = -this->dst.win.width;
+ this->dst.win.x = this->dst.rect.x + this->dst.rect.width - 1;
+ } else {
+ this->dst.dx = this->dst.win.width;
+ this->dst.win.x = this->dst.rect.x;
+ }
+ if (this->dst.vso == B2R2_TY_VSO_BOTTOM_TO_TOP) {
+ this->dst.dy = -this->dst.win.height;
+ this->dst.win.y = this->dst.rect.y + this->dst.rect.height - 1;
+ } else {
+ this->dst.dy = this->dst.win.height;
+ this->dst.win.y = this->dst.rect.y;
+ }
+
+ *buf_count = this->buf_count;
+ *node_count = this->node_count;
+
+ if (this->buf_count > 0)
+ *bufs = &this->work_bufs[0];
+
+ b2r2_log_info("%s: dst.win=(%d, %d, %d, %d), dst.dx=%d, dst.dy=%d\n",
+ __func__, this->dst.win.x, this->dst.win.y,
+ this->dst.win.width, this->dst.win.height, this->dst.dx,
+ this->dst.dy);
+ if (this->buf_count > 0)
+ b2r2_log_info("%s: buf_count=%d, buf_size=%d, node_count=%d\n",
+ __func__, *buf_count, bufs[0]->size, *node_count);
+ else
+ b2r2_log_info("%s: buf_count=%d, node_count=%d\n",
+ __func__, *buf_count, *node_count);
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+unsupported:
+ return ret;
+}
+
+/**
+ * b2r2_node_split_configure() - configures the node list
+ */
+int b2r2_node_split_configure(struct b2r2_node_split_job *this,
+ struct b2r2_node *first)
+{
+ int ret;
+
+ struct b2r2_node_split_buf *dst = &this->dst;
+ struct b2r2_node *node = first;
+
+ u32 x_pixels = 0;
+ u32 y_pixels = 0;
+
+ reset_nodes(node);
+
+ while (y_pixels < dst->rect.height) {
+ s32 dst_x = dst->win.x;
+ s32 dst_w = dst->win.width;
+
+ /* Clamp window height */
+ if (dst->win.height > dst->rect.height - y_pixels)
+ dst->win.height = dst->rect.height - y_pixels;
+
+ while (x_pixels < dst->rect.width) {
+
+ /* Clamp window width */
+ if (dst_w > dst->rect.width - x_pixels)
+ dst->win.width = dst->rect.width - x_pixels;
+
+ ret = configure_tile(this, node, &node);
+ if (ret < 0)
+ goto error;
+
+ dst->win.x += dst->dx;
+ x_pixels += max(dst->dx, -dst->dx);
+ b2r2_log_info("%s: x_pixels=%d\n", __func__, x_pixels);
+ }
+
+ dst->win.y += dst->dy;
+ y_pixels += max(dst->dy, -dst->dy);
+
+ dst->win.x = dst_x;
+ dst->win.width = dst_w;
+ x_pixels = 0;
+
+ b2r2_log_info("%s: y_pixels=%d\n", __func__, y_pixels);
+ }
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: error!\n", __func__);
+ return ret;
+}
+
+/**
+ * b2r2_node_split_assign_buffers() - assigns temporary buffers to the node list
+ */
+int b2r2_node_split_assign_buffers(struct b2r2_node_split_job *this,
+ struct b2r2_node *first, struct b2r2_work_buf *bufs,
+ u32 buf_count)
+{
+ struct b2r2_node *node = first;
+
+ while (node != NULL) {
+ /* The indices are offset by one */
+ if (node->dst_tmp_index) {
+ BUG_ON(node->dst_tmp_index > buf_count);
+
+ b2r2_log_info("%s: assigning buf %d as dst\n",
+ __func__, node->dst_tmp_index);
+
+ node->node.GROUP1.B2R2_TBA =
+ bufs[node->dst_tmp_index - 1].phys_addr;
+ }
+ if (node->src_tmp_index) {
+ u32 addr = bufs[node->src_tmp_index - 1].phys_addr;
+
+ b2r2_log_info("%s: "
+ "assigning buf %d as src %d ", __func__,
+ node->src_tmp_index, node->src_index);
+
+ BUG_ON(node->src_tmp_index > buf_count);
+
+ switch (node->src_index) {
+ case 1:
+ b2r2_log_info("1\n");
+ node->node.GROUP3.B2R2_SBA = addr;
+ break;
+ case 2:
+ b2r2_log_info("2\n");
+ node->node.GROUP4.B2R2_SBA = addr;
+ break;
+ case 3:
+ b2r2_log_info("3\n");
+ node->node.GROUP5.B2R2_SBA = addr;
+ break;
+ default:
+ BUG_ON(1);
+ break;
+ }
+ }
+
+ b2r2_log_info("%s: tba=%p\tsba=%p\n", __func__,
+ (void *)node->node.GROUP1.B2R2_TBA,
+ (void *)node->node.GROUP4.B2R2_SBA);
+
+ node = node->next;
+ }
+
+ return 0;
+}
+
+/**
+ * b2r2_node_split_unassign_buffers() - releases temporary buffers
+ */
+void b2r2_node_split_unassign_buffers(struct b2r2_node_split_job *this,
+ struct b2r2_node *first)
+{
+ return;
+}
+
+/**
+ * b2r2_node_split_cancel() - cancels and releases a job instance
+ */
+void b2r2_node_split_cancel(struct b2r2_node_split_job *this)
+{
+ memset(this, 0, sizeof(*this));
+
+ return;
+}
+
+/*
+ * Private functions
+ */
+
+static int check_rect(const struct b2r2_blt_img *img,
+ const struct b2r2_blt_rect *rect,
+ const struct b2r2_blt_rect *clip)
+{
+ int ret;
+
+ s32 l, r, b, t;
+
+ /* Check rectangle dimensions*/
+ if ((rect->width <= 0) || (rect->height <= 0)) {
+ b2r2_log_warn("%s: "
+ "Illegal rect (%d, %d, %d, %d)\n",
+ __func__, rect->x, rect->y, rect->width, rect->height);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* If we are using clip we should only look at the intersection of the
+ rects */
+ if (clip) {
+ l = MAX(rect->x, clip->x);
+ t = MAX(rect->y, clip->y);
+ r = MIN(rect->x + rect->width, clip->x + clip->width);
+ b = MIN(rect->y + rect->height, clip->y + clip->height);
+ } else {
+ l = rect->x;
+ t = rect->y;
+ r = rect->x + rect->width;
+ b = rect->y + rect->height;
+ }
+
+ /* Check so that the rect isn't outside the buffer */
+ if ((l < 0) || (t < 0) || (l >= img->width) || (t >= img->height)) {
+ b2r2_log_warn("%s: "
+ "rect origin outside buffer\n", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if ((r > img->width) || (b > img->height)) {
+ b2r2_log_warn("%s: "
+ "rect ends outside buffer\n", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Check so the intersected rectangle isn't empty */
+ if ((l == r) || (t == b)) {
+ b2r2_log_warn("%s: "
+ "rect is empty (width or height zero)\n",
+ __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ return 0;
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+}
+
+/**
+ * analyze_fmt_conv() - analyze the format conversions needed for a job
+ */
+static int analyze_fmt_conv(struct b2r2_node_split_buf *src,
+ struct b2r2_node_split_buf *dst,
+ const u32 **vmx, u32 *node_count)
+{
+ if (is_rgb_fmt(src->fmt)) {
+ if (is_yvu_fmt(dst->fmt))
+ *vmx = &vmx_rgb_to_yvu[0];
+ else if (is_yuv_fmt(dst->fmt))
+ *vmx = &vmx_rgb_to_yuv[0];
+ else if (is_bgr_fmt(dst->fmt))
+ *vmx = &vmx_rgb_to_bgr[0];
+ } else if (is_yvu_fmt(src->fmt)) {
+ if (is_rgb_fmt(dst->fmt))
+ *vmx = &vmx_yvu_to_rgb[0];
+ else if (is_bgr_fmt(dst->fmt))
+ *vmx = &vmx_yvu_to_bgr[0];
+ else if (is_yuv_fmt(dst->fmt) &&
+ !is_yvu_fmt(dst->fmt))
+ *vmx = &vmx_yvu_to_yuv[0];
+ } else if (is_yuv_fmt(src->fmt)) {
+ if (is_rgb_fmt(dst->fmt))
+ *vmx = &vmx_yuv_to_rgb[0];
+ else if (is_bgr_fmt(dst->fmt))
+ *vmx = &vmx_yuv_to_bgr[0];
+ else if (is_yvu_fmt(dst->fmt))
+ *vmx = &vmx_yvu_to_yuv[0];
+ } else if (is_bgr_fmt(src->fmt)) {
+ if (is_rgb_fmt(dst->fmt))
+ *vmx = &vmx_rgb_to_bgr[0];
+ else if (is_yvu_fmt(dst->fmt))
+ *vmx = &vmx_bgr_to_yvu[0];
+ else if (is_yuv_fmt(dst->fmt))
+ *vmx = &vmx_bgr_to_yuv[0];
+ }
+
+ if (dst->type == B2R2_FMT_TYPE_RASTER) {
+ *node_count = 1;
+ } else if (dst->type == B2R2_FMT_TYPE_SEMI_PLANAR) {
+ *node_count = 2;
+ } else if (dst->type == B2R2_FMT_TYPE_PLANAR) {
+ *node_count = 3;
+ } else {
+ /* That's strange... */
+ BUG_ON(1);
+ }
+
+ return 0;
+}
+
+/**
+ * analyze_color_fill() - analyze a color fill operation
+ */
+static int analyze_color_fill(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count)
+{
+ int ret;
+
+ /* Destination must be raster for raw fill to work */
+ if (this->dst.type != B2R2_FMT_TYPE_RASTER) {
+ b2r2_log_warn("%s: fill requires raster destination\n",
+ __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* We will try to fill the entire rectangle in one go */
+ memcpy(&this->dst.win, &this->dst.rect, sizeof(this->dst.win));
+
+ /* Check if this is a direct fill */
+ if ((!this->blend) && ((this->flags & B2R2_BLT_FLAG_SOURCE_FILL_RAW) ||
+ (this->dst.fmt == B2R2_BLT_FMT_32_BIT_ARGB8888) ||
+ (this->dst.fmt == B2R2_BLT_FMT_32_BIT_ABGR8888) ||
+ (this->dst.fmt == B2R2_BLT_FMT_32_BIT_AYUV8888))) {
+ this->type = B2R2_DIRECT_FILL;
+
+ /* The color format will be the same as the dst fmt */
+ this->src.fmt = this->dst.fmt;
+
+ /* The entire destination rectangle will be */
+ memcpy(&this->dst.win, &this->dst.rect,
+ sizeof(this->dst.win));
+ *node_count = 1;
+ } else {
+ this->type = B2R2_FILL;
+
+ /* Determine the fill color format */
+ if (this->flags & B2R2_BLT_FLAG_SOURCE_FILL_RAW) {
+ /* The color format will be the same as the dst fmt */
+ this->src.fmt = this->dst.fmt;
+ } else {
+ /* If the dst fmt is YUV the fill fmt will be as well */
+ if (is_yuv_fmt(this->dst.fmt)) {
+ this->src.fmt = B2R2_BLT_FMT_32_BIT_AYUV8888;
+ } else if (is_rgb_fmt(this->dst.fmt)) {
+ this->src.fmt = B2R2_BLT_FMT_32_BIT_ARGB8888;
+ } else if (is_bgr_fmt(this->dst.fmt)) {
+ /* Color will still be ARGB, we will translate
+ using IVMX (configured later) */
+ this->src.fmt = B2R2_BLT_FMT_32_BIT_ARGB8888;
+ } else {
+ /* Wait, what? */
+ b2r2_log_warn("%s: "
+ "Illegal destination format for fill",
+ __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+
+ /* Also, B2R2 seems to ignore the pixel alpha value */
+ if (((this->flags & B2R2_BLT_FLAG_PER_PIXEL_ALPHA_BLEND)
+ != 0) &&
+ ((this->flags & B2R2_BLT_FLAG_SOURCE_FILL_RAW)
+ == 0) && fmt_has_alpha(this->src.fmt)) {
+ u8 pixel_alpha = get_alpha(this->src.fmt,
+ this->src.color);
+ u32 new_global = pixel_alpha * this->global_alpha / 255;
+
+ this->global_alpha = (u8)new_global;
+
+ /* Set the pixel alpha to full opaque so we don't get
+ any nasty suprises */
+ this->src.color = set_alpha(this->src.fmt, 0xFF,
+ this->src.color);
+ }
+
+ ret = analyze_fmt_conv(&this->src, &this->dst, &this->ivmx,
+ node_count);
+ if (ret < 0)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+
+}
+
+/**
+ * analyze_transform() - analyze a transform operation (rescale, rotate, etc.)
+ */
+static int analyze_transform(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count)
+{
+ int ret;
+
+ bool is_scaling;
+
+ b2r2_log_info("%s\n", __func__);
+
+ /*
+ * The transform enum is defined so that all rotation transforms are
+ * masked with the rotation flag
+ */
+ this->rotation = (this->transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) != 0;
+
+ /* B2R2 cannot do rotations if the destination is not raster, or 422R */
+ if (this->rotation && (this->dst.type != B2R2_FMT_TYPE_RASTER ||
+ this->dst.fmt == B2R2_BLT_FMT_Y_CB_Y_CR ||
+ this->dst.fmt == B2R2_BLT_FMT_CB_Y_CR_Y)) {
+ b2r2_log_warn("%s: Unsupported operation "
+ "(rot && (!dst_raster || dst==422R))",
+ __func__);
+ ret = -ENOSYS;
+ goto unsupported;
+ }
+
+ /* Flip the image by changing the scan order of the destination */
+ if (this->transform & B2R2_BLT_TRANSFORM_FLIP_H)
+ this->dst.hso = B2R2_TY_HSO_RIGHT_TO_LEFT;
+ if (this->transform & B2R2_BLT_TRANSFORM_FLIP_V)
+ this->dst.vso = B2R2_TY_VSO_BOTTOM_TO_TOP;
+
+ /* Check for scaling */
+ if (this->rotation) {
+ is_scaling = (this->src.rect.width != this->dst.rect.height) ||
+ (this->src.rect.height != this->dst.rect.width);
+ } else {
+ is_scaling = (this->src.rect.width != this->dst.rect.width) ||
+ (this->src.rect.height != this->dst.rect.height);
+ }
+
+ /* Plane separated formats must be treated as scaling */
+ is_scaling = is_scaling ||
+ (this->src.type == B2R2_FMT_TYPE_SEMI_PLANAR) ||
+ (this->src.type == B2R2_FMT_TYPE_PLANAR) ||
+ (this->dst.type == B2R2_FMT_TYPE_SEMI_PLANAR) ||
+ (this->dst.type == B2R2_FMT_TYPE_PLANAR);
+
+ if (is_scaling && this->rotation && this->blend) {
+ /* TODO: This is unsupported. Fix it! */
+ b2r2_log_info("%s: Unsupported operation (rot+rescale+blend)\n",
+ __func__);
+ ret = -ENOSYS;
+ goto unsupported;
+ }
+
+ /* Check which type of transform */
+ if (is_scaling && this->rotation) {
+ ret = analyze_rot_scale(this, req, node_count, buf_count);
+ if (ret < 0)
+ goto error;
+ } else if (is_scaling) {
+ ret = analyze_scaling(this, req, node_count, buf_count);
+ if (ret < 0)
+ goto error;
+ } else if (this->rotation) {
+ ret = analyze_rotate(this, req, node_count, buf_count);
+ if (ret < 0)
+ goto error;
+ } else {
+ /* No additional nodes needed for a flip */
+ ret = analyze_copy(this, req, node_count, buf_count);
+ if (ret < 0)
+ goto error;
+ this->type = B2R2_FLIP;
+ }
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: error!\n", __func__);
+unsupported:
+ return ret;
+}
+
+/**
+ * analyze_copy() - analyze a copy operation
+ */
+static int analyze_copy(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count)
+{
+ int ret;
+
+ memcpy(&this->dst.win, &this->dst.rect, sizeof(this->dst.win));
+
+ if (!this->blend &&
+ !(this->flags & B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) &&
+ (this->src.fmt == this->dst.fmt) &&
+ (this->src.type == B2R2_FMT_TYPE_RASTER) &&
+ (this->dst.rect.x >= this->clip_rect.x) &&
+ (this->dst.rect.y >= this->clip_rect.y) &&
+ (this->dst.rect.x + this->dst.rect.width <=
+ this->clip_rect.x + this->clip_rect.width) &&
+ (this->dst.rect.y + this->dst.rect.height <=
+ this->clip_rect.y + this->clip_rect.height)) {
+ this->type = B2R2_DIRECT_COPY;
+ *node_count = 1;
+ } else {
+ u32 copy_count;
+
+ this->type = B2R2_COPY;
+
+ ret = analyze_fmt_conv(&this->src, &this->dst, &this->ivmx,
+ &copy_count);
+ if (ret < 0)
+ goto error;
+
+ *node_count = copy_count;
+ }
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+}
+
+static int calc_rot_count(u32 width, u32 height)
+{
+ int count;
+
+ count = width / B2R2_ROTATE_MAX_WIDTH;
+ if (width % B2R2_ROTATE_MAX_WIDTH)
+ count++;
+ if (height > B2R2_ROTATE_MAX_WIDTH &&
+ height % B2R2_ROTATE_MAX_WIDTH)
+ count *= 2;
+
+ return count;
+}
+
+static int analyze_rot_scale_downscale(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count)
+{
+ int ret;
+
+ struct b2r2_node_split_buf *src = &this->src;
+ struct b2r2_node_split_buf *dst = &this->dst;
+ struct b2r2_node_split_buf *tmp = &this->tmp_bufs[0];
+
+ u32 num_rows;
+ u32 num_cols;
+
+ u32 rot_count;
+ u32 rescale_count;
+
+ u32 nodes_per_rot;
+ u32 nodes_per_rescale;
+
+ u32 right_width;
+ u32 bottom_height;
+
+ const u32 *dummy_vmx;
+
+ b2r2_log_info("%s\n", __func__);
+
+ /* Calculate the desired tmp buffer size */
+ tmp->win.width = rescale(B2R2_RESCALE_MAX_WIDTH - 1, this->h_rsf);
+ tmp->win.width >>= 10;
+ tmp->win.width = min(tmp->win.width, dst->rect.height);
+ tmp->win.height = dst->rect.width;
+
+ setup_tmp_buf(tmp, this->max_buf_size, dst->fmt, tmp->win.width,
+ tmp->win.height);
+ tmp->tmp_buf_index = 1;
+ this->work_bufs[0].size = tmp->pitch * tmp->height;
+
+ tmp->win.width = tmp->rect.width;
+ tmp->win.height = tmp->rect.height;
+
+ tmp->dither = dst->dither;
+ dst->dither = 0;
+
+ /* Update the dst window with the actual tmp buffer dimensions */
+ dst->win.width = tmp->win.height;
+ dst->win.height = tmp->win.width;
+
+ /* The rotated stripes are written to the destination bottom-up */
+ if (this->dst.vso == B2R2_TY_VSO_TOP_TO_BOTTOM)
+ this->dst.vso = B2R2_TY_VSO_BOTTOM_TO_TOP;
+ else
+ this->dst.vso = B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ /*
+ * Calculate how many nodes are required to copy to and from the tmp
+ * buffer
+ */
+ ret = analyze_fmt_conv(src, tmp, &this->ivmx, &nodes_per_rescale);
+ if (ret < 0)
+ goto error;
+
+ /* We will not do any format conversion in the rotation stage */
+ ret = analyze_fmt_conv(tmp, dst, &dummy_vmx, &nodes_per_rot);
+ if (ret < 0)
+ goto error;
+
+ /* Calculate node count for the inner tiles */
+ num_cols = dst->rect.width / dst->win.width;
+ num_rows = dst->rect.height / dst->win.height;
+
+ rescale_count = num_cols * num_rows;
+ rot_count = calc_rot_count(dst->win.height, dst->win.width) *
+ num_cols * num_rows;
+
+ right_width = dst->rect.width % dst->win.width;
+ bottom_height = dst->rect.height % dst->win.height;
+
+ /* Calculate node count for the rightmost tiles */
+ if (right_width) {
+ u32 count = calc_rot_count(dst->win.height, right_width);
+
+ rot_count += count * num_rows;
+ rescale_count += num_rows;
+ b2r2_log_info("%s: rightmost: %d nodes\n", __func__,
+ count*num_rows);
+ }
+
+ /* Calculate node count for the bottom tiles */
+ if (bottom_height) {
+ u32 count = calc_rot_count(bottom_height, dst->win.width);
+
+ rot_count += count * num_cols;
+ rescale_count += num_cols;
+ b2r2_log_info("%s: bottom: %d nodes\n", __func__,
+ count * num_cols);
+
+ }
+
+ /* And finally for the bottom right corner */
+ if (right_width && bottom_height) {
+ u32 count = calc_rot_count(bottom_height, right_width);
+
+ rot_count += count;
+ rescale_count++;
+ b2r2_log_info("%s: bottom right: %d nodes\n", __func__, count);
+
+ }
+
+ *node_count = rot_count * nodes_per_rot;
+ *node_count += rescale_count * nodes_per_rescale;
+ *buf_count = 1;
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: error!\n", __func__);
+ return ret;
+}
+
+static int analyze_rot_scale_upscale(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count)
+{
+ /* TODO: When upscaling we should optimally to the rotation first... */
+ return analyze_rot_scale_downscale(this, req, node_count, buf_count);
+}
+
+/**
+ * analyze_rot_scaling() - analyzes a combined rotation and scaling op
+ */
+static int analyze_rot_scale(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count)
+{
+ int ret;
+
+ bool upscale;
+
+ ret = analyze_scale_factors(this);
+ if (ret < 0)
+ goto error;
+
+ upscale = (u32)this->h_rsf * (u32)this->v_rsf < (1 << 20);
+
+ if (upscale)
+ ret = analyze_rot_scale_upscale(this, req, node_count,
+ buf_count);
+ else
+ ret = analyze_rot_scale_downscale(this, req, node_count,
+ buf_count);
+
+ if (ret < 0)
+ goto error;
+
+ this->type = B2R2_SCALE_AND_ROTATE;
+
+ return 0;
+
+error:
+ return ret;
+}
+
+/**
+ * analyze_scaling() - analyze a rescale operation
+ */
+static int analyze_scaling(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count)
+{
+ int ret;
+
+ u32 copy_count;
+ u32 nbr_cols;
+
+ s32 dst_w;
+
+ b2r2_log_info("%s\n", __func__);
+
+ ret = analyze_scale_factors(this);
+ if (ret < 0)
+ goto error;
+
+ /* Find out how many nodes a simple copy would require */
+ ret = analyze_fmt_conv(&this->src, &this->dst, &this->ivmx,
+ &copy_count);
+ if (ret < 0)
+ goto error;
+
+ memcpy(&this->dst.win, &this->dst.rect, sizeof(this->dst.win));
+
+ /*
+ * We need to subtract from the actual maximum rescale width since the
+ * start of the stripe will be floored and the end ceiled. This could in
+ * some cases cause the stripe to be one pixel more than the maximum
+ * width.
+ *
+ * Example:
+ * x = 127.8, w = 127.8
+ *
+ * The stripe will touch pixels 127.8 through 255.6, i.e. 129 pixels.
+ */
+ dst_w = rescale(B2R2_RESCALE_MAX_WIDTH - 1, this->h_rsf);
+ if (dst_w < (1 << 10))
+ dst_w = 1;
+ else
+ dst_w >>= 10;
+
+ b2r2_log_info("%s: dst_w=%d dst.rect.width=%d\n", __func__, dst_w,
+ this->dst.rect.width);
+
+ this->dst.win.width = min(dst_w, this->dst.rect.width);
+
+ b2r2_log_info("%s: dst.win.width=%d\n", __func__, this->dst.win.width);
+
+ nbr_cols = this->dst.rect.width / this->dst.win.width;
+ if (this->dst.rect.width % this->dst.win.width)
+ nbr_cols++;
+
+ *node_count = copy_count * nbr_cols;
+
+ this->type = B2R2_SCALE;
+
+ b2r2_log_info("%s exit\n", __func__);
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+
+}
+
+/**
+ * analyze_rotate() - analyze a rotate operation
+ */
+static int analyze_rotate(struct b2r2_node_split_job *this,
+ const struct b2r2_blt_request *req, u32 *node_count,
+ u32 *buf_count)
+{
+ int ret;
+
+ u32 nodes_per_tile;
+
+ /* Find out how many nodes a simple copy would require */
+ ret = analyze_fmt_conv(&this->src, &this->dst, &this->ivmx,
+ &nodes_per_tile);
+ if (ret < 0)
+ goto error;
+
+ this->type = B2R2_ROTATE;
+
+ /* The rotated stripes are written to the destination bottom-up */
+ if (this->dst.vso == B2R2_TY_VSO_TOP_TO_BOTTOM)
+ this->dst.vso = B2R2_TY_VSO_BOTTOM_TO_TOP;
+ else
+ this->dst.vso = B2R2_TY_VSO_TOP_TO_BOTTOM;
+
+ memcpy(&this->dst.win, &this->dst.rect, sizeof(this->dst.win));
+
+ this->dst.win.height = min(this->dst.win.height, B2R2_ROTATE_MAX_WIDTH);
+
+ /*
+ * B2R2 cannot do rotations on stripes that are not a multiple of 16
+ * pixels high (if larger than 16 pixels).
+ */
+ if (this->dst.win.width > 16)
+ this->dst.win.width -= (this->dst.win.width % 16);
+
+ /* Blending cannot be combined with rotation */
+ if (this->blend) {
+ struct b2r2_node_split_buf *tmp = &this->tmp_bufs[0];
+ enum b2r2_blt_fmt tmp_fmt;
+
+ if (is_yuv_fmt(this->dst.fmt))
+ tmp_fmt = B2R2_BLT_FMT_32_BIT_AYUV8888;
+ else if (is_bgr_fmt(this->dst.fmt))
+ tmp_fmt = B2R2_BLT_FMT_32_BIT_ABGR8888;
+ else
+ tmp_fmt = B2R2_BLT_FMT_32_BIT_ARGB8888;
+
+ setup_tmp_buf(tmp, this->max_buf_size, tmp_fmt,
+ this->dst.win.width, this->dst.win.height);
+
+ tmp->tmp_buf_index = 1;
+
+ tmp->vso = B2R2_TY_VSO_BOTTOM_TO_TOP;
+
+ this->dst.win.width = tmp->rect.width;
+ this->dst.win.height = tmp->rect.height;
+
+ memcpy(&tmp->win, &tmp->rect, sizeof(tmp->win));
+
+ *buf_count = 1;
+ this->work_bufs[0].size = tmp->pitch * tmp->height;
+
+ /*
+ * One more node per tile is required to rotate to the temp
+ * buffer.
+ */
+ nodes_per_tile++;
+ }
+
+ /* Finally, calculate the node count */
+ *node_count = nodes_per_tile *
+ calc_rot_count(this->src.rect.width, this->src.rect.height);
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+}
+
+/**
+ * analyze_scale_factors() - determines the scale factors for the op
+ */
+static int analyze_scale_factors(struct b2r2_node_split_job *this)
+{
+ int ret;
+
+ u16 hsf;
+ u16 vsf;
+
+ if (this->rotation) {
+ ret = calculate_scale_factor(this->src.rect.width,
+ this->dst.rect.height, &hsf);
+ if (ret < 0)
+ goto error;
+
+ ret = calculate_scale_factor(this->src.rect.height,
+ this->dst.rect.width, &vsf);
+ if (ret < 0)
+ goto error;
+ } else {
+ ret = calculate_scale_factor(this->src.rect.width,
+ this->dst.rect.width, &hsf);
+ if (ret < 0)
+ goto error;
+
+ ret = calculate_scale_factor(this->src.rect.height,
+ this->dst.rect.height, &vsf);
+ if (ret < 0)
+ goto error;
+ }
+
+ this->h_rescale = hsf != (1 << 10);
+ this->v_rescale = vsf != (1 << 10);
+
+ this->h_rsf = hsf;
+ this->v_rsf = vsf;
+
+ b2r2_log_info("%s: h_rsf=%.4x\n", __func__, this->h_rsf);
+ b2r2_log_info("%s: v_rsf=%.4x\n", __func__, this->v_rsf);
+
+ return 0;
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+}
+
+/**
+ * configure_tile() - configures one tile of a blit operation
+ */
+static int configure_tile(struct b2r2_node_split_job *this,
+ struct b2r2_node *node, struct b2r2_node **next)
+{
+ int ret = 0;
+
+ struct b2r2_node *last;
+ struct b2r2_node_split_buf *src = &this->src;
+ struct b2r2_node_split_buf *dst = &this->dst;
+
+ struct b2r2_blt_rect dst_norm;
+ struct b2r2_blt_rect src_norm;
+
+ /* Normalize the dest coords to the dest rect coordinate space */
+ dst_norm.x = dst->win.x - dst->rect.x;
+ dst_norm.y = dst->win.y - dst->rect.y;
+ dst_norm.width = dst->win.width;
+ dst_norm.height = dst->win.height;
+
+ if (dst->vso == B2R2_TY_VSO_BOTTOM_TO_TOP) {
+ /* The y coord should be counted from the bottom */
+ dst_norm.y = dst->rect.height - (dst_norm.y + 1);
+ }
+ if (dst->hso == B2R2_TY_HSO_RIGHT_TO_LEFT) {
+ /* The x coord should be counted from the right */
+ dst_norm.x = dst->rect.width - (dst_norm.x + 1);
+ }
+
+ /* If the destination is rotated we should swap x, y */
+ if (this->rotation) {
+ src_norm.x = dst_norm.y;
+ src_norm.y = dst_norm.x;
+ src_norm.width = dst_norm.height;
+ src_norm.height = dst_norm.width;
+ } else {
+ src_norm.x = dst_norm.x;
+ src_norm.y = dst_norm.y;
+ src_norm.width = dst_norm.width;
+ src_norm.height = dst_norm.height;
+ }
+
+ /* Convert to src coordinate space */
+ src->win.x = src_norm.x + src->rect.x;
+ src->win.y = src_norm.y + src->rect.y;
+ src->win.width = src_norm.width;
+ src->win.height = src_norm.height;
+
+ /* Do the configuration depending on operation type */
+ switch (this->type) {
+ case B2R2_DIRECT_FILL:
+ configure_direct_fill(node, this->src.color, dst, &last);
+ break;
+
+ case B2R2_DIRECT_COPY:
+ configure_direct_copy(node, src, dst, &last);
+ break;
+
+ case B2R2_FILL:
+ ret = configure_fill(node, src->color, src->fmt,
+ dst, this->ivmx, &last);
+ break;
+
+ case B2R2_FLIP: /* FLIP is just a copy with different VSO/HSO */
+ case B2R2_COPY:
+ ret = configure_copy(node, src, dst, this->ivmx, &last, this);
+ break;
+
+ case B2R2_ROTATE:
+ {
+ struct b2r2_node_split_buf *tmp = &this->tmp_bufs[0];
+
+ if (this->blend) {
+ b2r2_log_info("%s: rotation + blend\n",
+ __func__);
+
+ tmp->win.x = 0;
+ tmp->win.y = tmp->win.height - 1;
+ tmp->win.width = dst->win.width;
+ tmp->win.height = dst->win.height;
+
+ /* Rotate to the temp buf */
+ ret = configure_rotate(node, src, tmp,
+ this->ivmx, &node, NULL);
+ if (ret < 0)
+ goto error;
+
+ /* Then do a copy to the destination */
+ ret = configure_copy(node, tmp, dst, NULL,
+ &last, this);
+ } else {
+ /* Just do a rotation */
+ ret = configure_rotate(node, src, dst,
+ this->ivmx, &last, this);
+ }
+ }
+ break;
+
+ case B2R2_SCALE:
+ ret = configure_scale(node, src, dst, this->h_rsf, this->v_rsf,
+ this->ivmx, &last, this);
+ break;
+
+ case B2R2_SCALE_AND_ROTATE:
+ ret = configure_rot_scale(this, node, &last);
+ break;
+
+ default:
+ b2r2_log_warn("%s: Unsupported request\n", __func__);
+ ret = -ENOSYS;
+ goto error;
+ break;
+
+ }
+
+ if (ret < 0)
+ goto error;
+
+ /* Scale and rotate will configure its own blending and clipping */
+ if (this->type != B2R2_SCALE_AND_ROTATE) {
+
+ /* Configure blending and clipping */
+ do {
+ if (node == NULL) {
+ b2r2_log_warn("%s: "
+ "Internal error! Out of nodes!\n",
+ __func__);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (this->blend)
+ configure_blend(node, this->flags,
+ this->global_alpha,
+ this->swap_fg_bg);
+ if (this->clip)
+ configure_clip(node, &this->clip_rect);
+
+ node = node->next;
+
+ } while (node != last);
+ }
+
+ /* Consume the nodes */
+ *next = last;
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: Error!\n", __func__);
+ return ret;
+}
+
+/*
+ * configure_sub_rot() - configure a sub-rotation
+ *
+ * This functions configures a set of nodes for rotation using the destination
+ * window instead of the rectangle for calculating tiles.
+ */
+static int configure_sub_rot(struct b2r2_node *node,
+ struct b2r2_node_split_buf *src,
+ struct b2r2_node_split_buf *dst,
+ const u32 *ivmx, struct b2r2_node **next,
+ struct b2r2_node_split_job *job)
+{
+ int ret;
+
+ struct b2r2_blt_rect src_win;
+ struct b2r2_blt_rect dst_win;
+
+ u32 y_pixels;
+ u32 x_pixels;
+
+ memcpy(&src_win, &src->win, sizeof(src_win));
+ memcpy(&dst_win, &dst->win, sizeof(dst_win));
+
+ b2r2_log_info("%s: src_win=(%d, %d, %d, %d) "
+ "dst_win=(%d, %d, %d, %d)\n", __func__,
+ src_win.x, src_win.y, src_win.width, src_win.height,
+ dst_win.x, dst_win.y, dst_win.width, dst_win.height);
+
+ dst->win.height = B2R2_ROTATE_MAX_WIDTH;
+ if (dst->win.width % B2R2_ROTATE_MAX_WIDTH)
+ dst->win.width -= dst->win.width % B2R2_ROTATE_MAX_WIDTH;
+
+ while (x_pixels < dst_win.width) {
+ u32 src_x = src->win.x;
+ u32 src_w = src->win.width;
+ u32 dst_y = dst->win.y;
+ u32 dst_h = dst->win.height;
+
+ dst->win.width = min(dst->win.width,
+ dst_win.width - (int)x_pixels);
+ src->win.height = dst->win.width;
+
+ b2r2_log_info("%s: x_pixels=%d\n", __func__, x_pixels);
+
+ while (y_pixels < dst_win.height) {
+ dst->win.height = min(dst->win.height,
+ dst_win.height - (int)y_pixels);
+ src->win.width = dst->win.height;
+
+ b2r2_log_info("%s: y_pixels=%d\n", __func__, y_pixels);
+
+ ret = configure_rotate(node, src, dst, ivmx, &node,
+ job);
+ if (ret < 0)
+ goto error;
+
+ src->win.x += (src->hso == B2R2_TY_HSO_LEFT_TO_RIGHT) ?
+ src->win.width : -src->win.width;
+ dst->win.y += (dst->vso == B2R2_TY_VSO_TOP_TO_BOTTOM) ?
+ dst->win.height : -dst->win.height;
+
+ y_pixels += dst->win.height;
+ }
+
+ src->win.x = src_x;
+ src->win.y += (src->vso == B2R2_TY_VSO_TOP_TO_BOTTOM) ?
+ src->win.height : -src->win.height;
+ src->win.width = src_w;
+
+ dst->win.x += (dst->hso == B2R2_TY_HSO_LEFT_TO_RIGHT) ?
+ dst->win.width : -dst->win.width;
+ dst->win.y = dst_y;
+ dst->win.height = dst_h;
+
+ x_pixels += dst->win.width;
+ y_pixels = 0;
+
+ }
+
+ memcpy(&src->win, &src_win, sizeof(src->win));
+ memcpy(&dst->win, &dst_win, sizeof(dst->win));
+
+ *next = node;
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: error!\n", __func__);
+ return ret;
+}
+
+/**
+ * configure_rot_downscale() - configures a combined rotate and downscale
+ *
+ * When doing a downscale it is better to do the rotation last.
+ */
+static int configure_rot_downscale(struct b2r2_node_split_job *this,
+ struct b2r2_node *node, struct b2r2_node **next)
+{
+ int ret;
+
+ struct b2r2_node_split_buf *src = &this->src;
+ struct b2r2_node_split_buf *dst = &this->dst;
+ struct b2r2_node_split_buf *tmp = &this->tmp_bufs[0];
+
+ tmp->win.x = 0;
+ tmp->win.y = 0;
+ tmp->win.width = dst->win.height;
+ tmp->win.height = dst->win.width;
+
+ ret = configure_scale(node, src, tmp, this->h_rsf, this->v_rsf,
+ this->ivmx, &node, this);
+ if (ret < 0)
+ goto error;
+
+ ret = configure_sub_rot(node, tmp, dst, NULL, &node, this);
+ if (ret < 0)
+ goto error;
+
+ *next = node;
+
+ return 0;
+
+error:
+ b2r2_log_info("%s: error!\n", __func__);
+ return ret;
+}
+
+/**
+ * configure_rot_upscale() - configures a combined rotate and upscale
+ *
+ * When doing an upscale it is better to do the rotation first.
+ */
+static int configure_rot_upscale(struct b2r2_node_split_job *this,
+ struct b2r2_node *node, struct b2r2_node **next)
+{
+ /* TODO: Implement a optimal upscale (rotation first) */
+ return configure_rot_downscale(this, node, next);
+}
+
+/**
+ * configure_rot_scale() - configures a combined rotation and scaling op
+ */
+static int configure_rot_scale(struct b2r2_node_split_job *this,
+ struct b2r2_node *node, struct b2r2_node **next)
+{
+ int ret;
+
+ bool upscale = (u32)this->h_rsf * (u32)this->v_rsf < (1 << 10);
+
+ if (upscale)
+ ret = configure_rot_upscale(this, node, next);
+ else
+ ret = configure_rot_downscale(this, node, next);
+
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: error!\n", __func__);
+ return ret;
+}
+
+/**
+ * configure_direct_fill() - configures the given node for direct fill
+ *
+ * @node - the node to configure
+ * @color - the fill color
+ * @dst - the destination buffer
+ * @next - the next empty node in the node list
+ *
+ * This operation will always consume one node only.
+ */
+static void configure_direct_fill(struct b2r2_node *node, u32 color,
+ struct b2r2_node_split_buf *dst, struct b2r2_node **next)
+{
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_COLOR_FILL | B2R2_CIC_SOURCE_1;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_1_DIRECT_FILL;
+
+ /* Target setup */
+ set_target(node, dst->addr, dst);
+
+ /* Source setup */
+
+ /* It seems B2R2 checks so that source and dest has the same format */
+ node->node.GROUP3.B2R2_STY = to_native_fmt(dst->fmt);
+ node->node.GROUP2.B2R2_S1CF = color;
+ node->node.GROUP2.B2R2_S2CF = 0;
+
+ /* Consume the node */
+ *next = node->next;
+}
+
+/**
+ * configure_direct_copy() - configures the node for direct copy
+ *
+ * @node - the node to configure
+ * @src - the source buffer
+ * @dst - the destination buffer
+ * @next - the next empty node in the node list
+ *
+ * This operation will always consume one node only.
+ */
+static void configure_direct_copy(struct b2r2_node *node,
+ struct b2r2_node_split_buf *src,
+ struct b2r2_node_split_buf *dst, struct b2r2_node **next)
+{
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_1;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_1_DIRECT_COPY;
+
+ /* Source setup, use the base function to avoid altering the INS */
+ set_src(&node->node.GROUP3, src->addr, src);
+
+ /* Target setup */
+ set_target(node, dst->addr, dst);
+
+ /* Consume the node */
+ *next = node->next;
+}
+
+/**
+ * configure_fill() - configures the given node for color fill
+ *
+ * @node - the node to configure
+ * @color - the fill color
+ * @fmt - the source color format
+ * @dst - the destination buffer
+ * @next - the next empty node in the node list
+ *
+ * A normal fill operation can be combined with any other per pixel operations
+ * such as blend.
+ *
+ * This operation will consume as many nodes as are required to write to the
+ * destination format.
+ */
+static int configure_fill(struct b2r2_node *node, u32 color,
+ enum b2r2_blt_fmt fmt, struct b2r2_node_split_buf *dst,
+ const u32 *ivmx, struct b2r2_node **next)
+{
+ int ret;
+ struct b2r2_node *last;
+
+ /* Configure the destination */
+ ret = configure_dst(node, dst, ivmx, &last);
+ if (ret < 0)
+ goto error;
+
+ do {
+ if (node == NULL) {
+ b2r2_log_warn("%s: "
+ "Internal error! Out of nodes!\n", __func__);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_2 |
+ B2R2_CIC_COLOR_FILL;
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_2_COLOR_FILL_REGISTER;
+ node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BYPASS_S2_S3;
+
+ /* B2R2 has a bug that disables color fill from S2. As a
+ workaround we use S1 for the color. */
+ node->node.GROUP2.B2R2_S1CF = 0;
+ node->node.GROUP2.B2R2_S2CF = color;
+
+ /* TO BE REMOVED: */
+ set_src_2(node, dst->addr, dst);
+ node->node.GROUP4.B2R2_STY = to_native_fmt(fmt);
+
+ /* Setup the iVMX for color conversion */
+ if (ivmx != NULL)
+ set_ivmx(node, ivmx);
+
+ if ((dst->type == B2R2_FMT_TYPE_PLANAR) ||
+ (dst->type == B2R2_FMT_TYPE_SEMI_PLANAR)) {
+
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_RESCALE2D_ENABLED;
+ node->node.GROUP8.B2R2_FCTL =
+ B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_LUMA_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_LUMA_VF2D_MODE_ENABLE_RESIZER;
+ node->node.GROUP9.B2R2_RSF =
+ (1 << (B2R2_RSF_HSRC_INC_SHIFT + 10)) |
+ (1 << (B2R2_RSF_VSRC_INC_SHIFT + 10));
+ node->node.GROUP9.B2R2_RZI =
+ B2R2_RZI_DEFAULT_HNB_REPEAT |
+ (2 << B2R2_RZI_VNB_REPEAT_SHIFT);
+
+ node->node.GROUP10.B2R2_RSF =
+ (1 << (B2R2_RSF_HSRC_INC_SHIFT + 10)) |
+ (1 << (B2R2_RSF_VSRC_INC_SHIFT + 10));
+ node->node.GROUP10.B2R2_RZI =
+ B2R2_RZI_DEFAULT_HNB_REPEAT |
+ (2 << B2R2_RZI_VNB_REPEAT_SHIFT);
+ }
+
+ node = node->next;
+
+ } while (node != last);
+
+ /* Consume the nodes */
+ *next = node;
+
+ return 0;
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+}
+
+/**
+ * configure_copy() - configures the given node for a copy operation
+ *
+ * @node - the node to configure
+ * @src - the source buffer
+ * @dst - the destination buffer
+ * @ivmx - the iVMX to use for color conversion
+ * @next - the next empty node in the node list
+ *
+ * This operation will consume as many nodes as are required to write to the
+ * destination format.
+ */
+static int configure_copy(struct b2r2_node *node,
+ struct b2r2_node_split_buf *src,
+ struct b2r2_node_split_buf *dst, const u32 *ivmx,
+ struct b2r2_node **next,
+ struct b2r2_node_split_job *this)
+{
+ int ret;
+
+ struct b2r2_node *last;
+
+ ret = configure_dst(node, dst, ivmx, &last);
+ if (ret < 0)
+ goto error;
+
+ /* Configure the source for each node */
+ do {
+ if (node == NULL) {
+ b2r2_log_warn("%s: "
+ " Internal error! Out of nodes!\n", __func__);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BYPASS_S2_S3;
+ if (this != NULL &&
+ (this->flags & B2R2_BLT_FLAG_SOURCE_COLOR_KEY)
+ != 0) {
+ u32 key_color = 0;
+
+ node->node.GROUP0.B2R2_ACK |=
+ B2R2_ACK_CKEY_SEL_SRC_AFTER_CLUT |
+ B2R2_ACK_CKEY_RED_MATCH_IF_BETWEEN |
+ B2R2_ACK_CKEY_GREEN_MATCH_IF_BETWEEN |
+ B2R2_ACK_CKEY_BLUE_MATCH_IF_BETWEEN;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_CKEY_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_COLOR_KEY;
+
+ key_color = to_RGB888(this->flag_param, src->fmt);
+ node->node.GROUP12.B2R2_KEY1 = key_color;
+ node->node.GROUP12.B2R2_KEY2 = key_color;
+ }
+
+ if (this != NULL &&
+ (this->flags &
+ B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) != 0) {
+ struct b2r2_blt_request *request =
+ container_of(this, struct b2r2_blt_request,
+ node_split_job);
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_CLUTOP_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_CLUT;
+ node->node.GROUP7.B2R2_CCO =
+ B2R2_CCO_CLUT_COLOR_CORRECTION |
+ B2R2_CCO_CLUT_UPDATE;
+ node->node.GROUP7.B2R2_CML = request->clut_phys_addr;
+ }
+ /* Configure the source(s) */
+ configure_src(node, src, ivmx);
+
+ node = node->next;
+ } while (node != last);
+
+ /* Consume the nodes */
+ *next = node;
+
+ return 0;
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+}
+
+/**
+ * configure_rotate() - configures the given node for rotation
+ *
+ * @node - the node to configure
+ * @src - the source buffer
+ * @dst - the destination buffer
+ * @ivmx - the iVMX to use for color conversion
+ * @next - the next empty node in the node list
+ *
+ * This operation will consume as many nodes are are required by the combination
+ * of rotating and writing the destination format.
+ */
+static int configure_rotate(struct b2r2_node *node,
+ struct b2r2_node_split_buf *src,
+ struct b2r2_node_split_buf *dst, const u32 *ivmx,
+ struct b2r2_node **next,
+ struct b2r2_node_split_job *this)
+{
+ int ret;
+
+ struct b2r2_node *last;
+
+ ret = configure_copy(node, src, dst, ivmx, &last, this);
+ if (ret < 0)
+ goto error;
+
+ do {
+ if (node == NULL) {
+ b2r2_log_warn("%s: "
+ "Internal error! Out of nodes!\n", __func__);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_ROTATION_ENABLED;
+
+ b2r2_log_debug("%s:\n"
+ "\tB2R2_TXY: %.8x\tB2R2_TSZ: %.8x\n"
+ "\tB2R2_S1XY: %.8x\tB2R2_S1SZ: %.8x\n"
+ "\tB2R2_S2XY: %.8x\tB2R2_S2SZ: %.8x\n"
+ "\tB2R2_S3XY: %.8x\tB2R2_S3SZ: %.8x\n"
+ "-----------------------------------\n",
+ __func__, node->node.GROUP1.B2R2_TXY,
+ node->node.GROUP1.B2R2_TSZ,
+ node->node.GROUP3.B2R2_SXY,
+ node->node.GROUP3.B2R2_SSZ,
+ node->node.GROUP4.B2R2_SXY,
+ node->node.GROUP4.B2R2_SSZ,
+ node->node.GROUP5.B2R2_SXY,
+ node->node.GROUP5.B2R2_SSZ);
+
+ node = node->next;
+
+ } while (node != last);
+
+ /* Consume the nodes */
+ *next = node;
+
+ return 0;
+error:
+ b2r2_log_warn("%s: error!\n", __func__);
+ return ret;
+}
+
+/**
+ * configure_scale() - configures the given node for scaling
+ *
+ * @node - the node to configure
+ * @src - the source buffer
+ * @dst - the destination buffer
+ * @h_rsf - the horizontal rescale factor
+ * @v_rsf - the vertical rescale factor
+ * @ivmx - the iVMX to use for color conversion
+ * @next - the next empty node in the node list
+ */
+static int configure_scale(struct b2r2_node *node,
+ struct b2r2_node_split_buf *src,
+ struct b2r2_node_split_buf *dst,
+ u16 h_rsf, u16 v_rsf,
+ const u32 *ivmx, struct b2r2_node **next,
+ struct b2r2_node_split_job *this)
+{
+ int ret;
+
+ struct b2r2_node *last;
+
+ struct b2r2_filter_spec *hf = NULL;
+ struct b2r2_filter_spec *vf = NULL;
+
+ u32 fctl = 0;
+ u32 rsf = 0;
+ u32 rzi = 0;
+ u32 hsrc_init = 0;
+ u32 vsrc_init = 0;
+ u32 hfp = 0;
+ u32 vfp = 0;
+
+ u16 luma_h_rsf = h_rsf;
+ u16 luma_v_rsf = v_rsf;
+
+ struct b2r2_filter_spec *luma_hf = NULL;
+ struct b2r2_filter_spec *luma_vf = NULL;
+
+ u32 luma_fctl = 0;
+ u32 luma_rsf = 0;
+ u32 luma_rzi = 0;
+ u32 luma_hsrc_init = 0;
+ u32 luma_vsrc_init = 0;
+ u32 luma_hfp = 0;
+ u32 luma_vfp = 0;
+
+ s32 src_x;
+ s32 src_y;
+ s32 src_w;
+ s32 src_h;
+
+ bool upsample;
+ bool downsample;
+
+ struct b2r2_blt_rect tmp_win = src->win;
+
+ /* Rescale the normalized source window */
+ src_x = inv_rescale(src->win.x - src->rect.x, luma_h_rsf);
+ src_y = inv_rescale(src->win.y - src->rect.y, luma_v_rsf);
+ src_w = inv_rescale(src->win.width, luma_h_rsf);
+ src_h = inv_rescale(src->win.height, luma_v_rsf);
+
+ /* Convert to src coordinate space */
+ src->win.x = (src_x >> 10) + src->rect.x;
+ src->win.y = (src_y >> 10) + src->rect.y;
+
+ /*
+ * Since the stripe might start and end on a fractional pixel
+ * we need to count all the touched pixels in the width.
+ *
+ * Example:
+ * src_x = 1.8, src_w = 2.8
+ *
+ * The stripe touches pixels 1.8 through 4.6, i.e. 4 pixels
+ */
+ src->win.width = ((src_x & 0x3ff) + src_w + 0x3ff) >> 10;
+ src->win.height = ((src_y & 0x3ff) + src_h + 0x3ff) >> 10;
+
+ luma_hsrc_init = src_x & 0x3ff;
+ luma_vsrc_init = src_y & 0x3ff;
+
+ /* Check for upsampling of chroma */
+ upsample = src->type != B2R2_FMT_TYPE_RASTER &&
+ !is_yuv444_fmt(src->fmt);
+ if (upsample) {
+ h_rsf /= 2;
+
+ if (is_yuv420_fmt(src->fmt))
+ v_rsf /= 2;
+ }
+
+ /* Check for downsampling of chroma */
+ downsample = dst->type != B2R2_FMT_TYPE_RASTER &&
+ !is_yuv444_fmt(dst->fmt);
+ if (downsample) {
+ h_rsf *= 2;
+
+ if (is_yuv420_fmt(dst->fmt))
+ v_rsf *= 2;
+ }
+
+ src_x = inv_rescale(tmp_win.x - src->rect.x, h_rsf);
+ src_y = inv_rescale(tmp_win.y - src->rect.y, v_rsf);
+ hsrc_init = src_x & 0x3ff;
+ vsrc_init = src_y & 0x3ff;
+
+ /* Configure resize and filters */
+ fctl = B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER;
+ luma_fctl = B2R2_FCTL_LUMA_HF2D_MODE_ENABLE_RESIZER |
+ B2R2_FCTL_LUMA_VF2D_MODE_ENABLE_RESIZER;
+
+ rsf = (h_rsf << B2R2_RSF_HSRC_INC_SHIFT) |
+ (v_rsf << B2R2_RSF_VSRC_INC_SHIFT);
+ luma_rsf = (luma_h_rsf << B2R2_RSF_HSRC_INC_SHIFT) |
+ (luma_v_rsf << B2R2_RSF_VSRC_INC_SHIFT);
+
+ rzi = B2R2_RZI_DEFAULT_HNB_REPEAT |
+ (2 << B2R2_RZI_VNB_REPEAT_SHIFT) |
+ (hsrc_init << B2R2_RZI_HSRC_INIT_SHIFT) |
+ (vsrc_init << B2R2_RZI_VSRC_INIT_SHIFT);
+ luma_rzi = B2R2_RZI_DEFAULT_HNB_REPEAT |
+ (2 << B2R2_RZI_VNB_REPEAT_SHIFT) |
+ (luma_hsrc_init << B2R2_RZI_HSRC_INIT_SHIFT) |
+ (luma_vsrc_init << B2R2_RZI_VSRC_INIT_SHIFT);
+
+ /*
+ * We should only filter if there is an actual rescale (i.e. not when
+ * up or downsampling).
+ */
+ if (luma_h_rsf != (1 << 10)) {
+ hf = b2r2_filter_find(h_rsf);
+ luma_hf = b2r2_filter_find(luma_h_rsf);
+ }
+ if (luma_v_rsf != (1 << 10)) {
+ vf = b2r2_filter_find(v_rsf);
+ luma_vf = b2r2_filter_find(luma_v_rsf);
+ }
+
+ if (hf) {
+ fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_COLOR_CHANNEL_FILTER;
+ hfp = hf->h_coeffs_phys_addr;
+ }
+
+ if (vf) {
+ fctl |= B2R2_FCTL_VF2D_MODE_ENABLE_COLOR_CHANNEL_FILTER;
+ vfp = vf->v_coeffs_phys_addr;
+ }
+
+ if (luma_hf) {
+ luma_fctl |= B2R2_FCTL_LUMA_HF2D_MODE_ENABLE_FILTER;
+ luma_hfp = luma_hf->h_coeffs_phys_addr;
+ }
+
+ if (luma_vf) {
+ luma_fctl |= B2R2_FCTL_LUMA_VF2D_MODE_ENABLE_FILTER;
+ luma_vfp = luma_vf->v_coeffs_phys_addr;
+ }
+
+ ret = configure_copy(node, src, dst, ivmx, &last, this);
+ if (ret < 0)
+ goto error;
+
+ do {
+ bool chroma_rescale =
+ (h_rsf != (1 << 10)) || (v_rsf != (1 << 10));
+ bool luma_rescale =
+ (luma_h_rsf != (1 << 10)) || (luma_v_rsf != (1 << 10));
+ bool src_raster = src->type == B2R2_FMT_TYPE_RASTER;
+ bool dst_raster = dst->type == B2R2_FMT_TYPE_RASTER;
+ bool dst_chroma =
+ node->node.GROUP1.B2R2_TTY & B2R2_TTY_CHROMA_NOT_LUMA;
+
+ if (node == NULL) {
+ b2r2_log_warn("%s: "
+ "Internal error! Out of nodes!\n", __func__);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_FILTER_CONTROL;
+
+ /*
+ * If the source format is anything other than raster, we
+ * always have to enable both chroma and luma resizers. This
+ * could be a bug in the hardware, since it is not mentioned in
+ * the specification.
+ *
+ * Otherwise, we will only enable the chroma resizer when
+ * writing chroma and the luma resizer when writing luma
+ * (or both when writing raster). Also, if there is no rescale
+ * to be done there's no point in using the resizers.
+ */
+
+ if (!src_raster || (chroma_rescale &&
+ (dst_raster || dst_chroma))) {
+ /* Enable chroma resize */
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_RESCALE2D_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_RESIZE_CHROMA;
+ node->node.GROUP8.B2R2_FCTL |= fctl;
+
+ node->node.GROUP9.B2R2_RSF = rsf;
+ node->node.GROUP9.B2R2_RZI = rzi;
+ node->node.GROUP9.B2R2_HFP = hfp;
+ node->node.GROUP9.B2R2_VFP = vfp;
+ }
+
+ if (!src_raster || (luma_rescale &&
+ (dst_raster || !dst_chroma))) {
+ /* Enable luma resize */
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_RESCALE2D_ENABLED;
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_RESIZE_LUMA;
+ node->node.GROUP8.B2R2_FCTL |= luma_fctl;
+
+ node->node.GROUP10.B2R2_RSF = luma_rsf;
+ node->node.GROUP10.B2R2_RZI = luma_rzi;
+ node->node.GROUP10.B2R2_HFP = luma_hfp;
+ node->node.GROUP10.B2R2_VFP = luma_vfp;
+ }
+
+ b2r2_log_info("%s:\n"
+ "\tB2R2_TXY: %.8x\tB2R2_TSZ: %.8x\n"
+ "\tB2R2_S1XY: %.8x\tB2R2_S1SZ: %.8x\n"
+ "\tB2R2_S2XY: %.8x\tB2R2_S2SZ: %.8x\n"
+ "\tB2R2_S3XY: %.8x\tB2R2_S3SZ: %.8x\n"
+ "----------------------------------\n",
+ __func__, node->node.GROUP1.B2R2_TXY,
+ node->node.GROUP1.B2R2_TSZ,
+ node->node.GROUP3.B2R2_SXY,
+ node->node.GROUP3.B2R2_SSZ,
+ node->node.GROUP4.B2R2_SXY,
+ node->node.GROUP4.B2R2_SSZ,
+ node->node.GROUP5.B2R2_SXY,
+ node->node.GROUP5.B2R2_SSZ);
+
+ node = node->next;
+
+ } while (node != last);
+
+
+
+ /* Consume the nodes */
+ *next = node;
+
+ return 0;
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+}
+
+/**
+ * configure_src() - configures the source registers and the iVMX
+ *
+ * @node - the node to configure
+ * @src - the source buffer
+ * @ivmx - the iVMX to use for color conversion
+ *
+ * This operation will not consume any nodes
+ */
+static void configure_src(struct b2r2_node *node,
+ struct b2r2_node_split_buf *src, const u32 *ivmx)
+{
+ struct b2r2_node_split_buf tmp_buf;
+
+ b2r2_log_info("%s: src.win=(%d, %d, %d, %d)\n", __func__,
+ src->win.x, src->win.y, src->win.width,
+ src->win.height);
+
+ /* Configure S1 - S3 */
+ switch (src->type) {
+ case B2R2_FMT_TYPE_RASTER:
+ set_src_2(node, src->addr, src);
+ break;
+ case B2R2_FMT_TYPE_SEMI_PLANAR:
+ memcpy(&tmp_buf, src, sizeof(tmp_buf));
+
+ /*
+ * For 420 and 422 the chroma has lower resolution than the
+ * luma
+ */
+ if (!is_yuv444_fmt(src->fmt)) {
+ tmp_buf.win.x >>= 1;
+ tmp_buf.win.width = (tmp_buf.win.width + 1) / 2;
+
+ if (is_yuv420_fmt(src->fmt)) {
+ tmp_buf.win.height =
+ (tmp_buf.win.height + 1) / 2;
+ tmp_buf.win.y >>= 1;
+ }
+ }
+
+ set_src_3(node, src->addr, src);
+ set_src_2(node, tmp_buf.chroma_addr, &tmp_buf);
+ break;
+ case B2R2_FMT_TYPE_PLANAR:
+ memcpy(&tmp_buf, src, sizeof(tmp_buf));
+
+ if (!is_yuv444_fmt(src->fmt)) {
+ /*
+ * Each chroma buffer will have half as many values
+ * per line as the luma buffer
+ */
+ tmp_buf.pitch = (tmp_buf.pitch + 1) / 2;
+
+ /* Horizontal resolution is half */
+ tmp_buf.win.x >>= 1;
+ tmp_buf.win.width = (tmp_buf.win.width + 1) / 2;
+
+ /*
+ * If the buffer is in YUV420 format, the vertical
+ * resolution is half as well
+ */
+ if (is_yuv420_fmt(src->fmt)) {
+ tmp_buf.win.height =
+ (tmp_buf.win.height + 1) / 2;
+ tmp_buf.win.y >>= 1;
+ }
+ }
+
+ set_src_3(node, src->addr, src); /* Y */
+ set_src_2(node, tmp_buf.chroma_addr, &tmp_buf); /* U */
+ set_src_1(node, tmp_buf.chroma_cr_addr, &tmp_buf); /* V */
+
+ break;
+ default:
+ /* Should never, ever happen */
+ BUG_ON(1);
+ break;
+ }
+
+ /* Configure the iVMX for color space conversions */
+ if (ivmx != NULL)
+ set_ivmx(node, ivmx);
+}
+
+/**
+ * configure_dst() - configures the destination registers of the given node
+ *
+ * @node - the node to configure
+ * @ivmx - the iVMX to use for color conversion
+ * @dst - the destination buffer
+ *
+ * This operation will consume as many nodes as are required to write the
+ * destination format.
+ */
+static int configure_dst(struct b2r2_node *node,
+ struct b2r2_node_split_buf *dst, const u32 *ivmx,
+ struct b2r2_node **next)
+{
+ int ret;
+ int nbr_planes = 1;
+ int i;
+
+ struct b2r2_node_split_buf dst_planes[3];
+
+ b2r2_log_info("%s: dst.win=(%d, %d, %d, %d)\n", __func__,
+ dst->win.x, dst->win.y, dst->win.width,
+ dst->win.height);
+
+ memcpy(&dst_planes[0], dst, sizeof(dst_planes[0]));
+
+ if (dst->type != B2R2_FMT_TYPE_RASTER) {
+ /* There will be at least 2 planes */
+ nbr_planes = 2;
+
+ memcpy(&dst_planes[1], dst, sizeof(dst_planes[1]));
+
+ dst_planes[1].addr = dst->chroma_addr;
+ dst_planes[1].plane_selection = B2R2_TTY_CHROMA_NOT_LUMA;
+
+ if (!is_yuv444_fmt(dst->fmt)) {
+ /* Horizontal resolution is half */
+ dst_planes[1].win.x /= 2;
+ /*
+ * Must round up the chroma size to handle cases when
+ * luma size is not divisible by 2. E.g. luma width==7 r
+ * equires chroma width==4. Chroma width==7/2==3 is only
+ * enough for luma width==6.
+ */
+ dst_planes[1].win.width =
+ (dst_planes[1].win.width + 1) / 2;
+
+ /*
+ * If the buffer is in YUV420 format, the vertical
+ * resolution is half as well. Height must be rounded in
+ * the same way as is done for width.
+ */
+ if (is_yuv420_fmt(dst->fmt)) {
+ dst_planes[1].win.y /= 2;
+ dst_planes[1].win.height =
+ (dst_planes[1].win.height + 1) / 2;
+ }
+ }
+
+ if (dst->type == B2R2_FMT_TYPE_PLANAR) {
+ /* There will be a third plane as well */
+ nbr_planes = 3;
+
+ if (!is_yuv444_fmt(dst->fmt)) {
+ /* The chroma planes have half the luma pitch */
+ dst_planes[1].pitch /= 2;
+ }
+
+ memcpy(&dst_planes[2], &dst_planes[1],
+ sizeof(dst_planes[2]));
+ dst_planes[2].addr = dst->chroma_cr_addr;
+
+ /*
+ * The third plane will be Cr.
+ * The flag B2R2_TTY_CB_NOT_CR actually works
+ * the other way around, i.e. as if it was
+ * B2R2_TTY_CR_NOT_CB.
+ */
+ dst_planes[2].chroma_selection = B2R2_TTY_CB_NOT_CR;
+ }
+
+ }
+
+ /* Configure one node for each plane */
+ for (i = 0; i < nbr_planes; i++) {
+
+ if (node == NULL) {
+ b2r2_log_warn("%s: "
+ "Internal error! Out of nodes!\n", __func__);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /*
+ * When writing chroma, there's no need to read the luma and
+ * vice versa.
+ */
+ if ((node->node.GROUP3.B2R2_STY & B2R2_NATIVE_YUV) &&
+ (nbr_planes > 1)) {
+ if (i != 0) {
+ node->node.GROUP4.B2R2_STY |=
+ B2R2_S3TY_ENABLE_BLANK_ACCESS;
+ }
+ if (i != 1) {
+ node->node.GROUP0.B2R2_INS &=
+ ~B2R2_INS_SOURCE_2_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_2_COLOR_FILL_REGISTER;
+ }
+ if (i != 2) {
+ node->node.GROUP0.B2R2_INS &=
+ ~B2R2_INS_SOURCE_1_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_INS |=
+ B2R2_INS_SOURCE_1_COLOR_FILL_REGISTER;
+ }
+ } else if ((node->node.GROUP3.B2R2_STY &
+ (B2R2_NATIVE_YCBCR42X_MBN |
+ B2R2_NATIVE_YCBCR42X_R2B)) &&
+ (nbr_planes > 1)) {
+ if (i != 0) {
+ node->node.GROUP4.B2R2_STY |=
+ B2R2_S3TY_ENABLE_BLANK_ACCESS;
+ }
+ }
+
+
+ set_target(node, dst_planes[i].addr, &dst_planes[i]);
+
+ node = node->next;
+ }
+
+ /* Consume the nodes */
+ *next = node;
+
+ return 0;
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+
+}
+
+/**
+ * configure_blend() - configures the given node for alpha blending
+ *
+ * @node - the node to configure
+ * @flags - the flags passed in the blt_request
+ * @global_alpha - the global alpha to use (if enabled in flags)
+ * @swap_fg_bg - if true, fg will be on s1 instead of s2
+ *
+ * This operation will not consume any nodes.
+ *
+ * NOTE: This method should be called _AFTER_ the destination has been
+ * configured.
+ *
+ * WARNING: Take care when using this with semi-planar or planar sources since
+ * either S1 or S2 will be overwritten!
+ */
+static void configure_blend(struct b2r2_node *node, u32 flags, u32 global_alpha,
+ bool swap_fg_bg)
+{
+ node->node.GROUP0.B2R2_ACK &= ~(B2R2_ACK_MODE_BYPASS_S2_S3);
+
+ /* Check if the foreground is premultiplied */
+ if ((flags & B2R2_BLT_FLAG_SRC_IS_NOT_PREMULT) != 0)
+ node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BLEND_NOT_PREMULT;
+ else
+ node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BLEND_PREMULT;
+
+
+ /* Check if global alpha blend should be enabled */
+ if ((flags & B2R2_BLT_FLAG_GLOBAL_ALPHA_BLEND) != 0) {
+
+ /* B2R2 expects the global alpha to be in 0...128 range */
+ global_alpha = (global_alpha*128)/255;
+
+ node->node.GROUP0.B2R2_ACK |=
+ global_alpha << B2R2_ACK_GALPHA_ROPID_SHIFT;
+ } else {
+ node->node.GROUP0.B2R2_ACK |=
+ (128 << B2R2_ACK_GALPHA_ROPID_SHIFT);
+ }
+
+ /* Copy the destination config to the appropriate source and clear any
+ clashing flags */
+ if (swap_fg_bg) {
+ /* S1 will be foreground, S2 background */
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_2;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_2_FETCH_FROM_MEM;
+ node->node.GROUP0.B2R2_ACK |= B2R2_ACK_SWAP_FG_BG;
+
+ node->node.GROUP4.B2R2_SBA = node->node.GROUP1.B2R2_TBA;
+ node->node.GROUP4.B2R2_STY = node->node.GROUP1.B2R2_TTY;
+ node->node.GROUP4.B2R2_SXY = node->node.GROUP1.B2R2_TXY;
+ node->node.GROUP4.B2R2_SSZ = node->node.GROUP1.B2R2_TSZ;
+
+ node->node.GROUP4.B2R2_STY &= ~(B2R2_S2TY_A1_SUBST_KEY_MODE |
+ B2R2_S2TY_CHROMA_LEFT_EXT_AVERAGE);
+ } else {
+ /* S1 will be background, S2 foreground */
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_1;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_1_FETCH_FROM_MEM;
+
+ node->node.GROUP3.B2R2_SBA = node->node.GROUP1.B2R2_TBA;
+ node->node.GROUP3.B2R2_STY |= node->node.GROUP1.B2R2_TTY;
+ node->node.GROUP3.B2R2_SXY = node->node.GROUP1.B2R2_TXY;
+
+ node->node.GROUP3.B2R2_STY &= ~(B2R2_S1TY_A1_SUBST_KEY_MODE);
+
+ }
+}
+
+/**
+ * configure_clip() - configures destination clipping for the given node
+ *
+ * @node - the node to configure
+ * @clip_rect - the clip rectangle
+ *
+ * This operation does not consume any nodes.
+ */
+static void configure_clip(struct b2r2_node *node,
+ struct b2r2_blt_rect *clip_rect)
+{
+ s32 l = clip_rect->x;
+ s32 r = clip_rect->x + clip_rect->width - 1;
+ s32 t = clip_rect->y;
+ s32 b = clip_rect->y + clip_rect->height - 1;
+
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_CLIP_WINDOW;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_RECT_CLIP_ENABLED;
+
+ /* Clip window setup */
+ node->node.GROUP6.B2R2_CWO =
+ ((t & 0x7FFF) << B2R2_CWO_Y_SHIFT) |
+ ((l & 0x7FFF) << B2R2_CWO_X_SHIFT);
+ node->node.GROUP6.B2R2_CWS =
+ ((b & 0x7FFF) << B2R2_CWO_Y_SHIFT) |
+ ((r & 0x7FFF) << B2R2_CWO_X_SHIFT);
+}
+
+/**
+ * set_buf() - configures the given buffer with the provided values
+ *
+ * @addr - the physical base address
+ * @img - the blt image to base the buffer on
+ * @rect - the rectangle to use
+ * @color_fill - determines whether the buffer should be used for color fill
+ * @color - the color to use in case of color fill
+ */
+static void set_buf(struct b2r2_node_split_buf *buf, u32 addr,
+ const struct b2r2_blt_img *img,
+ const struct b2r2_blt_rect *rect, bool color_fill, u32 color)
+{
+ memset(buf, 0, sizeof(*buf));
+
+ buf->fmt = img->fmt;
+ buf->type = get_fmt_type(img->fmt);
+
+ if (color_fill) {
+ buf->type = B2R2_FMT_TYPE_RASTER;
+ buf->color = color;
+ } else {
+ buf->addr = addr;
+
+ buf->alpha_range = get_alpha_range(img->fmt);
+
+ if (img->pitch == 0)
+ buf->pitch = fmt_byte_pitch(img->fmt, img->width);
+ else
+ buf->pitch = img->pitch;
+
+ buf->height = img->height;
+ buf->width = img->width;
+
+ switch (buf->type) {
+ case B2R2_FMT_TYPE_SEMI_PLANAR:
+ buf->chroma_addr = (u32)(((u8 *)addr) +
+ buf->pitch * buf->height);
+ break;
+ case B2R2_FMT_TYPE_PLANAR:
+ if (is_yuv422_fmt(buf->fmt) ||
+ is_yuv420_fmt(buf->fmt)) {
+ buf->chroma_addr = (u32)(((u8 *)addr) +
+ buf->pitch * buf->height);
+ } else {
+ buf->chroma_cr_addr = (u32)(((u8 *)addr) +
+ buf->pitch * buf->height);
+ }
+ if (is_yuv420_fmt(buf->fmt)) {
+ /*
+ * Use ceil(height/2) in case
+ * buffer height is not divisible by 2.
+ */
+ buf->chroma_cr_addr =
+ (u32)(((u8 *)buf->chroma_addr) +
+ (buf->pitch >> 1) *
+ ((buf->height + 1) >> 1));
+ } else if (is_yuv422_fmt(buf->fmt)) {
+ buf->chroma_cr_addr =
+ (u32)(((u8 *)buf->chroma_addr) +
+ (buf->pitch >> 1) * buf->height);
+ } else if (is_yvu420_fmt(buf->fmt)) {
+ buf->chroma_addr =
+ (u32)(((u8 *)buf->chroma_cr_addr) +
+ (buf->pitch >> 1) *
+ ((buf->height + 1) >> 1));
+ } else if (is_yvu422_fmt(buf->fmt)) {
+ buf->chroma_addr =
+ (u32)(((u8 *)buf->chroma_cr_addr) +
+ (buf->pitch >> 1) * buf->height);
+ }
+ break;
+ default:
+ break;
+ }
+
+ memcpy(&buf->rect, rect, sizeof(buf->rect));
+ }
+}
+
+/**
+ * setup_tmp_buf() - configure a temporary buffer
+ */
+static int setup_tmp_buf(struct b2r2_node_split_buf *tmp, u32 max_size,
+ enum b2r2_blt_fmt pref_fmt, u32 pref_width, u32 pref_height)
+{
+ int ret;
+
+ enum b2r2_blt_fmt fmt;
+
+ u32 width;
+ u32 height;
+ u32 pitch;
+ u32 size;
+
+ /* Determine what format we should use for the tmp buf */
+ if (is_rgb_fmt(pref_fmt)) {
+ fmt = B2R2_BLT_FMT_32_BIT_ARGB8888;
+ } else if (is_bgr_fmt(pref_fmt)) {
+ fmt = B2R2_BLT_FMT_32_BIT_ABGR8888;
+ } else if (is_yvu_fmt(pref_fmt)) {
+ fmt = B2R2_BLT_FMT_CB_Y_CR_Y;
+ } else if (is_yuv_fmt(pref_fmt)) {
+ fmt = B2R2_BLT_FMT_32_BIT_AYUV8888;
+ } else {
+ /* Wait, what? */
+ b2r2_log_warn("%s: "
+ "Cannot create tmp buf from this fmt (%d)\n", __func__,
+ pref_fmt);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* See if we can fit the entire preferred rectangle */
+ width = pref_width;
+ height = pref_height;
+ pitch = fmt_byte_pitch(fmt, width);
+ size = pitch * height;
+
+ if (size > max_size) {
+ /* We need to limit the size, so we choose a different width */
+ width = MIN(width, B2R2_RESCALE_MAX_WIDTH);
+ pitch = fmt_byte_pitch(fmt, width);
+ height = MIN(height, max_size / pitch);
+ size = pitch * height;
+ }
+
+ /* We should at least have enough room for one scanline */
+ if (height == 0) {
+ b2r2_log_warn("%s: Not enough tmp mem!\n",
+ __func__);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ memset(tmp, 0, sizeof(*tmp));
+
+ tmp->fmt = fmt;
+ tmp->type = B2R2_FMT_TYPE_RASTER;
+ tmp->height = height;
+ tmp->width = width;
+ tmp->pitch = pitch;
+
+ tmp->rect.width = width;
+ tmp->rect.height = tmp->height;
+ tmp->alpha_range = B2R2_TY_ALPHA_RANGE_255;
+
+ return 0;
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+
+}
+
+/**
+ * get_alpha_range() - returns the alpha range of the given format
+ */
+static enum b2r2_ty get_alpha_range(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ case B2R2_BLT_FMT_8_BIT_A8:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ return B2R2_TY_ALPHA_RANGE_255; /* 0 - 255 */
+ default:
+ return B2R2_TY_ALPHA_RANGE_128; /* 0 - 128 */
+ }
+}
+
+/**
+ * get_alpha() - returns the pixel alpha in 0...255 range
+ */
+static u8 get_alpha(enum b2r2_blt_fmt fmt, u32 pixel)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ return (pixel >> 24) & 0xff;
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ return (pixel & 0xfff) >> 16;
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ return (((pixel >> 12) & 0xf) * 255) / 15;
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ return (pixel >> 15) * 255;
+ case B2R2_BLT_FMT_1_BIT_A1:
+ return pixel * 255;
+ case B2R2_BLT_FMT_8_BIT_A8:
+ return pixel;
+ default:
+ return 255;
+ }
+}
+
+/**
+ * set_alpha() - returns a color value with the alpha component set
+ */
+static u32 set_alpha(enum b2r2_blt_fmt fmt, u8 alpha, u32 color)
+{
+ u32 alpha_mask;
+
+ switch (fmt) {
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ color &= 0x00ffffff;
+ alpha_mask = alpha << 24;
+ break;
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ color &= 0x00ffff;
+ alpha_mask = alpha << 16;
+ break;
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ color &= 0x0fff;
+ alpha_mask = (alpha << 8) & 0xF000;
+ break;
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ color &= 0x7fff;
+ alpha_mask = (alpha / 255) << 15 ;
+ break;
+ case B2R2_BLT_FMT_1_BIT_A1:
+ color = 0;
+ alpha_mask = (alpha / 255);
+ break;
+ case B2R2_BLT_FMT_8_BIT_A8:
+ color = 0;
+ alpha_mask = alpha;
+ break;
+ default:
+ alpha_mask = 0;
+ }
+
+ return color | alpha_mask;
+}
+
+/**
+ * fmt_has_alpha() - returns whether the given format carries an alpha value
+ */
+static bool fmt_has_alpha(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ case B2R2_BLT_FMT_1_BIT_A1:
+ case B2R2_BLT_FMT_8_BIT_A8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * is_rgb_fmt() - returns whether the given format is a rgb format
+ */
+static bool is_rgb_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_1_BIT_A1:
+ case B2R2_BLT_FMT_8_BIT_A8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * is_bgr_fmt() - returns whether the given format is a bgr format
+ */
+static bool is_bgr_fmt(enum b2r2_blt_fmt fmt)
+{
+ return (fmt == B2R2_BLT_FMT_32_BIT_ABGR8888);
+}
+
+/**
+ * is_yuv_fmt() - returns whether the given format is a yuv format
+ */
+static bool is_yuv_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * is_yvu_fmt() - returns whether the given format is a yvu format
+ */
+static bool is_yvu_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * is_yuv420_fmt() - returns whether the given format is a yuv420 format
+ */
+static bool is_yuv420_fmt(enum b2r2_blt_fmt fmt)
+{
+
+ switch (fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool is_yuv422_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * is_yvu420_fmt() - returns whether the given format is a yvu420 format
+ */
+static bool is_yvu420_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool is_yvu422_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+/**
+ * is_yuv444_fmt() - returns whether the given format is a yuv444 format
+ */
+static bool is_yuv444_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * get_fmt_byte_pitch() - returns the pitch of a pixmap with the given width
+ */
+static int fmt_byte_pitch(enum b2r2_blt_fmt fmt, u32 width)
+{
+ int pitch;
+
+ switch (fmt) {
+
+ case B2R2_BLT_FMT_1_BIT_A1:
+ pitch = width >> 3; /* Shift is faster than division */
+ if ((width & 0x3) != 0) /* Check for remainder */
+ pitch++;
+ return pitch;
+
+ case B2R2_BLT_FMT_8_BIT_A8: /* Fall through */
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR: /* Fall through */
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR: /* Fall through */
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE: /* Fall through */
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR: /* Fall through */
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR: /* Fall through */
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR: /* Fall through */
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR: /* Fall through */
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE: /* Fall through */
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR: /* Fall through */
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR: /* Fall through */
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ return width;
+
+ case B2R2_BLT_FMT_16_BIT_ARGB4444: /* Fall through */
+ case B2R2_BLT_FMT_16_BIT_ARGB1555: /* Fall through */
+ case B2R2_BLT_FMT_16_BIT_RGB565: /* Fall through */
+ case B2R2_BLT_FMT_Y_CB_Y_CR: /* Fall through */
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ return width << 1;
+
+ case B2R2_BLT_FMT_24_BIT_RGB888: /* Fall through */
+ case B2R2_BLT_FMT_24_BIT_ARGB8565: /* Fall through */
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ return width * 3;
+
+ case B2R2_BLT_FMT_32_BIT_ARGB8888: /* Fall through */
+ case B2R2_BLT_FMT_32_BIT_ABGR8888: /* Fall through */
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ return width << 2;
+
+ default:
+ /* Should never, ever happen */
+ BUG_ON(1);
+ return 0;
+ }
+}
+
+/**
+ * to_native_fmt() - returns the native B2R2 format
+ */
+static enum b2r2_native_fmt to_native_fmt(enum b2r2_blt_fmt fmt)
+{
+
+ switch (fmt) {
+ case B2R2_BLT_FMT_UNUSED:
+ return B2R2_NATIVE_RGB565;
+ case B2R2_BLT_FMT_1_BIT_A1:
+ return B2R2_NATIVE_A1;
+ case B2R2_BLT_FMT_8_BIT_A8:
+ return B2R2_NATIVE_A8;
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ return B2R2_NATIVE_RGB565;
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ return B2R2_NATIVE_ARGB4444;
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ return B2R2_NATIVE_ARGB1555;
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ return B2R2_NATIVE_ARGB8565;
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ return B2R2_NATIVE_RGB888;
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ return B2R2_NATIVE_YCBCR888;
+ case B2R2_BLT_FMT_32_BIT_ABGR8888: /* Not actually supported by HW */
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ return B2R2_NATIVE_ARGB8888;
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ return B2R2_NATIVE_AYCBCR8888;
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ return B2R2_NATIVE_YCBCR422R;
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ return B2R2_NATIVE_YCBCR422R;
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ return B2R2_NATIVE_YCBCR42X_R2B;
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ return B2R2_NATIVE_YCBCR42X_MBN;
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ return B2R2_NATIVE_YUV;
+ default:
+ /* Should never ever happen */
+ return B2R2_NATIVE_BYTE;
+ }
+}
+
+/**
+ * Bit-expand the color from fmt to RGB888 with blue at LSB.
+ * Copy MSBs into missing LSBs.
+ */
+static u32 to_RGB888(u32 color, const enum b2r2_blt_fmt fmt)
+{
+ u32 out_color = 0;
+ u32 r = 0;
+ u32 g = 0;
+ u32 b = 0;
+ switch (fmt) {
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ r = ((color & 0xf00) << 12) | ((color & 0xf00) << 8);
+ g = ((color & 0xf0) << 8) | ((color & 0xf0) << 4);
+ b = ((color & 0xf) << 4) | (color & 0xf);
+ out_color = r | g | b;
+ break;
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ r = ((color & 0x7c00) << 9) | ((color & 0x7000) << 4);
+ g = ((color & 0x3e0) << 6) | ((color & 0x380) << 1);
+ b = ((color & 0x1f) << 3) | ((color & 0x1c) >> 2);
+ out_color = r | g | b;
+ break;
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ r = ((color & 0xf800) << 8) | ((color & 0xe000) << 3);
+ g = ((color & 0x7e0) << 5) | ((color & 0x600) >> 1);
+ b = ((color & 0x1f) << 3) | ((color & 0x1c) >> 2);
+ out_color = r | g | b;
+ break;
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ out_color = color & 0xffffff;
+ break;
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ r = (color & 0xff) << 16;
+ g = color & 0xff00;
+ b = (color & 0xff0000) >> 16;
+ out_color = r | g | b;
+ break;
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ r = ((color & 0xf800) << 8) | ((color & 0xe000) << 3);
+ g = ((color & 0x7e0) << 5) | ((color & 0x600) >> 1);
+ b = ((color & 0x1f) << 3) | ((color & 0x1c) >> 2);
+ out_color = r | g | b;
+ break;
+ default:
+ break;
+ }
+
+ return out_color;
+}
+
+/**
+ * get_fmt_type() - returns the type of the given format (raster, planar, etc.)
+ */
+static enum b2r2_fmt_type get_fmt_type(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ case B2R2_BLT_FMT_1_BIT_A1:
+ case B2R2_BLT_FMT_8_BIT_A8:
+ return B2R2_FMT_TYPE_RASTER;
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ return B2R2_FMT_TYPE_PLANAR;
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ return B2R2_FMT_TYPE_SEMI_PLANAR;
+ default:
+ return B2R2_FMT_TYPE_RASTER;
+ }
+}
+
+/**
+ * is_transform() - returns whether the given request is a transform operation
+ */
+static bool is_transform(const struct b2r2_blt_request *req)
+{
+ return (req->user_req.transform != B2R2_BLT_TRANSFORM_NONE) ||
+ (req->user_req.src_rect.width !=
+ req->user_req.dst_rect.width) ||
+ (req->user_req.src_rect.height !=
+ req->user_req.dst_rect.height);
+}
+
+/**
+ * calculate_scale_factor() - calculates the scale factor between the given
+ * values
+ */
+static int calculate_scale_factor(u32 from, u32 to, u16 *sf_out)
+{
+ int ret;
+ u32 sf;
+
+ b2r2_log_info("%s\n", __func__);
+
+ if (to == from) {
+ *sf_out = 1 << 10;
+ return 0;
+ } else if (to == 0) {
+ b2r2_log_err("%s: To is 0!\n", __func__);
+ BUG_ON(1);
+ }
+
+ sf = (from << 10) / to;
+
+ if ((sf & 0xffff0000) != 0) {
+ /* Overflow error */
+ b2r2_log_warn("%s: "
+ "Scale factor too large\n", __func__);
+ ret = -EINVAL;
+ goto error;
+ } else if (sf == 0) {
+ b2r2_log_warn("%s: "
+ "Scale factor too small\n", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ *sf_out = (u16)sf;
+
+ b2r2_log_info("%s exit\n", __func__);
+
+ return 0;
+
+error:
+ b2r2_log_warn("%s: Exit...\n", __func__);
+ return ret;
+}
+
+/**
+ * rescale() - rescales the given dimension
+ *
+ * Returns the rescaled dimension in 22.10 fixed point format.
+ */
+static s32 rescale(s32 dim, u16 sf)
+{
+ b2r2_log_info("%s\n", __func__);
+
+ if (sf == 0) {
+ b2r2_log_err("%s: Scale factor is 0!\n", __func__);
+ BUG_ON(1);
+ }
+
+ /*
+ * This is normally not safe to do, since it drastically decreases the
+ * precision of the integer part of the dimension. But since the B2R2
+ * hardware only has 12-bit registers for these values, we are safe.
+ */
+ return (dim << 20) / sf;
+}
+
+/**
+ * inv_rescale() - does an inverted rescale of the given dimension
+ *
+ * Returns the rescaled dimension in 22.10 fixed point format.
+ */
+static s32 inv_rescale(s32 dim, u16 sf)
+{
+ if (sf == 0)
+ return dim;
+
+ return dim * sf;
+}
+
+/**
+ * set_target() - sets the target registers of the given node
+ */
+static void set_target(struct b2r2_node *node, u32 addr,
+ struct b2r2_node_split_buf *buf)
+{
+ s32 l;
+ s32 r;
+ s32 t;
+ s32 b;
+
+ if (buf->tmp_buf_index)
+ node->dst_tmp_index = buf->tmp_buf_index;
+
+ node->node.GROUP1.B2R2_TBA = addr;
+ node->node.GROUP1.B2R2_TTY = buf->pitch | to_native_fmt(buf->fmt) |
+ buf->alpha_range | buf->chroma_selection | buf->hso |
+ buf->vso | buf->dither | buf->plane_selection;
+ node->node.GROUP1.B2R2_TSZ =
+ ((buf->win.width & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((buf->win.height & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+ node->node.GROUP1.B2R2_TXY =
+ ((buf->win.x & 0xffff) << B2R2_XY_X_SHIFT) |
+ ((buf->win.y & 0xffff) << B2R2_XY_Y_SHIFT);
+
+ /* Check if the rectangle is outside the buffer */
+ if (buf->vso == B2R2_TY_VSO_BOTTOM_TO_TOP)
+ t = buf->win.y - (buf->win.height - 1);
+ else
+ t = buf->win.y;
+
+ if (buf->hso == B2R2_TY_HSO_RIGHT_TO_LEFT)
+ l = buf->win.x - (buf->win.width - 1);
+ else
+ l = buf->win.x;
+
+ r = l + buf->win.width;
+ b = t + buf->win.height;
+
+ /* Clip to the destination buffer to prevent memory overwrites */
+ if ((l < 0) || (r > buf->width) || (t < 0) || (b > buf->height)) {
+ /* The clip rectangle is including the borders */
+ l = MAX(l, 0);
+ r = MIN(r, buf->width) - 1;
+ t = MAX(t, 0);
+ b = MIN(b, buf->height) - 1;
+
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_CLIP_WINDOW;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_RECT_CLIP_ENABLED;
+ node->node.GROUP6.B2R2_CWO =
+ ((l & 0x7FFF) << B2R2_CWS_X_SHIFT) |
+ ((t & 0x7FFF) << B2R2_CWS_Y_SHIFT);
+ node->node.GROUP6.B2R2_CWS =
+ ((r & 0x7FFF) << B2R2_CWO_X_SHIFT) |
+ ((b & 0x7FFF) << B2R2_CWO_Y_SHIFT);
+ }
+
+}
+
+/**
+ * set_src() - configures the given source register with the given values
+ */
+static void set_src(struct b2r2_src_config *src, u32 addr,
+ struct b2r2_node_split_buf *buf)
+{
+ src->B2R2_SBA = addr;
+ src->B2R2_STY = buf->pitch | to_native_fmt(buf->fmt) |
+ buf->alpha_range | buf->hso | buf->vso;
+ src->B2R2_SSZ = ((buf->win.width & 0xfff) << B2R2_SZ_WIDTH_SHIFT) |
+ ((buf->win.height & 0xfff) << B2R2_SZ_HEIGHT_SHIFT);
+ src->B2R2_SXY = ((buf->win.x & 0xffff) << B2R2_XY_X_SHIFT) |
+ ((buf->win.y & 0xffff) << B2R2_XY_Y_SHIFT);
+
+}
+
+/**
+ * set_src_1() - sets the source 1 registers of the given node
+ */
+static void set_src_1(struct b2r2_node *node, u32 addr,
+ struct b2r2_node_split_buf *buf)
+{
+ if (buf->tmp_buf_index)
+ node->src_tmp_index = buf->tmp_buf_index;
+
+ node->src_index = 1;
+
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_1;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_1_FETCH_FROM_MEM;
+
+ node->node.GROUP3.B2R2_SBA = addr;
+ node->node.GROUP3.B2R2_STY = buf->pitch | to_native_fmt(buf->fmt) |
+ buf->alpha_range | buf->hso | buf->vso;
+ node->node.GROUP3.B2R2_SXY =
+ ((buf->win.x & 0xffff) << B2R2_XY_X_SHIFT) |
+ ((buf->win.y & 0xffff) << B2R2_XY_Y_SHIFT);
+
+ /* Source 1 has no size register */
+}
+
+/**
+ * set_src_2() - sets the source 2 registers of the given node
+ */
+static void set_src_2(struct b2r2_node *node, u32 addr,
+ struct b2r2_node_split_buf *buf)
+{
+ if (buf->tmp_buf_index)
+ node->src_tmp_index = buf->tmp_buf_index;
+
+ node->src_index = 2;
+
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_2;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_2_FETCH_FROM_MEM;
+
+ set_src(&node->node.GROUP4, addr, buf);
+}
+
+/**
+ * set_src_3() - sets the source 3 registers of the given node
+ */
+static void set_src_3(struct b2r2_node *node, u32 addr,
+ struct b2r2_node_split_buf *buf)
+{
+ if (buf->tmp_buf_index)
+ node->src_tmp_index = buf->tmp_buf_index;
+
+ node->src_index = 3;
+
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_3;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_3_FETCH_FROM_MEM;
+
+ set_src(&node->node.GROUP5, addr, buf);
+}
+
+/**
+ * set_ivmx() - configures the iVMX registers with the given values
+ */
+static void set_ivmx(struct b2r2_node *node, const u32 *vmx_values)
+{
+ node->node.GROUP0.B2R2_CIC |= B2R2_CIC_IVMX;
+ node->node.GROUP0.B2R2_INS |= B2R2_INS_IVMX_ENABLED;
+
+ node->node.GROUP15.B2R2_VMX0 = vmx_values[0];
+ node->node.GROUP15.B2R2_VMX1 = vmx_values[1];
+ node->node.GROUP15.B2R2_VMX2 = vmx_values[2];
+ node->node.GROUP15.B2R2_VMX3 = vmx_values[3];
+}
+
+/**
+ * reset_nodes() - clears the node list
+ */
+static void reset_nodes(struct b2r2_node *node)
+{
+ while (node != NULL) {
+ memset(&node->node, 0, sizeof(node->node));
+
+ node->src_tmp_index = 0;
+ node->dst_tmp_index = 0;
+
+ /* TODO: Implement support for short linked lists */
+ node->node.GROUP0.B2R2_CIC = 0x7ffff;
+
+ if (node->next != NULL)
+ node->node.GROUP0.B2R2_NIP =
+ node->next->physical_address;
+ node = node->next;
+ }
+}
+
+int b2r2_node_split_init(void)
+{
+ b2r2_filters_init();
+
+ return 0;
+}
+
+void b2r2_node_split_exit(void)
+{
+ b2r2_filters_exit();
+}
diff --git a/drivers/video/b2r2/b2r2_node_split.h b/drivers/video/b2r2/b2r2_node_split.h
new file mode 100644
index 00000000000..5bceac28488
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_node_split.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 node splitter
+ *
+ * Author: Fredrik Allansson <fredrik.allansson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef __B2R2_NODE_SPLIT_H_
+#define __B2R2_NODE_SPLIT_H_
+
+#include "b2r2_internal.h"
+#include "b2r2_hw.h"
+
+/**
+ * b2r2_node_split_analyze() - Analyzes a B2R2 request
+ *
+ * @req - The request to analyze
+ * @max_buf_size - The largest size allowed for intermediate buffers
+ * @node_count - Number of nodes required for the job
+ * @buf_count - Number of intermediate buffers required for the job
+ * @bufs - An array of buffers needed for intermediate buffers
+ *
+ * Analyzes the request and determines how many nodes and intermediate buffers
+ * are required.
+ *
+ * It is the responsibility of the caller to allocate memory and assign the
+ * physical addresses. After that b2r2_node_split_assign_buffers should be
+ * called to assign the buffers to the right nodes.
+ *
+ * Returns:
+ * A handle identifing the analyzed request if successful, a negative
+ * value otherwise.
+ */
+int b2r2_node_split_analyze(const struct b2r2_blt_request *req, u32 max_buf_size,
+ u32 *node_count, struct b2r2_work_buf **bufs, u32* buf_count,
+ struct b2r2_node_split_job *job);
+
+/**
+ * b2r2_node_split_configure() - Performs a node split
+ *
+ * @handle - A handle for the analyzed request
+ * @first - The first node in the list of nodes to use
+ *
+ * Fills the supplied list of nodes with the parameters acquired by analyzing
+ * the request.
+ *
+ * All pointers to intermediate buffers are represented by integers to be used
+ * in the array returned by b2r2_node_split_analyze.
+ *
+ * Returns:
+ * A negative value if an error occurred, 0 otherwise.
+ */
+int b2r2_node_split_configure(struct b2r2_node_split_job *job,
+ struct b2r2_node *first);
+
+/**
+ * b2r2_node_split_assign_buffers() - Assignes physical addresses
+ *
+ * @handle - The handle for the job
+ * @first - The first node in the node list
+ * @bufs - Buffers with assigned physical addresses
+ * @buf_count - Number of physical addresses
+ *
+ * Assigns the physical addresses where intermediate buffers are required in
+ * the node list.
+ *
+ * The order of the elements of 'bufs' must be maintained from the call to
+ * b2r2_node_split_analyze.
+ *
+ * Returns:
+ * A negative value if an error occurred, 0 otherwise.
+ */
+int b2r2_node_split_assign_buffers(struct b2r2_node_split_job *job,
+ struct b2r2_node *first, struct b2r2_work_buf *bufs,
+ u32 buf_count);
+
+/**
+ * b2r2_node_split_unassign_buffers() - Removes all physical addresses
+ *
+ * @handle - The handle associated with the job
+ * @first - The first node in the node list
+ *
+ * Removes all references to intermediate buffers from the node list.
+ *
+ * This makes it possible to reuse the node list with new buffers by calling
+ * b2r2_node_split_assign_buffers again. Useful for caching node lists.
+ */
+void b2r2_node_split_unassign_buffers(struct b2r2_node_split_job *job,
+ struct b2r2_node *first);
+
+/**
+ * b2r2_node_split_release() - Releases all resources for a job
+ *
+ * @handle - The handle identifying the job. This will be set to 0.
+ *
+ * Releases all resources associated with a job.
+ *
+ * This should always be called once b2r2_node_split_analyze has been called
+ * in order to release any resources allocated while analyzing.
+ */
+void b2r2_node_split_cancel(struct b2r2_node_split_job *job);
+
+/**
+ * b2r2_node_split_init() - Initializes the node split module
+ *
+ * Initializes the node split module and creates debugfs files.
+ */
+int b2r2_node_split_init(void);
+
+/**
+ * b2r2_node_split_exit() - Deinitializes the node split module
+ *
+ * Releases all resources for the node split module.
+ */
+void b2r2_node_split_exit(void);
+
+#endif
diff --git a/drivers/video/b2r2/b2r2_profiler/Makefile b/drivers/video/b2r2/b2r2_profiler/Makefile
new file mode 100644
index 00000000000..69a85524fd7
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_profiler/Makefile
@@ -0,0 +1,3 @@
+# Make file for loadable module B2R2 Profiler
+
+obj-$(CONFIG_B2R2_PROFILER) += b2r2_profiler.o
diff --git a/drivers/video/b2r2/b2r2_profiler/b2r2_profiler.c b/drivers/video/b2r2/b2r2_profiler/b2r2_profiler.c
new file mode 100644
index 00000000000..2e7c5ca5a7a
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_profiler/b2r2_profiler.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * ST-Ericsson B2R2 profiler implementation
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+
+#include <video/b2r2_blt.h>
+#include "../b2r2_profiler_api.h"
+
+
+#define S32_MAX 2147483647
+
+
+static int src_format_filter_on = false;
+module_param(src_format_filter_on, bool, S_IRUGO | S_IWUSR);
+static unsigned int src_format_filter;
+module_param(src_format_filter, uint, S_IRUGO | S_IWUSR);
+
+static int print_blts_on = 0;
+module_param(print_blts_on, bool, S_IRUGO | S_IWUSR);
+static int use_mpix_per_second_in_print_blts = 1;
+module_param(use_mpix_per_second_in_print_blts, bool, S_IRUGO | S_IWUSR);
+
+static int min_avg_max_mpix_per_second_on = 1;
+module_param(min_avg_max_mpix_per_second_on, bool, S_IRUGO | S_IWUSR);
+
+static const unsigned int min_avg_max_mpix_per_second_num_blts_used = 400;
+static struct {
+ unsigned long sampling_start_time_jiffies;
+
+ s32 min_mpix_per_second;
+ struct b2r2_blt_req min_blt_request;
+ struct b2r2_blt_profiling_info min_blt_profiling_info;
+
+ s32 max_mpix_per_second;
+ struct b2r2_blt_req max_blt_request;
+ struct b2r2_blt_profiling_info max_blt_profiling_info;
+
+ s32 accumulated_num_pixels;
+ s32 accumulated_num_usecs;
+
+ u32 num_blts_done;
+} min_avg_max_mpix_per_second_state;
+
+
+static s32 nsec_2_usec(const s32 nsec);
+
+static int is_scale_blt(const struct b2r2_blt_req * const request);
+static s32 get_blt_mpix_per_second(const struct b2r2_blt_req * const request, const struct b2r2_blt_profiling_info * const blt_profiling_info);
+static void print_blt(const struct b2r2_blt_req * const request, const struct b2r2_blt_profiling_info * const blt_profiling_info);
+
+static s32 get_num_pixels_in_blt(const struct b2r2_blt_req * const request);
+static s32 get_mpix_per_second(const s32 num_pixels, const s32 num_usecs);
+static void print_min_avg_max_mpix_per_second_state(void);
+static void reset_min_avg_max_mpix_per_second_state(void);
+static void do_min_avg_max_mpix_per_second(const struct b2r2_blt_req * const request, const struct b2r2_blt_profiling_info * const blt_profiling_info);
+
+static void blt_done(const struct b2r2_blt_req * const blt, const s32 request_id, const struct b2r2_blt_profiling_info * const blt_profiling_info);
+
+
+static struct b2r2_profiler this = {
+ .blt_done = blt_done,
+};
+
+
+static s32 nsec_2_usec(const s32 nsec)
+{
+ return nsec / 1000;
+}
+
+
+static int is_scale_blt(const struct b2r2_blt_req * const request)
+{
+ if ((request->transform & B2R2_BLT_TRANSFORM_CCW_ROT_90 &&
+ (request->src_rect.width != request->dst_rect.height ||
+ request->src_rect.height != request->dst_rect.width)) ||
+ (!(request->transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) &&
+ (request->src_rect.width != request->dst_rect.width ||
+ request->src_rect.height != request->dst_rect.height)))
+ return 1;
+ else
+ return 0;
+}
+
+static s32 get_blt_mpix_per_second(const struct b2r2_blt_req * const request, const struct b2r2_blt_profiling_info * const blt_profiling_info)
+{
+ return get_mpix_per_second(get_num_pixels_in_blt(request),
+ nsec_2_usec(blt_profiling_info->nsec_active_in_cpu + blt_profiling_info->nsec_active_in_b2r2));
+}
+
+static void print_blt(const struct b2r2_blt_req * const request, const struct b2r2_blt_profiling_info * const blt_profiling_info)
+{
+ char tmp_str[128];
+ sprintf(tmp_str, "SF: %#10x, DF: %#10x, F: %#10x, T: %#3x, S: %1i, P: %7i",
+ request->src_img.fmt,
+ request->dst_img.fmt,
+ request->flags,
+ request->transform,
+ is_scale_blt(request),
+ get_num_pixels_in_blt(request));
+ if (use_mpix_per_second_in_print_blts)
+ printk(KERN_ALERT "%s, MPix/s: %3i\n",
+ tmp_str,
+ get_blt_mpix_per_second(request, blt_profiling_info));
+ else
+ printk(KERN_ALERT "%s, CPU: %10i, B2R2: %10i, Tot: %10i ns\n",
+ tmp_str,
+ blt_profiling_info->nsec_active_in_cpu,
+ blt_profiling_info->nsec_active_in_b2r2,
+ blt_profiling_info->total_time_nsec);
+}
+
+
+static s32 get_num_pixels_in_blt(const struct b2r2_blt_req * const request)
+{
+ s32 num_pixels_in_src = request->src_rect.width * request->src_rect.height;
+ s32 num_pixels_in_dst = request->dst_rect.width * request->dst_rect.height;
+ if (request->flags & (B2R2_BLT_FLAG_SOURCE_FILL |
+ B2R2_BLT_FLAG_SOURCE_FILL_RAW))
+ return num_pixels_in_dst;
+ else
+ return (num_pixels_in_src + num_pixels_in_dst) / 2;
+}
+
+static s32 get_mpix_per_second(const s32 num_pixels, const s32 num_usecs)
+{
+ s32 num_pixels_scale_factor = num_pixels != 0 ? S32_MAX / num_pixels : S32_MAX;
+ s32 num_usecs_scale_factor = num_usecs != 0 ? S32_MAX / num_usecs : S32_MAX;
+ s32 scale_factor = min(num_pixels_scale_factor, num_usecs_scale_factor);
+
+ s32 num_pixels_scaled = num_pixels * scale_factor;
+ s32 num_usecs_scaled = num_usecs * scale_factor;
+
+ if (num_usecs_scaled < 1000000)
+ return 0;
+
+ return (num_pixels_scaled / 1000000) / (num_usecs_scaled / 1000000);
+}
+
+static void print_min_avg_max_mpix_per_second_state(void)
+{
+ printk(KERN_ALERT "Min: %3i, Avg: %3i, Max: %3i MPix/s\n",
+ min_avg_max_mpix_per_second_state.min_mpix_per_second,
+ get_mpix_per_second(min_avg_max_mpix_per_second_state.accumulated_num_pixels,
+ min_avg_max_mpix_per_second_state.accumulated_num_usecs),
+ min_avg_max_mpix_per_second_state.max_mpix_per_second);
+ printk(KERN_ALERT "Min blit:\n");
+ print_blt(&min_avg_max_mpix_per_second_state.min_blt_request,
+ &min_avg_max_mpix_per_second_state.min_blt_profiling_info);
+ printk(KERN_ALERT "Max blit:\n");
+ print_blt(&min_avg_max_mpix_per_second_state.max_blt_request,
+ &min_avg_max_mpix_per_second_state.max_blt_profiling_info);
+}
+
+static void reset_min_avg_max_mpix_per_second_state(void)
+{
+ min_avg_max_mpix_per_second_state.sampling_start_time_jiffies =
+ jiffies;
+ min_avg_max_mpix_per_second_state.min_mpix_per_second = S32_MAX;
+ min_avg_max_mpix_per_second_state.max_mpix_per_second = 0;
+ min_avg_max_mpix_per_second_state.accumulated_num_pixels = 0;
+ min_avg_max_mpix_per_second_state.accumulated_num_usecs = 0;
+ min_avg_max_mpix_per_second_state.num_blts_done = 0;
+}
+
+static void do_min_avg_max_mpix_per_second(const struct b2r2_blt_req * const request, const struct b2r2_blt_profiling_info * const blt_profiling_info)
+{
+ s32 num_pixels_in_blt;
+ s32 num_usec_blt_took;
+ s32 blt_mpix_per_second;
+
+ if (time_before(jiffies, min_avg_max_mpix_per_second_state.sampling_start_time_jiffies))
+ return;
+
+ num_pixels_in_blt = get_num_pixels_in_blt(request);
+ num_usec_blt_took = nsec_2_usec(blt_profiling_info->nsec_active_in_cpu + blt_profiling_info->nsec_active_in_b2r2);
+ blt_mpix_per_second = get_mpix_per_second(num_pixels_in_blt,
+ num_usec_blt_took);
+
+ if (blt_mpix_per_second <= min_avg_max_mpix_per_second_state.min_mpix_per_second) {
+ min_avg_max_mpix_per_second_state.min_mpix_per_second =
+ blt_mpix_per_second;
+ memcpy(&min_avg_max_mpix_per_second_state.min_blt_request,
+ request, sizeof(struct b2r2_blt_req));
+ memcpy(&min_avg_max_mpix_per_second_state.min_blt_profiling_info,
+ blt_profiling_info, sizeof(struct b2r2_blt_profiling_info));
+ }
+
+ if (blt_mpix_per_second >= min_avg_max_mpix_per_second_state.max_mpix_per_second) {
+ min_avg_max_mpix_per_second_state.max_mpix_per_second =
+ blt_mpix_per_second;
+ memcpy(&min_avg_max_mpix_per_second_state.max_blt_request,
+ request, sizeof(struct b2r2_blt_req));
+ memcpy(&min_avg_max_mpix_per_second_state.max_blt_profiling_info,
+ blt_profiling_info, sizeof(struct b2r2_blt_profiling_info));
+ }
+
+ min_avg_max_mpix_per_second_state.accumulated_num_pixels +=
+ num_pixels_in_blt;
+ min_avg_max_mpix_per_second_state.accumulated_num_usecs +=
+ num_usec_blt_took;
+
+ min_avg_max_mpix_per_second_state.num_blts_done++;
+
+ if (min_avg_max_mpix_per_second_state.num_blts_done >= min_avg_max_mpix_per_second_num_blts_used) {
+ print_min_avg_max_mpix_per_second_state();
+ reset_min_avg_max_mpix_per_second_state();
+ /* The printouts initiated above can disturb the next measurement
+ so we delay it two seconds to give the printouts a chance to finish. */
+ min_avg_max_mpix_per_second_state.sampling_start_time_jiffies =
+ jiffies + (2 * HZ);
+ }
+}
+
+static void blt_done(const struct b2r2_blt_req * const request, const s32 request_id, const struct b2r2_blt_profiling_info * const blt_profiling_info)
+{
+ /* Filters */
+ if (src_format_filter_on && request->src_img.fmt != src_format_filter)
+ return;
+
+ /* Processors */
+ if (print_blts_on)
+ print_blt(request, blt_profiling_info);
+
+ if (min_avg_max_mpix_per_second_on)
+ do_min_avg_max_mpix_per_second(request, blt_profiling_info);
+}
+
+
+static int __init b2r2_profiler_init(void)
+{
+ reset_min_avg_max_mpix_per_second_state();
+
+ return b2r2_register_profiler(&this);
+}
+module_init(b2r2_profiler_init);
+
+static void __exit b2r2_profiler_exit(void)
+{
+ b2r2_unregister_profiler(&this);
+}
+module_exit(b2r2_profiler_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Johan Mossberg (johan.xx.mossberg@stericsson.com)");
+MODULE_DESCRIPTION("B2R2 Profiler");
diff --git a/drivers/video/b2r2/b2r2_profiler_api.h b/drivers/video/b2r2/b2r2_profiler_api.h
new file mode 100644
index 00000000000..5f1f9abbe49
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_profiler_api.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 profiling API
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+
+#ifndef _LINUX_VIDEO_B2R2_PROFILER_API_H
+#define _LINUX_VIDEO_B2R2_PROFILER_API_H
+
+#include <video/b2r2_blt.h>
+
+/**
+ * struct b2r2_blt_profiling_info - Profiling information for a blit
+ *
+ * @nsec_active_in_cpu: The number of nanoseconds the job was active in the CPU.
+ * This is an approximate value, check out the code for more
+ * info.
+ * @nsec_active_in_b2r2: The number of nanoseconds the job was active in B2R2. This
+ * is an approximate value, check out the code for more info.
+ * @total_time_nsec: The total time the job took in nano seconds. Includes ideling.
+ */
+struct b2r2_blt_profiling_info {
+ s32 nsec_active_in_cpu;
+ s32 nsec_active_in_b2r2;
+ s32 total_time_nsec;
+};
+
+/**
+ * struct b2r2_profiler - B2R2 profiler.
+ *
+ * The callbacks are never run concurrently. No heavy stuff must be done in the
+ * callbacks as this might adversely affect the B2R2 driver. The callbacks must
+ * not call the B2R2 profiler API as this will cause a deadlock. If the callbacks
+ * call into the B2R2 driver care must be taken as deadlock situations can arise.
+ *
+ * @blt_done: Called when a blit has finished, timed out or been canceled.
+ */
+struct b2r2_profiler {
+ void (*blt_done)(const struct b2r2_blt_req * const request, const s32 request_id, const struct b2r2_blt_profiling_info * const blt_profiling_info);
+};
+
+/**
+ * b2r2_register_profiler() - Registers a profiler.
+ *
+ * Currently only one profiler can be registered at any given time.
+ *
+ * @profiler: The profiler
+ *
+ * Returns 0 on success, negative error code on failure
+ */
+int b2r2_register_profiler(const struct b2r2_profiler * const profiler);
+
+/**
+ * b2r2_unregister_profiler() - Unregisters a profiler.
+ *
+ * @profiler: The profiler
+ */
+void b2r2_unregister_profiler(const struct b2r2_profiler * const profiler);
+
+#endif /* #ifdef _LINUX_VIDEO_B2R2_PROFILER_API_H */
diff --git a/drivers/video/b2r2/b2r2_profiler_socket.c b/drivers/video/b2r2/b2r2_profiler_socket.c
new file mode 100644
index 00000000000..5631a730d4e
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_profiler_socket.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 profiler socket communication
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/semaphore.h>
+#include <asm/errno.h>
+
+#include "b2r2_profiler_api.h"
+#include "b2r2_internal.h"
+
+
+/*
+ * TODO: Call the profiler in a seperate thread and have a circular buffer
+ * between the B2R2 driver and that thread. That way the profiler can not slow
+ * down or kill the B2R2 driver. Seems a bit overkill right now as there is
+ * only one B2R2 profiler and we have full control over it but the situation
+ * may be different in the future.
+ */
+
+
+static const struct b2r2_profiler *b2r2_profiler;
+static DECLARE_MUTEX(b2r2_profiler_lock);
+
+
+int b2r2_register_profiler(const struct b2r2_profiler * const profiler)
+{
+ int return_value;
+
+ return_value = down_interruptible(&b2r2_profiler_lock);
+ if (return_value != 0)
+ return return_value;
+
+ if (b2r2_profiler != NULL) {
+ return_value = -EUSERS;
+
+ goto cleanup;
+ }
+
+ b2r2_profiler = profiler;
+
+ return_value = 0;
+
+cleanup:
+ up(&b2r2_profiler_lock);
+
+ return return_value;
+}
+EXPORT_SYMBOL(b2r2_register_profiler);
+
+void b2r2_unregister_profiler(const struct b2r2_profiler * const profiler)
+{
+ down(&b2r2_profiler_lock);
+
+ if (profiler == b2r2_profiler)
+ b2r2_profiler = NULL;
+
+ up(&b2r2_profiler_lock);
+}
+EXPORT_SYMBOL(b2r2_unregister_profiler);
+
+
+bool is_profiler_registered_approx(void)
+{
+ /* No locking by design, to make it fast, hence the approx */
+ if (b2r2_profiler != NULL)
+ return true;
+ else
+ return false;
+}
+
+void b2r2_call_profiler_blt_done(const struct b2r2_blt_request * const request)
+{
+ int return_value;
+ struct b2r2_blt_profiling_info blt_profiling_info;
+
+ return_value = down_interruptible(&b2r2_profiler_lock);
+ if (return_value != 0) {
+ dev_err(b2r2_blt_device(),
+ "%s: Failed to acquire semaphore, ret=%i. Lost profiler call!\n",
+ __func__, return_value);
+
+ return;
+ }
+
+ if (NULL == b2r2_profiler)
+ goto cleanup;
+
+ blt_profiling_info.nsec_active_in_cpu = request->nsec_active_in_cpu;
+ blt_profiling_info.nsec_active_in_b2r2 = request->job.nsec_active_in_hw;
+ blt_profiling_info.total_time_nsec = request->total_time_nsec;
+
+ b2r2_profiler->blt_done(&request->user_req, request->request_id, &blt_profiling_info);
+
+cleanup:
+ up(&b2r2_profiler_lock);
+}
diff --git a/drivers/video/b2r2/b2r2_profiler_socket.h b/drivers/video/b2r2/b2r2_profiler_socket.h
new file mode 100644
index 00000000000..80b2c20293f
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_profiler_socket.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 profiler socket communication
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef _LINUX_VIDEO_B2R2_PROFILER_SOCKET_H
+#define _LINUX_VIDEO_B2R2_PROFILER_SOCKET_H
+
+#include "b2r2_internal.h"
+
+/* Will give a correct result most of the time but can be wrong */
+bool is_profiler_registered_approx(void);
+
+void b2r2_call_profiler_blt_done(const struct b2r2_blt_request * const request);
+
+#endif /* _LINUX_VIDEO_B2R2_PROFILER_SOCKET_H */
diff --git a/drivers/video/b2r2/b2r2_structures.h b/drivers/video/b2r2/b2r2_structures.h
new file mode 100644
index 00000000000..99fa7f047d3
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_structures.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 register struct
+ *
+ * Author: Robert Fekete <robert.fekete@stericsson.com>
+ * Author: Paul Wannback
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+
+#ifndef __B2R2_STRUCTURES_H
+#define __B2R2_STRUCTURES_H
+
+/* C struct view */
+struct b2r2_memory_map {
+ unsigned char fill0[2304];
+ unsigned int BLT_SSBA17; /* @2304 */
+ unsigned int BLT_SSBA18; /* @2308 */
+ unsigned int BLT_SSBA19; /* @2312 */
+ unsigned int BLT_SSBA20; /* @2316 */
+ unsigned int BLT_SSBA21; /* @2320 */
+ unsigned int BLT_SSBA22; /* @2324 */
+ unsigned int BLT_SSBA23; /* @2328 */
+ unsigned int BLT_SSBA24; /* @2332 */
+ unsigned char fill1[32];
+ unsigned int BLT_STBA5; /* @2368 */
+ unsigned int BLT_STBA6; /* @2372 */
+ unsigned int BLT_STBA7; /* @2376 */
+ unsigned int BLT_STBA8; /* @2380 */
+ unsigned char fill2[176];
+ unsigned int BLT_CTL; /* @2560 */
+ unsigned int BLT_ITS; /* @2564 */
+ unsigned int BLT_STA1; /* @2568 */
+ unsigned char fill3[4];
+ unsigned int BLT_SSBA1; /* @2576 */
+ unsigned int BLT_SSBA2; /* @2580 */
+ unsigned int BLT_SSBA3; /* @2584 */
+ unsigned int BLT_SSBA4; /* @2588 */
+ unsigned int BLT_SSBA5; /* @2592 */
+ unsigned int BLT_SSBA6; /* @2596 */
+ unsigned int BLT_SSBA7; /* @2600 */
+ unsigned int BLT_SSBA8; /* @2604 */
+ unsigned int BLT_STBA1; /* @2608 */
+ unsigned int BLT_STBA2; /* @2612 */
+ unsigned int BLT_STBA3; /* @2616 */
+ unsigned int BLT_STBA4; /* @2620 */
+ unsigned int BLT_CQ1_TRIG_IP; /* @2624 */
+ unsigned int BLT_CQ1_TRIG_CTL; /* @2628 */
+ unsigned int BLT_CQ1_PACE_CTL; /* @2632 */
+ unsigned int BLT_CQ1_IP; /* @2636 */
+ unsigned int BLT_CQ2_TRIG_IP; /* @2640 */
+ unsigned int BLT_CQ2_TRIG_CTL; /* @2644 */
+ unsigned int BLT_CQ2_PACE_CTL; /* @2648 */
+ unsigned int BLT_CQ2_IP; /* @2652 */
+ unsigned int BLT_AQ1_CTL; /* @2656 */
+ unsigned int BLT_AQ1_IP; /* @2660 */
+ unsigned int BLT_AQ1_LNA; /* @2664 */
+ unsigned int BLT_AQ1_STA; /* @2668 */
+ unsigned int BLT_AQ2_CTL; /* @2672 */
+ unsigned int BLT_AQ2_IP; /* @2676 */
+ unsigned int BLT_AQ2_LNA; /* @2680 */
+ unsigned int BLT_AQ2_STA; /* @2684 */
+ unsigned int BLT_AQ3_CTL; /* @2688 */
+ unsigned int BLT_AQ3_IP; /* @2692 */
+ unsigned int BLT_AQ3_LNA; /* @2696 */
+ unsigned int BLT_AQ3_STA; /* @2700 */
+ unsigned int BLT_AQ4_CTL; /* @2704 */
+ unsigned int BLT_AQ4_IP; /* @2708 */
+ unsigned int BLT_AQ4_LNA; /* @2712 */
+ unsigned int BLT_AQ4_STA; /* @2716 */
+ unsigned int BLT_SSBA9; /* @2720 */
+ unsigned int BLT_SSBA10; /* @2724 */
+ unsigned int BLT_SSBA11; /* @2728 */
+ unsigned int BLT_SSBA12; /* @2732 */
+ unsigned int BLT_SSBA13; /* @2736 */
+ unsigned int BLT_SSBA14; /* @2740 */
+ unsigned int BLT_SSBA15; /* @2744 */
+ unsigned int BLT_SSBA16; /* @2748 */
+ unsigned int BLT_SGA1; /* @2752 */
+ unsigned int BLT_SGA2; /* @2756 */
+ unsigned char fill4[8];
+ unsigned int BLT_ITM0; /* @2768 */
+ unsigned int BLT_ITM1; /* @2772 */
+ unsigned int BLT_ITM2; /* @2776 */
+ unsigned int BLT_ITM3; /* @2780 */
+ unsigned char fill5[16];
+ unsigned int BLT_DFV2; /* @2800 */
+ unsigned int BLT_DFV1; /* @2804 */
+ unsigned int BLT_PRI; /* @2808 */
+ unsigned char fill6[8];
+ unsigned int PLUGS1_OP2; /* @2820 */
+ unsigned int PLUGS1_CHZ; /* @2824 */
+ unsigned int PLUGS1_MSZ; /* @2828 */
+ unsigned int PLUGS1_PGZ; /* @2832 */
+ unsigned char fill7[16];
+ unsigned int PLUGS2_OP2; /* @2852 */
+ unsigned int PLUGS2_CHZ; /* @2856 */
+ unsigned int PLUGS2_MSZ; /* @2860 */
+ unsigned int PLUGS2_PGZ; /* @2864 */
+ unsigned char fill8[16];
+ unsigned int PLUGS3_OP2; /* @2884 */
+ unsigned int PLUGS3_CHZ; /* @2888 */
+ unsigned int PLUGS3_MSZ; /* @2892 */
+ unsigned int PLUGS3_PGZ; /* @2896 */
+ unsigned char fill9[48];
+ unsigned int PLUGT_OP2; /* @2948 */
+ unsigned int PLUGT_CHZ; /* @2952 */
+ unsigned int PLUGT_MSZ; /* @2956 */
+ unsigned int PLUGT_PGZ; /* @2960 */
+ unsigned char fill10[108];
+ unsigned int BLT_NIP; /* @3072 */
+ unsigned int BLT_CIC; /* @3076 */
+ unsigned int BLT_INS; /* @3080 */
+ unsigned int BLT_ACK; /* @3084 */
+ unsigned int BLT_TBA; /* @3088 */
+ unsigned int BLT_TTY; /* @3092 */
+ unsigned int BLT_TXY; /* @3096 */
+ unsigned int BLT_TSZ; /* @3100 */
+ unsigned int BLT_S1CF; /* @3104 */
+ unsigned int BLT_S2CF; /* @3108 */
+ unsigned int BLT_S1BA; /* @3112 */
+ unsigned int BLT_S1TY; /* @3116 */
+ unsigned int BLT_S1XY; /* @3120 */
+ unsigned char fill11[4];
+ unsigned int BLT_S2BA; /* @3128 */
+ unsigned int BLT_S2TY; /* @3132 */
+ unsigned int BLT_S2XY; /* @3136 */
+ unsigned int BLT_S2SZ; /* @3140 */
+ unsigned int BLT_S3BA; /* @3144 */
+ unsigned int BLT_S3TY; /* @3148 */
+ unsigned int BLT_S3XY; /* @3152 */
+ unsigned int BLT_S3SZ; /* @3156 */
+ unsigned int BLT_CWO; /* @3160 */
+ unsigned int BLT_CWS; /* @3164 */
+ unsigned int BLT_CCO; /* @3168 */
+ unsigned int BLT_CML; /* @3172 */
+ unsigned int BLT_FCTL; /* @3176 */
+ unsigned int BLT_PMK; /* @3180 */
+ unsigned int BLT_RSF; /* @3184 */
+ unsigned int BLT_RZI; /* @3188 */
+ unsigned int BLT_HFP; /* @3192 */
+ unsigned int BLT_VFP; /* @3196 */
+ unsigned int BLT_Y_RSF; /* @3200 */
+ unsigned int BLT_Y_RZI; /* @3204 */
+ unsigned int BLT_Y_HFP; /* @3208 */
+ unsigned int BLT_Y_VFP; /* @3212 */
+ unsigned char fill12[16];
+ unsigned int BLT_KEY1; /* @3232 */
+ unsigned int BLT_KEY2; /* @3236 */
+ unsigned char fill13[8];
+ unsigned int BLT_SAR; /* @3248 */
+ unsigned int BLT_USR; /* @3252 */
+ unsigned char fill14[8];
+ unsigned int BLT_IVMX0; /* @3264 */
+ unsigned int BLT_IVMX1; /* @3268 */
+ unsigned int BLT_IVMX2; /* @3272 */
+ unsigned int BLT_IVMX3; /* @3276 */
+ unsigned int BLT_OVMX0; /* @3280 */
+ unsigned int BLT_OVMX1; /* @3284 */
+ unsigned int BLT_OVMX2; /* @3288 */
+ unsigned int BLT_OVMX3; /* @3292 */
+ unsigned char fill15[8];
+ unsigned int BLT_VC1R; /* @3304 */
+ unsigned char fill16[20];
+ unsigned int BLT_Y_HFC0; /* @3328 */
+ unsigned int BLT_Y_HFC1; /* @3332 */
+ unsigned int BLT_Y_HFC2; /* @3336 */
+ unsigned int BLT_Y_HFC3; /* @3340 */
+ unsigned int BLT_Y_HFC4; /* @3344 */
+ unsigned int BLT_Y_HFC5; /* @3348 */
+ unsigned int BLT_Y_HFC6; /* @3352 */
+ unsigned int BLT_Y_HFC7; /* @3356 */
+ unsigned int BLT_Y_HFC8; /* @3360 */
+ unsigned int BLT_Y_HFC9; /* @3364 */
+ unsigned int BLT_Y_HFC10; /* @3368 */
+ unsigned int BLT_Y_HFC11; /* @3372 */
+ unsigned int BLT_Y_HFC12; /* @3376 */
+ unsigned int BLT_Y_HFC13; /* @3380 */
+ unsigned int BLT_Y_HFC14; /* @3384 */
+ unsigned int BLT_Y_HFC15; /* @3388 */
+ unsigned char fill17[80];
+ unsigned int BLT_Y_VFC0; /* @3472 */
+ unsigned int BLT_Y_VFC1; /* @3476 */
+ unsigned int BLT_Y_VFC2; /* @3480 */
+ unsigned int BLT_Y_VFC3; /* @3484 */
+ unsigned int BLT_Y_VFC4; /* @3488 */
+ unsigned int BLT_Y_VFC5; /* @3492 */
+ unsigned int BLT_Y_VFC6; /* @3496 */
+ unsigned int BLT_Y_VFC7; /* @3500 */
+ unsigned int BLT_Y_VFC8; /* @3504 */
+ unsigned int BLT_Y_VFC9; /* @3508 */
+ unsigned char fill18[72];
+ unsigned int BLT_HFC0; /* @3584 */
+ unsigned int BLT_HFC1; /* @3588 */
+ unsigned int BLT_HFC2; /* @3592 */
+ unsigned int BLT_HFC3; /* @3596 */
+ unsigned int BLT_HFC4; /* @3600 */
+ unsigned int BLT_HFC5; /* @3604 */
+ unsigned int BLT_HFC6; /* @3608 */
+ unsigned int BLT_HFC7; /* @3612 */
+ unsigned int BLT_HFC8; /* @3616 */
+ unsigned int BLT_HFC9; /* @3620 */
+ unsigned int BLT_HFC10; /* @3624 */
+ unsigned int BLT_HFC11; /* @3628 */
+ unsigned int BLT_HFC12; /* @3632 */
+ unsigned int BLT_HFC13; /* @3636 */
+ unsigned int BLT_HFC14; /* @3640 */
+ unsigned int BLT_HFC15; /* @3644 */
+ unsigned char fill19[80];
+ unsigned int BLT_VFC0; /* @3728 */
+ unsigned int BLT_VFC1; /* @3732 */
+ unsigned int BLT_VFC2; /* @3736 */
+ unsigned int BLT_VFC3; /* @3740 */
+ unsigned int BLT_VFC4; /* @3744 */
+ unsigned int BLT_VFC5; /* @3748 */
+ unsigned int BLT_VFC6; /* @3752 */
+ unsigned int BLT_VFC7; /* @3756 */
+ unsigned int BLT_VFC8; /* @3760 */
+ unsigned int BLT_VFC9; /* @3764 */
+};
+
+#endif /* !defined(__B2R2_STRUCTURES_H) */
+
diff --git a/drivers/video/b2r2/b2r2_timing.c b/drivers/video/b2r2/b2r2_timing.c
new file mode 100644
index 00000000000..4f3e2b8b042
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_timing.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 timing
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/time.h>
+
+
+u32 b2r2_get_curr_nsec(void)
+{
+ struct timespec ts;
+
+ getrawmonotonic(&ts);
+
+ return (u32)timespec_to_ns(&ts);
+}
diff --git a/drivers/video/b2r2/b2r2_timing.h b/drivers/video/b2r2/b2r2_timing.h
new file mode 100644
index 00000000000..e87113c0ec9
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_timing.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 timing
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef _LINUX_DRIVERS_VIDEO_B2R2_TIMING_H_
+#define _LINUX_DRIVERS_VIDEO_B2R2_TIMING_H_
+
+/**
+ * b2r2_get_curr_nsec() - Return the current nanosecond. Notice that the value
+ * wraps when the u32 limit is reached.
+ *
+ */
+u32 b2r2_get_curr_nsec(void);
+
+#endif /* _LINUX_DRIVERS_VIDEO_B2R2_TIMING_H_ */
diff --git a/drivers/video/b2r2/b2r2_utils.c b/drivers/video/b2r2/b2r2_utils.c
new file mode 100644
index 00000000000..44ea06e51fd
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_utils.c
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 utils
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com> for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include "b2r2_utils.h"
+
+#include "b2r2_debug.h"
+
+#include <video/b2r2_blt.h>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+
+const s32 b2r2_s32_max = 2147483647;
+
+
+void b2r2_get_img_bounding_rect(struct b2r2_blt_img *img,
+ struct b2r2_blt_rect *bounding_rect)
+{
+ bounding_rect->x = 0;
+ bounding_rect->y = 0;
+ bounding_rect->width = img->width;
+ bounding_rect->height = img->height;
+}
+
+
+bool b2r2_is_zero_area_rect(struct b2r2_blt_rect *rect)
+{
+ return rect->width == 0 || rect->height == 0;
+}
+
+bool b2r2_is_rect_inside_rect(struct b2r2_blt_rect *rect1,
+ struct b2r2_blt_rect *rect2)
+{
+ return rect1->x >= rect2->x &&
+ rect1->y >= rect2->y &&
+ rect1->x + rect1->width <= rect2->x + rect2->width &&
+ rect1->y + rect1->height <= rect2->y + rect2->height;
+}
+
+void b2r2_intersect_rects(struct b2r2_blt_rect *rect1,
+ struct b2r2_blt_rect *rect2, struct b2r2_blt_rect *intersection)
+{
+ struct b2r2_blt_rect tmp_rect;
+
+ tmp_rect.x = max(rect1->x, rect2->x);
+ tmp_rect.y = max(rect1->y, rect2->y);
+ tmp_rect.width = min(rect1->x + rect1->width, rect2->x + rect2->width)
+ - tmp_rect.x;
+ if (tmp_rect.width < 0)
+ tmp_rect.width = 0;
+ tmp_rect.height =
+ min(rect1->y + rect1->height, rect2->y + rect2->height) -
+ tmp_rect.y;
+ if (tmp_rect.height < 0)
+ tmp_rect.height = 0;
+
+ *intersection = tmp_rect;
+}
+
+
+int b2r2_get_fmt_bpp(enum b2r2_blt_fmt fmt)
+{
+ /*
+ * Currently this function is not used that often but if that changes a
+ * lookup table could make it a lot faster.
+ */
+ switch (fmt) {
+ case B2R2_BLT_FMT_1_BIT_A1:
+ return 1;
+
+ case B2R2_BLT_FMT_8_BIT_A8:
+ return 8;
+
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ return 12;
+
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ return 16;
+
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ return 24;
+
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ return 32;
+
+ default:
+ b2r2_log_err("%s: Internal error! Format %#x not recognized.\n",
+ __func__, fmt);
+ return 32;
+ }
+}
+
+int b2r2_get_fmt_y_bpp(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ return 8;
+
+ default:
+ b2r2_log_err("%s: Internal error! Non YCbCr format supplied.\n",
+ __func__);
+ return 8;
+ }
+}
+
+
+bool b2r2_is_single_plane_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_1_BIT_A1:
+ case B2R2_BLT_FMT_8_BIT_A8:
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool b2r2_is_independent_pixel_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_1_BIT_A1:
+ case B2R2_BLT_FMT_8_BIT_A8:
+ case B2R2_BLT_FMT_16_BIT_ARGB4444:
+ case B2R2_BLT_FMT_16_BIT_ARGB1555:
+ case B2R2_BLT_FMT_16_BIT_RGB565:
+ case B2R2_BLT_FMT_24_BIT_RGB888:
+ case B2R2_BLT_FMT_24_BIT_ARGB8565:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_ARGB8888:
+ case B2R2_BLT_FMT_32_BIT_ABGR8888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool b2r2_is_ycbcri_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool b2r2_is_ycbcrsp_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool b2r2_is_ycbcrp_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool b2r2_is_ycbcr420_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool b2r2_is_ycbcr422_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_Y_CB_Y_CR:
+ case B2R2_BLT_FMT_CB_Y_CR_Y:
+ case B2R2_BLT_FMT_YUV422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool b2r2_is_ycbcr444_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_YUV444_PACKED_PLANAR:
+ case B2R2_BLT_FMT_32_BIT_AYUV8888:
+ case B2R2_BLT_FMT_24_BIT_YUV888:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool b2r2_is_mb_fmt(enum b2r2_blt_fmt fmt)
+{
+ switch (fmt) {
+ case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE:
+ case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+u32 b2r2_calc_pitch_from_width(s32 width, enum b2r2_blt_fmt fmt)
+{
+ if (b2r2_is_single_plane_fmt(fmt)) {
+ return (u32)b2r2_div_round_up(width *
+ b2r2_get_fmt_bpp(fmt), 8);
+ } else if (b2r2_is_ycbcrsp_fmt(fmt) || b2r2_is_ycbcrp_fmt(fmt)) {
+ return (u32)b2r2_div_round_up(width *
+ b2r2_get_fmt_y_bpp(fmt), 8);
+ } else {
+ b2r2_log_err("%s: Internal error! "
+ "Pitchless format supplied.\n",
+ __func__);
+ return 0;
+ }
+}
+
+u32 b2r2_get_img_pitch(struct b2r2_blt_img *img)
+{
+ if (img->pitch != 0)
+ return img->pitch;
+ else
+ return b2r2_calc_pitch_from_width(img->width, img->fmt);
+}
+
+s32 b2r2_get_img_size(struct b2r2_blt_img *img)
+{
+ if (b2r2_is_single_plane_fmt(img->fmt)) {
+ return (s32)b2r2_get_img_pitch(img) * img->height;
+ } else if (b2r2_is_ycbcrsp_fmt(img->fmt) ||
+ b2r2_is_ycbcrp_fmt(img->fmt)) {
+ s32 y_plane_size;
+
+ y_plane_size = (s32)b2r2_get_img_pitch(img) * img->height;
+
+ if (b2r2_is_ycbcr420_fmt(img->fmt)) {
+ return y_plane_size + y_plane_size / 2;
+ } else if (b2r2_is_ycbcr422_fmt(img->fmt)) {
+ return y_plane_size * 2;
+ } else if (b2r2_is_ycbcr444_fmt(img->fmt)) {
+ return y_plane_size * 3;
+ } else {
+ b2r2_log_err("%s: Internal error! "
+ "Format %#x not recognized.\n",
+ __func__, img->fmt);
+ return 0;
+ }
+ } else if (b2r2_is_mb_fmt(img->fmt)) {
+ return (img->width * img->height *
+ b2r2_get_fmt_bpp(img->fmt)) / 8;
+ } else {
+ b2r2_log_err("%s: Internal error! "
+ "Format %#x not recognized.\n",
+ __func__, img->fmt);
+ return 0;
+ }
+}
+
+
+s32 b2r2_div_round_up(s32 dividend, s32 divisor)
+{
+ s32 quotient = dividend / divisor;
+ if (dividend % divisor != 0)
+ quotient++;
+
+ return quotient;
+}
+
+bool b2r2_is_aligned(s32 value, s32 alignment)
+{
+ return value % alignment == 0;
+}
+
+s32 b2r2_align_up(s32 value, s32 alignment)
+{
+ s32 remainder = abs(value) % abs(alignment);
+ s32 value_to_add;
+
+ if (remainder > 0) {
+ if (value >= 0)
+ value_to_add = alignment - remainder;
+ else
+ value_to_add = remainder;
+ } else {
+ value_to_add = 0;
+ }
+
+ return value + value_to_add;
+}
diff --git a/drivers/video/b2r2/b2r2_utils.h b/drivers/video/b2r2/b2r2_utils.h
new file mode 100644
index 00000000000..745d71c0126
--- /dev/null
+++ b/drivers/video/b2r2/b2r2_utils.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson B2R2 utils
+ *
+ * Author: Johan Mossberg <johan.xx.mossberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef _LINUX_DRIVERS_VIDEO_B2R2_UTILS_H_
+#define _LINUX_DRIVERS_VIDEO_B2R2_UTILS_H_
+
+#include <video/b2r2_blt.h>
+
+extern const s32 b2r2_s32_max;
+
+void b2r2_get_img_bounding_rect(struct b2r2_blt_img *img,
+ struct b2r2_blt_rect *bounding_rect);
+
+bool b2r2_is_zero_area_rect(struct b2r2_blt_rect *rect);
+bool b2r2_is_rect_inside_rect(struct b2r2_blt_rect *rect1,
+ struct b2r2_blt_rect *rect2);
+void b2r2_intersect_rects(struct b2r2_blt_rect *rect1,
+ struct b2r2_blt_rect *rect2, struct b2r2_blt_rect *intersection);
+
+int b2r2_get_fmt_bpp(enum b2r2_blt_fmt fmt);
+int b2r2_get_fmt_y_bpp(enum b2r2_blt_fmt fmt);
+
+bool b2r2_is_single_plane_fmt(enum b2r2_blt_fmt fmt);
+bool b2r2_is_independent_pixel_fmt(enum b2r2_blt_fmt fmt);
+bool b2r2_is_ycbcri_fmt(enum b2r2_blt_fmt fmt);
+bool b2r2_is_ycbcrsp_fmt(enum b2r2_blt_fmt fmt);
+bool b2r2_is_ycbcrp_fmt(enum b2r2_blt_fmt fmt);
+bool b2r2_is_ycbcr420_fmt(enum b2r2_blt_fmt fmt);
+bool b2r2_is_ycbcr422_fmt(enum b2r2_blt_fmt fmt);
+bool b2r2_is_ycbcr444_fmt(enum b2r2_blt_fmt fmt);
+bool b2r2_is_mb_fmt(enum b2r2_blt_fmt fmt);
+
+/*
+ * Rounds up if an invalid width causes the pitch to be non byte aligned.
+ */
+u32 b2r2_calc_pitch_from_width(s32 width, enum b2r2_blt_fmt fmt);
+u32 b2r2_get_img_pitch(struct b2r2_blt_img *img);
+s32 b2r2_get_img_size(struct b2r2_blt_img *img);
+
+s32 b2r2_div_round_up(s32 dividend, s32 divisor);
+bool b2r2_is_aligned(s32 value, s32 alignment);
+s32 b2r2_align_up(s32 value, s32 alignment);
+
+#endif
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 55044351889..16fe812724f 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -25,6 +25,7 @@ struct pwm_bl_data {
struct pwm_device *pwm;
struct device *dev;
unsigned int period;
+ unsigned int lth_brightness;
int (*notify)(struct device *,
int brightness);
};
@@ -48,7 +49,9 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
pwm_config(pb->pwm, 0, pb->period);
pwm_disable(pb->pwm);
} else {
- pwm_config(pb->pwm, brightness * pb->period / max, pb->period);
+ brightness = pb->lth_brightness + (brightness *
+ (pb->period - pb->lth_brightness) / max);
+ pwm_config(pb->pwm, brightness, pb->period);
pwm_enable(pb->pwm);
}
return 0;
@@ -92,6 +95,8 @@ static int pwm_backlight_probe(struct platform_device *pdev)
pb->period = data->pwm_period_ns;
pb->notify = data->notify;
+ pb->lth_brightness = data->lth_brightness *
+ (data->pwm_period_ns / data->max_brightness);
pb->dev = &pdev->dev;
pb->pwm = pwm_request(data->pwm_id, "backlight");
diff --git a/drivers/video/mcde/Kconfig b/drivers/video/mcde/Kconfig
new file mode 100644
index 00000000000..a1310332cf9
--- /dev/null
+++ b/drivers/video/mcde/Kconfig
@@ -0,0 +1,83 @@
+config FB_MCDE
+ tristate "MCDE support"
+ depends on FB
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_SYS_FOPS
+ select HWMEM
+ ---help---
+ This enables support for MCDE based frame buffer driver.
+
+ Please read the file <file:Documentation/fb/mcde.txt>
+
+config MCDE_DISPLAY_GENERIC_DSI
+ tristate "Generic DSI display driver"
+ depends on FB_MCDE
+
+config MCDE_DISPLAY_SONY_SY35560_DSI
+ tristate "Sony sy35560 display driver"
+ depends on FB_MCDE
+
+config MCDE_DISPLAY_DPI
+ bool "Support for DPI displays within MCDE"
+ depends on FB_MCDE
+ default n
+ ---help---
+ Add this option to choose which DPI display driver for MCDE to include
+
+ DPI (Display Pixel Interface) is a MIPI Alliance standard used for
+ active-matrix LCDs. The DPI uses parallel data lines.
+
+menu "MCDE DPI displays"
+ depends on MCDE_DISPLAY_DPI
+
+config MCDE_DISPLAY_VUIB500_DPI
+ tristate "DPI display driver for the VUIB500 board"
+ ---help---
+ The VUIB500 is an ST-Ericsson user interface board.
+
+endmenu
+
+config MCDE_DISPLAY_AV8100
+ tristate "AV8100 HDMI/CVBS display driver"
+ depends on FB_MCDE
+ select AV8100
+
+config MCDE_DISPLAY_HDMI_FB_AUTO_CREATE
+ bool "HDMI_FB_AUTO_CREATE"
+ default y
+ depends on MCDE_DISPLAY_AV8100
+ ---help---
+ Say Y if you want the HDMI frame buffer to be created on start
+ Say N if you want the HDMI frame buffer to be created when HDMI
+ cable is plugged (needs user space HDMIservice)
+
+config MCDE_DISPLAY_AB8500_DENC
+ tristate "AB8500 CVBS display driver"
+ depends on FB_MCDE
+ select AB8500_DENC
+
+config FB_MCDE_DEBUG
+ bool "MCDE debug messages"
+ depends on FB_MCDE
+ ---help---
+ Say Y here if you want the MCDE driver to output debug messages
+
+config FB_MCDE_VDEBUG
+ bool "MCDE verbose debug messages"
+ depends on FB_MCDE_DEBUG
+ ---help---
+ Say Y here if you want the MCDE driver to output more debug messages
+
+config MCDE_FB_AVOID_REALLOC
+ bool "MCDE early allocate framebuffer"
+ default n
+ depends on FB_MCDE
+ ---help---
+ If you say Y here maximum frame buffer size is allocated and
+ used for all resolutions. If you say N here, the frame buffer is
+ reallocated when resolution is changed. This reallocation might
+ fail because of fragmented memory. Note that this memory will
+ never be deallocated, while the MCDE framebuffer is used.
+
diff --git a/drivers/video/mcde/Makefile b/drivers/video/mcde/Makefile
new file mode 100644
index 00000000000..93097f8961e
--- /dev/null
+++ b/drivers/video/mcde/Makefile
@@ -0,0 +1,16 @@
+
+mcde-objs := mcde_mod.o mcde_hw.o mcde_dss.o mcde_display.o mcde_bus.o mcde_fb.o
+obj-$(CONFIG_FB_MCDE) += mcde.o
+
+obj-$(CONFIG_MCDE_DISPLAY_GENERIC_DSI) += display-generic_dsi.o
+obj-$(CONFIG_MCDE_DISPLAY_SONY_SY35560_DSI) += display-sony_sy35560_dsi.o
+obj-$(CONFIG_MCDE_DISPLAY_VUIB500_DPI) += display-vuib500-dpi.o
+obj-$(CONFIG_MCDE_DISPLAY_AB8500_DENC) += display-ab8500.o
+obj-$(CONFIG_MCDE_DISPLAY_AV8100) += display-av8100.o
+
+ifdef CONFIG_FB_MCDE_DEBUG
+EXTRA_CFLAGS += -DDEBUG
+endif
+ifdef CONFIG_FB_MCDE_VDEBUG
+EXTRA_CFLAGS += -DVERBOSE_DEBUG
+endif
diff --git a/drivers/video/mcde/display-ab8500.c b/drivers/video/mcde/display-ab8500.c
new file mode 100644
index 00000000000..8b932d3d8d2
--- /dev/null
+++ b/drivers/video/mcde/display-ab8500.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * AB8500 display driver
+ *
+ * Author: Marcel Tunnissen <marcel.tuennissen@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/mfd/ab8500/denc.h>
+#include <video/mcde_display.h>
+#include <video/mcde_display-ab8500.h>
+
+#define AB8500_DISP_TRACE dev_dbg(&ddev->dev, "%s\n", __func__)
+
+#define SDTV_PIXCLOCK 37037
+
+/*
+ * PAL:
+ * Total nr of active lines: 576
+ * Total nr of blanking lines: 49
+ * total: 625
+ */
+#define PAL_HBP 132
+#define PAL_HFP 12
+#define PAL_VBP_FIELD_1 22
+#define PAL_VBP_FIELD_2 23
+#define PAL_VFP_FIELD_1 2
+#define PAL_VFP_FIELD_2 2
+
+/*
+ * NTSC (ITU-R BT.470-5):
+ * Total nr of active lines: 486
+ * Total nr of blanking lines: 39
+ * total: 525
+ */
+#define NTSC_ORG_HBP 122
+#define NTSC_ORG_HFP 16
+#define NTSC_ORG_VBP_FIELD_1 16
+#define NTSC_ORG_VBP_FIELD_2 17
+#define NTSC_ORG_VFP_FIELD_1 3
+#define NTSC_ORG_VFP_FIELD_2 3
+
+/*
+ * NTSC (DV variant):
+ * Total nr of active lines: 480
+ * Total nr of blanking lines: 45
+ * total: 525
+ */
+#define NTSC_HBP 122
+#define NTSC_HFP 16
+#define NTSC_VBP_FIELD_1 19
+#define NTSC_VBP_FIELD_2 20
+#define NTSC_VFP_FIELD_1 3
+#define NTSC_VFP_FIELD_2 3
+
+struct display_driver_data {
+ struct ab8500_denc_conf denc_conf;
+ struct platform_device *denc_dev;
+ int nr_regulators;
+ struct regulator **regulator;
+};
+
+static int try_video_mode(struct mcde_display_device *ddev,
+ struct mcde_video_mode *video_mode);
+static int set_video_mode(struct mcde_display_device *ddev,
+ struct mcde_video_mode *video_mode);
+static int set_power_mode(struct mcde_display_device *ddev,
+ enum mcde_display_power_mode power_mode);
+static int on_first_update(struct mcde_display_device *ddev);
+static int display_update(struct mcde_display_device *ddev);
+
+static int __devinit ab8500_probe(struct mcde_display_device *ddev)
+{
+ int ret = 0;
+ int i;
+ struct ab8500_display_platform_data *pdata = ddev->dev.platform_data;
+ struct display_driver_data *driver_data;
+
+ AB8500_DISP_TRACE;
+
+ if (pdata == NULL) {
+ dev_err(&ddev->dev, "%s:Platform data missing\n", __func__);
+ return -EINVAL;
+ }
+ if (ddev->port->type != MCDE_PORTTYPE_DPI) {
+ dev_err(&ddev->dev, "%s:Invalid port type %d\n", __func__,
+ ddev->port->type);
+ return -EINVAL;
+ }
+
+ driver_data = (struct display_driver_data *)
+ kzalloc(sizeof(struct display_driver_data), GFP_KERNEL);
+ if (!driver_data) {
+ dev_err(&ddev->dev, "Failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+ driver_data->denc_dev = ab8500_denc_get_device();
+ if (!driver_data->denc_dev) {
+ dev_err(&ddev->dev, "Failed to get DENC device\n");
+ ret = -ENODEV;
+ goto dev_get_failed;
+ }
+
+ driver_data->regulator = kzalloc(pdata->nr_regulators *
+ sizeof(struct regulator *), GFP_KERNEL);
+ if (!driver_data->regulator) {
+ dev_err(&ddev->dev, "Failed to allocate regulator list\n");
+ ret = -ENOMEM;
+ goto reg_alloc_failed;
+ }
+ for (i = 0; i < pdata->nr_regulators; i++) {
+ driver_data->regulator[i] = regulator_get(&ddev->dev,
+ pdata->regulator_id[i]);
+ if (IS_ERR(driver_data->regulator[i])) {
+ ret = PTR_ERR(driver_data->regulator[i]);
+ dev_warn(&ddev->dev, "%s:Failed to get regulator %s\n",
+ __func__, pdata->regulator_id[i]);
+ goto regulator_get_failed;
+ }
+ }
+ driver_data->nr_regulators = pdata->nr_regulators;
+
+ dev_set_drvdata(&ddev->dev, driver_data);
+
+ ddev->try_video_mode = try_video_mode;
+ ddev->set_video_mode = set_video_mode;
+ ddev->set_power_mode = set_power_mode;
+ ddev->on_first_update = on_first_update;
+ ddev->update = display_update;
+ ddev->prepare_for_update = NULL;
+
+ return 0;
+
+regulator_get_failed:
+ for (i--; i >= 0; i--)
+ regulator_put(driver_data->regulator[i]);
+ kfree(driver_data->regulator);
+ driver_data->regulator = NULL;
+reg_alloc_failed:
+ ab8500_denc_put_device(driver_data->denc_dev);
+dev_get_failed:
+ kfree(driver_data);
+ return ret;
+}
+
+static int __devexit ab8500_remove(struct mcde_display_device *ddev)
+{
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+ AB8500_DISP_TRACE;
+
+ ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_OFF);
+
+ if (driver_data->regulator) {
+ int i;
+ for (i = driver_data->nr_regulators - 1; i >= 0; i--)
+ regulator_put(driver_data->regulator[i]);
+ kfree(driver_data->regulator);
+ driver_data->regulator = NULL;
+ driver_data->nr_regulators = 0;
+ }
+ ab8500_denc_put_device(driver_data->denc_dev);
+ kfree(driver_data);
+ return 0;
+}
+
+static int ab8500_resume(struct mcde_display_device *ddev)
+{
+ int ret = 0;
+ AB8500_DISP_TRACE;
+
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_STANDBY);
+ if (ret < 0)
+ dev_warn(&ddev->dev, "%s: Failed to resume display\n",
+ __func__);
+
+ return ret;
+}
+
+static int ab8500_suspend(struct mcde_display_device *ddev, pm_message_t state)
+{
+ int ret = 0;
+ AB8500_DISP_TRACE;
+
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_OFF);
+ if (ret < 0)
+ dev_warn(&ddev->dev, "%s: Failed to suspend display\n",
+ __func__);
+
+ return ret;
+}
+
+
+static struct mcde_display_driver ab8500_driver = {
+ .probe = ab8500_probe,
+ .remove = ab8500_remove,
+ .suspend = ab8500_suspend,
+ .resume = ab8500_resume,
+ .driver = {
+ .name = "mcde_tv_ab8500",
+ },
+};
+
+static void print_vmode(struct mcde_video_mode *vmode)
+{
+ pr_debug("resolution: %dx%d\n", vmode->xres, vmode->yres);
+ pr_debug(" pixclock: %d\n", vmode->pixclock);
+ pr_debug(" hbp: %d\n", vmode->hbp);
+ pr_debug(" hfp: %d\n", vmode->hfp);
+ pr_debug(" vbp: %d\n", vmode->vbp);
+ pr_debug(" vfp: %d\n", vmode->vfp);
+ pr_debug("interlaced: %s\n", vmode->interlaced ? "true" : "false");
+}
+
+static int try_video_mode(
+ struct mcde_display_device *ddev, struct mcde_video_mode *video_mode)
+{
+ AB8500_DISP_TRACE;
+
+ if (ddev == NULL || video_mode == NULL) {
+ dev_warn(&ddev->dev, "%s:ddev = NULL or video_mode = NULL\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (video_mode->xres != 720) {
+ dev_warn(&ddev->dev,
+ "%s:Failed to find video mode x=%d, y=%d\n",
+ __func__, video_mode->xres, video_mode->yres);
+ return -EINVAL;
+ }
+
+ /* TODO: move this part to MCDE: mcde_dss_try_video_mode? */
+ /* check for PAL */
+ switch (video_mode->yres) {
+ case 576:
+ /* set including SAV/EAV: */
+ video_mode->hbp = PAL_HBP;
+ video_mode->hfp = PAL_HFP;
+ video_mode->vbp = PAL_VBP_FIELD_1 + PAL_VBP_FIELD_2;
+ video_mode->vfp = PAL_VFP_FIELD_1 + PAL_VFP_FIELD_2;
+ video_mode->interlaced = true;
+ video_mode->pixclock = SDTV_PIXCLOCK;
+ break;
+ case 480:
+ /* set including SAV/EAV */
+ video_mode->hbp = NTSC_HBP;
+ video_mode->hfp = NTSC_HFP;
+ video_mode->vbp = NTSC_VBP_FIELD_1 + NTSC_VBP_FIELD_2;
+ video_mode->vfp = NTSC_VFP_FIELD_1 + NTSC_VFP_FIELD_2;
+ video_mode->interlaced = true;
+ video_mode->pixclock = SDTV_PIXCLOCK;
+ break;
+ case 486:
+ /* set including SAV/EAV */
+ video_mode->hbp = NTSC_ORG_HBP;
+ video_mode->hfp = NTSC_ORG_HFP;
+ video_mode->vbp = NTSC_ORG_VBP_FIELD_1 + NTSC_ORG_VBP_FIELD_2;
+ video_mode->vfp = NTSC_ORG_VFP_FIELD_1 + NTSC_ORG_VFP_FIELD_2;
+ video_mode->interlaced = true;
+ video_mode->pixclock = SDTV_PIXCLOCK;
+ break;
+ default:
+ dev_warn(&ddev->dev,
+ "%s:Failed to find video mode x=%d, y=%d\n",
+ __func__, video_mode->xres, video_mode->yres);
+ return -EINVAL;
+ }
+
+ print_vmode(video_mode);
+
+ return 0;
+
+}
+
+static int set_video_mode(
+ struct mcde_display_device *ddev, struct mcde_video_mode *video_mode)
+{
+ int res;
+ struct ab8500_display_platform_data *pdata = ddev->dev.platform_data;
+ struct display_driver_data *driver_data =
+ (struct display_driver_data *)dev_get_drvdata(&ddev->dev);
+ AB8500_DISP_TRACE;
+
+ if (ddev == NULL || video_mode == NULL) {
+ dev_warn(&ddev->dev, "%s:ddev = NULL or video_mode = NULL\n",
+ __func__);
+ return -EINVAL;
+ }
+ ddev->video_mode = *video_mode;
+
+ if (video_mode->xres != 720) {
+ dev_warn(&ddev->dev, "%s:Failed to set video mode x=%d, y=%d\n",
+ __func__, video_mode->xres, video_mode->yres);
+ return -EINVAL;
+ }
+
+ /* check for PAL BDGHI and N */
+ switch (video_mode->yres) {
+ case 576:
+ driver_data->denc_conf.TV_std = TV_STD_PAL_BDGHI;
+ /* TODO: how to choose LOW DEF FILTER */
+ driver_data->denc_conf.cr_filter = TV_CR_PAL_HIGH_DEF_FILTER;
+ /* TODO: PAL N (e.g. uses a setup of 7.5 IRE) */
+ driver_data->denc_conf.black_level_setup = false;
+ break;
+ case 480: /* NTSC, PAL M DV variant */
+ case 486: /* NTSC, PAL M original */
+ /* TODO: PAL M */
+ driver_data->denc_conf.TV_std = TV_STD_NTSC_M;
+ /* TODO: how to choose LOW DEF FILTER */
+ driver_data->denc_conf.cr_filter = TV_CR_NTSC_HIGH_DEF_FILTER;
+ driver_data->denc_conf.black_level_setup = true;
+ break;
+ default:
+ dev_warn(&ddev->dev, "%s:Failed to set video mode x=%d, y=%d\n",
+ __func__, video_mode->xres, video_mode->yres);
+ return -EINVAL;
+ }
+
+
+ driver_data->denc_conf.progressive = !video_mode->interlaced;
+ driver_data->denc_conf.act_output = true;
+ driver_data->denc_conf.test_pattern = false;
+ driver_data->denc_conf.partial_blanking = true;
+ driver_data->denc_conf.blank_all = false;
+ driver_data->denc_conf.suppress_col = false;
+ driver_data->denc_conf.phase_reset_mode = TV_PHASE_RST_MOD_DISABLE;
+ driver_data->denc_conf.dac_enable = false;
+ driver_data->denc_conf.act_dc_output = true;
+
+ set_power_mode(ddev, MCDE_DISPLAY_PM_STANDBY);
+ mcde_chnl_set_col_convert(ddev->chnl_state,
+ &pdata->rgb_2_yCbCr_transform);
+ mcde_chnl_stop_flow(ddev->chnl_state);
+ res = mcde_chnl_set_video_mode(ddev->chnl_state, &ddev->video_mode);
+ if (res < 0) {
+ dev_warn(&ddev->dev, "%s:Failed to set video mode on channel\n",
+ __func__);
+
+ return res;
+ }
+ ddev->update_flags |= UPDATE_FLAG_VIDEO_MODE;
+
+ return 0;
+}
+
+static int set_power_mode(struct mcde_display_device *ddev,
+ enum mcde_display_power_mode power_mode)
+{
+ int ret;
+ int i;
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+ AB8500_DISP_TRACE;
+
+ /* OFF -> STANDBY */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_OFF &&
+ power_mode != MCDE_DISPLAY_PM_OFF) {
+ dev_dbg(&ddev->dev, "off -> standby\n");
+ if (ddev->platform_enable) {
+ ret = ddev->platform_enable(ddev);
+ if (ret)
+ goto error;
+ }
+ if (driver_data->regulator) {
+ for (i = 0; i < driver_data->nr_regulators; i++) {
+ ret = regulator_enable(
+ driver_data->regulator[i]);
+ if (ret)
+ goto off_to_standby_failed;
+ dev_dbg(&ddev->dev, "regulator %d on\n", i);
+ }
+ }
+ ab8500_denc_power_up(driver_data->denc_dev);
+ ab8500_denc_reset(driver_data->denc_dev, true);
+ ddev->power_mode = MCDE_DISPLAY_PM_STANDBY;
+ }
+ /* STANDBY -> ON */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_STANDBY &&
+ power_mode == MCDE_DISPLAY_PM_ON) {
+ dev_dbg(&ddev->dev, "standby -> on\n");
+ ddev->power_mode = MCDE_DISPLAY_PM_ON;
+ }
+ /* ON -> STANDBY */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_ON &&
+ power_mode <= MCDE_DISPLAY_PM_STANDBY) {
+ dev_dbg(&ddev->dev, "on -> standby\n");
+ ab8500_denc_reset(driver_data->denc_dev, false);
+ ddev->power_mode = MCDE_DISPLAY_PM_STANDBY;
+ }
+ /* STANDBY -> OFF */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_STANDBY &&
+ power_mode == MCDE_DISPLAY_PM_OFF) {
+ bool error = false;
+ dev_dbg(&ddev->dev, "standby -> off\n");
+ if (driver_data->regulator) {
+ for (i = 0; i < driver_data->nr_regulators; i++) {
+ ret = regulator_disable(
+ driver_data->regulator[i]);
+ /* continue in case of an error */
+ error |= (ret != 0);
+ dev_dbg(&ddev->dev, "regulator %d off\n", i);
+ }
+ }
+ if (ddev->platform_disable) {
+ ret = ddev->platform_disable(ddev);
+ error |= (ret != 0);
+ }
+ if (error) {
+ /* the latest error code is returned */
+ goto error;
+ }
+ memset(&(ddev->video_mode), 0, sizeof(struct mcde_video_mode));
+ ab8500_denc_power_down(driver_data->denc_dev);
+ ddev->power_mode = MCDE_DISPLAY_PM_OFF;
+ }
+
+ return 0;
+
+ /* In case of an error, try to leave in off-state */
+off_to_standby_failed:
+ for (i--; i >= 0; i--)
+ regulator_disable(driver_data->regulator[i]);
+ ddev->platform_disable(ddev);
+
+error:
+ dev_err(&ddev->dev, "Failed to set power mode");
+ return ret;
+}
+
+static int on_first_update(struct mcde_display_device *ddev)
+{
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+
+ ab8500_denc_conf(driver_data->denc_dev, &driver_data->denc_conf);
+ ab8500_denc_conf_plug_detect(driver_data->denc_dev, true, false,
+ TV_PLUG_TIME_2S);
+ ab8500_denc_mask_int_plug_det(driver_data->denc_dev, false, false);
+ ddev->first_update = false;
+ return 0;
+}
+
+static int display_update(struct mcde_display_device *ddev)
+{
+ int ret;
+
+ if (ddev->first_update)
+ on_first_update(ddev);
+ if (ddev->power_mode != MCDE_DISPLAY_PM_ON && ddev->set_power_mode) {
+ ret = set_power_mode(ddev, MCDE_DISPLAY_PM_ON);
+ if (ret < 0)
+ goto error;
+ }
+ ret = mcde_chnl_update(ddev->chnl_state, &ddev->update_area);
+ if (ret < 0)
+ goto error;
+out:
+ return ret;
+error:
+ dev_warn(&ddev->dev, "%s:Failed to set power mode to on\n", __func__);
+ goto out;
+}
+
+/* Module init */
+static int __init mcde_display_tvout_ab8500_init(void)
+{
+ pr_debug("%s\n", __func__);
+
+ return mcde_display_driver_register(&ab8500_driver);
+}
+late_initcall(mcde_display_tvout_ab8500_init);
+
+static void __exit mcde_display_tvout_ab8500_exit(void)
+{
+ pr_debug("%s\n", __func__);
+
+ mcde_display_driver_unregister(&ab8500_driver);
+}
+module_exit(mcde_display_tvout_ab8500_exit);
+
+MODULE_AUTHOR("Marcel Tunnissen <marcel.tuennissen@stericsson.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ST-Ericsson MCDE TVout through AB8500 display driver");
diff --git a/drivers/video/mcde/display-av8100.c b/drivers/video/mcde/display-av8100.c
new file mode 100644
index 00000000000..eb81be54d22
--- /dev/null
+++ b/drivers/video/mcde/display-av8100.c
@@ -0,0 +1,1492 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson HDMI display driver
+ *
+ * Author: Per Persson <per-xb-persson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <video/mcde_display.h>
+#include <video/mcde_display-av8100.h>
+#include <video/av8100.h>
+#include <video/hdmi.h>
+
+#define SWITCH_HELPSTR ", 0=HDMI, 1=SDTV, 2=DVI\n"
+
+struct cea_vesa_video_mode {
+ u32 cea;
+ u32 vesa_cea_nr;
+ struct mcde_video_mode *video_mode;
+};
+
+static int hdmi_try_video_mode(
+ struct mcde_display_device *ddev, struct mcde_video_mode *video_mode);
+static int hdmi_set_video_mode(
+ struct mcde_display_device *ddev, struct mcde_video_mode *video_mode);
+static int hdmi_set_pixel_format(
+ struct mcde_display_device *ddev, enum mcde_ovly_pix_fmt format);
+static struct mcde_video_mode *video_mode_get(struct mcde_display_device *ddev,
+ u8 cea, u8 vesa_cea_nr);
+
+static ssize_t show_hdmisdtvswitch(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_hdmisdtvswitch(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_input_pixel_format(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_input_pixel_format(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_disponoff(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_disponoff(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_vesacea(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t show_timing(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_timing(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t store_stayalive(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(disponoff, S_IRUGO | S_IWUSR, show_disponoff,
+ store_disponoff);
+static DEVICE_ATTR(vesacea, S_IRUGO, show_vesacea, NULL);
+static DEVICE_ATTR(timing, S_IRUGO | S_IWUSR, show_timing, store_timing);
+static DEVICE_ATTR(stayalive, S_IWUSR, NULL, store_stayalive);
+
+static DEVICE_ATTR(hdmisdtvswitch, S_IRUGO | S_IWUSR, show_hdmisdtvswitch,
+ store_hdmisdtvswitch);
+static DEVICE_ATTR(input_pixel_format, S_IRUGO | S_IWUSR,
+ show_input_pixel_format, store_input_pixel_format);
+
+static ssize_t show_hdmisdtvswitch(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mcde_display_device *mdev = to_mcde_display_device(dev);
+ int index;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ sprintf(buf, "%1x%s", mdev->port->hdmi_sdtv_switch, SWITCH_HELPSTR);
+ index = 1 + strlen(SWITCH_HELPSTR) + 1;
+
+ return index;
+}
+
+static ssize_t store_hdmisdtvswitch(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mcde_display_device *mdev = to_mcde_display_device(dev);
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (count > 0) {
+ if ((*buf == 0) || (*buf == '0')) {
+ dev_dbg(dev, "hdmi/sdtv switch = hdmi\n");
+ mdev->port->hdmi_sdtv_switch = HDMI_SWITCH;
+ mdev->native_x_res = NATIVE_XRES_HDMI;
+ mdev->native_y_res = NATIVE_YRES_HDMI;
+ } else if ((*buf == 1) || (*buf == '1')) {
+ dev_dbg(dev, "hdmi/sdtv switch = sdtv\n");
+ mdev->port->hdmi_sdtv_switch = SDTV_SWITCH;
+ mdev->native_x_res = NATIVE_XRES_SDTV;
+ mdev->native_y_res = NATIVE_YRES_SDTV;
+ } else if ((*buf == 2) || (*buf == '2')) {
+ dev_dbg(dev, "hdmi/sdtv switch = dvi\n");
+ mdev->port->hdmi_sdtv_switch = DVI_SWITCH;
+ mdev->native_x_res = NATIVE_XRES_HDMI;
+ mdev->native_y_res = NATIVE_YRES_HDMI;
+ }
+ /* implicitely read by a memcmp in dss */
+ mdev->video_mode.force_update = true;
+ }
+
+ return count;
+}
+
+static ssize_t show_input_pixel_format(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mcde_display_device *ddev = to_mcde_display_device(dev);
+
+ return sprintf(buf, "%d\n", ddev->port->pixel_format);
+}
+
+static ssize_t store_input_pixel_format(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mcde_display_device *ddev = to_mcde_display_device(dev);
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+ if (count > 0) {
+ unsigned long input;
+ if (strict_strtoul(buf, 10, &input) != 0)
+ return -EINVAL;
+ switch (input) {
+ /* intentional fall through */
+ case MCDE_PORTPIXFMT_DSI_16BPP:
+ case MCDE_PORTPIXFMT_DSI_18BPP:
+ case MCDE_PORTPIXFMT_DSI_18BPP_PACKED:
+ case MCDE_PORTPIXFMT_DSI_24BPP:
+ case MCDE_PORTPIXFMT_DSI_YCBCR422:
+ ddev->port->pixel_format = input;
+ break;
+ default:
+ dev_warn(&ddev->dev, "invalid format (%ld)\n",
+ input);
+ return -EINVAL;
+ break;
+ }
+ /* implicitely read by a memcmp in dss */
+ ddev->video_mode.force_update = true;
+ driver_data->update_port_pixel_format = true;
+ }
+
+ return count;
+}
+
+static ssize_t show_disponoff(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mcde_display_device *ddev = to_mcde_display_device(dev);
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (ddev->fbi && driver_data->fbdevname) {
+ dev_dbg(dev, "name:%s\n", driver_data->fbdevname);
+ strcpy(buf, driver_data->fbdevname);
+ return strlen(driver_data->fbdevname) + 1;
+ }
+ return 0;
+}
+
+static ssize_t store_disponoff(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mcde_display_device *mdev = to_mcde_display_device(dev);
+ bool enable = false;
+ u8 cea = 0;
+ u8 vesa_cea_nr = 0;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if ((count != DISPONOFF_SIZE) && (count != DISPONOFF_SIZE + 1))
+ return -EINVAL;
+
+ if ((*buf == '0') && (*(buf + 1) == '1'))
+ enable = true;
+ cea = (hex_to_bin(buf[2]) << 4) + hex_to_bin(buf[3]);
+ vesa_cea_nr = (hex_to_bin(buf[4]) << 4) + hex_to_bin(buf[5]);
+ dev_dbg(dev, "enable:%d cea:%d nr:%d\n", enable, cea, vesa_cea_nr);
+
+ hdmi_fb_onoff(mdev, enable, cea, vesa_cea_nr);
+
+ return count;
+}
+
+static ssize_t show_timing(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mcde_display_device *ddev = to_mcde_display_device(dev);
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+ struct mcde_video_mode *video_mode;
+ int index;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ index = 0;
+ if (driver_data->video_mode) {
+ video_mode = driver_data->video_mode;
+ memcpy(buf + index, &video_mode->xres, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->yres, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->pixclock, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->hbp, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->hfp, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->vbp, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->vfp, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->interlaced, sizeof(u32));
+ index += sizeof(u32);
+ }
+ return index;
+}
+
+static ssize_t store_timing(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mcde_display_device *ddev = to_mcde_display_device(dev);
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (count != TIMING_SIZE)
+ return -EINVAL;
+
+ driver_data->video_mode = video_mode_get(ddev, *buf, *(buf + 1));
+
+ return count;
+}
+
+static ssize_t store_stayalive(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mcde_display_device *ddev = to_mcde_display_device(dev);
+
+ if (count != STAYALIVE_SIZE)
+ return -EINVAL;
+
+ if ((*buf == 1) || (*buf == '1'))
+ ddev->stay_alive = true;
+ else
+ ddev->stay_alive = false;
+
+ dev_dbg(dev, "%s %d\n", __func__, ddev->stay_alive);
+
+ return count;
+}
+
+static int ceanr_convert(struct mcde_display_device *ddev,
+ u8 cea, u8 vesa_cea_nr, int buffering,
+ u16 *w, u16 *h, u16 *vw, u16 *vh)
+{
+ struct mcde_video_mode *video_mode;
+
+ dev_dbg(&ddev->dev, "%s\n", __func__);
+ video_mode = video_mode_get(ddev, cea, vesa_cea_nr);
+ if (video_mode) {
+ *w = video_mode->xres;
+ *h = video_mode->yres;
+ *vw = video_mode->xres;
+ *vh = video_mode->yres * buffering;
+ dev_dbg(&ddev->dev, "cea:%d nr:%d found\n",
+ cea, vesa_cea_nr);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/* Supported HDMI modes */
+static struct mcde_video_mode video_modes_supp_hdmi[] = {
+ /* 0 CEA #1 640_480_60_P */
+ {
+ .xres = 640, .yres = 480,
+ .pixclock = 39682,
+ .hbp = 112, .hfp = 48,
+ .vbp = 33, .vfp = 12
+ },
+ /* 1 720_480_60_P */
+ {
+ .xres = 720, .yres = 480,
+ .pixclock = 37000,
+ .hbp = 104, .hfp = 34,
+ .vbp = 30, .vfp = 15
+ },
+ /* 2 720_576_50_P */
+ {
+ .xres = 720, .yres = 576,
+ .pixclock = 37037,
+ .hbp = 132, .hfp = 12,
+ .vbp = 44, .vfp = 5
+ },
+ /* 3 1280_720_60_P */
+ {
+ .xres = 1280, .yres = 720,
+ .pixclock = 13468,
+ .hbp = 256, .hfp = 114,
+ .vbp = 20, .vfp = 10
+ },
+ /* 4 1280_720_50_P */
+ {
+ .xres = 1280, .yres = 720,
+ .pixclock = 13468,
+ .hbp = 260, .hfp = 440,
+ .vbp = 25, .vfp = 5
+ },
+ /* 5 1280_720_30_P */
+ {
+ .xres = 1280, .yres = 720,
+ .pixclock = 13468,
+ .hbp = 260, .hfp = 1760,
+ .vbp = 20, .vfp = 10
+ },
+ /* 6 1280_720_24_P */
+ {
+ .xres = 1280, .yres = 720,
+ .pixclock = 16835,
+ .hbp = 260, .hfp = 1760,
+ .vbp = 20, .vfp = 10
+ },
+ /* 7 1280_720_25_P */
+ {
+ .xres = 1280, .yres = 720,
+ .pixclock = 13468,
+ .hbp = 260, .hfp = 2420,
+ .vbp = 20, .vfp = 10
+ },
+ /* 8 1920_1080_30_P */
+ {
+ .xres = 1920, .yres = 1080,
+ .pixclock = 13468,
+ .hbp = 189, .hfp = 91,
+ .vbp = 36, .vfp = 9
+ },
+ /* 9 1920_1080_24_P */
+ {
+ .xres = 1920, .yres = 1080,
+ .pixclock = 13468,
+ .hbp = 170, .hfp = 660,
+ .vbp = 36, .vfp = 9
+ },
+ /* 10 1920_1080_25_P */
+ {
+ .xres = 1920, .yres = 1080,
+ .pixclock = 13468,
+ .hbp = 192, .hfp = 528,
+ .vbp = 36, .vfp = 9
+ },
+ /* 11 720_480_60_I */
+ {
+ .xres = 720, .yres = 480,
+ .pixclock = 74074,
+ .hbp = 126, .hfp = 12,
+ .vbp = 44, .vfp = 1,
+ .interlaced = true,
+ },
+ /* 12 720_576_50_I */
+ {
+ .xres = 720, .yres = 576,
+ .pixclock = 74074,
+ .hbp = 132, .hfp = 12,
+ .vbp = 44, .vfp = 5,
+ .interlaced = true,
+ },
+ /* 13 1920_1080_50_I */
+ {
+ .xres = 1920, .yres = 1080,
+ .pixclock = 13468,
+ .hbp = 192, .hfp = 528,
+ .vbp = 20, .vfp = 25,
+ .interlaced = true,
+ },
+ /* 14 1920_1080_60_I */
+ {
+ .xres = 1920, .yres = 1080,
+ .pixclock = 13468,
+ .hbp = 192, .hfp = 88,
+ .vbp = 20, .vfp = 25,
+ .interlaced = true,
+ },
+ /* 15 VESA #9 800_600_60_P */
+ {
+ .xres = 800, .yres = 600,
+ .pixclock = 25000,
+ .hbp = 168, .hfp = 88,
+ .vbp = 23, .vfp = 5,
+ .interlaced = false,
+ },
+ /* 16 VESA #14 848_480_60_P */
+ {
+ .xres = 848, .yres = 480,
+ .pixclock = 29630,
+ .hbp = 128, .hfp = 112,
+ .vbp = 23, .vfp = 14,
+ .interlaced = false,
+ },
+ /* 17 VESA #16 1024_768_60_P */
+ {
+ .xres = 1024, .yres = 768,
+ .pixclock = 15385,
+ .hbp = 160, .hfp = 160,
+ .vbp = 29, .vfp = 9,
+ .interlaced = false,
+ },
+ /* 18 VESA #22 1280_768_60_P */
+ {
+ .xres = 1280, .yres = 768,
+ .pixclock = 14652,
+ .hbp = 80, .hfp = 80,
+ .vbp = 12, .vfp = 10,
+ .interlaced = false,
+ },
+ /* 19 VESA #23 1280_768_60_P */
+ {
+ .xres = 1280, .yres = 768,
+ .pixclock = 12579,
+ .hbp = 192, .hfp = 192,
+ .vbp = 20, .vfp = 10,
+ .interlaced = false,
+ },
+ /* 20 VESA #27 1280_800_60_P */
+ {
+ .xres = 1280, .yres = 800,
+ .pixclock = 14085,
+ .hbp = 80, .hfp = 80,
+ .vbp = 14, .vfp = 9,
+ .interlaced = false,
+ },
+ /* 21 VESA #28 1280_800_60_P */
+ {
+ .xres = 1280, .yres = 800,
+ .pixclock = 11976,
+ .hbp = 200, .hfp = 200,
+ .vbp = 22, .vfp = 9,
+ .interlaced = false,
+ },
+ /* 22 VESA #39 1360_768_60_P */
+ {
+ .xres = 1360, .yres = 768,
+ .pixclock = 11696,
+ .hbp = 176, .hfp = 256,
+ .vbp = 18, .vfp = 9,
+ .interlaced = false,
+ },
+ /* 23 VESA #81 1366_768_60_P */
+ {
+ .xres = 1366, .yres = 768,
+ .pixclock = 11662,
+ .hbp = 213, .hfp = 213,
+ .vbp = 24, .vfp = 6,
+ .interlaced = false,
+ },
+};
+
+/* Supported TVout modes */
+static struct mcde_video_mode video_modes_supp_sdtv[] = {
+ /* 720_480_60_I) */
+ {
+ .xres = 720, .yres = 480,
+ .pixclock = 74074,
+ .hbp = 126, .hfp = 12,
+ .vbp = 44, .vfp = 1,
+ .interlaced = true,
+ },
+ /* 720_576_50_I) */
+ {
+ .xres = 720, .yres = 576,
+ .pixclock = 74074,
+ .hbp = 132, .hfp = 12,
+ .vbp = 44, .vfp = 5,
+ .interlaced = true,
+ },
+};
+
+static struct cea_vesa_video_mode cea_vesa_video_mode[] = {
+ /* 640_480_60_P */
+ {
+ .cea = 1, .vesa_cea_nr = 1,
+ .video_mode = &video_modes_supp_hdmi[0],
+ },
+ /* 720_480_60_P */
+ {
+ .cea = 1, .vesa_cea_nr = 2,
+ .video_mode = &video_modes_supp_hdmi[1],
+ },
+ /* 720_480_60_P */
+ {
+ .cea = 1, .vesa_cea_nr = 3,
+ .video_mode = &video_modes_supp_hdmi[1],
+ },
+ /* 720_576_50_P */
+ {
+ .cea = 1, .vesa_cea_nr = 17,
+ .video_mode = &video_modes_supp_hdmi[2],
+ },
+ /* 720_576_50_P */
+ {
+ .cea = 1, .vesa_cea_nr = 18,
+ .video_mode = &video_modes_supp_hdmi[2],
+ },
+ /* 1280_720_60_P */
+ {
+ .cea = 1, .vesa_cea_nr = 4,
+ .video_mode = &video_modes_supp_hdmi[3],
+ },
+ /* 1280_720_50_P */
+ {
+ .cea = 1, .vesa_cea_nr = 19,
+ .video_mode = &video_modes_supp_hdmi[4],
+ },
+ /* 1280_720_30_P */
+ {
+ .cea = 1, .vesa_cea_nr = 62,
+ .video_mode = &video_modes_supp_hdmi[5],
+ },
+ /* 1280_720_24_P */
+ {
+ .cea = 1, .vesa_cea_nr = 60,
+ .video_mode = &video_modes_supp_hdmi[6],
+ },
+ /* 1280_720_25_P */
+ {
+ .cea = 1, .vesa_cea_nr = 61,
+ .video_mode = &video_modes_supp_hdmi[7],
+ },
+ /* 1920_1080_30_P */
+ {
+ .cea = 1, .vesa_cea_nr = 34,
+ .video_mode = &video_modes_supp_hdmi[8],
+ },
+ /* 1920_1080_24_P */
+ {
+ .cea = 1, .vesa_cea_nr = 32,
+ .video_mode = &video_modes_supp_hdmi[9],
+ },
+ /* 1920_1080_25_P */
+ {
+ .cea = 1, .vesa_cea_nr = 33,
+ .video_mode = &video_modes_supp_hdmi[10],
+ },
+ /* 720_480_60_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 6,
+ .video_mode = &video_modes_supp_hdmi[11],
+ },
+ /* 720_480_60_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 7,
+ .video_mode = &video_modes_supp_hdmi[11],
+ },
+ /* 720_576_50_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 21,
+ .video_mode = &video_modes_supp_hdmi[12],
+ },
+ /* 720_576_50_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 22,
+ .video_mode = &video_modes_supp_hdmi[12],
+ },
+ /* 1920_1080_50_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 20,
+ .video_mode = &video_modes_supp_hdmi[13],
+ },
+ /* 1920_1080_60_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 5,
+ .video_mode = &video_modes_supp_hdmi[14],
+ },
+ /* VESA #4 640_480_60_P) */
+ {
+ .cea = 0, .vesa_cea_nr = 4,
+ .video_mode = &video_modes_supp_hdmi[0],
+ },
+ /* VESA #9 800_600_60_P) */
+ {
+ .cea = 0, .vesa_cea_nr = 9,
+ .video_mode = &video_modes_supp_hdmi[15],
+ },
+ /* VESA #14 848_480_60_P) */
+ {
+ .cea = 0, .vesa_cea_nr = 14,
+ .video_mode = &video_modes_supp_hdmi[16],
+ },
+ /* VESA #16 1024_768_60_P) */
+ {
+ .cea = 0, .vesa_cea_nr = 16,
+ .video_mode = &video_modes_supp_hdmi[17],
+ },
+ /* VESA #22 1280_768_60_P) */
+ {
+ .cea = 0, .vesa_cea_nr = 22,
+ .video_mode = &video_modes_supp_hdmi[18],
+ },
+ /* VESA #23 1280_768_60_P) */
+ {
+ .cea = 0, .vesa_cea_nr = 23,
+ .video_mode = &video_modes_supp_hdmi[19],
+ },
+ /* VESA #27 1280_800_60_P) */
+ {
+ .cea = 0, .vesa_cea_nr = 27,
+ .video_mode = &video_modes_supp_hdmi[20],
+ },
+ /* VESA #28 1280_800_60_P) */
+ {
+ .cea = 0, .vesa_cea_nr = 28,
+ .video_mode = &video_modes_supp_hdmi[21],
+ },
+ /* VESA #39 1360_768_60_P) */
+ {
+ .cea = 0, .vesa_cea_nr = 39,
+ .video_mode = &video_modes_supp_hdmi[22],
+ },
+ /* VESA #81 1366_768_60_P) */
+ {
+ .cea = 0, .vesa_cea_nr = 81,
+ .video_mode = &video_modes_supp_hdmi[23],
+ },
+};
+
+static ssize_t show_vesacea(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int findex;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ for (findex = 0; findex < ARRAY_SIZE(cea_vesa_video_mode); findex++) {
+ *(buf + findex * 2) = cea_vesa_video_mode[findex].cea;
+ *(buf + findex * 2 + 1) =
+ cea_vesa_video_mode[findex].vesa_cea_nr;
+ }
+ *(buf + findex * 2) = '\0';
+
+ return findex * 2 + 1;
+}
+
+static struct mcde_video_mode *video_mode_get(struct mcde_display_device *ddev,
+ u8 cea, u8 vesa_cea_nr)
+{
+ int findex;
+
+ dev_dbg(&ddev->dev, "%s\n", __func__);
+
+ for (findex = 0; findex < ARRAY_SIZE(cea_vesa_video_mode); findex++)
+ if ((cea == cea_vesa_video_mode[findex].cea) &&
+ (vesa_cea_nr ==
+ cea_vesa_video_mode[findex].vesa_cea_nr)) {
+ dev_dbg(&ddev->dev, "cea:%d nr:%d\n", cea, vesa_cea_nr);
+ return cea_vesa_video_mode[findex].video_mode;
+ }
+
+ return NULL;
+}
+
+#define AV8100_MAX_LEVEL 255
+
+static int hdmi_try_video_mode(
+ struct mcde_display_device *ddev, struct mcde_video_mode *video_mode)
+{
+ int index = 0;
+ int match_level = AV8100_MAX_LEVEL;
+ int found_index = -1;
+ struct mcde_video_mode *video_modes_supp;
+ int array_size;
+
+ if (ddev == NULL || video_mode == NULL) {
+ pr_warning("%s:ddev = NULL or video_mode = NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_vdbg(&ddev->dev, "%s\n", __func__);
+
+ if (ddev->port->hdmi_sdtv_switch == SDTV_SWITCH) {
+ video_mode->interlaced = true;
+ video_modes_supp = video_modes_supp_sdtv;
+ array_size = ARRAY_SIZE(video_modes_supp_sdtv);
+ } else {
+ video_modes_supp = video_modes_supp_hdmi;
+ array_size = ARRAY_SIZE(video_modes_supp_hdmi);
+ }
+
+ while (index < array_size) {
+ /* 1. Check if all parameters match */
+ if ((video_mode->xres == video_modes_supp[index].xres) &&
+ (video_mode->yres == video_modes_supp[index].yres) &&
+ ((video_mode->xres + video_mode->hbp +
+ video_mode->hfp) ==
+ (video_modes_supp[index].xres +
+ video_modes_supp[index].hbp +
+ video_modes_supp[index].hfp)) &&
+ ((video_mode->yres + video_mode->vbp + video_mode->vfp)
+ ==
+ (video_modes_supp[index].yres +
+ video_modes_supp[index].vbp +
+ video_modes_supp[index].vfp)) &&
+ (video_mode->pixclock ==
+ video_modes_supp[index].pixclock) &&
+ (video_mode->interlaced ==
+ video_modes_supp[index].interlaced)) {
+ match_level = 1;
+ found_index = index;
+ break;
+ }
+
+ /* 2. Check if xres,yres,htot,vtot,interlaced match */
+ if ((match_level > 2) &&
+ (video_mode->xres == video_modes_supp[index].xres) &&
+ (video_mode->yres == video_modes_supp[index].yres) &&
+ ((video_mode->xres + video_mode->hbp +
+ video_mode->hfp) ==
+ (video_modes_supp[index].xres +
+ video_modes_supp[index].hbp +
+ video_modes_supp[index].hfp)) &&
+ ((video_mode->yres + video_mode->vbp + video_mode->vfp)
+ ==
+ (video_modes_supp[index].yres +
+ video_modes_supp[index].vbp +
+ video_modes_supp[index].vfp)) &&
+ (video_mode->interlaced ==
+ video_modes_supp[index].interlaced)) {
+ match_level = 2;
+ found_index = index;
+ }
+
+ /* 3. Check if xres,yres,pixelclock,interlaced match */
+ if ((match_level > 3) &&
+ (video_mode->xres == video_modes_supp[index].xres) &&
+ (video_mode->yres == video_modes_supp[index].yres) &&
+ (video_mode->interlaced ==
+ video_modes_supp[index].interlaced) &&
+ (video_mode->pixclock ==
+ video_modes_supp[index].pixclock)) {
+ match_level = 3;
+ found_index = index;
+ }
+
+ /* 4. Check if xres,yres,interlaced match */
+ if ((match_level > 4) &&
+ (video_mode->xres == video_modes_supp[index].xres) &&
+ (video_mode->yres == video_modes_supp[index].yres) &&
+ (video_mode->interlaced ==
+ video_modes_supp[index].interlaced)) {
+ match_level = 4;
+ found_index = index;
+ }
+
+ index++;
+ }
+
+ if (found_index == -1) {
+ dev_dbg(&ddev->dev, "video_mode not accepted\n");
+ dev_dbg(&ddev->dev, "xres:%d yres:%d pixclock:%d hbp:%d hfp:%d "
+ "vfp:%d vbp:%d intlcd:%d\n",
+ video_mode->xres, video_mode->yres,
+ video_mode->pixclock,
+ video_mode->hbp, video_mode->hfp,
+ video_mode->vfp, video_mode->vbp,
+ video_mode->interlaced);
+ return -EINVAL;
+ }
+
+ memset(video_mode, 0, sizeof(struct mcde_video_mode));
+ memcpy(video_mode, &video_modes_supp[found_index],
+ sizeof(struct mcde_video_mode));
+
+ dev_dbg(&ddev->dev, "%s:HDMI video_mode %d chosen. Level:%d\n",
+ __func__, found_index, match_level);
+
+ return 0;
+}
+
+static int hdmi_set_video_mode(
+ struct mcde_display_device *dev, struct mcde_video_mode *video_mode)
+{
+ int ret;
+ union av8100_configuration av8100_config;
+ struct mcde_display_hdmi_platform_data *pdata = dev->dev.platform_data;
+ struct display_driver_data *driver_data = dev_get_drvdata(&dev->dev);
+ struct av8100_status status;
+
+ /* TODO check video_mode_params */
+ if (dev == NULL || video_mode == NULL) {
+ pr_warning("%s:ddev = NULL or video_mode = NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(&dev->dev, "%s:\n", __func__);
+ dev_vdbg(&dev->dev, "%s:xres:%d yres:%d hbp:%d hfp:%d vbp:%d vfp:%d "
+ "interlaced:%d\n", __func__,
+ video_mode->xres,
+ video_mode->yres,
+ video_mode->hbp,
+ video_mode->hfp,
+ video_mode->vbp,
+ video_mode->vfp,
+ video_mode->interlaced);
+
+ if (driver_data->update_port_pixel_format) {
+ hdmi_set_pixel_format(dev, dev->pixel_format);
+ driver_data->update_port_pixel_format = false;
+ }
+
+ memset(&(dev->video_mode), 0, sizeof(struct mcde_video_mode));
+ memcpy(&(dev->video_mode), video_mode, sizeof(struct mcde_video_mode));
+
+ if (dev->port->pixel_format == MCDE_PORTPIXFMT_DSI_YCBCR422)
+ mcde_chnl_set_col_convert(dev->chnl_state,
+ &pdata->rgb_2_yCbCr_transform);
+ mcde_chnl_stop_flow(dev->chnl_state);
+
+ ret = mcde_chnl_set_video_mode(dev->chnl_state, &dev->video_mode);
+ if (ret < 0) {
+ dev_warn(&dev->dev, "Failed to set video mode\n");
+ return ret;
+ }
+
+ status = av8100_status_get();
+ if (status.av8100_state == AV8100_OPMODE_UNDEFINED)
+ return -EINVAL;
+
+ if (av8100_ver_get() == AV8100_CHIPVER_1) {
+ if (status.av8100_state >= AV8100_OPMODE_STANDBY) {
+ /* Disable interrupts */
+ ret = av8100_disable_interrupt();
+ if (ret) {
+ dev_err(&dev->dev,
+ "%s:av8100_disable_interrupt failed\n",
+ __func__);
+ return ret;
+ }
+
+ ret = av8100_powerdown();
+ if (ret) {
+ dev_err(&dev->dev,
+ "av8100_powerdown failed\n");
+ return ret;
+ }
+
+ msleep(10);
+ }
+ }
+
+ /* Set to powerup with interrupts disabled */
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_STANDBY) {
+ ret = av8100_powerup();
+ if (ret) {
+ dev_err(&dev->dev, "av8100_powerup failed\n");
+ return ret;
+ }
+ }
+
+ if (status.av8100_state < AV8100_OPMODE_IDLE) {
+ ret = av8100_download_firmware(NULL, 0, I2C_INTERFACE);
+ if (ret) {
+ dev_err(&dev->dev, "av8100_download_firmware failed\n");
+ av8100_powerdown();
+ return ret;
+ }
+ }
+
+ if (av8100_disable_interrupt())
+ return -EFAULT;
+
+ /*
+ * Don't look at dev->port->hdmi_sdtv_switch; it states only which
+ * one should be started, not which one is currently working
+ */
+ if (av8100_conf_get(AV8100_COMMAND_HDMI, &av8100_config))
+ return -EFAULT;
+ if (av8100_config.hdmi_format.hdmi_mode == AV8100_HDMI_ON) {
+ /* Set HDMI mode to OFF */
+ av8100_config.hdmi_format.hdmi_mode = AV8100_HDMI_OFF;
+ av8100_config.hdmi_format.dvi_format = AV8100_DVI_CTRL_CTL0;
+ av8100_config.hdmi_format.hdmi_format = AV8100_HDMI;
+ if (av8100_conf_prep(AV8100_COMMAND_HDMI, &av8100_config))
+ return -EFAULT;
+
+ if (av8100_conf_w(AV8100_COMMAND_HDMI, NULL, NULL,
+ I2C_INTERFACE))
+ return -EFAULT;
+ }
+ if (av8100_conf_get(AV8100_COMMAND_DENC, &av8100_config))
+ return -EFAULT;
+ if (av8100_config.denc_format.enable) {
+ /* Turn off DENC */
+ av8100_config.denc_format.enable = 0;
+ if (av8100_conf_prep(AV8100_COMMAND_DENC, &av8100_config))
+ return -EFAULT;
+ if (av8100_conf_w(AV8100_COMMAND_DENC, NULL, NULL,
+ I2C_INTERFACE))
+ return -EFAULT;
+ }
+
+ /* Get current av8100 video output format */
+ ret = av8100_conf_get(AV8100_COMMAND_VIDEO_OUTPUT_FORMAT,
+ &av8100_config);
+ if (ret) {
+ dev_err(&dev->dev, "%s:av8100_conf_get "
+ "AV8100_COMMAND_VIDEO_OUTPUT_FORMAT failed\n",
+ __func__);
+ return ret;
+ }
+
+ if (dev->port->hdmi_sdtv_switch == SDTV_SWITCH)
+ av8100_config.video_output_format.video_output_cea_vesa =
+ dev->video_mode.yres == NATIVE_YRES_SDTV ?
+ AV8100_CEA21_22_576I_PAL_50HZ :
+ AV8100_CEA6_7_NTSC_60HZ;
+ else
+ av8100_config.video_output_format.video_output_cea_vesa =
+ av8100_video_output_format_get(
+ dev->video_mode.xres,
+ dev->video_mode.yres,
+ dev->video_mode.xres +
+ dev->video_mode.hbp + dev->video_mode.hfp,
+ dev->video_mode.yres +
+ dev->video_mode.vbp + dev->video_mode.vfp,
+ dev->video_mode.pixclock,
+ dev->video_mode.interlaced);
+
+ if (AV8100_VIDEO_OUTPUT_CEA_VESA_MAX ==
+ av8100_config.video_output_format.video_output_cea_vesa) {
+ dev_err(&dev->dev, "%s:video output format not found "
+ "\n", __func__);
+ return ret;
+ }
+
+ ret = av8100_conf_prep(AV8100_COMMAND_VIDEO_OUTPUT_FORMAT,
+ &av8100_config);
+ if (ret) {
+ dev_err(&dev->dev, "%s:av8100_conf_prep "
+ "AV8100_COMMAND_VIDEO_OUTPUT_FORMAT failed\n",
+ __func__);
+ return ret;
+ }
+
+ /* Get current av8100 video input format */
+ ret = av8100_conf_get(AV8100_COMMAND_VIDEO_INPUT_FORMAT,
+ &av8100_config);
+ if (ret) {
+ dev_err(&dev->dev, "%s:av8100_conf_get "
+ "AV8100_COMMAND_VIDEO_INPUT_FORMAT failed\n",
+ __func__);
+ return ret;
+ }
+
+ /* Set correct av8100 video input pixel format */
+ switch (dev->port->pixel_format) {
+ case MCDE_PORTPIXFMT_DSI_16BPP:
+ default:
+ av8100_config.video_input_format.input_pixel_format =
+ AV8100_INPUT_PIX_RGB565;
+ break;
+ case MCDE_PORTPIXFMT_DSI_18BPP:
+ av8100_config.video_input_format.input_pixel_format =
+ AV8100_INPUT_PIX_RGB666;
+ break;
+ case MCDE_PORTPIXFMT_DSI_18BPP_PACKED:
+ av8100_config.video_input_format.input_pixel_format =
+ AV8100_INPUT_PIX_RGB666P;
+ break;
+ case MCDE_PORTPIXFMT_DSI_24BPP:
+ av8100_config.video_input_format.input_pixel_format =
+ AV8100_INPUT_PIX_RGB888;
+ break;
+ case MCDE_PORTPIXFMT_DSI_YCBCR422:
+ av8100_config.video_input_format.input_pixel_format =
+ AV8100_INPUT_PIX_YCBCR422;
+ break;
+ }
+
+ /* Set ui_x4 */
+ av8100_config.video_input_format.ui_x4 = dev->port->phy.dsi.ui;
+
+ /* Set TE_config */
+ switch (dev->port->sync_src) {
+ case MCDE_SYNCSRC_TE0:
+ av8100_config.video_input_format.TE_config = AV8100_TE_IT_LINE;
+ break;
+ case MCDE_SYNCSRC_TE1:
+ av8100_config.video_input_format.TE_config = AV8100_TE_GPIO_IT;
+ break;
+ case MCDE_SYNCSRC_TE_POLLING:
+ av8100_config.video_input_format.TE_config =
+ AV8100_TE_DSI_LANE; /* Only on DSI, no interrupts */
+ break;
+ case MCDE_SYNCSRC_OFF:
+ default:
+ av8100_config.video_input_format.TE_config = AV8100_TE_OFF;
+ break;
+ }
+
+ ret = av8100_conf_prep(AV8100_COMMAND_VIDEO_INPUT_FORMAT,
+ &av8100_config);
+ if (ret) {
+ dev_err(&dev->dev, "%s:av8100_conf_prep "
+ "AV8100_COMMAND_VIDEO_INPUT_FORMAT failed\n",
+ __func__);
+ return ret;
+ }
+
+ ret = av8100_conf_w(AV8100_COMMAND_VIDEO_INPUT_FORMAT,
+ NULL, NULL, I2C_INTERFACE);
+ if (ret) {
+ dev_err(&dev->dev, "%s:av8100_conf_w "
+ "AV8100_COMMAND_VIDEO_INPUT_FORMAT failed\n",
+ __func__);
+ return ret;
+ }
+
+ if (dev->port->hdmi_sdtv_switch == SDTV_SWITCH) {
+ if (dev->port->pixel_format != MCDE_PORTPIXFMT_DSI_YCBCR422)
+ av8100_config.color_transform =
+ AV8100_COLOR_TRANSFORM_RGB_TO_DENC;
+ else
+ av8100_config.color_transform =
+ AV8100_COLOR_TRANSFORM_YUV_TO_DENC;
+ } else if (dev->port->pixel_format == MCDE_PORTPIXFMT_DSI_YCBCR422) {
+ av8100_config.color_transform =
+ AV8100_COLOR_TRANSFORM_YUV_TO_RGB;
+ } else {
+ av8100_config.color_transform =
+ AV8100_COLOR_TRANSFORM_INDENTITY;
+ }
+
+ ret = av8100_conf_prep(
+ AV8100_COMMAND_COLORSPACECONVERSION,
+ &av8100_config);
+ if (ret) {
+ dev_err(&dev->dev, "%s:av8100_configuration_prepare "
+ "AV8100_COMMAND_COLORSPACECONVERSION failed\n",
+ __func__);
+ return ret;
+ }
+
+ ret = av8100_conf_w(
+ AV8100_COMMAND_COLORSPACECONVERSION,
+ NULL, NULL, I2C_INTERFACE);
+ if (ret) {
+ dev_err(&dev->dev, "%s:av8100_conf_w "
+ "AV8100_COMMAND_COLORSPACECONVERSION failed\n",
+ __func__);
+ return ret;
+ }
+
+ /* Set video output format */
+ ret = av8100_conf_w(AV8100_COMMAND_VIDEO_OUTPUT_FORMAT,
+ NULL, NULL, I2C_INTERFACE);
+ if (ret) {
+ dev_err(&dev->dev, "av8100_conf_w failed\n");
+ return ret;
+ }
+
+ /* Set audio input format */
+ ret = av8100_conf_w(AV8100_COMMAND_AUDIO_INPUT_FORMAT,
+ NULL, NULL, I2C_INTERFACE);
+ if (ret) {
+ dev_err(&dev->dev, "%s:av8100_conf_w "
+ "AV8100_COMMAND_AUDIO_INPUT_FORMAT failed\n",
+ __func__);
+ return ret;
+ }
+
+ dev->update_flags |= UPDATE_FLAG_VIDEO_MODE;
+ dev->first_update = true;
+
+ return 0;
+}
+
+static u16 rotate_byte_left(u8 c, int nr)
+{
+ return (0xff & (c << nr)) | (0xff & (c >> (8 - nr)));
+}
+
+static u16 map_yv(u8 in)
+{
+ return rotate_byte_left(in, 3) << 4;
+}
+
+static u16 map_u(u8 in)
+{
+ return rotate_byte_left(in, 5) << 4;
+}
+
+static int hdmi_set_pixel_format(
+ struct mcde_display_device *ddev, enum mcde_ovly_pix_fmt format)
+{
+ dev_dbg(&ddev->dev, "%s\n", __func__);
+ ddev->pixel_format = format;
+
+ return 0;
+}
+
+static int hdmi_set_port_pixel_format(struct mcde_display_device *ddev)
+{
+ int ret;
+
+ dev_dbg(&ddev->dev, "%s\n", __func__);
+ mcde_chnl_stop_flow(ddev->chnl_state);
+ ret = mcde_chnl_set_pixel_format(ddev->chnl_state,
+ ddev->port->pixel_format);
+
+ if (ret < 0) {
+ dev_warn(&ddev->dev, "%s: Failed to set pixel format = %d\n",
+ __func__, ddev->port->pixel_format);
+ return ret;
+ }
+
+ if (ddev->port->pixel_format == MCDE_PORTPIXFMT_DSI_YCBCR422 &&
+ av8100_ver_get() == 2) {
+ /* The V2 version has an error for unpacking YUV422 */
+ struct mcde_palette_table palette = {
+ .map_col_ch0 = *map_yv,
+ .map_col_ch1 = *map_u,
+ .map_col_ch2 = *map_yv,
+ };
+ ret = mcde_chnl_set_palette(ddev->chnl_state, &palette);
+ } else {
+ ret = mcde_chnl_set_palette(ddev->chnl_state, NULL);
+ }
+
+ return 0;
+}
+
+static int hdmi_apply_config(struct mcde_display_device *ddev)
+{
+ int ret;
+
+ if (!ddev->update_flags)
+ return 0;
+
+ ret = mcde_chnl_apply(ddev->chnl_state);
+ if (ret < 0) {
+ dev_warn(&ddev->dev, "%s:Failed to apply to channel\n",
+ __func__);
+ return ret;
+ }
+ ddev->update_flags = 0;
+
+ return 0;
+}
+
+static int hdmi_on_first_update(struct mcde_display_device *dev)
+{
+ int ret;
+ union av8100_configuration av8100_config;
+
+ dev->first_update = false;
+
+ /*
+ * Prepare HDMI configuration
+ * Avoid simultaneous output of DENC and HDMI/DVI.
+ * Only one of them should be enabled.
+ * Note HDMI/DVI and DENC are always turned off in set_video_mode.
+ */
+ switch (dev->port->hdmi_sdtv_switch) {
+ case SDTV_SWITCH:
+ if (av8100_conf_get(AV8100_COMMAND_DENC, &av8100_config))
+ return -EFAULT;
+ av8100_config.denc_format.enable = 1;
+ if (dev->video_mode.yres == NATIVE_YRES_SDTV) {
+ av8100_config.denc_format.standard_selection =
+ AV8100_PAL_BDGHI;
+ av8100_config.denc_format.cvbs_video_format =
+ AV8100_CVBS_625;
+ } else {
+ av8100_config.denc_format.standard_selection =
+ AV8100_NTSC_M;
+ av8100_config.denc_format.cvbs_video_format =
+ AV8100_CVBS_525;
+ }
+ ret = av8100_conf_prep(AV8100_COMMAND_DENC, &av8100_config);
+ break;
+ case DVI_SWITCH:
+ av8100_config.hdmi_format.hdmi_mode = AV8100_HDMI_ON;
+ av8100_config.hdmi_format.hdmi_format = AV8100_DVI;
+ av8100_config.hdmi_format.dvi_format = AV8100_DVI_CTRL_CTL0;
+ ret = av8100_conf_prep(AV8100_COMMAND_HDMI, &av8100_config);
+ break;
+ case HDMI_SWITCH:
+ default:
+ av8100_config.hdmi_format.hdmi_mode = AV8100_HDMI_ON;
+ av8100_config.hdmi_format.hdmi_format = AV8100_HDMI;
+ av8100_config.hdmi_format.dvi_format = AV8100_DVI_CTRL_CTL0;
+ ret = av8100_conf_prep(AV8100_COMMAND_HDMI, &av8100_config);
+ break;
+ }
+
+ if (ret) {
+ dev_err(&dev->dev, "%s:av8100_conf_prep "
+ "AV8100_COMMAND_HDMI/DENC failed\n", __func__);
+ return ret;
+ }
+
+ /* Enable interrupts */
+ ret = av8100_enable_interrupt();
+ if (ret) {
+ dev_err(&dev->dev, "%s:av8100_enable_interrupt failed\n",
+ __func__);
+ return ret;
+ }
+
+ if (dev->port->hdmi_sdtv_switch == SDTV_SWITCH)
+ ret = av8100_conf_w(AV8100_COMMAND_DENC, NULL, NULL,
+ I2C_INTERFACE);
+ else
+ ret = av8100_conf_w(AV8100_COMMAND_HDMI, NULL, NULL,
+ I2C_INTERFACE);
+ if (ret) {
+ dev_err(&dev->dev, "%s:av8100_conf_w "
+ "AV8100_COMMAND_HDMI/DENC failed\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int hdmi_set_power_mode(struct mcde_display_device *ddev,
+ enum mcde_display_power_mode power_mode)
+{
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+ int ret = 0;
+
+ /* OFF -> STANDBY */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_OFF &&
+ power_mode != MCDE_DISPLAY_PM_OFF) {
+ if (ddev->platform_enable) {
+ ret = ddev->platform_enable(ddev);
+ if (ret)
+ return ret;
+ }
+
+ ret = av8100_powerup();
+ if (ret) {
+ dev_err(&ddev->dev, "av8100_powerup failed\n");
+ return ret;
+ }
+ av8100_enable_interrupt();
+
+ /*
+ * the regulator for analog TV out is only enabled here,
+ * this means that one needs to switch to the OFF state
+ * to be able to switch from HDMI to CVBS.
+ */
+ if (ddev->port->hdmi_sdtv_switch == SDTV_SWITCH) {
+ ret = regulator_enable(driver_data->cvbs_regulator);
+ if (ret)
+ return ret;
+ driver_data->cvbs_regulator_enabled = true;
+ }
+ ddev->power_mode = MCDE_DISPLAY_PM_STANDBY;
+
+ hdmi_set_port_pixel_format(ddev);
+ }
+ /* STANDBY -> ON */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_STANDBY &&
+ power_mode == MCDE_DISPLAY_PM_ON) {
+
+ ddev->power_mode = MCDE_DISPLAY_PM_ON;
+ goto set_power_and_exit;
+ }
+ /* ON -> STANDBY */
+ else if (ddev->power_mode == MCDE_DISPLAY_PM_ON &&
+ power_mode <= MCDE_DISPLAY_PM_STANDBY) {
+ ddev->power_mode = MCDE_DISPLAY_PM_STANDBY;
+ }
+
+ /* STANDBY -> OFF */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_STANDBY &&
+ power_mode == MCDE_DISPLAY_PM_OFF) {
+ memset(&(ddev->video_mode), 0, sizeof(struct mcde_video_mode));
+ ret = av8100_powerdown();
+ if (ret)
+ dev_err(&ddev->dev, "%s:av8100_powerdown failed\n"
+ , __func__);
+ if (ddev->platform_disable) {
+ ret = ddev->platform_disable(ddev);
+ if (ret)
+ return ret;
+ }
+ if (driver_data->cvbs_regulator_enabled) {
+ ret = regulator_disable(driver_data->cvbs_regulator);
+ if (ret)
+ return ret;
+ driver_data->cvbs_regulator_enabled = false;
+ }
+ ddev->power_mode = MCDE_DISPLAY_PM_OFF;
+ }
+
+set_power_and_exit:
+ mcde_chnl_set_power_mode(ddev->chnl_state, ddev->power_mode);
+
+ return ret;
+}
+
+static int __devinit hdmi_probe(struct mcde_display_device *dev)
+{
+ int ret = 0;
+ struct display_driver_data *driver_data;
+ struct mcde_display_hdmi_platform_data *pdata =
+ dev->dev.platform_data;
+
+ if (pdata == NULL) {
+ dev_err(&dev->dev, "%s:Platform data missing\n", __func__);
+ return -EINVAL;
+ }
+
+ if (dev->port->type != MCDE_PORTTYPE_DSI) {
+ dev_err(&dev->dev, "%s:Invalid port type %d\n",
+ __func__, dev->port->type);
+ return -EINVAL;
+ }
+
+ driver_data = (struct display_driver_data *)
+ kzalloc(sizeof(struct display_driver_data), GFP_KERNEL);
+ if (!driver_data) {
+ dev_err(&dev->dev, "Failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ /* DSI use clock continous mode if AV8100_CHIPVER_1 > 1 */
+ if (av8100_ver_get() > AV8100_CHIPVER_1)
+ dev->port->phy.dsi.clk_cont = true;
+
+ dev->prepare_for_update = NULL;
+ dev->on_first_update = hdmi_on_first_update;
+ dev->try_video_mode = hdmi_try_video_mode;
+ dev->set_video_mode = hdmi_set_video_mode;
+ dev->apply_config = hdmi_apply_config;
+ dev->set_pixel_format = hdmi_set_pixel_format;
+ dev->set_power_mode = hdmi_set_power_mode;
+ dev->ceanr_convert = ceanr_convert;
+
+ /* Create sysfs files */
+ if (device_create_file(&dev->dev, &dev_attr_hdmisdtvswitch))
+ dev_info(&dev->dev,
+ "Unable to create hdmisdtvswitch attr\n");
+ if (device_create_file(&dev->dev, &dev_attr_input_pixel_format))
+ dev_info(&dev->dev,
+ "Unable to create input_pixel_format attr\n");
+ if (device_create_file(&dev->dev, &dev_attr_disponoff))
+ dev_info(&dev->dev,
+ "Unable to create disponoff attr\n");
+ if (device_create_file(&dev->dev, &dev_attr_vesacea))
+ dev_info(&dev->dev,
+ "Unable to create ceavesa attr\n");
+ if (device_create_file(&dev->dev, &dev_attr_timing))
+ dev_info(&dev->dev,
+ "Unable to create timing attr\n");
+ if (device_create_file(&dev->dev, &dev_attr_stayalive))
+ dev_info(&dev->dev,
+ "Unable to create stayalive attr\n");
+
+ if (pdata->cvbs_regulator_id) {
+ driver_data->cvbs_regulator = regulator_get(&dev->dev,
+ pdata->cvbs_regulator_id);
+ if (IS_ERR(driver_data->cvbs_regulator)) {
+ ret = PTR_ERR(driver_data->cvbs_regulator);
+ dev_warn(&dev->dev, "%s:Failed to get regulator %s\n",
+ __func__, pdata->cvbs_regulator_id);
+ driver_data->cvbs_regulator = NULL;
+ goto av_regulator_get_failed;
+ }
+ }
+
+ dev_set_drvdata(&dev->dev, driver_data);
+ dev_info(&dev->dev, "HDMI display probed\n");
+
+ return 0;
+
+av_regulator_get_failed:
+ kfree(driver_data);
+ return ret;
+}
+
+static int __devexit hdmi_remove(struct mcde_display_device *dev)
+{
+ struct display_driver_data *driver_data = dev_get_drvdata(&dev->dev);
+ struct mcde_display_hdmi_platform_data *pdata =
+ dev->dev.platform_data;
+
+ /* Remove sysfs files */
+ device_remove_file(&dev->dev, &dev_attr_input_pixel_format);
+ device_remove_file(&dev->dev, &dev_attr_hdmisdtvswitch);
+ device_remove_file(&dev->dev, &dev_attr_disponoff);
+ device_remove_file(&dev->dev, &dev_attr_vesacea);
+ device_remove_file(&dev->dev, &dev_attr_timing);
+ device_remove_file(&dev->dev, &dev_attr_stayalive);
+
+ dev->set_power_mode(dev, MCDE_DISPLAY_PM_OFF);
+
+ if (driver_data->cvbs_regulator)
+ regulator_put(driver_data->cvbs_regulator);
+ kfree(driver_data);
+ if (pdata->hdmi_platform_enable) {
+ if (pdata->regulator)
+ regulator_put(pdata->regulator);
+ if (pdata->reset_gpio) {
+ gpio_direction_input(pdata->reset_gpio);
+ gpio_free(pdata->reset_gpio);
+ }
+ }
+
+ return 0;
+}
+
+#if !defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)
+static int hdmi_resume(struct mcde_display_device *ddev)
+{
+ int ret;
+
+ /* set_power_mode will handle call platform_enable */
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_STANDBY);
+ if (ret < 0)
+ dev_warn(&ddev->dev, "%s:Failed to resume display\n"
+ , __func__);
+
+ ddev->update(ddev);
+ return ret;
+}
+
+static int hdmi_suspend(struct mcde_display_device *ddev, pm_message_t state)
+{
+ int ret;
+
+ /* set_power_mode will handle call platform_disable */
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_OFF);
+ if (ret < 0)
+ dev_warn(&ddev->dev, "%s:Failed to suspend display\n"
+ , __func__);
+
+ return ret;
+}
+#endif
+
+static struct mcde_display_driver hdmi_driver = {
+ .probe = hdmi_probe,
+ .remove = hdmi_remove,
+#if !defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)
+ .suspend = hdmi_suspend,
+ .resume = hdmi_resume,
+#else
+ .suspend = NULL,
+ .resume = NULL,
+#endif
+ .driver = {
+ .name = "av8100_hdmi",
+ },
+};
+
+/* Module init */
+static int __init mcde_display_hdmi_init(void)
+{
+ pr_info("%s\n", __func__);
+
+ return mcde_display_driver_register(&hdmi_driver);
+
+}
+late_initcall(mcde_display_hdmi_init);
+
+static void __exit mcde_display_hdmi_exit(void)
+{
+ pr_info("%s\n", __func__);
+
+ mcde_display_driver_unregister(&hdmi_driver);
+}
+module_exit(mcde_display_hdmi_exit);
+
+MODULE_AUTHOR("Per Persson <per.xb.persson@stericsson.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ST-Ericsson hdmi display driver");
diff --git a/drivers/video/mcde/display-generic_dsi.c b/drivers/video/mcde/display-generic_dsi.c
new file mode 100644
index 00000000000..9bb5babda5f
--- /dev/null
+++ b/drivers/video/mcde/display-generic_dsi.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson MCDE generic DCS display driver
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+
+#include <video/mcde_display.h>
+#include <video/mcde_display-generic_dsi.h>
+
+static int generic_platform_enable(struct mcde_display_device *dev)
+{
+ struct mcde_display_generic_platform_data *pdata =
+ dev->dev.platform_data;
+
+ dev_dbg(&dev->dev, "%s: Reset & power on generic display\n", __func__);
+
+ if (pdata->regulator) {
+ if (regulator_enable(pdata->regulator) < 0) {
+ dev_err(&dev->dev, "%s:Failed to enable regulator\n"
+ , __func__);
+ return -EINVAL;
+ }
+ }
+ if (pdata->reset_gpio)
+ gpio_set_value(pdata->reset_gpio, pdata->reset_high);
+ mdelay(pdata->reset_delay);
+ if (pdata->reset_gpio)
+ gpio_set_value(pdata->reset_gpio, !pdata->reset_high);
+
+ return 0;
+}
+
+static int generic_platform_disable(struct mcde_display_device *dev)
+{
+ struct mcde_display_generic_platform_data *pdata =
+ dev->dev.platform_data;
+
+ dev_dbg(&dev->dev, "%s:Reset & power off generic display\n", __func__);
+
+ if (pdata->regulator) {
+ if (regulator_disable(pdata->regulator) < 0) {
+ dev_err(&dev->dev, "%s:Failed to disable regulator\n"
+ , __func__);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int generic_set_power_mode(struct mcde_display_device *ddev,
+ enum mcde_display_power_mode power_mode)
+{
+ int ret = 0;
+ struct mcde_display_generic_platform_data *pdata =
+ ddev->dev.platform_data;
+
+ dev_dbg(&ddev->dev, "%s:Set Power mode\n", __func__);
+
+ /* OFF -> STANDBY */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_OFF &&
+ power_mode != MCDE_DISPLAY_PM_OFF) {
+
+ if (ddev->platform_enable) {
+ ret = ddev->platform_enable(ddev);
+ if (ret)
+ return ret;
+ }
+
+ ddev->power_mode = MCDE_DISPLAY_PM_STANDBY;
+ }
+
+ /* STANDBY -> ON */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_STANDBY &&
+ power_mode == MCDE_DISPLAY_PM_ON) {
+
+ ret = mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_EXIT_SLEEP_MODE, NULL, 0);
+ if (ret)
+ return ret;
+
+ msleep(pdata->sleep_out_delay);
+
+ ret = mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_SET_DISPLAY_ON, NULL, 0);
+ if (ret)
+ return ret;
+
+ ddev->power_mode = MCDE_DISPLAY_PM_ON;
+ goto set_power_and_exit;
+ }
+ /* ON -> STANDBY */
+ else if (ddev->power_mode == MCDE_DISPLAY_PM_ON &&
+ power_mode <= MCDE_DISPLAY_PM_STANDBY) {
+ ret = mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_SET_DISPLAY_OFF, NULL, 0);
+ if (ret)
+ return ret;
+
+ ret = mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_ENTER_SLEEP_MODE, NULL, 0);
+ if (ret)
+ return ret;
+
+ ddev->power_mode = MCDE_DISPLAY_PM_STANDBY;
+ }
+
+ /* SLEEP -> OFF */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_STANDBY &&
+ power_mode == MCDE_DISPLAY_PM_OFF) {
+ if (ddev->platform_disable) {
+ ret = ddev->platform_disable(ddev);
+ if (ret)
+ return ret;
+ }
+ ddev->power_mode = MCDE_DISPLAY_PM_OFF;
+ }
+
+set_power_and_exit:
+ mcde_chnl_set_power_mode(ddev->chnl_state, ddev->power_mode);
+
+ return ret;
+}
+
+static int __devinit generic_probe(struct mcde_display_device *dev)
+{
+ int ret = 0;
+ struct mcde_display_generic_platform_data *pdata =
+ dev->dev.platform_data;
+
+ if (pdata == NULL) {
+ dev_err(&dev->dev, "%s:Platform data missing\n", __func__);
+ return -EINVAL;
+ }
+
+ if (dev->port->type != MCDE_PORTTYPE_DSI) {
+ dev_err(&dev->dev,
+ "%s:Invalid port type %d\n",
+ __func__, dev->port->type);
+ return -EINVAL;
+ }
+
+ if (!dev->platform_enable && !dev->platform_disable) {
+ pdata->generic_platform_enable = true;
+ if (pdata->reset_gpio) {
+ ret = gpio_request(pdata->reset_gpio, NULL);
+ if (ret) {
+ dev_warn(&dev->dev,
+ "%s:Failed to request gpio %d\n",
+ __func__, pdata->reset_gpio);
+ goto gpio_request_failed;
+ }
+ gpio_direction_output(pdata->reset_gpio,
+ !pdata->reset_high);
+ }
+ if (pdata->regulator_id) {
+ pdata->regulator = regulator_get(NULL,
+ pdata->regulator_id);
+ if (IS_ERR(pdata->regulator)) {
+ ret = PTR_ERR(pdata->regulator);
+ dev_warn(&dev->dev,
+ "%s:Failed to get regulator '%s'\n",
+ __func__, pdata->regulator_id);
+ pdata->regulator = NULL;
+ goto regulator_get_failed;
+ }
+ regulator_set_voltage(pdata->regulator,
+ pdata->min_supply_voltage,
+ pdata->max_supply_voltage);
+ /*
+ * When u-boot has display a startup screen.
+ * U-boot has turned on display power however the
+ * regulator framework does not know about that
+ * This is the case here, the display driver has to
+ * enable the regulator for the display.
+ */
+ if (dev->power_mode == MCDE_DISPLAY_PM_STANDBY) {
+ ret = regulator_enable(pdata->regulator);
+ if (ret < 0) {
+ dev_err(&dev->dev,
+ "%s:Failed to enable regulator\n"
+ , __func__);
+ goto regulator_enable_failed;
+ }
+ }
+ }
+ }
+
+ /* TODO: Remove when DSI send command uses interrupts */
+ dev->prepare_for_update = NULL;
+ dev->platform_enable = generic_platform_enable,
+ dev->platform_disable = generic_platform_disable,
+ dev->set_power_mode = generic_set_power_mode;
+
+ dev_info(&dev->dev, "Generic display probed\n");
+
+ goto out;
+regulator_enable_failed:
+regulator_get_failed:
+ if (pdata->generic_platform_enable && pdata->reset_gpio)
+ gpio_free(pdata->reset_gpio);
+gpio_request_failed:
+out:
+ return ret;
+}
+
+static int __devexit generic_remove(struct mcde_display_device *dev)
+{
+ struct mcde_display_generic_platform_data *pdata =
+ dev->dev.platform_data;
+
+ dev->set_power_mode(dev, MCDE_DISPLAY_PM_OFF);
+
+ if (!pdata->generic_platform_enable)
+ return 0;
+
+ if (pdata->regulator)
+ regulator_put(pdata->regulator);
+ if (pdata->reset_gpio) {
+ gpio_direction_input(pdata->reset_gpio);
+ gpio_free(pdata->reset_gpio);
+ }
+
+ return 0;
+}
+
+#if !defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)
+static int generic_resume(struct mcde_display_device *ddev)
+{
+ int ret;
+
+ /* set_power_mode will handle call platform_enable */
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_STANDBY);
+ if (ret < 0)
+ dev_warn(&ddev->dev, "%s:Failed to resume display\n"
+ , __func__);
+ ddev->set_synchronized_update(ddev,
+ ddev->get_synchronized_update(ddev));
+ ddev->update(ddev);
+ return ret;
+}
+
+static int generic_suspend(struct mcde_display_device *ddev, pm_message_t state)
+{
+ int ret;
+
+ /* set_power_mode will handle call platform_disable */
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_OFF);
+ if (ret < 0)
+ dev_warn(&ddev->dev, "%s:Failed to suspend display\n"
+ , __func__);
+ return ret;
+}
+#endif
+
+static struct mcde_display_driver generic_driver = {
+ .probe = generic_probe,
+ .remove = generic_remove,
+#if !defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)
+ .suspend = generic_suspend,
+ .resume = generic_resume,
+#else
+ .suspend = NULL,
+ .resume = NULL,
+#endif
+ .driver = {
+ .name = "mcde_disp_generic",
+ },
+};
+
+/* Module init */
+static int __init mcde_display_generic_init(void)
+{
+ pr_info("%s\n", __func__);
+
+ return mcde_display_driver_register(&generic_driver);
+}
+module_init(mcde_display_generic_init);
+
+static void __exit mcde_display_generic_exit(void)
+{
+ pr_info("%s\n", __func__);
+
+ mcde_display_driver_unregister(&generic_driver);
+}
+module_exit(mcde_display_generic_exit);
+
+MODULE_AUTHOR("Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ST-Ericsson MCDE generic DCS display driver");
diff --git a/drivers/video/mcde/display-sony_sy35560_dsi.c b/drivers/video/mcde/display-sony_sy35560_dsi.c
new file mode 100644
index 00000000000..28a36b98c77
--- /dev/null
+++ b/drivers/video/mcde/display-sony_sy35560_dsi.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * ST-Ericsson MCDE Sony sy35560 DCS display driver
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+
+#include <video/mcde_dss.h>
+#include <video/mcde_display.h>
+#include <video/mcde_display-sony_sy35560_dsi.h>
+
+static int counter = 1;
+
+/*
+ * Work queue function for ESD check
+ *
+ * read display registers and perform power off/on/init sequence
+ * if register value indicate HW failure
+ */
+static void sony_sy35560_esd_work_fn(struct work_struct *work)
+{
+ u8 value;
+ struct sony_sy35560_device *dev = container_of(work,
+ struct sony_sy35560_device,
+ esd_work.work);
+
+
+ mcde_dsi_dcs_write(((struct mcde_display_device *)dev)->chnl_state,
+ 0x00, &value, 1);
+ pr_info("%s: %d) Read register 0x00, value 0x%.2X\n",
+ __func__, counter, value);
+
+ /* for now there are no registers available for ESD status check
+ * just perform power off/on/init every 4th work queue task
+ */
+
+ if (counter % 4 == 0) {
+ bool vsync_enabled;
+
+ pr_info("%s:Resetting display....", __func__);
+
+ /* get current display state */
+ vsync_enabled = ((struct mcde_display_device *)dev)->
+ get_synchronized_update(
+ (struct mcde_display_device *)dev);
+
+ /* wait a while to make sure finish refresh before resetting */
+ mdelay(10);
+
+ ((struct mcde_display_device *)dev)->set_power_mode(
+ (struct mcde_display_device *)dev,
+ MCDE_DISPLAY_PM_OFF);
+ ((struct mcde_display_device *)dev)->set_power_mode(
+ (struct mcde_display_device *)dev,
+ MCDE_DISPLAY_PM_STANDBY);
+
+ if (vsync_enabled) {
+ u8 m = 0;
+ mcde_dsi_dcs_write(
+ ((struct mcde_display_device *)dev)->chnl_state,
+ DCS_CMD_SET_TEAR_ON, &m, 1);
+ mcde_dss_set_synchronized_update(
+ (struct mcde_display_device *)dev, 0);
+
+ /* refresh display */
+ ((struct mcde_display_device *)dev)->update(
+ (struct mcde_display_device *)dev);
+
+ mcde_dss_set_synchronized_update(
+ (struct mcde_display_device *)dev, 1);
+ } else {
+ /* need to wait a while before turning on the display */
+ mdelay(5);
+
+ ((struct mcde_display_device *)dev)->set_power_mode(
+ (struct mcde_display_device *)dev,
+ MCDE_DISPLAY_PM_ON);
+ }
+ }
+ counter++;
+
+ queue_delayed_work(((struct sony_sy35560_device *)dev)->esd_wq,
+ &(((struct sony_sy35560_device *)dev)->esd_work),
+ SONY_SY35560_ESD_CHECK_PERIOD);
+}
+
+static int __devinit sony_sy35560_probe(struct mcde_display_device *dev)
+{
+ struct mcde_chnl_state *chnl;
+
+ u32 id;
+ u8 id1, id2, id3;
+ int len = 1;
+ int ret = 0;
+ int readret = 0;
+
+ /* create a workqueue for ESD status check */
+ ((struct sony_sy35560_device *)dev)->esd_wq =
+ create_singlethread_workqueue("sony_esd");
+ if (((struct sony_sy35560_device *)dev)->esd_wq == NULL) {
+ dev_warn(&dev->dev, "can't create ESD workqueue\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ INIT_DELAYED_WORK(&(((struct sony_sy35560_device *)dev)->esd_work),
+ sony_sy35560_esd_work_fn);
+
+ /* Acquire MCDE resources */
+ chnl = mcde_chnl_get(dev->chnl_id, dev->fifo, dev->port);
+ if (IS_ERR(chnl)) {
+ ret = PTR_ERR(chnl);
+ dev_warn(&dev->dev, "Failed to acquire MCDE channel\n");
+ goto out;
+ }
+
+ /* plugnplay: use registers DAh, DBh and DCh to detect display */
+ readret = mcde_dsi_dcs_read(chnl, 0xDA, &id1, &len);
+ if (!readret)
+ readret = mcde_dsi_dcs_read(chnl, 0xDB, &id2, &len);
+ if (!readret)
+ readret = mcde_dsi_dcs_read(chnl, 0xDC, &id3, &len);
+
+ if (readret) {
+ dev_info(&dev->dev,
+ "mcde_dsi_dcs_read failed to read display ID\n");
+ goto read_fail;
+ }
+
+ id = (id3 << 16) | (id2 << 8) | id1;
+
+ switch (id) {
+ case 0x018101:
+ dev_info(&dev->dev,
+ "Cygnus Cut1 display (ID 0x%.6X) probed\n", id);
+ /* add display specific initialization here */
+ break;
+
+ case 0x028101:
+ dev_info(&dev->dev,
+ "Cygnus Cut2 display (ID 0x%.6X) probed\n", id);
+ /* add display specific initialization here */
+ break;
+
+ default:
+ dev_info(&dev->dev,
+ "Display with id 0x%.6X probed\n", id);
+ break;
+ }
+
+read_fail:
+ /* close MCDE channel */
+ mcde_chnl_put(chnl);
+ chnl = NULL;
+out:
+ return ret;
+}
+
+static int __devexit sony_sy35560_remove(struct mcde_display_device *dev)
+{
+ dev->set_power_mode(dev, MCDE_DISPLAY_PM_OFF);
+
+ cancel_delayed_work(&(((struct sony_sy35560_device *)dev)->esd_work));
+ destroy_workqueue(((struct sony_sy35560_device *)dev)->esd_wq);
+ return 0;
+}
+
+static int sony_sy35560_resume(struct mcde_display_device *ddev)
+{
+ int ret;
+ /* set_power_mode will handle call platform_enable */
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_STANDBY);
+ if (ret < 0)
+ dev_warn(&ddev->dev, "%s:Failed to resume display\n"
+ , __func__);
+
+ queue_delayed_work(((struct sony_sy35560_device *)ddev)->esd_wq,
+ &(((struct sony_sy35560_device *)ddev)->esd_work),
+ SONY_SY35560_ESD_CHECK_PERIOD);
+
+ return ret;
+}
+
+static int sony_sy35560_suspend(struct mcde_display_device *ddev,
+ pm_message_t state)
+{
+ int ret;
+ /* set_power_mode will handle call platform_disable */
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_OFF);
+ if (ret < 0)
+ dev_warn(&ddev->dev, "%s:Failed to suspend display\n"
+ , __func__);
+
+ cancel_delayed_work(&(((struct sony_sy35560_device *)ddev)->esd_work));
+
+ return ret;
+}
+
+static struct mcde_display_driver sony_sy35560_driver = {
+ .probe = sony_sy35560_probe,
+ .remove = sony_sy35560_remove,
+ .suspend = sony_sy35560_suspend,
+ .resume = sony_sy35560_resume,
+ .driver = {
+ .name = "mcde_disp_sony",
+ },
+};
+
+/* Module init */
+
+static int __init mcde_display_sony_sy35560_init(void)
+{
+ pr_info("%s\n", __func__);
+
+ return mcde_display_driver_register(&sony_sy35560_driver);
+}
+module_init(mcde_display_sony_sy35560_init);
+
+static void __exit mcde_display_sony_sy35560_exit(void)
+{
+ pr_info("%s\n", __func__);
+
+ mcde_display_driver_unregister(&sony_sy35560_driver);
+}
+module_exit(mcde_display_sony_sy35560_exit);
+
+MODULE_AUTHOR("Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ST-Ericsson MCDE Sony sy35560 DCS display driver");
+
diff --git a/drivers/video/mcde/display-vuib500-dpi.c b/drivers/video/mcde/display-vuib500-dpi.c
new file mode 100644
index 00000000000..def5fe7c0ab
--- /dev/null
+++ b/drivers/video/mcde/display-vuib500-dpi.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson MCDE DPI display driver
+ * The VUIB500 is an user interface board the can be attached to an HREF. It
+ * supports the DPI pixel interface and converts this to an analog VGA signal,
+ * which can be connected to a monitor using a DSUB connector. The VUIB board
+ * uses an external power supply of 5V.
+ *
+ * Author: Marcel Tunnissen <marcel.tuennissen@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <video/mcde_display.h>
+#include <video/mcde_display-vuib500-dpi.h>
+
+#define DPI_DISP_TRACE dev_dbg(&ddev->dev, "%s\n", __func__)
+
+static int try_video_mode(struct mcde_display_device *ddev,
+ struct mcde_video_mode *video_mode);
+static int set_video_mode(struct mcde_display_device *ddev,
+ struct mcde_video_mode *video_mode);
+
+static int __devinit dpi_display_probe(struct mcde_display_device *ddev)
+{
+ int ret = 0;
+ struct mcde_display_dpi_platform_data *pdata = ddev->dev.platform_data;
+ DPI_DISP_TRACE;
+
+ if (pdata == NULL) {
+ dev_err(&ddev->dev, "%s:Platform data missing\n", __func__);
+ ret = -EINVAL;
+ goto no_pdata;
+ }
+
+ if (ddev->port->type != MCDE_PORTTYPE_DPI) {
+ dev_err(&ddev->dev,
+ "%s:Invalid port type %d\n",
+ __func__, ddev->port->type);
+ ret = -EINVAL;
+ goto invalid_port_type;
+ }
+
+ ddev->try_video_mode = try_video_mode;
+ ddev->set_video_mode = set_video_mode;
+ ddev->prepare_for_update = NULL;
+ dev_info(&ddev->dev, "DPI display probed\n");
+
+ goto out;
+invalid_port_type:
+no_pdata:
+out:
+ return ret;
+}
+
+static int __devexit dpi_display_remove(struct mcde_display_device *ddev)
+{
+ DPI_DISP_TRACE;
+
+ ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_OFF);
+
+ return 0;
+}
+
+static int dpi_display_resume(struct mcde_display_device *ddev)
+{
+ int ret;
+ DPI_DISP_TRACE;
+
+ /* set_power_mode will handle call platform_enable */
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_STANDBY);
+ if (ret < 0)
+ dev_warn(&ddev->dev, "%s:Failed to resume display\n"
+ , __func__);
+ return ret;
+}
+
+static int dpi_display_suspend(struct mcde_display_device *ddev,
+ pm_message_t state)
+{
+ int ret;
+ DPI_DISP_TRACE;
+
+ /* set_power_mode will handle call platform_disable */
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_OFF);
+ if (ret < 0)
+ dev_warn(&ddev->dev, "%s:Failed to suspend display\n"
+ , __func__);
+ return ret;
+}
+
+static void print_vmode(struct mcde_video_mode *vmode)
+{
+ pr_debug("resolution: %dx%d\n", vmode->xres, vmode->yres);
+ pr_debug(" pixclock: %d\n", vmode->pixclock);
+ pr_debug(" hbp: %d\n", vmode->hbp);
+ pr_debug(" hfp: %d\n", vmode->hfp);
+ pr_debug(" hsw: %d\n", vmode->hsw);
+ pr_debug(" vbp: %d\n", vmode->vbp);
+ pr_debug(" vfp: %d\n", vmode->vfp);
+ pr_debug(" vsw: %d\n", vmode->vsw);
+ pr_debug("interlaced: %s\n", vmode->interlaced ? "true" : "false");
+}
+
+/* Taken from the programmed value of the LCD clock in PRCMU */
+#define PIX_CLK_FREQ 25000000
+#define VMODE_XRES 640
+#define VMODE_YRES 480
+
+static int try_video_mode(
+ struct mcde_display_device *ddev, struct mcde_video_mode *video_mode)
+{
+ int res = -EINVAL;
+ DPI_DISP_TRACE;
+
+ if (ddev == NULL || video_mode == NULL) {
+ dev_warn(&ddev->dev, "%s:ddev = NULL or video_mode = NULL\n",
+ __func__);
+ return res;
+ }
+
+ if (video_mode->xres == VMODE_XRES && video_mode->yres == VMODE_YRES) {
+ video_mode->hbp = 40;
+ video_mode->hfp = 8;
+ video_mode->hsw = 96;
+ video_mode->vbp = 25;
+ video_mode->vfp = 2;
+ video_mode->vsw = 2;
+ /*
+ * The pixclock setting is not used within MCDE. The clock is
+ * setup elsewhere. But the pixclock value is visible in user
+ * space.
+ */
+ video_mode->pixclock = (int) (1e+12 * (1.0 / PIX_CLK_FREQ));
+ res = 0;
+ } /* TODO: add more supported resolutions here */
+ video_mode->interlaced = false;
+
+ if (res == 0)
+ print_vmode(video_mode);
+ else
+ dev_warn(&ddev->dev,
+ "%s:Failed to find video mode x=%d, y=%d\n",
+ __func__, video_mode->xres, video_mode->yres);
+
+ return res;
+
+}
+
+static int set_video_mode(
+ struct mcde_display_device *ddev, struct mcde_video_mode *video_mode)
+{
+ int res;
+ DPI_DISP_TRACE;
+
+ if (ddev == NULL || video_mode == NULL) {
+ dev_warn(&ddev->dev, "%s:ddev = NULL or video_mode = NULL\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (video_mode->xres != VMODE_XRES || video_mode->yres != VMODE_YRES) {
+ dev_warn(&ddev->dev, "%s:Failed to set video mode x=%d, y=%d\n",
+ __func__, video_mode->xres, video_mode->yres);
+ return -EINVAL;
+ }
+ ddev->video_mode = *video_mode;
+ print_vmode(video_mode);
+
+ res = mcde_chnl_set_video_mode(ddev->chnl_state, &ddev->video_mode);
+ if (res < 0) {
+ dev_warn(&ddev->dev, "%s:Failed to set video mode on channel\n",
+ __func__);
+
+ }
+ /* notify mcde display driver about updated video mode */
+ ddev->update_flags |= UPDATE_FLAG_VIDEO_MODE;
+ return res;
+}
+
+static struct mcde_display_driver dpi_display_driver = {
+ .probe = dpi_display_probe,
+ .remove = dpi_display_remove,
+ .suspend = dpi_display_suspend,
+ .resume = dpi_display_resume,
+ .driver = {
+ .name = "mcde_display_dpi",
+ },
+};
+
+/* Module init */
+static int __init mcde_dpi_display_init(void)
+{
+ pr_info("%s\n", __func__);
+
+ return mcde_display_driver_register(&dpi_display_driver);
+}
+module_init(mcde_dpi_display_init);
+
+static void __exit mcde_dpi_display_exit(void)
+{
+ pr_info("%s\n", __func__);
+
+ mcde_display_driver_unregister(&dpi_display_driver);
+}
+module_exit(mcde_dpi_display_exit);
+
+MODULE_AUTHOR("Marcel Tunnissen <marcel.tuennissen@stericsson.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ST-Ericsson MCDE DPI display driver fro VUIB500 display");
diff --git a/drivers/video/mcde/dsilink_regs.h b/drivers/video/mcde/dsilink_regs.h
new file mode 100644
index 00000000000..102d4fb0a56
--- /dev/null
+++ b/drivers/video/mcde/dsilink_regs.h
@@ -0,0 +1,2032 @@
+
+#define DSI_VAL2REG(__reg, __fld, __val) \
+ (((__val) << __reg##_##__fld##_SHIFT) & __reg##_##__fld##_MASK)
+#define DSI_REG2VAL(__reg, __fld, __val) \
+ (((__val) & __reg##_##__fld##_MASK) >> __reg##_##__fld##_SHIFT)
+
+#define DSI_MCTL_INTEGRATION_MODE 0x00000000
+#define DSI_MCTL_INTEGRATION_MODE_INT_MODE_EN_SHIFT 0
+#define DSI_MCTL_INTEGRATION_MODE_INT_MODE_EN_MASK 0x00000001
+#define DSI_MCTL_INTEGRATION_MODE_INT_MODE_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_INTEGRATION_MODE, INT_MODE_EN, __x)
+#define DSI_MCTL_MAIN_DATA_CTL 0x00000004
+#define DSI_MCTL_MAIN_DATA_CTL_LINK_EN_SHIFT 0
+#define DSI_MCTL_MAIN_DATA_CTL_LINK_EN_MASK 0x00000001
+#define DSI_MCTL_MAIN_DATA_CTL_LINK_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, LINK_EN, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_IF1_MODE_SHIFT 1
+#define DSI_MCTL_MAIN_DATA_CTL_IF1_MODE_MASK 0x00000002
+#define DSI_MCTL_MAIN_DATA_CTL_IF1_MODE_CMD 0
+#define DSI_MCTL_MAIN_DATA_CTL_IF1_MODE_VID 1
+#define DSI_MCTL_MAIN_DATA_CTL_IF1_MODE_ENUM(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, IF1_MODE, \
+ DSI_MCTL_MAIN_DATA_CTL_IF1_MODE_##__x)
+#define DSI_MCTL_MAIN_DATA_CTL_IF1_MODE(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, IF1_MODE, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_VID_EN_SHIFT 2
+#define DSI_MCTL_MAIN_DATA_CTL_VID_EN_MASK 0x00000004
+#define DSI_MCTL_MAIN_DATA_CTL_VID_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, VID_EN, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_TVG_SEL_SHIFT 3
+#define DSI_MCTL_MAIN_DATA_CTL_TVG_SEL_MASK 0x00000008
+#define DSI_MCTL_MAIN_DATA_CTL_TVG_SEL(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, TVG_SEL, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_TBG_SEL_SHIFT 4
+#define DSI_MCTL_MAIN_DATA_CTL_TBG_SEL_MASK 0x00000010
+#define DSI_MCTL_MAIN_DATA_CTL_TBG_SEL(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, TBG_SEL, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_IF1_TE_EN_SHIFT 5
+#define DSI_MCTL_MAIN_DATA_CTL_IF1_TE_EN_MASK 0x00000020
+#define DSI_MCTL_MAIN_DATA_CTL_IF1_TE_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, IF1_TE_EN, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_IF2_TE_EN_SHIFT 6
+#define DSI_MCTL_MAIN_DATA_CTL_IF2_TE_EN_MASK 0x00000040
+#define DSI_MCTL_MAIN_DATA_CTL_IF2_TE_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, IF2_TE_EN, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_REG_TE_EN_SHIFT 7
+#define DSI_MCTL_MAIN_DATA_CTL_REG_TE_EN_MASK 0x00000080
+#define DSI_MCTL_MAIN_DATA_CTL_REG_TE_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, REG_TE_EN, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_READ_EN_SHIFT 8
+#define DSI_MCTL_MAIN_DATA_CTL_READ_EN_MASK 0x00000100
+#define DSI_MCTL_MAIN_DATA_CTL_READ_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, READ_EN, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_BTA_EN_SHIFT 9
+#define DSI_MCTL_MAIN_DATA_CTL_BTA_EN_MASK 0x00000200
+#define DSI_MCTL_MAIN_DATA_CTL_BTA_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, BTA_EN, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_DISP_GEN_ECC_SHIFT 10
+#define DSI_MCTL_MAIN_DATA_CTL_DISP_GEN_ECC_MASK 0x00000400
+#define DSI_MCTL_MAIN_DATA_CTL_DISP_GEN_ECC(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, DISP_GEN_ECC, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_DISP_GEN_CHECKSUM_SHIFT 11
+#define DSI_MCTL_MAIN_DATA_CTL_DISP_GEN_CHECKSUM_MASK 0x00000800
+#define DSI_MCTL_MAIN_DATA_CTL_DISP_GEN_CHECKSUM(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, DISP_GEN_CHECKSUM, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_HOST_EOT_GEN_SHIFT 12
+#define DSI_MCTL_MAIN_DATA_CTL_HOST_EOT_GEN_MASK 0x00001000
+#define DSI_MCTL_MAIN_DATA_CTL_HOST_EOT_GEN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, HOST_EOT_GEN, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_DISP_EOT_GEN_SHIFT 13
+#define DSI_MCTL_MAIN_DATA_CTL_DISP_EOT_GEN_MASK 0x00002000
+#define DSI_MCTL_MAIN_DATA_CTL_DISP_EOT_GEN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, DISP_EOT_GEN, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_DLX_REMAP_EN_SHIFT 14
+#define DSI_MCTL_MAIN_DATA_CTL_DLX_REMAP_EN_MASK 0x00004000
+#define DSI_MCTL_MAIN_DATA_CTL_DLX_REMAP_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, DLX_REMAP_EN, __x)
+#define DSI_MCTL_MAIN_DATA_CTL_TE_POLLING_EN_SHIFT 15
+#define DSI_MCTL_MAIN_DATA_CTL_TE_POLLING_EN_MASK 0x00008000
+#define DSI_MCTL_MAIN_DATA_CTL_TE_POLLING_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_DATA_CTL, TE_POLLING_EN, __x)
+#define DSI_MCTL_MAIN_PHY_CTL 0x00000008
+#define DSI_MCTL_MAIN_PHY_CTL_LANE2_EN_SHIFT 0
+#define DSI_MCTL_MAIN_PHY_CTL_LANE2_EN_MASK 0x00000001
+#define DSI_MCTL_MAIN_PHY_CTL_LANE2_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_PHY_CTL, LANE2_EN, __x)
+#define DSI_MCTL_MAIN_PHY_CTL_FORCE_STOP_MODE_SHIFT 1
+#define DSI_MCTL_MAIN_PHY_CTL_FORCE_STOP_MODE_MASK 0x00000002
+#define DSI_MCTL_MAIN_PHY_CTL_FORCE_STOP_MODE(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_PHY_CTL, FORCE_STOP_MODE, __x)
+#define DSI_MCTL_MAIN_PHY_CTL_CLK_CONTINUOUS_SHIFT 2
+#define DSI_MCTL_MAIN_PHY_CTL_CLK_CONTINUOUS_MASK 0x00000004
+#define DSI_MCTL_MAIN_PHY_CTL_CLK_CONTINUOUS(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_PHY_CTL, CLK_CONTINUOUS, __x)
+#define DSI_MCTL_MAIN_PHY_CTL_CLK_ULPM_EN_SHIFT 3
+#define DSI_MCTL_MAIN_PHY_CTL_CLK_ULPM_EN_MASK 0x00000008
+#define DSI_MCTL_MAIN_PHY_CTL_CLK_ULPM_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_PHY_CTL, CLK_ULPM_EN, __x)
+#define DSI_MCTL_MAIN_PHY_CTL_DAT1_ULPM_EN_SHIFT 4
+#define DSI_MCTL_MAIN_PHY_CTL_DAT1_ULPM_EN_MASK 0x00000010
+#define DSI_MCTL_MAIN_PHY_CTL_DAT1_ULPM_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_PHY_CTL, DAT1_ULPM_EN, __x)
+#define DSI_MCTL_MAIN_PHY_CTL_DAT2_ULPM_EN_SHIFT 5
+#define DSI_MCTL_MAIN_PHY_CTL_DAT2_ULPM_EN_MASK 0x00000020
+#define DSI_MCTL_MAIN_PHY_CTL_DAT2_ULPM_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_PHY_CTL, DAT2_ULPM_EN, __x)
+#define DSI_MCTL_MAIN_PHY_CTL_WAIT_BURST_TIME_SHIFT 6
+#define DSI_MCTL_MAIN_PHY_CTL_WAIT_BURST_TIME_MASK 0x000003C0
+#define DSI_MCTL_MAIN_PHY_CTL_WAIT_BURST_TIME(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_PHY_CTL, WAIT_BURST_TIME, __x)
+#define DSI_MCTL_PLL_CTL 0x0000000C
+#define DSI_MCTL_PLL_CTL_PLL_MULT_SHIFT 0
+#define DSI_MCTL_PLL_CTL_PLL_MULT_MASK 0x000000FF
+#define DSI_MCTL_PLL_CTL_PLL_MULT(__x) \
+ DSI_VAL2REG(DSI_MCTL_PLL_CTL, PLL_MULT, __x)
+#define DSI_MCTL_PLL_CTL_PLL_OUT_DIV_SHIFT 8
+#define DSI_MCTL_PLL_CTL_PLL_OUT_DIV_MASK 0x00003F00
+#define DSI_MCTL_PLL_CTL_PLL_OUT_DIV(__x) \
+ DSI_VAL2REG(DSI_MCTL_PLL_CTL, PLL_OUT_DIV, __x)
+#define DSI_MCTL_PLL_CTL_PLL_IN_DIV_SHIFT 14
+#define DSI_MCTL_PLL_CTL_PLL_IN_DIV_MASK 0x0001C000
+#define DSI_MCTL_PLL_CTL_PLL_IN_DIV(__x) \
+ DSI_VAL2REG(DSI_MCTL_PLL_CTL, PLL_IN_DIV, __x)
+#define DSI_MCTL_PLL_CTL_PLL_SEL_DIV2_SHIFT 17
+#define DSI_MCTL_PLL_CTL_PLL_SEL_DIV2_MASK 0x00020000
+#define DSI_MCTL_PLL_CTL_PLL_SEL_DIV2(__x) \
+ DSI_VAL2REG(DSI_MCTL_PLL_CTL, PLL_SEL_DIV2, __x)
+#define DSI_MCTL_PLL_CTL_PLL_OUT_SEL_SHIFT 18
+#define DSI_MCTL_PLL_CTL_PLL_OUT_SEL_MASK 0x00040000
+#define DSI_MCTL_PLL_CTL_PLL_OUT_SEL_INT_PLL 0
+#define DSI_MCTL_PLL_CTL_PLL_OUT_SEL_SYS_PLL 1
+#define DSI_MCTL_PLL_CTL_PLL_OUT_SEL_ENUM(__x) \
+ DSI_VAL2REG(DSI_MCTL_PLL_CTL, PLL_OUT_SEL, \
+ DSI_MCTL_PLL_CTL_PLL_OUT_SEL_##__x)
+#define DSI_MCTL_PLL_CTL_PLL_OUT_SEL(__x) \
+ DSI_VAL2REG(DSI_MCTL_PLL_CTL, PLL_OUT_SEL, __x)
+#define DSI_MCTL_PLL_CTL_PLL_MASTER_SHIFT 31
+#define DSI_MCTL_PLL_CTL_PLL_MASTER_MASK 0x80000000
+#define DSI_MCTL_PLL_CTL_PLL_MASTER(__x) \
+ DSI_VAL2REG(DSI_MCTL_PLL_CTL, PLL_MASTER, __x)
+#define DSI_MCTL_LANE_STS 0x00000010
+#define DSI_MCTL_LANE_STS_CLKLANE_STATE_SHIFT 0
+#define DSI_MCTL_LANE_STS_CLKLANE_STATE_MASK 0x00000003
+#define DSI_MCTL_LANE_STS_CLKLANE_STATE_START 0
+#define DSI_MCTL_LANE_STS_CLKLANE_STATE_IDLE 1
+#define DSI_MCTL_LANE_STS_CLKLANE_STATE_HS 2
+#define DSI_MCTL_LANE_STS_CLKLANE_STATE_ULPM 3
+#define DSI_MCTL_LANE_STS_CLKLANE_STATE_ENUM(__x) \
+ DSI_VAL2REG(DSI_MCTL_LANE_STS, CLKLANE_STATE, \
+ DSI_MCTL_LANE_STS_CLKLANE_STATE_##__x)
+#define DSI_MCTL_LANE_STS_CLKLANE_STATE(__x) \
+ DSI_VAL2REG(DSI_MCTL_LANE_STS, CLKLANE_STATE, __x)
+#define DSI_MCTL_LANE_STS_DATLANE1_STATE_SHIFT 2
+#define DSI_MCTL_LANE_STS_DATLANE1_STATE_MASK 0x0000001C
+#define DSI_MCTL_LANE_STS_DATLANE1_STATE_START 0
+#define DSI_MCTL_LANE_STS_DATLANE1_STATE_IDLE 1
+#define DSI_MCTL_LANE_STS_DATLANE1_STATE_WRITE 2
+#define DSI_MCTL_LANE_STS_DATLANE1_STATE_ULPM 3
+#define DSI_MCTL_LANE_STS_DATLANE1_STATE_READ 4
+#define DSI_MCTL_LANE_STS_DATLANE1_STATE_ENUM(__x) \
+ DSI_VAL2REG(DSI_MCTL_LANE_STS, DATLANE1_STATE, \
+ DSI_MCTL_LANE_STS_DATLANE1_STATE_##__x)
+#define DSI_MCTL_LANE_STS_DATLANE1_STATE(__x) \
+ DSI_VAL2REG(DSI_MCTL_LANE_STS, DATLANE1_STATE, __x)
+#define DSI_MCTL_LANE_STS_DATLANE2_STATE_SHIFT 5
+#define DSI_MCTL_LANE_STS_DATLANE2_STATE_MASK 0x00000060
+#define DSI_MCTL_LANE_STS_DATLANE2_STATE_START 0
+#define DSI_MCTL_LANE_STS_DATLANE2_STATE_IDLE 1
+#define DSI_MCTL_LANE_STS_DATLANE2_STATE_WRITE 2
+#define DSI_MCTL_LANE_STS_DATLANE2_STATE_ULPM 3
+#define DSI_MCTL_LANE_STS_DATLANE2_STATE_ENUM(__x) \
+ DSI_VAL2REG(DSI_MCTL_LANE_STS, DATLANE2_STATE, \
+ DSI_MCTL_LANE_STS_DATLANE2_STATE_##__x)
+#define DSI_MCTL_LANE_STS_DATLANE2_STATE(__x) \
+ DSI_VAL2REG(DSI_MCTL_LANE_STS, DATLANE2_STATE, __x)
+#define DSI_MCTL_DPHY_TIMEOUT 0x00000014
+#define DSI_MCTL_DPHY_TIMEOUT_CLK_DIV_SHIFT 0
+#define DSI_MCTL_DPHY_TIMEOUT_CLK_DIV_MASK 0x0000000F
+#define DSI_MCTL_DPHY_TIMEOUT_CLK_DIV(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_TIMEOUT, CLK_DIV, __x)
+#define DSI_MCTL_DPHY_TIMEOUT_HSTX_TO_VAL_SHIFT 4
+#define DSI_MCTL_DPHY_TIMEOUT_HSTX_TO_VAL_MASK 0x0003FFF0
+#define DSI_MCTL_DPHY_TIMEOUT_HSTX_TO_VAL(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_TIMEOUT, HSTX_TO_VAL, __x)
+#define DSI_MCTL_DPHY_TIMEOUT_LPRX_TO_VAL_SHIFT 18
+#define DSI_MCTL_DPHY_TIMEOUT_LPRX_TO_VAL_MASK 0xFFFC0000
+#define DSI_MCTL_DPHY_TIMEOUT_LPRX_TO_VAL(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_TIMEOUT, LPRX_TO_VAL, __x)
+#define DSI_MCTL_ULPOUT_TIME 0x00000018
+#define DSI_MCTL_ULPOUT_TIME_CKLANE_ULPOUT_TIME_SHIFT 0
+#define DSI_MCTL_ULPOUT_TIME_CKLANE_ULPOUT_TIME_MASK 0x000001FF
+#define DSI_MCTL_ULPOUT_TIME_CKLANE_ULPOUT_TIME(__x) \
+ DSI_VAL2REG(DSI_MCTL_ULPOUT_TIME, CKLANE_ULPOUT_TIME, __x)
+#define DSI_MCTL_ULPOUT_TIME_DATA_ULPOUT_TIME_SHIFT 9
+#define DSI_MCTL_ULPOUT_TIME_DATA_ULPOUT_TIME_MASK 0x0003FE00
+#define DSI_MCTL_ULPOUT_TIME_DATA_ULPOUT_TIME(__x) \
+ DSI_VAL2REG(DSI_MCTL_ULPOUT_TIME, DATA_ULPOUT_TIME, __x)
+#define DSI_MCTL_DPHY_STATIC 0x0000001C
+#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_CLK_SHIFT 0
+#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_CLK_MASK 0x00000001
+#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_CLK(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_STATIC, SWAP_PINS_CLK, __x)
+#define DSI_MCTL_DPHY_STATIC_HS_INVERT_CLK_SHIFT 1
+#define DSI_MCTL_DPHY_STATIC_HS_INVERT_CLK_MASK 0x00000002
+#define DSI_MCTL_DPHY_STATIC_HS_INVERT_CLK(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_STATIC, HS_INVERT_CLK, __x)
+#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_DAT1_SHIFT 2
+#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_DAT1_MASK 0x00000004
+#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_DAT1(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_STATIC, SWAP_PINS_DAT1, __x)
+#define DSI_MCTL_DPHY_STATIC_HS_INVERT_DAT1_SHIFT 3
+#define DSI_MCTL_DPHY_STATIC_HS_INVERT_DAT1_MASK 0x00000008
+#define DSI_MCTL_DPHY_STATIC_HS_INVERT_DAT1(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_STATIC, HS_INVERT_DAT1, __x)
+#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_DAT2_SHIFT 4
+#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_DAT2_MASK 0x00000010
+#define DSI_MCTL_DPHY_STATIC_SWAP_PINS_DAT2(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_STATIC, SWAP_PINS_DAT2, __x)
+#define DSI_MCTL_DPHY_STATIC_HS_INVERT_DAT2_SHIFT 5
+#define DSI_MCTL_DPHY_STATIC_HS_INVERT_DAT2_MASK 0x00000020
+#define DSI_MCTL_DPHY_STATIC_HS_INVERT_DAT2(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_STATIC, HS_INVERT_DAT2, __x)
+#define DSI_MCTL_DPHY_STATIC_UI_X4_SHIFT 6
+#define DSI_MCTL_DPHY_STATIC_UI_X4_MASK 0x00000FC0
+#define DSI_MCTL_DPHY_STATIC_UI_X4(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_STATIC, UI_X4, __x)
+#define DSI_MCTL_MAIN_EN 0x00000020
+#define DSI_MCTL_MAIN_EN_PLL_START_SHIFT 0
+#define DSI_MCTL_MAIN_EN_PLL_START_MASK 0x00000001
+#define DSI_MCTL_MAIN_EN_PLL_START(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_EN, PLL_START, __x)
+#define DSI_MCTL_MAIN_EN_CKLANE_EN_SHIFT 3
+#define DSI_MCTL_MAIN_EN_CKLANE_EN_MASK 0x00000008
+#define DSI_MCTL_MAIN_EN_CKLANE_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_EN, CKLANE_EN, __x)
+#define DSI_MCTL_MAIN_EN_DAT1_EN_SHIFT 4
+#define DSI_MCTL_MAIN_EN_DAT1_EN_MASK 0x00000010
+#define DSI_MCTL_MAIN_EN_DAT1_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_EN, DAT1_EN, __x)
+#define DSI_MCTL_MAIN_EN_DAT2_EN_SHIFT 5
+#define DSI_MCTL_MAIN_EN_DAT2_EN_MASK 0x00000020
+#define DSI_MCTL_MAIN_EN_DAT2_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_EN, DAT2_EN, __x)
+#define DSI_MCTL_MAIN_EN_CLKLANE_ULPM_REQ_SHIFT 6
+#define DSI_MCTL_MAIN_EN_CLKLANE_ULPM_REQ_MASK 0x00000040
+#define DSI_MCTL_MAIN_EN_CLKLANE_ULPM_REQ(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_EN, CLKLANE_ULPM_REQ, __x)
+#define DSI_MCTL_MAIN_EN_DAT1_ULPM_REQ_SHIFT 7
+#define DSI_MCTL_MAIN_EN_DAT1_ULPM_REQ_MASK 0x00000080
+#define DSI_MCTL_MAIN_EN_DAT1_ULPM_REQ(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_EN, DAT1_ULPM_REQ, __x)
+#define DSI_MCTL_MAIN_EN_DAT2_ULPM_REQ_SHIFT 8
+#define DSI_MCTL_MAIN_EN_DAT2_ULPM_REQ_MASK 0x00000100
+#define DSI_MCTL_MAIN_EN_DAT2_ULPM_REQ(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_EN, DAT2_ULPM_REQ, __x)
+#define DSI_MCTL_MAIN_EN_IF1_EN_SHIFT 9
+#define DSI_MCTL_MAIN_EN_IF1_EN_MASK 0x00000200
+#define DSI_MCTL_MAIN_EN_IF1_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_EN, IF1_EN, __x)
+#define DSI_MCTL_MAIN_EN_IF2_EN_SHIFT 10
+#define DSI_MCTL_MAIN_EN_IF2_EN_MASK 0x00000400
+#define DSI_MCTL_MAIN_EN_IF2_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_EN, IF2_EN, __x)
+#define DSI_MCTL_MAIN_STS 0x00000024
+#define DSI_MCTL_MAIN_STS_PLL_LOCK_SHIFT 0
+#define DSI_MCTL_MAIN_STS_PLL_LOCK_MASK 0x00000001
+#define DSI_MCTL_MAIN_STS_PLL_LOCK(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS, PLL_LOCK, __x)
+#define DSI_MCTL_MAIN_STS_CLKLANE_READY_SHIFT 1
+#define DSI_MCTL_MAIN_STS_CLKLANE_READY_MASK 0x00000002
+#define DSI_MCTL_MAIN_STS_CLKLANE_READY(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS, CLKLANE_READY, __x)
+#define DSI_MCTL_MAIN_STS_DAT1_READY_SHIFT 2
+#define DSI_MCTL_MAIN_STS_DAT1_READY_MASK 0x00000004
+#define DSI_MCTL_MAIN_STS_DAT1_READY(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS, DAT1_READY, __x)
+#define DSI_MCTL_MAIN_STS_DAT2_READY_SHIFT 3
+#define DSI_MCTL_MAIN_STS_DAT2_READY_MASK 0x00000008
+#define DSI_MCTL_MAIN_STS_DAT2_READY(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS, DAT2_READY, __x)
+#define DSI_MCTL_MAIN_STS_HSTX_TO_ERR_SHIFT 4
+#define DSI_MCTL_MAIN_STS_HSTX_TO_ERR_MASK 0x00000010
+#define DSI_MCTL_MAIN_STS_HSTX_TO_ERR(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS, HSTX_TO_ERR, __x)
+#define DSI_MCTL_MAIN_STS_LPRX_TO_ERR_SHIFT 5
+#define DSI_MCTL_MAIN_STS_LPRX_TO_ERR_MASK 0x00000020
+#define DSI_MCTL_MAIN_STS_LPRX_TO_ERR(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS, LPRX_TO_ERR, __x)
+#define DSI_MCTL_MAIN_STS_CRS_UNTERM_PCK_SHIFT 6
+#define DSI_MCTL_MAIN_STS_CRS_UNTERM_PCK_MASK 0x00000040
+#define DSI_MCTL_MAIN_STS_CRS_UNTERM_PCK(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS, CRS_UNTERM_PCK, __x)
+#define DSI_MCTL_MAIN_STS_VRS_UNTERM_PCK_SHIFT 7
+#define DSI_MCTL_MAIN_STS_VRS_UNTERM_PCK_MASK 0x00000080
+#define DSI_MCTL_MAIN_STS_VRS_UNTERM_PCK(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS, VRS_UNTERM_PCK, __x)
+#define DSI_MCTL_DPHY_ERR 0x00000028
+#define DSI_MCTL_DPHY_ERR_ERR_ESC_1_SHIFT 6
+#define DSI_MCTL_DPHY_ERR_ERR_ESC_1_MASK 0x00000040
+#define DSI_MCTL_DPHY_ERR_ERR_ESC_1(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR, ERR_ESC_1, __x)
+#define DSI_MCTL_DPHY_ERR_ERR_ESC_2_SHIFT 7
+#define DSI_MCTL_DPHY_ERR_ERR_ESC_2_MASK 0x00000080
+#define DSI_MCTL_DPHY_ERR_ERR_ESC_2(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR, ERR_ESC_2, __x)
+#define DSI_MCTL_DPHY_ERR_ERR_SYNCESC_1_SHIFT 8
+#define DSI_MCTL_DPHY_ERR_ERR_SYNCESC_1_MASK 0x00000100
+#define DSI_MCTL_DPHY_ERR_ERR_SYNCESC_1(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR, ERR_SYNCESC_1, __x)
+#define DSI_MCTL_DPHY_ERR_ERR_SYNCESC_2_SHIFT 9
+#define DSI_MCTL_DPHY_ERR_ERR_SYNCESC_2_MASK 0x00000200
+#define DSI_MCTL_DPHY_ERR_ERR_SYNCESC_2(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR, ERR_SYNCESC_2, __x)
+#define DSI_MCTL_DPHY_ERR_ERR_CONTROL_1_SHIFT 10
+#define DSI_MCTL_DPHY_ERR_ERR_CONTROL_1_MASK 0x00000400
+#define DSI_MCTL_DPHY_ERR_ERR_CONTROL_1(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR, ERR_CONTROL_1, __x)
+#define DSI_MCTL_DPHY_ERR_ERR_CONTROL_2_SHIFT 11
+#define DSI_MCTL_DPHY_ERR_ERR_CONTROL_2_MASK 0x00000800
+#define DSI_MCTL_DPHY_ERR_ERR_CONTROL_2(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR, ERR_CONTROL_2, __x)
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP0_1_SHIFT 12
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP0_1_MASK 0x00001000
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP0_1(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR, ERR_CONT_LP0_1, __x)
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP0_2_SHIFT 13
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP0_2_MASK 0x00002000
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP0_2(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR, ERR_CONT_LP0_2, __x)
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP1_1_SHIFT 14
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP1_1_MASK 0x00004000
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP1_1(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR, ERR_CONT_LP1_1, __x)
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP1_2_SHIFT 15
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP1_2_MASK 0x00008000
+#define DSI_MCTL_DPHY_ERR_ERR_CONT_LP1_2(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR, ERR_CONT_LP1_2, __x)
+#define DSI_INT_VID_RDDATA 0x00000030
+#define DSI_INT_VID_RDDATA_IF_DATA_SHIFT 0
+#define DSI_INT_VID_RDDATA_IF_DATA_MASK 0x0000FFFF
+#define DSI_INT_VID_RDDATA_IF_DATA(__x) \
+ DSI_VAL2REG(DSI_INT_VID_RDDATA, IF_DATA, __x)
+#define DSI_INT_VID_RDDATA_IF_VALID_SHIFT 16
+#define DSI_INT_VID_RDDATA_IF_VALID_MASK 0x00010000
+#define DSI_INT_VID_RDDATA_IF_VALID(__x) \
+ DSI_VAL2REG(DSI_INT_VID_RDDATA, IF_VALID, __x)
+#define DSI_INT_VID_RDDATA_IF_START_SHIFT 17
+#define DSI_INT_VID_RDDATA_IF_START_MASK 0x00020000
+#define DSI_INT_VID_RDDATA_IF_START(__x) \
+ DSI_VAL2REG(DSI_INT_VID_RDDATA, IF_START, __x)
+#define DSI_INT_VID_RDDATA_IF_FRAME_SYNC_SHIFT 18
+#define DSI_INT_VID_RDDATA_IF_FRAME_SYNC_MASK 0x00040000
+#define DSI_INT_VID_RDDATA_IF_FRAME_SYNC(__x) \
+ DSI_VAL2REG(DSI_INT_VID_RDDATA, IF_FRAME_SYNC, __x)
+#define DSI_INT_VID_GNT 0x00000034
+#define DSI_INT_VID_GNT_IF_STALL_SHIFT 0
+#define DSI_INT_VID_GNT_IF_STALL_MASK 0x00000001
+#define DSI_INT_VID_GNT_IF_STALL(__x) \
+ DSI_VAL2REG(DSI_INT_VID_GNT, IF_STALL, __x)
+#define DSI_INT_CMD_RDDATA 0x00000038
+#define DSI_INT_CMD_RDDATA_IF_DATA_SHIFT 0
+#define DSI_INT_CMD_RDDATA_IF_DATA_MASK 0x0000FFFF
+#define DSI_INT_CMD_RDDATA_IF_DATA(__x) \
+ DSI_VAL2REG(DSI_INT_CMD_RDDATA, IF_DATA, __x)
+#define DSI_INT_CMD_RDDATA_IF_VALID_SHIFT 16
+#define DSI_INT_CMD_RDDATA_IF_VALID_MASK 0x00010000
+#define DSI_INT_CMD_RDDATA_IF_VALID(__x) \
+ DSI_VAL2REG(DSI_INT_CMD_RDDATA, IF_VALID, __x)
+#define DSI_INT_CMD_RDDATA_IF_START_SHIFT 17
+#define DSI_INT_CMD_RDDATA_IF_START_MASK 0x00020000
+#define DSI_INT_CMD_RDDATA_IF_START(__x) \
+ DSI_VAL2REG(DSI_INT_CMD_RDDATA, IF_START, __x)
+#define DSI_INT_CMD_RDDATA_IF_FRAME_SYNC_SHIFT 18
+#define DSI_INT_CMD_RDDATA_IF_FRAME_SYNC_MASK 0x00040000
+#define DSI_INT_CMD_RDDATA_IF_FRAME_SYNC(__x) \
+ DSI_VAL2REG(DSI_INT_CMD_RDDATA, IF_FRAME_SYNC, __x)
+#define DSI_INT_CMD_GNT 0x0000003C
+#define DSI_INT_CMD_GNT_IF_STALL_SHIFT 0
+#define DSI_INT_CMD_GNT_IF_STALL_MASK 0x00000001
+#define DSI_INT_CMD_GNT_IF_STALL(__x) \
+ DSI_VAL2REG(DSI_INT_CMD_GNT, IF_STALL, __x)
+#define DSI_INT_INTERRUPT_CTL 0x00000040
+#define DSI_INT_INTERRUPT_CTL_INT_VAL_SHIFT 0
+#define DSI_INT_INTERRUPT_CTL_INT_VAL_MASK 0x00000001
+#define DSI_INT_INTERRUPT_CTL_INT_VAL(__x) \
+ DSI_VAL2REG(DSI_INT_INTERRUPT_CTL, INT_VAL, __x)
+#define DSI_CMD_MODE_CTL 0x00000050
+#define DSI_CMD_MODE_CTL_IF1_ID_SHIFT 0
+#define DSI_CMD_MODE_CTL_IF1_ID_MASK 0x00000003
+#define DSI_CMD_MODE_CTL_IF1_ID(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_CTL, IF1_ID, __x)
+#define DSI_CMD_MODE_CTL_IF2_ID_SHIFT 2
+#define DSI_CMD_MODE_CTL_IF2_ID_MASK 0x0000000C
+#define DSI_CMD_MODE_CTL_IF2_ID(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_CTL, IF2_ID, __x)
+#define DSI_CMD_MODE_CTL_IF1_LP_EN_SHIFT 4
+#define DSI_CMD_MODE_CTL_IF1_LP_EN_MASK 0x00000010
+#define DSI_CMD_MODE_CTL_IF1_LP_EN(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_CTL, IF1_LP_EN, __x)
+#define DSI_CMD_MODE_CTL_IF2_LP_EN_SHIFT 5
+#define DSI_CMD_MODE_CTL_IF2_LP_EN_MASK 0x00000020
+#define DSI_CMD_MODE_CTL_IF2_LP_EN(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_CTL, IF2_LP_EN, __x)
+#define DSI_CMD_MODE_CTL_ARB_MODE_SHIFT 6
+#define DSI_CMD_MODE_CTL_ARB_MODE_MASK 0x00000040
+#define DSI_CMD_MODE_CTL_ARB_MODE(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_CTL, ARB_MODE, __x)
+#define DSI_CMD_MODE_CTL_ARB_PRI_SHIFT 7
+#define DSI_CMD_MODE_CTL_ARB_PRI_MASK 0x00000080
+#define DSI_CMD_MODE_CTL_ARB_PRI(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_CTL, ARB_PRI, __x)
+#define DSI_CMD_MODE_CTL_FIL_VALUE_SHIFT 8
+#define DSI_CMD_MODE_CTL_FIL_VALUE_MASK 0x0000FF00
+#define DSI_CMD_MODE_CTL_FIL_VALUE(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_CTL, FIL_VALUE, __x)
+#define DSI_CMD_MODE_CTL_TE_TIMEOUT_SHIFT 16
+#define DSI_CMD_MODE_CTL_TE_TIMEOUT_MASK 0x03FF0000
+#define DSI_CMD_MODE_CTL_TE_TIMEOUT(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_CTL, TE_TIMEOUT, __x)
+#define DSI_CMD_MODE_STS 0x00000054
+#define DSI_CMD_MODE_STS_ERR_NO_TE_SHIFT 0
+#define DSI_CMD_MODE_STS_ERR_NO_TE_MASK 0x00000001
+#define DSI_CMD_MODE_STS_ERR_NO_TE(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS, ERR_NO_TE, __x)
+#define DSI_CMD_MODE_STS_ERR_TE_MISS_SHIFT 1
+#define DSI_CMD_MODE_STS_ERR_TE_MISS_MASK 0x00000002
+#define DSI_CMD_MODE_STS_ERR_TE_MISS(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS, ERR_TE_MISS, __x)
+#define DSI_CMD_MODE_STS_ERR_SDI1_UNDERRUN_SHIFT 2
+#define DSI_CMD_MODE_STS_ERR_SDI1_UNDERRUN_MASK 0x00000004
+#define DSI_CMD_MODE_STS_ERR_SDI1_UNDERRUN(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS, ERR_SDI1_UNDERRUN, __x)
+#define DSI_CMD_MODE_STS_ERR_SDI2_UNDERRUN_SHIFT 3
+#define DSI_CMD_MODE_STS_ERR_SDI2_UNDERRUN_MASK 0x00000008
+#define DSI_CMD_MODE_STS_ERR_SDI2_UNDERRUN(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS, ERR_SDI2_UNDERRUN, __x)
+#define DSI_CMD_MODE_STS_ERR_UNWANTED_RD_SHIFT 4
+#define DSI_CMD_MODE_STS_ERR_UNWANTED_RD_MASK 0x00000010
+#define DSI_CMD_MODE_STS_ERR_UNWANTED_RD(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS, ERR_UNWANTED_RD, __x)
+#define DSI_CMD_MODE_STS_CSM_RUNNING_SHIFT 5
+#define DSI_CMD_MODE_STS_CSM_RUNNING_MASK 0x00000020
+#define DSI_CMD_MODE_STS_CSM_RUNNING(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS, CSM_RUNNING, __x)
+#define DSI_DIRECT_CMD_SEND 0x00000060
+#define DSI_DIRECT_CMD_SEND_START_SHIFT 0
+#define DSI_DIRECT_CMD_SEND_START_MASK 0xFFFFFFFF
+#define DSI_DIRECT_CMD_SEND_START(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_SEND, START, __x)
+#define DSI_DIRECT_CMD_MAIN_SETTINGS 0x00000064
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_SHIFT 0
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_MASK 0x00000007
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_WRITE 0
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_READ 1
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_TE_REQ 4
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_TRIG_REQ 5
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_BTA_REQ 6
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_ENUM(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_MAIN_SETTINGS, CMD_NAT, \
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_##__x)
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_MAIN_SETTINGS, CMD_NAT, __x)
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT_SHIFT 3
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT_MASK 0x00000008
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_MAIN_SETTINGS, CMD_LONGNOTSHORT, __x)
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT 8
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_MASK 0x00003F00
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_SHORT_WRITE_0 5
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_SHORT_WRITE_1 21
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_LONG_WRITE 57
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_READ 6
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_MAIN_SETTINGS, CMD_HEAD, \
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_##__x)
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_MAIN_SETTINGS, CMD_HEAD, __x)
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT 14
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_MASK 0x0000C000
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_MAIN_SETTINGS, CMD_ID, __x)
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT 16
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_MASK 0x001F0000
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_MAIN_SETTINGS, CMD_SIZE, __x)
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN_SHIFT 21
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN_MASK 0x00200000
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_MAIN_SETTINGS, CMD_LP_EN, __x)
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_TRIGGER_VAL_SHIFT 24
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_TRIGGER_VAL_MASK 0x0F000000
+#define DSI_DIRECT_CMD_MAIN_SETTINGS_TRIGGER_VAL(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_MAIN_SETTINGS, TRIGGER_VAL, __x)
+#define DSI_DIRECT_CMD_STS 0x00000068
+#define DSI_DIRECT_CMD_STS_CMD_TRANSMISSION_SHIFT 0
+#define DSI_DIRECT_CMD_STS_CMD_TRANSMISSION_MASK 0x00000001
+#define DSI_DIRECT_CMD_STS_CMD_TRANSMISSION(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, CMD_TRANSMISSION, __x)
+#define DSI_DIRECT_CMD_STS_WRITE_COMPLETED_SHIFT 1
+#define DSI_DIRECT_CMD_STS_WRITE_COMPLETED_MASK 0x00000002
+#define DSI_DIRECT_CMD_STS_WRITE_COMPLETED(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, WRITE_COMPLETED, __x)
+#define DSI_DIRECT_CMD_STS_TRIGGER_COMPLETED_SHIFT 2
+#define DSI_DIRECT_CMD_STS_TRIGGER_COMPLETED_MASK 0x00000004
+#define DSI_DIRECT_CMD_STS_TRIGGER_COMPLETED(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, TRIGGER_COMPLETED, __x)
+#define DSI_DIRECT_CMD_STS_READ_COMPLETED_SHIFT 3
+#define DSI_DIRECT_CMD_STS_READ_COMPLETED_MASK 0x00000008
+#define DSI_DIRECT_CMD_STS_READ_COMPLETED(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, READ_COMPLETED, __x)
+#define DSI_DIRECT_CMD_STS_ACKNOWLEDGE_RECEIVED_SHIFT 4
+#define DSI_DIRECT_CMD_STS_ACKNOWLEDGE_RECEIVED_MASK 0x00000010
+#define DSI_DIRECT_CMD_STS_ACKNOWLEDGE_RECEIVED(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, ACKNOWLEDGE_RECEIVED, __x)
+#define DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED_SHIFT 5
+#define DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED_MASK 0x00000020
+#define DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, ACKNOWLEDGE_WITH_ERR_RECEIVED, __x)
+#define DSI_DIRECT_CMD_STS_TRIGGER_RECEIVED_SHIFT 6
+#define DSI_DIRECT_CMD_STS_TRIGGER_RECEIVED_MASK 0x00000040
+#define DSI_DIRECT_CMD_STS_TRIGGER_RECEIVED(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, TRIGGER_RECEIVED, __x)
+#define DSI_DIRECT_CMD_STS_TE_RECEIVED_SHIFT 7
+#define DSI_DIRECT_CMD_STS_TE_RECEIVED_MASK 0x00000080
+#define DSI_DIRECT_CMD_STS_TE_RECEIVED(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, TE_RECEIVED, __x)
+#define DSI_DIRECT_CMD_STS_BTA_COMPLETED_SHIFT 8
+#define DSI_DIRECT_CMD_STS_BTA_COMPLETED_MASK 0x00000100
+#define DSI_DIRECT_CMD_STS_BTA_COMPLETED(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, BTA_COMPLETED, __x)
+#define DSI_DIRECT_CMD_STS_BTA_FINISHED_SHIFT 9
+#define DSI_DIRECT_CMD_STS_BTA_FINISHED_MASK 0x00000200
+#define DSI_DIRECT_CMD_STS_BTA_FINISHED(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, BTA_FINISHED, __x)
+#define DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR_SHIFT 10
+#define DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR_MASK 0x00000400
+#define DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, READ_COMPLETED_WITH_ERR, __x)
+#define DSI_DIRECT_CMD_STS_TRIGGER_VAL_SHIFT 11
+#define DSI_DIRECT_CMD_STS_TRIGGER_VAL_MASK 0x00007800
+#define DSI_DIRECT_CMD_STS_TRIGGER_VAL(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, TRIGGER_VAL, __x)
+#define DSI_DIRECT_CMD_STS_ACK_VAL_SHIFT 16
+#define DSI_DIRECT_CMD_STS_ACK_VAL_MASK 0xFFFF0000
+#define DSI_DIRECT_CMD_STS_ACK_VAL(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS, ACK_VAL, __x)
+#define DSI_DIRECT_CMD_RD_INIT 0x0000006C
+#define DSI_DIRECT_CMD_RD_INIT_RESET_SHIFT 0
+#define DSI_DIRECT_CMD_RD_INIT_RESET_MASK 0xFFFFFFFF
+#define DSI_DIRECT_CMD_RD_INIT_RESET(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_INIT, RESET, __x)
+#define DSI_DIRECT_CMD_WRDAT0 0x00000070
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT0_SHIFT 0
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT0_MASK 0x000000FF
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT0(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT0, WRDAT0, __x)
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT1_SHIFT 8
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT1_MASK 0x0000FF00
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT1(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT0, WRDAT1, __x)
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT2_SHIFT 16
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT2_MASK 0x00FF0000
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT2(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT0, WRDAT2, __x)
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT3_SHIFT 24
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT3_MASK 0xFF000000
+#define DSI_DIRECT_CMD_WRDAT0_WRDAT3(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT0, WRDAT3, __x)
+#define DSI_DIRECT_CMD_WRDAT1 0x00000074
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT4_SHIFT 0
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT4_MASK 0x000000FF
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT4(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT1, WRDAT4, __x)
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT5_SHIFT 8
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT5_MASK 0x0000FF00
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT5(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT1, WRDAT5, __x)
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT6_SHIFT 16
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT6_MASK 0x00FF0000
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT6(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT1, WRDAT6, __x)
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT7_SHIFT 24
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT7_MASK 0xFF000000
+#define DSI_DIRECT_CMD_WRDAT1_WRDAT7(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT1, WRDAT7, __x)
+#define DSI_DIRECT_CMD_WRDAT2 0x00000078
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT8_SHIFT 0
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT8_MASK 0x000000FF
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT8(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT2, WRDAT8, __x)
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT9_SHIFT 8
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT9_MASK 0x0000FF00
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT9(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT2, WRDAT9, __x)
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT10_SHIFT 16
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT10_MASK 0x00FF0000
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT10(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT2, WRDAT10, __x)
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT11_SHIFT 24
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT11_MASK 0xFF000000
+#define DSI_DIRECT_CMD_WRDAT2_WRDAT11(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT2, WRDAT11, __x)
+#define DSI_DIRECT_CMD_WRDAT3 0x0000007C
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT12_SHIFT 0
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT12_MASK 0x000000FF
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT12(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT3, WRDAT12, __x)
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT13_SHIFT 8
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT13_MASK 0x0000FF00
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT13(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT3, WRDAT13, __x)
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT14_SHIFT 16
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT14_MASK 0x00FF0000
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT14(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT3, WRDAT14, __x)
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT15_SHIFT 24
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT15_MASK 0xFF000000
+#define DSI_DIRECT_CMD_WRDAT3_WRDAT15(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_WRDAT3, WRDAT15, __x)
+#define DSI_DIRECT_CMD_RDDAT 0x00000080
+#define DSI_DIRECT_CMD_RDDAT_RDDAT0_SHIFT 0
+#define DSI_DIRECT_CMD_RDDAT_RDDAT0_MASK 0x000000FF
+#define DSI_DIRECT_CMD_RDDAT_RDDAT0(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RDDAT, RDDAT0, __x)
+#define DSI_DIRECT_CMD_RDDAT_RDDAT1_SHIFT 8
+#define DSI_DIRECT_CMD_RDDAT_RDDAT1_MASK 0x0000FF00
+#define DSI_DIRECT_CMD_RDDAT_RDDAT1(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RDDAT, RDDAT1, __x)
+#define DSI_DIRECT_CMD_RDDAT_RDDAT2_SHIFT 16
+#define DSI_DIRECT_CMD_RDDAT_RDDAT2_MASK 0x00FF0000
+#define DSI_DIRECT_CMD_RDDAT_RDDAT2(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RDDAT, RDDAT2, __x)
+#define DSI_DIRECT_CMD_RDDAT_RDDAT3_SHIFT 24
+#define DSI_DIRECT_CMD_RDDAT_RDDAT3_MASK 0xFF000000
+#define DSI_DIRECT_CMD_RDDAT_RDDAT3(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RDDAT, RDDAT3, __x)
+#define DSI_DIRECT_CMD_RD_PROPERTY 0x00000084
+#define DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE_SHIFT 0
+#define DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE_MASK 0x0000FFFF
+#define DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_PROPERTY, RD_SIZE, __x)
+#define DSI_DIRECT_CMD_RD_PROPERTY_RD_ID_SHIFT 16
+#define DSI_DIRECT_CMD_RD_PROPERTY_RD_ID_MASK 0x00030000
+#define DSI_DIRECT_CMD_RD_PROPERTY_RD_ID(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_PROPERTY, RD_ID, __x)
+#define DSI_DIRECT_CMD_RD_PROPERTY_RD_DCSNOTGENERIC_SHIFT 18
+#define DSI_DIRECT_CMD_RD_PROPERTY_RD_DCSNOTGENERIC_MASK 0x00040000
+#define DSI_DIRECT_CMD_RD_PROPERTY_RD_DCSNOTGENERIC(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_PROPERTY, RD_DCSNOTGENERIC, __x)
+#define DSI_DIRECT_CMD_RD_STS 0x00000088
+#define DSI_DIRECT_CMD_RD_STS_ERR_FIXED_SHIFT 0
+#define DSI_DIRECT_CMD_RD_STS_ERR_FIXED_MASK 0x00000001
+#define DSI_DIRECT_CMD_RD_STS_ERR_FIXED(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS, ERR_FIXED, __x)
+#define DSI_DIRECT_CMD_RD_STS_ERR_UNCORRECTABLE_SHIFT 1
+#define DSI_DIRECT_CMD_RD_STS_ERR_UNCORRECTABLE_MASK 0x00000002
+#define DSI_DIRECT_CMD_RD_STS_ERR_UNCORRECTABLE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS, ERR_UNCORRECTABLE, __x)
+#define DSI_DIRECT_CMD_RD_STS_ERR_CHECKSUM_SHIFT 2
+#define DSI_DIRECT_CMD_RD_STS_ERR_CHECKSUM_MASK 0x00000004
+#define DSI_DIRECT_CMD_RD_STS_ERR_CHECKSUM(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS, ERR_CHECKSUM, __x)
+#define DSI_DIRECT_CMD_RD_STS_ERR_UNDECODABLE_SHIFT 3
+#define DSI_DIRECT_CMD_RD_STS_ERR_UNDECODABLE_MASK 0x00000008
+#define DSI_DIRECT_CMD_RD_STS_ERR_UNDECODABLE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS, ERR_UNDECODABLE, __x)
+#define DSI_DIRECT_CMD_RD_STS_ERR_RECEIVE_SHIFT 4
+#define DSI_DIRECT_CMD_RD_STS_ERR_RECEIVE_MASK 0x00000010
+#define DSI_DIRECT_CMD_RD_STS_ERR_RECEIVE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS, ERR_RECEIVE, __x)
+#define DSI_DIRECT_CMD_RD_STS_ERR_OVERSIZE_SHIFT 5
+#define DSI_DIRECT_CMD_RD_STS_ERR_OVERSIZE_MASK 0x00000020
+#define DSI_DIRECT_CMD_RD_STS_ERR_OVERSIZE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS, ERR_OVERSIZE, __x)
+#define DSI_DIRECT_CMD_RD_STS_ERR_WRONG_LENGTH_SHIFT 6
+#define DSI_DIRECT_CMD_RD_STS_ERR_WRONG_LENGTH_MASK 0x00000040
+#define DSI_DIRECT_CMD_RD_STS_ERR_WRONG_LENGTH(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS, ERR_WRONG_LENGTH, __x)
+#define DSI_DIRECT_CMD_RD_STS_ERR_MISSING_EOT_SHIFT 7
+#define DSI_DIRECT_CMD_RD_STS_ERR_MISSING_EOT_MASK 0x00000080
+#define DSI_DIRECT_CMD_RD_STS_ERR_MISSING_EOT(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS, ERR_MISSING_EOT, __x)
+#define DSI_DIRECT_CMD_RD_STS_ERR_EOT_WITH_ERR_SHIFT 8
+#define DSI_DIRECT_CMD_RD_STS_ERR_EOT_WITH_ERR_MASK 0x00000100
+#define DSI_DIRECT_CMD_RD_STS_ERR_EOT_WITH_ERR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS, ERR_EOT_WITH_ERR, __x)
+#define DSI_VID_MAIN_CTL 0x00000090
+#define DSI_VID_MAIN_CTL_START_MODE_SHIFT 0
+#define DSI_VID_MAIN_CTL_START_MODE_MASK 0x00000003
+#define DSI_VID_MAIN_CTL_START_MODE(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, START_MODE, __x)
+#define DSI_VID_MAIN_CTL_STOP_MODE_SHIFT 2
+#define DSI_VID_MAIN_CTL_STOP_MODE_MASK 0x0000000C
+#define DSI_VID_MAIN_CTL_STOP_MODE(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, STOP_MODE, __x)
+#define DSI_VID_MAIN_CTL_VID_ID_SHIFT 4
+#define DSI_VID_MAIN_CTL_VID_ID_MASK 0x00000030
+#define DSI_VID_MAIN_CTL_VID_ID(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, VID_ID, __x)
+#define DSI_VID_MAIN_CTL_HEADER_SHIFT 6
+#define DSI_VID_MAIN_CTL_HEADER_MASK 0x00000FC0
+#define DSI_VID_MAIN_CTL_HEADER(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, HEADER, __x)
+#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE_SHIFT 12
+#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE_MASK 0x00003000
+#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE_16BITS 0
+#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE_18BITS 1
+#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE_18BITS_LOOSE 2
+#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE_24BITS 3
+#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE_ENUM(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, VID_PIXEL_MODE, \
+ DSI_VID_MAIN_CTL_VID_PIXEL_MODE_##__x)
+#define DSI_VID_MAIN_CTL_VID_PIXEL_MODE(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, VID_PIXEL_MODE, __x)
+#define DSI_VID_MAIN_CTL_BURST_MODE_SHIFT 14
+#define DSI_VID_MAIN_CTL_BURST_MODE_MASK 0x00004000
+#define DSI_VID_MAIN_CTL_BURST_MODE(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, BURST_MODE, __x)
+#define DSI_VID_MAIN_CTL_SYNC_PULSE_ACTIVE_SHIFT 15
+#define DSI_VID_MAIN_CTL_SYNC_PULSE_ACTIVE_MASK 0x00008000
+#define DSI_VID_MAIN_CTL_SYNC_PULSE_ACTIVE(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, SYNC_PULSE_ACTIVE, __x)
+#define DSI_VID_MAIN_CTL_SYNC_PULSE_HORIZONTAL_SHIFT 16
+#define DSI_VID_MAIN_CTL_SYNC_PULSE_HORIZONTAL_MASK 0x00010000
+#define DSI_VID_MAIN_CTL_SYNC_PULSE_HORIZONTAL(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, SYNC_PULSE_HORIZONTAL, __x)
+#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_SHIFT 17
+#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_MASK 0x00060000
+#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_NULL 0
+#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_BLANKING 1
+#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_LP_0 2
+#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_LP_1 3
+#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_ENUM(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, REG_BLKLINE_MODE, \
+ DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_##__x)
+#define DSI_VID_MAIN_CTL_REG_BLKLINE_MODE(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, REG_BLKLINE_MODE, __x)
+#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_SHIFT 19
+#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_MASK 0x00180000
+#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_NULL 0
+#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_BLANKING 1
+#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_LP_0 2
+#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_LP_1 3
+#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_ENUM(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, REG_BLKEOL_MODE, \
+ DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_##__x)
+#define DSI_VID_MAIN_CTL_REG_BLKEOL_MODE(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, REG_BLKEOL_MODE, __x)
+#define DSI_VID_MAIN_CTL_RECOVERY_MODE_SHIFT 21
+#define DSI_VID_MAIN_CTL_RECOVERY_MODE_MASK 0x00600000
+#define DSI_VID_MAIN_CTL_RECOVERY_MODE(__x) \
+ DSI_VAL2REG(DSI_VID_MAIN_CTL, RECOVERY_MODE, __x)
+#define DSI_VID_VSIZE 0x00000094
+#define DSI_VID_VSIZE_VSA_LENGTH_SHIFT 0
+#define DSI_VID_VSIZE_VSA_LENGTH_MASK 0x0000003F
+#define DSI_VID_VSIZE_VSA_LENGTH(__x) \
+ DSI_VAL2REG(DSI_VID_VSIZE, VSA_LENGTH, __x)
+#define DSI_VID_VSIZE_VBP_LENGTH_SHIFT 6
+#define DSI_VID_VSIZE_VBP_LENGTH_MASK 0x00000FC0
+#define DSI_VID_VSIZE_VBP_LENGTH(__x) \
+ DSI_VAL2REG(DSI_VID_VSIZE, VBP_LENGTH, __x)
+#define DSI_VID_VSIZE_VFP_LENGTH_SHIFT 12
+#define DSI_VID_VSIZE_VFP_LENGTH_MASK 0x000FF000
+#define DSI_VID_VSIZE_VFP_LENGTH(__x) \
+ DSI_VAL2REG(DSI_VID_VSIZE, VFP_LENGTH, __x)
+#define DSI_VID_VSIZE_VACT_LENGTH_SHIFT 20
+#define DSI_VID_VSIZE_VACT_LENGTH_MASK 0x7FF00000
+#define DSI_VID_VSIZE_VACT_LENGTH(__x) \
+ DSI_VAL2REG(DSI_VID_VSIZE, VACT_LENGTH, __x)
+#define DSI_VID_HSIZE1 0x00000098
+#define DSI_VID_HSIZE1_HSA_LENGTH_SHIFT 0
+#define DSI_VID_HSIZE1_HSA_LENGTH_MASK 0x000003FF
+#define DSI_VID_HSIZE1_HSA_LENGTH(__x) \
+ DSI_VAL2REG(DSI_VID_HSIZE1, HSA_LENGTH, __x)
+#define DSI_VID_HSIZE1_HBP_LENGTH_SHIFT 10
+#define DSI_VID_HSIZE1_HBP_LENGTH_MASK 0x000FFC00
+#define DSI_VID_HSIZE1_HBP_LENGTH(__x) \
+ DSI_VAL2REG(DSI_VID_HSIZE1, HBP_LENGTH, __x)
+#define DSI_VID_HSIZE1_HFP_LENGTH_SHIFT 20
+#define DSI_VID_HSIZE1_HFP_LENGTH_MASK 0x7FF00000
+#define DSI_VID_HSIZE1_HFP_LENGTH(__x) \
+ DSI_VAL2REG(DSI_VID_HSIZE1, HFP_LENGTH, __x)
+#define DSI_VID_HSIZE2 0x0000009C
+#define DSI_VID_HSIZE2_RGB_SIZE_SHIFT 0
+#define DSI_VID_HSIZE2_RGB_SIZE_MASK 0x00001FFF
+#define DSI_VID_HSIZE2_RGB_SIZE(__x) \
+ DSI_VAL2REG(DSI_VID_HSIZE2, RGB_SIZE, __x)
+#define DSI_VID_BLKSIZE1 0x000000A0
+#define DSI_VID_BLKSIZE1_BLKLINE_EVENT_PCK_SHIFT 0
+#define DSI_VID_BLKSIZE1_BLKLINE_EVENT_PCK_MASK 0x00001FFF
+#define DSI_VID_BLKSIZE1_BLKLINE_EVENT_PCK(__x) \
+ DSI_VAL2REG(DSI_VID_BLKSIZE1, BLKLINE_EVENT_PCK, __x)
+#define DSI_VID_BLKSIZE1_BLKEOL_PCK_SHIFT 13
+#define DSI_VID_BLKSIZE1_BLKEOL_PCK_MASK 0x03FFE000
+#define DSI_VID_BLKSIZE1_BLKEOL_PCK(__x) \
+ DSI_VAL2REG(DSI_VID_BLKSIZE1, BLKEOL_PCK, __x)
+#define DSI_VID_BLKSIZE2 0x000000A4
+#define DSI_VID_BLKSIZE2_BLKLINE_PULSE_PCK_SHIFT 0
+#define DSI_VID_BLKSIZE2_BLKLINE_PULSE_PCK_MASK 0x00001FFF
+#define DSI_VID_BLKSIZE2_BLKLINE_PULSE_PCK(__x) \
+ DSI_VAL2REG(DSI_VID_BLKSIZE2, BLKLINE_PULSE_PCK, __x)
+#define DSI_VID_PCK_TIME 0x000000A8
+#define DSI_VID_PCK_TIME_BLKEOL_DURATION_SHIFT 0
+#define DSI_VID_PCK_TIME_BLKEOL_DURATION_MASK 0x00001FFF
+#define DSI_VID_PCK_TIME_BLKEOL_DURATION(__x) \
+ DSI_VAL2REG(DSI_VID_PCK_TIME, BLKEOL_DURATION, __x)
+#define DSI_VID_DPHY_TIME 0x000000AC
+#define DSI_VID_DPHY_TIME_REG_LINE_DURATION_SHIFT 0
+#define DSI_VID_DPHY_TIME_REG_LINE_DURATION_MASK 0x00001FFF
+#define DSI_VID_DPHY_TIME_REG_LINE_DURATION(__x) \
+ DSI_VAL2REG(DSI_VID_DPHY_TIME, REG_LINE_DURATION, __x)
+#define DSI_VID_DPHY_TIME_REG_WAKEUP_TIME_SHIFT 13
+#define DSI_VID_DPHY_TIME_REG_WAKEUP_TIME_MASK 0x00FFE000
+#define DSI_VID_DPHY_TIME_REG_WAKEUP_TIME(__x) \
+ DSI_VAL2REG(DSI_VID_DPHY_TIME, REG_WAKEUP_TIME, __x)
+#define DSI_VID_ERR_COLOR 0x000000B0
+#define DSI_VID_ERR_COLOR_COL_RED_SHIFT 0
+#define DSI_VID_ERR_COLOR_COL_RED_MASK 0x000000FF
+#define DSI_VID_ERR_COLOR_COL_RED(__x) \
+ DSI_VAL2REG(DSI_VID_ERR_COLOR, COL_RED, __x)
+#define DSI_VID_ERR_COLOR_COL_GREEN_SHIFT 8
+#define DSI_VID_ERR_COLOR_COL_GREEN_MASK 0x0000FF00
+#define DSI_VID_ERR_COLOR_COL_GREEN(__x) \
+ DSI_VAL2REG(DSI_VID_ERR_COLOR, COL_GREEN, __x)
+#define DSI_VID_ERR_COLOR_COL_BLUE_SHIFT 16
+#define DSI_VID_ERR_COLOR_COL_BLUE_MASK 0x00FF0000
+#define DSI_VID_ERR_COLOR_COL_BLUE(__x) \
+ DSI_VAL2REG(DSI_VID_ERR_COLOR, COL_BLUE, __x)
+#define DSI_VID_ERR_COLOR_PAD_VAL_SHIFT 24
+#define DSI_VID_ERR_COLOR_PAD_VAL_MASK 0xFF000000
+#define DSI_VID_ERR_COLOR_PAD_VAL(__x) \
+ DSI_VAL2REG(DSI_VID_ERR_COLOR, PAD_VAL, __x)
+#define DSI_VID_VPOS 0x000000B4
+#define DSI_VID_VPOS_LINE_POS_SHIFT 0
+#define DSI_VID_VPOS_LINE_POS_MASK 0x00000003
+#define DSI_VID_VPOS_LINE_POS(__x) \
+ DSI_VAL2REG(DSI_VID_VPOS, LINE_POS, __x)
+#define DSI_VID_VPOS_LINE_VAL_SHIFT 2
+#define DSI_VID_VPOS_LINE_VAL_MASK 0x00001FFC
+#define DSI_VID_VPOS_LINE_VAL(__x) \
+ DSI_VAL2REG(DSI_VID_VPOS, LINE_VAL, __x)
+#define DSI_VID_HPOS 0x000000B8
+#define DSI_VID_HPOS_HORIZONTAL_POS_SHIFT 0
+#define DSI_VID_HPOS_HORIZONTAL_POS_MASK 0x00000007
+#define DSI_VID_HPOS_HORIZONTAL_POS(__x) \
+ DSI_VAL2REG(DSI_VID_HPOS, HORIZONTAL_POS, __x)
+#define DSI_VID_HPOS_HORIZONTAL_VAL_SHIFT 3
+#define DSI_VID_HPOS_HORIZONTAL_VAL_MASK 0x0000FFF8
+#define DSI_VID_HPOS_HORIZONTAL_VAL(__x) \
+ DSI_VAL2REG(DSI_VID_HPOS, HORIZONTAL_VAL, __x)
+#define DSI_VID_MODE_STS 0x000000BC
+#define DSI_VID_MODE_STS_VSG_RUNNING_SHIFT 0
+#define DSI_VID_MODE_STS_VSG_RUNNING_MASK 0x00000001
+#define DSI_VID_MODE_STS_VSG_RUNNING(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS, VSG_RUNNING, __x)
+#define DSI_VID_MODE_STS_ERR_MISSING_DATA_SHIFT 1
+#define DSI_VID_MODE_STS_ERR_MISSING_DATA_MASK 0x00000002
+#define DSI_VID_MODE_STS_ERR_MISSING_DATA(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS, ERR_MISSING_DATA, __x)
+#define DSI_VID_MODE_STS_ERR_MISSING_HSYNC_SHIFT 2
+#define DSI_VID_MODE_STS_ERR_MISSING_HSYNC_MASK 0x00000004
+#define DSI_VID_MODE_STS_ERR_MISSING_HSYNC(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS, ERR_MISSING_HSYNC, __x)
+#define DSI_VID_MODE_STS_ERR_MISSING_VSYNC_SHIFT 3
+#define DSI_VID_MODE_STS_ERR_MISSING_VSYNC_MASK 0x00000008
+#define DSI_VID_MODE_STS_ERR_MISSING_VSYNC(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS, ERR_MISSING_VSYNC, __x)
+#define DSI_VID_MODE_STS_REG_ERR_SMALL_LENGTH_SHIFT 4
+#define DSI_VID_MODE_STS_REG_ERR_SMALL_LENGTH_MASK 0x00000010
+#define DSI_VID_MODE_STS_REG_ERR_SMALL_LENGTH(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS, REG_ERR_SMALL_LENGTH, __x)
+#define DSI_VID_MODE_STS_REG_ERR_SMALL_HEIGHT_SHIFT 5
+#define DSI_VID_MODE_STS_REG_ERR_SMALL_HEIGHT_MASK 0x00000020
+#define DSI_VID_MODE_STS_REG_ERR_SMALL_HEIGHT(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS, REG_ERR_SMALL_HEIGHT, __x)
+#define DSI_VID_MODE_STS_ERR_BURSTWRITE_SHIFT 6
+#define DSI_VID_MODE_STS_ERR_BURSTWRITE_MASK 0x00000040
+#define DSI_VID_MODE_STS_ERR_BURSTWRITE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS, ERR_BURSTWRITE, __x)
+#define DSI_VID_MODE_STS_ERR_LONGWRITE_SHIFT 7
+#define DSI_VID_MODE_STS_ERR_LONGWRITE_MASK 0x00000080
+#define DSI_VID_MODE_STS_ERR_LONGWRITE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS, ERR_LONGWRITE, __x)
+#define DSI_VID_MODE_STS_ERR_LONGREAD_SHIFT 8
+#define DSI_VID_MODE_STS_ERR_LONGREAD_MASK 0x00000100
+#define DSI_VID_MODE_STS_ERR_LONGREAD(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS, ERR_LONGREAD, __x)
+#define DSI_VID_MODE_STS_ERR_VRS_WRONG_LENGTH_SHIFT 9
+#define DSI_VID_MODE_STS_ERR_VRS_WRONG_LENGTH_MASK 0x00000200
+#define DSI_VID_MODE_STS_ERR_VRS_WRONG_LENGTH(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS, ERR_VRS_WRONG_LENGTH, __x)
+#define DSI_VID_MODE_STS_VSG_RECOVERY_SHIFT 10
+#define DSI_VID_MODE_STS_VSG_RECOVERY_MASK 0x00000400
+#define DSI_VID_MODE_STS_VSG_RECOVERY(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS, VSG_RECOVERY, __x)
+#define DSI_VID_VCA_SETTING1 0x000000C0
+#define DSI_VID_VCA_SETTING1_MAX_BURST_LIMIT_SHIFT 0
+#define DSI_VID_VCA_SETTING1_MAX_BURST_LIMIT_MASK 0x0000FFFF
+#define DSI_VID_VCA_SETTING1_MAX_BURST_LIMIT(__x) \
+ DSI_VAL2REG(DSI_VID_VCA_SETTING1, MAX_BURST_LIMIT, __x)
+#define DSI_VID_VCA_SETTING1_BURST_LP_SHIFT 16
+#define DSI_VID_VCA_SETTING1_BURST_LP_MASK 0x00010000
+#define DSI_VID_VCA_SETTING1_BURST_LP(__x) \
+ DSI_VAL2REG(DSI_VID_VCA_SETTING1, BURST_LP, __x)
+#define DSI_VID_VCA_SETTING2 0x000000C4
+#define DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_SHIFT 0
+#define DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_MASK 0x0000FFFF
+#define DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT(__x) \
+ DSI_VAL2REG(DSI_VID_VCA_SETTING2, EXACT_BURST_LIMIT, __x)
+#define DSI_VID_VCA_SETTING2_MAX_LINE_LIMIT_SHIFT 16
+#define DSI_VID_VCA_SETTING2_MAX_LINE_LIMIT_MASK 0xFFFF0000
+#define DSI_VID_VCA_SETTING2_MAX_LINE_LIMIT(__x) \
+ DSI_VAL2REG(DSI_VID_VCA_SETTING2, MAX_LINE_LIMIT, __x)
+#define DSI_TVG_CTL 0x000000C8
+#define DSI_TVG_CTL_TVG_RUN_SHIFT 0
+#define DSI_TVG_CTL_TVG_RUN_MASK 0x00000001
+#define DSI_TVG_CTL_TVG_RUN(__x) \
+ DSI_VAL2REG(DSI_TVG_CTL, TVG_RUN, __x)
+#define DSI_TVG_CTL_TVG_STOPMODE_SHIFT 1
+#define DSI_TVG_CTL_TVG_STOPMODE_MASK 0x00000006
+#define DSI_TVG_CTL_TVG_STOPMODE(__x) \
+ DSI_VAL2REG(DSI_TVG_CTL, TVG_STOPMODE, __x)
+#define DSI_TVG_CTL_TVG_MODE_SHIFT 3
+#define DSI_TVG_CTL_TVG_MODE_MASK 0x00000018
+#define DSI_TVG_CTL_TVG_MODE(__x) \
+ DSI_VAL2REG(DSI_TVG_CTL, TVG_MODE, __x)
+#define DSI_TVG_CTL_TVG_STRIPE_SIZE_SHIFT 5
+#define DSI_TVG_CTL_TVG_STRIPE_SIZE_MASK 0x000000E0
+#define DSI_TVG_CTL_TVG_STRIPE_SIZE(__x) \
+ DSI_VAL2REG(DSI_TVG_CTL, TVG_STRIPE_SIZE, __x)
+#define DSI_TVG_IMG_SIZE 0x000000CC
+#define DSI_TVG_IMG_SIZE_TVG_LINE_SIZE_SHIFT 0
+#define DSI_TVG_IMG_SIZE_TVG_LINE_SIZE_MASK 0x00001FFF
+#define DSI_TVG_IMG_SIZE_TVG_LINE_SIZE(__x) \
+ DSI_VAL2REG(DSI_TVG_IMG_SIZE, TVG_LINE_SIZE, __x)
+#define DSI_TVG_IMG_SIZE_TVG_NBLINE_SHIFT 16
+#define DSI_TVG_IMG_SIZE_TVG_NBLINE_MASK 0x07FF0000
+#define DSI_TVG_IMG_SIZE_TVG_NBLINE(__x) \
+ DSI_VAL2REG(DSI_TVG_IMG_SIZE, TVG_NBLINE, __x)
+#define DSI_TVG_COLOR1 0x000000D0
+#define DSI_TVG_COLOR1_COL1_RED_SHIFT 0
+#define DSI_TVG_COLOR1_COL1_RED_MASK 0x000000FF
+#define DSI_TVG_COLOR1_COL1_RED(__x) \
+ DSI_VAL2REG(DSI_TVG_COLOR1, COL1_RED, __x)
+#define DSI_TVG_COLOR1_COL1_GREEN_SHIFT 8
+#define DSI_TVG_COLOR1_COL1_GREEN_MASK 0x0000FF00
+#define DSI_TVG_COLOR1_COL1_GREEN(__x) \
+ DSI_VAL2REG(DSI_TVG_COLOR1, COL1_GREEN, __x)
+#define DSI_TVG_COLOR1_COL1_BLUE_SHIFT 16
+#define DSI_TVG_COLOR1_COL1_BLUE_MASK 0x00FF0000
+#define DSI_TVG_COLOR1_COL1_BLUE(__x) \
+ DSI_VAL2REG(DSI_TVG_COLOR1, COL1_BLUE, __x)
+#define DSI_TVG_COLOR2 0x000000D4
+#define DSI_TVG_COLOR2_COL2_RED_SHIFT 0
+#define DSI_TVG_COLOR2_COL2_RED_MASK 0x000000FF
+#define DSI_TVG_COLOR2_COL2_RED(__x) \
+ DSI_VAL2REG(DSI_TVG_COLOR2, COL2_RED, __x)
+#define DSI_TVG_COLOR2_COL2_GREEN_SHIFT 8
+#define DSI_TVG_COLOR2_COL2_GREEN_MASK 0x0000FF00
+#define DSI_TVG_COLOR2_COL2_GREEN(__x) \
+ DSI_VAL2REG(DSI_TVG_COLOR2, COL2_GREEN, __x)
+#define DSI_TVG_COLOR2_COL2_BLUE_SHIFT 16
+#define DSI_TVG_COLOR2_COL2_BLUE_MASK 0x00FF0000
+#define DSI_TVG_COLOR2_COL2_BLUE(__x) \
+ DSI_VAL2REG(DSI_TVG_COLOR2, COL2_BLUE, __x)
+#define DSI_TVG_STS 0x000000D8
+#define DSI_TVG_STS_TVG_RUNNING_SHIFT 0
+#define DSI_TVG_STS_TVG_RUNNING_MASK 0x00000001
+#define DSI_TVG_STS_TVG_RUNNING(__x) \
+ DSI_VAL2REG(DSI_TVG_STS, TVG_RUNNING, __x)
+#define DSI_TBG_CTL 0x000000E0
+#define DSI_TBG_CTL_TBG_START_SHIFT 0
+#define DSI_TBG_CTL_TBG_START_MASK 0x00000001
+#define DSI_TBG_CTL_TBG_START(__x) \
+ DSI_VAL2REG(DSI_TBG_CTL, TBG_START, __x)
+#define DSI_TBG_CTL_TBG_HS_REQ_SHIFT 1
+#define DSI_TBG_CTL_TBG_HS_REQ_MASK 0x00000002
+#define DSI_TBG_CTL_TBG_HS_REQ(__x) \
+ DSI_VAL2REG(DSI_TBG_CTL, TBG_HS_REQ, __x)
+#define DSI_TBG_CTL_TBG_DATA_SEL_SHIFT 2
+#define DSI_TBG_CTL_TBG_DATA_SEL_MASK 0x00000004
+#define DSI_TBG_CTL_TBG_DATA_SEL(__x) \
+ DSI_VAL2REG(DSI_TBG_CTL, TBG_DATA_SEL, __x)
+#define DSI_TBG_CTL_TBG_MODE_SHIFT 3
+#define DSI_TBG_CTL_TBG_MODE_MASK 0x00000018
+#define DSI_TBG_CTL_TBG_MODE_1BYTE 0
+#define DSI_TBG_CTL_TBG_MODE_2BYTE 1
+#define DSI_TBG_CTL_TBG_MODE_BURST_COUNTER 2
+#define DSI_TBG_CTL_TBG_MODE_BURST 3
+#define DSI_TBG_CTL_TBG_MODE_ENUM(__x) \
+ DSI_VAL2REG(DSI_TBG_CTL, TBG_MODE, DSI_TBG_CTL_TBG_MODE_##__x)
+#define DSI_TBG_CTL_TBG_MODE(__x) \
+ DSI_VAL2REG(DSI_TBG_CTL, TBG_MODE, __x)
+#define DSI_TBG_SETTING 0x000000E4
+#define DSI_TBG_SETTING_TBG_DATA_SHIFT 0
+#define DSI_TBG_SETTING_TBG_DATA_MASK 0x0000FFFF
+#define DSI_TBG_SETTING_TBG_DATA(__x) \
+ DSI_VAL2REG(DSI_TBG_SETTING, TBG_DATA, __x)
+#define DSI_TBG_SETTING_TBG_CPT_SHIFT 16
+#define DSI_TBG_SETTING_TBG_CPT_MASK 0x0FFF0000
+#define DSI_TBG_SETTING_TBG_CPT(__x) \
+ DSI_VAL2REG(DSI_TBG_SETTING, TBG_CPT, __x)
+#define DSI_TBG_STS 0x000000E8
+#define DSI_TBG_STS_TBG_STATUS_SHIFT 0
+#define DSI_TBG_STS_TBG_STATUS_MASK 0x00000001
+#define DSI_TBG_STS_TBG_STATUS(__x) \
+ DSI_VAL2REG(DSI_TBG_STS, TBG_STATUS, __x)
+#define DSI_MCTL_MAIN_STS_CTL 0x000000F0
+#define DSI_MCTL_MAIN_STS_CTL_PLL_LOCK_EN_SHIFT 0
+#define DSI_MCTL_MAIN_STS_CTL_PLL_LOCK_EN_MASK 0x00000001
+#define DSI_MCTL_MAIN_STS_CTL_PLL_LOCK_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, PLL_LOCK_EN, __x)
+#define DSI_MCTL_MAIN_STS_CTL_CLKLANE_READY_EN_SHIFT 1
+#define DSI_MCTL_MAIN_STS_CTL_CLKLANE_READY_EN_MASK 0x00000002
+#define DSI_MCTL_MAIN_STS_CTL_CLKLANE_READY_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, CLKLANE_READY_EN, __x)
+#define DSI_MCTL_MAIN_STS_CTL_DAT1_READY_EN_SHIFT 2
+#define DSI_MCTL_MAIN_STS_CTL_DAT1_READY_EN_MASK 0x00000004
+#define DSI_MCTL_MAIN_STS_CTL_DAT1_READY_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, DAT1_READY_EN, __x)
+#define DSI_MCTL_MAIN_STS_CTL_DAT2_READY_EN_SHIFT 3
+#define DSI_MCTL_MAIN_STS_CTL_DAT2_READY_EN_MASK 0x00000008
+#define DSI_MCTL_MAIN_STS_CTL_DAT2_READY_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, DAT2_READY_EN, __x)
+#define DSI_MCTL_MAIN_STS_CTL_HSTX_TO_ERR_EN_SHIFT 4
+#define DSI_MCTL_MAIN_STS_CTL_HSTX_TO_ERR_EN_MASK 0x00000010
+#define DSI_MCTL_MAIN_STS_CTL_HSTX_TO_ERR_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, HSTX_TO_ERR_EN, __x)
+#define DSI_MCTL_MAIN_STS_CTL_LPRX_TO_ERR_EN_SHIFT 5
+#define DSI_MCTL_MAIN_STS_CTL_LPRX_TO_ERR_EN_MASK 0x00000020
+#define DSI_MCTL_MAIN_STS_CTL_LPRX_TO_ERR_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, LPRX_TO_ERR_EN, __x)
+#define DSI_MCTL_MAIN_STS_CTL_CRS_UNTERM_PCK_ERR_EN_SHIFT 6
+#define DSI_MCTL_MAIN_STS_CTL_CRS_UNTERM_PCK_ERR_EN_MASK 0x00000040
+#define DSI_MCTL_MAIN_STS_CTL_CRS_UNTERM_PCK_ERR_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, CRS_UNTERM_PCK_ERR_EN, __x)
+#define DSI_MCTL_MAIN_STS_CTL_VRS_UNTERM_PCK_ERR_EN_SHIFT 7
+#define DSI_MCTL_MAIN_STS_CTL_VRS_UNTERM_PCK_ERR_EN_MASK 0x00000080
+#define DSI_MCTL_MAIN_STS_CTL_VRS_UNTERM_PCK_ERR_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, VRS_UNTERM_PCK_ERR_EN, __x)
+#define DSI_MCTL_MAIN_STS_CTL_PLL_LOCK_EDGE_SHIFT 16
+#define DSI_MCTL_MAIN_STS_CTL_PLL_LOCK_EDGE_MASK 0x00010000
+#define DSI_MCTL_MAIN_STS_CTL_PLL_LOCK_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, PLL_LOCK_EDGE, __x)
+#define DSI_MCTL_MAIN_STS_CTL_CLKLANE_READY_EDGE_SHIFT 17
+#define DSI_MCTL_MAIN_STS_CTL_CLKLANE_READY_EDGE_MASK 0x00020000
+#define DSI_MCTL_MAIN_STS_CTL_CLKLANE_READY_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, CLKLANE_READY_EDGE, __x)
+#define DSI_MCTL_MAIN_STS_CTL_DAT1_READY_EDGE_SHIFT 18
+#define DSI_MCTL_MAIN_STS_CTL_DAT1_READY_EDGE_MASK 0x00040000
+#define DSI_MCTL_MAIN_STS_CTL_DAT1_READY_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, DAT1_READY_EDGE, __x)
+#define DSI_MCTL_MAIN_STS_CTL_DAT2_READY_EDGE_SHIFT 19
+#define DSI_MCTL_MAIN_STS_CTL_DAT2_READY_EDGE_MASK 0x00080000
+#define DSI_MCTL_MAIN_STS_CTL_DAT2_READY_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, DAT2_READY_EDGE, __x)
+#define DSI_MCTL_MAIN_STS_CTL_HSTX_TO_ERR_EDGE_SHIFT 20
+#define DSI_MCTL_MAIN_STS_CTL_HSTX_TO_ERR_EDGE_MASK 0x00100000
+#define DSI_MCTL_MAIN_STS_CTL_HSTX_TO_ERR_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, HSTX_TO_ERR_EDGE, __x)
+#define DSI_MCTL_MAIN_STS_CTL_LPRX_TO_ERR_EDGE_SHIFT 21
+#define DSI_MCTL_MAIN_STS_CTL_LPRX_TO_ERR_EDGE_MASK 0x00200000
+#define DSI_MCTL_MAIN_STS_CTL_LPRX_TO_ERR_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, LPRX_TO_ERR_EDGE, __x)
+#define DSI_MCTL_MAIN_STS_CTL_CRS_UNTERM_PCK_ERR_EDGE_SHIFT 22
+#define DSI_MCTL_MAIN_STS_CTL_CRS_UNTERM_PCK_ERR_EDGE_MASK 0x00400000
+#define DSI_MCTL_MAIN_STS_CTL_CRS_UNTERM_PCK_ERR_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, CRS_UNTERM_PCK_ERR_EDGE, __x)
+#define DSI_MCTL_MAIN_STS_CTL_VRS_UNTERM_PCK_ERR_EDGE_SHIFT 23
+#define DSI_MCTL_MAIN_STS_CTL_VRS_UNTERM_PCK_ERR_EDGE_MASK 0x00800000
+#define DSI_MCTL_MAIN_STS_CTL_VRS_UNTERM_PCK_ERR_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CTL, VRS_UNTERM_PCK_ERR_EDGE, __x)
+#define DSI_CMD_MODE_STS_CTL 0x000000F4
+#define DSI_CMD_MODE_STS_CTL_ERR_NO_TE_EN_SHIFT 0
+#define DSI_CMD_MODE_STS_CTL_ERR_NO_TE_EN_MASK 0x00000001
+#define DSI_CMD_MODE_STS_CTL_ERR_NO_TE_EN(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, ERR_NO_TE_EN, __x)
+#define DSI_CMD_MODE_STS_CTL_ERR_TE_MISS_EN_SHIFT 1
+#define DSI_CMD_MODE_STS_CTL_ERR_TE_MISS_EN_MASK 0x00000002
+#define DSI_CMD_MODE_STS_CTL_ERR_TE_MISS_EN(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, ERR_TE_MISS_EN, __x)
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI1_UNDERRUN_EN_SHIFT 2
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI1_UNDERRUN_EN_MASK 0x00000004
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI1_UNDERRUN_EN(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, ERR_SDI1_UNDERRUN_EN, __x)
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI2_UNDERRUN_EN_SHIFT 3
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI2_UNDERRUN_EN_MASK 0x00000008
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI2_UNDERRUN_EN(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, ERR_SDI2_UNDERRUN_EN, __x)
+#define DSI_CMD_MODE_STS_CTL_ERR_UNWANTED_RD_EN_SHIFT 4
+#define DSI_CMD_MODE_STS_CTL_ERR_UNWANTED_RD_EN_MASK 0x00000010
+#define DSI_CMD_MODE_STS_CTL_ERR_UNWANTED_RD_EN(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, ERR_UNWANTED_RD_EN, __x)
+#define DSI_CMD_MODE_STS_CTL_CSM_RUNNING_EN_SHIFT 5
+#define DSI_CMD_MODE_STS_CTL_CSM_RUNNING_EN_MASK 0x00000020
+#define DSI_CMD_MODE_STS_CTL_CSM_RUNNING_EN(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, CSM_RUNNING_EN, __x)
+#define DSI_CMD_MODE_STS_CTL_ERR_NO_TE_EDGE_SHIFT 16
+#define DSI_CMD_MODE_STS_CTL_ERR_NO_TE_EDGE_MASK 0x00010000
+#define DSI_CMD_MODE_STS_CTL_ERR_NO_TE_EDGE(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, ERR_NO_TE_EDGE, __x)
+#define DSI_CMD_MODE_STS_CTL_ERR_TE_MISS_EDGE_SHIFT 17
+#define DSI_CMD_MODE_STS_CTL_ERR_TE_MISS_EDGE_MASK 0x00020000
+#define DSI_CMD_MODE_STS_CTL_ERR_TE_MISS_EDGE(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, ERR_TE_MISS_EDGE, __x)
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI1_UNDERRUN_EDGE_SHIFT 18
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI1_UNDERRUN_EDGE_MASK 0x00040000
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI1_UNDERRUN_EDGE(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, ERR_SDI1_UNDERRUN_EDGE, __x)
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI2_UNDERRUN_EDGE_SHIFT 19
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI2_UNDERRUN_EDGE_MASK 0x00080000
+#define DSI_CMD_MODE_STS_CTL_ERR_SDI2_UNDERRUN_EDGE(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, ERR_SDI2_UNDERRUN_EDGE, __x)
+#define DSI_CMD_MODE_STS_CTL_ERR_UNWANTED_RD_EDGE_SHIFT 20
+#define DSI_CMD_MODE_STS_CTL_ERR_UNWANTED_RD_EDGE_MASK 0x00100000
+#define DSI_CMD_MODE_STS_CTL_ERR_UNWANTED_RD_EDGE(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, ERR_UNWANTED_RD_EDGE, __x)
+#define DSI_CMD_MODE_STS_CTL_CSM_RUNNING_EDGE_SHIFT 21
+#define DSI_CMD_MODE_STS_CTL_CSM_RUNNING_EDGE_MASK 0x00200000
+#define DSI_CMD_MODE_STS_CTL_CSM_RUNNING_EDGE(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CTL, CSM_RUNNING_EDGE, __x)
+#define DSI_DIRECT_CMD_STS_CTL 0x000000F8
+#define DSI_DIRECT_CMD_STS_CTL_CMD_TRANSMISSION_EN_SHIFT 0
+#define DSI_DIRECT_CMD_STS_CTL_CMD_TRANSMISSION_EN_MASK 0x00000001
+#define DSI_DIRECT_CMD_STS_CTL_CMD_TRANSMISSION_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, CMD_TRANSMISSION_EN, __x)
+#define DSI_DIRECT_CMD_STS_CTL_WRITE_COMPLETED_EN_SHIFT 1
+#define DSI_DIRECT_CMD_STS_CTL_WRITE_COMPLETED_EN_MASK 0x00000002
+#define DSI_DIRECT_CMD_STS_CTL_WRITE_COMPLETED_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, WRITE_COMPLETED_EN, __x)
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_COMPLETED_EN_SHIFT 2
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_COMPLETED_EN_MASK 0x00000004
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_COMPLETED_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, TRIGGER_COMPLETED_EN, __x)
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_EN_SHIFT 3
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_EN_MASK 0x00000008
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, READ_COMPLETED_EN, __x)
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_RECEIVED_EN_SHIFT 4
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_RECEIVED_EN_MASK 0x00000010
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_RECEIVED_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, ACKNOWLEDGE_RECEIVED_EN, __x)
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_WITH_ERR_EN_SHIFT 5
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_WITH_ERR_EN_MASK 0x00000020
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_WITH_ERR_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, ACKNOWLEDGE_WITH_ERR_EN, __x)
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_RECEIVED_EN_SHIFT 6
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_RECEIVED_EN_MASK 0x00000040
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_RECEIVED_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, TRIGGER_RECEIVED_EN, __x)
+#define DSI_DIRECT_CMD_STS_CTL_TE_RECEIVED_EN_SHIFT 7
+#define DSI_DIRECT_CMD_STS_CTL_TE_RECEIVED_EN_MASK 0x00000080
+#define DSI_DIRECT_CMD_STS_CTL_TE_RECEIVED_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, TE_RECEIVED_EN, __x)
+#define DSI_DIRECT_CMD_STS_CTL_BTA_COMPLETED_EN_SHIFT 8
+#define DSI_DIRECT_CMD_STS_CTL_BTA_COMPLETED_EN_MASK 0x00000100
+#define DSI_DIRECT_CMD_STS_CTL_BTA_COMPLETED_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, BTA_COMPLETED_EN, __x)
+#define DSI_DIRECT_CMD_STS_CTL_BTA_FINISHED_EN_SHIFT 9
+#define DSI_DIRECT_CMD_STS_CTL_BTA_FINISHED_EN_MASK 0x00000200
+#define DSI_DIRECT_CMD_STS_CTL_BTA_FINISHED_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, BTA_FINISHED_EN, __x)
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_WITH_ERR_EN_SHIFT 10
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_WITH_ERR_EN_MASK 0x00000400
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_WITH_ERR_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, READ_COMPLETED_WITH_ERR_EN, __x)
+#define DSI_DIRECT_CMD_STS_CTL_CMD_TRANSMISSION_EDGE_SHIFT 16
+#define DSI_DIRECT_CMD_STS_CTL_CMD_TRANSMISSION_EDGE_MASK 0x00010000
+#define DSI_DIRECT_CMD_STS_CTL_CMD_TRANSMISSION_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, CMD_TRANSMISSION_EDGE, __x)
+#define DSI_DIRECT_CMD_STS_CTL_WRITE_COMPLETED_EDGE_SHIFT 17
+#define DSI_DIRECT_CMD_STS_CTL_WRITE_COMPLETED_EDGE_MASK 0x00020000
+#define DSI_DIRECT_CMD_STS_CTL_WRITE_COMPLETED_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, WRITE_COMPLETED_EDGE, __x)
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_COMPLETED_EDGE_SHIFT 18
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_COMPLETED_EDGE_MASK 0x00040000
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_COMPLETED_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, TRIGGER_COMPLETED_EDGE, __x)
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_EDGE_SHIFT 19
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_EDGE_MASK 0x00080000
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, READ_COMPLETED_EDGE, __x)
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_RECEIVED_EDGE_SHIFT 20
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_RECEIVED_EDGE_MASK 0x00100000
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_RECEIVED_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, ACKNOWLEDGE_RECEIVED_EDGE, __x)
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_WITH_ERR_EDGE_SHIFT 21
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_WITH_ERR_EDGE_MASK 0x00200000
+#define DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_WITH_ERR_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, ACKNOWLEDGE_WITH_ERR_EDGE, __x)
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_RECEIVED_EDGE_SHIFT 22
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_RECEIVED_EDGE_MASK 0x00400000
+#define DSI_DIRECT_CMD_STS_CTL_TRIGGER_RECEIVED_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, TRIGGER_RECEIVED_EDGE, __x)
+#define DSI_DIRECT_CMD_STS_CTL_TE_RECEIVED_EDGE_SHIFT 23
+#define DSI_DIRECT_CMD_STS_CTL_TE_RECEIVED_EDGE_MASK 0x00800000
+#define DSI_DIRECT_CMD_STS_CTL_TE_RECEIVED_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, TE_RECEIVED_EDGE, __x)
+#define DSI_DIRECT_CMD_STS_CTL_BTA_COMPLETED_EDGE_SHIFT 24
+#define DSI_DIRECT_CMD_STS_CTL_BTA_COMPLETED_EDGE_MASK 0x01000000
+#define DSI_DIRECT_CMD_STS_CTL_BTA_COMPLETED_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, BTA_COMPLETED_EDGE, __x)
+#define DSI_DIRECT_CMD_STS_CTL_BTA_FINISHED_EDGE_SHIFT 25
+#define DSI_DIRECT_CMD_STS_CTL_BTA_FINISHED_EDGE_MASK 0x02000000
+#define DSI_DIRECT_CMD_STS_CTL_BTA_FINISHED_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, BTA_FINISHED_EDGE, __x)
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_WITH_ERR_EDGE_SHIFT 26
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_WITH_ERR_EDGE_MASK 0x04000000
+#define DSI_DIRECT_CMD_STS_CTL_READ_COMPLETED_WITH_ERR_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CTL, READ_COMPLETED_WITH_ERR_EDGE, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL 0x000000FC
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_FIXED_EN_SHIFT 0
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_FIXED_EN_MASK 0x00000001
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_FIXED_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_FIXED_EN, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNCORRECTABLE_EN_SHIFT 1
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNCORRECTABLE_EN_MASK 0x00000002
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNCORRECTABLE_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_UNCORRECTABLE_EN, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_CHECKSUM_EN_SHIFT 2
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_CHECKSUM_EN_MASK 0x00000004
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_CHECKSUM_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_CHECKSUM_EN, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNDECODABLE_EN_SHIFT 3
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNDECODABLE_EN_MASK 0x00000008
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNDECODABLE_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_UNDECODABLE_EN, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_RECEIVE_EN_SHIFT 4
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_RECEIVE_EN_MASK 0x00000010
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_RECEIVE_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_RECEIVE_EN, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_OVERSIZE_EN_SHIFT 5
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_OVERSIZE_EN_MASK 0x00000020
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_OVERSIZE_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_OVERSIZE_EN, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_WRONG_LENGTH_EN_SHIFT 6
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_WRONG_LENGTH_EN_MASK 0x00000040
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_WRONG_LENGTH_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_WRONG_LENGTH_EN, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_MISSING_EOT_EN_SHIFT 7
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_MISSING_EOT_EN_MASK 0x00000080
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_MISSING_EOT_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_MISSING_EOT_EN, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_EOT_WITH_ERR_EN_SHIFT 8
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_EOT_WITH_ERR_EN_MASK 0x00000100
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_EOT_WITH_ERR_EN(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_EOT_WITH_ERR_EN, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_FIXED_EDGE_SHIFT 16
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_FIXED_EDGE_MASK 0x00010000
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_FIXED_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_FIXED_EDGE, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNCORRECTABLE_EDGE_SHIFT 17
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNCORRECTABLE_EDGE_MASK 0x00020000
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNCORRECTABLE_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_UNCORRECTABLE_EDGE, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_CHECKSUM_EDGE_SHIFT 18
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_CHECKSUM_EDGE_MASK 0x00040000
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_CHECKSUM_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_CHECKSUM_EDGE, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNDECODABLE_EDGE_SHIFT 19
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNDECODABLE_EDGE_MASK 0x00080000
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_UNDECODABLE_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_UNDECODABLE_EDGE, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_RECEIVE_EDGE_SHIFT 20
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_RECEIVE_EDGE_MASK 0x00100000
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_RECEIVE_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_RECEIVE_EDGE, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_OVERSIZE_EDGE_SHIFT 21
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_OVERSIZE_EDGE_MASK 0x00200000
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_OVERSIZE_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_OVERSIZE_EDGE, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_WRONG_LENGTH_EDGE_SHIFT 22
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_WRONG_LENGTH_EDGE_MASK 0x00400000
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_WRONG_LENGTH_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_WRONG_LENGTH_EDGE, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_MISSING_EOT_EDGE_SHIFT 23
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_MISSING_EOT_EDGE_MASK 0x00800000
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_MISSING_EOT_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_MISSING_EOT_EDGE, __x)
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_EOT_WITH_ERR_EDGE_SHIFT 24
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_EOT_WITH_ERR_EDGE_MASK 0x01000000
+#define DSI_DIRECT_CMD_RD_STS_CTL_ERR_EOT_WITH_ERR_EDGE(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CTL, ERR_EOT_WITH_ERR_EDGE, __x)
+#define DSI_VID_MODE_STS_CTL 0x00000100
+#define DSI_VID_MODE_STS_CTL_VSG_RUNNING_EN_SHIFT 0
+#define DSI_VID_MODE_STS_CTL_VSG_RUNNING_EN_MASK 0x00000001
+#define DSI_VID_MODE_STS_CTL_VSG_RUNNING_EN(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, VSG_RUNNING_EN, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA_EN_SHIFT 1
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA_EN_MASK 0x00000002
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA_EN(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_MISSING_DATA_EN, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_HSYNC_EN_SHIFT 2
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_HSYNC_EN_MASK 0x00000004
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_HSYNC_EN(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_MISSING_HSYNC_EN, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC_EN_SHIFT 3
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC_EN_MASK 0x00000008
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC_EN(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_MISSING_VSYNC_EN, __x)
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_LENGTH_EN_SHIFT 4
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_LENGTH_EN_MASK 0x00000010
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_LENGTH_EN(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, REG_ERR_SMALL_LENGTH_EN, __x)
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_HEIGHT_EN_SHIFT 5
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_HEIGHT_EN_MASK 0x00000020
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_HEIGHT_EN(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, REG_ERR_SMALL_HEIGHT_EN, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_BURSTWRITE_EN_SHIFT 6
+#define DSI_VID_MODE_STS_CTL_ERR_BURSTWRITE_EN_MASK 0x00000040
+#define DSI_VID_MODE_STS_CTL_ERR_BURSTWRITE_EN(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_BURSTWRITE_EN, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_LONGWRITE_EN_SHIFT 7
+#define DSI_VID_MODE_STS_CTL_ERR_LONGWRITE_EN_MASK 0x00000080
+#define DSI_VID_MODE_STS_CTL_ERR_LONGWRITE_EN(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_LONGWRITE_EN, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_LONGREAD_EN_SHIFT 8
+#define DSI_VID_MODE_STS_CTL_ERR_LONGREAD_EN_MASK 0x00000100
+#define DSI_VID_MODE_STS_CTL_ERR_LONGREAD_EN(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_LONGREAD_EN, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_VRS_WRONG_LENGTH_EN_SHIFT 9
+#define DSI_VID_MODE_STS_CTL_ERR_VRS_WRONG_LENGTH_EN_MASK 0x00000200
+#define DSI_VID_MODE_STS_CTL_ERR_VRS_WRONG_LENGTH_EN(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_VRS_WRONG_LENGTH_EN, __x)
+#define DSI_VID_MODE_STS_CTL_VSG_RUNNING_EDGE_SHIFT 16
+#define DSI_VID_MODE_STS_CTL_VSG_RUNNING_EDGE_MASK 0x00010000
+#define DSI_VID_MODE_STS_CTL_VSG_RUNNING_EDGE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, VSG_RUNNING_EDGE, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA_EDGE_SHIFT 17
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA_EDGE_MASK 0x00020000
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA_EDGE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_MISSING_DATA_EDGE, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_HSYNC_EDGE_SHIFT 18
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_HSYNC_EDGE_MASK 0x00040000
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_HSYNC_EDGE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_MISSING_HSYNC_EDGE, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC_EDGE_SHIFT 19
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC_EDGE_MASK 0x00080000
+#define DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC_EDGE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_MISSING_VSYNC_EDGE, __x)
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_LENGTH_EDGE_SHIFT 20
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_LENGTH_EDGE_MASK 0x00100000
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_LENGTH_EDGE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, REG_ERR_SMALL_LENGTH_EDGE, __x)
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_HEIGHT_EDGE_SHIFT 21
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_HEIGHT_EDGE_MASK 0x00200000
+#define DSI_VID_MODE_STS_CTL_REG_ERR_SMALL_HEIGHT_EDGE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, REG_ERR_SMALL_HEIGHT_EDGE, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_BURSTWRITE_EDGE_SHIFT 22
+#define DSI_VID_MODE_STS_CTL_ERR_BURSTWRITE_EDGE_MASK 0x00400000
+#define DSI_VID_MODE_STS_CTL_ERR_BURSTWRITE_EDGE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_BURSTWRITE_EDGE, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_LONGWRITE_EDGE_SHIFT 23
+#define DSI_VID_MODE_STS_CTL_ERR_LONGWRITE_EDGE_MASK 0x00800000
+#define DSI_VID_MODE_STS_CTL_ERR_LONGWRITE_EDGE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_LONGWRITE_EDGE, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_LONGREAD_EDGE_SHIFT 24
+#define DSI_VID_MODE_STS_CTL_ERR_LONGREAD_EDGE_MASK 0x01000000
+#define DSI_VID_MODE_STS_CTL_ERR_LONGREAD_EDGE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_LONGREAD_EDGE, __x)
+#define DSI_VID_MODE_STS_CTL_ERR_VRS_WRONG_LENGTH_EDGE_SHIFT 25
+#define DSI_VID_MODE_STS_CTL_ERR_VRS_WRONG_LENGTH_EDGE_MASK 0x02000000
+#define DSI_VID_MODE_STS_CTL_ERR_VRS_WRONG_LENGTH_EDGE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, ERR_VRS_WRONG_LENGTH_EDGE, __x)
+#define DSI_VID_MODE_STS_CTL_VSG_RECOVERY_EDGE_SHIFT 26
+#define DSI_VID_MODE_STS_CTL_VSG_RECOVERY_EDGE_MASK 0x04000000
+#define DSI_VID_MODE_STS_CTL_VSG_RECOVERY_EDGE(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CTL, VSG_RECOVERY_EDGE, __x)
+#define DSI_TG_STS_CTL 0x00000104
+#define DSI_TG_STS_CTL_TVG_STS_EN_SHIFT 0
+#define DSI_TG_STS_CTL_TVG_STS_EN_MASK 0x00000001
+#define DSI_TG_STS_CTL_TVG_STS_EN(__x) \
+ DSI_VAL2REG(DSI_TG_STS_CTL, TVG_STS_EN, __x)
+#define DSI_TG_STS_CTL_TBG_STS_EN_SHIFT 1
+#define DSI_TG_STS_CTL_TBG_STS_EN_MASK 0x00000002
+#define DSI_TG_STS_CTL_TBG_STS_EN(__x) \
+ DSI_VAL2REG(DSI_TG_STS_CTL, TBG_STS_EN, __x)
+#define DSI_TG_STS_CTL_TVG_STS_EDGE_SHIFT 16
+#define DSI_TG_STS_CTL_TVG_STS_EDGE_MASK 0x00010000
+#define DSI_TG_STS_CTL_TVG_STS_EDGE(__x) \
+ DSI_VAL2REG(DSI_TG_STS_CTL, TVG_STS_EDGE, __x)
+#define DSI_TG_STS_CTL_TBG_STS_EDGE_SHIFT 17
+#define DSI_TG_STS_CTL_TBG_STS_EDGE_MASK 0x00020000
+#define DSI_TG_STS_CTL_TBG_STS_EDGE(__x) \
+ DSI_VAL2REG(DSI_TG_STS_CTL, TBG_STS_EDGE, __x)
+#define DSI_MCTL_DHPY_ERR_CTL 0x00000108
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_1_EN_SHIFT 6
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_1_EN_MASK 0x00000040
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_1_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_ESC_1_EN, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_2_EN_SHIFT 7
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_2_EN_MASK 0x00000080
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_2_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_ESC_2_EN, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_1_EN_SHIFT 8
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_1_EN_MASK 0x00000100
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_1_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_SYNCESC_1_EN, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_2_EN_SHIFT 9
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_2_EN_MASK 0x00000200
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_2_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_SYNCESC_2_EN, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_1_EN_SHIFT 10
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_1_EN_MASK 0x00000400
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_1_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONTROL_1_EN, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_2_EN_SHIFT 11
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_2_EN_MASK 0x00000800
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_2_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONTROL_2_EN, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_1_EN_SHIFT 12
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_1_EN_MASK 0x00001000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_1_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONT_LP0_1_EN, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_2_EN_SHIFT 13
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_2_EN_MASK 0x00002000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_2_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONT_LP0_2_EN, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_1_EN_SHIFT 14
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_1_EN_MASK 0x00004000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_1_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONT_LP1_1_EN, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_2_EN_SHIFT 15
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_2_EN_MASK 0x00008000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_2_EN(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONT_LP1_2_EN, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_1_EDGE_SHIFT 22
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_1_EDGE_MASK 0x00400000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_1_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_ESC_1_EDGE, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_2_EDGE_SHIFT 23
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_2_EDGE_MASK 0x00800000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_ESC_2_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_ESC_2_EDGE, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_1_EDGE_SHIFT 24
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_1_EDGE_MASK 0x01000000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_1_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_SYNCESC_1_EDGE, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_2_EDGE_SHIFT 25
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_2_EDGE_MASK 0x02000000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_SYNCESC_2_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_SYNCESC_2_EDGE, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_1_EDGE_SHIFT 26
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_1_EDGE_MASK 0x04000000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_1_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONTROL_1_EDGE, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_2_EDGE_SHIFT 27
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_2_EDGE_MASK 0x08000000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONTROL_2_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONTROL_2_EDGE, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_1_EDGE_SHIFT 28
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_1_EDGE_MASK 0x10000000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_1_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONT_LP0_1_EDGE, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_2_EDGE_SHIFT 29
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_2_EDGE_MASK 0x20000000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP0_2_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONT_LP0_2_EDGE, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_1_EDGE_SHIFT 30
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_1_EDGE_MASK 0x40000000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_1_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONT_LP1_1_EDGE, __x)
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_2_EDGE_SHIFT 31
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_2_EDGE_MASK 0x80000000
+#define DSI_MCTL_DHPY_ERR_CTL_ERR_CONT_LP1_2_EDGE(__x) \
+ DSI_VAL2REG(DSI_MCTL_DHPY_ERR_CTL, ERR_CONT_LP1_2_EDGE, __x)
+#define DSI_MCTL_MAIN_STS_CLR 0x00000110
+#define DSI_MCTL_MAIN_STS_CLR_PLL_LOCK_CLR_SHIFT 0
+#define DSI_MCTL_MAIN_STS_CLR_PLL_LOCK_CLR_MASK 0x00000001
+#define DSI_MCTL_MAIN_STS_CLR_PLL_LOCK_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CLR, PLL_LOCK_CLR, __x)
+#define DSI_MCTL_MAIN_STS_CLR_CLKLANE_READY_CLR_SHIFT 1
+#define DSI_MCTL_MAIN_STS_CLR_CLKLANE_READY_CLR_MASK 0x00000002
+#define DSI_MCTL_MAIN_STS_CLR_CLKLANE_READY_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CLR, CLKLANE_READY_CLR, __x)
+#define DSI_MCTL_MAIN_STS_CLR_DAT1_READY_CLR_SHIFT 2
+#define DSI_MCTL_MAIN_STS_CLR_DAT1_READY_CLR_MASK 0x00000004
+#define DSI_MCTL_MAIN_STS_CLR_DAT1_READY_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CLR, DAT1_READY_CLR, __x)
+#define DSI_MCTL_MAIN_STS_CLR_DAT2_READY_CLR_SHIFT 3
+#define DSI_MCTL_MAIN_STS_CLR_DAT2_READY_CLR_MASK 0x00000008
+#define DSI_MCTL_MAIN_STS_CLR_DAT2_READY_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CLR, DAT2_READY_CLR, __x)
+#define DSI_MCTL_MAIN_STS_CLR_HSTX_TO_ERR_CLR_SHIFT 4
+#define DSI_MCTL_MAIN_STS_CLR_HSTX_TO_ERR_CLR_MASK 0x00000010
+#define DSI_MCTL_MAIN_STS_CLR_HSTX_TO_ERR_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CLR, HSTX_TO_ERR_CLR, __x)
+#define DSI_MCTL_MAIN_STS_CLR_LPRX_TO_ERR_CLR_SHIFT 5
+#define DSI_MCTL_MAIN_STS_CLR_LPRX_TO_ERR_CLR_MASK 0x00000020
+#define DSI_MCTL_MAIN_STS_CLR_LPRX_TO_ERR_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CLR, LPRX_TO_ERR_CLR, __x)
+#define DSI_MCTL_MAIN_STS_CLR_CRS_UNTERM_PCK_CLR_SHIFT 6
+#define DSI_MCTL_MAIN_STS_CLR_CRS_UNTERM_PCK_CLR_MASK 0x00000040
+#define DSI_MCTL_MAIN_STS_CLR_CRS_UNTERM_PCK_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CLR, CRS_UNTERM_PCK_CLR, __x)
+#define DSI_MCTL_MAIN_STS_CLR_VRS_UNTERM_PCK_CLR_SHIFT 7
+#define DSI_MCTL_MAIN_STS_CLR_VRS_UNTERM_PCK_CLR_MASK 0x00000080
+#define DSI_MCTL_MAIN_STS_CLR_VRS_UNTERM_PCK_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_CLR, VRS_UNTERM_PCK_CLR, __x)
+#define DSI_CMD_MODE_STS_CLR 0x00000114
+#define DSI_CMD_MODE_STS_CLR_ERR_NO_TE_CLR_SHIFT 0
+#define DSI_CMD_MODE_STS_CLR_ERR_NO_TE_CLR_MASK 0x00000001
+#define DSI_CMD_MODE_STS_CLR_ERR_NO_TE_CLR(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CLR, ERR_NO_TE_CLR, __x)
+#define DSI_CMD_MODE_STS_CLR_ERR_TE_MISS_CLR_SHIFT 1
+#define DSI_CMD_MODE_STS_CLR_ERR_TE_MISS_CLR_MASK 0x00000002
+#define DSI_CMD_MODE_STS_CLR_ERR_TE_MISS_CLR(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CLR, ERR_TE_MISS_CLR, __x)
+#define DSI_CMD_MODE_STS_CLR_ERR_SDI1_UNDERRUN_CLR_SHIFT 2
+#define DSI_CMD_MODE_STS_CLR_ERR_SDI1_UNDERRUN_CLR_MASK 0x00000004
+#define DSI_CMD_MODE_STS_CLR_ERR_SDI1_UNDERRUN_CLR(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CLR, ERR_SDI1_UNDERRUN_CLR, __x)
+#define DSI_CMD_MODE_STS_CLR_ERR_SDI2_UNDERRUN_CLR_SHIFT 3
+#define DSI_CMD_MODE_STS_CLR_ERR_SDI2_UNDERRUN_CLR_MASK 0x00000008
+#define DSI_CMD_MODE_STS_CLR_ERR_SDI2_UNDERRUN_CLR(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CLR, ERR_SDI2_UNDERRUN_CLR, __x)
+#define DSI_CMD_MODE_STS_CLR_ERR_UNWANTED_RD_CLR_SHIFT 4
+#define DSI_CMD_MODE_STS_CLR_ERR_UNWANTED_RD_CLR_MASK 0x00000010
+#define DSI_CMD_MODE_STS_CLR_ERR_UNWANTED_RD_CLR(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CLR, ERR_UNWANTED_RD_CLR, __x)
+#define DSI_CMD_MODE_STS_CLR_CSM_RUNNING_CLR_SHIFT 5
+#define DSI_CMD_MODE_STS_CLR_CSM_RUNNING_CLR_MASK 0x00000020
+#define DSI_CMD_MODE_STS_CLR_CSM_RUNNING_CLR(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_CLR, CSM_RUNNING_CLR, __x)
+#define DSI_DIRECT_CMD_STS_CLR 0x00000118
+#define DSI_DIRECT_CMD_STS_CLR_CMD_TRANSMISSION_CLR_SHIFT 0
+#define DSI_DIRECT_CMD_STS_CLR_CMD_TRANSMISSION_CLR_MASK 0x00000001
+#define DSI_DIRECT_CMD_STS_CLR_CMD_TRANSMISSION_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CLR, CMD_TRANSMISSION_CLR, __x)
+#define DSI_DIRECT_CMD_STS_CLR_WRITE_COMPLETED_CLR_SHIFT 1
+#define DSI_DIRECT_CMD_STS_CLR_WRITE_COMPLETED_CLR_MASK 0x00000002
+#define DSI_DIRECT_CMD_STS_CLR_WRITE_COMPLETED_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CLR, WRITE_COMPLETED_CLR, __x)
+#define DSI_DIRECT_CMD_STS_CLR_TRIGGER_COMPLETED_CLR_SHIFT 2
+#define DSI_DIRECT_CMD_STS_CLR_TRIGGER_COMPLETED_CLR_MASK 0x00000004
+#define DSI_DIRECT_CMD_STS_CLR_TRIGGER_COMPLETED_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CLR, TRIGGER_COMPLETED_CLR, __x)
+#define DSI_DIRECT_CMD_STS_CLR_READ_COMPLETED_CLR_SHIFT 3
+#define DSI_DIRECT_CMD_STS_CLR_READ_COMPLETED_CLR_MASK 0x00000008
+#define DSI_DIRECT_CMD_STS_CLR_READ_COMPLETED_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CLR, READ_COMPLETED_CLR, __x)
+#define DSI_DIRECT_CMD_STS_CLR_ACKNOWLEDGE_RECEIVED_CLR_SHIFT 4
+#define DSI_DIRECT_CMD_STS_CLR_ACKNOWLEDGE_RECEIVED_CLR_MASK 0x00000010
+#define DSI_DIRECT_CMD_STS_CLR_ACKNOWLEDGE_RECEIVED_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CLR, ACKNOWLEDGE_RECEIVED_CLR, __x)
+#define DSI_DIRECT_CMD_STS_CLR_ACKNOWLEDGE_WITH_ERR_RECEIVED_CLR_SHIFT 5
+#define DSI_DIRECT_CMD_STS_CLR_ACKNOWLEDGE_WITH_ERR_RECEIVED_CLR_MASK 0x00000020
+#define DSI_DIRECT_CMD_STS_CLR_ACKNOWLEDGE_WITH_ERR_RECEIVED_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CLR, ACKNOWLEDGE_WITH_ERR_RECEIVED_CLR, __x)
+#define DSI_DIRECT_CMD_STS_CLR_TRIGGER_RECEIVED_CLR_SHIFT 6
+#define DSI_DIRECT_CMD_STS_CLR_TRIGGER_RECEIVED_CLR_MASK 0x00000040
+#define DSI_DIRECT_CMD_STS_CLR_TRIGGER_RECEIVED_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CLR, TRIGGER_RECEIVED_CLR, __x)
+#define DSI_DIRECT_CMD_STS_CLR_TE_RECEIVED_CLR_SHIFT 7
+#define DSI_DIRECT_CMD_STS_CLR_TE_RECEIVED_CLR_MASK 0x00000080
+#define DSI_DIRECT_CMD_STS_CLR_TE_RECEIVED_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CLR, TE_RECEIVED_CLR, __x)
+#define DSI_DIRECT_CMD_STS_CLR_BTA_COMPLETED_CLR_SHIFT 8
+#define DSI_DIRECT_CMD_STS_CLR_BTA_COMPLETED_CLR_MASK 0x00000100
+#define DSI_DIRECT_CMD_STS_CLR_BTA_COMPLETED_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CLR, BTA_COMPLETED_CLR, __x)
+#define DSI_DIRECT_CMD_STS_CLR_BTA_FINISHED_CLR_SHIFT 9
+#define DSI_DIRECT_CMD_STS_CLR_BTA_FINISHED_CLR_MASK 0x00000200
+#define DSI_DIRECT_CMD_STS_CLR_BTA_FINISHED_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CLR, BTA_FINISHED_CLR, __x)
+#define DSI_DIRECT_CMD_STS_CLR_READ_COMPLETED_WITH_ERR_CLR_SHIFT 10
+#define DSI_DIRECT_CMD_STS_CLR_READ_COMPLETED_WITH_ERR_CLR_MASK 0x00000400
+#define DSI_DIRECT_CMD_STS_CLR_READ_COMPLETED_WITH_ERR_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_CLR, READ_COMPLETED_WITH_ERR_CLR, __x)
+#define DSI_DIRECT_CMD_RD_STS_CLR 0x0000011C
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_FIXED_CLR_SHIFT 0
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_FIXED_CLR_MASK 0x00000001
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_FIXED_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CLR, ERR_FIXED_CLR, __x)
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_UNCORRECTABLE_CLR_SHIFT 1
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_UNCORRECTABLE_CLR_MASK 0x00000002
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_UNCORRECTABLE_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CLR, ERR_UNCORRECTABLE_CLR, __x)
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_CHECKSUM_CLR_SHIFT 2
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_CHECKSUM_CLR_MASK 0x00000004
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_CHECKSUM_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CLR, ERR_CHECKSUM_CLR, __x)
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_UNDECODABLE_CLR_SHIFT 3
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_UNDECODABLE_CLR_MASK 0x00000008
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_UNDECODABLE_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CLR, ERR_UNDECODABLE_CLR, __x)
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_RECEIVE_CLR_SHIFT 4
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_RECEIVE_CLR_MASK 0x00000010
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_RECEIVE_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CLR, ERR_RECEIVE_CLR, __x)
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_OVERSIZE_CLR_SHIFT 5
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_OVERSIZE_CLR_MASK 0x00000020
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_OVERSIZE_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CLR, ERR_OVERSIZE_CLR, __x)
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_WRONG_LENGTH_CLR_SHIFT 6
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_WRONG_LENGTH_CLR_MASK 0x00000040
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_WRONG_LENGTH_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CLR, ERR_WRONG_LENGTH_CLR, __x)
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_MISSING_EOT_CLR_SHIFT 7
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_MISSING_EOT_CLR_MASK 0x00000080
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_MISSING_EOT_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CLR, ERR_MISSING_EOT_CLR, __x)
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_EOT_WITH_ERR_CLR_SHIFT 8
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_EOT_WITH_ERR_CLR_MASK 0x00000100
+#define DSI_DIRECT_CMD_RD_STS_CLR_ERR_EOT_WITH_ERR_CLR(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_CLR, ERR_EOT_WITH_ERR_CLR, __x)
+#define DSI_VID_MODE_STS_CLR 0x00000120
+#define DSI_VID_MODE_STS_CLR_VSG_STS_CLR_SHIFT 0
+#define DSI_VID_MODE_STS_CLR_VSG_STS_CLR_MASK 0x00000001
+#define DSI_VID_MODE_STS_CLR_VSG_STS_CLR(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CLR, VSG_STS_CLR, __x)
+#define DSI_VID_MODE_STS_CLR_ERR_MISSING_DATA_CLR_SHIFT 1
+#define DSI_VID_MODE_STS_CLR_ERR_MISSING_DATA_CLR_MASK 0x00000002
+#define DSI_VID_MODE_STS_CLR_ERR_MISSING_DATA_CLR(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CLR, ERR_MISSING_DATA_CLR, __x)
+#define DSI_VID_MODE_STS_CLR_ERR_MISSING_HSYNC_CLR_SHIFT 2
+#define DSI_VID_MODE_STS_CLR_ERR_MISSING_HSYNC_CLR_MASK 0x00000004
+#define DSI_VID_MODE_STS_CLR_ERR_MISSING_HSYNC_CLR(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CLR, ERR_MISSING_HSYNC_CLR, __x)
+#define DSI_VID_MODE_STS_CLR_ERR_MISSING_VSYNC_CLR_SHIFT 3
+#define DSI_VID_MODE_STS_CLR_ERR_MISSING_VSYNC_CLR_MASK 0x00000008
+#define DSI_VID_MODE_STS_CLR_ERR_MISSING_VSYNC_CLR(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CLR, ERR_MISSING_VSYNC_CLR, __x)
+#define DSI_VID_MODE_STS_CLR_REG_ERR_SMALL_LENGTH_CLR_SHIFT 4
+#define DSI_VID_MODE_STS_CLR_REG_ERR_SMALL_LENGTH_CLR_MASK 0x00000010
+#define DSI_VID_MODE_STS_CLR_REG_ERR_SMALL_LENGTH_CLR(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CLR, REG_ERR_SMALL_LENGTH_CLR, __x)
+#define DSI_VID_MODE_STS_CLR_REG_ERR_SMALL_HEIGHT_CLR_SHIFT 5
+#define DSI_VID_MODE_STS_CLR_REG_ERR_SMALL_HEIGHT_CLR_MASK 0x00000020
+#define DSI_VID_MODE_STS_CLR_REG_ERR_SMALL_HEIGHT_CLR(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CLR, REG_ERR_SMALL_HEIGHT_CLR, __x)
+#define DSI_VID_MODE_STS_CLR_ERR_BURSTWRITE_CLR_SHIFT 6
+#define DSI_VID_MODE_STS_CLR_ERR_BURSTWRITE_CLR_MASK 0x00000040
+#define DSI_VID_MODE_STS_CLR_ERR_BURSTWRITE_CLR(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CLR, ERR_BURSTWRITE_CLR, __x)
+#define DSI_VID_MODE_STS_CLR_ERR_LONGWRITE_CLR_SHIFT 7
+#define DSI_VID_MODE_STS_CLR_ERR_LONGWRITE_CLR_MASK 0x00000080
+#define DSI_VID_MODE_STS_CLR_ERR_LONGWRITE_CLR(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CLR, ERR_LONGWRITE_CLR, __x)
+#define DSI_VID_MODE_STS_CLR_ERR_LONGREAD_CLR_SHIFT 8
+#define DSI_VID_MODE_STS_CLR_ERR_LONGREAD_CLR_MASK 0x00000100
+#define DSI_VID_MODE_STS_CLR_ERR_LONGREAD_CLR(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CLR, ERR_LONGREAD_CLR, __x)
+#define DSI_VID_MODE_STS_CLR_ERR_VRS_WRONG_LENGTH_CLR_SHIFT 9
+#define DSI_VID_MODE_STS_CLR_ERR_VRS_WRONG_LENGTH_CLR_MASK 0x00000200
+#define DSI_VID_MODE_STS_CLR_ERR_VRS_WRONG_LENGTH_CLR(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CLR, ERR_VRS_WRONG_LENGTH_CLR, __x)
+#define DSI_VID_MODE_STS_CLR_VSG_RECOVERY_CLR_SHIFT 10
+#define DSI_VID_MODE_STS_CLR_VSG_RECOVERY_CLR_MASK 0x00000400
+#define DSI_VID_MODE_STS_CLR_VSG_RECOVERY_CLR(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_CLR, VSG_RECOVERY_CLR, __x)
+#define DSI_TG_STS_CLR 0x00000124
+#define DSI_TG_STS_CLR_TVG_STS_CLR_SHIFT 0
+#define DSI_TG_STS_CLR_TVG_STS_CLR_MASK 0x00000001
+#define DSI_TG_STS_CLR_TVG_STS_CLR(__x) \
+ DSI_VAL2REG(DSI_TG_STS_CLR, TVG_STS_CLR, __x)
+#define DSI_TG_STS_CLR_TBG_STS_CLR_SHIFT 1
+#define DSI_TG_STS_CLR_TBG_STS_CLR_MASK 0x00000002
+#define DSI_TG_STS_CLR_TBG_STS_CLR(__x) \
+ DSI_VAL2REG(DSI_TG_STS_CLR, TBG_STS_CLR, __x)
+#define DSI_MCTL_DPHY_ERR_CLR 0x00000128
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_ESC_1_CLR_SHIFT 6
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_ESC_1_CLR_MASK 0x00000040
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_ESC_1_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_CLR, ERR_ESC_1_CLR, __x)
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_ESC_2_CLR_SHIFT 7
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_ESC_2_CLR_MASK 0x00000080
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_ESC_2_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_CLR, ERR_ESC_2_CLR, __x)
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_SYNCESC_1_CLR_SHIFT 8
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_SYNCESC_1_CLR_MASK 0x00000100
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_SYNCESC_1_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_CLR, ERR_SYNCESC_1_CLR, __x)
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_SYNCESC_2_CLR_SHIFT 9
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_SYNCESC_2_CLR_MASK 0x00000200
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_SYNCESC_2_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_CLR, ERR_SYNCESC_2_CLR, __x)
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONTROL_1_CLR_SHIFT 10
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONTROL_1_CLR_MASK 0x00000400
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONTROL_1_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_CLR, ERR_CONTROL_1_CLR, __x)
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONTROL_2_CLR_SHIFT 11
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONTROL_2_CLR_MASK 0x00000800
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONTROL_2_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_CLR, ERR_CONTROL_2_CLR, __x)
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP0_1_CLR_SHIFT 12
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP0_1_CLR_MASK 0x00001000
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP0_1_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_CLR, ERR_CONT_LP0_1_CLR, __x)
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP0_2_CLR_SHIFT 13
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP0_2_CLR_MASK 0x00002000
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP0_2_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_CLR, ERR_CONT_LP0_2_CLR, __x)
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP1_1_CLR_SHIFT 14
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP1_1_CLR_MASK 0x00004000
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP1_1_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_CLR, ERR_CONT_LP1_1_CLR, __x)
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP1_2_CLR_SHIFT 15
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP1_2_CLR_MASK 0x00008000
+#define DSI_MCTL_DPHY_ERR_CLR_ERR_CONT_LP1_2_CLR(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_CLR, ERR_CONT_LP1_2_CLR, __x)
+#define DSI_MCTL_MAIN_STS_FLAG 0x00000130
+#define DSI_MCTL_MAIN_STS_FLAG_PLL_LOCK_FLAG_SHIFT 0
+#define DSI_MCTL_MAIN_STS_FLAG_PLL_LOCK_FLAG_MASK 0x00000001
+#define DSI_MCTL_MAIN_STS_FLAG_PLL_LOCK_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_FLAG, PLL_LOCK_FLAG, __x)
+#define DSI_MCTL_MAIN_STS_FLAG_CLKLANE_READY_FLAG_SHIFT 1
+#define DSI_MCTL_MAIN_STS_FLAG_CLKLANE_READY_FLAG_MASK 0x00000002
+#define DSI_MCTL_MAIN_STS_FLAG_CLKLANE_READY_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_FLAG, CLKLANE_READY_FLAG, __x)
+#define DSI_MCTL_MAIN_STS_FLAG_DAT1_READY_FLAG_SHIFT 2
+#define DSI_MCTL_MAIN_STS_FLAG_DAT1_READY_FLAG_MASK 0x00000004
+#define DSI_MCTL_MAIN_STS_FLAG_DAT1_READY_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_FLAG, DAT1_READY_FLAG, __x)
+#define DSI_MCTL_MAIN_STS_FLAG_DAT2_READY_FLAG_SHIFT 3
+#define DSI_MCTL_MAIN_STS_FLAG_DAT2_READY_FLAG_MASK 0x00000008
+#define DSI_MCTL_MAIN_STS_FLAG_DAT2_READY_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_FLAG, DAT2_READY_FLAG, __x)
+#define DSI_MCTL_MAIN_STS_FLAG_HSTX_TO_ERR_FLAG_SHIFT 4
+#define DSI_MCTL_MAIN_STS_FLAG_HSTX_TO_ERR_FLAG_MASK 0x00000010
+#define DSI_MCTL_MAIN_STS_FLAG_HSTX_TO_ERR_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_FLAG, HSTX_TO_ERR_FLAG, __x)
+#define DSI_MCTL_MAIN_STS_FLAG_LPRX_TO_ERR_FLAG_SHIFT 5
+#define DSI_MCTL_MAIN_STS_FLAG_LPRX_TO_ERR_FLAG_MASK 0x00000020
+#define DSI_MCTL_MAIN_STS_FLAG_LPRX_TO_ERR_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_FLAG, LPRX_TO_ERR_FLAG, __x)
+#define DSI_MCTL_MAIN_STS_FLAG_CRS_UNTERM_PCK_FLAG_SHIFT 6
+#define DSI_MCTL_MAIN_STS_FLAG_CRS_UNTERM_PCK_FLAG_MASK 0x00000040
+#define DSI_MCTL_MAIN_STS_FLAG_CRS_UNTERM_PCK_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_FLAG, CRS_UNTERM_PCK_FLAG, __x)
+#define DSI_MCTL_MAIN_STS_FLAG_VRS_UNTERM_PCK_FLAG_SHIFT 7
+#define DSI_MCTL_MAIN_STS_FLAG_VRS_UNTERM_PCK_FLAG_MASK 0x00000080
+#define DSI_MCTL_MAIN_STS_FLAG_VRS_UNTERM_PCK_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_MAIN_STS_FLAG, VRS_UNTERM_PCK_FLAG, __x)
+#define DSI_CMD_MODE_STS_FLAG 0x00000134
+#define DSI_CMD_MODE_STS_FLAG_ERR_NO_TE_FLAG_SHIFT 0
+#define DSI_CMD_MODE_STS_FLAG_ERR_NO_TE_FLAG_MASK 0x00000001
+#define DSI_CMD_MODE_STS_FLAG_ERR_NO_TE_FLAG(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_FLAG, ERR_NO_TE_FLAG, __x)
+#define DSI_CMD_MODE_STS_FLAG_ERR_TE_MISS_FLAG_SHIFT 1
+#define DSI_CMD_MODE_STS_FLAG_ERR_TE_MISS_FLAG_MASK 0x00000002
+#define DSI_CMD_MODE_STS_FLAG_ERR_TE_MISS_FLAG(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_FLAG, ERR_TE_MISS_FLAG, __x)
+#define DSI_CMD_MODE_STS_FLAG_ERR_SDI1_UNDERRUN_FLAG_SHIFT 2
+#define DSI_CMD_MODE_STS_FLAG_ERR_SDI1_UNDERRUN_FLAG_MASK 0x00000004
+#define DSI_CMD_MODE_STS_FLAG_ERR_SDI1_UNDERRUN_FLAG(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_FLAG, ERR_SDI1_UNDERRUN_FLAG, __x)
+#define DSI_CMD_MODE_STS_FLAG_ERR_SDI2_UNDERRUN_FLAG_SHIFT 3
+#define DSI_CMD_MODE_STS_FLAG_ERR_SDI2_UNDERRUN_FLAG_MASK 0x00000008
+#define DSI_CMD_MODE_STS_FLAG_ERR_SDI2_UNDERRUN_FLAG(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_FLAG, ERR_SDI2_UNDERRUN_FLAG, __x)
+#define DSI_CMD_MODE_STS_FLAG_ERR_UNWANTED_RD_FLAG_SHIFT 4
+#define DSI_CMD_MODE_STS_FLAG_ERR_UNWANTED_RD_FLAG_MASK 0x00000010
+#define DSI_CMD_MODE_STS_FLAG_ERR_UNWANTED_RD_FLAG(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_FLAG, ERR_UNWANTED_RD_FLAG, __x)
+#define DSI_CMD_MODE_STS_FLAG_CSM_RUNNING_FLAG_SHIFT 5
+#define DSI_CMD_MODE_STS_FLAG_CSM_RUNNING_FLAG_MASK 0x00000020
+#define DSI_CMD_MODE_STS_FLAG_CSM_RUNNING_FLAG(__x) \
+ DSI_VAL2REG(DSI_CMD_MODE_STS_FLAG, CSM_RUNNING_FLAG, __x)
+#define DSI_DIRECT_CMD_STS_FLAG 0x00000138
+#define DSI_DIRECT_CMD_STS_FLAG_CMD_TRANSMISSION_FLAG_SHIFT 0
+#define DSI_DIRECT_CMD_STS_FLAG_CMD_TRANSMISSION_FLAG_MASK 0x00000001
+#define DSI_DIRECT_CMD_STS_FLAG_CMD_TRANSMISSION_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_FLAG, CMD_TRANSMISSION_FLAG, __x)
+#define DSI_DIRECT_CMD_STS_FLAG_WRITE_COMPLETED_FLAG_SHIFT 1
+#define DSI_DIRECT_CMD_STS_FLAG_WRITE_COMPLETED_FLAG_MASK 0x00000002
+#define DSI_DIRECT_CMD_STS_FLAG_WRITE_COMPLETED_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_FLAG, WRITE_COMPLETED_FLAG, __x)
+#define DSI_DIRECT_CMD_STS_FLAG_TRIGGER_COMPLETED_FLAG_SHIFT 2
+#define DSI_DIRECT_CMD_STS_FLAG_TRIGGER_COMPLETED_FLAG_MASK 0x00000004
+#define DSI_DIRECT_CMD_STS_FLAG_TRIGGER_COMPLETED_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_FLAG, TRIGGER_COMPLETED_FLAG, __x)
+#define DSI_DIRECT_CMD_STS_FLAG_READ_COMPLETED_FLAG_SHIFT 3
+#define DSI_DIRECT_CMD_STS_FLAG_READ_COMPLETED_FLAG_MASK 0x00000008
+#define DSI_DIRECT_CMD_STS_FLAG_READ_COMPLETED_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_FLAG, READ_COMPLETED_FLAG, __x)
+#define DSI_DIRECT_CMD_STS_FLAG_ACKNOWLEDGE_RECEIVED_FLAG_SHIFT 4
+#define DSI_DIRECT_CMD_STS_FLAG_ACKNOWLEDGE_RECEIVED_FLAG_MASK 0x00000010
+#define DSI_DIRECT_CMD_STS_FLAG_ACKNOWLEDGE_RECEIVED_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_FLAG, ACKNOWLEDGE_RECEIVED_FLAG, __x)
+#define DSI_DIRECT_CMD_STS_FLAG_ACKNOWLEDGE_WITH_ERR_RECEIVED_FLAG_SHIFT 5
+#define DSI_DIRECT_CMD_STS_FLAG_ACKNOWLEDGE_WITH_ERR_RECEIVED_FLAG_MASK 0x00000020
+#define DSI_DIRECT_CMD_STS_FLAG_ACKNOWLEDGE_WITH_ERR_RECEIVED_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_FLAG, ACKNOWLEDGE_WITH_ERR_RECEIVED_FLAG, __x)
+#define DSI_DIRECT_CMD_STS_FLAG_TRIGGER_RECEIVED_FLAG_SHIFT 6
+#define DSI_DIRECT_CMD_STS_FLAG_TRIGGER_RECEIVED_FLAG_MASK 0x00000040
+#define DSI_DIRECT_CMD_STS_FLAG_TRIGGER_RECEIVED_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_FLAG, TRIGGER_RECEIVED_FLAG, __x)
+#define DSI_DIRECT_CMD_STS_FLAG_TE_RECEIVED_FLAG_SHIFT 7
+#define DSI_DIRECT_CMD_STS_FLAG_TE_RECEIVED_FLAG_MASK 0x00000080
+#define DSI_DIRECT_CMD_STS_FLAG_TE_RECEIVED_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_FLAG, TE_RECEIVED_FLAG, __x)
+#define DSI_DIRECT_CMD_STS_FLAG_BTA_COMPLETED_FLAG_SHIFT 8
+#define DSI_DIRECT_CMD_STS_FLAG_BTA_COMPLETED_FLAG_MASK 0x00000100
+#define DSI_DIRECT_CMD_STS_FLAG_BTA_COMPLETED_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_FLAG, BTA_COMPLETED_FLAG, __x)
+#define DSI_DIRECT_CMD_STS_FLAG_BTA_FINISHED_FLAG_SHIFT 9
+#define DSI_DIRECT_CMD_STS_FLAG_BTA_FINISHED_FLAG_MASK 0x00000200
+#define DSI_DIRECT_CMD_STS_FLAG_BTA_FINISHED_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_FLAG, BTA_FINISHED_FLAG, __x)
+#define DSI_DIRECT_CMD_STS_FLAG_READ_COMPLETED_WITH_ERR_FLAG_SHIFT 10
+#define DSI_DIRECT_CMD_STS_FLAG_READ_COMPLETED_WITH_ERR_FLAG_MASK 0x00000400
+#define DSI_DIRECT_CMD_STS_FLAG_READ_COMPLETED_WITH_ERR_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_STS_FLAG, READ_COMPLETED_WITH_ERR_FLAG, __x)
+#define DSI_DIRECT_CMD_RD_STS_FLAG 0x0000013C
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_FIXED_FLAG_SHIFT 0
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_FIXED_FLAG_MASK 0x00000001
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_FIXED_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_FLAG, ERR_FIXED_FLAG, __x)
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_UNCORRECTABLE_FLAG_SHIFT 1
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_UNCORRECTABLE_FLAG_MASK 0x00000002
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_UNCORRECTABLE_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_FLAG, ERR_UNCORRECTABLE_FLAG, __x)
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_CHECKSUM_FLAG_SHIFT 2
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_CHECKSUM_FLAG_MASK 0x00000004
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_CHECKSUM_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_FLAG, ERR_CHECKSUM_FLAG, __x)
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_UNDECODABLE_FLAG_SHIFT 3
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_UNDECODABLE_FLAG_MASK 0x00000008
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_UNDECODABLE_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_FLAG, ERR_UNDECODABLE_FLAG, __x)
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_RECEIVE_FLAG_SHIFT 4
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_RECEIVE_FLAG_MASK 0x00000010
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_RECEIVE_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_FLAG, ERR_RECEIVE_FLAG, __x)
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_OVERSIZE_FLAG_SHIFT 5
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_OVERSIZE_FLAG_MASK 0x00000020
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_OVERSIZE_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_FLAG, ERR_OVERSIZE_FLAG, __x)
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_WRONG_LENGTH_FLAG_SHIFT 6
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_WRONG_LENGTH_FLAG_MASK 0x00000040
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_WRONG_LENGTH_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_FLAG, ERR_WRONG_LENGTH_FLAG, __x)
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_MISSING_EOT_FLAG_SHIFT 7
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_MISSING_EOT_FLAG_MASK 0x00000080
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_MISSING_EOT_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_FLAG, ERR_MISSING_EOT_FLAG, __x)
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_EOT_WITH_ERR_FLAG_SHIFT 8
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_EOT_WITH_ERR_FLAG_MASK 0x00000100
+#define DSI_DIRECT_CMD_RD_STS_FLAG_ERR_EOT_WITH_ERR_FLAG(__x) \
+ DSI_VAL2REG(DSI_DIRECT_CMD_RD_STS_FLAG, ERR_EOT_WITH_ERR_FLAG, __x)
+#define DSI_VID_MODE_STS_FLAG 0x00000140
+#define DSI_VID_MODE_STS_FLAG_VSG_STS_FLAG_SHIFT 0
+#define DSI_VID_MODE_STS_FLAG_VSG_STS_FLAG_MASK 0x00000001
+#define DSI_VID_MODE_STS_FLAG_VSG_STS_FLAG(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_FLAG, VSG_STS_FLAG, __x)
+#define DSI_VID_MODE_STS_FLAG_ERR_MISSING_DATA_FLAG_SHIFT 1
+#define DSI_VID_MODE_STS_FLAG_ERR_MISSING_DATA_FLAG_MASK 0x00000002
+#define DSI_VID_MODE_STS_FLAG_ERR_MISSING_DATA_FLAG(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_FLAG, ERR_MISSING_DATA_FLAG, __x)
+#define DSI_VID_MODE_STS_FLAG_ERR_MISSING_HSYNC_FLAG_SHIFT 2
+#define DSI_VID_MODE_STS_FLAG_ERR_MISSING_HSYNC_FLAG_MASK 0x00000004
+#define DSI_VID_MODE_STS_FLAG_ERR_MISSING_HSYNC_FLAG(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_FLAG, ERR_MISSING_HSYNC_FLAG, __x)
+#define DSI_VID_MODE_STS_FLAG_ERR_MISSING_VSYNC_FLAG_SHIFT 3
+#define DSI_VID_MODE_STS_FLAG_ERR_MISSING_VSYNC_FLAG_MASK 0x00000008
+#define DSI_VID_MODE_STS_FLAG_ERR_MISSING_VSYNC_FLAG(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_FLAG, ERR_MISSING_VSYNC_FLAG, __x)
+#define DSI_VID_MODE_STS_FLAG_REG_ERR_SMALL_LENGTH_FLAG_SHIFT 4
+#define DSI_VID_MODE_STS_FLAG_REG_ERR_SMALL_LENGTH_FLAG_MASK 0x00000010
+#define DSI_VID_MODE_STS_FLAG_REG_ERR_SMALL_LENGTH_FLAG(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_FLAG, REG_ERR_SMALL_LENGTH_FLAG, __x)
+#define DSI_VID_MODE_STS_FLAG_REG_ERR_SMALL_HEIGHT_FLAG_SHIFT 5
+#define DSI_VID_MODE_STS_FLAG_REG_ERR_SMALL_HEIGHT_FLAG_MASK 0x00000020
+#define DSI_VID_MODE_STS_FLAG_REG_ERR_SMALL_HEIGHT_FLAG(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_FLAG, REG_ERR_SMALL_HEIGHT_FLAG, __x)
+#define DSI_VID_MODE_STS_FLAG_ERR_BURSTWRITE_FLAG_SHIFT 6
+#define DSI_VID_MODE_STS_FLAG_ERR_BURSTWRITE_FLAG_MASK 0x00000040
+#define DSI_VID_MODE_STS_FLAG_ERR_BURSTWRITE_FLAG(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_FLAG, ERR_BURSTWRITE_FLAG, __x)
+#define DSI_VID_MODE_STS_FLAG_ERR_LONGWRITE_FLAG_SHIFT 7
+#define DSI_VID_MODE_STS_FLAG_ERR_LONGWRITE_FLAG_MASK 0x00000080
+#define DSI_VID_MODE_STS_FLAG_ERR_LONGWRITE_FLAG(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_FLAG, ERR_LONGWRITE_FLAG, __x)
+#define DSI_VID_MODE_STS_FLAG_ERR_LONGREAD_FLAG_SHIFT 8
+#define DSI_VID_MODE_STS_FLAG_ERR_LONGREAD_FLAG_MASK 0x00000100
+#define DSI_VID_MODE_STS_FLAG_ERR_LONGREAD_FLAG(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_FLAG, ERR_LONGREAD_FLAG, __x)
+#define DSI_VID_MODE_STS_FLAG_ERR_VRS_WRONG_LENGTH_FLAG_SHIFT 9
+#define DSI_VID_MODE_STS_FLAG_ERR_VRS_WRONG_LENGTH_FLAG_MASK 0x00000200
+#define DSI_VID_MODE_STS_FLAG_ERR_VRS_WRONG_LENGTH_FLAG(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_FLAG, ERR_VRS_WRONG_LENGTH_FLAG, __x)
+#define DSI_VID_MODE_STS_FLAG_VSG_RECOVERY_FLAG_SHIFT 10
+#define DSI_VID_MODE_STS_FLAG_VSG_RECOVERY_FLAG_MASK 0x00000400
+#define DSI_VID_MODE_STS_FLAG_VSG_RECOVERY_FLAG(__x) \
+ DSI_VAL2REG(DSI_VID_MODE_STS_FLAG, VSG_RECOVERY_FLAG, __x)
+#define DSI_TG_STS_FLAG 0x00000144
+#define DSI_TG_STS_FLAG_TVG_STS_FLAG_SHIFT 0
+#define DSI_TG_STS_FLAG_TVG_STS_FLAG_MASK 0x00000001
+#define DSI_TG_STS_FLAG_TVG_STS_FLAG(__x) \
+ DSI_VAL2REG(DSI_TG_STS_FLAG, TVG_STS_FLAG, __x)
+#define DSI_TG_STS_FLAG_TBG_STS_FLAG_SHIFT 1
+#define DSI_TG_STS_FLAG_TBG_STS_FLAG_MASK 0x00000002
+#define DSI_TG_STS_FLAG_TBG_STS_FLAG(__x) \
+ DSI_VAL2REG(DSI_TG_STS_FLAG, TBG_STS_FLAG, __x)
+#define DSI_MCTL_DPHY_ERR_FLAG 0x00000148
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_ESC_1_FLAG_SHIFT 6
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_ESC_1_FLAG_MASK 0x00000040
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_ESC_1_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_FLAG, ERR_ESC_1_FLAG, __x)
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_ESC_2_FLAG_SHIFT 7
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_ESC_2_FLAG_MASK 0x00000080
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_ESC_2_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_FLAG, ERR_ESC_2_FLAG, __x)
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_SYNCESC_1_FLAG_SHIFT 8
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_SYNCESC_1_FLAG_MASK 0x00000100
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_SYNCESC_1_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_FLAG, ERR_SYNCESC_1_FLAG, __x)
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_SYNCESC_2_FLAG_SHIFT 9
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_SYNCESC_2_FLAG_MASK 0x00000200
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_SYNCESC_2_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_FLAG, ERR_SYNCESC_2_FLAG, __x)
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONTROL_1_FLAG_SHIFT 10
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONTROL_1_FLAG_MASK 0x00000400
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONTROL_1_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_FLAG, ERR_CONTROL_1_FLAG, __x)
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONTROL_2_FLAG_SHIFT 11
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONTROL_2_FLAG_MASK 0x00000800
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONTROL_2_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_FLAG, ERR_CONTROL_2_FLAG, __x)
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP0_1_FLAG_SHIFT 12
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP0_1_FLAG_MASK 0x00001000
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP0_1_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_FLAG, ERR_CONT_LP0_1_FLAG, __x)
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP0_2_FLAG_SHIFT 13
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP0_2_FLAG_MASK 0x00002000
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP0_2_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_FLAG, ERR_CONT_LP0_2_FLAG, __x)
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP1_1_FLAG_SHIFT 14
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP1_1_FLAG_MASK 0x00004000
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP1_1_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_FLAG, ERR_CONT_LP1_1_FLAG, __x)
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP1_2_FLAG_SHIFT 15
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP1_2_FLAG_MASK 0x00008000
+#define DSI_MCTL_DPHY_ERR_FLAG_ERR_CONT_LP1_2_FLAG(__x) \
+ DSI_VAL2REG(DSI_MCTL_DPHY_ERR_FLAG, ERR_CONT_LP1_2_FLAG, __x)
+#define DSI_DPHY_LANES_TRIM 0x00000150
+#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_DAT1_SHIFT 0
+#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_DAT1_MASK 0x00000003
+#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_DAT1(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_SKEW_DAT1, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_CD_OFF_DAT1_SHIFT 2
+#define DSI_DPHY_LANES_TRIM_DPHY_CD_OFF_DAT1_MASK 0x00000004
+#define DSI_DPHY_LANES_TRIM_DPHY_CD_OFF_DAT1(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_CD_OFF_DAT1, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_DAT1_SHIFT 3
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_DAT1_MASK 0x00000008
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_DAT1(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_HSTX_SLEWRATE_UP_DAT1, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_DAT1_SHIFT 4
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_DAT1_MASK 0x00000010
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_DAT1(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_HSTX_SLEWRATE_DOWN_DAT1, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_DAT1_SHIFT 5
+#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_DAT1_MASK 0x00000020
+#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_DAT1(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_TEST_RESERVED_1_DAT1, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_CLK_SHIFT 6
+#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_CLK_MASK 0x000000C0
+#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_CLK(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_SKEW_CLK, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_LP_RX_VIL_CLK_SHIFT 8
+#define DSI_DPHY_LANES_TRIM_DPHY_LP_RX_VIL_CLK_MASK 0x00000300
+#define DSI_DPHY_LANES_TRIM_DPHY_LP_RX_VIL_CLK(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_LP_RX_VIL_CLK, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_LP_TX_SLEWRATE_CLK_SHIFT 10
+#define DSI_DPHY_LANES_TRIM_DPHY_LP_TX_SLEWRATE_CLK_MASK 0x00000C00
+#define DSI_DPHY_LANES_TRIM_DPHY_LP_TX_SLEWRATE_CLK(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_LP_TX_SLEWRATE_CLK, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_SHIFT 12
+#define DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_MASK 0x00001000
+#define DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_0_81 0
+#define DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_0_90 1
+#define DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_ENUM(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_SPECS_90_81B, \
+ DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_##__x)
+#define DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_SPECS_90_81B, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_CLK_SHIFT 13
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_CLK_MASK 0x00002000
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_CLK(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_HSTX_SLEWRATE_UP_CLK, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_CLK_SHIFT 14
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_CLK_MASK 0x00004000
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_CLK(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_HSTX_SLEWRATE_DOWN_CLK, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_CLK_SHIFT 15
+#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_CLK_MASK 0x00008000
+#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_CLK(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_TEST_RESERVED_1_CLK, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_DAT2_SHIFT 16
+#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_DAT2_MASK 0x00030000
+#define DSI_DPHY_LANES_TRIM_DPHY_SKEW_DAT2(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_SKEW_DAT2, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_DAT2_SHIFT 18
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_DAT2_MASK 0x00040000
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_UP_DAT2(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_HSTX_SLEWRATE_UP_DAT2, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_DAT2_SHIFT 19
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_DAT2_MASK 0x00080000
+#define DSI_DPHY_LANES_TRIM_DPHY_HSTX_SLEWRATE_DOWN_DAT2(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_HSTX_SLEWRATE_DOWN_DAT2, __x)
+#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_DAT2_SHIFT 20
+#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_DAT2_MASK 0x00100000
+#define DSI_DPHY_LANES_TRIM_DPHY_TEST_RESERVED_1_DAT2(__x) \
+ DSI_VAL2REG(DSI_DPHY_LANES_TRIM, DPHY_TEST_RESERVED_1_DAT2, __x)
+#define DSI_ID_REG 0x00000FF0
+#define DSI_ID_REG_Y_SHIFT 0
+#define DSI_ID_REG_Y_MASK 0x0000000F
+#define DSI_ID_REG_Y(__x) \
+ DSI_VAL2REG(DSI_ID_REG, Y, __x)
+#define DSI_ID_REG_X_SHIFT 4
+#define DSI_ID_REG_X_MASK 0x000000F0
+#define DSI_ID_REG_X(__x) \
+ DSI_VAL2REG(DSI_ID_REG, X, __x)
+#define DSI_ID_REG_H_SHIFT 8
+#define DSI_ID_REG_H_MASK 0x00000300
+#define DSI_ID_REG_H(__x) \
+ DSI_VAL2REG(DSI_ID_REG, H, __x)
+#define DSI_ID_REG_PRODUCT_ID_SHIFT 10
+#define DSI_ID_REG_PRODUCT_ID_MASK 0x0003FC00
+#define DSI_ID_REG_PRODUCT_ID(__x) \
+ DSI_VAL2REG(DSI_ID_REG, PRODUCT_ID, __x)
+#define DSI_ID_REG_VENDOR_ID_SHIFT 18
+#define DSI_ID_REG_VENDOR_ID_MASK 0xFFFC0000
+#define DSI_ID_REG_VENDOR_ID(__x) \
+ DSI_VAL2REG(DSI_ID_REG, VENDOR_ID, __x)
+#define DSI_IP_CONF 0x00000FF4
+#define DSI_IP_CONF_FIFO_SIZE_SHIFT 0
+#define DSI_IP_CONF_FIFO_SIZE_MASK 0x0000003F
+#define DSI_IP_CONF_FIFO_SIZE(__x) \
+ DSI_VAL2REG(DSI_IP_CONF, FIFO_SIZE, __x)
diff --git a/drivers/video/mcde/mcde_bus.c b/drivers/video/mcde/mcde_bus.c
new file mode 100644
index 00000000000..3d7049652a1
--- /dev/null
+++ b/drivers/video/mcde/mcde_bus.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * ST-Ericsson MCDE display bus driver
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/notifier.h>
+
+#include <video/mcde_display.h>
+#include <video/mcde_dss.h>
+
+#define to_mcde_display_driver(__drv) \
+ container_of((__drv), struct mcde_display_driver, driver)
+
+static BLOCKING_NOTIFIER_HEAD(bus_notifier_list);
+
+static int mcde_drv_suspend(struct device *_dev, pm_message_t state);
+static int mcde_drv_resume(struct device *_dev);
+struct bus_type mcde_bus_type;
+
+static int mcde_suspend_device(struct device *dev, void *data)
+{
+ pm_message_t* state = (pm_message_t *) data;
+ if (dev->driver->suspend)
+ return dev->driver->suspend(dev, *state);
+ return 0;
+}
+
+static int mcde_resume_device(struct device *dev, void *data)
+{
+ if (dev->driver->resume)
+ return dev->driver->resume(dev);
+ return 0;
+}
+
+/* Bus driver */
+
+static int mcde_bus_match(struct device *_dev, struct device_driver *driver)
+{
+ pr_debug("Matching device %s with driver %s\n",
+ dev_name(_dev), driver->name);
+
+ return strncmp(dev_name(_dev), driver->name, strlen(driver->name)) == 0;
+}
+
+static int mcde_bus_suspend(struct device *_dev, pm_message_t state)
+{
+ int ret;
+ ret = bus_for_each_dev(&mcde_bus_type, NULL, &state,
+ mcde_suspend_device);
+ if (ret) {
+ /* TODO Resume all suspended devices */
+ /* mcde_bus_resume(dev); */
+ return ret;
+ }
+ return 0;
+}
+
+static int mcde_bus_resume(struct device *_dev)
+{
+ return bus_for_each_dev(&mcde_bus_type, NULL, NULL, mcde_resume_device);
+}
+
+struct bus_type mcde_bus_type = {
+ .name = "mcde_bus",
+ .match = mcde_bus_match,
+ .suspend = mcde_bus_suspend,
+ .resume = mcde_bus_resume,
+};
+
+static int mcde_drv_probe(struct device *_dev)
+{
+ struct mcde_display_driver *drv = to_mcde_display_driver(_dev->driver);
+ struct mcde_display_device *dev = to_mcde_display_device(_dev);
+
+ return drv->probe(dev);
+}
+
+static int mcde_drv_remove(struct device *_dev)
+{
+ struct mcde_display_driver *drv = to_mcde_display_driver(_dev->driver);
+ struct mcde_display_device *dev = to_mcde_display_device(_dev);
+
+ return drv->remove(dev);
+}
+
+static void mcde_drv_shutdown(struct device *_dev)
+{
+ struct mcde_display_driver *drv = to_mcde_display_driver(_dev->driver);
+ struct mcde_display_device *dev = to_mcde_display_device(_dev);
+
+ drv->shutdown(dev);
+}
+
+static int mcde_drv_suspend(struct device *_dev, pm_message_t state)
+{
+ struct mcde_display_driver *drv = to_mcde_display_driver(_dev->driver);
+ struct mcde_display_device *dev = to_mcde_display_device(_dev);
+
+ return drv->suspend(dev, state);
+}
+
+static int mcde_drv_resume(struct device *_dev)
+{
+ struct mcde_display_driver *drv = to_mcde_display_driver(_dev->driver);
+ struct mcde_display_device *dev = to_mcde_display_device(_dev);
+
+ return drv->resume(dev);
+}
+
+/* Bus device */
+
+static void mcde_bus_release(struct device *dev)
+{
+}
+
+struct device mcde_bus = {
+ .init_name = "mcde_bus",
+ .release = mcde_bus_release
+};
+
+/* Public bus API */
+
+int mcde_display_driver_register(struct mcde_display_driver *drv)
+{
+ drv->driver.bus = &mcde_bus_type;
+ if (drv->probe)
+ drv->driver.probe = mcde_drv_probe;
+ if (drv->remove)
+ drv->driver.remove = mcde_drv_remove;
+ if (drv->shutdown)
+ drv->driver.shutdown = mcde_drv_shutdown;
+ if (drv->suspend)
+ drv->driver.suspend = mcde_drv_suspend;
+ if (drv->resume)
+ drv->driver.resume = mcde_drv_resume;
+
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(mcde_display_driver_register);
+
+void mcde_display_driver_unregister(struct mcde_display_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(mcde_display_driver_unregister);
+
+static void mcde_display_dev_release(struct device *dev)
+{
+ /* Do nothing */
+}
+
+int mcde_display_device_register(struct mcde_display_device *dev)
+{
+ /* Setup device */
+ if (!dev)
+ return -EINVAL;
+ dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ dev->dev.bus = &mcde_bus_type;
+ if (dev->dev.parent != NULL)
+ dev->dev.parent = &mcde_bus;
+ dev->dev.release = mcde_display_dev_release;
+ if (dev->id != -1)
+ dev_set_name(&dev->dev, "%s.%d", dev->name, dev->id);
+ else
+ dev_set_name(&dev->dev, dev->name);
+
+ mcde_display_init_device(dev);
+
+ return device_register(&dev->dev);
+}
+EXPORT_SYMBOL(mcde_display_device_register);
+
+void mcde_display_device_unregister(struct mcde_display_device *dev)
+{
+ device_unregister(&dev->dev);
+}
+EXPORT_SYMBOL(mcde_display_device_unregister);
+
+/* Notifications */
+int mcde_dss_register_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&bus_notifier_list, nb);
+}
+EXPORT_SYMBOL(mcde_dss_register_notifier);
+
+int mcde_dss_unregister_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&bus_notifier_list, nb);
+}
+EXPORT_SYMBOL(mcde_dss_unregister_notifier);
+
+static int bus_notify_callback(struct notifier_block *nb,
+ unsigned long event, void *dev)
+{
+ struct mcde_display_device *ddev = to_mcde_display_device(dev);
+
+ if (event == BUS_NOTIFY_BOUND_DRIVER) {
+ ddev->initialized = true;
+ blocking_notifier_call_chain(&bus_notifier_list,
+ MCDE_DSS_EVENT_DISPLAY_REGISTERED, ddev);
+ } else if (event == BUS_NOTIFY_UNBIND_DRIVER) {
+ ddev->initialized = false;
+ blocking_notifier_call_chain(&bus_notifier_list,
+ MCDE_DSS_EVENT_DISPLAY_UNREGISTERED, ddev);
+ }
+ return 0;
+}
+
+struct notifier_block bus_nb = {
+ .notifier_call = bus_notify_callback,
+};
+
+/* Driver init/exit */
+
+int __init mcde_display_init(void)
+{
+ int ret;
+
+ ret = bus_register(&mcde_bus_type);
+ if (ret) {
+ pr_warning("Unable to register bus type\n");
+ goto no_bus_registration;
+ }
+ ret = device_register(&mcde_bus);
+ if (ret) {
+ pr_warning("Unable to register bus device\n");
+ goto no_device_registration;
+ }
+ ret = bus_register_notifier(&mcde_bus_type, &bus_nb);
+ if (ret) {
+ pr_warning("Unable to register bus notifier\n");
+ goto no_bus_notifier;
+ }
+
+ goto out;
+
+no_bus_notifier:
+ device_unregister(&mcde_bus);
+no_device_registration:
+ bus_unregister(&mcde_bus_type);
+no_bus_registration:
+out:
+ return ret;
+}
+
+void mcde_display_exit(void)
+{
+ bus_unregister_notifier(&mcde_bus_type, &bus_nb);
+ device_unregister(&mcde_bus);
+ bus_unregister(&mcde_bus_type);
+}
diff --git a/drivers/video/mcde/mcde_display.c b/drivers/video/mcde/mcde_display.c
new file mode 100644
index 00000000000..144a1a67d02
--- /dev/null
+++ b/drivers/video/mcde/mcde_display.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson MCDE display driver
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include <video/mcde_display.h>
+
+/*temp*/
+#include <linux/delay.h>
+
+static void mcde_display_get_native_resolution_default(
+ struct mcde_display_device *ddev, u16 *x_res, u16 *y_res)
+{
+ if (x_res)
+ *x_res = ddev->native_x_res;
+ if (y_res)
+ *y_res = ddev->native_y_res;
+}
+
+static enum mcde_ovly_pix_fmt mcde_display_get_default_pixel_format_default(
+ struct mcde_display_device *ddev)
+{
+ return ddev->default_pixel_format;
+}
+
+static void mcde_display_get_physical_size_default(
+ struct mcde_display_device *ddev, u16 *width, u16 *height)
+{
+ if (width)
+ *width = ddev->physical_width;
+ if (height)
+ *height = ddev->physical_height;
+}
+
+static int mcde_display_set_power_mode_default(struct mcde_display_device *ddev,
+ enum mcde_display_power_mode power_mode)
+{
+ int ret = 0;
+
+ /* OFF -> STANDBY */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_OFF &&
+ power_mode != MCDE_DISPLAY_PM_OFF) {
+ if (ddev->platform_enable) {
+ ret = ddev->platform_enable(ddev);
+ if (ret)
+ return ret;
+ }
+ ddev->power_mode = MCDE_DISPLAY_PM_STANDBY;
+ /* force register settings */
+ if (ddev->port->type == MCDE_PORTTYPE_DPI)
+ ddev->update_flags = UPDATE_FLAG_VIDEO_MODE | UPDATE_FLAG_PIXEL_FORMAT;
+ }
+
+ if (ddev->port->type == MCDE_PORTTYPE_DSI) {
+ /* STANDBY -> ON */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_STANDBY &&
+ power_mode == MCDE_DISPLAY_PM_ON) {
+ ret = mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_EXIT_SLEEP_MODE, NULL, 0);
+ if (ret)
+ return ret;
+
+ ret = mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_SET_DISPLAY_ON, NULL, 0);
+ if (ret)
+ return ret;
+
+ ddev->power_mode = MCDE_DISPLAY_PM_ON;
+ } else if (ddev->power_mode == MCDE_DISPLAY_PM_ON &&
+ power_mode <= MCDE_DISPLAY_PM_STANDBY) {
+ /* ON -> STANDBY */
+ ret = mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_SET_DISPLAY_OFF, NULL, 0);
+ if (ret)
+ return ret;
+
+ ret = mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_ENTER_SLEEP_MODE, NULL, 0);
+ if (ret)
+ return ret;
+
+ ddev->power_mode = MCDE_DISPLAY_PM_STANDBY;
+ }
+ } else if (ddev->port->type == MCDE_PORTTYPE_DPI) {
+ ddev->power_mode = power_mode;
+ } else if (ddev->power_mode != power_mode) {
+ return -EINVAL;
+ }
+
+ /* SLEEP -> OFF */
+ if (ddev->power_mode == MCDE_DISPLAY_PM_STANDBY &&
+ power_mode == MCDE_DISPLAY_PM_OFF) {
+ if (ddev->platform_disable) {
+ ret = ddev->platform_disable(ddev);
+ if (ret)
+ return ret;
+ }
+ ddev->power_mode = MCDE_DISPLAY_PM_OFF;
+ }
+
+ mcde_chnl_set_power_mode(ddev->chnl_state, ddev->power_mode);
+
+ return ret;
+}
+
+static inline enum mcde_display_power_mode mcde_display_get_power_mode_default(
+ struct mcde_display_device *ddev)
+{
+ return ddev->power_mode;
+}
+
+static inline int mcde_display_try_video_mode_default(
+ struct mcde_display_device *ddev,
+ struct mcde_video_mode *video_mode)
+{
+ /* TODO Check if inside native_xres and native_yres */
+ return 0;
+}
+
+static int mcde_display_set_video_mode_default(struct mcde_display_device *ddev,
+ struct mcde_video_mode *video_mode)
+{
+ int ret;
+ struct mcde_video_mode channel_video_mode;
+
+ if (!video_mode)
+ return -EINVAL;
+
+ ddev->video_mode = *video_mode;
+ channel_video_mode = ddev->video_mode;
+ /* Dependant on if display should rotate or MCDE should rotate */
+ if (ddev->rotation == MCDE_DISPLAY_ROT_90_CCW ||
+ ddev->rotation == MCDE_DISPLAY_ROT_90_CW) {
+ channel_video_mode.xres = ddev->native_x_res;
+ channel_video_mode.yres = ddev->native_y_res;
+ }
+ ret = mcde_chnl_set_video_mode(ddev->chnl_state, &channel_video_mode);
+ if (ret < 0) {
+ dev_warn(&ddev->dev, "%s:Failed to set video mode\n", __func__);
+ return ret;
+ }
+
+ ddev->update_flags |= UPDATE_FLAG_VIDEO_MODE;
+
+ return 0;
+}
+
+static inline void mcde_display_get_video_mode_default(
+ struct mcde_display_device *ddev, struct mcde_video_mode *video_mode)
+{
+ if (video_mode)
+ *video_mode = ddev->video_mode;
+}
+
+static int mcde_display_set_pixel_format_default(
+ struct mcde_display_device *ddev, enum mcde_ovly_pix_fmt format)
+{
+ int ret;
+
+ ddev->pixel_format = format;
+ ret = mcde_chnl_set_pixel_format(ddev->chnl_state,
+ ddev->port->pixel_format);
+ if (ret < 0) {
+ dev_warn(&ddev->dev, "%s:Failed to set pixel format = %d\n",
+ __func__, format);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline enum mcde_ovly_pix_fmt mcde_display_get_pixel_format_default(
+ struct mcde_display_device *ddev)
+{
+ return ddev->pixel_format;
+}
+
+
+static int mcde_display_set_rotation_default(struct mcde_display_device *ddev,
+ enum mcde_display_rotation rotation)
+{
+ int ret;
+
+ ret = mcde_chnl_set_rotation(ddev->chnl_state, rotation,
+ ddev->rotbuf1, ddev->rotbuf2);
+ if (ret < 0) {
+ dev_warn(&ddev->dev, "%s:Failed to set rotation = %d\n",
+ __func__, rotation);
+ return ret;
+ }
+
+ if (rotation == MCDE_DISPLAY_ROT_180_CCW) {
+ u8 param = 0x40;
+ (void) mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_SET_ADDRESS_MODE, &param, 1);
+ } else if (ddev->rotation == MCDE_DISPLAY_ROT_180_CCW &&
+ rotation != MCDE_DISPLAY_ROT_180_CCW) {
+ u8 param = 0;
+ (void) mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_SET_ADDRESS_MODE, &param, 1);
+ }
+
+ ddev->rotation = rotation;
+ ddev->update_flags |= UPDATE_FLAG_ROTATION;
+
+ return 0;
+}
+
+static inline enum mcde_display_rotation mcde_display_get_rotation_default(
+ struct mcde_display_device *ddev)
+{
+ return ddev->rotation;
+}
+
+static int mcde_display_set_synchronized_update_default(
+ struct mcde_display_device *ddev, bool enable)
+{
+ if (ddev->port->type == MCDE_PORTTYPE_DSI && enable) {
+ int ret;
+ u8 m = 0;
+
+ if (ddev->port->sync_src == MCDE_SYNCSRC_OFF)
+ return -EINVAL;
+
+ ret = mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_SET_TEAR_ON, &m, 1);
+ if (ret < 0) {
+ dev_warn(&ddev->dev,
+ "%s:Failed to set synchornized update = %d\n",
+ __func__, enable);
+ return ret;
+ }
+ }
+ ddev->synchronized_update = enable;
+ return 0;
+}
+
+static inline bool mcde_display_get_synchronized_update_default(
+ struct mcde_display_device *ddev)
+{
+ return ddev->synchronized_update;
+}
+
+static int mcde_display_apply_config_default(struct mcde_display_device *ddev)
+{
+ int ret;
+
+ ret = mcde_chnl_enable_synchronized_update(ddev->chnl_state,
+ ddev->synchronized_update);
+
+ if (ret < 0) {
+ dev_warn(&ddev->dev,
+ "%s:Failed to enable synchronized update\n",
+ __func__);
+ return ret;
+ }
+
+ if (!ddev->update_flags)
+ return 0;
+
+ if (ddev->update_flags & UPDATE_FLAG_VIDEO_MODE)
+ mcde_chnl_stop_flow(ddev->chnl_state);
+
+ ret = mcde_chnl_apply(ddev->chnl_state);
+ if (ret < 0) {
+ dev_warn(&ddev->dev, "%s:Failed to apply to channel\n",
+ __func__);
+ return ret;
+ }
+ ddev->update_flags = 0;
+ ddev->first_update = true;
+
+ return 0;
+}
+
+static int mcde_display_invalidate_area_default(
+ struct mcde_display_device *ddev,
+ struct mcde_rectangle *area)
+{
+ dev_vdbg(&ddev->dev, "%s\n", __func__);
+ if (area) {
+ /* take union of rects */
+ u16 t;
+ t = min(ddev->update_area.x, area->x);
+ /* note should be > 0 */
+ ddev->update_area.w = max(ddev->update_area.x +
+ ddev->update_area.w,
+ area->x + area->w) - t;
+ ddev->update_area.x = t;
+ t = min(ddev->update_area.y, area->y);
+ ddev->update_area.h = max(ddev->update_area.y +
+ ddev->update_area.h,
+ area->y + area->h) - t;
+ ddev->update_area.y = t;
+ /* TODO: Implement real clipping when partial refresh is
+ activated.*/
+ ddev->update_area.w = min((u16) ddev->video_mode.xres,
+ (u16) ddev->update_area.w);
+ ddev->update_area.h = min((u16) ddev->video_mode.yres,
+ (u16) ddev->update_area.h);
+ } else {
+ ddev->update_area.x = 0;
+ ddev->update_area.y = 0;
+ ddev->update_area.w = ddev->video_mode.xres;
+ ddev->update_area.h = ddev->video_mode.yres;
+ /* Invalidate_area(ddev, NULL) means reset area to empty
+ * rectangle really. After that the rectangle should grow by
+ * taking an union (above). This means that the code should
+ * really look like below, however the code above is a temp fix
+ * for rotation.
+ * TODO: fix
+ * ddev->update_area.x = ddev->video_mode.xres;
+ * ddev->update_area.y = ddev->video_mode.yres;
+ * ddev->update_area.w = 0;
+ * ddev->update_area.h = 0;
+ */
+ }
+
+ return 0;
+}
+
+static int mcde_display_update_default(struct mcde_display_device *ddev,
+ bool tripple_buffer)
+{
+ int ret = 0;
+
+ /* TODO: Dirty */
+ if (ddev->prepare_for_update) {
+ /* TODO: Send dirty rectangle */
+ ret = ddev->prepare_for_update(ddev, 0, 0,
+ ddev->native_x_res, ddev->native_y_res);
+ if (ret < 0) {
+ dev_warn(&ddev->dev,
+ "%s:Failed to prepare for update\n", __func__);
+ return ret;
+ }
+ }
+ /* TODO: Calculate & set update rect */
+ ret = mcde_chnl_update(ddev->chnl_state, &ddev->update_area,
+ tripple_buffer);
+ if (ret < 0) {
+ dev_warn(&ddev->dev, "%s:Failed to update channel\n", __func__);
+ return ret;
+ }
+ if (ddev->first_update && ddev->on_first_update)
+ ddev->on_first_update(ddev);
+
+ if (ddev->power_mode != MCDE_DISPLAY_PM_ON && ddev->set_power_mode) {
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_ON);
+ if (ret < 0) {
+ dev_warn(&ddev->dev,
+ "%s:Failed to set power mode to on\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ dev_vdbg(&ddev->dev, "Overlay updated, chnl=%d\n", ddev->chnl_id);
+
+ return 0;
+}
+
+static int mcde_display_prepare_for_update_default(
+ struct mcde_display_device *ddev,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ int ret;
+ u8 params[8] = { x >> 8, x & 0xff,
+ (x + w - 1) >> 8, (x + w - 1) & 0xff,
+ y >> 8, y & 0xff,
+ (y + h - 1) >> 8, (y + h - 1) & 0xff };
+
+ if (ddev->port->type != MCDE_PORTTYPE_DSI)
+ return -EINVAL;
+
+ ret = mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_SET_COLUMN_ADDRESS, &params[0], 4);
+ if (ret)
+ return ret;
+
+ ret = mcde_dsi_dcs_write(ddev->chnl_state,
+ DCS_CMD_SET_PAGE_ADDRESS, &params[4], 4);
+
+ return ret;
+}
+
+static inline int mcde_display_on_first_update_default(
+ struct mcde_display_device *ddev)
+{
+ ddev->first_update = false;
+ return 0;
+}
+
+void mcde_display_init_device(struct mcde_display_device *ddev)
+{
+ /* Setup default callbacks */
+ ddev->get_native_resolution =
+ mcde_display_get_native_resolution_default;
+ ddev->get_default_pixel_format =
+ mcde_display_get_default_pixel_format_default;
+ ddev->get_physical_size = mcde_display_get_physical_size_default;
+ ddev->set_power_mode = mcde_display_set_power_mode_default;
+ ddev->get_power_mode = mcde_display_get_power_mode_default;
+ ddev->try_video_mode = mcde_display_try_video_mode_default;
+ ddev->set_video_mode = mcde_display_set_video_mode_default;
+ ddev->get_video_mode = mcde_display_get_video_mode_default;
+ ddev->set_pixel_format = mcde_display_set_pixel_format_default;
+ ddev->get_pixel_format = mcde_display_get_pixel_format_default;
+ ddev->set_rotation = mcde_display_set_rotation_default;
+ ddev->get_rotation = mcde_display_get_rotation_default;
+ ddev->set_synchronized_update =
+ mcde_display_set_synchronized_update_default;
+ ddev->get_synchronized_update =
+ mcde_display_get_synchronized_update_default;
+ ddev->apply_config = mcde_display_apply_config_default;
+ ddev->invalidate_area = mcde_display_invalidate_area_default;
+ ddev->update = mcde_display_update_default;
+ ddev->prepare_for_update = mcde_display_prepare_for_update_default;
+ ddev->on_first_update = mcde_display_on_first_update_default;
+}
+
diff --git a/drivers/video/mcde/mcde_dss.c b/drivers/video/mcde/mcde_dss.c
new file mode 100644
index 00000000000..361510b46fc
--- /dev/null
+++ b/drivers/video/mcde/mcde_dss.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson MCDE display sub system driver
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <video/mcde_dss.h>
+
+#define to_overlay(x) container_of(x, struct mcde_overlay, kobj)
+
+void overlay_release(struct kobject *kobj)
+{
+ struct mcde_overlay *ovly = to_overlay(kobj);
+
+ kfree(ovly);
+}
+
+struct kobj_type ovly_type = {
+ .release = overlay_release,
+};
+
+static int apply_overlay(struct mcde_overlay *ovly,
+ struct mcde_overlay_info *info, bool force)
+{
+ int ret = 0;
+ if (ovly->ddev->invalidate_area) {
+ /* TODO: transform ovly coord to screen coords (vmode):
+ * add offset
+ */
+ struct mcde_rectangle dirty = info->dirty;
+ ret = ovly->ddev->invalidate_area(ovly->ddev, &dirty);
+ }
+
+ if (ovly->info.paddr != info->paddr || force)
+ mcde_ovly_set_source_buf(ovly->state, info->paddr);
+
+ if (ovly->info.stride != info->stride || ovly->info.fmt != info->fmt ||
+ force)
+ mcde_ovly_set_source_info(ovly->state, info->stride, info->fmt);
+ if (ovly->info.src_x != info->src_x ||
+ ovly->info.src_y != info->src_y ||
+ ovly->info.w != info->w ||
+ ovly->info.h != info->h || force)
+ mcde_ovly_set_source_area(ovly->state,
+ info->src_x, info->src_y, info->w, info->h);
+ if (ovly->info.dst_x != info->dst_x || ovly->info.dst_y != info->dst_y
+ || ovly->info.dst_z != info->dst_z ||
+ force)
+ mcde_ovly_set_dest_pos(ovly->state,
+ info->dst_x, info->dst_y, info->dst_z);
+
+ mcde_ovly_apply(ovly->state);
+ ovly->info = *info;
+
+ return ret;
+}
+
+/* MCDE DSS operations */
+
+int mcde_dss_open_channel(struct mcde_display_device *ddev)
+{
+ int ret;
+ struct mcde_chnl_state *chnl;
+
+ /* Acquire MCDE resources */
+ chnl = mcde_chnl_get(ddev->chnl_id, ddev->fifo, ddev->port);
+ if (IS_ERR(chnl)) {
+ ret = PTR_ERR(chnl);
+ dev_warn(&ddev->dev, "Failed to acquire MCDE channel\n");
+ return ret;
+ }
+ ddev->chnl_state = chnl;
+ return 0;
+}
+EXPORT_SYMBOL(mcde_dss_open_channel);
+
+void mcde_dss_close_channel(struct mcde_display_device *ddev)
+{
+ mcde_chnl_put(ddev->chnl_state);
+ ddev->chnl_state = NULL;
+}
+EXPORT_SYMBOL(mcde_dss_close_channel);
+
+int mcde_dss_enable_display(struct mcde_display_device *ddev)
+{
+ int ret;
+
+ if (ddev->enabled)
+ return 0;
+
+ mcde_chnl_enable(ddev->chnl_state);
+
+ /* Initiate display communication */
+ ret = ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_STANDBY);
+ if (ret < 0) {
+ dev_warn(&ddev->dev, "Failed to initialize display\n");
+ goto display_failed;
+ }
+
+ ret = ddev->set_synchronized_update(ddev,
+ ddev->get_synchronized_update(ddev));
+ if (ret < 0)
+ dev_warn(&ddev->dev, "Failed to set sync\n");
+
+ ret = mcde_chnl_enable_synchronized_update(ddev->chnl_state,
+ ddev->synchronized_update);
+ if (ret < 0) {
+ dev_warn(&ddev->dev,
+ "%s:Failed to enable synchronized update\n",
+ __func__);
+ goto enable_sync_failed;
+ }
+ /* TODO: call driver for all defaults like sync_update above */
+
+ dev_dbg(&ddev->dev, "Display enabled, chnl=%d\n",
+ ddev->chnl_id);
+ ddev->enabled = true;
+ return 0;
+
+enable_sync_failed:
+ ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_OFF);
+display_failed:
+ mcde_chnl_disable(ddev->chnl_state);
+ return ret;
+}
+EXPORT_SYMBOL(mcde_dss_enable_display);
+
+void mcde_dss_disable_display(struct mcde_display_device *ddev)
+{
+ if (!ddev->enabled)
+ return;
+
+ /* TODO: Disable overlays */
+
+ mcde_chnl_stop_flow(ddev->chnl_state);
+
+ (void)ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_OFF);
+
+ mcde_chnl_disable(ddev->chnl_state);
+
+ ddev->enabled = false;
+
+ dev_dbg(&ddev->dev, "Display disabled, chnl=%d\n", ddev->chnl_id);
+}
+EXPORT_SYMBOL(mcde_dss_disable_display);
+
+int mcde_dss_apply_channel(struct mcde_display_device *ddev)
+{
+ if (!ddev->apply_config)
+ return -EINVAL;
+
+ return ddev->apply_config(ddev);
+}
+EXPORT_SYMBOL(mcde_dss_apply_channel);
+
+struct mcde_overlay *mcde_dss_create_overlay(struct mcde_display_device *ddev,
+ struct mcde_overlay_info *info)
+{
+ struct mcde_overlay *ovly;
+
+ ovly = kzalloc(sizeof(struct mcde_overlay), GFP_KERNEL);
+ if (!ovly)
+ return NULL;
+
+ kobject_init(&ovly->kobj, &ovly_type); /* Local ref */
+ kobject_get(&ovly->kobj); /* Creator ref */
+ INIT_LIST_HEAD(&ovly->list);
+ list_add(&ddev->ovlys, &ovly->list);
+ ovly->info = *info;
+ ovly->ddev = ddev;
+
+ return ovly;
+}
+EXPORT_SYMBOL(mcde_dss_create_overlay);
+
+void mcde_dss_destroy_overlay(struct mcde_overlay *ovly)
+{
+ list_del(&ovly->list);
+ if (ovly->state)
+ mcde_dss_disable_overlay(ovly);
+ kobject_put(&ovly->kobj);
+}
+EXPORT_SYMBOL(mcde_dss_destroy_overlay);
+
+int mcde_dss_enable_overlay(struct mcde_overlay *ovly)
+{
+ int ret;
+
+ if (!ovly->ddev->chnl_state)
+ return -EINVAL;
+
+ if (!ovly->state) {
+ struct mcde_ovly_state *state;
+ state = mcde_ovly_get(ovly->ddev->chnl_state);
+ if (IS_ERR(state)) {
+ ret = PTR_ERR(state);
+ dev_warn(&ovly->ddev->dev,
+ "Failed to acquire overlay\n");
+ return ret;
+ }
+ ovly->state = state;
+ }
+
+ apply_overlay(ovly, &ovly->info, true);
+
+ dev_vdbg(&ovly->ddev->dev, "Overlay enabled, chnl=%d\n",
+ ovly->ddev->chnl_id);
+ return 0;
+}
+EXPORT_SYMBOL(mcde_dss_enable_overlay);
+
+int mcde_dss_apply_overlay(struct mcde_overlay *ovly,
+ struct mcde_overlay_info *info)
+{
+ if (info == NULL)
+ info = &ovly->info;
+ return apply_overlay(ovly, info, false);
+}
+EXPORT_SYMBOL(mcde_dss_apply_overlay);
+
+void mcde_dss_disable_overlay(struct mcde_overlay *ovly)
+{
+ if (!ovly->state)
+ return;
+
+ mcde_ovly_put(ovly->state);
+
+ dev_dbg(&ovly->ddev->dev, "Overlay disabled, chnl=%d\n",
+ ovly->ddev->chnl_id);
+
+ ovly->state = NULL;
+}
+EXPORT_SYMBOL(mcde_dss_disable_overlay);
+
+int mcde_dss_update_overlay(struct mcde_overlay *ovly, bool tripple_buffer)
+{
+ int ret;
+ dev_vdbg(&ovly->ddev->dev, "Overlay update, chnl=%d\n",
+ ovly->ddev->chnl_id);
+
+ if (!ovly->state || !ovly->ddev->update || !ovly->ddev->invalidate_area)
+ return -EINVAL;
+
+ /* Do not perform an update if power mode is off */
+ if (ovly->ddev->get_power_mode(ovly->ddev) == MCDE_DISPLAY_PM_OFF)
+ return 0;
+
+ ret = ovly->ddev->update(ovly->ddev, tripple_buffer);
+ if (ret)
+ return ret;
+
+ return ovly->ddev->invalidate_area(ovly->ddev, NULL);
+}
+EXPORT_SYMBOL(mcde_dss_update_overlay);
+
+void mcde_dss_get_native_resolution(struct mcde_display_device *ddev,
+ u16 *x_res, u16 *y_res)
+{
+ ddev->get_native_resolution(ddev, x_res, y_res);
+}
+EXPORT_SYMBOL(mcde_dss_get_native_resolution);
+
+enum mcde_ovly_pix_fmt mcde_dss_get_default_pixel_format(
+ struct mcde_display_device *ddev)
+{
+ return ddev->get_default_pixel_format(ddev);
+}
+EXPORT_SYMBOL(mcde_dss_get_default_pixel_format);
+
+void mcde_dss_get_physical_size(struct mcde_display_device *ddev,
+ u16 *physical_width, u16 *physical_height)
+{
+ ddev->get_physical_size(ddev, physical_width, physical_height);
+}
+EXPORT_SYMBOL(mcde_dss_get_physical_size);
+
+int mcde_dss_try_video_mode(struct mcde_display_device *ddev,
+ struct mcde_video_mode *video_mode)
+{
+ return ddev->try_video_mode(ddev, video_mode);
+}
+EXPORT_SYMBOL(mcde_dss_try_video_mode);
+
+int mcde_dss_set_video_mode(struct mcde_display_device *ddev,
+ struct mcde_video_mode *vmode)
+{
+ int ret;
+ struct mcde_video_mode old_vmode;
+
+ ddev->get_video_mode(ddev, &old_vmode);
+ if (memcmp(vmode, &old_vmode, sizeof(old_vmode)) == 0)
+ return 0;
+
+ ret = ddev->set_video_mode(ddev, vmode);
+ if (ret)
+ return ret;
+
+ return ddev->invalidate_area(ddev, NULL);
+}
+EXPORT_SYMBOL(mcde_dss_set_video_mode);
+
+void mcde_dss_get_video_mode(struct mcde_display_device *ddev,
+ struct mcde_video_mode *video_mode)
+{
+ ddev->get_video_mode(ddev, video_mode);
+}
+EXPORT_SYMBOL(mcde_dss_get_video_mode);
+
+int mcde_dss_set_pixel_format(struct mcde_display_device *ddev,
+ enum mcde_ovly_pix_fmt pix_fmt)
+{
+ enum mcde_ovly_pix_fmt old_pix_fmt;
+
+ old_pix_fmt = ddev->get_pixel_format(ddev);
+ if (old_pix_fmt == pix_fmt)
+ return 0;
+
+ return ddev->set_pixel_format(ddev, pix_fmt);
+}
+EXPORT_SYMBOL(mcde_dss_set_pixel_format);
+
+int mcde_dss_get_pixel_format(struct mcde_display_device *ddev)
+{
+ return ddev->get_pixel_format(ddev);
+}
+EXPORT_SYMBOL(mcde_dss_get_pixel_format);
+
+int mcde_dss_set_rotation(struct mcde_display_device *ddev,
+ enum mcde_display_rotation rotation)
+{
+ enum mcde_display_rotation old_rotation;
+
+ old_rotation = ddev->get_rotation(ddev);
+ if (old_rotation == rotation)
+ return 0;
+
+ return ddev->set_rotation(ddev, rotation);
+}
+EXPORT_SYMBOL(mcde_dss_set_rotation);
+
+enum mcde_display_rotation mcde_dss_get_rotation(
+ struct mcde_display_device *ddev)
+{
+ return ddev->get_rotation(ddev);
+}
+EXPORT_SYMBOL(mcde_dss_get_rotation);
+
+int mcde_dss_set_synchronized_update(struct mcde_display_device *ddev,
+ bool enable)
+{
+ int ret;
+ ret = ddev->set_synchronized_update(ddev, enable);
+ if (ret)
+ return ret;
+ if (ddev->chnl_state)
+ mcde_chnl_enable_synchronized_update(ddev->chnl_state, enable);
+ return 0;
+}
+EXPORT_SYMBOL(mcde_dss_set_synchronized_update);
+
+bool mcde_dss_get_synchronized_update(struct mcde_display_device *ddev)
+{
+ return ddev->get_synchronized_update(ddev);
+}
+EXPORT_SYMBOL(mcde_dss_get_synchronized_update);
+
+int __init mcde_dss_init(void)
+{
+ return 0;
+}
+
+void mcde_dss_exit(void)
+{
+}
+
diff --git a/drivers/video/mcde/mcde_fb.c b/drivers/video/mcde/mcde_fb.c
new file mode 100644
index 00000000000..29eeb5434b6
--- /dev/null
+++ b/drivers/video/mcde/mcde_fb.c
@@ -0,0 +1,806 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * ST-Ericsson MCDE frame buffer driver
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/hwmem.h>
+#include <linux/io.h>
+
+#include <linux/console.h>
+
+#include <video/mcde_fb.h>
+
+#define MCDE_FB_BPP_MAX 16
+#define MCDE_FB_VXRES_MAX 1920
+#define MCDE_FB_VYRES_MAX 2160
+
+static struct fb_ops fb_ops;
+
+struct pix_fmt_info {
+ enum mcde_ovly_pix_fmt pix_fmt;
+
+ u32 bpp;
+ struct fb_bitfield r;
+ struct fb_bitfield g;
+ struct fb_bitfield b;
+ struct fb_bitfield a;
+ u32 nonstd;
+};
+
+struct pix_fmt_info pix_fmt_map[] = {
+ {
+ .pix_fmt = MCDE_OVLYPIXFMT_RGB565,
+ .bpp = 16,
+ .r = { .offset = 11, .length = 5 },
+ .g = { .offset = 5, .length = 6 },
+ .b = { .offset = 0, .length = 5 },
+ }, {
+ .pix_fmt = MCDE_OVLYPIXFMT_RGBA5551,
+ .bpp = 16,
+ .r = { .offset = 11, .length = 5 },
+ .g = { .offset = 6, .length = 5 },
+ .b = { .offset = 1, .length = 5 },
+ .a = { .offset = 0, .length = 1 },
+ }, {
+ .pix_fmt = MCDE_OVLYPIXFMT_RGBA4444,
+ .bpp = 16,
+ .r = { .offset = 12, .length = 4 },
+ .g = { .offset = 8, .length = 4 },
+ .b = { .offset = 4, .length = 4 },
+ .a = { .offset = 0, .length = 4 },
+ }, {
+ .pix_fmt = MCDE_OVLYPIXFMT_YCbCr422,
+ .bpp = 16,
+ .nonstd = MCDE_OVLYPIXFMT_YCbCr422,
+ }, {
+ .pix_fmt = MCDE_OVLYPIXFMT_RGB888,
+ .bpp = 24,
+ .r = { .offset = 16, .length = 8 },
+ .g = { .offset = 8, .length = 8 },
+ .b = { .offset = 0, .length = 8 },
+ }, {
+ .pix_fmt = MCDE_OVLYPIXFMT_RGBA8888,
+ .bpp = 32,
+ .r = { .offset = 16, .length = 8 },
+ .g = { .offset = 8, .length = 8 },
+ .b = { .offset = 0, .length = 8 },
+ .a = { .offset = 24, .length = 8 },
+ }, {
+ .pix_fmt = MCDE_OVLYPIXFMT_RGBX8888,
+ .bpp = 32,
+ .r = { .offset = 16, .length = 8 },
+ .g = { .offset = 8, .length = 8 },
+ .b = { .offset = 0, .length = 8 },
+ }
+
+};
+
+static struct platform_device mcde_fb_device = {
+ .name = "mcde_fb",
+ .id = -1,
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void early_suspend(struct early_suspend *data)
+{
+ int i;
+ struct mcde_fb *mfb =
+ container_of(data, struct mcde_fb, early_suspend);
+
+ acquire_console_sem();
+ for (i = 0; i < mfb->num_ovlys; i++) {
+ if (mfb->ovlys[i] && mfb->ovlys[i]->ddev &&
+ (mfb->ovlys[i]->ddev->stay_alive == false))
+ mcde_dss_disable_display(mfb->ovlys[i]->ddev);
+ }
+ release_console_sem();
+}
+
+static void late_resume(struct early_suspend *data)
+{
+ int i;
+ struct mcde_fb *mfb =
+ container_of(data, struct mcde_fb, early_suspend);
+
+ acquire_console_sem();
+ for (i = 0; i < mfb->num_ovlys; i++) {
+ if (mfb->ovlys[i]) {
+ struct mcde_overlay *ovly = mfb->ovlys[i];
+ (void) mcde_dss_enable_display(ovly->ddev);
+ }
+ }
+ release_console_sem();
+}
+#endif
+
+/* Helpers */
+
+static struct pix_fmt_info *find_pix_fmt_info(enum mcde_ovly_pix_fmt pix_fmt)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pix_fmt_map); i++) {
+ if (pix_fmt_map[i].pix_fmt == pix_fmt)
+ return &pix_fmt_map[i];
+ }
+ return NULL;
+}
+
+static bool bitfield_cmp(struct fb_bitfield *bf1, struct fb_bitfield *bf2)
+{
+ return bf1->offset == bf2->offset &&
+ bf1->length == bf2->length &&
+ bf1->msb_right == bf2->msb_right;
+}
+
+static struct pix_fmt_info *var_to_pix_fmt_info(struct fb_var_screeninfo *var)
+{
+ int i;
+ struct pix_fmt_info *info;
+
+ if (var->nonstd)
+ return find_pix_fmt_info(var->nonstd);
+
+ for (i = 0; i < ARRAY_SIZE(pix_fmt_map); i++) {
+ info = &pix_fmt_map[i];
+ if (info->bpp == var->bits_per_pixel &&
+ bitfield_cmp(&info->r, &var->red) &&
+ bitfield_cmp(&info->g, &var->green) &&
+ bitfield_cmp(&info->b, &var->blue) &&
+ bitfield_cmp(&info->a, &var->transp))
+ return info;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pix_fmt_map); i++) {
+ info = &pix_fmt_map[i];
+ if (var->bits_per_pixel == info->bpp)
+ return info;
+ }
+
+ return NULL;
+}
+
+static void pix_fmt_info_to_var(struct pix_fmt_info *pix_fmt_info,
+ struct fb_var_screeninfo *var)
+{
+ var->bits_per_pixel = pix_fmt_info->bpp;
+ var->nonstd = pix_fmt_info->nonstd;
+ var->red = pix_fmt_info->r;
+ var->green = pix_fmt_info->g;
+ var->blue = pix_fmt_info->b;
+ var->transp = pix_fmt_info->a;
+}
+
+static int init_var_fmt(struct fb_var_screeninfo *var,
+ u16 w, u16 h, u16 vw, u16 vh, enum mcde_ovly_pix_fmt pix_fmt,
+ u32 rotate)
+{
+ struct pix_fmt_info *info;
+
+ info = find_pix_fmt_info(pix_fmt);
+ if (!info)
+ return -EINVAL;
+
+ var->bits_per_pixel = info->bpp;
+ var->nonstd = info->nonstd;
+ var->red = info->r;
+ var->green = info->g;
+ var->blue = info->b;
+ var->transp = info->a;
+ var->grayscale = false;
+
+ var->xres = w;
+ var->yres = h;
+ var->xres_virtual = vw;
+ var->yres_virtual = vh;
+ var->xoffset = 0;
+ var->yoffset = 0;
+ var->activate = FB_ACTIVATE_NOW;
+ var->rotate = rotate;
+
+ return 0;
+};
+
+static int reallocate_fb_mem(struct fb_info *fbi, u32 size)
+{
+ struct mcde_fb *mfb = to_mcde_fb(fbi);
+ void *vaddr;
+ struct hwmem_alloc *alloc;
+ struct hwmem_mem_chunk mem_chunk;
+ size_t num_mem_chunks = 1;
+ int name;
+
+ size = PAGE_ALIGN(size);
+
+ if (size == fbi->screen_size)
+ return 0;
+
+/* TODO: Remove once hwmem has support for defragmentation */
+#ifdef CONFIG_MCDE_FB_AVOID_REALLOC
+ if (!mfb->alloc) {
+ u32 old_size = size;
+
+ size = MCDE_FB_BPP_MAX / 8 * MCDE_FB_VXRES_MAX *
+ MCDE_FB_VYRES_MAX;
+#endif
+
+ alloc = hwmem_alloc(size, HWMEM_ALLOC_HINT_WRITE_COMBINE |
+ HWMEM_ALLOC_HINT_UNCACHED,
+ (HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE |
+ HWMEM_ACCESS_IMPORT),
+ HWMEM_MEM_CONTIGUOUS_SYS);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
+
+ name = hwmem_get_name(alloc);
+ if (name < 0) {
+ hwmem_release(alloc);
+ return name;
+ }
+
+ if (mfb->alloc) {
+ hwmem_kunmap(mfb->alloc);
+ hwmem_unpin(mfb->alloc);
+ hwmem_release(mfb->alloc);
+ }
+
+ (void)hwmem_pin(alloc, &mem_chunk, &num_mem_chunks);
+
+ vaddr = hwmem_kmap(alloc);
+ if (vaddr == NULL) {
+ hwmem_unpin(alloc);
+ hwmem_release(alloc);
+ return -ENOMEM;
+ }
+
+ mfb->alloc = alloc;
+ mfb->alloc_name = name;
+
+ fbi->screen_base = vaddr;
+ fbi->fix.smem_start = mem_chunk.paddr;
+
+#ifdef CONFIG_MCDE_FB_AVOID_REALLOC
+ size = old_size;
+ }
+#endif
+
+ fbi->screen_size = size;
+ fbi->fix.smem_len = size;
+
+ return 0;
+}
+
+static void free_fb_mem(struct fb_info *fbi)
+{
+ struct mcde_fb *mfb = to_mcde_fb(fbi);
+
+ if (mfb->alloc) {
+ hwmem_kunmap(mfb->alloc);
+ hwmem_unpin(mfb->alloc);
+ hwmem_release(mfb->alloc);
+ mfb->alloc = NULL;
+ mfb->alloc_name = 0;
+
+ fbi->fix.smem_start = 0;
+ fbi->fix.smem_len = 0;
+ fbi->screen_base = 0;
+ fbi->screen_size = 0;
+ }
+}
+
+static void init_fb(struct fb_info *fbi)
+{
+ struct mcde_fb *mfb = to_mcde_fb(fbi);
+
+ strlcpy(fbi->fix.id, "mcde_fb", sizeof(fbi->fix.id));
+ fbi->fix.type = FB_TYPE_PACKED_PIXELS;
+ fbi->fix.visual = FB_VISUAL_TRUECOLOR;
+ fbi->fix.xpanstep = 1;
+ fbi->fix.ypanstep = 1;
+ fbi->flags = FBINFO_HWACCEL_DISABLED;
+ fbi->fbops = &fb_ops;
+ fbi->pseudo_palette = &mfb->pseudo_palette[0];
+}
+
+static void get_ovly_info(struct fb_info *fbi, struct mcde_overlay *ovly,
+ struct mcde_overlay_info *info)
+{
+ struct mcde_fb *mfb = to_mcde_fb(fbi);
+
+ memset(info, 0, sizeof(*info));
+ info->paddr = fbi->fix.smem_start +
+ fbi->fix.line_length * fbi->var.yoffset;
+ /* TODO: move mem check to check_var/pan_display */
+ if (info->paddr + fbi->fix.line_length * fbi->var.yres >
+ fbi->fix.smem_start + fbi->fix.smem_len)
+ info->paddr = fbi->fix.smem_start;
+ info->fmt = mfb->pix_fmt;
+ info->stride = fbi->fix.line_length;
+ if (ovly) {
+ info->src_x = ovly->info.src_x;
+ info->src_y = ovly->info.src_y;
+ info->dst_x = ovly->info.dst_x;
+ info->dst_y = ovly->info.dst_y;
+ info->dst_z = 1;
+ } else {
+ info->src_x = 0;
+ info->src_y = 0;
+ info->dst_x = 0;
+ info->dst_y = 0;
+ info->dst_z = 1;
+ }
+ info->w = fbi->var.xres;
+ info->h = fbi->var.yres;
+ info->dirty.x = 0;
+ info->dirty.y = 0;
+ info->dirty.w = fbi->var.xres;
+ info->dirty.h = fbi->var.yres;
+}
+
+void vmode_to_var(struct mcde_video_mode *video_mode,
+ struct fb_var_screeninfo *var)
+{
+ /* TODO: use only 1 vbp and 1 vfp */
+ var->xres = video_mode->xres;
+ var->yres = video_mode->yres;
+ var->pixclock = video_mode->pixclock;
+ var->upper_margin = video_mode->vbp;
+ var->lower_margin = video_mode->vfp;
+ var->vsync_len = video_mode->vsw;
+ var->left_margin = video_mode->hbp;
+ var->right_margin = video_mode->hfp;
+ var->hsync_len = video_mode->hsw;
+ var->vmode &= ~FB_VMODE_INTERLACED;
+ var->vmode |= video_mode->interlaced ?
+ FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
+}
+
+void var_to_vmode(struct fb_var_screeninfo *var,
+ struct mcde_video_mode *video_mode)
+{
+ video_mode->xres = var->xres;
+ video_mode->yres = var->yres;
+ video_mode->pixclock = var->pixclock;
+ video_mode->vbp = var->upper_margin;
+ video_mode->vfp = var->lower_margin;
+ video_mode->vsw = var->vsync_len;
+ video_mode->hbp = var->left_margin;
+ video_mode->hfp = var->right_margin;
+ video_mode->hsw = var->hsync_len;
+ video_mode->interlaced = (var->vmode & FB_VMODE_INTERLACED) ==
+ FB_VMODE_INTERLACED;
+}
+
+enum mcde_display_rotation var_to_rotation(struct fb_var_screeninfo *var)
+{
+ enum mcde_display_rotation rot;
+
+ switch (var->rotate) {
+ case FB_ROTATE_UR:
+ rot = MCDE_DISPLAY_ROT_0;
+ break;
+ case FB_ROTATE_CW:
+ rot = MCDE_DISPLAY_ROT_90_CW;
+ break;
+ case FB_ROTATE_UD:
+ rot = MCDE_DISPLAY_ROT_180_CW;
+ break;
+ case FB_ROTATE_CCW:
+ rot = MCDE_DISPLAY_ROT_90_CCW;
+ break;
+ default:
+ rot = MCDE_DISPLAY_ROT_0;
+ break;
+ }
+ dev_vdbg(&mcde_fb_device.dev, "var_rot: %d -> mcde_rot: %d\n",
+ var->rotate, rot);
+ return rot;
+}
+
+static struct mcde_display_device *fb_to_display(struct fb_info *fbi)
+{
+ int i;
+ struct mcde_fb *mfb = to_mcde_fb(fbi);
+
+ for (i = 0; i < mfb->num_ovlys; i++) {
+ if (mfb->ovlys[i])
+ return mfb->ovlys[i]->ddev;
+ }
+ return NULL;
+}
+
+static int check_var(struct fb_var_screeninfo *var, struct fb_info *fbi,
+ struct mcde_display_device *ddev)
+{
+ int ret;
+ u16 w = -1, h = -1;
+ struct mcde_video_mode vmode;
+ struct pix_fmt_info *fmtinfo;
+
+ /* TODO: check sizes/offsets/memory validity */
+
+ /* Device physical size */
+ mcde_dss_get_physical_size(ddev, &w, &h);
+ var->width = w;
+ var->height = h;
+
+ /* Rotation */
+ if (var->rotate > 3) {
+ dev_info(&(ddev->dev), "check_var failed var->rotate\n");
+ return -EINVAL;
+ }
+
+ /* Video mode */
+ var_to_vmode(var, &vmode);
+ ret = mcde_dss_try_video_mode(ddev, &vmode);
+ if (ret < 0) {
+ dev_vdbg(&(ddev->dev), "check_var failed "
+ "mcde_dss_try_video_mode with size = %x\n", ret);
+ return ret;
+ }
+ vmode_to_var(&vmode, var);
+
+ /* Pixel format */
+ fmtinfo = var_to_pix_fmt_info(var);
+ if (!fmtinfo) {
+ dev_vdbg(&(ddev->dev), "check_var failed fmtinfo\n");
+ return -EINVAL;
+ }
+ pix_fmt_info_to_var(fmtinfo, var);
+
+ /* Not used */
+ var->grayscale = 0;
+ var->sync = 0;
+
+ return 0;
+}
+
+static int apply_var(struct fb_info *fbi, struct mcde_display_device *ddev)
+{
+ int ret, i;
+ struct mcde_fb *mfb = to_mcde_fb(fbi);
+ struct fb_var_screeninfo *var;
+ struct mcde_video_mode vmode;
+ struct pix_fmt_info *fmt;
+ u32 line_len, size;
+
+ dev_vdbg(&(ddev->dev), "%s\n", __func__);
+
+ var = &fbi->var;
+
+ /* Reallocate memory */
+ line_len = (fbi->var.bits_per_pixel * var->xres_virtual) / 8;
+ line_len = ALIGN(line_len, MCDE_BUF_LINE_ALIGMENT);
+ size = line_len * var->yres_virtual;
+ ret = reallocate_fb_mem(fbi, size);
+ if (ret) {
+ dev_vdbg(&(ddev->dev), "apply_var failed with"
+ "reallocate mem with size = %d\n", size);
+ return ret;
+ }
+ fbi->fix.line_length = line_len;
+
+ if (ddev) {
+ /* Apply pixel format */
+ fmt = var_to_pix_fmt_info(var);
+ mfb->pix_fmt = fmt->pix_fmt;
+
+ /* Apply rotation */
+ mcde_dss_set_rotation(ddev, var_to_rotation(var));
+ /* Apply video mode */
+ memset(&vmode, 0, sizeof(struct mcde_video_mode));
+ var_to_vmode(var, &vmode);
+ ret = mcde_dss_set_video_mode(ddev, &vmode);
+ if (ret)
+ return ret;
+
+ mcde_dss_apply_channel(ddev);
+ }
+
+ /* Apply overlay info */
+ for (i = 0; i < mfb->num_ovlys; i++) {
+ struct mcde_overlay *ovly = mfb->ovlys[i];
+ struct mcde_overlay_info info;
+ int num_buffers;
+
+ get_ovly_info(fbi, ovly, &info);
+ (void) mcde_dss_apply_overlay(ovly, &info);
+
+ num_buffers = var->yres_virtual / var->yres;
+ mcde_dss_update_overlay(ovly, num_buffers == 3);
+ }
+
+ return 0;
+}
+
+/* FB ops */
+
+static int mcde_fb_open(struct fb_info *fbi, int user)
+{
+ dev_vdbg(fbi->dev, "%s\n", __func__);
+ return 0;
+}
+
+static int mcde_fb_release(struct fb_info *fbi, int user)
+{
+ dev_vdbg(fbi->dev, "%s\n", __func__);
+ return 0;
+}
+
+static int mcde_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
+{
+ struct mcde_display_device *ddev = fb_to_display(fbi);
+
+ dev_vdbg(fbi->dev, "%s\n", __func__);
+
+ if (!ddev) {
+ printk(KERN_ERR "mcde_fb_check_var failed !ddev\n");
+ return -ENODEV;
+ }
+
+ return check_var(var, fbi, ddev);
+}
+
+static int mcde_fb_set_par(struct fb_info *fbi)
+{
+ dev_vdbg(fbi->dev, "%s\n", __func__);
+
+ return apply_var(fbi, fb_to_display(fbi));
+}
+
+static int mcde_fb_blank(int blank, struct fb_info *fbi)
+{
+ int ret = 0;
+ struct mcde_display_device *ddev = fb_to_display(fbi);
+
+ dev_vdbg(fbi->dev, "%s\n", __func__);
+
+ switch (blank) {
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_POWERDOWN:
+ mcde_dss_disable_display(ddev);
+ break;
+ case FB_BLANK_UNBLANK:
+ ret = mcde_dss_enable_display(ddev);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int mcde_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fbi)
+{
+ dev_vdbg(fbi->dev, "%s\n", __func__);
+
+ if (var->xoffset == fbi->var.xoffset &&
+ var->yoffset == fbi->var.yoffset)
+ return 0;
+
+ fbi->var.xoffset = var->xoffset;
+ fbi->var.yoffset = var->yoffset;
+ return apply_var(fbi, fb_to_display(fbi));
+}
+
+static void mcde_fb_rotate(struct fb_info *fbi, int rotate)
+{
+ dev_vdbg(fbi->dev, "%s\n", __func__);
+}
+
+static int mcde_fb_ioctl(struct fb_info *fbi, unsigned int cmd,
+ unsigned long arg)
+{
+ struct mcde_fb *mfb = to_mcde_fb(fbi);
+
+ if (cmd == MCDE_GET_BUFFER_NAME_IOC)
+ return mfb->alloc_name;
+
+ return -EINVAL;
+}
+
+static struct fb_ops fb_ops = {
+ /* creg, cmap */
+ .owner = THIS_MODULE,
+ .fb_open = mcde_fb_open,
+ .fb_release = mcde_fb_release,
+ .fb_read = fb_sys_read,
+ .fb_write = fb_sys_write,
+ .fb_fillrect = sys_fillrect,
+ .fb_copyarea = sys_copyarea,
+ .fb_imageblit = sys_imageblit,
+ .fb_check_var = mcde_fb_check_var,
+ .fb_set_par = mcde_fb_set_par,
+ .fb_blank = mcde_fb_blank,
+ .fb_pan_display = mcde_fb_pan_display,
+ .fb_rotate = mcde_fb_rotate,
+ .fb_ioctl = mcde_fb_ioctl,
+};
+
+/* FB driver */
+
+struct fb_info *mcde_fb_create(struct mcde_display_device *ddev,
+ u16 w, u16 h, u16 vw, u16 vh, enum mcde_ovly_pix_fmt pix_fmt,
+ u32 rotate)
+{
+ int ret = 0;
+ struct fb_info *fbi;
+ struct mcde_fb *mfb;
+ struct mcde_overlay *ovly = NULL;
+ struct mcde_overlay_info ovly_info;
+
+ dev_vdbg(&ddev->dev, "%s\n", __func__);
+ if (!ddev->initialized) {
+ dev_warn(&ddev->dev, "%s: Device not initialized\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Init fb */
+ fbi = framebuffer_alloc(sizeof(struct mcde_fb), &mcde_fb_device.dev);
+ if (fbi == NULL) {
+ ret = -ENOMEM;
+ goto fb_alloc_failed;
+ }
+ init_fb(fbi);
+ mfb = to_mcde_fb(fbi);
+
+ ret = mcde_dss_open_channel(ddev);
+ if (ret)
+ goto channel_open_failed;
+
+ ret = mcde_dss_enable_display(ddev);
+ if (ret)
+ goto display_enable_failed;
+
+ /* Prepare var and allocate frame buffer memory */
+ init_var_fmt(&fbi->var, w, h, vw, vh, pix_fmt, rotate);
+ check_var(&fbi->var, fbi, ddev);
+ ret = apply_var(fbi, ddev);
+ if (ret)
+ goto apply_var_failed;
+
+ mcde_dss_set_pixel_format(ddev, ddev->port->pixel_format);
+
+ /* Setup overlay */
+ get_ovly_info(fbi, NULL, &ovly_info);
+ ovly = mcde_dss_create_overlay(ddev, &ovly_info);
+ if (!ovly) {
+ ret = PTR_ERR(ovly);
+ goto ovly_alloc_failed;
+ }
+ mfb->ovlys[0] = ovly;
+ mfb->num_ovlys = 1;
+
+ ret = mcde_dss_enable_overlay(ovly);
+ if (ret)
+ goto ovly_enable_failed;
+
+ mfb->id = ddev->id;
+
+ /* Register framebuffer */
+ ret = register_framebuffer(fbi);
+ if (ret)
+ goto fb_register_failed;
+
+ ddev->fbi = fbi;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ mfb->early_suspend.level =
+ EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ mfb->early_suspend.suspend = early_suspend;
+ mfb->early_suspend.resume = late_resume;
+ register_early_suspend(&mfb->early_suspend);
+#endif
+
+ goto out;
+fb_register_failed:
+ mcde_dss_disable_overlay(ovly);
+ovly_enable_failed:
+ mcde_dss_destroy_overlay(ovly);
+ovly_alloc_failed:
+ free_fb_mem(fbi);
+apply_var_failed:
+ mcde_dss_disable_display(ddev);
+display_enable_failed:
+ mcde_dss_close_channel(ddev);
+channel_open_failed:
+ framebuffer_release(fbi);
+ fbi = NULL;
+fb_alloc_failed:
+out:
+ return ret ? ERR_PTR(ret) : fbi;
+}
+EXPORT_SYMBOL(mcde_fb_create);
+
+int mcde_fb_attach_overlay(struct fb_info *fb_info, struct mcde_overlay *ovl)
+{
+ /* TODO: Attach extra overlay targets */
+ return -EINVAL;
+}
+
+void mcde_fb_destroy(struct mcde_display_device *dev)
+{
+ struct mcde_fb *mfb;
+ int i;
+
+ dev_vdbg(&dev->dev, "%s\n", __func__);
+
+ mcde_dss_disable_display(dev);
+ mcde_dss_close_channel(dev);
+
+ mfb = to_mcde_fb(dev->fbi);
+ for (i = 0; i < mfb->num_ovlys; i++) {
+ if (mfb->ovlys[i])
+ mcde_dss_destroy_overlay(mfb->ovlys[i]);
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&mfb->early_suspend);
+#endif
+ unregister_framebuffer(dev->fbi);
+ free_fb_mem(dev->fbi);
+ framebuffer_release(dev->fbi);
+ dev->fbi = NULL;
+}
+
+/* Overlay fbs' platform device */
+static int mcde_fb_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int mcde_fb_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver mcde_fb_driver = {
+ .probe = mcde_fb_probe,
+ .remove = mcde_fb_remove,
+ .driver = {
+ .name = "mcde_fb",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* MCDE fb init */
+
+int __init mcde_fb_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&mcde_fb_driver);
+ if (ret)
+ goto fb_driver_failed;
+ ret = platform_device_register(&mcde_fb_device);
+ if (ret)
+ goto fb_device_failed;
+
+ goto out;
+fb_device_failed:
+ platform_driver_unregister(&mcde_fb_driver);
+fb_driver_failed:
+out:
+ return ret;
+}
+
+void mcde_fb_exit(void)
+{
+ platform_device_unregister(&mcde_fb_device);
+ platform_driver_unregister(&mcde_fb_driver);
+}
diff --git a/drivers/video/mcde/mcde_hw.c b/drivers/video/mcde/mcde_hw.c
new file mode 100644
index 00000000000..cdfce306430
--- /dev/null
+++ b/drivers/video/mcde/mcde_hw.c
@@ -0,0 +1,3389 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson MCDE base driver
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+
+#include <video/mcde.h>
+#include "dsilink_regs.h"
+#include "mcde_regs.h"
+
+static void disable_channel(struct mcde_chnl_state *chnl);
+static void watchdog_auto_sync_timer_function(unsigned long arg);
+static int _mcde_chnl_apply(struct mcde_chnl_state *chnl);
+static void disable_flow(struct mcde_chnl_state *chnl);
+static void enable_channel(struct mcde_chnl_state *chnl);
+static void do_softwaretrig(struct mcde_chnl_state *chnl);
+static int is_channel_enabled(struct mcde_chnl_state *chnl);
+static void dsi_te_poll_req(struct mcde_chnl_state *chnl);
+static void dsi_te_poll_set_timer(struct mcde_chnl_state *chnl,
+ unsigned int timeout);
+static void dsi_te_timer_function(unsigned long value);
+
+#define OVLY_TIMEOUT 100
+#define CHNL_TIMEOUT 100
+#define SCREEN_PPL_HIGH 1920
+#define SCREEN_PPL_CEA2 720
+#define SCREEN_LPF_CEA2 480
+#define DSI_DELAY0_CEA2_ADD 10
+
+#define MCDE_SLEEP_WATCHDOG 500
+#define DSI_TE_NO_ANSWER_TIMEOUT_INIT 2500
+#define DSI_TE_NO_ANSWER_TIMEOUT 250
+
+static u8 *mcdeio;
+static u8 **dsiio;
+static struct platform_device *mcde_dev;
+static u8 num_dsilinks;
+static u8 num_channels;
+static u8 num_overlays;
+static u8 hardware_version;
+static int mcde_irq;
+
+#ifdef CONFIG_REGULATOR
+static struct regulator *regulator_vana;
+static struct regulator *regulator_mcde_epod;
+static struct regulator *regulator_esram_epod;
+#endif
+static struct clk *clock_dpi;
+static struct clk *clock_dsi;
+static struct clk *clock_mcde;
+static struct clk *clock_dsi_lp;
+static u8 mcde_is_enabled;
+static struct mutex mcde_hw_lock;
+static struct delayed_work hw_timeout_work;
+static u8 enable_dsi;
+static u8 dsi_is_enabled;
+
+static u8 mcde_dynamic_power_management = true;
+
+static inline u32 dsi_rreg(int i, u32 reg)
+{
+ return readl(dsiio[i] + reg);
+}
+static inline void dsi_wreg(int i, u32 reg, u32 val)
+{
+ writel(val, dsiio[i] + reg);
+}
+#define dsi_rfld(__i, __reg, __fld) \
+ ((dsi_rreg(__i, __reg) & __reg##_##__fld##_MASK) >> \
+ __reg##_##__fld##_SHIFT)
+#define dsi_wfld(__i, __reg, __fld, __val) \
+ dsi_wreg(__i, __reg, (dsi_rreg(__i, __reg) & \
+ ~__reg##_##__fld##_MASK) | (((__val) << __reg##_##__fld##_SHIFT) & \
+ __reg##_##__fld##_MASK))
+
+static inline u32 mcde_rreg(u32 reg)
+{
+ return readl(mcdeio + reg);
+}
+static inline void mcde_wreg(u32 reg, u32 val)
+{
+ writel(val, mcdeio + reg);
+}
+#define mcde_wreg_fld(__reg, __fld_mask, __fld_shift, __val) \
+ mcde_wreg(__reg, (mcde_rreg(__reg) & ~(__fld_mask)) |\
+ (((__val) << (__fld_shift)) & (__fld_mask)))
+
+#define mcde_rfld(__reg, __fld) \
+ ((mcde_rreg(__reg) & __reg##_##__fld##_MASK) >> \
+ __reg##_##__fld##_SHIFT)
+#define mcde_wfld(__reg, __fld, __val) \
+ mcde_wreg_fld(__reg, __reg##_##__fld##_MASK,\
+ __reg##_##__fld##_SHIFT, __val)
+
+struct ovly_regs {
+ u8 ch_id;
+ bool enabled;
+ u32 baseaddress0;
+ u32 baseaddress1;
+ bool update;
+ u8 bits_per_pixel;
+ u8 bpp;
+ bool bgr;
+ bool bebo;
+ bool opq;
+ u8 col_conv;
+ u8 alpha_source;
+ u8 alpha_value;
+ u8 pixoff;
+ u16 ppl;
+ u16 lpf;
+ u16 cropx;
+ u16 cropy;
+ u16 xpos;
+ u16 ypos;
+ u8 z;
+};
+
+struct mcde_ovly_state {
+ bool inuse;
+ bool update;
+ u8 idx; /* MCDE overlay index */
+ struct mcde_chnl_state *chnl; /* Owner channel */
+
+ /* Staged settings */
+ u32 paddr;
+ u16 stride;
+ enum mcde_ovly_pix_fmt pix_fmt;
+
+ u16 src_x;
+ u16 src_y;
+ u16 dst_x;
+ u16 dst_y;
+ u16 dst_z;
+ u16 w;
+ u16 h;
+
+ u8 alpha_source;
+ u8 alpha_value;
+
+ /* Applied settings */
+ struct ovly_regs regs;
+};
+
+static struct mcde_ovly_state *overlays;
+
+struct chnl_regs {
+ bool floen;
+ u16 x;
+ u16 y;
+ u16 ppl;
+ u16 lpf;
+ u8 bpp;
+ bool internal_clk; /* CLKTYPE field */
+ u16 pcd;
+ u8 clksel;
+ u8 cdwin;
+ u16 (*map_r)(u8);
+ u16 (*map_g)(u8);
+ u16 (*map_b)(u8);
+ bool palette_enable;
+ bool bcd;
+ bool synchronized_update;
+ bool roten;
+ u8 rotdir;
+ u32 rotbuf1; /* TODO: Replace with eSRAM alloc */
+ u32 rotbuf2; /* TODO: Replace with eSRAM alloc */
+
+ /* Blending */
+ u8 blend_ctrl;
+ bool blend_en;
+ u8 alpha_blend;
+
+ /* DSI */
+ u8 dsipacking;
+};
+
+struct col_regs {
+ u16 y_red;
+ u16 y_green;
+ u16 y_blue;
+ u16 cb_red;
+ u16 cb_green;
+ u16 cb_blue;
+ u16 cr_red;
+ u16 cr_green;
+ u16 cr_blue;
+ u16 off_y;
+ u16 off_cb;
+ u16 off_cr;
+};
+
+struct tv_regs {
+ u16 dho; /* TV mode: left border width; destination horizontal offset */
+ /* LCD MODE: horizontal back porch */
+ u16 alw; /* TV mode: right border width */
+ /* LCD mode: horizontal front porch */
+ u16 hsw; /* horizontal synch width */
+ u16 dvo; /* TV mode: top border width; destination horizontal offset */
+ /* LCD MODE: vertical back porch */
+ u16 bsl; /* TV mode: bottom border width; blanking start line */
+ /* LCD MODE: vertical front porch */
+ /* field 1 */
+ u16 bel1; /* TV mode: field total vertical blanking lines */
+ /* LCD mode: vertical sync width */
+ u16 fsl1; /* field vbp */
+ /* field 2 */
+ u16 bel2;
+ u16 fsl2;
+ u8 tv_mode;
+ bool sel_mode_tv;
+ bool inv_clk;
+ bool interlaced_en;
+ u32 lcdtim1;
+};
+
+struct mcde_chnl_state {
+ bool enabled;
+ bool reserved;
+ enum mcde_chnl id;
+ enum mcde_fifo fifo;
+ struct mcde_port port;
+ struct mcde_ovly_state *ovly0;
+ struct mcde_ovly_state *ovly1;
+ const struct chnl_config *cfg;
+ u32 transactionid;
+ u32 transactionid_regs;
+ u32 transactionid_hw;
+ wait_queue_head_t waitq_hw; /* Waitq for transactionid_hw */
+ /* Used as watchdog timer for auto sync feature */
+ struct timer_list auto_sync_timer;
+ struct timer_list dsi_te_timer;
+
+ enum mcde_display_power_mode power_mode;
+
+ /* Staged settings */
+ u16 (*map_r)(u8);
+ u16 (*map_g)(u8);
+ u16 (*map_b)(u8);
+ bool palette_enable;
+ bool synchronized_update;
+ struct mcde_video_mode vmode;
+ enum mcde_display_rotation rotation;
+ u32 rotbuf1;
+ u32 rotbuf2;
+
+ /* Blending */
+ u8 blend_ctrl;
+ bool blend_en;
+ u8 alpha_blend;
+
+ /* Applied settings */
+ struct chnl_regs regs;
+ struct col_regs col_regs;
+ struct tv_regs tv_regs;
+
+ /* an interlaced digital TV signal generates a VCMP per field */
+ bool vcmp_per_field;
+ bool even_vcmp;
+
+ bool continous_running;
+ bool disable_software_trig;
+ bool formatter_updated;
+ bool esram_is_enabled;
+};
+
+static struct mcde_chnl_state *channels;
+
+struct chnl_config {
+ /* Key */
+ enum mcde_chnl_path path;
+
+ /* Value */
+ bool swap_a_c0;
+ bool swap_a_c0_set;
+ bool swap_b_c1;
+ bool swap_b_c1_set;
+ bool fabmux;
+ bool fabmux_set;
+ bool f01mux;
+ bool f01mux_set;
+};
+
+static /* TODO: const, compiler bug? */ struct chnl_config chnl_configs[] = {
+ /* Channel A */
+ { .path = MCDE_CHNLPATH_CHNLA_FIFOA_DPI_0,
+ .swap_a_c0 = false, .swap_a_c0_set = true },
+ { .path = MCDE_CHNLPATH_CHNLA_FIFOA_DSI_IFC0_0,
+ .swap_a_c0 = false, .swap_a_c0_set = true,
+ .fabmux = false, .fabmux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLA_FIFOA_DSI_IFC0_1,
+ .swap_a_c0 = false, .swap_a_c0_set = true,
+ .fabmux = true, .fabmux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLA_FIFOC0_DSI_IFC0_2,
+ .swap_a_c0 = true, .swap_a_c0_set = true,
+ .f01mux = false, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLA_FIFOC0_DSI_IFC1_0,
+ .swap_a_c0 = true, .swap_a_c0_set = true,
+ .f01mux = false, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLA_FIFOC0_DSI_IFC1_1,
+ .swap_a_c0 = true, .swap_a_c0_set = true,
+ .f01mux = true, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLA_FIFOA_DSI_IFC1_2,
+ .swap_a_c0 = false, .swap_a_c0_set = true,
+ .fabmux = false, .fabmux_set = true },
+ /* Channel B */
+ { .path = MCDE_CHNLPATH_CHNLB_FIFOB_DPI_1,
+ .swap_b_c1 = false, .swap_b_c1_set = true },
+ { .path = MCDE_CHNLPATH_CHNLB_FIFOB_DSI_IFC0_0,
+ .swap_b_c1 = false, .swap_b_c1_set = true,
+ .fabmux = true, .fabmux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLB_FIFOB_DSI_IFC0_1,
+ .swap_b_c1 = false, .swap_b_c1_set = true,
+ .fabmux = false, .fabmux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLB_FIFOC1_DSI_IFC0_2,
+ .swap_b_c1 = true, .swap_b_c1_set = true,
+ .f01mux = true, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLB_FIFOC1_DSI_IFC1_0,
+ .swap_b_c1 = true, .swap_b_c1_set = true,
+ .f01mux = true, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLB_FIFOC1_DSI_IFC1_1,
+ .swap_b_c1 = true, .swap_b_c1_set = true,
+ .f01mux = false, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLB_FIFOB_DSI_IFC1_2,
+ .swap_b_c1 = false, .swap_b_c1_set = true,
+ .fabmux = true, .fabmux_set = true },
+ /* Channel C0 */
+ { .path = MCDE_CHNLPATH_CHNLC0_FIFOA_DSI_IFC0_0,
+ .swap_a_c0 = true, .swap_a_c0_set = true,
+ .fabmux = false, .fabmux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLC0_FIFOA_DSI_IFC0_1,
+ .swap_a_c0 = true, .swap_a_c0_set = true,
+ .fabmux = true, .fabmux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLC0_FIFOC0_DSI_IFC0_2,
+ .swap_a_c0 = false, .swap_a_c0_set = true,
+ .f01mux = false, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLC0_FIFOC0_DSI_IFC1_0,
+ .swap_a_c0 = false, .swap_a_c0_set = true,
+ .f01mux = false, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLC0_FIFOC0_DSI_IFC1_1,
+ .swap_a_c0 = false, .swap_a_c0_set = true,
+ .f01mux = true, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLC0_FIFOA_DSI_IFC1_2,
+ .swap_a_c0 = true, .swap_a_c0_set = true,
+ .fabmux = false, .fabmux_set = true },
+ /* Channel C1 */
+ { .path = MCDE_CHNLPATH_CHNLC1_FIFOB_DSI_IFC0_0,
+ .swap_b_c1 = true, .swap_b_c1_set = true,
+ .fabmux = true, .fabmux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLC1_FIFOB_DSI_IFC0_1,
+ .swap_b_c1 = true, .swap_b_c1_set = true,
+ .fabmux = false, .fabmux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLC1_FIFOC1_DSI_IFC0_2,
+ .swap_b_c1 = false, .swap_b_c1_set = true,
+ .f01mux = true, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLC1_FIFOC1_DSI_IFC1_0,
+ .swap_b_c1 = false, .swap_b_c1_set = true,
+ .f01mux = true, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLC1_FIFOC1_DSI_IFC1_1,
+ .swap_b_c1 = false, .swap_b_c1_set = true,
+ .f01mux = false, .f01mux_set = true },
+ { .path = MCDE_CHNLPATH_CHNLC1_FIFOB_DSI_IFC1_2,
+ .swap_b_c1 = true, .swap_b_c1_set = true,
+ .fabmux = true, .fabmux_set = true },
+};
+
+static int enable_clocks_and_power(struct platform_device *pdev)
+{
+ struct mcde_platform_data *pdata = pdev->dev.platform_data;
+ int ret = 0;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+#ifdef CONFIG_REGULATOR
+ if (regulator_mcde_epod) {
+ ret = regulator_enable(regulator_mcde_epod);
+ if (ret < 0) {
+ dev_warn(&pdev->dev, "%s: regulator_enable failed\n",
+ __func__);
+ return ret;
+ }
+ } else {
+ dev_warn(&pdev->dev, "%s: mcde_epod regulator is null\n"
+ , __func__);
+ return -EINVAL;
+ }
+#endif
+ pdata->platform_set_clocks();
+ if (enable_dsi > 0) {
+ pdata->platform_enable_dsipll();
+ dsi_is_enabled = true;
+ }
+
+ ret = clk_enable(clock_mcde);
+ if (ret < 0) {
+ dev_warn(&pdev->dev, "%s: "
+ "clk_enable mcde failed ret = %d\n", __func__, ret);
+ goto clk_mcde_err;
+ }
+
+ return ret;
+
+clk_mcde_err:
+#ifdef CONFIG_REGULATOR
+ if (regulator_mcde_epod)
+ regulator_disable(regulator_mcde_epod);
+#endif
+ return ret;
+}
+
+static int disable_clocks_and_power(struct platform_device *pdev)
+{
+ struct mcde_platform_data *pdata = pdev->dev.platform_data;
+ int ret = 0;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ clk_disable(clock_mcde);
+ if (enable_dsi > 0) {
+ pdata->platform_disable_dsipll();
+ dsi_is_enabled = false;
+ }
+
+#ifdef CONFIG_REGULATOR
+ if (regulator_mcde_epod) {
+ ret = regulator_disable(regulator_mcde_epod);
+ if (ret < 0) {
+ dev_warn(&pdev->dev, "%s: regulator_disable failed\n"
+ , __func__);
+ goto regulator_mcde_epod_err;
+ }
+ } else {
+ dev_warn(&pdev->dev, "%s: mcde_epod regulator is null\n"
+ , __func__);
+ goto regulator_mcde_epod_err;
+ }
+ return ret;
+
+regulator_mcde_epod_err:
+ clk_enable(clock_mcde);
+#endif
+ return ret;
+}
+
+static void update_mcde_registers(void)
+{
+ struct mcde_platform_data *pdata = mcde_dev->dev.platform_data;
+
+ if (hardware_version == MCDE_CHIP_VERSION_1_0_4) {
+ /* Setup output muxing */
+ mcde_wreg(MCDE_CONF0,
+ MCDE_CONF0_IFIFOCTRLWTRMRKLVL(7));
+
+ mcde_wfld(MCDE_RISOVL, OVLFDRIS, 1);
+ mcde_wfld(MCDE_RISPP, VCMPARIS, 1);
+ mcde_wfld(MCDE_RISPP, VCMPBRIS, 1);
+
+ /* Enable channel VCMP interrupts */
+ mcde_wreg(MCDE_IMSCPP,
+ MCDE_IMSCPP_VCMPAIM(true) |
+ MCDE_IMSCPP_VCMPBIM(true));
+ } else {
+ /* Setup output muxing */
+ mcde_wreg(MCDE_CONF0,
+ MCDE_CONF0_IFIFOCTRLWTRMRKLVL(7) |
+ MCDE_CONF0_OUTMUX0(pdata->outmux[0]) |
+ MCDE_CONF0_OUTMUX1(pdata->outmux[1]) |
+ MCDE_CONF0_OUTMUX2(pdata->outmux[2]) |
+ MCDE_CONF0_OUTMUX3(pdata->outmux[3]) |
+ MCDE_CONF0_OUTMUX4(pdata->outmux[4]) |
+ pdata->syncmux);
+
+ mcde_wfld(MCDE_RISOVL, OVLFDRIS, 1);
+ mcde_wfld(MCDE_RISPP, VCMPARIS, 1);
+ mcde_wfld(MCDE_RISPP, VCMPBRIS, 1);
+ mcde_wfld(MCDE_RISPP, VCMPC0RIS, 1);
+ mcde_wfld(MCDE_RISPP, VCMPC1RIS, 1);
+
+ /* Enable channel VCMP interrupts */
+ mcde_wreg(MCDE_IMSCPP,
+ MCDE_IMSCPP_VCMPAIM(true) |
+ MCDE_IMSCPP_VCMPBIM(true) |
+ MCDE_IMSCPP_VCMPC0IM(true) |
+ MCDE_IMSCPP_VCMPC1IM(true));
+ }
+
+ /* Enable overlay fetch done interrupts */
+ mcde_wfld(MCDE_IMSCOVL, OVLFDIM, 0x3f);
+
+ /* Setup sync pulse length */
+ mcde_wreg(MCDE_VSCRC0,
+ MCDE_VSCRC0_VSPMIN(1) |
+ MCDE_VSCRC0_VSPMAX(0xff));
+ mcde_wreg(MCDE_VSCRC1,
+ MCDE_VSCRC1_VSPMIN(1) |
+ MCDE_VSCRC1_VSPMAX(0xff));
+}
+
+static void disable_formatter(struct mcde_port *port)
+{
+ if (port->type == MCDE_PORTTYPE_DSI) {
+ if (port->phy.dsi.clk_dsi)
+ clk_disable(port->phy.dsi.clk_dsi);
+ if (port->phy.dsi.clk_dsi_lp)
+ clk_disable(port->phy.dsi.clk_dsi_lp);
+ if (port->phy.dsi.reg_vana)
+ regulator_disable(port->phy.dsi.reg_vana);
+ }
+ if (port->type == MCDE_PORTTYPE_DPI) {
+ if (port->phy.dpi.clk_dpi)
+ clk_disable(port->phy.dpi.clk_dpi);
+ }
+}
+
+static int disable_mcde_hw(bool force_disable)
+{
+ int i;
+ int ret;
+ bool mcde_up = false;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ if (!mcde_is_enabled)
+ return 0;
+
+ for (i = 0; i < num_channels; i++) {
+ struct mcde_chnl_state *chnl = &channels[i];
+ if (force_disable ||
+ (chnl->enabled && !chnl->continous_running)) {
+ u8 counter = 10;
+ disable_channel(chnl);
+ if (chnl->port.type == MCDE_PORTTYPE_DSI)
+ /*
+ * Wait for CSM_RUNNING,
+ * all data sent for display
+ */
+ while (dsi_rfld(chnl->port.link,
+ DSI_CMD_MODE_STS, CSM_RUNNING)
+ && counter--) {
+ cpu_relax();
+ udelay(100);
+ }
+ if (chnl->formatter_updated) {
+ disable_formatter(&chnl->port);
+ chnl->formatter_updated = false;
+ }
+ if (chnl->esram_is_enabled) {
+ int ret;
+ ret = regulator_disable(chnl->port.reg_esram);
+ if (ret < 0) {
+ dev_warn(&mcde_dev->dev,
+ "%s: disable failed\n",
+ __func__);
+ }
+ chnl->esram_is_enabled = false;
+ }
+ } else if (chnl->enabled && chnl->continous_running) {
+ mcde_up = true;
+ }
+ }
+
+ if (mcde_up)
+ return 0;
+
+ for (i = 0; i < num_channels; i++) {
+ struct mcde_chnl_state *chnl = &channels[i];
+ chnl->formatter_updated = false;
+ del_timer(&chnl->dsi_te_timer);
+ del_timer(&chnl->auto_sync_timer);
+ }
+
+ free_irq(mcde_irq, &mcde_dev->dev);
+
+ ret = disable_clocks_and_power(mcde_dev);
+ if (ret < 0) {
+ dev_dbg(&mcde_dev->dev,
+ "%s: disable_clocks_and_power failed\n"
+ , __func__);
+ return -EINVAL;
+ }
+ mcde_is_enabled = false;
+ return 0;
+}
+
+static void dpi_video_mode_apply(struct mcde_chnl_state *chnl)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+ chnl->tv_regs.interlaced_en = chnl->vmode.interlaced;
+
+ chnl->tv_regs.sel_mode_tv = chnl->port.phy.dpi.tv_mode;
+ if (chnl->tv_regs.sel_mode_tv) {
+ /* TV mode */
+ u32 bel;
+ /* -4 since hsw is excluding SAV/EAV, 2 bytes each */
+ chnl->tv_regs.hsw = chnl->vmode.hbp + chnl->vmode.hfp - 4;
+ /* vbp_field2 = vbp_field1 + 1 */
+ chnl->tv_regs.fsl1 = chnl->vmode.vbp / 2;
+ chnl->tv_regs.fsl2 = chnl->vmode.vbp - chnl->tv_regs.fsl1;
+ /* +1 since vbp_field2 = vbp_field1 + 1 */
+ bel = chnl->vmode.vbp + chnl->vmode.vfp;
+ /* in TV mode: bel2 = bel1 + 1 */
+ chnl->tv_regs.bel1 = bel / 2;
+ chnl->tv_regs.bel2 = bel - chnl->tv_regs.bel1;
+ if (chnl->port.phy.dpi.bus_width == 4)
+ chnl->tv_regs.tv_mode = MCDE_TVCRA_TVMODE_SDTV_656P_BE;
+ else
+ chnl->tv_regs.tv_mode = MCDE_TVCRA_TVMODE_SDTV_656P;
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_8)
+ chnl->tv_regs.inv_clk = true;
+ else {
+ chnl->tv_regs.dho = MCDE_CONFIG_TVOUT_HBORDER;
+ chnl->tv_regs.alw = MCDE_CONFIG_TVOUT_HBORDER;
+ chnl->tv_regs.dvo = MCDE_CONFIG_TVOUT_VBORDER;
+ chnl->tv_regs.bsl = MCDE_CONFIG_TVOUT_VBORDER;
+ }
+ } else {
+ /* LCD mode */
+ u32 polarity;
+ chnl->tv_regs.hsw = chnl->vmode.hsw;
+ chnl->tv_regs.dho = chnl->vmode.hbp;
+ chnl->tv_regs.alw = chnl->vmode.hfp;
+ chnl->tv_regs.bel1 = chnl->vmode.vsw;
+ chnl->tv_regs.bel2 = chnl->tv_regs.bel1;
+ chnl->tv_regs.dvo = chnl->vmode.vbp;
+ chnl->tv_regs.bsl = chnl->vmode.vfp;
+ chnl->tv_regs.fsl1 = 0;
+ chnl->tv_regs.fsl2 = 0;
+ polarity = chnl->port.phy.dpi.polarity;
+ chnl->tv_regs.lcdtim1 |= MCDE_LCDTIM1A_IPC(
+ (polarity & DPI_ACT_ON_FALLING_EDGE) != 0);
+ chnl->tv_regs.lcdtim1 = MCDE_LCDTIM1A_IHS(
+ (polarity & DPI_ACT_LOW_HSYNC) != 0);
+ chnl->tv_regs.lcdtim1 |= MCDE_LCDTIM1A_IVS(
+ (polarity & DPI_ACT_LOW_VSYNC) != 0);
+ chnl->tv_regs.lcdtim1 |= MCDE_LCDTIM1A_IOE(
+ (polarity & DPI_ACT_LOW_DATA_ENABLE) != 0);
+ }
+}
+
+static void update_dpi_registers(enum mcde_chnl chnl_id, struct tv_regs *regs)
+{
+ u8 idx = chnl_id;
+
+ dev_dbg(&mcde_dev->dev, "%s\n", __func__);
+ mcde_wreg(MCDE_TVCRA + idx * MCDE_TVCRA_GROUPOFFSET,
+ MCDE_TVCRA_SEL_MOD(regs->sel_mode_tv) |
+ MCDE_TVCRA_INTEREN(regs->interlaced_en) |
+ MCDE_TVCRA_IFIELD(0) |
+ MCDE_TVCRA_TVMODE(regs->tv_mode) |
+ MCDE_TVCRA_SDTVMODE(MCDE_TVCRA_SDTVMODE_Y0CBY1CR) |
+ MCDE_TVCRA_CKINV(regs->inv_clk) |
+ MCDE_TVCRA_AVRGEN(0));
+ mcde_wreg(MCDE_TVBLUA + idx * MCDE_TVBLUA_GROUPOFFSET,
+ MCDE_TVBLUA_TVBLU(MCDE_CONFIG_TVOUT_BACKGROUND_LUMINANCE) |
+ MCDE_TVBLUA_TVBCB(MCDE_CONFIG_TVOUT_BACKGROUND_CHROMINANCE_CB)|
+ MCDE_TVBLUA_TVBCR(MCDE_CONFIG_TVOUT_BACKGROUND_CHROMINANCE_CR));
+
+ /* Vertical timing registers */
+ mcde_wreg(MCDE_TVDVOA + idx * MCDE_TVDVOA_GROUPOFFSET,
+ MCDE_TVDVOA_DVO1(regs->dvo) |
+ MCDE_TVDVOA_DVO2(regs->dvo));
+ mcde_wreg(MCDE_TVBL1A + idx * MCDE_TVBL1A_GROUPOFFSET,
+ MCDE_TVBL1A_BEL1(regs->bel1) |
+ MCDE_TVBL1A_BSL1(regs->bsl));
+ mcde_wreg(MCDE_TVBL2A + idx * MCDE_TVBL1A_GROUPOFFSET,
+ MCDE_TVBL2A_BEL2(regs->bel2) |
+ MCDE_TVBL2A_BSL2(regs->bsl));
+ mcde_wreg(MCDE_TVISLA + idx * MCDE_TVISLA_GROUPOFFSET,
+ MCDE_TVISLA_FSL1(regs->fsl1) |
+ MCDE_TVISLA_FSL2(regs->fsl2));
+
+ /* Horizontal timing registers */
+ if (!regs->sel_mode_tv ||
+ hardware_version == MCDE_CHIP_VERSION_3_0_8) {
+ mcde_wreg(MCDE_TVLBALWA + idx * MCDE_TVLBALWA_GROUPOFFSET,
+ MCDE_TVLBALWA_LBW(regs->hsw) |
+ MCDE_TVLBALWA_ALW(regs->alw));
+ mcde_wreg(MCDE_TVTIM1A + idx * MCDE_TVTIM1A_GROUPOFFSET,
+ MCDE_TVTIM1A_DHO(regs->dho));
+ } else {
+ /* in earlier versions the LBW and DHO fields are swapped
+ * TV mode only
+ */
+ mcde_wreg(MCDE_TVLBALWA + idx * MCDE_TVLBALWA_GROUPOFFSET,
+ MCDE_TVLBALWA_LBW(regs->dho) |
+ MCDE_TVLBALWA_ALW(regs->alw));
+ mcde_wreg(MCDE_TVTIM1A + idx * MCDE_TVTIM1A_GROUPOFFSET,
+ MCDE_TVTIM1A_DHO(regs->hsw));
+ }
+ if (!regs->sel_mode_tv)
+ mcde_wreg(MCDE_LCDTIM1A + idx * MCDE_LCDTIM1A_GROUPOFFSET,
+ regs->lcdtim1);
+}
+
+static void update_col_registers(enum mcde_chnl chnl_id, struct col_regs *regs)
+{
+ u8 idx = chnl_id;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+ mcde_wreg(MCDE_RGBCONV1A + idx * MCDE_RGBCONV1A_GROUPOFFSET,
+ MCDE_RGBCONV1A_YR_RED(regs->y_red) |
+ MCDE_RGBCONV1A_YR_GREEN(regs->y_green));
+ mcde_wreg(MCDE_RGBCONV2A + idx * MCDE_RGBCONV2A_GROUPOFFSET,
+ MCDE_RGBCONV2A_YR_BLUE(regs->y_blue) |
+ MCDE_RGBCONV2A_CR_RED(regs->cr_red));
+ mcde_wreg(MCDE_RGBCONV3A + idx * MCDE_RGBCONV3A_GROUPOFFSET,
+ MCDE_RGBCONV3A_CR_GREEN(regs->cr_green) |
+ MCDE_RGBCONV3A_CR_BLUE(regs->cr_blue));
+ mcde_wreg(MCDE_RGBCONV4A + idx * MCDE_RGBCONV4A_GROUPOFFSET,
+ MCDE_RGBCONV4A_CB_RED(regs->cb_red) |
+ MCDE_RGBCONV4A_CB_GREEN(regs->cb_green));
+ mcde_wreg(MCDE_RGBCONV5A + idx * MCDE_RGBCONV5A_GROUPOFFSET,
+ MCDE_RGBCONV5A_CB_BLUE(regs->cb_blue) |
+ MCDE_RGBCONV5A_OFF_RED(regs->off_cr));
+ mcde_wreg(MCDE_RGBCONV6A + idx * MCDE_RGBCONV6A_GROUPOFFSET,
+ MCDE_RGBCONV6A_OFF_GREEN(regs->off_y) |
+ MCDE_RGBCONV6A_OFF_BLUE(regs->off_cb));
+}
+
+/* MCDE internal helpers */
+static u8 portfmt2dsipacking(enum mcde_port_pix_fmt pix_fmt)
+{
+ switch (pix_fmt) {
+ case MCDE_PORTPIXFMT_DSI_16BPP:
+ return MCDE_DSIVID0CONF0_PACKING_RGB565;
+ case MCDE_PORTPIXFMT_DSI_18BPP_PACKED:
+ return MCDE_DSIVID0CONF0_PACKING_RGB666;
+ case MCDE_PORTPIXFMT_DSI_18BPP:
+ case MCDE_PORTPIXFMT_DSI_24BPP:
+ default:
+ return MCDE_DSIVID0CONF0_PACKING_RGB888;
+ case MCDE_PORTPIXFMT_DSI_YCBCR422:
+ return MCDE_DSIVID0CONF0_PACKING_HDTV;
+ }
+}
+
+static u8 portfmt2bpp(enum mcde_port_pix_fmt pix_fmt)
+{
+ /* TODO: Check DPI spec *//* REVIEW: Remove or check */
+ switch (pix_fmt) {
+ case MCDE_PORTPIXFMT_DPI_16BPP_C1:
+ case MCDE_PORTPIXFMT_DPI_16BPP_C2:
+ case MCDE_PORTPIXFMT_DPI_16BPP_C3:
+ case MCDE_PORTPIXFMT_DSI_16BPP:
+ case MCDE_PORTPIXFMT_DSI_YCBCR422:
+ return 16;
+ case MCDE_PORTPIXFMT_DPI_18BPP_C1:
+ case MCDE_PORTPIXFMT_DPI_18BPP_C2:
+ case MCDE_PORTPIXFMT_DSI_18BPP_PACKED:
+ return 18;
+ case MCDE_PORTPIXFMT_DSI_18BPP:
+ case MCDE_PORTPIXFMT_DPI_24BPP:
+ case MCDE_PORTPIXFMT_DSI_24BPP:
+ return 24;
+ default:
+ return 1;
+ }
+}
+
+static u8 bpp2outbpp(u8 bpp)
+{
+ switch (bpp) {
+ case 16:
+ return MCDE_CRA1_OUTBPP_16BPP;
+ case 18:
+ return MCDE_CRA1_OUTBPP_18BPP;
+ case 24:
+ return MCDE_CRA1_OUTBPP_24BPP;
+ default:
+ return 0;
+ }
+}
+
+static u8 portfmt2cdwin(enum mcde_port_pix_fmt pix_fmt)
+{
+ switch (pix_fmt) {
+ case MCDE_PORTPIXFMT_DPI_16BPP_C1:
+ return MCDE_CRA1_CDWIN_16BPP_C1;
+ case MCDE_PORTPIXFMT_DPI_16BPP_C2:
+ return MCDE_CRA1_CDWIN_16BPP_C2;
+ case MCDE_PORTPIXFMT_DPI_16BPP_C3:
+ return MCDE_CRA1_CDWIN_16BPP_C3;
+ case MCDE_PORTPIXFMT_DPI_18BPP_C1:
+ return MCDE_CRA1_CDWIN_18BPP_C1;
+ case MCDE_PORTPIXFMT_DPI_18BPP_C2:
+ return MCDE_CRA1_CDWIN_18BPP_C2;
+ case MCDE_PORTPIXFMT_DPI_24BPP:
+ return MCDE_CRA1_CDWIN_24BPP;
+ default:
+ /* only DPI formats are relevant */
+ return 0;
+ }
+}
+
+static u32 get_output_fifo_size(enum mcde_fifo fifo)
+{
+ u32 ret = 1; /* Avoid div by zero */
+
+ switch (fifo) {
+ case MCDE_FIFO_A:
+ case MCDE_FIFO_B:
+ ret = MCDE_FIFO_AB_SIZE;
+ break;
+ case MCDE_FIFO_C0:
+ case MCDE_FIFO_C1:
+ ret = MCDE_FIFO_C0C1_SIZE;
+ break;
+ default:
+ dev_vdbg(&mcde_dev->dev, "Unsupported fifo");
+ break;
+ }
+ return ret;
+}
+
+static u8 get_dsi_formid(const struct mcde_port *port)
+{
+ if (port->ifc == DSI_VIDEO_MODE && port->link == 0)
+ return MCDE_CTRLA_FORMID_DSI0VID;
+ else if (port->ifc == DSI_VIDEO_MODE && port->link == 1)
+ return MCDE_CTRLA_FORMID_DSI1VID;
+ else if (port->ifc == DSI_VIDEO_MODE && port->link == 2)
+ return MCDE_CTRLA_FORMID_DSI2VID;
+ else if (port->ifc == DSI_CMD_MODE && port->link == 0)
+ return MCDE_CTRLA_FORMID_DSI0CMD;
+ else if (port->ifc == DSI_CMD_MODE && port->link == 1)
+ return MCDE_CTRLA_FORMID_DSI1CMD;
+ else if (port->ifc == DSI_CMD_MODE && port->link == 2)
+ return MCDE_CTRLA_FORMID_DSI2CMD;
+ return 0;
+}
+
+static struct mcde_chnl_state *find_channel_by_dsilink(int link)
+{
+ struct mcde_chnl_state *chnl = &channels[0];
+ for (; chnl < &channels[num_channels]; chnl++)
+ if (chnl->enabled && chnl->port.link == link &&
+ chnl->port.type == MCDE_PORTTYPE_DSI)
+ return chnl;
+ return NULL;
+}
+
+static inline void mcde_handle_vcmp(struct mcde_chnl_state *chnl, u32 int_fld)
+{
+ if (!chnl->vcmp_per_field ||
+ (chnl->vcmp_per_field && chnl->even_vcmp)) {
+ chnl->transactionid_hw = chnl->transactionid_regs;
+ wake_up(&chnl->waitq_hw);
+ if (chnl->port.update_auto_trig &&
+ chnl->port.sync_src == MCDE_SYNCSRC_OFF &&
+ chnl->port.type == MCDE_PORTTYPE_DSI &&
+ chnl->continous_running) {
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_8)
+ enable_channel(chnl);
+
+ mcde_wreg(MCDE_CHNL0SYNCHSW +
+ chnl->id * MCDE_CHNL0SYNCHSW_GROUPOFFSET,
+ MCDE_CHNL0SYNCHSW_SW_TRIG(true));
+
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_8)
+ disable_flow(chnl);
+ mod_timer(&chnl->auto_sync_timer,
+ jiffies +
+ msecs_to_jiffies(MCDE_AUTO_SYNC_WATCHDOG
+ * 1000));
+ }
+ }
+ chnl->even_vcmp = !chnl->even_vcmp;
+ mcde_wreg_fld(MCDE_RISPP, 0x1 << int_fld, int_fld, 1);
+}
+
+static irqreturn_t mcde_irq_handler(int irq, void *dev)
+{
+ int i;
+ u32 irq_status;
+
+ /* Handle overlay irqs */
+ irq_status = mcde_rfld(MCDE_RISOVL, OVLFDRIS);
+ mcde_wfld(MCDE_RISOVL, OVLFDRIS, irq_status);
+
+ /* Handle channel irqs */
+ irq_status = mcde_rreg(MCDE_RISPP);
+ if (irq_status & MCDE_RISPP_VCMPARIS_MASK)
+ mcde_handle_vcmp(&channels[MCDE_CHNL_A],
+ MCDE_RISPP_VCMPARIS_SHIFT);
+ if (irq_status & MCDE_RISPP_VCMPBRIS_MASK)
+ mcde_handle_vcmp(&channels[MCDE_CHNL_B],
+ MCDE_RISPP_VCMPBRIS_SHIFT);
+ if (irq_status & MCDE_RISPP_VCMPC0RIS_MASK)
+ mcde_handle_vcmp(&channels[MCDE_CHNL_C0],
+ MCDE_RISPP_VCMPC0RIS_SHIFT);
+ if (irq_status & MCDE_RISPP_VCMPC1RIS_MASK)
+ mcde_handle_vcmp(&channels[MCDE_CHNL_C1],
+ MCDE_RISPP_VCMPC1RIS_SHIFT);
+ for (i = 0; i < num_dsilinks; i++) {
+ struct mcde_chnl_state *chnl_from_dsi;
+
+ chnl_from_dsi = find_channel_by_dsilink(i);
+
+ if (chnl_from_dsi == NULL)
+ continue;
+
+ irq_status = dsi_rfld(i, DSI_DIRECT_CMD_STS_FLAG,
+ TE_RECEIVED_FLAG);
+ if (irq_status) {
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_8)
+ disable_flow(chnl_from_dsi);
+ dsi_wreg(i, DSI_DIRECT_CMD_STS_CLR,
+ DSI_DIRECT_CMD_STS_CLR_TE_RECEIVED_CLR(true));
+ dev_vdbg(&mcde_dev->dev, "BTA TE DSI%d\n", i);
+ do_softwaretrig(chnl_from_dsi);
+ dev_vdbg(&mcde_dev->dev, "SW TRIG DSI%d, chnl=%d\n", i,
+ chnl_from_dsi->id);
+ }
+
+ irq_status = dsi_rfld(i, DSI_CMD_MODE_STS_FLAG, ERR_NO_TE_FLAG);
+ if (irq_status) {
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_8)
+ disable_flow(chnl_from_dsi);
+ dsi_wreg(i, DSI_CMD_MODE_STS_CLR,
+ DSI_CMD_MODE_STS_CLR_ERR_NO_TE_CLR(true));
+ dev_info(&mcde_dev->dev, "NO_TE DSI%d\n", i);
+ }
+
+ irq_status = dsi_rfld(i, DSI_DIRECT_CMD_STS, TRIGGER_RECEIVED);
+ if (irq_status) {
+ /* DSI TE polling answer received */
+ dsi_wreg(i, DSI_DIRECT_CMD_STS_CLR,
+ DSI_DIRECT_CMD_STS_CLR_TRIGGER_RECEIVED_CLR(
+ true));
+
+ if (chnl_from_dsi->port.sync_src ==
+ MCDE_SYNCSRC_TE_POLLING)
+ /* Reset timer */
+ dsi_te_poll_set_timer(chnl_from_dsi,
+ DSI_TE_NO_ANSWER_TIMEOUT);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void wait_for_channel(struct mcde_chnl_state *chnl)
+{
+ int ret;
+ u32 id = chnl->transactionid_regs;
+
+ if (chnl->transactionid_hw >= id)
+ return;
+
+ ret = wait_event_timeout(chnl->waitq_hw,
+ chnl->transactionid_hw == id,
+ msecs_to_jiffies(CHNL_TIMEOUT));
+ if (!ret)
+ dev_warn(&mcde_dev->dev,
+ "Wait for channel timeout (chnl=%d,%d<%d)!\n",
+ chnl->id, chnl->transactionid_hw,
+ id);
+}
+
+static int update_channel_static_registers(struct mcde_chnl_state *chnl)
+{
+ const struct chnl_config *cfg = chnl->cfg;
+ const struct mcde_port *port = &chnl->port;
+
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_5) {
+ /* Fifo & muxing */
+ if (cfg->swap_a_c0_set)
+ mcde_wfld(MCDE_CONF0, SWAP_A_C0_V1, cfg->swap_a_c0);
+ if (cfg->swap_b_c1_set)
+ mcde_wfld(MCDE_CONF0, SWAP_B_C1_V1, cfg->swap_b_c1);
+ if (cfg->fabmux_set)
+ mcde_wfld(MCDE_CR, FABMUX_V1, cfg->fabmux);
+ if (cfg->f01mux_set)
+ mcde_wfld(MCDE_CR, F01MUX_V1, cfg->f01mux);
+
+ if (port->type == MCDE_PORTTYPE_DPI) {
+ if (port->link == 0)
+ mcde_wfld(MCDE_CR, DPIA_EN_V1, true);
+ else if (port->link == 1)
+ mcde_wfld(MCDE_CR, DPIB_EN_V1, true);
+ } else if (port->type == MCDE_PORTTYPE_DSI) {
+ if (port->ifc == DSI_VIDEO_MODE && port->link == 0)
+ mcde_wfld(MCDE_CR, DSIVID0_EN_V1, true);
+ else if (port->ifc == DSI_VIDEO_MODE && port->link == 1)
+ mcde_wfld(MCDE_CR, DSIVID1_EN_V1, true);
+ else if (port->ifc == DSI_VIDEO_MODE && port->link == 2)
+ mcde_wfld(MCDE_CR, DSIVID2_EN_V1, true);
+ else if (port->ifc == DSI_CMD_MODE && port->link == 0)
+ mcde_wfld(MCDE_CR, DSICMD0_EN_V1, true);
+ else if (port->ifc == DSI_CMD_MODE && port->link == 1)
+ mcde_wfld(MCDE_CR, DSICMD1_EN_V1, true);
+ else if (port->ifc == DSI_CMD_MODE && port->link == 2)
+ mcde_wfld(MCDE_CR, DSICMD2_EN_V1, true);
+ }
+
+ if (chnl->fifo == MCDE_FIFO_C0)
+ mcde_wreg(MCDE_CTRLC0, MCDE_CTRLC0_FIFOWTRMRK(
+ get_output_fifo_size(MCDE_FIFO_C0)));
+ else if (chnl->fifo == MCDE_FIFO_C1)
+ mcde_wreg(MCDE_CTRLC1, MCDE_CTRLC1_FIFOWTRMRK(
+ get_output_fifo_size(MCDE_FIFO_C1)));
+ else if (port->update_auto_trig &&
+ (port->sync_src == MCDE_SYNCSRC_TE0))
+ mcde_wreg(MCDE_CTRLC0, MCDE_CTRLC0_FIFOWTRMRK(
+ get_output_fifo_size(MCDE_FIFO_C0)));
+ else if (port->update_auto_trig &&
+ (port->sync_src == MCDE_SYNCSRC_TE1))
+ mcde_wreg(MCDE_CTRLC1, MCDE_CTRLC1_FIFOWTRMRK(
+ get_output_fifo_size(MCDE_FIFO_C1)));
+ } else if (hardware_version == MCDE_CHIP_VERSION_3_0_8) {
+ switch (chnl->fifo) {
+ case MCDE_FIFO_A:
+ mcde_wreg(MCDE_CHNL0MUXING_V2 + chnl->id *
+ MCDE_CHNL0MUXING_V2_GROUPOFFSET,
+ MCDE_CHNL0MUXING_V2_FIFO_ID_ENUM(FIFO_A));
+ if (port->type == MCDE_PORTTYPE_DPI) {
+ mcde_wfld(MCDE_CTRLA, FORMTYPE,
+ MCDE_CTRLA_FORMTYPE_DPITV);
+ mcde_wfld(MCDE_CTRLA, FORMID, port->link);
+ mcde_wfld(MCDE_CTRLA, FIFOWTRMRK,
+ get_output_fifo_size(MCDE_FIFO_A));
+ } else if (port->type == MCDE_PORTTYPE_DSI) {
+ mcde_wfld(MCDE_CTRLA, FORMTYPE,
+ MCDE_CTRLA_FORMTYPE_DSI);
+ mcde_wfld(MCDE_CTRLA, FORMID,
+ get_dsi_formid(port));
+ mcde_wfld(MCDE_CTRLA, FIFOWTRMRK,
+ get_output_fifo_size(MCDE_FIFO_A));
+ }
+ break;
+ case MCDE_FIFO_B:
+ mcde_wreg(MCDE_CHNL0MUXING_V2 + chnl->id *
+ MCDE_CHNL0MUXING_V2_GROUPOFFSET,
+ MCDE_CHNL0MUXING_V2_FIFO_ID_ENUM(FIFO_B));
+ if (port->type == MCDE_PORTTYPE_DPI) {
+ mcde_wfld(MCDE_CTRLB, FORMTYPE,
+ MCDE_CTRLB_FORMTYPE_DPITV);
+ mcde_wfld(MCDE_CTRLB, FORMID, port->link);
+ mcde_wfld(MCDE_CTRLB, FIFOWTRMRK,
+ get_output_fifo_size(MCDE_FIFO_B));
+ } else if (port->type == MCDE_PORTTYPE_DSI) {
+ mcde_wfld(MCDE_CTRLB, FORMTYPE,
+ MCDE_CTRLB_FORMTYPE_DSI);
+ mcde_wfld(MCDE_CTRLB, FORMID,
+ get_dsi_formid(port));
+ mcde_wfld(MCDE_CTRLB, FIFOWTRMRK,
+ get_output_fifo_size(MCDE_FIFO_B));
+ }
+
+ break;
+ case MCDE_FIFO_C0:
+ mcde_wreg(MCDE_CHNL0MUXING_V2 + chnl->id *
+ MCDE_CHNL0MUXING_V2_GROUPOFFSET,
+ MCDE_CHNL0MUXING_V2_FIFO_ID_ENUM(FIFO_C0));
+ if (port->type == MCDE_PORTTYPE_DPI)
+ return -EINVAL;
+ mcde_wfld(MCDE_CTRLC0, FORMTYPE,
+ MCDE_CTRLC0_FORMTYPE_DSI);
+ mcde_wfld(MCDE_CTRLC0, FORMID, get_dsi_formid(port));
+ mcde_wfld(MCDE_CTRLC0, FIFOWTRMRK,
+ get_output_fifo_size(MCDE_FIFO_C0));
+ break;
+ case MCDE_FIFO_C1:
+ mcde_wreg(MCDE_CHNL0MUXING_V2 + chnl->id *
+ MCDE_CHNL0MUXING_V2_GROUPOFFSET,
+ MCDE_CHNL0MUXING_V2_FIFO_ID_ENUM(FIFO_C1));
+ if (port->type == MCDE_PORTTYPE_DPI)
+ return -EINVAL;
+ mcde_wfld(MCDE_CTRLC1, FORMTYPE,
+ MCDE_CTRLC1_FORMTYPE_DSI);
+ mcde_wfld(MCDE_CTRLC1, FORMID, get_dsi_formid(port));
+ mcde_wfld(MCDE_CTRLC1, FIFOWTRMRK,
+ get_output_fifo_size(MCDE_FIFO_C1));
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (hardware_version == MCDE_CHIP_VERSION_1_0_4) {
+ switch (chnl->fifo) {
+ case MCDE_FIFO_A:
+ /* only channel A is supported */
+ if (chnl->id != 0)
+ return -EINVAL;
+
+ if (port->type == MCDE_PORTTYPE_DSI) {
+ if ((port->link == 1 &&
+ port->ifc == DSI_VIDEO_MODE) ||
+ (port->link == 0 && port->ifc == DSI_CMD_MODE))
+ return -EINVAL;
+ mcde_wfld(MCDE_CR, DSI0_EN_V3, true);
+
+ } else if (port->type == MCDE_PORTTYPE_DPI) {
+ mcde_wfld(MCDE_CR, DPI_EN_V3, true);
+ }
+ mcde_wfld(MCDE_CTRLA, FIFOWTRMRK,
+ get_output_fifo_size(MCDE_FIFO_A));
+ break;
+ case MCDE_FIFO_B:
+ if (port->type != MCDE_PORTTYPE_DSI)
+ return -EINVAL;
+ /* only channel B is supported */
+ if (chnl->id != 1)
+ return -EINVAL;
+
+ if ((port->link == 0 && port->ifc == DSI_VIDEO_MODE) ||
+ (port->link == 1 && port->ifc == DSI_CMD_MODE))
+ return -EINVAL;
+
+ mcde_wfld(MCDE_CR, DSI1_EN_V3, true);
+ mcde_wfld(MCDE_CTRLB, FIFOWTRMRK,
+ get_output_fifo_size(MCDE_FIFO_B));
+
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ /* Formatter */
+ if (port->type == MCDE_PORTTYPE_DSI) {
+ int i = 0;
+ u8 idx;
+ u8 lnk = port->link;
+ int ret;
+
+ if (port->phy.dsi.reg_vana) {
+ ret = regulator_enable(port->phy.dsi.reg_vana);
+ if (ret < 0) {
+ dev_warn(&mcde_dev->dev,
+ "%s: regulator_enable failed\n",
+ __func__);
+ goto regulator_vana_err;
+ }
+ }
+
+ if (port->phy.dsi.clk_dsi) {
+ ret = clk_enable(port->phy.dsi.clk_dsi);
+ if (ret < 0) {
+ dev_warn(&mcde_dev->dev, "%s: "
+ "clk_enable dsi failed ret = %d\n",
+ __func__, ret);
+ goto clk_dsi_err;
+ }
+ }
+ if (port->phy.dsi.clk_dsi_lp) {
+ ret = clk_enable(port->phy.dsi.clk_dsi_lp);
+ if (ret < 0) {
+ dev_warn(&mcde_dev->dev, "%s: "
+ "clk_enable dsi_lp failed ret = %d\n",
+ __func__, ret);
+ goto clk_dsi_lp_err;
+ }
+ }
+
+ if (hardware_version == MCDE_CHIP_VERSION_1_0_4)
+ idx = chnl->id;
+ else
+ idx = 2 * port->link + port->ifc;
+
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, LINK_EN, true);
+ dev_dbg(&mcde_dev->dev, "DSI%d LINK_EN\n", lnk);
+
+ if (port->sync_src == MCDE_SYNCSRC_TE_POLLING) {
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_5) {
+ dev_err(&mcde_dev->dev,
+ "DSI TE polling is not supported on this HW\n");
+ goto dsi_link_error;
+ }
+
+ /* Enable DSI TE polling */
+ dsi_te_poll_req(chnl);
+
+ /* Set timer to detect non TE answer */
+ dsi_te_poll_set_timer(chnl,
+ DSI_TE_NO_ANSWER_TIMEOUT_INIT);
+ } else {
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, BTA_EN, true);
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, READ_EN, true);
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, REG_TE_EN, true);
+ }
+
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_5) {
+ if (port->phy.dsi.data_lanes_swap) {
+ dev_warn(&mcde_dev->dev,
+ "DSI %d data lane remap not available!\n",
+ lnk);
+ goto dsi_link_error;
+ }
+ } else
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, DLX_REMAP_EN,
+ port->phy.dsi.data_lanes_swap);
+
+ dsi_wreg(lnk, DSI_MCTL_DPHY_STATIC,
+ DSI_MCTL_DPHY_STATIC_UI_X4(port->phy.dsi.ui));
+ dsi_wreg(lnk, DSI_DPHY_LANES_TRIM,
+ DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_ENUM(0_90));
+ dsi_wreg(lnk, DSI_MCTL_DPHY_TIMEOUT,
+ DSI_MCTL_DPHY_TIMEOUT_CLK_DIV(0xf) |
+ DSI_MCTL_DPHY_TIMEOUT_HSTX_TO_VAL(0x3fff) |
+ DSI_MCTL_DPHY_TIMEOUT_LPRX_TO_VAL(0x3fff));
+ dsi_wreg(lnk, DSI_MCTL_MAIN_PHY_CTL,
+ DSI_MCTL_MAIN_PHY_CTL_WAIT_BURST_TIME(0xf) |
+ DSI_MCTL_MAIN_PHY_CTL_LANE2_EN(true) |
+ DSI_MCTL_MAIN_PHY_CTL_CLK_CONTINUOUS(
+ port->phy.dsi.clk_cont));
+ dsi_wreg(lnk, DSI_MCTL_ULPOUT_TIME,
+ DSI_MCTL_ULPOUT_TIME_CKLANE_ULPOUT_TIME(1) |
+ DSI_MCTL_ULPOUT_TIME_DATA_ULPOUT_TIME(1));
+ /* TODO: make enum */
+ dsi_wfld(lnk, DSI_CMD_MODE_CTL, ARB_MODE, false);
+ /* TODO: make enum */
+ dsi_wfld(lnk, DSI_CMD_MODE_CTL, ARB_PRI, port->ifc == 1);
+ dsi_wreg(lnk, DSI_MCTL_MAIN_EN,
+ DSI_MCTL_MAIN_EN_PLL_START(true) |
+ DSI_MCTL_MAIN_EN_CKLANE_EN(true) |
+ DSI_MCTL_MAIN_EN_DAT1_EN(true) |
+ DSI_MCTL_MAIN_EN_DAT2_EN(port->phy.dsi.num_data_lanes
+ == 2) |
+ DSI_MCTL_MAIN_EN_IF1_EN(port->ifc == 0) |
+ DSI_MCTL_MAIN_EN_IF2_EN(port->ifc == 1));
+ while (dsi_rfld(lnk, DSI_MCTL_MAIN_STS, CLKLANE_READY) == 0 ||
+ dsi_rfld(lnk, DSI_MCTL_MAIN_STS, DAT1_READY) == 0 ||
+ dsi_rfld(lnk, DSI_MCTL_MAIN_STS, DAT2_READY) == 0) {
+ mdelay(1);
+ if (i++ == 10) {
+ dev_warn(&mcde_dev->dev,
+ "DSI lane not ready (link=%d)!\n", lnk);
+ goto dsi_link_error;
+ }
+ }
+
+ mcde_wreg(MCDE_DSIVID0CONF0 +
+ idx * MCDE_DSIVID0CONF0_GROUPOFFSET,
+ MCDE_DSIVID0CONF0_BLANKING(0) |
+ MCDE_DSIVID0CONF0_VID_MODE(
+ port->mode == MCDE_PORTMODE_VID) |
+ MCDE_DSIVID0CONF0_CMD8(true) |
+ MCDE_DSIVID0CONF0_BIT_SWAP(false) |
+ MCDE_DSIVID0CONF0_BYTE_SWAP(false) |
+ MCDE_DSIVID0CONF0_DCSVID_NOTGEN(true));
+
+ if (port->mode == MCDE_PORTMODE_CMD) {
+ if (port->ifc == DSI_VIDEO_MODE)
+ dsi_wfld(port->link, DSI_CMD_MODE_CTL, IF1_ID,
+ port->phy.dsi.virt_id);
+ else if (port->ifc == DSI_CMD_MODE)
+ dsi_wfld(port->link, DSI_CMD_MODE_CTL, IF2_ID,
+ port->phy.dsi.virt_id);
+ }
+ }
+
+ if (port->type == MCDE_PORTTYPE_DPI) {
+ if (port->phy.dpi.clk_dpi) {
+ int ret;
+ ret = clk_enable(port->phy.dpi.clk_dpi);
+ if (ret < 0) {
+ dev_warn(&mcde_dev->dev, "%s: "
+ "clk_enable dsi_lp failed ret = %d\n",
+ __func__, ret);
+ goto clk_dpi_err;
+ }
+ }
+ }
+
+ mcde_wfld(MCDE_CR, MCDEEN, true);
+ chnl->formatter_updated = true;
+
+ dev_vdbg(&mcde_dev->dev, "Static registers setup, chnl=%d\n", chnl->id);
+
+ return 0;
+dsi_link_error:
+ if (port->phy.dsi.clk_dsi_lp)
+ clk_disable(port->phy.dsi.clk_dsi_lp);
+clk_dsi_lp_err:
+ if (port->phy.dsi.clk_dsi)
+ clk_disable(port->phy.dsi.clk_dsi);
+clk_dsi_err:
+ if (port->phy.dsi.reg_vana) {
+ regulator_disable(port->phy.dsi.reg_vana);
+ chnl->port.phy.dsi.reg_vana = NULL;
+ }
+regulator_vana_err:
+clk_dpi_err:
+ return -EINVAL;
+}
+
+/* REVIEW: Make update_* an mcde_rectangle? */
+static void update_overlay_registers(u8 idx, struct ovly_regs *regs,
+ struct mcde_port *port, enum mcde_fifo fifo,
+ u16 update_x, u16 update_y, u16 update_w,
+ u16 update_h, s16 stride, bool interlaced,
+ enum mcde_display_rotation rotation)
+{
+ /* TODO: fix clipping for small overlay */
+ u32 lmrgn = (regs->cropx + update_x) * regs->bits_per_pixel;
+ u32 tmrgn = (regs->cropy + update_y) * stride;
+ u32 ppl = regs->ppl - update_x;
+ u32 lpf = regs->lpf - update_y;
+ s32 ljinc = stride;
+ u32 pixelfetchwtrmrklevel;
+ u8 nr_of_bufs = 1;
+ u32 fifo_size;
+ u32 sel_mod = MCDE_EXTSRC0CR_SEL_MOD_SOFTWARE_SEL;
+
+ if (rotation == MCDE_DISPLAY_ROT_180_CCW) {
+ ljinc = -ljinc;
+ tmrgn += stride * (regs->lpf - 1) / 8;
+ }
+
+ /*
+ * TODO: Preferably most of this is done in some apply function instead
+ * of every update. Problem is however that at overlay apply
+ * there is no port type info available (and the question is
+ * whether it is appropriate to add a port type there).
+ * Note that lpf has a dependency on update_y.
+ */
+ if (port->type == MCDE_PORTTYPE_DPI && port->phy.dpi.tv_mode)
+ /* REVIEW: Why not for DSI? enable in regs? */
+ regs->col_conv = MCDE_OVL0CR_COLCCTRL_ENABLED_NO_SAT;
+ else if (port->type == MCDE_PORTTYPE_DSI) {
+ if (port->pixel_format == MCDE_PORTPIXFMT_DSI_YCBCR422)
+ regs->col_conv = MCDE_OVL0CR_COLCCTRL_ENABLED_NO_SAT;
+ else
+ regs->col_conv = MCDE_OVL0CR_COLCCTRL_DISABLED;
+ if (interlaced) {
+ nr_of_bufs = 2;
+ lpf = lpf / 2;
+ ljinc *= 2;
+ }
+ }
+
+ fifo_size = get_output_fifo_size(fifo);
+
+ if ((fifo == MCDE_FIFO_A || fifo == MCDE_FIFO_B) &&
+ regs->ppl >= fifo_size * 2)
+ pixelfetchwtrmrklevel = MCDE_PIXFETCH_LARGE_WTRMRKLVL;
+ else
+ pixelfetchwtrmrklevel = MCDE_PIXFETCH_MEDIUM_WTRMRKLVL;
+
+ if (port->update_auto_trig && port->type == MCDE_PORTTYPE_DSI) {
+ switch (port->sync_src) {
+ case MCDE_SYNCSRC_OFF:
+ sel_mod = MCDE_EXTSRC0CR_SEL_MOD_SOFTWARE_SEL;
+ break;
+ case MCDE_SYNCSRC_TE0:
+ case MCDE_SYNCSRC_TE1:
+ case MCDE_SYNCSRC_TE_POLLING:
+ default:
+ sel_mod = MCDE_EXTSRC0CR_SEL_MOD_AUTO_TOGGLE;
+ break;
+ }
+ } else if (port->type == MCDE_PORTTYPE_DPI)
+ sel_mod = MCDE_EXTSRC0CR_SEL_MOD_SOFTWARE_SEL;
+
+ regs->update = false;
+ mcde_wreg(MCDE_EXTSRC0CONF + idx * MCDE_EXTSRC0CONF_GROUPOFFSET,
+ MCDE_EXTSRC0CONF_BUF_ID(0) |
+ MCDE_EXTSRC0CONF_BUF_NB(nr_of_bufs) |
+ MCDE_EXTSRC0CONF_PRI_OVLID(idx) |
+ MCDE_EXTSRC0CONF_BPP(regs->bpp) |
+ MCDE_EXTSRC0CONF_BGR(regs->bgr) |
+ MCDE_EXTSRC0CONF_BEBO(regs->bebo) |
+ MCDE_EXTSRC0CONF_BEPO(false));
+ mcde_wreg(MCDE_EXTSRC0CR + idx * MCDE_EXTSRC0CR_GROUPOFFSET,
+ MCDE_EXTSRC0CR_SEL_MOD(sel_mod) |
+ MCDE_EXTSRC0CR_MULTIOVL_CTRL_ENUM(PRIMARY) |
+ MCDE_EXTSRC0CR_FS_DIV_DISABLE(false) |
+ MCDE_EXTSRC0CR_FORCE_FS_DIV(false));
+ mcde_wreg(MCDE_OVL0CR + idx * MCDE_OVL0CR_GROUPOFFSET,
+ MCDE_OVL0CR_OVLEN(regs->enabled) |
+ MCDE_OVL0CR_COLCCTRL(regs->col_conv) |
+ MCDE_OVL0CR_CKEYGEN(false) |
+ MCDE_OVL0CR_ALPHAPMEN(false) |
+ MCDE_OVL0CR_OVLF(false) |
+ MCDE_OVL0CR_OVLR(false) |
+ MCDE_OVL0CR_OVLB(false) |
+ MCDE_OVL0CR_FETCH_ROPC(0) |
+ MCDE_OVL0CR_STBPRIO(0) |
+ MCDE_OVL0CR_BURSTSIZE_ENUM(HW_8W) |
+ /* TODO: enum, get from ovly */
+ MCDE_OVL0CR_MAXOUTSTANDING_ENUM(8_REQ) |
+ /* TODO: _HW_8W, calculate? */
+ MCDE_OVL0CR_ROTBURSTSIZE_ENUM(HW_8W));
+ mcde_wreg(MCDE_OVL0CONF + idx * MCDE_OVL0CONF_GROUPOFFSET,
+ MCDE_OVL0CONF_PPL(ppl) |
+ MCDE_OVL0CONF_EXTSRC_ID(idx) |
+ MCDE_OVL0CONF_LPF(lpf));
+ mcde_wreg(MCDE_OVL0CONF2 + idx * MCDE_OVL0CONF2_GROUPOFFSET,
+ MCDE_OVL0CONF2_BP(regs->alpha_source) |
+ MCDE_OVL0CONF2_ALPHAVALUE(regs->alpha_value) |
+ MCDE_OVL0CONF2_OPQ(regs->opq) |
+ MCDE_OVL0CONF2_PIXOFF(lmrgn & 63) |
+ MCDE_OVL0CONF2_PIXELFETCHERWATERMARKLEVEL(
+ pixelfetchwtrmrklevel));
+ mcde_wreg(MCDE_OVL0LJINC + idx * MCDE_OVL0LJINC_GROUPOFFSET,
+ ljinc);
+ mcde_wreg(MCDE_OVL0CROP + idx * MCDE_OVL0CROP_GROUPOFFSET,
+ MCDE_OVL0CROP_TMRGN(tmrgn) |
+ MCDE_OVL0CROP_LMRGN(lmrgn >> 6));
+
+ dev_vdbg(&mcde_dev->dev, "Overlay registers setup, idx=%d\n", idx);
+}
+
+static void update_overlay_registers_on_the_fly(u8 idx, struct ovly_regs *regs)
+{
+ mcde_wreg(MCDE_OVL0COMP + idx * MCDE_OVL0COMP_GROUPOFFSET,
+ MCDE_OVL0COMP_XPOS(regs->xpos) |
+ MCDE_OVL0COMP_CH_ID(regs->ch_id) |
+ MCDE_OVL0COMP_YPOS(regs->ypos) |
+ MCDE_OVL0COMP_Z(regs->z));
+
+ mcde_wreg(MCDE_EXTSRC0A0 + idx * MCDE_EXTSRC0A0_GROUPOFFSET,
+ regs->baseaddress0);
+ mcde_wreg(MCDE_EXTSRC0A1 + idx * MCDE_EXTSRC0A1_GROUPOFFSET,
+ regs->baseaddress1);
+}
+
+static void do_softwaretrig(struct mcde_chnl_state *chnl)
+{
+ /*
+ * For main and secondary display,
+ * FLOWEN has to be set before a SOFTWARE TRIG
+ * Otherwise no overlay interrupt is triggerd
+ * However FLOWEN must not be triggered before SOFTWARE TRIG
+ * if rotation is enabled
+ */
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_8)
+ enable_channel(chnl);
+ else if ((!is_channel_enabled(chnl) && !chnl->regs.roten)
+ || chnl->power_mode != MCDE_DISPLAY_PM_ON)
+ enable_channel(chnl);
+
+ mcde_wreg(MCDE_CHNL0SYNCHSW +
+ chnl->id * MCDE_CHNL0SYNCHSW_GROUPOFFSET,
+ MCDE_CHNL0SYNCHSW_SW_TRIG(true));
+
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_8)
+ disable_flow(chnl);
+ else
+ enable_channel(chnl);
+}
+
+static void disable_flow(struct mcde_chnl_state *chnl)
+{
+ switch (chnl->id) {
+ case MCDE_CHNL_A:
+ mcde_wfld(MCDE_CRA0, FLOEN, false);
+ break;
+ case MCDE_CHNL_B:
+ mcde_wfld(MCDE_CRB0, FLOEN, false);
+ break;
+ case MCDE_CHNL_C0:
+ mcde_wfld(MCDE_CRC, C1EN, false);
+ break;
+ case MCDE_CHNL_C1:
+ mcde_wfld(MCDE_CRC, C2EN, false);
+ break;
+ }
+}
+#define MCDE_FLOWEN_MAX_TRIAL 60
+
+static void disable_channel(struct mcde_chnl_state *chnl)
+{
+ int i;
+ const struct mcde_port *port = &chnl->port;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ chnl->disable_software_trig = true;
+
+ if (port->type == MCDE_PORTTYPE_DSI) {
+ dsi_wfld(port->link, DSI_MCTL_MAIN_PHY_CTL, CLK_CONTINUOUS,
+ false);
+ if (port->sync_src == MCDE_SYNCSRC_TE_POLLING)
+ del_timer(&chnl->dsi_te_timer);
+ }
+
+ if (chnl->port.update_auto_trig &&
+ chnl->port.sync_src == MCDE_SYNCSRC_OFF &&
+ chnl->port.type == MCDE_PORTTYPE_DSI) {
+ del_timer(&chnl->auto_sync_timer);
+ chnl->continous_running = false;
+ }
+
+ if (hardware_version == MCDE_CHIP_VERSION_1_0_4) {
+ if (is_channel_enabled(chnl)) {
+ wait_for_channel(chnl);
+ /*
+ * Just to make sure that a frame is triggered when
+ * we try to disable the channel
+ */
+ chnl->transactionid++;
+ mcde_wreg(MCDE_CHNL0SYNCHSW +
+ chnl->id * MCDE_CHNL0SYNCHSW_GROUPOFFSET,
+ MCDE_CHNL0SYNCHSW_SW_TRIG(true));
+ disable_flow(chnl);
+ wait_for_channel(chnl);
+ for (i = 0; i < MCDE_FLOWEN_MAX_TRIAL; i++) {
+ if (!is_channel_enabled(chnl)) {
+ dev_vdbg(&mcde_dev->dev,
+ "Flow %d off after >= %d ms\n",
+ chnl->id, i);
+ goto break_switch;
+ }
+ msleep(1);
+ }
+ } else {
+ dev_vdbg(&mcde_dev->dev,
+ "Flow %d disable after >= %d ms\n",
+ chnl->id, i);
+ goto break_switch;
+ }
+ } else {
+ switch (chnl->id) {
+ case MCDE_CHNL_A:
+ mcde_wfld(MCDE_CRA0, FLOEN, false);
+ wait_for_channel(chnl);
+ for (i = 0; i < MCDE_FLOWEN_MAX_TRIAL; i++) {
+ msleep(1);
+ if (!mcde_rfld(MCDE_CRA0, FLOEN)) {
+ dev_vdbg(&mcde_dev->dev,
+ "Flow (A) off after >= %d ms\n", i);
+ goto break_switch;
+ }
+ }
+ dev_warn(&mcde_dev->dev,
+ "%s: channel A timeout\n", __func__);
+ break;
+ case MCDE_CHNL_B:
+ mcde_wfld(MCDE_CRB0, FLOEN, false);
+ wait_for_channel(chnl);
+ for (i = 0; i < MCDE_FLOWEN_MAX_TRIAL; i++) {
+ msleep(1);
+ if (!mcde_rfld(MCDE_CRB0, FLOEN)) {
+ dev_vdbg(&mcde_dev->dev,
+ "Flow (B) off after >= %d ms\n", i);
+ goto break_switch;
+ }
+ }
+ dev_warn(&mcde_dev->dev, "%s: channel B timeout\n",
+ __func__);
+ break;
+ case MCDE_CHNL_C0:
+ mcde_wfld(MCDE_CRC, C1EN, false);
+ wait_for_channel(chnl);
+ for (i = 0; i < MCDE_FLOWEN_MAX_TRIAL; i++) {
+ msleep(1);
+ if (!mcde_rfld(MCDE_CRC, C1EN)) {
+ dev_vdbg(&mcde_dev->dev,
+ "Flow (C1) off after >= %d ms\n", i);
+ goto break_switch;
+ }
+ }
+ dev_warn(&mcde_dev->dev, "%s: channel C0 timeout\n",
+ __func__);
+ break;
+ case MCDE_CHNL_C1:
+ mcde_wfld(MCDE_CRC, C2EN, false);
+ wait_for_channel(chnl);
+ for (i = 0; i < MCDE_FLOWEN_MAX_TRIAL; i++) {
+ msleep(1);
+ if (!mcde_rfld(MCDE_CRC, C2EN)) {
+ dev_vdbg(&mcde_dev->dev,
+ "Flow (C2) off after >= %d ms\n", i);
+ goto break_switch;
+ }
+ }
+ dev_warn(&mcde_dev->dev, "%s: channel C1 timeout\n",
+ __func__);
+ break;
+ }
+ }
+break_switch:
+ chnl->continous_running = false;
+}
+
+static void enable_channel(struct mcde_chnl_state *chnl)
+{
+ const struct mcde_port *port = &chnl->port;
+ int i;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ if (port->type == MCDE_PORTTYPE_DSI)
+ dsi_wfld(port->link, DSI_MCTL_MAIN_PHY_CTL, CLK_CONTINUOUS,
+ port->phy.dsi.clk_cont);
+
+ switch (chnl->id) {
+ case MCDE_CHNL_A:
+ mcde_wfld(MCDE_CRA0, FLOEN, true);
+ for (i = 0; i < MCDE_FLOWEN_MAX_TRIAL; i++) {
+ if (mcde_rfld(MCDE_CRA0, FLOEN)) {
+ dev_vdbg(&mcde_dev->dev,
+ "Flow (A) enable after >= %d ms\n", i);
+ return;
+ }
+ msleep(1);
+ }
+ break;
+ case MCDE_CHNL_B:
+ mcde_wfld(MCDE_CRB0, FLOEN, true);
+ for (i = 0; i < MCDE_FLOWEN_MAX_TRIAL; i++) {
+ if (mcde_rfld(MCDE_CRB0, FLOEN)) {
+ dev_vdbg(&mcde_dev->dev,
+ "Flow (B) enable after >= %d ms\n", i);
+ return;
+ }
+ msleep(1);
+ }
+ break;
+ case MCDE_CHNL_C0:
+ mcde_wfld(MCDE_CRC, C1EN, true);
+ for (i = 0; i < MCDE_FLOWEN_MAX_TRIAL; i++) {
+ if (mcde_rfld(MCDE_CRC, C1EN)) {
+ dev_vdbg(&mcde_dev->dev,
+ "Flow (C1) enable after >= %d ms\n", i);
+ return;
+ }
+ msleep(1);
+ }
+ mcde_wfld(MCDE_CRC, POWEREN, true);
+ break;
+ case MCDE_CHNL_C1:
+ mcde_wfld(MCDE_CRC, C2EN, true);
+ for (i = 0; i < MCDE_FLOWEN_MAX_TRIAL; i++) {
+ if (mcde_rfld(MCDE_CRC, C2EN)) {
+ dev_vdbg(&mcde_dev->dev,
+ "Flow (C2) enable after >= %d ms\n", i);
+ return;
+ }
+ msleep(1);
+ }
+ mcde_wfld(MCDE_CRC, POWEREN, true);
+ break;
+ }
+}
+#undef MCDE_FLOWEN_MAX_TRIAL
+
+static int is_channel_enabled(struct mcde_chnl_state *chnl)
+{
+ switch (chnl->id) {
+ case MCDE_CHNL_A:
+ return mcde_rfld(MCDE_CRA0, FLOEN);
+ case MCDE_CHNL_B:
+ return mcde_rfld(MCDE_CRB0, FLOEN);
+ case MCDE_CHNL_C0:
+ return mcde_rfld(MCDE_CRC, C1EN);
+ case MCDE_CHNL_C1:
+ return mcde_rfld(MCDE_CRC, C2EN);
+ }
+ return 0;
+}
+
+static void watchdog_auto_sync_timer_function(unsigned long arg)
+{
+ int i;
+ for (i = 0; i < num_channels; i++) {
+ struct mcde_chnl_state *chnl = &channels[i];
+ if (chnl->port.update_auto_trig &&
+ chnl->port.sync_src == MCDE_SYNCSRC_OFF &&
+ chnl->port.type == MCDE_PORTTYPE_DSI &&
+ chnl->continous_running) {
+ mcde_wreg(MCDE_CHNL0SYNCHSW +
+ chnl->id
+ * MCDE_CHNL0SYNCHSW_GROUPOFFSET,
+ MCDE_CHNL0SYNCHSW_SW_TRIG(true));
+ mod_timer(&chnl->auto_sync_timer,
+ jiffies +
+ msecs_to_jiffies(MCDE_AUTO_SYNC_WATCHDOG
+ * 1000));
+ }
+ }
+}
+
+static void work_sleep_function(struct work_struct *ptr)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+ if (mutex_trylock(&mcde_hw_lock) == 1) {
+ if (mcde_dynamic_power_management)
+ (void)disable_mcde_hw(false);
+ mutex_unlock(&mcde_hw_lock);
+ }
+}
+
+/* TODO get from register */
+#define MCDE_CLK_FREQ_MHZ 160
+
+void update_channel_registers(enum mcde_chnl chnl_id, struct chnl_regs *regs,
+ struct mcde_port *port, enum mcde_fifo fifo,
+ struct mcde_video_mode *video_mode)
+{
+ u8 idx = chnl_id;
+ u32 out_synch_src = MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_FORMATTER;
+ u32 src_synch = MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SOFTWARE;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ /* Channel */
+ if (port->update_auto_trig && port->type == MCDE_PORTTYPE_DSI) {
+ switch (port->sync_src) {
+ case MCDE_SYNCSRC_TE0:
+ out_synch_src = MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_VSYNC0;
+ src_synch = MCDE_CHNL0SYNCHMOD_SRC_SYNCH_OUTPUT;
+ break;
+ case MCDE_SYNCSRC_OFF:
+ src_synch = MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SOFTWARE;
+ break;
+ case MCDE_SYNCSRC_TE1:
+ default:
+ out_synch_src = MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_VSYNC1;
+ src_synch = MCDE_CHNL0SYNCHMOD_SRC_SYNCH_OUTPUT;
+ break;
+ case MCDE_SYNCSRC_TE_POLLING:
+ src_synch = MCDE_CHNL0SYNCHMOD_SRC_SYNCH_OUTPUT;
+ break;
+ }
+ } else if (port->type == MCDE_PORTTYPE_DPI) {
+ src_synch = port->update_auto_trig ?
+ MCDE_CHNL0SYNCHMOD_SRC_SYNCH_OUTPUT :
+ MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SOFTWARE;
+ }
+
+ mcde_wreg(MCDE_CHNL0CONF + idx * MCDE_CHNL0CONF_GROUPOFFSET,
+ MCDE_CHNL0CONF_PPL(regs->ppl-1) |
+ MCDE_CHNL0CONF_LPF(regs->lpf-1));
+ mcde_wreg(MCDE_CHNL0STAT + idx * MCDE_CHNL0STAT_GROUPOFFSET,
+ MCDE_CHNL0STAT_CHNLBLBCKGND_EN(false) |
+ MCDE_CHNL0STAT_CHNLRD(true));
+ mcde_wreg(MCDE_CHNL0SYNCHMOD +
+ idx * MCDE_CHNL0SYNCHMOD_GROUPOFFSET,
+ MCDE_CHNL0SYNCHMOD_SRC_SYNCH(src_synch) |
+ MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC(out_synch_src));
+ mcde_wreg(MCDE_CHNL0BCKGNDCOL + idx * MCDE_CHNL0BCKGNDCOL_GROUPOFFSET,
+ MCDE_CHNL0BCKGNDCOL_B(0) |
+ MCDE_CHNL0BCKGNDCOL_G(0) |
+ MCDE_CHNL0BCKGNDCOL_R(0));
+
+ if (chnl_id == MCDE_CHNL_A || chnl_id == MCDE_CHNL_B) {
+ u32 mcde_crx0;
+ u32 mcde_crx1;
+ u32 mcde_pal0x;
+ u32 mcde_pal1x;
+ if (chnl_id == MCDE_CHNL_A) {
+ mcde_crx0 = MCDE_CRA0;
+ mcde_crx1 = MCDE_CRA1;
+ mcde_pal0x = MCDE_PAL0A;
+ mcde_pal1x = MCDE_PAL1A;
+ } else {
+ mcde_crx0 = MCDE_CRB0;
+ mcde_crx1 = MCDE_CRB1;
+ mcde_pal0x = MCDE_PAL0B;
+ mcde_pal1x = MCDE_PAL1B;
+ }
+ mcde_wreg_fld(mcde_crx0, MCDE_CRA0_ROTEN_MASK,
+ MCDE_CRA0_ROTEN_SHIFT, regs->roten);
+ mcde_wreg_fld(mcde_crx0, MCDE_CRA0_PALEN_MASK,
+ MCDE_CRA0_PALEN_SHIFT, regs->palette_enable);
+ mcde_wreg(mcde_crx1,
+ MCDE_CRA1_PCD(regs->pcd) |
+ MCDE_CRA1_CLKSEL(regs->clksel) |
+ MCDE_CRA1_CDWIN(regs->cdwin) |
+ MCDE_CRA1_OUTBPP(bpp2outbpp(regs->bpp)) |
+ MCDE_CRA1_BCD(regs->bcd) |
+ MCDE_CRA1_CLKTYPE(regs->internal_clk)
+ );
+ if (regs->palette_enable) {
+ int i;
+ for (i = 0; i < 256; i++) {
+ mcde_wreg(mcde_pal0x,
+ MCDE_PAL0A_GREEN(regs->map_g(i)) |
+ MCDE_PAL0A_BLUE(regs->map_b(i)));
+ mcde_wreg(mcde_pal1x,
+ MCDE_PAL1A_RED(regs->map_r(i)));
+ }
+ }
+ }
+
+ /* Formatter */
+ if (port->type == MCDE_PORTTYPE_DSI) {
+ u8 fidx;
+ u32 temp, packet;
+ /* pkt_div is used to avoid underflow in output fifo for
+ * large packets */
+ u32 pkt_div = 1;
+ u32 dsi_delay0 = 0;
+ u32 screen_ppl, screen_lpf;
+
+ if (hardware_version == MCDE_CHIP_VERSION_1_0_4)
+ fidx = chnl_id;
+ else
+ fidx = 2 * port->link + port->ifc;
+
+ screen_ppl = video_mode->xres;
+ screen_lpf = video_mode->yres;
+
+ if (screen_ppl == SCREEN_PPL_HIGH) {
+ pkt_div = (screen_ppl - 1) /
+ get_output_fifo_size(fifo) + 1;
+ } else {
+ pkt_div = screen_ppl /
+ (get_output_fifo_size(fifo) * 2) + 1;
+ }
+
+ if (video_mode->interlaced)
+ screen_lpf /= 2;
+
+ /* pkt_delay_progressive = pixelclock * htot /
+ * (1E12 / 160E6) / pkt_div */
+ dsi_delay0 = (video_mode->pixclock + 1) *
+ (video_mode->xres + video_mode->hbp +
+ video_mode->hfp) /
+ (1000000 / MCDE_CLK_FREQ_MHZ) / pkt_div;
+
+ if ((screen_ppl == SCREEN_PPL_CEA2) &&
+ (screen_lpf == SCREEN_LPF_CEA2))
+ dsi_delay0 += DSI_DELAY0_CEA2_ADD;
+
+ temp = mcde_rreg(MCDE_DSIVID0CONF0 +
+ fidx * MCDE_DSIVID0CONF0_GROUPOFFSET);
+ mcde_wreg(MCDE_DSIVID0CONF0 +
+ fidx * MCDE_DSIVID0CONF0_GROUPOFFSET,
+ (temp & ~MCDE_DSIVID0CONF0_PACKING_MASK) |
+ MCDE_DSIVID0CONF0_PACKING(regs->dsipacking));
+ /* 1==CMD8 */
+ packet = ((screen_ppl / pkt_div * regs->bpp) >> 3) + 1;
+ mcde_wreg(MCDE_DSIVID0FRAME +
+ fidx * MCDE_DSIVID0FRAME_GROUPOFFSET,
+ MCDE_DSIVID0FRAME_FRAME(packet * pkt_div * screen_lpf));
+ mcde_wreg(MCDE_DSIVID0PKT + fidx * MCDE_DSIVID0PKT_GROUPOFFSET,
+ MCDE_DSIVID0PKT_PACKET(packet));
+ mcde_wreg(MCDE_DSIVID0SYNC +
+ fidx * MCDE_DSIVID0SYNC_GROUPOFFSET,
+ MCDE_DSIVID0SYNC_SW(0) |
+ MCDE_DSIVID0SYNC_DMA(0));
+ mcde_wreg(MCDE_DSIVID0CMDW +
+ fidx * MCDE_DSIVID0CMDW_GROUPOFFSET,
+ MCDE_DSIVID0CMDW_CMDW_START(DCS_CMD_WRITE_START) |
+ MCDE_DSIVID0CMDW_CMDW_CONTINUE(DCS_CMD_WRITE_CONTINUE));
+ mcde_wreg(MCDE_DSIVID0DELAY0 +
+ fidx * MCDE_DSIVID0DELAY0_GROUPOFFSET,
+ MCDE_DSIVID0DELAY0_INTPKTDEL(dsi_delay0));
+ mcde_wreg(MCDE_DSIVID0DELAY1 +
+ fidx * MCDE_DSIVID0DELAY1_GROUPOFFSET,
+ MCDE_DSIVID0DELAY1_TEREQDEL(0) |
+ MCDE_DSIVID0DELAY1_FRAMESTARTDEL(0));
+ }
+
+ if (regs->roten) {
+ /* TODO: Allocate memory in ESRAM instead of
+ static allocations. */
+ mcde_wreg(MCDE_ROTADD0A + chnl_id * MCDE_ROTADD0A_GROUPOFFSET,
+ regs->rotbuf1);
+ mcde_wreg(MCDE_ROTADD1A + chnl_id * MCDE_ROTADD1A_GROUPOFFSET,
+ regs->rotbuf2);
+
+ mcde_wreg(MCDE_ROTACONF + chnl_id * MCDE_ROTACONF_GROUPOFFSET,
+ MCDE_ROTACONF_ROTBURSTSIZE_ENUM(8W) |
+ MCDE_ROTACONF_ROTBURSTSIZE_HW(1) |
+ MCDE_ROTACONF_ROTDIR(regs->rotdir) |
+ MCDE_ROTACONF_STRIP_WIDTH_ENUM(16PIX) |
+ MCDE_ROTACONF_RD_MAXOUT_ENUM(4_REQ) |
+ MCDE_ROTACONF_WR_MAXOUT_ENUM(8_REQ));
+ }
+
+ /* Blending */
+ if (chnl_id == MCDE_CHNL_A) {
+ mcde_wfld(MCDE_CRA0, BLENDEN, regs->blend_en);
+ mcde_wfld(MCDE_CRA0, BLENDCTRL, regs->blend_ctrl);
+ mcde_wfld(MCDE_CRA0, ALPHABLEND, regs->alpha_blend);
+ } else if (chnl_id == MCDE_CHNL_B) {
+ mcde_wfld(MCDE_CRB0, BLENDEN, regs->blend_en);
+ mcde_wfld(MCDE_CRB0, BLENDCTRL, regs->blend_ctrl);
+ mcde_wfld(MCDE_CRB0, ALPHABLEND, regs->alpha_blend);
+ }
+
+ dev_vdbg(&mcde_dev->dev, "Channel registers setup, chnl=%d\n", chnl_id);
+}
+
+static int enable_mcde_hw(void)
+{
+ int ret;
+ int i;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ cancel_delayed_work(&hw_timeout_work);
+
+ schedule_delayed_work(&hw_timeout_work,
+ msecs_to_jiffies(MCDE_SLEEP_WATCHDOG));
+
+ if (mcde_is_enabled)
+ return 0;
+
+ ret = enable_clocks_and_power(mcde_dev);
+ if (ret < 0) {
+ dev_dbg(&mcde_dev->dev,
+ "%s: Enable clocks and power failed\n"
+ , __func__);
+ cancel_delayed_work(&hw_timeout_work);
+ return -EINVAL;
+ }
+
+ ret = request_irq(mcde_irq, mcde_irq_handler, 0, "mcde",
+ &mcde_dev->dev);
+ if (ret) {
+ dev_dbg(&mcde_dev->dev, "Failed to request irq (irq=%d)\n",
+ mcde_irq);
+ cancel_delayed_work(&hw_timeout_work);
+ return -EINVAL;
+ }
+
+ update_mcde_registers();
+
+ /* update hardware with same settings as before power down*/
+ for (i = 0; i < num_channels; i++) {
+ struct mcde_chnl_state *chnl = &channels[i];
+ if (chnl->enabled) {
+ if (chnl->ovly0 && chnl->ovly0->inuse &&
+ chnl->ovly0->regs.enabled)
+ chnl->ovly0->regs.update = true;
+ if (chnl->ovly1 && chnl->ovly1->inuse &&
+ chnl->ovly1->regs.enabled)
+ chnl->ovly1->regs.update = true;
+ chnl->transactionid++;
+ }
+ }
+
+ mcde_is_enabled = true;
+ return 0;
+}
+
+/* DSI */
+int mcde_dsi_dcs_write(struct mcde_chnl_state *chnl, u8 cmd, u8* data, int len)
+{
+ int i;
+ u32 wrdat[4] = { 0, 0, 0, 0 };
+ u32 settings;
+ u8 link = chnl->port.link;
+ u8 virt_id = chnl->port.phy.dsi.virt_id;
+ u32 ok;
+ u32 error;
+
+ /* REVIEW: One command at a time */
+ /* REVIEW: Allow read/write on unreserved ports */
+ if (len > MCDE_MAX_DCS_WRITE || chnl->port.type != MCDE_PORTTYPE_DSI)
+ return -EINVAL;
+
+ mutex_lock(&mcde_hw_lock);
+
+ if (enable_mcde_hw()) {
+ mutex_unlock(&mcde_hw_lock);
+ return -EINVAL;
+ }
+ if (!chnl->formatter_updated)
+ (void)update_channel_static_registers(chnl);
+
+ wait_for_channel(chnl);
+ if (chnl->port.type == MCDE_PORTTYPE_DSI) {
+ u8 counter = 10;
+ /*
+ * Wait for CSM_RUNNING,
+ * all data sent for display
+ */
+ while (dsi_rfld(chnl->port.link,
+ DSI_CMD_MODE_STS, CSM_RUNNING)
+ && counter--) {
+ cpu_relax();
+ udelay(100);
+ }
+ }
+
+ wrdat[0] = cmd;
+ for (i = 1; i <= len; i++)
+ wrdat[i>>2] |= ((u32)data[i-1] << ((i & 3) * 8));
+
+ settings = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_ENUM(WRITE) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT(len > 1) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID(virt_id) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE(len+1) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN(true);
+ if (len == 0)
+ settings |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM(
+ DCS_SHORT_WRITE_0);
+ else if (len == 1)
+ settings |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM(
+ DCS_SHORT_WRITE_1);
+ else
+ settings |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM(
+ DCS_LONG_WRITE);
+
+ dsi_wreg(link, DSI_DIRECT_CMD_MAIN_SETTINGS, settings);
+ dsi_wreg(link, DSI_DIRECT_CMD_WRDAT0, wrdat[0]);
+ if (len > 3)
+ dsi_wreg(link, DSI_DIRECT_CMD_WRDAT1, wrdat[1]);
+ if (len > 7)
+ dsi_wreg(link, DSI_DIRECT_CMD_WRDAT2, wrdat[2]);
+ if (len > 11)
+ dsi_wreg(link, DSI_DIRECT_CMD_WRDAT3, wrdat[3]);
+ dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR, ~0);
+ dsi_wreg(link, DSI_DIRECT_CMD_SEND, true);
+
+ /* TODO: irq wait and error check */
+ mdelay(10);
+
+ ok = dsi_rreg(link, DSI_DIRECT_CMD_STS);
+ error = dsi_rreg(link, DSI_CMD_MODE_STS);
+ dev_vdbg(&mcde_dev->dev, "DSI Write ok %x error %x\n", ok, error);
+
+ dsi_wreg(link, DSI_CMD_MODE_STS_CLR, ~0);
+ dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR, ~0);
+
+ mutex_unlock(&mcde_hw_lock);
+
+ return 0;
+}
+
+int mcde_dsi_dcs_read(struct mcde_chnl_state *chnl, u8 cmd, u8* data, int *len)
+{
+ int ret = 0;
+ u8 link = chnl->port.link;
+ u8 virt_id = chnl->port.phy.dsi.virt_id;
+ u32 settings;
+ int wait = 100;
+ bool error, ok;
+
+ if (*len > MCDE_MAX_DCS_READ || chnl->port.type != MCDE_PORTTYPE_DSI)
+ return -EINVAL;
+
+ mutex_lock(&mcde_hw_lock);
+
+ if (enable_mcde_hw()) {
+ mutex_unlock(&mcde_hw_lock);
+ return -EINVAL;
+ }
+ if (!chnl->formatter_updated)
+ (void)update_channel_static_registers(chnl);
+
+ wait_for_channel(chnl);
+ if (chnl->port.type == MCDE_PORTTYPE_DSI) {
+ u8 counter = 10;
+ /*
+ * Wait for CSM_RUNNING,
+ * all data sent for display
+ */
+ while (dsi_rfld(chnl->port.link,
+ DSI_CMD_MODE_STS, CSM_RUNNING)
+ && counter--) {
+ cpu_relax();
+ udelay(100);
+ }
+ }
+
+ dsi_wfld(link, DSI_MCTL_MAIN_DATA_CTL, BTA_EN, true);
+ dsi_wfld(link, DSI_MCTL_MAIN_DATA_CTL, READ_EN, true);
+ settings = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_ENUM(READ) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT(false) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID(virt_id) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE(1) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN(true) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM(DCS_READ);
+ dsi_wreg(link, DSI_DIRECT_CMD_MAIN_SETTINGS, settings);
+ dsi_wreg(link, DSI_DIRECT_CMD_WRDAT0, cmd);
+ dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR, ~0);
+ dsi_wreg(link, DSI_DIRECT_CMD_RD_STS_CLR, ~0);
+ dsi_wreg(link, DSI_DIRECT_CMD_SEND, true);
+
+ /* TODO */
+ while (wait-- && !(error = dsi_rfld(link, DSI_DIRECT_CMD_STS,
+ READ_COMPLETED_WITH_ERR)) && !(ok = dsi_rfld(link,
+ DSI_DIRECT_CMD_STS, READ_COMPLETED)))
+ mdelay(10);
+
+ if (ok) {
+ int rdsize;
+ u32 rddat;
+
+ rdsize = dsi_rfld(link, DSI_DIRECT_CMD_RD_PROPERTY, RD_SIZE);
+ rddat = dsi_rreg(link, DSI_DIRECT_CMD_RDDAT);
+ if (rdsize < *len)
+ pr_err("DCS incomplete read %d<%d (%.8X)\n",
+ rdsize, *len, rddat);/* REVIEW: dev_dbg */
+ *len = min(*len, rdsize);
+ memcpy(data, &rddat, *len);
+ } else {
+ pr_err("DCS read failed, err=%d, sts=%X\n",
+ error, dsi_rreg(link, DSI_DIRECT_CMD_STS));
+ ret = -EIO;
+ }
+
+ dsi_wreg(link, DSI_CMD_MODE_STS_CLR, ~0);
+ dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR, ~0);
+
+ mutex_unlock(&mcde_hw_lock);
+
+ return ret;
+}
+
+static void dsi_te_poll_req(struct mcde_chnl_state *chnl)
+{
+ u8 lnk = chnl->port.link;
+ const struct mcde_port *port = &chnl->port;
+
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, REG_TE_EN, false);
+ if (port->ifc == 0)
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, IF1_TE_EN, true);
+ if (port->ifc == 1)
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, IF2_TE_EN, true);
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, BTA_EN, true);
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, READ_EN, true);
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, HOST_EOT_GEN, true);
+ dsi_wfld(lnk, DSI_CMD_MODE_CTL, TE_TIMEOUT, 0x3FF);
+ dsi_wfld(lnk, DSI_MCTL_MAIN_DATA_CTL, TE_POLLING_EN, true);
+}
+
+static void dsi_te_poll_set_timer(struct mcde_chnl_state *chnl,
+ unsigned int timeout)
+{
+ mod_timer(&chnl->dsi_te_timer,
+ jiffies +
+ msecs_to_jiffies(timeout));
+}
+
+static void dsi_te_timer_function(unsigned long arg)
+{
+ struct mcde_chnl_state *chnl;
+ u8 lnk;
+
+ if (arg >= num_channels) {
+ dev_err(&mcde_dev->dev, "%s invalid arg:%ld\n", __func__, arg);
+ return;
+ }
+
+ chnl = &channels[arg];
+
+ if (mcde_is_enabled && chnl->enabled && chnl->formatter_updated) {
+ lnk = chnl->port.link;
+ /* No TE answer; force stop */
+ dsi_wfld(lnk, DSI_MCTL_MAIN_PHY_CTL, FORCE_STOP_MODE, true);
+ udelay(20);
+ dsi_wfld(lnk, DSI_MCTL_MAIN_PHY_CTL, FORCE_STOP_MODE, false);
+ dev_info(&mcde_dev->dev, "DSI%d force stop\n", lnk);
+ dsi_te_poll_set_timer(chnl, DSI_TE_NO_ANSWER_TIMEOUT);
+ } else {
+ dev_info(&mcde_dev->dev, "1:DSI force stop\n");
+ }
+}
+
+static void dsi_te_request(struct mcde_chnl_state *chnl)
+{
+ u8 link = chnl->port.link;
+ u8 virt_id = chnl->port.phy.dsi.virt_id;
+ u32 settings;
+
+ dev_vdbg(&mcde_dev->dev, "Request BTA TE, chnl=%d\n",
+ chnl->id);
+
+ dsi_wfld(link, DSI_MCTL_MAIN_DATA_CTL, BTA_EN, true);
+ dsi_wfld(link, DSI_MCTL_MAIN_DATA_CTL, REG_TE_EN, true);
+ dsi_wfld(link, DSI_CMD_MODE_CTL, TE_TIMEOUT, 0x3FF);
+ settings = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_ENUM(TE_REQ) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT(false) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID(virt_id) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE(2) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN(true) |
+ DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_ENUM(DCS_SHORT_WRITE_1);
+ dsi_wreg(link, DSI_DIRECT_CMD_MAIN_SETTINGS, settings);
+ dsi_wreg(link, DSI_DIRECT_CMD_WRDAT0, DCS_CMD_SET_TEAR_ON);
+ dsi_wreg(link, DSI_DIRECT_CMD_STS_CLR,
+ DSI_DIRECT_CMD_STS_CLR_TE_RECEIVED_CLR(true));
+ dsi_wfld(link, DSI_DIRECT_CMD_STS_CTL, TE_RECEIVED_EN, true);
+ dsi_wreg(link, DSI_CMD_MODE_STS_CLR,
+ DSI_CMD_MODE_STS_CLR_ERR_NO_TE_CLR(true));
+ dsi_wfld(link, DSI_CMD_MODE_STS_CTL, ERR_NO_TE_EN, true);
+ dsi_wreg(link, DSI_DIRECT_CMD_SEND, true);
+}
+
+/* MCDE channels */
+static struct mcde_chnl_state *_mcde_chnl_get(enum mcde_chnl chnl_id,
+ enum mcde_fifo fifo, const struct mcde_port *port)
+{
+ int i;
+ struct mcde_chnl_state *chnl = NULL;
+ enum mcde_chnl_path path;
+ const struct chnl_config *cfg = NULL;
+
+ /* Allocate channel */
+ for (i = 0; i < num_channels; i++) {
+ if (chnl_id == channels[i].id)
+ chnl = &channels[i];
+ }
+ if (!chnl) {
+ dev_dbg(&mcde_dev->dev, "Invalid channel, chnl=%d\n", chnl_id);
+ return ERR_PTR(-EINVAL);
+ }
+ if (chnl->reserved) {
+ dev_dbg(&mcde_dev->dev, "Channel in use, chnl=%d\n", chnl_id);
+ return ERR_PTR(-EBUSY);
+ }
+
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_5) {
+ path = MCDE_CHNLPATH(chnl->id, fifo, port->type,
+ port->ifc, port->link);
+ for (i = 0; i < ARRAY_SIZE(chnl_configs); i++)
+ if (chnl_configs[i].path == path) {
+ cfg = &chnl_configs[i];
+ break;
+ }
+ if (cfg == NULL) {
+ dev_dbg(&mcde_dev->dev, "Invalid config, chnl=%d,"
+ " path=0x%.8X\n", chnl_id, path);
+ return ERR_PTR(-EINVAL);
+ } else {
+ dev_info(&mcde_dev->dev, "Config, chnl=%d,"
+ " path=0x%.8X\n", chnl_id, path);
+ }
+ }
+ /* TODO: verify that cfg is ok to activate (check other chnl cfgs) */
+
+ chnl->cfg = cfg;
+ chnl->port = *port;
+ chnl->fifo = fifo;
+ chnl->formatter_updated = false;
+
+ chnl->blend_en = true;
+ chnl->blend_ctrl = MCDE_CRA0_BLENDCTRL_SOURCE;
+ chnl->alpha_blend = 0xFF;
+
+ _mcde_chnl_apply(chnl);
+ chnl->reserved = true;
+
+ if (chnl->port.type == MCDE_PORTTYPE_DSI) {
+#ifdef CONFIG_REGULATOR
+ chnl->port.phy.dsi.reg_vana = regulator_vana;
+#else
+ chnl->port.phy.dsi.reg_vana = NULL;
+#endif
+ chnl->port.phy.dsi.clk_dsi = clock_dsi;
+ chnl->port.phy.dsi.clk_dsi_lp = clock_dsi_lp;
+ enable_dsi++;
+ if (!dsi_is_enabled && mcde_is_enabled) {
+ struct mcde_platform_data *pdata =
+ mcde_dev->dev.platform_data;
+ pdata->platform_enable_dsipll();
+ dsi_is_enabled = true;
+ }
+ } else if (chnl->port.type == MCDE_PORTTYPE_DPI) {
+ chnl->port.phy.dpi.clk_dpi = clock_dpi;
+ if (chnl->port.phy.dpi.tv_mode)
+ chnl->vcmp_per_field = true;
+ }
+
+#ifdef CONFIG_REGULATOR
+ chnl->port.reg_esram = regulator_esram_epod;
+#else
+ chnl->port.reg_esram = NULL;
+#endif
+
+ return chnl;
+}
+
+static int _mcde_chnl_apply(struct mcde_chnl_state *chnl)
+{
+ bool roten = false;
+ u8 rotdir = 0;
+
+ if (chnl->rotation == MCDE_DISPLAY_ROT_90_CCW) {
+ roten = true;
+ rotdir = MCDE_ROTACONF_ROTDIR_CCW;
+ } else if (chnl->rotation == MCDE_DISPLAY_ROT_90_CW) {
+ roten = true;
+ rotdir = MCDE_ROTACONF_ROTDIR_CW;
+ }
+ /* REVIEW: 180 deg? */
+
+ chnl->regs.bpp = portfmt2bpp(chnl->port.pixel_format);
+ chnl->regs.synchronized_update = chnl->synchronized_update;
+ chnl->regs.roten = roten;
+ chnl->regs.rotdir = rotdir;
+ chnl->regs.rotbuf1 = chnl->rotbuf1;
+ chnl->regs.rotbuf2 = chnl->rotbuf2;
+ chnl->regs.palette_enable = chnl->palette_enable;
+ chnl->regs.map_r = chnl->map_r;
+ chnl->regs.map_g = chnl->map_g;
+ chnl->regs.map_b = chnl->map_b;
+ if (chnl->port.type == MCDE_PORTTYPE_DSI) {
+ chnl->regs.clksel = MCDE_CRA1_CLKSEL_166MHZ;
+ chnl->regs.dsipacking =
+ portfmt2dsipacking(chnl->port.pixel_format);
+ } else if (chnl->port.type == MCDE_PORTTYPE_DPI) {
+ if (chnl->port.phy.dpi.tv_mode) {
+ chnl->regs.internal_clk = false;
+ chnl->regs.bcd = true;
+ if (chnl->id == MCDE_CHNL_A)
+ chnl->regs.clksel = MCDE_CRA1_CLKSEL_EXT_TV1;
+ else
+ chnl->regs.clksel = MCDE_CRA1_CLKSEL_EXT_TV2;
+ } else {
+ chnl->regs.internal_clk = true;
+ chnl->regs.clksel = MCDE_CRA1_CLKSEL_LCD;
+ chnl->regs.cdwin =
+ portfmt2cdwin(chnl->port.pixel_format);
+ chnl->regs.bcd = (chnl->port.phy.dpi.clock_div < 2);
+ if (!chnl->regs.bcd)
+ chnl->regs.pcd =
+ chnl->port.phy.dpi.clock_div - 2;
+ }
+ dpi_video_mode_apply(chnl);
+ }
+
+ chnl->regs.blend_ctrl = chnl->blend_ctrl;
+ chnl->regs.blend_en = chnl->blend_en;
+ chnl->regs.alpha_blend = chnl->alpha_blend;
+
+ chnl->transactionid++;
+
+ dev_vdbg(&mcde_dev->dev, "Channel applied, chnl=%d\n", chnl->id);
+ return 0;
+}
+
+static void chnl_update_registers(struct mcde_chnl_state *chnl)
+{
+ /* REVIEW: Move content to update_channel_register */
+ /* and remove this one */
+ if (chnl->port.type == MCDE_PORTTYPE_DPI)
+ update_dpi_registers(chnl->id, &chnl->tv_regs);
+ if (chnl->id == MCDE_CHNL_A || chnl->id == MCDE_CHNL_B)
+ update_col_registers(chnl->id, &chnl->col_regs);
+ update_channel_registers(chnl->id, &chnl->regs, &chnl->port,
+ chnl->fifo, &chnl->vmode);
+
+ chnl->transactionid_regs = chnl->transactionid;
+}
+
+static void chnl_update_continous(struct mcde_chnl_state *chnl,
+ bool tripple_buffer)
+{
+
+ if (chnl->continous_running) {
+ chnl->transactionid_regs = chnl->transactionid;
+ if (!tripple_buffer)
+ wait_for_channel(chnl);
+ }
+
+ if (!chnl->continous_running) {
+ if (chnl->transactionid_regs < chnl->transactionid)
+ chnl_update_registers(chnl);
+ if (chnl->port.sync_src == MCDE_SYNCSRC_TE0) {
+ mcde_wfld(MCDE_CRC, SYCEN0, true);
+ } else if (chnl->port.sync_src == MCDE_SYNCSRC_TE1) {
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_8) {
+ mcde_wfld(MCDE_VSCRC1, VSSEL, 1);
+ mcde_wfld(MCDE_CRC, SYCEN1, true);
+ } else {
+ mcde_wfld(MCDE_VSCRC1, VSSEL, 0);
+ mcde_wfld(MCDE_CRC, SYCEN0, true);
+ }
+ }
+ chnl->continous_running = true;
+
+ /*
+ * For main and secondary display,
+ * FLOWEN has to be set before a SOFTWARE TRIG
+ * Otherwise not overlay interrupt is triggerd
+ */
+ enable_channel(chnl);
+
+ if (chnl->port.type == MCDE_PORTTYPE_DSI &&
+ chnl->port.sync_src == MCDE_SYNCSRC_OFF) {
+ chnl->disable_software_trig = false;
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_8) {
+ mcde_wreg(MCDE_CHNL0SYNCHSW +
+ chnl->id * MCDE_CHNL0SYNCHSW_GROUPOFFSET,
+ MCDE_CHNL0SYNCHSW_SW_TRIG(true));
+ disable_flow(chnl);
+ }
+ mod_timer(&chnl->auto_sync_timer,
+ jiffies +
+ msecs_to_jiffies(MCDE_AUTO_SYNC_WATCHDOG * 1000));
+ }
+ }
+}
+
+static void chnl_update_non_continous(struct mcde_chnl_state *chnl)
+{
+ /* Commit settings to registers */
+ wait_for_channel(chnl);
+ if (chnl->transactionid_regs < chnl->transactionid)
+ chnl_update_registers(chnl);
+
+ /* TODO: look at port sync source and synched_update */
+ if (chnl->regs.synchronized_update &&
+ chnl->power_mode == MCDE_DISPLAY_PM_ON) {
+ if (chnl->port.type == MCDE_PORTTYPE_DSI &&
+ chnl->port.sync_src == MCDE_SYNCSRC_BTA) {
+ if (hardware_version == MCDE_CHIP_VERSION_3_0_8)
+ enable_channel(chnl);
+ while (dsi_rfld(chnl->port.link, DSI_CMD_MODE_STS,
+ CSM_RUNNING)) {
+ cpu_relax();
+ udelay(100);
+ }
+ dsi_te_request(chnl);
+ }
+ } else {
+ do_softwaretrig(chnl);
+ dev_vdbg(&mcde_dev->dev, "Channel update (no sync), chnl=%d\n",
+ chnl->id);
+ }
+
+}
+
+static void chnl_update_overlay(struct mcde_chnl_state *chnl,
+ struct mcde_ovly_state *ovly)
+{
+ if (!ovly)
+ return;
+
+ update_overlay_registers_on_the_fly(ovly->idx, &ovly->regs);
+ if (ovly->regs.update) {
+ update_overlay_registers(ovly->idx, &ovly->regs, &chnl->port,
+ chnl->fifo, chnl->regs.x, chnl->regs.y,
+ chnl->regs.ppl, chnl->regs.lpf, ovly->stride,
+ chnl->vmode.interlaced, chnl->rotation);
+ }
+}
+
+static int _mcde_chnl_update(struct mcde_chnl_state *chnl,
+ struct mcde_rectangle *update_area,
+ bool tripple_buffer)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ /* TODO: lock & make wait->trig async */
+ if (!chnl->enabled || !update_area
+ || (update_area->w == 0 && update_area->h == 0)) {
+ return -EINVAL;
+ }
+
+ if (chnl->port.update_auto_trig && tripple_buffer)
+ wait_for_channel(chnl);
+
+ chnl->regs.x = update_area->x;
+ chnl->regs.y = update_area->y;
+ /* TODO Crop against video_mode.xres and video_mode.yres */
+ chnl->regs.ppl = update_area->w;
+ chnl->regs.lpf = update_area->h;
+ if (chnl->port.type == MCDE_PORTTYPE_DPI &&
+ chnl->port.phy.dpi.tv_mode) {
+ /* subtract border */
+ chnl->regs.ppl -= chnl->tv_regs.dho + chnl->tv_regs.alw;
+ /* subtract double borders, ie. for both fields */
+ chnl->regs.lpf -= 2 * (chnl->tv_regs.dvo + chnl->tv_regs.bsl);
+ } else if (chnl->port.type == MCDE_PORTTYPE_DSI &&
+ chnl->vmode.interlaced)
+ chnl->regs.lpf /= 2;
+
+ chnl_update_overlay(chnl, chnl->ovly0);
+ chnl_update_overlay(chnl, chnl->ovly1);
+
+ if (chnl->port.update_auto_trig)
+ chnl_update_continous(chnl, tripple_buffer);
+ else
+ chnl_update_non_continous(chnl);
+
+ dev_vdbg(&mcde_dev->dev, "Channel updated, chnl=%d\n", chnl->id);
+ return 0;
+}
+
+/* API entry points */
+/* MCDE channels */
+struct mcde_chnl_state *mcde_chnl_get(enum mcde_chnl chnl_id,
+ enum mcde_fifo fifo, const struct mcde_port *port)
+{
+ struct mcde_chnl_state *chnl;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+ chnl = _mcde_chnl_get(chnl_id, fifo, port);
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+
+ return chnl;
+}
+
+int mcde_chnl_set_pixel_format(struct mcde_chnl_state *chnl,
+ enum mcde_port_pix_fmt pix_fmt)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ if (!chnl->reserved)
+ return -EINVAL;
+ chnl->port.pixel_format = pix_fmt;
+
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+
+ return 0;
+}
+
+int mcde_chnl_set_palette(struct mcde_chnl_state *chnl,
+ struct mcde_palette_table *palette)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ if (!chnl->reserved)
+ return -EINVAL;
+ if (palette != NULL) {
+ chnl->map_r = palette->map_col_ch0;
+ chnl->map_g = palette->map_col_ch1;
+ chnl->map_b = palette->map_col_ch2;
+ chnl->palette_enable = true;
+ } else {
+ chnl->map_r = NULL;
+ chnl->map_g = NULL;
+ chnl->map_b = NULL;
+ chnl->palette_enable = false;
+ }
+
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+ return 0;
+}
+
+void mcde_chnl_set_col_convert(struct mcde_chnl_state *chnl,
+ struct mcde_col_convert *col_convert)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ chnl->col_regs.y_red = col_convert->matrix[0][0];
+ chnl->col_regs.y_green = col_convert->matrix[0][1];
+ chnl->col_regs.y_blue = col_convert->matrix[0][2];
+ chnl->col_regs.cb_red = col_convert->matrix[1][0];
+ chnl->col_regs.cb_green = col_convert->matrix[1][1];
+ chnl->col_regs.cb_blue = col_convert->matrix[1][2];
+ chnl->col_regs.cr_red = col_convert->matrix[2][0];
+ chnl->col_regs.cr_green = col_convert->matrix[2][1];
+ chnl->col_regs.cr_blue = col_convert->matrix[2][2];
+ chnl->col_regs.off_y = col_convert->offset[0];
+ chnl->col_regs.off_cb = col_convert->offset[1];
+ chnl->col_regs.off_cr = col_convert->offset[2];
+
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+}
+
+int mcde_chnl_set_video_mode(struct mcde_chnl_state *chnl,
+ struct mcde_video_mode *vmode)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ if (chnl == NULL || vmode == NULL)
+ return -EINVAL;
+
+ chnl->vmode = *vmode;
+
+ if (chnl->ovly0)
+ chnl->ovly0->update = true;
+ if (chnl->ovly1)
+ chnl->ovly1->update = true;
+
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+
+ return 0;
+}
+EXPORT_SYMBOL(mcde_chnl_set_video_mode);
+
+int mcde_chnl_set_rotation(struct mcde_chnl_state *chnl,
+ enum mcde_display_rotation rotation, u32 rotbuf1, u32 rotbuf2)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ if (!chnl->reserved)
+ return -EINVAL;
+
+ if (chnl->id != MCDE_CHNL_A && chnl->id != MCDE_CHNL_B)
+ return -EINVAL;
+
+ chnl->rotation = rotation;
+ chnl->rotbuf1 = rotbuf1;
+ chnl->rotbuf2 = rotbuf2;
+
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+
+ return 0;
+}
+
+int mcde_chnl_enable_synchronized_update(struct mcde_chnl_state *chnl,
+ bool enable)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+ if (!chnl->reserved)
+ return -EINVAL;
+
+ chnl->synchronized_update = enable;
+
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+
+ return 0;
+}
+
+int mcde_chnl_set_power_mode(struct mcde_chnl_state *chnl,
+ enum mcde_display_power_mode power_mode)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ if (!chnl->reserved)
+ return -EINVAL;
+
+ chnl->power_mode = power_mode;
+
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+
+ return 0;
+}
+
+int mcde_chnl_apply(struct mcde_chnl_state *chnl)
+{
+ int ret ;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ if (!chnl->reserved)
+ return -EINVAL;
+
+ mutex_lock(&mcde_hw_lock);
+ ret = _mcde_chnl_apply(chnl);
+ mutex_unlock(&mcde_hw_lock);
+
+ dev_vdbg(&mcde_dev->dev, "%s exit with ret %d\n", __func__, ret);
+
+ return ret;
+}
+
+int mcde_chnl_update(struct mcde_chnl_state *chnl,
+ struct mcde_rectangle *update_area,
+ bool tripple_buffer)
+{
+ int ret;
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ if (!chnl->reserved)
+ return -EINVAL;
+
+ mutex_lock(&mcde_hw_lock);
+ enable_mcde_hw();
+ if (!chnl->formatter_updated)
+ (void)update_channel_static_registers(chnl);
+
+ if (chnl->regs.roten && !chnl->esram_is_enabled) {
+ ret = regulator_enable(chnl->port.reg_esram);
+ if (ret < 0) {
+ dev_warn(&mcde_dev->dev, "%s: disable failed\n",
+ __func__);
+ }
+ chnl->esram_is_enabled = true;
+ }
+
+ ret = _mcde_chnl_update(chnl, update_area, tripple_buffer);
+
+ mutex_unlock(&mcde_hw_lock);
+
+ dev_vdbg(&mcde_dev->dev, "%s exit with ret %d\n", __func__, ret);
+
+ return ret;
+}
+
+void mcde_chnl_put(struct mcde_chnl_state *chnl)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ chnl->reserved = false;
+ chnl->port.reg_esram = NULL;
+ if (chnl->port.type == MCDE_PORTTYPE_DSI) {
+ chnl->port.phy.dsi.reg_vana = NULL;
+ chnl->port.phy.dsi.clk_dsi = NULL;
+ chnl->port.phy.dsi.clk_dsi_lp = NULL;
+ enable_dsi--;
+ if (dsi_is_enabled && enable_dsi == 0) {
+ struct mcde_platform_data *pdata =
+ mcde_dev->dev.platform_data;
+ pdata->platform_disable_dsipll();
+ dsi_is_enabled = false;
+ }
+ } else if (chnl->port.type == MCDE_PORTTYPE_DPI) {
+ chnl->port.phy.dpi.clk_dpi = NULL;
+ if (chnl->port.phy.dpi.tv_mode) {
+ chnl->vcmp_per_field = false;
+ chnl->even_vcmp = false;
+ }
+ }
+
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+}
+
+void mcde_chnl_stop_flow(struct mcde_chnl_state *chnl)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ mutex_lock(&mcde_hw_lock);
+ if (mcde_is_enabled && chnl->enabled)
+ disable_channel(chnl);
+ mutex_unlock(&mcde_hw_lock);
+
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+}
+
+void mcde_chnl_enable(struct mcde_chnl_state *chnl)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ mutex_lock(&mcde_hw_lock);
+ chnl->enabled = true;
+ mutex_unlock(&mcde_hw_lock);
+
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+}
+
+void mcde_chnl_disable(struct mcde_chnl_state *chnl)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ mutex_lock(&mcde_hw_lock);
+ cancel_delayed_work(&hw_timeout_work);
+ /* This channel is disabled here */
+ chnl->enabled = false;
+ if (mcde_is_enabled && chnl->formatter_updated
+ && chnl->port.type == MCDE_PORTTYPE_DSI) {
+ u8 counter = 10;
+ /*
+ * Wait for CSM_RUNNING,
+ * all data sent for display
+ */
+ while (dsi_rfld(chnl->port.link,
+ DSI_CMD_MODE_STS, CSM_RUNNING)
+ && counter--) {
+ cpu_relax();
+ udelay(100);
+ }
+ }
+ if (chnl->formatter_updated) {
+ disable_formatter(&chnl->port);
+ chnl->formatter_updated = false;
+ }
+ if (chnl->esram_is_enabled) {
+ int ret;
+ ret = regulator_disable(chnl->port.reg_esram);
+ if (ret < 0) {
+ dev_warn(&mcde_dev->dev, "%s: disable failed\n",
+ __func__);
+ }
+ chnl->esram_is_enabled = false;
+ }
+ del_timer(&chnl->dsi_te_timer);
+ del_timer(&chnl->auto_sync_timer);
+ /*
+ * Check if other channels can be disabled and
+ * if the hardware can be shutdown
+ */
+ (void)disable_mcde_hw(false);
+ mutex_unlock(&mcde_hw_lock);
+
+ dev_vdbg(&mcde_dev->dev, "%s exit\n", __func__);
+}
+
+/* MCDE overlays */
+struct mcde_ovly_state *mcde_ovly_get(struct mcde_chnl_state *chnl)
+{
+ struct mcde_ovly_state *ovly;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ if (!chnl->reserved)
+ return ERR_PTR(-EINVAL);
+
+ if (!chnl->ovly0->inuse)
+ ovly = chnl->ovly0;
+ else if (chnl->ovly1 && !chnl->ovly1->inuse)
+ ovly = chnl->ovly1;
+ else
+ ovly = ERR_PTR(-EBUSY);
+
+ if (!IS_ERR(ovly)) {
+ ovly->inuse = true;
+ ovly->paddr = 0;
+ ovly->stride = 0;
+ ovly->pix_fmt = MCDE_OVLYPIXFMT_RGB565;
+ ovly->src_x = 0;
+ ovly->src_y = 0;
+ ovly->dst_x = 0;
+ ovly->dst_y = 0;
+ ovly->dst_z = 0;
+ ovly->w = 0;
+ ovly->h = 0;
+ ovly->alpha_value = 0xFF;
+ ovly->alpha_source = MCDE_OVL1CONF2_BP_PER_PIXEL_ALPHA;
+ ovly->update = true;
+ mcde_ovly_apply(ovly);
+ }
+
+ return ovly;
+}
+
+void mcde_ovly_put(struct mcde_ovly_state *ovly)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ if (!ovly->inuse)
+ return;
+ if (ovly->regs.enabled) {
+ ovly->paddr = 0;
+ ovly->update = true;
+ mcde_ovly_apply(ovly);/* REVIEW: API call calling API call! */
+ }
+ ovly->inuse = false;
+}
+
+void mcde_ovly_set_source_buf(struct mcde_ovly_state *ovly, u32 paddr)
+{
+ if (!ovly->inuse)
+ return;
+
+ if (paddr == 0 || ovly->paddr == 0)
+ ovly->update = true;
+
+ ovly->paddr = paddr;
+}
+
+void mcde_ovly_set_source_info(struct mcde_ovly_state *ovly,
+ u32 stride, enum mcde_ovly_pix_fmt pix_fmt)
+{
+ if (!ovly->inuse)
+ return;
+
+ ovly->stride = stride;
+ ovly->pix_fmt = pix_fmt;
+ ovly->update = true;
+}
+
+void mcde_ovly_set_source_area(struct mcde_ovly_state *ovly,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ if (!ovly->inuse)
+ return;
+
+ ovly->src_x = x;
+ ovly->src_y = y;
+ ovly->w = w;
+ ovly->h = h;
+ ovly->update = true;
+}
+
+void mcde_ovly_set_dest_pos(struct mcde_ovly_state *ovly, u16 x, u16 y, u8 z)
+{
+ if (!ovly->inuse)
+ return;
+
+ ovly->dst_x = x;
+ ovly->dst_y = y;
+ ovly->dst_z = z;
+}
+
+void mcde_ovly_apply(struct mcde_ovly_state *ovly)
+{
+ if (!ovly->inuse)
+ return;
+
+ mutex_lock(&mcde_hw_lock);
+
+ ovly->regs.ch_id = ovly->chnl->id;
+ ovly->regs.enabled = ovly->paddr != 0;
+ ovly->regs.baseaddress0 = ovly->paddr;
+ ovly->regs.baseaddress1 = ovly->regs.baseaddress0 + ovly->stride;
+ /*TODO set to true if interlaced *//* REVIEW: Video mode interlaced? */
+ if (!ovly->regs.update)
+ ovly->regs.update = ovly->update;
+ ovly->update = false;
+
+ switch (ovly->pix_fmt) {/* REVIEW: Extract to table */
+ case MCDE_OVLYPIXFMT_RGB565:
+ ovly->regs.bits_per_pixel = 16;
+ ovly->regs.bpp = MCDE_EXTSRC0CONF_BPP_RGB565;
+ ovly->regs.bgr = false;
+ ovly->regs.bebo = false;
+ ovly->regs.opq = true;
+ break;
+ case MCDE_OVLYPIXFMT_RGBA5551:
+ ovly->regs.bits_per_pixel = 16;
+ ovly->regs.bpp = MCDE_EXTSRC0CONF_BPP_IRGB1555;
+ ovly->regs.bgr = false;
+ ovly->regs.bebo = false;
+ ovly->regs.opq = false;
+ break;
+ case MCDE_OVLYPIXFMT_RGBA4444:
+ ovly->regs.bits_per_pixel = 16;
+ ovly->regs.bpp = MCDE_EXTSRC0CONF_BPP_ARGB4444;
+ ovly->regs.bgr = false;
+ ovly->regs.bebo = false;
+ ovly->regs.opq = false;
+ break;
+ case MCDE_OVLYPIXFMT_RGB888:
+ ovly->regs.bits_per_pixel = 24;
+ ovly->regs.bpp = MCDE_EXTSRC0CONF_BPP_RGB888;
+ ovly->regs.bgr = false;
+ ovly->regs.bebo = false;
+ ovly->regs.opq = true;
+ break;
+ case MCDE_OVLYPIXFMT_RGBX8888:
+ ovly->regs.bits_per_pixel = 32;
+ ovly->regs.bpp = MCDE_EXTSRC0CONF_BPP_XRGB8888;
+ ovly->regs.bgr = false;
+ ovly->regs.bebo = true;
+ ovly->regs.opq = true;
+ break;
+ case MCDE_OVLYPIXFMT_RGBA8888:
+ ovly->regs.bits_per_pixel = 32;
+ ovly->regs.bpp = MCDE_EXTSRC0CONF_BPP_ARGB8888;
+ ovly->regs.bgr = false;
+ ovly->regs.bebo = false;
+ ovly->regs.opq = false;
+ break;
+ case MCDE_OVLYPIXFMT_YCbCr422:
+ ovly->regs.bits_per_pixel = 16;
+ ovly->regs.bpp = MCDE_EXTSRC0CONF_BPP_YCBCR422;
+ ovly->regs.bgr = false;
+ ovly->regs.bebo = false;
+ ovly->regs.opq = true;
+ break;
+ default:
+ break;
+ }
+
+ ovly->regs.ppl = ovly->w;
+ ovly->regs.lpf = ovly->h;
+ ovly->regs.cropx = ovly->src_x;
+ ovly->regs.cropy = ovly->src_y;
+ ovly->regs.xpos = ovly->dst_x;
+ ovly->regs.ypos = ovly->dst_y;
+ ovly->regs.z = ovly->dst_z > 0; /* 0 or 1 */
+ ovly->regs.col_conv = MCDE_OVL0CR_COLCCTRL_DISABLED;
+ ovly->regs.alpha_source = ovly->alpha_source;
+ ovly->regs.alpha_value = ovly->alpha_value;
+ ovly->chnl->transactionid++;
+
+ mutex_unlock(&mcde_hw_lock);
+
+ dev_vdbg(&mcde_dev->dev, "Overlay applied, idx=%d chnl=%d\n",
+ ovly->idx, ovly->chnl->id);
+}
+
+static int init_clocks_and_power(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct mcde_platform_data *pdata = pdev->dev.platform_data;
+
+#ifdef CONFIG_REGULATOR
+ if (pdata->regulator_mcde_epod_id) {
+ regulator_mcde_epod = regulator_get(&pdev->dev,
+ pdata->regulator_mcde_epod_id);
+ if (IS_ERR(regulator_mcde_epod)) {
+ ret = PTR_ERR(regulator_mcde_epod);
+ dev_warn(&pdev->dev,
+ "%s: Failed to get regulator '%s'\n",
+ __func__, pdata->regulator_mcde_epod_id);
+ regulator_mcde_epod = NULL;
+ return ret;
+ }
+ } else {
+ dev_dbg(&pdev->dev, "%s: No regulator id supplied\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (pdata->regulator_esram_epod_id) {
+ regulator_esram_epod = regulator_get(&pdev->dev,
+ pdata->regulator_esram_epod_id);
+ if (IS_ERR(regulator_esram_epod)) {
+ ret = PTR_ERR(regulator_esram_epod);
+ dev_warn(&pdev->dev,
+ "%s: Failed to get regulator '%s'\n",
+ __func__, pdata->regulator_esram_epod_id);
+ regulator_esram_epod = NULL;
+ goto regulator_esram_err;
+ }
+ } else {
+ dev_dbg(&pdev->dev, "%s: No regulator id supplied\n",
+ __func__);
+ }
+
+ if (pdata->regulator_vana_id) {
+ regulator_vana = regulator_get(&pdev->dev,
+ pdata->regulator_vana_id);
+ if (IS_ERR(regulator_vana)) {
+ ret = PTR_ERR(regulator_vana);
+ dev_warn(&pdev->dev,
+ "%s: Failed to get regulator '%s'\n",
+ __func__, pdata->regulator_vana_id);
+ regulator_vana = NULL;
+ goto regulator_vana_err;
+ }
+ } else {
+ dev_dbg(&pdev->dev, "%s: No regulator id supplied\n",
+ __func__);
+ ret = -EINVAL;
+ goto regulator_vana_err;
+ }
+#endif
+
+ clock_dsi = clk_get(&pdev->dev, pdata->clock_dsi_id);
+ if (IS_ERR(clock_dsi)) {
+ ret = PTR_ERR(clock_dsi);
+ dev_warn(&pdev->dev, "%s: Failed to get clock '%s'\n",
+ __func__, pdata->clock_dsi_id);
+ goto clk_dsi_err;
+ }
+
+ clock_dsi_lp = clk_get(&pdev->dev, pdata->clock_dsi_lp_id);
+ if (IS_ERR(clock_dsi_lp)) {
+ ret = PTR_ERR(clock_dsi_lp);
+ dev_warn(&pdev->dev, "%s: Failed to get clock '%s'\n",
+ __func__, pdata->clock_dsi_lp_id);
+ goto clk_dsi_lp_err;
+ }
+
+ clock_dpi = clk_get(&pdev->dev, pdata->clock_dpi_id);
+ if (IS_ERR(clock_dpi)) {
+ ret = PTR_ERR(clock_dpi);
+ dev_warn(&pdev->dev, "%s: Failed to get clock '%s'\n",
+ __func__, pdata->clock_dpi_id);
+ goto clk_dpi_err;
+ }
+
+ clock_mcde = clk_get(&pdev->dev, pdata->clock_mcde_id);
+ if (IS_ERR(clock_mcde)) {
+ ret = PTR_ERR(clock_mcde);
+ dev_warn(&pdev->dev, "%s: Failed to get clock '%s'\n",
+ __func__, pdata->clock_mcde_id);
+ goto clk_mcde_err;
+ }
+
+ return ret;
+
+clk_mcde_err:
+ clk_put(clock_dpi);
+clk_dpi_err:
+ clk_put(clock_dsi_lp);
+clk_dsi_lp_err:
+ clk_put(clock_dsi);
+clk_dsi_err:
+#ifdef CONFIG_REGULATOR
+ if (regulator_vana)
+ regulator_put(regulator_vana);
+regulator_vana_err:
+ if (regulator_esram_epod)
+ regulator_put(regulator_esram_epod);
+regulator_esram_err:
+ if (regulator_mcde_epod)
+ regulator_put(regulator_mcde_epod);
+#endif
+ return ret;
+}
+
+static void remove_clocks_and_power(struct platform_device *pdev)
+{
+ /* REVIEW: Release only if exist */
+ /* REVIEW: Remove make sure MCDE is done */
+ clk_put(clock_dpi);
+ clk_put(clock_dsi_lp);
+ clk_put(clock_dsi);
+ clk_put(clock_mcde);
+#ifdef CONFIG_REGULATOR
+ if (regulator_vana)
+ regulator_put(regulator_vana);
+ if (regulator_mcde_epod)
+ regulator_put(regulator_mcde_epod);
+ if (regulator_esram_epod)
+ regulator_put(regulator_esram_epod);
+#endif
+}
+
+static int __devinit mcde_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int i;
+ struct resource *res;
+ struct mcde_platform_data *pdata = pdev->dev.platform_data;
+ u8 major_version;
+ u8 minor_version;
+ u8 development_version;
+
+ if (!pdata) {
+ dev_dbg(&pdev->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ num_dsilinks = pdata->num_dsilinks;
+ mcde_dev = pdev;
+
+ num_channels = pdata->num_channels;
+ num_overlays = pdata->num_overlays;
+
+ channels = kzalloc(num_channels * sizeof(struct mcde_chnl_state),
+ GFP_KERNEL);
+ if (!channels) {
+ ret = -ENOMEM;
+ goto failed_channels_alloc;
+ }
+
+ overlays = kzalloc(num_overlays * sizeof(struct mcde_ovly_state),
+ GFP_KERNEL);
+ if (!overlays) {
+ ret = -ENOMEM;
+ goto failed_overlays_alloc;
+ }
+
+ dsiio = kzalloc(num_dsilinks * sizeof(*dsiio), GFP_KERNEL);
+ if (!dsiio) {
+ ret = -ENOMEM;
+ goto failed_dsi_alloc;
+ }
+
+ /* Hook up irq */
+ mcde_irq = platform_get_irq(pdev, 0);
+ if (mcde_irq <= 0) {
+ dev_dbg(&pdev->dev, "No irq defined\n");
+ ret = -EINVAL;
+ goto failed_irq_get;
+ }
+
+ /* Map I/O */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_dbg(&pdev->dev, "No MCDE io defined\n");
+ ret = -EINVAL;
+ goto failed_get_mcde_io;
+ }
+ mcdeio = ioremap(res->start, res->end - res->start + 1);
+ if (!mcdeio) {
+ dev_dbg(&pdev->dev, "MCDE iomap failed\n");
+ ret = -EINVAL;
+ goto failed_map_mcde_io;
+ }
+ dev_info(&pdev->dev, "MCDE iomap: 0x%.8X->0x%.8X\n",
+ (u32)res->start, (u32)mcdeio);
+ for (i = 0; i < num_dsilinks; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1+i);
+ if (!res) {
+ dev_dbg(&pdev->dev, "No DSI%d io defined\n", i);
+ ret = -EINVAL;
+ goto failed_get_dsi_io;
+ }
+ dsiio[i] = ioremap(res->start, res->end - res->start + 1);
+ if (!dsiio[i]) {
+ dev_dbg(&pdev->dev, "MCDE DSI%d iomap failed\n", i);
+ ret = -EINVAL;
+ goto failed_map_dsi_io;
+ }
+ dev_info(&pdev->dev, "MCDE DSI%d iomap: 0x%.8X->0x%.8X\n",
+ i, (u32)res->start, (u32)dsiio[i]);
+ }
+
+ ret = init_clocks_and_power(pdev);
+ if (ret < 0) {
+ dev_warn(&pdev->dev, "%s: init_clocks_and_power failed\n"
+ , __func__);
+ goto failed_init_clocks;
+ }
+
+ INIT_DELAYED_WORK_DEFERRABLE(&hw_timeout_work, work_sleep_function);
+
+ ret = enable_clocks_and_power(mcde_dev);
+ if (ret < 0) {
+ dev_dbg(&mcde_dev->dev,
+ "%s: Enable clocks and power failed\n"
+ , __func__);
+ goto failed_enable_clocks;
+ }
+
+ update_mcde_registers();
+ mcde_is_enabled = true;
+
+ ret = request_irq(mcde_irq, mcde_irq_handler, 0, "mcde",
+ &mcde_dev->dev);
+ if (ret) {
+ dev_dbg(&mcde_dev->dev, "Failed to request irq (irq=%d)\n",
+ mcde_irq);
+ goto failed_request_irq;
+ }
+
+ schedule_delayed_work(&hw_timeout_work,
+ msecs_to_jiffies(MCDE_SLEEP_WATCHDOG));
+
+ major_version = MCDE_REG2VAL(MCDE_PID, MAJOR_VERSION,
+ mcde_rreg(MCDE_PID));
+ minor_version = MCDE_REG2VAL(MCDE_PID, MINOR_VERSION,
+ mcde_rreg(MCDE_PID));
+ development_version = MCDE_REG2VAL(MCDE_PID, DEVELOPMENT_VERSION,
+ mcde_rreg(MCDE_PID));
+
+ dev_info(&mcde_dev->dev, "MCDE HW revision %u.%u.%u.%u\n",
+ major_version, minor_version, development_version,
+ mcde_rfld(MCDE_PID, METALFIX_VERSION));
+
+ if (major_version == 3 && minor_version == 0 &&
+ development_version >= 8) {
+ hardware_version = MCDE_CHIP_VERSION_3_0_8;
+ dev_info(&mcde_dev->dev, "V2 HW\n");
+ } else if (major_version == 3 && minor_version == 0 &&
+ development_version >= 5) {
+ hardware_version = MCDE_CHIP_VERSION_3_0_5;
+ dev_info(&mcde_dev->dev, "V1 HW\n");
+ } else if (major_version == 1 && minor_version == 0 &&
+ development_version >= 4) {
+ hardware_version = MCDE_CHIP_VERSION_1_0_4;
+ mcde_dynamic_power_management = false;
+ dev_info(&mcde_dev->dev, "V1_U5500 HW\n");
+ } else {
+ dev_err(&mcde_dev->dev, "Unsupported HW version\n");
+ ret = -ENOTSUPP;
+ goto failed_hardware_version;
+ }
+
+ for (i = 0; i < num_overlays; i++)
+ overlays[i].idx = i;
+
+ if (hardware_version == MCDE_CHIP_VERSION_1_0_4) {
+ channels[0].ovly0 = &overlays[0];
+ channels[0].ovly1 = &overlays[1];
+ channels[1].ovly0 = &overlays[2];
+ channels[1].ovly1 = NULL;
+ } else {
+ channels[0].ovly0 = &overlays[0];
+ channels[0].ovly1 = &overlays[1];
+ channels[1].ovly0 = &overlays[2];
+ channels[1].ovly1 = &overlays[3];
+ channels[2].ovly0 = &overlays[4];
+ channels[2].ovly1 = NULL;
+ channels[3].ovly0 = &overlays[5];
+ channels[3].ovly1 = NULL;
+ }
+
+ for (i = 0; i < num_channels; i++) {
+ channels[i].id = i;
+
+ channels[i].ovly0->chnl = &channels[i];
+ if (channels[i].ovly1)
+ channels[i].ovly1->chnl = &channels[i];
+
+ init_waitqueue_head(&channels[i].waitq_hw);
+ init_timer(&channels[i].auto_sync_timer);
+ channels[i].auto_sync_timer.function =
+ watchdog_auto_sync_timer_function;
+
+ init_timer(&channels[i].dsi_te_timer);
+ channels[i].dsi_te_timer.function =
+ dsi_te_timer_function;
+ channels[i].dsi_te_timer.data = i;
+ }
+
+ return 0;
+
+failed_hardware_version:
+ free_irq(mcde_irq, &pdev->dev);
+failed_request_irq:
+ disable_mcde_hw(true);
+failed_enable_clocks:
+ remove_clocks_and_power(pdev);
+failed_init_clocks:
+failed_map_dsi_io:
+failed_get_dsi_io:
+ for (i = 0; i < num_dsilinks; i++) {
+ if (dsiio[i])
+ iounmap(dsiio[i]);
+ }
+ iounmap(mcdeio);
+failed_map_mcde_io:
+failed_get_mcde_io:
+failed_irq_get:
+ kfree(dsiio);
+ dsiio = NULL;
+failed_dsi_alloc:
+ kfree(overlays);
+ overlays = NULL;
+failed_overlays_alloc:
+ kfree(channels);
+ channels = NULL;
+failed_channels_alloc:
+ return ret;
+}
+
+static int __devexit mcde_remove(struct platform_device *pdev)
+{
+ struct mcde_chnl_state *chnl = &channels[0];
+
+ for (; chnl < &channels[num_channels]; chnl++) {
+ if (del_timer(&chnl->auto_sync_timer))
+ dev_vdbg(&mcde_dev->dev,
+ "%s timer could not be stopped\n"
+ , __func__);
+ if (del_timer(&chnl->dsi_te_timer))
+ dev_vdbg(&mcde_dev->dev,
+ "%s dsi timer could not be stopped\n"
+ , __func__);
+ }
+
+ remove_clocks_and_power(pdev);
+ return 0;
+}
+
+#if !defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)
+static int mcde_resume(struct platform_device *pdev)
+{
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ mutex_lock(&mcde_hw_lock);
+
+ if (enable_mcde_hw()) {
+ mutex_unlock(&mcde_hw_lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&mcde_hw_lock);
+ return 0;
+}
+
+static int mcde_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int ret;
+
+ dev_vdbg(&mcde_dev->dev, "%s\n", __func__);
+
+ mutex_lock(&mcde_hw_lock);
+
+ cancel_delayed_work(&hw_timeout_work);
+
+ if (!mcde_is_enabled) {
+ mutex_unlock(&mcde_hw_lock);
+ return 0;
+ }
+ (void)disable_mcde_hw(true);
+
+ mutex_unlock(&mcde_hw_lock);
+
+ return ret;
+}
+#endif
+
+static struct platform_driver mcde_driver = {
+ .probe = mcde_probe,
+ .remove = mcde_remove,
+#if !defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM)
+ .suspend = mcde_suspend,
+ .resume = mcde_resume,
+#else
+ .suspend = NULL,
+ .resume = NULL,
+#endif
+ .driver = {
+ .name = "mcde",
+ },
+};
+
+int __init mcde_init(void)
+{
+ mutex_init(&mcde_hw_lock);
+ return platform_driver_register(&mcde_driver);
+}
+
+void mcde_exit(void)
+{
+ /* REVIEW: shutdown MCDE? */
+ platform_driver_unregister(&mcde_driver);
+}
diff --git a/drivers/video/mcde/mcde_mod.c b/drivers/video/mcde/mcde_mod.c
new file mode 100644
index 00000000000..60df0d4965f
--- /dev/null
+++ b/drivers/video/mcde/mcde_mod.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * ST-Ericsson MCDE driver
+ *
+ * Author: Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <video/mcde.h>
+#include <video/mcde_fb.h>
+#include <video/mcde_dss.h>
+#include <video/mcde_display.h>
+
+/* Module init */
+
+static int __init mcde_subsystem_init(void)
+{
+ int ret;
+ pr_info("MCDE subsystem init begin\n");
+
+ /* MCDE module init sequence */
+ ret = mcde_init();
+ if (ret)
+ goto mcde_failed;
+ ret = mcde_display_init();
+ if (ret)
+ goto mcde_display_failed;
+ ret = mcde_dss_init();
+ if (ret)
+ goto mcde_dss_failed;
+ ret = mcde_fb_init();
+ if (ret)
+ goto mcde_fb_failed;
+ pr_info("MCDE subsystem init done\n");
+
+ goto done;
+mcde_fb_failed:
+ mcde_dss_exit();
+mcde_dss_failed:
+ mcde_display_exit();
+mcde_display_failed:
+ mcde_exit();
+mcde_failed:
+done:
+ return ret;
+}
+#ifdef MODULE
+module_init(mcde_subsystem_init);
+#else
+fs_initcall(mcde_subsystem_init);
+#endif
+
+static void __exit mcde_module_exit(void)
+{
+ mcde_exit();
+ mcde_display_exit();
+ mcde_dss_exit();
+}
+module_exit(mcde_module_exit);
+
+MODULE_AUTHOR("Marcus Lorentzon <marcus.xm.lorentzon@stericsson.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ST-Ericsson MCDE driver");
+
diff --git a/drivers/video/mcde/mcde_regs.h b/drivers/video/mcde/mcde_regs.h
new file mode 100644
index 00000000000..7f28c26360b
--- /dev/null
+++ b/drivers/video/mcde/mcde_regs.h
@@ -0,0 +1,5315 @@
+
+#define MCDE_VAL2REG(__reg, __fld, __val) \
+ (((__val) << __reg##_##__fld##_SHIFT) & __reg##_##__fld##_MASK)
+#define MCDE_REG2VAL(__reg, __fld, __val) \
+ (((__val) & __reg##_##__fld##_MASK) >> __reg##_##__fld##_SHIFT)
+
+#define MCDE_CR 0x00000000
+#define MCDE_CR_DSICMD2_EN_V1_SHIFT 0
+#define MCDE_CR_DSICMD2_EN_V1_MASK 0x00000001
+#define MCDE_CR_DSICMD2_EN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, DSICMD2_EN_V1, __x)
+#define MCDE_CR_DSICMD1_EN_V1_SHIFT 1
+#define MCDE_CR_DSICMD1_EN_V1_MASK 0x00000002
+#define MCDE_CR_DSICMD1_EN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, DSICMD1_EN_V1, __x)
+#define MCDE_CR_DSI0_EN_V3_SHIFT 0
+#define MCDE_CR_DSI0_EN_V3_MASK 0x00000001
+#define MCDE_CR_DSI0_EN_V3(__x) \
+ MCDE_VAL2REG(MCDE_CR, DSI0_EN_V3, __x)
+#define MCDE_CR_DSI1_EN_V3_SHIFT 1
+#define MCDE_CR_DSI1_EN_V3_MASK 0x00000002
+#define MCDE_CR_DSI1_EN_V3(__x) \
+ MCDE_VAL2REG(MCDE_CR, DSI1_EN_V3, __x)
+#define MCDE_CR_DSICMD0_EN_V1_SHIFT 2
+#define MCDE_CR_DSICMD0_EN_V1_MASK 0x00000004
+#define MCDE_CR_DSICMD0_EN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, DSICMD0_EN_V1, __x)
+#define MCDE_CR_DSIVID2_EN_V1_SHIFT 3
+#define MCDE_CR_DSIVID2_EN_V1_MASK 0x00000008
+#define MCDE_CR_DSIVID2_EN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, DSIVID2_EN_V1, __x)
+#define MCDE_CR_DSIVID1_EN_V1_SHIFT 4
+#define MCDE_CR_DSIVID1_EN_V1_MASK 0x00000010
+#define MCDE_CR_DSIVID1_EN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, DSIVID1_EN_V1, __x)
+#define MCDE_CR_DSIVID0_EN_V1_SHIFT 5
+#define MCDE_CR_DSIVID0_EN_V1_MASK 0x00000020
+#define MCDE_CR_DSIVID0_EN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, DSIVID0_EN_V1, __x)
+#define MCDE_CR_DBIC1_EN_V1_SHIFT 6
+#define MCDE_CR_DBIC1_EN_V1_MASK 0x00000040
+#define MCDE_CR_DBIC1_EN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, DBIC1_EN_V1, __x)
+#define MCDE_CR_DBIC0_EN_V1_SHIFT 7
+#define MCDE_CR_DBIC0_EN_V1_MASK 0x00000080
+#define MCDE_CR_DBIC0_EN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, DBIC0_EN_V1, __x)
+#define MCDE_CR_DBI_EN_V3_SHIFT 7
+#define MCDE_CR_DBI_EN_V3_MASK 0x00000080
+#define MCDE_CR_DBI_EN_V3(__x) \
+ MCDE_VAL2REG(MCDE_CR, DBI_EN_V3, __x)
+#define MCDE_CR_DPIB_EN_V1_SHIFT 8
+#define MCDE_CR_DPIB_EN_V1_MASK 0x00000100
+#define MCDE_CR_DPIB_EN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, DPIB_EN_V1, __x)
+#define MCDE_CR_DPIA_EN_V1_SHIFT 9
+#define MCDE_CR_DPIA_EN_V1_MASK 0x00000200
+#define MCDE_CR_DPIA_EN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, DPIA_EN_V1, __x)
+#define MCDE_CR_DPI_EN_V3_SHIFT 9
+#define MCDE_CR_DPI_EN_V3_MASK 0x00000200
+#define MCDE_CR_DPI_EN_V3(__x) \
+ MCDE_VAL2REG(MCDE_CR, DPI_EN_V3, __x)
+#define MCDE_CR_IFIFOCTRLEN_SHIFT 15
+#define MCDE_CR_IFIFOCTRLEN_MASK 0x00008000
+#define MCDE_CR_IFIFOCTRLEN(__x) \
+ MCDE_VAL2REG(MCDE_CR, IFIFOCTRLEN, __x)
+#define MCDE_CR_F01MUX_V1_SHIFT 16
+#define MCDE_CR_F01MUX_V1_MASK 0x00010000
+#define MCDE_CR_F01MUX_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, F01MUX_V1, __x)
+#define MCDE_CR_FABMUX_V1_SHIFT 17
+#define MCDE_CR_FABMUX_V1_MASK 0x00020000
+#define MCDE_CR_FABMUX_V1(__x) \
+ MCDE_VAL2REG(MCDE_CR, FABMUX_V1, __x)
+#define MCDE_CR_AUTOCLKG_EN_SHIFT 30
+#define MCDE_CR_AUTOCLKG_EN_MASK 0x40000000
+#define MCDE_CR_AUTOCLKG_EN(__x) \
+ MCDE_VAL2REG(MCDE_CR, AUTOCLKG_EN, __x)
+#define MCDE_CR_MCDEEN_SHIFT 31
+#define MCDE_CR_MCDEEN_MASK 0x80000000
+#define MCDE_CR_MCDEEN(__x) \
+ MCDE_VAL2REG(MCDE_CR, MCDEEN, __x)
+#define MCDE_CONF0 0x00000004
+#define MCDE_CONF0_SYNCMUX0_SHIFT 0
+#define MCDE_CONF0_SYNCMUX0_MASK 0x00000001
+#define MCDE_CONF0_SYNCMUX0(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, SYNCMUX0, __x)
+#define MCDE_CONF0_SYNCMUX1_SHIFT 1
+#define MCDE_CONF0_SYNCMUX1_MASK 0x00000002
+#define MCDE_CONF0_SYNCMUX1(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, SYNCMUX1, __x)
+#define MCDE_CONF0_SYNCMUX2_SHIFT 2
+#define MCDE_CONF0_SYNCMUX2_MASK 0x00000004
+#define MCDE_CONF0_SYNCMUX2(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, SYNCMUX2, __x)
+#define MCDE_CONF0_SYNCMUX3_SHIFT 3
+#define MCDE_CONF0_SYNCMUX3_MASK 0x00000008
+#define MCDE_CONF0_SYNCMUX3(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, SYNCMUX3, __x)
+#define MCDE_CONF0_SYNCMUX4_SHIFT 4
+#define MCDE_CONF0_SYNCMUX4_MASK 0x00000010
+#define MCDE_CONF0_SYNCMUX4(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, SYNCMUX4, __x)
+#define MCDE_CONF0_SYNCMUX5_SHIFT 5
+#define MCDE_CONF0_SYNCMUX5_MASK 0x00000020
+#define MCDE_CONF0_SYNCMUX5(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, SYNCMUX5, __x)
+#define MCDE_CONF0_SYNCMUX6_SHIFT 6
+#define MCDE_CONF0_SYNCMUX6_MASK 0x00000040
+#define MCDE_CONF0_SYNCMUX6(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, SYNCMUX6, __x)
+#define MCDE_CONF0_SYNCMUX7_SHIFT 7
+#define MCDE_CONF0_SYNCMUX7_MASK 0x00000080
+#define MCDE_CONF0_SYNCMUX7(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, SYNCMUX7, __x)
+#define MCDE_CONF0_SWAP_A_C0_V1_SHIFT 8
+#define MCDE_CONF0_SWAP_A_C0_V1_MASK 0x00000100
+#define MCDE_CONF0_SWAP_A_C0_V1(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, SWAP_A_C0_V1, __x)
+#define MCDE_CONF0_SWAP_B_C1_V1_SHIFT 9
+#define MCDE_CONF0_SWAP_B_C1_V1_MASK 0x00000200
+#define MCDE_CONF0_SWAP_B_C1_V1(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, SWAP_B_C1_V1, __x)
+#define MCDE_CONF0_FSYNCTRLA_V1_SHIFT 10
+#define MCDE_CONF0_FSYNCTRLA_V1_MASK 0x00000400
+#define MCDE_CONF0_FSYNCTRLA_V1(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, FSYNCTRLA_V1, __x)
+#define MCDE_CONF0_FSYNCTRLB_V1_SHIFT 11
+#define MCDE_CONF0_FSYNCTRLB_V1_MASK 0x00000800
+#define MCDE_CONF0_FSYNCTRLB_V1(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, FSYNCTRLB_V1, __x)
+#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT 12
+#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_MASK 0x00007000
+#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, IFIFOCTRLWTRMRKLVL, __x)
+#define MCDE_CONF0_OUTMUX0_SHIFT 16
+#define MCDE_CONF0_OUTMUX0_MASK 0x00070000
+#define MCDE_CONF0_OUTMUX0(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, OUTMUX0, __x)
+#define MCDE_CONF0_OUTMUX1_SHIFT 19
+#define MCDE_CONF0_OUTMUX1_MASK 0x00380000
+#define MCDE_CONF0_OUTMUX1(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, OUTMUX1, __x)
+#define MCDE_CONF0_OUTMUX2_SHIFT 22
+#define MCDE_CONF0_OUTMUX2_MASK 0x01C00000
+#define MCDE_CONF0_OUTMUX2(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, OUTMUX2, __x)
+#define MCDE_CONF0_OUTMUX3_SHIFT 25
+#define MCDE_CONF0_OUTMUX3_MASK 0x0E000000
+#define MCDE_CONF0_OUTMUX3(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, OUTMUX3, __x)
+#define MCDE_CONF0_OUTMUX4_SHIFT 28
+#define MCDE_CONF0_OUTMUX4_MASK 0x70000000
+#define MCDE_CONF0_OUTMUX4(__x) \
+ MCDE_VAL2REG(MCDE_CONF0, OUTMUX4, __x)
+#define MCDE_SSP 0x00000008
+#define MCDE_SSP_SSPDATA_SHIFT 0
+#define MCDE_SSP_SSPDATA_MASK 0x000000FF
+#define MCDE_SSP_SSPDATA(__x) \
+ MCDE_VAL2REG(MCDE_SSP, SSPDATA, __x)
+#define MCDE_SSP_SSPCMD_SHIFT 8
+#define MCDE_SSP_SSPCMD_MASK 0x00000100
+#define MCDE_SSP_SSPCMD_DATA 0
+#define MCDE_SSP_SSPCMD_COMMAND 1
+#define MCDE_SSP_SSPCMD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_SSP, SSPCMD, MCDE_SSP_SSPCMD_##__x)
+#define MCDE_SSP_SSPCMD(__x) \
+ MCDE_VAL2REG(MCDE_SSP, SSPCMD, __x)
+#define MCDE_SSP_SSPEN_SHIFT 16
+#define MCDE_SSP_SSPEN_MASK 0x00010000
+#define MCDE_SSP_SSPEN(__x) \
+ MCDE_VAL2REG(MCDE_SSP, SSPEN, __x)
+#define MCDE_AIS 0x00000100
+#define MCDE_AIS_MCDEPPI_SHIFT 0
+#define MCDE_AIS_MCDEPPI_MASK 0x00000001
+#define MCDE_AIS_MCDEPPI(__x) \
+ MCDE_VAL2REG(MCDE_AIS, MCDEPPI, __x)
+#define MCDE_AIS_MCDEOVLI_SHIFT 1
+#define MCDE_AIS_MCDEOVLI_MASK 0x00000002
+#define MCDE_AIS_MCDEOVLI(__x) \
+ MCDE_VAL2REG(MCDE_AIS, MCDEOVLI, __x)
+#define MCDE_AIS_MCDECHNLI_SHIFT 2
+#define MCDE_AIS_MCDECHNLI_MASK 0x00000004
+#define MCDE_AIS_MCDECHNLI(__x) \
+ MCDE_VAL2REG(MCDE_AIS, MCDECHNLI, __x)
+#define MCDE_AIS_MCDEERRI_SHIFT 3
+#define MCDE_AIS_MCDEERRI_MASK 0x00000008
+#define MCDE_AIS_MCDEERRI(__x) \
+ MCDE_VAL2REG(MCDE_AIS, MCDEERRI, __x)
+#define MCDE_AIS_DSI0AI_SHIFT 4
+#define MCDE_AIS_DSI0AI_MASK 0x00000010
+#define MCDE_AIS_DSI0AI(__x) \
+ MCDE_VAL2REG(MCDE_AIS, DSI0AI, __x)
+#define MCDE_AIS_DSI1AI_SHIFT 5
+#define MCDE_AIS_DSI1AI_MASK 0x00000020
+#define MCDE_AIS_DSI1AI(__x) \
+ MCDE_VAL2REG(MCDE_AIS, DSI1AI, __x)
+#define MCDE_AIS_DSI2AI_SHIFT 6
+#define MCDE_AIS_DSI2AI_MASK 0x00000040
+#define MCDE_AIS_DSI2AI(__x) \
+ MCDE_VAL2REG(MCDE_AIS, DSI2AI, __x)
+#define MCDE_IMSCPP 0x00000104
+#define MCDE_IMSCPP_VCMPAIM_SHIFT 0
+#define MCDE_IMSCPP_VCMPAIM_MASK 0x00000001
+#define MCDE_IMSCPP_VCMPAIM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCPP, VCMPAIM, __x)
+#define MCDE_IMSCPP_VCMPBIM_SHIFT 1
+#define MCDE_IMSCPP_VCMPBIM_MASK 0x00000002
+#define MCDE_IMSCPP_VCMPBIM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCPP, VCMPBIM, __x)
+#define MCDE_IMSCPP_VSCC0IM_SHIFT 2
+#define MCDE_IMSCPP_VSCC0IM_MASK 0x00000004
+#define MCDE_IMSCPP_VSCC0IM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCPP, VSCC0IM, __x)
+#define MCDE_IMSCPP_VSCC1IM_SHIFT 3
+#define MCDE_IMSCPP_VSCC1IM_MASK 0x00000008
+#define MCDE_IMSCPP_VSCC1IM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCPP, VSCC1IM, __x)
+#define MCDE_IMSCPP_VCMPC0IM_SHIFT 4
+#define MCDE_IMSCPP_VCMPC0IM_MASK 0x00000010
+#define MCDE_IMSCPP_VCMPC0IM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCPP, VCMPC0IM, __x)
+#define MCDE_IMSCPP_VCMPC1IM_SHIFT 5
+#define MCDE_IMSCPP_VCMPC1IM_MASK 0x00000020
+#define MCDE_IMSCPP_VCMPC1IM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCPP, VCMPC1IM, __x)
+#define MCDE_IMSCPP_ROTFDIM_B_SHIFT 6
+#define MCDE_IMSCPP_ROTFDIM_B_MASK 0x00000040
+#define MCDE_IMSCPP_ROTFDIM_B(__x) \
+ MCDE_VAL2REG(MCDE_IMSCPP, ROTFDIM_B, __x)
+#define MCDE_IMSCPP_ROTFDIM_A_SHIFT 7
+#define MCDE_IMSCPP_ROTFDIM_A_MASK 0x00000080
+#define MCDE_IMSCPP_ROTFDIM_A(__x) \
+ MCDE_VAL2REG(MCDE_IMSCPP, ROTFDIM_A, __x)
+#define MCDE_IMSCOVL 0x00000108
+#define MCDE_IMSCOVL_OVLRDIM_SHIFT 0
+#define MCDE_IMSCOVL_OVLRDIM_MASK 0x0000FFFF
+#define MCDE_IMSCOVL_OVLRDIM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCOVL, OVLRDIM, __x)
+#define MCDE_IMSCOVL_OVLFDIM_SHIFT 16
+#define MCDE_IMSCOVL_OVLFDIM_MASK 0xFFFF0000
+#define MCDE_IMSCOVL_OVLFDIM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCOVL, OVLFDIM, __x)
+#define MCDE_IMSCCHNL 0x0000010C
+#define MCDE_IMSCCHNL_CHNLRDIM_SHIFT 0
+#define MCDE_IMSCCHNL_CHNLRDIM_MASK 0x0000FFFF
+#define MCDE_IMSCCHNL_CHNLRDIM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCCHNL, CHNLRDIM, __x)
+#define MCDE_IMSCCHNL_CHNLAIM_SHIFT 16
+#define MCDE_IMSCCHNL_CHNLAIM_MASK 0xFFFF0000
+#define MCDE_IMSCCHNL_CHNLAIM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCCHNL, CHNLAIM, __x)
+#define MCDE_IMSCERR 0x00000110
+#define MCDE_IMSCERR_FUAIM_SHIFT 0
+#define MCDE_IMSCERR_FUAIM_MASK 0x00000001
+#define MCDE_IMSCERR_FUAIM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCERR, FUAIM, __x)
+#define MCDE_IMSCERR_FUBIM_SHIFT 1
+#define MCDE_IMSCERR_FUBIM_MASK 0x00000002
+#define MCDE_IMSCERR_FUBIM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCERR, FUBIM, __x)
+#define MCDE_IMSCERR_SCHBLCKDIM_SHIFT 2
+#define MCDE_IMSCERR_SCHBLCKDIM_MASK 0x00000004
+#define MCDE_IMSCERR_SCHBLCKDIM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCERR, SCHBLCKDIM, __x)
+#define MCDE_IMSCERR_ROTAFEIM_WRITE_SHIFT 3
+#define MCDE_IMSCERR_ROTAFEIM_WRITE_MASK 0x00000008
+#define MCDE_IMSCERR_ROTAFEIM_WRITE(__x) \
+ MCDE_VAL2REG(MCDE_IMSCERR, ROTAFEIM_WRITE, __x)
+#define MCDE_IMSCERR_ROTAFEIM_READ_SHIFT 4
+#define MCDE_IMSCERR_ROTAFEIM_READ_MASK 0x00000010
+#define MCDE_IMSCERR_ROTAFEIM_READ(__x) \
+ MCDE_VAL2REG(MCDE_IMSCERR, ROTAFEIM_READ, __x)
+#define MCDE_IMSCERR_ROTBFEIM_WRITE_SHIFT 5
+#define MCDE_IMSCERR_ROTBFEIM_WRITE_MASK 0x00000020
+#define MCDE_IMSCERR_ROTBFEIM_WRITE(__x) \
+ MCDE_VAL2REG(MCDE_IMSCERR, ROTBFEIM_WRITE, __x)
+#define MCDE_IMSCERR_ROTBFEIM_READ_SHIFT 6
+#define MCDE_IMSCERR_ROTBFEIM_READ_MASK 0x00000040
+#define MCDE_IMSCERR_ROTBFEIM_READ(__x) \
+ MCDE_VAL2REG(MCDE_IMSCERR, ROTBFEIM_READ, __x)
+#define MCDE_IMSCERR_FUC0IM_SHIFT 7
+#define MCDE_IMSCERR_FUC0IM_MASK 0x00000080
+#define MCDE_IMSCERR_FUC0IM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCERR, FUC0IM, __x)
+#define MCDE_IMSCERR_FUC1IM_SHIFT 8
+#define MCDE_IMSCERR_FUC1IM_MASK 0x00000100
+#define MCDE_IMSCERR_FUC1IM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCERR, FUC1IM, __x)
+#define MCDE_IMSCERR_OVLFERRIM_SHIFT 16
+#define MCDE_IMSCERR_OVLFERRIM_MASK 0xFFFF0000
+#define MCDE_IMSCERR_OVLFERRIM(__x) \
+ MCDE_VAL2REG(MCDE_IMSCERR, OVLFERRIM, __x)
+#define MCDE_RISPP 0x00000114
+#define MCDE_RISPP_VCMPARIS_SHIFT 0
+#define MCDE_RISPP_VCMPARIS_MASK 0x00000001
+#define MCDE_RISPP_VCMPARIS(__x) \
+ MCDE_VAL2REG(MCDE_RISPP, VCMPARIS, __x)
+#define MCDE_RISPP_VCMPBRIS_SHIFT 1
+#define MCDE_RISPP_VCMPBRIS_MASK 0x00000002
+#define MCDE_RISPP_VCMPBRIS(__x) \
+ MCDE_VAL2REG(MCDE_RISPP, VCMPBRIS, __x)
+#define MCDE_RISPP_VSCC0RIS_SHIFT 2
+#define MCDE_RISPP_VSCC0RIS_MASK 0x00000004
+#define MCDE_RISPP_VSCC0RIS(__x) \
+ MCDE_VAL2REG(MCDE_RISPP, VSCC0RIS, __x)
+#define MCDE_RISPP_VSCC1RIS_SHIFT 3
+#define MCDE_RISPP_VSCC1RIS_MASK 0x00000008
+#define MCDE_RISPP_VSCC1RIS(__x) \
+ MCDE_VAL2REG(MCDE_RISPP, VSCC1RIS, __x)
+#define MCDE_RISPP_VCMPC0RIS_SHIFT 4
+#define MCDE_RISPP_VCMPC0RIS_MASK 0x00000010
+#define MCDE_RISPP_VCMPC0RIS(__x) \
+ MCDE_VAL2REG(MCDE_RISPP, VCMPC0RIS, __x)
+#define MCDE_RISPP_VCMPC1RIS_SHIFT 5
+#define MCDE_RISPP_VCMPC1RIS_MASK 0x00000020
+#define MCDE_RISPP_VCMPC1RIS(__x) \
+ MCDE_VAL2REG(MCDE_RISPP, VCMPC1RIS, __x)
+#define MCDE_RISPP_ROTFDRIS_B_SHIFT 6
+#define MCDE_RISPP_ROTFDRIS_B_MASK 0x00000040
+#define MCDE_RISPP_ROTFDRIS_B(__x) \
+ MCDE_VAL2REG(MCDE_RISPP, ROTFDRIS_B, __x)
+#define MCDE_RISPP_ROTFDRIS_A_SHIFT 7
+#define MCDE_RISPP_ROTFDRIS_A_MASK 0x00000080
+#define MCDE_RISPP_ROTFDRIS_A(__x) \
+ MCDE_VAL2REG(MCDE_RISPP, ROTFDRIS_A, __x)
+#define MCDE_RISOVL 0x00000118
+#define MCDE_RISOVL_OVLRDRIS_SHIFT 0
+#define MCDE_RISOVL_OVLRDRIS_MASK 0x0000FFFF
+#define MCDE_RISOVL_OVLRDRIS(__x) \
+ MCDE_VAL2REG(MCDE_RISOVL, OVLRDRIS, __x)
+#define MCDE_RISOVL_OVLFDRIS_SHIFT 16
+#define MCDE_RISOVL_OVLFDRIS_MASK 0xFFFF0000
+#define MCDE_RISOVL_OVLFDRIS(__x) \
+ MCDE_VAL2REG(MCDE_RISOVL, OVLFDRIS, __x)
+#define MCDE_RISCHNL 0x0000011C
+#define MCDE_RISCHNL_CHNLRDRIS_SHIFT 0
+#define MCDE_RISCHNL_CHNLRDRIS_MASK 0x0000FFFF
+#define MCDE_RISCHNL_CHNLRDRIS(__x) \
+ MCDE_VAL2REG(MCDE_RISCHNL, CHNLRDRIS, __x)
+#define MCDE_RISCHNL_CHNLARIS_SHIFT 16
+#define MCDE_RISCHNL_CHNLARIS_MASK 0xFFFF0000
+#define MCDE_RISCHNL_CHNLARIS(__x) \
+ MCDE_VAL2REG(MCDE_RISCHNL, CHNLARIS, __x)
+#define MCDE_RISERR 0x00000120
+#define MCDE_RISERR_FUARIS_SHIFT 0
+#define MCDE_RISERR_FUARIS_MASK 0x00000001
+#define MCDE_RISERR_FUARIS(__x) \
+ MCDE_VAL2REG(MCDE_RISERR, FUARIS, __x)
+#define MCDE_RISERR_FUBRIS_SHIFT 1
+#define MCDE_RISERR_FUBRIS_MASK 0x00000002
+#define MCDE_RISERR_FUBRIS(__x) \
+ MCDE_VAL2REG(MCDE_RISERR, FUBRIS, __x)
+#define MCDE_RISERR_SCHBLCKDRIS_SHIFT 2
+#define MCDE_RISERR_SCHBLCKDRIS_MASK 0x00000004
+#define MCDE_RISERR_SCHBLCKDRIS(__x) \
+ MCDE_VAL2REG(MCDE_RISERR, SCHBLCKDRIS, __x)
+#define MCDE_RISERR_ROTAFERIS_WRITE_SHIFT 3
+#define MCDE_RISERR_ROTAFERIS_WRITE_MASK 0x00000008
+#define MCDE_RISERR_ROTAFERIS_WRITE(__x) \
+ MCDE_VAL2REG(MCDE_RISERR, ROTAFERIS_WRITE, __x)
+#define MCDE_RISERR_ROTAFERIS_READ_SHIFT 4
+#define MCDE_RISERR_ROTAFERIS_READ_MASK 0x00000010
+#define MCDE_RISERR_ROTAFERIS_READ(__x) \
+ MCDE_VAL2REG(MCDE_RISERR, ROTAFERIS_READ, __x)
+#define MCDE_RISERR_ROTBFERIS_WRITE_SHIFT 5
+#define MCDE_RISERR_ROTBFERIS_WRITE_MASK 0x00000020
+#define MCDE_RISERR_ROTBFERIS_WRITE(__x) \
+ MCDE_VAL2REG(MCDE_RISERR, ROTBFERIS_WRITE, __x)
+#define MCDE_RISERR_ROTBFERIS_READ_SHIFT 6
+#define MCDE_RISERR_ROTBFERIS_READ_MASK 0x00000040
+#define MCDE_RISERR_ROTBFERIS_READ(__x) \
+ MCDE_VAL2REG(MCDE_RISERR, ROTBFERIS_READ, __x)
+#define MCDE_RISERR_FUC0RIS_SHIFT 7
+#define MCDE_RISERR_FUC0RIS_MASK 0x00000080
+#define MCDE_RISERR_FUC0RIS(__x) \
+ MCDE_VAL2REG(MCDE_RISERR, FUC0RIS, __x)
+#define MCDE_RISERR_FUC1RIS_SHIFT 8
+#define MCDE_RISERR_FUC1RIS_MASK 0x00000100
+#define MCDE_RISERR_FUC1RIS(__x) \
+ MCDE_VAL2REG(MCDE_RISERR, FUC1RIS, __x)
+#define MCDE_RISERR_OVLFERRRIS_SHIFT 16
+#define MCDE_RISERR_OVLFERRRIS_MASK 0xFFFF0000
+#define MCDE_RISERR_OVLFERRRIS(__x) \
+ MCDE_VAL2REG(MCDE_RISERR, OVLFERRRIS, __x)
+#define MCDE_MISPP 0x00000124
+#define MCDE_MISPP_VCMPAMIS_SHIFT 0
+#define MCDE_MISPP_VCMPAMIS_MASK 0x00000001
+#define MCDE_MISPP_VCMPAMIS(__x) \
+ MCDE_VAL2REG(MCDE_MISPP, VCMPAMIS, __x)
+#define MCDE_MISPP_VCMPBMIS_SHIFT 1
+#define MCDE_MISPP_VCMPBMIS_MASK 0x00000002
+#define MCDE_MISPP_VCMPBMIS(__x) \
+ MCDE_VAL2REG(MCDE_MISPP, VCMPBMIS, __x)
+#define MCDE_MISPP_VSCC0MIS_SHIFT 2
+#define MCDE_MISPP_VSCC0MIS_MASK 0x00000004
+#define MCDE_MISPP_VSCC0MIS(__x) \
+ MCDE_VAL2REG(MCDE_MISPP, VSCC0MIS, __x)
+#define MCDE_MISPP_VSCC1MIS_SHIFT 3
+#define MCDE_MISPP_VSCC1MIS_MASK 0x00000008
+#define MCDE_MISPP_VSCC1MIS(__x) \
+ MCDE_VAL2REG(MCDE_MISPP, VSCC1MIS, __x)
+#define MCDE_MISPP_VCMPC0MIS_SHIFT 4
+#define MCDE_MISPP_VCMPC0MIS_MASK 0x00000010
+#define MCDE_MISPP_VCMPC0MIS(__x) \
+ MCDE_VAL2REG(MCDE_MISPP, VCMPC0MIS, __x)
+#define MCDE_MISPP_VCMPC1MIS_SHIFT 5
+#define MCDE_MISPP_VCMPC1MIS_MASK 0x00000020
+#define MCDE_MISPP_VCMPC1MIS(__x) \
+ MCDE_VAL2REG(MCDE_MISPP, VCMPC1MIS, __x)
+#define MCDE_MISPP_ROTFDMIS_A_SHIFT 6
+#define MCDE_MISPP_ROTFDMIS_A_MASK 0x00000040
+#define MCDE_MISPP_ROTFDMIS_A(__x) \
+ MCDE_VAL2REG(MCDE_MISPP, ROTFDMIS_A, __x)
+#define MCDE_MISPP_ROTFDMIS_B_SHIFT 7
+#define MCDE_MISPP_ROTFDMIS_B_MASK 0x00000080
+#define MCDE_MISPP_ROTFDMIS_B(__x) \
+ MCDE_VAL2REG(MCDE_MISPP, ROTFDMIS_B, __x)
+#define MCDE_MISOVL 0x00000128
+#define MCDE_MISOVL_OVLRDMIS_SHIFT 0
+#define MCDE_MISOVL_OVLRDMIS_MASK 0x0000FFFF
+#define MCDE_MISOVL_OVLRDMIS(__x) \
+ MCDE_VAL2REG(MCDE_MISOVL, OVLRDMIS, __x)
+#define MCDE_MISOVL_OVLFDMIS_SHIFT 16
+#define MCDE_MISOVL_OVLFDMIS_MASK 0xFFFF0000
+#define MCDE_MISOVL_OVLFDMIS(__x) \
+ MCDE_VAL2REG(MCDE_MISOVL, OVLFDMIS, __x)
+#define MCDE_MISCHNL 0x0000012C
+#define MCDE_MISCHNL_CHNLRDMIS_SHIFT 0
+#define MCDE_MISCHNL_CHNLRDMIS_MASK 0x0000FFFF
+#define MCDE_MISCHNL_CHNLRDMIS(__x) \
+ MCDE_VAL2REG(MCDE_MISCHNL, CHNLRDMIS, __x)
+#define MCDE_MISCHNL_CHNLAMIS_SHIFT 16
+#define MCDE_MISCHNL_CHNLAMIS_MASK 0xFFFF0000
+#define MCDE_MISCHNL_CHNLAMIS(__x) \
+ MCDE_VAL2REG(MCDE_MISCHNL, CHNLAMIS, __x)
+#define MCDE_MISERR 0x00000130
+#define MCDE_MISERR_FUAMIS_SHIFT 0
+#define MCDE_MISERR_FUAMIS_MASK 0x00000001
+#define MCDE_MISERR_FUAMIS(__x) \
+ MCDE_VAL2REG(MCDE_MISERR, FUAMIS, __x)
+#define MCDE_MISERR_FUBMIS_SHIFT 1
+#define MCDE_MISERR_FUBMIS_MASK 0x00000002
+#define MCDE_MISERR_FUBMIS(__x) \
+ MCDE_VAL2REG(MCDE_MISERR, FUBMIS, __x)
+#define MCDE_MISERR_SCHBLCKDMIS_SHIFT 2
+#define MCDE_MISERR_SCHBLCKDMIS_MASK 0x00000004
+#define MCDE_MISERR_SCHBLCKDMIS(__x) \
+ MCDE_VAL2REG(MCDE_MISERR, SCHBLCKDMIS, __x)
+#define MCDE_MISERR_ROTAFEMIS_WRITE_SHIFT 3
+#define MCDE_MISERR_ROTAFEMIS_WRITE_MASK 0x00000008
+#define MCDE_MISERR_ROTAFEMIS_WRITE(__x) \
+ MCDE_VAL2REG(MCDE_MISERR, ROTAFEMIS_WRITE, __x)
+#define MCDE_MISERR_ROTAFEMIS_READ_SHIFT 4
+#define MCDE_MISERR_ROTAFEMIS_READ_MASK 0x00000010
+#define MCDE_MISERR_ROTAFEMIS_READ(__x) \
+ MCDE_VAL2REG(MCDE_MISERR, ROTAFEMIS_READ, __x)
+#define MCDE_MISERR_ROTBFEMIS_WRITE_SHIFT 5
+#define MCDE_MISERR_ROTBFEMIS_WRITE_MASK 0x00000020
+#define MCDE_MISERR_ROTBFEMIS_WRITE(__x) \
+ MCDE_VAL2REG(MCDE_MISERR, ROTBFEMIS_WRITE, __x)
+#define MCDE_MISERR_ROTBFEMIS_READ_SHIFT 6
+#define MCDE_MISERR_ROTBFEMIS_READ_MASK 0x00000040
+#define MCDE_MISERR_ROTBFEMIS_READ(__x) \
+ MCDE_VAL2REG(MCDE_MISERR, ROTBFEMIS_READ, __x)
+#define MCDE_MISERR_FUC0MIS_SHIFT 7
+#define MCDE_MISERR_FUC0MIS_MASK 0x00000080
+#define MCDE_MISERR_FUC0MIS(__x) \
+ MCDE_VAL2REG(MCDE_MISERR, FUC0MIS, __x)
+#define MCDE_MISERR_FUC1MIS_SHIFT 8
+#define MCDE_MISERR_FUC1MIS_MASK 0x00000100
+#define MCDE_MISERR_FUC1MIS(__x) \
+ MCDE_VAL2REG(MCDE_MISERR, FUC1MIS, __x)
+#define MCDE_MISERR_OVLFERMIS_SHIFT 16
+#define MCDE_MISERR_OVLFERMIS_MASK 0xFFFF0000
+#define MCDE_MISERR_OVLFERMIS(__x) \
+ MCDE_VAL2REG(MCDE_MISERR, OVLFERMIS, __x)
+#define MCDE_SISPP 0x00000134
+#define MCDE_SISPP_VCMPASIS_SHIFT 0
+#define MCDE_SISPP_VCMPASIS_MASK 0x00000001
+#define MCDE_SISPP_VCMPASIS(__x) \
+ MCDE_VAL2REG(MCDE_SISPP, VCMPASIS, __x)
+#define MCDE_SISPP_VCMPBSIS_SHIFT 1
+#define MCDE_SISPP_VCMPBSIS_MASK 0x00000002
+#define MCDE_SISPP_VCMPBSIS(__x) \
+ MCDE_VAL2REG(MCDE_SISPP, VCMPBSIS, __x)
+#define MCDE_SISPP_VSCC0SIS_SHIFT 2
+#define MCDE_SISPP_VSCC0SIS_MASK 0x00000004
+#define MCDE_SISPP_VSCC0SIS(__x) \
+ MCDE_VAL2REG(MCDE_SISPP, VSCC0SIS, __x)
+#define MCDE_SISPP_VSCC1SIS_SHIFT 3
+#define MCDE_SISPP_VSCC1SIS_MASK 0x00000008
+#define MCDE_SISPP_VSCC1SIS(__x) \
+ MCDE_VAL2REG(MCDE_SISPP, VSCC1SIS, __x)
+#define MCDE_SISPP_VCMPC0SIS_SHIFT 4
+#define MCDE_SISPP_VCMPC0SIS_MASK 0x00000010
+#define MCDE_SISPP_VCMPC0SIS(__x) \
+ MCDE_VAL2REG(MCDE_SISPP, VCMPC0SIS, __x)
+#define MCDE_SISPP_VCMPC1SIS_SHIFT 5
+#define MCDE_SISPP_VCMPC1SIS_MASK 0x00000020
+#define MCDE_SISPP_VCMPC1SIS(__x) \
+ MCDE_VAL2REG(MCDE_SISPP, VCMPC1SIS, __x)
+#define MCDE_SISPP_ROTFDSIS_A_SHIFT 6
+#define MCDE_SISPP_ROTFDSIS_A_MASK 0x00000040
+#define MCDE_SISPP_ROTFDSIS_A(__x) \
+ MCDE_VAL2REG(MCDE_SISPP, ROTFDSIS_A, __x)
+#define MCDE_SISPP_ROTFDSIS_B_SHIFT 7
+#define MCDE_SISPP_ROTFDSIS_B_MASK 0x00000080
+#define MCDE_SISPP_ROTFDSIS_B(__x) \
+ MCDE_VAL2REG(MCDE_SISPP, ROTFDSIS_B, __x)
+#define MCDE_SISOVL 0x00000138
+#define MCDE_SISOVL_OVLRDSIS_SHIFT 0
+#define MCDE_SISOVL_OVLRDSIS_MASK 0x0000FFFF
+#define MCDE_SISOVL_OVLRDSIS(__x) \
+ MCDE_VAL2REG(MCDE_SISOVL, OVLRDSIS, __x)
+#define MCDE_SISOVL_OVLFDSIS_SHIFT 16
+#define MCDE_SISOVL_OVLFDSIS_MASK 0xFFFF0000
+#define MCDE_SISOVL_OVLFDSIS(__x) \
+ MCDE_VAL2REG(MCDE_SISOVL, OVLFDSIS, __x)
+#define MCDE_SISCHNL 0x0000013C
+#define MCDE_SISCHNL_CHNLRDSIS_SHIFT 0
+#define MCDE_SISCHNL_CHNLRDSIS_MASK 0x0000FFFF
+#define MCDE_SISCHNL_CHNLRDSIS(__x) \
+ MCDE_VAL2REG(MCDE_SISCHNL, CHNLRDSIS, __x)
+#define MCDE_SISCHNL_CHNLASIS_SHIFT 16
+#define MCDE_SISCHNL_CHNLASIS_MASK 0xFFFF0000
+#define MCDE_SISCHNL_CHNLASIS(__x) \
+ MCDE_VAL2REG(MCDE_SISCHNL, CHNLASIS, __x)
+#define MCDE_SISERR 0x00000140
+#define MCDE_SISERR_FUASIS_SHIFT 0
+#define MCDE_SISERR_FUASIS_MASK 0x00000001
+#define MCDE_SISERR_FUASIS(__x) \
+ MCDE_VAL2REG(MCDE_SISERR, FUASIS, __x)
+#define MCDE_SISERR_FUBSIS_SHIFT 1
+#define MCDE_SISERR_FUBSIS_MASK 0x00000002
+#define MCDE_SISERR_FUBSIS(__x) \
+ MCDE_VAL2REG(MCDE_SISERR, FUBSIS, __x)
+#define MCDE_SISERR_SCHBLCKDSIS_SHIFT 2
+#define MCDE_SISERR_SCHBLCKDSIS_MASK 0x00000004
+#define MCDE_SISERR_SCHBLCKDSIS(__x) \
+ MCDE_VAL2REG(MCDE_SISERR, SCHBLCKDSIS, __x)
+#define MCDE_SISERR_ROTAFESIS_WRITE_SHIFT 3
+#define MCDE_SISERR_ROTAFESIS_WRITE_MASK 0x00000008
+#define MCDE_SISERR_ROTAFESIS_WRITE(__x) \
+ MCDE_VAL2REG(MCDE_SISERR, ROTAFESIS_WRITE, __x)
+#define MCDE_SISERR_ROTAFESIS_READ_SHIFT 4
+#define MCDE_SISERR_ROTAFESIS_READ_MASK 0x00000010
+#define MCDE_SISERR_ROTAFESIS_READ(__x) \
+ MCDE_VAL2REG(MCDE_SISERR, ROTAFESIS_READ, __x)
+#define MCDE_SISERR_ROTBFESIS_WRITE_SHIFT 5
+#define MCDE_SISERR_ROTBFESIS_WRITE_MASK 0x00000020
+#define MCDE_SISERR_ROTBFESIS_WRITE(__x) \
+ MCDE_VAL2REG(MCDE_SISERR, ROTBFESIS_WRITE, __x)
+#define MCDE_SISERR_ROTBFESIS_READ_SHIFT 6
+#define MCDE_SISERR_ROTBFESIS_READ_MASK 0x00000040
+#define MCDE_SISERR_ROTBFESIS_READ(__x) \
+ MCDE_VAL2REG(MCDE_SISERR, ROTBFESIS_READ, __x)
+#define MCDE_SISERR_FUC0SIS_SHIFT 7
+#define MCDE_SISERR_FUC0SIS_MASK 0x00000080
+#define MCDE_SISERR_FUC0SIS(__x) \
+ MCDE_VAL2REG(MCDE_SISERR, FUC0SIS, __x)
+#define MCDE_SISERR_FUC1SIS_SHIFT 8
+#define MCDE_SISERR_FUC1SIS_MASK 0x00000100
+#define MCDE_SISERR_FUC1SIS(__x) \
+ MCDE_VAL2REG(MCDE_SISERR, FUC1SIS, __x)
+#define MCDE_SISERR_OVLFERSIS_SHIFT 16
+#define MCDE_SISERR_OVLFERSIS_MASK 0xFFFF0000
+#define MCDE_SISERR_OVLFERSIS(__x) \
+ MCDE_VAL2REG(MCDE_SISERR, OVLFERSIS, __x)
+#define MCDE_PID 0x000001FC
+#define MCDE_PID_METALFIX_VERSION_SHIFT 0
+#define MCDE_PID_METALFIX_VERSION_MASK 0x000000FF
+#define MCDE_PID_METALFIX_VERSION(__x) \
+ MCDE_VAL2REG(MCDE_PID, METALFIX_VERSION, __x)
+#define MCDE_PID_DEVELOPMENT_VERSION_SHIFT 8
+#define MCDE_PID_DEVELOPMENT_VERSION_MASK 0x0000FF00
+#define MCDE_PID_DEVELOPMENT_VERSION(__x) \
+ MCDE_VAL2REG(MCDE_PID, DEVELOPMENT_VERSION, __x)
+#define MCDE_PID_MINOR_VERSION_SHIFT 16
+#define MCDE_PID_MINOR_VERSION_MASK 0x00FF0000
+#define MCDE_PID_MINOR_VERSION(__x) \
+ MCDE_VAL2REG(MCDE_PID, MINOR_VERSION, __x)
+#define MCDE_PID_MAJOR_VERSION_SHIFT 24
+#define MCDE_PID_MAJOR_VERSION_MASK 0xFF000000
+#define MCDE_PID_MAJOR_VERSION(__x) \
+ MCDE_VAL2REG(MCDE_PID, MAJOR_VERSION, __x)
+#define MCDE_EXTSRC0A0 0x00000200
+#define MCDE_EXTSRC0A0_GROUPOFFSET 0x20
+#define MCDE_EXTSRC0A0_BASEADDRESS0_SHIFT 3
+#define MCDE_EXTSRC0A0_BASEADDRESS0_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC0A0_BASEADDRESS0(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0A0, BASEADDRESS0, __x)
+#define MCDE_EXTSRC1A0 0x00000220
+#define MCDE_EXTSRC1A0_BASEADDRESS0_SHIFT 3
+#define MCDE_EXTSRC1A0_BASEADDRESS0_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC1A0_BASEADDRESS0(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1A0, BASEADDRESS0, __x)
+#define MCDE_EXTSRC2A0 0x00000240
+#define MCDE_EXTSRC2A0_BASEADDRESS0_SHIFT 3
+#define MCDE_EXTSRC2A0_BASEADDRESS0_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC2A0_BASEADDRESS0(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2A0, BASEADDRESS0, __x)
+#define MCDE_EXTSRC3A0 0x00000260
+#define MCDE_EXTSRC3A0_BASEADDRESS0_SHIFT 3
+#define MCDE_EXTSRC3A0_BASEADDRESS0_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC3A0_BASEADDRESS0(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3A0, BASEADDRESS0, __x)
+#define MCDE_EXTSRC4A0 0x00000280
+#define MCDE_EXTSRC4A0_BASEADDRESS0_SHIFT 3
+#define MCDE_EXTSRC4A0_BASEADDRESS0_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC4A0_BASEADDRESS0(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4A0, BASEADDRESS0, __x)
+#define MCDE_EXTSRC5A0 0x000002A0
+#define MCDE_EXTSRC5A0_BASEADDRESS0_SHIFT 3
+#define MCDE_EXTSRC5A0_BASEADDRESS0_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC5A0_BASEADDRESS0(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5A0, BASEADDRESS0, __x)
+#define MCDE_EXTSRC6A0 0x000002C0
+#define MCDE_EXTSRC6A0_BASEADDRESS0_SHIFT 3
+#define MCDE_EXTSRC6A0_BASEADDRESS0_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC6A0_BASEADDRESS0(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6A0, BASEADDRESS0, __x)
+#define MCDE_EXTSRC7A0 0x000002E0
+#define MCDE_EXTSRC7A0_BASEADDRESS0_SHIFT 3
+#define MCDE_EXTSRC7A0_BASEADDRESS0_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC7A0_BASEADDRESS0(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7A0, BASEADDRESS0, __x)
+#define MCDE_EXTSRC8A0 0x00000300
+#define MCDE_EXTSRC8A0_BASEADDRESS0_SHIFT 3
+#define MCDE_EXTSRC8A0_BASEADDRESS0_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC8A0_BASEADDRESS0(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8A0, BASEADDRESS0, __x)
+#define MCDE_EXTSRC9A0 0x00000320
+#define MCDE_EXTSRC9A0_BASEADDRESS0_SHIFT 3
+#define MCDE_EXTSRC9A0_BASEADDRESS0_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC9A0_BASEADDRESS0(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9A0, BASEADDRESS0, __x)
+#define MCDE_EXTSRC0A1 0x00000204
+#define MCDE_EXTSRC0A1_GROUPOFFSET 0x20
+#define MCDE_EXTSRC0A1_BASEADDRESS1_SHIFT 3
+#define MCDE_EXTSRC0A1_BASEADDRESS1_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC0A1_BASEADDRESS1(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0A1, BASEADDRESS1, __x)
+#define MCDE_EXTSRC1A1 0x00000224
+#define MCDE_EXTSRC1A1_BASEADDRESS1_SHIFT 3
+#define MCDE_EXTSRC1A1_BASEADDRESS1_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC1A1_BASEADDRESS1(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1A1, BASEADDRESS1, __x)
+#define MCDE_EXTSRC2A1 0x00000244
+#define MCDE_EXTSRC2A1_BASEADDRESS1_SHIFT 3
+#define MCDE_EXTSRC2A1_BASEADDRESS1_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC2A1_BASEADDRESS1(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2A1, BASEADDRESS1, __x)
+#define MCDE_EXTSRC3A1 0x00000264
+#define MCDE_EXTSRC3A1_BASEADDRESS1_SHIFT 3
+#define MCDE_EXTSRC3A1_BASEADDRESS1_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC3A1_BASEADDRESS1(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3A1, BASEADDRESS1, __x)
+#define MCDE_EXTSRC4A1 0x00000284
+#define MCDE_EXTSRC4A1_BASEADDRESS1_SHIFT 3
+#define MCDE_EXTSRC4A1_BASEADDRESS1_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC4A1_BASEADDRESS1(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4A1, BASEADDRESS1, __x)
+#define MCDE_EXTSRC5A1 0x000002A4
+#define MCDE_EXTSRC5A1_BASEADDRESS1_SHIFT 3
+#define MCDE_EXTSRC5A1_BASEADDRESS1_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC5A1_BASEADDRESS1(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5A1, BASEADDRESS1, __x)
+#define MCDE_EXTSRC6A1 0x000002C4
+#define MCDE_EXTSRC6A1_BASEADDRESS1_SHIFT 3
+#define MCDE_EXTSRC6A1_BASEADDRESS1_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC6A1_BASEADDRESS1(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6A1, BASEADDRESS1, __x)
+#define MCDE_EXTSRC7A1 0x000002E4
+#define MCDE_EXTSRC7A1_BASEADDRESS1_SHIFT 3
+#define MCDE_EXTSRC7A1_BASEADDRESS1_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC7A1_BASEADDRESS1(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7A1, BASEADDRESS1, __x)
+#define MCDE_EXTSRC8A1 0x00000304
+#define MCDE_EXTSRC8A1_BASEADDRESS1_SHIFT 3
+#define MCDE_EXTSRC8A1_BASEADDRESS1_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC8A1_BASEADDRESS1(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8A1, BASEADDRESS1, __x)
+#define MCDE_EXTSRC9A1 0x00000324
+#define MCDE_EXTSRC9A1_BASEADDRESS1_SHIFT 3
+#define MCDE_EXTSRC9A1_BASEADDRESS1_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC9A1_BASEADDRESS1(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9A1, BASEADDRESS1, __x)
+#define MCDE_EXTSRC6A2 0x000002C8
+#define MCDE_EXTSRC6A2_BASEADDRESS2_SHIFT 3
+#define MCDE_EXTSRC6A2_BASEADDRESS2_MASK 0xFFFFFFF8
+#define MCDE_EXTSRC6A2_BASEADDRESS2(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6A2, BASEADDRESS2, __x)
+#define MCDE_EXTSRC0CONF 0x0000020C
+#define MCDE_EXTSRC0CONF_GROUPOFFSET 0x20
+#define MCDE_EXTSRC0CONF_BUF_ID_SHIFT 0
+#define MCDE_EXTSRC0CONF_BUF_ID_MASK 0x00000003
+#define MCDE_EXTSRC0CONF_BUF_ID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CONF, BUF_ID, __x)
+#define MCDE_EXTSRC0CONF_BUF_NB_SHIFT 2
+#define MCDE_EXTSRC0CONF_BUF_NB_MASK 0x0000000C
+#define MCDE_EXTSRC0CONF_BUF_NB(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CONF, BUF_NB, __x)
+#define MCDE_EXTSRC0CONF_PRI_OVLID_SHIFT 4
+#define MCDE_EXTSRC0CONF_PRI_OVLID_MASK 0x000000F0
+#define MCDE_EXTSRC0CONF_PRI_OVLID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CONF, PRI_OVLID, __x)
+#define MCDE_EXTSRC0CONF_BPP_SHIFT 8
+#define MCDE_EXTSRC0CONF_BPP_MASK 0x00000F00
+#define MCDE_EXTSRC0CONF_BPP_1BPP_PAL 0
+#define MCDE_EXTSRC0CONF_BPP_2BPP_PAL 1
+#define MCDE_EXTSRC0CONF_BPP_4BPP_PAL 2
+#define MCDE_EXTSRC0CONF_BPP_8BPP_PAL 3
+#define MCDE_EXTSRC0CONF_BPP_RGB444 4
+#define MCDE_EXTSRC0CONF_BPP_ARGB4444 5
+#define MCDE_EXTSRC0CONF_BPP_IRGB1555 6
+#define MCDE_EXTSRC0CONF_BPP_RGB565 7
+#define MCDE_EXTSRC0CONF_BPP_RGB888 8
+#define MCDE_EXTSRC0CONF_BPP_XRGB8888 9
+#define MCDE_EXTSRC0CONF_BPP_ARGB8888 10
+#define MCDE_EXTSRC0CONF_BPP_YCBCR422 11
+#define MCDE_EXTSRC0CONF_BPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CONF, BPP, MCDE_EXTSRC0CONF_BPP_##__x)
+#define MCDE_EXTSRC0CONF_BPP(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CONF, BPP, __x)
+#define MCDE_EXTSRC0CONF_BGR_SHIFT 12
+#define MCDE_EXTSRC0CONF_BGR_MASK 0x00001000
+#define MCDE_EXTSRC0CONF_BGR_RGB 0
+#define MCDE_EXTSRC0CONF_BGR_BGR 1
+#define MCDE_EXTSRC0CONF_BGR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CONF, BGR, MCDE_EXTSRC0CONF_BGR_##__x)
+#define MCDE_EXTSRC0CONF_BGR(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CONF, BGR, __x)
+#define MCDE_EXTSRC0CONF_BEBO_SHIFT 13
+#define MCDE_EXTSRC0CONF_BEBO_MASK 0x00002000
+#define MCDE_EXTSRC0CONF_BEBO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC0CONF_BEBO_BIG_ENDIAN 1
+#define MCDE_EXTSRC0CONF_BEBO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CONF, BEBO, MCDE_EXTSRC0CONF_BEBO_##__x)
+#define MCDE_EXTSRC0CONF_BEBO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CONF, BEBO, __x)
+#define MCDE_EXTSRC0CONF_BEPO_SHIFT 14
+#define MCDE_EXTSRC0CONF_BEPO_MASK 0x00004000
+#define MCDE_EXTSRC0CONF_BEPO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC0CONF_BEPO_BIG_ENDIAN 1
+#define MCDE_EXTSRC0CONF_BEPO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CONF, BEPO, MCDE_EXTSRC0CONF_BEPO_##__x)
+#define MCDE_EXTSRC0CONF_BEPO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CONF, BEPO, __x)
+#define MCDE_EXTSRC1CONF 0x0000022C
+#define MCDE_EXTSRC1CONF_BUF_ID_SHIFT 0
+#define MCDE_EXTSRC1CONF_BUF_ID_MASK 0x00000003
+#define MCDE_EXTSRC1CONF_BUF_ID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CONF, BUF_ID, __x)
+#define MCDE_EXTSRC1CONF_BUF_NB_SHIFT 2
+#define MCDE_EXTSRC1CONF_BUF_NB_MASK 0x0000000C
+#define MCDE_EXTSRC1CONF_BUF_NB(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CONF, BUF_NB, __x)
+#define MCDE_EXTSRC1CONF_PRI_OVLID_SHIFT 4
+#define MCDE_EXTSRC1CONF_PRI_OVLID_MASK 0x000000F0
+#define MCDE_EXTSRC1CONF_PRI_OVLID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CONF, PRI_OVLID, __x)
+#define MCDE_EXTSRC1CONF_BPP_SHIFT 8
+#define MCDE_EXTSRC1CONF_BPP_MASK 0x00000F00
+#define MCDE_EXTSRC1CONF_BPP_1BPP_PAL 0
+#define MCDE_EXTSRC1CONF_BPP_2BPP_PAL 1
+#define MCDE_EXTSRC1CONF_BPP_4BPP_PAL 2
+#define MCDE_EXTSRC1CONF_BPP_8BPP_PAL 3
+#define MCDE_EXTSRC1CONF_BPP_RGB444 4
+#define MCDE_EXTSRC1CONF_BPP_ARGB4444 5
+#define MCDE_EXTSRC1CONF_BPP_IRGB1555 6
+#define MCDE_EXTSRC1CONF_BPP_RGB565 7
+#define MCDE_EXTSRC1CONF_BPP_RGB888 8
+#define MCDE_EXTSRC1CONF_BPP_XRGB8888 9
+#define MCDE_EXTSRC1CONF_BPP_ARGB8888 10
+#define MCDE_EXTSRC1CONF_BPP_YCBCR422 11
+#define MCDE_EXTSRC1CONF_BPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CONF, BPP, MCDE_EXTSRC1CONF_BPP_##__x)
+#define MCDE_EXTSRC1CONF_BPP(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CONF, BPP, __x)
+#define MCDE_EXTSRC1CONF_BGR_SHIFT 12
+#define MCDE_EXTSRC1CONF_BGR_MASK 0x00001000
+#define MCDE_EXTSRC1CONF_BGR_RGB 0
+#define MCDE_EXTSRC1CONF_BGR_BGR 1
+#define MCDE_EXTSRC1CONF_BGR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CONF, BGR, MCDE_EXTSRC1CONF_BGR_##__x)
+#define MCDE_EXTSRC1CONF_BGR(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CONF, BGR, __x)
+#define MCDE_EXTSRC1CONF_BEBO_SHIFT 13
+#define MCDE_EXTSRC1CONF_BEBO_MASK 0x00002000
+#define MCDE_EXTSRC1CONF_BEBO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC1CONF_BEBO_BIG_ENDIAN 1
+#define MCDE_EXTSRC1CONF_BEBO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CONF, BEBO, MCDE_EXTSRC1CONF_BEBO_##__x)
+#define MCDE_EXTSRC1CONF_BEBO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CONF, BEBO, __x)
+#define MCDE_EXTSRC1CONF_BEPO_SHIFT 14
+#define MCDE_EXTSRC1CONF_BEPO_MASK 0x00004000
+#define MCDE_EXTSRC1CONF_BEPO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC1CONF_BEPO_BIG_ENDIAN 1
+#define MCDE_EXTSRC1CONF_BEPO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CONF, BEPO, MCDE_EXTSRC1CONF_BEPO_##__x)
+#define MCDE_EXTSRC1CONF_BEPO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CONF, BEPO, __x)
+#define MCDE_EXTSRC2CONF 0x0000024C
+#define MCDE_EXTSRC2CONF_BUF_ID_SHIFT 0
+#define MCDE_EXTSRC2CONF_BUF_ID_MASK 0x00000003
+#define MCDE_EXTSRC2CONF_BUF_ID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CONF, BUF_ID, __x)
+#define MCDE_EXTSRC2CONF_BUF_NB_SHIFT 2
+#define MCDE_EXTSRC2CONF_BUF_NB_MASK 0x0000000C
+#define MCDE_EXTSRC2CONF_BUF_NB(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CONF, BUF_NB, __x)
+#define MCDE_EXTSRC2CONF_PRI_OVLID_SHIFT 4
+#define MCDE_EXTSRC2CONF_PRI_OVLID_MASK 0x000000F0
+#define MCDE_EXTSRC2CONF_PRI_OVLID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CONF, PRI_OVLID, __x)
+#define MCDE_EXTSRC2CONF_BPP_SHIFT 8
+#define MCDE_EXTSRC2CONF_BPP_MASK 0x00000F00
+#define MCDE_EXTSRC2CONF_BPP_1BPP_PAL 0
+#define MCDE_EXTSRC2CONF_BPP_2BPP_PAL 1
+#define MCDE_EXTSRC2CONF_BPP_4BPP_PAL 2
+#define MCDE_EXTSRC2CONF_BPP_8BPP_PAL 3
+#define MCDE_EXTSRC2CONF_BPP_RGB444 4
+#define MCDE_EXTSRC2CONF_BPP_ARGB4444 5
+#define MCDE_EXTSRC2CONF_BPP_IRGB1555 6
+#define MCDE_EXTSRC2CONF_BPP_RGB565 7
+#define MCDE_EXTSRC2CONF_BPP_RGB888 8
+#define MCDE_EXTSRC2CONF_BPP_XRGB8888 9
+#define MCDE_EXTSRC2CONF_BPP_ARGB8888 10
+#define MCDE_EXTSRC2CONF_BPP_YCBCR422 11
+#define MCDE_EXTSRC2CONF_BPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CONF, BPP, MCDE_EXTSRC2CONF_BPP_##__x)
+#define MCDE_EXTSRC2CONF_BPP(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CONF, BPP, __x)
+#define MCDE_EXTSRC2CONF_BGR_SHIFT 12
+#define MCDE_EXTSRC2CONF_BGR_MASK 0x00001000
+#define MCDE_EXTSRC2CONF_BGR_RGB 0
+#define MCDE_EXTSRC2CONF_BGR_BGR 1
+#define MCDE_EXTSRC2CONF_BGR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CONF, BGR, MCDE_EXTSRC2CONF_BGR_##__x)
+#define MCDE_EXTSRC2CONF_BGR(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CONF, BGR, __x)
+#define MCDE_EXTSRC2CONF_BEBO_SHIFT 13
+#define MCDE_EXTSRC2CONF_BEBO_MASK 0x00002000
+#define MCDE_EXTSRC2CONF_BEBO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC2CONF_BEBO_BIG_ENDIAN 1
+#define MCDE_EXTSRC2CONF_BEBO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CONF, BEBO, MCDE_EXTSRC2CONF_BEBO_##__x)
+#define MCDE_EXTSRC2CONF_BEBO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CONF, BEBO, __x)
+#define MCDE_EXTSRC2CONF_BEPO_SHIFT 14
+#define MCDE_EXTSRC2CONF_BEPO_MASK 0x00004000
+#define MCDE_EXTSRC2CONF_BEPO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC2CONF_BEPO_BIG_ENDIAN 1
+#define MCDE_EXTSRC2CONF_BEPO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CONF, BEPO, MCDE_EXTSRC2CONF_BEPO_##__x)
+#define MCDE_EXTSRC2CONF_BEPO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CONF, BEPO, __x)
+#define MCDE_EXTSRC3CONF 0x0000026C
+#define MCDE_EXTSRC3CONF_BUF_ID_SHIFT 0
+#define MCDE_EXTSRC3CONF_BUF_ID_MASK 0x00000003
+#define MCDE_EXTSRC3CONF_BUF_ID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CONF, BUF_ID, __x)
+#define MCDE_EXTSRC3CONF_BUF_NB_SHIFT 2
+#define MCDE_EXTSRC3CONF_BUF_NB_MASK 0x0000000C
+#define MCDE_EXTSRC3CONF_BUF_NB(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CONF, BUF_NB, __x)
+#define MCDE_EXTSRC3CONF_PRI_OVLID_SHIFT 4
+#define MCDE_EXTSRC3CONF_PRI_OVLID_MASK 0x000000F0
+#define MCDE_EXTSRC3CONF_PRI_OVLID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CONF, PRI_OVLID, __x)
+#define MCDE_EXTSRC3CONF_BPP_SHIFT 8
+#define MCDE_EXTSRC3CONF_BPP_MASK 0x00000F00
+#define MCDE_EXTSRC3CONF_BPP_1BPP_PAL 0
+#define MCDE_EXTSRC3CONF_BPP_2BPP_PAL 1
+#define MCDE_EXTSRC3CONF_BPP_4BPP_PAL 2
+#define MCDE_EXTSRC3CONF_BPP_8BPP_PAL 3
+#define MCDE_EXTSRC3CONF_BPP_RGB444 4
+#define MCDE_EXTSRC3CONF_BPP_ARGB4444 5
+#define MCDE_EXTSRC3CONF_BPP_IRGB1555 6
+#define MCDE_EXTSRC3CONF_BPP_RGB565 7
+#define MCDE_EXTSRC3CONF_BPP_RGB888 8
+#define MCDE_EXTSRC3CONF_BPP_XRGB8888 9
+#define MCDE_EXTSRC3CONF_BPP_ARGB8888 10
+#define MCDE_EXTSRC3CONF_BPP_YCBCR422 11
+#define MCDE_EXTSRC3CONF_BPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CONF, BPP, MCDE_EXTSRC3CONF_BPP_##__x)
+#define MCDE_EXTSRC3CONF_BPP(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CONF, BPP, __x)
+#define MCDE_EXTSRC3CONF_BGR_SHIFT 12
+#define MCDE_EXTSRC3CONF_BGR_MASK 0x00001000
+#define MCDE_EXTSRC3CONF_BGR_RGB 0
+#define MCDE_EXTSRC3CONF_BGR_BGR 1
+#define MCDE_EXTSRC3CONF_BGR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CONF, BGR, MCDE_EXTSRC3CONF_BGR_##__x)
+#define MCDE_EXTSRC3CONF_BGR(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CONF, BGR, __x)
+#define MCDE_EXTSRC3CONF_BEBO_SHIFT 13
+#define MCDE_EXTSRC3CONF_BEBO_MASK 0x00002000
+#define MCDE_EXTSRC3CONF_BEBO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC3CONF_BEBO_BIG_ENDIAN 1
+#define MCDE_EXTSRC3CONF_BEBO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CONF, BEBO, MCDE_EXTSRC3CONF_BEBO_##__x)
+#define MCDE_EXTSRC3CONF_BEBO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CONF, BEBO, __x)
+#define MCDE_EXTSRC3CONF_BEPO_SHIFT 14
+#define MCDE_EXTSRC3CONF_BEPO_MASK 0x00004000
+#define MCDE_EXTSRC3CONF_BEPO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC3CONF_BEPO_BIG_ENDIAN 1
+#define MCDE_EXTSRC3CONF_BEPO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CONF, BEPO, MCDE_EXTSRC3CONF_BEPO_##__x)
+#define MCDE_EXTSRC3CONF_BEPO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CONF, BEPO, __x)
+#define MCDE_EXTSRC4CONF 0x0000028C
+#define MCDE_EXTSRC4CONF_BUF_ID_SHIFT 0
+#define MCDE_EXTSRC4CONF_BUF_ID_MASK 0x00000003
+#define MCDE_EXTSRC4CONF_BUF_ID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CONF, BUF_ID, __x)
+#define MCDE_EXTSRC4CONF_BUF_NB_SHIFT 2
+#define MCDE_EXTSRC4CONF_BUF_NB_MASK 0x0000000C
+#define MCDE_EXTSRC4CONF_BUF_NB(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CONF, BUF_NB, __x)
+#define MCDE_EXTSRC4CONF_PRI_OVLID_SHIFT 4
+#define MCDE_EXTSRC4CONF_PRI_OVLID_MASK 0x000000F0
+#define MCDE_EXTSRC4CONF_PRI_OVLID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CONF, PRI_OVLID, __x)
+#define MCDE_EXTSRC4CONF_BPP_SHIFT 8
+#define MCDE_EXTSRC4CONF_BPP_MASK 0x00000F00
+#define MCDE_EXTSRC4CONF_BPP_1BPP_PAL 0
+#define MCDE_EXTSRC4CONF_BPP_2BPP_PAL 1
+#define MCDE_EXTSRC4CONF_BPP_4BPP_PAL 2
+#define MCDE_EXTSRC4CONF_BPP_8BPP_PAL 3
+#define MCDE_EXTSRC4CONF_BPP_RGB444 4
+#define MCDE_EXTSRC4CONF_BPP_ARGB4444 5
+#define MCDE_EXTSRC4CONF_BPP_IRGB1555 6
+#define MCDE_EXTSRC4CONF_BPP_RGB565 7
+#define MCDE_EXTSRC4CONF_BPP_RGB888 8
+#define MCDE_EXTSRC4CONF_BPP_XRGB8888 9
+#define MCDE_EXTSRC4CONF_BPP_ARGB8888 10
+#define MCDE_EXTSRC4CONF_BPP_YCBCR422 11
+#define MCDE_EXTSRC4CONF_BPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CONF, BPP, MCDE_EXTSRC4CONF_BPP_##__x)
+#define MCDE_EXTSRC4CONF_BPP(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CONF, BPP, __x)
+#define MCDE_EXTSRC4CONF_BGR_SHIFT 12
+#define MCDE_EXTSRC4CONF_BGR_MASK 0x00001000
+#define MCDE_EXTSRC4CONF_BGR_RGB 0
+#define MCDE_EXTSRC4CONF_BGR_BGR 1
+#define MCDE_EXTSRC4CONF_BGR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CONF, BGR, MCDE_EXTSRC4CONF_BGR_##__x)
+#define MCDE_EXTSRC4CONF_BGR(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CONF, BGR, __x)
+#define MCDE_EXTSRC4CONF_BEBO_SHIFT 13
+#define MCDE_EXTSRC4CONF_BEBO_MASK 0x00002000
+#define MCDE_EXTSRC4CONF_BEBO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC4CONF_BEBO_BIG_ENDIAN 1
+#define MCDE_EXTSRC4CONF_BEBO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CONF, BEBO, MCDE_EXTSRC4CONF_BEBO_##__x)
+#define MCDE_EXTSRC4CONF_BEBO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CONF, BEBO, __x)
+#define MCDE_EXTSRC4CONF_BEPO_SHIFT 14
+#define MCDE_EXTSRC4CONF_BEPO_MASK 0x00004000
+#define MCDE_EXTSRC4CONF_BEPO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC4CONF_BEPO_BIG_ENDIAN 1
+#define MCDE_EXTSRC4CONF_BEPO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CONF, BEPO, MCDE_EXTSRC4CONF_BEPO_##__x)
+#define MCDE_EXTSRC4CONF_BEPO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CONF, BEPO, __x)
+#define MCDE_EXTSRC5CONF 0x000002AC
+#define MCDE_EXTSRC5CONF_BUF_ID_SHIFT 0
+#define MCDE_EXTSRC5CONF_BUF_ID_MASK 0x00000003
+#define MCDE_EXTSRC5CONF_BUF_ID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CONF, BUF_ID, __x)
+#define MCDE_EXTSRC5CONF_BUF_NB_SHIFT 2
+#define MCDE_EXTSRC5CONF_BUF_NB_MASK 0x0000000C
+#define MCDE_EXTSRC5CONF_BUF_NB(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CONF, BUF_NB, __x)
+#define MCDE_EXTSRC5CONF_PRI_OVLID_SHIFT 4
+#define MCDE_EXTSRC5CONF_PRI_OVLID_MASK 0x000000F0
+#define MCDE_EXTSRC5CONF_PRI_OVLID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CONF, PRI_OVLID, __x)
+#define MCDE_EXTSRC5CONF_BPP_SHIFT 8
+#define MCDE_EXTSRC5CONF_BPP_MASK 0x00000F00
+#define MCDE_EXTSRC5CONF_BPP_1BPP_PAL 0
+#define MCDE_EXTSRC5CONF_BPP_2BPP_PAL 1
+#define MCDE_EXTSRC5CONF_BPP_4BPP_PAL 2
+#define MCDE_EXTSRC5CONF_BPP_8BPP_PAL 3
+#define MCDE_EXTSRC5CONF_BPP_RGB444 4
+#define MCDE_EXTSRC5CONF_BPP_ARGB4444 5
+#define MCDE_EXTSRC5CONF_BPP_IRGB1555 6
+#define MCDE_EXTSRC5CONF_BPP_RGB565 7
+#define MCDE_EXTSRC5CONF_BPP_RGB888 8
+#define MCDE_EXTSRC5CONF_BPP_XRGB8888 9
+#define MCDE_EXTSRC5CONF_BPP_ARGB8888 10
+#define MCDE_EXTSRC5CONF_BPP_YCBCR422 11
+#define MCDE_EXTSRC5CONF_BPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CONF, BPP, MCDE_EXTSRC5CONF_BPP_##__x)
+#define MCDE_EXTSRC5CONF_BPP(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CONF, BPP, __x)
+#define MCDE_EXTSRC5CONF_BGR_SHIFT 12
+#define MCDE_EXTSRC5CONF_BGR_MASK 0x00001000
+#define MCDE_EXTSRC5CONF_BGR_RGB 0
+#define MCDE_EXTSRC5CONF_BGR_BGR 1
+#define MCDE_EXTSRC5CONF_BGR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CONF, BGR, MCDE_EXTSRC5CONF_BGR_##__x)
+#define MCDE_EXTSRC5CONF_BGR(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CONF, BGR, __x)
+#define MCDE_EXTSRC5CONF_BEBO_SHIFT 13
+#define MCDE_EXTSRC5CONF_BEBO_MASK 0x00002000
+#define MCDE_EXTSRC5CONF_BEBO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC5CONF_BEBO_BIG_ENDIAN 1
+#define MCDE_EXTSRC5CONF_BEBO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CONF, BEBO, MCDE_EXTSRC5CONF_BEBO_##__x)
+#define MCDE_EXTSRC5CONF_BEBO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CONF, BEBO, __x)
+#define MCDE_EXTSRC5CONF_BEPO_SHIFT 14
+#define MCDE_EXTSRC5CONF_BEPO_MASK 0x00004000
+#define MCDE_EXTSRC5CONF_BEPO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC5CONF_BEPO_BIG_ENDIAN 1
+#define MCDE_EXTSRC5CONF_BEPO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CONF, BEPO, MCDE_EXTSRC5CONF_BEPO_##__x)
+#define MCDE_EXTSRC5CONF_BEPO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CONF, BEPO, __x)
+#define MCDE_EXTSRC6CONF 0x000002CC
+#define MCDE_EXTSRC6CONF_BUF_ID_SHIFT 0
+#define MCDE_EXTSRC6CONF_BUF_ID_MASK 0x00000003
+#define MCDE_EXTSRC6CONF_BUF_ID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CONF, BUF_ID, __x)
+#define MCDE_EXTSRC6CONF_BUF_NB_SHIFT 2
+#define MCDE_EXTSRC6CONF_BUF_NB_MASK 0x0000000C
+#define MCDE_EXTSRC6CONF_BUF_NB(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CONF, BUF_NB, __x)
+#define MCDE_EXTSRC6CONF_PRI_OVLID_SHIFT 4
+#define MCDE_EXTSRC6CONF_PRI_OVLID_MASK 0x000000F0
+#define MCDE_EXTSRC6CONF_PRI_OVLID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CONF, PRI_OVLID, __x)
+#define MCDE_EXTSRC6CONF_BPP_SHIFT 8
+#define MCDE_EXTSRC6CONF_BPP_MASK 0x00000F00
+#define MCDE_EXTSRC6CONF_BPP_1BPP_PAL 0
+#define MCDE_EXTSRC6CONF_BPP_2BPP_PAL 1
+#define MCDE_EXTSRC6CONF_BPP_4BPP_PAL 2
+#define MCDE_EXTSRC6CONF_BPP_8BPP_PAL 3
+#define MCDE_EXTSRC6CONF_BPP_RGB444 4
+#define MCDE_EXTSRC6CONF_BPP_ARGB4444 5
+#define MCDE_EXTSRC6CONF_BPP_IRGB1555 6
+#define MCDE_EXTSRC6CONF_BPP_RGB565 7
+#define MCDE_EXTSRC6CONF_BPP_RGB888 8
+#define MCDE_EXTSRC6CONF_BPP_XRGB8888 9
+#define MCDE_EXTSRC6CONF_BPP_ARGB8888 10
+#define MCDE_EXTSRC6CONF_BPP_YCBCR422 11
+#define MCDE_EXTSRC6CONF_BPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CONF, BPP, MCDE_EXTSRC6CONF_BPP_##__x)
+#define MCDE_EXTSRC6CONF_BPP(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CONF, BPP, __x)
+#define MCDE_EXTSRC6CONF_BGR_SHIFT 12
+#define MCDE_EXTSRC6CONF_BGR_MASK 0x00001000
+#define MCDE_EXTSRC6CONF_BGR_RGB 0
+#define MCDE_EXTSRC6CONF_BGR_BGR 1
+#define MCDE_EXTSRC6CONF_BGR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CONF, BGR, MCDE_EXTSRC6CONF_BGR_##__x)
+#define MCDE_EXTSRC6CONF_BGR(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CONF, BGR, __x)
+#define MCDE_EXTSRC6CONF_BEBO_SHIFT 13
+#define MCDE_EXTSRC6CONF_BEBO_MASK 0x00002000
+#define MCDE_EXTSRC6CONF_BEBO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC6CONF_BEBO_BIG_ENDIAN 1
+#define MCDE_EXTSRC6CONF_BEBO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CONF, BEBO, MCDE_EXTSRC6CONF_BEBO_##__x)
+#define MCDE_EXTSRC6CONF_BEBO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CONF, BEBO, __x)
+#define MCDE_EXTSRC6CONF_BEPO_SHIFT 14
+#define MCDE_EXTSRC6CONF_BEPO_MASK 0x00004000
+#define MCDE_EXTSRC6CONF_BEPO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC6CONF_BEPO_BIG_ENDIAN 1
+#define MCDE_EXTSRC6CONF_BEPO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CONF, BEPO, MCDE_EXTSRC6CONF_BEPO_##__x)
+#define MCDE_EXTSRC6CONF_BEPO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CONF, BEPO, __x)
+#define MCDE_EXTSRC7CONF 0x000002EC
+#define MCDE_EXTSRC7CONF_BUF_ID_SHIFT 0
+#define MCDE_EXTSRC7CONF_BUF_ID_MASK 0x00000003
+#define MCDE_EXTSRC7CONF_BUF_ID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CONF, BUF_ID, __x)
+#define MCDE_EXTSRC7CONF_BUF_NB_SHIFT 2
+#define MCDE_EXTSRC7CONF_BUF_NB_MASK 0x0000000C
+#define MCDE_EXTSRC7CONF_BUF_NB(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CONF, BUF_NB, __x)
+#define MCDE_EXTSRC7CONF_PRI_OVLID_SHIFT 4
+#define MCDE_EXTSRC7CONF_PRI_OVLID_MASK 0x000000F0
+#define MCDE_EXTSRC7CONF_PRI_OVLID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CONF, PRI_OVLID, __x)
+#define MCDE_EXTSRC7CONF_BPP_SHIFT 8
+#define MCDE_EXTSRC7CONF_BPP_MASK 0x00000F00
+#define MCDE_EXTSRC7CONF_BPP_1BPP_PAL 0
+#define MCDE_EXTSRC7CONF_BPP_2BPP_PAL 1
+#define MCDE_EXTSRC7CONF_BPP_4BPP_PAL 2
+#define MCDE_EXTSRC7CONF_BPP_8BPP_PAL 3
+#define MCDE_EXTSRC7CONF_BPP_RGB444 4
+#define MCDE_EXTSRC7CONF_BPP_ARGB4444 5
+#define MCDE_EXTSRC7CONF_BPP_IRGB1555 6
+#define MCDE_EXTSRC7CONF_BPP_RGB565 7
+#define MCDE_EXTSRC7CONF_BPP_RGB888 8
+#define MCDE_EXTSRC7CONF_BPP_XRGB8888 9
+#define MCDE_EXTSRC7CONF_BPP_ARGB8888 10
+#define MCDE_EXTSRC7CONF_BPP_YCBCR422 11
+#define MCDE_EXTSRC7CONF_BPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CONF, BPP, MCDE_EXTSRC7CONF_BPP_##__x)
+#define MCDE_EXTSRC7CONF_BPP(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CONF, BPP, __x)
+#define MCDE_EXTSRC7CONF_BGR_SHIFT 12
+#define MCDE_EXTSRC7CONF_BGR_MASK 0x00001000
+#define MCDE_EXTSRC7CONF_BGR_RGB 0
+#define MCDE_EXTSRC7CONF_BGR_BGR 1
+#define MCDE_EXTSRC7CONF_BGR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CONF, BGR, MCDE_EXTSRC7CONF_BGR_##__x)
+#define MCDE_EXTSRC7CONF_BGR(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CONF, BGR, __x)
+#define MCDE_EXTSRC7CONF_BEBO_SHIFT 13
+#define MCDE_EXTSRC7CONF_BEBO_MASK 0x00002000
+#define MCDE_EXTSRC7CONF_BEBO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC7CONF_BEBO_BIG_ENDIAN 1
+#define MCDE_EXTSRC7CONF_BEBO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CONF, BEBO, MCDE_EXTSRC7CONF_BEBO_##__x)
+#define MCDE_EXTSRC7CONF_BEBO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CONF, BEBO, __x)
+#define MCDE_EXTSRC7CONF_BEPO_SHIFT 14
+#define MCDE_EXTSRC7CONF_BEPO_MASK 0x00004000
+#define MCDE_EXTSRC7CONF_BEPO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC7CONF_BEPO_BIG_ENDIAN 1
+#define MCDE_EXTSRC7CONF_BEPO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CONF, BEPO, MCDE_EXTSRC7CONF_BEPO_##__x)
+#define MCDE_EXTSRC7CONF_BEPO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CONF, BEPO, __x)
+#define MCDE_EXTSRC8CONF 0x0000030C
+#define MCDE_EXTSRC8CONF_BUF_ID_SHIFT 0
+#define MCDE_EXTSRC8CONF_BUF_ID_MASK 0x00000003
+#define MCDE_EXTSRC8CONF_BUF_ID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CONF, BUF_ID, __x)
+#define MCDE_EXTSRC8CONF_BUF_NB_SHIFT 2
+#define MCDE_EXTSRC8CONF_BUF_NB_MASK 0x0000000C
+#define MCDE_EXTSRC8CONF_BUF_NB(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CONF, BUF_NB, __x)
+#define MCDE_EXTSRC8CONF_PRI_OVLID_SHIFT 4
+#define MCDE_EXTSRC8CONF_PRI_OVLID_MASK 0x000000F0
+#define MCDE_EXTSRC8CONF_PRI_OVLID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CONF, PRI_OVLID, __x)
+#define MCDE_EXTSRC8CONF_BPP_SHIFT 8
+#define MCDE_EXTSRC8CONF_BPP_MASK 0x00000F00
+#define MCDE_EXTSRC8CONF_BPP_1BPP_PAL 0
+#define MCDE_EXTSRC8CONF_BPP_2BPP_PAL 1
+#define MCDE_EXTSRC8CONF_BPP_4BPP_PAL 2
+#define MCDE_EXTSRC8CONF_BPP_8BPP_PAL 3
+#define MCDE_EXTSRC8CONF_BPP_RGB444 4
+#define MCDE_EXTSRC8CONF_BPP_ARGB4444 5
+#define MCDE_EXTSRC8CONF_BPP_IRGB1555 6
+#define MCDE_EXTSRC8CONF_BPP_RGB565 7
+#define MCDE_EXTSRC8CONF_BPP_RGB888 8
+#define MCDE_EXTSRC8CONF_BPP_XRGB8888 9
+#define MCDE_EXTSRC8CONF_BPP_ARGB8888 10
+#define MCDE_EXTSRC8CONF_BPP_YCBCR422 11
+#define MCDE_EXTSRC8CONF_BPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CONF, BPP, MCDE_EXTSRC8CONF_BPP_##__x)
+#define MCDE_EXTSRC8CONF_BPP(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CONF, BPP, __x)
+#define MCDE_EXTSRC8CONF_BGR_SHIFT 12
+#define MCDE_EXTSRC8CONF_BGR_MASK 0x00001000
+#define MCDE_EXTSRC8CONF_BGR_RGB 0
+#define MCDE_EXTSRC8CONF_BGR_BGR 1
+#define MCDE_EXTSRC8CONF_BGR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CONF, BGR, MCDE_EXTSRC8CONF_BGR_##__x)
+#define MCDE_EXTSRC8CONF_BGR(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CONF, BGR, __x)
+#define MCDE_EXTSRC8CONF_BEBO_SHIFT 13
+#define MCDE_EXTSRC8CONF_BEBO_MASK 0x00002000
+#define MCDE_EXTSRC8CONF_BEBO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC8CONF_BEBO_BIG_ENDIAN 1
+#define MCDE_EXTSRC8CONF_BEBO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CONF, BEBO, MCDE_EXTSRC8CONF_BEBO_##__x)
+#define MCDE_EXTSRC8CONF_BEBO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CONF, BEBO, __x)
+#define MCDE_EXTSRC8CONF_BEPO_SHIFT 14
+#define MCDE_EXTSRC8CONF_BEPO_MASK 0x00004000
+#define MCDE_EXTSRC8CONF_BEPO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC8CONF_BEPO_BIG_ENDIAN 1
+#define MCDE_EXTSRC8CONF_BEPO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CONF, BEPO, MCDE_EXTSRC8CONF_BEPO_##__x)
+#define MCDE_EXTSRC8CONF_BEPO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CONF, BEPO, __x)
+#define MCDE_EXTSRC9CONF 0x0000032C
+#define MCDE_EXTSRC9CONF_BUF_ID_SHIFT 0
+#define MCDE_EXTSRC9CONF_BUF_ID_MASK 0x00000003
+#define MCDE_EXTSRC9CONF_BUF_ID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CONF, BUF_ID, __x)
+#define MCDE_EXTSRC9CONF_BUF_NB_SHIFT 2
+#define MCDE_EXTSRC9CONF_BUF_NB_MASK 0x0000000C
+#define MCDE_EXTSRC9CONF_BUF_NB(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CONF, BUF_NB, __x)
+#define MCDE_EXTSRC9CONF_PRI_OVLID_SHIFT 4
+#define MCDE_EXTSRC9CONF_PRI_OVLID_MASK 0x000000F0
+#define MCDE_EXTSRC9CONF_PRI_OVLID(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CONF, PRI_OVLID, __x)
+#define MCDE_EXTSRC9CONF_BPP_SHIFT 8
+#define MCDE_EXTSRC9CONF_BPP_MASK 0x00000F00
+#define MCDE_EXTSRC9CONF_BPP_1BPP_PAL 0
+#define MCDE_EXTSRC9CONF_BPP_2BPP_PAL 1
+#define MCDE_EXTSRC9CONF_BPP_4BPP_PAL 2
+#define MCDE_EXTSRC9CONF_BPP_8BPP_PAL 3
+#define MCDE_EXTSRC9CONF_BPP_RGB444 4
+#define MCDE_EXTSRC9CONF_BPP_ARGB4444 5
+#define MCDE_EXTSRC9CONF_BPP_IRGB1555 6
+#define MCDE_EXTSRC9CONF_BPP_RGB565 7
+#define MCDE_EXTSRC9CONF_BPP_RGB888 8
+#define MCDE_EXTSRC9CONF_BPP_XRGB8888 9
+#define MCDE_EXTSRC9CONF_BPP_ARGB8888 10
+#define MCDE_EXTSRC9CONF_BPP_YCBCR422 11
+#define MCDE_EXTSRC9CONF_BPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CONF, BPP, MCDE_EXTSRC9CONF_BPP_##__x)
+#define MCDE_EXTSRC9CONF_BPP(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CONF, BPP, __x)
+#define MCDE_EXTSRC9CONF_BGR_SHIFT 12
+#define MCDE_EXTSRC9CONF_BGR_MASK 0x00001000
+#define MCDE_EXTSRC9CONF_BGR_RGB 0
+#define MCDE_EXTSRC9CONF_BGR_BGR 1
+#define MCDE_EXTSRC9CONF_BGR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CONF, BGR, MCDE_EXTSRC9CONF_BGR_##__x)
+#define MCDE_EXTSRC9CONF_BGR(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CONF, BGR, __x)
+#define MCDE_EXTSRC9CONF_BEBO_SHIFT 13
+#define MCDE_EXTSRC9CONF_BEBO_MASK 0x00002000
+#define MCDE_EXTSRC9CONF_BEBO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC9CONF_BEBO_BIG_ENDIAN 1
+#define MCDE_EXTSRC9CONF_BEBO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CONF, BEBO, MCDE_EXTSRC9CONF_BEBO_##__x)
+#define MCDE_EXTSRC9CONF_BEBO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CONF, BEBO, __x)
+#define MCDE_EXTSRC9CONF_BEPO_SHIFT 14
+#define MCDE_EXTSRC9CONF_BEPO_MASK 0x00004000
+#define MCDE_EXTSRC9CONF_BEPO_LITTLE_ENDIAN 0
+#define MCDE_EXTSRC9CONF_BEPO_BIG_ENDIAN 1
+#define MCDE_EXTSRC9CONF_BEPO_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CONF, BEPO, MCDE_EXTSRC9CONF_BEPO_##__x)
+#define MCDE_EXTSRC9CONF_BEPO(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CONF, BEPO, __x)
+#define MCDE_EXTSRC0CR 0x00000210
+#define MCDE_EXTSRC0CR_GROUPOFFSET 0x20
+#define MCDE_EXTSRC0CR_SEL_MOD_SHIFT 0
+#define MCDE_EXTSRC0CR_SEL_MOD_MASK 0x00000003
+#define MCDE_EXTSRC0CR_SEL_MOD_EXTERNAL_SEL 0
+#define MCDE_EXTSRC0CR_SEL_MOD_AUTO_TOGGLE 1
+#define MCDE_EXTSRC0CR_SEL_MOD_SOFTWARE_SEL 2
+#define MCDE_EXTSRC0CR_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CR, SEL_MOD, MCDE_EXTSRC0CR_SEL_MOD_##__x)
+#define MCDE_EXTSRC0CR_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CR, SEL_MOD, __x)
+#define MCDE_EXTSRC0CR_MULTIOVL_CTRL_SHIFT 2
+#define MCDE_EXTSRC0CR_MULTIOVL_CTRL_MASK 0x00000004
+#define MCDE_EXTSRC0CR_MULTIOVL_CTRL_ALL 0
+#define MCDE_EXTSRC0CR_MULTIOVL_CTRL_PRIMARY 1
+#define MCDE_EXTSRC0CR_MULTIOVL_CTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CR, MULTIOVL_CTRL, \
+ MCDE_EXTSRC0CR_MULTIOVL_CTRL_##__x)
+#define MCDE_EXTSRC0CR_MULTIOVL_CTRL(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CR, MULTIOVL_CTRL, __x)
+#define MCDE_EXTSRC0CR_FS_DIV_DISABLE_SHIFT 3
+#define MCDE_EXTSRC0CR_FS_DIV_DISABLE_MASK 0x00000008
+#define MCDE_EXTSRC0CR_FS_DIV_DISABLE(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CR, FS_DIV_DISABLE, __x)
+#define MCDE_EXTSRC0CR_FORCE_FS_DIV_SHIFT 4
+#define MCDE_EXTSRC0CR_FORCE_FS_DIV_MASK 0x00000010
+#define MCDE_EXTSRC0CR_FORCE_FS_DIV(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC0CR, FORCE_FS_DIV, __x)
+#define MCDE_EXTSRC1CR 0x00000230
+#define MCDE_EXTSRC1CR_SEL_MOD_SHIFT 0
+#define MCDE_EXTSRC1CR_SEL_MOD_MASK 0x00000003
+#define MCDE_EXTSRC1CR_SEL_MOD_EXTERNAL_SEL 0
+#define MCDE_EXTSRC1CR_SEL_MOD_AUTO_TOGGLE 1
+#define MCDE_EXTSRC1CR_SEL_MOD_SOFTWARE_SEL 2
+#define MCDE_EXTSRC1CR_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CR, SEL_MOD, MCDE_EXTSRC1CR_SEL_MOD_##__x)
+#define MCDE_EXTSRC1CR_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CR, SEL_MOD, __x)
+#define MCDE_EXTSRC1CR_MULTIOVL_CTRL_SHIFT 2
+#define MCDE_EXTSRC1CR_MULTIOVL_CTRL_MASK 0x00000004
+#define MCDE_EXTSRC1CR_MULTIOVL_CTRL_ALL 0
+#define MCDE_EXTSRC1CR_MULTIOVL_CTRL_PRIMARY 1
+#define MCDE_EXTSRC1CR_MULTIOVL_CTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CR, MULTIOVL_CTRL, \
+ MCDE_EXTSRC1CR_MULTIOVL_CTRL_##__x)
+#define MCDE_EXTSRC1CR_MULTIOVL_CTRL(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CR, MULTIOVL_CTRL, __x)
+#define MCDE_EXTSRC1CR_FS_DIV_DISABLE_SHIFT 3
+#define MCDE_EXTSRC1CR_FS_DIV_DISABLE_MASK 0x00000008
+#define MCDE_EXTSRC1CR_FS_DIV_DISABLE(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CR, FS_DIV_DISABLE, __x)
+#define MCDE_EXTSRC1CR_FORCE_FS_DIV_SHIFT 4
+#define MCDE_EXTSRC1CR_FORCE_FS_DIV_MASK 0x00000010
+#define MCDE_EXTSRC1CR_FORCE_FS_DIV(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC1CR, FORCE_FS_DIV, __x)
+#define MCDE_EXTSRC2CR 0x00000250
+#define MCDE_EXTSRC2CR_SEL_MOD_SHIFT 0
+#define MCDE_EXTSRC2CR_SEL_MOD_MASK 0x00000003
+#define MCDE_EXTSRC2CR_SEL_MOD_EXTERNAL_SEL 0
+#define MCDE_EXTSRC2CR_SEL_MOD_AUTO_TOGGLE 1
+#define MCDE_EXTSRC2CR_SEL_MOD_SOFTWARE_SEL 2
+#define MCDE_EXTSRC2CR_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CR, SEL_MOD, MCDE_EXTSRC2CR_SEL_MOD_##__x)
+#define MCDE_EXTSRC2CR_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CR, SEL_MOD, __x)
+#define MCDE_EXTSRC2CR_MULTIOVL_CTRL_SHIFT 2
+#define MCDE_EXTSRC2CR_MULTIOVL_CTRL_MASK 0x00000004
+#define MCDE_EXTSRC2CR_MULTIOVL_CTRL_ALL 0
+#define MCDE_EXTSRC2CR_MULTIOVL_CTRL_PRIMARY 1
+#define MCDE_EXTSRC2CR_MULTIOVL_CTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CR, MULTIOVL_CTRL, \
+ MCDE_EXTSRC2CR_MULTIOVL_CTRL_##__x)
+#define MCDE_EXTSRC2CR_MULTIOVL_CTRL(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CR, MULTIOVL_CTRL, __x)
+#define MCDE_EXTSRC2CR_FS_DIV_DISABLE_SHIFT 3
+#define MCDE_EXTSRC2CR_FS_DIV_DISABLE_MASK 0x00000008
+#define MCDE_EXTSRC2CR_FS_DIV_DISABLE(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CR, FS_DIV_DISABLE, __x)
+#define MCDE_EXTSRC2CR_FORCE_FS_DIV_SHIFT 4
+#define MCDE_EXTSRC2CR_FORCE_FS_DIV_MASK 0x00000010
+#define MCDE_EXTSRC2CR_FORCE_FS_DIV(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC2CR, FORCE_FS_DIV, __x)
+#define MCDE_EXTSRC3CR 0x00000270
+#define MCDE_EXTSRC3CR_SEL_MOD_SHIFT 0
+#define MCDE_EXTSRC3CR_SEL_MOD_MASK 0x00000003
+#define MCDE_EXTSRC3CR_SEL_MOD_EXTERNAL_SEL 0
+#define MCDE_EXTSRC3CR_SEL_MOD_AUTO_TOGGLE 1
+#define MCDE_EXTSRC3CR_SEL_MOD_SOFTWARE_SEL 2
+#define MCDE_EXTSRC3CR_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CR, SEL_MOD, MCDE_EXTSRC3CR_SEL_MOD_##__x)
+#define MCDE_EXTSRC3CR_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CR, SEL_MOD, __x)
+#define MCDE_EXTSRC3CR_MULTIOVL_CTRL_SHIFT 2
+#define MCDE_EXTSRC3CR_MULTIOVL_CTRL_MASK 0x00000004
+#define MCDE_EXTSRC3CR_MULTIOVL_CTRL_ALL 0
+#define MCDE_EXTSRC3CR_MULTIOVL_CTRL_PRIMARY 1
+#define MCDE_EXTSRC3CR_MULTIOVL_CTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CR, MULTIOVL_CTRL, \
+ MCDE_EXTSRC3CR_MULTIOVL_CTRL_##__x)
+#define MCDE_EXTSRC3CR_MULTIOVL_CTRL(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CR, MULTIOVL_CTRL, __x)
+#define MCDE_EXTSRC3CR_FS_DIV_DISABLE_SHIFT 3
+#define MCDE_EXTSRC3CR_FS_DIV_DISABLE_MASK 0x00000008
+#define MCDE_EXTSRC3CR_FS_DIV_DISABLE(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CR, FS_DIV_DISABLE, __x)
+#define MCDE_EXTSRC3CR_FORCE_FS_DIV_SHIFT 4
+#define MCDE_EXTSRC3CR_FORCE_FS_DIV_MASK 0x00000010
+#define MCDE_EXTSRC3CR_FORCE_FS_DIV(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC3CR, FORCE_FS_DIV, __x)
+#define MCDE_EXTSRC4CR 0x00000290
+#define MCDE_EXTSRC4CR_SEL_MOD_SHIFT 0
+#define MCDE_EXTSRC4CR_SEL_MOD_MASK 0x00000003
+#define MCDE_EXTSRC4CR_SEL_MOD_EXTERNAL_SEL 0
+#define MCDE_EXTSRC4CR_SEL_MOD_AUTO_TOGGLE 1
+#define MCDE_EXTSRC4CR_SEL_MOD_SOFTWARE_SEL 2
+#define MCDE_EXTSRC4CR_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CR, SEL_MOD, MCDE_EXTSRC4CR_SEL_MOD_##__x)
+#define MCDE_EXTSRC4CR_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CR, SEL_MOD, __x)
+#define MCDE_EXTSRC4CR_MULTIOVL_CTRL_SHIFT 2
+#define MCDE_EXTSRC4CR_MULTIOVL_CTRL_MASK 0x00000004
+#define MCDE_EXTSRC4CR_MULTIOVL_CTRL_ALL 0
+#define MCDE_EXTSRC4CR_MULTIOVL_CTRL_PRIMARY 1
+#define MCDE_EXTSRC4CR_MULTIOVL_CTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CR, MULTIOVL_CTRL, \
+ MCDE_EXTSRC4CR_MULTIOVL_CTRL_##__x)
+#define MCDE_EXTSRC4CR_MULTIOVL_CTRL(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CR, MULTIOVL_CTRL, __x)
+#define MCDE_EXTSRC4CR_FS_DIV_DISABLE_SHIFT 3
+#define MCDE_EXTSRC4CR_FS_DIV_DISABLE_MASK 0x00000008
+#define MCDE_EXTSRC4CR_FS_DIV_DISABLE(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CR, FS_DIV_DISABLE, __x)
+#define MCDE_EXTSRC4CR_FORCE_FS_DIV_SHIFT 4
+#define MCDE_EXTSRC4CR_FORCE_FS_DIV_MASK 0x00000010
+#define MCDE_EXTSRC4CR_FORCE_FS_DIV(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC4CR, FORCE_FS_DIV, __x)
+#define MCDE_EXTSRC5CR 0x000002B0
+#define MCDE_EXTSRC5CR_SEL_MOD_SHIFT 0
+#define MCDE_EXTSRC5CR_SEL_MOD_MASK 0x00000003
+#define MCDE_EXTSRC5CR_SEL_MOD_EXTERNAL_SEL 0
+#define MCDE_EXTSRC5CR_SEL_MOD_AUTO_TOGGLE 1
+#define MCDE_EXTSRC5CR_SEL_MOD_SOFTWARE_SEL 2
+#define MCDE_EXTSRC5CR_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CR, SEL_MOD, MCDE_EXTSRC5CR_SEL_MOD_##__x)
+#define MCDE_EXTSRC5CR_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CR, SEL_MOD, __x)
+#define MCDE_EXTSRC5CR_MULTIOVL_CTRL_SHIFT 2
+#define MCDE_EXTSRC5CR_MULTIOVL_CTRL_MASK 0x00000004
+#define MCDE_EXTSRC5CR_MULTIOVL_CTRL_ALL 0
+#define MCDE_EXTSRC5CR_MULTIOVL_CTRL_PRIMARY 1
+#define MCDE_EXTSRC5CR_MULTIOVL_CTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CR, MULTIOVL_CTRL, \
+ MCDE_EXTSRC5CR_MULTIOVL_CTRL_##__x)
+#define MCDE_EXTSRC5CR_MULTIOVL_CTRL(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CR, MULTIOVL_CTRL, __x)
+#define MCDE_EXTSRC5CR_FS_DIV_DISABLE_SHIFT 3
+#define MCDE_EXTSRC5CR_FS_DIV_DISABLE_MASK 0x00000008
+#define MCDE_EXTSRC5CR_FS_DIV_DISABLE(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CR, FS_DIV_DISABLE, __x)
+#define MCDE_EXTSRC5CR_FORCE_FS_DIV_SHIFT 4
+#define MCDE_EXTSRC5CR_FORCE_FS_DIV_MASK 0x00000010
+#define MCDE_EXTSRC5CR_FORCE_FS_DIV(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC5CR, FORCE_FS_DIV, __x)
+#define MCDE_EXTSRC6CR 0x000002D0
+#define MCDE_EXTSRC6CR_SEL_MOD_SHIFT 0
+#define MCDE_EXTSRC6CR_SEL_MOD_MASK 0x00000003
+#define MCDE_EXTSRC6CR_SEL_MOD_EXTERNAL_SEL 0
+#define MCDE_EXTSRC6CR_SEL_MOD_AUTO_TOGGLE 1
+#define MCDE_EXTSRC6CR_SEL_MOD_SOFTWARE_SEL 2
+#define MCDE_EXTSRC6CR_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CR, SEL_MOD, MCDE_EXTSRC6CR_SEL_MOD_##__x)
+#define MCDE_EXTSRC6CR_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CR, SEL_MOD, __x)
+#define MCDE_EXTSRC6CR_MULTIOVL_CTRL_SHIFT 2
+#define MCDE_EXTSRC6CR_MULTIOVL_CTRL_MASK 0x00000004
+#define MCDE_EXTSRC6CR_MULTIOVL_CTRL_ALL 0
+#define MCDE_EXTSRC6CR_MULTIOVL_CTRL_PRIMARY 1
+#define MCDE_EXTSRC6CR_MULTIOVL_CTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CR, MULTIOVL_CTRL, \
+ MCDE_EXTSRC6CR_MULTIOVL_CTRL_##__x)
+#define MCDE_EXTSRC6CR_MULTIOVL_CTRL(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CR, MULTIOVL_CTRL, __x)
+#define MCDE_EXTSRC6CR_FS_DIV_DISABLE_SHIFT 3
+#define MCDE_EXTSRC6CR_FS_DIV_DISABLE_MASK 0x00000008
+#define MCDE_EXTSRC6CR_FS_DIV_DISABLE(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CR, FS_DIV_DISABLE, __x)
+#define MCDE_EXTSRC6CR_FORCE_FS_DIV_SHIFT 4
+#define MCDE_EXTSRC6CR_FORCE_FS_DIV_MASK 0x00000010
+#define MCDE_EXTSRC6CR_FORCE_FS_DIV(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC6CR, FORCE_FS_DIV, __x)
+#define MCDE_EXTSRC7CR 0x000002F0
+#define MCDE_EXTSRC7CR_SEL_MOD_SHIFT 0
+#define MCDE_EXTSRC7CR_SEL_MOD_MASK 0x00000003
+#define MCDE_EXTSRC7CR_SEL_MOD_EXTERNAL_SEL 0
+#define MCDE_EXTSRC7CR_SEL_MOD_AUTO_TOGGLE 1
+#define MCDE_EXTSRC7CR_SEL_MOD_SOFTWARE_SEL 2
+#define MCDE_EXTSRC7CR_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CR, SEL_MOD, MCDE_EXTSRC7CR_SEL_MOD_##__x)
+#define MCDE_EXTSRC7CR_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CR, SEL_MOD, __x)
+#define MCDE_EXTSRC7CR_MULTIOVL_CTRL_SHIFT 2
+#define MCDE_EXTSRC7CR_MULTIOVL_CTRL_MASK 0x00000004
+#define MCDE_EXTSRC7CR_MULTIOVL_CTRL_ALL 0
+#define MCDE_EXTSRC7CR_MULTIOVL_CTRL_PRIMARY 1
+#define MCDE_EXTSRC7CR_MULTIOVL_CTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CR, MULTIOVL_CTRL, \
+ MCDE_EXTSRC7CR_MULTIOVL_CTRL_##__x)
+#define MCDE_EXTSRC7CR_MULTIOVL_CTRL(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CR, MULTIOVL_CTRL, __x)
+#define MCDE_EXTSRC7CR_FS_DIV_DISABLE_SHIFT 3
+#define MCDE_EXTSRC7CR_FS_DIV_DISABLE_MASK 0x00000008
+#define MCDE_EXTSRC7CR_FS_DIV_DISABLE(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CR, FS_DIV_DISABLE, __x)
+#define MCDE_EXTSRC7CR_FORCE_FS_DIV_SHIFT 4
+#define MCDE_EXTSRC7CR_FORCE_FS_DIV_MASK 0x00000010
+#define MCDE_EXTSRC7CR_FORCE_FS_DIV(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC7CR, FORCE_FS_DIV, __x)
+#define MCDE_EXTSRC8CR 0x00000310
+#define MCDE_EXTSRC8CR_SEL_MOD_SHIFT 0
+#define MCDE_EXTSRC8CR_SEL_MOD_MASK 0x00000003
+#define MCDE_EXTSRC8CR_SEL_MOD_EXTERNAL_SEL 0
+#define MCDE_EXTSRC8CR_SEL_MOD_AUTO_TOGGLE 1
+#define MCDE_EXTSRC8CR_SEL_MOD_SOFTWARE_SEL 2
+#define MCDE_EXTSRC8CR_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CR, SEL_MOD, MCDE_EXTSRC8CR_SEL_MOD_##__x)
+#define MCDE_EXTSRC8CR_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CR, SEL_MOD, __x)
+#define MCDE_EXTSRC8CR_MULTIOVL_CTRL_SHIFT 2
+#define MCDE_EXTSRC8CR_MULTIOVL_CTRL_MASK 0x00000004
+#define MCDE_EXTSRC8CR_MULTIOVL_CTRL_ALL 0
+#define MCDE_EXTSRC8CR_MULTIOVL_CTRL_PRIMARY 1
+#define MCDE_EXTSRC8CR_MULTIOVL_CTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CR, MULTIOVL_CTRL, \
+ MCDE_EXTSRC8CR_MULTIOVL_CTRL_##__x)
+#define MCDE_EXTSRC8CR_MULTIOVL_CTRL(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CR, MULTIOVL_CTRL, __x)
+#define MCDE_EXTSRC8CR_FS_DIV_DISABLE_SHIFT 3
+#define MCDE_EXTSRC8CR_FS_DIV_DISABLE_MASK 0x00000008
+#define MCDE_EXTSRC8CR_FS_DIV_DISABLE(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CR, FS_DIV_DISABLE, __x)
+#define MCDE_EXTSRC8CR_FORCE_FS_DIV_SHIFT 4
+#define MCDE_EXTSRC8CR_FORCE_FS_DIV_MASK 0x00000010
+#define MCDE_EXTSRC8CR_FORCE_FS_DIV(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC8CR, FORCE_FS_DIV, __x)
+#define MCDE_EXTSRC9CR 0x00000330
+#define MCDE_EXTSRC9CR_SEL_MOD_SHIFT 0
+#define MCDE_EXTSRC9CR_SEL_MOD_MASK 0x00000003
+#define MCDE_EXTSRC9CR_SEL_MOD_EXTERNAL_SEL 0
+#define MCDE_EXTSRC9CR_SEL_MOD_AUTO_TOGGLE 1
+#define MCDE_EXTSRC9CR_SEL_MOD_SOFTWARE_SEL 2
+#define MCDE_EXTSRC9CR_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CR, SEL_MOD, MCDE_EXTSRC9CR_SEL_MOD_##__x)
+#define MCDE_EXTSRC9CR_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CR, SEL_MOD, __x)
+#define MCDE_EXTSRC9CR_MULTIOVL_CTRL_SHIFT 2
+#define MCDE_EXTSRC9CR_MULTIOVL_CTRL_MASK 0x00000004
+#define MCDE_EXTSRC9CR_MULTIOVL_CTRL_ALL 0
+#define MCDE_EXTSRC9CR_MULTIOVL_CTRL_PRIMARY 1
+#define MCDE_EXTSRC9CR_MULTIOVL_CTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CR, MULTIOVL_CTRL, \
+ MCDE_EXTSRC9CR_MULTIOVL_CTRL_##__x)
+#define MCDE_EXTSRC9CR_MULTIOVL_CTRL(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CR, MULTIOVL_CTRL, __x)
+#define MCDE_EXTSRC9CR_FS_DIV_DISABLE_SHIFT 3
+#define MCDE_EXTSRC9CR_FS_DIV_DISABLE_MASK 0x00000008
+#define MCDE_EXTSRC9CR_FS_DIV_DISABLE(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CR, FS_DIV_DISABLE, __x)
+#define MCDE_EXTSRC9CR_FORCE_FS_DIV_SHIFT 4
+#define MCDE_EXTSRC9CR_FORCE_FS_DIV_MASK 0x00000010
+#define MCDE_EXTSRC9CR_FORCE_FS_DIV(__x) \
+ MCDE_VAL2REG(MCDE_EXTSRC9CR, FORCE_FS_DIV, __x)
+#define MCDE_OVL0CR 0x00000400
+#define MCDE_OVL0CR_GROUPOFFSET 0x20
+#define MCDE_OVL0CR_OVLEN_SHIFT 0
+#define MCDE_OVL0CR_OVLEN_MASK 0x00000001
+#define MCDE_OVL0CR_OVLEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, OVLEN, __x)
+#define MCDE_OVL0CR_COLCCTRL_SHIFT 1
+#define MCDE_OVL0CR_COLCCTRL_MASK 0x00000006
+#define MCDE_OVL0CR_COLCCTRL_DISABLED 0
+#define MCDE_OVL0CR_COLCCTRL_ENABLED_NO_SAT 1
+#define MCDE_OVL0CR_COLCCTRL_ENABLED_SAT 2
+#define MCDE_OVL0CR_COLCCTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, COLCCTRL, MCDE_OVL0CR_COLCCTRL_##__x)
+#define MCDE_OVL0CR_COLCCTRL(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, COLCCTRL, __x)
+#define MCDE_OVL0CR_CKEYGEN_SHIFT 3
+#define MCDE_OVL0CR_CKEYGEN_MASK 0x00000008
+#define MCDE_OVL0CR_CKEYGEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, CKEYGEN, __x)
+#define MCDE_OVL0CR_ALPHAPMEN_SHIFT 4
+#define MCDE_OVL0CR_ALPHAPMEN_MASK 0x00000010
+#define MCDE_OVL0CR_ALPHAPMEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, ALPHAPMEN, __x)
+#define MCDE_OVL0CR_OVLF_SHIFT 5
+#define MCDE_OVL0CR_OVLF_MASK 0x00000020
+#define MCDE_OVL0CR_OVLF(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, OVLF, __x)
+#define MCDE_OVL0CR_OVLR_SHIFT 6
+#define MCDE_OVL0CR_OVLR_MASK 0x00000040
+#define MCDE_OVL0CR_OVLR(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, OVLR, __x)
+#define MCDE_OVL0CR_OVLB_SHIFT 7
+#define MCDE_OVL0CR_OVLB_MASK 0x00000080
+#define MCDE_OVL0CR_OVLB(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, OVLB, __x)
+#define MCDE_OVL0CR_FETCH_ROPC_SHIFT 8
+#define MCDE_OVL0CR_FETCH_ROPC_MASK 0x0000FF00
+#define MCDE_OVL0CR_FETCH_ROPC(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, FETCH_ROPC, __x)
+#define MCDE_OVL0CR_STBPRIO_SHIFT 16
+#define MCDE_OVL0CR_STBPRIO_MASK 0x000F0000
+#define MCDE_OVL0CR_STBPRIO(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, STBPRIO, __x)
+#define MCDE_OVL0CR_BURSTSIZE_SHIFT 20
+#define MCDE_OVL0CR_BURSTSIZE_MASK 0x00F00000
+#define MCDE_OVL0CR_BURSTSIZE_1W 0
+#define MCDE_OVL0CR_BURSTSIZE_2W 1
+#define MCDE_OVL0CR_BURSTSIZE_4W 2
+#define MCDE_OVL0CR_BURSTSIZE_8W 3
+#define MCDE_OVL0CR_BURSTSIZE_16W 4
+#define MCDE_OVL0CR_BURSTSIZE_HW_1W 8
+#define MCDE_OVL0CR_BURSTSIZE_HW_2W 9
+#define MCDE_OVL0CR_BURSTSIZE_HW_4W 10
+#define MCDE_OVL0CR_BURSTSIZE_HW_8W 11
+#define MCDE_OVL0CR_BURSTSIZE_HW_16W 12
+#define MCDE_OVL0CR_BURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, BURSTSIZE, MCDE_OVL0CR_BURSTSIZE_##__x)
+#define MCDE_OVL0CR_BURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, BURSTSIZE, __x)
+#define MCDE_OVL0CR_MAXOUTSTANDING_SHIFT 24
+#define MCDE_OVL0CR_MAXOUTSTANDING_MASK 0x0F000000
+#define MCDE_OVL0CR_MAXOUTSTANDING_1_REQ 0
+#define MCDE_OVL0CR_MAXOUTSTANDING_2_REQ 1
+#define MCDE_OVL0CR_MAXOUTSTANDING_4_REQ 2
+#define MCDE_OVL0CR_MAXOUTSTANDING_8_REQ 3
+#define MCDE_OVL0CR_MAXOUTSTANDING_16_REQ 4
+#define MCDE_OVL0CR_MAXOUTSTANDING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, MAXOUTSTANDING, \
+ MCDE_OVL0CR_MAXOUTSTANDING_##__x)
+#define MCDE_OVL0CR_MAXOUTSTANDING(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, MAXOUTSTANDING, __x)
+#define MCDE_OVL0CR_ROTBURSTSIZE_SHIFT 28
+#define MCDE_OVL0CR_ROTBURSTSIZE_MASK 0xF0000000
+#define MCDE_OVL0CR_ROTBURSTSIZE_1W 0
+#define MCDE_OVL0CR_ROTBURSTSIZE_2W 1
+#define MCDE_OVL0CR_ROTBURSTSIZE_4W 2
+#define MCDE_OVL0CR_ROTBURSTSIZE_8W 3
+#define MCDE_OVL0CR_ROTBURSTSIZE_16W 4
+#define MCDE_OVL0CR_ROTBURSTSIZE_HW_1W 8
+#define MCDE_OVL0CR_ROTBURSTSIZE_HW_2W 9
+#define MCDE_OVL0CR_ROTBURSTSIZE_HW_4W 10
+#define MCDE_OVL0CR_ROTBURSTSIZE_HW_8W 11
+#define MCDE_OVL0CR_ROTBURSTSIZE_HW_16W 12
+#define MCDE_OVL0CR_ROTBURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, ROTBURSTSIZE, MCDE_OVL0CR_ROTBURSTSIZE_##__x)
+#define MCDE_OVL0CR_ROTBURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CR, ROTBURSTSIZE, __x)
+#define MCDE_OVL1CR 0x00000420
+#define MCDE_OVL1CR_OVLEN_SHIFT 0
+#define MCDE_OVL1CR_OVLEN_MASK 0x00000001
+#define MCDE_OVL1CR_OVLEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, OVLEN, __x)
+#define MCDE_OVL1CR_COLCCTRL_SHIFT 1
+#define MCDE_OVL1CR_COLCCTRL_MASK 0x00000006
+#define MCDE_OVL1CR_COLCCTRL_DISABLED 0
+#define MCDE_OVL1CR_COLCCTRL_ENABLED_NO_SAT 1
+#define MCDE_OVL1CR_COLCCTRL_ENABLED_SAT 2
+#define MCDE_OVL1CR_COLCCTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, COLCCTRL, MCDE_OVL1CR_COLCCTRL_##__x)
+#define MCDE_OVL1CR_COLCCTRL(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, COLCCTRL, __x)
+#define MCDE_OVL1CR_CKEYGEN_SHIFT 3
+#define MCDE_OVL1CR_CKEYGEN_MASK 0x00000008
+#define MCDE_OVL1CR_CKEYGEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, CKEYGEN, __x)
+#define MCDE_OVL1CR_ALPHAPMEN_SHIFT 4
+#define MCDE_OVL1CR_ALPHAPMEN_MASK 0x00000010
+#define MCDE_OVL1CR_ALPHAPMEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, ALPHAPMEN, __x)
+#define MCDE_OVL1CR_OVLF_SHIFT 5
+#define MCDE_OVL1CR_OVLF_MASK 0x00000020
+#define MCDE_OVL1CR_OVLF(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, OVLF, __x)
+#define MCDE_OVL1CR_OVLR_SHIFT 6
+#define MCDE_OVL1CR_OVLR_MASK 0x00000040
+#define MCDE_OVL1CR_OVLR(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, OVLR, __x)
+#define MCDE_OVL1CR_OVLB_SHIFT 7
+#define MCDE_OVL1CR_OVLB_MASK 0x00000080
+#define MCDE_OVL1CR_OVLB(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, OVLB, __x)
+#define MCDE_OVL1CR_FETCH_ROPC_SHIFT 8
+#define MCDE_OVL1CR_FETCH_ROPC_MASK 0x0000FF00
+#define MCDE_OVL1CR_FETCH_ROPC(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, FETCH_ROPC, __x)
+#define MCDE_OVL1CR_STBPRIO_SHIFT 16
+#define MCDE_OVL1CR_STBPRIO_MASK 0x000F0000
+#define MCDE_OVL1CR_STBPRIO(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, STBPRIO, __x)
+#define MCDE_OVL1CR_BURSTSIZE_SHIFT 20
+#define MCDE_OVL1CR_BURSTSIZE_MASK 0x00F00000
+#define MCDE_OVL1CR_BURSTSIZE_1W 0
+#define MCDE_OVL1CR_BURSTSIZE_2W 1
+#define MCDE_OVL1CR_BURSTSIZE_4W 2
+#define MCDE_OVL1CR_BURSTSIZE_8W 3
+#define MCDE_OVL1CR_BURSTSIZE_16W 4
+#define MCDE_OVL1CR_BURSTSIZE_HW_1W 8
+#define MCDE_OVL1CR_BURSTSIZE_HW_2W 9
+#define MCDE_OVL1CR_BURSTSIZE_HW_4W 10
+#define MCDE_OVL1CR_BURSTSIZE_HW_8W 11
+#define MCDE_OVL1CR_BURSTSIZE_HW_16W 12
+#define MCDE_OVL1CR_BURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, BURSTSIZE, MCDE_OVL1CR_BURSTSIZE_##__x)
+#define MCDE_OVL1CR_BURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, BURSTSIZE, __x)
+#define MCDE_OVL1CR_MAXOUTSTANDING_SHIFT 24
+#define MCDE_OVL1CR_MAXOUTSTANDING_MASK 0x0F000000
+#define MCDE_OVL1CR_MAXOUTSTANDING_1_REQ 0
+#define MCDE_OVL1CR_MAXOUTSTANDING_2_REQ 1
+#define MCDE_OVL1CR_MAXOUTSTANDING_4_REQ 2
+#define MCDE_OVL1CR_MAXOUTSTANDING_8_REQ 3
+#define MCDE_OVL1CR_MAXOUTSTANDING_16_REQ 4
+#define MCDE_OVL1CR_MAXOUTSTANDING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, MAXOUTSTANDING, \
+ MCDE_OVL1CR_MAXOUTSTANDING_##__x)
+#define MCDE_OVL1CR_MAXOUTSTANDING(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, MAXOUTSTANDING, __x)
+#define MCDE_OVL1CR_ROTBURSTSIZE_SHIFT 28
+#define MCDE_OVL1CR_ROTBURSTSIZE_MASK 0xF0000000
+#define MCDE_OVL1CR_ROTBURSTSIZE_1W 0
+#define MCDE_OVL1CR_ROTBURSTSIZE_2W 1
+#define MCDE_OVL1CR_ROTBURSTSIZE_4W 2
+#define MCDE_OVL1CR_ROTBURSTSIZE_8W 3
+#define MCDE_OVL1CR_ROTBURSTSIZE_16W 4
+#define MCDE_OVL1CR_ROTBURSTSIZE_HW_1W 8
+#define MCDE_OVL1CR_ROTBURSTSIZE_HW_2W 9
+#define MCDE_OVL1CR_ROTBURSTSIZE_HW_4W 10
+#define MCDE_OVL1CR_ROTBURSTSIZE_HW_8W 11
+#define MCDE_OVL1CR_ROTBURSTSIZE_HW_16W 12
+#define MCDE_OVL1CR_ROTBURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, ROTBURSTSIZE, MCDE_OVL1CR_ROTBURSTSIZE_##__x)
+#define MCDE_OVL1CR_ROTBURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CR, ROTBURSTSIZE, __x)
+#define MCDE_OVL2CR 0x00000440
+#define MCDE_OVL2CR_OVLEN_SHIFT 0
+#define MCDE_OVL2CR_OVLEN_MASK 0x00000001
+#define MCDE_OVL2CR_OVLEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, OVLEN, __x)
+#define MCDE_OVL2CR_COLCCTRL_SHIFT 1
+#define MCDE_OVL2CR_COLCCTRL_MASK 0x00000006
+#define MCDE_OVL2CR_COLCCTRL_DISABLED 0
+#define MCDE_OVL2CR_COLCCTRL_ENABLED_NO_SAT 1
+#define MCDE_OVL2CR_COLCCTRL_ENABLED_SAT 2
+#define MCDE_OVL2CR_COLCCTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, COLCCTRL, MCDE_OVL2CR_COLCCTRL_##__x)
+#define MCDE_OVL2CR_COLCCTRL(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, COLCCTRL, __x)
+#define MCDE_OVL2CR_CKEYGEN_SHIFT 3
+#define MCDE_OVL2CR_CKEYGEN_MASK 0x00000008
+#define MCDE_OVL2CR_CKEYGEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, CKEYGEN, __x)
+#define MCDE_OVL2CR_ALPHAPMEN_SHIFT 4
+#define MCDE_OVL2CR_ALPHAPMEN_MASK 0x00000010
+#define MCDE_OVL2CR_ALPHAPMEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, ALPHAPMEN, __x)
+#define MCDE_OVL2CR_OVLF_SHIFT 5
+#define MCDE_OVL2CR_OVLF_MASK 0x00000020
+#define MCDE_OVL2CR_OVLF(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, OVLF, __x)
+#define MCDE_OVL2CR_OVLR_SHIFT 6
+#define MCDE_OVL2CR_OVLR_MASK 0x00000040
+#define MCDE_OVL2CR_OVLR(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, OVLR, __x)
+#define MCDE_OVL2CR_OVLB_SHIFT 7
+#define MCDE_OVL2CR_OVLB_MASK 0x00000080
+#define MCDE_OVL2CR_OVLB(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, OVLB, __x)
+#define MCDE_OVL2CR_FETCH_ROPC_SHIFT 8
+#define MCDE_OVL2CR_FETCH_ROPC_MASK 0x0000FF00
+#define MCDE_OVL2CR_FETCH_ROPC(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, FETCH_ROPC, __x)
+#define MCDE_OVL2CR_STBPRIO_SHIFT 16
+#define MCDE_OVL2CR_STBPRIO_MASK 0x000F0000
+#define MCDE_OVL2CR_STBPRIO(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, STBPRIO, __x)
+#define MCDE_OVL2CR_BURSTSIZE_SHIFT 20
+#define MCDE_OVL2CR_BURSTSIZE_MASK 0x00F00000
+#define MCDE_OVL2CR_BURSTSIZE_1W 0
+#define MCDE_OVL2CR_BURSTSIZE_2W 1
+#define MCDE_OVL2CR_BURSTSIZE_4W 2
+#define MCDE_OVL2CR_BURSTSIZE_8W 3
+#define MCDE_OVL2CR_BURSTSIZE_16W 4
+#define MCDE_OVL2CR_BURSTSIZE_HW_1W 8
+#define MCDE_OVL2CR_BURSTSIZE_HW_2W 9
+#define MCDE_OVL2CR_BURSTSIZE_HW_4W 10
+#define MCDE_OVL2CR_BURSTSIZE_HW_8W 11
+#define MCDE_OVL2CR_BURSTSIZE_HW_16W 12
+#define MCDE_OVL2CR_BURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, BURSTSIZE, MCDE_OVL2CR_BURSTSIZE_##__x)
+#define MCDE_OVL2CR_BURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, BURSTSIZE, __x)
+#define MCDE_OVL2CR_MAXOUTSTANDING_SHIFT 24
+#define MCDE_OVL2CR_MAXOUTSTANDING_MASK 0x0F000000
+#define MCDE_OVL2CR_MAXOUTSTANDING_1_REQ 0
+#define MCDE_OVL2CR_MAXOUTSTANDING_2_REQ 1
+#define MCDE_OVL2CR_MAXOUTSTANDING_4_REQ 2
+#define MCDE_OVL2CR_MAXOUTSTANDING_8_REQ 3
+#define MCDE_OVL2CR_MAXOUTSTANDING_16_REQ 4
+#define MCDE_OVL2CR_MAXOUTSTANDING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, MAXOUTSTANDING, \
+ MCDE_OVL2CR_MAXOUTSTANDING_##__x)
+#define MCDE_OVL2CR_MAXOUTSTANDING(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, MAXOUTSTANDING, __x)
+#define MCDE_OVL2CR_ROTBURSTSIZE_SHIFT 28
+#define MCDE_OVL2CR_ROTBURSTSIZE_MASK 0xF0000000
+#define MCDE_OVL2CR_ROTBURSTSIZE_1W 0
+#define MCDE_OVL2CR_ROTBURSTSIZE_2W 1
+#define MCDE_OVL2CR_ROTBURSTSIZE_4W 2
+#define MCDE_OVL2CR_ROTBURSTSIZE_8W 3
+#define MCDE_OVL2CR_ROTBURSTSIZE_16W 4
+#define MCDE_OVL2CR_ROTBURSTSIZE_HW_1W 8
+#define MCDE_OVL2CR_ROTBURSTSIZE_HW_2W 9
+#define MCDE_OVL2CR_ROTBURSTSIZE_HW_4W 10
+#define MCDE_OVL2CR_ROTBURSTSIZE_HW_8W 11
+#define MCDE_OVL2CR_ROTBURSTSIZE_HW_16W 12
+#define MCDE_OVL2CR_ROTBURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, ROTBURSTSIZE, MCDE_OVL2CR_ROTBURSTSIZE_##__x)
+#define MCDE_OVL2CR_ROTBURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CR, ROTBURSTSIZE, __x)
+#define MCDE_OVL3CR 0x00000460
+#define MCDE_OVL3CR_OVLEN_SHIFT 0
+#define MCDE_OVL3CR_OVLEN_MASK 0x00000001
+#define MCDE_OVL3CR_OVLEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, OVLEN, __x)
+#define MCDE_OVL3CR_COLCCTRL_SHIFT 1
+#define MCDE_OVL3CR_COLCCTRL_MASK 0x00000006
+#define MCDE_OVL3CR_COLCCTRL_DISABLED 0
+#define MCDE_OVL3CR_COLCCTRL_ENABLED_NO_SAT 1
+#define MCDE_OVL3CR_COLCCTRL_ENABLED_SAT 2
+#define MCDE_OVL3CR_COLCCTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, COLCCTRL, MCDE_OVL3CR_COLCCTRL_##__x)
+#define MCDE_OVL3CR_COLCCTRL(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, COLCCTRL, __x)
+#define MCDE_OVL3CR_CKEYGEN_SHIFT 3
+#define MCDE_OVL3CR_CKEYGEN_MASK 0x00000008
+#define MCDE_OVL3CR_CKEYGEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, CKEYGEN, __x)
+#define MCDE_OVL3CR_ALPHAPMEN_SHIFT 4
+#define MCDE_OVL3CR_ALPHAPMEN_MASK 0x00000010
+#define MCDE_OVL3CR_ALPHAPMEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, ALPHAPMEN, __x)
+#define MCDE_OVL3CR_OVLF_SHIFT 5
+#define MCDE_OVL3CR_OVLF_MASK 0x00000020
+#define MCDE_OVL3CR_OVLF(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, OVLF, __x)
+#define MCDE_OVL3CR_OVLR_SHIFT 6
+#define MCDE_OVL3CR_OVLR_MASK 0x00000040
+#define MCDE_OVL3CR_OVLR(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, OVLR, __x)
+#define MCDE_OVL3CR_OVLB_SHIFT 7
+#define MCDE_OVL3CR_OVLB_MASK 0x00000080
+#define MCDE_OVL3CR_OVLB(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, OVLB, __x)
+#define MCDE_OVL3CR_FETCH_ROPC_SHIFT 8
+#define MCDE_OVL3CR_FETCH_ROPC_MASK 0x0000FF00
+#define MCDE_OVL3CR_FETCH_ROPC(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, FETCH_ROPC, __x)
+#define MCDE_OVL3CR_STBPRIO_SHIFT 16
+#define MCDE_OVL3CR_STBPRIO_MASK 0x000F0000
+#define MCDE_OVL3CR_STBPRIO(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, STBPRIO, __x)
+#define MCDE_OVL3CR_BURSTSIZE_SHIFT 20
+#define MCDE_OVL3CR_BURSTSIZE_MASK 0x00F00000
+#define MCDE_OVL3CR_BURSTSIZE_1W 0
+#define MCDE_OVL3CR_BURSTSIZE_2W 1
+#define MCDE_OVL3CR_BURSTSIZE_4W 2
+#define MCDE_OVL3CR_BURSTSIZE_8W 3
+#define MCDE_OVL3CR_BURSTSIZE_16W 4
+#define MCDE_OVL3CR_BURSTSIZE_HW_1W 8
+#define MCDE_OVL3CR_BURSTSIZE_HW_2W 9
+#define MCDE_OVL3CR_BURSTSIZE_HW_4W 10
+#define MCDE_OVL3CR_BURSTSIZE_HW_8W 11
+#define MCDE_OVL3CR_BURSTSIZE_HW_16W 12
+#define MCDE_OVL3CR_BURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, BURSTSIZE, MCDE_OVL3CR_BURSTSIZE_##__x)
+#define MCDE_OVL3CR_BURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, BURSTSIZE, __x)
+#define MCDE_OVL3CR_MAXOUTSTANDING_SHIFT 24
+#define MCDE_OVL3CR_MAXOUTSTANDING_MASK 0x0F000000
+#define MCDE_OVL3CR_MAXOUTSTANDING_1_REQ 0
+#define MCDE_OVL3CR_MAXOUTSTANDING_2_REQ 1
+#define MCDE_OVL3CR_MAXOUTSTANDING_4_REQ 2
+#define MCDE_OVL3CR_MAXOUTSTANDING_8_REQ 3
+#define MCDE_OVL3CR_MAXOUTSTANDING_16_REQ 4
+#define MCDE_OVL3CR_MAXOUTSTANDING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, MAXOUTSTANDING, \
+ MCDE_OVL3CR_MAXOUTSTANDING_##__x)
+#define MCDE_OVL3CR_MAXOUTSTANDING(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, MAXOUTSTANDING, __x)
+#define MCDE_OVL3CR_ROTBURSTSIZE_SHIFT 28
+#define MCDE_OVL3CR_ROTBURSTSIZE_MASK 0xF0000000
+#define MCDE_OVL3CR_ROTBURSTSIZE_1W 0
+#define MCDE_OVL3CR_ROTBURSTSIZE_2W 1
+#define MCDE_OVL3CR_ROTBURSTSIZE_4W 2
+#define MCDE_OVL3CR_ROTBURSTSIZE_8W 3
+#define MCDE_OVL3CR_ROTBURSTSIZE_16W 4
+#define MCDE_OVL3CR_ROTBURSTSIZE_HW_1W 8
+#define MCDE_OVL3CR_ROTBURSTSIZE_HW_2W 9
+#define MCDE_OVL3CR_ROTBURSTSIZE_HW_4W 10
+#define MCDE_OVL3CR_ROTBURSTSIZE_HW_8W 11
+#define MCDE_OVL3CR_ROTBURSTSIZE_HW_16W 12
+#define MCDE_OVL3CR_ROTBURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, ROTBURSTSIZE, MCDE_OVL3CR_ROTBURSTSIZE_##__x)
+#define MCDE_OVL3CR_ROTBURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CR, ROTBURSTSIZE, __x)
+#define MCDE_OVL4CR 0x00000480
+#define MCDE_OVL4CR_OVLEN_SHIFT 0
+#define MCDE_OVL4CR_OVLEN_MASK 0x00000001
+#define MCDE_OVL4CR_OVLEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, OVLEN, __x)
+#define MCDE_OVL4CR_COLCCTRL_SHIFT 1
+#define MCDE_OVL4CR_COLCCTRL_MASK 0x00000006
+#define MCDE_OVL4CR_COLCCTRL_DISABLED 0
+#define MCDE_OVL4CR_COLCCTRL_ENABLED_NO_SAT 1
+#define MCDE_OVL4CR_COLCCTRL_ENABLED_SAT 2
+#define MCDE_OVL4CR_COLCCTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, COLCCTRL, MCDE_OVL4CR_COLCCTRL_##__x)
+#define MCDE_OVL4CR_COLCCTRL(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, COLCCTRL, __x)
+#define MCDE_OVL4CR_CKEYGEN_SHIFT 3
+#define MCDE_OVL4CR_CKEYGEN_MASK 0x00000008
+#define MCDE_OVL4CR_CKEYGEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, CKEYGEN, __x)
+#define MCDE_OVL4CR_ALPHAPMEN_SHIFT 4
+#define MCDE_OVL4CR_ALPHAPMEN_MASK 0x00000010
+#define MCDE_OVL4CR_ALPHAPMEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, ALPHAPMEN, __x)
+#define MCDE_OVL4CR_OVLF_SHIFT 5
+#define MCDE_OVL4CR_OVLF_MASK 0x00000020
+#define MCDE_OVL4CR_OVLF(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, OVLF, __x)
+#define MCDE_OVL4CR_OVLR_SHIFT 6
+#define MCDE_OVL4CR_OVLR_MASK 0x00000040
+#define MCDE_OVL4CR_OVLR(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, OVLR, __x)
+#define MCDE_OVL4CR_OVLB_SHIFT 7
+#define MCDE_OVL4CR_OVLB_MASK 0x00000080
+#define MCDE_OVL4CR_OVLB(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, OVLB, __x)
+#define MCDE_OVL4CR_FETCH_ROPC_SHIFT 8
+#define MCDE_OVL4CR_FETCH_ROPC_MASK 0x0000FF00
+#define MCDE_OVL4CR_FETCH_ROPC(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, FETCH_ROPC, __x)
+#define MCDE_OVL4CR_STBPRIO_SHIFT 16
+#define MCDE_OVL4CR_STBPRIO_MASK 0x000F0000
+#define MCDE_OVL4CR_STBPRIO(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, STBPRIO, __x)
+#define MCDE_OVL4CR_BURSTSIZE_SHIFT 20
+#define MCDE_OVL4CR_BURSTSIZE_MASK 0x00F00000
+#define MCDE_OVL4CR_BURSTSIZE_1W 0
+#define MCDE_OVL4CR_BURSTSIZE_2W 1
+#define MCDE_OVL4CR_BURSTSIZE_4W 2
+#define MCDE_OVL4CR_BURSTSIZE_8W 3
+#define MCDE_OVL4CR_BURSTSIZE_16W 4
+#define MCDE_OVL4CR_BURSTSIZE_HW_1W 8
+#define MCDE_OVL4CR_BURSTSIZE_HW_2W 9
+#define MCDE_OVL4CR_BURSTSIZE_HW_4W 10
+#define MCDE_OVL4CR_BURSTSIZE_HW_8W 11
+#define MCDE_OVL4CR_BURSTSIZE_HW_16W 12
+#define MCDE_OVL4CR_BURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, BURSTSIZE, MCDE_OVL4CR_BURSTSIZE_##__x)
+#define MCDE_OVL4CR_BURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, BURSTSIZE, __x)
+#define MCDE_OVL4CR_MAXOUTSTANDING_SHIFT 24
+#define MCDE_OVL4CR_MAXOUTSTANDING_MASK 0x0F000000
+#define MCDE_OVL4CR_MAXOUTSTANDING_1_REQ 0
+#define MCDE_OVL4CR_MAXOUTSTANDING_2_REQ 1
+#define MCDE_OVL4CR_MAXOUTSTANDING_4_REQ 2
+#define MCDE_OVL4CR_MAXOUTSTANDING_8_REQ 3
+#define MCDE_OVL4CR_MAXOUTSTANDING_16_REQ 4
+#define MCDE_OVL4CR_MAXOUTSTANDING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, MAXOUTSTANDING, \
+ MCDE_OVL4CR_MAXOUTSTANDING_##__x)
+#define MCDE_OVL4CR_MAXOUTSTANDING(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, MAXOUTSTANDING, __x)
+#define MCDE_OVL4CR_ROTBURSTSIZE_SHIFT 28
+#define MCDE_OVL4CR_ROTBURSTSIZE_MASK 0xF0000000
+#define MCDE_OVL4CR_ROTBURSTSIZE_1W 0
+#define MCDE_OVL4CR_ROTBURSTSIZE_2W 1
+#define MCDE_OVL4CR_ROTBURSTSIZE_4W 2
+#define MCDE_OVL4CR_ROTBURSTSIZE_8W 3
+#define MCDE_OVL4CR_ROTBURSTSIZE_16W 4
+#define MCDE_OVL4CR_ROTBURSTSIZE_HW_1W 8
+#define MCDE_OVL4CR_ROTBURSTSIZE_HW_2W 9
+#define MCDE_OVL4CR_ROTBURSTSIZE_HW_4W 10
+#define MCDE_OVL4CR_ROTBURSTSIZE_HW_8W 11
+#define MCDE_OVL4CR_ROTBURSTSIZE_HW_16W 12
+#define MCDE_OVL4CR_ROTBURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, ROTBURSTSIZE, MCDE_OVL4CR_ROTBURSTSIZE_##__x)
+#define MCDE_OVL4CR_ROTBURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CR, ROTBURSTSIZE, __x)
+#define MCDE_OVL5CR 0x000004A0
+#define MCDE_OVL5CR_OVLEN_SHIFT 0
+#define MCDE_OVL5CR_OVLEN_MASK 0x00000001
+#define MCDE_OVL5CR_OVLEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, OVLEN, __x)
+#define MCDE_OVL5CR_COLCCTRL_SHIFT 1
+#define MCDE_OVL5CR_COLCCTRL_MASK 0x00000006
+#define MCDE_OVL5CR_COLCCTRL_DISABLED 0
+#define MCDE_OVL5CR_COLCCTRL_ENABLED_NO_SAT 1
+#define MCDE_OVL5CR_COLCCTRL_ENABLED_SAT 2
+#define MCDE_OVL5CR_COLCCTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, COLCCTRL, MCDE_OVL5CR_COLCCTRL_##__x)
+#define MCDE_OVL5CR_COLCCTRL(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, COLCCTRL, __x)
+#define MCDE_OVL5CR_CKEYGEN_SHIFT 3
+#define MCDE_OVL5CR_CKEYGEN_MASK 0x00000008
+#define MCDE_OVL5CR_CKEYGEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, CKEYGEN, __x)
+#define MCDE_OVL5CR_ALPHAPMEN_SHIFT 4
+#define MCDE_OVL5CR_ALPHAPMEN_MASK 0x00000010
+#define MCDE_OVL5CR_ALPHAPMEN(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, ALPHAPMEN, __x)
+#define MCDE_OVL5CR_OVLF_SHIFT 5
+#define MCDE_OVL5CR_OVLF_MASK 0x00000020
+#define MCDE_OVL5CR_OVLF(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, OVLF, __x)
+#define MCDE_OVL5CR_OVLR_SHIFT 6
+#define MCDE_OVL5CR_OVLR_MASK 0x00000040
+#define MCDE_OVL5CR_OVLR(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, OVLR, __x)
+#define MCDE_OVL5CR_OVLB_SHIFT 7
+#define MCDE_OVL5CR_OVLB_MASK 0x00000080
+#define MCDE_OVL5CR_OVLB(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, OVLB, __x)
+#define MCDE_OVL5CR_FETCH_ROPC_SHIFT 8
+#define MCDE_OVL5CR_FETCH_ROPC_MASK 0x0000FF00
+#define MCDE_OVL5CR_FETCH_ROPC(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, FETCH_ROPC, __x)
+#define MCDE_OVL5CR_STBPRIO_SHIFT 16
+#define MCDE_OVL5CR_STBPRIO_MASK 0x000F0000
+#define MCDE_OVL5CR_STBPRIO(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, STBPRIO, __x)
+#define MCDE_OVL5CR_BURSTSIZE_SHIFT 20
+#define MCDE_OVL5CR_BURSTSIZE_MASK 0x00F00000
+#define MCDE_OVL5CR_BURSTSIZE_1W 0
+#define MCDE_OVL5CR_BURSTSIZE_2W 1
+#define MCDE_OVL5CR_BURSTSIZE_4W 2
+#define MCDE_OVL5CR_BURSTSIZE_8W 3
+#define MCDE_OVL5CR_BURSTSIZE_16W 4
+#define MCDE_OVL5CR_BURSTSIZE_HW_1W 8
+#define MCDE_OVL5CR_BURSTSIZE_HW_2W 9
+#define MCDE_OVL5CR_BURSTSIZE_HW_4W 10
+#define MCDE_OVL5CR_BURSTSIZE_HW_8W 11
+#define MCDE_OVL5CR_BURSTSIZE_HW_16W 12
+#define MCDE_OVL5CR_BURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, BURSTSIZE, MCDE_OVL5CR_BURSTSIZE_##__x)
+#define MCDE_OVL5CR_BURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, BURSTSIZE, __x)
+#define MCDE_OVL5CR_MAXOUTSTANDING_SHIFT 24
+#define MCDE_OVL5CR_MAXOUTSTANDING_MASK 0x0F000000
+#define MCDE_OVL5CR_MAXOUTSTANDING_1_REQ 0
+#define MCDE_OVL5CR_MAXOUTSTANDING_2_REQ 1
+#define MCDE_OVL5CR_MAXOUTSTANDING_4_REQ 2
+#define MCDE_OVL5CR_MAXOUTSTANDING_8_REQ 3
+#define MCDE_OVL5CR_MAXOUTSTANDING_16_REQ 4
+#define MCDE_OVL5CR_MAXOUTSTANDING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, MAXOUTSTANDING, \
+ MCDE_OVL5CR_MAXOUTSTANDING_##__x)
+#define MCDE_OVL5CR_MAXOUTSTANDING(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, MAXOUTSTANDING, __x)
+#define MCDE_OVL5CR_ROTBURSTSIZE_SHIFT 28
+#define MCDE_OVL5CR_ROTBURSTSIZE_MASK 0xF0000000
+#define MCDE_OVL5CR_ROTBURSTSIZE_1W 0
+#define MCDE_OVL5CR_ROTBURSTSIZE_2W 1
+#define MCDE_OVL5CR_ROTBURSTSIZE_4W 2
+#define MCDE_OVL5CR_ROTBURSTSIZE_8W 3
+#define MCDE_OVL5CR_ROTBURSTSIZE_16W 4
+#define MCDE_OVL5CR_ROTBURSTSIZE_HW_1W 8
+#define MCDE_OVL5CR_ROTBURSTSIZE_HW_2W 9
+#define MCDE_OVL5CR_ROTBURSTSIZE_HW_4W 10
+#define MCDE_OVL5CR_ROTBURSTSIZE_HW_8W 11
+#define MCDE_OVL5CR_ROTBURSTSIZE_HW_16W 12
+#define MCDE_OVL5CR_ROTBURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, ROTBURSTSIZE, MCDE_OVL5CR_ROTBURSTSIZE_##__x)
+#define MCDE_OVL5CR_ROTBURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CR, ROTBURSTSIZE, __x)
+#define MCDE_OVL0CONF 0x00000404
+#define MCDE_OVL0CONF_GROUPOFFSET 0x20
+#define MCDE_OVL0CONF_PPL_SHIFT 0
+#define MCDE_OVL0CONF_PPL_MASK 0x000007FF
+#define MCDE_OVL0CONF_PPL(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CONF, PPL, __x)
+#define MCDE_OVL0CONF_EXTSRC_ID_SHIFT 11
+#define MCDE_OVL0CONF_EXTSRC_ID_MASK 0x00007800
+#define MCDE_OVL0CONF_EXTSRC_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CONF, EXTSRC_ID, __x)
+#define MCDE_OVL0CONF_LPF_SHIFT 16
+#define MCDE_OVL0CONF_LPF_MASK 0x07FF0000
+#define MCDE_OVL0CONF_LPF(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CONF, LPF, __x)
+#define MCDE_OVL1CONF 0x00000424
+#define MCDE_OVL1CONF_PPL_SHIFT 0
+#define MCDE_OVL1CONF_PPL_MASK 0x000007FF
+#define MCDE_OVL1CONF_PPL(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CONF, PPL, __x)
+#define MCDE_OVL1CONF_EXTSRC_ID_SHIFT 11
+#define MCDE_OVL1CONF_EXTSRC_ID_MASK 0x00007800
+#define MCDE_OVL1CONF_EXTSRC_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CONF, EXTSRC_ID, __x)
+#define MCDE_OVL1CONF_LPF_SHIFT 16
+#define MCDE_OVL1CONF_LPF_MASK 0x07FF0000
+#define MCDE_OVL1CONF_LPF(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CONF, LPF, __x)
+#define MCDE_OVL2CONF 0x00000444
+#define MCDE_OVL2CONF_PPL_SHIFT 0
+#define MCDE_OVL2CONF_PPL_MASK 0x000007FF
+#define MCDE_OVL2CONF_PPL(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CONF, PPL, __x)
+#define MCDE_OVL2CONF_EXTSRC_ID_SHIFT 11
+#define MCDE_OVL2CONF_EXTSRC_ID_MASK 0x00007800
+#define MCDE_OVL2CONF_EXTSRC_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CONF, EXTSRC_ID, __x)
+#define MCDE_OVL2CONF_LPF_SHIFT 16
+#define MCDE_OVL2CONF_LPF_MASK 0x07FF0000
+#define MCDE_OVL2CONF_LPF(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CONF, LPF, __x)
+#define MCDE_OVL3CONF 0x00000464
+#define MCDE_OVL3CONF_PPL_SHIFT 0
+#define MCDE_OVL3CONF_PPL_MASK 0x000007FF
+#define MCDE_OVL3CONF_PPL(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CONF, PPL, __x)
+#define MCDE_OVL3CONF_EXTSRC_ID_SHIFT 11
+#define MCDE_OVL3CONF_EXTSRC_ID_MASK 0x00007800
+#define MCDE_OVL3CONF_EXTSRC_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CONF, EXTSRC_ID, __x)
+#define MCDE_OVL3CONF_LPF_SHIFT 16
+#define MCDE_OVL3CONF_LPF_MASK 0x07FF0000
+#define MCDE_OVL3CONF_LPF(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CONF, LPF, __x)
+#define MCDE_OVL4CONF 0x00000484
+#define MCDE_OVL4CONF_PPL_SHIFT 0
+#define MCDE_OVL4CONF_PPL_MASK 0x000007FF
+#define MCDE_OVL4CONF_PPL(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CONF, PPL, __x)
+#define MCDE_OVL4CONF_EXTSRC_ID_SHIFT 11
+#define MCDE_OVL4CONF_EXTSRC_ID_MASK 0x00007800
+#define MCDE_OVL4CONF_EXTSRC_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CONF, EXTSRC_ID, __x)
+#define MCDE_OVL4CONF_LPF_SHIFT 16
+#define MCDE_OVL4CONF_LPF_MASK 0x07FF0000
+#define MCDE_OVL4CONF_LPF(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CONF, LPF, __x)
+#define MCDE_OVL5CONF 0x000004A4
+#define MCDE_OVL5CONF_PPL_SHIFT 0
+#define MCDE_OVL5CONF_PPL_MASK 0x000007FF
+#define MCDE_OVL5CONF_PPL(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CONF, PPL, __x)
+#define MCDE_OVL5CONF_EXTSRC_ID_SHIFT 11
+#define MCDE_OVL5CONF_EXTSRC_ID_MASK 0x00007800
+#define MCDE_OVL5CONF_EXTSRC_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CONF, EXTSRC_ID, __x)
+#define MCDE_OVL5CONF_LPF_SHIFT 16
+#define MCDE_OVL5CONF_LPF_MASK 0x07FF0000
+#define MCDE_OVL5CONF_LPF(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CONF, LPF, __x)
+#define MCDE_OVL0CONF2 0x00000408
+#define MCDE_OVL0CONF2_GROUPOFFSET 0x20
+#define MCDE_OVL0CONF2_BP_SHIFT 0
+#define MCDE_OVL0CONF2_BP_MASK 0x00000001
+#define MCDE_OVL0CONF2_BP_PER_PIXEL_ALPHA 0
+#define MCDE_OVL0CONF2_BP_CONSTANT_ALPHA 1
+#define MCDE_OVL0CONF2_BP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CONF2, BP, MCDE_OVL0CONF2_BP_##__x)
+#define MCDE_OVL0CONF2_BP(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CONF2, BP, __x)
+#define MCDE_OVL0CONF2_ALPHAVALUE_SHIFT 1
+#define MCDE_OVL0CONF2_ALPHAVALUE_MASK 0x000001FE
+#define MCDE_OVL0CONF2_ALPHAVALUE(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CONF2, ALPHAVALUE, __x)
+#define MCDE_OVL0CONF2_OPQ_SHIFT 9
+#define MCDE_OVL0CONF2_OPQ_MASK 0x00000200
+#define MCDE_OVL0CONF2_OPQ(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CONF2, OPQ, __x)
+#define MCDE_OVL0CONF2_PIXOFF_SHIFT 10
+#define MCDE_OVL0CONF2_PIXOFF_MASK 0x0000FC00
+#define MCDE_OVL0CONF2_PIXOFF(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CONF2, PIXOFF, __x)
+#define MCDE_OVL0CONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT 16
+#define MCDE_OVL0CONF2_PIXELFETCHERWATERMARKLEVEL_MASK 0x1FFF0000
+#define MCDE_OVL0CONF2_PIXELFETCHERWATERMARKLEVEL(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CONF2, PIXELFETCHERWATERMARKLEVEL, __x)
+#define MCDE_OVL1CONF2 0x00000428
+#define MCDE_OVL1CONF2_BP_SHIFT 0
+#define MCDE_OVL1CONF2_BP_MASK 0x00000001
+#define MCDE_OVL1CONF2_BP_PER_PIXEL_ALPHA 0
+#define MCDE_OVL1CONF2_BP_CONSTANT_ALPHA 1
+#define MCDE_OVL1CONF2_BP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CONF2, BP, MCDE_OVL1CONF2_BP_##__x)
+#define MCDE_OVL1CONF2_BP(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CONF2, BP, __x)
+#define MCDE_OVL1CONF2_ALPHAVALUE_SHIFT 1
+#define MCDE_OVL1CONF2_ALPHAVALUE_MASK 0x000001FE
+#define MCDE_OVL1CONF2_ALPHAVALUE(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CONF2, ALPHAVALUE, __x)
+#define MCDE_OVL1CONF2_OPQ_SHIFT 9
+#define MCDE_OVL1CONF2_OPQ_MASK 0x00000200
+#define MCDE_OVL1CONF2_OPQ(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CONF2, OPQ, __x)
+#define MCDE_OVL1CONF2_PIXOFF_SHIFT 10
+#define MCDE_OVL1CONF2_PIXOFF_MASK 0x0000FC00
+#define MCDE_OVL1CONF2_PIXOFF(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CONF2, PIXOFF, __x)
+#define MCDE_OVL1CONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT 16
+#define MCDE_OVL1CONF2_PIXELFETCHERWATERMARKLEVEL_MASK 0x1FFF0000
+#define MCDE_OVL1CONF2_PIXELFETCHERWATERMARKLEVEL(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CONF2, PIXELFETCHERWATERMARKLEVEL, __x)
+#define MCDE_OVL2CONF2 0x00000448
+#define MCDE_OVL2CONF2_BP_SHIFT 0
+#define MCDE_OVL2CONF2_BP_MASK 0x00000001
+#define MCDE_OVL2CONF2_BP_PER_PIXEL_ALPHA 0
+#define MCDE_OVL2CONF2_BP_CONSTANT_ALPHA 1
+#define MCDE_OVL2CONF2_BP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CONF2, BP, MCDE_OVL2CONF2_BP_##__x)
+#define MCDE_OVL2CONF2_BP(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CONF2, BP, __x)
+#define MCDE_OVL2CONF2_ALPHAVALUE_SHIFT 1
+#define MCDE_OVL2CONF2_ALPHAVALUE_MASK 0x000001FE
+#define MCDE_OVL2CONF2_ALPHAVALUE(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CONF2, ALPHAVALUE, __x)
+#define MCDE_OVL2CONF2_OPQ_SHIFT 9
+#define MCDE_OVL2CONF2_OPQ_MASK 0x00000200
+#define MCDE_OVL2CONF2_OPQ(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CONF2, OPQ, __x)
+#define MCDE_OVL2CONF2_PIXOFF_SHIFT 10
+#define MCDE_OVL2CONF2_PIXOFF_MASK 0x0000FC00
+#define MCDE_OVL2CONF2_PIXOFF(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CONF2, PIXOFF, __x)
+#define MCDE_OVL2CONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT 16
+#define MCDE_OVL2CONF2_PIXELFETCHERWATERMARKLEVEL_MASK 0x1FFF0000
+#define MCDE_OVL2CONF2_PIXELFETCHERWATERMARKLEVEL(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CONF2, PIXELFETCHERWATERMARKLEVEL, __x)
+#define MCDE_OVL3CONF2 0x00000468
+#define MCDE_OVL3CONF2_BP_SHIFT 0
+#define MCDE_OVL3CONF2_BP_MASK 0x00000001
+#define MCDE_OVL3CONF2_BP_PER_PIXEL_ALPHA 0
+#define MCDE_OVL3CONF2_BP_CONSTANT_ALPHA 1
+#define MCDE_OVL3CONF2_BP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CONF2, BP, MCDE_OVL3CONF2_BP_##__x)
+#define MCDE_OVL3CONF2_BP(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CONF2, BP, __x)
+#define MCDE_OVL3CONF2_ALPHAVALUE_SHIFT 1
+#define MCDE_OVL3CONF2_ALPHAVALUE_MASK 0x000001FE
+#define MCDE_OVL3CONF2_ALPHAVALUE(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CONF2, ALPHAVALUE, __x)
+#define MCDE_OVL3CONF2_OPQ_SHIFT 9
+#define MCDE_OVL3CONF2_OPQ_MASK 0x00000200
+#define MCDE_OVL3CONF2_OPQ(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CONF2, OPQ, __x)
+#define MCDE_OVL3CONF2_PIXOFF_SHIFT 10
+#define MCDE_OVL3CONF2_PIXOFF_MASK 0x0000FC00
+#define MCDE_OVL3CONF2_PIXOFF(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CONF2, PIXOFF, __x)
+#define MCDE_OVL3CONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT 16
+#define MCDE_OVL3CONF2_PIXELFETCHERWATERMARKLEVEL_MASK 0x1FFF0000
+#define MCDE_OVL3CONF2_PIXELFETCHERWATERMARKLEVEL(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CONF2, PIXELFETCHERWATERMARKLEVEL, __x)
+#define MCDE_OVL4CONF2 0x00000488
+#define MCDE_OVL4CONF2_BP_SHIFT 0
+#define MCDE_OVL4CONF2_BP_MASK 0x00000001
+#define MCDE_OVL4CONF2_BP_PER_PIXEL_ALPHA 0
+#define MCDE_OVL4CONF2_BP_CONSTANT_ALPHA 1
+#define MCDE_OVL4CONF2_BP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CONF2, BP, MCDE_OVL4CONF2_BP_##__x)
+#define MCDE_OVL4CONF2_BP(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CONF2, BP, __x)
+#define MCDE_OVL4CONF2_ALPHAVALUE_SHIFT 1
+#define MCDE_OVL4CONF2_ALPHAVALUE_MASK 0x000001FE
+#define MCDE_OVL4CONF2_ALPHAVALUE(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CONF2, ALPHAVALUE, __x)
+#define MCDE_OVL4CONF2_OPQ_SHIFT 9
+#define MCDE_OVL4CONF2_OPQ_MASK 0x00000200
+#define MCDE_OVL4CONF2_OPQ(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CONF2, OPQ, __x)
+#define MCDE_OVL4CONF2_PIXOFF_SHIFT 10
+#define MCDE_OVL4CONF2_PIXOFF_MASK 0x0000FC00
+#define MCDE_OVL4CONF2_PIXOFF(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CONF2, PIXOFF, __x)
+#define MCDE_OVL4CONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT 16
+#define MCDE_OVL4CONF2_PIXELFETCHERWATERMARKLEVEL_MASK 0x1FFF0000
+#define MCDE_OVL4CONF2_PIXELFETCHERWATERMARKLEVEL(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CONF2, PIXELFETCHERWATERMARKLEVEL, __x)
+#define MCDE_OVL5CONF2 0x000004A8
+#define MCDE_OVL5CONF2_BP_SHIFT 0
+#define MCDE_OVL5CONF2_BP_MASK 0x00000001
+#define MCDE_OVL5CONF2_BP_PER_PIXEL_ALPHA 0
+#define MCDE_OVL5CONF2_BP_CONSTANT_ALPHA 1
+#define MCDE_OVL5CONF2_BP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CONF2, BP, MCDE_OVL5CONF2_BP_##__x)
+#define MCDE_OVL5CONF2_BP(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CONF2, BP, __x)
+#define MCDE_OVL5CONF2_ALPHAVALUE_SHIFT 1
+#define MCDE_OVL5CONF2_ALPHAVALUE_MASK 0x000001FE
+#define MCDE_OVL5CONF2_ALPHAVALUE(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CONF2, ALPHAVALUE, __x)
+#define MCDE_OVL5CONF2_OPQ_SHIFT 9
+#define MCDE_OVL5CONF2_OPQ_MASK 0x00000200
+#define MCDE_OVL5CONF2_OPQ(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CONF2, OPQ, __x)
+#define MCDE_OVL5CONF2_PIXOFF_SHIFT 10
+#define MCDE_OVL5CONF2_PIXOFF_MASK 0x0000FC00
+#define MCDE_OVL5CONF2_PIXOFF(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CONF2, PIXOFF, __x)
+#define MCDE_OVL5CONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT 16
+#define MCDE_OVL5CONF2_PIXELFETCHERWATERMARKLEVEL_MASK 0x1FFF0000
+#define MCDE_OVL5CONF2_PIXELFETCHERWATERMARKLEVEL(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CONF2, PIXELFETCHERWATERMARKLEVEL, __x)
+#define MCDE_OVL0LJINC 0x0000040C
+#define MCDE_OVL0LJINC_GROUPOFFSET 0x20
+#define MCDE_OVL0LJINC_LJINC_SHIFT 0
+#define MCDE_OVL0LJINC_LJINC_MASK 0xFFFFFFFF
+#define MCDE_OVL0LJINC_LJINC(__x) \
+ MCDE_VAL2REG(MCDE_OVL0LJINC, LJINC, __x)
+#define MCDE_OVL1LJINC 0x0000042C
+#define MCDE_OVL1LJINC_LJINC_SHIFT 0
+#define MCDE_OVL1LJINC_LJINC_MASK 0xFFFFFFFF
+#define MCDE_OVL1LJINC_LJINC(__x) \
+ MCDE_VAL2REG(MCDE_OVL1LJINC, LJINC, __x)
+#define MCDE_OVL2LJINC 0x0000044C
+#define MCDE_OVL2LJINC_LJINC_SHIFT 0
+#define MCDE_OVL2LJINC_LJINC_MASK 0xFFFFFFFF
+#define MCDE_OVL2LJINC_LJINC(__x) \
+ MCDE_VAL2REG(MCDE_OVL2LJINC, LJINC, __x)
+#define MCDE_OVL3LJINC 0x0000046C
+#define MCDE_OVL3LJINC_LJINC_SHIFT 0
+#define MCDE_OVL3LJINC_LJINC_MASK 0xFFFFFFFF
+#define MCDE_OVL3LJINC_LJINC(__x) \
+ MCDE_VAL2REG(MCDE_OVL3LJINC, LJINC, __x)
+#define MCDE_OVL4LJINC 0x0000048C
+#define MCDE_OVL4LJINC_LJINC_SHIFT 0
+#define MCDE_OVL4LJINC_LJINC_MASK 0xFFFFFFFF
+#define MCDE_OVL4LJINC_LJINC(__x) \
+ MCDE_VAL2REG(MCDE_OVL4LJINC, LJINC, __x)
+#define MCDE_OVL5LJINC 0x000004AC
+#define MCDE_OVL5LJINC_LJINC_SHIFT 0
+#define MCDE_OVL5LJINC_LJINC_MASK 0xFFFFFFFF
+#define MCDE_OVL5LJINC_LJINC(__x) \
+ MCDE_VAL2REG(MCDE_OVL5LJINC, LJINC, __x)
+#define MCDE_OVL0CROP 0x00000410
+#define MCDE_OVL0CROP_GROUPOFFSET 0x20
+#define MCDE_OVL0CROP_TMRGN_SHIFT 0
+#define MCDE_OVL0CROP_TMRGN_MASK 0x003FFFFF
+#define MCDE_OVL0CROP_TMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CROP, TMRGN, __x)
+#define MCDE_OVL0CROP_LMRGN_SHIFT 22
+#define MCDE_OVL0CROP_LMRGN_MASK 0xFFC00000
+#define MCDE_OVL0CROP_LMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL0CROP, LMRGN, __x)
+#define MCDE_OVL1CROP 0x00000430
+#define MCDE_OVL1CROP_TMRGN_SHIFT 0
+#define MCDE_OVL1CROP_TMRGN_MASK 0x003FFFFF
+#define MCDE_OVL1CROP_TMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CROP, TMRGN, __x)
+#define MCDE_OVL1CROP_LMRGN_SHIFT 22
+#define MCDE_OVL1CROP_LMRGN_MASK 0xFFC00000
+#define MCDE_OVL1CROP_LMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL1CROP, LMRGN, __x)
+#define MCDE_OVL2CROP 0x00000450
+#define MCDE_OVL2CROP_TMRGN_SHIFT 0
+#define MCDE_OVL2CROP_TMRGN_MASK 0x003FFFFF
+#define MCDE_OVL2CROP_TMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CROP, TMRGN, __x)
+#define MCDE_OVL2CROP_LMRGN_SHIFT 22
+#define MCDE_OVL2CROP_LMRGN_MASK 0xFFC00000
+#define MCDE_OVL2CROP_LMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL2CROP, LMRGN, __x)
+#define MCDE_OVL3CROP 0x00000470
+#define MCDE_OVL3CROP_TMRGN_SHIFT 0
+#define MCDE_OVL3CROP_TMRGN_MASK 0x003FFFFF
+#define MCDE_OVL3CROP_TMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CROP, TMRGN, __x)
+#define MCDE_OVL3CROP_LMRGN_SHIFT 22
+#define MCDE_OVL3CROP_LMRGN_MASK 0xFFC00000
+#define MCDE_OVL3CROP_LMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL3CROP, LMRGN, __x)
+#define MCDE_OVL4CROP 0x00000490
+#define MCDE_OVL4CROP_TMRGN_SHIFT 0
+#define MCDE_OVL4CROP_TMRGN_MASK 0x003FFFFF
+#define MCDE_OVL4CROP_TMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CROP, TMRGN, __x)
+#define MCDE_OVL4CROP_LMRGN_SHIFT 22
+#define MCDE_OVL4CROP_LMRGN_MASK 0xFFC00000
+#define MCDE_OVL4CROP_LMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL4CROP, LMRGN, __x)
+#define MCDE_OVL5CROP 0x000004B0
+#define MCDE_OVL5CROP_TMRGN_SHIFT 0
+#define MCDE_OVL5CROP_TMRGN_MASK 0x003FFFFF
+#define MCDE_OVL5CROP_TMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CROP, TMRGN, __x)
+#define MCDE_OVL5CROP_LMRGN_SHIFT 22
+#define MCDE_OVL5CROP_LMRGN_MASK 0xFFC00000
+#define MCDE_OVL5CROP_LMRGN(__x) \
+ MCDE_VAL2REG(MCDE_OVL5CROP, LMRGN, __x)
+#define MCDE_OVL0COMP 0x00000414
+#define MCDE_OVL0COMP_GROUPOFFSET 0x20
+#define MCDE_OVL0COMP_XPOS_SHIFT 0
+#define MCDE_OVL0COMP_XPOS_MASK 0x000007FF
+#define MCDE_OVL0COMP_XPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL0COMP, XPOS, __x)
+#define MCDE_OVL0COMP_CH_ID_SHIFT 11
+#define MCDE_OVL0COMP_CH_ID_MASK 0x00007800
+#define MCDE_OVL0COMP_CH_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL0COMP, CH_ID, __x)
+#define MCDE_OVL0COMP_YPOS_SHIFT 16
+#define MCDE_OVL0COMP_YPOS_MASK 0x07FF0000
+#define MCDE_OVL0COMP_YPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL0COMP, YPOS, __x)
+#define MCDE_OVL0COMP_Z_SHIFT 27
+#define MCDE_OVL0COMP_Z_MASK 0x78000000
+#define MCDE_OVL0COMP_Z(__x) \
+ MCDE_VAL2REG(MCDE_OVL0COMP, Z, __x)
+#define MCDE_OVL1COMP 0x00000434
+#define MCDE_OVL1COMP_XPOS_SHIFT 0
+#define MCDE_OVL1COMP_XPOS_MASK 0x000007FF
+#define MCDE_OVL1COMP_XPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL1COMP, XPOS, __x)
+#define MCDE_OVL1COMP_CH_ID_SHIFT 11
+#define MCDE_OVL1COMP_CH_ID_MASK 0x00007800
+#define MCDE_OVL1COMP_CH_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL1COMP, CH_ID, __x)
+#define MCDE_OVL1COMP_YPOS_SHIFT 16
+#define MCDE_OVL1COMP_YPOS_MASK 0x07FF0000
+#define MCDE_OVL1COMP_YPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL1COMP, YPOS, __x)
+#define MCDE_OVL1COMP_Z_SHIFT 27
+#define MCDE_OVL1COMP_Z_MASK 0x78000000
+#define MCDE_OVL1COMP_Z(__x) \
+ MCDE_VAL2REG(MCDE_OVL1COMP, Z, __x)
+#define MCDE_OVL2COMP 0x00000454
+#define MCDE_OVL2COMP_XPOS_SHIFT 0
+#define MCDE_OVL2COMP_XPOS_MASK 0x000007FF
+#define MCDE_OVL2COMP_XPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL2COMP, XPOS, __x)
+#define MCDE_OVL2COMP_CH_ID_SHIFT 11
+#define MCDE_OVL2COMP_CH_ID_MASK 0x00007800
+#define MCDE_OVL2COMP_CH_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL2COMP, CH_ID, __x)
+#define MCDE_OVL2COMP_YPOS_SHIFT 16
+#define MCDE_OVL2COMP_YPOS_MASK 0x07FF0000
+#define MCDE_OVL2COMP_YPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL2COMP, YPOS, __x)
+#define MCDE_OVL2COMP_Z_SHIFT 27
+#define MCDE_OVL2COMP_Z_MASK 0x78000000
+#define MCDE_OVL2COMP_Z(__x) \
+ MCDE_VAL2REG(MCDE_OVL2COMP, Z, __x)
+#define MCDE_OVL3COMP 0x00000474
+#define MCDE_OVL3COMP_XPOS_SHIFT 0
+#define MCDE_OVL3COMP_XPOS_MASK 0x000007FF
+#define MCDE_OVL3COMP_XPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL3COMP, XPOS, __x)
+#define MCDE_OVL3COMP_CH_ID_SHIFT 11
+#define MCDE_OVL3COMP_CH_ID_MASK 0x00007800
+#define MCDE_OVL3COMP_CH_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL3COMP, CH_ID, __x)
+#define MCDE_OVL3COMP_YPOS_SHIFT 16
+#define MCDE_OVL3COMP_YPOS_MASK 0x07FF0000
+#define MCDE_OVL3COMP_YPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL3COMP, YPOS, __x)
+#define MCDE_OVL3COMP_Z_SHIFT 27
+#define MCDE_OVL3COMP_Z_MASK 0x78000000
+#define MCDE_OVL3COMP_Z(__x) \
+ MCDE_VAL2REG(MCDE_OVL3COMP, Z, __x)
+#define MCDE_OVL4COMP 0x00000494
+#define MCDE_OVL4COMP_XPOS_SHIFT 0
+#define MCDE_OVL4COMP_XPOS_MASK 0x000007FF
+#define MCDE_OVL4COMP_XPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL4COMP, XPOS, __x)
+#define MCDE_OVL4COMP_CH_ID_SHIFT 11
+#define MCDE_OVL4COMP_CH_ID_MASK 0x00007800
+#define MCDE_OVL4COMP_CH_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL4COMP, CH_ID, __x)
+#define MCDE_OVL4COMP_YPOS_SHIFT 16
+#define MCDE_OVL4COMP_YPOS_MASK 0x07FF0000
+#define MCDE_OVL4COMP_YPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL4COMP, YPOS, __x)
+#define MCDE_OVL4COMP_Z_SHIFT 27
+#define MCDE_OVL4COMP_Z_MASK 0x78000000
+#define MCDE_OVL4COMP_Z(__x) \
+ MCDE_VAL2REG(MCDE_OVL4COMP, Z, __x)
+#define MCDE_OVL5COMP 0x000004B4
+#define MCDE_OVL5COMP_XPOS_SHIFT 0
+#define MCDE_OVL5COMP_XPOS_MASK 0x000007FF
+#define MCDE_OVL5COMP_XPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL5COMP, XPOS, __x)
+#define MCDE_OVL5COMP_CH_ID_SHIFT 11
+#define MCDE_OVL5COMP_CH_ID_MASK 0x00007800
+#define MCDE_OVL5COMP_CH_ID(__x) \
+ MCDE_VAL2REG(MCDE_OVL5COMP, CH_ID, __x)
+#define MCDE_OVL5COMP_YPOS_SHIFT 16
+#define MCDE_OVL5COMP_YPOS_MASK 0x07FF0000
+#define MCDE_OVL5COMP_YPOS(__x) \
+ MCDE_VAL2REG(MCDE_OVL5COMP, YPOS, __x)
+#define MCDE_OVL5COMP_Z_SHIFT 27
+#define MCDE_OVL5COMP_Z_MASK 0x78000000
+#define MCDE_OVL5COMP_Z(__x) \
+ MCDE_VAL2REG(MCDE_OVL5COMP, Z, __x)
+#define MCDE_CHNL0CONF 0x00000600
+#define MCDE_CHNL0CONF_GROUPOFFSET 0x20
+#define MCDE_CHNL0CONF_PPL_SHIFT 0
+#define MCDE_CHNL0CONF_PPL_MASK 0x000007FF
+#define MCDE_CHNL0CONF_PPL(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0CONF, PPL, __x)
+#define MCDE_CHNL0CONF_LPF_SHIFT 16
+#define MCDE_CHNL0CONF_LPF_MASK 0x07FF0000
+#define MCDE_CHNL0CONF_LPF(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0CONF, LPF, __x)
+#define MCDE_CHNL1CONF 0x00000620
+#define MCDE_CHNL1CONF_PPL_SHIFT 0
+#define MCDE_CHNL1CONF_PPL_MASK 0x000007FF
+#define MCDE_CHNL1CONF_PPL(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1CONF, PPL, __x)
+#define MCDE_CHNL1CONF_LPF_SHIFT 16
+#define MCDE_CHNL1CONF_LPF_MASK 0x07FF0000
+#define MCDE_CHNL1CONF_LPF(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1CONF, LPF, __x)
+#define MCDE_CHNL2CONF 0x00000640
+#define MCDE_CHNL2CONF_PPL_SHIFT 0
+#define MCDE_CHNL2CONF_PPL_MASK 0x000007FF
+#define MCDE_CHNL2CONF_PPL(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2CONF, PPL, __x)
+#define MCDE_CHNL2CONF_LPF_SHIFT 16
+#define MCDE_CHNL2CONF_LPF_MASK 0x07FF0000
+#define MCDE_CHNL2CONF_LPF(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2CONF, LPF, __x)
+#define MCDE_CHNL3CONF 0x00000660
+#define MCDE_CHNL3CONF_PPL_SHIFT 0
+#define MCDE_CHNL3CONF_PPL_MASK 0x000007FF
+#define MCDE_CHNL3CONF_PPL(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3CONF, PPL, __x)
+#define MCDE_CHNL3CONF_LPF_SHIFT 16
+#define MCDE_CHNL3CONF_LPF_MASK 0x07FF0000
+#define MCDE_CHNL3CONF_LPF(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3CONF, LPF, __x)
+#define MCDE_CHNL0STAT 0x00000604
+#define MCDE_CHNL0STAT_GROUPOFFSET 0x20
+#define MCDE_CHNL0STAT_CHNLRD_SHIFT 0
+#define MCDE_CHNL0STAT_CHNLRD_MASK 0x00000001
+#define MCDE_CHNL0STAT_CHNLRD(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0STAT, CHNLRD, __x)
+#define MCDE_CHNL0STAT_CHNLA_SHIFT 1
+#define MCDE_CHNL0STAT_CHNLA_MASK 0x00000002
+#define MCDE_CHNL0STAT_CHNLA(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0STAT, CHNLA, __x)
+#define MCDE_CHNL0STAT_CHNLBLBCKGND_EN_SHIFT 16
+#define MCDE_CHNL0STAT_CHNLBLBCKGND_EN_MASK 0x00010000
+#define MCDE_CHNL0STAT_CHNLBLBCKGND_EN(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0STAT, CHNLBLBCKGND_EN, __x)
+#define MCDE_CHNL1STAT 0x00000624
+#define MCDE_CHNL1STAT_CHNLRD_SHIFT 0
+#define MCDE_CHNL1STAT_CHNLRD_MASK 0x00000001
+#define MCDE_CHNL1STAT_CHNLRD(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1STAT, CHNLRD, __x)
+#define MCDE_CHNL1STAT_CHNLA_SHIFT 1
+#define MCDE_CHNL1STAT_CHNLA_MASK 0x00000002
+#define MCDE_CHNL1STAT_CHNLA(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1STAT, CHNLA, __x)
+#define MCDE_CHNL1STAT_CHNLBLBCKGND_EN_SHIFT 16
+#define MCDE_CHNL1STAT_CHNLBLBCKGND_EN_MASK 0x00010000
+#define MCDE_CHNL1STAT_CHNLBLBCKGND_EN(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1STAT, CHNLBLBCKGND_EN, __x)
+#define MCDE_CHNL2STAT 0x00000644
+#define MCDE_CHNL2STAT_CHNLRD_SHIFT 0
+#define MCDE_CHNL2STAT_CHNLRD_MASK 0x00000001
+#define MCDE_CHNL2STAT_CHNLRD(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2STAT, CHNLRD, __x)
+#define MCDE_CHNL2STAT_CHNLA_SHIFT 1
+#define MCDE_CHNL2STAT_CHNLA_MASK 0x00000002
+#define MCDE_CHNL2STAT_CHNLA(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2STAT, CHNLA, __x)
+#define MCDE_CHNL2STAT_CHNLBLBCKGND_EN_SHIFT 16
+#define MCDE_CHNL2STAT_CHNLBLBCKGND_EN_MASK 0x00010000
+#define MCDE_CHNL2STAT_CHNLBLBCKGND_EN(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2STAT, CHNLBLBCKGND_EN, __x)
+#define MCDE_CHNL3STAT 0x00000664
+#define MCDE_CHNL3STAT_CHNLRD_SHIFT 0
+#define MCDE_CHNL3STAT_CHNLRD_MASK 0x00000001
+#define MCDE_CHNL3STAT_CHNLRD(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3STAT, CHNLRD, __x)
+#define MCDE_CHNL3STAT_CHNLA_SHIFT 1
+#define MCDE_CHNL3STAT_CHNLA_MASK 0x00000002
+#define MCDE_CHNL3STAT_CHNLA(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3STAT, CHNLA, __x)
+#define MCDE_CHNL3STAT_CHNLBLBCKGND_EN_SHIFT 16
+#define MCDE_CHNL3STAT_CHNLBLBCKGND_EN_MASK 0x00010000
+#define MCDE_CHNL3STAT_CHNLBLBCKGND_EN(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3STAT, CHNLBLBCKGND_EN, __x)
+#define MCDE_CHNL0SYNCHMOD 0x00000608
+#define MCDE_CHNL0SYNCHMOD_GROUPOFFSET 0x20
+#define MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SHIFT 0
+#define MCDE_CHNL0SYNCHMOD_SRC_SYNCH_MASK 0x00000003
+#define MCDE_CHNL0SYNCHMOD_SRC_SYNCH_OUTPUT 0
+#define MCDE_CHNL0SYNCHMOD_SRC_SYNCH_AUTO 1
+#define MCDE_CHNL0SYNCHMOD_SRC_SYNCH_SOFTWARE 2
+#define MCDE_CHNL0SYNCHMOD_SRC_SYNCH_EXTERNAL 3
+#define MCDE_CHNL0SYNCHMOD_SRC_SYNCH_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0SYNCHMOD, SRC_SYNCH, \
+ MCDE_CHNL0SYNCHMOD_SRC_SYNCH_##__x)
+#define MCDE_CHNL0SYNCHMOD_SRC_SYNCH(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0SYNCHMOD, SRC_SYNCH, __x)
+#define MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_SHIFT 2
+#define MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_MASK 0x0000001C
+#define MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_FORMATTER 0
+#define MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_VSYNC0 1
+#define MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_VSYNC1 2
+#define MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0SYNCHMOD, OUT_SYNCH_SRC, \
+ MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC_##__x)
+#define MCDE_CHNL0SYNCHMOD_OUT_SYNCH_SRC(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0SYNCHMOD, OUT_SYNCH_SRC, __x)
+#define MCDE_CHNL1SYNCHMOD 0x00000628
+#define MCDE_CHNL1SYNCHMOD_SRC_SYNCH_SHIFT 0
+#define MCDE_CHNL1SYNCHMOD_SRC_SYNCH_MASK 0x00000003
+#define MCDE_CHNL1SYNCHMOD_SRC_SYNCH_OUTPUT 0
+#define MCDE_CHNL1SYNCHMOD_SRC_SYNCH_AUTO 1
+#define MCDE_CHNL1SYNCHMOD_SRC_SYNCH_SOFTWARE 2
+#define MCDE_CHNL1SYNCHMOD_SRC_SYNCH_EXTERNAL 3
+#define MCDE_CHNL1SYNCHMOD_SRC_SYNCH_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1SYNCHMOD, SRC_SYNCH, \
+ MCDE_CHNL1SYNCHMOD_SRC_SYNCH_##__x)
+#define MCDE_CHNL1SYNCHMOD_SRC_SYNCH(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1SYNCHMOD, SRC_SYNCH, __x)
+#define MCDE_CHNL1SYNCHMOD_OUT_SYNCH_SRC_SHIFT 2
+#define MCDE_CHNL1SYNCHMOD_OUT_SYNCH_SRC_MASK 0x0000001C
+#define MCDE_CHNL1SYNCHMOD_OUT_SYNCH_SRC_FORMATTER 0
+#define MCDE_CHNL1SYNCHMOD_OUT_SYNCH_SRC_VSYNC0 1
+#define MCDE_CHNL1SYNCHMOD_OUT_SYNCH_SRC_VSYNC1 2
+#define MCDE_CHNL1SYNCHMOD_OUT_SYNCH_SRC_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1SYNCHMOD, OUT_SYNCH_SRC, \
+ MCDE_CHNL1SYNCHMOD_OUT_SYNCH_SRC_##__x)
+#define MCDE_CHNL1SYNCHMOD_OUT_SYNCH_SRC(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1SYNCHMOD, OUT_SYNCH_SRC, __x)
+#define MCDE_CHNL2SYNCHMOD 0x00000648
+#define MCDE_CHNL2SYNCHMOD_SRC_SYNCH_SHIFT 0
+#define MCDE_CHNL2SYNCHMOD_SRC_SYNCH_MASK 0x00000003
+#define MCDE_CHNL2SYNCHMOD_SRC_SYNCH_OUTPUT 0
+#define MCDE_CHNL2SYNCHMOD_SRC_SYNCH_AUTO 1
+#define MCDE_CHNL2SYNCHMOD_SRC_SYNCH_SOFTWARE 2
+#define MCDE_CHNL2SYNCHMOD_SRC_SYNCH_EXTERNAL 3
+#define MCDE_CHNL2SYNCHMOD_SRC_SYNCH_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2SYNCHMOD, SRC_SYNCH, \
+ MCDE_CHNL2SYNCHMOD_SRC_SYNCH_##__x)
+#define MCDE_CHNL2SYNCHMOD_SRC_SYNCH(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2SYNCHMOD, SRC_SYNCH, __x)
+#define MCDE_CHNL2SYNCHMOD_OUT_SYNCH_SRC_SHIFT 2
+#define MCDE_CHNL2SYNCHMOD_OUT_SYNCH_SRC_MASK 0x0000001C
+#define MCDE_CHNL2SYNCHMOD_OUT_SYNCH_SRC_FORMATTER 0
+#define MCDE_CHNL2SYNCHMOD_OUT_SYNCH_SRC_VSYNC0 1
+#define MCDE_CHNL2SYNCHMOD_OUT_SYNCH_SRC_VSYNC1 2
+#define MCDE_CHNL2SYNCHMOD_OUT_SYNCH_SRC_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2SYNCHMOD, OUT_SYNCH_SRC, \
+ MCDE_CHNL2SYNCHMOD_OUT_SYNCH_SRC_##__x)
+#define MCDE_CHNL2SYNCHMOD_OUT_SYNCH_SRC(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2SYNCHMOD, OUT_SYNCH_SRC, __x)
+#define MCDE_CHNL3SYNCHMOD 0x00000668
+#define MCDE_CHNL3SYNCHMOD_SRC_SYNCH_SHIFT 0
+#define MCDE_CHNL3SYNCHMOD_SRC_SYNCH_MASK 0x00000003
+#define MCDE_CHNL3SYNCHMOD_SRC_SYNCH_OUTPUT 0
+#define MCDE_CHNL3SYNCHMOD_SRC_SYNCH_AUTO 1
+#define MCDE_CHNL3SYNCHMOD_SRC_SYNCH_SOFTWARE 2
+#define MCDE_CHNL3SYNCHMOD_SRC_SYNCH_EXTERNAL 3
+#define MCDE_CHNL3SYNCHMOD_SRC_SYNCH_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3SYNCHMOD, SRC_SYNCH, \
+ MCDE_CHNL3SYNCHMOD_SRC_SYNCH_##__x)
+#define MCDE_CHNL3SYNCHMOD_SRC_SYNCH(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3SYNCHMOD, SRC_SYNCH, __x)
+#define MCDE_CHNL3SYNCHMOD_OUT_SYNCH_SRC_SHIFT 2
+#define MCDE_CHNL3SYNCHMOD_OUT_SYNCH_SRC_MASK 0x0000001C
+#define MCDE_CHNL3SYNCHMOD_OUT_SYNCH_SRC_FORMATTER 0
+#define MCDE_CHNL3SYNCHMOD_OUT_SYNCH_SRC_VSYNC0 1
+#define MCDE_CHNL3SYNCHMOD_OUT_SYNCH_SRC_VSYNC1 2
+#define MCDE_CHNL3SYNCHMOD_OUT_SYNCH_SRC_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3SYNCHMOD, OUT_SYNCH_SRC, \
+ MCDE_CHNL3SYNCHMOD_OUT_SYNCH_SRC_##__x)
+#define MCDE_CHNL3SYNCHMOD_OUT_SYNCH_SRC(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3SYNCHMOD, OUT_SYNCH_SRC, __x)
+#define MCDE_CHNL0SYNCHSW 0x0000060C
+#define MCDE_CHNL0SYNCHSW_GROUPOFFSET 0x20
+#define MCDE_CHNL0SYNCHSW_SW_TRIG_SHIFT 0
+#define MCDE_CHNL0SYNCHSW_SW_TRIG_MASK 0x00000001
+#define MCDE_CHNL0SYNCHSW_SW_TRIG(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0SYNCHSW, SW_TRIG, __x)
+#define MCDE_CHNL1SYNCHSW 0x0000062C
+#define MCDE_CHNL1SYNCHSW_SW_TRIG_SHIFT 0
+#define MCDE_CHNL1SYNCHSW_SW_TRIG_MASK 0x00000001
+#define MCDE_CHNL1SYNCHSW_SW_TRIG(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1SYNCHSW, SW_TRIG, __x)
+#define MCDE_CHNL2SYNCHSW 0x0000064C
+#define MCDE_CHNL2SYNCHSW_SW_TRIG_SHIFT 0
+#define MCDE_CHNL2SYNCHSW_SW_TRIG_MASK 0x00000001
+#define MCDE_CHNL2SYNCHSW_SW_TRIG(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2SYNCHSW, SW_TRIG, __x)
+#define MCDE_CHNL3SYNCHSW 0x0000066C
+#define MCDE_CHNL3SYNCHSW_SW_TRIG_SHIFT 0
+#define MCDE_CHNL3SYNCHSW_SW_TRIG_MASK 0x00000001
+#define MCDE_CHNL3SYNCHSW_SW_TRIG(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3SYNCHSW, SW_TRIG, __x)
+#define MCDE_CHNL0BCKGNDCOL 0x00000610
+#define MCDE_CHNL0BCKGNDCOL_GROUPOFFSET 0x20
+#define MCDE_CHNL0BCKGNDCOL_B_SHIFT 0
+#define MCDE_CHNL0BCKGNDCOL_B_MASK 0x000000FF
+#define MCDE_CHNL0BCKGNDCOL_B(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0BCKGNDCOL, B, __x)
+#define MCDE_CHNL0BCKGNDCOL_G_SHIFT 8
+#define MCDE_CHNL0BCKGNDCOL_G_MASK 0x0000FF00
+#define MCDE_CHNL0BCKGNDCOL_G(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0BCKGNDCOL, G, __x)
+#define MCDE_CHNL0BCKGNDCOL_R_SHIFT 16
+#define MCDE_CHNL0BCKGNDCOL_R_MASK 0x00FF0000
+#define MCDE_CHNL0BCKGNDCOL_R(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0BCKGNDCOL, R, __x)
+#define MCDE_CHNL1BCKGNDCOL 0x00000630
+#define MCDE_CHNL1BCKGNDCOL_B_SHIFT 0
+#define MCDE_CHNL1BCKGNDCOL_B_MASK 0x000000FF
+#define MCDE_CHNL1BCKGNDCOL_B(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1BCKGNDCOL, B, __x)
+#define MCDE_CHNL1BCKGNDCOL_G_SHIFT 8
+#define MCDE_CHNL1BCKGNDCOL_G_MASK 0x0000FF00
+#define MCDE_CHNL1BCKGNDCOL_G(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1BCKGNDCOL, G, __x)
+#define MCDE_CHNL1BCKGNDCOL_R_SHIFT 16
+#define MCDE_CHNL1BCKGNDCOL_R_MASK 0x00FF0000
+#define MCDE_CHNL1BCKGNDCOL_R(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1BCKGNDCOL, R, __x)
+#define MCDE_CHNL2BCKGNDCOL 0x00000650
+#define MCDE_CHNL2BCKGNDCOL_B_SHIFT 0
+#define MCDE_CHNL2BCKGNDCOL_B_MASK 0x000000FF
+#define MCDE_CHNL2BCKGNDCOL_B(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2BCKGNDCOL, B, __x)
+#define MCDE_CHNL2BCKGNDCOL_G_SHIFT 8
+#define MCDE_CHNL2BCKGNDCOL_G_MASK 0x0000FF00
+#define MCDE_CHNL2BCKGNDCOL_G(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2BCKGNDCOL, G, __x)
+#define MCDE_CHNL2BCKGNDCOL_R_SHIFT 16
+#define MCDE_CHNL2BCKGNDCOL_R_MASK 0x00FF0000
+#define MCDE_CHNL2BCKGNDCOL_R(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2BCKGNDCOL, R, __x)
+#define MCDE_CHNL3BCKGNDCOL 0x00000670
+#define MCDE_CHNL3BCKGNDCOL_B_SHIFT 0
+#define MCDE_CHNL3BCKGNDCOL_B_MASK 0x000000FF
+#define MCDE_CHNL3BCKGNDCOL_B(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3BCKGNDCOL, B, __x)
+#define MCDE_CHNL3BCKGNDCOL_G_SHIFT 8
+#define MCDE_CHNL3BCKGNDCOL_G_MASK 0x0000FF00
+#define MCDE_CHNL3BCKGNDCOL_G(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3BCKGNDCOL, G, __x)
+#define MCDE_CHNL3BCKGNDCOL_R_SHIFT 16
+#define MCDE_CHNL3BCKGNDCOL_R_MASK 0x00FF0000
+#define MCDE_CHNL3BCKGNDCOL_R(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3BCKGNDCOL, R, __x)
+#define MCDE_CHNL0PRIO_V1 0x00000614
+#define MCDE_CHNL0PRIO_V1_GROUPOFFSET 0x20
+#define MCDE_CHNL0PRIO_V1_CHNLPRIO_SHIFT 0
+#define MCDE_CHNL0PRIO_V1_CHNLPRIO_MASK 0x0000000F
+#define MCDE_CHNL0PRIO_V1_CHNLPRIO(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0PRIO_V1, CHNLPRIO, __x)
+#define MCDE_CHNL1PRIO_V1 0x00000634
+#define MCDE_CHNL1PRIO_V1_CHNLPRIO_SHIFT 0
+#define MCDE_CHNL1PRIO_V1_CHNLPRIO_MASK 0x0000000F
+#define MCDE_CHNL1PRIO_V1_CHNLPRIO(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1PRIO_V1, CHNLPRIO, __x)
+#define MCDE_CHNL2PRIO_V1 0x00000654
+#define MCDE_CHNL2PRIO_V1_CHNLPRIO_SHIFT 0
+#define MCDE_CHNL2PRIO_V1_CHNLPRIO_MASK 0x0000000F
+#define MCDE_CHNL2PRIO_V1_CHNLPRIO(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2PRIO_V1, CHNLPRIO, __x)
+#define MCDE_CHNL3PRIO_V1 0x00000674
+#define MCDE_CHNL3PRIO_V1_CHNLPRIO_SHIFT 0
+#define MCDE_CHNL3PRIO_V1_CHNLPRIO_MASK 0x0000000F
+#define MCDE_CHNL3PRIO_V1_CHNLPRIO(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3PRIO_V1, CHNLPRIO, __x)
+#define MCDE_CHNL0MUXING_V2 0x00000614
+#define MCDE_CHNL0MUXING_V2_GROUPOFFSET 0x20
+#define MCDE_CHNL0MUXING_V2_FIFO_ID_SHIFT 0
+#define MCDE_CHNL0MUXING_V2_FIFO_ID_MASK 0x00000007
+#define MCDE_CHNL0MUXING_V2_FIFO_ID_FIFO_A 0
+#define MCDE_CHNL0MUXING_V2_FIFO_ID_FIFO_B 1
+#define MCDE_CHNL0MUXING_V2_FIFO_ID_FIFO_C0 2
+#define MCDE_CHNL0MUXING_V2_FIFO_ID_FIFO_C1 3
+#define MCDE_CHNL0MUXING_V2_FIFO_ID_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0MUXING_V2, FIFO_ID, \
+ MCDE_CHNL0MUXING_V2_FIFO_ID_##__x)
+#define MCDE_CHNL0MUXING_V2_FIFO_ID(__x) \
+ MCDE_VAL2REG(MCDE_CHNL0MUXING_V2, FIFO_ID, __x)
+#define MCDE_CHNL1MUXING_V2 0x00000634
+#define MCDE_CHNL1MUXING_V2_FIFO_ID_SHIFT 0
+#define MCDE_CHNL1MUXING_V2_FIFO_ID_MASK 0x00000007
+#define MCDE_CHNL1MUXING_V2_FIFO_ID_FIFO_A 0
+#define MCDE_CHNL1MUXING_V2_FIFO_ID_FIFO_B 1
+#define MCDE_CHNL1MUXING_V2_FIFO_ID_FIFO_C0 2
+#define MCDE_CHNL1MUXING_V2_FIFO_ID_FIFO_C1 3
+#define MCDE_CHNL1MUXING_V2_FIFO_ID_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1MUXING_V2, FIFO_ID, \
+ MCDE_CHNL1MUXING_V2_FIFO_ID_##__x)
+#define MCDE_CHNL1MUXING_V2_FIFO_ID(__x) \
+ MCDE_VAL2REG(MCDE_CHNL1MUXING_V2, FIFO_ID, __x)
+#define MCDE_CHNL2MUXING_V2 0x00000654
+#define MCDE_CHNL2MUXING_V2_FIFO_ID_SHIFT 0
+#define MCDE_CHNL2MUXING_V2_FIFO_ID_MASK 0x00000007
+#define MCDE_CHNL2MUXING_V2_FIFO_ID_FIFO_A 0
+#define MCDE_CHNL2MUXING_V2_FIFO_ID_FIFO_B 1
+#define MCDE_CHNL2MUXING_V2_FIFO_ID_FIFO_C0 2
+#define MCDE_CHNL2MUXING_V2_FIFO_ID_FIFO_C1 3
+#define MCDE_CHNL2MUXING_V2_FIFO_ID_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2MUXING_V2, FIFO_ID, \
+ MCDE_CHNL2MUXING_V2_FIFO_ID_##__x)
+#define MCDE_CHNL2MUXING_V2_FIFO_ID(__x) \
+ MCDE_VAL2REG(MCDE_CHNL2MUXING_V2, FIFO_ID, __x)
+#define MCDE_CHNL3MUXING_V2 0x00000674
+#define MCDE_CHNL3MUXING_V2_FIFO_ID_SHIFT 0
+#define MCDE_CHNL3MUXING_V2_FIFO_ID_MASK 0x00000007
+#define MCDE_CHNL3MUXING_V2_FIFO_ID_FIFO_A 0
+#define MCDE_CHNL3MUXING_V2_FIFO_ID_FIFO_B 1
+#define MCDE_CHNL3MUXING_V2_FIFO_ID_FIFO_C0 2
+#define MCDE_CHNL3MUXING_V2_FIFO_ID_FIFO_C1 3
+#define MCDE_CHNL3MUXING_V2_FIFO_ID_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3MUXING_V2, FIFO_ID, \
+ MCDE_CHNL3MUXING_V2_FIFO_ID_##__x)
+#define MCDE_CHNL3MUXING_V2_FIFO_ID(__x) \
+ MCDE_VAL2REG(MCDE_CHNL3MUXING_V2, FIFO_ID, __x)
+#define MCDE_CRA0 0x00000800
+#define MCDE_CRA0_GROUPOFFSET 0x200
+#define MCDE_CRA0_FLOEN_SHIFT 0
+#define MCDE_CRA0_FLOEN_MASK 0x00000001
+#define MCDE_CRA0_FLOEN(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, FLOEN, __x)
+#define MCDE_CRA0_POWEREN_SHIFT 1
+#define MCDE_CRA0_POWEREN_MASK 0x00000002
+#define MCDE_CRA0_POWEREN(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, POWEREN, __x)
+#define MCDE_CRA0_BLENDEN_SHIFT 2
+#define MCDE_CRA0_BLENDEN_MASK 0x00000004
+#define MCDE_CRA0_BLENDEN(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, BLENDEN, __x)
+#define MCDE_CRA0_AFLICKEN_SHIFT 3
+#define MCDE_CRA0_AFLICKEN_MASK 0x00000008
+#define MCDE_CRA0_AFLICKEN(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, AFLICKEN, __x)
+#define MCDE_CRA0_PALEN_SHIFT 4
+#define MCDE_CRA0_PALEN_MASK 0x00000010
+#define MCDE_CRA0_PALEN(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, PALEN, __x)
+#define MCDE_CRA0_DITHEN_SHIFT 5
+#define MCDE_CRA0_DITHEN_MASK 0x00000020
+#define MCDE_CRA0_DITHEN(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, DITHEN, __x)
+#define MCDE_CRA0_GAMEN_SHIFT 6
+#define MCDE_CRA0_GAMEN_MASK 0x00000040
+#define MCDE_CRA0_GAMEN(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, GAMEN, __x)
+#define MCDE_CRA0_KEYCTRL_SHIFT 7
+#define MCDE_CRA0_KEYCTRL_MASK 0x00000380
+#define MCDE_CRA0_KEYCTRL_OFF 0
+#define MCDE_CRA0_KEYCTRL_ALPHA_RGB 1
+#define MCDE_CRA0_KEYCTRL_RGB 2
+#define MCDE_CRA0_KEYCTRL_FALPHA_FRGB 4
+#define MCDE_CRA0_KEYCTRL_FRGB 5
+#define MCDE_CRA0_KEYCTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, KEYCTRL, MCDE_CRA0_KEYCTRL_##__x)
+#define MCDE_CRA0_KEYCTRL(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, KEYCTRL, __x)
+#define MCDE_CRA0_BLENDCTRL_SHIFT 10
+#define MCDE_CRA0_BLENDCTRL_MASK 0x00000400
+#define MCDE_CRA0_BLENDCTRL_SOURCE 0
+#define MCDE_CRA0_BLENDCTRL_CONSTANT 1
+#define MCDE_CRA0_BLENDCTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, BLENDCTRL, MCDE_CRA0_BLENDCTRL_##__x)
+#define MCDE_CRA0_BLENDCTRL(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, BLENDCTRL, __x)
+#define MCDE_CRA0_FLICKMODE_SHIFT 11
+#define MCDE_CRA0_FLICKMODE_MASK 0x00001800
+#define MCDE_CRA0_FLICKMODE_FORCE_FILTER_0 0
+#define MCDE_CRA0_FLICKMODE_ADAPTIVE 1
+#define MCDE_CRA0_FLICKMODE_TEST_MODE 2
+#define MCDE_CRA0_FLICKMODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, FLICKMODE, MCDE_CRA0_FLICKMODE_##__x)
+#define MCDE_CRA0_FLICKMODE(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, FLICKMODE, __x)
+#define MCDE_CRA0_FLOCKFORMAT_SHIFT 13
+#define MCDE_CRA0_FLOCKFORMAT_MASK 0x00002000
+#define MCDE_CRA0_FLOCKFORMAT_YCBCR 0
+#define MCDE_CRA0_FLOCKFORMAT_RGB 1
+#define MCDE_CRA0_FLOCKFORMAT_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, FLOCKFORMAT, MCDE_CRA0_FLOCKFORMAT_##__x)
+#define MCDE_CRA0_FLOCKFORMAT(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, FLOCKFORMAT, __x)
+#define MCDE_CRA0_PALMODE_SHIFT 14
+#define MCDE_CRA0_PALMODE_MASK 0x00004000
+#define MCDE_CRA0_PALMODE_PALETTE 0
+#define MCDE_CRA0_PALMODE_GAMMA 1
+#define MCDE_CRA0_PALMODE(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, PALMODE, __x)
+#define MCDE_CRA0_OLEDEN_SHIFT 15
+#define MCDE_CRA0_OLEDEN_MASK 0x00008000
+#define MCDE_CRA0_OLEDEN(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, OLEDEN, __x)
+#define MCDE_CRA0_ALPHABLEND_SHIFT 16
+#define MCDE_CRA0_ALPHABLEND_MASK 0x00FF0000
+#define MCDE_CRA0_ALPHABLEND(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, ALPHABLEND, __x)
+#define MCDE_CRA0_ROTEN_SHIFT 24
+#define MCDE_CRA0_ROTEN_MASK 0x01000000
+#define MCDE_CRA0_ROTEN(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, ROTEN, __x)
+#define MCDE_CRA0_ROTBURSTSIZE_V1_SHIFT 25
+#define MCDE_CRA0_ROTBURSTSIZE_V1_MASK 0x0E000000
+#define MCDE_CRA0_ROTBURSTSIZE_V1_1W 0
+#define MCDE_CRA0_ROTBURSTSIZE_V1_2W 1
+#define MCDE_CRA0_ROTBURSTSIZE_V1_4W 2
+#define MCDE_CRA0_ROTBURSTSIZE_V1_8W 3
+#define MCDE_CRA0_ROTBURSTSIZE_V1_16W 4
+#define MCDE_CRA0_ROTBURSTSIZE_V1_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, ROTBURSTSIZE_V1, \
+ MCDE_CRA0_ROTBURSTSIZE_V1_##__x)
+#define MCDE_CRA0_ROTBURSTSIZE_V1(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, ROTBURSTSIZE_V1, __x)
+#define MCDE_CRA0_ROTBURSTSIZE_HW_V1_SHIFT 28
+#define MCDE_CRA0_ROTBURSTSIZE_HW_V1_MASK 0x10000000
+#define MCDE_CRA0_ROTBURSTSIZE_HW_V1(__x) \
+ MCDE_VAL2REG(MCDE_CRA0, ROTBURSTSIZE_HW_V1, __x)
+#define MCDE_CRB0 0x00000A00
+#define MCDE_CRB0_FLOEN_SHIFT 0
+#define MCDE_CRB0_FLOEN_MASK 0x00000001
+#define MCDE_CRB0_FLOEN(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, FLOEN, __x)
+#define MCDE_CRB0_POWEREN_SHIFT 1
+#define MCDE_CRB0_POWEREN_MASK 0x00000002
+#define MCDE_CRB0_POWEREN(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, POWEREN, __x)
+#define MCDE_CRB0_BLENDEN_SHIFT 2
+#define MCDE_CRB0_BLENDEN_MASK 0x00000004
+#define MCDE_CRB0_BLENDEN(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, BLENDEN, __x)
+#define MCDE_CRB0_AFLICKEN_SHIFT 3
+#define MCDE_CRB0_AFLICKEN_MASK 0x00000008
+#define MCDE_CRB0_AFLICKEN(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, AFLICKEN, __x)
+#define MCDE_CRB0_PALEN_SHIFT 4
+#define MCDE_CRB0_PALEN_MASK 0x00000010
+#define MCDE_CRB0_PALEN(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, PALEN, __x)
+#define MCDE_CRB0_DITHEN_SHIFT 5
+#define MCDE_CRB0_DITHEN_MASK 0x00000020
+#define MCDE_CRB0_DITHEN(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, DITHEN, __x)
+#define MCDE_CRB0_GAMEN_SHIFT 6
+#define MCDE_CRB0_GAMEN_MASK 0x00000040
+#define MCDE_CRB0_GAMEN(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, GAMEN, __x)
+#define MCDE_CRB0_KEYCTRL_SHIFT 7
+#define MCDE_CRB0_KEYCTRL_MASK 0x00000380
+#define MCDE_CRB0_KEYCTRL_OFF 0
+#define MCDE_CRB0_KEYCTRL_ALPHA_RGB 1
+#define MCDE_CRB0_KEYCTRL_RGB 2
+#define MCDE_CRB0_KEYCTRL_FALPHA_FRGB 4
+#define MCDE_CRB0_KEYCTRL_FRGB 5
+#define MCDE_CRB0_KEYCTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, KEYCTRL, MCDE_CRB0_KEYCTRL_##__x)
+#define MCDE_CRB0_KEYCTRL(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, KEYCTRL, __x)
+#define MCDE_CRB0_BLENDCTRL_SHIFT 10
+#define MCDE_CRB0_BLENDCTRL_MASK 0x00000400
+#define MCDE_CRB0_BLENDCTRL_SOURCE 0
+#define MCDE_CRB0_BLENDCTRL_CONSTANT 1
+#define MCDE_CRB0_BLENDCTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, BLENDCTRL, MCDE_CRB0_BLENDCTRL_##__x)
+#define MCDE_CRB0_BLENDCTRL(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, BLENDCTRL, __x)
+#define MCDE_CRB0_FLICKMODE_SHIFT 11
+#define MCDE_CRB0_FLICKMODE_MASK 0x00001800
+#define MCDE_CRB0_FLICKMODE_FORCE_FILTER_0 0
+#define MCDE_CRB0_FLICKMODE_ADAPTIVE 1
+#define MCDE_CRB0_FLICKMODE_TEST_MODE 2
+#define MCDE_CRB0_FLICKMODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, FLICKMODE, MCDE_CRB0_FLICKMODE_##__x)
+#define MCDE_CRB0_FLICKMODE(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, FLICKMODE, __x)
+#define MCDE_CRB0_FLOCKFORMAT_SHIFT 13
+#define MCDE_CRB0_FLOCKFORMAT_MASK 0x00002000
+#define MCDE_CRB0_FLOCKFORMAT_YCBCR 0
+#define MCDE_CRB0_FLOCKFORMAT_RGB 1
+#define MCDE_CRB0_FLOCKFORMAT_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, FLOCKFORMAT, MCDE_CRB0_FLOCKFORMAT_##__x)
+#define MCDE_CRB0_FLOCKFORMAT(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, FLOCKFORMAT, __x)
+#define MCDE_CRB0_PALMODE_SHIFT 14
+#define MCDE_CRB0_PALMODE_MASK 0x00004000
+#define MCDE_CRB0_PALMODE_PALETTE 0
+#define MCDE_CRB0_PALMODE_GAMMA 1
+#define MCDE_CRB0_PALMODE(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, PALMODE, __x)
+#define MCDE_CRB0_OLEDEN_SHIFT 15
+#define MCDE_CRB0_OLEDEN_MASK 0x00008000
+#define MCDE_CRB0_OLEDEN(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, OLEDEN, __x)
+#define MCDE_CRB0_ALPHABLEND_SHIFT 16
+#define MCDE_CRB0_ALPHABLEND_MASK 0x00FF0000
+#define MCDE_CRB0_ALPHABLEND(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, ALPHABLEND, __x)
+#define MCDE_CRB0_ROTEN_SHIFT 24
+#define MCDE_CRB0_ROTEN_MASK 0x01000000
+#define MCDE_CRB0_ROTEN(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, ROTEN, __x)
+#define MCDE_CRB0_ROTBURSTSIZE_V1_SHIFT 25
+#define MCDE_CRB0_ROTBURSTSIZE_V1_MASK 0x0E000000
+#define MCDE_CRB0_ROTBURSTSIZE_V1_1W 0
+#define MCDE_CRB0_ROTBURSTSIZE_V1_2W 1
+#define MCDE_CRB0_ROTBURSTSIZE_V1_4W 2
+#define MCDE_CRB0_ROTBURSTSIZE_V1_8W 3
+#define MCDE_CRB0_ROTBURSTSIZE_V1_16W 4
+#define MCDE_CRB0_ROTBURSTSIZE_V1_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, ROTBURSTSIZE_V1, \
+ MCDE_CRB0_ROTBURSTSIZE_V1_##__x)
+#define MCDE_CRB0_ROTBURSTSIZE_V1(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, ROTBURSTSIZE_V1, __x)
+#define MCDE_CRB0_ROTBURSTSIZE_HW_V1_SHIFT 28
+#define MCDE_CRB0_ROTBURSTSIZE_HW_V1_MASK 0x10000000
+#define MCDE_CRB0_ROTBURSTSIZE_HW_V1(__x) \
+ MCDE_VAL2REG(MCDE_CRB0, ROTBURSTSIZE_HW_V1, __x)
+#define MCDE_CRA1 0x00000804
+#define MCDE_CRA1_GROUPOFFSET 0x200
+#define MCDE_CRA1_PCD_SHIFT 0
+#define MCDE_CRA1_PCD_MASK 0x000003FF
+#define MCDE_CRA1_PCD(__x) \
+ MCDE_VAL2REG(MCDE_CRA1, PCD, __x)
+#define MCDE_CRA1_CLKSEL_SHIFT 10
+#define MCDE_CRA1_CLKSEL_MASK 0x00001C00
+#define MCDE_CRA1_CLKSEL_LCD 0
+#define MCDE_CRA1_CLKSEL_HDMI 1
+#define MCDE_CRA1_CLKSEL_TV 2
+#define MCDE_CRA1_CLKSEL_EXT_TV1 3
+#define MCDE_CRA1_CLKSEL_EXT_TV2 4
+#define MCDE_CRA1_CLKSEL_166MHZ 5
+#define MCDE_CRA1_CLKSEL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRA1, CLKSEL, MCDE_CRA1_CLKSEL_##__x)
+#define MCDE_CRA1_CLKSEL(__x) \
+ MCDE_VAL2REG(MCDE_CRA1, CLKSEL, __x)
+#define MCDE_CRA1_CDWIN_SHIFT 13
+#define MCDE_CRA1_CDWIN_MASK 0x0001E000
+#define MCDE_CRA1_CDWIN_8BPP_C1 0
+#define MCDE_CRA1_CDWIN_12BPP_C1 1
+#define MCDE_CRA1_CDWIN_12BPP_C2 2
+#define MCDE_CRA1_CDWIN_16BPP_C1 3
+#define MCDE_CRA1_CDWIN_16BPP_C2 4
+#define MCDE_CRA1_CDWIN_16BPP_C3 5
+#define MCDE_CRA1_CDWIN_18BPP_C1 6
+#define MCDE_CRA1_CDWIN_18BPP_C2 7
+#define MCDE_CRA1_CDWIN_24BPP 8
+#define MCDE_CRA1_CDWIN_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRA1, CDWIN, MCDE_CRA1_CDWIN_##__x)
+#define MCDE_CRA1_CDWIN(__x) \
+ MCDE_VAL2REG(MCDE_CRA1, CDWIN, __x)
+#define MCDE_CRA1_OUTBPP_SHIFT 25
+#define MCDE_CRA1_OUTBPP_MASK 0x1E000000
+#define MCDE_CRA1_OUTBPP_MONO1 0
+#define MCDE_CRA1_OUTBPP_MONO2 1
+#define MCDE_CRA1_OUTBPP_MONO4 2
+#define MCDE_CRA1_OUTBPP_MONO8 3
+#define MCDE_CRA1_OUTBPP_8BPP 4
+#define MCDE_CRA1_OUTBPP_12BPP 5
+#define MCDE_CRA1_OUTBPP_15BPP 6
+#define MCDE_CRA1_OUTBPP_16BPP 7
+#define MCDE_CRA1_OUTBPP_18BPP 8
+#define MCDE_CRA1_OUTBPP_24BPP 9
+#define MCDE_CRA1_OUTBPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRA1, OUTBPP, MCDE_CRA1_OUTBPP_##__x)
+#define MCDE_CRA1_OUTBPP(__x) \
+ MCDE_VAL2REG(MCDE_CRA1, OUTBPP, __x)
+#define MCDE_CRA1_BCD_SHIFT 29
+#define MCDE_CRA1_BCD_MASK 0x20000000
+#define MCDE_CRA1_BCD(__x) \
+ MCDE_VAL2REG(MCDE_CRA1, BCD, __x)
+#define MCDE_CRA1_CLKTYPE_SHIFT 30
+#define MCDE_CRA1_CLKTYPE_MASK 0x40000000
+#define MCDE_CRA1_CLKTYPE_EXTERNAL 0
+#define MCDE_CRA1_CLKTYPE_INTERNAL 1
+#define MCDE_CRA1_CLKTYPE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRA1, CLKTYPE, MCDE_CRA1_CLKTYPE_##__x)
+#define MCDE_CRA1_CLKTYPE(__x) \
+ MCDE_VAL2REG(MCDE_CRA1, CLKTYPE, __x)
+#define MCDE_CRA1_TEFFECTEN_V1_SHIFT 31
+#define MCDE_CRA1_TEFFECTEN_V1_MASK 0x80000000
+#define MCDE_CRA1_TEFFECTEN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CRA1, TEFFECTEN_V1, __x)
+#define MCDE_CRB1 0x00000A04
+#define MCDE_CRB1_PCD_SHIFT 0
+#define MCDE_CRB1_PCD_MASK 0x000003FF
+#define MCDE_CRB1_PCD(__x) \
+ MCDE_VAL2REG(MCDE_CRB1, PCD, __x)
+#define MCDE_CRB1_CLKSEL_SHIFT 10
+#define MCDE_CRB1_CLKSEL_MASK 0x00001C00
+#define MCDE_CRB1_CLKSEL_LCD 0
+#define MCDE_CRB1_CLKSEL_HDMI 1
+#define MCDE_CRB1_CLKSEL_TV 2
+#define MCDE_CRB1_CLKSEL_EXT_TV1 3
+#define MCDE_CRB1_CLKSEL_EXT_TV2 4
+#define MCDE_CRB1_CLKSEL_166MHZ 5
+#define MCDE_CRB1_CLKSEL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRB1, CLKSEL, MCDE_CRB1_CLKSEL_##__x)
+#define MCDE_CRB1_CLKSEL(__x) \
+ MCDE_VAL2REG(MCDE_CRB1, CLKSEL, __x)
+#define MCDE_CRB1_CDWIN_SHIFT 13
+#define MCDE_CRB1_CDWIN_MASK 0x0001E000
+#define MCDE_CRB1_CDWIN_8BPP_C1 0
+#define MCDE_CRB1_CDWIN_12BPP_C1 1
+#define MCDE_CRB1_CDWIN_12BPP_C2 2
+#define MCDE_CRB1_CDWIN_16BPP_C1 3
+#define MCDE_CRB1_CDWIN_16BPP_C2 4
+#define MCDE_CRB1_CDWIN_16BPP_C3 5
+#define MCDE_CRB1_CDWIN_18BPP_C1 6
+#define MCDE_CRB1_CDWIN_18BPP_C2 7
+#define MCDE_CRB1_CDWIN_24BPP 8
+#define MCDE_CRB1_CDWIN_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRB1, CDWIN, MCDE_CRB1_CDWIN_##__x)
+#define MCDE_CRB1_CDWIN(__x) \
+ MCDE_VAL2REG(MCDE_CRB1, CDWIN, __x)
+#define MCDE_CRB1_OUTBPP_SHIFT 25
+#define MCDE_CRB1_OUTBPP_MASK 0x1E000000
+#define MCDE_CRB1_OUTBPP_MONO1 0
+#define MCDE_CRB1_OUTBPP_MONO2 1
+#define MCDE_CRB1_OUTBPP_MONO4 2
+#define MCDE_CRB1_OUTBPP_MONO8 3
+#define MCDE_CRB1_OUTBPP_8BPP 4
+#define MCDE_CRB1_OUTBPP_12BPP 5
+#define MCDE_CRB1_OUTBPP_15BPP 6
+#define MCDE_CRB1_OUTBPP_16BPP 7
+#define MCDE_CRB1_OUTBPP_18BPP 8
+#define MCDE_CRB1_OUTBPP_24BPP 9
+#define MCDE_CRB1_OUTBPP_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRB1, OUTBPP, MCDE_CRB1_OUTBPP_##__x)
+#define MCDE_CRB1_OUTBPP(__x) \
+ MCDE_VAL2REG(MCDE_CRB1, OUTBPP, __x)
+#define MCDE_CRB1_BCD_SHIFT 29
+#define MCDE_CRB1_BCD_MASK 0x20000000
+#define MCDE_CRB1_BCD(__x) \
+ MCDE_VAL2REG(MCDE_CRB1, BCD, __x)
+#define MCDE_CRB1_CLKTYPE_SHIFT 30
+#define MCDE_CRB1_CLKTYPE_MASK 0x40000000
+#define MCDE_CRB1_CLKTYPE_EXTERNAL 0
+#define MCDE_CRB1_CLKTYPE_INTERNAL 1
+#define MCDE_CRB1_CLKTYPE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRB1, CLKTYPE, MCDE_CRB1_CLKTYPE_##__x)
+#define MCDE_CRB1_CLKTYPE(__x) \
+ MCDE_VAL2REG(MCDE_CRB1, CLKTYPE, __x)
+#define MCDE_CRB1_TEFFECTEN_V1_SHIFT 31
+#define MCDE_CRB1_TEFFECTEN_V1_MASK 0x80000000
+#define MCDE_CRB1_TEFFECTEN_V1(__x) \
+ MCDE_VAL2REG(MCDE_CRB1, TEFFECTEN_V1, __x)
+#define MCDE_COLKEYA 0x00000808
+#define MCDE_COLKEYA_GROUPOFFSET 0x200
+#define MCDE_COLKEYA_KEYB_SHIFT 0
+#define MCDE_COLKEYA_KEYB_MASK 0x000000FF
+#define MCDE_COLKEYA_KEYB(__x) \
+ MCDE_VAL2REG(MCDE_COLKEYA, KEYB, __x)
+#define MCDE_COLKEYA_KEYG_SHIFT 8
+#define MCDE_COLKEYA_KEYG_MASK 0x0000FF00
+#define MCDE_COLKEYA_KEYG(__x) \
+ MCDE_VAL2REG(MCDE_COLKEYA, KEYG, __x)
+#define MCDE_COLKEYA_KEYR_SHIFT 16
+#define MCDE_COLKEYA_KEYR_MASK 0x00FF0000
+#define MCDE_COLKEYA_KEYR(__x) \
+ MCDE_VAL2REG(MCDE_COLKEYA, KEYR, __x)
+#define MCDE_COLKEYA_KEYA_SHIFT 24
+#define MCDE_COLKEYA_KEYA_MASK 0xFF000000
+#define MCDE_COLKEYA_KEYA(__x) \
+ MCDE_VAL2REG(MCDE_COLKEYA, KEYA, __x)
+#define MCDE_COLKEYB 0x00000A08
+#define MCDE_COLKEYB_KEYB_SHIFT 0
+#define MCDE_COLKEYB_KEYB_MASK 0x000000FF
+#define MCDE_COLKEYB_KEYB(__x) \
+ MCDE_VAL2REG(MCDE_COLKEYB, KEYB, __x)
+#define MCDE_COLKEYB_KEYG_SHIFT 8
+#define MCDE_COLKEYB_KEYG_MASK 0x0000FF00
+#define MCDE_COLKEYB_KEYG(__x) \
+ MCDE_VAL2REG(MCDE_COLKEYB, KEYG, __x)
+#define MCDE_COLKEYB_KEYR_SHIFT 16
+#define MCDE_COLKEYB_KEYR_MASK 0x00FF0000
+#define MCDE_COLKEYB_KEYR(__x) \
+ MCDE_VAL2REG(MCDE_COLKEYB, KEYR, __x)
+#define MCDE_COLKEYB_KEYA_SHIFT 24
+#define MCDE_COLKEYB_KEYA_MASK 0xFF000000
+#define MCDE_COLKEYB_KEYA(__x) \
+ MCDE_VAL2REG(MCDE_COLKEYB, KEYA, __x)
+#define MCDE_FCOLKEYA 0x0000080C
+#define MCDE_FCOLKEYA_GROUPOFFSET 0x200
+#define MCDE_FCOLKEYA_FKEYB_SHIFT 0
+#define MCDE_FCOLKEYA_FKEYB_MASK 0x000000FF
+#define MCDE_FCOLKEYA_FKEYB(__x) \
+ MCDE_VAL2REG(MCDE_FCOLKEYA, FKEYB, __x)
+#define MCDE_FCOLKEYA_FKEYG_SHIFT 8
+#define MCDE_FCOLKEYA_FKEYG_MASK 0x0000FF00
+#define MCDE_FCOLKEYA_FKEYG(__x) \
+ MCDE_VAL2REG(MCDE_FCOLKEYA, FKEYG, __x)
+#define MCDE_FCOLKEYA_FKEYR_SHIFT 16
+#define MCDE_FCOLKEYA_FKEYR_MASK 0x00FF0000
+#define MCDE_FCOLKEYA_FKEYR(__x) \
+ MCDE_VAL2REG(MCDE_FCOLKEYA, FKEYR, __x)
+#define MCDE_FCOLKEYA_FKEYA_SHIFT 24
+#define MCDE_FCOLKEYA_FKEYA_MASK 0xFF000000
+#define MCDE_FCOLKEYA_FKEYA(__x) \
+ MCDE_VAL2REG(MCDE_FCOLKEYA, FKEYA, __x)
+#define MCDE_FCOLKEYB 0x00000A0C
+#define MCDE_FCOLKEYB_FKEYB_SHIFT 0
+#define MCDE_FCOLKEYB_FKEYB_MASK 0x000000FF
+#define MCDE_FCOLKEYB_FKEYB(__x) \
+ MCDE_VAL2REG(MCDE_FCOLKEYB, FKEYB, __x)
+#define MCDE_FCOLKEYB_FKEYG_SHIFT 8
+#define MCDE_FCOLKEYB_FKEYG_MASK 0x0000FF00
+#define MCDE_FCOLKEYB_FKEYG(__x) \
+ MCDE_VAL2REG(MCDE_FCOLKEYB, FKEYG, __x)
+#define MCDE_FCOLKEYB_FKEYR_SHIFT 16
+#define MCDE_FCOLKEYB_FKEYR_MASK 0x00FF0000
+#define MCDE_FCOLKEYB_FKEYR(__x) \
+ MCDE_VAL2REG(MCDE_FCOLKEYB, FKEYR, __x)
+#define MCDE_FCOLKEYB_FKEYA_SHIFT 24
+#define MCDE_FCOLKEYB_FKEYA_MASK 0xFF000000
+#define MCDE_FCOLKEYB_FKEYA(__x) \
+ MCDE_VAL2REG(MCDE_FCOLKEYB, FKEYA, __x)
+#define MCDE_RGBCONV1A 0x00000810
+#define MCDE_RGBCONV1A_GROUPOFFSET 0x200
+#define MCDE_RGBCONV1A_YR_GREEN_SHIFT 0
+#define MCDE_RGBCONV1A_YR_GREEN_MASK 0x000007FF
+#define MCDE_RGBCONV1A_YR_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV1A, YR_GREEN, __x)
+#define MCDE_RGBCONV1A_YR_RED_SHIFT 16
+#define MCDE_RGBCONV1A_YR_RED_MASK 0x07FF0000
+#define MCDE_RGBCONV1A_YR_RED(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV1A, YR_RED, __x)
+#define MCDE_RGBCONV1B 0x00000A10
+#define MCDE_RGBCONV1B_YR_GREEN_SHIFT 0
+#define MCDE_RGBCONV1B_YR_GREEN_MASK 0x000007FF
+#define MCDE_RGBCONV1B_YR_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV1B, YR_GREEN, __x)
+#define MCDE_RGBCONV1B_YR_RED_SHIFT 16
+#define MCDE_RGBCONV1B_YR_RED_MASK 0x07FF0000
+#define MCDE_RGBCONV1B_YR_RED(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV1B, YR_RED, __x)
+#define MCDE_RGBCONV2A 0x00000814
+#define MCDE_RGBCONV2A_GROUPOFFSET 0x200
+#define MCDE_RGBCONV2A_CR_RED_SHIFT 0
+#define MCDE_RGBCONV2A_CR_RED_MASK 0x000007FF
+#define MCDE_RGBCONV2A_CR_RED(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV2A, CR_RED, __x)
+#define MCDE_RGBCONV2A_YR_BLUE_SHIFT 16
+#define MCDE_RGBCONV2A_YR_BLUE_MASK 0x07FF0000
+#define MCDE_RGBCONV2A_YR_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV2A, YR_BLUE, __x)
+#define MCDE_RGBCONV2B 0x00000A14
+#define MCDE_RGBCONV2B_CR_RED_SHIFT 0
+#define MCDE_RGBCONV2B_CR_RED_MASK 0x000007FF
+#define MCDE_RGBCONV2B_CR_RED(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV2B, CR_RED, __x)
+#define MCDE_RGBCONV2B_YR_BLUE_SHIFT 16
+#define MCDE_RGBCONV2B_YR_BLUE_MASK 0x07FF0000
+#define MCDE_RGBCONV2B_YR_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV2B, YR_BLUE, __x)
+#define MCDE_RGBCONV3A 0x00000818
+#define MCDE_RGBCONV3A_GROUPOFFSET 0x200
+#define MCDE_RGBCONV3A_CR_BLUE_SHIFT 0
+#define MCDE_RGBCONV3A_CR_BLUE_MASK 0x000007FF
+#define MCDE_RGBCONV3A_CR_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV3A, CR_BLUE, __x)
+#define MCDE_RGBCONV3A_CR_GREEN_SHIFT 16
+#define MCDE_RGBCONV3A_CR_GREEN_MASK 0x07FF0000
+#define MCDE_RGBCONV3A_CR_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV3A, CR_GREEN, __x)
+#define MCDE_RGBCONV3B 0x00000A18
+#define MCDE_RGBCONV3B_CR_BLUE_SHIFT 0
+#define MCDE_RGBCONV3B_CR_BLUE_MASK 0x000007FF
+#define MCDE_RGBCONV3B_CR_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV3B, CR_BLUE, __x)
+#define MCDE_RGBCONV3B_CR_GREEN_SHIFT 16
+#define MCDE_RGBCONV3B_CR_GREEN_MASK 0x07FF0000
+#define MCDE_RGBCONV3B_CR_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV3B, CR_GREEN, __x)
+#define MCDE_RGBCONV4A 0x0000081C
+#define MCDE_RGBCONV4A_GROUPOFFSET 0x200
+#define MCDE_RGBCONV4A_CB_GREEN_SHIFT 0
+#define MCDE_RGBCONV4A_CB_GREEN_MASK 0x000007FF
+#define MCDE_RGBCONV4A_CB_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV4A, CB_GREEN, __x)
+#define MCDE_RGBCONV4A_CB_RED_SHIFT 16
+#define MCDE_RGBCONV4A_CB_RED_MASK 0x07FF0000
+#define MCDE_RGBCONV4A_CB_RED(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV4A, CB_RED, __x)
+#define MCDE_RGBCONV4B 0x00000A1C
+#define MCDE_RGBCONV4B_CB_GREEN_SHIFT 0
+#define MCDE_RGBCONV4B_CB_GREEN_MASK 0x000007FF
+#define MCDE_RGBCONV4B_CB_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV4B, CB_GREEN, __x)
+#define MCDE_RGBCONV4B_CB_RED_SHIFT 16
+#define MCDE_RGBCONV4B_CB_RED_MASK 0x07FF0000
+#define MCDE_RGBCONV4B_CB_RED(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV4B, CB_RED, __x)
+#define MCDE_RGBCONV5A 0x00000820
+#define MCDE_RGBCONV5A_GROUPOFFSET 0x200
+#define MCDE_RGBCONV5A_OFF_RED_SHIFT 0
+#define MCDE_RGBCONV5A_OFF_RED_MASK 0x000007FF
+#define MCDE_RGBCONV5A_OFF_RED(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV5A, OFF_RED, __x)
+#define MCDE_RGBCONV5A_CB_BLUE_SHIFT 16
+#define MCDE_RGBCONV5A_CB_BLUE_MASK 0x07FF0000
+#define MCDE_RGBCONV5A_CB_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV5A, CB_BLUE, __x)
+#define MCDE_RGBCONV5B 0x00000A20
+#define MCDE_RGBCONV5B_OFF_RED_SHIFT 0
+#define MCDE_RGBCONV5B_OFF_RED_MASK 0x000007FF
+#define MCDE_RGBCONV5B_OFF_RED(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV5B, OFF_RED, __x)
+#define MCDE_RGBCONV5B_CB_BLUE_SHIFT 16
+#define MCDE_RGBCONV5B_CB_BLUE_MASK 0x07FF0000
+#define MCDE_RGBCONV5B_CB_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV5B, CB_BLUE, __x)
+#define MCDE_RGBCONV6A 0x00000824
+#define MCDE_RGBCONV6A_GROUPOFFSET 0x200
+#define MCDE_RGBCONV6A_OFF_BLUE_SHIFT 0
+#define MCDE_RGBCONV6A_OFF_BLUE_MASK 0x000007FF
+#define MCDE_RGBCONV6A_OFF_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV6A, OFF_BLUE, __x)
+#define MCDE_RGBCONV6A_OFF_GREEN_SHIFT 16
+#define MCDE_RGBCONV6A_OFF_GREEN_MASK 0x07FF0000
+#define MCDE_RGBCONV6A_OFF_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV6A, OFF_GREEN, __x)
+#define MCDE_RGBCONV6B 0x00000A24
+#define MCDE_RGBCONV6B_OFF_BLUE_SHIFT 0
+#define MCDE_RGBCONV6B_OFF_BLUE_MASK 0x000007FF
+#define MCDE_RGBCONV6B_OFF_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV6B, OFF_BLUE, __x)
+#define MCDE_RGBCONV6B_OFF_GREEN_SHIFT 16
+#define MCDE_RGBCONV6B_OFF_GREEN_MASK 0x07FF0000
+#define MCDE_RGBCONV6B_OFF_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_RGBCONV6B, OFF_GREEN, __x)
+#define MCDE_FFCOEF0 0x00000828
+#define MCDE_FFCOEF0_COEFF0_N1_SHIFT 0
+#define MCDE_FFCOEF0_COEFF0_N1_MASK 0x000000FF
+#define MCDE_FFCOEF0_COEFF0_N1(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF0, COEFF0_N1, __x)
+#define MCDE_FFCOEF0_COEFF0_N2_SHIFT 8
+#define MCDE_FFCOEF0_COEFF0_N2_MASK 0x0000FF00
+#define MCDE_FFCOEF0_COEFF0_N2(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF0, COEFF0_N2, __x)
+#define MCDE_FFCOEF0_COEFF0_N3_SHIFT 16
+#define MCDE_FFCOEF0_COEFF0_N3_MASK 0x00FF0000
+#define MCDE_FFCOEF0_COEFF0_N3(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF0, COEFF0_N3, __x)
+#define MCDE_FFCOEF0_T0_SHIFT 24
+#define MCDE_FFCOEF0_T0_MASK 0x0F000000
+#define MCDE_FFCOEF0_T0(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF0, T0, __x)
+#define MCDE_FFCOEF1 0x0000082C
+#define MCDE_FFCOEF1_COEFF1_N1_SHIFT 0
+#define MCDE_FFCOEF1_COEFF1_N1_MASK 0x000000FF
+#define MCDE_FFCOEF1_COEFF1_N1(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF1, COEFF1_N1, __x)
+#define MCDE_FFCOEF1_COEFF1_N2_SHIFT 8
+#define MCDE_FFCOEF1_COEFF1_N2_MASK 0x0000FF00
+#define MCDE_FFCOEF1_COEFF1_N2(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF1, COEFF1_N2, __x)
+#define MCDE_FFCOEF1_COEFF1_N3_SHIFT 16
+#define MCDE_FFCOEF1_COEFF1_N3_MASK 0x00FF0000
+#define MCDE_FFCOEF1_COEFF1_N3(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF1, COEFF1_N3, __x)
+#define MCDE_FFCOEF1_T1_SHIFT 24
+#define MCDE_FFCOEF1_T1_MASK 0x0F000000
+#define MCDE_FFCOEF1_T1(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF1, T1, __x)
+#define MCDE_FFCOEF2 0x00000830
+#define MCDE_FFCOEF2_COEFF2_N1_SHIFT 0
+#define MCDE_FFCOEF2_COEFF2_N1_MASK 0x000000FF
+#define MCDE_FFCOEF2_COEFF2_N1(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF2, COEFF2_N1, __x)
+#define MCDE_FFCOEF2_COEFF2_N2_SHIFT 8
+#define MCDE_FFCOEF2_COEFF2_N2_MASK 0x0000FF00
+#define MCDE_FFCOEF2_COEFF2_N2(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF2, COEFF2_N2, __x)
+#define MCDE_FFCOEF2_COEFF2_N3_SHIFT 16
+#define MCDE_FFCOEF2_COEFF2_N3_MASK 0x00FF0000
+#define MCDE_FFCOEF2_COEFF2_N3(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF2, COEFF2_N3, __x)
+#define MCDE_FFCOEF2_T2_SHIFT 24
+#define MCDE_FFCOEF2_T2_MASK 0x0F000000
+#define MCDE_FFCOEF2_T2(__x) \
+ MCDE_VAL2REG(MCDE_FFCOEF2, T2, __x)
+#define MCDE_MCDE_WDATAA_V2 0x00000834
+#define MCDE_MCDE_WDATAA_V2_GROUPOFFSET 0x200
+#define MCDE_MCDE_WDATAA_V2_DC_SHIFT 24
+#define MCDE_MCDE_WDATAA_V2_DC_MASK 0x01000000
+#define MCDE_MCDE_WDATAA_V2_DC(__x) \
+ MCDE_VAL2REG(MCDE_MCDE_WDATAA_V2, DC, __x)
+#define MCDE_MCDE_WDATAA_V2_DATAVALUE_SHIFT 0
+#define MCDE_MCDE_WDATAA_V2_DATAVALUE_MASK 0x00FFFFFF
+#define MCDE_MCDE_WDATAA_V2_DATAVALUE(__x) \
+ MCDE_VAL2REG(MCDE_MCDE_WDATAA_V2, DATAVALUE, __x)
+#define MCDE_MCDE_WDATAB_V2 0x00000A34
+#define MCDE_MCDE_WDATAB_V2_DC_SHIFT 24
+#define MCDE_MCDE_WDATAB_V2_DC_MASK 0x01000000
+#define MCDE_MCDE_WDATAB_V2_DC(__x) \
+ MCDE_VAL2REG(MCDE_MCDE_WDATAB_V2, DC, __x)
+#define MCDE_MCDE_WDATAB_V2_DATAVALUE_SHIFT 0
+#define MCDE_MCDE_WDATAB_V2_DATAVALUE_MASK 0x00FFFFFF
+#define MCDE_MCDE_WDATAB_V2_DATAVALUE(__x) \
+ MCDE_VAL2REG(MCDE_MCDE_WDATAB_V2, DATAVALUE, __x)
+#define MCDE_TVCRA 0x00000838
+#define MCDE_TVCRA_GROUPOFFSET 0x200
+#define MCDE_TVCRA_SEL_MOD_SHIFT 0
+#define MCDE_TVCRA_SEL_MOD_MASK 0x00000001
+#define MCDE_TVCRA_SEL_MOD_LCD 0
+#define MCDE_TVCRA_SEL_MOD_TV 1
+#define MCDE_TVCRA_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_TVCRA, SEL_MOD, MCDE_TVCRA_SEL_MOD_##__x)
+#define MCDE_TVCRA_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_TVCRA, SEL_MOD, __x)
+#define MCDE_TVCRA_INTEREN_SHIFT 1
+#define MCDE_TVCRA_INTEREN_MASK 0x00000002
+#define MCDE_TVCRA_INTEREN(__x) \
+ MCDE_VAL2REG(MCDE_TVCRA, INTEREN, __x)
+#define MCDE_TVCRA_IFIELD_SHIFT 2
+#define MCDE_TVCRA_IFIELD_MASK 0x00000004
+#define MCDE_TVCRA_IFIELD(__x) \
+ MCDE_VAL2REG(MCDE_TVCRA, IFIELD, __x)
+#define MCDE_TVCRA_TVMODE_SHIFT 3
+#define MCDE_TVCRA_TVMODE_MASK 0x00000038
+#define MCDE_TVCRA_TVMODE_SDTV_656P 0
+#define MCDE_TVCRA_TVMODE_HDTV_480P 1
+#define MCDE_TVCRA_TVMODE_HDTV_720P 2
+#define MCDE_TVCRA_TVMODE_SDTV_656P_LE 3
+#define MCDE_TVCRA_TVMODE_SDTV_656P_BE 4
+#define MCDE_TVCRA_TVMODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_TVCRA, TVMODE, MCDE_TVCRA_TVMODE_##__x)
+#define MCDE_TVCRA_TVMODE(__x) \
+ MCDE_VAL2REG(MCDE_TVCRA, TVMODE, __x)
+#define MCDE_TVCRA_SDTVMODE_SHIFT 6
+#define MCDE_TVCRA_SDTVMODE_MASK 0x000000C0
+#define MCDE_TVCRA_SDTVMODE_Y0CBY1CR 0
+#define MCDE_TVCRA_SDTVMODE_CBY0CRY1 1
+#define MCDE_TVCRA_SDTVMODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_TVCRA, SDTVMODE, MCDE_TVCRA_SDTVMODE_##__x)
+#define MCDE_TVCRA_SDTVMODE(__x) \
+ MCDE_VAL2REG(MCDE_TVCRA, SDTVMODE, __x)
+#define MCDE_TVCRA_AVRGEN_SHIFT 8
+#define MCDE_TVCRA_AVRGEN_MASK 0x00000100
+#define MCDE_TVCRA_AVRGEN(__x) \
+ MCDE_VAL2REG(MCDE_TVCRA, AVRGEN, __x)
+#define MCDE_TVCRA_CKINV_SHIFT 9
+#define MCDE_TVCRA_CKINV_MASK 0x00000200
+#define MCDE_TVCRA_CKINV(__x) \
+ MCDE_VAL2REG(MCDE_TVCRA, CKINV, __x)
+#define MCDE_TVCRB 0x00000A38
+#define MCDE_TVCRB_SEL_MOD_SHIFT 0
+#define MCDE_TVCRB_SEL_MOD_MASK 0x00000001
+#define MCDE_TVCRB_SEL_MOD_LCD 0
+#define MCDE_TVCRB_SEL_MOD_TV 1
+#define MCDE_TVCRB_SEL_MOD_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_TVCRB, SEL_MOD, MCDE_TVCRB_SEL_MOD_##__x)
+#define MCDE_TVCRB_SEL_MOD(__x) \
+ MCDE_VAL2REG(MCDE_TVCRB, SEL_MOD, __x)
+#define MCDE_TVCRB_INTEREN_SHIFT 1
+#define MCDE_TVCRB_INTEREN_MASK 0x00000002
+#define MCDE_TVCRB_INTEREN(__x) \
+ MCDE_VAL2REG(MCDE_TVCRB, INTEREN, __x)
+#define MCDE_TVCRB_IFIELD_SHIFT 2
+#define MCDE_TVCRB_IFIELD_MASK 0x00000004
+#define MCDE_TVCRB_IFIELD(__x) \
+ MCDE_VAL2REG(MCDE_TVCRB, IFIELD, __x)
+#define MCDE_TVCRB_TVMODE_SHIFT 3
+#define MCDE_TVCRB_TVMODE_MASK 0x00000038
+#define MCDE_TVCRB_TVMODE_SDTV_656P 0
+#define MCDE_TVCRB_TVMODE_HDTV_480P 1
+#define MCDE_TVCRB_TVMODE_HDTV_720P 2
+#define MCDE_TVCRB_TVMODE_SDTV_656P_LE 3
+#define MCDE_TVCRB_TVMODE_SDTV_656P_BE 4
+#define MCDE_TVCRB_TVMODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_TVCRB, TVMODE, MCDE_TVCRB_TVMODE_##__x)
+#define MCDE_TVCRB_TVMODE(__x) \
+ MCDE_VAL2REG(MCDE_TVCRB, TVMODE, __x)
+#define MCDE_TVCRB_SDTVMODE_SHIFT 6
+#define MCDE_TVCRB_SDTVMODE_MASK 0x000000C0
+#define MCDE_TVCRB_SDTVMODE_Y0CBY1CR 0
+#define MCDE_TVCRB_SDTVMODE_CBY0CRY1 1
+#define MCDE_TVCRB_SDTVMODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_TVCRB, SDTVMODE, MCDE_TVCRB_SDTVMODE_##__x)
+#define MCDE_TVCRB_SDTVMODE(__x) \
+ MCDE_VAL2REG(MCDE_TVCRB, SDTVMODE, __x)
+#define MCDE_TVCRB_AVRGEN_SHIFT 8
+#define MCDE_TVCRB_AVRGEN_MASK 0x00000100
+#define MCDE_TVCRB_AVRGEN(__x) \
+ MCDE_VAL2REG(MCDE_TVCRB, AVRGEN, __x)
+#define MCDE_TVCRB_CKINV_SHIFT 9
+#define MCDE_TVCRB_CKINV_MASK 0x00000200
+#define MCDE_TVCRB_CKINV(__x) \
+ MCDE_VAL2REG(MCDE_TVCRB, CKINV, __x)
+#define MCDE_TVBL1A 0x0000083C
+#define MCDE_TVBL1A_GROUPOFFSET 0x200
+#define MCDE_TVBL1A_BEL1_SHIFT 0
+#define MCDE_TVBL1A_BEL1_MASK 0x000007FF
+#define MCDE_TVBL1A_BEL1(__x) \
+ MCDE_VAL2REG(MCDE_TVBL1A, BEL1, __x)
+#define MCDE_TVBL1A_BSL1_SHIFT 16
+#define MCDE_TVBL1A_BSL1_MASK 0x07FF0000
+#define MCDE_TVBL1A_BSL1(__x) \
+ MCDE_VAL2REG(MCDE_TVBL1A, BSL1, __x)
+#define MCDE_TVBL1B 0x00000A3C
+#define MCDE_TVBL1B_BEL1_SHIFT 0
+#define MCDE_TVBL1B_BEL1_MASK 0x000007FF
+#define MCDE_TVBL1B_BEL1(__x) \
+ MCDE_VAL2REG(MCDE_TVBL1B, BEL1, __x)
+#define MCDE_TVBL1B_BSL1_SHIFT 16
+#define MCDE_TVBL1B_BSL1_MASK 0x07FF0000
+#define MCDE_TVBL1B_BSL1(__x) \
+ MCDE_VAL2REG(MCDE_TVBL1B, BSL1, __x)
+#define MCDE_TVISLA 0x00000840
+#define MCDE_TVISLA_GROUPOFFSET 0x200
+#define MCDE_TVISLA_FSL1_SHIFT 0
+#define MCDE_TVISLA_FSL1_MASK 0x000007FF
+#define MCDE_TVISLA_FSL1(__x) \
+ MCDE_VAL2REG(MCDE_TVISLA, FSL1, __x)
+#define MCDE_TVISLA_FSL2_SHIFT 16
+#define MCDE_TVISLA_FSL2_MASK 0x07FF0000
+#define MCDE_TVISLA_FSL2(__x) \
+ MCDE_VAL2REG(MCDE_TVISLA, FSL2, __x)
+#define MCDE_TVISLB 0x00000A40
+#define MCDE_TVISLB_FSL1_SHIFT 0
+#define MCDE_TVISLB_FSL1_MASK 0x000007FF
+#define MCDE_TVISLB_FSL1(__x) \
+ MCDE_VAL2REG(MCDE_TVISLB, FSL1, __x)
+#define MCDE_TVISLB_FSL2_SHIFT 16
+#define MCDE_TVISLB_FSL2_MASK 0x07FF0000
+#define MCDE_TVISLB_FSL2(__x) \
+ MCDE_VAL2REG(MCDE_TVISLB, FSL2, __x)
+#define MCDE_TVDVOA 0x00000844
+#define MCDE_TVDVOA_GROUPOFFSET 0x200
+#define MCDE_TVDVOA_DVO1_SHIFT 0
+#define MCDE_TVDVOA_DVO1_MASK 0x000007FF
+#define MCDE_TVDVOA_DVO1(__x) \
+ MCDE_VAL2REG(MCDE_TVDVOA, DVO1, __x)
+#define MCDE_TVDVOA_DVO2_SHIFT 16
+#define MCDE_TVDVOA_DVO2_MASK 0x07FF0000
+#define MCDE_TVDVOA_DVO2(__x) \
+ MCDE_VAL2REG(MCDE_TVDVOA, DVO2, __x)
+#define MCDE_TVDVOB 0x00000A44
+#define MCDE_TVDVOB_DVO1_SHIFT 0
+#define MCDE_TVDVOB_DVO1_MASK 0x000007FF
+#define MCDE_TVDVOB_DVO1(__x) \
+ MCDE_VAL2REG(MCDE_TVDVOB, DVO1, __x)
+#define MCDE_TVDVOB_DVO2_SHIFT 16
+#define MCDE_TVDVOB_DVO2_MASK 0x07FF0000
+#define MCDE_TVDVOB_DVO2(__x) \
+ MCDE_VAL2REG(MCDE_TVDVOB, DVO2, __x)
+#define MCDE_TVTIM1A 0x0000084C
+#define MCDE_TVTIM1A_GROUPOFFSET 0x200
+#define MCDE_TVTIM1A_DHO_SHIFT 0
+#define MCDE_TVTIM1A_DHO_MASK 0x000007FF
+#define MCDE_TVTIM1A_DHO(__x) \
+ MCDE_VAL2REG(MCDE_TVTIM1A, DHO, __x)
+#define MCDE_TVTIM1B 0x00000A4C
+#define MCDE_TVTIM1B_DHO_SHIFT 0
+#define MCDE_TVTIM1B_DHO_MASK 0x000007FF
+#define MCDE_TVTIM1B_DHO(__x) \
+ MCDE_VAL2REG(MCDE_TVTIM1B, DHO, __x)
+#define MCDE_TVLBALWA 0x00000850
+#define MCDE_TVLBALWA_GROUPOFFSET 0x200
+#define MCDE_TVLBALWA_LBW_SHIFT 0
+#define MCDE_TVLBALWA_LBW_MASK 0x000007FF
+#define MCDE_TVLBALWA_LBW(__x) \
+ MCDE_VAL2REG(MCDE_TVLBALWA, LBW, __x)
+#define MCDE_TVLBALWA_ALW_SHIFT 16
+#define MCDE_TVLBALWA_ALW_MASK 0x07FF0000
+#define MCDE_TVLBALWA_ALW(__x) \
+ MCDE_VAL2REG(MCDE_TVLBALWA, ALW, __x)
+#define MCDE_TVLBALWB 0x00000A50
+#define MCDE_TVLBALWB_LBW_SHIFT 0
+#define MCDE_TVLBALWB_LBW_MASK 0x000007FF
+#define MCDE_TVLBALWB_LBW(__x) \
+ MCDE_VAL2REG(MCDE_TVLBALWB, LBW, __x)
+#define MCDE_TVLBALWB_ALW_SHIFT 16
+#define MCDE_TVLBALWB_ALW_MASK 0x07FF0000
+#define MCDE_TVLBALWB_ALW(__x) \
+ MCDE_VAL2REG(MCDE_TVLBALWB, ALW, __x)
+#define MCDE_TVBL2A 0x00000854
+#define MCDE_TVBL2A_GROUPOFFSET 0x200
+#define MCDE_TVBL2A_BEL2_SHIFT 0
+#define MCDE_TVBL2A_BEL2_MASK 0x000007FF
+#define MCDE_TVBL2A_BEL2(__x) \
+ MCDE_VAL2REG(MCDE_TVBL2A, BEL2, __x)
+#define MCDE_TVBL2A_BSL2_SHIFT 16
+#define MCDE_TVBL2A_BSL2_MASK 0x07FF0000
+#define MCDE_TVBL2A_BSL2(__x) \
+ MCDE_VAL2REG(MCDE_TVBL2A, BSL2, __x)
+#define MCDE_TVBL2B 0x00000A54
+#define MCDE_TVBL2B_BEL2_SHIFT 0
+#define MCDE_TVBL2B_BEL2_MASK 0x000007FF
+#define MCDE_TVBL2B_BEL2(__x) \
+ MCDE_VAL2REG(MCDE_TVBL2B, BEL2, __x)
+#define MCDE_TVBL2B_BSL2_SHIFT 16
+#define MCDE_TVBL2B_BSL2_MASK 0x07FF0000
+#define MCDE_TVBL2B_BSL2(__x) \
+ MCDE_VAL2REG(MCDE_TVBL2B, BSL2, __x)
+#define MCDE_TVBLUA 0x00000858
+#define MCDE_TVBLUA_GROUPOFFSET 0x200
+#define MCDE_TVBLUA_TVBLU_SHIFT 0
+#define MCDE_TVBLUA_TVBLU_MASK 0x000000FF
+#define MCDE_TVBLUA_TVBLU(__x) \
+ MCDE_VAL2REG(MCDE_TVBLUA, TVBLU, __x)
+#define MCDE_TVBLUA_TVBCB_SHIFT 8
+#define MCDE_TVBLUA_TVBCB_MASK 0x0000FF00
+#define MCDE_TVBLUA_TVBCB(__x) \
+ MCDE_VAL2REG(MCDE_TVBLUA, TVBCB, __x)
+#define MCDE_TVBLUA_TVBCR_SHIFT 16
+#define MCDE_TVBLUA_TVBCR_MASK 0x00FF0000
+#define MCDE_TVBLUA_TVBCR(__x) \
+ MCDE_VAL2REG(MCDE_TVBLUA, TVBCR, __x)
+#define MCDE_TVBLUB 0x00000A58
+#define MCDE_TVBLUB_TVBLU_SHIFT 0
+#define MCDE_TVBLUB_TVBLU_MASK 0x000000FF
+#define MCDE_TVBLUB_TVBLU(__x) \
+ MCDE_VAL2REG(MCDE_TVBLUB, TVBLU, __x)
+#define MCDE_TVBLUB_TVBCB_SHIFT 8
+#define MCDE_TVBLUB_TVBCB_MASK 0x0000FF00
+#define MCDE_TVBLUB_TVBCB(__x) \
+ MCDE_VAL2REG(MCDE_TVBLUB, TVBCB, __x)
+#define MCDE_TVBLUB_TVBCR_SHIFT 16
+#define MCDE_TVBLUB_TVBCR_MASK 0x00FF0000
+#define MCDE_TVBLUB_TVBCR(__x) \
+ MCDE_VAL2REG(MCDE_TVBLUB, TVBCR, __x)
+#define MCDE_LCDTIM1A 0x00000860
+#define MCDE_LCDTIM1A_GROUPOFFSET 0x200
+#define MCDE_LCDTIM1A_IVP_SHIFT 19
+#define MCDE_LCDTIM1A_IVP_MASK 0x00080000
+#define MCDE_LCDTIM1A_IVP(__x) \
+ MCDE_VAL2REG(MCDE_LCDTIM1A, IVP, __x)
+#define MCDE_LCDTIM1A_IVS_SHIFT 20
+#define MCDE_LCDTIM1A_IVS_MASK 0x00100000
+#define MCDE_LCDTIM1A_IVS(__x) \
+ MCDE_VAL2REG(MCDE_LCDTIM1A, IVS, __x)
+#define MCDE_LCDTIM1A_IHS_SHIFT 21
+#define MCDE_LCDTIM1A_IHS_MASK 0x00200000
+#define MCDE_LCDTIM1A_IHS(__x) \
+ MCDE_VAL2REG(MCDE_LCDTIM1A, IHS, __x)
+#define MCDE_LCDTIM1A_IPC_SHIFT 22
+#define MCDE_LCDTIM1A_IPC_MASK 0x00400000
+#define MCDE_LCDTIM1A_IPC(__x) \
+ MCDE_VAL2REG(MCDE_LCDTIM1A, IPC, __x)
+#define MCDE_LCDTIM1A_IOE_SHIFT 23
+#define MCDE_LCDTIM1A_IOE_MASK 0x00800000
+#define MCDE_LCDTIM1A_IOE(__x) \
+ MCDE_VAL2REG(MCDE_LCDTIM1A, IOE, __x)
+#define MCDE_LCDTIM1B 0x00000A60
+#define MCDE_LCDTIM1B_IVP_SHIFT 19
+#define MCDE_LCDTIM1B_IVP_MASK 0x00080000
+#define MCDE_LCDTIM1B_IVP(__x) \
+ MCDE_VAL2REG(MCDE_LCDTIM1B, IVP, __x)
+#define MCDE_LCDTIM1B_IVS_SHIFT 20
+#define MCDE_LCDTIM1B_IVS_MASK 0x00100000
+#define MCDE_LCDTIM1B_IVS(__x) \
+ MCDE_VAL2REG(MCDE_LCDTIM1B, IVS, __x)
+#define MCDE_LCDTIM1B_IHS_SHIFT 21
+#define MCDE_LCDTIM1B_IHS_MASK 0x00200000
+#define MCDE_LCDTIM1B_IHS(__x) \
+ MCDE_VAL2REG(MCDE_LCDTIM1B, IHS, __x)
+#define MCDE_LCDTIM1B_IPC_SHIFT 22
+#define MCDE_LCDTIM1B_IPC_MASK 0x00400000
+#define MCDE_LCDTIM1B_IPC(__x) \
+ MCDE_VAL2REG(MCDE_LCDTIM1B, IPC, __x)
+#define MCDE_LCDTIM1B_IOE_SHIFT 23
+#define MCDE_LCDTIM1B_IOE_MASK 0x00800000
+#define MCDE_LCDTIM1B_IOE(__x) \
+ MCDE_VAL2REG(MCDE_LCDTIM1B, IOE, __x)
+#define MCDE_DITCTRLA 0x00000864
+#define MCDE_DITCTRLA_GROUPOFFSET 0x200
+#define MCDE_DITCTRLA_TEMP_SHIFT 0
+#define MCDE_DITCTRLA_TEMP_MASK 0x00000001
+#define MCDE_DITCTRLA_TEMP(__x) \
+ MCDE_VAL2REG(MCDE_DITCTRLA, TEMP, __x)
+#define MCDE_DITCTRLA_COMP_SHIFT 1
+#define MCDE_DITCTRLA_COMP_MASK 0x00000002
+#define MCDE_DITCTRLA_COMP(__x) \
+ MCDE_VAL2REG(MCDE_DITCTRLA, COMP, __x)
+#define MCDE_DITCTRLA_MASK_SHIFT 4
+#define MCDE_DITCTRLA_MASK_MASK 0x00000010
+#define MCDE_DITCTRLA_MASK(__x) \
+ MCDE_VAL2REG(MCDE_DITCTRLA, MASK, __x)
+#define MCDE_DITCTRLA_FOFFX_SHIFT 5
+#define MCDE_DITCTRLA_FOFFX_MASK 0x000003E0
+#define MCDE_DITCTRLA_FOFFX(__x) \
+ MCDE_VAL2REG(MCDE_DITCTRLA, FOFFX, __x)
+#define MCDE_DITCTRLA_FOFFY_SHIFT 10
+#define MCDE_DITCTRLA_FOFFY_MASK 0x00007C00
+#define MCDE_DITCTRLA_FOFFY(__x) \
+ MCDE_VAL2REG(MCDE_DITCTRLA, FOFFY, __x)
+#define MCDE_DITCTRLB 0x00000A64
+#define MCDE_DITCTRLB_TEMP_SHIFT 0
+#define MCDE_DITCTRLB_TEMP_MASK 0x00000001
+#define MCDE_DITCTRLB_TEMP(__x) \
+ MCDE_VAL2REG(MCDE_DITCTRLB, TEMP, __x)
+#define MCDE_DITCTRLB_COMP_SHIFT 1
+#define MCDE_DITCTRLB_COMP_MASK 0x00000002
+#define MCDE_DITCTRLB_COMP(__x) \
+ MCDE_VAL2REG(MCDE_DITCTRLB, COMP, __x)
+#define MCDE_DITCTRLB_MASK_SHIFT 4
+#define MCDE_DITCTRLB_MASK_MASK 0x00000010
+#define MCDE_DITCTRLB_MASK(__x) \
+ MCDE_VAL2REG(MCDE_DITCTRLB, MASK, __x)
+#define MCDE_DITCTRLB_FOFFX_SHIFT 5
+#define MCDE_DITCTRLB_FOFFX_MASK 0x000003E0
+#define MCDE_DITCTRLB_FOFFX(__x) \
+ MCDE_VAL2REG(MCDE_DITCTRLB, FOFFX, __x)
+#define MCDE_DITCTRLB_FOFFY_SHIFT 10
+#define MCDE_DITCTRLB_FOFFY_MASK 0x00007C00
+#define MCDE_DITCTRLB_FOFFY(__x) \
+ MCDE_VAL2REG(MCDE_DITCTRLB, FOFFY, __x)
+#define MCDE_DITOFFA 0x00000868
+#define MCDE_DITOFFA_GROUPOFFSET 0x200
+#define MCDE_DITOFFA_XG_SHIFT 0
+#define MCDE_DITOFFA_XG_MASK 0x0000001F
+#define MCDE_DITOFFA_XG(__x) \
+ MCDE_VAL2REG(MCDE_DITOFFA, XG, __x)
+#define MCDE_DITOFFA_YG_SHIFT 8
+#define MCDE_DITOFFA_YG_MASK 0x00001F00
+#define MCDE_DITOFFA_YG(__x) \
+ MCDE_VAL2REG(MCDE_DITOFFA, YG, __x)
+#define MCDE_DITOFFA_XB_SHIFT 16
+#define MCDE_DITOFFA_XB_MASK 0x001F0000
+#define MCDE_DITOFFA_XB(__x) \
+ MCDE_VAL2REG(MCDE_DITOFFA, XB, __x)
+#define MCDE_DITOFFA_YB_SHIFT 24
+#define MCDE_DITOFFA_YB_MASK 0x1F000000
+#define MCDE_DITOFFA_YB(__x) \
+ MCDE_VAL2REG(MCDE_DITOFFA, YB, __x)
+#define MCDE_DITOFFB 0x00000A68
+#define MCDE_DITOFFB_XG_SHIFT 0
+#define MCDE_DITOFFB_XG_MASK 0x0000001F
+#define MCDE_DITOFFB_XG(__x) \
+ MCDE_VAL2REG(MCDE_DITOFFB, XG, __x)
+#define MCDE_DITOFFB_YG_SHIFT 8
+#define MCDE_DITOFFB_YG_MASK 0x00001F00
+#define MCDE_DITOFFB_YG(__x) \
+ MCDE_VAL2REG(MCDE_DITOFFB, YG, __x)
+#define MCDE_DITOFFB_XB_SHIFT 16
+#define MCDE_DITOFFB_XB_MASK 0x001F0000
+#define MCDE_DITOFFB_XB(__x) \
+ MCDE_VAL2REG(MCDE_DITOFFB, XB, __x)
+#define MCDE_DITOFFB_YB_SHIFT 24
+#define MCDE_DITOFFB_YB_MASK 0x1F000000
+#define MCDE_DITOFFB_YB(__x) \
+ MCDE_VAL2REG(MCDE_DITOFFB, YB, __x)
+#define MCDE_PAL0A 0x0000086C
+#define MCDE_PAL0A_GROUPOFFSET 0x200
+#define MCDE_PAL0A_BLUE_SHIFT 0
+#define MCDE_PAL0A_BLUE_MASK 0x00000FFF
+#define MCDE_PAL0A_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_PAL0A, BLUE, __x)
+#define MCDE_PAL0A_GREEN_SHIFT 16
+#define MCDE_PAL0A_GREEN_MASK 0x0FFF0000
+#define MCDE_PAL0A_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_PAL0A, GREEN, __x)
+#define MCDE_PAL0B 0x00000A6C
+#define MCDE_PAL0B_BLUE_SHIFT 0
+#define MCDE_PAL0B_BLUE_MASK 0x00000FFF
+#define MCDE_PAL0B_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_PAL0B, BLUE, __x)
+#define MCDE_PAL0B_GREEN_SHIFT 16
+#define MCDE_PAL0B_GREEN_MASK 0x0FFF0000
+#define MCDE_PAL0B_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_PAL0B, GREEN, __x)
+#define MCDE_PAL1A 0x00000870
+#define MCDE_PAL1A_GROUPOFFSET 0x200
+#define MCDE_PAL1A_RED_SHIFT 0
+#define MCDE_PAL1A_RED_MASK 0x00000FFF
+#define MCDE_PAL1A_RED(__x) \
+ MCDE_VAL2REG(MCDE_PAL1A, RED, __x)
+#define MCDE_PAL1B 0x00000A70
+#define MCDE_PAL1B_RED_SHIFT 0
+#define MCDE_PAL1B_RED_MASK 0x00000FFF
+#define MCDE_PAL1B_RED(__x) \
+ MCDE_VAL2REG(MCDE_PAL1B, RED, __x)
+#define MCDE_ROTADD0A 0x00000874
+#define MCDE_ROTADD0A_GROUPOFFSET 0x200
+#define MCDE_ROTADD0A_ROTADD0_SHIFT 3
+#define MCDE_ROTADD0A_ROTADD0_MASK 0xFFFFFFF8
+#define MCDE_ROTADD0A_ROTADD0(__x) \
+ MCDE_VAL2REG(MCDE_ROTADD0A, ROTADD0, __x)
+#define MCDE_ROTADD0B 0x00000A74
+#define MCDE_ROTADD0B_ROTADD0_SHIFT 3
+#define MCDE_ROTADD0B_ROTADD0_MASK 0xFFFFFFF8
+#define MCDE_ROTADD0B_ROTADD0(__x) \
+ MCDE_VAL2REG(MCDE_ROTADD0B, ROTADD0, __x)
+#define MCDE_ROTADD1A 0x00000878
+#define MCDE_ROTADD1A_GROUPOFFSET 0x200
+#define MCDE_ROTADD1A_ROTADD1_SHIFT 3
+#define MCDE_ROTADD1A_ROTADD1_MASK 0xFFFFFFF8
+#define MCDE_ROTADD1A_ROTADD1(__x) \
+ MCDE_VAL2REG(MCDE_ROTADD1A, ROTADD1, __x)
+#define MCDE_ROTADD1B 0x00000A78
+#define MCDE_ROTADD1B_ROTADD1_SHIFT 3
+#define MCDE_ROTADD1B_ROTADD1_MASK 0xFFFFFFF8
+#define MCDE_ROTADD1B_ROTADD1(__x) \
+ MCDE_VAL2REG(MCDE_ROTADD1B, ROTADD1, __x)
+#define MCDE_ROTACONF 0x0000087C
+#define MCDE_ROTACONF_GROUPOFFSET 0x200
+#define MCDE_ROTACONF_ROTBURSTSIZE_SHIFT 0
+#define MCDE_ROTACONF_ROTBURSTSIZE_MASK 0x00000003
+#define MCDE_ROTACONF_ROTBURSTSIZE_1W 0
+#define MCDE_ROTACONF_ROTBURSTSIZE_2W 1
+#define MCDE_ROTACONF_ROTBURSTSIZE_4W 2
+#define MCDE_ROTACONF_ROTBURSTSIZE_8W 3
+#define MCDE_ROTACONF_ROTBURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, ROTBURSTSIZE, \
+ MCDE_ROTACONF_ROTBURSTSIZE_##__x)
+#define MCDE_ROTACONF_ROTBURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, ROTBURSTSIZE, __x)
+#define MCDE_ROTACONF_ROTBURSTSIZE_HW_SHIFT 2
+#define MCDE_ROTACONF_ROTBURSTSIZE_HW_MASK 0x00000004
+#define MCDE_ROTACONF_ROTBURSTSIZE_HW(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, ROTBURSTSIZE_HW, __x)
+#define MCDE_ROTACONF_ROTDIR_SHIFT 3
+#define MCDE_ROTACONF_ROTDIR_MASK 0x00000008
+#define MCDE_ROTACONF_ROTDIR_CCW 0
+#define MCDE_ROTACONF_ROTDIR_CW 1
+#define MCDE_ROTACONF_ROTDIR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, ROTDIR, MCDE_ROTACONF_ROTDIR_##__x)
+#define MCDE_ROTACONF_ROTDIR(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, ROTDIR, __x)
+#define MCDE_ROTACONF_WR_MAXOUT_SHIFT 4
+#define MCDE_ROTACONF_WR_MAXOUT_MASK 0x00000030
+#define MCDE_ROTACONF_WR_MAXOUT_1_REQ 0
+#define MCDE_ROTACONF_WR_MAXOUT_2_REQ 1
+#define MCDE_ROTACONF_WR_MAXOUT_4_REQ 2
+#define MCDE_ROTACONF_WR_MAXOUT_8_REQ 3
+#define MCDE_ROTACONF_WR_MAXOUT_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, WR_MAXOUT, MCDE_ROTACONF_WR_MAXOUT_##__x)
+#define MCDE_ROTACONF_WR_MAXOUT(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, WR_MAXOUT, __x)
+#define MCDE_ROTACONF_RD_MAXOUT_SHIFT 6
+#define MCDE_ROTACONF_RD_MAXOUT_MASK 0x000000C0
+#define MCDE_ROTACONF_RD_MAXOUT_1_REQ 0
+#define MCDE_ROTACONF_RD_MAXOUT_2_REQ 1
+#define MCDE_ROTACONF_RD_MAXOUT_4_REQ 2
+#define MCDE_ROTACONF_RD_MAXOUT_8_REQ 3
+#define MCDE_ROTACONF_RD_MAXOUT_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, RD_MAXOUT, MCDE_ROTACONF_RD_MAXOUT_##__x)
+#define MCDE_ROTACONF_RD_MAXOUT(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, RD_MAXOUT, __x)
+#define MCDE_ROTACONF_STRIP_WIDTH_SHIFT 8
+#define MCDE_ROTACONF_STRIP_WIDTH_MASK 0x00007F00
+#define MCDE_ROTACONF_STRIP_WIDTH_2PIX 0
+#define MCDE_ROTACONF_STRIP_WIDTH_4PIX 1
+#define MCDE_ROTACONF_STRIP_WIDTH_8PIX 2
+#define MCDE_ROTACONF_STRIP_WIDTH_16PIX 3
+#define MCDE_ROTACONF_STRIP_WIDTH_32PIX 4
+#define MCDE_ROTACONF_STRIP_WIDTH_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, STRIP_WIDTH, \
+ MCDE_ROTACONF_STRIP_WIDTH_##__x)
+#define MCDE_ROTACONF_STRIP_WIDTH(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, STRIP_WIDTH, __x)
+#define MCDE_ROTACONF_SINGLE_BUF_SHIFT 15
+#define MCDE_ROTACONF_SINGLE_BUF_MASK 0x00008000
+#define MCDE_ROTACONF_SINGLE_BUF(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, SINGLE_BUF, __x)
+#define MCDE_ROTACONF_WR_ROPC_SHIFT 16
+#define MCDE_ROTACONF_WR_ROPC_MASK 0x00FF0000
+#define MCDE_ROTACONF_WR_ROPC(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, WR_ROPC, __x)
+#define MCDE_ROTACONF_RD_ROPC_SHIFT 24
+#define MCDE_ROTACONF_RD_ROPC_MASK 0xFF000000
+#define MCDE_ROTACONF_RD_ROPC(__x) \
+ MCDE_VAL2REG(MCDE_ROTACONF, RD_ROPC, __x)
+#define MCDE_ROTBCONF 0x00000A7C
+#define MCDE_ROTBCONF_ROTBURSTSIZE_SHIFT 0
+#define MCDE_ROTBCONF_ROTBURSTSIZE_MASK 0x00000003
+#define MCDE_ROTBCONF_ROTBURSTSIZE_1W 0
+#define MCDE_ROTBCONF_ROTBURSTSIZE_2W 1
+#define MCDE_ROTBCONF_ROTBURSTSIZE_4W 2
+#define MCDE_ROTBCONF_ROTBURSTSIZE_8W 3
+#define MCDE_ROTBCONF_ROTBURSTSIZE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, ROTBURSTSIZE, \
+ MCDE_ROTBCONF_ROTBURSTSIZE_##__x)
+#define MCDE_ROTBCONF_ROTBURSTSIZE(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, ROTBURSTSIZE, __x)
+#define MCDE_ROTBCONF_ROTBURSTSIZE_HW_SHIFT 2
+#define MCDE_ROTBCONF_ROTBURSTSIZE_HW_MASK 0x00000004
+#define MCDE_ROTBCONF_ROTBURSTSIZE_HW(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, ROTBURSTSIZE_HW, __x)
+#define MCDE_ROTBCONF_ROTDIR_SHIFT 3
+#define MCDE_ROTBCONF_ROTDIR_MASK 0x00000008
+#define MCDE_ROTBCONF_ROTDIR_CCW 0
+#define MCDE_ROTBCONF_ROTDIR_CW 1
+#define MCDE_ROTBCONF_ROTDIR_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, ROTDIR, MCDE_ROTBCONF_ROTDIR_##__x)
+#define MCDE_ROTBCONF_ROTDIR(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, ROTDIR, __x)
+#define MCDE_ROTBCONF_WR_MAXOUT_SHIFT 4
+#define MCDE_ROTBCONF_WR_MAXOUT_MASK 0x00000030
+#define MCDE_ROTBCONF_WR_MAXOUT_1_REQ 0
+#define MCDE_ROTBCONF_WR_MAXOUT_2_REQ 1
+#define MCDE_ROTBCONF_WR_MAXOUT_4_REQ 2
+#define MCDE_ROTBCONF_WR_MAXOUT_8_REQ 3
+#define MCDE_ROTBCONF_WR_MAXOUT_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, WR_MAXOUT, MCDE_ROTBCONF_WR_MAXOUT_##__x)
+#define MCDE_ROTBCONF_WR_MAXOUT(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, WR_MAXOUT, __x)
+#define MCDE_ROTBCONF_RD_MAXOUT_SHIFT 6
+#define MCDE_ROTBCONF_RD_MAXOUT_MASK 0x000000C0
+#define MCDE_ROTBCONF_RD_MAXOUT_1_REQ 0
+#define MCDE_ROTBCONF_RD_MAXOUT_2_REQ 1
+#define MCDE_ROTBCONF_RD_MAXOUT_4_REQ 2
+#define MCDE_ROTBCONF_RD_MAXOUT_8_REQ 3
+#define MCDE_ROTBCONF_RD_MAXOUT_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, RD_MAXOUT, MCDE_ROTBCONF_RD_MAXOUT_##__x)
+#define MCDE_ROTBCONF_RD_MAXOUT(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, RD_MAXOUT, __x)
+#define MCDE_ROTBCONF_STRIP_WIDTH_SHIFT 8
+#define MCDE_ROTBCONF_STRIP_WIDTH_MASK 0x00007F00
+#define MCDE_ROTBCONF_STRIP_WIDTH_2PIX 0
+#define MCDE_ROTBCONF_STRIP_WIDTH_4PIX 1
+#define MCDE_ROTBCONF_STRIP_WIDTH_8PIX 2
+#define MCDE_ROTBCONF_STRIP_WIDTH_16PIX 3
+#define MCDE_ROTBCONF_STRIP_WIDTH_32PIX 4
+#define MCDE_ROTBCONF_STRIP_WIDTH_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, STRIP_WIDTH, \
+ MCDE_ROTBCONF_STRIP_WIDTH_##__x)
+#define MCDE_ROTBCONF_STRIP_WIDTH(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, STRIP_WIDTH, __x)
+#define MCDE_ROTBCONF_SINGLE_BUF_SHIFT 15
+#define MCDE_ROTBCONF_SINGLE_BUF_MASK 0x00008000
+#define MCDE_ROTBCONF_SINGLE_BUF(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, SINGLE_BUF, __x)
+#define MCDE_ROTBCONF_WR_ROPC_SHIFT 16
+#define MCDE_ROTBCONF_WR_ROPC_MASK 0x00FF0000
+#define MCDE_ROTBCONF_WR_ROPC(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, WR_ROPC, __x)
+#define MCDE_ROTBCONF_RD_ROPC_SHIFT 24
+#define MCDE_ROTBCONF_RD_ROPC_MASK 0xFF000000
+#define MCDE_ROTBCONF_RD_ROPC(__x) \
+ MCDE_VAL2REG(MCDE_ROTBCONF, RD_ROPC, __x)
+#define MCDE_SYNCHCONFA 0x00000880
+#define MCDE_SYNCHCONFA_GROUPOFFSET 0x200
+#define MCDE_SYNCHCONFA_HWREQVEVENT_SHIFT 0
+#define MCDE_SYNCHCONFA_HWREQVEVENT_MASK 0x00000003
+#define MCDE_SYNCHCONFA_HWREQVEVENT_VSYNC 0
+#define MCDE_SYNCHCONFA_HWREQVEVENT_BACK_PORCH 1
+#define MCDE_SYNCHCONFA_HWREQVEVENT_ACTIVE_VIDEO 2
+#define MCDE_SYNCHCONFA_HWREQVEVENT_FRONT_PORCH 3
+#define MCDE_SYNCHCONFA_HWREQVEVENT_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFA, HWREQVEVENT, \
+ MCDE_SYNCHCONFA_HWREQVEVENT_##__x)
+#define MCDE_SYNCHCONFA_HWREQVEVENT(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFA, HWREQVEVENT, __x)
+#define MCDE_SYNCHCONFA_HWREQVCNT_SHIFT 2
+#define MCDE_SYNCHCONFA_HWREQVCNT_MASK 0x0000FFFC
+#define MCDE_SYNCHCONFA_HWREQVCNT(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFA, HWREQVCNT, __x)
+#define MCDE_SYNCHCONFA_SWINTVEVENT_SHIFT 16
+#define MCDE_SYNCHCONFA_SWINTVEVENT_MASK 0x00030000
+#define MCDE_SYNCHCONFA_SWINTVEVENT_VSYNC 0
+#define MCDE_SYNCHCONFA_SWINTVEVENT_BACK_PORCH 1
+#define MCDE_SYNCHCONFA_SWINTVEVENT_ACTIVE_VIDEO 2
+#define MCDE_SYNCHCONFA_SWINTVEVENT_FRONT_PORCH 3
+#define MCDE_SYNCHCONFA_SWINTVEVENT_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFA, SWINTVEVENT, \
+ MCDE_SYNCHCONFA_SWINTVEVENT_##__x)
+#define MCDE_SYNCHCONFA_SWINTVEVENT(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFA, SWINTVEVENT, __x)
+#define MCDE_SYNCHCONFA_SWINTVCNT_SHIFT 18
+#define MCDE_SYNCHCONFA_SWINTVCNT_MASK 0xFFFC0000
+#define MCDE_SYNCHCONFA_SWINTVCNT(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFA, SWINTVCNT, __x)
+#define MCDE_SYNCHCONFB 0x00000A80
+#define MCDE_SYNCHCONFB_HWREQVEVENT_SHIFT 0
+#define MCDE_SYNCHCONFB_HWREQVEVENT_MASK 0x00000003
+#define MCDE_SYNCHCONFB_HWREQVEVENT_VSYNC 0
+#define MCDE_SYNCHCONFB_HWREQVEVENT_BACK_PORCH 1
+#define MCDE_SYNCHCONFB_HWREQVEVENT_ACTIVE_VIDEO 2
+#define MCDE_SYNCHCONFB_HWREQVEVENT_FRONT_PORCH 3
+#define MCDE_SYNCHCONFB_HWREQVEVENT_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFB, HWREQVEVENT, \
+ MCDE_SYNCHCONFB_HWREQVEVENT_##__x)
+#define MCDE_SYNCHCONFB_HWREQVEVENT(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFB, HWREQVEVENT, __x)
+#define MCDE_SYNCHCONFB_HWREQVCNT_SHIFT 2
+#define MCDE_SYNCHCONFB_HWREQVCNT_MASK 0x0000FFFC
+#define MCDE_SYNCHCONFB_HWREQVCNT(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFB, HWREQVCNT, __x)
+#define MCDE_SYNCHCONFB_SWINTVEVENT_SHIFT 16
+#define MCDE_SYNCHCONFB_SWINTVEVENT_MASK 0x00030000
+#define MCDE_SYNCHCONFB_SWINTVEVENT_VSYNC 0
+#define MCDE_SYNCHCONFB_SWINTVEVENT_BACK_PORCH 1
+#define MCDE_SYNCHCONFB_SWINTVEVENT_ACTIVE_VIDEO 2
+#define MCDE_SYNCHCONFB_SWINTVEVENT_FRONT_PORCH 3
+#define MCDE_SYNCHCONFB_SWINTVEVENT_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFB, SWINTVEVENT, \
+ MCDE_SYNCHCONFB_SWINTVEVENT_##__x)
+#define MCDE_SYNCHCONFB_SWINTVEVENT(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFB, SWINTVEVENT, __x)
+#define MCDE_SYNCHCONFB_SWINTVCNT_SHIFT 18
+#define MCDE_SYNCHCONFB_SWINTVCNT_MASK 0xFFFC0000
+#define MCDE_SYNCHCONFB_SWINTVCNT(__x) \
+ MCDE_VAL2REG(MCDE_SYNCHCONFB, SWINTVCNT, __x)
+#define MCDE_CTRLA 0x00000884
+#define MCDE_CTRLA_GROUPOFFSET 0x200
+#define MCDE_CTRLA_FIFOWTRMRK_SHIFT 0
+#define MCDE_CTRLA_FIFOWTRMRK_MASK 0x000003FF
+#define MCDE_CTRLA_FIFOWTRMRK(__x) \
+ MCDE_VAL2REG(MCDE_CTRLA, FIFOWTRMRK, __x)
+#define MCDE_CTRLA_FIFOEMPTY_SHIFT 12
+#define MCDE_CTRLA_FIFOEMPTY_MASK 0x00001000
+#define MCDE_CTRLA_FIFOEMPTY(__x) \
+ MCDE_VAL2REG(MCDE_CTRLA, FIFOEMPTY, __x)
+#define MCDE_CTRLA_FIFOFULL_SHIFT 13
+#define MCDE_CTRLA_FIFOFULL_MASK 0x00002000
+#define MCDE_CTRLA_FIFOFULL(__x) \
+ MCDE_VAL2REG(MCDE_CTRLA, FIFOFULL, __x)
+#define MCDE_CTRLA_FORMID_SHIFT 16
+#define MCDE_CTRLA_FORMID_MASK 0x00070000
+#define MCDE_CTRLA_FORMID_DSI0VID 0
+#define MCDE_CTRLA_FORMID_DSI0CMD 1
+#define MCDE_CTRLA_FORMID_DSI1VID 2
+#define MCDE_CTRLA_FORMID_DSI1CMD 3
+#define MCDE_CTRLA_FORMID_DSI2VID 4
+#define MCDE_CTRLA_FORMID_DSI2CMD 5
+#define MCDE_CTRLA_FORMID_DPIA 0
+#define MCDE_CTRLA_FORMID_DPIB 1
+#define MCDE_CTRLA_FORMID_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CTRLA, FORMID, MCDE_CTRLA_FORMID_##__x)
+#define MCDE_CTRLA_FORMID(__x) \
+ MCDE_VAL2REG(MCDE_CTRLA, FORMID, __x)
+#define MCDE_CTRLA_FORMTYPE_SHIFT 20
+#define MCDE_CTRLA_FORMTYPE_MASK 0x00700000
+#define MCDE_CTRLA_FORMTYPE_DPITV 0
+#define MCDE_CTRLA_FORMTYPE_DBI 1
+#define MCDE_CTRLA_FORMTYPE_DSI 2
+#define MCDE_CTRLA_FORMTYPE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CTRLA, FORMTYPE, MCDE_CTRLA_FORMTYPE_##__x)
+#define MCDE_CTRLA_FORMTYPE(__x) \
+ MCDE_VAL2REG(MCDE_CTRLA, FORMTYPE, __x)
+#define MCDE_CTRLB 0x00000A84
+#define MCDE_CTRLB_FIFOWTRMRK_SHIFT 0
+#define MCDE_CTRLB_FIFOWTRMRK_MASK 0x000003FF
+#define MCDE_CTRLB_FIFOWTRMRK(__x) \
+ MCDE_VAL2REG(MCDE_CTRLB, FIFOWTRMRK, __x)
+#define MCDE_CTRLB_FIFOEMPTY_SHIFT 12
+#define MCDE_CTRLB_FIFOEMPTY_MASK 0x00001000
+#define MCDE_CTRLB_FIFOEMPTY(__x) \
+ MCDE_VAL2REG(MCDE_CTRLB, FIFOEMPTY, __x)
+#define MCDE_CTRLB_FIFOFULL_SHIFT 13
+#define MCDE_CTRLB_FIFOFULL_MASK 0x00002000
+#define MCDE_CTRLB_FIFOFULL(__x) \
+ MCDE_VAL2REG(MCDE_CTRLB, FIFOFULL, __x)
+#define MCDE_CTRLB_FORMID_SHIFT 16
+#define MCDE_CTRLB_FORMID_MASK 0x00070000
+#define MCDE_CTRLB_FORMID_DSI0VID 0
+#define MCDE_CTRLB_FORMID_DSI0CMD 1
+#define MCDE_CTRLB_FORMID_DSI1VID 2
+#define MCDE_CTRLB_FORMID_DSI1CMD 3
+#define MCDE_CTRLB_FORMID_DSI2VID 4
+#define MCDE_CTRLB_FORMID_DSI2CMD 5
+#define MCDE_CTRLB_FORMID_DPIA 0
+#define MCDE_CTRLB_FORMID_DPIB 1
+#define MCDE_CTRLB_FORMID_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CTRLB, FORMID, MCDE_CTRLB_FORMID_##__x)
+#define MCDE_CTRLB_FORMID(__x) \
+ MCDE_VAL2REG(MCDE_CTRLB, FORMID, __x)
+#define MCDE_CTRLB_FORMTYPE_SHIFT 20
+#define MCDE_CTRLB_FORMTYPE_MASK 0x00700000
+#define MCDE_CTRLB_FORMTYPE_DPITV 0
+#define MCDE_CTRLB_FORMTYPE_DBI 1
+#define MCDE_CTRLB_FORMTYPE_DSI 2
+#define MCDE_CTRLB_FORMTYPE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CTRLB, FORMTYPE, MCDE_CTRLB_FORMTYPE_##__x)
+#define MCDE_CTRLB_FORMTYPE(__x) \
+ MCDE_VAL2REG(MCDE_CTRLB, FORMTYPE, __x)
+#define MCDE_GAM0A 0x00000888
+#define MCDE_GAM0A_GROUPOFFSET 0x200
+#define MCDE_GAM0A_BLUE_SHIFT 0
+#define MCDE_GAM0A_BLUE_MASK 0x00FFFFFF
+#define MCDE_GAM0A_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_GAM0A, BLUE, __x)
+#define MCDE_GAM0B 0x00000A88
+#define MCDE_GAM0B_BLUE_SHIFT 0
+#define MCDE_GAM0B_BLUE_MASK 0x00FFFFFF
+#define MCDE_GAM0B_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_GAM0B, BLUE, __x)
+#define MCDE_GAM1A 0x0000088C
+#define MCDE_GAM1A_GROUPOFFSET 0x200
+#define MCDE_GAM1A_GREEN_SHIFT 0
+#define MCDE_GAM1A_GREEN_MASK 0x00FFFFFF
+#define MCDE_GAM1A_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_GAM1A, GREEN, __x)
+#define MCDE_GAM1B 0x00000A8C
+#define MCDE_GAM1B_GREEN_SHIFT 0
+#define MCDE_GAM1B_GREEN_MASK 0x00FFFFFF
+#define MCDE_GAM1B_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_GAM1B, GREEN, __x)
+#define MCDE_GAM2A 0x00000890
+#define MCDE_GAM2A_GROUPOFFSET 0x200
+#define MCDE_GAM2A_RED_SHIFT 0
+#define MCDE_GAM2A_RED_MASK 0x00FFFFFF
+#define MCDE_GAM2A_RED(__x) \
+ MCDE_VAL2REG(MCDE_GAM2A, RED, __x)
+#define MCDE_GAM2B 0x00000A90
+#define MCDE_GAM2B_RED_SHIFT 0
+#define MCDE_GAM2B_RED_MASK 0x00FFFFFF
+#define MCDE_GAM2B_RED(__x) \
+ MCDE_VAL2REG(MCDE_GAM2B, RED, __x)
+#define MCDE_OLEDCONV1A 0x00000894
+#define MCDE_OLEDCONV1A_GROUPOFFSET 0x200
+#define MCDE_OLEDCONV1A_ALPHA_RED_SHIFT 0
+#define MCDE_OLEDCONV1A_ALPHA_RED_MASK 0x00003FFF
+#define MCDE_OLEDCONV1A_ALPHA_RED(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV1A, ALPHA_RED, __x)
+#define MCDE_OLEDCONV1A_ALPHA_GREEN_SHIFT 16
+#define MCDE_OLEDCONV1A_ALPHA_GREEN_MASK 0x3FFF0000
+#define MCDE_OLEDCONV1A_ALPHA_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV1A, ALPHA_GREEN, __x)
+#define MCDE_OLEDCONV1B 0x00000A94
+#define MCDE_OLEDCONV1B_ALPHA_RED_SHIFT 0
+#define MCDE_OLEDCONV1B_ALPHA_RED_MASK 0x00003FFF
+#define MCDE_OLEDCONV1B_ALPHA_RED(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV1B, ALPHA_RED, __x)
+#define MCDE_OLEDCONV1B_ALPHA_GREEN_SHIFT 16
+#define MCDE_OLEDCONV1B_ALPHA_GREEN_MASK 0x3FFF0000
+#define MCDE_OLEDCONV1B_ALPHA_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV1B, ALPHA_GREEN, __x)
+#define MCDE_OLEDCONV2A 0x00000898
+#define MCDE_OLEDCONV2A_GROUPOFFSET 0x200
+#define MCDE_OLEDCONV2A_ALPHA_BLUE_SHIFT 0
+#define MCDE_OLEDCONV2A_ALPHA_BLUE_MASK 0x00003FFF
+#define MCDE_OLEDCONV2A_ALPHA_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV2A, ALPHA_BLUE, __x)
+#define MCDE_OLEDCONV2A_BETA_RED_SHIFT 16
+#define MCDE_OLEDCONV2A_BETA_RED_MASK 0x3FFF0000
+#define MCDE_OLEDCONV2A_BETA_RED(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV2A, BETA_RED, __x)
+#define MCDE_OLEDCONV2B 0x00000A98
+#define MCDE_OLEDCONV2B_ALPHA_BLUE_SHIFT 0
+#define MCDE_OLEDCONV2B_ALPHA_BLUE_MASK 0x00003FFF
+#define MCDE_OLEDCONV2B_ALPHA_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV2B, ALPHA_BLUE, __x)
+#define MCDE_OLEDCONV2B_BETA_RED_SHIFT 16
+#define MCDE_OLEDCONV2B_BETA_RED_MASK 0x3FFF0000
+#define MCDE_OLEDCONV2B_BETA_RED(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV2B, BETA_RED, __x)
+#define MCDE_OLEDCONV3A 0x0000089C
+#define MCDE_OLEDCONV3A_GROUPOFFSET 0x200
+#define MCDE_OLEDCONV3A_BETA_GREEN_SHIFT 0
+#define MCDE_OLEDCONV3A_BETA_GREEN_MASK 0x00003FFF
+#define MCDE_OLEDCONV3A_BETA_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV3A, BETA_GREEN, __x)
+#define MCDE_OLEDCONV3A_BETA_BLUE_SHIFT 16
+#define MCDE_OLEDCONV3A_BETA_BLUE_MASK 0x3FFF0000
+#define MCDE_OLEDCONV3A_BETA_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV3A, BETA_BLUE, __x)
+#define MCDE_OLEDCONV3B 0x00000A9C
+#define MCDE_OLEDCONV3B_BETA_GREEN_SHIFT 0
+#define MCDE_OLEDCONV3B_BETA_GREEN_MASK 0x00003FFF
+#define MCDE_OLEDCONV3B_BETA_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV3B, BETA_GREEN, __x)
+#define MCDE_OLEDCONV3B_BETA_BLUE_SHIFT 16
+#define MCDE_OLEDCONV3B_BETA_BLUE_MASK 0x3FFF0000
+#define MCDE_OLEDCONV3B_BETA_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV3B, BETA_BLUE, __x)
+#define MCDE_OLEDCONV4A 0x000008A0
+#define MCDE_OLEDCONV4A_GROUPOFFSET 0x200
+#define MCDE_OLEDCONV4A_GAMMA_RED_SHIFT 0
+#define MCDE_OLEDCONV4A_GAMMA_RED_MASK 0x00003FFF
+#define MCDE_OLEDCONV4A_GAMMA_RED(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV4A, GAMMA_RED, __x)
+#define MCDE_OLEDCONV4A_GAMMA_GREEN_SHIFT 16
+#define MCDE_OLEDCONV4A_GAMMA_GREEN_MASK 0x3FFF0000
+#define MCDE_OLEDCONV4A_GAMMA_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV4A, GAMMA_GREEN, __x)
+#define MCDE_OLEDCONV4B 0x00000AA0
+#define MCDE_OLEDCONV4B_GAMMA_RED_SHIFT 0
+#define MCDE_OLEDCONV4B_GAMMA_RED_MASK 0x00003FFF
+#define MCDE_OLEDCONV4B_GAMMA_RED(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV4B, GAMMA_RED, __x)
+#define MCDE_OLEDCONV4B_GAMMA_GREEN_SHIFT 16
+#define MCDE_OLEDCONV4B_GAMMA_GREEN_MASK 0x3FFF0000
+#define MCDE_OLEDCONV4B_GAMMA_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV4B, GAMMA_GREEN, __x)
+#define MCDE_OLEDCONV5A 0x000008A4
+#define MCDE_OLEDCONV5A_GROUPOFFSET 0x200
+#define MCDE_OLEDCONV5A_GAMMA_BLUE_SHIFT 0
+#define MCDE_OLEDCONV5A_GAMMA_BLUE_MASK 0x00003FFF
+#define MCDE_OLEDCONV5A_GAMMA_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV5A, GAMMA_BLUE, __x)
+#define MCDE_OLEDCONV5A_OFF_RED_SHIFT 16
+#define MCDE_OLEDCONV5A_OFF_RED_MASK 0x3FFF0000
+#define MCDE_OLEDCONV5A_OFF_RED(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV5A, OFF_RED, __x)
+#define MCDE_OLEDCONV5B 0x00000AA4
+#define MCDE_OLEDCONV5B_GAMMA_BLUE_SHIFT 0
+#define MCDE_OLEDCONV5B_GAMMA_BLUE_MASK 0x00003FFF
+#define MCDE_OLEDCONV5B_GAMMA_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV5B, GAMMA_BLUE, __x)
+#define MCDE_OLEDCONV5B_OFF_RED_SHIFT 16
+#define MCDE_OLEDCONV5B_OFF_RED_MASK 0x3FFF0000
+#define MCDE_OLEDCONV5B_OFF_RED(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV5B, OFF_RED, __x)
+#define MCDE_OLEDCONV6A 0x000008A8
+#define MCDE_OLEDCONV6A_GROUPOFFSET 0x200
+#define MCDE_OLEDCONV6A_OFF_GREEN_SHIFT 0
+#define MCDE_OLEDCONV6A_OFF_GREEN_MASK 0x00003FFF
+#define MCDE_OLEDCONV6A_OFF_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV6A, OFF_GREEN, __x)
+#define MCDE_OLEDCONV6A_OFF_BLUE_SHIFT 16
+#define MCDE_OLEDCONV6A_OFF_BLUE_MASK 0x3FFF0000
+#define MCDE_OLEDCONV6A_OFF_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV6A, OFF_BLUE, __x)
+#define MCDE_OLEDCONV6B 0x00000AA8
+#define MCDE_OLEDCONV6B_OFF_GREEN_SHIFT 0
+#define MCDE_OLEDCONV6B_OFF_GREEN_MASK 0x00003FFF
+#define MCDE_OLEDCONV6B_OFF_GREEN(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV6B, OFF_GREEN, __x)
+#define MCDE_OLEDCONV6B_OFF_BLUE_SHIFT 16
+#define MCDE_OLEDCONV6B_OFF_BLUE_MASK 0x3FFF0000
+#define MCDE_OLEDCONV6B_OFF_BLUE(__x) \
+ MCDE_VAL2REG(MCDE_OLEDCONV6B, OFF_BLUE, __x)
+#define MCDE_CRC 0x00000C00
+#define MCDE_CRC_FLOEN_SHIFT 0
+#define MCDE_CRC_FLOEN_MASK 0x00000001
+#define MCDE_CRC_FLOEN(__x) \
+ MCDE_VAL2REG(MCDE_CRC, FLOEN, __x)
+#define MCDE_CRC_POWEREN_SHIFT 1
+#define MCDE_CRC_POWEREN_MASK 0x00000002
+#define MCDE_CRC_POWEREN(__x) \
+ MCDE_VAL2REG(MCDE_CRC, POWEREN, __x)
+#define MCDE_CRC_C1EN_SHIFT 2
+#define MCDE_CRC_C1EN_MASK 0x00000004
+#define MCDE_CRC_C1EN(__x) \
+ MCDE_VAL2REG(MCDE_CRC, C1EN, __x)
+#define MCDE_CRC_C2EN_SHIFT 3
+#define MCDE_CRC_C2EN_MASK 0x00000008
+#define MCDE_CRC_C2EN(__x) \
+ MCDE_VAL2REG(MCDE_CRC, C2EN, __x)
+#define MCDE_CRC_WMLVL1_SHIFT 4
+#define MCDE_CRC_WMLVL1_MASK 0x00000010
+#define MCDE_CRC_WMLVL1(__x) \
+ MCDE_VAL2REG(MCDE_CRC, WMLVL1, __x)
+#define MCDE_CRC_WMLVL2_SHIFT 5
+#define MCDE_CRC_WMLVL2_MASK 0x00000020
+#define MCDE_CRC_WMLVL2(__x) \
+ MCDE_VAL2REG(MCDE_CRC, WMLVL2, __x)
+#define MCDE_CRC_SYNCSEL_SHIFT 6
+#define MCDE_CRC_SYNCSEL_MASK 0x00000040
+#define MCDE_CRC_SYNCSEL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, SYNCSEL, __x)
+#define MCDE_CRC_SYCEN0_SHIFT 7
+#define MCDE_CRC_SYCEN0_MASK 0x00000080
+#define MCDE_CRC_SYCEN0(__x) \
+ MCDE_VAL2REG(MCDE_CRC, SYCEN0, __x)
+#define MCDE_CRC_SYCEN1_SHIFT 8
+#define MCDE_CRC_SYCEN1_MASK 0x00000100
+#define MCDE_CRC_SYCEN1(__x) \
+ MCDE_VAL2REG(MCDE_CRC, SYCEN1, __x)
+#define MCDE_CRC_SIZE1_SHIFT 9
+#define MCDE_CRC_SIZE1_MASK 0x00000200
+#define MCDE_CRC_SIZE1(__x) \
+ MCDE_VAL2REG(MCDE_CRC, SIZE1, __x)
+#define MCDE_CRC_SIZE2_SHIFT 10
+#define MCDE_CRC_SIZE2_MASK 0x00000400
+#define MCDE_CRC_SIZE2(__x) \
+ MCDE_VAL2REG(MCDE_CRC, SIZE2, __x)
+#define MCDE_CRC_INBAND1_SHIFT 11
+#define MCDE_CRC_INBAND1_MASK 0x00000800
+#define MCDE_CRC_INBAND1(__x) \
+ MCDE_VAL2REG(MCDE_CRC, INBAND1, __x)
+#define MCDE_CRC_INBAND2_SHIFT 12
+#define MCDE_CRC_INBAND2_MASK 0x00001000
+#define MCDE_CRC_INBAND2(__x) \
+ MCDE_VAL2REG(MCDE_CRC, INBAND2, __x)
+#define MCDE_CRC_CLKSEL_SHIFT 13
+#define MCDE_CRC_CLKSEL_MASK 0x00006000
+#define MCDE_CRC_CLKSEL_166MHz 0
+#define MCDE_CRC_CLKSEL_48MHz 1
+#define MCDE_CRC_CLKSEL_LCD 2
+#define MCDE_CRC_CLKSEL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRC, CLKSEL, MCDE_CRC_CLKSEL_##__x)
+#define MCDE_CRC_CLKSEL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, CLKSEL, __x)
+#define MCDE_CRC_YUVCONVC1EN_SHIFT 15
+#define MCDE_CRC_YUVCONVC1EN_MASK 0x00008000
+#define MCDE_CRC_YUVCONVC1EN(__x) \
+ MCDE_VAL2REG(MCDE_CRC, YUVCONVC1EN, __x)
+#define MCDE_CRC_CS1EN_SHIFT 16
+#define MCDE_CRC_CS1EN_MASK 0x00010000
+#define MCDE_CRC_CS1EN(__x) \
+ MCDE_VAL2REG(MCDE_CRC, CS1EN, __x)
+#define MCDE_CRC_CS2EN_SHIFT 17
+#define MCDE_CRC_CS2EN_MASK 0x00020000
+#define MCDE_CRC_CS2EN(__x) \
+ MCDE_VAL2REG(MCDE_CRC, CS2EN, __x)
+#define MCDE_CRC_RESEN_SHIFT 18
+#define MCDE_CRC_RESEN_MASK 0x00040000
+#define MCDE_CRC_RESEN(__x) \
+ MCDE_VAL2REG(MCDE_CRC, RESEN, __x)
+#define MCDE_CRC_CS1POL_SHIFT 19
+#define MCDE_CRC_CS1POL_MASK 0x00080000
+#define MCDE_CRC_CS1POL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, CS1POL, __x)
+#define MCDE_CRC_CS2POL_SHIFT 20
+#define MCDE_CRC_CS2POL_MASK 0x00100000
+#define MCDE_CRC_CS2POL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, CS2POL, __x)
+#define MCDE_CRC_CD1POL_SHIFT 21
+#define MCDE_CRC_CD1POL_MASK 0x00200000
+#define MCDE_CRC_CD1POL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, CD1POL, __x)
+#define MCDE_CRC_CD2POL_SHIFT 22
+#define MCDE_CRC_CD2POL_MASK 0x00400000
+#define MCDE_CRC_CD2POL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, CD2POL, __x)
+#define MCDE_CRC_WR1POL_SHIFT 23
+#define MCDE_CRC_WR1POL_MASK 0x00800000
+#define MCDE_CRC_WR1POL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, WR1POL, __x)
+#define MCDE_CRC_WR2POL_SHIFT 24
+#define MCDE_CRC_WR2POL_MASK 0x01000000
+#define MCDE_CRC_WR2POL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, WR2POL, __x)
+#define MCDE_CRC_RD1POL_SHIFT 25
+#define MCDE_CRC_RD1POL_MASK 0x02000000
+#define MCDE_CRC_RD1POL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, RD1POL, __x)
+#define MCDE_CRC_RD2POL_SHIFT 26
+#define MCDE_CRC_RD2POL_MASK 0x04000000
+#define MCDE_CRC_RD2POL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, RD2POL, __x)
+#define MCDE_CRC_RES1POL_SHIFT 27
+#define MCDE_CRC_RES1POL_MASK 0x08000000
+#define MCDE_CRC_RES1POL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, RES1POL, __x)
+#define MCDE_CRC_RES2POL_SHIFT 28
+#define MCDE_CRC_RES2POL_MASK 0x10000000
+#define MCDE_CRC_RES2POL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, RES2POL, __x)
+#define MCDE_CRC_SYNCCTRL_SHIFT 29
+#define MCDE_CRC_SYNCCTRL_MASK 0x60000000
+#define MCDE_CRC_SYNCCTRL_OFF 0
+#define MCDE_CRC_SYNCCTRL_C0 1
+#define MCDE_CRC_SYNCCTRL_C1 2
+#define MCDE_CRC_SYNCCTRL_PING_PONG 3
+#define MCDE_CRC_SYNCCTRL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CRC, SYNCCTRL, MCDE_CRC_SYNCCTRL_##__x)
+#define MCDE_CRC_SYNCCTRL(__x) \
+ MCDE_VAL2REG(MCDE_CRC, SYNCCTRL, __x)
+#define MCDE_CRC_CLAMPC1EN_SHIFT 31
+#define MCDE_CRC_CLAMPC1EN_MASK 0x80000000
+#define MCDE_CRC_CLAMPC1EN(__x) \
+ MCDE_VAL2REG(MCDE_CRC, CLAMPC1EN, __x)
+#define MCDE_PBCCRC0 0x00000C04
+#define MCDE_PBCCRC0_GROUPOFFSET 0x4
+#define MCDE_PBCCRC0_BSCM_SHIFT 0
+#define MCDE_PBCCRC0_BSCM_MASK 0x00000007
+#define MCDE_PBCCRC0_BSCM_1_8BIT 0
+#define MCDE_PBCCRC0_BSCM_2_8BIT 1
+#define MCDE_PBCCRC0_BSCM_3_8BIT 2
+#define MCDE_PBCCRC0_BSCM_1_16BIT 3
+#define MCDE_PBCCRC0_BSCM_2_16BIT 4
+#define MCDE_PBCCRC0_BSCM_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC0, BSCM, MCDE_PBCCRC0_BSCM_##__x)
+#define MCDE_PBCCRC0_BSCM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC0, BSCM, __x)
+#define MCDE_PBCCRC0_BSDM_SHIFT 3
+#define MCDE_PBCCRC0_BSDM_MASK 0x00000038
+#define MCDE_PBCCRC0_BSDM_1_8BIT 0
+#define MCDE_PBCCRC0_BSDM_2_8BIT 1
+#define MCDE_PBCCRC0_BSDM_3_8BIT 2
+#define MCDE_PBCCRC0_BSDM_1_16BIT 3
+#define MCDE_PBCCRC0_BSDM_2_16BIT 4
+#define MCDE_PBCCRC0_BSDM_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC0, BSDM, MCDE_PBCCRC0_BSDM_##__x)
+#define MCDE_PBCCRC0_BSDM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC0, BSDM, __x)
+#define MCDE_PBCCRC0_PDM_SHIFT 6
+#define MCDE_PBCCRC0_PDM_MASK 0x000000C0
+#define MCDE_PBCCRC0_PDM_NORMAL 0
+#define MCDE_PBCCRC0_PDM_16_TO_32 1
+#define MCDE_PBCCRC0_PDM_24_TO_32_RIGHT 2
+#define MCDE_PBCCRC0_PDM_24_TO_32_LEFT 3
+#define MCDE_PBCCRC0_PDM_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC0, PDM, MCDE_PBCCRC0_PDM_##__x)
+#define MCDE_PBCCRC0_PDM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC0, PDM, __x)
+#define MCDE_PBCCRC0_PDCTRL_SHIFT 12
+#define MCDE_PBCCRC0_PDCTRL_MASK 0x00001000
+#define MCDE_PBCCRC0_PDCTRL(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC0, PDCTRL, __x)
+#define MCDE_PBCCRC0_BPP_SHIFT 13
+#define MCDE_PBCCRC0_BPP_MASK 0x0000E000
+#define MCDE_PBCCRC0_BPP_8BPP 0
+#define MCDE_PBCCRC0_BPP_12BPP 1
+#define MCDE_PBCCRC0_BPP_15BPP 2
+#define MCDE_PBCCRC0_BPP_16BPP 3
+#define MCDE_PBCCRC0_BPP_18BPP 4
+#define MCDE_PBCCRC0_BPP_24BPP 5
+#define MCDE_PBCCRC0_BPP(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC0, BPP, __x)
+#define MCDE_PBCCRC1 0x00000C08
+#define MCDE_PBCCRC1_BSCM_SHIFT 0
+#define MCDE_PBCCRC1_BSCM_MASK 0x00000007
+#define MCDE_PBCCRC1_BSCM_1_8BIT 0
+#define MCDE_PBCCRC1_BSCM_2_8BIT 1
+#define MCDE_PBCCRC1_BSCM_3_8BIT 2
+#define MCDE_PBCCRC1_BSCM_1_16BIT 3
+#define MCDE_PBCCRC1_BSCM_2_16BIT 4
+#define MCDE_PBCCRC1_BSCM_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC1, BSCM, MCDE_PBCCRC1_BSCM_##__x)
+#define MCDE_PBCCRC1_BSCM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC1, BSCM, __x)
+#define MCDE_PBCCRC1_BSDM_SHIFT 3
+#define MCDE_PBCCRC1_BSDM_MASK 0x00000038
+#define MCDE_PBCCRC1_BSDM_1_8BIT 0
+#define MCDE_PBCCRC1_BSDM_2_8BIT 1
+#define MCDE_PBCCRC1_BSDM_3_8BIT 2
+#define MCDE_PBCCRC1_BSDM_1_16BIT 3
+#define MCDE_PBCCRC1_BSDM_2_16BIT 4
+#define MCDE_PBCCRC1_BSDM_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC1, BSDM, MCDE_PBCCRC1_BSDM_##__x)
+#define MCDE_PBCCRC1_BSDM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC1, BSDM, __x)
+#define MCDE_PBCCRC1_PDM_SHIFT 6
+#define MCDE_PBCCRC1_PDM_MASK 0x000000C0
+#define MCDE_PBCCRC1_PDM_NORMAL 0
+#define MCDE_PBCCRC1_PDM_16_TO_32 1
+#define MCDE_PBCCRC1_PDM_24_TO_32_RIGHT 2
+#define MCDE_PBCCRC1_PDM_24_TO_32_LEFT 3
+#define MCDE_PBCCRC1_PDM_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC1, PDM, MCDE_PBCCRC1_PDM_##__x)
+#define MCDE_PBCCRC1_PDM(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC1, PDM, __x)
+#define MCDE_PBCCRC1_PDCTRL_SHIFT 12
+#define MCDE_PBCCRC1_PDCTRL_MASK 0x00001000
+#define MCDE_PBCCRC1_PDCTRL(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC1, PDCTRL, __x)
+#define MCDE_PBCCRC1_BPP_SHIFT 13
+#define MCDE_PBCCRC1_BPP_MASK 0x0000E000
+#define MCDE_PBCCRC1_BPP_8BPP 0
+#define MCDE_PBCCRC1_BPP_12BPP 1
+#define MCDE_PBCCRC1_BPP_15BPP 2
+#define MCDE_PBCCRC1_BPP_16BPP 3
+#define MCDE_PBCCRC1_BPP_18BPP 4
+#define MCDE_PBCCRC1_BPP_24BPP 5
+#define MCDE_PBCCRC1_BPP(__x) \
+ MCDE_VAL2REG(MCDE_PBCCRC1, BPP, __x)
+#define MCDE_PBCBMRC00 0x00000C0C
+#define MCDE_PBCBMRC00_GROUPOFFSET 0x4
+#define MCDE_PBCBMRC00_MUXI_SHIFT 0
+#define MCDE_PBCBMRC00_MUXI_MASK 0xFFFFFFFF
+#define MCDE_PBCBMRC00_MUXI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBMRC00, MUXI, __x)
+#define MCDE_PBCBMRC01 0x00000C10
+#define MCDE_PBCBMRC01_MUXI_SHIFT 0
+#define MCDE_PBCBMRC01_MUXI_MASK 0xFFFFFFFF
+#define MCDE_PBCBMRC01_MUXI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBMRC01, MUXI, __x)
+#define MCDE_PBCBMRC02 0x00000C14
+#define MCDE_PBCBMRC02_MUXI_SHIFT 0
+#define MCDE_PBCBMRC02_MUXI_MASK 0xFFFFFFFF
+#define MCDE_PBCBMRC02_MUXI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBMRC02, MUXI, __x)
+#define MCDE_PBCBMRC03 0x00000C18
+#define MCDE_PBCBMRC03_MUXI_SHIFT 0
+#define MCDE_PBCBMRC03_MUXI_MASK 0xFFFFFFFF
+#define MCDE_PBCBMRC03_MUXI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBMRC03, MUXI, __x)
+#define MCDE_PBCBMRC04 0x00000C1C
+#define MCDE_PBCBMRC04_MUXI_SHIFT 0
+#define MCDE_PBCBMRC04_MUXI_MASK 0xFFFFFFFF
+#define MCDE_PBCBMRC04_MUXI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBMRC04, MUXI, __x)
+#define MCDE_PBCBMRC10 0x00000C20
+#define MCDE_PBCBMRC10_MUXI_SHIFT 0
+#define MCDE_PBCBMRC10_MUXI_MASK 0xFFFFFFFF
+#define MCDE_PBCBMRC10_MUXI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBMRC10, MUXI, __x)
+#define MCDE_PBCBMRC11 0x00000C24
+#define MCDE_PBCBMRC11_MUXI_SHIFT 0
+#define MCDE_PBCBMRC11_MUXI_MASK 0xFFFFFFFF
+#define MCDE_PBCBMRC11_MUXI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBMRC11, MUXI, __x)
+#define MCDE_PBCBMRC12 0x00000C28
+#define MCDE_PBCBMRC12_MUXI_SHIFT 0
+#define MCDE_PBCBMRC12_MUXI_MASK 0xFFFFFFFF
+#define MCDE_PBCBMRC12_MUXI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBMRC12, MUXI, __x)
+#define MCDE_PBCBMRC13 0x00000C2C
+#define MCDE_PBCBMRC13_MUXI_SHIFT 0
+#define MCDE_PBCBMRC13_MUXI_MASK 0xFFFFFFFF
+#define MCDE_PBCBMRC13_MUXI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBMRC13, MUXI, __x)
+#define MCDE_PBCBMRC14 0x00000C30
+#define MCDE_PBCBMRC14_MUXI_SHIFT 0
+#define MCDE_PBCBMRC14_MUXI_MASK 0xFFFFFFFF
+#define MCDE_PBCBMRC14_MUXI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBMRC14, MUXI, __x)
+#define MCDE_PBCBCRC00 0x00000C34
+#define MCDE_PBCBCRC00_GROUPOFFSET 0x4
+#define MCDE_PBCBCRC00_CTLI_SHIFT 0
+#define MCDE_PBCBCRC00_CTLI_MASK 0xFFFFFFFF
+#define MCDE_PBCBCRC00_CTLI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBCRC00, CTLI, __x)
+#define MCDE_PBCBCRC10 0x00000C38
+#define MCDE_PBCBCRC10_CTLI_SHIFT 0
+#define MCDE_PBCBCRC10_CTLI_MASK 0xFFFFFFFF
+#define MCDE_PBCBCRC10_CTLI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBCRC10, CTLI, __x)
+#define MCDE_PBCBCRC01 0x00000C48
+#define MCDE_PBCBCRC01_GROUPOFFSET 0x4
+#define MCDE_PBCBCRC01_CTLI_SHIFT 0
+#define MCDE_PBCBCRC01_CTLI_MASK 0xFFFFFFFF
+#define MCDE_PBCBCRC01_CTLI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBCRC01, CTLI, __x)
+#define MCDE_PBCBCRC11 0x00000C4C
+#define MCDE_PBCBCRC11_CTLI_SHIFT 0
+#define MCDE_PBCBCRC11_CTLI_MASK 0xFFFFFFFF
+#define MCDE_PBCBCRC11_CTLI(__x) \
+ MCDE_VAL2REG(MCDE_PBCBCRC11, CTLI, __x)
+#define MCDE_VSCRC0 0x00000C5C
+#define MCDE_VSCRC0_GROUPOFFSET 0x4
+#define MCDE_VSCRC0_VSPMIN_SHIFT 0
+#define MCDE_VSCRC0_VSPMIN_MASK 0x00000FFF
+#define MCDE_VSCRC0_VSPMIN(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC0, VSPMIN, __x)
+#define MCDE_VSCRC0_VSPMAX_SHIFT 12
+#define MCDE_VSCRC0_VSPMAX_MASK 0x00FFF000
+#define MCDE_VSCRC0_VSPMAX(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC0, VSPMAX, __x)
+#define MCDE_VSCRC0_VSPDIV_SHIFT 24
+#define MCDE_VSCRC0_VSPDIV_MASK 0x07000000
+#define MCDE_VSCRC0_VSPDIV(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC0, VSPDIV, __x)
+#define MCDE_VSCRC0_VSPOL_SHIFT 27
+#define MCDE_VSCRC0_VSPOL_MASK 0x08000000
+#define MCDE_VSCRC0_VSPOL_ACTIVE_HIGH 0
+#define MCDE_VSCRC0_VSPOL_ACTIVE_LOW 1
+#define MCDE_VSCRC0_VSPOL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC0, VSPOL, MCDE_VSCRC0_VSPOL_##__x)
+#define MCDE_VSCRC0_VSPOL(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC0, VSPOL, __x)
+#define MCDE_VSCRC0_VSSEL_SHIFT 28
+#define MCDE_VSCRC0_VSSEL_MASK 0x10000000
+#define MCDE_VSCRC0_VSSEL_VSYNC 0
+#define MCDE_VSCRC0_VSSEL_HSYNC 1
+#define MCDE_VSCRC0_VSSEL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC0, VSSEL, MCDE_VSCRC0_VSSEL_##__x)
+#define MCDE_VSCRC0_VSSEL(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC0, VSSEL, __x)
+#define MCDE_VSCRC0_VSDBL_SHIFT 29
+#define MCDE_VSCRC0_VSDBL_MASK 0xE0000000
+#define MCDE_VSCRC0_VSDBL(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC0, VSDBL, __x)
+#define MCDE_VSCRC1 0x00000C60
+#define MCDE_VSCRC1_VSPMIN_SHIFT 0
+#define MCDE_VSCRC1_VSPMIN_MASK 0x00000FFF
+#define MCDE_VSCRC1_VSPMIN(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC1, VSPMIN, __x)
+#define MCDE_VSCRC1_VSPMAX_SHIFT 12
+#define MCDE_VSCRC1_VSPMAX_MASK 0x00FFF000
+#define MCDE_VSCRC1_VSPMAX(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC1, VSPMAX, __x)
+#define MCDE_VSCRC1_VSPDIV_SHIFT 24
+#define MCDE_VSCRC1_VSPDIV_MASK 0x07000000
+#define MCDE_VSCRC1_VSPDIV(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC1, VSPDIV, __x)
+#define MCDE_VSCRC1_VSPOL_SHIFT 27
+#define MCDE_VSCRC1_VSPOL_MASK 0x08000000
+#define MCDE_VSCRC1_VSPOL_ACTIVE_HIGH 0
+#define MCDE_VSCRC1_VSPOL_ACTIVE_LOW 1
+#define MCDE_VSCRC1_VSPOL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC1, VSPOL, MCDE_VSCRC1_VSPOL_##__x)
+#define MCDE_VSCRC1_VSPOL(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC1, VSPOL, __x)
+#define MCDE_VSCRC1_VSSEL_SHIFT 28
+#define MCDE_VSCRC1_VSSEL_MASK 0x10000000
+#define MCDE_VSCRC1_VSSEL_VSYNC 0
+#define MCDE_VSCRC1_VSSEL_HSYNC 1
+#define MCDE_VSCRC1_VSSEL_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC1, VSSEL, MCDE_VSCRC1_VSSEL_##__x)
+#define MCDE_VSCRC1_VSSEL(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC1, VSSEL, __x)
+#define MCDE_VSCRC1_VSDBL_SHIFT 29
+#define MCDE_VSCRC1_VSDBL_MASK 0xE0000000
+#define MCDE_VSCRC1_VSDBL(__x) \
+ MCDE_VAL2REG(MCDE_VSCRC1, VSDBL, __x)
+#define MCDE_SCTRC 0x00000C64
+#define MCDE_SCTRC_SYNCDELC0_SHIFT 0
+#define MCDE_SCTRC_SYNCDELC0_MASK 0x000000FF
+#define MCDE_SCTRC_SYNCDELC0(__x) \
+ MCDE_VAL2REG(MCDE_SCTRC, SYNCDELC0, __x)
+#define MCDE_SCTRC_SYNCDELC1_SHIFT 8
+#define MCDE_SCTRC_SYNCDELC1_MASK 0x0000FF00
+#define MCDE_SCTRC_SYNCDELC1(__x) \
+ MCDE_VAL2REG(MCDE_SCTRC, SYNCDELC1, __x)
+#define MCDE_SCTRC_TRDELC_SHIFT 16
+#define MCDE_SCTRC_TRDELC_MASK 0x0FFF0000
+#define MCDE_SCTRC_TRDELC(__x) \
+ MCDE_VAL2REG(MCDE_SCTRC, TRDELC, __x)
+#define MCDE_SCSRC 0x00000C68
+#define MCDE_SCSRC_VSTAC0_SHIFT 0
+#define MCDE_SCSRC_VSTAC0_MASK 0x00000001
+#define MCDE_SCSRC_VSTAC0(__x) \
+ MCDE_VAL2REG(MCDE_SCSRC, VSTAC0, __x)
+#define MCDE_SCSRC_VSTAC1_SHIFT 1
+#define MCDE_SCSRC_VSTAC1_MASK 0x00000002
+#define MCDE_SCSRC_VSTAC1(__x) \
+ MCDE_VAL2REG(MCDE_SCSRC, VSTAC1, __x)
+#define MCDE_BCNR0 0x00000C6C
+#define MCDE_BCNR0_GROUPOFFSET 0x4
+#define MCDE_BCNR0_BCN_SHIFT 0
+#define MCDE_BCNR0_BCN_MASK 0x000000FF
+#define MCDE_BCNR0_BCN(__x) \
+ MCDE_VAL2REG(MCDE_BCNR0, BCN, __x)
+#define MCDE_BCNR1 0x00000C70
+#define MCDE_BCNR1_BCN_SHIFT 0
+#define MCDE_BCNR1_BCN_MASK 0x000000FF
+#define MCDE_BCNR1_BCN(__x) \
+ MCDE_VAL2REG(MCDE_BCNR1, BCN, __x)
+#define MCDE_CSCDTR0 0x00000C74
+#define MCDE_CSCDTR0_GROUPOFFSET 0x4
+#define MCDE_CSCDTR0_CSACT_SHIFT 0
+#define MCDE_CSCDTR0_CSACT_MASK 0x000000FF
+#define MCDE_CSCDTR0_CSACT(__x) \
+ MCDE_VAL2REG(MCDE_CSCDTR0, CSACT, __x)
+#define MCDE_CSCDTR0_CSDEACT_SHIFT 8
+#define MCDE_CSCDTR0_CSDEACT_MASK 0x0000FF00
+#define MCDE_CSCDTR0_CSDEACT(__x) \
+ MCDE_VAL2REG(MCDE_CSCDTR0, CSDEACT, __x)
+#define MCDE_CSCDTR0_CDACT_SHIFT 16
+#define MCDE_CSCDTR0_CDACT_MASK 0x00FF0000
+#define MCDE_CSCDTR0_CDACT(__x) \
+ MCDE_VAL2REG(MCDE_CSCDTR0, CDACT, __x)
+#define MCDE_CSCDTR0_CDDEACT_SHIFT 24
+#define MCDE_CSCDTR0_CDDEACT_MASK 0xFF000000
+#define MCDE_CSCDTR0_CDDEACT(__x) \
+ MCDE_VAL2REG(MCDE_CSCDTR0, CDDEACT, __x)
+#define MCDE_CSCDTR1 0x00000C78
+#define MCDE_CSCDTR1_CSACT_SHIFT 0
+#define MCDE_CSCDTR1_CSACT_MASK 0x000000FF
+#define MCDE_CSCDTR1_CSACT(__x) \
+ MCDE_VAL2REG(MCDE_CSCDTR1, CSACT, __x)
+#define MCDE_CSCDTR1_CSDEACT_SHIFT 8
+#define MCDE_CSCDTR1_CSDEACT_MASK 0x0000FF00
+#define MCDE_CSCDTR1_CSDEACT(__x) \
+ MCDE_VAL2REG(MCDE_CSCDTR1, CSDEACT, __x)
+#define MCDE_CSCDTR1_CDACT_SHIFT 16
+#define MCDE_CSCDTR1_CDACT_MASK 0x00FF0000
+#define MCDE_CSCDTR1_CDACT(__x) \
+ MCDE_VAL2REG(MCDE_CSCDTR1, CDACT, __x)
+#define MCDE_CSCDTR1_CDDEACT_SHIFT 24
+#define MCDE_CSCDTR1_CDDEACT_MASK 0xFF000000
+#define MCDE_CSCDTR1_CDDEACT(__x) \
+ MCDE_VAL2REG(MCDE_CSCDTR1, CDDEACT, __x)
+#define MCDE_RDWRTR0 0x00000C7C
+#define MCDE_RDWRTR0_GROUPOFFSET 0x4
+#define MCDE_RDWRTR0_RWACT_SHIFT 0
+#define MCDE_RDWRTR0_RWACT_MASK 0x000000FF
+#define MCDE_RDWRTR0_RWACT(__x) \
+ MCDE_VAL2REG(MCDE_RDWRTR0, RWACT, __x)
+#define MCDE_RDWRTR0_RWDEACT_SHIFT 8
+#define MCDE_RDWRTR0_RWDEACT_MASK 0x0000FF00
+#define MCDE_RDWRTR0_RWDEACT(__x) \
+ MCDE_VAL2REG(MCDE_RDWRTR0, RWDEACT, __x)
+#define MCDE_RDWRTR0_MOTINT_SHIFT 16
+#define MCDE_RDWRTR0_MOTINT_MASK 0x00010000
+#define MCDE_RDWRTR0_MOTINT(__x) \
+ MCDE_VAL2REG(MCDE_RDWRTR0, MOTINT, __x)
+#define MCDE_RDWRTR1 0x00000C80
+#define MCDE_RDWRTR1_RWACT_SHIFT 0
+#define MCDE_RDWRTR1_RWACT_MASK 0x000000FF
+#define MCDE_RDWRTR1_RWACT(__x) \
+ MCDE_VAL2REG(MCDE_RDWRTR1, RWACT, __x)
+#define MCDE_RDWRTR1_RWDEACT_SHIFT 8
+#define MCDE_RDWRTR1_RWDEACT_MASK 0x0000FF00
+#define MCDE_RDWRTR1_RWDEACT(__x) \
+ MCDE_VAL2REG(MCDE_RDWRTR1, RWDEACT, __x)
+#define MCDE_RDWRTR1_MOTINT_SHIFT 16
+#define MCDE_RDWRTR1_MOTINT_MASK 0x00010000
+#define MCDE_RDWRTR1_MOTINT(__x) \
+ MCDE_VAL2REG(MCDE_RDWRTR1, MOTINT, __x)
+#define MCDE_DOTR0 0x00000C84
+#define MCDE_DOTR0_GROUPOFFSET 0x4
+#define MCDE_DOTR0_DOACT_SHIFT 0
+#define MCDE_DOTR0_DOACT_MASK 0x000000FF
+#define MCDE_DOTR0_DOACT(__x) \
+ MCDE_VAL2REG(MCDE_DOTR0, DOACT, __x)
+#define MCDE_DOTR0_DODEACT_SHIFT 8
+#define MCDE_DOTR0_DODEACT_MASK 0x0000FF00
+#define MCDE_DOTR0_DODEACT(__x) \
+ MCDE_VAL2REG(MCDE_DOTR0, DODEACT, __x)
+#define MCDE_DOTR1 0x00000C88
+#define MCDE_DOTR1_DOACT_SHIFT 0
+#define MCDE_DOTR1_DOACT_MASK 0x000000FF
+#define MCDE_DOTR1_DOACT(__x) \
+ MCDE_VAL2REG(MCDE_DOTR1, DOACT, __x)
+#define MCDE_DOTR1_DODEACT_SHIFT 8
+#define MCDE_DOTR1_DODEACT_MASK 0x0000FF00
+#define MCDE_DOTR1_DODEACT(__x) \
+ MCDE_VAL2REG(MCDE_DOTR1, DODEACT, __x)
+#define MCDE_WCMDC0_V1 0x00000C8C
+#define MCDE_WCMDC0_V1_GROUPOFFSET 0x4
+#define MCDE_WCMDC0_V1_COMMANDVALUE_SHIFT 0
+#define MCDE_WCMDC0_V1_COMMANDVALUE_MASK 0x00FFFFFF
+#define MCDE_WCMDC0_V1_COMMANDVALUE(__x) \
+ MCDE_VAL2REG(MCDE_WCMDC0_V1, COMMANDVALUE, __x)
+#define MCDE_WCMDC1_V1 0x00000C90
+#define MCDE_WCMDC1_V1_COMMANDVALUE_SHIFT 0
+#define MCDE_WCMDC1_V1_COMMANDVALUE_MASK 0x00FFFFFF
+#define MCDE_WCMDC1_V1_COMMANDVALUE(__x) \
+ MCDE_VAL2REG(MCDE_WCMDC1_V1, COMMANDVALUE, __x)
+#define MCDE_WDATADC0 0x00000C94
+#define MCDE_WDATADC0_GROUPOFFSET 0x4
+#define MCDE_WDATADC0_DATAVALUE_SHIFT 0
+#define MCDE_WDATADC0_DATAVALUE_MASK 0x00FFFFFF
+#define MCDE_WDATADC0_DATAVALUE(__x) \
+ MCDE_VAL2REG(MCDE_WDATADC0, DATAVALUE, __x)
+#define MCDE_WDATADC1 0x00000C98
+#define MCDE_WDATADC1_DATAVALUE_SHIFT 0
+#define MCDE_WDATADC1_DATAVALUE_MASK 0x00FFFFFF
+#define MCDE_WDATADC1_DATAVALUE(__x) \
+ MCDE_VAL2REG(MCDE_WDATADC1, DATAVALUE, __x)
+#define MCDE_RDATADC0 0x00000C9C
+#define MCDE_RDATADC0_GROUPOFFSET 0x4
+#define MCDE_RDATADC0_DATAREADFROMDISPLAYMODULE_SHIFT 0
+#define MCDE_RDATADC0_DATAREADFROMDISPLAYMODULE_MASK 0x0000FFFF
+#define MCDE_RDATADC0_DATAREADFROMDISPLAYMODULE(__x) \
+ MCDE_VAL2REG(MCDE_RDATADC0, DATAREADFROMDISPLAYMODULE, __x)
+#define MCDE_RDATADC0_STARTREAD_SHIFT 16
+#define MCDE_RDATADC0_STARTREAD_MASK 0x00010000
+#define MCDE_RDATADC0_STARTREAD(__x) \
+ MCDE_VAL2REG(MCDE_RDATADC0, STARTREAD, __x)
+#define MCDE_RDATADC1 0x00000CA0
+#define MCDE_RDATADC1_DATAREADFROMDISPLAYMODULE_SHIFT 0
+#define MCDE_RDATADC1_DATAREADFROMDISPLAYMODULE_MASK 0x0000FFFF
+#define MCDE_RDATADC1_DATAREADFROMDISPLAYMODULE(__x) \
+ MCDE_VAL2REG(MCDE_RDATADC1, DATAREADFROMDISPLAYMODULE, __x)
+#define MCDE_RDATADC1_STARTREAD_SHIFT 16
+#define MCDE_RDATADC1_STARTREAD_MASK 0x00010000
+#define MCDE_RDATADC1_STARTREAD(__x) \
+ MCDE_VAL2REG(MCDE_RDATADC1, STARTREAD, __x)
+#define MCDE_STATC_V1 0x00000CA4
+#define MCDE_STATC_V1_STATBUSY0_SHIFT 0
+#define MCDE_STATC_V1_STATBUSY0_MASK 0x00000001
+#define MCDE_STATC_V1_STATBUSY0(__x) \
+ MCDE_VAL2REG(MCDE_STATC_V1, STATBUSY0, __x)
+#define MCDE_STATC_V1_FIFOEMPTY0_SHIFT 1
+#define MCDE_STATC_V1_FIFOEMPTY0_MASK 0x00000002
+#define MCDE_STATC_V1_FIFOEMPTY0(__x) \
+ MCDE_VAL2REG(MCDE_STATC_V1, FIFOEMPTY0, __x)
+#define MCDE_STATC_V1_FIFOFULL0_SHIFT 2
+#define MCDE_STATC_V1_FIFOFULL0_MASK 0x00000004
+#define MCDE_STATC_V1_FIFOFULL0(__x) \
+ MCDE_VAL2REG(MCDE_STATC_V1, FIFOFULL0, __x)
+#define MCDE_STATC_V1_FIFOCMDEMPTY0_SHIFT 3
+#define MCDE_STATC_V1_FIFOCMDEMPTY0_MASK 0x00000008
+#define MCDE_STATC_V1_FIFOCMDEMPTY0(__x) \
+ MCDE_VAL2REG(MCDE_STATC_V1, FIFOCMDEMPTY0, __x)
+#define MCDE_STATC_V1_FIFOCMDFULL0_SHIFT 4
+#define MCDE_STATC_V1_FIFOCMDFULL0_MASK 0x00000010
+#define MCDE_STATC_V1_FIFOCMDFULL0(__x) \
+ MCDE_VAL2REG(MCDE_STATC_V1, FIFOCMDFULL0, __x)
+#define MCDE_STATC_V1_STATBUSY1_SHIFT 5
+#define MCDE_STATC_V1_STATBUSY1_MASK 0x00000020
+#define MCDE_STATC_V1_STATBUSY1(__x) \
+ MCDE_VAL2REG(MCDE_STATC_V1, STATBUSY1, __x)
+#define MCDE_STATC_V1_FIFOEMPTY1_SHIFT 6
+#define MCDE_STATC_V1_FIFOEMPTY1_MASK 0x00000040
+#define MCDE_STATC_V1_FIFOEMPTY1(__x) \
+ MCDE_VAL2REG(MCDE_STATC_V1, FIFOEMPTY1, __x)
+#define MCDE_STATC_V1_FIFOFULL1_SHIFT 7
+#define MCDE_STATC_V1_FIFOFULL1_MASK 0x00000080
+#define MCDE_STATC_V1_FIFOFULL1(__x) \
+ MCDE_VAL2REG(MCDE_STATC_V1, FIFOFULL1, __x)
+#define MCDE_STATC_V1_FIFOCMDEMPTY1_SHIFT 8
+#define MCDE_STATC_V1_FIFOCMDEMPTY1_MASK 0x00000100
+#define MCDE_STATC_V1_FIFOCMDEMPTY1(__x) \
+ MCDE_VAL2REG(MCDE_STATC_V1, FIFOCMDEMPTY1, __x)
+#define MCDE_STATC_V1_FIFOCMDFULL1_SHIFT 9
+#define MCDE_STATC_V1_FIFOCMDFULL1_MASK 0x00000200
+#define MCDE_STATC_V1_FIFOCMDFULL1(__x) \
+ MCDE_VAL2REG(MCDE_STATC_V1, FIFOCMDFULL1, __x)
+#define MCDE_CTRLC0 0x00000CA8
+#define MCDE_CTRLC0_GROUPOFFSET 0x4
+#define MCDE_CTRLC0_FIFOWTRMRK_SHIFT 0
+#define MCDE_CTRLC0_FIFOWTRMRK_MASK 0x000000FF
+#define MCDE_CTRLC0_FIFOWTRMRK(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC0, FIFOWTRMRK, __x)
+#define MCDE_CTRLC0_FIFOEMPTY_SHIFT 12
+#define MCDE_CTRLC0_FIFOEMPTY_MASK 0x00001000
+#define MCDE_CTRLC0_FIFOEMPTY(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC0, FIFOEMPTY, __x)
+#define MCDE_CTRLC0_FIFOFULL_SHIFT 13
+#define MCDE_CTRLC0_FIFOFULL_MASK 0x00002000
+#define MCDE_CTRLC0_FIFOFULL(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC0, FIFOFULL, __x)
+#define MCDE_CTRLC0_FORMID_SHIFT 16
+#define MCDE_CTRLC0_FORMID_MASK 0x00070000
+#define MCDE_CTRLC0_FORMID_DSI0VID 0
+#define MCDE_CTRLC0_FORMID_DSI0CMD 1
+#define MCDE_CTRLC0_FORMID_DSI1VID 2
+#define MCDE_CTRLC0_FORMID_DSI1CMD 3
+#define MCDE_CTRLC0_FORMID_DSI2VID 4
+#define MCDE_CTRLC0_FORMID_DSI2CMD 5
+#define MCDE_CTRLC0_FORMID_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC0, FORMID, MCDE_CTRLC0_FORMID_##__x)
+#define MCDE_CTRLC0_FORMID(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC0, FORMID, __x)
+#define MCDE_CTRLC0_FORMTYPE_SHIFT 20
+#define MCDE_CTRLC0_FORMTYPE_MASK 0x00700000
+#define MCDE_CTRLC0_FORMTYPE_DPITV 0
+#define MCDE_CTRLC0_FORMTYPE_DBI 1
+#define MCDE_CTRLC0_FORMTYPE_DSI 2
+#define MCDE_CTRLC0_FORMTYPE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC0, FORMTYPE, MCDE_CTRLC0_FORMTYPE_##__x)
+#define MCDE_CTRLC0_FORMTYPE(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC0, FORMTYPE, __x)
+#define MCDE_CTRLC1 0x00000CAC
+#define MCDE_CTRLC1_FIFOWTRMRK_SHIFT 0
+#define MCDE_CTRLC1_FIFOWTRMRK_MASK 0x000000FF
+#define MCDE_CTRLC1_FIFOWTRMRK(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC1, FIFOWTRMRK, __x)
+#define MCDE_CTRLC1_FIFOEMPTY_SHIFT 12
+#define MCDE_CTRLC1_FIFOEMPTY_MASK 0x00001000
+#define MCDE_CTRLC1_FIFOEMPTY(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC1, FIFOEMPTY, __x)
+#define MCDE_CTRLC1_FIFOFULL_SHIFT 13
+#define MCDE_CTRLC1_FIFOFULL_MASK 0x00002000
+#define MCDE_CTRLC1_FIFOFULL(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC1, FIFOFULL, __x)
+#define MCDE_CTRLC1_FORMID_SHIFT 16
+#define MCDE_CTRLC1_FORMID_MASK 0x00070000
+#define MCDE_CTRLC1_FORMID_DSI0VID 0
+#define MCDE_CTRLC1_FORMID_DSI0CMD 1
+#define MCDE_CTRLC1_FORMID_DSI1VID 2
+#define MCDE_CTRLC1_FORMID_DSI1CMD 3
+#define MCDE_CTRLC1_FORMID_DSI2VID 4
+#define MCDE_CTRLC1_FORMID_DSI2CMD 5
+#define MCDE_CTRLC1_FORMID_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC1, FORMID, MCDE_CTRLC1_FORMID_##__x)
+#define MCDE_CTRLC1_FORMID(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC1, FORMID, __x)
+#define MCDE_CTRLC1_FORMTYPE_SHIFT 20
+#define MCDE_CTRLC1_FORMTYPE_MASK 0x00700000
+#define MCDE_CTRLC1_FORMTYPE_DPITV 0
+#define MCDE_CTRLC1_FORMTYPE_DBI 1
+#define MCDE_CTRLC1_FORMTYPE_DSI 2
+#define MCDE_CTRLC1_FORMTYPE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC1, FORMTYPE, MCDE_CTRLC1_FORMTYPE_##__x)
+#define MCDE_CTRLC1_FORMTYPE(__x) \
+ MCDE_VAL2REG(MCDE_CTRLC1, FORMTYPE, __x)
+#define MCDE_DSIVID0CONF0 0x00000E00
+#define MCDE_DSIVID0CONF0_GROUPOFFSET 0x20
+#define MCDE_DSIVID0CONF0_BLANKING_SHIFT 0
+#define MCDE_DSIVID0CONF0_BLANKING_MASK 0x000000FF
+#define MCDE_DSIVID0CONF0_BLANKING(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0CONF0, BLANKING, __x)
+#define MCDE_DSIVID0CONF0_VID_MODE_SHIFT 12
+#define MCDE_DSIVID0CONF0_VID_MODE_MASK 0x00001000
+#define MCDE_DSIVID0CONF0_VID_MODE_CMD 0
+#define MCDE_DSIVID0CONF0_VID_MODE_VID 1
+#define MCDE_DSIVID0CONF0_VID_MODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0CONF0, VID_MODE, \
+ MCDE_DSIVID0CONF0_VID_MODE_##__x)
+#define MCDE_DSIVID0CONF0_VID_MODE(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0CONF0, VID_MODE, __x)
+#define MCDE_DSIVID0CONF0_CMD8_SHIFT 13
+#define MCDE_DSIVID0CONF0_CMD8_MASK 0x00002000
+#define MCDE_DSIVID0CONF0_CMD8(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0CONF0, CMD8, __x)
+#define MCDE_DSIVID0CONF0_BIT_SWAP_SHIFT 16
+#define MCDE_DSIVID0CONF0_BIT_SWAP_MASK 0x00010000
+#define MCDE_DSIVID0CONF0_BIT_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0CONF0, BIT_SWAP, __x)
+#define MCDE_DSIVID0CONF0_BYTE_SWAP_SHIFT 17
+#define MCDE_DSIVID0CONF0_BYTE_SWAP_MASK 0x00020000
+#define MCDE_DSIVID0CONF0_BYTE_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0CONF0, BYTE_SWAP, __x)
+#define MCDE_DSIVID0CONF0_DCSVID_NOTGEN_SHIFT 18
+#define MCDE_DSIVID0CONF0_DCSVID_NOTGEN_MASK 0x00040000
+#define MCDE_DSIVID0CONF0_DCSVID_NOTGEN(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0CONF0, DCSVID_NOTGEN, __x)
+#define MCDE_DSIVID0CONF0_PACKING_SHIFT 20
+#define MCDE_DSIVID0CONF0_PACKING_MASK 0x00700000
+#define MCDE_DSIVID0CONF0_PACKING_RGB565 0
+#define MCDE_DSIVID0CONF0_PACKING_RGB666 1
+#define MCDE_DSIVID0CONF0_PACKING_RGB888 2
+#define MCDE_DSIVID0CONF0_PACKING_BGR888 3
+#define MCDE_DSIVID0CONF0_PACKING_HDTV 4
+#define MCDE_DSIVID0CONF0_PACKING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0CONF0, PACKING, \
+ MCDE_DSIVID0CONF0_PACKING_##__x)
+#define MCDE_DSIVID0CONF0_PACKING(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0CONF0, PACKING, __x)
+#define MCDE_DSICMD0CONF0 0x00000E20
+#define MCDE_DSICMD0CONF0_BLANKING_SHIFT 0
+#define MCDE_DSICMD0CONF0_BLANKING_MASK 0x000000FF
+#define MCDE_DSICMD0CONF0_BLANKING(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0CONF0, BLANKING, __x)
+#define MCDE_DSICMD0CONF0_VID_MODE_SHIFT 12
+#define MCDE_DSICMD0CONF0_VID_MODE_MASK 0x00001000
+#define MCDE_DSICMD0CONF0_VID_MODE_CMD 0
+#define MCDE_DSICMD0CONF0_VID_MODE_VID 1
+#define MCDE_DSICMD0CONF0_VID_MODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0CONF0, VID_MODE, \
+ MCDE_DSICMD0CONF0_VID_MODE_##__x)
+#define MCDE_DSICMD0CONF0_VID_MODE(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0CONF0, VID_MODE, __x)
+#define MCDE_DSICMD0CONF0_CMD8_SHIFT 13
+#define MCDE_DSICMD0CONF0_CMD8_MASK 0x00002000
+#define MCDE_DSICMD0CONF0_CMD8(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0CONF0, CMD8, __x)
+#define MCDE_DSICMD0CONF0_BIT_SWAP_SHIFT 16
+#define MCDE_DSICMD0CONF0_BIT_SWAP_MASK 0x00010000
+#define MCDE_DSICMD0CONF0_BIT_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0CONF0, BIT_SWAP, __x)
+#define MCDE_DSICMD0CONF0_BYTE_SWAP_SHIFT 17
+#define MCDE_DSICMD0CONF0_BYTE_SWAP_MASK 0x00020000
+#define MCDE_DSICMD0CONF0_BYTE_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0CONF0, BYTE_SWAP, __x)
+#define MCDE_DSICMD0CONF0_DCSVID_NOTGEN_SHIFT 18
+#define MCDE_DSICMD0CONF0_DCSVID_NOTGEN_MASK 0x00040000
+#define MCDE_DSICMD0CONF0_DCSVID_NOTGEN(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0CONF0, DCSVID_NOTGEN, __x)
+#define MCDE_DSICMD0CONF0_PACKING_SHIFT 20
+#define MCDE_DSICMD0CONF0_PACKING_MASK 0x00700000
+#define MCDE_DSICMD0CONF0_PACKING_RGB565 0
+#define MCDE_DSICMD0CONF0_PACKING_RGB666 1
+#define MCDE_DSICMD0CONF0_PACKING_RGB888 2
+#define MCDE_DSICMD0CONF0_PACKING_BGR888 3
+#define MCDE_DSICMD0CONF0_PACKING_HDTV 4
+#define MCDE_DSICMD0CONF0_PACKING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0CONF0, PACKING, \
+ MCDE_DSICMD0CONF0_PACKING_##__x)
+#define MCDE_DSICMD0CONF0_PACKING(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0CONF0, PACKING, __x)
+#define MCDE_DSIVID1CONF0 0x00000E40
+#define MCDE_DSIVID1CONF0_BLANKING_SHIFT 0
+#define MCDE_DSIVID1CONF0_BLANKING_MASK 0x000000FF
+#define MCDE_DSIVID1CONF0_BLANKING(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1CONF0, BLANKING, __x)
+#define MCDE_DSIVID1CONF0_VID_MODE_SHIFT 12
+#define MCDE_DSIVID1CONF0_VID_MODE_MASK 0x00001000
+#define MCDE_DSIVID1CONF0_VID_MODE_CMD 0
+#define MCDE_DSIVID1CONF0_VID_MODE_VID 1
+#define MCDE_DSIVID1CONF0_VID_MODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1CONF0, VID_MODE, \
+ MCDE_DSIVID1CONF0_VID_MODE_##__x)
+#define MCDE_DSIVID1CONF0_VID_MODE(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1CONF0, VID_MODE, __x)
+#define MCDE_DSIVID1CONF0_CMD8_SHIFT 13
+#define MCDE_DSIVID1CONF0_CMD8_MASK 0x00002000
+#define MCDE_DSIVID1CONF0_CMD8(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1CONF0, CMD8, __x)
+#define MCDE_DSIVID1CONF0_BIT_SWAP_SHIFT 16
+#define MCDE_DSIVID1CONF0_BIT_SWAP_MASK 0x00010000
+#define MCDE_DSIVID1CONF0_BIT_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1CONF0, BIT_SWAP, __x)
+#define MCDE_DSIVID1CONF0_BYTE_SWAP_SHIFT 17
+#define MCDE_DSIVID1CONF0_BYTE_SWAP_MASK 0x00020000
+#define MCDE_DSIVID1CONF0_BYTE_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1CONF0, BYTE_SWAP, __x)
+#define MCDE_DSIVID1CONF0_DCSVID_NOTGEN_SHIFT 18
+#define MCDE_DSIVID1CONF0_DCSVID_NOTGEN_MASK 0x00040000
+#define MCDE_DSIVID1CONF0_DCSVID_NOTGEN(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1CONF0, DCSVID_NOTGEN, __x)
+#define MCDE_DSIVID1CONF0_PACKING_SHIFT 20
+#define MCDE_DSIVID1CONF0_PACKING_MASK 0x00700000
+#define MCDE_DSIVID1CONF0_PACKING_RGB565 0
+#define MCDE_DSIVID1CONF0_PACKING_RGB666 1
+#define MCDE_DSIVID1CONF0_PACKING_RGB888 2
+#define MCDE_DSIVID1CONF0_PACKING_BGR888 3
+#define MCDE_DSIVID1CONF0_PACKING_HDTV 4
+#define MCDE_DSIVID1CONF0_PACKING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1CONF0, PACKING, \
+ MCDE_DSIVID1CONF0_PACKING_##__x)
+#define MCDE_DSIVID1CONF0_PACKING(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1CONF0, PACKING, __x)
+#define MCDE_DSICMD1CONF0 0x00000E60
+#define MCDE_DSICMD1CONF0_BLANKING_SHIFT 0
+#define MCDE_DSICMD1CONF0_BLANKING_MASK 0x000000FF
+#define MCDE_DSICMD1CONF0_BLANKING(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1CONF0, BLANKING, __x)
+#define MCDE_DSICMD1CONF0_VID_MODE_SHIFT 12
+#define MCDE_DSICMD1CONF0_VID_MODE_MASK 0x00001000
+#define MCDE_DSICMD1CONF0_VID_MODE_CMD 0
+#define MCDE_DSICMD1CONF0_VID_MODE_VID 1
+#define MCDE_DSICMD1CONF0_VID_MODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1CONF0, VID_MODE, \
+ MCDE_DSICMD1CONF0_VID_MODE_##__x)
+#define MCDE_DSICMD1CONF0_VID_MODE(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1CONF0, VID_MODE, __x)
+#define MCDE_DSICMD1CONF0_CMD8_SHIFT 13
+#define MCDE_DSICMD1CONF0_CMD8_MASK 0x00002000
+#define MCDE_DSICMD1CONF0_CMD8(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1CONF0, CMD8, __x)
+#define MCDE_DSICMD1CONF0_BIT_SWAP_SHIFT 16
+#define MCDE_DSICMD1CONF0_BIT_SWAP_MASK 0x00010000
+#define MCDE_DSICMD1CONF0_BIT_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1CONF0, BIT_SWAP, __x)
+#define MCDE_DSICMD1CONF0_BYTE_SWAP_SHIFT 17
+#define MCDE_DSICMD1CONF0_BYTE_SWAP_MASK 0x00020000
+#define MCDE_DSICMD1CONF0_BYTE_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1CONF0, BYTE_SWAP, __x)
+#define MCDE_DSICMD1CONF0_DCSVID_NOTGEN_SHIFT 18
+#define MCDE_DSICMD1CONF0_DCSVID_NOTGEN_MASK 0x00040000
+#define MCDE_DSICMD1CONF0_DCSVID_NOTGEN(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1CONF0, DCSVID_NOTGEN, __x)
+#define MCDE_DSICMD1CONF0_PACKING_SHIFT 20
+#define MCDE_DSICMD1CONF0_PACKING_MASK 0x00700000
+#define MCDE_DSICMD1CONF0_PACKING_RGB565 0
+#define MCDE_DSICMD1CONF0_PACKING_RGB666 1
+#define MCDE_DSICMD1CONF0_PACKING_RGB888 2
+#define MCDE_DSICMD1CONF0_PACKING_BGR888 3
+#define MCDE_DSICMD1CONF0_PACKING_HDTV 4
+#define MCDE_DSICMD1CONF0_PACKING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1CONF0, PACKING, \
+ MCDE_DSICMD1CONF0_PACKING_##__x)
+#define MCDE_DSICMD1CONF0_PACKING(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1CONF0, PACKING, __x)
+#define MCDE_DSIVID2CONF0 0x00000E80
+#define MCDE_DSIVID2CONF0_BLANKING_SHIFT 0
+#define MCDE_DSIVID2CONF0_BLANKING_MASK 0x000000FF
+#define MCDE_DSIVID2CONF0_BLANKING(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2CONF0, BLANKING, __x)
+#define MCDE_DSIVID2CONF0_VID_MODE_SHIFT 12
+#define MCDE_DSIVID2CONF0_VID_MODE_MASK 0x00001000
+#define MCDE_DSIVID2CONF0_VID_MODE_CMD 0
+#define MCDE_DSIVID2CONF0_VID_MODE_VID 1
+#define MCDE_DSIVID2CONF0_VID_MODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2CONF0, VID_MODE, \
+ MCDE_DSIVID2CONF0_VID_MODE_##__x)
+#define MCDE_DSIVID2CONF0_VID_MODE(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2CONF0, VID_MODE, __x)
+#define MCDE_DSIVID2CONF0_CMD8_SHIFT 13
+#define MCDE_DSIVID2CONF0_CMD8_MASK 0x00002000
+#define MCDE_DSIVID2CONF0_CMD8(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2CONF0, CMD8, __x)
+#define MCDE_DSIVID2CONF0_BIT_SWAP_SHIFT 16
+#define MCDE_DSIVID2CONF0_BIT_SWAP_MASK 0x00010000
+#define MCDE_DSIVID2CONF0_BIT_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2CONF0, BIT_SWAP, __x)
+#define MCDE_DSIVID2CONF0_BYTE_SWAP_SHIFT 17
+#define MCDE_DSIVID2CONF0_BYTE_SWAP_MASK 0x00020000
+#define MCDE_DSIVID2CONF0_BYTE_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2CONF0, BYTE_SWAP, __x)
+#define MCDE_DSIVID2CONF0_DCSVID_NOTGEN_SHIFT 18
+#define MCDE_DSIVID2CONF0_DCSVID_NOTGEN_MASK 0x00040000
+#define MCDE_DSIVID2CONF0_DCSVID_NOTGEN(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2CONF0, DCSVID_NOTGEN, __x)
+#define MCDE_DSIVID2CONF0_PACKING_SHIFT 20
+#define MCDE_DSIVID2CONF0_PACKING_MASK 0x00700000
+#define MCDE_DSIVID2CONF0_PACKING_RGB565 0
+#define MCDE_DSIVID2CONF0_PACKING_RGB666 1
+#define MCDE_DSIVID2CONF0_PACKING_RGB888 2
+#define MCDE_DSIVID2CONF0_PACKING_BGR888 3
+#define MCDE_DSIVID2CONF0_PACKING_HDTV 4
+#define MCDE_DSIVID2CONF0_PACKING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2CONF0, PACKING, \
+ MCDE_DSIVID2CONF0_PACKING_##__x)
+#define MCDE_DSIVID2CONF0_PACKING(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2CONF0, PACKING, __x)
+#define MCDE_DSICMD2CONF0 0x00000EA0
+#define MCDE_DSICMD2CONF0_BLANKING_SHIFT 0
+#define MCDE_DSICMD2CONF0_BLANKING_MASK 0x000000FF
+#define MCDE_DSICMD2CONF0_BLANKING(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2CONF0, BLANKING, __x)
+#define MCDE_DSICMD2CONF0_VID_MODE_SHIFT 12
+#define MCDE_DSICMD2CONF0_VID_MODE_MASK 0x00001000
+#define MCDE_DSICMD2CONF0_VID_MODE_CMD 0
+#define MCDE_DSICMD2CONF0_VID_MODE_VID 1
+#define MCDE_DSICMD2CONF0_VID_MODE_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2CONF0, VID_MODE, \
+ MCDE_DSICMD2CONF0_VID_MODE_##__x)
+#define MCDE_DSICMD2CONF0_VID_MODE(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2CONF0, VID_MODE, __x)
+#define MCDE_DSICMD2CONF0_CMD8_SHIFT 13
+#define MCDE_DSICMD2CONF0_CMD8_MASK 0x00002000
+#define MCDE_DSICMD2CONF0_CMD8(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2CONF0, CMD8, __x)
+#define MCDE_DSICMD2CONF0_BIT_SWAP_SHIFT 16
+#define MCDE_DSICMD2CONF0_BIT_SWAP_MASK 0x00010000
+#define MCDE_DSICMD2CONF0_BIT_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2CONF0, BIT_SWAP, __x)
+#define MCDE_DSICMD2CONF0_BYTE_SWAP_SHIFT 17
+#define MCDE_DSICMD2CONF0_BYTE_SWAP_MASK 0x00020000
+#define MCDE_DSICMD2CONF0_BYTE_SWAP(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2CONF0, BYTE_SWAP, __x)
+#define MCDE_DSICMD2CONF0_DCSVID_NOTGEN_SHIFT 18
+#define MCDE_DSICMD2CONF0_DCSVID_NOTGEN_MASK 0x00040000
+#define MCDE_DSICMD2CONF0_DCSVID_NOTGEN(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2CONF0, DCSVID_NOTGEN, __x)
+#define MCDE_DSICMD2CONF0_PACKING_SHIFT 20
+#define MCDE_DSICMD2CONF0_PACKING_MASK 0x00700000
+#define MCDE_DSICMD2CONF0_PACKING_RGB565 0
+#define MCDE_DSICMD2CONF0_PACKING_RGB666 1
+#define MCDE_DSICMD2CONF0_PACKING_RGB888 2
+#define MCDE_DSICMD2CONF0_PACKING_BGR888 3
+#define MCDE_DSICMD2CONF0_PACKING_HDTV 4
+#define MCDE_DSICMD2CONF0_PACKING_ENUM(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2CONF0, PACKING, \
+ MCDE_DSICMD2CONF0_PACKING_##__x)
+#define MCDE_DSICMD2CONF0_PACKING(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2CONF0, PACKING, __x)
+#define MCDE_DSIVID0FRAME 0x00000E04
+#define MCDE_DSIVID0FRAME_GROUPOFFSET 0x20
+#define MCDE_DSIVID0FRAME_FRAME_SHIFT 0
+#define MCDE_DSIVID0FRAME_FRAME_MASK 0x00FFFFFF
+#define MCDE_DSIVID0FRAME_FRAME(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0FRAME, FRAME, __x)
+#define MCDE_DSICMD0FRAME 0x00000E24
+#define MCDE_DSICMD0FRAME_FRAME_SHIFT 0
+#define MCDE_DSICMD0FRAME_FRAME_MASK 0x00FFFFFF
+#define MCDE_DSICMD0FRAME_FRAME(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0FRAME, FRAME, __x)
+#define MCDE_DSIVID1FRAME 0x00000E44
+#define MCDE_DSIVID1FRAME_FRAME_SHIFT 0
+#define MCDE_DSIVID1FRAME_FRAME_MASK 0x00FFFFFF
+#define MCDE_DSIVID1FRAME_FRAME(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1FRAME, FRAME, __x)
+#define MCDE_DSICMD1FRAME 0x00000E64
+#define MCDE_DSICMD1FRAME_FRAME_SHIFT 0
+#define MCDE_DSICMD1FRAME_FRAME_MASK 0x00FFFFFF
+#define MCDE_DSICMD1FRAME_FRAME(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1FRAME, FRAME, __x)
+#define MCDE_DSIVID2FRAME 0x00000E84
+#define MCDE_DSIVID2FRAME_FRAME_SHIFT 0
+#define MCDE_DSIVID2FRAME_FRAME_MASK 0x00FFFFFF
+#define MCDE_DSIVID2FRAME_FRAME(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2FRAME, FRAME, __x)
+#define MCDE_DSICMD2FRAME 0x00000EA4
+#define MCDE_DSICMD2FRAME_FRAME_SHIFT 0
+#define MCDE_DSICMD2FRAME_FRAME_MASK 0x00FFFFFF
+#define MCDE_DSICMD2FRAME_FRAME(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2FRAME, FRAME, __x)
+#define MCDE_DSIVID0PKT 0x00000E08
+#define MCDE_DSIVID0PKT_GROUPOFFSET 0x20
+#define MCDE_DSIVID0PKT_PACKET_SHIFT 0
+#define MCDE_DSIVID0PKT_PACKET_MASK 0x0000FFFF
+#define MCDE_DSIVID0PKT_PACKET(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0PKT, PACKET, __x)
+#define MCDE_DSICMD0PKT 0x00000E28
+#define MCDE_DSICMD0PKT_PACKET_SHIFT 0
+#define MCDE_DSICMD0PKT_PACKET_MASK 0x0000FFFF
+#define MCDE_DSICMD0PKT_PACKET(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0PKT, PACKET, __x)
+#define MCDE_DSIVID1PKT 0x00000E48
+#define MCDE_DSIVID1PKT_PACKET_SHIFT 0
+#define MCDE_DSIVID1PKT_PACKET_MASK 0x0000FFFF
+#define MCDE_DSIVID1PKT_PACKET(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1PKT, PACKET, __x)
+#define MCDE_DSICMD1PKT 0x00000E68
+#define MCDE_DSICMD1PKT_PACKET_SHIFT 0
+#define MCDE_DSICMD1PKT_PACKET_MASK 0x0000FFFF
+#define MCDE_DSICMD1PKT_PACKET(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1PKT, PACKET, __x)
+#define MCDE_DSIVID2PKT 0x00000E88
+#define MCDE_DSIVID2PKT_PACKET_SHIFT 0
+#define MCDE_DSIVID2PKT_PACKET_MASK 0x0000FFFF
+#define MCDE_DSIVID2PKT_PACKET(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2PKT, PACKET, __x)
+#define MCDE_DSICMD2PKT 0x00000EA8
+#define MCDE_DSICMD2PKT_PACKET_SHIFT 0
+#define MCDE_DSICMD2PKT_PACKET_MASK 0x0000FFFF
+#define MCDE_DSICMD2PKT_PACKET(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2PKT, PACKET, __x)
+#define MCDE_DSIVID0SYNC 0x00000E0C
+#define MCDE_DSIVID0SYNC_GROUPOFFSET 0x20
+#define MCDE_DSIVID0SYNC_DMA_SHIFT 0
+#define MCDE_DSIVID0SYNC_DMA_MASK 0x00000FFF
+#define MCDE_DSIVID0SYNC_DMA(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0SYNC, DMA, __x)
+#define MCDE_DSIVID0SYNC_SW_SHIFT 16
+#define MCDE_DSIVID0SYNC_SW_MASK 0x0FFF0000
+#define MCDE_DSIVID0SYNC_SW(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0SYNC, SW, __x)
+#define MCDE_DSICMD0SYNC 0x00000E2C
+#define MCDE_DSICMD0SYNC_DMA_SHIFT 0
+#define MCDE_DSICMD0SYNC_DMA_MASK 0x00000FFF
+#define MCDE_DSICMD0SYNC_DMA(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0SYNC, DMA, __x)
+#define MCDE_DSICMD0SYNC_SW_SHIFT 16
+#define MCDE_DSICMD0SYNC_SW_MASK 0x0FFF0000
+#define MCDE_DSICMD0SYNC_SW(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0SYNC, SW, __x)
+#define MCDE_DSIVID1SYNC 0x00000E4C
+#define MCDE_DSIVID1SYNC_DMA_SHIFT 0
+#define MCDE_DSIVID1SYNC_DMA_MASK 0x00000FFF
+#define MCDE_DSIVID1SYNC_DMA(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1SYNC, DMA, __x)
+#define MCDE_DSIVID1SYNC_SW_SHIFT 16
+#define MCDE_DSIVID1SYNC_SW_MASK 0x0FFF0000
+#define MCDE_DSIVID1SYNC_SW(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1SYNC, SW, __x)
+#define MCDE_DSICMD1SYNC 0x00000E6C
+#define MCDE_DSICMD1SYNC_DMA_SHIFT 0
+#define MCDE_DSICMD1SYNC_DMA_MASK 0x00000FFF
+#define MCDE_DSICMD1SYNC_DMA(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1SYNC, DMA, __x)
+#define MCDE_DSICMD1SYNC_SW_SHIFT 16
+#define MCDE_DSICMD1SYNC_SW_MASK 0x0FFF0000
+#define MCDE_DSICMD1SYNC_SW(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1SYNC, SW, __x)
+#define MCDE_DSIVID2SYNC 0x00000E8C
+#define MCDE_DSIVID2SYNC_DMA_SHIFT 0
+#define MCDE_DSIVID2SYNC_DMA_MASK 0x00000FFF
+#define MCDE_DSIVID2SYNC_DMA(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2SYNC, DMA, __x)
+#define MCDE_DSIVID2SYNC_SW_SHIFT 16
+#define MCDE_DSIVID2SYNC_SW_MASK 0x0FFF0000
+#define MCDE_DSIVID2SYNC_SW(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2SYNC, SW, __x)
+#define MCDE_DSICMD2SYNC 0x00000EAC
+#define MCDE_DSICMD2SYNC_DMA_SHIFT 0
+#define MCDE_DSICMD2SYNC_DMA_MASK 0x00000FFF
+#define MCDE_DSICMD2SYNC_DMA(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2SYNC, DMA, __x)
+#define MCDE_DSICMD2SYNC_SW_SHIFT 16
+#define MCDE_DSICMD2SYNC_SW_MASK 0x0FFF0000
+#define MCDE_DSICMD2SYNC_SW(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2SYNC, SW, __x)
+#define MCDE_DSIVID0CMDW 0x00000E10
+#define MCDE_DSIVID0CMDW_GROUPOFFSET 0x20
+#define MCDE_DSIVID0CMDW_CMDW_CONTINUE_SHIFT 0
+#define MCDE_DSIVID0CMDW_CMDW_CONTINUE_MASK 0x0000FFFF
+#define MCDE_DSIVID0CMDW_CMDW_CONTINUE(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0CMDW, CMDW_CONTINUE, __x)
+#define MCDE_DSIVID0CMDW_CMDW_START_SHIFT 16
+#define MCDE_DSIVID0CMDW_CMDW_START_MASK 0xFFFF0000
+#define MCDE_DSIVID0CMDW_CMDW_START(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0CMDW, CMDW_START, __x)
+#define MCDE_DSICMD0CMDW 0x00000E30
+#define MCDE_DSICMD0CMDW_CMDW_CONTINUE_SHIFT 0
+#define MCDE_DSICMD0CMDW_CMDW_CONTINUE_MASK 0x0000FFFF
+#define MCDE_DSICMD0CMDW_CMDW_CONTINUE(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0CMDW, CMDW_CONTINUE, __x)
+#define MCDE_DSICMD0CMDW_CMDW_START_SHIFT 16
+#define MCDE_DSICMD0CMDW_CMDW_START_MASK 0xFFFF0000
+#define MCDE_DSICMD0CMDW_CMDW_START(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0CMDW, CMDW_START, __x)
+#define MCDE_DSIVID1CMDW 0x00000E50
+#define MCDE_DSIVID1CMDW_CMDW_CONTINUE_SHIFT 0
+#define MCDE_DSIVID1CMDW_CMDW_CONTINUE_MASK 0x0000FFFF
+#define MCDE_DSIVID1CMDW_CMDW_CONTINUE(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1CMDW, CMDW_CONTINUE, __x)
+#define MCDE_DSIVID1CMDW_CMDW_START_SHIFT 16
+#define MCDE_DSIVID1CMDW_CMDW_START_MASK 0xFFFF0000
+#define MCDE_DSIVID1CMDW_CMDW_START(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1CMDW, CMDW_START, __x)
+#define MCDE_DSICMD1CMDW 0x00000E70
+#define MCDE_DSICMD1CMDW_CMDW_CONTINUE_SHIFT 0
+#define MCDE_DSICMD1CMDW_CMDW_CONTINUE_MASK 0x0000FFFF
+#define MCDE_DSICMD1CMDW_CMDW_CONTINUE(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1CMDW, CMDW_CONTINUE, __x)
+#define MCDE_DSICMD1CMDW_CMDW_START_SHIFT 16
+#define MCDE_DSICMD1CMDW_CMDW_START_MASK 0xFFFF0000
+#define MCDE_DSICMD1CMDW_CMDW_START(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1CMDW, CMDW_START, __x)
+#define MCDE_DSIVID2CMDW 0x00000E90
+#define MCDE_DSIVID2CMDW_CMDW_CONTINUE_SHIFT 0
+#define MCDE_DSIVID2CMDW_CMDW_CONTINUE_MASK 0x0000FFFF
+#define MCDE_DSIVID2CMDW_CMDW_CONTINUE(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2CMDW, CMDW_CONTINUE, __x)
+#define MCDE_DSIVID2CMDW_CMDW_START_SHIFT 16
+#define MCDE_DSIVID2CMDW_CMDW_START_MASK 0xFFFF0000
+#define MCDE_DSIVID2CMDW_CMDW_START(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2CMDW, CMDW_START, __x)
+#define MCDE_DSICMD2CMDW 0x00000EB0
+#define MCDE_DSICMD2CMDW_CMDW_CONTINUE_SHIFT 0
+#define MCDE_DSICMD2CMDW_CMDW_CONTINUE_MASK 0x0000FFFF
+#define MCDE_DSICMD2CMDW_CMDW_CONTINUE(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2CMDW, CMDW_CONTINUE, __x)
+#define MCDE_DSICMD2CMDW_CMDW_START_SHIFT 16
+#define MCDE_DSICMD2CMDW_CMDW_START_MASK 0xFFFF0000
+#define MCDE_DSICMD2CMDW_CMDW_START(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2CMDW, CMDW_START, __x)
+#define MCDE_DSIVID0DELAY0 0x00000E14
+#define MCDE_DSIVID0DELAY0_GROUPOFFSET 0x20
+#define MCDE_DSIVID0DELAY0_INTPKTDEL_SHIFT 0
+#define MCDE_DSIVID0DELAY0_INTPKTDEL_MASK 0x0000FFFF
+#define MCDE_DSIVID0DELAY0_INTPKTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0DELAY0, INTPKTDEL, __x)
+#define MCDE_DSICMD0DELAY0 0x00000E34
+#define MCDE_DSICMD0DELAY0_INTPKTDEL_SHIFT 0
+#define MCDE_DSICMD0DELAY0_INTPKTDEL_MASK 0x0000FFFF
+#define MCDE_DSICMD0DELAY0_INTPKTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0DELAY0, INTPKTDEL, __x)
+#define MCDE_DSIVID1DELAY0 0x00000E54
+#define MCDE_DSIVID1DELAY0_INTPKTDEL_SHIFT 0
+#define MCDE_DSIVID1DELAY0_INTPKTDEL_MASK 0x0000FFFF
+#define MCDE_DSIVID1DELAY0_INTPKTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1DELAY0, INTPKTDEL, __x)
+#define MCDE_DSICMD1DELAY0 0x00000E74
+#define MCDE_DSICMD1DELAY0_INTPKTDEL_SHIFT 0
+#define MCDE_DSICMD1DELAY0_INTPKTDEL_MASK 0x0000FFFF
+#define MCDE_DSICMD1DELAY0_INTPKTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1DELAY0, INTPKTDEL, __x)
+#define MCDE_DSIVID2DELAY0 0x00000E94
+#define MCDE_DSIVID2DELAY0_INTPKTDEL_SHIFT 0
+#define MCDE_DSIVID2DELAY0_INTPKTDEL_MASK 0x0000FFFF
+#define MCDE_DSIVID2DELAY0_INTPKTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2DELAY0, INTPKTDEL, __x)
+#define MCDE_DSICMD2DELAY0 0x00000EB4
+#define MCDE_DSICMD2DELAY0_INTPKTDEL_SHIFT 0
+#define MCDE_DSICMD2DELAY0_INTPKTDEL_MASK 0x0000FFFF
+#define MCDE_DSICMD2DELAY0_INTPKTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2DELAY0, INTPKTDEL, __x)
+#define MCDE_DSIVID0DELAY1 0x00000E18
+#define MCDE_DSIVID0DELAY1_GROUPOFFSET 0x20
+#define MCDE_DSIVID0DELAY1_TEREQDEL_SHIFT 0
+#define MCDE_DSIVID0DELAY1_TEREQDEL_MASK 0x00000FFF
+#define MCDE_DSIVID0DELAY1_TEREQDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0DELAY1, TEREQDEL, __x)
+#define MCDE_DSIVID0DELAY1_FRAMESTARTDEL_SHIFT 16
+#define MCDE_DSIVID0DELAY1_FRAMESTARTDEL_MASK 0x00FF0000
+#define MCDE_DSIVID0DELAY1_FRAMESTARTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID0DELAY1, FRAMESTARTDEL, __x)
+#define MCDE_DSICMD0DELAY1 0x00000E38
+#define MCDE_DSICMD0DELAY1_TEREQDEL_SHIFT 0
+#define MCDE_DSICMD0DELAY1_TEREQDEL_MASK 0x00000FFF
+#define MCDE_DSICMD0DELAY1_TEREQDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0DELAY1, TEREQDEL, __x)
+#define MCDE_DSICMD0DELAY1_FRAMESTARTDEL_SHIFT 16
+#define MCDE_DSICMD0DELAY1_FRAMESTARTDEL_MASK 0x00FF0000
+#define MCDE_DSICMD0DELAY1_FRAMESTARTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD0DELAY1, FRAMESTARTDEL, __x)
+#define MCDE_DSIVID1DELAY1 0x00000E58
+#define MCDE_DSIVID1DELAY1_TEREQDEL_SHIFT 0
+#define MCDE_DSIVID1DELAY1_TEREQDEL_MASK 0x00000FFF
+#define MCDE_DSIVID1DELAY1_TEREQDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1DELAY1, TEREQDEL, __x)
+#define MCDE_DSIVID1DELAY1_FRAMESTARTDEL_SHIFT 16
+#define MCDE_DSIVID1DELAY1_FRAMESTARTDEL_MASK 0x00FF0000
+#define MCDE_DSIVID1DELAY1_FRAMESTARTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID1DELAY1, FRAMESTARTDEL, __x)
+#define MCDE_DSICMD1DELAY1 0x00000E78
+#define MCDE_DSICMD1DELAY1_TEREQDEL_SHIFT 0
+#define MCDE_DSICMD1DELAY1_TEREQDEL_MASK 0x00000FFF
+#define MCDE_DSICMD1DELAY1_TEREQDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1DELAY1, TEREQDEL, __x)
+#define MCDE_DSICMD1DELAY1_FRAMESTARTDEL_SHIFT 16
+#define MCDE_DSICMD1DELAY1_FRAMESTARTDEL_MASK 0x00FF0000
+#define MCDE_DSICMD1DELAY1_FRAMESTARTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD1DELAY1, FRAMESTARTDEL, __x)
+#define MCDE_DSIVID2DELAY1 0x00000E98
+#define MCDE_DSIVID2DELAY1_TEREQDEL_SHIFT 0
+#define MCDE_DSIVID2DELAY1_TEREQDEL_MASK 0x00000FFF
+#define MCDE_DSIVID2DELAY1_TEREQDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2DELAY1, TEREQDEL, __x)
+#define MCDE_DSIVID2DELAY1_FRAMESTARTDEL_SHIFT 16
+#define MCDE_DSIVID2DELAY1_FRAMESTARTDEL_MASK 0x00FF0000
+#define MCDE_DSIVID2DELAY1_FRAMESTARTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSIVID2DELAY1, FRAMESTARTDEL, __x)
+#define MCDE_DSICMD2DELAY1 0x00000EB8
+#define MCDE_DSICMD2DELAY1_TEREQDEL_SHIFT 0
+#define MCDE_DSICMD2DELAY1_TEREQDEL_MASK 0x00000FFF
+#define MCDE_DSICMD2DELAY1_TEREQDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2DELAY1, TEREQDEL, __x)
+#define MCDE_DSICMD2DELAY1_FRAMESTARTDEL_SHIFT 16
+#define MCDE_DSICMD2DELAY1_FRAMESTARTDEL_MASK 0x00FF0000
+#define MCDE_DSICMD2DELAY1_FRAMESTARTDEL(__x) \
+ MCDE_VAL2REG(MCDE_DSICMD2DELAY1, FRAMESTARTDEL, __x)
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index 2839e281cd6..b7b5014ff71 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -517,10 +517,10 @@ static W1_MASTER_ATTR_RO(max_slave_count, S_IRUGO);
static W1_MASTER_ATTR_RO(attempts, S_IRUGO);
static W1_MASTER_ATTR_RO(timeout, S_IRUGO);
static W1_MASTER_ATTR_RO(pointer, S_IRUGO);
-static W1_MASTER_ATTR_RW(search, S_IRUGO | S_IWUGO);
-static W1_MASTER_ATTR_RW(pullup, S_IRUGO | S_IWUGO);
-static W1_MASTER_ATTR_RW(add, S_IRUGO | S_IWUGO);
-static W1_MASTER_ATTR_RW(remove, S_IRUGO | S_IWUGO);
+static W1_MASTER_ATTR_RW(search, S_IRUGO | S_IWUSR | S_IWGRP);
+static W1_MASTER_ATTR_RW(pullup, S_IRUGO | S_IWUSR | S_IWGRP);
+static W1_MASTER_ATTR_RW(add, S_IRUGO | S_IWUSR | S_IWGRP);
+static W1_MASTER_ATTR_RW(remove, S_IRUGO | S_IWUSR | S_IWGRP);
static struct attribute *w1_master_default_attrs[] = {
&w1_master_attribute_name.attr,
diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c
index b8ec7aca3c8..93bea241426 100644
--- a/drivers/watchdog/mpcore_wdt.c
+++ b/drivers/watchdog/mpcore_wdt.c
@@ -32,11 +32,13 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/cpufreq.h>
+#include <linux/kexec.h>
#include <asm/smp_twd.h>
struct mpcore_wdt {
- unsigned long timer_alive;
+ cpumask_t timer_alive;
struct device *dev;
void __iomem *base;
int irq;
@@ -47,6 +49,8 @@ struct mpcore_wdt {
static struct platform_device *mpcore_wdt_dev;
static DEFINE_SPINLOCK(wdt_lock);
+static DEFINE_PER_CPU(unsigned long, mpcore_wdt_rate);
+
#define TIMER_MARGIN 60
static int mpcore_margin = TIMER_MARGIN;
module_param(mpcore_margin, int, 0);
@@ -67,6 +71,8 @@ MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, "
"set to 1 to ignore reboots, 0 to reboot (default="
__MODULE_STRING(ONLY_TESTING) ")");
+#define MPCORE_WDT_PERIPHCLK_PRESCALER 2
+
/*
* This is the interrupt handler. Note that we only use this
* in testing mode, so don't actually do a reboot here.
@@ -99,9 +105,8 @@ static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
spin_lock(&wdt_lock);
/* Assume prescale is set to 256 */
- count = __raw_readl(wdt->base + TWD_WDOG_COUNTER);
- count = (0xFFFFFFFFU - count) * (HZ / 5);
- count = (count / 256) * mpcore_margin;
+ count = per_cpu(mpcore_wdt_rate, smp_processor_id()) / 256;
+ count = count*mpcore_margin;
/* Reload the counter */
writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
@@ -109,6 +114,56 @@ static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
spin_unlock(&wdt_lock);
}
+static void mpcore_wdt_set_rate(unsigned long new_rate)
+{
+ unsigned long count;
+ unsigned long long rate_tmp;
+ unsigned long old_rate;
+
+ spin_lock(&wdt_lock);
+ old_rate = per_cpu(mpcore_wdt_rate, smp_processor_id());
+ per_cpu(mpcore_wdt_rate, smp_processor_id()) = new_rate;
+
+ if (mpcore_wdt_dev) {
+ struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_dev);
+ count = readl(wdt->base + TWD_WDOG_COUNTER);
+ /* The goal: count = count * (new_rate/old_rate); */
+ rate_tmp = (unsigned long long)count * new_rate;
+ do_div(rate_tmp, old_rate);
+ count = rate_tmp;
+ writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
+ wdt->perturb = wdt->perturb ? 0 : 1;
+ }
+ spin_unlock(&wdt_lock);
+}
+
+static void mpcore_wdt_update_cpu_frequency_on_cpu(void *data)
+{
+ struct cpufreq_freqs *freq = data;
+ mpcore_wdt_set_rate((freq->new * 1000) /
+ MPCORE_WDT_PERIPHCLK_PRESCALER);
+}
+
+static int mpcore_wdt_cpufreq_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct cpufreq_freqs *freq = data;
+
+ if (event == CPUFREQ_RESUMECHANGE ||
+ (event == CPUFREQ_PRECHANGE && freq->new > freq->old) ||
+ (event == CPUFREQ_POSTCHANGE && freq->new < freq->old))
+ smp_call_function_single(freq->cpu,
+ mpcore_wdt_update_cpu_frequency_on_cpu,
+ freq, 1);
+
+ return 0;
+}
+
+static struct notifier_block mpcore_wdt_cpufreq_notifier_block = {
+ .notifier_call = mpcore_wdt_cpufreq_notifier,
+};
+
+
static void mpcore_wdt_stop(struct mpcore_wdt *wdt)
{
spin_lock(&wdt_lock);
@@ -143,6 +198,20 @@ static int mpcore_wdt_set_heartbeat(int t)
return 0;
}
+static int mpcore_wdt_stop_notifier(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_dev);
+ printk(KERN_INFO "Stopping watchdog on non-crashing core %u\n",
+ smp_processor_id());
+ mpcore_wdt_stop(wdt);
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block mpcore_wdt_stop_block = {
+ .notifier_call = mpcore_wdt_stop_notifier,
+};
+
/*
* /dev/watchdog handling
*/
@@ -150,7 +219,7 @@ static int mpcore_wdt_open(struct inode *inode, struct file *file)
{
struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_dev);
- if (test_and_set_bit(0, &wdt->timer_alive))
+ if (cpumask_test_and_set_cpu(smp_processor_id(), &wdt->timer_alive))
return -EBUSY;
if (nowayout)
@@ -158,6 +227,9 @@ static int mpcore_wdt_open(struct inode *inode, struct file *file)
file->private_data = wdt;
+ atomic_notifier_chain_register(&crash_percpu_notifier_list,
+ &mpcore_wdt_stop_block);
+
/*
* Activate timer
*/
@@ -181,7 +253,7 @@ static int mpcore_wdt_release(struct inode *inode, struct file *file)
"unexpected close, not stopping watchdog!\n");
mpcore_wdt_keepalive(wdt);
}
- clear_bit(0, &wdt->timer_alive);
+ cpumask_clear_cpu(smp_processor_id(), &wdt->timer_alive);
wdt->expect_close = 0;
return 0;
}
@@ -425,16 +497,31 @@ static char banner[] __initdata = KERN_INFO "MPcore Watchdog Timer: 0.1. "
static int __init mpcore_wdt_init(void)
{
+ int i;
+
/*
* Check that the margin value is within it's range;
* if not reset to the default
*/
if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
mpcore_wdt_set_heartbeat(TIMER_MARGIN);
- printk(KERN_INFO "mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
+ printk(KERN_INFO "mpcore_wdt: mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
TIMER_MARGIN);
}
+ cpufreq_register_notifier(&mpcore_wdt_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ for_each_online_cpu(i)
+ per_cpu(mpcore_wdt_rate, i) =
+ (cpufreq_get(i) * 1000) / MPCORE_WDT_PERIPHCLK_PRESCALER;
+
+ for_each_online_cpu(i)
+ printk(KERN_INFO
+ "mpcore_wdt: rate for core %d is %lu.%02luMHz.\n", i,
+ per_cpu(mpcore_wdt_rate, i) / 1000000,
+ (per_cpu(mpcore_wdt_rate, i) / 10000) % 100);
+
printk(banner, mpcore_noboot, mpcore_margin, nowayout);
return platform_driver_register(&mpcore_wdt_driver);